[infiniband] Update all other MAD users to use a management interface
[people/peper/gpxe.git] / src / net / infiniband / ib_pathrec.c
1 /*
2  * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 FILE_LICENCE ( GPL2_OR_LATER );
20
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <byteswap.h>
25 #include <errno.h>
26 #include <gpxe/infiniband.h>
27 #include <gpxe/ib_mi.h>
28 #include <gpxe/ib_pathrec.h>
29
30 /** @file
31  *
32  * Infiniband path lookups
33  *
34  */
35
36 /**
37  * Handle path transaction completion
38  *
39  * @v ibdev             Infiniband device
40  * @v mi                Management interface
41  * @v madx              Management transaction
42  * @v rc                Status code
43  * @v mad               Received MAD (or NULL on error)
44  * @v av                Source address vector (or NULL on error)
45  */
46 static void ib_path_complete ( struct ib_device *ibdev,
47                                struct ib_mad_interface *mi,
48                                struct ib_mad_transaction *madx,
49                                int rc, union ib_mad *mad,
50                                struct ib_address_vector *av __unused ) {
51         struct ib_path *path = ib_madx_get_ownerdata ( madx );
52         struct ib_gid *dgid = &path->av.gid;
53         struct ib_path_record *pathrec = &mad->sa.sa_data.path_record;
54
55         /* Report failures */
56         if ( rc != 0 ) {
57                 DBGC ( ibdev, "IBDEV %p path lookup for %08x:%08x:%08x:%08x "
58                        "failed: %s\n", ibdev, htonl ( dgid->u.dwords[0] ),
59                        htonl ( dgid->u.dwords[1] ),
60                        htonl ( dgid->u.dwords[2] ),
61                        htonl ( dgid->u.dwords[3] ), strerror ( rc ) );
62                 goto out;
63         }
64
65         /* Extract values from MAD */
66         path->av.lid = ntohs ( pathrec->dlid );
67         path->av.sl = ( pathrec->reserved__sl & 0x0f );
68         path->av.rate = ( pathrec->rate_selector__rate & 0x3f );
69         DBGC ( ibdev, "IBDEV %p path to %08x:%08x:%08x:%08x is %04x sl %d "
70                "rate %d\n", ibdev, htonl ( dgid->u.dwords[0] ),
71                htonl ( dgid->u.dwords[1] ), htonl ( dgid->u.dwords[2] ),
72                htonl ( dgid->u.dwords[3] ), path->av.lid, path->av.sl,
73                path->av.rate );
74
75  out:
76         /* Destroy the completed transaction */
77         ib_destroy_madx ( ibdev, mi, madx );
78         path->madx = NULL;
79
80         /* Hand off to upper completion handler */
81         path->op->complete ( ibdev, path, rc, &path->av );
82 }
83
84 /** Path transaction completion operations */
85 static struct ib_mad_transaction_operations ib_path_op = {
86         .complete = ib_path_complete,
87 };
88
89 /**
90  * Create path
91  *
92  * @v ibdev             Infiniband device
93  * @v av                Address vector to complete
94  * @v op                Path operations
95  * @ret path            Path
96  */
97 struct ib_path *
98 ib_create_path ( struct ib_device *ibdev, struct ib_address_vector *av,
99                  struct ib_path_operations *op ) {
100         struct ib_path *path;
101         union ib_mad mad;
102         struct ib_mad_sa *sa = &mad.sa;
103
104         /* Allocate and initialise structure */
105         path = zalloc ( sizeof ( *path ) );
106         if ( ! path )
107                 goto err_alloc_path;
108         path->ibdev = ibdev;
109         memcpy ( &path->av, av, sizeof ( path->av ) );
110         path->op = op;
111
112         /* Construct path request */
113         memset ( sa, 0, sizeof ( *sa ) );
114         sa->mad_hdr.mgmt_class = IB_MGMT_CLASS_SUBN_ADM;
115         sa->mad_hdr.class_version = IB_SA_CLASS_VERSION;
116         sa->mad_hdr.method = IB_MGMT_METHOD_GET;
117         sa->mad_hdr.attr_id = htons ( IB_SA_ATTR_PATH_REC );
118         sa->sa_hdr.comp_mask[1] =
119                 htonl ( IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID );
120         memcpy ( &sa->sa_data.path_record.dgid, &path->av.gid,
121                  sizeof ( sa->sa_data.path_record.dgid ) );
122         memcpy ( &sa->sa_data.path_record.sgid, &ibdev->gid,
123                  sizeof ( sa->sa_data.path_record.sgid ) );
124
125         /* Create management transaction */
126         path->madx = ib_create_madx ( ibdev, ibdev->gsi, &mad, NULL,
127                                       &ib_path_op );
128         if ( ! path->madx )
129                 goto err_create_madx;
130         ib_madx_set_ownerdata ( path->madx, path );
131
132         return path;
133
134         ib_destroy_madx ( ibdev, ibdev->gsi, path->madx );
135  err_create_madx:
136         free ( path );
137  err_alloc_path:
138         return NULL;
139 }
140
141 /**
142  * Destroy path
143  *
144  * @v ibdev             Infiniband device
145  * @v path              Path
146  */
147 void ib_destroy_path ( struct ib_device *ibdev, struct ib_path *path ) {
148
149         if ( path->madx )
150                 ib_destroy_madx ( ibdev, ibdev->gsi, path->madx );
151         free ( path );
152 }
153
154 /** Number of path cache entries
155  *
156  * Must be a power of two.
157  */
158 #define IB_NUM_CACHED_PATHS 4
159
160 /** A cached path */
161 struct ib_cached_path {
162         /** Path */
163         struct ib_path *path;
164 };
165
166 /** Path cache */
167 static struct ib_cached_path ib_path_cache[IB_NUM_CACHED_PATHS];
168
169 /** Oldest path cache entry index */
170 static unsigned int ib_path_cache_idx;
171
172 /**
173  * Find path cache entry
174  *
175  * @v ibdev             Infiniband device
176  * @v dgid              Destination GID
177  * @ret path            Path cache entry, or NULL
178  */
179 static struct ib_cached_path *
180 ib_find_path_cache_entry ( struct ib_device *ibdev, struct ib_gid *dgid ) {
181         struct ib_cached_path *cached;
182         unsigned int i;
183
184         for ( i = 0 ; i < IB_NUM_CACHED_PATHS ; i++ ) {
185                 cached = &ib_path_cache[i];
186                 if ( ! cached->path )
187                         continue;
188                 if ( cached->path->ibdev != ibdev )
189                         continue;
190                 if ( memcmp ( &cached->path->av.gid, dgid,
191                               sizeof ( cached->path->av.gid ) ) != 0 )
192                         continue;
193                 return cached;
194         }
195
196         return NULL;
197 }
198
199 /**
200  * Handle cached path transaction completion
201  *
202  * @v ibdev             Infiniband device
203  * @v path              Path
204  * @v rc                Status code
205  * @v av                Address vector, or NULL on error
206  */
207 static void ib_cached_path_complete ( struct ib_device *ibdev,
208                                       struct ib_path *path, int rc,
209                                       struct ib_address_vector *av __unused ) {
210         struct ib_cached_path *cached = ib_path_get_ownerdata ( path );
211
212         /* If the transaction failed, erase the cache entry */
213         if ( rc != 0 ) {
214                 /* Destroy the old cache entry */
215                 ib_destroy_path ( ibdev, path );
216                 memset ( cached, 0, sizeof ( *cached ) );
217                 return;
218         }
219
220         /* Do not destroy the completed transaction; we still need to
221          * refer to the resolved path.
222          */
223 }
224
225 /** Cached path transaction completion operations */
226 static struct ib_path_operations ib_cached_path_op = {
227         .complete = ib_cached_path_complete,
228 };
229
230 /**
231  * Resolve path
232  *
233  * @v ibdev             Infiniband device
234  * @v av                Address vector to complete
235  * @ret rc              Return status code
236  *
237  * This provides a non-transactional way to resolve a path, via a
238  * cache similar to ARP.
239  */
240 int ib_resolve_path ( struct ib_device *ibdev, struct ib_address_vector *av ) {
241         struct ib_gid *gid = &av->gid;
242         struct ib_cached_path *cached;
243         unsigned int cache_idx;
244
245         /* Sanity check */
246         if ( ! av->gid_present ) {
247                 DBGC ( ibdev, "IBDEV %p attempt to look up path "
248                        "without GID\n", ibdev );
249                 return -EINVAL;
250         }
251
252         /* Look in cache for a matching entry */
253         cached = ib_find_path_cache_entry ( ibdev, gid );
254         if ( cached && cached->path->av.lid ) {
255                 /* Populated entry found */
256                 av->lid = cached->path->av.lid;
257                 av->rate = cached->path->av.rate;
258                 av->sl = cached->path->av.sl;
259                 DBGC2 ( ibdev, "IBDEV %p cache hit for %08x:%08x:%08x:%08x\n",
260                         ibdev, htonl ( gid->u.dwords[0] ),
261                         htonl ( gid->u.dwords[1] ), htonl ( gid->u.dwords[2] ),
262                         htonl ( gid->u.dwords[3] ) );
263                 return 0;
264         }
265         DBGC ( ibdev, "IBDEV %p cache miss for %08x:%08x:%08x:%08x%s\n",
266                ibdev, htonl ( gid->u.dwords[0] ), htonl ( gid->u.dwords[1] ),
267                htonl ( gid->u.dwords[2] ), htonl ( gid->u.dwords[3] ),
268                ( cached ? " (in progress)" : "" ) );
269
270         /* If lookup is already in progress, do nothing */
271         if ( cached )
272                 return -ENOENT;
273
274         /* Locate a new cache entry to use */
275         cache_idx = ( (ib_path_cache_idx++) % IB_NUM_CACHED_PATHS );
276         cached = &ib_path_cache[cache_idx];
277
278         /* Destroy the old cache entry */
279         if ( cached->path )
280                 ib_destroy_path ( ibdev, cached->path );
281         memset ( cached, 0, sizeof ( *cached ) );
282
283         /* Create new path */
284         cached->path = ib_create_path ( ibdev, av, &ib_cached_path_op );
285         if ( ! cached->path ) {
286                 DBGC ( ibdev, "IBDEV %p could not create path\n",
287                        ibdev );
288                 return -ENOMEM;
289         }
290         ib_path_set_ownerdata ( cached->path, cached );
291
292         /* Not found yet */
293         return -ENOENT;
294 }