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