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