1 /*
2  * Copyright (c) 2004-2009 Voltaire, Inc. All rights reserved.
3  * Copyright (c) 2002-2009 Mellanox Technologies LTD. All rights reserved.
4  * Copyright (c) 1996-2003 Intel Corporation. All rights reserved.
5  * Copyright (c) 2009 HNR Consulting. All rights reserved.
6  *
7  * This software is available to you under a choice of one of two
8  * licenses.  You may choose to be licensed under the terms of the GNU
9  * General Public License (GPL) Version 2, available from the file
10  * COPYING in the main directory of this source tree, or the
11  * OpenIB.org BSD license below:
12  *
13  *     Redistribution and use in source and binary forms, with or
14  *     without modification, are permitted provided that the following
15  *     conditions are met:
16  *
17  *      - Redistributions of source code must retain the above
18  *        copyright notice, this list of conditions and the following
19  *        disclaimer.
20  *
21  *      - Redistributions in binary form must reproduce the above
22  *        copyright notice, this list of conditions and the following
23  *        disclaimer in the documentation and/or other materials
24  *        provided with the distribution.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
30  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
31  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
32  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33  * SOFTWARE.
34  *
35  */
36 
37 /*
38  * Abstract:
39  *    Implementation of osm_mcast_tbl_t.
40  * This object represents a multicast forwarding table.
41  * This object is part of the opensm family of objects.
42  */
43 
44 #if HAVE_CONFIG_H
45 #  include <config.h>
46 #endif				/* HAVE_CONFIG_H */
47 
48 #include <stdlib.h>
49 #include <string.h>
50 #include <complib/cl_math.h>
51 #include <iba/ib_types.h>
52 #include <opensm/osm_file_ids.h>
53 #define FILE_ID OSM_FILE_MCAST_TBL_C
54 #include <opensm/osm_mcast_tbl.h>
55 
56 void osm_mcast_tbl_init(IN osm_mcast_tbl_t * p_tbl, IN uint8_t num_ports,
57 			IN uint16_t 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;
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 
85 void osm_mcast_tbl_destroy(IN osm_mcast_tbl_t * p_tbl)
86 {
87 	free(p_tbl->p_mask_tbl);
88 }
89 
90 void osm_mcast_tbl_set(IN osm_mcast_tbl_t * p_tbl, IN uint16_t mlid_ho,
91 		       IN uint8_t port)
92 {
93 	unsigned mlid_offset, mask_offset, bit_mask;
94 	int16_t block_num;
95 
96 	CL_ASSERT(p_tbl && p_tbl->p_mask_tbl);
97 	CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
98 	CL_ASSERT(mlid_ho <= p_tbl->max_mlid_ho);
99 
100 	mlid_offset = mlid_ho - IB_LID_MCAST_START_HO;
101 	mask_offset = port / IB_MCAST_MASK_SIZE;
102 	bit_mask = cl_ntoh16((uint16_t) (1 << (port % IB_MCAST_MASK_SIZE)));
103 	(*p_tbl->p_mask_tbl)[mlid_offset][mask_offset] |= bit_mask;
104 
105 	block_num = (int16_t) (mlid_offset / IB_MCAST_BLOCK_SIZE);
106 
107 	if (block_num > p_tbl->max_block_in_use)
108 		p_tbl->max_block_in_use = (uint16_t) block_num;
109 }
110 
111 int osm_mcast_tbl_realloc(IN osm_mcast_tbl_t * p_tbl, IN unsigned mlid_offset)
112 {
113 	size_t mft_depth, size;
114 	uint16_t (*p_mask_tbl)[][IB_MCAST_POSITION_MAX + 1];
115 
116 	if (mlid_offset < p_tbl->mft_depth)
117 		goto done;
118 
119 	/*
120 	   The number of bytes needed in the mask table is:
121 	   The (maximum bit mask 'position' + 1) times the
122 	   number of bytes in each bit mask times the
123 	   number of MLIDs supported by the table.
124 
125 	   We must always allocate the array with the maximum position
126 	   since it is (and must be) defined that way the table structure
127 	   in order to create a pointer to a two dimensional array.
128 	 */
129 	mft_depth = (mlid_offset / IB_MCAST_BLOCK_SIZE + 1) * IB_MCAST_BLOCK_SIZE;
130 	size = mft_depth * (IB_MCAST_POSITION_MAX + 1) * IB_MCAST_MASK_SIZE / 8;
131 	p_mask_tbl = realloc(p_tbl->p_mask_tbl, size);
132 	if (!p_mask_tbl)
133 		return -1;
134 	memset((uint8_t *)p_mask_tbl + p_tbl->mft_depth * (IB_MCAST_POSITION_MAX + 1) * IB_MCAST_MASK_SIZE / 8,
135 	       0,
136 	       size - p_tbl->mft_depth * (IB_MCAST_POSITION_MAX + 1) * IB_MCAST_MASK_SIZE / 8);
137 	p_tbl->p_mask_tbl = p_mask_tbl;
138 	p_tbl->mft_depth = mft_depth;
139 done:
140 	p_tbl->max_mlid_ho = mlid_offset + IB_LID_MCAST_START_HO;
141 	return 0;
142 }
143 
144 boolean_t osm_mcast_tbl_is_port(IN const osm_mcast_tbl_t * p_tbl,
145 				IN uint16_t mlid_ho, IN uint8_t port_num)
146 {
147 	unsigned mlid_offset, mask_offset, bit_mask;
148 
149 	CL_ASSERT(p_tbl);
150 
151 	if (p_tbl->p_mask_tbl) {
152 		CL_ASSERT(port_num <=
153 			  (p_tbl->max_position + 1) * IB_MCAST_MASK_SIZE);
154 		CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
155 		CL_ASSERT(mlid_ho <= p_tbl->max_mlid_ho);
156 
157 		mlid_offset = mlid_ho - IB_LID_MCAST_START_HO;
158 		mask_offset = port_num / IB_MCAST_MASK_SIZE;
159 		bit_mask = cl_ntoh16((uint16_t)
160 				     (1 << (port_num % IB_MCAST_MASK_SIZE)));
161 		return (((*p_tbl->
162 			  p_mask_tbl)[mlid_offset][mask_offset] & bit_mask) ==
163 			bit_mask);
164 	}
165 
166 	return FALSE;
167 }
168 
169 boolean_t osm_mcast_tbl_is_any_port(IN const osm_mcast_tbl_t * p_tbl,
170 				    IN uint16_t mlid_ho)
171 {
172 	unsigned mlid_offset;
173 	uint8_t position;
174 	uint16_t result = 0;
175 
176 	CL_ASSERT(p_tbl);
177 
178 	if (p_tbl->p_mask_tbl) {
179 		CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
180 		CL_ASSERT(mlid_ho <= p_tbl->max_mlid_ho);
181 
182 		mlid_offset = mlid_ho - IB_LID_MCAST_START_HO;
183 
184 		for (position = 0; position <= p_tbl->max_position; position++)
185 			result |= (*p_tbl->p_mask_tbl)[mlid_offset][position];
186 	}
187 
188 	return (result != 0);
189 }
190 
191 ib_api_status_t osm_mcast_tbl_set_block(IN osm_mcast_tbl_t * p_tbl,
192 					IN const ib_net16_t * p_block,
193 					IN int16_t block_num,
194 					IN uint8_t position)
195 {
196 	uint32_t i;
197 	uint16_t mlid_start_ho;
198 
199 	CL_ASSERT(p_tbl);
200 	CL_ASSERT(p_block);
201 
202 	if (block_num > p_tbl->max_block)
203 		return IB_INVALID_PARAMETER;
204 
205 	if (position > p_tbl->max_position)
206 		return IB_INVALID_PARAMETER;
207 
208 	mlid_start_ho = (uint16_t) (block_num * IB_MCAST_BLOCK_SIZE);
209 
210 	if (mlid_start_ho + IB_MCAST_BLOCK_SIZE - 1 > p_tbl->mft_depth)
211 		return IB_INVALID_PARAMETER;
212 
213 	for (i = 0; i < IB_MCAST_BLOCK_SIZE; i++)
214 		(*p_tbl->p_mask_tbl)[mlid_start_ho + i][position] = p_block[i];
215 
216 	if (block_num > p_tbl->max_block_in_use)
217 		p_tbl->max_block_in_use = (uint16_t) block_num;
218 
219 	return IB_SUCCESS;
220 }
221 
222 void osm_mcast_tbl_clear_mlid(IN osm_mcast_tbl_t * p_tbl, IN uint16_t mlid_ho)
223 {
224 	unsigned mlid_offset;
225 
226 	CL_ASSERT(p_tbl);
227 	CL_ASSERT(mlid_ho >= IB_LID_MCAST_START_HO);
228 
229 	mlid_offset = mlid_ho - IB_LID_MCAST_START_HO;
230 	if (p_tbl->p_mask_tbl && mlid_offset < p_tbl->mft_depth)
231 		memset((uint8_t *)p_tbl->p_mask_tbl + mlid_offset * (IB_MCAST_POSITION_MAX + 1) * IB_MCAST_MASK_SIZE / 8,
232 		       0,
233 		       (IB_MCAST_POSITION_MAX + 1) * IB_MCAST_MASK_SIZE / 8);
234 }
235 
236 boolean_t osm_mcast_tbl_get_block(IN osm_mcast_tbl_t * p_tbl,
237 				  IN int16_t block_num, IN uint8_t position,
238 				  OUT ib_net16_t * p_block)
239 {
240 	uint32_t i;
241 	uint16_t mlid_start_ho;
242 
243 	CL_ASSERT(p_tbl);
244 	CL_ASSERT(p_block);
245 
246 	if (block_num > p_tbl->max_block_in_use)
247 		return FALSE;
248 
249 	if (position > p_tbl->max_position) {
250 		/*
251 		   Caller shouldn't do this for efficiency's sake...
252 		 */
253 		memset(p_block, 0, IB_SMP_DATA_SIZE);
254 		return TRUE;
255 	}
256 
257 	CL_ASSERT(block_num * IB_MCAST_BLOCK_SIZE <= p_tbl->mft_depth);
258 
259 	mlid_start_ho = (uint16_t) (block_num * IB_MCAST_BLOCK_SIZE);
260 
261 	for (i = 0; i < IB_MCAST_BLOCK_SIZE; i++)
262 		p_block[i] = (*p_tbl->p_mask_tbl)[mlid_start_ho + i][position];
263 
264 	return TRUE;
265 }
266