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