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 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 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 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 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 **********************************************************************/ 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 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