1 /*
2 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2007 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
6 *
7 * This software is available to you under a choice of one of two
8 * licenses. You may choose to be licensed under the terms of the GNU
9 * General Public License (GPL) Version 2, available from the file
10 * COPYING in the main directory of this source tree, or the
11 * OpenIB.org BSD license below:
12 *
13 * Redistribution and use in source and binary forms, with or
14 * without modification, are permitted provided that the following
15 * conditions are met:
16 *
17 * - Redistributions of source code must retain the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer.
20 *
21 * - Redistributions in binary form must reproduce the above
22 * copyright notice, this list of conditions and the following
23 * disclaimer in the documentation and/or other materials
24 * provided with the distribution.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 * SOFTWARE.
34 *
35 */
36
37 /*
38 * Abstract:
39 * Implementation of osm_lr_rcv_t.
40 * This object represents the LinkRecord Receiver object.
41 * This object is part of the opensm family of objects.
42 */
43
44 #if HAVE_CONFIG_H
45 # include <config.h>
46 #endif /* HAVE_CONFIG_H */
47
48 #include <string.h>
49 #include <iba/ib_types.h>
50 #include <complib/cl_qmap.h>
51 #include <complib/cl_debug.h>
52 #include <opensm/osm_file_ids.h>
53 #define FILE_ID OSM_FILE_SA_LINK_RECORD_C
54 #include <vendor/osm_vendor_api.h>
55 #include <opensm/osm_node.h>
56 #include <opensm/osm_switch.h>
57 #include <opensm/osm_helper.h>
58 #include <opensm/osm_pkey.h>
59 #include <opensm/osm_sa.h>
60
61 #define SA_LR_RESP_SIZE SA_ITEM_RESP_SIZE(link_rec)
62
lr_rcv_build_physp_link(IN osm_sa_t * sa,IN ib_net16_t from_lid,IN ib_net16_t to_lid,IN uint8_t from_port,IN uint8_t to_port,IN cl_qlist_t * p_list)63 static void lr_rcv_build_physp_link(IN osm_sa_t * sa, IN ib_net16_t from_lid,
64 IN ib_net16_t to_lid, IN uint8_t from_port,
65 IN uint8_t to_port, IN cl_qlist_t * p_list)
66 {
67 osm_sa_item_t *p_lr_item;
68
69 p_lr_item = malloc(SA_LR_RESP_SIZE);
70 if (p_lr_item == NULL) {
71 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1801: "
72 "Unable to acquire link record\n"
73 "\t\t\t\tFrom port %u\n" "\t\t\t\tTo port %u\n"
74 "\t\t\t\tFrom lid %u\n" "\t\t\t\tTo lid %u\n",
75 from_port, to_port,
76 cl_ntoh16(from_lid), cl_ntoh16(to_lid));
77 return;
78 }
79 memset(p_lr_item, 0, SA_LR_RESP_SIZE);
80
81 p_lr_item->resp.link_rec.from_port_num = from_port;
82 p_lr_item->resp.link_rec.to_port_num = to_port;
83 p_lr_item->resp.link_rec.to_lid = to_lid;
84 p_lr_item->resp.link_rec.from_lid = from_lid;
85
86 cl_qlist_insert_tail(p_list, &p_lr_item->list_item);
87 }
88
get_base_lid(IN const osm_physp_t * p_physp)89 static ib_net16_t get_base_lid(IN const osm_physp_t * p_physp)
90 {
91 if (p_physp->p_node->node_info.node_type == IB_NODE_TYPE_SWITCH)
92 p_physp = osm_node_get_physp_ptr(p_physp->p_node, 0);
93 return osm_physp_get_base_lid(p_physp);
94 }
95
lr_rcv_get_physp_link(IN osm_sa_t * sa,IN const ib_link_record_t * p_lr,IN const osm_physp_t * p_src_physp,IN const osm_physp_t * p_dest_physp,IN const ib_net64_t comp_mask,IN cl_qlist_t * p_list,IN const osm_physp_t * p_req_physp)96 static void lr_rcv_get_physp_link(IN osm_sa_t * sa,
97 IN const ib_link_record_t * p_lr,
98 IN const osm_physp_t * p_src_physp,
99 IN const osm_physp_t * p_dest_physp,
100 IN const ib_net64_t comp_mask,
101 IN cl_qlist_t * p_list,
102 IN const osm_physp_t * p_req_physp)
103 {
104 uint8_t src_port_num;
105 uint8_t dest_port_num;
106 ib_net16_t from_base_lid;
107 ib_net16_t to_base_lid;
108 ib_net16_t lmc_mask;
109
110 OSM_LOG_ENTER(sa->p_log);
111
112 /*
113 If only one end of the link is specified, determine
114 the other side.
115 */
116 if (p_src_physp) {
117 if (p_dest_physp) {
118 /*
119 Ensure the two physp's are actually connected.
120 If not, bail out.
121 */
122 if (osm_physp_get_remote(p_src_physp) != p_dest_physp)
123 goto Exit;
124 } else {
125 p_dest_physp = osm_physp_get_remote(p_src_physp);
126 if (p_dest_physp == NULL)
127 goto Exit;
128 }
129 } else {
130 if (p_dest_physp) {
131 p_src_physp = osm_physp_get_remote(p_dest_physp);
132 if (p_src_physp == NULL)
133 goto Exit;
134 } else
135 goto Exit; /* no physp's, so nothing to do */
136 }
137
138 /* Check that the p_src_physp, p_dest_physp and p_req_physp
139 all share a pkey (doesn't have to be the same p_key). */
140 if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_dest_physp,
141 sa->p_subn->opt.allow_both_pkeys)) {
142 OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
143 "Source and Dest PhysPorts do not share PKey\n");
144 goto Exit;
145 }
146 if (!osm_physp_share_pkey(sa->p_log, p_src_physp, p_req_physp,
147 sa->p_subn->opt.allow_both_pkeys)) {
148 OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
149 "Source and Requester PhysPorts do not share PKey\n");
150 goto Exit;
151 }
152 if (!osm_physp_share_pkey(sa->p_log, p_req_physp, p_dest_physp,
153 sa->p_subn->opt.allow_both_pkeys)) {
154 OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
155 "Requester and Dest PhysPorts do not share PKey\n");
156 goto Exit;
157 }
158
159 src_port_num = osm_physp_get_port_num(p_src_physp);
160 dest_port_num = osm_physp_get_port_num(p_dest_physp);
161
162 if (comp_mask & IB_LR_COMPMASK_FROM_PORT)
163 if (src_port_num != p_lr->from_port_num)
164 goto Exit;
165
166 if (comp_mask & IB_LR_COMPMASK_TO_PORT)
167 if (dest_port_num != p_lr->to_port_num)
168 goto Exit;
169
170 from_base_lid = get_base_lid(p_src_physp);
171 to_base_lid = get_base_lid(p_dest_physp);
172
173 lmc_mask = ~((1 << sa->p_subn->opt.lmc) - 1);
174 lmc_mask = cl_hton16(lmc_mask);
175
176 if (comp_mask & IB_LR_COMPMASK_FROM_LID)
177 if (from_base_lid != (p_lr->from_lid & lmc_mask))
178 goto Exit;
179
180 if (comp_mask & IB_LR_COMPMASK_TO_LID)
181 if (to_base_lid != (p_lr->to_lid & lmc_mask))
182 goto Exit;
183
184 OSM_LOG(sa->p_log, OSM_LOG_DEBUG, "Acquiring link record\n"
185 "\t\t\t\tsrc port 0x%" PRIx64 " (port %u)"
186 ", dest port 0x%" PRIx64 " (port %u)\n",
187 cl_ntoh64(osm_physp_get_port_guid(p_src_physp)), src_port_num,
188 cl_ntoh64(osm_physp_get_port_guid(p_dest_physp)),
189 dest_port_num);
190
191 lr_rcv_build_physp_link(sa, from_base_lid, to_base_lid, src_port_num,
192 dest_port_num, p_list);
193
194 Exit:
195 OSM_LOG_EXIT(sa->p_log);
196 }
197
lr_rcv_get_port_links(IN osm_sa_t * sa,IN const ib_link_record_t * p_lr,IN const osm_port_t * p_src_port,IN const osm_port_t * p_dest_port,IN const ib_net64_t comp_mask,IN cl_qlist_t * p_list,IN const osm_physp_t * p_req_physp)198 static void lr_rcv_get_port_links(IN osm_sa_t * sa,
199 IN const ib_link_record_t * p_lr,
200 IN const osm_port_t * p_src_port,
201 IN const osm_port_t * p_dest_port,
202 IN const ib_net64_t comp_mask,
203 IN cl_qlist_t * p_list,
204 IN const osm_physp_t * p_req_physp)
205 {
206 const osm_physp_t *p_src_physp;
207 const osm_physp_t *p_dest_physp;
208 const cl_qmap_t *p_node_tbl;
209 osm_node_t *p_node;
210 uint8_t port_num;
211 uint8_t num_ports;
212 uint8_t dest_num_ports;
213 uint8_t dest_port_num;
214
215 OSM_LOG_ENTER(sa->p_log);
216
217 if (p_src_port) {
218 if (p_dest_port) {
219 /*
220 Build an LR for every link connected between both ports.
221 The inner function will discard physp combinations
222 that do not actually connect. Don't bother screening
223 for that here.
224 */
225 num_ports = osm_node_get_num_physp(p_src_port->p_node);
226 dest_num_ports =
227 osm_node_get_num_physp(p_dest_port->p_node);
228 for (port_num = 1; port_num < num_ports; port_num++) {
229 p_src_physp =
230 osm_node_get_physp_ptr(p_src_port->p_node,
231 port_num);
232 for (dest_port_num = 1;
233 dest_port_num < dest_num_ports;
234 dest_port_num++) {
235 p_dest_physp =
236 osm_node_get_physp_ptr(p_dest_port->
237 p_node,
238 dest_port_num);
239 /* both physical ports should be with data */
240 if (p_src_physp && p_dest_physp)
241 lr_rcv_get_physp_link
242 (sa, p_lr, p_src_physp,
243 p_dest_physp, comp_mask,
244 p_list, p_req_physp);
245 }
246 }
247 } else {
248 /*
249 Build an LR for every link connected from the source port.
250 */
251 if (comp_mask & IB_LR_COMPMASK_FROM_PORT) {
252 port_num = p_lr->from_port_num;
253 /* If the port number is out of the range of the p_src_port, then
254 this couldn't be a relevant record. */
255 if (port_num <
256 p_src_port->p_node->physp_tbl_size) {
257 p_src_physp =
258 osm_node_get_physp_ptr(p_src_port->
259 p_node,
260 port_num);
261 if (p_src_physp)
262 lr_rcv_get_physp_link
263 (sa, p_lr, p_src_physp,
264 NULL, comp_mask, p_list,
265 p_req_physp);
266 }
267 } else {
268 num_ports =
269 osm_node_get_num_physp(p_src_port->p_node);
270 for (port_num = 1; port_num < num_ports;
271 port_num++) {
272 p_src_physp =
273 osm_node_get_physp_ptr(p_src_port->
274 p_node,
275 port_num);
276 if (p_src_physp)
277 lr_rcv_get_physp_link
278 (sa, p_lr, p_src_physp,
279 NULL, comp_mask, p_list,
280 p_req_physp);
281 }
282 }
283 }
284 } else {
285 if (p_dest_port) {
286 /*
287 Build an LR for every link connected to the dest port.
288 */
289 if (comp_mask & IB_LR_COMPMASK_TO_PORT) {
290 port_num = p_lr->to_port_num;
291 /* If the port number is out of the range of the p_dest_port, then
292 this couldn't be a relevant record. */
293 if (port_num <
294 p_dest_port->p_node->physp_tbl_size) {
295 p_dest_physp =
296 osm_node_get_physp_ptr(p_dest_port->
297 p_node,
298 port_num);
299 if (p_dest_physp)
300 lr_rcv_get_physp_link
301 (sa, p_lr, NULL,
302 p_dest_physp, comp_mask,
303 p_list, p_req_physp);
304 }
305 } else {
306 num_ports =
307 osm_node_get_num_physp(p_dest_port->p_node);
308 for (port_num = 1; port_num < num_ports;
309 port_num++) {
310 p_dest_physp =
311 osm_node_get_physp_ptr(p_dest_port->
312 p_node,
313 port_num);
314 if (p_dest_physp)
315 lr_rcv_get_physp_link
316 (sa, p_lr, NULL,
317 p_dest_physp, comp_mask,
318 p_list, p_req_physp);
319 }
320 }
321 } else {
322 /*
323 Process the world (recurse once back into this function).
324 */
325 p_node_tbl = &sa->p_subn->node_guid_tbl;
326 p_node = (osm_node_t *) cl_qmap_head(p_node_tbl);
327
328 while (p_node != (osm_node_t *) cl_qmap_end(p_node_tbl)) {
329 num_ports = osm_node_get_num_physp(p_node);
330 for (port_num = 1; port_num < num_ports;
331 port_num++) {
332 p_src_physp =
333 osm_node_get_physp_ptr(p_node,
334 port_num);
335 if (p_src_physp)
336 lr_rcv_get_physp_link
337 (sa, p_lr, p_src_physp,
338 NULL, comp_mask, p_list,
339 p_req_physp);
340 }
341 p_node = (osm_node_t *) cl_qmap_next(&p_node->
342 map_item);
343 }
344 }
345 }
346
347 OSM_LOG_EXIT(sa->p_log);
348 }
349
350 /**********************************************************************
351 Returns the SA status to return to the client.
352 **********************************************************************/
lr_rcv_get_end_points(IN osm_sa_t * sa,IN const osm_madw_t * p_madw,OUT const osm_port_t ** pp_src_port,OUT const osm_port_t ** pp_dest_port)353 static ib_net16_t lr_rcv_get_end_points(IN osm_sa_t * sa,
354 IN const osm_madw_t * p_madw,
355 OUT const osm_port_t ** pp_src_port,
356 OUT const osm_port_t ** pp_dest_port)
357 {
358 const ib_link_record_t *p_lr;
359 const ib_sa_mad_t *p_sa_mad;
360 ib_net64_t comp_mask;
361 ib_net16_t sa_status = IB_SA_MAD_STATUS_SUCCESS;
362
363 OSM_LOG_ENTER(sa->p_log);
364
365 /*
366 Determine what fields are valid and then get a pointer
367 to the source and destination port objects, if possible.
368 */
369 p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
370 p_lr = (ib_link_record_t *) ib_sa_mad_get_payload_ptr(p_sa_mad);
371
372 comp_mask = p_sa_mad->comp_mask;
373 *pp_src_port = NULL;
374 *pp_dest_port = NULL;
375
376 if (comp_mask & IB_LR_COMPMASK_FROM_LID) {
377 *pp_src_port = osm_get_port_by_lid(sa->p_subn, p_lr->from_lid);
378 if (!*pp_src_port) {
379 /*
380 This 'error' is the client's fault (bad lid) so
381 don't enter it as an error in our own log.
382 Return an error response to the client.
383 */
384 OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
385 "No source port with LID %u\n",
386 cl_ntoh16(p_lr->from_lid));
387
388 sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
389 goto Exit;
390 }
391 }
392
393 if (comp_mask & IB_LR_COMPMASK_TO_LID) {
394 *pp_dest_port = osm_get_port_by_lid(sa->p_subn, p_lr->to_lid);
395 if (!*pp_dest_port) {
396 /*
397 This 'error' is the client's fault (bad lid) so
398 don't enter it as an error in our own log.
399 Return an error response to the client.
400 */
401 OSM_LOG(sa->p_log, OSM_LOG_VERBOSE,
402 "No dest port with LID %u\n",
403 cl_ntoh16(p_lr->to_lid));
404
405 sa_status = IB_SA_MAD_STATUS_NO_RECORDS;
406 goto Exit;
407 }
408 }
409
410 Exit:
411 OSM_LOG_EXIT(sa->p_log);
412 return sa_status;
413 }
414
osm_lr_rcv_process(IN void * context,IN void * data)415 void osm_lr_rcv_process(IN void *context, IN void *data)
416 {
417 osm_sa_t *sa = context;
418 osm_madw_t *p_madw = data;
419 const ib_link_record_t *p_lr;
420 const ib_sa_mad_t *p_sa_mad;
421 const osm_port_t *p_src_port;
422 const osm_port_t *p_dest_port;
423 cl_qlist_t lr_list;
424 ib_net16_t status;
425 osm_physp_t *p_req_physp;
426
427 OSM_LOG_ENTER(sa->p_log);
428
429 CL_ASSERT(p_madw);
430
431 p_sa_mad = osm_madw_get_sa_mad_ptr(p_madw);
432 p_lr = ib_sa_mad_get_payload_ptr(p_sa_mad);
433
434 CL_ASSERT(p_sa_mad->attr_id == IB_MAD_ATTR_LINK_RECORD);
435
436 /* we only support SubnAdmGet and SubnAdmGetTable methods */
437 if (p_sa_mad->method != IB_MAD_METHOD_GET &&
438 p_sa_mad->method != IB_MAD_METHOD_GETTABLE) {
439 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1804: "
440 "Unsupported Method (%s) for LinkRecord request\n",
441 ib_get_sa_method_str(p_sa_mad->method));
442 osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
443 goto Exit;
444 }
445
446 cl_plock_acquire(sa->p_lock);
447
448 /* update the requester physical port */
449 p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
450 osm_madw_get_mad_addr_ptr
451 (p_madw));
452 if (p_req_physp == NULL) {
453 cl_plock_release(sa->p_lock);
454 OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 1805: "
455 "Cannot find requester physical port\n");
456 goto Exit;
457 }
458
459 if (OSM_LOG_IS_ACTIVE_V2(sa->p_log, OSM_LOG_DEBUG)) {
460 OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
461 "Requester port GUID 0x%" PRIx64 "\n",
462 cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
463 osm_dump_link_record_v2(sa->p_log, p_lr, FILE_ID, OSM_LOG_DEBUG);
464 }
465
466 cl_qlist_init(&lr_list);
467
468 /*
469 Most SA functions (including this one) are read-only on the
470 subnet object, so we grab the lock non-exclusively.
471 */
472 status = lr_rcv_get_end_points(sa, p_madw, &p_src_port, &p_dest_port);
473
474 if (status == IB_SA_MAD_STATUS_SUCCESS)
475 lr_rcv_get_port_links(sa, p_lr, p_src_port, p_dest_port,
476 p_sa_mad->comp_mask, &lr_list,
477 p_req_physp);
478
479 cl_plock_release(sa->p_lock);
480
481 osm_sa_respond(sa, p_madw, sizeof(ib_link_record_t), &lr_list);
482
483 Exit:
484 OSM_LOG_EXIT(sa->p_log);
485 }
486