1/*
2 * Copyright (c) 2004-2006 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_mcast_tbl_t.
39 * This object represents an multicast forwarding table.
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 <string.h>
49#include <complib/cl_math.h>
50#include <iba/ib_types.h>
51#include <opensm/osm_mcast_tbl.h>
52
53/**********************************************************************
54 **********************************************************************/
55ib_api_status_t
56osm_mcast_tbl_init(IN osm_mcast_tbl_t * const p_tbl,
57		   IN uint8_t const num_ports, IN uint16_t const capacity)
58{
59	CL_ASSERT(p_tbl);
60	CL_ASSERT(num_ports);
61
62	memset(p_tbl, 0, sizeof(*p_tbl));
63
64	p_tbl->max_block_in_use = -1;
65
66	if (capacity == 0) {
67		/*
68		   This switch apparently doesn't support multicast.
69		   Everything is initialized to zero already, so return.
70		 */
71		return (IB_SUCCESS);
72	}
73
74	p_tbl->num_entries = capacity;
75	p_tbl->num_ports = num_ports;
76	p_tbl->max_position =
77	    (uint8_t) ((ROUNDUP(num_ports, IB_MCAST_MASK_SIZE) /
78			IB_MCAST_MASK_SIZE) - 1);
79
80	p_tbl->max_block = (uint16_t) ((ROUNDUP(p_tbl->num_entries,
81						IB_MCAST_BLOCK_SIZE) /
82					IB_MCAST_BLOCK_SIZE) - 1);
83
84	p_tbl->max_mlid_ho = (uint16_t) (IB_LID_MCAST_START_HO + capacity - 1);
85
86	/*
87	   The number of bytes needed in the mask table is:
88	   The (maximum bit mask 'position' + 1) times the
89	   number of bytes in each bit mask times the
90	   number of MLIDs supported by the table.
91
92	   We must always allocate the array with the maximum position
93	   since it is (and must be) defined that way the table structure
94	   in order to create a pointer to a two dimensional array.
95	 */
96	p_tbl->p_mask_tbl = malloc(p_tbl->num_entries *
97				   (IB_MCAST_POSITION_MAX +
98				    1) * IB_MCAST_MASK_SIZE / 8);
99
100	if (p_tbl->p_mask_tbl == NULL)
101		return (IB_INSUFFICIENT_MEMORY);
102
103	memset(p_tbl->p_mask_tbl, 0,
104	       p_tbl->num_entries * (IB_MCAST_POSITION_MAX +
105				     1) * IB_MCAST_MASK_SIZE / 8);
106	return (IB_SUCCESS);
107}
108
109/**********************************************************************
110 **********************************************************************/
111void osm_mcast_tbl_destroy(IN osm_mcast_tbl_t * const p_tbl)
112{
113	free(p_tbl->p_mask_tbl);
114}
115
116/**********************************************************************
117 **********************************************************************/
118void
119osm_mcast_tbl_set(IN osm_mcast_tbl_t * const p_tbl,
120		  IN const uint16_t mlid_ho, IN const uint8_t port)
121{
122	uintn_t mlid_offset;
123	uintn_t mask_offset;
124	uintn_t bit_mask;
125	int16_t block_num;
126
127	CL_ASSERT(p_tbl);
128	CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
129	CL_ASSERT(mlid_ho <= p_tbl->max_mlid_ho);
130	CL_ASSERT(p_tbl->p_mask_tbl);
131
132	mlid_offset = mlid_ho - IB_LID_MCAST_START_HO;
133	mask_offset = port / IB_MCAST_MASK_SIZE;
134	bit_mask = cl_ntoh16((uint16_t) (1 << (port % IB_MCAST_MASK_SIZE)));
135	(*p_tbl->p_mask_tbl)[mlid_offset][mask_offset] |= bit_mask;
136
137	block_num = (int16_t) (mlid_offset / IB_MCAST_BLOCK_SIZE);
138
139	if (block_num > p_tbl->max_block_in_use)
140		p_tbl->max_block_in_use = (uint16_t) block_num;
141}
142
143/**********************************************************************
144 **********************************************************************/
145boolean_t
146osm_mcast_tbl_is_port(IN const osm_mcast_tbl_t * const p_tbl,
147		      IN const uint16_t mlid_ho, IN const uint8_t port_num)
148{
149	uintn_t mlid_offset;
150	uintn_t mask_offset;
151	uintn_t bit_mask;
152
153	CL_ASSERT(p_tbl);
154
155	if (p_tbl->p_mask_tbl) {
156		CL_ASSERT(port_num <=
157			  (p_tbl->max_position + 1) * IB_MCAST_MASK_SIZE);
158		CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
159		CL_ASSERT(mlid_ho <= p_tbl->max_mlid_ho);
160
161		mlid_offset = mlid_ho - IB_LID_MCAST_START_HO;
162		mask_offset = port_num / IB_MCAST_MASK_SIZE;
163		bit_mask = cl_ntoh16((uint16_t)
164				     (1 << (port_num % IB_MCAST_MASK_SIZE)));
165		return (((*p_tbl->
166			  p_mask_tbl)[mlid_offset][mask_offset] & bit_mask) ==
167			bit_mask);
168	}
169
170	return (FALSE);
171}
172
173/**********************************************************************
174 **********************************************************************/
175boolean_t
176osm_mcast_tbl_is_any_port(IN const osm_mcast_tbl_t * const p_tbl,
177			  IN const uint16_t mlid_ho)
178{
179	uintn_t mlid_offset;
180	uint8_t position;
181	uint16_t result = 0;
182
183	CL_ASSERT(p_tbl);
184
185	if (p_tbl->p_mask_tbl) {
186		CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
187		CL_ASSERT(mlid_ho <= p_tbl->max_mlid_ho);
188
189		mlid_offset = mlid_ho - IB_LID_MCAST_START_HO;
190
191		for (position = 0; position <= p_tbl->max_position; position++)
192			result |= (*p_tbl->p_mask_tbl)[mlid_offset][position];
193	}
194
195	return (result != 0);
196}
197
198/**********************************************************************
199 **********************************************************************/
200ib_api_status_t
201osm_mcast_tbl_set_block(IN osm_mcast_tbl_t * const p_tbl,
202			IN const ib_net16_t * const p_block,
203			IN const int16_t block_num, IN const uint8_t position)
204{
205	uint32_t i;
206	uint16_t mlid_start_ho;
207
208	CL_ASSERT(p_tbl);
209	CL_ASSERT(p_block);
210
211	if (block_num > p_tbl->max_block)
212		return (IB_INVALID_PARAMETER);
213
214	if (position > p_tbl->max_position)
215		return (IB_INVALID_PARAMETER);
216
217	mlid_start_ho = (uint16_t) (block_num * IB_MCAST_BLOCK_SIZE);
218
219	if (mlid_start_ho + IB_MCAST_BLOCK_SIZE - 1 > p_tbl->max_mlid_ho)
220		return (IB_INVALID_PARAMETER);
221
222	for (i = 0; i < IB_MCAST_BLOCK_SIZE; i++)
223		(*p_tbl->p_mask_tbl)[mlid_start_ho + i][position] = p_block[i];
224
225	if (block_num > p_tbl->max_block_in_use)
226		p_tbl->max_block_in_use = (uint16_t) block_num;
227
228	return (IB_SUCCESS);
229}
230
231/**********************************************************************
232 **********************************************************************/
233void
234osm_mcast_tbl_clear_mlid(IN osm_mcast_tbl_t * const p_tbl,
235			 IN const uint16_t mlid_ho)
236{
237	uint8_t i;
238	uintn_t mlid_offset;
239
240	CL_ASSERT(p_tbl);
241	CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
242
243	if (p_tbl->p_mask_tbl && (mlid_ho <= p_tbl->max_mlid_ho)) {
244		mlid_offset = mlid_ho - IB_LID_MCAST_START_HO;
245		for (i = 0; i <= p_tbl->max_position; i++)
246			(*p_tbl->p_mask_tbl)[mlid_offset][i] = 0;
247	}
248}
249
250/**********************************************************************
251 **********************************************************************/
252boolean_t
253osm_mcast_tbl_get_block(IN osm_mcast_tbl_t * const p_tbl,
254			IN int16_t const block_num,
255			IN uint8_t const position,
256			OUT ib_net16_t * const p_block)
257{
258	uint32_t i;
259	uint16_t mlid_start_ho;
260
261	CL_ASSERT(p_tbl);
262	CL_ASSERT(p_block);
263
264	if (block_num > p_tbl->max_block_in_use)
265		return (FALSE);
266
267	if (position > p_tbl->max_position) {
268		/*
269		   Caller shouldn't do this for efficiency's sake...
270		 */
271		memset(p_block, 0, IB_SMP_DATA_SIZE);
272		return (TRUE);
273	}
274
275	mlid_start_ho = (uint16_t) (block_num * IB_MCAST_BLOCK_SIZE);
276
277	if (mlid_start_ho + IB_MCAST_BLOCK_SIZE - 1 > p_tbl->max_mlid_ho)
278		return (IB_INVALID_PARAMETER);
279
280	for (i = 0; i < IB_MCAST_BLOCK_SIZE; i++)
281		p_block[i] = (*p_tbl->p_mask_tbl)[mlid_start_ho + i][position];
282
283	return (TRUE);
284}
285