1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2020 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_acl.h"
9 #include "prestera_span.h"
10 
11 struct prestera_acl {
12 	struct prestera_switch *sw;
13 	struct list_head rules;
14 };
15 
16 struct prestera_acl_ruleset {
17 	struct rhashtable rule_ht;
18 	struct prestera_switch *sw;
19 	u16 id;
20 };
21 
22 struct prestera_acl_rule {
23 	struct rhash_head ht_node;
24 	struct list_head list;
25 	struct list_head match_list;
26 	struct list_head action_list;
27 	struct prestera_flow_block *block;
28 	unsigned long cookie;
29 	u32 priority;
30 	u8 n_actions;
31 	u8 n_matches;
32 	u32 id;
33 };
34 
35 static const struct rhashtable_params prestera_acl_rule_ht_params = {
36 	.key_len = sizeof(unsigned long),
37 	.key_offset = offsetof(struct prestera_acl_rule, cookie),
38 	.head_offset = offsetof(struct prestera_acl_rule, ht_node),
39 	.automatic_shrinking = true,
40 };
41 
42 static struct prestera_acl_ruleset *
43 prestera_acl_ruleset_create(struct prestera_switch *sw)
44 {
45 	struct prestera_acl_ruleset *ruleset;
46 	int err;
47 
48 	ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL);
49 	if (!ruleset)
50 		return ERR_PTR(-ENOMEM);
51 
52 	err = rhashtable_init(&ruleset->rule_ht, &prestera_acl_rule_ht_params);
53 	if (err)
54 		goto err_rhashtable_init;
55 
56 	err = prestera_hw_acl_ruleset_create(sw, &ruleset->id);
57 	if (err)
58 		goto err_ruleset_create;
59 
60 	ruleset->sw = sw;
61 
62 	return ruleset;
63 
64 err_ruleset_create:
65 	rhashtable_destroy(&ruleset->rule_ht);
66 err_rhashtable_init:
67 	kfree(ruleset);
68 	return ERR_PTR(err);
69 }
70 
71 static void prestera_acl_ruleset_destroy(struct prestera_acl_ruleset *ruleset)
72 {
73 	prestera_hw_acl_ruleset_del(ruleset->sw, ruleset->id);
74 	rhashtable_destroy(&ruleset->rule_ht);
75 	kfree(ruleset);
76 }
77 
78 struct prestera_flow_block *
79 prestera_acl_block_create(struct prestera_switch *sw, struct net *net)
80 {
81 	struct prestera_flow_block *block;
82 
83 	block = kzalloc(sizeof(*block), GFP_KERNEL);
84 	if (!block)
85 		return NULL;
86 	INIT_LIST_HEAD(&block->binding_list);
87 	block->net = net;
88 	block->sw = sw;
89 
90 	block->ruleset = prestera_acl_ruleset_create(sw);
91 	if (IS_ERR(block->ruleset)) {
92 		kfree(block);
93 		return NULL;
94 	}
95 
96 	return block;
97 }
98 
99 void prestera_acl_block_destroy(struct prestera_flow_block *block)
100 {
101 	prestera_acl_ruleset_destroy(block->ruleset);
102 	WARN_ON(!list_empty(&block->binding_list));
103 	kfree(block);
104 }
105 
106 static struct prestera_flow_block_binding *
107 prestera_acl_block_lookup(struct prestera_flow_block *block,
108 			  struct prestera_port *port)
109 {
110 	struct prestera_flow_block_binding *binding;
111 
112 	list_for_each_entry(binding, &block->binding_list, list)
113 		if (binding->port == port)
114 			return binding;
115 
116 	return NULL;
117 }
118 
119 int prestera_acl_block_bind(struct prestera_flow_block *block,
120 			    struct prestera_port *port)
121 {
122 	struct prestera_flow_block_binding *binding;
123 	int err;
124 
125 	if (WARN_ON(prestera_acl_block_lookup(block, port)))
126 		return -EEXIST;
127 
128 	binding = kzalloc(sizeof(*binding), GFP_KERNEL);
129 	if (!binding)
130 		return -ENOMEM;
131 	binding->span_id = PRESTERA_SPAN_INVALID_ID;
132 	binding->port = port;
133 
134 	err = prestera_hw_acl_port_bind(port, block->ruleset->id);
135 	if (err)
136 		goto err_rules_bind;
137 
138 	list_add(&binding->list, &block->binding_list);
139 	return 0;
140 
141 err_rules_bind:
142 	kfree(binding);
143 	return err;
144 }
145 
146 int prestera_acl_block_unbind(struct prestera_flow_block *block,
147 			      struct prestera_port *port)
148 {
149 	struct prestera_flow_block_binding *binding;
150 
151 	binding = prestera_acl_block_lookup(block, port);
152 	if (!binding)
153 		return -ENOENT;
154 
155 	list_del(&binding->list);
156 
157 	prestera_hw_acl_port_unbind(port, block->ruleset->id);
158 
159 	kfree(binding);
160 	return 0;
161 }
162 
163 struct prestera_acl_ruleset *
164 prestera_acl_block_ruleset_get(struct prestera_flow_block *block)
165 {
166 	return block->ruleset;
167 }
168 
169 u16 prestera_acl_rule_ruleset_id_get(const struct prestera_acl_rule *rule)
170 {
171 	return rule->block->ruleset->id;
172 }
173 
174 struct net *prestera_acl_block_net(struct prestera_flow_block *block)
175 {
176 	return block->net;
177 }
178 
179 struct prestera_switch *prestera_acl_block_sw(struct prestera_flow_block *block)
180 {
181 	return block->sw;
182 }
183 
184 struct prestera_acl_rule *
185 prestera_acl_rule_lookup(struct prestera_acl_ruleset *ruleset,
186 			 unsigned long cookie)
187 {
188 	return rhashtable_lookup_fast(&ruleset->rule_ht, &cookie,
189 				      prestera_acl_rule_ht_params);
190 }
191 
192 struct prestera_acl_rule *
193 prestera_acl_rule_create(struct prestera_flow_block *block,
194 			 unsigned long cookie)
195 {
196 	struct prestera_acl_rule *rule;
197 
198 	rule = kzalloc(sizeof(*rule), GFP_KERNEL);
199 	if (!rule)
200 		return ERR_PTR(-ENOMEM);
201 
202 	INIT_LIST_HEAD(&rule->match_list);
203 	INIT_LIST_HEAD(&rule->action_list);
204 	rule->cookie = cookie;
205 	rule->block = block;
206 
207 	return rule;
208 }
209 
210 struct list_head *
211 prestera_acl_rule_match_list_get(struct prestera_acl_rule *rule)
212 {
213 	return &rule->match_list;
214 }
215 
216 struct list_head *
217 prestera_acl_rule_action_list_get(struct prestera_acl_rule *rule)
218 {
219 	return &rule->action_list;
220 }
221 
222 int prestera_acl_rule_action_add(struct prestera_acl_rule *rule,
223 				 struct prestera_acl_rule_action_entry *entry)
224 {
225 	struct prestera_acl_rule_action_entry *a_entry;
226 
227 	a_entry = kmalloc(sizeof(*a_entry), GFP_KERNEL);
228 	if (!a_entry)
229 		return -ENOMEM;
230 
231 	memcpy(a_entry, entry, sizeof(*entry));
232 	list_add(&a_entry->list, &rule->action_list);
233 
234 	rule->n_actions++;
235 	return 0;
236 }
237 
238 u8 prestera_acl_rule_action_len(struct prestera_acl_rule *rule)
239 {
240 	return rule->n_actions;
241 }
242 
243 u32 prestera_acl_rule_priority_get(struct prestera_acl_rule *rule)
244 {
245 	return rule->priority;
246 }
247 
248 void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
249 				    u32 priority)
250 {
251 	rule->priority = priority;
252 }
253 
254 int prestera_acl_rule_match_add(struct prestera_acl_rule *rule,
255 				struct prestera_acl_rule_match_entry *entry)
256 {
257 	struct prestera_acl_rule_match_entry *m_entry;
258 
259 	m_entry = kmalloc(sizeof(*m_entry), GFP_KERNEL);
260 	if (!m_entry)
261 		return -ENOMEM;
262 
263 	memcpy(m_entry, entry, sizeof(*entry));
264 	list_add(&m_entry->list, &rule->match_list);
265 
266 	rule->n_matches++;
267 	return 0;
268 }
269 
270 u8 prestera_acl_rule_match_len(struct prestera_acl_rule *rule)
271 {
272 	return rule->n_matches;
273 }
274 
275 void prestera_acl_rule_destroy(struct prestera_acl_rule *rule)
276 {
277 	struct prestera_acl_rule_action_entry *a_entry;
278 	struct prestera_acl_rule_match_entry *m_entry;
279 	struct list_head *pos, *n;
280 
281 	list_for_each_safe(pos, n, &rule->match_list) {
282 		m_entry = list_entry(pos, typeof(*m_entry), list);
283 		list_del(pos);
284 		kfree(m_entry);
285 	}
286 
287 	list_for_each_safe(pos, n, &rule->action_list) {
288 		a_entry = list_entry(pos, typeof(*a_entry), list);
289 		list_del(pos);
290 		kfree(a_entry);
291 	}
292 
293 	kfree(rule);
294 }
295 
296 int prestera_acl_rule_add(struct prestera_switch *sw,
297 			  struct prestera_acl_rule *rule)
298 {
299 	u32 rule_id;
300 	int err;
301 
302 	/* try to add rule to hash table first */
303 	err = rhashtable_insert_fast(&rule->block->ruleset->rule_ht,
304 				     &rule->ht_node,
305 				     prestera_acl_rule_ht_params);
306 	if (err)
307 		return err;
308 
309 	/* add rule to hw */
310 	err = prestera_hw_acl_rule_add(sw, rule, &rule_id);
311 	if (err)
312 		goto err_rule_add;
313 
314 	rule->id = rule_id;
315 
316 	list_add_tail(&rule->list, &sw->acl->rules);
317 
318 	return 0;
319 
320 err_rule_add:
321 	rhashtable_remove_fast(&rule->block->ruleset->rule_ht, &rule->ht_node,
322 			       prestera_acl_rule_ht_params);
323 	return err;
324 }
325 
326 void prestera_acl_rule_del(struct prestera_switch *sw,
327 			   struct prestera_acl_rule *rule)
328 {
329 	rhashtable_remove_fast(&rule->block->ruleset->rule_ht, &rule->ht_node,
330 			       prestera_acl_rule_ht_params);
331 	list_del(&rule->list);
332 	prestera_hw_acl_rule_del(sw, rule->id);
333 }
334 
335 int prestera_acl_rule_get_stats(struct prestera_switch *sw,
336 				struct prestera_acl_rule *rule,
337 				u64 *packets, u64 *bytes, u64 *last_use)
338 {
339 	u64 current_packets;
340 	u64 current_bytes;
341 	int err;
342 
343 	err = prestera_hw_acl_rule_stats_get(sw, rule->id, &current_packets,
344 					     &current_bytes);
345 	if (err)
346 		return err;
347 
348 	*packets = current_packets;
349 	*bytes = current_bytes;
350 	*last_use = jiffies;
351 
352 	return 0;
353 }
354 
355 int prestera_acl_init(struct prestera_switch *sw)
356 {
357 	struct prestera_acl *acl;
358 
359 	acl = kzalloc(sizeof(*acl), GFP_KERNEL);
360 	if (!acl)
361 		return -ENOMEM;
362 
363 	INIT_LIST_HEAD(&acl->rules);
364 	sw->acl = acl;
365 	acl->sw = sw;
366 
367 	return 0;
368 }
369 
370 void prestera_acl_fini(struct prestera_switch *sw)
371 {
372 	struct prestera_acl *acl = sw->acl;
373 
374 	WARN_ON(!list_empty(&acl->rules));
375 	kfree(acl);
376 }
377