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_link_mgr_t. 39 * This file implements the Link Manager object. 40 */ 41 42#if HAVE_CONFIG_H 43# include <config.h> 44#endif /* HAVE_CONFIG_H */ 45 46#include <string.h> 47#include <iba/ib_types.h> 48#include <complib/cl_debug.h> 49#include <opensm/osm_sm.h> 50#include <opensm/osm_node.h> 51#include <opensm/osm_switch.h> 52#include <opensm/osm_helper.h> 53#include <opensm/osm_msgdef.h> 54 55/********************************************************************** 56 **********************************************************************/ 57static boolean_t 58__osm_link_mgr_set_physp_pi(osm_sm_t * sm, 59 IN osm_physp_t * const p_physp, 60 IN uint8_t const port_state) 61{ 62 uint8_t payload[IB_SMP_DATA_SIZE]; 63 ib_port_info_t *const p_pi = (ib_port_info_t *) payload; 64 const ib_port_info_t *p_old_pi; 65 osm_madw_context_t context; 66 osm_node_t *p_node; 67 ib_api_status_t status; 68 uint8_t port_num; 69 uint8_t mtu; 70 uint8_t op_vls; 71 boolean_t esp0 = FALSE; 72 boolean_t send_set = FALSE; 73 osm_physp_t *p_remote_physp; 74 75 OSM_LOG_ENTER(sm->p_log); 76 77 p_node = osm_physp_get_node_ptr(p_physp); 78 79 port_num = osm_physp_get_port_num(p_physp); 80 81 if (port_num == 0) { 82 /* 83 CAs don't have a port 0, and for switch port 0, 84 we need to check if this is enhanced or base port 0. 85 For base port 0 the following parameters are not valid (p822, table 145). 86 */ 87 if (!p_node->sw) { 88 OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 4201: " 89 "Cannot find switch by guid: 0x%" PRIx64 "\n", 90 cl_ntoh64(p_node->node_info.node_guid)); 91 goto Exit; 92 } 93 94 if (ib_switch_info_is_enhanced_port0(&p_node->sw->switch_info) 95 == FALSE) { 96 /* This means the switch doesn't support enhanced port 0. 97 Can skip it. */ 98 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 99 "Skipping port 0, GUID 0x%016" PRIx64 "\n", 100 cl_ntoh64(osm_physp_get_port_guid(p_physp))); 101 goto Exit; 102 } 103 esp0 = TRUE; 104 } 105 106 /* 107 PAST THIS POINT WE ARE HANDLING EITHER A NON PORT 0 OR ENHANCED PORT 0 108 */ 109 110 p_old_pi = &p_physp->port_info; 111 112 memset(payload, 0, IB_SMP_DATA_SIZE); 113 memcpy(payload, p_old_pi, sizeof(ib_port_info_t)); 114 115 /* 116 Should never write back a value that is bigger then 3 in 117 the PortPhysicalState field - so can not simply copy! 118 119 Actually we want to write there: 120 port physical state - no change, 121 link down default state = polling 122 port state - as requested. 123 */ 124 p_pi->state_info2 = 0x02; 125 ib_port_info_set_port_state(p_pi, port_state); 126 127 if (ib_port_info_get_link_down_def_state(p_pi) != 128 ib_port_info_get_link_down_def_state(p_old_pi)) 129 send_set = TRUE; 130 131 /* didn't get PortInfo before */ 132 if (!ib_port_info_get_port_state(p_old_pi)) 133 send_set = TRUE; 134 135 /* we only change port fields if we do not change state */ 136 if (port_state == IB_LINK_NO_CHANGE) { 137 /* The following fields are relevant only for CA port, router, or Enh. SP0 */ 138 if (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH || 139 port_num == 0) { 140 p_pi->m_key = sm->p_subn->opt.m_key; 141 if (memcmp(&p_pi->m_key, &p_old_pi->m_key, 142 sizeof(p_pi->m_key))) 143 send_set = TRUE; 144 145 p_pi->subnet_prefix = sm->p_subn->opt.subnet_prefix; 146 if (memcmp(&p_pi->subnet_prefix, 147 &p_old_pi->subnet_prefix, 148 sizeof(p_pi->subnet_prefix))) 149 send_set = TRUE; 150 151 p_pi->base_lid = osm_physp_get_base_lid(p_physp); 152 if (memcmp(&p_pi->base_lid, &p_old_pi->base_lid, 153 sizeof(p_pi->base_lid))) 154 send_set = TRUE; 155 156 /* we are initializing the ports with our local sm_base_lid */ 157 p_pi->master_sm_base_lid = sm->p_subn->sm_base_lid; 158 if (memcmp(&p_pi->master_sm_base_lid, 159 &p_old_pi->master_sm_base_lid, 160 sizeof(p_pi->master_sm_base_lid))) 161 send_set = TRUE; 162 163 p_pi->m_key_lease_period = 164 sm->p_subn->opt.m_key_lease_period; 165 if (memcmp(&p_pi->m_key_lease_period, 166 &p_old_pi->m_key_lease_period, 167 sizeof(p_pi->m_key_lease_period))) 168 send_set = TRUE; 169 170 if (esp0 == FALSE) 171 p_pi->mkey_lmc = sm->p_subn->opt.lmc; 172 else { 173 if (sm->p_subn->opt.lmc_esp0) 174 p_pi->mkey_lmc = sm->p_subn->opt.lmc; 175 else 176 p_pi->mkey_lmc = 0; 177 } 178 if (memcmp(&p_pi->mkey_lmc, &p_old_pi->mkey_lmc, 179 sizeof(p_pi->mkey_lmc))) 180 send_set = TRUE; 181 182 ib_port_info_set_timeout(p_pi, 183 sm->p_subn->opt. 184 subnet_timeout); 185 if (ib_port_info_get_timeout(p_pi) != 186 ib_port_info_get_timeout(p_old_pi)) 187 send_set = TRUE; 188 } 189 190 /* 191 Several timeout mechanisms: 192 */ 193 p_remote_physp = osm_physp_get_remote(p_physp); 194 if (port_num != 0 && p_remote_physp) { 195 if (osm_node_get_type(osm_physp_get_node_ptr(p_physp)) 196 == IB_NODE_TYPE_ROUTER) { 197 ib_port_info_set_hoq_lifetime(p_pi, 198 sm->p_subn-> 199 opt. 200 leaf_head_of_queue_lifetime); 201 } else 202 if (osm_node_get_type 203 (osm_physp_get_node_ptr(p_physp)) == 204 IB_NODE_TYPE_SWITCH) { 205 /* Is remote end CA or router (a leaf port) ? */ 206 if (osm_node_get_type 207 (osm_physp_get_node_ptr(p_remote_physp)) != 208 IB_NODE_TYPE_SWITCH) { 209 ib_port_info_set_hoq_lifetime(p_pi, 210 sm-> 211 p_subn-> 212 opt. 213 leaf_head_of_queue_lifetime); 214 ib_port_info_set_vl_stall_count(p_pi, 215 sm-> 216 p_subn-> 217 opt. 218 leaf_vl_stall_count); 219 } else { 220 ib_port_info_set_hoq_lifetime(p_pi, 221 sm-> 222 p_subn-> 223 opt. 224 head_of_queue_lifetime); 225 ib_port_info_set_vl_stall_count(p_pi, 226 sm-> 227 p_subn-> 228 opt. 229 vl_stall_count); 230 } 231 } 232 if (ib_port_info_get_hoq_lifetime(p_pi) != 233 ib_port_info_get_hoq_lifetime(p_old_pi) || 234 ib_port_info_get_vl_stall_count(p_pi) != 235 ib_port_info_get_vl_stall_count(p_old_pi)) 236 send_set = TRUE; 237 } 238 239 ib_port_info_set_phy_and_overrun_err_thd(p_pi, 240 sm->p_subn->opt. 241 local_phy_errors_threshold, 242 sm->p_subn->opt. 243 overrun_errors_threshold); 244 if (memcmp(&p_pi->error_threshold, &p_old_pi->error_threshold, 245 sizeof(p_pi->error_threshold))) 246 send_set = TRUE; 247 248 /* 249 Set the easy common parameters for all port types, 250 then determine the neighbor MTU. 251 */ 252 p_pi->link_width_enabled = p_old_pi->link_width_supported; 253 if (memcmp(&p_pi->link_width_enabled, 254 &p_old_pi->link_width_enabled, 255 sizeof(p_pi->link_width_enabled))) 256 send_set = TRUE; 257 258 if (sm->p_subn->opt.force_link_speed && 259 (sm->p_subn->opt.force_link_speed != 15 || 260 ib_port_info_get_link_speed_enabled(p_pi) != 261 ib_port_info_get_link_speed_sup(p_pi))) { 262 ib_port_info_set_link_speed_enabled(p_pi, 263 sm->p_subn->opt. 264 force_link_speed); 265 if (memcmp(&p_pi->link_speed, &p_old_pi->link_speed, 266 sizeof(p_pi->link_speed))) 267 send_set = TRUE; 268 } 269 270 /* calc new op_vls and mtu */ 271 op_vls = 272 osm_physp_calc_link_op_vls(sm->p_log, sm->p_subn, p_physp); 273 mtu = osm_physp_calc_link_mtu(sm->p_log, p_physp); 274 275 ib_port_info_set_neighbor_mtu(p_pi, mtu); 276 if (ib_port_info_get_neighbor_mtu(p_pi) != 277 ib_port_info_get_neighbor_mtu(p_old_pi)) 278 send_set = TRUE; 279 280 ib_port_info_set_op_vls(p_pi, op_vls); 281 if (ib_port_info_get_op_vls(p_pi) != 282 ib_port_info_get_op_vls(p_old_pi)) 283 send_set = TRUE; 284 285 /* provide the vl_high_limit from the qos mgr */ 286 if (sm->p_subn->opt.qos && 287 p_physp->vl_high_limit != p_old_pi->vl_high_limit) { 288 send_set = TRUE; 289 p_pi->vl_high_limit = p_physp->vl_high_limit; 290 } 291 } 292 293 if (port_state != IB_LINK_NO_CHANGE && 294 port_state != ib_port_info_get_port_state(p_old_pi)) { 295 send_set = TRUE; 296 if (port_state == IB_LINK_ACTIVE) 297 context.pi_context.active_transition = TRUE; 298 else 299 context.pi_context.active_transition = FALSE; 300 } 301 302 context.pi_context.node_guid = osm_node_get_node_guid(p_node); 303 context.pi_context.port_guid = osm_physp_get_port_guid(p_physp); 304 context.pi_context.set_method = TRUE; 305 context.pi_context.light_sweep = FALSE; 306 307 /* We need to send the PortInfoSet request with the new sm_lid 308 in the following cases: 309 1. There is a change in the values (send_set == TRUE) 310 2. This is a switch external port (so it wasn't handled yet by 311 osm_lid_mgr) and first_time_master_sweep flag on the subnet is TRUE, 312 which means the SM just became master, and it then needs to send at 313 PortInfoSet to every port. 314 */ 315 if (osm_node_get_type(p_node) == IB_NODE_TYPE_SWITCH && port_num 316 && sm->p_subn->first_time_master_sweep == TRUE) 317 send_set = TRUE; 318 319 if (send_set) 320 status = osm_req_set(sm, osm_physp_get_dr_path_ptr(p_physp), 321 payload, sizeof(payload), 322 IB_MAD_ATTR_PORT_INFO, 323 cl_hton32(port_num), 324 CL_DISP_MSGID_NONE, &context); 325 326Exit: 327 OSM_LOG_EXIT(sm->p_log); 328 return send_set; 329} 330 331/********************************************************************** 332 **********************************************************************/ 333static osm_signal_t 334__osm_link_mgr_process_node(osm_sm_t * sm, 335 IN osm_node_t * const p_node, 336 IN const uint8_t link_state) 337{ 338 uint32_t i; 339 uint32_t num_physp; 340 osm_physp_t *p_physp; 341 uint8_t current_state; 342 osm_signal_t signal = OSM_SIGNAL_DONE; 343 344 OSM_LOG_ENTER(sm->p_log); 345 346 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 347 "Node 0x%" PRIx64 " going to %s\n", 348 cl_ntoh64(osm_node_get_node_guid(p_node)), 349 ib_get_port_state_str(link_state)); 350 351 /* 352 Set the PortInfo for every Physical Port associated 353 with this Port. Start iterating with port 1, since the linkstate 354 is not applicable to the management port on switches. 355 */ 356 num_physp = osm_node_get_num_physp(p_node); 357 for (i = 0; i < num_physp; i++) { 358 /* 359 Don't bother doing anything if this Physical Port is not valid. 360 or if the state of the port is already better then the 361 specified state. 362 */ 363 p_physp = osm_node_get_physp_ptr(p_node, (uint8_t) i); 364 if (!p_physp) 365 continue; 366 367 current_state = osm_physp_get_port_state(p_physp); 368 if (current_state == IB_LINK_DOWN) 369 continue; 370 371 /* 372 Normally we only send state update if state is lower 373 then required state. However, we need to send update if 374 no state change required. 375 */ 376 if (link_state != IB_LINK_NO_CHANGE && 377 link_state <= current_state) 378 OSM_LOG(sm->p_log, OSM_LOG_DEBUG, 379 "Physical port %u already %s. Skipping\n", 380 p_physp->port_num, 381 ib_get_port_state_str(current_state)); 382 else if (__osm_link_mgr_set_physp_pi(sm, p_physp, link_state)) 383 signal = OSM_SIGNAL_DONE_PENDING; 384 } 385 386 OSM_LOG_EXIT(sm->p_log); 387 return (signal); 388} 389 390/********************************************************************** 391 **********************************************************************/ 392osm_signal_t osm_link_mgr_process(osm_sm_t * sm, IN const uint8_t link_state) 393{ 394 cl_qmap_t *p_node_guid_tbl; 395 osm_node_t *p_node; 396 osm_signal_t signal = OSM_SIGNAL_DONE; 397 398 OSM_LOG_ENTER(sm->p_log); 399 400 p_node_guid_tbl = &sm->p_subn->node_guid_tbl; 401 402 CL_PLOCK_EXCL_ACQUIRE(sm->p_lock); 403 404 for (p_node = (osm_node_t *) cl_qmap_head(p_node_guid_tbl); 405 p_node != (osm_node_t *) cl_qmap_end(p_node_guid_tbl); 406 p_node = (osm_node_t *) cl_qmap_next(&p_node->map_item)) { 407 if (__osm_link_mgr_process_node(sm, p_node, link_state) == 408 OSM_SIGNAL_DONE_PENDING) 409 signal = OSM_SIGNAL_DONE_PENDING; 410 } 411 412 CL_PLOCK_RELEASE(sm->p_lock); 413 414 OSM_LOG_EXIT(sm->p_log); 415 return (signal); 416} 417