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, ¤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 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