1
2
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
11struct prestera_acl {
12 struct prestera_switch *sw;
13 struct list_head rules;
14};
15
16struct prestera_acl_ruleset {
17 struct rhashtable rule_ht;
18 struct prestera_switch *sw;
19 u16 id;
20};
21
22struct 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
35static 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
42static struct prestera_acl_ruleset *
43prestera_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
64err_ruleset_create:
65 rhashtable_destroy(&ruleset->rule_ht);
66err_rhashtable_init:
67 kfree(ruleset);
68 return ERR_PTR(err);
69}
70
71static 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
78struct prestera_flow_block *
79prestera_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
99void 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
106static struct prestera_flow_block_binding *
107prestera_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
119int 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
141err_rules_bind:
142 kfree(binding);
143 return err;
144}
145
146int 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
163struct prestera_acl_ruleset *
164prestera_acl_block_ruleset_get(struct prestera_flow_block *block)
165{
166 return block->ruleset;
167}
168
169u16 prestera_acl_rule_ruleset_id_get(const struct prestera_acl_rule *rule)
170{
171 return rule->block->ruleset->id;
172}
173
174struct net *prestera_acl_block_net(struct prestera_flow_block *block)
175{
176 return block->net;
177}
178
179struct prestera_switch *prestera_acl_block_sw(struct prestera_flow_block *block)
180{
181 return block->sw;
182}
183
184struct prestera_acl_rule *
185prestera_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
192struct prestera_acl_rule *
193prestera_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
210struct list_head *
211prestera_acl_rule_match_list_get(struct prestera_acl_rule *rule)
212{
213 return &rule->match_list;
214}
215
216struct list_head *
217prestera_acl_rule_action_list_get(struct prestera_acl_rule *rule)
218{
219 return &rule->action_list;
220}
221
222int 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
238u8 prestera_acl_rule_action_len(struct prestera_acl_rule *rule)
239{
240 return rule->n_actions;
241}
242
243u32 prestera_acl_rule_priority_get(struct prestera_acl_rule *rule)
244{
245 return rule->priority;
246}
247
248void prestera_acl_rule_priority_set(struct prestera_acl_rule *rule,
249 u32 priority)
250{
251 rule->priority = priority;
252}
253
254int 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
270u8 prestera_acl_rule_match_len(struct prestera_acl_rule *rule)
271{
272 return rule->n_matches;
273}
274
275void 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
296int prestera_acl_rule_add(struct prestera_switch *sw,
297 struct prestera_acl_rule *rule)
298{
299 u32 rule_id;
300 int err;
301
302
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
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
320err_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
326void 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
335int 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
355int 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
370void 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