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|
17  * +---------+
18  *  Rif is
19  *  used as
20  *  entry point
21  *  for vr in hw
22  */
23 
24 int prestera_router_hw_init(struct prestera_switch *sw)
25 {
26 	INIT_LIST_HEAD(&sw->router->vr_list);
27 	INIT_LIST_HEAD(&sw->router->rif_entry_list);
28 
29 	return 0;
30 }
31 
32 void prestera_router_hw_fini(struct prestera_switch *sw)
33 {
34 	WARN_ON(!list_empty(&sw->router->vr_list));
35 	WARN_ON(!list_empty(&sw->router->rif_entry_list));
36 }
37 
38 static struct prestera_vr *__prestera_vr_find(struct prestera_switch *sw,
39 					      u32 tb_id)
40 {
41 	struct prestera_vr *vr;
42 
43 	list_for_each_entry(vr, &sw->router->vr_list, router_node) {
44 		if (vr->tb_id == tb_id)
45 			return vr;
46 	}
47 
48 	return NULL;
49 }
50 
51 static struct prestera_vr *__prestera_vr_create(struct prestera_switch *sw,
52 						u32 tb_id,
53 						struct netlink_ext_ack *extack)
54 {
55 	struct prestera_vr *vr;
56 	int err;
57 
58 	vr = kzalloc(sizeof(*vr), GFP_KERNEL);
59 	if (!vr) {
60 		err = -ENOMEM;
61 		goto err_alloc_vr;
62 	}
63 
64 	vr->tb_id = tb_id;
65 
66 	err = prestera_hw_vr_create(sw, &vr->hw_vr_id);
67 	if (err)
68 		goto err_hw_create;
69 
70 	list_add(&vr->router_node, &sw->router->vr_list);
71 
72 	return vr;
73 
74 err_hw_create:
75 	kfree(vr);
76 err_alloc_vr:
77 	return ERR_PTR(err);
78 }
79 
80 static void __prestera_vr_destroy(struct prestera_switch *sw,
81 				  struct prestera_vr *vr)
82 {
83 	list_del(&vr->router_node);
84 	prestera_hw_vr_delete(sw, vr->hw_vr_id);
85 	kfree(vr);
86 }
87 
88 static struct prestera_vr *prestera_vr_get(struct prestera_switch *sw, u32 tb_id,
89 					   struct netlink_ext_ack *extack)
90 {
91 	struct prestera_vr *vr;
92 
93 	vr = __prestera_vr_find(sw, tb_id);
94 	if (vr) {
95 		refcount_inc(&vr->refcount);
96 	} else {
97 		vr = __prestera_vr_create(sw, tb_id, extack);
98 		if (IS_ERR(vr))
99 			return ERR_CAST(vr);
100 
101 		refcount_set(&vr->refcount, 1);
102 	}
103 
104 	return vr;
105 }
106 
107 static void prestera_vr_put(struct prestera_switch *sw, struct prestera_vr *vr)
108 {
109 	if (refcount_dec_and_test(&vr->refcount))
110 		__prestera_vr_destroy(sw, vr);
111 }
112 
113 /* iface is overhead struct. vr_id also can be removed. */
114 static int
115 __prestera_rif_entry_key_copy(const struct prestera_rif_entry_key *in,
116 			      struct prestera_rif_entry_key *out)
117 {
118 	memset(out, 0, sizeof(*out));
119 
120 	switch (in->iface.type) {
121 	case PRESTERA_IF_PORT_E:
122 		out->iface.dev_port.hw_dev_num = in->iface.dev_port.hw_dev_num;
123 		out->iface.dev_port.port_num = in->iface.dev_port.port_num;
124 		break;
125 	case PRESTERA_IF_LAG_E:
126 		out->iface.lag_id = in->iface.lag_id;
127 		break;
128 	case PRESTERA_IF_VID_E:
129 		out->iface.vlan_id = in->iface.vlan_id;
130 		break;
131 	default:
132 		WARN(1, "Unsupported iface type");
133 		return -EINVAL;
134 	}
135 
136 	out->iface.type = in->iface.type;
137 	return 0;
138 }
139 
140 struct prestera_rif_entry *
141 prestera_rif_entry_find(const struct prestera_switch *sw,
142 			const struct prestera_rif_entry_key *k)
143 {
144 	struct prestera_rif_entry *rif_entry;
145 	struct prestera_rif_entry_key lk; /* lookup key */
146 
147 	if (__prestera_rif_entry_key_copy(k, &lk))
148 		return NULL;
149 
150 	list_for_each_entry(rif_entry, &sw->router->rif_entry_list,
151 			    router_node) {
152 		if (!memcmp(k, &rif_entry->key, sizeof(*k)))
153 			return rif_entry;
154 	}
155 
156 	return NULL;
157 }
158 
159 void prestera_rif_entry_destroy(struct prestera_switch *sw,
160 				struct prestera_rif_entry *e)
161 {
162 	struct prestera_iface iface;
163 
164 	list_del(&e->router_node);
165 
166 	memcpy(&iface, &e->key.iface, sizeof(iface));
167 	iface.vr_id = e->vr->hw_vr_id;
168 	prestera_hw_rif_delete(sw, e->hw_id, &iface);
169 
170 	prestera_vr_put(sw, e->vr);
171 	kfree(e);
172 }
173 
174 struct prestera_rif_entry *
175 prestera_rif_entry_create(struct prestera_switch *sw,
176 			  struct prestera_rif_entry_key *k,
177 			  u32 tb_id, const unsigned char *addr)
178 {
179 	int err;
180 	struct prestera_rif_entry *e;
181 	struct prestera_iface iface;
182 
183 	e = kzalloc(sizeof(*e), GFP_KERNEL);
184 	if (!e)
185 		goto err_kzalloc;
186 
187 	if (__prestera_rif_entry_key_copy(k, &e->key))
188 		goto err_key_copy;
189 
190 	e->vr = prestera_vr_get(sw, tb_id, NULL);
191 	if (IS_ERR(e->vr))
192 		goto err_vr_get;
193 
194 	memcpy(&e->addr, addr, sizeof(e->addr));
195 
196 	/* HW */
197 	memcpy(&iface, &e->key.iface, sizeof(iface));
198 	iface.vr_id = e->vr->hw_vr_id;
199 	err = prestera_hw_rif_create(sw, &iface, e->addr, &e->hw_id);
200 	if (err)
201 		goto err_hw_create;
202 
203 	list_add(&e->router_node, &sw->router->rif_entry_list);
204 
205 	return e;
206 
207 err_hw_create:
208 	prestera_vr_put(sw, e->vr);
209 err_vr_get:
210 err_key_copy:
211 	kfree(e);
212 err_kzalloc:
213 	return NULL;
214 }
215