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
prestera_router_hw_init(struct prestera_switch * sw)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
prestera_router_hw_fini(struct prestera_switch * sw)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
__prestera_vr_find(struct prestera_switch * sw,u32 tb_id)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
__prestera_vr_create(struct prestera_switch * sw,u32 tb_id,struct netlink_ext_ack * extack)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
__prestera_vr_destroy(struct prestera_switch * sw,struct prestera_vr * vr)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
prestera_vr_get(struct prestera_switch * sw,u32 tb_id,struct netlink_ext_ack * extack)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
prestera_vr_put(struct prestera_switch * sw,struct prestera_vr * vr)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
__prestera_rif_entry_key_copy(const struct prestera_rif_entry_key * in,struct prestera_rif_entry_key * out)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 *
prestera_rif_entry_find(const struct prestera_switch * sw,const struct prestera_rif_entry_key * k)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
prestera_rif_entry_destroy(struct prestera_switch * sw,struct prestera_rif_entry * e)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 *
prestera_rif_entry_create(struct prestera_switch * sw,struct prestera_rif_entry_key * k,u32 tb_id,const unsigned char * addr)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 *
prestera_fib_node_find(struct prestera_switch * sw,struct prestera_fib_key * key)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
__prestera_fib_node_destruct(struct prestera_switch * sw,struct prestera_fib_node * fib_node)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
prestera_fib_node_destroy(struct prestera_switch * sw,struct prestera_fib_node * fib_node)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 *
prestera_fib_node_create(struct prestera_switch * sw,struct prestera_fib_key * key,enum prestera_fib_type fib_type)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 }
333