1321936Shselasky/*
2321936Shselasky * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3321936Shselasky * Copyright (c) 2002-2005,2008 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_lftr_rcv_t.
40321936Shselasky *   This object represents the LinearForwardingTable 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_debug.h>
51321936Shselasky#include <complib/cl_qlist.h>
52321936Shselasky#include <opensm/osm_file_ids.h>
53321936Shselasky#define FILE_ID OSM_FILE_SA_LFT_RECORD_C
54321936Shselasky#include <vendor/osm_vendor_api.h>
55321936Shselasky#include <opensm/osm_switch.h>
56321936Shselasky#include <opensm/osm_helper.h>
57321936Shselasky#include <opensm/osm_pkey.h>
58321936Shselasky#include <opensm/osm_sa.h>
59321936Shselasky
60321936Shselasky#define SA_LFTR_RESP_SIZE SA_ITEM_RESP_SIZE(lft_rec)
61321936Shselasky
62321936Shselaskytypedef struct osm_lftr_search_ctxt {
63321936Shselasky	const ib_lft_record_t *p_rcvd_rec;
64321936Shselasky	ib_net64_t comp_mask;
65321936Shselasky	cl_qlist_t *p_list;
66321936Shselasky	osm_sa_t *sa;
67321936Shselasky	const osm_physp_t *p_req_physp;
68321936Shselasky} osm_lftr_search_ctxt_t;
69321936Shselasky
70321936Shselaskystatic ib_api_status_t lftr_rcv_new_lftr(IN osm_sa_t * sa,
71321936Shselasky					 IN const osm_switch_t * p_sw,
72321936Shselasky					 IN cl_qlist_t * p_list,
73321936Shselasky					 IN ib_net16_t lid, IN uint16_t block)
74321936Shselasky{
75321936Shselasky	osm_sa_item_t *p_rec_item;
76321936Shselasky	ib_api_status_t status = IB_SUCCESS;
77321936Shselasky
78321936Shselasky	OSM_LOG_ENTER(sa->p_log);
79321936Shselasky
80321936Shselasky	p_rec_item = malloc(SA_LFTR_RESP_SIZE);
81321936Shselasky	if (p_rec_item == NULL) {
82321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4402: "
83321936Shselasky			"rec_item alloc failed\n");
84321936Shselasky		status = IB_INSUFFICIENT_RESOURCES;
85321936Shselasky		goto Exit;
86321936Shselasky	}
87321936Shselasky
88321936Shselasky	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
89321936Shselasky		"New LinearForwardingTable: sw 0x%016" PRIx64
90321936Shselasky		"\n\t\t\t\tblock 0x%02X lid %u\n",
91321936Shselasky		cl_ntoh64(osm_node_get_node_guid(p_sw->p_node)),
92321936Shselasky		block, cl_ntoh16(lid));
93321936Shselasky
94321936Shselasky	memset(p_rec_item, 0, SA_LFTR_RESP_SIZE);
95321936Shselasky
96321936Shselasky	p_rec_item->resp.lft_rec.lid = lid;
97321936Shselasky	p_rec_item->resp.lft_rec.block_num = cl_hton16(block);
98321936Shselasky
99321936Shselasky	/* copy the lft block */
100321936Shselasky	osm_switch_get_lft_block(p_sw, block, p_rec_item->resp.lft_rec.lft);
101321936Shselasky
102321936Shselasky	cl_qlist_insert_tail(p_list, &p_rec_item->list_item);
103321936Shselasky
104321936ShselaskyExit:
105321936Shselasky	OSM_LOG_EXIT(sa->p_log);
106321936Shselasky	return status;
107321936Shselasky}
108321936Shselasky
109321936Shselaskystatic void lftr_rcv_by_comp_mask(IN cl_map_item_t * p_map_item, IN void *cxt)
110321936Shselasky{
111321936Shselasky	const osm_lftr_search_ctxt_t *p_ctxt = cxt;
112321936Shselasky	const osm_switch_t *p_sw = (osm_switch_t *) p_map_item;
113321936Shselasky	const ib_lft_record_t *const p_rcvd_rec = p_ctxt->p_rcvd_rec;
114321936Shselasky	osm_sa_t *sa = p_ctxt->sa;
115321936Shselasky	ib_net64_t const comp_mask = p_ctxt->comp_mask;
116321936Shselasky	const osm_physp_t *const p_req_physp = p_ctxt->p_req_physp;
117321936Shselasky	osm_port_t *p_port;
118321936Shselasky	uint16_t min_lid_ho, max_lid_ho;
119321936Shselasky	uint16_t min_block, max_block, block;
120321936Shselasky	const osm_physp_t *p_physp;
121321936Shselasky
122321936Shselasky	/* In switches, the port guid is the node guid. */
123321936Shselasky	p_port = osm_get_port_by_guid(sa->p_subn,
124321936Shselasky				      p_sw->p_node->node_info.port_guid);
125321936Shselasky	if (!p_port) {
126321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4405: "
127321936Shselasky			"Failed to find Port by Node Guid:0x%016" PRIx64
128321936Shselasky			"\n", cl_ntoh64(p_sw->p_node->node_info.node_guid));
129321936Shselasky		return;
130321936Shselasky	}
131321936Shselasky
132321936Shselasky	/* check that the requester physp and the current physp are under
133321936Shselasky	   the same partition. */
134321936Shselasky	p_physp = p_port->p_physp;
135321936Shselasky	if (!p_physp) {
136321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4406: "
137321936Shselasky			"Failed to find default physical Port by Node Guid:0x%016"
138321936Shselasky			PRIx64 "\n",
139321936Shselasky			cl_ntoh64(p_sw->p_node->node_info.node_guid));
140321936Shselasky		return;
141321936Shselasky	}
142321936Shselasky	if (!osm_physp_share_pkey(sa->p_log, p_req_physp,
143321936Shselasky				  p_physp, sa->p_subn->opt.allow_both_pkeys))
144321936Shselasky		return;
145321936Shselasky
146321936Shselasky	/* get the port 0 of the switch */
147321936Shselasky	osm_port_get_lid_range_ho(p_port, &min_lid_ho, &max_lid_ho);
148321936Shselasky
149321936Shselasky	/* compare the lids - if required */
150321936Shselasky	if (comp_mask & IB_LFTR_COMPMASK_LID) {
151321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
152321936Shselasky			"Comparing lid:%u to port lid range: %u .. %u\n",
153321936Shselasky			cl_ntoh16(p_rcvd_rec->lid), min_lid_ho, max_lid_ho);
154321936Shselasky		/* ok we are ready for range check */
155321936Shselasky		if (min_lid_ho > cl_ntoh16(p_rcvd_rec->lid) ||
156321936Shselasky		    max_lid_ho < cl_ntoh16(p_rcvd_rec->lid))
157321936Shselasky			return;
158321936Shselasky	}
159321936Shselasky
160321936Shselasky	/* now we need to decide which blocks to output */
161321936Shselasky	max_block = osm_switch_get_max_block_id_in_use(p_sw);
162321936Shselasky	if (comp_mask & IB_LFTR_COMPMASK_BLOCK) {
163321936Shselasky		min_block = cl_ntoh16(p_rcvd_rec->block_num);
164321936Shselasky		if (min_block > max_block)
165321936Shselasky			return;
166321936Shselasky		max_block = min_block;
167321936Shselasky	} else			/* use as many blocks as "in use" */
168321936Shselasky		min_block = 0;
169321936Shselasky
170321936Shselasky	/* so we can add these blocks one by one ... */
171321936Shselasky	for (block = min_block; block <= max_block; block++)
172321936Shselasky		lftr_rcv_new_lftr(sa, p_sw, p_ctxt->p_list,
173321936Shselasky				  osm_port_get_base_lid(p_port), block);
174321936Shselasky}
175321936Shselasky
176321936Shselaskyvoid osm_lftr_rcv_process(IN void *ctx, IN void *data)
177321936Shselasky{
178321936Shselasky	osm_sa_t *sa = ctx;
179321936Shselasky	osm_madw_t *p_madw = data;
180321936Shselasky	const ib_sa_mad_t *p_rcvd_mad;
181321936Shselasky	const ib_lft_record_t *p_rcvd_rec;
182321936Shselasky	cl_qlist_t rec_list;
183321936Shselasky	osm_lftr_search_ctxt_t context;
184321936Shselasky	osm_physp_t *p_req_physp;
185321936Shselasky
186321936Shselasky	CL_ASSERT(sa);
187321936Shselasky
188321936Shselasky	OSM_LOG_ENTER(sa->p_log);
189321936Shselasky
190321936Shselasky	CL_ASSERT(p_madw);
191321936Shselasky
192321936Shselasky	p_rcvd_mad = osm_madw_get_sa_mad_ptr(p_madw);
193321936Shselasky	p_rcvd_rec = (ib_lft_record_t *) ib_sa_mad_get_payload_ptr(p_rcvd_mad);
194321936Shselasky
195321936Shselasky	CL_ASSERT(p_rcvd_mad->attr_id == IB_MAD_ATTR_LFT_RECORD);
196321936Shselasky
197321936Shselasky	/* we only support SubnAdmGet and SubnAdmGetTable methods */
198321936Shselasky	if (p_rcvd_mad->method != IB_MAD_METHOD_GET &&
199321936Shselasky	    p_rcvd_mad->method != IB_MAD_METHOD_GETTABLE) {
200321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4408: "
201321936Shselasky			"Unsupported Method (%s) for LFTRecord request\n",
202321936Shselasky			ib_get_sa_method_str(p_rcvd_mad->method));
203321936Shselasky		osm_sa_send_error(sa, p_madw, IB_MAD_STATUS_UNSUP_METHOD_ATTR);
204321936Shselasky		goto Exit;
205321936Shselasky	}
206321936Shselasky
207321936Shselasky	cl_plock_acquire(sa->p_lock);
208321936Shselasky
209321936Shselasky	/* update the requester physical port */
210321936Shselasky	p_req_physp = osm_get_physp_by_mad_addr(sa->p_log, sa->p_subn,
211321936Shselasky						osm_madw_get_mad_addr_ptr
212321936Shselasky						(p_madw));
213321936Shselasky	if (p_req_physp == NULL) {
214321936Shselasky		cl_plock_release(sa->p_lock);
215321936Shselasky		OSM_LOG(sa->p_log, OSM_LOG_ERROR, "ERR 4407: "
216321936Shselasky			"Cannot find requester physical port\n");
217321936Shselasky		goto Exit;
218321936Shselasky	}
219321936Shselasky
220321936Shselasky	OSM_LOG(sa->p_log, OSM_LOG_DEBUG,
221321936Shselasky		"Requester port GUID 0x%" PRIx64 "\n",
222321936Shselasky		cl_ntoh64(osm_physp_get_port_guid(p_req_physp)));
223321936Shselasky
224321936Shselasky	cl_qlist_init(&rec_list);
225321936Shselasky
226321936Shselasky	context.p_rcvd_rec = p_rcvd_rec;
227321936Shselasky	context.p_list = &rec_list;
228321936Shselasky	context.comp_mask = p_rcvd_mad->comp_mask;
229321936Shselasky	context.sa = sa;
230321936Shselasky	context.p_req_physp = p_req_physp;
231321936Shselasky
232321936Shselasky	/* Go over all switches */
233321936Shselasky	cl_qmap_apply_func(&sa->p_subn->sw_guid_tbl, lftr_rcv_by_comp_mask,
234321936Shselasky			   &context);
235321936Shselasky
236321936Shselasky	cl_plock_release(sa->p_lock);
237321936Shselasky
238321936Shselasky	osm_sa_respond(sa, p_madw, sizeof(ib_lft_record_t), &rec_list);
239321936Shselasky
240321936ShselaskyExit:
241321936Shselasky	OSM_LOG_EXIT(sa->p_log);
242321936Shselasky}
243