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 **********************************************************************/
57 static boolean_t
__osm_link_mgr_set_physp_pi(osm_sm_t * sm,IN osm_physp_t * const p_physp,IN uint8_t const port_state)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
326 Exit:
327 OSM_LOG_EXIT(sm->p_log);
328 return send_set;
329 }
330
331 /**********************************************************************
332 **********************************************************************/
333 static osm_signal_t
__osm_link_mgr_process_node(osm_sm_t * sm,IN osm_node_t * const p_node,IN const uint8_t link_state)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 **********************************************************************/
osm_link_mgr_process(osm_sm_t * sm,IN const uint8_t link_state)392 osm_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