1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 2 /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ 3 4 #include <linux/kernel.h> 5 #include <linux/errno.h> 6 #include <linux/netdevice.h> 7 #include <net/net_namespace.h> 8 #include <net/flow_dissector.h> 9 #include <net/pkt_cls.h> 10 #include <net/tc_act/tc_gact.h> 11 #include <net/tc_act/tc_mirred.h> 12 #include <net/tc_act/tc_vlan.h> 13 14 #include "spectrum.h" 15 #include "core_acl_flex_keys.h" 16 17 static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, 18 struct mlxsw_sp_acl_block *block, 19 struct mlxsw_sp_acl_rule_info *rulei, 20 struct flow_action *flow_action, 21 struct netlink_ext_ack *extack) 22 { 23 const struct flow_action_entry *act; 24 int mirror_act_count = 0; 25 int err, i; 26 27 if (!flow_action_has_entries(flow_action)) 28 return 0; 29 if (!flow_action_mixed_hw_stats_types_check(flow_action, extack)) 30 return -EOPNOTSUPP; 31 32 act = flow_action_first_entry_get(flow_action); 33 if (act->hw_stats_type == FLOW_ACTION_HW_STATS_TYPE_ANY || 34 act->hw_stats_type == FLOW_ACTION_HW_STATS_TYPE_IMMEDIATE) { 35 /* Count action is inserted first */ 36 err = mlxsw_sp_acl_rulei_act_count(mlxsw_sp, rulei, extack); 37 if (err) 38 return err; 39 } else if (act->hw_stats_type != FLOW_ACTION_HW_STATS_TYPE_DISABLED) { 40 NL_SET_ERR_MSG_MOD(extack, "Unsupported action HW stats type"); 41 return -EOPNOTSUPP; 42 } 43 44 flow_action_for_each(i, act, flow_action) { 45 switch (act->id) { 46 case FLOW_ACTION_ACCEPT: 47 err = mlxsw_sp_acl_rulei_act_terminate(rulei); 48 if (err) { 49 NL_SET_ERR_MSG_MOD(extack, "Cannot append terminate action"); 50 return err; 51 } 52 break; 53 case FLOW_ACTION_DROP: { 54 bool ingress; 55 56 if (mlxsw_sp_acl_block_is_mixed_bound(block)) { 57 NL_SET_ERR_MSG_MOD(extack, "Drop action is not supported when block is bound to ingress and egress"); 58 return -EOPNOTSUPP; 59 } 60 ingress = mlxsw_sp_acl_block_is_ingress_bound(block); 61 err = mlxsw_sp_acl_rulei_act_drop(rulei, ingress, 62 act->cookie, extack); 63 if (err) { 64 NL_SET_ERR_MSG_MOD(extack, "Cannot append drop action"); 65 return err; 66 } 67 68 /* Forbid block with this rulei to be bound 69 * to ingress/egress in future. Ingress rule is 70 * a blocker for egress and vice versa. 71 */ 72 if (ingress) 73 rulei->egress_bind_blocker = 1; 74 else 75 rulei->ingress_bind_blocker = 1; 76 } 77 break; 78 case FLOW_ACTION_TRAP: 79 err = mlxsw_sp_acl_rulei_act_trap(rulei); 80 if (err) { 81 NL_SET_ERR_MSG_MOD(extack, "Cannot append trap action"); 82 return err; 83 } 84 break; 85 case FLOW_ACTION_GOTO: { 86 u32 chain_index = act->chain_index; 87 struct mlxsw_sp_acl_ruleset *ruleset; 88 u16 group_id; 89 90 ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, block, 91 chain_index, 92 MLXSW_SP_ACL_PROFILE_FLOWER); 93 if (IS_ERR(ruleset)) 94 return PTR_ERR(ruleset); 95 96 group_id = mlxsw_sp_acl_ruleset_group_id(ruleset); 97 err = mlxsw_sp_acl_rulei_act_jump(rulei, group_id); 98 if (err) { 99 NL_SET_ERR_MSG_MOD(extack, "Cannot append jump action"); 100 return err; 101 } 102 } 103 break; 104 case FLOW_ACTION_REDIRECT: { 105 struct net_device *out_dev; 106 struct mlxsw_sp_fid *fid; 107 u16 fid_index; 108 109 if (mlxsw_sp_acl_block_is_egress_bound(block)) { 110 NL_SET_ERR_MSG_MOD(extack, "Redirect action is not supported on egress"); 111 return -EOPNOTSUPP; 112 } 113 114 /* Forbid block with this rulei to be bound 115 * to egress in future. 116 */ 117 rulei->egress_bind_blocker = 1; 118 119 fid = mlxsw_sp_acl_dummy_fid(mlxsw_sp); 120 fid_index = mlxsw_sp_fid_index(fid); 121 err = mlxsw_sp_acl_rulei_act_fid_set(mlxsw_sp, rulei, 122 fid_index, extack); 123 if (err) 124 return err; 125 126 out_dev = act->dev; 127 err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei, 128 out_dev, extack); 129 if (err) 130 return err; 131 } 132 break; 133 case FLOW_ACTION_MIRRED: { 134 struct net_device *out_dev = act->dev; 135 136 if (mirror_act_count++) { 137 NL_SET_ERR_MSG_MOD(extack, "Multiple mirror actions per rule are not supported"); 138 return -EOPNOTSUPP; 139 } 140 141 err = mlxsw_sp_acl_rulei_act_mirror(mlxsw_sp, rulei, 142 block, out_dev, 143 extack); 144 if (err) 145 return err; 146 } 147 break; 148 case FLOW_ACTION_VLAN_MANGLE: { 149 u16 proto = be16_to_cpu(act->vlan.proto); 150 u8 prio = act->vlan.prio; 151 u16 vid = act->vlan.vid; 152 153 return mlxsw_sp_acl_rulei_act_vlan(mlxsw_sp, rulei, 154 act->id, vid, 155 proto, prio, extack); 156 } 157 default: 158 NL_SET_ERR_MSG_MOD(extack, "Unsupported action"); 159 dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n"); 160 return -EOPNOTSUPP; 161 } 162 } 163 return 0; 164 } 165 166 static int mlxsw_sp_flower_parse_meta(struct mlxsw_sp_acl_rule_info *rulei, 167 struct flow_cls_offload *f, 168 struct mlxsw_sp_acl_block *block) 169 { 170 struct flow_rule *rule = flow_cls_offload_flow_rule(f); 171 struct mlxsw_sp_port *mlxsw_sp_port; 172 struct net_device *ingress_dev; 173 struct flow_match_meta match; 174 175 if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_META)) 176 return 0; 177 178 flow_rule_match_meta(rule, &match); 179 if (match.mask->ingress_ifindex != 0xFFFFFFFF) { 180 NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported ingress ifindex mask"); 181 return -EINVAL; 182 } 183 184 ingress_dev = __dev_get_by_index(block->net, 185 match.key->ingress_ifindex); 186 if (!ingress_dev) { 187 NL_SET_ERR_MSG_MOD(f->common.extack, "Can't find specified ingress port to match on"); 188 return -EINVAL; 189 } 190 191 if (!mlxsw_sp_port_dev_check(ingress_dev)) { 192 NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on non-mlxsw ingress port"); 193 return -EINVAL; 194 } 195 196 mlxsw_sp_port = netdev_priv(ingress_dev); 197 if (mlxsw_sp_port->mlxsw_sp != block->mlxsw_sp) { 198 NL_SET_ERR_MSG_MOD(f->common.extack, "Can't match on a port from different device"); 199 return -EINVAL; 200 } 201 202 mlxsw_sp_acl_rulei_keymask_u32(rulei, 203 MLXSW_AFK_ELEMENT_SRC_SYS_PORT, 204 mlxsw_sp_port->local_port, 205 0xFFFFFFFF); 206 return 0; 207 } 208 209 static void mlxsw_sp_flower_parse_ipv4(struct mlxsw_sp_acl_rule_info *rulei, 210 struct flow_cls_offload *f) 211 { 212 struct flow_match_ipv4_addrs match; 213 214 flow_rule_match_ipv4_addrs(f->rule, &match); 215 216 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31, 217 (char *) &match.key->src, 218 (char *) &match.mask->src, 4); 219 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31, 220 (char *) &match.key->dst, 221 (char *) &match.mask->dst, 4); 222 } 223 224 static void mlxsw_sp_flower_parse_ipv6(struct mlxsw_sp_acl_rule_info *rulei, 225 struct flow_cls_offload *f) 226 { 227 struct flow_match_ipv6_addrs match; 228 229 flow_rule_match_ipv6_addrs(f->rule, &match); 230 231 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_96_127, 232 &match.key->src.s6_addr[0x0], 233 &match.mask->src.s6_addr[0x0], 4); 234 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_64_95, 235 &match.key->src.s6_addr[0x4], 236 &match.mask->src.s6_addr[0x4], 4); 237 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_32_63, 238 &match.key->src.s6_addr[0x8], 239 &match.mask->src.s6_addr[0x8], 4); 240 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP_0_31, 241 &match.key->src.s6_addr[0xC], 242 &match.mask->src.s6_addr[0xC], 4); 243 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_96_127, 244 &match.key->dst.s6_addr[0x0], 245 &match.mask->dst.s6_addr[0x0], 4); 246 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_64_95, 247 &match.key->dst.s6_addr[0x4], 248 &match.mask->dst.s6_addr[0x4], 4); 249 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_32_63, 250 &match.key->dst.s6_addr[0x8], 251 &match.mask->dst.s6_addr[0x8], 4); 252 mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP_0_31, 253 &match.key->dst.s6_addr[0xC], 254 &match.mask->dst.s6_addr[0xC], 4); 255 } 256 257 static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp, 258 struct mlxsw_sp_acl_rule_info *rulei, 259 struct flow_cls_offload *f, 260 u8 ip_proto) 261 { 262 const struct flow_rule *rule = flow_cls_offload_flow_rule(f); 263 struct flow_match_ports match; 264 265 if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) 266 return 0; 267 268 if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) { 269 NL_SET_ERR_MSG_MOD(f->common.extack, "Only UDP and TCP keys are supported"); 270 dev_err(mlxsw_sp->bus_info->dev, "Only UDP and TCP keys are supported\n"); 271 return -EINVAL; 272 } 273 274 flow_rule_match_ports(rule, &match); 275 mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_L4_PORT, 276 ntohs(match.key->dst), 277 ntohs(match.mask->dst)); 278 mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_L4_PORT, 279 ntohs(match.key->src), 280 ntohs(match.mask->src)); 281 return 0; 282 } 283 284 static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp, 285 struct mlxsw_sp_acl_rule_info *rulei, 286 struct flow_cls_offload *f, 287 u8 ip_proto) 288 { 289 const struct flow_rule *rule = flow_cls_offload_flow_rule(f); 290 struct flow_match_tcp match; 291 292 if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) 293 return 0; 294 295 if (ip_proto != IPPROTO_TCP) { 296 NL_SET_ERR_MSG_MOD(f->common.extack, "TCP keys supported only for TCP"); 297 dev_err(mlxsw_sp->bus_info->dev, "TCP keys supported only for TCP\n"); 298 return -EINVAL; 299 } 300 301 flow_rule_match_tcp(rule, &match); 302 303 if (match.mask->flags & htons(0x0E00)) { 304 NL_SET_ERR_MSG_MOD(f->common.extack, "TCP flags match not supported on reserved bits"); 305 dev_err(mlxsw_sp->bus_info->dev, "TCP flags match not supported on reserved bits\n"); 306 return -EINVAL; 307 } 308 309 mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_TCP_FLAGS, 310 ntohs(match.key->flags), 311 ntohs(match.mask->flags)); 312 return 0; 313 } 314 315 static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp, 316 struct mlxsw_sp_acl_rule_info *rulei, 317 struct flow_cls_offload *f, 318 u16 n_proto) 319 { 320 const struct flow_rule *rule = flow_cls_offload_flow_rule(f); 321 struct flow_match_ip match; 322 323 if (!flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) 324 return 0; 325 326 if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) { 327 NL_SET_ERR_MSG_MOD(f->common.extack, "IP keys supported only for IPv4/6"); 328 dev_err(mlxsw_sp->bus_info->dev, "IP keys supported only for IPv4/6\n"); 329 return -EINVAL; 330 } 331 332 flow_rule_match_ip(rule, &match); 333 334 mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_, 335 match.key->ttl, match.mask->ttl); 336 337 mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN, 338 match.key->tos & 0x3, 339 match.mask->tos & 0x3); 340 341 mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP, 342 match.key->tos >> 2, 343 match.mask->tos >> 2); 344 345 return 0; 346 } 347 348 static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, 349 struct mlxsw_sp_acl_block *block, 350 struct mlxsw_sp_acl_rule_info *rulei, 351 struct flow_cls_offload *f) 352 { 353 struct flow_rule *rule = flow_cls_offload_flow_rule(f); 354 struct flow_dissector *dissector = rule->match.dissector; 355 u16 n_proto_mask = 0; 356 u16 n_proto_key = 0; 357 u16 addr_type = 0; 358 u8 ip_proto = 0; 359 int err; 360 361 if (dissector->used_keys & 362 ~(BIT(FLOW_DISSECTOR_KEY_META) | 363 BIT(FLOW_DISSECTOR_KEY_CONTROL) | 364 BIT(FLOW_DISSECTOR_KEY_BASIC) | 365 BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | 366 BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | 367 BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | 368 BIT(FLOW_DISSECTOR_KEY_PORTS) | 369 BIT(FLOW_DISSECTOR_KEY_TCP) | 370 BIT(FLOW_DISSECTOR_KEY_IP) | 371 BIT(FLOW_DISSECTOR_KEY_VLAN))) { 372 dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n"); 373 NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key"); 374 return -EOPNOTSUPP; 375 } 376 377 mlxsw_sp_acl_rulei_priority(rulei, f->common.prio); 378 379 err = mlxsw_sp_flower_parse_meta(rulei, f, block); 380 if (err) 381 return err; 382 383 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { 384 struct flow_match_control match; 385 386 flow_rule_match_control(rule, &match); 387 addr_type = match.key->addr_type; 388 } 389 390 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 391 struct flow_match_basic match; 392 393 flow_rule_match_basic(rule, &match); 394 n_proto_key = ntohs(match.key->n_proto); 395 n_proto_mask = ntohs(match.mask->n_proto); 396 397 if (n_proto_key == ETH_P_ALL) { 398 n_proto_key = 0; 399 n_proto_mask = 0; 400 } 401 mlxsw_sp_acl_rulei_keymask_u32(rulei, 402 MLXSW_AFK_ELEMENT_ETHERTYPE, 403 n_proto_key, n_proto_mask); 404 405 ip_proto = match.key->ip_proto; 406 mlxsw_sp_acl_rulei_keymask_u32(rulei, 407 MLXSW_AFK_ELEMENT_IP_PROTO, 408 match.key->ip_proto, 409 match.mask->ip_proto); 410 } 411 412 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 413 struct flow_match_eth_addrs match; 414 415 flow_rule_match_eth_addrs(rule, &match); 416 mlxsw_sp_acl_rulei_keymask_buf(rulei, 417 MLXSW_AFK_ELEMENT_DMAC_32_47, 418 match.key->dst, 419 match.mask->dst, 2); 420 mlxsw_sp_acl_rulei_keymask_buf(rulei, 421 MLXSW_AFK_ELEMENT_DMAC_0_31, 422 match.key->dst + 2, 423 match.mask->dst + 2, 4); 424 mlxsw_sp_acl_rulei_keymask_buf(rulei, 425 MLXSW_AFK_ELEMENT_SMAC_32_47, 426 match.key->src, 427 match.mask->src, 2); 428 mlxsw_sp_acl_rulei_keymask_buf(rulei, 429 MLXSW_AFK_ELEMENT_SMAC_0_31, 430 match.key->src + 2, 431 match.mask->src + 2, 4); 432 } 433 434 if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { 435 struct flow_match_vlan match; 436 437 flow_rule_match_vlan(rule, &match); 438 if (mlxsw_sp_acl_block_is_egress_bound(block)) { 439 NL_SET_ERR_MSG_MOD(f->common.extack, "vlan_id key is not supported on egress"); 440 return -EOPNOTSUPP; 441 } 442 443 /* Forbid block with this rulei to be bound 444 * to egress in future. 445 */ 446 rulei->egress_bind_blocker = 1; 447 448 if (match.mask->vlan_id != 0) 449 mlxsw_sp_acl_rulei_keymask_u32(rulei, 450 MLXSW_AFK_ELEMENT_VID, 451 match.key->vlan_id, 452 match.mask->vlan_id); 453 if (match.mask->vlan_priority != 0) 454 mlxsw_sp_acl_rulei_keymask_u32(rulei, 455 MLXSW_AFK_ELEMENT_PCP, 456 match.key->vlan_priority, 457 match.mask->vlan_priority); 458 } 459 460 if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) 461 mlxsw_sp_flower_parse_ipv4(rulei, f); 462 463 if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) 464 mlxsw_sp_flower_parse_ipv6(rulei, f); 465 466 err = mlxsw_sp_flower_parse_ports(mlxsw_sp, rulei, f, ip_proto); 467 if (err) 468 return err; 469 err = mlxsw_sp_flower_parse_tcp(mlxsw_sp, rulei, f, ip_proto); 470 if (err) 471 return err; 472 473 err = mlxsw_sp_flower_parse_ip(mlxsw_sp, rulei, f, n_proto_key & n_proto_mask); 474 if (err) 475 return err; 476 477 return mlxsw_sp_flower_parse_actions(mlxsw_sp, block, rulei, 478 &f->rule->action, 479 f->common.extack); 480 } 481 482 int mlxsw_sp_flower_replace(struct mlxsw_sp *mlxsw_sp, 483 struct mlxsw_sp_acl_block *block, 484 struct flow_cls_offload *f) 485 { 486 struct mlxsw_sp_acl_rule_info *rulei; 487 struct mlxsw_sp_acl_ruleset *ruleset; 488 struct mlxsw_sp_acl_rule *rule; 489 int err; 490 491 ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block, 492 f->common.chain_index, 493 MLXSW_SP_ACL_PROFILE_FLOWER, NULL); 494 if (IS_ERR(ruleset)) 495 return PTR_ERR(ruleset); 496 497 rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie, NULL, 498 f->common.extack); 499 if (IS_ERR(rule)) { 500 err = PTR_ERR(rule); 501 goto err_rule_create; 502 } 503 504 rulei = mlxsw_sp_acl_rule_rulei(rule); 505 err = mlxsw_sp_flower_parse(mlxsw_sp, block, rulei, f); 506 if (err) 507 goto err_flower_parse; 508 509 err = mlxsw_sp_acl_rulei_commit(rulei); 510 if (err) 511 goto err_rulei_commit; 512 513 err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule); 514 if (err) 515 goto err_rule_add; 516 517 mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); 518 return 0; 519 520 err_rule_add: 521 err_rulei_commit: 522 err_flower_parse: 523 mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule); 524 err_rule_create: 525 mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); 526 return err; 527 } 528 529 void mlxsw_sp_flower_destroy(struct mlxsw_sp *mlxsw_sp, 530 struct mlxsw_sp_acl_block *block, 531 struct flow_cls_offload *f) 532 { 533 struct mlxsw_sp_acl_ruleset *ruleset; 534 struct mlxsw_sp_acl_rule *rule; 535 536 ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block, 537 f->common.chain_index, 538 MLXSW_SP_ACL_PROFILE_FLOWER, NULL); 539 if (IS_ERR(ruleset)) 540 return; 541 542 rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie); 543 if (rule) { 544 mlxsw_sp_acl_rule_del(mlxsw_sp, rule); 545 mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule); 546 } 547 548 mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); 549 } 550 551 int mlxsw_sp_flower_stats(struct mlxsw_sp *mlxsw_sp, 552 struct mlxsw_sp_acl_block *block, 553 struct flow_cls_offload *f) 554 { 555 struct mlxsw_sp_acl_ruleset *ruleset; 556 struct mlxsw_sp_acl_rule *rule; 557 u64 packets; 558 u64 lastuse; 559 u64 bytes; 560 int err; 561 562 ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block, 563 f->common.chain_index, 564 MLXSW_SP_ACL_PROFILE_FLOWER, NULL); 565 if (WARN_ON(IS_ERR(ruleset))) 566 return -EINVAL; 567 568 rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie); 569 if (!rule) 570 return -EINVAL; 571 572 err = mlxsw_sp_acl_rule_get_stats(mlxsw_sp, rule, &packets, &bytes, 573 &lastuse); 574 if (err) 575 goto err_rule_get_stats; 576 577 flow_stats_update(&f->stats, bytes, packets, lastuse); 578 579 mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); 580 return 0; 581 582 err_rule_get_stats: 583 mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); 584 return err; 585 } 586 587 int mlxsw_sp_flower_tmplt_create(struct mlxsw_sp *mlxsw_sp, 588 struct mlxsw_sp_acl_block *block, 589 struct flow_cls_offload *f) 590 { 591 struct mlxsw_sp_acl_ruleset *ruleset; 592 struct mlxsw_sp_acl_rule_info rulei; 593 int err; 594 595 memset(&rulei, 0, sizeof(rulei)); 596 err = mlxsw_sp_flower_parse(mlxsw_sp, block, &rulei, f); 597 if (err) 598 return err; 599 ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block, 600 f->common.chain_index, 601 MLXSW_SP_ACL_PROFILE_FLOWER, 602 &rulei.values.elusage); 603 604 /* keep the reference to the ruleset */ 605 return PTR_ERR_OR_ZERO(ruleset); 606 } 607 608 void mlxsw_sp_flower_tmplt_destroy(struct mlxsw_sp *mlxsw_sp, 609 struct mlxsw_sp_acl_block *block, 610 struct flow_cls_offload *f) 611 { 612 struct mlxsw_sp_acl_ruleset *ruleset; 613 614 ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, block, 615 f->common.chain_index, 616 MLXSW_SP_ACL_PROFILE_FLOWER, NULL); 617 if (IS_ERR(ruleset)) 618 return; 619 /* put the reference to the ruleset kept in create */ 620 mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); 621 mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); 622 } 623