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 *
prestera_acl_ruleset_create(struct prestera_switch * sw)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
prestera_acl_ruleset_destroy(struct prestera_acl_ruleset * ruleset)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 *
prestera_acl_block_create(struct prestera_switch * sw,struct net * net)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
prestera_acl_block_destroy(struct prestera_flow_block * block)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 *
prestera_acl_block_lookup(struct prestera_flow_block * block,struct prestera_port * port)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
prestera_acl_block_bind(struct prestera_flow_block * block,struct prestera_port * port)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
prestera_acl_block_unbind(struct prestera_flow_block * block,struct prestera_port * port)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 *
prestera_acl_block_ruleset_get(struct prestera_flow_block * block)164 prestera_acl_block_ruleset_get(struct prestera_flow_block *block)
165 {
166 return block->ruleset;
167 }
168
prestera_acl_rule_ruleset_id_get(const struct prestera_acl_rule * rule)169 u16 prestera_acl_rule_ruleset_id_get(const struct prestera_acl_rule *rule)
170 {
171 return rule->block->ruleset->id;
172 }
173
prestera_acl_block_net(struct prestera_flow_block * block)174 struct net *prestera_acl_block_net(struct prestera_flow_block *block)
175 {
176 return block->net;
177 }
178
prestera_acl_block_sw(struct prestera_flow_block * block)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 *
prestera_acl_rule_lookup(struct prestera_acl_ruleset * ruleset,unsigned long cookie)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 *
prestera_acl_rule_create(struct prestera_flow_block * block,unsigned long cookie)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 *
prestera_acl_rule_match_list_get(struct prestera_acl_rule * rule)211 prestera_acl_rule_match_list_get(struct prestera_acl_rule *rule)
212 {
213 return &rule->match_list;
214 }
215
216 struct list_head *
prestera_acl_rule_action_list_get(struct prestera_acl_rule * rule)217 prestera_acl_rule_action_list_get(struct prestera_acl_rule *rule)
218 {
219 return &rule->action_list;
220 }
221
prestera_acl_rule_action_add(struct prestera_acl_rule * rule,struct prestera_acl_rule_action_entry * entry)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
prestera_acl_rule_action_len(struct prestera_acl_rule * rule)238 u8 prestera_acl_rule_action_len(struct prestera_acl_rule *rule)
239 {
240 return rule->n_actions;
241 }
242
prestera_acl_rule_priority_get(struct prestera_acl_rule * rule)243 u32 prestera_acl_rule_priority_get(struct prestera_acl_rule *rule)
244 {
245 return rule->priority;
246 }
247
prestera_acl_rule_priority_set(struct prestera_acl_rule * rule,u32 priority)248 void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
249 u32 priority)
250 {
251 rule->priority = priority;
252 }
253
prestera_acl_rule_match_add(struct prestera_acl_rule * rule,struct prestera_acl_rule_match_entry * entry)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
prestera_acl_rule_match_len(struct prestera_acl_rule * rule)270 u8 prestera_acl_rule_match_len(struct prestera_acl_rule *rule)
271 {
272 return rule->n_matches;
273 }
274
prestera_acl_rule_destroy(struct prestera_acl_rule * rule)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
prestera_acl_rule_add(struct prestera_switch * sw,struct prestera_acl_rule * rule)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
prestera_acl_rule_del(struct prestera_switch * sw,struct prestera_acl_rule * rule)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
prestera_acl_rule_get_stats(struct prestera_switch * sw,struct prestera_acl_rule * rule,u64 * packets,u64 * bytes,u64 * last_use)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, ¤t_packets,
344 ¤t_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
prestera_acl_init(struct prestera_switch * sw)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
prestera_acl_fini(struct prestera_switch * sw)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