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_si_rcv_t. 39 * This object represents the SwitchInfo 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_passivelock.h> 51#include <complib/cl_debug.h> 52#include <opensm/osm_log.h> 53#include <opensm/osm_switch.h> 54#include <opensm/osm_subnet.h> 55#include <opensm/osm_helper.h> 56#include <opensm/osm_opensm.h> 57 58/********************************************************************** 59 The plock must be held before calling this function. 60**********************************************************************/ 61static void 62__osm_si_rcv_get_port_info(IN osm_sm_t * sm, IN osm_switch_t * const p_sw) 63{ 64 osm_madw_context_t context; 65 uint8_t port_num; 66 osm_physp_t *p_physp; 67 osm_node_t *p_node; 68 uint8_t num_ports; 69 ib_api_status_t status = IB_SUCCESS; 70 71 OSM_LOG_ENTER(sm->p_log); 72 73 CL_ASSERT(p_sw); 74 75 p_node = p_sw->p_node; 76 77 CL_ASSERT(osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH); 78 79 /* 80 Request PortInfo attribute for each port on the switch. 81 */ 82 p_physp = osm_node_get_physp_ptr(p_node, 0); 83 84 context.pi_context.node_guid = osm_node_get_node_guid(p_node); 85 context.pi_context.port_guid = osm_physp_get_port_guid(p_physp); 86 context.pi_context.set_method = FALSE; 87 context.pi_context.light_sweep = FALSE; 88 context.pi_context.active_transition = FALSE; 89 90 num_ports = osm_node_get_num_physp(p_node); 91 92 for (port_num = 0; port_num < num_ports; port_num++) { 93 status = osm_req_get(sm, osm_physp_get_dr_path_ptr(p_physp), 94 IB_MAD_ATTR_PORT_INFO, cl_hton32(port_num), 95 CL_DISP_MSGID_NONE, &context); 96 if (status != IB_SUCCESS) 97 /* continue the loop despite the error */ 98 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 3602: " 99 "Failure initiating PortInfo request (%s)\n", 100 ib_get_err_str(status)); 101 } 102 103 OSM_LOG_EXIT(sm->p_log); 104} 105 106#if 0 107/********************************************************************** 108 The plock must be held before calling this function. 109**********************************************************************/ 110static void 111__osm_si_rcv_get_fwd_tbl(IN osm_sm_t * sm, IN osm_switch_t * const p_sw) 112{ 113 osm_madw_context_t context; 114 osm_dr_path_t *p_dr_path; 115 osm_physp_t *p_physp; 116 osm_node_t *p_node; 117 uint32_t block_id_ho; 118 uint32_t max_block_id_ho; 119 ib_api_status_t status = IB_SUCCESS; 120 121 OSM_LOG_ENTER(sm->p_log); 122 123 CL_ASSERT(p_sw); 124 125 p_node = p_sw->p_node; 126 127 CL_ASSERT(osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH); 128 129 context.lft_context.node_guid = osm_node_get_node_guid(p_node); 130 context.lft_context.set_method = FALSE; 131 132 max_block_id_ho = osm_switch_get_max_block_id_in_use(p_sw); 133 134 p_physp = osm_node_get_physp_ptr(p_node, 0); 135 p_dr_path = osm_physp_get_dr_path_ptr(p_physp); 136 137 for (block_id_ho = 0; block_id_ho <= max_block_id_ho; block_id_ho++) { 138 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 139 "Retrieving FT block %u\n", block_id_ho); 140 141 status = osm_req_get(sm, p_dr_path, IB_MAD_ATTR_LIN_FWD_TBL, 142 cl_hton32(block_id_ho), 143 CL_DISP_MSGID_NONE, &context); 144 if (status != IB_SUCCESS) 145 /* continue the loop despite the error */ 146 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 3603: " 147 "Failure initiating PortInfo request (%s)\n", 148 ib_get_err_str(status)); 149 } 150 151 OSM_LOG_EXIT(sm->p_log); 152} 153 154/********************************************************************** 155 The plock must be held before calling this function. 156**********************************************************************/ 157static void 158__osm_si_rcv_get_mcast_fwd_tbl(IN osm_sm_t * sm, IN osm_switch_t * const p_sw) 159{ 160 osm_madw_context_t context; 161 osm_dr_path_t *p_dr_path; 162 osm_physp_t *p_physp; 163 osm_node_t *p_node; 164 osm_mcast_tbl_t *p_tbl; 165 uint32_t block_id_ho; 166 uint32_t max_block_id_ho; 167 uint32_t position; 168 uint32_t max_position; 169 uint32_t attr_mod_ho; 170 ib_api_status_t status = IB_SUCCESS; 171 172 OSM_LOG_ENTER(sm->p_log); 173 174 CL_ASSERT(p_sw); 175 176 p_node = p_sw->p_node; 177 178 CL_ASSERT(osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH); 179 180 if (osm_switch_get_mcast_fwd_tbl_size(p_sw) == 0) { 181 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 182 "Multicast not supported by switch 0x%016" PRIx64 "\n", 183 cl_ntoh64(osm_node_get_node_guid(p_node))); 184 goto Exit; 185 } 186 187 context.mft_context.node_guid = osm_node_get_node_guid(p_node); 188 context.mft_context.set_method = FALSE; 189 190 p_tbl = osm_switch_get_mcast_tbl_ptr(p_sw); 191 max_block_id_ho = osm_mcast_tbl_get_max_block(p_tbl); 192 193 if (max_block_id_ho > IB_MCAST_MAX_BLOCK_ID) { 194 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 3609: " 195 "Out-of-range mcast block size = %u on switch 0x%016" 196 PRIx64 "\n", max_block_id_ho, 197 cl_ntoh64(osm_node_get_node_guid(p_node))); 198 goto Exit; 199 } 200 201 max_position = osm_mcast_tbl_get_max_position(p_tbl); 202 203 CL_ASSERT(max_position <= IB_MCAST_POSITION_MAX); 204 205 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 206 "Max MFT block = %u, Max position = %u\n", max_block_id_ho, 207 max_position); 208 209 p_physp = osm_node_get_physp_ptr(p_node, 0); 210 p_dr_path = osm_physp_get_dr_path_ptr(p_physp); 211 212 for (block_id_ho = 0; block_id_ho <= max_block_id_ho; block_id_ho++) { 213 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 214 "Retrieving MFT block %u\n", block_id_ho); 215 216 for (position = 0; position <= max_position; position++) { 217 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 218 "Retrieving MFT position %u\n", position); 219 220 attr_mod_ho = 221 block_id_ho | position << IB_MCAST_POSITION_SHIFT; 222 status = 223 osm_req_get(sm, p_dr_path, 224 IB_MAD_ATTR_MCAST_FWD_TBL, 225 cl_hton32(attr_mod_ho), 226 CL_DISP_MSGID_NONE, &context); 227 if (status != IB_SUCCESS) 228 /* continue the loop despite the error */ 229 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 3607: " 230 "Failure initiating PortInfo request (%s)\n", 231 ib_get_err_str(status)); 232 } 233 } 234 235Exit: 236 OSM_LOG_EXIT(sm->p_log); 237} 238#endif 239 240/********************************************************************** 241 Lock must be held on entry to this function. 242**********************************************************************/ 243static void 244__osm_si_rcv_process_new(IN osm_sm_t * sm, 245 IN osm_node_t * const p_node, 246 IN const osm_madw_t * const p_madw) 247{ 248 osm_switch_t *p_sw; 249 osm_switch_t *p_check; 250 ib_switch_info_t *p_si; 251 ib_smp_t *p_smp; 252 cl_qmap_t *p_sw_guid_tbl; 253 254 CL_ASSERT(sm); 255 256 OSM_LOG_ENTER(sm->p_log); 257 258 CL_ASSERT(p_madw); 259 260 p_sw_guid_tbl = &sm->p_subn->sw_guid_tbl; 261 262 p_smp = osm_madw_get_smp_ptr(p_madw); 263 p_si = (ib_switch_info_t *) ib_smp_get_payload_ptr(p_smp); 264 265 osm_dump_switch_info(sm->p_log, p_si, OSM_LOG_DEBUG); 266 267 /* 268 Allocate a new switch object for this switch, 269 and place it in the switch table. 270 */ 271 p_sw = osm_switch_new(p_node, p_madw); 272 if (p_sw == NULL) { 273 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 3608: " 274 "Unable to allocate new switch object\n"); 275 goto Exit; 276 } 277 278 /* set subnet max mlid to the minimum MulticastFDBCap of all switches */ 279 if (p_sw->mcast_tbl.max_mlid_ho < sm->p_subn->max_mcast_lid_ho) { 280 sm->p_subn->max_mcast_lid_ho = p_sw->mcast_tbl.max_mlid_ho; 281 OSM_LOG(sm->p_log, OSM_LOG_VERBOSE, 282 "Subnet max multicast lid is 0x%X\n", 283 sm->p_subn->max_mcast_lid_ho); 284 } 285 286 /* set subnet max unicast lid to the minimum LinearFDBCap of all switches */ 287 if (cl_ntoh16(p_si->lin_cap) < sm->p_subn->max_ucast_lid_ho) { 288 sm->p_subn->max_ucast_lid_ho = cl_ntoh16(p_si->lin_cap); 289 OSM_LOG(sm->p_log, OSM_LOG_VERBOSE, 290 "Subnet max unicast lid is 0x%X\n", 291 sm->p_subn->max_ucast_lid_ho); 292 } 293 294 p_check = (osm_switch_t *) cl_qmap_insert(p_sw_guid_tbl, 295 osm_node_get_node_guid 296 (p_node), &p_sw->map_item); 297 298 if (p_check != p_sw) { 299 /* 300 This shouldn't happen since we hold the lock! 301 */ 302 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 3605: " 303 "Unable to add new switch object to database\n"); 304 osm_switch_delete(&p_sw); 305 goto Exit; 306 } 307 308 p_node->sw = p_sw; 309 310 /* 311 Update the switch info according to the 312 info we just received. 313 */ 314 osm_switch_set_switch_info(p_sw, p_si); 315 p_sw->discovery_count++; 316 317 /* 318 Get the PortInfo attribute for every port. 319 */ 320 __osm_si_rcv_get_port_info(sm, p_sw); 321 322 /* 323 Don't bother retrieving the current unicast and multicast tables 324 from the switches. The current version of SM does 325 not support silent take-over of an existing multicast 326 configuration. 327 328 Gathering the multicast tables can also generate large amounts 329 of extra subnet-init traffic. 330 331 The code to retrieve the tables was fully debugged. 332 */ 333#if 0 334 __osm_si_rcv_get_fwd_tbl(sm, p_sw); 335 if (!sm->p_subn->opt.disable_multicast) 336 __osm_si_rcv_get_mcast_fwd_tbl(sm, p_sw); 337#endif 338 339Exit: 340 OSM_LOG_EXIT(sm->p_log); 341} 342 343/********************************************************************** 344 Lock must be held on entry to this function. 345 Return 1 if the caller is expected to send a change_detected event. 346 this can not be done internally as the event needs the lock... 347**********************************************************************/ 348static boolean_t 349__osm_si_rcv_process_existing(IN osm_sm_t * sm, 350 IN osm_node_t * const p_node, 351 IN const osm_madw_t * const p_madw) 352{ 353 osm_switch_t *p_sw = p_node->sw; 354 ib_switch_info_t *p_si; 355 osm_si_context_t *p_si_context; 356 ib_smp_t *p_smp; 357 boolean_t is_change_detected = FALSE; 358 359 OSM_LOG_ENTER(sm->p_log); 360 361 CL_ASSERT(p_madw); 362 363 p_smp = osm_madw_get_smp_ptr(p_madw); 364 p_si = (ib_switch_info_t *) ib_smp_get_payload_ptr(p_smp); 365 p_si_context = osm_madw_get_si_context_ptr(p_madw); 366 367 if (p_si_context->set_method) { 368 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 369 "Received logical SetResp()\n"); 370 371 osm_switch_set_switch_info(p_sw, p_si); 372 } else { 373 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 374 "Received logical GetResp()\n"); 375 376 osm_switch_set_switch_info(p_sw, p_si); 377 378 /* 379 Check the port state change bit. If true, then this switch 380 has seen a port state transition, so continue probing. 381 */ 382 if (p_si_context->light_sweep == TRUE) { 383 /* This is a light sweep */ 384 /* If the mad was returned with an error - 385 signal a change to the state manager. */ 386 if (ib_smp_get_status(p_smp) != 0) { 387 OSM_LOG(sm->p_log, OSM_LOG_VERBOSE, 388 "GetResp() received with error in light sweep. " 389 "Commencing heavy sweep\n"); 390 is_change_detected = TRUE; 391 } else { 392 /* 393 If something changed, then just signal the 394 state manager. Don't attempt to probe 395 further during a light sweep. 396 */ 397 if (ib_switch_info_get_state_change(p_si)) { 398 osm_dump_switch_info(sm->p_log, p_si, 399 OSM_LOG_DEBUG); 400 is_change_detected = TRUE; 401 } 402 } 403 } else { 404 /* 405 This is a heavy sweep. Get information regardless 406 of the state change bit. 407 */ 408 p_sw->discovery_count++; 409 OSM_LOG(sm->p_log, OSM_LOG_VERBOSE, 410 "discovery_count is:%u\n", 411 p_sw->discovery_count); 412 413 /* If this is the first discovery - then get the port_info */ 414 if (p_sw->discovery_count == 1) 415 __osm_si_rcv_get_port_info(sm, p_sw); 416 else 417 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 418 "Not discovering again through switch:0x%" 419 PRIx64 "\n", 420 osm_node_get_node_guid(p_sw->p_node)); 421 } 422 } 423 424 OSM_LOG_EXIT(sm->p_log); 425 return is_change_detected; 426} 427 428/********************************************************************** 429 **********************************************************************/ 430void osm_si_rcv_process(IN void *context, IN void *data) 431{ 432 osm_sm_t *sm = context; 433 osm_madw_t *p_madw = data; 434 ib_switch_info_t *p_si; 435 ib_smp_t *p_smp; 436 osm_node_t *p_node; 437 ib_net64_t node_guid; 438 osm_si_context_t *p_context; 439 440 CL_ASSERT(sm); 441 442 OSM_LOG_ENTER(sm->p_log); 443 444 CL_ASSERT(p_madw); 445 446 p_smp = osm_madw_get_smp_ptr(p_madw); 447 p_si = (ib_switch_info_t *) ib_smp_get_payload_ptr(p_smp); 448 449 /* 450 Acquire the switch object and add the switch info. 451 */ 452 p_context = osm_madw_get_si_context_ptr(p_madw); 453 454 node_guid = p_context->node_guid; 455 456 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 457 "Switch GUID 0x%016" PRIx64 ", TID 0x%" PRIx64 "\n", 458 cl_ntoh64(node_guid), cl_ntoh64(p_smp->trans_id)); 459 460 CL_PLOCK_EXCL_ACQUIRE(sm->p_lock); 461 462 p_node = osm_get_node_by_guid(sm->p_subn, node_guid); 463 if (!p_node) 464 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 3606: " 465 "SwitchInfo received for nonexistent node " 466 "with GUID 0x%" PRIx64 "\n", cl_ntoh64(node_guid)); 467 else { 468 469 /* 470 Hack for bad value in Mellanox switch 471 */ 472 if (cl_ntoh16(p_si->lin_top) > IB_LID_UCAST_END_HO) { 473 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 3610: " 474 "\n\t\t\t\tBad LinearFDBTop value = 0x%X " 475 "on switch 0x%" PRIx64 476 "\n\t\t\t\tForcing internal correction to 0x%X\n", 477 cl_ntoh16(p_si->lin_top), 478 cl_ntoh64(osm_node_get_node_guid(p_node)), 0); 479 480 p_si->lin_top = 0; 481 } 482 483 /* 484 Acquire the switch object for this switch. 485 */ 486 if (!p_node->sw) { 487 __osm_si_rcv_process_new(sm, p_node, p_madw); 488 /* 489 A new switch was found during the sweep so we need 490 to ignore the current LFT settings. 491 */ 492 sm->p_subn->ignore_existing_lfts = TRUE; 493 } else { 494 /* we might get back a request for signaling change was detected */ 495 if (__osm_si_rcv_process_existing(sm, p_node, p_madw)) { 496 CL_PLOCK_RELEASE(sm->p_lock); 497 sm->p_subn->force_heavy_sweep = TRUE; 498 goto Exit; 499 } 500 } 501 } 502 503 CL_PLOCK_RELEASE(sm->p_lock); 504Exit: 505 OSM_LOG_EXIT(sm->p_log); 506} 507