xref: /NextBSD/contrib/ofed/management/opensm/opensm/osm_link_mgr.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
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