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