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