1219820Sjeff/*
2219820Sjeff * Copyright (c) 2006-2008 Voltaire, Inc. All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff *
32219820Sjeff */
33219820Sjeff
34219820Sjeff/*
35219820Sjeff * Abstract:
36219820Sjeff *    Implementation of osm_prtn_t.
37219820Sjeff * This object represents an IBA partition.
38219820Sjeff * This object is part of the opensm family of objects.
39219820Sjeff */
40219820Sjeff
41219820Sjeff#if HAVE_CONFIG_H
42219820Sjeff#  include <config.h>
43219820Sjeff#endif				/* HAVE_CONFIG_H */
44219820Sjeff
45219820Sjeff#include <stdlib.h>
46219820Sjeff#include <string.h>
47219820Sjeff#include <stdio.h>
48219820Sjeff#include <sys/stat.h>
49219820Sjeff#include <complib/cl_debug.h>
50219820Sjeff#include <iba/ib_types.h>
51219820Sjeff#include <opensm/osm_opensm.h>
52219820Sjeff#include <opensm/osm_partition.h>
53219820Sjeff#include <opensm/osm_node.h>
54219820Sjeff#include <opensm/osm_sa.h>
55219820Sjeff#include <opensm/osm_multicast.h>
56219820Sjeff
57219820Sjeffextern int osm_prtn_config_parse_file(osm_log_t * const p_log,
58219820Sjeff				      osm_subn_t * const p_subn,
59219820Sjeff				      const char *file_name);
60219820Sjeff
61219820Sjeffstatic uint16_t global_pkey_counter;
62219820Sjeff
63219820Sjeffosm_prtn_t *osm_prtn_new(IN const char *name, IN const uint16_t pkey)
64219820Sjeff{
65219820Sjeff	osm_prtn_t *p = malloc(sizeof(*p));
66219820Sjeff	if (!p)
67219820Sjeff		return NULL;
68219820Sjeff
69219820Sjeff	memset(p, 0, sizeof(*p));
70219820Sjeff	p->pkey = pkey;
71219820Sjeff	p->sl = OSM_DEFAULT_SL;
72219820Sjeff	cl_map_construct(&p->full_guid_tbl);
73219820Sjeff	cl_map_init(&p->full_guid_tbl, 32);
74219820Sjeff	cl_map_construct(&p->part_guid_tbl);
75219820Sjeff	cl_map_init(&p->part_guid_tbl, 32);
76219820Sjeff
77219820Sjeff	if (name && *name)
78219820Sjeff		strncpy(p->name, name, sizeof(p->name));
79219820Sjeff	else
80219820Sjeff		snprintf(p->name, sizeof(p->name), "%04x", cl_ntoh16(pkey));
81219820Sjeff
82219820Sjeff	return p;
83219820Sjeff}
84219820Sjeff
85219820Sjeffvoid osm_prtn_delete(IN OUT osm_prtn_t ** const pp_prtn)
86219820Sjeff{
87219820Sjeff	osm_prtn_t *p = *pp_prtn;
88219820Sjeff
89219820Sjeff	cl_map_remove_all(&p->full_guid_tbl);
90219820Sjeff	cl_map_destroy(&p->full_guid_tbl);
91219820Sjeff	cl_map_remove_all(&p->part_guid_tbl);
92219820Sjeff	cl_map_destroy(&p->part_guid_tbl);
93219820Sjeff	free(p);
94219820Sjeff	*pp_prtn = NULL;
95219820Sjeff}
96219820Sjeff
97219820Sjeffib_api_status_t osm_prtn_add_port(osm_log_t * p_log, osm_subn_t * p_subn,
98219820Sjeff				  osm_prtn_t * p, ib_net64_t guid,
99219820Sjeff				  boolean_t full)
100219820Sjeff{
101219820Sjeff	ib_api_status_t status = IB_SUCCESS;
102219820Sjeff	cl_map_t *p_tbl;
103219820Sjeff	osm_port_t *p_port;
104219820Sjeff	osm_physp_t *p_physp;
105219820Sjeff
106219820Sjeff	p_port = osm_get_port_by_guid(p_subn, guid);
107219820Sjeff	if (!p_port) {
108219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE,
109219820Sjeff			"port 0x%" PRIx64 " not found\n", cl_ntoh64(guid));
110219820Sjeff		return status;
111219820Sjeff	}
112219820Sjeff
113219820Sjeff	p_physp = p_port->p_physp;
114219820Sjeff	if (!p_physp) {
115219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE,
116219820Sjeff			"no physical for port 0x%" PRIx64 "\n",
117219820Sjeff			cl_ntoh64(guid));
118219820Sjeff		return status;
119219820Sjeff	}
120219820Sjeff
121219820Sjeff	if (cl_map_remove(&p->part_guid_tbl, guid) ||
122219820Sjeff	    cl_map_remove(&p->full_guid_tbl, guid)) {
123219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE,
124219820Sjeff			"port 0x%" PRIx64 " already in "
125219820Sjeff			"partition \'%s\' (0x%04x). Will overwrite\n",
126219820Sjeff			cl_ntoh64(guid), p->name, cl_ntoh16(p->pkey));
127219820Sjeff	}
128219820Sjeff
129219820Sjeff	p_tbl = (full == TRUE) ? &p->full_guid_tbl : &p->part_guid_tbl;
130219820Sjeff
131219820Sjeff	if (cl_map_insert(p_tbl, guid, p_physp) == NULL)
132219820Sjeff		return IB_INSUFFICIENT_MEMORY;
133219820Sjeff
134219820Sjeff	return status;
135219820Sjeff}
136219820Sjeff
137219820Sjeffib_api_status_t osm_prtn_add_all(osm_log_t * p_log, osm_subn_t * p_subn,
138219820Sjeff				 osm_prtn_t * p, boolean_t full)
139219820Sjeff{
140219820Sjeff	cl_qmap_t *p_port_tbl = &p_subn->port_guid_tbl;
141219820Sjeff	cl_map_item_t *p_item;
142219820Sjeff	osm_port_t *p_port;
143219820Sjeff	ib_api_status_t status = IB_SUCCESS;
144219820Sjeff
145219820Sjeff	p_item = cl_qmap_head(p_port_tbl);
146219820Sjeff	while (p_item != cl_qmap_end(p_port_tbl)) {
147219820Sjeff		p_port = (osm_port_t *) p_item;
148219820Sjeff		p_item = cl_qmap_next(p_item);
149219820Sjeff		status = osm_prtn_add_port(p_log, p_subn, p,
150219820Sjeff					   osm_port_get_guid(p_port), full);
151219820Sjeff		if (status != IB_SUCCESS)
152219820Sjeff			goto _err;
153219820Sjeff	}
154219820Sjeff
155219820Sjeff_err:
156219820Sjeff	return status;
157219820Sjeff}
158219820Sjeff
159219820Sjeffstatic const ib_gid_t osm_ipoib_mgid = {
160219820Sjeff	{
161219820Sjeff	 0xff,			/*  multicast field */
162219820Sjeff	 0x12,			/*  non-permanent bit, link local scope */
163219820Sjeff	 0x40, 0x1b,		/*  IPv4 signature */
164219820Sjeff	 0xff, 0xff,		/*  16 bits of P_Key (to be filled in) */
165219820Sjeff	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/*  48 bits of zeros */
166219820Sjeff	 0xff, 0xff, 0xff, 0xff,	/*  32 bit IPv4 broadcast address */
167219820Sjeff	 },
168219820Sjeff};
169219820Sjeff
170219820Sjeff/*
171219820Sjeff * HACK: Until TS resolves their noncompliant join compmask,
172219820Sjeff * we have to pre-define the MGID
173219820Sjeff */
174219820Sjeffstatic const ib_gid_t osm_ts_ipoib_mgid = {
175219820Sjeff	{
176219820Sjeff	 0xff,			/*  multicast field */
177219820Sjeff	 0x12,			/*  non-permanent bit, link local scope */
178219820Sjeff	 0x40, 0x1b,		/*  IPv4 signature */
179219820Sjeff	 0xff, 0xff,		/*  16 bits of P_Key (to be filled in) */
180219820Sjeff	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/*  48 bits of zeros */
181219820Sjeff	 0x00, 0x00, 0x00, 0x01,	/*  32 bit IPv4 broadcast address */
182219820Sjeff	 },
183219820Sjeff};
184219820Sjeff
185219820Sjeffib_api_status_t osm_prtn_add_mcgroup(osm_log_t * p_log,
186219820Sjeff				     osm_subn_t * p_subn, osm_prtn_t * p,
187219820Sjeff				     uint8_t rate,
188219820Sjeff				     uint8_t mtu, uint8_t scope)
189219820Sjeff{
190219820Sjeff	ib_member_rec_t mc_rec;
191219820Sjeff	ib_net64_t comp_mask;
192219820Sjeff	ib_net16_t pkey;
193219820Sjeff	osm_mgrp_t *p_mgrp = NULL;
194219820Sjeff	osm_sa_t *p_sa = &p_subn->p_osm->sa;
195219820Sjeff	ib_api_status_t status = IB_SUCCESS;
196219820Sjeff	uint8_t hop_limit;
197219820Sjeff
198219820Sjeff	pkey = p->pkey | cl_hton16(0x8000);
199219820Sjeff	if (!scope)
200219820Sjeff		scope = OSM_DEFAULT_MGRP_SCOPE;
201219820Sjeff	hop_limit = (scope == IB_MC_SCOPE_LINK_LOCAL) ? 0 : IB_HOPLIMIT_MAX;
202219820Sjeff
203219820Sjeff	memset(&mc_rec, 0, sizeof(mc_rec));
204219820Sjeff
205219820Sjeff	mc_rec.mgid = osm_ipoib_mgid;	/* ipv4 broadcast group */
206219820Sjeff	memcpy(&mc_rec.mgid.raw[4], &pkey, sizeof(pkey));
207219820Sjeff
208219820Sjeff	mc_rec.qkey = CL_HTON32(0x0b1b);
209219820Sjeff	mc_rec.mtu = (mtu ? mtu : OSM_DEFAULT_MGRP_MTU) | (2 << 6);	/* 2048 Bytes */
210219820Sjeff	mc_rec.tclass = 0;
211219820Sjeff	mc_rec.pkey = pkey;
212219820Sjeff	mc_rec.rate = (rate ? rate : OSM_DEFAULT_MGRP_RATE) | (2 << 6);	/* 10Gb/sec */
213219820Sjeff	mc_rec.pkt_life = p_subn->opt.subnet_timeout;
214219820Sjeff	mc_rec.sl_flow_hop = ib_member_set_sl_flow_hop(p->sl, 0, hop_limit);
215219820Sjeff	/* Scope in MCMemberRecord (if present) needs to be consistent with MGID */
216219820Sjeff	mc_rec.scope_state = ib_member_set_scope_state(scope, IB_MC_REC_STATE_FULL_MEMBER);
217219820Sjeff	ib_mgid_set_scope(&mc_rec.mgid, scope);
218219820Sjeff
219219820Sjeff	/* don't update rate, mtu */
220219820Sjeff	comp_mask = IB_MCR_COMPMASK_MTU | IB_MCR_COMPMASK_MTU_SEL |
221219820Sjeff	    IB_MCR_COMPMASK_RATE | IB_MCR_COMPMASK_RATE_SEL;
222219820Sjeff	status = osm_mcmr_rcv_find_or_create_new_mgrp(p_sa, comp_mask, &mc_rec,
223219820Sjeff						      &p_mgrp);
224219820Sjeff	if (!p_mgrp || status != IB_SUCCESS)
225219820Sjeff		OSM_LOG(p_log, OSM_LOG_ERROR,
226219820Sjeff			"Failed to create MC group with pkey 0x%04x\n",
227219820Sjeff			cl_ntoh16(pkey));
228219820Sjeff	if (p_mgrp) {
229219820Sjeff		p_mgrp->well_known = TRUE;
230219820Sjeff		p->mlid = p_mgrp->mlid;
231219820Sjeff	}
232219820Sjeff
233219820Sjeff	/* workaround for TS */
234219820Sjeff	/* FIXME: remove this upon TS fixes */
235219820Sjeff	mc_rec.mgid = osm_ts_ipoib_mgid;
236219820Sjeff	memcpy(&mc_rec.mgid.raw[4], &pkey, sizeof(pkey));
237219820Sjeff	/* Scope in MCMemberRecord (if present) needs to be consistent with MGID */
238219820Sjeff	mc_rec.scope_state = ib_member_set_scope_state(scope, IB_MC_REC_STATE_FULL_MEMBER);
239219820Sjeff	ib_mgid_set_scope(&mc_rec.mgid, scope);
240219820Sjeff
241219820Sjeff	status = osm_mcmr_rcv_find_or_create_new_mgrp(p_sa, comp_mask, &mc_rec,
242219820Sjeff						      &p_mgrp);
243219820Sjeff	if (p_mgrp) {
244219820Sjeff		p_mgrp->well_known = TRUE;
245219820Sjeff		if (!p->mlid)
246219820Sjeff			p->mlid = p_mgrp->mlid;
247219820Sjeff	}
248219820Sjeff
249219820Sjeff	return status;
250219820Sjeff}
251219820Sjeff
252219820Sjeffstatic uint16_t __generate_pkey(osm_subn_t * p_subn)
253219820Sjeff{
254219820Sjeff	uint16_t pkey;
255219820Sjeff
256219820Sjeff	cl_qmap_t *m = &p_subn->prtn_pkey_tbl;
257219820Sjeff	while (global_pkey_counter < cl_ntoh16(IB_DEFAULT_PARTIAL_PKEY) - 1) {
258219820Sjeff		pkey = ++global_pkey_counter;
259219820Sjeff		pkey = cl_hton16(pkey);
260219820Sjeff		if (cl_qmap_get(m, pkey) == cl_qmap_end(m))
261219820Sjeff			return pkey;
262219820Sjeff	}
263219820Sjeff	return 0;
264219820Sjeff}
265219820Sjeff
266219820Sjeffosm_prtn_t *osm_prtn_find_by_name(osm_subn_t * p_subn, const char *name)
267219820Sjeff{
268219820Sjeff	cl_map_item_t *p_next;
269219820Sjeff	osm_prtn_t *p;
270219820Sjeff
271219820Sjeff	p_next = cl_qmap_head(&p_subn->prtn_pkey_tbl);
272219820Sjeff	while (p_next != cl_qmap_end(&p_subn->prtn_pkey_tbl)) {
273219820Sjeff		p = (osm_prtn_t *) p_next;
274219820Sjeff		p_next = cl_qmap_next(&p->map_item);
275219820Sjeff		if (!strncmp(p->name, name, sizeof(p->name)))
276219820Sjeff			return p;
277219820Sjeff	}
278219820Sjeff
279219820Sjeff	return NULL;
280219820Sjeff}
281219820Sjeff
282219820Sjeffosm_prtn_t *osm_prtn_make_new(osm_log_t * p_log, osm_subn_t * p_subn,
283219820Sjeff			      const char *name, uint16_t pkey)
284219820Sjeff{
285219820Sjeff	osm_prtn_t *p = NULL, *p_check;
286219820Sjeff
287219820Sjeff	pkey &= cl_hton16((uint16_t) ~ 0x8000);
288219820Sjeff
289219820Sjeff	if (!pkey) {
290219820Sjeff		if (name && (p = osm_prtn_find_by_name(p_subn, name)))
291219820Sjeff			return p;
292219820Sjeff		if (!(pkey = __generate_pkey(p_subn)))
293219820Sjeff			return NULL;
294219820Sjeff	}
295219820Sjeff
296219820Sjeff	p = osm_prtn_new(name, pkey);
297219820Sjeff	if (!p) {
298219820Sjeff		OSM_LOG(p_log, OSM_LOG_ERROR, "Unable to create"
299219820Sjeff			" partition \'%s\' (0x%04x)\n", name, cl_ntoh16(pkey));
300219820Sjeff		return NULL;
301219820Sjeff	}
302219820Sjeff
303219820Sjeff	p_check = (osm_prtn_t *) cl_qmap_insert(&p_subn->prtn_pkey_tbl,
304219820Sjeff						p->pkey, &p->map_item);
305219820Sjeff	if (p != p_check) {
306219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE, "Duplicated partition"
307219820Sjeff			" definition: \'%s\' (0x%04x) prev name \'%s\'"
308219820Sjeff			".  Will use it\n",
309219820Sjeff			name, cl_ntoh16(pkey), p_check->name);
310219820Sjeff		osm_prtn_delete(&p);
311219820Sjeff		p = p_check;
312219820Sjeff	}
313219820Sjeff
314219820Sjeff	return p;
315219820Sjeff}
316219820Sjeff
317219820Sjeffstatic ib_api_status_t osm_prtn_make_default(osm_log_t * const p_log,
318219820Sjeff					     osm_subn_t * const p_subn,
319219820Sjeff					     boolean_t no_config)
320219820Sjeff{
321219820Sjeff	ib_api_status_t status = IB_UNKNOWN_ERROR;
322219820Sjeff	osm_prtn_t *p;
323219820Sjeff
324219820Sjeff	p = osm_prtn_make_new(p_log, p_subn, "Default",
325219820Sjeff			      IB_DEFAULT_PARTIAL_PKEY);
326219820Sjeff	if (!p)
327219820Sjeff		goto _err;
328219820Sjeff	status = osm_prtn_add_all(p_log, p_subn, p, no_config);
329219820Sjeff	if (status != IB_SUCCESS)
330219820Sjeff		goto _err;
331219820Sjeff	cl_map_remove(&p->part_guid_tbl, p_subn->sm_port_guid);
332219820Sjeff	status =
333219820Sjeff	    osm_prtn_add_port(p_log, p_subn, p, p_subn->sm_port_guid, TRUE);
334219820Sjeff
335219820Sjeff	if (no_config)
336219820Sjeff		osm_prtn_add_mcgroup(p_log, p_subn, p, 0, 0, 0);
337219820Sjeff
338219820Sjeff_err:
339219820Sjeff	return status;
340219820Sjeff}
341219820Sjeff
342219820Sjeffib_api_status_t osm_prtn_make_partitions(osm_log_t * const p_log,
343219820Sjeff					 osm_subn_t * const p_subn)
344219820Sjeff{
345219820Sjeff	struct stat statbuf;
346219820Sjeff	const char *file_name;
347219820Sjeff	boolean_t is_config = TRUE;
348219820Sjeff	ib_api_status_t status = IB_SUCCESS;
349219820Sjeff	cl_map_item_t *p_next;
350219820Sjeff	osm_prtn_t *p;
351219820Sjeff
352219820Sjeff	file_name = p_subn->opt.partition_config_file ?
353219820Sjeff	    p_subn->opt.partition_config_file : OSM_DEFAULT_PARTITION_CONFIG_FILE;
354219820Sjeff	if (stat(file_name, &statbuf))
355219820Sjeff		is_config = FALSE;
356219820Sjeff
357219820Sjeff	/* clean up current port maps */
358219820Sjeff	p_next = cl_qmap_head(&p_subn->prtn_pkey_tbl);
359219820Sjeff	while (p_next != cl_qmap_end(&p_subn->prtn_pkey_tbl)) {
360219820Sjeff		p = (osm_prtn_t *) p_next;
361219820Sjeff		p_next = cl_qmap_next(&p->map_item);
362219820Sjeff		cl_map_remove_all(&p->part_guid_tbl);
363219820Sjeff		cl_map_remove_all(&p->full_guid_tbl);
364219820Sjeff	}
365219820Sjeff
366219820Sjeff	global_pkey_counter = 0;
367219820Sjeff
368219820Sjeff	status = osm_prtn_make_default(p_log, p_subn, !is_config);
369219820Sjeff	if (status != IB_SUCCESS)
370219820Sjeff		goto _err;
371219820Sjeff
372219820Sjeff	if (is_config && osm_prtn_config_parse_file(p_log, p_subn, file_name)) {
373219820Sjeff		OSM_LOG(p_log, OSM_LOG_VERBOSE, "Partition configuration "
374219820Sjeff			"was not fully processed\n");
375219820Sjeff	}
376219820Sjeff
377219820Sjeff	/* and now clean up empty partitions */
378219820Sjeff	p_next = cl_qmap_head(&p_subn->prtn_pkey_tbl);
379219820Sjeff	while (p_next != cl_qmap_end(&p_subn->prtn_pkey_tbl)) {
380219820Sjeff		p = (osm_prtn_t *) p_next;
381219820Sjeff		p_next = cl_qmap_next(&p->map_item);
382219820Sjeff		if (cl_map_count(&p->part_guid_tbl) == 0 &&
383219820Sjeff		    cl_map_count(&p->full_guid_tbl) == 0) {
384219820Sjeff			cl_qmap_remove_item(&p_subn->prtn_pkey_tbl,
385219820Sjeff					    (cl_map_item_t *) p);
386219820Sjeff			osm_prtn_delete(&p);
387219820Sjeff		}
388219820Sjeff	}
389219820Sjeff
390219820Sjeff_err:
391219820Sjeff	return status;
392219820Sjeff}
393