1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
3
4#include <linux/kernel.h>
5
6#include "core_acl_flex_actions.h"
7#include "spectrum.h"
8#include "spectrum_mr.h"
9
10struct mlxsw_sp2_mr_tcam {
11	struct mlxsw_sp *mlxsw_sp;
12	struct mlxsw_sp_flow_block *flow_block;
13	struct mlxsw_sp_acl_ruleset *ruleset4;
14	struct mlxsw_sp_acl_ruleset *ruleset6;
15};
16
17struct mlxsw_sp2_mr_route {
18	struct mlxsw_sp2_mr_tcam *mr_tcam;
19};
20
21static struct mlxsw_sp_acl_ruleset *
22mlxsw_sp2_mr_tcam_proto_ruleset(struct mlxsw_sp2_mr_tcam *mr_tcam,
23				enum mlxsw_sp_l3proto proto)
24{
25	switch (proto) {
26	case MLXSW_SP_L3_PROTO_IPV4:
27		return mr_tcam->ruleset4;
28	case MLXSW_SP_L3_PROTO_IPV6:
29		return mr_tcam->ruleset6;
30	}
31	return NULL;
32}
33
34static int mlxsw_sp2_mr_tcam_bind_group(struct mlxsw_sp *mlxsw_sp,
35					enum mlxsw_reg_pemrbt_protocol protocol,
36					struct mlxsw_sp_acl_ruleset *ruleset)
37{
38	char pemrbt_pl[MLXSW_REG_PEMRBT_LEN];
39	u16 group_id;
40
41	group_id = mlxsw_sp_acl_ruleset_group_id(ruleset);
42
43	mlxsw_reg_pemrbt_pack(pemrbt_pl, protocol, group_id);
44	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pemrbt), pemrbt_pl);
45}
46
47static const enum mlxsw_afk_element mlxsw_sp2_mr_tcam_usage_ipv4[] = {
48		MLXSW_AFK_ELEMENT_VIRT_ROUTER,
49		MLXSW_AFK_ELEMENT_SRC_IP_0_31,
50		MLXSW_AFK_ELEMENT_DST_IP_0_31,
51};
52
53static int mlxsw_sp2_mr_tcam_ipv4_init(struct mlxsw_sp2_mr_tcam *mr_tcam)
54{
55	struct mlxsw_afk_element_usage elusage;
56	int err;
57
58	/* Initialize IPv4 ACL group. */
59	mlxsw_afk_element_usage_fill(&elusage,
60				     mlxsw_sp2_mr_tcam_usage_ipv4,
61				     ARRAY_SIZE(mlxsw_sp2_mr_tcam_usage_ipv4));
62	mr_tcam->ruleset4 = mlxsw_sp_acl_ruleset_get(mr_tcam->mlxsw_sp,
63						     mr_tcam->flow_block,
64						     MLXSW_SP_L3_PROTO_IPV4,
65						     MLXSW_SP_ACL_PROFILE_MR,
66						     &elusage);
67
68	if (IS_ERR(mr_tcam->ruleset4))
69		return PTR_ERR(mr_tcam->ruleset4);
70
71	/* MC Router groups should be bound before routes are inserted. */
72	err = mlxsw_sp2_mr_tcam_bind_group(mr_tcam->mlxsw_sp,
73					   MLXSW_REG_PEMRBT_PROTO_IPV4,
74					   mr_tcam->ruleset4);
75	if (err)
76		goto err_bind_group;
77
78	return 0;
79
80err_bind_group:
81	mlxsw_sp_acl_ruleset_put(mr_tcam->mlxsw_sp, mr_tcam->ruleset4);
82	return err;
83}
84
85static void mlxsw_sp2_mr_tcam_ipv4_fini(struct mlxsw_sp2_mr_tcam *mr_tcam)
86{
87	mlxsw_sp_acl_ruleset_put(mr_tcam->mlxsw_sp, mr_tcam->ruleset4);
88}
89
90static const enum mlxsw_afk_element mlxsw_sp2_mr_tcam_usage_ipv6[] = {
91		MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_3,
92		MLXSW_AFK_ELEMENT_VIRT_ROUTER_4_7,
93		MLXSW_AFK_ELEMENT_VIRT_ROUTER_MSB,
94		MLXSW_AFK_ELEMENT_SRC_IP_96_127,
95		MLXSW_AFK_ELEMENT_SRC_IP_64_95,
96		MLXSW_AFK_ELEMENT_SRC_IP_32_63,
97		MLXSW_AFK_ELEMENT_SRC_IP_0_31,
98		MLXSW_AFK_ELEMENT_DST_IP_96_127,
99		MLXSW_AFK_ELEMENT_DST_IP_64_95,
100		MLXSW_AFK_ELEMENT_DST_IP_32_63,
101		MLXSW_AFK_ELEMENT_DST_IP_0_31,
102};
103
104static int mlxsw_sp2_mr_tcam_ipv6_init(struct mlxsw_sp2_mr_tcam *mr_tcam)
105{
106	struct mlxsw_afk_element_usage elusage;
107	int err;
108
109	/* Initialize IPv6 ACL group */
110	mlxsw_afk_element_usage_fill(&elusage,
111				     mlxsw_sp2_mr_tcam_usage_ipv6,
112				     ARRAY_SIZE(mlxsw_sp2_mr_tcam_usage_ipv6));
113	mr_tcam->ruleset6 = mlxsw_sp_acl_ruleset_get(mr_tcam->mlxsw_sp,
114						     mr_tcam->flow_block,
115						     MLXSW_SP_L3_PROTO_IPV6,
116						     MLXSW_SP_ACL_PROFILE_MR,
117						     &elusage);
118
119	if (IS_ERR(mr_tcam->ruleset6))
120		return PTR_ERR(mr_tcam->ruleset6);
121
122	/* MC Router groups should be bound before routes are inserted. */
123	err = mlxsw_sp2_mr_tcam_bind_group(mr_tcam->mlxsw_sp,
124					   MLXSW_REG_PEMRBT_PROTO_IPV6,
125					   mr_tcam->ruleset6);
126	if (err)
127		goto err_bind_group;
128
129	return 0;
130
131err_bind_group:
132	mlxsw_sp_acl_ruleset_put(mr_tcam->mlxsw_sp, mr_tcam->ruleset6);
133	return err;
134}
135
136static void mlxsw_sp2_mr_tcam_ipv6_fini(struct mlxsw_sp2_mr_tcam *mr_tcam)
137{
138	mlxsw_sp_acl_ruleset_put(mr_tcam->mlxsw_sp, mr_tcam->ruleset6);
139}
140
141static void
142mlxsw_sp2_mr_tcam_rule_parse4(struct mlxsw_sp_acl_rule_info *rulei,
143			      struct mlxsw_sp_mr_route_key *key)
144{
145	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_VIRT_ROUTER,
146				       key->vrid, GENMASK(11, 0));
147	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
148				       (char *) &key->source.addr4,
149				       (char *) &key->source_mask.addr4, 4);
150	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
151				       (char *) &key->group.addr4,
152				       (char *) &key->group_mask.addr4, 4);
153}
154
155static void
156mlxsw_sp2_mr_tcam_rule_parse6(struct mlxsw_sp_acl_rule_info *rulei,
157			      struct mlxsw_sp_mr_route_key *key)
158{
159	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_3,
160				       key->vrid, GENMASK(3, 0));
161	mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_VIRT_ROUTER_4_7,
162				       key->vrid >> 4, GENMASK(3, 0));
163	mlxsw_sp_acl_rulei_keymask_u32(rulei,
164				       MLXSW_AFK_ELEMENT_VIRT_ROUTER_MSB,
165				       key->vrid >> 8, GENMASK(3, 0));
166	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127,
167				       &key->source.addr6.s6_addr[0x0],
168				       &key->source_mask.addr6.s6_addr[0x0], 4);
169	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95,
170				       &key->source.addr6.s6_addr[0x4],
171				       &key->source_mask.addr6.s6_addr[0x4], 4);
172	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63,
173				       &key->source.addr6.s6_addr[0x8],
174				       &key->source_mask.addr6.s6_addr[0x8], 4);
175	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31,
176				       &key->source.addr6.s6_addr[0xc],
177				       &key->source_mask.addr6.s6_addr[0xc], 4);
178	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127,
179				       &key->group.addr6.s6_addr[0x0],
180				       &key->group_mask.addr6.s6_addr[0x0], 4);
181	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95,
182				       &key->group.addr6.s6_addr[0x4],
183				       &key->group_mask.addr6.s6_addr[0x4], 4);
184	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63,
185				       &key->group.addr6.s6_addr[0x8],
186				       &key->group_mask.addr6.s6_addr[0x8], 4);
187	mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31,
188				       &key->group.addr6.s6_addr[0xc],
189				       &key->group_mask.addr6.s6_addr[0xc], 4);
190}
191
192static void
193mlxsw_sp2_mr_tcam_rule_parse(struct mlxsw_sp_acl_rule *rule,
194			     struct mlxsw_sp_mr_route_key *key,
195			     unsigned int priority)
196{
197	struct mlxsw_sp_acl_rule_info *rulei;
198
199	rulei = mlxsw_sp_acl_rule_rulei(rule);
200	rulei->priority = priority;
201	switch (key->proto) {
202	case MLXSW_SP_L3_PROTO_IPV4:
203		return mlxsw_sp2_mr_tcam_rule_parse4(rulei, key);
204	case MLXSW_SP_L3_PROTO_IPV6:
205		return mlxsw_sp2_mr_tcam_rule_parse6(rulei, key);
206	}
207}
208
209static int
210mlxsw_sp2_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv,
211			       void *route_priv,
212			       struct mlxsw_sp_mr_route_key *key,
213			       struct mlxsw_afa_block *afa_block,
214			       enum mlxsw_sp_mr_route_prio prio)
215{
216	struct mlxsw_sp2_mr_route *mr_route = route_priv;
217	struct mlxsw_sp2_mr_tcam *mr_tcam = priv;
218	struct mlxsw_sp_acl_ruleset *ruleset;
219	struct mlxsw_sp_acl_rule *rule;
220	int err;
221
222	mr_route->mr_tcam = mr_tcam;
223	ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, key->proto);
224	if (WARN_ON(!ruleset))
225		return -EINVAL;
226
227	rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset,
228					(unsigned long) route_priv, afa_block,
229					NULL);
230	if (IS_ERR(rule))
231		return PTR_ERR(rule);
232
233	mlxsw_sp2_mr_tcam_rule_parse(rule, key, prio);
234	err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule);
235	if (err)
236		goto err_rule_add;
237
238	return 0;
239
240err_rule_add:
241	mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
242	return err;
243}
244
245static void
246mlxsw_sp2_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp, void *priv,
247				void *route_priv,
248				struct mlxsw_sp_mr_route_key *key)
249{
250	struct mlxsw_sp2_mr_tcam *mr_tcam = priv;
251	struct mlxsw_sp_acl_ruleset *ruleset;
252	struct mlxsw_sp_acl_rule *rule;
253
254	ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, key->proto);
255	if (WARN_ON(!ruleset))
256		return;
257
258	rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset,
259					(unsigned long) route_priv);
260	if (WARN_ON(!rule))
261		return;
262
263	mlxsw_sp_acl_rule_del(mlxsw_sp, rule);
264	mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule);
265}
266
267static int
268mlxsw_sp2_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp,
269			       void *route_priv,
270			       struct mlxsw_sp_mr_route_key *key,
271			       struct mlxsw_afa_block *afa_block)
272{
273	struct mlxsw_sp2_mr_route *mr_route = route_priv;
274	struct mlxsw_sp2_mr_tcam *mr_tcam = mr_route->mr_tcam;
275	struct mlxsw_sp_acl_ruleset *ruleset;
276	struct mlxsw_sp_acl_rule *rule;
277
278	ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, key->proto);
279	if (WARN_ON(!ruleset))
280		return -EINVAL;
281
282	rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset,
283					(unsigned long) route_priv);
284	if (WARN_ON(!rule))
285		return -EINVAL;
286
287	return mlxsw_sp_acl_rule_action_replace(mlxsw_sp, rule, afa_block);
288}
289
290static int mlxsw_sp2_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
291{
292	struct mlxsw_sp2_mr_tcam *mr_tcam = priv;
293	int err;
294
295	mr_tcam->mlxsw_sp = mlxsw_sp;
296	mr_tcam->flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, NULL);
297	if (!mr_tcam->flow_block)
298		return -ENOMEM;
299
300	err = mlxsw_sp2_mr_tcam_ipv4_init(mr_tcam);
301	if (err)
302		goto err_ipv4_init;
303
304	err = mlxsw_sp2_mr_tcam_ipv6_init(mr_tcam);
305	if (err)
306		goto err_ipv6_init;
307
308	return 0;
309
310err_ipv6_init:
311	mlxsw_sp2_mr_tcam_ipv4_fini(mr_tcam);
312err_ipv4_init:
313	mlxsw_sp_flow_block_destroy(mr_tcam->flow_block);
314	return err;
315}
316
317static void mlxsw_sp2_mr_tcam_fini(void *priv)
318{
319	struct mlxsw_sp2_mr_tcam *mr_tcam = priv;
320
321	mlxsw_sp2_mr_tcam_ipv6_fini(mr_tcam);
322	mlxsw_sp2_mr_tcam_ipv4_fini(mr_tcam);
323	mlxsw_sp_flow_block_destroy(mr_tcam->flow_block);
324}
325
326const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops = {
327	.priv_size = sizeof(struct mlxsw_sp2_mr_tcam),
328	.init = mlxsw_sp2_mr_tcam_init,
329	.fini = mlxsw_sp2_mr_tcam_fini,
330	.route_priv_size = sizeof(struct mlxsw_sp2_mr_route),
331	.route_create = mlxsw_sp2_mr_tcam_route_create,
332	.route_destroy = mlxsw_sp2_mr_tcam_route_destroy,
333	.route_update = mlxsw_sp2_mr_tcam_route_update,
334};
335