1/*
2 * Copyright (c) 2006-2008 Voltaire, Inc. All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 *
32 */
33
34/*
35 * Abstract:
36 *    Implementation of osm_prtn_t.
37 * This object represents an IBA partition.
38 * This object is part of the opensm family of objects.
39 */
40
41#if HAVE_CONFIG_H
42#  include <config.h>
43#endif				/* HAVE_CONFIG_H */
44
45#include <stdlib.h>
46#include <string.h>
47#include <stdio.h>
48#include <sys/stat.h>
49#include <complib/cl_debug.h>
50#include <iba/ib_types.h>
51#include <opensm/osm_opensm.h>
52#include <opensm/osm_partition.h>
53#include <opensm/osm_node.h>
54#include <opensm/osm_sa.h>
55#include <opensm/osm_multicast.h>
56
57extern int osm_prtn_config_parse_file(osm_log_t * const p_log,
58				      osm_subn_t * const p_subn,
59				      const char *file_name);
60
61static uint16_t global_pkey_counter;
62
63osm_prtn_t *osm_prtn_new(IN const char *name, IN const uint16_t pkey)
64{
65	osm_prtn_t *p = malloc(sizeof(*p));
66	if (!p)
67		return NULL;
68
69	memset(p, 0, sizeof(*p));
70	p->pkey = pkey;
71	p->sl = OSM_DEFAULT_SL;
72	cl_map_construct(&p->full_guid_tbl);
73	cl_map_init(&p->full_guid_tbl, 32);
74	cl_map_construct(&p->part_guid_tbl);
75	cl_map_init(&p->part_guid_tbl, 32);
76
77	if (name && *name)
78		strncpy(p->name, name, sizeof(p->name));
79	else
80		snprintf(p->name, sizeof(p->name), "%04x", cl_ntoh16(pkey));
81
82	return p;
83}
84
85void osm_prtn_delete(IN OUT osm_prtn_t ** const pp_prtn)
86{
87	osm_prtn_t *p = *pp_prtn;
88
89	cl_map_remove_all(&p->full_guid_tbl);
90	cl_map_destroy(&p->full_guid_tbl);
91	cl_map_remove_all(&p->part_guid_tbl);
92	cl_map_destroy(&p->part_guid_tbl);
93	free(p);
94	*pp_prtn = NULL;
95}
96
97ib_api_status_t osm_prtn_add_port(osm_log_t * p_log, osm_subn_t * p_subn,
98				  osm_prtn_t * p, ib_net64_t guid,
99				  boolean_t full)
100{
101	ib_api_status_t status = IB_SUCCESS;
102	cl_map_t *p_tbl;
103	osm_port_t *p_port;
104	osm_physp_t *p_physp;
105
106	p_port = osm_get_port_by_guid(p_subn, guid);
107	if (!p_port) {
108		OSM_LOG(p_log, OSM_LOG_VERBOSE,
109			"port 0x%" PRIx64 " not found\n", cl_ntoh64(guid));
110		return status;
111	}
112
113	p_physp = p_port->p_physp;
114	if (!p_physp) {
115		OSM_LOG(p_log, OSM_LOG_VERBOSE,
116			"no physical for port 0x%" PRIx64 "\n",
117			cl_ntoh64(guid));
118		return status;
119	}
120
121	if (cl_map_remove(&p->part_guid_tbl, guid) ||
122	    cl_map_remove(&p->full_guid_tbl, guid)) {
123		OSM_LOG(p_log, OSM_LOG_VERBOSE,
124			"port 0x%" PRIx64 " already in "
125			"partition \'%s\' (0x%04x). Will overwrite\n",
126			cl_ntoh64(guid), p->name, cl_ntoh16(p->pkey));
127	}
128
129	p_tbl = (full == TRUE) ? &p->full_guid_tbl : &p->part_guid_tbl;
130
131	if (cl_map_insert(p_tbl, guid, p_physp) == NULL)
132		return IB_INSUFFICIENT_MEMORY;
133
134	return status;
135}
136
137ib_api_status_t osm_prtn_add_all(osm_log_t * p_log, osm_subn_t * p_subn,
138				 osm_prtn_t * p, boolean_t full)
139{
140	cl_qmap_t *p_port_tbl = &p_subn->port_guid_tbl;
141	cl_map_item_t *p_item;
142	osm_port_t *p_port;
143	ib_api_status_t status = IB_SUCCESS;
144
145	p_item = cl_qmap_head(p_port_tbl);
146	while (p_item != cl_qmap_end(p_port_tbl)) {
147		p_port = (osm_port_t *) p_item;
148		p_item = cl_qmap_next(p_item);
149		status = osm_prtn_add_port(p_log, p_subn, p,
150					   osm_port_get_guid(p_port), full);
151		if (status != IB_SUCCESS)
152			goto _err;
153	}
154
155_err:
156	return status;
157}
158
159static const ib_gid_t osm_ipoib_mgid = {
160	{
161	 0xff,			/*  multicast field */
162	 0x12,			/*  non-permanent bit, link local scope */
163	 0x40, 0x1b,		/*  IPv4 signature */
164	 0xff, 0xff,		/*  16 bits of P_Key (to be filled in) */
165	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/*  48 bits of zeros */
166	 0xff, 0xff, 0xff, 0xff,	/*  32 bit IPv4 broadcast address */
167	 },
168};
169
170/*
171 * HACK: Until TS resolves their noncompliant join compmask,
172 * we have to pre-define the MGID
173 */
174static const ib_gid_t osm_ts_ipoib_mgid = {
175	{
176	 0xff,			/*  multicast field */
177	 0x12,			/*  non-permanent bit, link local scope */
178	 0x40, 0x1b,		/*  IPv4 signature */
179	 0xff, 0xff,		/*  16 bits of P_Key (to be filled in) */
180	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/*  48 bits of zeros */
181	 0x00, 0x00, 0x00, 0x01,	/*  32 bit IPv4 broadcast address */
182	 },
183};
184
185ib_api_status_t osm_prtn_add_mcgroup(osm_log_t * p_log,
186				     osm_subn_t * p_subn, osm_prtn_t * p,
187				     uint8_t rate,
188				     uint8_t mtu, uint8_t scope)
189{
190	ib_member_rec_t mc_rec;
191	ib_net64_t comp_mask;
192	ib_net16_t pkey;
193	osm_mgrp_t *p_mgrp = NULL;
194	osm_sa_t *p_sa = &p_subn->p_osm->sa;
195	ib_api_status_t status = IB_SUCCESS;
196	uint8_t hop_limit;
197
198	pkey = p->pkey | cl_hton16(0x8000);
199	if (!scope)
200		scope = OSM_DEFAULT_MGRP_SCOPE;
201	hop_limit = (scope == IB_MC_SCOPE_LINK_LOCAL) ? 0 : IB_HOPLIMIT_MAX;
202
203	memset(&mc_rec, 0, sizeof(mc_rec));
204
205	mc_rec.mgid = osm_ipoib_mgid;	/* ipv4 broadcast group */
206	memcpy(&mc_rec.mgid.raw[4], &pkey, sizeof(pkey));
207
208	mc_rec.qkey = CL_HTON32(0x0b1b);
209	mc_rec.mtu = (mtu ? mtu : OSM_DEFAULT_MGRP_MTU) | (2 << 6);	/* 2048 Bytes */
210	mc_rec.tclass = 0;
211	mc_rec.pkey = pkey;
212	mc_rec.rate = (rate ? rate : OSM_DEFAULT_MGRP_RATE) | (2 << 6);	/* 10Gb/sec */
213	mc_rec.pkt_life = p_subn->opt.subnet_timeout;
214	mc_rec.sl_flow_hop = ib_member_set_sl_flow_hop(p->sl, 0, hop_limit);
215	/* Scope in MCMemberRecord (if present) needs to be consistent with MGID */
216	mc_rec.scope_state = ib_member_set_scope_state(scope, IB_MC_REC_STATE_FULL_MEMBER);
217	ib_mgid_set_scope(&mc_rec.mgid, scope);
218
219	/* don't update rate, mtu */
220	comp_mask = IB_MCR_COMPMASK_MTU | IB_MCR_COMPMASK_MTU_SEL |
221	    IB_MCR_COMPMASK_RATE | IB_MCR_COMPMASK_RATE_SEL;
222	status = osm_mcmr_rcv_find_or_create_new_mgrp(p_sa, comp_mask, &mc_rec,
223						      &p_mgrp);
224	if (!p_mgrp || status != IB_SUCCESS)
225		OSM_LOG(p_log, OSM_LOG_ERROR,
226			"Failed to create MC group with pkey 0x%04x\n",
227			cl_ntoh16(pkey));
228	if (p_mgrp) {
229		p_mgrp->well_known = TRUE;
230		p->mlid = p_mgrp->mlid;
231	}
232
233	/* workaround for TS */
234	/* FIXME: remove this upon TS fixes */
235	mc_rec.mgid = osm_ts_ipoib_mgid;
236	memcpy(&mc_rec.mgid.raw[4], &pkey, sizeof(pkey));
237	/* Scope in MCMemberRecord (if present) needs to be consistent with MGID */
238	mc_rec.scope_state = ib_member_set_scope_state(scope, IB_MC_REC_STATE_FULL_MEMBER);
239	ib_mgid_set_scope(&mc_rec.mgid, scope);
240
241	status = osm_mcmr_rcv_find_or_create_new_mgrp(p_sa, comp_mask, &mc_rec,
242						      &p_mgrp);
243	if (p_mgrp) {
244		p_mgrp->well_known = TRUE;
245		if (!p->mlid)
246			p->mlid = p_mgrp->mlid;
247	}
248
249	return status;
250}
251
252static uint16_t __generate_pkey(osm_subn_t * p_subn)
253{
254	uint16_t pkey;
255
256	cl_qmap_t *m = &p_subn->prtn_pkey_tbl;
257	while (global_pkey_counter < cl_ntoh16(IB_DEFAULT_PARTIAL_PKEY) - 1) {
258		pkey = ++global_pkey_counter;
259		pkey = cl_hton16(pkey);
260		if (cl_qmap_get(m, pkey) == cl_qmap_end(m))
261			return pkey;
262	}
263	return 0;
264}
265
266osm_prtn_t *osm_prtn_find_by_name(osm_subn_t * p_subn, const char *name)
267{
268	cl_map_item_t *p_next;
269	osm_prtn_t *p;
270
271	p_next = cl_qmap_head(&p_subn->prtn_pkey_tbl);
272	while (p_next != cl_qmap_end(&p_subn->prtn_pkey_tbl)) {
273		p = (osm_prtn_t *) p_next;
274		p_next = cl_qmap_next(&p->map_item);
275		if (!strncmp(p->name, name, sizeof(p->name)))
276			return p;
277	}
278
279	return NULL;
280}
281
282osm_prtn_t *osm_prtn_make_new(osm_log_t * p_log, osm_subn_t * p_subn,
283			      const char *name, uint16_t pkey)
284{
285	osm_prtn_t *p = NULL, *p_check;
286
287	pkey &= cl_hton16((uint16_t) ~ 0x8000);
288
289	if (!pkey) {
290		if (name && (p = osm_prtn_find_by_name(p_subn, name)))
291			return p;
292		if (!(pkey = __generate_pkey(p_subn)))
293			return NULL;
294	}
295
296	p = osm_prtn_new(name, pkey);
297	if (!p) {
298		OSM_LOG(p_log, OSM_LOG_ERROR, "Unable to create"
299			" partition \'%s\' (0x%04x)\n", name, cl_ntoh16(pkey));
300		return NULL;
301	}
302
303	p_check = (osm_prtn_t *) cl_qmap_insert(&p_subn->prtn_pkey_tbl,
304						p->pkey, &p->map_item);
305	if (p != p_check) {
306		OSM_LOG(p_log, OSM_LOG_VERBOSE, "Duplicated partition"
307			" definition: \'%s\' (0x%04x) prev name \'%s\'"
308			".  Will use it\n",
309			name, cl_ntoh16(pkey), p_check->name);
310		osm_prtn_delete(&p);
311		p = p_check;
312	}
313
314	return p;
315}
316
317static ib_api_status_t osm_prtn_make_default(osm_log_t * const p_log,
318					     osm_subn_t * const p_subn,
319					     boolean_t no_config)
320{
321	ib_api_status_t status = IB_UNKNOWN_ERROR;
322	osm_prtn_t *p;
323
324	p = osm_prtn_make_new(p_log, p_subn, "Default",
325			      IB_DEFAULT_PARTIAL_PKEY);
326	if (!p)
327		goto _err;
328	status = osm_prtn_add_all(p_log, p_subn, p, no_config);
329	if (status != IB_SUCCESS)
330		goto _err;
331	cl_map_remove(&p->part_guid_tbl, p_subn->sm_port_guid);
332	status =
333	    osm_prtn_add_port(p_log, p_subn, p, p_subn->sm_port_guid, TRUE);
334
335	if (no_config)
336		osm_prtn_add_mcgroup(p_log, p_subn, p, 0, 0, 0);
337
338_err:
339	return status;
340}
341
342ib_api_status_t osm_prtn_make_partitions(osm_log_t * const p_log,
343					 osm_subn_t * const p_subn)
344{
345	struct stat statbuf;
346	const char *file_name;
347	boolean_t is_config = TRUE;
348	ib_api_status_t status = IB_SUCCESS;
349	cl_map_item_t *p_next;
350	osm_prtn_t *p;
351
352	file_name = p_subn->opt.partition_config_file ?
353	    p_subn->opt.partition_config_file : OSM_DEFAULT_PARTITION_CONFIG_FILE;
354	if (stat(file_name, &statbuf))
355		is_config = FALSE;
356
357	/* clean up current port maps */
358	p_next = cl_qmap_head(&p_subn->prtn_pkey_tbl);
359	while (p_next != cl_qmap_end(&p_subn->prtn_pkey_tbl)) {
360		p = (osm_prtn_t *) p_next;
361		p_next = cl_qmap_next(&p->map_item);
362		cl_map_remove_all(&p->part_guid_tbl);
363		cl_map_remove_all(&p->full_guid_tbl);
364	}
365
366	global_pkey_counter = 0;
367
368	status = osm_prtn_make_default(p_log, p_subn, !is_config);
369	if (status != IB_SUCCESS)
370		goto _err;
371
372	if (is_config && osm_prtn_config_parse_file(p_log, p_subn, file_name)) {
373		OSM_LOG(p_log, OSM_LOG_VERBOSE, "Partition configuration "
374			"was not fully processed\n");
375	}
376
377	/* and now clean up empty partitions */
378	p_next = cl_qmap_head(&p_subn->prtn_pkey_tbl);
379	while (p_next != cl_qmap_end(&p_subn->prtn_pkey_tbl)) {
380		p = (osm_prtn_t *) p_next;
381		p_next = cl_qmap_next(&p->map_item);
382		if (cl_map_count(&p->part_guid_tbl) == 0 &&
383		    cl_map_count(&p->full_guid_tbl) == 0) {
384			cl_qmap_remove_item(&p_subn->prtn_pkey_tbl,
385					    (cl_map_item_t *) p);
386			osm_prtn_delete(&p);
387		}
388	}
389
390_err:
391	return status;
392}
393