1/*
2 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2012 Mellanox Technologies LTD. All rights reserved.
4 * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5 * Copyright (c) 2009 HNR Consulting. All rights reserved.
6 * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
7 *
8 * This software is available to you under a choice of one of two
9 * licenses.  You may choose to be licensed under the terms of the GNU
10 * General Public License (GPL) Version 2, available from the file
11 * COPYING in the main directory of this source tree, or the
12 * OpenIB.org BSD license below:
13 *
14 *     Redistribution and use in source and binary forms, with or
15 *     without modification, are permitted provided that the following
16 *     conditions are met:
17 *
18 *      - Redistributions of source code must retain the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer.
21 *
22 *      - Redistributions in binary form must reproduce the above
23 *        copyright notice, this list of conditions and the following
24 *        disclaimer in the documentation and/or other materials
25 *        provided with the distribution.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
31 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
32 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
33 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
34 * SOFTWARE.
35 *
36 */
37
38/*
39 * Abstract:
40 *    Implementation of osm_pi_rcv_t.
41 * This object represents the PortInfo Receiver object.
42 * This object is part of the opensm family of objects.
43 */
44
45#if HAVE_CONFIG_H
46#  include <config.h>
47#endif				/* HAVE_CONFIG_H */
48
49#include <string.h>
50#include <stdlib.h>
51#include <iba/ib_types.h>
52#include <complib/cl_qmap.h>
53#include <complib/cl_passivelock.h>
54#include <complib/cl_debug.h>
55#include <opensm/osm_file_ids.h>
56#define FILE_ID OSM_FILE_PORT_INFO_RCV_C
57#include <vendor/osm_vendor_api.h>
58#include <opensm/osm_madw.h>
59#include <opensm/osm_log.h>
60#include <opensm/osm_node.h>
61#include <opensm/osm_subnet.h>
62#include <opensm/osm_mad_pool.h>
63#include <opensm/osm_msgdef.h>
64#include <opensm/osm_helper.h>
65#include <opensm/osm_pkey.h>
66#include <opensm/osm_remote_sm.h>
67#include <opensm/osm_opensm.h>
68#include <opensm/osm_ucast_mgr.h>
69
70static void pi_rcv_check_and_fix_lid(osm_log_t * log, ib_port_info_t * pi,
71				     osm_physp_t * p)
72{
73	if (PF(cl_ntoh16(pi->base_lid) > IB_LID_UCAST_END_HO)) {
74		OSM_LOG(log, OSM_LOG_ERROR, "ERR 0F04: "
75			"Got invalid base LID %u from the network. "
76			"Corrected to %u\n", cl_ntoh16(pi->base_lid),
77			cl_ntoh16(p->port_info.base_lid));
78		pi->base_lid = p->port_info.base_lid;
79	}
80}
81
82static void pi_rcv_process_endport(IN osm_sm_t * sm, IN osm_physp_t * p_physp,
83				   IN const ib_port_info_t * p_pi)
84{
85	osm_madw_context_t context;
86	ib_api_status_t status;
87	ib_net64_t port_guid;
88	int extended;
89	uint8_t rate, mtu, mpb;
90	unsigned data_vls;
91	cl_qmap_t *p_sm_tbl;
92	osm_remote_sm_t *p_sm;
93
94	OSM_LOG_ENTER(sm->p_log);
95
96	port_guid = osm_physp_get_port_guid(p_physp);
97
98	/* HACK extended port 0 should be handled too! */
99	if (osm_physp_get_port_num(p_physp) != 0 &&
100	    ib_port_info_get_port_state(p_pi) != IB_LINK_DOWN) {
101		/* track the minimal endport MTU, rate, and operational VLs */
102		mtu = ib_port_info_get_mtu_cap(p_pi);
103		if (mtu < sm->p_subn->min_ca_mtu) {
104			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
105				"Setting endport minimal MTU to:%u defined by port:0x%"
106				PRIx64 "\n", mtu, cl_ntoh64(port_guid));
107			sm->p_subn->min_ca_mtu = mtu;
108		}
109
110		extended = p_pi->capability_mask & IB_PORT_CAP_HAS_EXT_SPEEDS;
111		rate = ib_port_info_compute_rate(p_pi, extended);
112		if (ib_path_compare_rates(rate, sm->p_subn->min_ca_rate) < 0) {
113			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
114				"Setting endport minimal rate to:%u defined by port:0x%"
115				PRIx64 "\n", rate, cl_ntoh64(port_guid));
116			sm->p_subn->min_ca_rate = rate;
117		}
118
119		data_vls = 1U << (ib_port_info_get_vl_cap(p_pi) - 1);
120		if (data_vls > 1U << (sm->p_subn->opt.max_op_vls - 1))
121			data_vls = 1U << (sm->p_subn->opt.max_op_vls - 1);
122		if (data_vls >= IB_MAX_NUM_VLS)
123			data_vls = IB_MAX_NUM_VLS - 1;
124		if ((uint8_t)data_vls < sm->p_subn->min_data_vls) {
125			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
126				"Setting endport minimal data VLs to:%u defined by port:0x%"
127				PRIx64 "\n", data_vls, cl_ntoh64(port_guid));
128			sm->p_subn->min_data_vls = data_vls;
129		}
130	}
131
132	/* Check M_Key vs M_Key protect, can we control the port ? */
133	mpb = ib_port_info_get_mpb(p_pi);
134	if (mpb > 0 && p_pi->m_key == 0) {
135		OSM_LOG(sm->p_log, OSM_LOG_INFO,
136			"Port 0x%" PRIx64 " has unknown M_Key, protection level %u\n",
137			cl_ntoh64(port_guid), mpb);
138	}
139
140	if (port_guid != sm->p_subn->sm_port_guid) {
141		p_sm_tbl = &sm->p_subn->sm_guid_tbl;
142		if (p_pi->capability_mask & IB_PORT_CAP_IS_SM) {
143			/*
144			 * Before querying the SM - we want to make sure we
145			 * clean its state, so if the querying fails we
146			 * recognize that this SM is not active.
147			 */
148			p_sm =
149			    (osm_remote_sm_t *) cl_qmap_get(p_sm_tbl,
150							    port_guid);
151			if (p_sm != (osm_remote_sm_t *) cl_qmap_end(p_sm_tbl))
152				/* clean it up */
153				p_sm->smi.pri_state =
154				    0xF0 & p_sm->smi.pri_state;
155			if (sm->p_subn->opt.ignore_other_sm)
156				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
157					"Ignoring SM on port 0x%" PRIx64 "\n",
158					cl_ntoh64(port_guid));
159			else {
160				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
161					"Detected another SM. Requesting SMInfo "
162					"from port 0x%" PRIx64 "\n",
163					cl_ntoh64(port_guid));
164
165				/*
166				   This port indicates it's an SM and
167				   it's not our own port.
168				   Acquire the SMInfo Attribute.
169				 */
170				memset(&context, 0, sizeof(context));
171				context.smi_context.set_method = FALSE;
172				context.smi_context.port_guid = port_guid;
173				status = osm_req_get(sm,
174						     osm_physp_get_dr_path_ptr
175						     (p_physp),
176						     IB_MAD_ATTR_SM_INFO, 0,
177						     FALSE,
178						     ib_port_info_get_m_key(&p_physp->port_info),
179						     CL_DISP_MSGID_NONE,
180						     &context);
181
182				if (status != IB_SUCCESS)
183					OSM_LOG(sm->p_log, OSM_LOG_ERROR,
184						"ERR 0F05: "
185						"Failure requesting SMInfo (%s) "
186						"from port 0x%" PRIx64 "\n",
187						ib_get_err_str(status),
188						cl_ntoh64(port_guid));
189			}
190		} else {
191			p_sm =
192			    (osm_remote_sm_t *) cl_qmap_remove(p_sm_tbl,
193							       port_guid);
194			if (p_sm != (osm_remote_sm_t *) cl_qmap_end(p_sm_tbl))
195				free(p_sm);
196		}
197	}
198
199	OSM_LOG_EXIT(sm->p_log);
200}
201
202/**********************************************************************
203 The plock must be held before calling this function.
204**********************************************************************/
205static void pi_rcv_process_switch_port0(IN osm_sm_t * sm,
206					IN osm_node_t * p_node,
207					IN osm_physp_t * p_physp,
208					IN ib_port_info_t * p_pi)
209{
210	ib_api_status_t status;
211	osm_madw_context_t context;
212	uint8_t port, num_ports;
213
214	OSM_LOG_ENTER(sm->p_log);
215
216	if (p_physp->need_update)
217		sm->p_subn->ignore_existing_lfts = TRUE;
218
219	pi_rcv_check_and_fix_lid(sm->p_log, p_pi, p_physp);
220
221	/* Update the PortInfo attribute */
222	osm_physp_set_port_info(p_physp, p_pi, sm);
223
224	/* Determine if base switch port 0 */
225	if (p_node->sw &&
226	    !ib_switch_info_is_enhanced_port0(&p_node->sw->switch_info))
227		/* PortState is not used on BSP0 but just in case it is DOWN */
228		p_physp->port_info = *p_pi;
229
230	/* Now, query PortInfo for the switch external ports */
231	num_ports = osm_node_get_num_physp(p_node);
232
233	context.pi_context.node_guid = osm_node_get_node_guid(p_node);
234	context.pi_context.port_guid = osm_physp_get_port_guid(p_physp);
235	context.pi_context.set_method = FALSE;
236	context.pi_context.light_sweep = FALSE;
237	context.pi_context.active_transition = FALSE;
238	context.pi_context.client_rereg = FALSE;
239
240	for (port = 1; port < num_ports; port++) {
241		status = osm_req_get(sm, osm_physp_get_dr_path_ptr(p_physp),
242				     IB_MAD_ATTR_PORT_INFO, cl_hton32(port),
243				     FALSE,
244				     ib_port_info_get_m_key(&p_physp->port_info),
245				     CL_DISP_MSGID_NONE, &context);
246		if (status != IB_SUCCESS)
247			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F16: "
248				"Failure initiating PortInfo request (%s)\n",
249				ib_get_err_str(status));
250	}
251
252	pi_rcv_process_endport(sm, p_physp, p_pi);
253	OSM_LOG_EXIT(sm->p_log);
254}
255
256/**********************************************************************
257 The plock must be held before calling this function.
258**********************************************************************/
259static void pi_rcv_process_switch_ext_port(IN osm_sm_t * sm,
260					   IN osm_node_t * p_node,
261					   IN osm_physp_t * p_physp,
262					   IN ib_port_info_t * p_pi)
263{
264	ib_api_status_t status = IB_SUCCESS;
265	osm_madw_context_t context;
266	osm_physp_t *p_remote_physp, *physp0;
267	osm_node_t *p_remote_node;
268	ib_net64_t m_key;
269	unsigned data_vls;
270	uint8_t port_num;
271	uint8_t remote_port_num;
272	osm_dr_path_t path;
273	int mlnx_epi_supported = 0;
274
275	OSM_LOG_ENTER(sm->p_log);
276
277	/*
278	   Check the state of the physical port.
279	   If there appears to be something on the other end of the wire,
280	   then ask for NodeInfo.  Ignore the switch management port.
281	 */
282	port_num = osm_physp_get_port_num(p_physp);
283
284	if (sm->p_subn->opt.fdr10)
285		mlnx_epi_supported = is_mlnx_ext_port_info_supported(
286						ib_node_info_get_vendor_id(&p_node->node_info),
287						p_node->node_info.device_id);
288
289	/* if in_sweep_hop_0 is TRUE, then this means the SM is on the switch,
290	   and we got switchInfo of our local switch. Do not continue
291	   probing through the switch. */
292	switch (ib_port_info_get_port_state(p_pi)) {
293	case IB_LINK_DOWN:
294		p_remote_physp = osm_physp_get_remote(p_physp);
295		if (p_remote_physp) {
296			p_remote_node =
297			    osm_physp_get_node_ptr(p_remote_physp);
298			remote_port_num =
299			    osm_physp_get_port_num(p_remote_physp);
300
301			OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
302				"Unlinking local node 0x%" PRIx64
303				", port %u"
304				"\n\t\t\t\tand remote node 0x%" PRIx64
305				", port %u\n",
306				cl_ntoh64(osm_node_get_node_guid
307					  (p_node)), port_num,
308				cl_ntoh64(osm_node_get_node_guid
309					  (p_remote_node)),
310				remote_port_num);
311
312			if (sm->ucast_mgr.cache_valid)
313				osm_ucast_cache_add_link(&sm->ucast_mgr,
314							 p_physp,
315							 p_remote_physp);
316
317			osm_node_unlink(p_node, (uint8_t) port_num,
318					p_remote_node,
319					(uint8_t) remote_port_num);
320
321		}
322		break;
323
324	case IB_LINK_INIT:
325	case IB_LINK_ARMED:
326	case IB_LINK_ACTIVE:
327		physp0 = osm_node_get_physp_ptr(p_node, 0);
328		if (mlnx_epi_supported) {
329			m_key = ib_port_info_get_m_key(&physp0->port_info);
330
331			context.pi_context.node_guid = osm_node_get_node_guid(p_node);
332			context.pi_context.port_guid = osm_physp_get_port_guid(p_physp);
333			context.pi_context.set_method = FALSE;
334			context.pi_context.light_sweep = FALSE;
335			context.pi_context.active_transition = FALSE;
336			context.pi_context.client_rereg = FALSE;
337			status = osm_req_get(sm,
338					     osm_physp_get_dr_path_ptr(p_physp),
339					     IB_MAD_ATTR_MLNX_EXTENDED_PORT_INFO,
340					     cl_hton32(port_num), FALSE, m_key,
341					     CL_DISP_MSGID_NONE, &context);
342			if (status != IB_SUCCESS)
343				OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F11: "
344					"Failure initiating MLNX ExtPortInfo request (%s)\n",
345					ib_get_err_str(status));
346		}
347		if (sm->p_subn->in_sweep_hop_0 == FALSE) {
348			/*
349			   To avoid looping forever, only probe the port if it
350			   is NOT the port that responded to the SMP.
351
352			   Request node info from the other end of this link:
353			   1) Copy the current path from the parent node.
354			   2) Extend the path to the next hop thru this port.
355			   3) Request node info with the new path
356
357			 */
358			if (p_pi->local_port_num !=
359			    osm_physp_get_port_num(p_physp)) {
360				path = *osm_physp_get_dr_path_ptr(p_physp);
361
362				if (osm_dr_path_extend(&path,
363						       osm_physp_get_port_num
364						       (p_physp))) {
365					OSM_LOG(sm->p_log, OSM_LOG_ERROR,
366						"ERR 0F08: "
367						"DR path with hop count %d couldn't be extended\n",
368						path.hop_count);
369					break;
370				}
371
372				memset(&context, 0, sizeof(context));
373				context.ni_context.node_guid =
374				    osm_node_get_node_guid(p_node);
375				context.ni_context.port_num =
376				    osm_physp_get_port_num(p_physp);
377
378				status = osm_req_get(sm, &path,
379						     IB_MAD_ATTR_NODE_INFO, 0,
380						     TRUE, 0,
381						     CL_DISP_MSGID_NONE,
382						     &context);
383
384				if (status != IB_SUCCESS)
385					OSM_LOG(sm->p_log, OSM_LOG_ERROR,
386						"ERR 0F02: "
387						"Failure initiating NodeInfo request (%s)\n",
388						ib_get_err_str(status));
389			} else
390				OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
391					"Skipping SMP responder port %u\n",
392					p_pi->local_port_num);
393		}
394		break;
395
396	default:
397		OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F03: "
398			"Unknown link state = %u, port = %u\n",
399			ib_port_info_get_port_state(p_pi),
400			p_pi->local_port_num);
401		break;
402	}
403
404	if (ib_port_info_get_port_state(p_pi) > IB_LINK_INIT && p_node->sw &&
405	    !ib_switch_info_get_state_change(&p_node->sw->switch_info) &&
406	    p_node->sw->need_update == 1)
407		p_node->sw->need_update = 0;
408
409	if (p_physp->need_update)
410		sm->p_subn->ignore_existing_lfts = TRUE;
411
412	/*
413	   Update the PortInfo attribute.
414	 */
415	osm_physp_set_port_info(p_physp, p_pi, sm);
416
417	if (ib_port_info_get_port_state(p_pi) == IB_LINK_DOWN)
418		goto Exit;
419
420	p_remote_physp = osm_physp_get_remote(p_physp);
421	if (p_remote_physp) {
422		p_remote_node = osm_physp_get_node_ptr(p_remote_physp);
423		if (p_remote_node->sw) {
424			data_vls = 1U << (ib_port_info_get_vl_cap(p_pi) - 1);
425			if (data_vls > 1U << (sm->p_subn->opt.max_op_vls - 1))
426				data_vls = 1U << (sm->p_subn->opt.max_op_vls - 1);
427			if (data_vls >= IB_MAX_NUM_VLS)
428				data_vls = IB_MAX_NUM_VLS - 1;
429			if ((uint8_t)data_vls < sm->p_subn->min_sw_data_vls) {
430				OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
431					"Setting switch port minimal data VLs "
432					"to:%u defined by node:0x%"
433					PRIx64 ", port:%u\n", data_vls,
434					cl_ntoh64(osm_node_get_node_guid(p_node)),
435					port_num);
436				sm->p_subn->min_sw_data_vls = data_vls;
437			}
438		}
439	}
440
441Exit:
442	OSM_LOG_EXIT(sm->p_log);
443}
444
445static void pi_rcv_process_ca_or_router_port(IN osm_sm_t * sm,
446					     IN osm_node_t * p_node,
447					     IN osm_physp_t * p_physp,
448					     IN ib_port_info_t * p_pi)
449{
450	OSM_LOG_ENTER(sm->p_log);
451
452	UNUSED_PARAM(p_node);
453
454	pi_rcv_check_and_fix_lid(sm->p_log, p_pi, p_physp);
455
456	osm_physp_set_port_info(p_physp, p_pi, sm);
457
458	pi_rcv_process_endport(sm, p_physp, p_pi);
459
460	OSM_LOG_EXIT(sm->p_log);
461}
462
463#define IBM_VENDOR_ID  (0x5076)
464static void get_pkey_table(IN osm_log_t * p_log, IN osm_sm_t * sm,
465			   IN osm_node_t * p_node, IN osm_physp_t * p_physp)
466{
467
468	osm_madw_context_t context;
469	ib_api_status_t status;
470	osm_dr_path_t path;
471	osm_physp_t *physp0;
472	ib_net64_t m_key;
473	uint8_t port_num;
474	uint16_t block_num, max_blocks;
475	uint32_t attr_mod_ho;
476
477	OSM_LOG_ENTER(p_log);
478
479	path = *osm_physp_get_dr_path_ptr(p_physp);
480
481	context.pkey_context.node_guid = osm_node_get_node_guid(p_node);
482	context.pkey_context.port_guid = osm_physp_get_port_guid(p_physp);
483	context.pkey_context.set_method = FALSE;
484
485	port_num = p_physp->port_num;
486
487	if (!p_node->sw || port_num == 0)
488		/* The maximum blocks is defined by the node info partition cap
489		   for CA, router, and switch management ports. */
490		max_blocks =
491		    (cl_ntoh16(p_node->node_info.partition_cap) +
492		     IB_NUM_PKEY_ELEMENTS_IN_BLOCK - 1)
493		    / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
494	else {
495		/* This is a switch, and not a management port. The maximum blocks
496		   is defined in the switch info partition enforcement cap. */
497
498		/* Check for IBM eHCA firmware defect in reporting partition enforcement cap */
499		if (cl_ntoh32(ib_node_info_get_vendor_id(&p_node->node_info)) ==
500		    IBM_VENDOR_ID)
501			p_node->sw->switch_info.enforce_cap = 0;
502
503		/* Bail out if this is a switch with no partition enforcement capability */
504		if (cl_ntoh16(p_node->sw->switch_info.enforce_cap) == 0)
505			goto Exit;
506
507		max_blocks = (cl_ntoh16(p_node->sw->switch_info.enforce_cap) +
508			      IB_NUM_PKEY_ELEMENTS_IN_BLOCK -
509			      1) / IB_NUM_PKEY_ELEMENTS_IN_BLOCK;
510	}
511
512	p_physp->pkeys.rcv_blocks_cnt = max_blocks;
513	for (block_num = 0; block_num < max_blocks; block_num++) {
514		if (osm_node_get_type(p_node) != IB_NODE_TYPE_SWITCH ||
515		    osm_physp_get_port_num(p_physp) == 0) {
516			attr_mod_ho = block_num;
517			m_key = ib_port_info_get_m_key(&p_physp->port_info);
518		} else {
519			attr_mod_ho = block_num | (port_num << 16);
520			physp0 = osm_node_get_physp_ptr(p_node, 0);
521			m_key = ib_port_info_get_m_key(&physp0->port_info);
522		}
523		status = osm_req_get(sm, &path, IB_MAD_ATTR_P_KEY_TABLE,
524				     cl_hton32(attr_mod_ho), FALSE,
525				     m_key, CL_DISP_MSGID_NONE, &context);
526
527		if (status != IB_SUCCESS) {
528			OSM_LOG(p_log, OSM_LOG_ERROR, "ERR 0F12: "
529				"Failure initiating PKeyTable request (%s)\n",
530				ib_get_err_str(status));
531			goto Exit;
532		}
533	}
534
535Exit:
536	OSM_LOG_EXIT(p_log);
537}
538
539static void pi_rcv_get_pkey_slvl_vla_tables(IN osm_sm_t * sm,
540					    IN osm_node_t * p_node,
541					    IN osm_physp_t * p_physp)
542{
543	OSM_LOG_ENTER(sm->p_log);
544
545	get_pkey_table(sm->p_log, sm, p_node, p_physp);
546
547	OSM_LOG_EXIT(sm->p_log);
548}
549
550static int osm_pi_rcv_update_self(IN osm_sm_t *sm, IN osm_physp_t *p_physp,
551				  IN ib_port_info_t *p_pi)
552{
553	if (ib_port_info_get_port_state(p_pi) == IB_LINK_DOWN)
554		return 0;
555
556	if (sm->p_subn->need_update || p_physp->need_update > 1 ||
557	    ib_port_info_get_port_state(p_pi) == IB_LINK_INIT)
558		return 1;
559
560	return 0;
561}
562
563static void pi_rcv_process_set(IN osm_sm_t * sm, IN osm_node_t * p_node,
564			       IN uint8_t port_num, IN osm_madw_t * p_madw)
565{
566	osm_physp_t *p_physp;
567	ib_net64_t port_guid;
568	ib_smp_t *p_smp;
569	ib_port_info_t *p_pi;
570	osm_pi_context_t *p_context;
571	osm_log_level_t level;
572
573	OSM_LOG_ENTER(sm->p_log);
574
575	p_context = osm_madw_get_pi_context_ptr(p_madw);
576
577	CL_ASSERT(p_node);
578
579	p_physp = osm_node_get_physp_ptr(p_node, port_num);
580	CL_ASSERT(p_physp);
581
582	port_guid = osm_physp_get_port_guid(p_physp);
583
584	p_smp = osm_madw_get_smp_ptr(p_madw);
585	p_pi = ib_smp_get_payload_ptr(p_smp);
586
587	/* check for error */
588	if (cl_ntoh16(p_smp->status) & 0x7fff) {
589		/* If port already ACTIVE, don't treat status 7 as error */
590		if (p_context->active_transition &&
591		    (cl_ntoh16(p_smp->status) & 0x7fff) == 0x1c) {
592			level = OSM_LOG_INFO;
593			OSM_LOG(sm->p_log, OSM_LOG_INFO,
594				"Received error status 0x%x for SetResp() during ACTIVE transition\n",
595				cl_ntoh16(p_smp->status) & 0x7fff);
596			/* Should there be a subsequent Get to validate that port is ACTIVE ? */
597		} else {
598			level = OSM_LOG_ERROR;
599			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F10: "
600				"Received error status for SetResp()\n");
601		}
602		osm_dump_port_info_v2(sm->p_log, osm_node_get_node_guid(p_node),
603				      port_guid, port_num, p_pi, FILE_ID, level);
604	} else
605		osm_physp_set_port_info(p_physp, p_pi, sm);
606
607	OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
608		"Received logical SetResp() for GUID 0x%" PRIx64
609		", port num %u"
610		"\n\t\t\t\tfor parent node GUID 0x%" PRIx64
611		" TID 0x%" PRIx64 "\n",
612		cl_ntoh64(port_guid), port_num,
613		cl_ntoh64(osm_node_get_node_guid(p_node)),
614		cl_ntoh64(p_smp->trans_id));
615
616
617	OSM_LOG_EXIT(sm->p_log);
618}
619
620static int osm_pi_rcv_update_neighbor(IN osm_physp_t *p_physp)
621{
622	osm_physp_t *p_rem_physp = p_physp->p_remote_physp;
623	osm_node_t *p_node;
624
625	/*
626	 * Our own port - this is the only case where CA port
627	 * is discovered before its' neighbor port
628	 */
629	if (!p_rem_physp)
630		return p_physp->need_update;
631
632	p_node = osm_physp_get_node_ptr(p_rem_physp);
633	CL_ASSERT(p_node);
634
635	/* CA/RTR to CA/RTR connection */
636	if (!p_node->sw)
637		return p_physp->need_update;
638
639	return (ib_switch_info_get_state_change(&p_node->sw->switch_info) ? 1 : p_physp->need_update);
640}
641
642void osm_pi_rcv_process(IN void *context, IN void *data)
643{
644	osm_sm_t *sm = context;
645	osm_madw_t *p_madw = data;
646	ib_port_info_t *p_pi;
647	ib_smp_t *p_smp;
648	osm_port_t *p_port;
649	osm_physp_t *p_physp;
650	osm_dr_path_t *p_dr_path;
651	osm_node_t *p_node;
652	osm_pi_context_t *p_context;
653	ib_net64_t port_guid, node_guid;
654	uint8_t port_num;
655
656	CL_ASSERT(sm);
657
658	OSM_LOG_ENTER(sm->p_log);
659
660	CL_ASSERT(p_madw);
661
662	p_smp = osm_madw_get_smp_ptr(p_madw);
663	p_context = osm_madw_get_pi_context_ptr(p_madw);
664	p_pi = ib_smp_get_payload_ptr(p_smp);
665
666	CL_ASSERT(p_smp->attr_id == IB_MAD_ATTR_PORT_INFO);
667
668	/*
669	 * Attribute modifier has already been validated upon MAD receive,
670	 * which means that port_num has to be valid - it originated from
671	 * the request attribute modifier.
672	 */
673	port_num = (uint8_t) cl_ntoh32(p_smp->attr_mod);
674
675	port_guid = p_context->port_guid;
676	node_guid = p_context->node_guid;
677
678	osm_dump_port_info_v2(sm->p_log, node_guid, port_guid, port_num, p_pi,
679			      FILE_ID, OSM_LOG_DEBUG);
680
681	/* On receipt of client reregister, clear the reregister bit so
682	   reregistering won't be sent again and again */
683	if (p_context->set_method &&
684	    (ib_port_info_get_client_rereg(p_pi) || p_context->client_rereg)) {
685		OSM_LOG(sm->p_log, OSM_LOG_DEBUG,
686			"Client reregister received on response\n");
687		ib_port_info_set_client_rereg(p_pi, 0);
688		p_context->client_rereg = FALSE;
689	}
690
691	/*
692	   we might get a response during a light sweep looking for a change in
693	   the status of a remote port that did not respond in earlier sweeps.
694	   So if the context of the Get was light_sweep - we do not need to
695	   do anything with the response - just flag that we need a heavy sweep
696	 */
697	if (p_context->light_sweep == TRUE) {
698		OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
699			"Got light sweep response from remote port of parent node "
700			"GUID 0x%" PRIx64 " port 0x%016" PRIx64
701			", Commencing heavy sweep\n",
702			cl_ntoh64(node_guid), cl_ntoh64(port_guid));
703		sm->p_subn->force_heavy_sweep = TRUE;
704		sm->p_subn->ignore_existing_lfts = TRUE;
705		goto Exit;
706	}
707
708	CL_PLOCK_EXCL_ACQUIRE(sm->p_lock);
709	p_port = osm_get_port_by_guid(sm->p_subn, port_guid);
710	if (PF(!p_port)) {
711		CL_PLOCK_RELEASE(sm->p_lock);
712		OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F06: "
713			"No port object for port with GUID 0x%" PRIx64
714			"\n\t\t\t\tfor parent node GUID 0x%" PRIx64
715			", TID 0x%" PRIx64 "\n",
716			cl_ntoh64(port_guid),
717			cl_ntoh64(node_guid), cl_ntoh64(p_smp->trans_id));
718		goto Exit;
719	}
720
721	p_node = p_port->p_node;
722	CL_ASSERT(p_node);
723
724	if (PF(p_pi->local_port_num > p_node->node_info.num_ports)) {
725		CL_PLOCK_RELEASE(sm->p_lock);
726		OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F15: "
727			"Received PortInfo for port GUID 0x%" PRIx64 " is "
728			"non-compliant and is being ignored since the "
729			"local port num %u > num ports %u\n",
730			cl_ntoh64(port_guid), p_pi->local_port_num,
731			p_node->node_info.num_ports);
732		goto Exit;
733	}
734
735	/*
736	   If we were setting the PortInfo, then receiving
737	   this attribute was not part of sweeping the subnet.
738	   In this case, just update the PortInfo attribute.
739
740	   In an unfortunate blunder, the IB spec defines the
741	   return method for Set() as a GetResp().  Thus, we can't
742	   use the method (what would have been SetResp()) to determine
743	   our course of action.  So, we have to carry this extra
744	   boolean around to determine if we were doing Get() or Set().
745	 */
746	if (p_context->set_method)
747		pi_rcv_process_set(sm, p_node, port_num, p_madw);
748	else {
749
750		/*
751		   This PortInfo arrived because we did a Get() method,
752		   most likely due to a subnet sweep in progress.
753		 */
754		OSM_LOG(sm->p_log, OSM_LOG_VERBOSE,
755			"Discovered port num %u with GUID 0x%" PRIx64
756			" for parent node GUID 0x%" PRIx64
757			", TID 0x%" PRIx64 "\n",
758			port_num, cl_ntoh64(port_guid),
759			cl_ntoh64(node_guid), cl_ntoh64(p_smp->trans_id));
760
761		p_physp = osm_node_get_physp_ptr(p_node, port_num);
762
763		CL_ASSERT(p_physp);
764
765		/* Update the directed route path to this port
766		   in case the old path is no longer usable. */
767		p_dr_path = osm_physp_get_dr_path_ptr(p_physp);
768		osm_dr_path_init(p_dr_path, p_smp->hop_count,
769				 p_smp->initial_path);
770
771		p_physp->need_update = osm_pi_rcv_update_self(sm, p_physp, p_pi);
772
773		switch (osm_node_get_type(p_node)) {
774		case IB_NODE_TYPE_CA:
775		case IB_NODE_TYPE_ROUTER:
776			if (!p_node->physp_discovered[port_num]) {
777				p_port->discovery_count++;
778				p_node->physp_discovered[port_num] = 1;
779			}
780			p_physp->need_update = osm_pi_rcv_update_neighbor(p_physp);
781			pi_rcv_process_ca_or_router_port(sm, p_node, p_physp,
782							 p_pi);
783			break;
784		case IB_NODE_TYPE_SWITCH:
785			if (!p_node->physp_discovered[port_num]) {
786				p_port->discovery_count++;
787				p_node->physp_discovered[port_num] = 1;
788			}
789			if (port_num == 0)
790				pi_rcv_process_switch_port0(sm, p_node,
791							    p_physp, p_pi);
792			else
793				pi_rcv_process_switch_ext_port(sm, p_node,
794							       p_physp, p_pi);
795			break;
796		default:
797			OSM_LOG(sm->p_log, OSM_LOG_ERROR, "ERR 0F07: "
798				"Unknown node type %u with GUID 0x%" PRIx64
799				"\n", osm_node_get_type(p_node),
800				cl_ntoh64(node_guid));
801			break;
802		}
803
804		/*
805		   Get the tables on the physp.
806		 */
807		if (p_physp->need_update || (p_node->sw &&
808					     p_node->sw->need_update))
809			pi_rcv_get_pkey_slvl_vla_tables(sm, p_node, p_physp);
810
811	}
812
813	CL_PLOCK_RELEASE(sm->p_lock);
814
815Exit:
816	/*
817	   Release the lock before jumping here!!
818	 */
819	OSM_LOG_EXIT(sm->p_log);
820}
821