GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / net / ethernet / marvell / prestera / prestera_router_hw.c
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2019-2021 Marvell International Ltd. All rights reserved */
3
4 #include <linux/rhashtable.h>
5
6 #include "prestera.h"
7 #include "prestera_hw.h"
8 #include "prestera_router_hw.h"
9 #include "prestera_acl.h"
10
11 /*            +--+
12  *   +------->|vr|<-+
13  *   |        +--+  |
14  *   |              |
15  * +-+-------+   +--+---+-+
16  * |rif_entry|   |fib_node|
17  * +---------+   +--------+
18  *  Rif is        Fib - is exit point
19  *  used as
20  *  entry point
21  *  for vr in hw
22  */
23
24 #define PRESTERA_NHGR_UNUSED (0)
25 #define PRESTERA_NHGR_DROP (0xFFFFFFFF)
26
27 static const struct rhashtable_params __prestera_fib_ht_params = {
28         .key_offset  = offsetof(struct prestera_fib_node, key),
29         .head_offset = offsetof(struct prestera_fib_node, ht_node),
30         .key_len     = sizeof(struct prestera_fib_key),
31         .automatic_shrinking = true,
32 };
33
34 int prestera_router_hw_init(struct prestera_switch *sw)
35 {
36         int err;
37
38         err = rhashtable_init(&sw->router->fib_ht,
39                               &__prestera_fib_ht_params);
40         if (err)
41                 goto err_fib_ht_init;
42
43         INIT_LIST_HEAD(&sw->router->vr_list);
44         INIT_LIST_HEAD(&sw->router->rif_entry_list);
45
46 err_fib_ht_init:
47         return 0;
48 }
49
50 void prestera_router_hw_fini(struct prestera_switch *sw)
51 {
52         WARN_ON(!list_empty(&sw->router->vr_list));
53         WARN_ON(!list_empty(&sw->router->rif_entry_list));
54         rhashtable_destroy(&sw->router->fib_ht);
55 }
56
57 static struct prestera_vr *__prestera_vr_find(struct prestera_switch *sw,
58                                               u32 tb_id)
59 {
60         struct prestera_vr *vr;
61
62         list_for_each_entry(vr, &sw->router->vr_list, router_node) {
63                 if (vr->tb_id == tb_id)
64                         return vr;
65         }
66
67         return NULL;
68 }
69
70 static struct prestera_vr *__prestera_vr_create(struct prestera_switch *sw,
71                                                 u32 tb_id,
72                                                 struct netlink_ext_ack *extack)
73 {
74         struct prestera_vr *vr;
75         int err;
76
77         vr = kzalloc(sizeof(*vr), GFP_KERNEL);
78         if (!vr) {
79                 err = -ENOMEM;
80                 goto err_alloc_vr;
81         }
82
83         vr->tb_id = tb_id;
84
85         err = prestera_hw_vr_create(sw, &vr->hw_vr_id);
86         if (err)
87                 goto err_hw_create;
88
89         list_add(&vr->router_node, &sw->router->vr_list);
90
91         return vr;
92
93 err_hw_create:
94         kfree(vr);
95 err_alloc_vr:
96         return ERR_PTR(err);
97 }
98
99 static void __prestera_vr_destroy(struct prestera_switch *sw,
100                                   struct prestera_vr *vr)
101 {
102         list_del(&vr->router_node);
103         prestera_hw_vr_delete(sw, vr->hw_vr_id);
104         kfree(vr);
105 }
106
107 static struct prestera_vr *prestera_vr_get(struct prestera_switch *sw, u32 tb_id,
108                                            struct netlink_ext_ack *extack)
109 {
110         struct prestera_vr *vr;
111
112         vr = __prestera_vr_find(sw, tb_id);
113         if (vr) {
114                 refcount_inc(&vr->refcount);
115         } else {
116                 vr = __prestera_vr_create(sw, tb_id, extack);
117                 if (IS_ERR(vr))
118                         return ERR_CAST(vr);
119
120                 refcount_set(&vr->refcount, 1);
121         }
122
123         return vr;
124 }
125
126 static void prestera_vr_put(struct prestera_switch *sw, struct prestera_vr *vr)
127 {
128         if (refcount_dec_and_test(&vr->refcount))
129                 __prestera_vr_destroy(sw, vr);
130 }
131
132 /* iface is overhead struct. vr_id also can be removed. */
133 static int
134 __prestera_rif_entry_key_copy(const struct prestera_rif_entry_key *in,
135                               struct prestera_rif_entry_key *out)
136 {
137         memset(out, 0, sizeof(*out));
138
139         switch (in->iface.type) {
140         case PRESTERA_IF_PORT_E:
141                 out->iface.dev_port.hw_dev_num = in->iface.dev_port.hw_dev_num;
142                 out->iface.dev_port.port_num = in->iface.dev_port.port_num;
143                 break;
144         case PRESTERA_IF_LAG_E:
145                 out->iface.lag_id = in->iface.lag_id;
146                 break;
147         case PRESTERA_IF_VID_E:
148                 out->iface.vlan_id = in->iface.vlan_id;
149                 break;
150         default:
151                 WARN(1, "Unsupported iface type");
152                 return -EINVAL;
153         }
154
155         out->iface.type = in->iface.type;
156         return 0;
157 }
158
159 struct prestera_rif_entry *
160 prestera_rif_entry_find(const struct prestera_switch *sw,
161                         const struct prestera_rif_entry_key *k)
162 {
163         struct prestera_rif_entry *rif_entry;
164         struct prestera_rif_entry_key lk; /* lookup key */
165
166         if (__prestera_rif_entry_key_copy(k, &lk))
167                 return NULL;
168
169         list_for_each_entry(rif_entry, &sw->router->rif_entry_list,
170                             router_node) {
171                 if (!memcmp(k, &rif_entry->key, sizeof(*k)))
172                         return rif_entry;
173         }
174
175         return NULL;
176 }
177
178 void prestera_rif_entry_destroy(struct prestera_switch *sw,
179                                 struct prestera_rif_entry *e)
180 {
181         struct prestera_iface iface;
182
183         list_del(&e->router_node);
184
185         memcpy(&iface, &e->key.iface, sizeof(iface));
186         iface.vr_id = e->vr->hw_vr_id;
187         prestera_hw_rif_delete(sw, e->hw_id, &iface);
188
189         prestera_vr_put(sw, e->vr);
190         kfree(e);
191 }
192
193 struct prestera_rif_entry *
194 prestera_rif_entry_create(struct prestera_switch *sw,
195                           struct prestera_rif_entry_key *k,
196                           u32 tb_id, const unsigned char *addr)
197 {
198         int err;
199         struct prestera_rif_entry *e;
200         struct prestera_iface iface;
201
202         e = kzalloc(sizeof(*e), GFP_KERNEL);
203         if (!e)
204                 goto err_kzalloc;
205
206         if (__prestera_rif_entry_key_copy(k, &e->key))
207                 goto err_key_copy;
208
209         e->vr = prestera_vr_get(sw, tb_id, NULL);
210         if (IS_ERR(e->vr))
211                 goto err_vr_get;
212
213         memcpy(&e->addr, addr, sizeof(e->addr));
214
215         /* HW */
216         memcpy(&iface, &e->key.iface, sizeof(iface));
217         iface.vr_id = e->vr->hw_vr_id;
218         err = prestera_hw_rif_create(sw, &iface, e->addr, &e->hw_id);
219         if (err)
220                 goto err_hw_create;
221
222         list_add(&e->router_node, &sw->router->rif_entry_list);
223
224         return e;
225
226 err_hw_create:
227         prestera_vr_put(sw, e->vr);
228 err_vr_get:
229 err_key_copy:
230         kfree(e);
231 err_kzalloc:
232         return NULL;
233 }
234
235 struct prestera_fib_node *
236 prestera_fib_node_find(struct prestera_switch *sw, struct prestera_fib_key *key)
237 {
238         struct prestera_fib_node *fib_node;
239
240         fib_node = rhashtable_lookup_fast(&sw->router->fib_ht, key,
241                                           __prestera_fib_ht_params);
242         return fib_node;
243 }
244
245 static void __prestera_fib_node_destruct(struct prestera_switch *sw,
246                                          struct prestera_fib_node *fib_node)
247 {
248         struct prestera_vr *vr;
249
250         vr = fib_node->info.vr;
251         prestera_hw_lpm_del(sw, vr->hw_vr_id, fib_node->key.addr.u.ipv4,
252                             fib_node->key.prefix_len);
253         switch (fib_node->info.type) {
254         case PRESTERA_FIB_TYPE_TRAP:
255                 break;
256         case PRESTERA_FIB_TYPE_DROP:
257                 break;
258         default:
259               pr_err("Unknown fib_node->info.type = %d",
260                      fib_node->info.type);
261         }
262
263         prestera_vr_put(sw, vr);
264 }
265
266 void prestera_fib_node_destroy(struct prestera_switch *sw,
267                                struct prestera_fib_node *fib_node)
268 {
269         __prestera_fib_node_destruct(sw, fib_node);
270         rhashtable_remove_fast(&sw->router->fib_ht, &fib_node->ht_node,
271                                __prestera_fib_ht_params);
272         kfree(fib_node);
273 }
274
275 struct prestera_fib_node *
276 prestera_fib_node_create(struct prestera_switch *sw,
277                          struct prestera_fib_key *key,
278                          enum prestera_fib_type fib_type)
279 {
280         struct prestera_fib_node *fib_node;
281         u32 grp_id;
282         struct prestera_vr *vr;
283         int err;
284
285         fib_node = kzalloc(sizeof(*fib_node), GFP_KERNEL);
286         if (!fib_node)
287                 goto err_kzalloc;
288
289         memcpy(&fib_node->key, key, sizeof(*key));
290         fib_node->info.type = fib_type;
291
292         vr = prestera_vr_get(sw, key->tb_id, NULL);
293         if (IS_ERR(vr))
294                 goto err_vr_get;
295
296         fib_node->info.vr = vr;
297
298         switch (fib_type) {
299         case PRESTERA_FIB_TYPE_TRAP:
300                 grp_id = PRESTERA_NHGR_UNUSED;
301                 break;
302         case PRESTERA_FIB_TYPE_DROP:
303                 grp_id = PRESTERA_NHGR_DROP;
304                 break;
305         default:
306                 pr_err("Unsupported fib_type %d", fib_type);
307                 goto err_nh_grp_get;
308         }
309
310         err = prestera_hw_lpm_add(sw, vr->hw_vr_id, key->addr.u.ipv4,
311                                   key->prefix_len, grp_id);
312         if (err)
313                 goto err_lpm_add;
314
315         err = rhashtable_insert_fast(&sw->router->fib_ht, &fib_node->ht_node,
316                                      __prestera_fib_ht_params);
317         if (err)
318                 goto err_ht_insert;
319
320         return fib_node;
321
322 err_ht_insert:
323         prestera_hw_lpm_del(sw, vr->hw_vr_id, key->addr.u.ipv4,
324                             key->prefix_len);
325 err_lpm_add:
326 err_nh_grp_get:
327         prestera_vr_put(sw, vr);
328 err_vr_get:
329         kfree(fib_node);
330 err_kzalloc:
331         return NULL;
332 }