1321936Shselasky/*
2321936Shselasky * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3321936Shselasky * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved.
4321936Shselasky * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5321936Shselasky * Copyright (c) 2013 Oracle and/or its affiliates. All rights reserved.
6321936Shselasky *
7321936Shselasky * This software is available to you under a choice of one of two
8321936Shselasky * licenses.  You may choose to be licensed under the terms of the GNU
9321936Shselasky * General Public License (GPL) Version 2, available from the file
10321936Shselasky * COPYING in the main directory of this source tree, or the
11321936Shselasky * OpenIB.org BSD license below:
12321936Shselasky *
13321936Shselasky *     Redistribution and use in source and binary forms, with or
14321936Shselasky *     without modification, are permitted provided that the following
15321936Shselasky *     conditions are met:
16321936Shselasky *
17321936Shselasky *      - Redistributions of source code must retain the above
18321936Shselasky *        copyright notice, this list of conditions and the following
19321936Shselasky *        disclaimer.
20321936Shselasky *
21321936Shselasky *      - Redistributions in binary form must reproduce the above
22321936Shselasky *        copyright notice, this list of conditions and the following
23321936Shselasky *        disclaimer in the documentation and/or other materials
24321936Shselasky *        provided with the distribution.
25321936Shselasky *
26321936Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27321936Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28321936Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29321936Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30321936Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31321936Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32321936Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33321936Shselasky * SOFTWARE.
34321936Shselasky *
35321936Shselasky */
36321936Shselasky
37321936Shselasky/*
38321936Shselasky * Abstract:
39321936Shselasky *    Implementation of osm_vlarb_rec_rcv_t.
40321936Shselasky * This object represents the VLArbitrationRecord Receiver object.
41321936Shselasky * This object is part of the opensm family of objects.
42321936Shselasky */
43321936Shselasky
44321936Shselasky#if HAVE_CONFIG_H
45321936Shselasky#  include <config.h>
46321936Shselasky#endif				/* HAVE_CONFIG_H */
47321936Shselasky
48321936Shselasky#include <string.h>
49321936Shselasky#include <iba/ib_types.h>
50321936Shselasky#include <complib/cl_qmap.h>
51321936Shselasky#include <complib/cl_passivelock.h>
52321936Shselasky#include <complib/cl_debug.h>
53321936Shselasky#include <complib/cl_qlist.h>
54321936Shselasky#include <opensm/osm_file_ids.h>
55321936Shselasky#define FILE_ID OSM_FILE_SA_VLARB_RECORD_C
56321936Shselasky#include <vendor/osm_vendor_api.h>
57321936Shselasky#include <opensm/osm_port.h>
58321936Shselasky#include <opensm/osm_node.h>
59321936Shselasky#include <opensm/osm_helper.h>
60321936Shselasky#include <opensm/osm_pkey.h>
61321936Shselasky#include <opensm/osm_sa.h>
62321936Shselasky
63321936Shselasky#define SA_VLA_RESP_SIZE SA_ITEM_RESP_SIZE(vlarb_rec)
64321936Shselasky
65321936Shselaskytypedef struct osm_vl_arb_search_ctxt {
66321936Shselasky	const ib_vl_arb_table_record_t *p_rcvd_rec;
67321936Shselasky	ib_net64_t comp_mask;
68321936Shselasky	uint8_t block_num;
69321936Shselasky	cl_qlist_t *p_list;
70321936Shselasky	osm_sa_t *sa;
71321936Shselasky	const osm_physp_t *p_req_physp;
72321936Shselasky} osm_vl_arb_search_ctxt_t;
73321936Shselasky
74321936Shselaskystatic void sa_vl_arb_create(IN osm_sa_t * sa, IN osm_physp_t * p_physp,
75321936Shselasky			     IN osm_vl_arb_search_ctxt_t * p_ctxt,
76321936Shselasky			     IN uint8_t block)
77321936Shselasky{
78321936Shselasky	osm_sa_item_t *p_rec_item;
79321936Shselasky	uint16_t lid;
80321936Shselasky
81321936Shselasky	OSM_LOG_ENTER(sa->p_log);
82321936Shselasky
83321936Shselasky	p_rec_item = malloc(SA_VLA_RESP_SIZE);
84321936Shselasky	if (p_rec_item == NULL) {
85321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 2A02: "
86321936Shselasky			"rec_item alloc failed\n");
87321936Shselasky		goto Exit;
88321936Shselasky	}
89321936Shselasky
90321936Shselasky	if (p_physp->p_node->node_info.node_type != IB_NODE_TYPE_SWITCH)
91321936Shselasky		lid = p_physp->port_info.base_lid;
92321936Shselasky	else
93321936Shselasky		lid = osm_node_get_base_lid(p_physp->p_node, 0);
94321936Shselasky
95321936Shselasky	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
96321936Shselasky		"New VLArbitration for: port 0x%016" PRIx64
97321936Shselasky		", lid %u, port %u Block:%u\n",
98321936Shselasky		cl_ntoh64(osm_physp_get_port_guid(p_physp)),
99321936Shselasky		cl_ntoh16(lid), osm_physp_get_port_num(p_physp), block);
100321936Shselasky
101321936Shselasky	memset(p_rec_item, 0, SA_VLA_RESP_SIZE);
102321936Shselasky
103321936Shselasky	p_rec_item->resp.vlarb_rec.lid = lid;
104321936Shselasky	p_rec_item->resp.vlarb_rec.port_num = osm_physp_get_port_num(p_physp);
105321936Shselasky	p_rec_item->resp.vlarb_rec.block_num = block;
106321936Shselasky	p_rec_item->resp.vlarb_rec.vl_arb_tbl = *(osm_physp_get_vla_tbl(p_physp, block));
107321936Shselasky
108321936Shselasky	cl_qlist_insert_tail(p_ctxt->p_list, &p_rec_item->list_item);
109321936Shselasky
110321936ShselaskyExit:
111321936Shselasky	OSM_LOG_EXIT(sa->p_log);
112321936Shselasky}
113321936Shselasky
114321936Shselaskystatic void sa_vl_arb_check_physp(IN osm_sa_t * sa, IN osm_physp_t * p_physp,
115321936Shselasky				  osm_vl_arb_search_ctxt_t * p_ctxt)
116321936Shselasky{
117321936Shselasky	ib_net64_t comp_mask = p_ctxt->comp_mask;
118321936Shselasky	uint8_t block;
119321936Shselasky
120321936Shselasky	OSM_LOG_ENTER(sa->p_log);
121321936Shselasky
122321936Shselasky	/* we got here with the phys port - all that's left is to get the right block */
123321936Shselasky	for (block = 1; block <= 4; block++) {
124321936Shselasky		if (!(comp_mask & IB_VLA_COMPMASK_BLOCK)
125321936Shselasky		    || block == p_ctxt->block_num)
126321936Shselasky			sa_vl_arb_create(sa, p_physp, p_ctxt, block);
127321936Shselasky	}
128321936Shselasky
129321936Shselasky	OSM_LOG_EXIT(sa->p_log);
130321936Shselasky}
131321936Shselasky
132321936Shselaskystatic void sa_vl_arb_by_comp_mask(osm_sa_t * sa, IN const osm_port_t * p_port,
133321936Shselasky				   osm_vl_arb_search_ctxt_t * p_ctxt)
134321936Shselasky{
135321936Shselasky	const ib_vl_arb_table_record_t *p_rcvd_rec;
136321936Shselasky	ib_net64_t comp_mask;
137321936Shselasky	osm_physp_t *p_physp;
138321936Shselasky	uint8_t port_num;
139321936Shselasky	uint8_t num_ports;
140321936Shselasky	const osm_physp_t *p_req_physp;
141321936Shselasky
142321936Shselasky	OSM_LOG_ENTER(sa->p_log);
143321936Shselasky
144321936Shselasky	p_rcvd_rec = p_ctxt->p_rcvd_rec;
145321936Shselasky	comp_mask = p_ctxt->comp_mask;
146321936Shselasky	port_num = p_rcvd_rec->port_num;
147321936Shselasky	p_req_physp = p_ctxt->p_req_physp;
148321936Shselasky
149321936Shselasky	/* if this is a switch port we can search all ports
150321936Shselasky	   otherwise we must be looking on port 0 */
151321936Shselasky	if (p_port->p_node->node_info.node_type != IB_NODE_TYPE_SWITCH) {
152321936Shselasky		/* we put it in the comp mask and port num */
153321936Shselasky		port_num = p_port->p_physp->port_num;
154321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
155321936Shselasky			"Using Physical Default Port Number: 0x%X (for End Node)\n",
156321936Shselasky			port_num);
157321936Shselasky		comp_mask |= IB_VLA_COMPMASK_OUT_PORT;
158321936Shselasky	}
159321936Shselasky
160321936Shselasky	if (comp_mask & IB_VLA_COMPMASK_OUT_PORT) {
161321936Shselasky		if (port_num < osm_node_get_num_physp(p_port->p_node)) {
162321936Shselasky			p_physp =
163321936Shselasky			    osm_node_get_physp_ptr(p_port->p_node, port_num);
164321936Shselasky			/* check that the p_physp is valid, and that the requester
165321936Shselasky			   and the p_physp share a pkey. */
166321936Shselasky			if (p_physp &&
167321936Shselasky			    osm_physp_share_pkey(sa->p_log, p_req_physp, p_physp,
168321936Shselasky						 sa->p_subn->opt.allow_both_pkeys))
169321936Shselasky				sa_vl_arb_check_physp(sa, p_physp, p_ctxt);
170321936Shselasky		} else {
171321936Shselasky			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 2A03: "
172321936Shselasky				"Given Physical Port Number: 0x%X is out of range should be < 0x%X\n",
173321936Shselasky				port_num,
174321936Shselasky				osm_node_get_num_physp(p_port->p_node));
175321936Shselasky			goto Exit;
176321936Shselasky		}
177321936Shselasky	} else {
178321936Shselasky		num_ports = osm_node_get_num_physp(p_port->p_node);
179321936Shselasky		for (port_num = 0; port_num < num_ports; port_num++) {
180321936Shselasky			p_physp =
181321936Shselasky			    osm_node_get_physp_ptr(p_port->p_node, port_num);
182321936Shselasky			if (!p_physp)
183321936Shselasky				continue;
184321936Shselasky
185321936Shselasky			/* if the requester and the p_physp don't share a pkey -
186321936Shselasky			   continue */
187321936Shselasky			if (!osm_physp_share_pkey(sa->p_log, p_req_physp, p_physp,
188321936Shselasky						  sa->p_subn->opt.allow_both_pkeys))
189321936Shselasky				continue;
190321936Shselasky
191321936Shselasky			sa_vl_arb_check_physp(sa, p_physp, p_ctxt);
192321936Shselasky		}
193321936Shselasky	}
194321936ShselaskyExit:
195321936Shselasky	OSM_LOG_EXIT(sa->p_log);
196321936Shselasky}
197321936Shselasky
198321936Shselaskystatic void sa_vl_arb_by_comp_mask_cb(IN cl_map_item_t * p_map_item, void *cxt)
199321936Shselasky{
200321936Shselasky	const osm_port_t *p_port = (osm_port_t *) p_map_item;
201321936Shselasky	osm_vl_arb_search_ctxt_t *p_ctxt = cxt;
202321936Shselasky
203321936Shselasky	sa_vl_arb_by_comp_mask(p_ctxt->sa, p_port, p_ctxt);
204321936Shselasky}
205321936Shselasky
206321936Shselaskyvoid osm_vlarb_rec_rcv_process(IN void *ctx, IN void *data)
207321936Shselasky{
208321936Shselasky	osm_sa_t *sa = ctx;
209321936Shselasky	osm_madw_t *p_madw = data;
210321936Shselasky	const ib_sa_mad_t *sad_mad;
211321936Shselasky	const ib_vl_arb_table_record_t *p_rcvd_rec;
212321936Shselasky	const osm_port_t *p_port = NULL;
213321936Shselasky	cl_qlist_t rec_list;
214321936Shselasky	osm_vl_arb_search_ctxt_t context;
215321936Shselasky	ib_api_status_t status = IB_SUCCESS;
216321936Shselasky	ib_net64_t comp_mask;
217321936Shselasky	osm_physp_t *p_req_physp;
218321936Shselasky
219321936Shselasky	CL_ASSERT(sa);
220321936Shselasky
221321936Shselasky	OSM_LOG_ENTER(sa->p_log);
222321936Shselasky
223321936Shselasky	CL_ASSERT(p_madw);
224321936Shselasky
225321936Shselasky	sad_mad = osm_madw_get_sa_mad_ptr(p_madw);
226321936Shselasky	p_rcvd_rec =
227321936Shselasky	    (ib_vl_arb_table_record_t *) ib_sa_mad_get_payload_ptr(sad_mad);
228321936Shselasky	comp_mask = sad_mad->comp_mask;
229321936Shselasky
230321936Shselasky	CL_ASSERT(sad_mad->attr_id == IB_MAD_ATTR_VLARB_RECORD);
231321936Shselasky
232321936Shselasky	/* we only support SubnAdmGet and SubnAdmGetTable methods */
233321936Shselasky	if (sad_mad->method != IB_MAD_METHOD_GET &&
234321936Shselasky	    sad_mad->method != IB_MAD_METHOD_GETTABLE) {
235321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 2A05: "
236321936Shselasky			"Unsupported Method (%s) for a VLArbRecord request\n",
237321936Shselasky			ib_get_sa_method_str(sad_mad->method));
238321936Shselasky		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
239321936Shselasky		goto Exit;
240321936Shselasky	}
241321936Shselasky
242321936Shselasky	cl_plock_acquire(sa->p_lock);
243321936Shselasky
244321936Shselasky	/* update the requester physical port */
245321936Shselasky	p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
246321936Shselasky						osm_madw_get_mad_addr_ptr
247321936Shselasky						(p_madw));
248321936Shselasky	if (p_req_physp == NULL) {
249321936Shselasky		cl_plock_release(sa->p_lock);
250321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 2A04: "
251321936Shselasky			"Cannot find requester physical port\n");
252321936Shselasky		goto Exit;
253321936Shselasky	}
254321936Shselasky	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
255321936Shselasky		"Requester port GUID 0x%" PRIx64 "\n",
256321936Shselasky		cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
257321936Shselasky
258321936Shselasky	cl_qlist_init(&rec_list);
259321936Shselasky
260321936Shselasky	context.p_rcvd_rec = p_rcvd_rec;
261321936Shselasky	context.p_list = &rec_list;
262321936Shselasky	context.comp_mask = sad_mad->comp_mask;
263321936Shselasky	context.sa = sa;
264321936Shselasky	context.block_num = p_rcvd_rec->block_num;
265321936Shselasky	context.p_req_physp = p_req_physp;
266321936Shselasky
267321936Shselasky	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
268321936Shselasky		"Got Query Lid:%u(%02X), Port:0x%02X(%02X), Block:0x%02X(%02X)\n",
269321936Shselasky		cl_ntoh16(p_rcvd_rec->lid),
270321936Shselasky		(comp_mask & IB_VLA_COMPMASK_LID) != 0, p_rcvd_rec->port_num,
271321936Shselasky		(comp_mask & IB_VLA_COMPMASK_OUT_PORT) != 0,
272321936Shselasky		p_rcvd_rec->block_num,
273321936Shselasky		(comp_mask & IB_VLA_COMPMASK_BLOCK) != 0);
274321936Shselasky
275321936Shselasky	/*
276321936Shselasky	   If the user specified a LID, it obviously narrows our
277321936Shselasky	   work load, since we don't have to search every port
278321936Shselasky	 */
279321936Shselasky	if (comp_mask & IB_VLA_COMPMASK_LID) {
280321936Shselasky		p_port = osm_get_port_by_lid(sa->p_subn, p_rcvd_rec->lid);
281321936Shselasky		if (!p_port) {
282321936Shselasky			status = IB_NOT_FOUND;
283321936Shselasky			OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 2A09: "
284321936Shselasky				"No port found with LID %u\n",
285321936Shselasky				cl_ntoh16(p_rcvd_rec->lid));
286321936Shselasky		}
287321936Shselasky	}
288321936Shselasky
289321936Shselasky	if (status == IB_SUCCESS) {
290321936Shselasky		/* if we got a unique port - no need for a port search */
291321936Shselasky		if (p_port)
292321936Shselasky			/*  this does the loop on all the port phys ports */
293321936Shselasky			sa_vl_arb_by_comp_mask(sa, p_port, &context);
294321936Shselasky		else
295321936Shselasky			cl_qmap_apply_func(&sa->p_subn->port_guid_tbl,
296321936Shselasky					   sa_vl_arb_by_comp_mask_cb, &context);
297321936Shselasky	}
298321936Shselasky
299321936Shselasky	cl_plock_release(sa->p_lock);
300321936Shselasky
301321936Shselasky	osm_sa_respond(sa, p_madw, sizeof(ib_vl_arb_table_record_t), &rec_list);
302321936Shselasky
303321936ShselaskyExit:
304321936Shselasky	OSM_LOG_EXIT(sa->p_log);
305321936Shselasky}
306