Commit | Line | Data |
---|---|---|
a1107487 IS |
1 | /* |
2 | * drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c | |
3 | * Copyright (c) 2017 Mellanox Technologies. All rights reserved. | |
4 | * Copyright (c) 2017 Ido Schimmel <idosch@mellanox.com> | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions are met: | |
8 | * | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * 3. Neither the names of the copyright holders nor the names of its | |
15 | * contributors may be used to endorse or promote products derived from | |
16 | * this software without specific prior written permission. | |
17 | * | |
18 | * Alternatively, this software may be distributed under the terms of the | |
19 | * GNU General Public License ("GPL") version 2 as published by the Free | |
20 | * Software Foundation. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
32 | * POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <linux/kernel.h> | |
36 | #include <linux/bitops.h> | |
37 | #include <linux/if_vlan.h> | |
38 | #include <linux/if_bridge.h> | |
39 | #include <linux/netdevice.h> | |
40 | #include <linux/rtnetlink.h> | |
41 | ||
42 | #include "spectrum.h" | |
43 | #include "reg.h" | |
44 | ||
45 | struct mlxsw_sp_fid_family; | |
46 | ||
47 | struct mlxsw_sp_fid_core { | |
48 | struct mlxsw_sp_fid_family *fid_family_arr[MLXSW_SP_FID_TYPE_MAX]; | |
49 | unsigned int *port_fid_mappings; | |
50 | }; | |
51 | ||
52 | struct mlxsw_sp_fid { | |
53 | struct list_head list; | |
54 | struct mlxsw_sp_rif *rif; | |
55 | unsigned int ref_count; | |
56 | u16 fid_index; | |
57 | struct mlxsw_sp_fid_family *fid_family; | |
58 | }; | |
59 | ||
60 | struct mlxsw_sp_fid_8021q { | |
61 | struct mlxsw_sp_fid common; | |
62 | u16 vid; | |
63 | }; | |
64 | ||
65 | struct mlxsw_sp_fid_8021d { | |
66 | struct mlxsw_sp_fid common; | |
67 | int br_ifindex; | |
68 | }; | |
69 | ||
70 | struct mlxsw_sp_flood_table { | |
71 | enum mlxsw_sp_flood_type packet_type; | |
72 | enum mlxsw_reg_sfgc_bridge_type bridge_type; | |
73 | enum mlxsw_flood_table_type table_type; | |
74 | int table_index; | |
75 | }; | |
76 | ||
77 | struct mlxsw_sp_fid_ops { | |
78 | void (*setup)(struct mlxsw_sp_fid *fid, const void *arg); | |
79 | int (*configure)(struct mlxsw_sp_fid *fid); | |
80 | void (*deconfigure)(struct mlxsw_sp_fid *fid); | |
81 | int (*index_alloc)(struct mlxsw_sp_fid *fid, const void *arg, | |
82 | u16 *p_fid_index); | |
83 | bool (*compare)(const struct mlxsw_sp_fid *fid, | |
84 | const void *arg); | |
85 | u16 (*flood_index)(const struct mlxsw_sp_fid *fid); | |
86 | int (*port_vid_map)(struct mlxsw_sp_fid *fid, | |
87 | struct mlxsw_sp_port *port, u16 vid); | |
88 | void (*port_vid_unmap)(struct mlxsw_sp_fid *fid, | |
89 | struct mlxsw_sp_port *port, u16 vid); | |
90 | }; | |
91 | ||
92 | struct mlxsw_sp_fid_family { | |
93 | enum mlxsw_sp_fid_type type; | |
94 | size_t fid_size; | |
95 | u16 start_index; | |
96 | u16 end_index; | |
97 | struct list_head fids_list; | |
98 | unsigned long *fids_bitmap; | |
99 | const struct mlxsw_sp_flood_table *flood_tables; | |
100 | int nr_flood_tables; | |
101 | enum mlxsw_sp_rif_type rif_type; | |
102 | const struct mlxsw_sp_fid_ops *ops; | |
103 | struct mlxsw_sp *mlxsw_sp; | |
104 | }; | |
105 | ||
106 | static const int mlxsw_sp_sfgc_uc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { | |
107 | [MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST] = 1, | |
108 | }; | |
109 | ||
110 | static const int mlxsw_sp_sfgc_bc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { | |
111 | [MLXSW_REG_SFGC_TYPE_BROADCAST] = 1, | |
a1107487 IS |
112 | [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP] = 1, |
113 | [MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL] = 1, | |
114 | [MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST] = 1, | |
9d45deb0 | 115 | [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6] = 1, |
a1107487 IS |
116 | }; |
117 | ||
118 | static const int mlxsw_sp_sfgc_mc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { | |
119 | [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4] = 1, | |
120 | }; | |
121 | ||
122 | static const int *mlxsw_sp_packet_type_sfgc_types[] = { | |
123 | [MLXSW_SP_FLOOD_TYPE_UC] = mlxsw_sp_sfgc_uc_packet_types, | |
124 | [MLXSW_SP_FLOOD_TYPE_BC] = mlxsw_sp_sfgc_bc_packet_types, | |
125 | [MLXSW_SP_FLOOD_TYPE_MC] = mlxsw_sp_sfgc_mc_packet_types, | |
126 | }; | |
127 | ||
128 | static const struct mlxsw_sp_flood_table * | |
129 | mlxsw_sp_fid_flood_table_lookup(const struct mlxsw_sp_fid *fid, | |
130 | enum mlxsw_sp_flood_type packet_type) | |
131 | { | |
132 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; | |
133 | int i; | |
134 | ||
135 | for (i = 0; i < fid_family->nr_flood_tables; i++) { | |
136 | if (fid_family->flood_tables[i].packet_type != packet_type) | |
137 | continue; | |
138 | return &fid_family->flood_tables[i]; | |
139 | } | |
140 | ||
141 | return NULL; | |
142 | } | |
143 | ||
144 | int mlxsw_sp_fid_flood_set(struct mlxsw_sp_fid *fid, | |
145 | enum mlxsw_sp_flood_type packet_type, u8 local_port, | |
146 | bool member) | |
147 | { | |
148 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; | |
149 | const struct mlxsw_sp_fid_ops *ops = fid_family->ops; | |
150 | const struct mlxsw_sp_flood_table *flood_table; | |
151 | char *sftr_pl; | |
152 | int err; | |
153 | ||
154 | if (WARN_ON(!fid_family->flood_tables || !ops->flood_index)) | |
155 | return -EINVAL; | |
156 | ||
157 | flood_table = mlxsw_sp_fid_flood_table_lookup(fid, packet_type); | |
158 | if (!flood_table) | |
159 | return -ESRCH; | |
160 | ||
161 | sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); | |
162 | if (!sftr_pl) | |
163 | return -ENOMEM; | |
164 | ||
165 | mlxsw_reg_sftr_pack(sftr_pl, flood_table->table_index, | |
166 | ops->flood_index(fid), flood_table->table_type, 1, | |
167 | local_port, member); | |
168 | err = mlxsw_reg_write(fid_family->mlxsw_sp->core, MLXSW_REG(sftr), | |
169 | sftr_pl); | |
170 | kfree(sftr_pl); | |
171 | return err; | |
172 | } | |
173 | ||
174 | int mlxsw_sp_fid_port_vid_map(struct mlxsw_sp_fid *fid, | |
175 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) | |
176 | { | |
177 | if (WARN_ON(!fid->fid_family->ops->port_vid_map)) | |
178 | return -EINVAL; | |
179 | return fid->fid_family->ops->port_vid_map(fid, mlxsw_sp_port, vid); | |
180 | } | |
181 | ||
182 | void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid, | |
183 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) | |
184 | { | |
185 | fid->fid_family->ops->port_vid_unmap(fid, mlxsw_sp_port, vid); | |
186 | } | |
187 | ||
188 | enum mlxsw_sp_rif_type mlxsw_sp_fid_rif_type(const struct mlxsw_sp_fid *fid) | |
189 | { | |
190 | return fid->fid_family->rif_type; | |
191 | } | |
192 | ||
193 | u16 mlxsw_sp_fid_index(const struct mlxsw_sp_fid *fid) | |
194 | { | |
195 | return fid->fid_index; | |
196 | } | |
197 | ||
198 | enum mlxsw_sp_fid_type mlxsw_sp_fid_type(const struct mlxsw_sp_fid *fid) | |
199 | { | |
200 | return fid->fid_family->type; | |
201 | } | |
202 | ||
203 | void mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif) | |
204 | { | |
205 | fid->rif = rif; | |
206 | } | |
207 | ||
e4f3c1c1 IS |
208 | enum mlxsw_sp_rif_type |
209 | mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp, | |
210 | enum mlxsw_sp_fid_type type) | |
211 | { | |
212 | struct mlxsw_sp_fid_core *fid_core = mlxsw_sp->fid_core; | |
213 | ||
214 | return fid_core->fid_family_arr[type]->rif_type; | |
215 | } | |
216 | ||
a1107487 IS |
217 | static struct mlxsw_sp_fid_8021q * |
218 | mlxsw_sp_fid_8021q_fid(const struct mlxsw_sp_fid *fid) | |
219 | { | |
220 | return container_of(fid, struct mlxsw_sp_fid_8021q, common); | |
221 | } | |
222 | ||
e4f3c1c1 IS |
223 | u16 mlxsw_sp_fid_8021q_vid(const struct mlxsw_sp_fid *fid) |
224 | { | |
225 | return mlxsw_sp_fid_8021q_fid(fid)->vid; | |
226 | } | |
227 | ||
a1107487 IS |
228 | static void mlxsw_sp_fid_8021q_setup(struct mlxsw_sp_fid *fid, const void *arg) |
229 | { | |
230 | u16 vid = *(u16 *) arg; | |
231 | ||
232 | mlxsw_sp_fid_8021q_fid(fid)->vid = vid; | |
233 | } | |
234 | ||
235 | static enum mlxsw_reg_sfmr_op mlxsw_sp_sfmr_op(bool valid) | |
236 | { | |
237 | return valid ? MLXSW_REG_SFMR_OP_CREATE_FID : | |
238 | MLXSW_REG_SFMR_OP_DESTROY_FID; | |
239 | } | |
240 | ||
241 | static int mlxsw_sp_fid_op(struct mlxsw_sp *mlxsw_sp, u16 fid_index, | |
242 | u16 fid_offset, bool valid) | |
243 | { | |
244 | char sfmr_pl[MLXSW_REG_SFMR_LEN]; | |
245 | ||
246 | mlxsw_reg_sfmr_pack(sfmr_pl, mlxsw_sp_sfmr_op(valid), fid_index, | |
247 | fid_offset); | |
248 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); | |
249 | } | |
250 | ||
251 | static int mlxsw_sp_fid_vid_map(struct mlxsw_sp *mlxsw_sp, u16 fid_index, | |
252 | u16 vid, bool valid) | |
253 | { | |
254 | enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_VID_TO_FID; | |
255 | char svfa_pl[MLXSW_REG_SVFA_LEN]; | |
256 | ||
257 | mlxsw_reg_svfa_pack(svfa_pl, 0, mt, valid, fid_index, vid); | |
258 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl); | |
259 | } | |
260 | ||
261 | static int __mlxsw_sp_fid_port_vid_map(struct mlxsw_sp *mlxsw_sp, u16 fid_index, | |
262 | u8 local_port, u16 vid, bool valid) | |
263 | { | |
264 | enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; | |
265 | char svfa_pl[MLXSW_REG_SVFA_LEN]; | |
266 | ||
267 | mlxsw_reg_svfa_pack(svfa_pl, local_port, mt, valid, fid_index, vid); | |
268 | return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl); | |
269 | } | |
270 | ||
271 | static int mlxsw_sp_fid_8021q_configure(struct mlxsw_sp_fid *fid) | |
272 | { | |
273 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; | |
274 | struct mlxsw_sp_fid_8021q *fid_8021q; | |
275 | int err; | |
276 | ||
277 | err = mlxsw_sp_fid_op(mlxsw_sp, fid->fid_index, fid->fid_index, true); | |
278 | if (err) | |
279 | return err; | |
280 | ||
281 | fid_8021q = mlxsw_sp_fid_8021q_fid(fid); | |
282 | err = mlxsw_sp_fid_vid_map(mlxsw_sp, fid->fid_index, fid_8021q->vid, | |
283 | true); | |
284 | if (err) | |
285 | goto err_fid_map; | |
286 | ||
287 | return 0; | |
288 | ||
289 | err_fid_map: | |
290 | mlxsw_sp_fid_op(mlxsw_sp, fid->fid_index, 0, false); | |
291 | return err; | |
292 | } | |
293 | ||
294 | static void mlxsw_sp_fid_8021q_deconfigure(struct mlxsw_sp_fid *fid) | |
295 | { | |
296 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; | |
297 | struct mlxsw_sp_fid_8021q *fid_8021q; | |
298 | ||
299 | fid_8021q = mlxsw_sp_fid_8021q_fid(fid); | |
300 | mlxsw_sp_fid_vid_map(mlxsw_sp, fid->fid_index, fid_8021q->vid, false); | |
301 | mlxsw_sp_fid_op(mlxsw_sp, fid->fid_index, 0, false); | |
302 | } | |
303 | ||
304 | static int mlxsw_sp_fid_8021q_index_alloc(struct mlxsw_sp_fid *fid, | |
305 | const void *arg, u16 *p_fid_index) | |
306 | { | |
307 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; | |
308 | u16 vid = *(u16 *) arg; | |
309 | ||
310 | /* Use 1:1 mapping for simplicity although not a must */ | |
311 | if (vid < fid_family->start_index || vid > fid_family->end_index) | |
312 | return -EINVAL; | |
313 | *p_fid_index = vid; | |
314 | ||
315 | return 0; | |
316 | } | |
317 | ||
318 | static bool | |
319 | mlxsw_sp_fid_8021q_compare(const struct mlxsw_sp_fid *fid, const void *arg) | |
320 | { | |
321 | u16 vid = *(u16 *) arg; | |
322 | ||
323 | return mlxsw_sp_fid_8021q_fid(fid)->vid == vid; | |
324 | } | |
325 | ||
326 | static u16 mlxsw_sp_fid_8021q_flood_index(const struct mlxsw_sp_fid *fid) | |
327 | { | |
328 | return fid->fid_index; | |
329 | } | |
330 | ||
331 | static int mlxsw_sp_fid_8021q_port_vid_map(struct mlxsw_sp_fid *fid, | |
332 | struct mlxsw_sp_port *mlxsw_sp_port, | |
333 | u16 vid) | |
334 | { | |
335 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
336 | u8 local_port = mlxsw_sp_port->local_port; | |
337 | ||
338 | /* In case there are no {Port, VID} => FID mappings on the port, | |
339 | * we can use the global VID => FID mapping we created when the | |
340 | * FID was configured. | |
341 | */ | |
342 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port] == 0) | |
343 | return 0; | |
344 | return __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, local_port, | |
345 | vid, true); | |
346 | } | |
347 | ||
348 | static void | |
349 | mlxsw_sp_fid_8021q_port_vid_unmap(struct mlxsw_sp_fid *fid, | |
350 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) | |
351 | { | |
352 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
353 | u8 local_port = mlxsw_sp_port->local_port; | |
354 | ||
355 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port] == 0) | |
356 | return; | |
357 | __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, local_port, vid, | |
358 | false); | |
359 | } | |
360 | ||
361 | static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_ops = { | |
362 | .setup = mlxsw_sp_fid_8021q_setup, | |
363 | .configure = mlxsw_sp_fid_8021q_configure, | |
364 | .deconfigure = mlxsw_sp_fid_8021q_deconfigure, | |
365 | .index_alloc = mlxsw_sp_fid_8021q_index_alloc, | |
366 | .compare = mlxsw_sp_fid_8021q_compare, | |
367 | .flood_index = mlxsw_sp_fid_8021q_flood_index, | |
368 | .port_vid_map = mlxsw_sp_fid_8021q_port_vid_map, | |
369 | .port_vid_unmap = mlxsw_sp_fid_8021q_port_vid_unmap, | |
370 | }; | |
371 | ||
372 | static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021q_flood_tables[] = { | |
373 | { | |
374 | .packet_type = MLXSW_SP_FLOOD_TYPE_UC, | |
375 | .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID, | |
da0abcf9 | 376 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, |
a1107487 IS |
377 | .table_index = 0, |
378 | }, | |
379 | { | |
380 | .packet_type = MLXSW_SP_FLOOD_TYPE_MC, | |
381 | .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID, | |
da0abcf9 | 382 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, |
a1107487 IS |
383 | .table_index = 1, |
384 | }, | |
385 | { | |
386 | .packet_type = MLXSW_SP_FLOOD_TYPE_BC, | |
387 | .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_1Q_FID, | |
da0abcf9 | 388 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, |
a1107487 IS |
389 | .table_index = 2, |
390 | }, | |
391 | }; | |
392 | ||
393 | /* Range and flood configuration must match mlxsw_config_profile */ | |
394 | static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021q_family = { | |
395 | .type = MLXSW_SP_FID_TYPE_8021Q, | |
396 | .fid_size = sizeof(struct mlxsw_sp_fid_8021q), | |
397 | .start_index = 1, | |
398 | .end_index = VLAN_VID_MASK, | |
399 | .flood_tables = mlxsw_sp_fid_8021q_flood_tables, | |
400 | .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021q_flood_tables), | |
401 | .rif_type = MLXSW_SP_RIF_TYPE_VLAN, | |
402 | .ops = &mlxsw_sp_fid_8021q_ops, | |
403 | }; | |
404 | ||
405 | static struct mlxsw_sp_fid_8021d * | |
406 | mlxsw_sp_fid_8021d_fid(const struct mlxsw_sp_fid *fid) | |
407 | { | |
408 | return container_of(fid, struct mlxsw_sp_fid_8021d, common); | |
409 | } | |
410 | ||
411 | static void mlxsw_sp_fid_8021d_setup(struct mlxsw_sp_fid *fid, const void *arg) | |
412 | { | |
413 | int br_ifindex = *(int *) arg; | |
414 | ||
415 | mlxsw_sp_fid_8021d_fid(fid)->br_ifindex = br_ifindex; | |
416 | } | |
417 | ||
418 | static int mlxsw_sp_fid_8021d_configure(struct mlxsw_sp_fid *fid) | |
419 | { | |
420 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; | |
421 | ||
422 | return mlxsw_sp_fid_op(fid_family->mlxsw_sp, fid->fid_index, 0, true); | |
423 | } | |
424 | ||
425 | static void mlxsw_sp_fid_8021d_deconfigure(struct mlxsw_sp_fid *fid) | |
426 | { | |
427 | mlxsw_sp_fid_op(fid->fid_family->mlxsw_sp, fid->fid_index, 0, false); | |
428 | } | |
429 | ||
430 | static int mlxsw_sp_fid_8021d_index_alloc(struct mlxsw_sp_fid *fid, | |
431 | const void *arg, u16 *p_fid_index) | |
432 | { | |
433 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; | |
434 | u16 nr_fids, fid_index; | |
435 | ||
436 | nr_fids = fid_family->end_index - fid_family->start_index + 1; | |
437 | fid_index = find_first_zero_bit(fid_family->fids_bitmap, nr_fids); | |
438 | if (fid_index == nr_fids) | |
439 | return -ENOBUFS; | |
440 | *p_fid_index = fid_family->start_index + fid_index; | |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
445 | static bool | |
446 | mlxsw_sp_fid_8021d_compare(const struct mlxsw_sp_fid *fid, const void *arg) | |
447 | { | |
448 | int br_ifindex = *(int *) arg; | |
449 | ||
450 | return mlxsw_sp_fid_8021d_fid(fid)->br_ifindex == br_ifindex; | |
451 | } | |
452 | ||
453 | static u16 mlxsw_sp_fid_8021d_flood_index(const struct mlxsw_sp_fid *fid) | |
454 | { | |
455 | return fid->fid_index - fid->fid_family->start_index; | |
456 | } | |
457 | ||
458 | static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) | |
459 | { | |
460 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
461 | struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; | |
462 | int err; | |
463 | ||
464 | list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list, | |
465 | list) { | |
466 | struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; | |
467 | u16 vid = mlxsw_sp_port_vlan->vid; | |
468 | ||
469 | if (!fid) | |
470 | continue; | |
471 | ||
472 | err = __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, | |
473 | mlxsw_sp_port->local_port, | |
474 | vid, true); | |
475 | if (err) | |
476 | goto err_fid_port_vid_map; | |
477 | } | |
478 | ||
479 | err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, true); | |
480 | if (err) | |
481 | goto err_port_vp_mode_set; | |
482 | ||
483 | return 0; | |
484 | ||
485 | err_port_vp_mode_set: | |
486 | err_fid_port_vid_map: | |
487 | list_for_each_entry_continue_reverse(mlxsw_sp_port_vlan, | |
488 | &mlxsw_sp_port->vlans_list, list) { | |
489 | struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; | |
490 | u16 vid = mlxsw_sp_port_vlan->vid; | |
491 | ||
492 | if (!fid) | |
493 | continue; | |
494 | ||
495 | __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, | |
496 | mlxsw_sp_port->local_port, vid, | |
497 | false); | |
498 | } | |
499 | return err; | |
500 | } | |
501 | ||
502 | static void mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) | |
503 | { | |
504 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
505 | struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; | |
506 | ||
507 | mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false); | |
508 | ||
509 | list_for_each_entry_reverse(mlxsw_sp_port_vlan, | |
510 | &mlxsw_sp_port->vlans_list, list) { | |
511 | struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; | |
512 | u16 vid = mlxsw_sp_port_vlan->vid; | |
513 | ||
514 | if (!fid) | |
515 | continue; | |
516 | ||
517 | __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, | |
518 | mlxsw_sp_port->local_port, vid, | |
519 | false); | |
520 | } | |
521 | } | |
522 | ||
523 | static int mlxsw_sp_fid_8021d_port_vid_map(struct mlxsw_sp_fid *fid, | |
524 | struct mlxsw_sp_port *mlxsw_sp_port, | |
525 | u16 vid) | |
526 | { | |
527 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
528 | u8 local_port = mlxsw_sp_port->local_port; | |
529 | int err; | |
530 | ||
531 | err = __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, | |
532 | mlxsw_sp_port->local_port, vid, true); | |
533 | if (err) | |
534 | return err; | |
535 | ||
536 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port]++ == 0) { | |
537 | err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port); | |
538 | if (err) | |
539 | goto err_port_vp_mode_trans; | |
540 | } | |
541 | ||
542 | return 0; | |
543 | ||
544 | err_port_vp_mode_trans: | |
545 | mlxsw_sp->fid_core->port_fid_mappings[local_port]--; | |
546 | __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, | |
547 | mlxsw_sp_port->local_port, vid, false); | |
548 | return err; | |
549 | } | |
550 | ||
551 | static void | |
552 | mlxsw_sp_fid_8021d_port_vid_unmap(struct mlxsw_sp_fid *fid, | |
553 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) | |
554 | { | |
555 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
556 | u8 local_port = mlxsw_sp_port->local_port; | |
557 | ||
558 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port] == 1) | |
559 | mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); | |
560 | mlxsw_sp->fid_core->port_fid_mappings[local_port]--; | |
561 | __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, | |
562 | mlxsw_sp_port->local_port, vid, false); | |
563 | } | |
564 | ||
565 | static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = { | |
566 | .setup = mlxsw_sp_fid_8021d_setup, | |
567 | .configure = mlxsw_sp_fid_8021d_configure, | |
568 | .deconfigure = mlxsw_sp_fid_8021d_deconfigure, | |
569 | .index_alloc = mlxsw_sp_fid_8021d_index_alloc, | |
570 | .compare = mlxsw_sp_fid_8021d_compare, | |
571 | .flood_index = mlxsw_sp_fid_8021d_flood_index, | |
572 | .port_vid_map = mlxsw_sp_fid_8021d_port_vid_map, | |
573 | .port_vid_unmap = mlxsw_sp_fid_8021d_port_vid_unmap, | |
574 | }; | |
575 | ||
576 | static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021d_flood_tables[] = { | |
577 | { | |
578 | .packet_type = MLXSW_SP_FLOOD_TYPE_UC, | |
579 | .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_VFID, | |
580 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID, | |
581 | .table_index = 0, | |
582 | }, | |
583 | { | |
584 | .packet_type = MLXSW_SP_FLOOD_TYPE_MC, | |
585 | .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_VFID, | |
586 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID, | |
587 | .table_index = 1, | |
588 | }, | |
589 | { | |
590 | .packet_type = MLXSW_SP_FLOOD_TYPE_BC, | |
591 | .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_VFID, | |
592 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID, | |
593 | .table_index = 2, | |
594 | }, | |
595 | }; | |
596 | ||
597 | /* Range and flood configuration must match mlxsw_config_profile */ | |
598 | static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021d_family = { | |
599 | .type = MLXSW_SP_FID_TYPE_8021D, | |
600 | .fid_size = sizeof(struct mlxsw_sp_fid_8021d), | |
601 | .start_index = VLAN_N_VID, | |
602 | .end_index = VLAN_N_VID + MLXSW_SP_FID_8021D_MAX - 1, | |
603 | .flood_tables = mlxsw_sp_fid_8021d_flood_tables, | |
604 | .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), | |
605 | .rif_type = MLXSW_SP_RIF_TYPE_FID, | |
606 | .ops = &mlxsw_sp_fid_8021d_ops, | |
607 | }; | |
608 | ||
609 | static int mlxsw_sp_fid_rfid_configure(struct mlxsw_sp_fid *fid) | |
610 | { | |
611 | /* rFIDs are allocated by the device during init */ | |
612 | return 0; | |
613 | } | |
614 | ||
615 | static void mlxsw_sp_fid_rfid_deconfigure(struct mlxsw_sp_fid *fid) | |
616 | { | |
617 | } | |
618 | ||
619 | static int mlxsw_sp_fid_rfid_index_alloc(struct mlxsw_sp_fid *fid, | |
620 | const void *arg, u16 *p_fid_index) | |
621 | { | |
622 | u16 rif_index = *(u16 *) arg; | |
623 | ||
624 | *p_fid_index = fid->fid_family->start_index + rif_index; | |
625 | ||
626 | return 0; | |
627 | } | |
628 | ||
629 | static bool mlxsw_sp_fid_rfid_compare(const struct mlxsw_sp_fid *fid, | |
630 | const void *arg) | |
631 | { | |
632 | u16 rif_index = *(u16 *) arg; | |
633 | ||
634 | return fid->fid_index == rif_index + fid->fid_family->start_index; | |
635 | } | |
636 | ||
637 | static int mlxsw_sp_fid_rfid_port_vid_map(struct mlxsw_sp_fid *fid, | |
638 | struct mlxsw_sp_port *mlxsw_sp_port, | |
639 | u16 vid) | |
640 | { | |
641 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
642 | u8 local_port = mlxsw_sp_port->local_port; | |
643 | int err; | |
644 | ||
645 | /* We only need to transition the port to virtual mode since | |
646 | * {Port, VID} => FID is done by the firmware upon RIF creation. | |
647 | */ | |
648 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port]++ == 0) { | |
649 | err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port); | |
650 | if (err) | |
651 | goto err_port_vp_mode_trans; | |
652 | } | |
653 | ||
654 | return 0; | |
655 | ||
656 | err_port_vp_mode_trans: | |
657 | mlxsw_sp->fid_core->port_fid_mappings[local_port]--; | |
658 | return err; | |
659 | } | |
660 | ||
661 | static void | |
662 | mlxsw_sp_fid_rfid_port_vid_unmap(struct mlxsw_sp_fid *fid, | |
663 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) | |
664 | { | |
665 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
666 | u8 local_port = mlxsw_sp_port->local_port; | |
667 | ||
668 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port] == 1) | |
669 | mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); | |
670 | mlxsw_sp->fid_core->port_fid_mappings[local_port]--; | |
671 | } | |
672 | ||
673 | static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_rfid_ops = { | |
674 | .configure = mlxsw_sp_fid_rfid_configure, | |
675 | .deconfigure = mlxsw_sp_fid_rfid_deconfigure, | |
676 | .index_alloc = mlxsw_sp_fid_rfid_index_alloc, | |
677 | .compare = mlxsw_sp_fid_rfid_compare, | |
678 | .port_vid_map = mlxsw_sp_fid_rfid_port_vid_map, | |
679 | .port_vid_unmap = mlxsw_sp_fid_rfid_port_vid_unmap, | |
680 | }; | |
681 | ||
682 | #define MLXSW_SP_RFID_BASE (15 * 1024) | |
683 | #define MLXSW_SP_RFID_MAX 1024 | |
684 | ||
685 | static const struct mlxsw_sp_fid_family mlxsw_sp_fid_rfid_family = { | |
686 | .type = MLXSW_SP_FID_TYPE_RFID, | |
687 | .fid_size = sizeof(struct mlxsw_sp_fid), | |
688 | .start_index = MLXSW_SP_RFID_BASE, | |
689 | .end_index = MLXSW_SP_RFID_BASE + MLXSW_SP_RFID_MAX - 1, | |
690 | .rif_type = MLXSW_SP_RIF_TYPE_SUBPORT, | |
691 | .ops = &mlxsw_sp_fid_rfid_ops, | |
692 | }; | |
693 | ||
694 | static int mlxsw_sp_fid_dummy_configure(struct mlxsw_sp_fid *fid) | |
695 | { | |
696 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; | |
697 | ||
698 | return mlxsw_sp_fid_op(mlxsw_sp, fid->fid_index, 0, true); | |
699 | } | |
700 | ||
701 | static void mlxsw_sp_fid_dummy_deconfigure(struct mlxsw_sp_fid *fid) | |
702 | { | |
703 | mlxsw_sp_fid_op(fid->fid_family->mlxsw_sp, fid->fid_index, 0, false); | |
704 | } | |
705 | ||
706 | static int mlxsw_sp_fid_dummy_index_alloc(struct mlxsw_sp_fid *fid, | |
707 | const void *arg, u16 *p_fid_index) | |
708 | { | |
709 | *p_fid_index = fid->fid_family->start_index; | |
710 | ||
711 | return 0; | |
712 | } | |
713 | ||
714 | static bool mlxsw_sp_fid_dummy_compare(const struct mlxsw_sp_fid *fid, | |
715 | const void *arg) | |
716 | { | |
717 | return true; | |
718 | } | |
719 | ||
720 | static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_dummy_ops = { | |
721 | .configure = mlxsw_sp_fid_dummy_configure, | |
722 | .deconfigure = mlxsw_sp_fid_dummy_deconfigure, | |
723 | .index_alloc = mlxsw_sp_fid_dummy_index_alloc, | |
724 | .compare = mlxsw_sp_fid_dummy_compare, | |
725 | }; | |
726 | ||
727 | static const struct mlxsw_sp_fid_family mlxsw_sp_fid_dummy_family = { | |
728 | .type = MLXSW_SP_FID_TYPE_DUMMY, | |
729 | .fid_size = sizeof(struct mlxsw_sp_fid), | |
730 | .start_index = MLXSW_SP_RFID_BASE - 1, | |
731 | .end_index = MLXSW_SP_RFID_BASE - 1, | |
732 | .ops = &mlxsw_sp_fid_dummy_ops, | |
733 | }; | |
734 | ||
735 | static const struct mlxsw_sp_fid_family *mlxsw_sp_fid_family_arr[] = { | |
736 | [MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp_fid_8021q_family, | |
737 | [MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp_fid_8021d_family, | |
738 | [MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family, | |
739 | [MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp_fid_dummy_family, | |
740 | }; | |
741 | ||
742 | static struct mlxsw_sp_fid *mlxsw_sp_fid_get(struct mlxsw_sp *mlxsw_sp, | |
743 | enum mlxsw_sp_fid_type type, | |
744 | const void *arg) | |
745 | { | |
746 | struct mlxsw_sp_fid_family *fid_family; | |
747 | struct mlxsw_sp_fid *fid; | |
748 | u16 fid_index; | |
749 | int err; | |
750 | ||
751 | fid_family = mlxsw_sp->fid_core->fid_family_arr[type]; | |
752 | list_for_each_entry(fid, &fid_family->fids_list, list) { | |
753 | if (!fid->fid_family->ops->compare(fid, arg)) | |
754 | continue; | |
755 | fid->ref_count++; | |
756 | return fid; | |
757 | } | |
758 | ||
759 | fid = kzalloc(fid_family->fid_size, GFP_KERNEL); | |
760 | if (!fid) | |
761 | return ERR_PTR(-ENOMEM); | |
762 | fid->fid_family = fid_family; | |
763 | ||
764 | err = fid->fid_family->ops->index_alloc(fid, arg, &fid_index); | |
765 | if (err) | |
766 | goto err_index_alloc; | |
767 | fid->fid_index = fid_index; | |
768 | __set_bit(fid_index - fid_family->start_index, fid_family->fids_bitmap); | |
769 | ||
770 | if (fid->fid_family->ops->setup) | |
771 | fid->fid_family->ops->setup(fid, arg); | |
772 | ||
773 | err = fid->fid_family->ops->configure(fid); | |
774 | if (err) | |
775 | goto err_configure; | |
776 | ||
777 | list_add(&fid->list, &fid_family->fids_list); | |
778 | fid->ref_count++; | |
779 | return fid; | |
780 | ||
781 | err_configure: | |
782 | __clear_bit(fid_index - fid_family->start_index, | |
783 | fid_family->fids_bitmap); | |
784 | err_index_alloc: | |
785 | kfree(fid); | |
786 | return ERR_PTR(err); | |
787 | } | |
788 | ||
789 | void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid) | |
790 | { | |
791 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; | |
792 | ||
793 | if (--fid->ref_count == 1 && fid->rif) { | |
794 | /* Destroy the associated RIF and let it drop the last | |
795 | * reference on the FID. | |
796 | */ | |
e4f3c1c1 | 797 | return mlxsw_sp_rif_destroy(fid->rif); |
a1107487 IS |
798 | } else if (fid->ref_count == 0) { |
799 | list_del(&fid->list); | |
800 | fid->fid_family->ops->deconfigure(fid); | |
801 | __clear_bit(fid->fid_index - fid_family->start_index, | |
802 | fid_family->fids_bitmap); | |
803 | kfree(fid); | |
804 | } | |
805 | } | |
806 | ||
807 | struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_get(struct mlxsw_sp *mlxsw_sp, u16 vid) | |
808 | { | |
809 | return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_8021Q, &vid); | |
810 | } | |
811 | ||
812 | struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_get(struct mlxsw_sp *mlxsw_sp, | |
813 | int br_ifindex) | |
814 | { | |
815 | return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_8021D, &br_ifindex); | |
816 | } | |
817 | ||
818 | struct mlxsw_sp_fid *mlxsw_sp_fid_rfid_get(struct mlxsw_sp *mlxsw_sp, | |
819 | u16 rif_index) | |
820 | { | |
821 | return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_RFID, &rif_index); | |
822 | } | |
823 | ||
824 | struct mlxsw_sp_fid *mlxsw_sp_fid_dummy_get(struct mlxsw_sp *mlxsw_sp) | |
825 | { | |
826 | return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_DUMMY, NULL); | |
827 | } | |
828 | ||
829 | static int | |
830 | mlxsw_sp_fid_flood_table_init(struct mlxsw_sp_fid_family *fid_family, | |
831 | const struct mlxsw_sp_flood_table *flood_table) | |
832 | { | |
833 | enum mlxsw_sp_flood_type packet_type = flood_table->packet_type; | |
834 | const int *sfgc_packet_types; | |
835 | int i; | |
836 | ||
837 | sfgc_packet_types = mlxsw_sp_packet_type_sfgc_types[packet_type]; | |
838 | for (i = 0; i < MLXSW_REG_SFGC_TYPE_MAX; i++) { | |
839 | struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; | |
840 | char sfgc_pl[MLXSW_REG_SFGC_LEN]; | |
841 | int err; | |
842 | ||
843 | if (!sfgc_packet_types[i]) | |
844 | continue; | |
845 | mlxsw_reg_sfgc_pack(sfgc_pl, i, flood_table->bridge_type, | |
846 | flood_table->table_type, | |
847 | flood_table->table_index); | |
848 | err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfgc), sfgc_pl); | |
849 | if (err) | |
850 | return err; | |
851 | } | |
852 | ||
853 | return 0; | |
854 | } | |
855 | ||
856 | static int | |
857 | mlxsw_sp_fid_flood_tables_init(struct mlxsw_sp_fid_family *fid_family) | |
858 | { | |
859 | int i; | |
860 | ||
861 | for (i = 0; i < fid_family->nr_flood_tables; i++) { | |
862 | const struct mlxsw_sp_flood_table *flood_table; | |
863 | int err; | |
864 | ||
865 | flood_table = &fid_family->flood_tables[i]; | |
866 | err = mlxsw_sp_fid_flood_table_init(fid_family, flood_table); | |
867 | if (err) | |
868 | return err; | |
869 | } | |
870 | ||
871 | return 0; | |
872 | } | |
873 | ||
874 | static int mlxsw_sp_fid_family_register(struct mlxsw_sp *mlxsw_sp, | |
875 | const struct mlxsw_sp_fid_family *tmpl) | |
876 | { | |
877 | u16 nr_fids = tmpl->end_index - tmpl->start_index + 1; | |
878 | struct mlxsw_sp_fid_family *fid_family; | |
879 | int err; | |
880 | ||
881 | fid_family = kmemdup(tmpl, sizeof(*fid_family), GFP_KERNEL); | |
882 | if (!fid_family) | |
883 | return -ENOMEM; | |
884 | ||
885 | fid_family->mlxsw_sp = mlxsw_sp; | |
886 | INIT_LIST_HEAD(&fid_family->fids_list); | |
887 | fid_family->fids_bitmap = kcalloc(BITS_TO_LONGS(nr_fids), | |
888 | sizeof(unsigned long), GFP_KERNEL); | |
889 | if (!fid_family->fids_bitmap) { | |
890 | err = -ENOMEM; | |
891 | goto err_alloc_fids_bitmap; | |
892 | } | |
893 | ||
894 | if (fid_family->flood_tables) { | |
895 | err = mlxsw_sp_fid_flood_tables_init(fid_family); | |
896 | if (err) | |
897 | goto err_fid_flood_tables_init; | |
898 | } | |
899 | ||
900 | mlxsw_sp->fid_core->fid_family_arr[tmpl->type] = fid_family; | |
901 | ||
902 | return 0; | |
903 | ||
904 | err_fid_flood_tables_init: | |
905 | kfree(fid_family->fids_bitmap); | |
906 | err_alloc_fids_bitmap: | |
907 | kfree(fid_family); | |
908 | return err; | |
909 | } | |
910 | ||
911 | static void | |
912 | mlxsw_sp_fid_family_unregister(struct mlxsw_sp *mlxsw_sp, | |
913 | struct mlxsw_sp_fid_family *fid_family) | |
914 | { | |
915 | mlxsw_sp->fid_core->fid_family_arr[fid_family->type] = NULL; | |
916 | kfree(fid_family->fids_bitmap); | |
917 | WARN_ON_ONCE(!list_empty(&fid_family->fids_list)); | |
918 | kfree(fid_family); | |
919 | } | |
920 | ||
921 | int mlxsw_sp_port_fids_init(struct mlxsw_sp_port *mlxsw_sp_port) | |
922 | { | |
923 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
924 | ||
925 | /* Track number of FIDs configured on the port with mapping type | |
926 | * PORT_VID_TO_FID, so that we know when to transition the port | |
927 | * back to non-virtual (VLAN) mode. | |
928 | */ | |
929 | mlxsw_sp->fid_core->port_fid_mappings[mlxsw_sp_port->local_port] = 0; | |
930 | ||
931 | return mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false); | |
932 | } | |
933 | ||
934 | void mlxsw_sp_port_fids_fini(struct mlxsw_sp_port *mlxsw_sp_port) | |
935 | { | |
936 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
937 | ||
938 | mlxsw_sp->fid_core->port_fid_mappings[mlxsw_sp_port->local_port] = 0; | |
939 | } | |
940 | ||
941 | int mlxsw_sp_fids_init(struct mlxsw_sp *mlxsw_sp) | |
942 | { | |
943 | unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); | |
944 | struct mlxsw_sp_fid_core *fid_core; | |
945 | int err, i; | |
946 | ||
947 | fid_core = kzalloc(sizeof(*mlxsw_sp->fid_core), GFP_KERNEL); | |
948 | if (!fid_core) | |
949 | return -ENOMEM; | |
950 | mlxsw_sp->fid_core = fid_core; | |
951 | ||
952 | fid_core->port_fid_mappings = kcalloc(max_ports, sizeof(unsigned int), | |
953 | GFP_KERNEL); | |
954 | if (!fid_core->port_fid_mappings) { | |
955 | err = -ENOMEM; | |
956 | goto err_alloc_port_fid_mappings; | |
957 | } | |
958 | ||
959 | for (i = 0; i < MLXSW_SP_FID_TYPE_MAX; i++) { | |
960 | err = mlxsw_sp_fid_family_register(mlxsw_sp, | |
961 | mlxsw_sp_fid_family_arr[i]); | |
962 | ||
963 | if (err) | |
964 | goto err_fid_ops_register; | |
965 | } | |
966 | ||
967 | return 0; | |
968 | ||
969 | err_fid_ops_register: | |
970 | for (i--; i >= 0; i--) { | |
971 | struct mlxsw_sp_fid_family *fid_family; | |
972 | ||
973 | fid_family = fid_core->fid_family_arr[i]; | |
974 | mlxsw_sp_fid_family_unregister(mlxsw_sp, fid_family); | |
975 | } | |
976 | kfree(fid_core->port_fid_mappings); | |
977 | err_alloc_port_fid_mappings: | |
978 | kfree(fid_core); | |
979 | return err; | |
980 | } | |
981 | ||
982 | void mlxsw_sp_fids_fini(struct mlxsw_sp *mlxsw_sp) | |
983 | { | |
984 | struct mlxsw_sp_fid_core *fid_core = mlxsw_sp->fid_core; | |
985 | int i; | |
986 | ||
987 | for (i = 0; i < MLXSW_SP_FID_TYPE_MAX; i++) | |
988 | mlxsw_sp_fid_family_unregister(mlxsw_sp, | |
989 | fid_core->fid_family_arr[i]); | |
990 | kfree(fid_core->port_fid_mappings); | |
991 | kfree(fid_core); | |
992 | } |