1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #include <linux/kernel.h> |
5 | #include <linux/bitops.h> |
6 | #include <linux/if_vlan.h> |
7 | #include <linux/if_bridge.h> |
8 | #include <linux/netdevice.h> |
9 | #include <linux/rhashtable.h> |
10 | #include <linux/rtnetlink.h> |
11 | #include <linux/refcount.h> |
12 | |
13 | #include "spectrum.h" |
14 | #include "reg.h" |
15 | |
16 | struct mlxsw_sp_fid_family; |
17 | |
18 | struct mlxsw_sp_fid_core { |
19 | struct rhashtable fid_ht; |
20 | struct rhashtable vni_ht; |
21 | struct mlxsw_sp_fid_family *fid_family_arr[MLXSW_SP_FID_TYPE_MAX]; |
22 | unsigned int *port_fid_mappings; |
23 | }; |
24 | |
25 | struct mlxsw_sp_fid_port_vid { |
26 | struct list_head list; |
27 | u16 local_port; |
28 | u16 vid; |
29 | }; |
30 | |
31 | struct mlxsw_sp_fid { |
32 | struct list_head list; |
33 | struct mlxsw_sp_rif *rif; |
34 | refcount_t ref_count; |
35 | u16 fid_index; |
36 | u16 fid_offset; |
37 | struct mlxsw_sp_fid_family *fid_family; |
38 | struct rhash_head ht_node; |
39 | |
40 | struct rhash_head vni_ht_node; |
41 | enum mlxsw_sp_nve_type nve_type; |
42 | __be32 vni; |
43 | u32 nve_flood_index; |
44 | int nve_ifindex; |
45 | u8 vni_valid:1, |
46 | nve_flood_index_valid:1; |
47 | struct list_head port_vid_list; /* Ordered by local port. */ |
48 | }; |
49 | |
50 | struct mlxsw_sp_fid_8021q { |
51 | struct mlxsw_sp_fid common; |
52 | u16 vid; |
53 | }; |
54 | |
55 | struct mlxsw_sp_fid_8021d { |
56 | struct mlxsw_sp_fid common; |
57 | int br_ifindex; |
58 | }; |
59 | |
60 | static const struct rhashtable_params mlxsw_sp_fid_ht_params = { |
61 | .key_len = sizeof_field(struct mlxsw_sp_fid, fid_index), |
62 | .key_offset = offsetof(struct mlxsw_sp_fid, fid_index), |
63 | .head_offset = offsetof(struct mlxsw_sp_fid, ht_node), |
64 | }; |
65 | |
66 | static const struct rhashtable_params mlxsw_sp_fid_vni_ht_params = { |
67 | .key_len = sizeof_field(struct mlxsw_sp_fid, vni), |
68 | .key_offset = offsetof(struct mlxsw_sp_fid, vni), |
69 | .head_offset = offsetof(struct mlxsw_sp_fid, vni_ht_node), |
70 | }; |
71 | |
72 | struct mlxsw_sp_flood_table { |
73 | enum mlxsw_sp_flood_type packet_type; |
74 | enum mlxsw_flood_table_type table_type; |
75 | int table_index; |
76 | }; |
77 | |
78 | struct mlxsw_sp_fid_ops { |
79 | void (*setup)(struct mlxsw_sp_fid *fid, const void *arg); |
80 | int (*configure)(struct mlxsw_sp_fid *fid); |
81 | void (*deconfigure)(struct mlxsw_sp_fid *fid); |
82 | int (*index_alloc)(struct mlxsw_sp_fid *fid, const void *arg, |
83 | u16 *p_fid_index); |
84 | bool (*compare)(const struct mlxsw_sp_fid *fid, |
85 | const void *arg); |
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 | int (*vni_set)(struct mlxsw_sp_fid *fid); |
91 | void (*vni_clear)(struct mlxsw_sp_fid *fid); |
92 | int (*nve_flood_index_set)(struct mlxsw_sp_fid *fid); |
93 | void (*nve_flood_index_clear)(struct mlxsw_sp_fid *fid); |
94 | void (*fdb_clear_offload)(const struct mlxsw_sp_fid *fid, |
95 | const struct net_device *nve_dev); |
96 | int (*vid_to_fid_rif_update)(const struct mlxsw_sp_fid *fid, |
97 | const struct mlxsw_sp_rif *rif); |
98 | }; |
99 | |
100 | struct mlxsw_sp_fid_family { |
101 | enum mlxsw_sp_fid_type type; |
102 | size_t fid_size; |
103 | u16 start_index; |
104 | u16 end_index; |
105 | struct list_head fids_list; |
106 | unsigned long *fids_bitmap; |
107 | const struct mlxsw_sp_flood_table *flood_tables; |
108 | int nr_flood_tables; |
109 | enum mlxsw_sp_rif_type rif_type; |
110 | const struct mlxsw_sp_fid_ops *ops; |
111 | struct mlxsw_sp *mlxsw_sp; |
112 | bool flood_rsp; |
113 | enum mlxsw_reg_bridge_type bridge_type; |
114 | u16 pgt_base; |
115 | bool smpe_index_valid; |
116 | }; |
117 | |
118 | static const int mlxsw_sp_sfgc_uc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { |
119 | [MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST] = 1, |
120 | }; |
121 | |
122 | static const int mlxsw_sp_sfgc_bc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { |
123 | [MLXSW_REG_SFGC_TYPE_BROADCAST] = 1, |
124 | [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP] = 1, |
125 | [MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL] = 1, |
126 | [MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST] = 1, |
127 | [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6] = 1, |
128 | }; |
129 | |
130 | static const int mlxsw_sp_sfgc_mc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { |
131 | [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4] = 1, |
132 | }; |
133 | |
134 | static const int *mlxsw_sp_packet_type_sfgc_types[] = { |
135 | [MLXSW_SP_FLOOD_TYPE_UC] = mlxsw_sp_sfgc_uc_packet_types, |
136 | [MLXSW_SP_FLOOD_TYPE_BC] = mlxsw_sp_sfgc_bc_packet_types, |
137 | [MLXSW_SP_FLOOD_TYPE_MC] = mlxsw_sp_sfgc_mc_packet_types, |
138 | }; |
139 | |
140 | struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp, |
141 | u16 fid_index) |
142 | { |
143 | struct mlxsw_sp_fid *fid; |
144 | |
145 | fid = rhashtable_lookup_fast(ht: &mlxsw_sp->fid_core->fid_ht, key: &fid_index, |
146 | params: mlxsw_sp_fid_ht_params); |
147 | if (fid) |
148 | refcount_inc(r: &fid->ref_count); |
149 | |
150 | return fid; |
151 | } |
152 | |
153 | int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex) |
154 | { |
155 | if (!fid->vni_valid) |
156 | return -EINVAL; |
157 | |
158 | *nve_ifindex = fid->nve_ifindex; |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | int mlxsw_sp_fid_nve_type(const struct mlxsw_sp_fid *fid, |
164 | enum mlxsw_sp_nve_type *p_type) |
165 | { |
166 | if (!fid->vni_valid) |
167 | return -EINVAL; |
168 | |
169 | *p_type = fid->nve_type; |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp, |
175 | __be32 vni) |
176 | { |
177 | struct mlxsw_sp_fid *fid; |
178 | |
179 | fid = rhashtable_lookup_fast(ht: &mlxsw_sp->fid_core->vni_ht, key: &vni, |
180 | params: mlxsw_sp_fid_vni_ht_params); |
181 | if (fid) |
182 | refcount_inc(r: &fid->ref_count); |
183 | |
184 | return fid; |
185 | } |
186 | |
187 | int mlxsw_sp_fid_vni(const struct mlxsw_sp_fid *fid, __be32 *vni) |
188 | { |
189 | if (!fid->vni_valid) |
190 | return -EINVAL; |
191 | |
192 | *vni = fid->vni; |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | int mlxsw_sp_fid_nve_flood_index_set(struct mlxsw_sp_fid *fid, |
198 | u32 nve_flood_index) |
199 | { |
200 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
201 | const struct mlxsw_sp_fid_ops *ops = fid_family->ops; |
202 | int err; |
203 | |
204 | if (WARN_ON(fid->nve_flood_index_valid)) |
205 | return -EINVAL; |
206 | |
207 | fid->nve_flood_index = nve_flood_index; |
208 | fid->nve_flood_index_valid = true; |
209 | err = ops->nve_flood_index_set(fid); |
210 | if (err) |
211 | goto err_nve_flood_index_set; |
212 | |
213 | return 0; |
214 | |
215 | err_nve_flood_index_set: |
216 | fid->nve_flood_index_valid = false; |
217 | return err; |
218 | } |
219 | |
220 | void mlxsw_sp_fid_nve_flood_index_clear(struct mlxsw_sp_fid *fid) |
221 | { |
222 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
223 | const struct mlxsw_sp_fid_ops *ops = fid_family->ops; |
224 | |
225 | if (WARN_ON(!fid->nve_flood_index_valid)) |
226 | return; |
227 | |
228 | fid->nve_flood_index_valid = false; |
229 | ops->nve_flood_index_clear(fid); |
230 | } |
231 | |
232 | bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid) |
233 | { |
234 | return fid->nve_flood_index_valid; |
235 | } |
236 | |
237 | int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type, |
238 | __be32 vni, int nve_ifindex) |
239 | { |
240 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
241 | const struct mlxsw_sp_fid_ops *ops = fid_family->ops; |
242 | struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; |
243 | int err; |
244 | |
245 | if (WARN_ON(fid->vni_valid)) |
246 | return -EINVAL; |
247 | |
248 | fid->nve_type = type; |
249 | fid->nve_ifindex = nve_ifindex; |
250 | fid->vni = vni; |
251 | err = rhashtable_lookup_insert_fast(ht: &mlxsw_sp->fid_core->vni_ht, |
252 | obj: &fid->vni_ht_node, |
253 | params: mlxsw_sp_fid_vni_ht_params); |
254 | if (err) |
255 | return err; |
256 | |
257 | fid->vni_valid = true; |
258 | err = ops->vni_set(fid); |
259 | if (err) |
260 | goto err_vni_set; |
261 | |
262 | return 0; |
263 | |
264 | err_vni_set: |
265 | fid->vni_valid = false; |
266 | rhashtable_remove_fast(ht: &mlxsw_sp->fid_core->vni_ht, obj: &fid->vni_ht_node, |
267 | params: mlxsw_sp_fid_vni_ht_params); |
268 | return err; |
269 | } |
270 | |
271 | void mlxsw_sp_fid_vni_clear(struct mlxsw_sp_fid *fid) |
272 | { |
273 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
274 | const struct mlxsw_sp_fid_ops *ops = fid_family->ops; |
275 | struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; |
276 | |
277 | if (WARN_ON(!fid->vni_valid)) |
278 | return; |
279 | |
280 | fid->vni_valid = false; |
281 | ops->vni_clear(fid); |
282 | rhashtable_remove_fast(ht: &mlxsw_sp->fid_core->vni_ht, obj: &fid->vni_ht_node, |
283 | params: mlxsw_sp_fid_vni_ht_params); |
284 | } |
285 | |
286 | bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid) |
287 | { |
288 | return fid->vni_valid; |
289 | } |
290 | |
291 | void mlxsw_sp_fid_fdb_clear_offload(const struct mlxsw_sp_fid *fid, |
292 | const struct net_device *nve_dev) |
293 | { |
294 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
295 | const struct mlxsw_sp_fid_ops *ops = fid_family->ops; |
296 | |
297 | if (ops->fdb_clear_offload) |
298 | ops->fdb_clear_offload(fid, nve_dev); |
299 | } |
300 | |
301 | static const struct mlxsw_sp_flood_table * |
302 | mlxsw_sp_fid_flood_table_lookup(const struct mlxsw_sp_fid *fid, |
303 | enum mlxsw_sp_flood_type packet_type) |
304 | { |
305 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
306 | int i; |
307 | |
308 | for (i = 0; i < fid_family->nr_flood_tables; i++) { |
309 | if (fid_family->flood_tables[i].packet_type != packet_type) |
310 | continue; |
311 | return &fid_family->flood_tables[i]; |
312 | } |
313 | |
314 | return NULL; |
315 | } |
316 | |
317 | static u16 |
318 | mlxsw_sp_fid_family_num_fids(const struct mlxsw_sp_fid_family *fid_family) |
319 | { |
320 | return fid_family->end_index - fid_family->start_index + 1; |
321 | } |
322 | |
323 | static u16 |
324 | mlxsw_sp_fid_family_pgt_size(const struct mlxsw_sp_fid_family *fid_family) |
325 | { |
326 | u16 num_fids = mlxsw_sp_fid_family_num_fids(fid_family); |
327 | |
328 | return num_fids * fid_family->nr_flood_tables; |
329 | } |
330 | |
331 | static u16 |
332 | mlxsw_sp_fid_flood_table_mid(const struct mlxsw_sp_fid_family *fid_family, |
333 | const struct mlxsw_sp_flood_table *flood_table, |
334 | u16 fid_offset) |
335 | { |
336 | u16 num_fids; |
337 | |
338 | num_fids = mlxsw_sp_fid_family_num_fids(fid_family); |
339 | return fid_family->pgt_base + num_fids * flood_table->table_index + |
340 | fid_offset; |
341 | } |
342 | |
343 | int mlxsw_sp_fid_flood_set(struct mlxsw_sp_fid *fid, |
344 | enum mlxsw_sp_flood_type packet_type, u16 local_port, |
345 | bool member) |
346 | { |
347 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
348 | const struct mlxsw_sp_flood_table *flood_table; |
349 | u16 mid_index; |
350 | |
351 | if (WARN_ON(!fid_family->flood_tables)) |
352 | return -EINVAL; |
353 | |
354 | flood_table = mlxsw_sp_fid_flood_table_lookup(fid, packet_type); |
355 | if (!flood_table) |
356 | return -ESRCH; |
357 | |
358 | mid_index = mlxsw_sp_fid_flood_table_mid(fid_family, flood_table, |
359 | fid_offset: fid->fid_offset); |
360 | return mlxsw_sp_pgt_entry_port_set(mlxsw_sp: fid_family->mlxsw_sp, mid: mid_index, |
361 | smpe: fid->fid_index, local_port, member); |
362 | } |
363 | |
364 | int mlxsw_sp_fid_port_vid_map(struct mlxsw_sp_fid *fid, |
365 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) |
366 | { |
367 | if (WARN_ON(!fid->fid_family->ops->port_vid_map)) |
368 | return -EINVAL; |
369 | return fid->fid_family->ops->port_vid_map(fid, mlxsw_sp_port, vid); |
370 | } |
371 | |
372 | void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid, |
373 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) |
374 | { |
375 | fid->fid_family->ops->port_vid_unmap(fid, mlxsw_sp_port, vid); |
376 | } |
377 | |
378 | u16 mlxsw_sp_fid_index(const struct mlxsw_sp_fid *fid) |
379 | { |
380 | return fid->fid_index; |
381 | } |
382 | |
383 | enum mlxsw_sp_fid_type mlxsw_sp_fid_type(const struct mlxsw_sp_fid *fid) |
384 | { |
385 | return fid->fid_family->type; |
386 | } |
387 | |
388 | struct mlxsw_sp_rif *mlxsw_sp_fid_rif(const struct mlxsw_sp_fid *fid) |
389 | { |
390 | return fid->rif; |
391 | } |
392 | |
393 | enum mlxsw_sp_rif_type |
394 | mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp, |
395 | enum mlxsw_sp_fid_type type) |
396 | { |
397 | struct mlxsw_sp_fid_core *fid_core = mlxsw_sp->fid_core; |
398 | |
399 | return fid_core->fid_family_arr[type]->rif_type; |
400 | } |
401 | |
402 | static struct mlxsw_sp_fid_8021q * |
403 | mlxsw_sp_fid_8021q_fid(const struct mlxsw_sp_fid *fid) |
404 | { |
405 | return container_of(fid, struct mlxsw_sp_fid_8021q, common); |
406 | } |
407 | |
408 | u16 mlxsw_sp_fid_8021q_vid(const struct mlxsw_sp_fid *fid) |
409 | { |
410 | return mlxsw_sp_fid_8021q_fid(fid)->vid; |
411 | } |
412 | |
413 | static void mlxsw_sp_fid_8021q_setup(struct mlxsw_sp_fid *fid, const void *arg) |
414 | { |
415 | u16 vid = *(u16 *) arg; |
416 | |
417 | mlxsw_sp_fid_8021q_fid(fid)->vid = vid; |
418 | fid->fid_offset = fid->fid_index - fid->fid_family->start_index; |
419 | } |
420 | |
421 | static enum mlxsw_reg_sfmr_op mlxsw_sp_sfmr_op(bool valid) |
422 | { |
423 | return valid ? MLXSW_REG_SFMR_OP_CREATE_FID : |
424 | MLXSW_REG_SFMR_OP_DESTROY_FID; |
425 | } |
426 | |
427 | static int mlxsw_sp_fid_op(const struct mlxsw_sp_fid *fid, bool valid) |
428 | { |
429 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
430 | char sfmr_pl[MLXSW_REG_SFMR_LEN]; |
431 | u16 smpe; |
432 | |
433 | smpe = fid->fid_family->smpe_index_valid ? fid->fid_index : 0; |
434 | |
435 | mlxsw_reg_sfmr_pack(payload: sfmr_pl, op: mlxsw_sp_sfmr_op(valid), fid: fid->fid_index, |
436 | fid_offset: fid->fid_offset, flood_rsp: fid->fid_family->flood_rsp, |
437 | bridge_type: fid->fid_family->bridge_type, |
438 | smpe_valid: fid->fid_family->smpe_index_valid, smpe); |
439 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(sfmr), payload: sfmr_pl); |
440 | } |
441 | |
442 | static int mlxsw_sp_fid_edit_op(const struct mlxsw_sp_fid *fid, |
443 | const struct mlxsw_sp_rif *rif) |
444 | { |
445 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
446 | char sfmr_pl[MLXSW_REG_SFMR_LEN]; |
447 | u16 smpe; |
448 | |
449 | smpe = fid->fid_family->smpe_index_valid ? fid->fid_index : 0; |
450 | |
451 | mlxsw_reg_sfmr_pack(payload: sfmr_pl, op: MLXSW_REG_SFMR_OP_CREATE_FID, |
452 | fid: fid->fid_index, fid_offset: fid->fid_offset, |
453 | flood_rsp: fid->fid_family->flood_rsp, |
454 | bridge_type: fid->fid_family->bridge_type, |
455 | smpe_valid: fid->fid_family->smpe_index_valid, smpe); |
456 | mlxsw_reg_sfmr_vv_set(buf: sfmr_pl, val: fid->vni_valid); |
457 | mlxsw_reg_sfmr_vni_set(buf: sfmr_pl, be32_to_cpu(fid->vni)); |
458 | mlxsw_reg_sfmr_vtfp_set(buf: sfmr_pl, val: fid->nve_flood_index_valid); |
459 | mlxsw_reg_sfmr_nve_tunnel_flood_ptr_set(buf: sfmr_pl, val: fid->nve_flood_index); |
460 | |
461 | if (rif) { |
462 | mlxsw_reg_sfmr_irif_v_set(buf: sfmr_pl, val: true); |
463 | mlxsw_reg_sfmr_irif_set(buf: sfmr_pl, val: mlxsw_sp_rif_index(rif)); |
464 | } |
465 | |
466 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(sfmr), payload: sfmr_pl); |
467 | } |
468 | |
469 | static int mlxsw_sp_fid_vni_to_fid_map(const struct mlxsw_sp_fid *fid, |
470 | const struct mlxsw_sp_rif *rif, |
471 | bool valid) |
472 | { |
473 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
474 | char svfa_pl[MLXSW_REG_SVFA_LEN]; |
475 | bool irif_valid; |
476 | u16 irif_index; |
477 | |
478 | irif_valid = !!rif; |
479 | irif_index = rif ? mlxsw_sp_rif_index(rif) : 0; |
480 | |
481 | mlxsw_reg_svfa_vni_pack(payload: svfa_pl, valid, fid: fid->fid_index, |
482 | be32_to_cpu(fid->vni), irif_v: irif_valid, irif: irif_index); |
483 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(svfa), payload: svfa_pl); |
484 | } |
485 | |
486 | static int mlxsw_sp_fid_to_fid_rif_update(const struct mlxsw_sp_fid *fid, |
487 | const struct mlxsw_sp_rif *rif) |
488 | { |
489 | return mlxsw_sp_fid_edit_op(fid, rif); |
490 | } |
491 | |
492 | static int mlxsw_sp_fid_vni_to_fid_rif_update(const struct mlxsw_sp_fid *fid, |
493 | const struct mlxsw_sp_rif *rif) |
494 | { |
495 | if (!fid->vni_valid) |
496 | return 0; |
497 | |
498 | return mlxsw_sp_fid_vni_to_fid_map(fid, rif, valid: fid->vni_valid); |
499 | } |
500 | |
501 | static int |
502 | mlxsw_sp_fid_vid_to_fid_map(const struct mlxsw_sp_fid *fid, u16 vid, bool valid, |
503 | const struct mlxsw_sp_rif *rif) |
504 | { |
505 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
506 | char svfa_pl[MLXSW_REG_SVFA_LEN]; |
507 | bool irif_valid; |
508 | u16 irif_index; |
509 | |
510 | irif_valid = !!rif; |
511 | irif_index = rif ? mlxsw_sp_rif_index(rif) : 0; |
512 | |
513 | mlxsw_reg_svfa_vid_pack(payload: svfa_pl, valid, fid: fid->fid_index, vid, irif_v: irif_valid, |
514 | irif: irif_index); |
515 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(svfa), payload: svfa_pl); |
516 | } |
517 | |
518 | static int |
519 | mlxsw_sp_fid_8021q_vid_to_fid_rif_update(const struct mlxsw_sp_fid *fid, |
520 | const struct mlxsw_sp_rif *rif) |
521 | { |
522 | struct mlxsw_sp_fid_8021q *fid_8021q = mlxsw_sp_fid_8021q_fid(fid); |
523 | |
524 | /* Update the global VID => FID mapping we created when the FID was |
525 | * configured. |
526 | */ |
527 | return mlxsw_sp_fid_vid_to_fid_map(fid, vid: fid_8021q->vid, valid: true, rif); |
528 | } |
529 | |
530 | static int |
531 | mlxsw_sp_fid_port_vid_to_fid_rif_update_one(const struct mlxsw_sp_fid *fid, |
532 | struct mlxsw_sp_fid_port_vid *pv, |
533 | bool irif_valid, u16 irif_index) |
534 | { |
535 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
536 | char svfa_pl[MLXSW_REG_SVFA_LEN]; |
537 | |
538 | mlxsw_reg_svfa_port_vid_pack(payload: svfa_pl, local_port: pv->local_port, valid: true, |
539 | fid: fid->fid_index, vid: pv->vid, irif_v: irif_valid, |
540 | irif: irif_index); |
541 | |
542 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(svfa), payload: svfa_pl); |
543 | } |
544 | |
545 | static int mlxsw_sp_fid_vid_to_fid_rif_set(const struct mlxsw_sp_fid *fid, |
546 | const struct mlxsw_sp_rif *rif) |
547 | { |
548 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
549 | struct mlxsw_sp_fid_port_vid *pv; |
550 | u16 irif_index; |
551 | int err; |
552 | |
553 | err = fid->fid_family->ops->vid_to_fid_rif_update(fid, rif); |
554 | if (err) |
555 | return err; |
556 | |
557 | irif_index = mlxsw_sp_rif_index(rif); |
558 | |
559 | list_for_each_entry(pv, &fid->port_vid_list, list) { |
560 | /* If port is not in virtual mode, then it does not have any |
561 | * {Port, VID}->FID mappings that need to be updated with the |
562 | * ingress RIF. |
563 | */ |
564 | if (!mlxsw_sp->fid_core->port_fid_mappings[pv->local_port]) |
565 | continue; |
566 | |
567 | err = mlxsw_sp_fid_port_vid_to_fid_rif_update_one(fid, pv, |
568 | irif_valid: true, |
569 | irif_index); |
570 | if (err) |
571 | goto err_port_vid_to_fid_rif_update_one; |
572 | } |
573 | |
574 | return 0; |
575 | |
576 | err_port_vid_to_fid_rif_update_one: |
577 | list_for_each_entry_continue_reverse(pv, &fid->port_vid_list, list) { |
578 | if (!mlxsw_sp->fid_core->port_fid_mappings[pv->local_port]) |
579 | continue; |
580 | |
581 | mlxsw_sp_fid_port_vid_to_fid_rif_update_one(fid, pv, irif_valid: false, irif_index: 0); |
582 | } |
583 | |
584 | fid->fid_family->ops->vid_to_fid_rif_update(fid, NULL); |
585 | return err; |
586 | } |
587 | |
588 | static void mlxsw_sp_fid_vid_to_fid_rif_unset(const struct mlxsw_sp_fid *fid) |
589 | { |
590 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
591 | struct mlxsw_sp_fid_port_vid *pv; |
592 | |
593 | list_for_each_entry(pv, &fid->port_vid_list, list) { |
594 | /* If port is not in virtual mode, then it does not have any |
595 | * {Port, VID}->FID mappings that need to be updated. |
596 | */ |
597 | if (!mlxsw_sp->fid_core->port_fid_mappings[pv->local_port]) |
598 | continue; |
599 | |
600 | mlxsw_sp_fid_port_vid_to_fid_rif_update_one(fid, pv, irif_valid: false, irif_index: 0); |
601 | } |
602 | |
603 | fid->fid_family->ops->vid_to_fid_rif_update(fid, NULL); |
604 | } |
605 | |
606 | static int mlxsw_sp_fid_reiv_handle(struct mlxsw_sp_fid *fid, u16 rif_index, |
607 | bool valid, u8 port_page) |
608 | { |
609 | u16 local_port_end = (port_page + 1) * MLXSW_REG_REIV_REC_MAX_COUNT - 1; |
610 | u16 local_port_start = port_page * MLXSW_REG_REIV_REC_MAX_COUNT; |
611 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
612 | struct mlxsw_sp_fid_port_vid *port_vid; |
613 | u8 rec_num, entries_num = 0; |
614 | char *reiv_pl; |
615 | int err; |
616 | |
617 | reiv_pl = kmalloc(MLXSW_REG_REIV_LEN, GFP_KERNEL); |
618 | if (!reiv_pl) |
619 | return -ENOMEM; |
620 | |
621 | mlxsw_reg_reiv_pack(payload: reiv_pl, port_page, erif: rif_index); |
622 | |
623 | list_for_each_entry(port_vid, &fid->port_vid_list, list) { |
624 | /* port_vid_list is sorted by local_port. */ |
625 | if (port_vid->local_port < local_port_start) |
626 | continue; |
627 | |
628 | if (port_vid->local_port > local_port_end) |
629 | break; |
630 | |
631 | rec_num = port_vid->local_port % MLXSW_REG_REIV_REC_MAX_COUNT; |
632 | mlxsw_reg_reiv_rec_update_set(buf: reiv_pl, index: rec_num, val: true); |
633 | mlxsw_reg_reiv_rec_evid_set(buf: reiv_pl, index: rec_num, |
634 | val: valid ? port_vid->vid : 0); |
635 | entries_num++; |
636 | } |
637 | |
638 | if (!entries_num) { |
639 | kfree(objp: reiv_pl); |
640 | return 0; |
641 | } |
642 | |
643 | err = mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(reiv), payload: reiv_pl); |
644 | if (err) |
645 | goto err_reg_write; |
646 | |
647 | kfree(objp: reiv_pl); |
648 | return 0; |
649 | |
650 | err_reg_write: |
651 | kfree(objp: reiv_pl); |
652 | return err; |
653 | } |
654 | |
655 | static int mlxsw_sp_fid_erif_eport_to_vid_map(struct mlxsw_sp_fid *fid, |
656 | u16 rif_index, bool valid) |
657 | { |
658 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
659 | u8 num_port_pages; |
660 | int err, i; |
661 | |
662 | num_port_pages = mlxsw_core_max_ports(mlxsw_core: mlxsw_sp->core) / |
663 | MLXSW_REG_REIV_REC_MAX_COUNT + 1; |
664 | |
665 | for (i = 0; i < num_port_pages; i++) { |
666 | err = mlxsw_sp_fid_reiv_handle(fid, rif_index, valid, port_page: i); |
667 | if (err) |
668 | goto err_reiv_handle; |
669 | } |
670 | |
671 | return 0; |
672 | |
673 | err_reiv_handle: |
674 | for (; i >= 0; i--) |
675 | mlxsw_sp_fid_reiv_handle(fid, rif_index, valid: !valid, port_page: i); |
676 | return err; |
677 | } |
678 | |
679 | int mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif) |
680 | { |
681 | u16 rif_index = mlxsw_sp_rif_index(rif); |
682 | int err; |
683 | |
684 | err = mlxsw_sp_fid_to_fid_rif_update(fid, rif); |
685 | if (err) |
686 | return err; |
687 | |
688 | err = mlxsw_sp_fid_vni_to_fid_rif_update(fid, rif); |
689 | if (err) |
690 | goto err_vni_to_fid_rif_update; |
691 | |
692 | err = mlxsw_sp_fid_vid_to_fid_rif_set(fid, rif); |
693 | if (err) |
694 | goto err_vid_to_fid_rif_set; |
695 | |
696 | err = mlxsw_sp_fid_erif_eport_to_vid_map(fid, rif_index, valid: true); |
697 | if (err) |
698 | goto err_erif_eport_to_vid_map; |
699 | |
700 | fid->rif = rif; |
701 | return 0; |
702 | |
703 | err_erif_eport_to_vid_map: |
704 | mlxsw_sp_fid_vid_to_fid_rif_unset(fid); |
705 | err_vid_to_fid_rif_set: |
706 | mlxsw_sp_fid_vni_to_fid_rif_update(fid, NULL); |
707 | err_vni_to_fid_rif_update: |
708 | mlxsw_sp_fid_to_fid_rif_update(fid, NULL); |
709 | return err; |
710 | } |
711 | |
712 | void mlxsw_sp_fid_rif_unset(struct mlxsw_sp_fid *fid) |
713 | { |
714 | u16 rif_index; |
715 | |
716 | if (!fid->rif) |
717 | return; |
718 | |
719 | rif_index = mlxsw_sp_rif_index(rif: fid->rif); |
720 | fid->rif = NULL; |
721 | |
722 | mlxsw_sp_fid_erif_eport_to_vid_map(fid, rif_index, valid: false); |
723 | mlxsw_sp_fid_vid_to_fid_rif_unset(fid); |
724 | mlxsw_sp_fid_vni_to_fid_rif_update(fid, NULL); |
725 | mlxsw_sp_fid_to_fid_rif_update(fid, NULL); |
726 | } |
727 | |
728 | static int mlxsw_sp_fid_vni_op(const struct mlxsw_sp_fid *fid) |
729 | { |
730 | int err; |
731 | |
732 | err = mlxsw_sp_fid_vni_to_fid_map(fid, rif: fid->rif, valid: fid->vni_valid); |
733 | if (err) |
734 | return err; |
735 | |
736 | err = mlxsw_sp_fid_edit_op(fid, rif: fid->rif); |
737 | if (err) |
738 | goto err_fid_edit_op; |
739 | |
740 | return 0; |
741 | |
742 | err_fid_edit_op: |
743 | mlxsw_sp_fid_vni_to_fid_map(fid, rif: fid->rif, valid: !fid->vni_valid); |
744 | return err; |
745 | } |
746 | |
747 | static int __mlxsw_sp_fid_port_vid_map(const struct mlxsw_sp_fid *fid, |
748 | u16 local_port, u16 vid, bool valid) |
749 | { |
750 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
751 | char svfa_pl[MLXSW_REG_SVFA_LEN]; |
752 | bool irif_valid = false; |
753 | u16 irif_index = 0; |
754 | |
755 | if (fid->rif) { |
756 | irif_valid = true; |
757 | irif_index = mlxsw_sp_rif_index(rif: fid->rif); |
758 | } |
759 | |
760 | mlxsw_reg_svfa_port_vid_pack(payload: svfa_pl, local_port, valid, fid: fid->fid_index, |
761 | vid, irif_v: irif_valid, irif: irif_index); |
762 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(svfa), payload: svfa_pl); |
763 | } |
764 | |
765 | static struct mlxsw_sp_fid_8021d * |
766 | mlxsw_sp_fid_8021d_fid(const struct mlxsw_sp_fid *fid) |
767 | { |
768 | return container_of(fid, struct mlxsw_sp_fid_8021d, common); |
769 | } |
770 | |
771 | static void mlxsw_sp_fid_8021d_setup(struct mlxsw_sp_fid *fid, const void *arg) |
772 | { |
773 | int br_ifindex = *(int *) arg; |
774 | |
775 | mlxsw_sp_fid_8021d_fid(fid)->br_ifindex = br_ifindex; |
776 | fid->fid_offset = fid->fid_index - fid->fid_family->start_index; |
777 | } |
778 | |
779 | static int mlxsw_sp_fid_8021d_configure(struct mlxsw_sp_fid *fid) |
780 | { |
781 | return mlxsw_sp_fid_op(fid, valid: true); |
782 | } |
783 | |
784 | static void mlxsw_sp_fid_8021d_deconfigure(struct mlxsw_sp_fid *fid) |
785 | { |
786 | if (fid->vni_valid) |
787 | mlxsw_sp_nve_fid_disable(mlxsw_sp: fid->fid_family->mlxsw_sp, fid); |
788 | mlxsw_sp_fid_op(fid, valid: false); |
789 | } |
790 | |
791 | static int mlxsw_sp_fid_8021d_index_alloc(struct mlxsw_sp_fid *fid, |
792 | const void *arg, u16 *p_fid_index) |
793 | { |
794 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
795 | u16 nr_fids, fid_index; |
796 | |
797 | nr_fids = fid_family->end_index - fid_family->start_index + 1; |
798 | fid_index = find_first_zero_bit(addr: fid_family->fids_bitmap, size: nr_fids); |
799 | if (fid_index == nr_fids) |
800 | return -ENOBUFS; |
801 | *p_fid_index = fid_family->start_index + fid_index; |
802 | |
803 | return 0; |
804 | } |
805 | |
806 | static bool |
807 | mlxsw_sp_fid_8021d_compare(const struct mlxsw_sp_fid *fid, const void *arg) |
808 | { |
809 | int br_ifindex = *(int *) arg; |
810 | |
811 | return mlxsw_sp_fid_8021d_fid(fid)->br_ifindex == br_ifindex; |
812 | } |
813 | |
814 | static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) |
815 | { |
816 | struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; |
817 | int err; |
818 | |
819 | list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list, |
820 | list) { |
821 | struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; |
822 | u16 vid = mlxsw_sp_port_vlan->vid; |
823 | |
824 | if (!fid) |
825 | continue; |
826 | |
827 | err = __mlxsw_sp_fid_port_vid_map(fid, |
828 | local_port: mlxsw_sp_port->local_port, |
829 | vid, valid: true); |
830 | if (err) |
831 | goto err_fid_port_vid_map; |
832 | } |
833 | |
834 | err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, enable: true); |
835 | if (err) |
836 | goto err_port_vp_mode_set; |
837 | |
838 | return 0; |
839 | |
840 | err_port_vp_mode_set: |
841 | err_fid_port_vid_map: |
842 | list_for_each_entry_continue_reverse(mlxsw_sp_port_vlan, |
843 | &mlxsw_sp_port->vlans_list, list) { |
844 | struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; |
845 | u16 vid = mlxsw_sp_port_vlan->vid; |
846 | |
847 | if (!fid) |
848 | continue; |
849 | |
850 | __mlxsw_sp_fid_port_vid_map(fid, local_port: mlxsw_sp_port->local_port, vid, |
851 | valid: false); |
852 | } |
853 | return err; |
854 | } |
855 | |
856 | static void mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) |
857 | { |
858 | struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; |
859 | |
860 | mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, enable: false); |
861 | |
862 | list_for_each_entry_reverse(mlxsw_sp_port_vlan, |
863 | &mlxsw_sp_port->vlans_list, list) { |
864 | struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid; |
865 | u16 vid = mlxsw_sp_port_vlan->vid; |
866 | |
867 | if (!fid) |
868 | continue; |
869 | |
870 | __mlxsw_sp_fid_port_vid_map(fid, local_port: mlxsw_sp_port->local_port, vid, |
871 | valid: false); |
872 | } |
873 | } |
874 | |
875 | static int |
876 | mlxsw_sp_fid_port_vid_list_add(struct mlxsw_sp_fid *fid, u16 local_port, |
877 | u16 vid) |
878 | { |
879 | struct mlxsw_sp_fid_port_vid *port_vid, *tmp_port_vid; |
880 | |
881 | port_vid = kzalloc(size: sizeof(*port_vid), GFP_KERNEL); |
882 | if (!port_vid) |
883 | return -ENOMEM; |
884 | |
885 | port_vid->local_port = local_port; |
886 | port_vid->vid = vid; |
887 | |
888 | list_for_each_entry(tmp_port_vid, &fid->port_vid_list, list) { |
889 | if (tmp_port_vid->local_port > local_port) |
890 | break; |
891 | } |
892 | |
893 | list_add_tail(new: &port_vid->list, head: &tmp_port_vid->list); |
894 | return 0; |
895 | } |
896 | |
897 | static void |
898 | mlxsw_sp_fid_port_vid_list_del(struct mlxsw_sp_fid *fid, u16 local_port, |
899 | u16 vid) |
900 | { |
901 | struct mlxsw_sp_fid_port_vid *port_vid, *tmp; |
902 | |
903 | list_for_each_entry_safe(port_vid, tmp, &fid->port_vid_list, list) { |
904 | if (port_vid->local_port != local_port || port_vid->vid != vid) |
905 | continue; |
906 | |
907 | list_del(entry: &port_vid->list); |
908 | kfree(objp: port_vid); |
909 | return; |
910 | } |
911 | } |
912 | |
913 | static int |
914 | mlxsw_sp_fid_mpe_table_map(const struct mlxsw_sp_fid *fid, u16 local_port, |
915 | u16 vid, bool valid) |
916 | { |
917 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
918 | char smpe_pl[MLXSW_REG_SMPE_LEN]; |
919 | |
920 | mlxsw_reg_smpe_pack(payload: smpe_pl, local_port, smpe_index: fid->fid_index, |
921 | evid: valid ? vid : 0); |
922 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(smpe), payload: smpe_pl); |
923 | } |
924 | |
925 | static int |
926 | mlxsw_sp_fid_erif_eport_to_vid_map_one(const struct mlxsw_sp_fid *fid, |
927 | u16 local_port, u16 vid, bool valid) |
928 | { |
929 | u8 port_page = local_port / MLXSW_REG_REIV_REC_MAX_COUNT; |
930 | u8 rec_num = local_port % MLXSW_REG_REIV_REC_MAX_COUNT; |
931 | struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; |
932 | u16 rif_index = mlxsw_sp_rif_index(rif: fid->rif); |
933 | char *reiv_pl; |
934 | int err; |
935 | |
936 | reiv_pl = kmalloc(MLXSW_REG_REIV_LEN, GFP_KERNEL); |
937 | if (!reiv_pl) |
938 | return -ENOMEM; |
939 | |
940 | mlxsw_reg_reiv_pack(payload: reiv_pl, port_page, erif: rif_index); |
941 | mlxsw_reg_reiv_rec_update_set(buf: reiv_pl, index: rec_num, val: true); |
942 | mlxsw_reg_reiv_rec_evid_set(buf: reiv_pl, index: rec_num, val: valid ? vid : 0); |
943 | err = mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(reiv), payload: reiv_pl); |
944 | kfree(objp: reiv_pl); |
945 | return err; |
946 | } |
947 | |
948 | static int mlxsw_sp_fid_evid_map(const struct mlxsw_sp_fid *fid, u16 local_port, |
949 | u16 vid, bool valid) |
950 | { |
951 | int err; |
952 | |
953 | err = mlxsw_sp_fid_mpe_table_map(fid, local_port, vid, valid); |
954 | if (err) |
955 | return err; |
956 | |
957 | if (!fid->rif) |
958 | return 0; |
959 | |
960 | err = mlxsw_sp_fid_erif_eport_to_vid_map_one(fid, local_port, vid, |
961 | valid); |
962 | if (err) |
963 | goto err_erif_eport_to_vid_map_one; |
964 | |
965 | return 0; |
966 | |
967 | err_erif_eport_to_vid_map_one: |
968 | mlxsw_sp_fid_mpe_table_map(fid, local_port, vid, valid: !valid); |
969 | return err; |
970 | } |
971 | |
972 | static int mlxsw_sp_fid_8021d_port_vid_map(struct mlxsw_sp_fid *fid, |
973 | struct mlxsw_sp_port *mlxsw_sp_port, |
974 | u16 vid) |
975 | { |
976 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
977 | u16 local_port = mlxsw_sp_port->local_port; |
978 | int err; |
979 | |
980 | err = __mlxsw_sp_fid_port_vid_map(fid, local_port: mlxsw_sp_port->local_port, vid, |
981 | valid: true); |
982 | if (err) |
983 | return err; |
984 | |
985 | err = mlxsw_sp_fid_evid_map(fid, local_port, vid, valid: true); |
986 | if (err) |
987 | goto err_fid_evid_map; |
988 | |
989 | err = mlxsw_sp_fid_port_vid_list_add(fid, local_port: mlxsw_sp_port->local_port, |
990 | vid); |
991 | if (err) |
992 | goto err_port_vid_list_add; |
993 | |
994 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port]++ == 0) { |
995 | err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port); |
996 | if (err) |
997 | goto err_port_vp_mode_trans; |
998 | } |
999 | |
1000 | return 0; |
1001 | |
1002 | err_port_vp_mode_trans: |
1003 | mlxsw_sp->fid_core->port_fid_mappings[local_port]--; |
1004 | mlxsw_sp_fid_port_vid_list_del(fid, local_port: mlxsw_sp_port->local_port, vid); |
1005 | err_port_vid_list_add: |
1006 | mlxsw_sp_fid_evid_map(fid, local_port, vid, valid: false); |
1007 | err_fid_evid_map: |
1008 | __mlxsw_sp_fid_port_vid_map(fid, local_port: mlxsw_sp_port->local_port, vid, valid: false); |
1009 | return err; |
1010 | } |
1011 | |
1012 | static void |
1013 | mlxsw_sp_fid_8021d_port_vid_unmap(struct mlxsw_sp_fid *fid, |
1014 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) |
1015 | { |
1016 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
1017 | u16 local_port = mlxsw_sp_port->local_port; |
1018 | |
1019 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port] == 1) |
1020 | mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); |
1021 | mlxsw_sp->fid_core->port_fid_mappings[local_port]--; |
1022 | mlxsw_sp_fid_port_vid_list_del(fid, local_port: mlxsw_sp_port->local_port, vid); |
1023 | mlxsw_sp_fid_evid_map(fid, local_port, vid, valid: false); |
1024 | __mlxsw_sp_fid_port_vid_map(fid, local_port: mlxsw_sp_port->local_port, vid, valid: false); |
1025 | } |
1026 | |
1027 | static int mlxsw_sp_fid_8021d_vni_set(struct mlxsw_sp_fid *fid) |
1028 | { |
1029 | return mlxsw_sp_fid_vni_op(fid); |
1030 | } |
1031 | |
1032 | static void mlxsw_sp_fid_8021d_vni_clear(struct mlxsw_sp_fid *fid) |
1033 | { |
1034 | mlxsw_sp_fid_vni_op(fid); |
1035 | } |
1036 | |
1037 | static int mlxsw_sp_fid_8021d_nve_flood_index_set(struct mlxsw_sp_fid *fid) |
1038 | { |
1039 | return mlxsw_sp_fid_edit_op(fid, rif: fid->rif); |
1040 | } |
1041 | |
1042 | static void mlxsw_sp_fid_8021d_nve_flood_index_clear(struct mlxsw_sp_fid *fid) |
1043 | { |
1044 | mlxsw_sp_fid_edit_op(fid, rif: fid->rif); |
1045 | } |
1046 | |
1047 | static void |
1048 | mlxsw_sp_fid_8021d_fdb_clear_offload(const struct mlxsw_sp_fid *fid, |
1049 | const struct net_device *nve_dev) |
1050 | { |
1051 | br_fdb_clear_offload(dev: nve_dev, vid: 0); |
1052 | } |
1053 | |
1054 | static int |
1055 | mlxsw_sp_fid_8021d_vid_to_fid_rif_update(const struct mlxsw_sp_fid *fid, |
1056 | const struct mlxsw_sp_rif *rif) |
1057 | { |
1058 | return 0; |
1059 | } |
1060 | |
1061 | static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = { |
1062 | .setup = mlxsw_sp_fid_8021d_setup, |
1063 | .configure = mlxsw_sp_fid_8021d_configure, |
1064 | .deconfigure = mlxsw_sp_fid_8021d_deconfigure, |
1065 | .index_alloc = mlxsw_sp_fid_8021d_index_alloc, |
1066 | .compare = mlxsw_sp_fid_8021d_compare, |
1067 | .port_vid_map = mlxsw_sp_fid_8021d_port_vid_map, |
1068 | .port_vid_unmap = mlxsw_sp_fid_8021d_port_vid_unmap, |
1069 | .vni_set = mlxsw_sp_fid_8021d_vni_set, |
1070 | .vni_clear = mlxsw_sp_fid_8021d_vni_clear, |
1071 | .nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set, |
1072 | .nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear, |
1073 | .fdb_clear_offload = mlxsw_sp_fid_8021d_fdb_clear_offload, |
1074 | .vid_to_fid_rif_update = mlxsw_sp_fid_8021d_vid_to_fid_rif_update, |
1075 | }; |
1076 | |
1077 | #define MLXSW_SP_FID_8021Q_MAX (VLAN_N_VID - 2) |
1078 | #define MLXSW_SP_FID_RFID_MAX (11 * 1024) |
1079 | |
1080 | static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021d_flood_tables[] = { |
1081 | { |
1082 | .packet_type = MLXSW_SP_FLOOD_TYPE_UC, |
1083 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, |
1084 | .table_index = 0, |
1085 | }, |
1086 | { |
1087 | .packet_type = MLXSW_SP_FLOOD_TYPE_MC, |
1088 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, |
1089 | .table_index = 1, |
1090 | }, |
1091 | { |
1092 | .packet_type = MLXSW_SP_FLOOD_TYPE_BC, |
1093 | .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, |
1094 | .table_index = 2, |
1095 | }, |
1096 | }; |
1097 | |
1098 | static bool |
1099 | mlxsw_sp_fid_8021q_compare(const struct mlxsw_sp_fid *fid, const void *arg) |
1100 | { |
1101 | u16 vid = *(u16 *) arg; |
1102 | |
1103 | return mlxsw_sp_fid_8021q_fid(fid)->vid == vid; |
1104 | } |
1105 | |
1106 | static void |
1107 | mlxsw_sp_fid_8021q_fdb_clear_offload(const struct mlxsw_sp_fid *fid, |
1108 | const struct net_device *nve_dev) |
1109 | { |
1110 | br_fdb_clear_offload(dev: nve_dev, vid: mlxsw_sp_fid_8021q_vid(fid)); |
1111 | } |
1112 | |
1113 | static void mlxsw_sp_fid_rfid_setup(struct mlxsw_sp_fid *fid, const void *arg) |
1114 | { |
1115 | fid->fid_offset = 0; |
1116 | } |
1117 | |
1118 | static int mlxsw_sp_fid_rfid_configure(struct mlxsw_sp_fid *fid) |
1119 | { |
1120 | return mlxsw_sp_fid_op(fid, valid: true); |
1121 | } |
1122 | |
1123 | static void mlxsw_sp_fid_rfid_deconfigure(struct mlxsw_sp_fid *fid) |
1124 | { |
1125 | mlxsw_sp_fid_op(fid, valid: false); |
1126 | } |
1127 | |
1128 | static int mlxsw_sp_fid_rfid_index_alloc(struct mlxsw_sp_fid *fid, |
1129 | const void *arg, u16 *p_fid_index) |
1130 | { |
1131 | u16 rif_index = *(u16 *) arg; |
1132 | |
1133 | *p_fid_index = fid->fid_family->start_index + rif_index; |
1134 | |
1135 | return 0; |
1136 | } |
1137 | |
1138 | static bool mlxsw_sp_fid_rfid_compare(const struct mlxsw_sp_fid *fid, |
1139 | const void *arg) |
1140 | { |
1141 | u16 rif_index = *(u16 *) arg; |
1142 | |
1143 | return fid->fid_index == rif_index + fid->fid_family->start_index; |
1144 | } |
1145 | |
1146 | static int mlxsw_sp_fid_rfid_port_vid_map(struct mlxsw_sp_fid *fid, |
1147 | struct mlxsw_sp_port *mlxsw_sp_port, |
1148 | u16 vid) |
1149 | { |
1150 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
1151 | u16 local_port = mlxsw_sp_port->local_port; |
1152 | int err; |
1153 | |
1154 | err = mlxsw_sp_fid_port_vid_list_add(fid, local_port: mlxsw_sp_port->local_port, |
1155 | vid); |
1156 | if (err) |
1157 | return err; |
1158 | |
1159 | /* Using legacy bridge model, we only need to transition the port to |
1160 | * virtual mode since {Port, VID} => FID is done by the firmware upon |
1161 | * RIF creation. Using unified bridge model, we need to map |
1162 | * {Port, VID} => FID and map egress VID. |
1163 | */ |
1164 | err = __mlxsw_sp_fid_port_vid_map(fid, local_port: mlxsw_sp_port->local_port, vid, |
1165 | valid: true); |
1166 | if (err) |
1167 | goto err_port_vid_map; |
1168 | |
1169 | if (fid->rif) { |
1170 | err = mlxsw_sp_fid_erif_eport_to_vid_map_one(fid, local_port, |
1171 | vid, valid: true); |
1172 | if (err) |
1173 | goto err_erif_eport_to_vid_map_one; |
1174 | } |
1175 | |
1176 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port]++ == 0) { |
1177 | err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port); |
1178 | if (err) |
1179 | goto err_port_vp_mode_trans; |
1180 | } |
1181 | |
1182 | return 0; |
1183 | |
1184 | err_port_vp_mode_trans: |
1185 | mlxsw_sp->fid_core->port_fid_mappings[local_port]--; |
1186 | if (fid->rif) |
1187 | mlxsw_sp_fid_erif_eport_to_vid_map_one(fid, local_port, vid, |
1188 | valid: false); |
1189 | err_erif_eport_to_vid_map_one: |
1190 | __mlxsw_sp_fid_port_vid_map(fid, local_port: mlxsw_sp_port->local_port, vid, valid: false); |
1191 | err_port_vid_map: |
1192 | mlxsw_sp_fid_port_vid_list_del(fid, local_port: mlxsw_sp_port->local_port, vid); |
1193 | return err; |
1194 | } |
1195 | |
1196 | static void |
1197 | mlxsw_sp_fid_rfid_port_vid_unmap(struct mlxsw_sp_fid *fid, |
1198 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) |
1199 | { |
1200 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
1201 | u16 local_port = mlxsw_sp_port->local_port; |
1202 | |
1203 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port] == 1) |
1204 | mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port); |
1205 | mlxsw_sp->fid_core->port_fid_mappings[local_port]--; |
1206 | |
1207 | if (fid->rif) |
1208 | mlxsw_sp_fid_erif_eport_to_vid_map_one(fid, local_port, vid, |
1209 | valid: false); |
1210 | __mlxsw_sp_fid_port_vid_map(fid, local_port: mlxsw_sp_port->local_port, vid, valid: false); |
1211 | mlxsw_sp_fid_port_vid_list_del(fid, local_port: mlxsw_sp_port->local_port, vid); |
1212 | } |
1213 | |
1214 | static int mlxsw_sp_fid_rfid_vni_set(struct mlxsw_sp_fid *fid) |
1215 | { |
1216 | return -EOPNOTSUPP; |
1217 | } |
1218 | |
1219 | static void mlxsw_sp_fid_rfid_vni_clear(struct mlxsw_sp_fid *fid) |
1220 | { |
1221 | WARN_ON_ONCE(1); |
1222 | } |
1223 | |
1224 | static int mlxsw_sp_fid_rfid_nve_flood_index_set(struct mlxsw_sp_fid *fid) |
1225 | { |
1226 | return -EOPNOTSUPP; |
1227 | } |
1228 | |
1229 | static void mlxsw_sp_fid_rfid_nve_flood_index_clear(struct mlxsw_sp_fid *fid) |
1230 | { |
1231 | WARN_ON_ONCE(1); |
1232 | } |
1233 | |
1234 | static int |
1235 | mlxsw_sp_fid_rfid_vid_to_fid_rif_update(const struct mlxsw_sp_fid *fid, |
1236 | const struct mlxsw_sp_rif *rif) |
1237 | { |
1238 | return 0; |
1239 | } |
1240 | |
1241 | static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_rfid_ops = { |
1242 | .setup = mlxsw_sp_fid_rfid_setup, |
1243 | .configure = mlxsw_sp_fid_rfid_configure, |
1244 | .deconfigure = mlxsw_sp_fid_rfid_deconfigure, |
1245 | .index_alloc = mlxsw_sp_fid_rfid_index_alloc, |
1246 | .compare = mlxsw_sp_fid_rfid_compare, |
1247 | .port_vid_map = mlxsw_sp_fid_rfid_port_vid_map, |
1248 | .port_vid_unmap = mlxsw_sp_fid_rfid_port_vid_unmap, |
1249 | .vni_set = mlxsw_sp_fid_rfid_vni_set, |
1250 | .vni_clear = mlxsw_sp_fid_rfid_vni_clear, |
1251 | .nve_flood_index_set = mlxsw_sp_fid_rfid_nve_flood_index_set, |
1252 | .nve_flood_index_clear = mlxsw_sp_fid_rfid_nve_flood_index_clear, |
1253 | .vid_to_fid_rif_update = mlxsw_sp_fid_rfid_vid_to_fid_rif_update, |
1254 | }; |
1255 | |
1256 | static void mlxsw_sp_fid_dummy_setup(struct mlxsw_sp_fid *fid, const void *arg) |
1257 | { |
1258 | fid->fid_offset = 0; |
1259 | } |
1260 | |
1261 | static int mlxsw_sp_fid_dummy_configure(struct mlxsw_sp_fid *fid) |
1262 | { |
1263 | return mlxsw_sp_fid_op(fid, valid: true); |
1264 | } |
1265 | |
1266 | static void mlxsw_sp_fid_dummy_deconfigure(struct mlxsw_sp_fid *fid) |
1267 | { |
1268 | mlxsw_sp_fid_op(fid, valid: false); |
1269 | } |
1270 | |
1271 | static int mlxsw_sp_fid_dummy_index_alloc(struct mlxsw_sp_fid *fid, |
1272 | const void *arg, u16 *p_fid_index) |
1273 | { |
1274 | *p_fid_index = fid->fid_family->start_index; |
1275 | |
1276 | return 0; |
1277 | } |
1278 | |
1279 | static bool mlxsw_sp_fid_dummy_compare(const struct mlxsw_sp_fid *fid, |
1280 | const void *arg) |
1281 | { |
1282 | return true; |
1283 | } |
1284 | |
1285 | static int mlxsw_sp_fid_dummy_vni_set(struct mlxsw_sp_fid *fid) |
1286 | { |
1287 | return -EOPNOTSUPP; |
1288 | } |
1289 | |
1290 | static void mlxsw_sp_fid_dummy_vni_clear(struct mlxsw_sp_fid *fid) |
1291 | { |
1292 | WARN_ON_ONCE(1); |
1293 | } |
1294 | |
1295 | static int mlxsw_sp_fid_dummy_nve_flood_index_set(struct mlxsw_sp_fid *fid) |
1296 | { |
1297 | return -EOPNOTSUPP; |
1298 | } |
1299 | |
1300 | static void mlxsw_sp_fid_dummy_nve_flood_index_clear(struct mlxsw_sp_fid *fid) |
1301 | { |
1302 | WARN_ON_ONCE(1); |
1303 | } |
1304 | |
1305 | static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_dummy_ops = { |
1306 | .setup = mlxsw_sp_fid_dummy_setup, |
1307 | .configure = mlxsw_sp_fid_dummy_configure, |
1308 | .deconfigure = mlxsw_sp_fid_dummy_deconfigure, |
1309 | .index_alloc = mlxsw_sp_fid_dummy_index_alloc, |
1310 | .compare = mlxsw_sp_fid_dummy_compare, |
1311 | .vni_set = mlxsw_sp_fid_dummy_vni_set, |
1312 | .vni_clear = mlxsw_sp_fid_dummy_vni_clear, |
1313 | .nve_flood_index_set = mlxsw_sp_fid_dummy_nve_flood_index_set, |
1314 | .nve_flood_index_clear = mlxsw_sp_fid_dummy_nve_flood_index_clear, |
1315 | }; |
1316 | |
1317 | static int mlxsw_sp_fid_8021q_configure(struct mlxsw_sp_fid *fid) |
1318 | { |
1319 | struct mlxsw_sp_fid_8021q *fid_8021q = mlxsw_sp_fid_8021q_fid(fid); |
1320 | int err; |
1321 | |
1322 | err = mlxsw_sp_fid_op(fid, valid: true); |
1323 | if (err) |
1324 | return err; |
1325 | |
1326 | err = mlxsw_sp_fid_vid_to_fid_map(fid, vid: fid_8021q->vid, valid: true, rif: fid->rif); |
1327 | if (err) |
1328 | goto err_vid_to_fid_map; |
1329 | |
1330 | return 0; |
1331 | |
1332 | err_vid_to_fid_map: |
1333 | mlxsw_sp_fid_op(fid, valid: false); |
1334 | return err; |
1335 | } |
1336 | |
1337 | static void mlxsw_sp_fid_8021q_deconfigure(struct mlxsw_sp_fid *fid) |
1338 | { |
1339 | struct mlxsw_sp_fid_8021q *fid_8021q = mlxsw_sp_fid_8021q_fid(fid); |
1340 | |
1341 | if (fid->vni_valid) |
1342 | mlxsw_sp_nve_fid_disable(mlxsw_sp: fid->fid_family->mlxsw_sp, fid); |
1343 | |
1344 | mlxsw_sp_fid_vid_to_fid_map(fid, vid: fid_8021q->vid, valid: false, NULL); |
1345 | mlxsw_sp_fid_op(fid, valid: false); |
1346 | } |
1347 | |
1348 | static int mlxsw_sp_fid_8021q_port_vid_map(struct mlxsw_sp_fid *fid, |
1349 | struct mlxsw_sp_port *mlxsw_sp_port, |
1350 | u16 vid) |
1351 | { |
1352 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
1353 | u16 local_port = mlxsw_sp_port->local_port; |
1354 | int err; |
1355 | |
1356 | /* In case there are no {Port, VID} => FID mappings on the port, |
1357 | * we can use the global VID => FID mapping we created when the |
1358 | * FID was configured, otherwise, configure new mapping. |
1359 | */ |
1360 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port]) { |
1361 | err = __mlxsw_sp_fid_port_vid_map(fid, local_port, vid, valid: true); |
1362 | if (err) |
1363 | return err; |
1364 | } |
1365 | |
1366 | err = mlxsw_sp_fid_evid_map(fid, local_port, vid, valid: true); |
1367 | if (err) |
1368 | goto err_fid_evid_map; |
1369 | |
1370 | err = mlxsw_sp_fid_port_vid_list_add(fid, local_port: mlxsw_sp_port->local_port, |
1371 | vid); |
1372 | if (err) |
1373 | goto err_port_vid_list_add; |
1374 | |
1375 | return 0; |
1376 | |
1377 | err_port_vid_list_add: |
1378 | mlxsw_sp_fid_evid_map(fid, local_port, vid, valid: false); |
1379 | err_fid_evid_map: |
1380 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port]) |
1381 | __mlxsw_sp_fid_port_vid_map(fid, local_port, vid, valid: false); |
1382 | return err; |
1383 | } |
1384 | |
1385 | static void |
1386 | mlxsw_sp_fid_8021q_port_vid_unmap(struct mlxsw_sp_fid *fid, |
1387 | struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) |
1388 | { |
1389 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
1390 | u16 local_port = mlxsw_sp_port->local_port; |
1391 | |
1392 | mlxsw_sp_fid_port_vid_list_del(fid, local_port: mlxsw_sp_port->local_port, vid); |
1393 | mlxsw_sp_fid_evid_map(fid, local_port, vid, valid: false); |
1394 | if (mlxsw_sp->fid_core->port_fid_mappings[local_port]) |
1395 | __mlxsw_sp_fid_port_vid_map(fid, local_port, vid, valid: false); |
1396 | } |
1397 | |
1398 | static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_ops = { |
1399 | .setup = mlxsw_sp_fid_8021q_setup, |
1400 | .configure = mlxsw_sp_fid_8021q_configure, |
1401 | .deconfigure = mlxsw_sp_fid_8021q_deconfigure, |
1402 | .index_alloc = mlxsw_sp_fid_8021d_index_alloc, |
1403 | .compare = mlxsw_sp_fid_8021q_compare, |
1404 | .port_vid_map = mlxsw_sp_fid_8021q_port_vid_map, |
1405 | .port_vid_unmap = mlxsw_sp_fid_8021q_port_vid_unmap, |
1406 | .vni_set = mlxsw_sp_fid_8021d_vni_set, |
1407 | .vni_clear = mlxsw_sp_fid_8021d_vni_clear, |
1408 | .nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set, |
1409 | .nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear, |
1410 | .fdb_clear_offload = mlxsw_sp_fid_8021q_fdb_clear_offload, |
1411 | .vid_to_fid_rif_update = mlxsw_sp_fid_8021q_vid_to_fid_rif_update, |
1412 | }; |
1413 | |
1414 | /* There are 4K-2 802.1Q FIDs */ |
1415 | #define MLXSW_SP_FID_8021Q_START 1 /* FID 0 is reserved. */ |
1416 | #define MLXSW_SP_FID_8021Q_END (MLXSW_SP_FID_8021Q_START + \ |
1417 | MLXSW_SP_FID_8021Q_MAX - 1) |
1418 | |
1419 | /* There are 1K 802.1D FIDs */ |
1420 | #define MLXSW_SP_FID_8021D_START (MLXSW_SP_FID_8021Q_END + 1) |
1421 | #define MLXSW_SP_FID_8021D_END (MLXSW_SP_FID_8021D_START + \ |
1422 | MLXSW_SP_FID_8021D_MAX - 1) |
1423 | |
1424 | /* There is one dummy FID */ |
1425 | #define MLXSW_SP_FID_DUMMY (MLXSW_SP_FID_8021D_END + 1) |
1426 | |
1427 | /* There are 11K rFIDs */ |
1428 | #define MLXSW_SP_RFID_START (MLXSW_SP_FID_DUMMY + 1) |
1429 | #define MLXSW_SP_RFID_END (MLXSW_SP_RFID_START + \ |
1430 | MLXSW_SP_FID_RFID_MAX - 1) |
1431 | |
1432 | static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_8021q_family = { |
1433 | .type = MLXSW_SP_FID_TYPE_8021Q, |
1434 | .fid_size = sizeof(struct mlxsw_sp_fid_8021q), |
1435 | .start_index = MLXSW_SP_FID_8021Q_START, |
1436 | .end_index = MLXSW_SP_FID_8021Q_END, |
1437 | .flood_tables = mlxsw_sp_fid_8021d_flood_tables, |
1438 | .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), |
1439 | .rif_type = MLXSW_SP_RIF_TYPE_VLAN, |
1440 | .ops = &mlxsw_sp_fid_8021q_ops, |
1441 | .flood_rsp = false, |
1442 | .bridge_type = MLXSW_REG_BRIDGE_TYPE_0, |
1443 | .smpe_index_valid = false, |
1444 | }; |
1445 | |
1446 | static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_8021d_family = { |
1447 | .type = MLXSW_SP_FID_TYPE_8021D, |
1448 | .fid_size = sizeof(struct mlxsw_sp_fid_8021d), |
1449 | .start_index = MLXSW_SP_FID_8021D_START, |
1450 | .end_index = MLXSW_SP_FID_8021D_END, |
1451 | .flood_tables = mlxsw_sp_fid_8021d_flood_tables, |
1452 | .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), |
1453 | .rif_type = MLXSW_SP_RIF_TYPE_FID, |
1454 | .ops = &mlxsw_sp_fid_8021d_ops, |
1455 | .bridge_type = MLXSW_REG_BRIDGE_TYPE_1, |
1456 | .smpe_index_valid = false, |
1457 | }; |
1458 | |
1459 | static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_dummy_family = { |
1460 | .type = MLXSW_SP_FID_TYPE_DUMMY, |
1461 | .fid_size = sizeof(struct mlxsw_sp_fid), |
1462 | .start_index = MLXSW_SP_FID_DUMMY, |
1463 | .end_index = MLXSW_SP_FID_DUMMY, |
1464 | .ops = &mlxsw_sp_fid_dummy_ops, |
1465 | .smpe_index_valid = false, |
1466 | }; |
1467 | |
1468 | static const struct mlxsw_sp_fid_family mlxsw_sp_fid_rfid_family = { |
1469 | .type = MLXSW_SP_FID_TYPE_RFID, |
1470 | .fid_size = sizeof(struct mlxsw_sp_fid), |
1471 | .start_index = MLXSW_SP_RFID_START, |
1472 | .end_index = MLXSW_SP_RFID_END, |
1473 | .rif_type = MLXSW_SP_RIF_TYPE_SUBPORT, |
1474 | .ops = &mlxsw_sp_fid_rfid_ops, |
1475 | .flood_rsp = true, |
1476 | .smpe_index_valid = false, |
1477 | }; |
1478 | |
1479 | const struct mlxsw_sp_fid_family *mlxsw_sp1_fid_family_arr[] = { |
1480 | [MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp1_fid_8021q_family, |
1481 | [MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp1_fid_8021d_family, |
1482 | [MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp1_fid_dummy_family, |
1483 | [MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family, |
1484 | }; |
1485 | |
1486 | static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021q_family = { |
1487 | .type = MLXSW_SP_FID_TYPE_8021Q, |
1488 | .fid_size = sizeof(struct mlxsw_sp_fid_8021q), |
1489 | .start_index = MLXSW_SP_FID_8021Q_START, |
1490 | .end_index = MLXSW_SP_FID_8021Q_END, |
1491 | .flood_tables = mlxsw_sp_fid_8021d_flood_tables, |
1492 | .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), |
1493 | .rif_type = MLXSW_SP_RIF_TYPE_VLAN, |
1494 | .ops = &mlxsw_sp_fid_8021q_ops, |
1495 | .flood_rsp = false, |
1496 | .bridge_type = MLXSW_REG_BRIDGE_TYPE_0, |
1497 | .smpe_index_valid = true, |
1498 | }; |
1499 | |
1500 | static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021d_family = { |
1501 | .type = MLXSW_SP_FID_TYPE_8021D, |
1502 | .fid_size = sizeof(struct mlxsw_sp_fid_8021d), |
1503 | .start_index = MLXSW_SP_FID_8021D_START, |
1504 | .end_index = MLXSW_SP_FID_8021D_END, |
1505 | .flood_tables = mlxsw_sp_fid_8021d_flood_tables, |
1506 | .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), |
1507 | .rif_type = MLXSW_SP_RIF_TYPE_FID, |
1508 | .ops = &mlxsw_sp_fid_8021d_ops, |
1509 | .bridge_type = MLXSW_REG_BRIDGE_TYPE_1, |
1510 | .smpe_index_valid = true, |
1511 | }; |
1512 | |
1513 | static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_dummy_family = { |
1514 | .type = MLXSW_SP_FID_TYPE_DUMMY, |
1515 | .fid_size = sizeof(struct mlxsw_sp_fid), |
1516 | .start_index = MLXSW_SP_FID_DUMMY, |
1517 | .end_index = MLXSW_SP_FID_DUMMY, |
1518 | .ops = &mlxsw_sp_fid_dummy_ops, |
1519 | .smpe_index_valid = false, |
1520 | }; |
1521 | |
1522 | const struct mlxsw_sp_fid_family *mlxsw_sp2_fid_family_arr[] = { |
1523 | [MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp2_fid_8021q_family, |
1524 | [MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp2_fid_8021d_family, |
1525 | [MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp2_fid_dummy_family, |
1526 | [MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family, |
1527 | }; |
1528 | |
1529 | static struct mlxsw_sp_fid *mlxsw_sp_fid_lookup(struct mlxsw_sp *mlxsw_sp, |
1530 | enum mlxsw_sp_fid_type type, |
1531 | const void *arg) |
1532 | { |
1533 | struct mlxsw_sp_fid_family *fid_family; |
1534 | struct mlxsw_sp_fid *fid; |
1535 | |
1536 | fid_family = mlxsw_sp->fid_core->fid_family_arr[type]; |
1537 | list_for_each_entry(fid, &fid_family->fids_list, list) { |
1538 | if (!fid->fid_family->ops->compare(fid, arg)) |
1539 | continue; |
1540 | refcount_inc(r: &fid->ref_count); |
1541 | return fid; |
1542 | } |
1543 | |
1544 | return NULL; |
1545 | } |
1546 | |
1547 | static struct mlxsw_sp_fid *mlxsw_sp_fid_get(struct mlxsw_sp *mlxsw_sp, |
1548 | enum mlxsw_sp_fid_type type, |
1549 | const void *arg) |
1550 | { |
1551 | struct mlxsw_sp_fid_family *fid_family; |
1552 | struct mlxsw_sp_fid *fid; |
1553 | u16 fid_index; |
1554 | int err; |
1555 | |
1556 | fid = mlxsw_sp_fid_lookup(mlxsw_sp, type, arg); |
1557 | if (fid) |
1558 | return fid; |
1559 | |
1560 | fid_family = mlxsw_sp->fid_core->fid_family_arr[type]; |
1561 | fid = kzalloc(size: fid_family->fid_size, GFP_KERNEL); |
1562 | if (!fid) |
1563 | return ERR_PTR(error: -ENOMEM); |
1564 | |
1565 | INIT_LIST_HEAD(list: &fid->port_vid_list); |
1566 | fid->fid_family = fid_family; |
1567 | |
1568 | err = fid->fid_family->ops->index_alloc(fid, arg, &fid_index); |
1569 | if (err) |
1570 | goto err_index_alloc; |
1571 | fid->fid_index = fid_index; |
1572 | __set_bit(fid_index - fid_family->start_index, fid_family->fids_bitmap); |
1573 | |
1574 | fid->fid_family->ops->setup(fid, arg); |
1575 | |
1576 | err = fid->fid_family->ops->configure(fid); |
1577 | if (err) |
1578 | goto err_configure; |
1579 | |
1580 | err = rhashtable_insert_fast(ht: &mlxsw_sp->fid_core->fid_ht, obj: &fid->ht_node, |
1581 | params: mlxsw_sp_fid_ht_params); |
1582 | if (err) |
1583 | goto err_rhashtable_insert; |
1584 | |
1585 | list_add(new: &fid->list, head: &fid_family->fids_list); |
1586 | refcount_set(r: &fid->ref_count, n: 1); |
1587 | return fid; |
1588 | |
1589 | err_rhashtable_insert: |
1590 | fid->fid_family->ops->deconfigure(fid); |
1591 | err_configure: |
1592 | __clear_bit(fid_index - fid_family->start_index, |
1593 | fid_family->fids_bitmap); |
1594 | err_index_alloc: |
1595 | kfree(objp: fid); |
1596 | return ERR_PTR(error: err); |
1597 | } |
1598 | |
1599 | void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid) |
1600 | { |
1601 | struct mlxsw_sp_fid_family *fid_family = fid->fid_family; |
1602 | struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; |
1603 | |
1604 | if (!refcount_dec_and_test(r: &fid->ref_count)) |
1605 | return; |
1606 | |
1607 | list_del(entry: &fid->list); |
1608 | rhashtable_remove_fast(ht: &mlxsw_sp->fid_core->fid_ht, |
1609 | obj: &fid->ht_node, params: mlxsw_sp_fid_ht_params); |
1610 | fid->fid_family->ops->deconfigure(fid); |
1611 | __clear_bit(fid->fid_index - fid_family->start_index, |
1612 | fid_family->fids_bitmap); |
1613 | WARN_ON_ONCE(!list_empty(&fid->port_vid_list)); |
1614 | kfree(objp: fid); |
1615 | } |
1616 | |
1617 | struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_get(struct mlxsw_sp *mlxsw_sp, u16 vid) |
1618 | { |
1619 | return mlxsw_sp_fid_get(mlxsw_sp, type: MLXSW_SP_FID_TYPE_8021Q, arg: &vid); |
1620 | } |
1621 | |
1622 | struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_get(struct mlxsw_sp *mlxsw_sp, |
1623 | int br_ifindex) |
1624 | { |
1625 | return mlxsw_sp_fid_get(mlxsw_sp, type: MLXSW_SP_FID_TYPE_8021D, arg: &br_ifindex); |
1626 | } |
1627 | |
1628 | struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_lookup(struct mlxsw_sp *mlxsw_sp, |
1629 | u16 vid) |
1630 | { |
1631 | return mlxsw_sp_fid_lookup(mlxsw_sp, type: MLXSW_SP_FID_TYPE_8021Q, arg: &vid); |
1632 | } |
1633 | |
1634 | struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_lookup(struct mlxsw_sp *mlxsw_sp, |
1635 | int br_ifindex) |
1636 | { |
1637 | return mlxsw_sp_fid_lookup(mlxsw_sp, type: MLXSW_SP_FID_TYPE_8021D, |
1638 | arg: &br_ifindex); |
1639 | } |
1640 | |
1641 | struct mlxsw_sp_fid *mlxsw_sp_fid_rfid_get(struct mlxsw_sp *mlxsw_sp, |
1642 | u16 rif_index) |
1643 | { |
1644 | return mlxsw_sp_fid_get(mlxsw_sp, type: MLXSW_SP_FID_TYPE_RFID, arg: &rif_index); |
1645 | } |
1646 | |
1647 | struct mlxsw_sp_fid *mlxsw_sp_fid_dummy_get(struct mlxsw_sp *mlxsw_sp) |
1648 | { |
1649 | return mlxsw_sp_fid_get(mlxsw_sp, type: MLXSW_SP_FID_TYPE_DUMMY, NULL); |
1650 | } |
1651 | |
1652 | static int |
1653 | mlxsw_sp_fid_flood_table_init(struct mlxsw_sp_fid_family *fid_family, |
1654 | const struct mlxsw_sp_flood_table *flood_table) |
1655 | { |
1656 | enum mlxsw_sp_flood_type packet_type = flood_table->packet_type; |
1657 | struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; |
1658 | const int *sfgc_packet_types; |
1659 | u16 mid_base; |
1660 | int err, i; |
1661 | |
1662 | mid_base = mlxsw_sp_fid_flood_table_mid(fid_family, flood_table, fid_offset: 0); |
1663 | |
1664 | sfgc_packet_types = mlxsw_sp_packet_type_sfgc_types[packet_type]; |
1665 | for (i = 0; i < MLXSW_REG_SFGC_TYPE_MAX; i++) { |
1666 | char sfgc_pl[MLXSW_REG_SFGC_LEN]; |
1667 | |
1668 | if (!sfgc_packet_types[i]) |
1669 | continue; |
1670 | |
1671 | mlxsw_reg_sfgc_pack(payload: sfgc_pl, type: i, bridge_type: fid_family->bridge_type, |
1672 | table_type: flood_table->table_type, flood_table: 0, mid_base); |
1673 | |
1674 | err = mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(sfgc), payload: sfgc_pl); |
1675 | if (err) |
1676 | return err; |
1677 | } |
1678 | |
1679 | return 0; |
1680 | } |
1681 | |
1682 | static int |
1683 | mlxsw_sp_fid_flood_tables_init(struct mlxsw_sp_fid_family *fid_family) |
1684 | { |
1685 | struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; |
1686 | u16 pgt_size; |
1687 | int err; |
1688 | int i; |
1689 | |
1690 | if (!fid_family->nr_flood_tables) |
1691 | return 0; |
1692 | |
1693 | pgt_size = mlxsw_sp_fid_family_pgt_size(fid_family); |
1694 | err = mlxsw_sp_pgt_mid_alloc_range(mlxsw_sp, mid_base: &fid_family->pgt_base, |
1695 | count: pgt_size); |
1696 | if (err) |
1697 | return err; |
1698 | |
1699 | for (i = 0; i < fid_family->nr_flood_tables; i++) { |
1700 | const struct mlxsw_sp_flood_table *flood_table; |
1701 | |
1702 | flood_table = &fid_family->flood_tables[i]; |
1703 | err = mlxsw_sp_fid_flood_table_init(fid_family, flood_table); |
1704 | if (err) |
1705 | goto err_flood_table_init; |
1706 | } |
1707 | |
1708 | return 0; |
1709 | |
1710 | err_flood_table_init: |
1711 | mlxsw_sp_pgt_mid_free_range(mlxsw_sp, mid_base: fid_family->pgt_base, count: pgt_size); |
1712 | return err; |
1713 | } |
1714 | |
1715 | static void |
1716 | mlxsw_sp_fid_flood_tables_fini(struct mlxsw_sp_fid_family *fid_family) |
1717 | { |
1718 | struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; |
1719 | u16 pgt_size; |
1720 | |
1721 | if (!fid_family->nr_flood_tables) |
1722 | return; |
1723 | |
1724 | pgt_size = mlxsw_sp_fid_family_pgt_size(fid_family); |
1725 | mlxsw_sp_pgt_mid_free_range(mlxsw_sp, mid_base: fid_family->pgt_base, count: pgt_size); |
1726 | } |
1727 | |
1728 | static int mlxsw_sp_fid_family_register(struct mlxsw_sp *mlxsw_sp, |
1729 | const struct mlxsw_sp_fid_family *tmpl) |
1730 | { |
1731 | u16 nr_fids = tmpl->end_index - tmpl->start_index + 1; |
1732 | struct mlxsw_sp_fid_family *fid_family; |
1733 | int err; |
1734 | |
1735 | fid_family = kmemdup(p: tmpl, size: sizeof(*fid_family), GFP_KERNEL); |
1736 | if (!fid_family) |
1737 | return -ENOMEM; |
1738 | |
1739 | fid_family->mlxsw_sp = mlxsw_sp; |
1740 | INIT_LIST_HEAD(list: &fid_family->fids_list); |
1741 | fid_family->fids_bitmap = bitmap_zalloc(nbits: nr_fids, GFP_KERNEL); |
1742 | if (!fid_family->fids_bitmap) { |
1743 | err = -ENOMEM; |
1744 | goto err_alloc_fids_bitmap; |
1745 | } |
1746 | |
1747 | if (fid_family->flood_tables) { |
1748 | err = mlxsw_sp_fid_flood_tables_init(fid_family); |
1749 | if (err) |
1750 | goto err_fid_flood_tables_init; |
1751 | } |
1752 | |
1753 | mlxsw_sp->fid_core->fid_family_arr[tmpl->type] = fid_family; |
1754 | |
1755 | return 0; |
1756 | |
1757 | err_fid_flood_tables_init: |
1758 | bitmap_free(bitmap: fid_family->fids_bitmap); |
1759 | err_alloc_fids_bitmap: |
1760 | kfree(objp: fid_family); |
1761 | return err; |
1762 | } |
1763 | |
1764 | static void |
1765 | mlxsw_sp_fid_family_unregister(struct mlxsw_sp *mlxsw_sp, |
1766 | struct mlxsw_sp_fid_family *fid_family) |
1767 | { |
1768 | mlxsw_sp->fid_core->fid_family_arr[fid_family->type] = NULL; |
1769 | |
1770 | if (fid_family->flood_tables) |
1771 | mlxsw_sp_fid_flood_tables_fini(fid_family); |
1772 | |
1773 | bitmap_free(bitmap: fid_family->fids_bitmap); |
1774 | WARN_ON_ONCE(!list_empty(&fid_family->fids_list)); |
1775 | kfree(objp: fid_family); |
1776 | } |
1777 | |
1778 | int mlxsw_sp_port_fids_init(struct mlxsw_sp_port *mlxsw_sp_port) |
1779 | { |
1780 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
1781 | |
1782 | /* Track number of FIDs configured on the port with mapping type |
1783 | * PORT_VID_TO_FID, so that we know when to transition the port |
1784 | * back to non-virtual (VLAN) mode. |
1785 | */ |
1786 | mlxsw_sp->fid_core->port_fid_mappings[mlxsw_sp_port->local_port] = 0; |
1787 | |
1788 | return mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, enable: false); |
1789 | } |
1790 | |
1791 | void mlxsw_sp_port_fids_fini(struct mlxsw_sp_port *mlxsw_sp_port) |
1792 | { |
1793 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
1794 | |
1795 | mlxsw_sp->fid_core->port_fid_mappings[mlxsw_sp_port->local_port] = 0; |
1796 | } |
1797 | |
1798 | int mlxsw_sp_fids_init(struct mlxsw_sp *mlxsw_sp) |
1799 | { |
1800 | unsigned int max_ports = mlxsw_core_max_ports(mlxsw_core: mlxsw_sp->core); |
1801 | struct mlxsw_sp_fid_core *fid_core; |
1802 | int err, i; |
1803 | |
1804 | fid_core = kzalloc(size: sizeof(*mlxsw_sp->fid_core), GFP_KERNEL); |
1805 | if (!fid_core) |
1806 | return -ENOMEM; |
1807 | mlxsw_sp->fid_core = fid_core; |
1808 | |
1809 | err = rhashtable_init(ht: &fid_core->fid_ht, params: &mlxsw_sp_fid_ht_params); |
1810 | if (err) |
1811 | goto err_rhashtable_fid_init; |
1812 | |
1813 | err = rhashtable_init(ht: &fid_core->vni_ht, params: &mlxsw_sp_fid_vni_ht_params); |
1814 | if (err) |
1815 | goto err_rhashtable_vni_init; |
1816 | |
1817 | fid_core->port_fid_mappings = kcalloc(n: max_ports, size: sizeof(unsigned int), |
1818 | GFP_KERNEL); |
1819 | if (!fid_core->port_fid_mappings) { |
1820 | err = -ENOMEM; |
1821 | goto err_alloc_port_fid_mappings; |
1822 | } |
1823 | |
1824 | for (i = 0; i < MLXSW_SP_FID_TYPE_MAX; i++) { |
1825 | err = mlxsw_sp_fid_family_register(mlxsw_sp, |
1826 | tmpl: mlxsw_sp->fid_family_arr[i]); |
1827 | |
1828 | if (err) |
1829 | goto err_fid_ops_register; |
1830 | } |
1831 | |
1832 | return 0; |
1833 | |
1834 | err_fid_ops_register: |
1835 | for (i--; i >= 0; i--) { |
1836 | struct mlxsw_sp_fid_family *fid_family; |
1837 | |
1838 | fid_family = fid_core->fid_family_arr[i]; |
1839 | mlxsw_sp_fid_family_unregister(mlxsw_sp, fid_family); |
1840 | } |
1841 | kfree(objp: fid_core->port_fid_mappings); |
1842 | err_alloc_port_fid_mappings: |
1843 | rhashtable_destroy(ht: &fid_core->vni_ht); |
1844 | err_rhashtable_vni_init: |
1845 | rhashtable_destroy(ht: &fid_core->fid_ht); |
1846 | err_rhashtable_fid_init: |
1847 | kfree(objp: fid_core); |
1848 | return err; |
1849 | } |
1850 | |
1851 | void mlxsw_sp_fids_fini(struct mlxsw_sp *mlxsw_sp) |
1852 | { |
1853 | struct mlxsw_sp_fid_core *fid_core = mlxsw_sp->fid_core; |
1854 | int i; |
1855 | |
1856 | for (i = 0; i < MLXSW_SP_FID_TYPE_MAX; i++) |
1857 | mlxsw_sp_fid_family_unregister(mlxsw_sp, |
1858 | fid_family: fid_core->fid_family_arr[i]); |
1859 | kfree(objp: fid_core->port_fid_mappings); |
1860 | rhashtable_destroy(ht: &fid_core->vni_ht); |
1861 | rhashtable_destroy(ht: &fid_core->fid_ht); |
1862 | kfree(objp: fid_core); |
1863 | } |
1864 | |