7e3992a681b30743a8383922977a08a1e97a0e08
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
1 /*
2  * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3  * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5  * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the names of the copyright holders nor the names of its
16  *    contributors may be used to endorse or promote products derived from
17  *    this software without specific prior written permission.
18  *
19  * Alternatively, this software may be distributed under the terms of the
20  * GNU General Public License ("GPL") version 2 as published by the Free
21  * Software Foundation.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #include <linux/kernel.h>
37 #include <linux/types.h>
38 #include <linux/rhashtable.h>
39 #include <linux/bitops.h>
40 #include <linux/in6.h>
41
42 #include "spectrum.h"
43 #include "core.h"
44 #include "reg.h"
45
46 #define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
47         for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
48
49 static bool
50 mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
51                              struct mlxsw_sp_prefix_usage *prefix_usage2)
52 {
53         unsigned char prefix;
54
55         mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
56                 if (!test_bit(prefix, prefix_usage2->b))
57                         return false;
58         }
59         return true;
60 }
61
62 static bool
63 mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
64                          struct mlxsw_sp_prefix_usage *prefix_usage2)
65 {
66         return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
67 }
68
69 static bool
70 mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
71 {
72         struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
73
74         return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
75 }
76
77 static void
78 mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
79                           struct mlxsw_sp_prefix_usage *prefix_usage2)
80 {
81         memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
82 }
83
84 static void
85 mlxsw_sp_prefix_usage_zero(struct mlxsw_sp_prefix_usage *prefix_usage)
86 {
87         memset(prefix_usage, 0, sizeof(*prefix_usage));
88 }
89
90 static void
91 mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
92                           unsigned char prefix_len)
93 {
94         set_bit(prefix_len, prefix_usage->b);
95 }
96
97 static void
98 mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
99                             unsigned char prefix_len)
100 {
101         clear_bit(prefix_len, prefix_usage->b);
102 }
103
104 struct mlxsw_sp_fib_key {
105         unsigned char addr[sizeof(struct in6_addr)];
106         unsigned char prefix_len;
107 };
108
109 enum mlxsw_sp_fib_entry_type {
110         MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
111         MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
112         MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
113 };
114
115 struct mlxsw_sp_fib_entry {
116         struct rhash_head ht_node;
117         struct mlxsw_sp_fib_key key;
118         enum mlxsw_sp_fib_entry_type type;
119         u8 added:1;
120         u16 rif; /* used for action local */
121         struct mlxsw_sp_vr *vr;
122 };
123
124 struct mlxsw_sp_fib {
125         struct rhashtable ht;
126         unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
127         struct mlxsw_sp_prefix_usage prefix_usage;
128 };
129
130 static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
131         .key_offset = offsetof(struct mlxsw_sp_fib_entry, key),
132         .head_offset = offsetof(struct mlxsw_sp_fib_entry, ht_node),
133         .key_len = sizeof(struct mlxsw_sp_fib_key),
134         .automatic_shrinking = true,
135 };
136
137 static int mlxsw_sp_fib_entry_insert(struct mlxsw_sp_fib *fib,
138                                      struct mlxsw_sp_fib_entry *fib_entry)
139 {
140         unsigned char prefix_len = fib_entry->key.prefix_len;
141         int err;
142
143         err = rhashtable_insert_fast(&fib->ht, &fib_entry->ht_node,
144                                      mlxsw_sp_fib_ht_params);
145         if (err)
146                 return err;
147         if (fib->prefix_ref_count[prefix_len]++ == 0)
148                 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
149         return 0;
150 }
151
152 static void mlxsw_sp_fib_entry_remove(struct mlxsw_sp_fib *fib,
153                                       struct mlxsw_sp_fib_entry *fib_entry)
154 {
155         unsigned char prefix_len = fib_entry->key.prefix_len;
156
157         if (--fib->prefix_ref_count[prefix_len] == 0)
158                 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
159         rhashtable_remove_fast(&fib->ht, &fib_entry->ht_node,
160                                mlxsw_sp_fib_ht_params);
161 }
162
163 static struct mlxsw_sp_fib_entry *
164 mlxsw_sp_fib_entry_create(struct mlxsw_sp_fib *fib, const void *addr,
165                           size_t addr_len, unsigned char prefix_len)
166 {
167         struct mlxsw_sp_fib_entry *fib_entry;
168
169         fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
170         if (!fib_entry)
171                 return NULL;
172         memcpy(fib_entry->key.addr, addr, addr_len);
173         fib_entry->key.prefix_len = prefix_len;
174         return fib_entry;
175 }
176
177 static void mlxsw_sp_fib_entry_destroy(struct mlxsw_sp_fib_entry *fib_entry)
178 {
179         kfree(fib_entry);
180 }
181
182 static struct mlxsw_sp_fib_entry *
183 mlxsw_sp_fib_entry_lookup(struct mlxsw_sp_fib *fib, const void *addr,
184                           size_t addr_len, unsigned char prefix_len)
185 {
186         struct mlxsw_sp_fib_key key = {{ 0 } };
187
188         memcpy(key.addr, addr, addr_len);
189         key.prefix_len = prefix_len;
190         return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
191 }
192
193 static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
194 {
195         struct mlxsw_sp_fib *fib;
196         int err;
197
198         fib = kzalloc(sizeof(*fib), GFP_KERNEL);
199         if (!fib)
200                 return ERR_PTR(-ENOMEM);
201         err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
202         if (err)
203                 goto err_rhashtable_init;
204         return fib;
205
206 err_rhashtable_init:
207         kfree(fib);
208         return ERR_PTR(err);
209 }
210
211 static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
212 {
213         rhashtable_destroy(&fib->ht);
214         kfree(fib);
215 }
216
217 static struct mlxsw_sp_lpm_tree *
218 mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp, bool one_reserved)
219 {
220         static struct mlxsw_sp_lpm_tree *lpm_tree;
221         int i;
222
223         for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
224                 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
225                 if (lpm_tree->ref_count == 0) {
226                         if (one_reserved)
227                                 one_reserved = false;
228                         else
229                                 return lpm_tree;
230                 }
231         }
232         return NULL;
233 }
234
235 static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
236                                    struct mlxsw_sp_lpm_tree *lpm_tree)
237 {
238         char ralta_pl[MLXSW_REG_RALTA_LEN];
239
240         mlxsw_reg_ralta_pack(ralta_pl, true, lpm_tree->proto, lpm_tree->id);
241         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
242 }
243
244 static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
245                                   struct mlxsw_sp_lpm_tree *lpm_tree)
246 {
247         char ralta_pl[MLXSW_REG_RALTA_LEN];
248
249         mlxsw_reg_ralta_pack(ralta_pl, false, lpm_tree->proto, lpm_tree->id);
250         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
251 }
252
253 static int
254 mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
255                                   struct mlxsw_sp_prefix_usage *prefix_usage,
256                                   struct mlxsw_sp_lpm_tree *lpm_tree)
257 {
258         char ralst_pl[MLXSW_REG_RALST_LEN];
259         u8 root_bin = 0;
260         u8 prefix;
261         u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
262
263         mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
264                 root_bin = prefix;
265
266         mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
267         mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
268                 if (prefix == 0)
269                         continue;
270                 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
271                                          MLXSW_REG_RALST_BIN_NO_CHILD);
272                 last_prefix = prefix;
273         }
274         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
275 }
276
277 static struct mlxsw_sp_lpm_tree *
278 mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
279                          struct mlxsw_sp_prefix_usage *prefix_usage,
280                          enum mlxsw_sp_l3proto proto, bool one_reserved)
281 {
282         struct mlxsw_sp_lpm_tree *lpm_tree;
283         int err;
284
285         lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp, one_reserved);
286         if (!lpm_tree)
287                 return ERR_PTR(-EBUSY);
288         lpm_tree->proto = proto;
289         err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
290         if (err)
291                 return ERR_PTR(err);
292
293         err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
294                                                 lpm_tree);
295         if (err)
296                 goto err_left_struct_set;
297         return lpm_tree;
298
299 err_left_struct_set:
300         mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
301         return ERR_PTR(err);
302 }
303
304 static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
305                                      struct mlxsw_sp_lpm_tree *lpm_tree)
306 {
307         return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
308 }
309
310 static struct mlxsw_sp_lpm_tree *
311 mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
312                       struct mlxsw_sp_prefix_usage *prefix_usage,
313                       enum mlxsw_sp_l3proto proto, bool one_reserved)
314 {
315         struct mlxsw_sp_lpm_tree *lpm_tree;
316         int i;
317
318         for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
319                 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
320                 if (lpm_tree->proto == proto &&
321                     mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
322                                              prefix_usage))
323                         goto inc_ref_count;
324         }
325         lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
326                                             proto, one_reserved);
327         if (IS_ERR(lpm_tree))
328                 return lpm_tree;
329
330 inc_ref_count:
331         lpm_tree->ref_count++;
332         return lpm_tree;
333 }
334
335 static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
336                                  struct mlxsw_sp_lpm_tree *lpm_tree)
337 {
338         if (--lpm_tree->ref_count == 0)
339                 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
340         return 0;
341 }
342
343 static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
344 {
345         struct mlxsw_sp_lpm_tree *lpm_tree;
346         int i;
347
348         for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
349                 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
350                 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
351         }
352 }
353
354 static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
355 {
356         struct mlxsw_sp_vr *vr;
357         int i;
358
359         for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
360                 vr = &mlxsw_sp->router.vrs[i];
361                 if (!vr->used)
362                         return vr;
363         }
364         return NULL;
365 }
366
367 static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
368                                      struct mlxsw_sp_vr *vr)
369 {
370         char raltb_pl[MLXSW_REG_RALTB_LEN];
371
372         mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, vr->lpm_tree->id);
373         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
374 }
375
376 static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
377                                        struct mlxsw_sp_vr *vr)
378 {
379         char raltb_pl[MLXSW_REG_RALTB_LEN];
380
381         /* Bind to tree 0 which is default */
382         mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, 0);
383         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
384 }
385
386 static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
387 {
388         /* For our purpose, squash main and local table into one */
389         if (tb_id == RT_TABLE_LOCAL)
390                 tb_id = RT_TABLE_MAIN;
391         return tb_id;
392 }
393
394 static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
395                                             u32 tb_id,
396                                             enum mlxsw_sp_l3proto proto)
397 {
398         struct mlxsw_sp_vr *vr;
399         int i;
400
401         tb_id = mlxsw_sp_fix_tb_id(tb_id);
402         for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
403                 vr = &mlxsw_sp->router.vrs[i];
404                 if (vr->used && vr->proto == proto && vr->tb_id == tb_id)
405                         return vr;
406         }
407         return NULL;
408 }
409
410 static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
411                                               unsigned char prefix_len,
412                                               u32 tb_id,
413                                               enum mlxsw_sp_l3proto proto)
414 {
415         struct mlxsw_sp_prefix_usage req_prefix_usage;
416         struct mlxsw_sp_lpm_tree *lpm_tree;
417         struct mlxsw_sp_vr *vr;
418         int err;
419
420         vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
421         if (!vr)
422                 return ERR_PTR(-EBUSY);
423         vr->fib = mlxsw_sp_fib_create();
424         if (IS_ERR(vr->fib))
425                 return ERR_CAST(vr->fib);
426
427         vr->proto = proto;
428         vr->tb_id = tb_id;
429         mlxsw_sp_prefix_usage_zero(&req_prefix_usage);
430         mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
431         lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
432                                          proto, true);
433         if (IS_ERR(lpm_tree)) {
434                 err = PTR_ERR(lpm_tree);
435                 goto err_tree_get;
436         }
437         vr->lpm_tree = lpm_tree;
438         err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
439         if (err)
440                 goto err_tree_bind;
441
442         vr->used = true;
443         return vr;
444
445 err_tree_bind:
446         mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
447 err_tree_get:
448         mlxsw_sp_fib_destroy(vr->fib);
449
450         return ERR_PTR(err);
451 }
452
453 static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
454                                 struct mlxsw_sp_vr *vr)
455 {
456         mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
457         mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
458         mlxsw_sp_fib_destroy(vr->fib);
459         vr->used = false;
460 }
461
462 static int
463 mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
464                            struct mlxsw_sp_prefix_usage *req_prefix_usage)
465 {
466         struct mlxsw_sp_lpm_tree *lpm_tree;
467
468         if (mlxsw_sp_prefix_usage_eq(req_prefix_usage,
469                                      &vr->lpm_tree->prefix_usage))
470                 return 0;
471
472         lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
473                                          vr->proto, false);
474         if (IS_ERR(lpm_tree)) {
475                 /* We failed to get a tree according to the required
476                  * prefix usage. However, the current tree might be still good
477                  * for us if our requirement is subset of the prefixes used
478                  * in the tree.
479                  */
480                 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
481                                                  &vr->lpm_tree->prefix_usage))
482                         return 0;
483                 return PTR_ERR(lpm_tree);
484         }
485
486         mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
487         mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
488         vr->lpm_tree = lpm_tree;
489         return mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
490 }
491
492 static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp,
493                                            unsigned char prefix_len,
494                                            u32 tb_id,
495                                            enum mlxsw_sp_l3proto proto)
496 {
497         struct mlxsw_sp_vr *vr;
498         int err;
499
500         tb_id = mlxsw_sp_fix_tb_id(tb_id);
501         vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id, proto);
502         if (!vr) {
503                 vr = mlxsw_sp_vr_create(mlxsw_sp, prefix_len, tb_id, proto);
504                 if (IS_ERR(vr))
505                         return vr;
506         } else {
507                 struct mlxsw_sp_prefix_usage req_prefix_usage;
508
509                 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
510                                           &vr->fib->prefix_usage);
511                 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
512                 /* Need to replace LPM tree in case new prefix is required. */
513                 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
514                                                  &req_prefix_usage);
515                 if (err)
516                         return ERR_PTR(err);
517         }
518         return vr;
519 }
520
521 static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
522 {
523         /* Destroy virtual router entity in case the associated FIB is empty
524          * and allow it to be used for other tables in future. Otherwise,
525          * check if some prefix usage did not disappear and change tree if
526          * that is the case. Note that in case new, smaller tree cannot be
527          * allocated, the original one will be kept being used.
528          */
529         if (mlxsw_sp_prefix_usage_none(&vr->fib->prefix_usage))
530                 mlxsw_sp_vr_destroy(mlxsw_sp, vr);
531         else
532                 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
533                                            &vr->fib->prefix_usage);
534 }
535
536 static void mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
537 {
538         struct mlxsw_sp_vr *vr;
539         int i;
540
541         for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
542                 vr = &mlxsw_sp->router.vrs[i];
543                 vr->id = i;
544         }
545 }
546
547 static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
548 {
549         char rgcr_pl[MLXSW_REG_RGCR_LEN];
550
551         mlxsw_reg_rgcr_pack(rgcr_pl, true);
552         mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, MLXSW_SP_RIF_MAX);
553         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
554 }
555
556 static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
557 {
558         char rgcr_pl[MLXSW_REG_RGCR_LEN];
559
560         mlxsw_reg_rgcr_pack(rgcr_pl, false);
561         mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
562 }
563
564 int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
565 {
566         int err;
567
568         err = __mlxsw_sp_router_init(mlxsw_sp);
569         if (err)
570                 return err;
571         mlxsw_sp_lpm_init(mlxsw_sp);
572         mlxsw_sp_vrs_init(mlxsw_sp);
573         return 0;
574 }
575
576 void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
577 {
578         __mlxsw_sp_router_fini(mlxsw_sp);
579 }
580
581 static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
582                                         struct mlxsw_sp_fib_entry *fib_entry,
583                                         enum mlxsw_reg_ralue_op op)
584 {
585         char ralue_pl[MLXSW_REG_RALUE_LEN];
586         u32 *p_dip = (u32 *) fib_entry->key.addr;
587         struct mlxsw_sp_vr *vr = fib_entry->vr;
588
589         mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
590                               fib_entry->key.prefix_len, *p_dip);
591         mlxsw_reg_ralue_act_local_pack(ralue_pl,
592                                        MLXSW_REG_RALUE_TRAP_ACTION_NOP, 0,
593                                        fib_entry->rif);
594         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
595 }
596
597 static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
598                                        struct mlxsw_sp_fib_entry *fib_entry,
599                                        enum mlxsw_reg_ralue_op op)
600 {
601         char ralue_pl[MLXSW_REG_RALUE_LEN];
602         u32 *p_dip = (u32 *) fib_entry->key.addr;
603         struct mlxsw_sp_vr *vr = fib_entry->vr;
604
605         mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
606                               fib_entry->key.prefix_len, *p_dip);
607         mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
608         return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
609 }
610
611 static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp,
612                                   struct mlxsw_sp_fib_entry *fib_entry,
613                                   enum mlxsw_reg_ralue_op op)
614 {
615         switch (fib_entry->type) {
616         case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
617                 return -EINVAL;
618         case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
619                 return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op);
620         case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
621                 return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op);
622         }
623         return -EINVAL;
624 }
625
626 static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
627                                  struct mlxsw_sp_fib_entry *fib_entry,
628                                  enum mlxsw_reg_ralue_op op)
629 {
630         switch (fib_entry->vr->proto) {
631         case MLXSW_SP_L3_PROTO_IPV4:
632                 return mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op);
633         case MLXSW_SP_L3_PROTO_IPV6:
634                 return -EINVAL;
635         }
636         return -EINVAL;
637 }
638
639 static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
640                                      struct mlxsw_sp_fib_entry *fib_entry)
641 {
642         enum mlxsw_reg_ralue_op op;
643
644         op = !fib_entry->added ? MLXSW_REG_RALUE_OP_WRITE_WRITE :
645                                  MLXSW_REG_RALUE_OP_WRITE_UPDATE;
646         return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
647 }
648
649 static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
650                                   struct mlxsw_sp_fib_entry *fib_entry)
651 {
652         return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
653                                      MLXSW_REG_RALUE_OP_WRITE_DELETE);
654 }
655
656 struct mlxsw_sp_router_fib4_add_info {
657         struct switchdev_trans_item tritem;
658         struct mlxsw_sp *mlxsw_sp;
659         struct mlxsw_sp_fib_entry *fib_entry;
660 };
661
662 static void mlxsw_sp_router_fib4_add_info_destroy(void const *data)
663 {
664         const struct mlxsw_sp_router_fib4_add_info *info = data;
665         struct mlxsw_sp_fib_entry *fib_entry = info->fib_entry;
666         struct mlxsw_sp *mlxsw_sp = info->mlxsw_sp;
667
668         mlxsw_sp_fib_entry_destroy(fib_entry);
669         mlxsw_sp_vr_put(mlxsw_sp, fib_entry->vr);
670         kfree(info);
671 }
672
673 static int
674 mlxsw_sp_router_fib4_entry_init(struct mlxsw_sp *mlxsw_sp,
675                                 const struct switchdev_obj_ipv4_fib *fib4,
676                                 struct mlxsw_sp_fib_entry *fib_entry)
677 {
678         struct fib_info *fi = fib4->fi;
679
680         if (fib4->type == RTN_LOCAL || fib4->type == RTN_BROADCAST) {
681                 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
682                 return 0;
683         }
684         if (fib4->type != RTN_UNICAST)
685                 return -EINVAL;
686
687         if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
688                 struct mlxsw_sp_rif *r;
689
690                 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
691                 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fi->fib_dev);
692                 if (!r)
693                         return -EINVAL;
694                 fib_entry->rif = r->rif;
695                 return 0;
696         }
697         return -EINVAL;
698 }
699
700 static int
701 mlxsw_sp_router_fib4_add_prepare(struct mlxsw_sp_port *mlxsw_sp_port,
702                                  const struct switchdev_obj_ipv4_fib *fib4,
703                                  struct switchdev_trans *trans)
704 {
705         struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
706         struct mlxsw_sp_router_fib4_add_info *info;
707         struct mlxsw_sp_fib_entry *fib_entry;
708         struct mlxsw_sp_vr *vr;
709         int err;
710
711         vr = mlxsw_sp_vr_get(mlxsw_sp, fib4->dst_len, fib4->tb_id,
712                              MLXSW_SP_L3_PROTO_IPV4);
713         if (IS_ERR(vr))
714                 return PTR_ERR(vr);
715
716         fib_entry = mlxsw_sp_fib_entry_create(vr->fib, &fib4->dst,
717                                               sizeof(fib4->dst), fib4->dst_len);
718         if (!fib_entry) {
719                 err = -ENOMEM;
720                 goto err_fib_entry_create;
721         }
722         fib_entry->vr = vr;
723
724         err = mlxsw_sp_router_fib4_entry_init(mlxsw_sp, fib4, fib_entry);
725         if (err)
726                 goto err_fib4_entry_init;
727
728         info = kmalloc(sizeof(*info), GFP_KERNEL);
729         if (!info) {
730                 err = -ENOMEM;
731                 goto err_alloc_info;
732         }
733         info->mlxsw_sp = mlxsw_sp;
734         info->fib_entry = fib_entry;
735         switchdev_trans_item_enqueue(trans, info,
736                                      mlxsw_sp_router_fib4_add_info_destroy,
737                                      &info->tritem);
738         return 0;
739
740 err_alloc_info:
741 err_fib4_entry_init:
742         mlxsw_sp_fib_entry_destroy(fib_entry);
743 err_fib_entry_create:
744         mlxsw_sp_vr_put(mlxsw_sp, vr);
745         return err;
746 }
747
748 static int
749 mlxsw_sp_router_fib4_add_commit(struct mlxsw_sp_port *mlxsw_sp_port,
750                                 const struct switchdev_obj_ipv4_fib *fib4,
751                                 struct switchdev_trans *trans)
752 {
753         struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
754         struct mlxsw_sp_router_fib4_add_info *info;
755         struct mlxsw_sp_fib_entry *fib_entry;
756         struct mlxsw_sp_vr *vr;
757         int err;
758
759         info = switchdev_trans_item_dequeue(trans);
760         fib_entry = info->fib_entry;
761         kfree(info);
762
763         vr = fib_entry->vr;
764         err = mlxsw_sp_fib_entry_insert(fib_entry->vr->fib, fib_entry);
765         if (err)
766                 goto err_fib_entry_insert;
767         err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
768         if (err)
769                 goto err_fib_entry_add;
770         return 0;
771
772 err_fib_entry_add:
773         mlxsw_sp_fib_entry_remove(vr->fib, fib_entry);
774 err_fib_entry_insert:
775         mlxsw_sp_fib_entry_destroy(fib_entry);
776         mlxsw_sp_vr_put(mlxsw_sp, vr);
777         return err;
778 }
779
780 int mlxsw_sp_router_fib4_add(struct mlxsw_sp_port *mlxsw_sp_port,
781                              const struct switchdev_obj_ipv4_fib *fib4,
782                              struct switchdev_trans *trans)
783 {
784         if (switchdev_trans_ph_prepare(trans))
785                 return mlxsw_sp_router_fib4_add_prepare(mlxsw_sp_port,
786                                                         fib4, trans);
787         return mlxsw_sp_router_fib4_add_commit(mlxsw_sp_port,
788                                                fib4, trans);
789 }
790
791 int mlxsw_sp_router_fib4_del(struct mlxsw_sp_port *mlxsw_sp_port,
792                              const struct switchdev_obj_ipv4_fib *fib4)
793 {
794         struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
795         struct mlxsw_sp_fib_entry *fib_entry;
796         struct mlxsw_sp_vr *vr;
797
798         vr = mlxsw_sp_vr_find(mlxsw_sp, fib4->tb_id, MLXSW_SP_L3_PROTO_IPV4);
799         if (!vr) {
800                 dev_warn(mlxsw_sp->bus_info->dev, "Failed to find virtual router for FIB4 entry being removed.\n");
801                 return -ENOENT;
802         }
803         fib_entry = mlxsw_sp_fib_entry_lookup(vr->fib, &fib4->dst,
804                                               sizeof(fib4->dst), fib4->dst_len);
805         if (!fib_entry) {
806                 dev_warn(mlxsw_sp->bus_info->dev, "Failed to find FIB4 entry being removed.\n");
807                 return PTR_ERR(vr);
808         }
809         mlxsw_sp_fib_entry_del(mlxsw_sp_port->mlxsw_sp, fib_entry);
810         mlxsw_sp_fib_entry_remove(vr->fib, fib_entry);
811         mlxsw_sp_fib_entry_destroy(fib_entry);
812         mlxsw_sp_vr_put(mlxsw_sp, vr);
813         return 0;
814 }