1/*
2 * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2015 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_node_t.
39 * This object represents an Infiniband Node.
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 <stdlib.h>
48#include <iba/ib_types.h>
49#include <opensm/osm_file_ids.h>
50#define FILE_ID OSM_FILE_NODE_C
51#include <opensm/osm_node.h>
52#include <opensm/osm_madw.h>
53
54void osm_node_init_physp(IN osm_node_t * p_node, uint8_t port_num,
55			 IN const osm_madw_t * p_madw)
56{
57	ib_net64_t port_guid;
58	ib_smp_t *p_smp;
59	ib_node_info_t *p_ni;
60
61	p_smp = osm_madw_get_smp_ptr(p_madw);
62
63	p_ni = ib_smp_get_payload_ptr(p_smp);
64	port_guid = p_ni->port_guid;
65
66	CL_ASSERT(port_num < p_node->physp_tbl_size);
67
68	osm_physp_init(&p_node->physp_table[port_num],
69		       port_guid, port_num, p_node,
70		       osm_madw_get_bind_handle(p_madw),
71		       p_smp->hop_count, p_smp->initial_path);
72}
73
74osm_node_t *osm_node_new(IN const osm_madw_t * p_madw)
75{
76	osm_node_t *p_node;
77	ib_smp_t *p_smp;
78	ib_node_info_t *p_ni;
79	uint8_t i;
80	uint32_t size;
81
82	p_smp = osm_madw_get_smp_ptr(p_madw);
83	p_ni = ib_smp_get_payload_ptr(p_smp);
84
85	/*
86	   The node object already contains one physical port object.
87	   Therefore, subtract 1 from the number of physical ports
88	   used by the switch.  This is not done for CA's since they
89	   need to occupy 1 more physp than they physically have since
90	   we still reserve room for a "port 0".
91	 */
92	size = p_ni->num_ports;
93
94	p_node = malloc(sizeof(*p_node) + sizeof(osm_physp_t) * size);
95	if (!p_node)
96		return NULL;
97
98	memset(p_node, 0, sizeof(*p_node) + sizeof(osm_physp_t) * size);
99	p_node->node_info = *p_ni;
100	p_node->physp_tbl_size = size + 1;
101
102	p_node->physp_discovered = malloc(sizeof(uint8_t) * p_node->physp_tbl_size);
103	if (!p_node->physp_discovered) {
104		free(p_node);
105		return NULL;
106	}
107	memset(p_node->physp_discovered, 0, sizeof(uint8_t) * p_node->physp_tbl_size);
108	/*
109	   Construct Physical Port objects owned by this Node.
110	   Then, initialize the Physical Port through with we
111	   discovered this port.
112	   For switches, all ports have the same GUID.
113	   For CAs and routers, each port has a different GUID, so we only
114	   know the GUID for the port that responded to our
115	   Get(NodeInfo).
116	 */
117	for (i = 0; i < p_node->physp_tbl_size; i++)
118		osm_physp_construct(&p_node->physp_table[i]);
119
120	if (p_ni->node_type == IB_NODE_TYPE_SWITCH)
121		for (i = 0; i <= p_ni->num_ports; i++)
122			osm_node_init_physp(p_node, i, p_madw);
123	else
124		osm_node_init_physp(p_node,
125				    ib_node_info_get_local_port_num(p_ni),
126				    p_madw);
127	p_node->print_desc = strdup(OSM_NODE_DESC_UNKNOWN);
128
129	return p_node;
130}
131
132static void node_destroy(IN osm_node_t * p_node)
133{
134	uint16_t i;
135
136	/*
137	   Cleanup all physports
138	 */
139	for (i = 0; i < p_node->physp_tbl_size; i++)
140		osm_physp_destroy(&p_node->physp_table[i]);
141
142	/* cleanup printable node_desc field */
143	if (p_node->print_desc)
144		free(p_node->print_desc);
145
146	/* cleanup physp_discovered array */
147	free(p_node->physp_discovered);
148}
149
150void osm_node_delete(IN OUT osm_node_t ** p_node)
151{
152	CL_ASSERT(p_node && *p_node);
153	node_destroy(*p_node);
154	free(*p_node);
155	*p_node = NULL;
156}
157
158void osm_node_link(IN osm_node_t * p_node, IN uint8_t port_num,
159		   IN osm_node_t * p_remote_node, IN uint8_t remote_port_num)
160{
161	osm_physp_t *p_physp;
162	osm_physp_t *p_remote_physp;
163
164	p_physp = osm_node_get_physp_ptr(p_node, port_num);
165	p_remote_physp = osm_node_get_physp_ptr(p_remote_node, remote_port_num);
166
167	if (p_physp->p_remote_physp)
168		p_physp->p_remote_physp->p_remote_physp = NULL;
169	if (p_remote_physp->p_remote_physp)
170		p_remote_physp->p_remote_physp->p_remote_physp = NULL;
171
172	osm_physp_link(p_physp, p_remote_physp);
173}
174
175void osm_node_unlink(IN osm_node_t * p_node, IN uint8_t port_num,
176		     IN osm_node_t * p_remote_node, IN uint8_t remote_port_num)
177{
178	osm_physp_t *p_physp;
179	osm_physp_t *p_remote_physp;
180
181	CL_ASSERT(port_num < p_node->physp_tbl_size);
182	CL_ASSERT(remote_port_num < p_remote_node->physp_tbl_size);
183
184	if (osm_node_link_exists(p_node, port_num,
185				 p_remote_node, remote_port_num)) {
186
187		p_physp = osm_node_get_physp_ptr(p_node, port_num);
188		p_remote_physp =
189		    osm_node_get_physp_ptr(p_remote_node, remote_port_num);
190
191		osm_physp_unlink(p_physp, p_remote_physp);
192	}
193}
194
195boolean_t osm_node_link_exists(IN osm_node_t * p_node, IN uint8_t port_num,
196			       IN osm_node_t * p_remote_node,
197			       IN uint8_t remote_port_num)
198{
199	osm_physp_t *p_physp;
200	osm_physp_t *p_remote_physp;
201
202	CL_ASSERT(port_num < p_node->physp_tbl_size);
203	CL_ASSERT(remote_port_num < p_remote_node->physp_tbl_size);
204
205	p_physp = osm_node_get_physp_ptr(p_node, port_num);
206	p_remote_physp = osm_node_get_physp_ptr(p_remote_node, remote_port_num);
207
208	return osm_physp_link_exists(p_physp, p_remote_physp);
209}
210
211boolean_t osm_node_link_has_valid_ports(IN osm_node_t * p_node,
212					IN uint8_t port_num,
213					IN osm_node_t * p_remote_node,
214					IN uint8_t remote_port_num)
215{
216	osm_physp_t *p_physp;
217	osm_physp_t *p_remote_physp;
218
219	CL_ASSERT(port_num < p_node->physp_tbl_size);
220	CL_ASSERT(remote_port_num < p_remote_node->physp_tbl_size);
221
222	p_physp = osm_node_get_physp_ptr(p_node, port_num);
223	p_remote_physp = osm_node_get_physp_ptr(p_remote_node, remote_port_num);
224
225	return (p_physp && p_remote_physp);
226}
227
228boolean_t osm_node_has_any_link(IN osm_node_t * p_node, IN uint8_t port_num)
229{
230	osm_physp_t *p_physp;
231	CL_ASSERT(port_num < p_node->physp_tbl_size);
232	p_physp = osm_node_get_physp_ptr(p_node, port_num);
233	return osm_physp_has_any_link(p_physp);
234}
235
236osm_node_t *osm_node_get_remote_node(IN osm_node_t * p_node,
237				     IN uint8_t port_num,
238				     OUT uint8_t * p_remote_port_num)
239{
240	osm_physp_t *p_physp;
241	osm_physp_t *p_remote_physp;
242
243	p_physp = osm_node_get_physp_ptr(p_node, port_num);
244
245	if (!p_physp || !osm_physp_has_any_link(p_physp))
246		return NULL;
247
248	p_remote_physp = osm_physp_get_remote(p_physp);
249	if (p_remote_port_num)
250		*p_remote_port_num = osm_physp_get_port_num(p_remote_physp);
251
252	return osm_physp_get_node_ptr(p_remote_physp);
253}
254
255/**********************************************************************
256 The lock must be held before calling this function.
257**********************************************************************/
258ib_net16_t osm_node_get_remote_base_lid(IN osm_node_t * p_node,
259					IN uint32_t port_num)
260{
261	osm_physp_t *p_physp;
262	osm_physp_t *p_remote_physp;
263	CL_ASSERT(port_num < p_node->physp_tbl_size);
264
265	p_physp = osm_node_get_physp_ptr(p_node, port_num);
266	if (p_physp) {
267		p_remote_physp = osm_physp_get_remote(p_physp);
268		return osm_physp_get_base_lid(p_remote_physp);
269	}
270
271	return 0;
272}
273