1/*
2 * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved.
3 * Copyright (c) 2002-2006 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 opensm pkey manipulation functions.
39 */
40
41#if HAVE_CONFIG_H
42#  include <config.h>
43#endif				/* HAVE_CONFIG_H */
44
45#include <stdlib.h>
46#include <stdio.h>
47#include <string.h>
48#include <complib/cl_debug.h>
49#include <iba/ib_types.h>
50#include <opensm/osm_pkey.h>
51#include <opensm/osm_log.h>
52#include <opensm/osm_port.h>
53#include <opensm/osm_node.h>
54#include <opensm/osm_switch.h>
55#include <opensm/osm_helper.h>
56
57/**********************************************************************
58 **********************************************************************/
59void osm_pkey_tbl_construct(IN osm_pkey_tbl_t * p_pkey_tbl)
60{
61	cl_ptr_vector_construct(&p_pkey_tbl->blocks);
62	cl_ptr_vector_construct(&p_pkey_tbl->new_blocks);
63	cl_map_construct(&p_pkey_tbl->keys);
64}
65
66/**********************************************************************
67 **********************************************************************/
68void osm_pkey_tbl_destroy(IN osm_pkey_tbl_t * p_pkey_tbl)
69{
70	ib_pkey_table_t *p_block;
71	uint16_t num_blocks, i;
72
73	num_blocks = (uint16_t) (cl_ptr_vector_get_size(&p_pkey_tbl->blocks));
74	for (i = 0; i < num_blocks; i++)
75		if ((p_block = cl_ptr_vector_get(&p_pkey_tbl->blocks, i)))
76			free(p_block);
77	cl_ptr_vector_destroy(&p_pkey_tbl->blocks);
78
79	num_blocks =
80	    (uint16_t) (cl_ptr_vector_get_size(&p_pkey_tbl->new_blocks));
81	for (i = 0; i < num_blocks; i++)
82		if ((p_block = cl_ptr_vector_get(&p_pkey_tbl->new_blocks, i)))
83			free(p_block);
84	cl_ptr_vector_destroy(&p_pkey_tbl->new_blocks);
85
86	cl_map_remove_all(&p_pkey_tbl->keys);
87	cl_map_destroy(&p_pkey_tbl->keys);
88}
89
90/**********************************************************************
91 **********************************************************************/
92ib_api_status_t osm_pkey_tbl_init(IN osm_pkey_tbl_t * p_pkey_tbl)
93{
94	cl_ptr_vector_init(&p_pkey_tbl->blocks, 0, 1);
95	cl_ptr_vector_init(&p_pkey_tbl->new_blocks, 0, 1);
96	cl_map_init(&p_pkey_tbl->keys, 1);
97	cl_qlist_init(&p_pkey_tbl->pending);
98	p_pkey_tbl->used_blocks = 0;
99	p_pkey_tbl->max_blocks = 0;
100	return (IB_SUCCESS);
101}
102
103/**********************************************************************
104 **********************************************************************/
105void osm_pkey_tbl_init_new_blocks(IN const osm_pkey_tbl_t * p_pkey_tbl)
106{
107	ib_pkey_table_t *p_block;
108	size_t b, num_blocks = cl_ptr_vector_get_size(&p_pkey_tbl->new_blocks);
109
110	for (b = 0; b < num_blocks; b++)
111		if ((p_block = cl_ptr_vector_get(&p_pkey_tbl->new_blocks, b)))
112			memset(p_block, 0, sizeof(*p_block));
113}
114
115/**********************************************************************
116 **********************************************************************/
117void osm_pkey_tbl_cleanup_pending(IN osm_pkey_tbl_t * p_pkey_tbl)
118{
119	cl_list_item_t *p_item;
120
121	p_item = cl_qlist_remove_head(&p_pkey_tbl->pending);
122	while (p_item != cl_qlist_end(&p_pkey_tbl->pending)) {
123		free((osm_pending_pkey_t *) p_item);
124	}
125}
126
127/**********************************************************************
128 **********************************************************************/
129ib_api_status_t
130osm_pkey_tbl_set(IN osm_pkey_tbl_t * p_pkey_tbl,
131		 IN uint16_t block, IN ib_pkey_table_t * p_tbl)
132{
133	uint16_t b, i;
134	ib_pkey_table_t *p_pkey_block;
135	uint16_t *p_prev_pkey;
136	ib_net16_t pkey;
137
138	/* make sure the block is allocated */
139	if (cl_ptr_vector_get_size(&p_pkey_tbl->blocks) > block)
140		p_pkey_block =
141		    (ib_pkey_table_t *) cl_ptr_vector_get(&p_pkey_tbl->blocks,
142							  block);
143	else
144		p_pkey_block = NULL;
145
146	if (!p_pkey_block) {
147		p_pkey_block =
148		    (ib_pkey_table_t *) malloc(sizeof(ib_pkey_table_t));
149		if (!p_pkey_block)
150			return (IB_ERROR);
151		memset(p_pkey_block, 0, sizeof(ib_pkey_table_t));
152		cl_ptr_vector_set(&p_pkey_tbl->blocks, block, p_pkey_block);
153	}
154
155	/* sets the block values */
156	memcpy(p_pkey_block, p_tbl, sizeof(ib_pkey_table_t));
157
158	/*
159	   NOTE: as the spec does not require uniqueness of PKeys in
160	   tables there is no other way but to refresh the entire keys map.
161
162	   Moreover, if the same key exists but with full membership it should
163	   have precedence on the key with limited membership !
164	 */
165	cl_map_remove_all(&p_pkey_tbl->keys);
166
167	for (b = 0; b < cl_ptr_vector_get_size(&p_pkey_tbl->blocks); b++) {
168
169		p_pkey_block = cl_ptr_vector_get(&p_pkey_tbl->blocks, b);
170		if (!p_pkey_block)
171			continue;
172
173		for (i = 0; i < IB_NUM_PKEY_ELEMENTS_IN_BLOCK; i++) {
174			pkey = p_pkey_block->pkey_entry[i];
175			if (ib_pkey_is_invalid(pkey))
176				continue;
177
178			/*
179			   ignore the PKey Full Member bit in the key but store
180			   the pointer to the table element as the map value
181			 */
182			p_prev_pkey =
183			    cl_map_get(&p_pkey_tbl->keys,
184				       ib_pkey_get_base(pkey));
185
186			/* we only insert if no previous or it is not full member */
187			if ((p_prev_pkey == NULL) ||
188			    (cl_ntoh16(*p_prev_pkey) < cl_ntoh16(pkey)))
189				cl_map_insert(&p_pkey_tbl->keys,
190					      ib_pkey_get_base(pkey),
191					      &(p_pkey_block->pkey_entry[i])
192				    );
193		}
194	}
195	return (IB_SUCCESS);
196}
197
198/**********************************************************************
199 **********************************************************************/
200/*
201  Store the given pkey in the "new" blocks array.
202  Also, make sure the regular block exists.
203*/
204ib_api_status_t
205osm_pkey_tbl_set_new_entry(IN osm_pkey_tbl_t * p_pkey_tbl,
206			   IN uint16_t block_idx,
207			   IN uint8_t pkey_idx, IN uint16_t pkey)
208{
209	ib_pkey_table_t *p_block;
210
211	if (!(p_block = osm_pkey_tbl_new_block_get(p_pkey_tbl, block_idx))) {
212		p_block = (ib_pkey_table_t *) malloc(sizeof(ib_pkey_table_t));
213		if (!p_block)
214			return (IB_ERROR);
215		memset(p_block, 0, sizeof(ib_pkey_table_t));
216		cl_ptr_vector_set(&p_pkey_tbl->new_blocks, block_idx, p_block);
217	}
218
219	p_block->pkey_entry[pkey_idx] = pkey;
220	if (p_pkey_tbl->used_blocks <= block_idx)
221		p_pkey_tbl->used_blocks = block_idx + 1;
222
223	return (IB_SUCCESS);
224}
225
226/**********************************************************************
227 **********************************************************************/
228boolean_t
229osm_pkey_find_next_free_entry(IN osm_pkey_tbl_t * p_pkey_tbl,
230			      OUT uint16_t * p_block_idx,
231			      OUT uint8_t * p_pkey_idx)
232{
233	ib_pkey_table_t *p_new_block;
234
235	CL_ASSERT(p_block_idx);
236	CL_ASSERT(p_pkey_idx);
237
238	while (*p_block_idx < p_pkey_tbl->max_blocks) {
239		if (*p_pkey_idx > IB_NUM_PKEY_ELEMENTS_IN_BLOCK - 1) {
240			*p_pkey_idx = 0;
241			(*p_block_idx)++;
242			if (*p_block_idx >= p_pkey_tbl->max_blocks)
243				return FALSE;
244		}
245
246		p_new_block =
247		    osm_pkey_tbl_new_block_get(p_pkey_tbl, *p_block_idx);
248
249		if (!p_new_block ||
250		    ib_pkey_is_invalid(p_new_block->pkey_entry[*p_pkey_idx]))
251			return TRUE;
252		else
253			(*p_pkey_idx)++;
254	}
255	return FALSE;
256}
257
258/**********************************************************************
259 **********************************************************************/
260ib_api_status_t
261osm_pkey_tbl_get_block_and_idx(IN osm_pkey_tbl_t * p_pkey_tbl,
262			       IN uint16_t * p_pkey,
263			       OUT uint16_t * p_block_idx,
264			       OUT uint8_t * p_pkey_idx)
265{
266	uint16_t num_of_blocks;
267	uint16_t block_index;
268	ib_pkey_table_t *block;
269
270	CL_ASSERT(p_block_idx != NULL);
271	CL_ASSERT(p_pkey_idx != NULL);
272
273	num_of_blocks = (uint16_t) cl_ptr_vector_get_size(&p_pkey_tbl->blocks);
274	for (block_index = 0; block_index < num_of_blocks; block_index++) {
275		block = osm_pkey_tbl_block_get(p_pkey_tbl, block_index);
276		if ((block->pkey_entry <= p_pkey) &&
277		    (p_pkey <
278		     block->pkey_entry + IB_NUM_PKEY_ELEMENTS_IN_BLOCK)) {
279			*p_block_idx = block_index;
280			*p_pkey_idx = (uint8_t) (p_pkey - block->pkey_entry);
281			return (IB_SUCCESS);
282		}
283	}
284	return (IB_NOT_FOUND);
285}
286
287/**********************************************************************
288 **********************************************************************/
289static boolean_t
290__osm_match_pkey(IN const ib_net16_t * pkey1, IN const ib_net16_t * pkey2)
291{
292
293	/* if both pkeys are not full member - this is not a match */
294	if (!(ib_pkey_is_full_member(*pkey1) || ib_pkey_is_full_member(*pkey2)))
295		return (FALSE);
296
297	/* compare if the bases are the same. if they are - then
298	   this is a match */
299	if (ib_pkey_get_base(*pkey1) != ib_pkey_get_base(*pkey2))
300		return (FALSE);
301
302	return (TRUE);
303}
304
305/**********************************************************************
306 **********************************************************************/
307boolean_t
308osm_physp_share_this_pkey(IN const osm_physp_t * const p_physp1,
309			  IN const osm_physp_t * const p_physp2,
310			  IN const ib_net16_t pkey)
311{
312	ib_net16_t *pkey1, *pkey2;
313
314	pkey1 = cl_map_get(&(osm_physp_get_pkey_tbl(p_physp1))->keys,
315			   ib_pkey_get_base(pkey));
316	pkey2 = cl_map_get(&(osm_physp_get_pkey_tbl(p_physp2))->keys,
317			   ib_pkey_get_base(pkey));
318	return (pkey1 && pkey2 && __osm_match_pkey(pkey1, pkey2));
319}
320
321/**********************************************************************
322 **********************************************************************/
323ib_net16_t
324osm_physp_find_common_pkey(IN const osm_physp_t * const p_physp1,
325			   IN const osm_physp_t * const p_physp2)
326{
327	ib_net16_t *pkey1, *pkey2;
328	uint64_t pkey1_base, pkey2_base;
329	const osm_pkey_tbl_t *pkey_tbl1, *pkey_tbl2;
330	cl_map_iterator_t map_iter1, map_iter2;
331
332	pkey_tbl1 = osm_physp_get_pkey_tbl(p_physp1);
333	pkey_tbl2 = osm_physp_get_pkey_tbl(p_physp2);
334
335	map_iter1 = cl_map_head(&pkey_tbl1->keys);
336	map_iter2 = cl_map_head(&pkey_tbl2->keys);
337
338	/* we rely on the fact the map are sorted by pkey */
339	while ((map_iter1 != cl_map_end(&pkey_tbl1->keys)) &&
340	       (map_iter2 != cl_map_end(&pkey_tbl2->keys))) {
341		pkey1 = (ib_net16_t *) cl_map_obj(map_iter1);
342		pkey2 = (ib_net16_t *) cl_map_obj(map_iter2);
343
344		if (__osm_match_pkey(pkey1, pkey2))
345			return *pkey1;
346
347		/* advance the lower value if they are not equal */
348		pkey1_base = cl_map_key(map_iter1);
349		pkey2_base = cl_map_key(map_iter2);
350		if (pkey2_base == pkey1_base) {
351			map_iter1 = cl_map_next(map_iter1);
352			map_iter2 = cl_map_next(map_iter2);
353		} else if (pkey2_base < pkey1_base)
354			map_iter2 = cl_map_next(map_iter2);
355		else
356			map_iter1 = cl_map_next(map_iter1);
357	}
358
359	return 0;
360}
361
362/**********************************************************************
363 **********************************************************************/
364boolean_t
365osm_physp_share_pkey(IN osm_log_t * p_log,
366		     IN const osm_physp_t * const p_physp_1,
367		     IN const osm_physp_t * const p_physp_2)
368{
369	const osm_pkey_tbl_t *pkey_tbl1, *pkey_tbl2;
370
371	if (p_physp_1 == p_physp_2)
372		return TRUE;
373
374	pkey_tbl1 = osm_physp_get_pkey_tbl(p_physp_1);
375	pkey_tbl2 = osm_physp_get_pkey_tbl(p_physp_2);
376
377	/*
378	   The spec: 10.9.2 does not require each phys port to have PKey Table.
379	   So actually if it does not, we need to use the default port instead.
380
381	   HACK: meanwhile we will ignore the check
382	 */
383	if (cl_is_map_empty(&pkey_tbl1->keys)
384	    || cl_is_map_empty(&pkey_tbl2->keys))
385		return TRUE;
386
387	return
388	    !ib_pkey_is_invalid(osm_physp_find_common_pkey
389				(p_physp_1, p_physp_2));
390}
391
392/**********************************************************************
393 **********************************************************************/
394boolean_t
395osm_port_share_pkey(IN osm_log_t * p_log,
396		    IN const osm_port_t * const p_port_1,
397		    IN const osm_port_t * const p_port_2)
398{
399
400	osm_physp_t *p_physp1, *p_physp2;
401	boolean_t ret;
402
403	OSM_LOG_ENTER(p_log);
404
405	if (!p_port_1 || !p_port_2) {
406		ret = FALSE;
407		goto Exit;
408	}
409
410	p_physp1 = p_port_1->p_physp;
411	p_physp2 = p_port_2->p_physp;
412
413	if (!p_physp1 || !p_physp2) {
414		ret = FALSE;
415		goto Exit;
416	}
417
418	ret = osm_physp_share_pkey(p_log, p_physp1, p_physp2);
419
420Exit:
421	OSM_LOG_EXIT(p_log);
422	return ret;
423}
424
425/**********************************************************************
426 **********************************************************************/
427boolean_t
428osm_physp_has_pkey(IN osm_log_t * p_log,
429		   IN const ib_net16_t pkey,
430		   IN const osm_physp_t * const p_physp)
431{
432
433	ib_net16_t *p_pkey, pkey_base;
434	const osm_pkey_tbl_t *pkey_tbl;
435	boolean_t res = FALSE;
436
437	OSM_LOG_ENTER(p_log);
438
439	OSM_LOG(p_log, OSM_LOG_DEBUG,
440		"Search for PKey: 0x%04x\n", cl_ntoh16(pkey));
441
442	/* if the pkey given is an invalid pkey - return TRUE. */
443	if (ib_pkey_is_invalid(pkey)) {
444		OSM_LOG(p_log, OSM_LOG_DEBUG,
445			"Given invalid PKey - we treat it loosely and allow it\n");
446		res = TRUE;
447		goto Exit;
448	}
449
450	pkey_base = ib_pkey_get_base(pkey);
451
452	pkey_tbl = osm_physp_get_pkey_tbl(p_physp);
453
454	p_pkey = cl_map_get(&pkey_tbl->keys, pkey_base);
455	if (p_pkey) {
456		res = TRUE;
457		OSM_LOG(p_log, OSM_LOG_DEBUG,
458			"PKey 0x%04x was found\n", cl_ntoh16(pkey));
459	} else {
460		OSM_LOG(p_log, OSM_LOG_DEBUG,
461			"PKey 0x%04x was not found\n", cl_ntoh16(pkey));
462	}
463
464Exit:
465	OSM_LOG_EXIT(p_log);
466	return res;
467}
468