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 }
333