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