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