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 
70 static 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 
82 static 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 **********************************************************************/
205 static 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 **********************************************************************/
259 static 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 
441 Exit:
442 	OSM_LOG_EXIT(sm->p_log);
443 }
444 
445 static 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)
464 static 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 
535 Exit:
536 	OSM_LOG_EXIT(p_log);
537 }
538 
539 static 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 
550 static 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 
563 static 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 
620 static 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 
642 void 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 
815 Exit:
816 	/*
817 	   Release the lock before jumping here!!
818 	 */
819 	OSM_LOG_EXIT(sm->p_log);
820 }
821