Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sfc: offload left-hand side rules for conntrack

Handle the (comparatively) simple case of a -trk rule on an efx netdev
(i.e. not a tunnel decap rule) with ct and goto chain actions.

Reviewed-by: Pieter Jansen van Vuuren <pieter.jansen-van-vuuren@amd.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: Edward Cree <ecree.xilinx@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Edward Cree and committed by
David S. Miller
01ad088f 1dfc29be

+599
+2
drivers/net/ethernet/sfc/bitfield.h
··· 26 26 /* Lowest bit numbers and widths */ 27 27 #define EFX_DUMMY_FIELD_LBN 0 28 28 #define EFX_DUMMY_FIELD_WIDTH 0 29 + #define EFX_BYTE_0_LBN 0 30 + #define EFX_BYTE_0_WIDTH 8 29 31 #define EFX_WORD_0_LBN 0 30 32 #define EFX_WORD_0_WIDTH 16 31 33 #define EFX_WORD_1_LBN 16
+287
drivers/net/ethernet/sfc/mae.c
··· 727 727 } 728 728 return 0; 729 729 } 730 + 731 + /* Checks for match fields not supported in LHS Outer Rules */ 732 + #define UNSUPPORTED(_field) ({ \ 733 + enum mask_type typ = classify_mask((const u8 *)&mask->_field, \ 734 + sizeof(mask->_field)); \ 735 + \ 736 + if (typ != MASK_ZEROES) { \ 737 + NL_SET_ERR_MSG_MOD(extack, "Unsupported match field " #_field);\ 738 + rc = -EOPNOTSUPP; \ 739 + } \ 740 + rc; \ 741 + }) 742 + #define UNSUPPORTED_BIT(_field) ({ \ 743 + if (mask->_field) { \ 744 + NL_SET_ERR_MSG_MOD(extack, "Unsupported match field " #_field);\ 745 + rc = -EOPNOTSUPP; \ 746 + } \ 747 + rc; \ 748 + }) 749 + 750 + /* LHS rules are (normally) inserted in the Outer Rule table, which means 751 + * they use ENC_ fields in hardware to match regular (not enc_) fields from 752 + * &struct efx_tc_match_fields. 753 + */ 754 + int efx_mae_match_check_caps_lhs(struct efx_nic *efx, 755 + const struct efx_tc_match_fields *mask, 756 + struct netlink_ext_ack *extack) 757 + { 758 + const u8 *supported_fields = efx->tc->caps->outer_rule_fields; 759 + __be32 ingress_port = cpu_to_be32(mask->ingress_port); 760 + enum mask_type ingress_port_mask_type; 761 + int rc; 762 + 763 + /* Check for _PREFIX assumes big-endian, so we need to convert */ 764 + ingress_port_mask_type = classify_mask((const u8 *)&ingress_port, 765 + sizeof(ingress_port)); 766 + rc = efx_mae_match_check_cap_typ(supported_fields[MAE_FIELD_INGRESS_PORT], 767 + ingress_port_mask_type); 768 + if (rc) { 769 + NL_SET_ERR_MSG_FMT_MOD(extack, "No support for %s mask in field %s\n", 770 + mask_type_name(ingress_port_mask_type), 771 + "ingress_port"); 772 + return rc; 773 + } 774 + if (CHECK(ENC_ETHER_TYPE, eth_proto) || 775 + CHECK(ENC_VLAN0_TCI, vlan_tci[0]) || 776 + CHECK(ENC_VLAN0_PROTO, vlan_proto[0]) || 777 + CHECK(ENC_VLAN1_TCI, vlan_tci[1]) || 778 + CHECK(ENC_VLAN1_PROTO, vlan_proto[1]) || 779 + CHECK(ENC_ETH_SADDR, eth_saddr) || 780 + CHECK(ENC_ETH_DADDR, eth_daddr) || 781 + CHECK(ENC_IP_PROTO, ip_proto) || 782 + CHECK(ENC_IP_TOS, ip_tos) || 783 + CHECK(ENC_IP_TTL, ip_ttl) || 784 + CHECK_BIT(ENC_IP_FRAG, ip_frag) || 785 + UNSUPPORTED_BIT(ip_firstfrag) || 786 + CHECK(ENC_SRC_IP4, src_ip) || 787 + CHECK(ENC_DST_IP4, dst_ip) || 788 + #ifdef CONFIG_IPV6 789 + CHECK(ENC_SRC_IP6, src_ip6) || 790 + CHECK(ENC_DST_IP6, dst_ip6) || 791 + #endif 792 + CHECK(ENC_L4_SPORT, l4_sport) || 793 + CHECK(ENC_L4_DPORT, l4_dport) || 794 + UNSUPPORTED(tcp_flags) || 795 + CHECK_BIT(TCP_SYN_FIN_RST, tcp_syn_fin_rst)) 796 + return rc; 797 + if (efx_tc_match_is_encap(mask)) { 798 + /* can't happen; disallowed for local rules, translated 799 + * for foreign rules. 800 + */ 801 + NL_SET_ERR_MSG_MOD(extack, "Unexpected encap match in LHS rule"); 802 + return -EOPNOTSUPP; 803 + } 804 + if (UNSUPPORTED(enc_keyid) || 805 + /* Can't filter on conntrack in LHS rules */ 806 + UNSUPPORTED_BIT(ct_state_trk) || 807 + UNSUPPORTED_BIT(ct_state_est) || 808 + UNSUPPORTED(ct_mark) || 809 + UNSUPPORTED(recirc_id)) 810 + return rc; 811 + return 0; 812 + } 813 + #undef UNSUPPORTED 730 814 #undef CHECK_BIT 731 815 #undef CHECK 732 816 ··· 1491 1407 */ 1492 1408 encap->fw_id = MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OUTER_RULE_ID_NULL; 1493 1409 return 0; 1410 + } 1411 + 1412 + static int efx_mae_populate_lhs_match_criteria(MCDI_DECLARE_STRUCT_PTR(match_crit), 1413 + const struct efx_tc_match *match) 1414 + { 1415 + if (match->mask.ingress_port) { 1416 + if (~match->mask.ingress_port) 1417 + return -EOPNOTSUPP; 1418 + MCDI_STRUCT_SET_DWORD(match_crit, 1419 + MAE_ENC_FIELD_PAIRS_INGRESS_MPORT_SELECTOR, 1420 + match->value.ingress_port); 1421 + } 1422 + MCDI_STRUCT_SET_DWORD(match_crit, MAE_ENC_FIELD_PAIRS_INGRESS_MPORT_SELECTOR_MASK, 1423 + match->mask.ingress_port); 1424 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE, 1425 + match->value.eth_proto); 1426 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETHER_TYPE_BE_MASK, 1427 + match->mask.eth_proto); 1428 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN0_TCI_BE, 1429 + match->value.vlan_tci[0]); 1430 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN0_TCI_BE_MASK, 1431 + match->mask.vlan_tci[0]); 1432 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN0_PROTO_BE, 1433 + match->value.vlan_proto[0]); 1434 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN0_PROTO_BE_MASK, 1435 + match->mask.vlan_proto[0]); 1436 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN1_TCI_BE, 1437 + match->value.vlan_tci[1]); 1438 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN1_TCI_BE_MASK, 1439 + match->mask.vlan_tci[1]); 1440 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN1_PROTO_BE, 1441 + match->value.vlan_proto[1]); 1442 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_VLAN1_PROTO_BE_MASK, 1443 + match->mask.vlan_proto[1]); 1444 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETH_SADDR_BE), 1445 + match->value.eth_saddr, ETH_ALEN); 1446 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETH_SADDR_BE_MASK), 1447 + match->mask.eth_saddr, ETH_ALEN); 1448 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETH_DADDR_BE), 1449 + match->value.eth_daddr, ETH_ALEN); 1450 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_ETH_DADDR_BE_MASK), 1451 + match->mask.eth_daddr, ETH_ALEN); 1452 + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO, 1453 + match->value.ip_proto); 1454 + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_PROTO_MASK, 1455 + match->mask.ip_proto); 1456 + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS, 1457 + match->value.ip_tos); 1458 + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TOS_MASK, 1459 + match->mask.ip_tos); 1460 + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TTL, 1461 + match->value.ip_ttl); 1462 + MCDI_STRUCT_SET_BYTE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_IP_TTL_MASK, 1463 + match->mask.ip_ttl); 1464 + MCDI_STRUCT_POPULATE_BYTE_1(match_crit, 1465 + MAE_ENC_FIELD_PAIRS_ENC_VLAN_FLAGS, 1466 + MAE_ENC_FIELD_PAIRS_ENC_IP_FRAG, 1467 + match->value.ip_frag); 1468 + MCDI_STRUCT_POPULATE_BYTE_1(match_crit, 1469 + MAE_ENC_FIELD_PAIRS_ENC_VLAN_FLAGS_MASK, 1470 + MAE_ENC_FIELD_PAIRS_ENC_IP_FRAG_MASK, 1471 + match->mask.ip_frag); 1472 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP4_BE, 1473 + match->value.src_ip); 1474 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP4_BE_MASK, 1475 + match->mask.src_ip); 1476 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP4_BE, 1477 + match->value.dst_ip); 1478 + MCDI_STRUCT_SET_DWORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP4_BE_MASK, 1479 + match->mask.dst_ip); 1480 + #ifdef CONFIG_IPV6 1481 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP6_BE), 1482 + &match->value.src_ip6, sizeof(struct in6_addr)); 1483 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_SRC_IP6_BE_MASK), 1484 + &match->mask.src_ip6, sizeof(struct in6_addr)); 1485 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP6_BE), 1486 + &match->value.dst_ip6, sizeof(struct in6_addr)); 1487 + memcpy(MCDI_STRUCT_PTR(match_crit, MAE_ENC_FIELD_PAIRS_ENC_DST_IP6_BE_MASK), 1488 + &match->mask.dst_ip6, sizeof(struct in6_addr)); 1489 + #endif 1490 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_SPORT_BE, 1491 + match->value.l4_sport); 1492 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_SPORT_BE_MASK, 1493 + match->mask.l4_sport); 1494 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE, 1495 + match->value.l4_dport); 1496 + MCDI_STRUCT_SET_WORD_BE(match_crit, MAE_ENC_FIELD_PAIRS_ENC_L4_DPORT_BE_MASK, 1497 + match->mask.l4_dport); 1498 + /* No enc-keys in LHS rules. Caps check should have caught this; any 1499 + * enc-keys from an fLHS should have been translated to regular keys 1500 + * and any EM should be a pseudo (we're an OR so can't have a direct 1501 + * EM with another OR). 1502 + */ 1503 + if (WARN_ON_ONCE(match->encap && !match->encap->type)) 1504 + return -EOPNOTSUPP; 1505 + if (WARN_ON_ONCE(match->mask.enc_src_ip)) 1506 + return -EOPNOTSUPP; 1507 + if (WARN_ON_ONCE(match->mask.enc_dst_ip)) 1508 + return -EOPNOTSUPP; 1509 + #ifdef CONFIG_IPV6 1510 + if (WARN_ON_ONCE(!ipv6_addr_any(&match->mask.enc_src_ip6))) 1511 + return -EOPNOTSUPP; 1512 + if (WARN_ON_ONCE(!ipv6_addr_any(&match->mask.enc_dst_ip6))) 1513 + return -EOPNOTSUPP; 1514 + #endif 1515 + if (WARN_ON_ONCE(match->mask.enc_ip_tos)) 1516 + return -EOPNOTSUPP; 1517 + if (WARN_ON_ONCE(match->mask.enc_ip_ttl)) 1518 + return -EOPNOTSUPP; 1519 + if (WARN_ON_ONCE(match->mask.enc_sport)) 1520 + return -EOPNOTSUPP; 1521 + if (WARN_ON_ONCE(match->mask.enc_dport)) 1522 + return -EOPNOTSUPP; 1523 + if (WARN_ON_ONCE(match->mask.enc_keyid)) 1524 + return -EOPNOTSUPP; 1525 + return 0; 1526 + } 1527 + 1528 + static int efx_mae_insert_lhs_outer_rule(struct efx_nic *efx, 1529 + struct efx_tc_lhs_rule *rule, u32 prio) 1530 + { 1531 + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_OUTER_RULE_INSERT_IN_LEN(MAE_ENC_FIELD_PAIRS_LEN)); 1532 + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_OUTER_RULE_INSERT_OUT_LEN); 1533 + MCDI_DECLARE_STRUCT_PTR(match_crit); 1534 + const struct efx_tc_lhs_action *act; 1535 + size_t outlen; 1536 + int rc; 1537 + 1538 + MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_PRIO, prio); 1539 + /* match */ 1540 + match_crit = _MCDI_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_FIELD_MATCH_CRITERIA); 1541 + rc = efx_mae_populate_lhs_match_criteria(match_crit, &rule->match); 1542 + if (rc) 1543 + return rc; 1544 + 1545 + /* action */ 1546 + act = &rule->lhs_act; 1547 + MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_ENCAP_TYPE, 1548 + MAE_MCDI_ENCAP_TYPE_NONE); 1549 + /* We always inhibit CT lookup on TCP_INTERESTING_FLAGS, since the 1550 + * SW path needs to process the packet to update the conntrack tables 1551 + * on connection establishment (SYN) or termination (FIN, RST). 1552 + */ 1553 + MCDI_POPULATE_DWORD_6(inbuf, MAE_OUTER_RULE_INSERT_IN_LOOKUP_CONTROL, 1554 + MAE_OUTER_RULE_INSERT_IN_DO_CT, !!act->zone, 1555 + MAE_OUTER_RULE_INSERT_IN_CT_TCP_FLAGS_INHIBIT, 1, 1556 + MAE_OUTER_RULE_INSERT_IN_CT_DOMAIN, 1557 + act->zone ? act->zone->zone : 0, 1558 + MAE_OUTER_RULE_INSERT_IN_CT_VNI_MODE, 1559 + MAE_CT_VNI_MODE_ZERO, 1560 + MAE_OUTER_RULE_INSERT_IN_DO_COUNT, !!act->count, 1561 + MAE_OUTER_RULE_INSERT_IN_RECIRC_ID, 1562 + act->rid ? act->rid->fw_id : 0); 1563 + if (act->count) 1564 + MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_INSERT_IN_COUNTER_ID, 1565 + act->count->cnt->fw_id); 1566 + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_OUTER_RULE_INSERT, inbuf, 1567 + sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); 1568 + if (rc) 1569 + return rc; 1570 + if (outlen < sizeof(outbuf)) 1571 + return -EIO; 1572 + rule->fw_id = MCDI_DWORD(outbuf, MAE_OUTER_RULE_INSERT_OUT_OR_ID); 1573 + return 0; 1574 + } 1575 + 1576 + int efx_mae_insert_lhs_rule(struct efx_nic *efx, struct efx_tc_lhs_rule *rule, 1577 + u32 prio) 1578 + { 1579 + return efx_mae_insert_lhs_outer_rule(efx, rule, prio); 1580 + } 1581 + 1582 + static int efx_mae_remove_lhs_outer_rule(struct efx_nic *efx, 1583 + struct efx_tc_lhs_rule *rule) 1584 + { 1585 + MCDI_DECLARE_BUF(outbuf, MC_CMD_MAE_OUTER_RULE_REMOVE_OUT_LEN(1)); 1586 + MCDI_DECLARE_BUF(inbuf, MC_CMD_MAE_OUTER_RULE_REMOVE_IN_LEN(1)); 1587 + size_t outlen; 1588 + int rc; 1589 + 1590 + MCDI_SET_DWORD(inbuf, MAE_OUTER_RULE_REMOVE_IN_OR_ID, rule->fw_id); 1591 + rc = efx_mcdi_rpc(efx, MC_CMD_MAE_OUTER_RULE_REMOVE, inbuf, 1592 + sizeof(inbuf), outbuf, sizeof(outbuf), &outlen); 1593 + if (rc) 1594 + return rc; 1595 + if (outlen < sizeof(outbuf)) 1596 + return -EIO; 1597 + /* FW freed a different ID than we asked for, should also never happen. 1598 + * Warn because it means we've now got a different idea to the FW of 1599 + * what encap_mds exist, which could cause mayhem later. 1600 + */ 1601 + if (WARN_ON(MCDI_DWORD(outbuf, MAE_OUTER_RULE_REMOVE_OUT_REMOVED_OR_ID) != rule->fw_id)) 1602 + return -EIO; 1603 + /* We're probably about to free @rule, but let's just make sure its 1604 + * fw_id is blatted so that it won't look valid if it leaks out. 1605 + */ 1606 + rule->fw_id = MC_CMD_MAE_OUTER_RULE_INSERT_OUT_OUTER_RULE_ID_NULL; 1607 + return 0; 1608 + } 1609 + 1610 + int efx_mae_remove_lhs_rule(struct efx_nic *efx, struct efx_tc_lhs_rule *rule) 1611 + { 1612 + return efx_mae_remove_lhs_outer_rule(efx, rule); 1494 1613 } 1495 1614 1496 1615 /* Populating is done by taking each byte of @value in turn and storing
+6
drivers/net/ethernet/sfc/mae.h
··· 84 84 int efx_mae_match_check_caps(struct efx_nic *efx, 85 85 const struct efx_tc_match_fields *mask, 86 86 struct netlink_ext_ack *extack); 87 + int efx_mae_match_check_caps_lhs(struct efx_nic *efx, 88 + const struct efx_tc_match_fields *mask, 89 + struct netlink_ext_ack *extack); 87 90 int efx_mae_check_encap_match_caps(struct efx_nic *efx, bool ipv6, 88 91 u8 ip_tos_mask, __be16 udp_sport_mask, 89 92 struct netlink_ext_ack *extack); ··· 115 112 struct efx_tc_encap_match *encap); 116 113 int efx_mae_unregister_encap_match(struct efx_nic *efx, 117 114 struct efx_tc_encap_match *encap); 115 + int efx_mae_insert_lhs_rule(struct efx_nic *efx, struct efx_tc_lhs_rule *rule, 116 + u32 prio); 117 + int efx_mae_remove_lhs_rule(struct efx_nic *efx, struct efx_tc_lhs_rule *rule); 118 118 struct efx_tc_ct_entry; /* see tc_conntrack.h */ 119 119 int efx_mae_insert_ct(struct efx_nic *efx, struct efx_tc_ct_entry *conn); 120 120 int efx_mae_remove_ct(struct efx_nic *efx, struct efx_tc_ct_entry *conn);
+6
drivers/net/ethernet/sfc/mcdi.h
··· 218 218 BUILD_BUG_ON(_field ## _LEN != 1); \ 219 219 *(u8 *)MCDI_STRUCT_PTR(_buf, _field) = _value; \ 220 220 } while (0) 221 + #define MCDI_STRUCT_POPULATE_BYTE_1(_buf, _field, _name, _value) do { \ 222 + efx_dword_t _temp; \ 223 + EFX_POPULATE_DWORD_1(_temp, _name, _value); \ 224 + MCDI_STRUCT_SET_BYTE(_buf, _field, \ 225 + EFX_DWORD_FIELD(_temp, EFX_BYTE_0)); \ 226 + } while (0) 221 227 #define MCDI_BYTE(_buf, _field) \ 222 228 ((void)BUILD_BUG_ON_ZERO(MC_CMD_ ## _field ## _LEN != 1), \ 223 229 *MCDI_PTR(_buf, _field))
+282
drivers/net/ethernet/sfc/tc.c
··· 12 12 #include <net/pkt_cls.h> 13 13 #include <net/vxlan.h> 14 14 #include <net/geneve.h> 15 + #include <net/tc_act/tc_ct.h> 15 16 #include "tc.h" 16 17 #include "tc_bindings.h" 17 18 #include "tc_encap_actions.h" ··· 96 95 .key_len = sizeof(unsigned long), 97 96 .key_offset = offsetof(struct efx_tc_flow_rule, cookie), 98 97 .head_offset = offsetof(struct efx_tc_flow_rule, linkage), 98 + }; 99 + 100 + static const struct rhashtable_params efx_tc_lhs_rule_ht_params = { 101 + .key_len = sizeof(unsigned long), 102 + .key_offset = offsetof(struct efx_tc_lhs_rule, cookie), 103 + .head_offset = offsetof(struct efx_tc_lhs_rule, linkage), 99 104 }; 100 105 101 106 static const struct rhashtable_params efx_tc_recirc_ht_params = { ··· 743 736 } 744 737 } 745 738 739 + /** 740 + * DOC: TC conntrack sequences 741 + * 742 + * The MAE hardware can handle at most two rounds of action rule matching, 743 + * consequently we support conntrack through the notion of a "left-hand side 744 + * rule". This is a rule which typically contains only the actions "ct" and 745 + * "goto chain N", and corresponds to one or more "right-hand side rules" in 746 + * chain N, which typically match on +trk+est, and may perform ct(nat) actions. 747 + * RHS rules go in the Action Rule table as normal but with a nonzero recirc_id 748 + * (the hardware equivalent of chain_index), while LHS rules may go in either 749 + * the Action Rule or the Outer Rule table, the latter being preferred for 750 + * performance reasons, and set both DO_CT and a recirc_id in their response. 751 + * 752 + * Besides the RHS rules, there are often also similar rules matching on 753 + * +trk+new which perform the ct(commit) action. These are not offloaded. 754 + */ 755 + 756 + static bool efx_tc_rule_is_lhs_rule(struct flow_rule *fr, 757 + struct efx_tc_match *match) 758 + { 759 + const struct flow_action_entry *fa; 760 + int i; 761 + 762 + flow_action_for_each(i, fa, &fr->action) { 763 + switch (fa->id) { 764 + case FLOW_ACTION_GOTO: 765 + return true; 766 + case FLOW_ACTION_CT: 767 + /* If rule is -trk, or doesn't mention trk at all, then 768 + * a CT action implies a conntrack lookup (hence it's an 769 + * LHS rule). If rule is +trk, then a CT action could 770 + * just be ct(nat) or even ct(commit) (though the latter 771 + * can't be offloaded). 772 + */ 773 + if (!match->mask.ct_state_trk || !match->value.ct_state_trk) 774 + return true; 775 + break; 776 + default: 777 + break; 778 + } 779 + } 780 + return false; 781 + } 782 + 783 + static int efx_tc_flower_handle_lhs_actions(struct efx_nic *efx, 784 + struct flow_cls_offload *tc, 785 + struct flow_rule *fr, 786 + struct net_device *net_dev, 787 + struct efx_tc_lhs_rule *rule) 788 + 789 + { 790 + struct netlink_ext_ack *extack = tc->common.extack; 791 + struct efx_tc_lhs_action *act = &rule->lhs_act; 792 + const struct flow_action_entry *fa; 793 + bool pipe = true; 794 + int i; 795 + 796 + flow_action_for_each(i, fa, &fr->action) { 797 + struct efx_tc_ct_zone *ct_zone; 798 + struct efx_tc_recirc_id *rid; 799 + 800 + if (!pipe) { 801 + /* more actions after a non-pipe action */ 802 + NL_SET_ERR_MSG_MOD(extack, "Action follows non-pipe action"); 803 + return -EINVAL; 804 + } 805 + switch (fa->id) { 806 + case FLOW_ACTION_GOTO: 807 + if (!fa->chain_index) { 808 + NL_SET_ERR_MSG_MOD(extack, "Can't goto chain 0, no looping in hw"); 809 + return -EOPNOTSUPP; 810 + } 811 + rid = efx_tc_get_recirc_id(efx, fa->chain_index, 812 + net_dev); 813 + if (IS_ERR(rid)) { 814 + NL_SET_ERR_MSG_MOD(extack, "Failed to allocate a hardware recirculation ID for this chain_index"); 815 + return PTR_ERR(rid); 816 + } 817 + act->rid = rid; 818 + if (fa->hw_stats) { 819 + struct efx_tc_counter_index *cnt; 820 + 821 + if (!(fa->hw_stats & FLOW_ACTION_HW_STATS_DELAYED)) { 822 + NL_SET_ERR_MSG_FMT_MOD(extack, 823 + "hw_stats_type %u not supported (only 'delayed')", 824 + fa->hw_stats); 825 + return -EOPNOTSUPP; 826 + } 827 + cnt = efx_tc_flower_get_counter_index(efx, tc->cookie, 828 + EFX_TC_COUNTER_TYPE_OR); 829 + if (IS_ERR(cnt)) { 830 + NL_SET_ERR_MSG_MOD(extack, "Failed to obtain a counter"); 831 + return PTR_ERR(cnt); 832 + } 833 + WARN_ON(act->count); /* can't happen */ 834 + act->count = cnt; 835 + } 836 + pipe = false; 837 + break; 838 + case FLOW_ACTION_CT: 839 + if (act->zone) { 840 + NL_SET_ERR_MSG_MOD(extack, "Can't offload multiple ct actions"); 841 + return -EOPNOTSUPP; 842 + } 843 + if (fa->ct.action & (TCA_CT_ACT_COMMIT | 844 + TCA_CT_ACT_FORCE)) { 845 + NL_SET_ERR_MSG_MOD(extack, "Can't offload ct commit/force"); 846 + return -EOPNOTSUPP; 847 + } 848 + if (fa->ct.action & TCA_CT_ACT_CLEAR) { 849 + NL_SET_ERR_MSG_MOD(extack, "Can't clear ct in LHS rule"); 850 + return -EOPNOTSUPP; 851 + } 852 + if (fa->ct.action & (TCA_CT_ACT_NAT | 853 + TCA_CT_ACT_NAT_SRC | 854 + TCA_CT_ACT_NAT_DST)) { 855 + NL_SET_ERR_MSG_MOD(extack, "Can't perform NAT in LHS rule - packet isn't conntracked yet"); 856 + return -EOPNOTSUPP; 857 + } 858 + if (fa->ct.action) { 859 + NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled ct.action %u for LHS rule\n", 860 + fa->ct.action); 861 + return -EOPNOTSUPP; 862 + } 863 + ct_zone = efx_tc_ct_register_zone(efx, fa->ct.zone, 864 + fa->ct.flow_table); 865 + if (IS_ERR(ct_zone)) { 866 + NL_SET_ERR_MSG_MOD(extack, "Failed to register for CT updates"); 867 + return PTR_ERR(ct_zone); 868 + } 869 + act->zone = ct_zone; 870 + break; 871 + default: 872 + NL_SET_ERR_MSG_FMT_MOD(extack, "Unhandled action %u for LHS rule\n", 873 + fa->id); 874 + return -EOPNOTSUPP; 875 + } 876 + } 877 + 878 + if (pipe) { 879 + NL_SET_ERR_MSG_MOD(extack, "Missing goto chain in LHS rule"); 880 + return -EOPNOTSUPP; 881 + } 882 + return 0; 883 + } 884 + 885 + static void efx_tc_flower_release_lhs_actions(struct efx_nic *efx, 886 + struct efx_tc_lhs_action *act) 887 + { 888 + if (act->rid) 889 + efx_tc_put_recirc_id(efx, act->rid); 890 + if (act->zone) 891 + efx_tc_ct_unregister_zone(efx, act->zone); 892 + if (act->count) 893 + efx_tc_flower_put_counter_index(efx, act->count); 894 + } 895 + 746 896 static int efx_tc_flower_replace_foreign(struct efx_nic *efx, 747 897 struct net_device *net_dev, 748 898 struct flow_cls_offload *tc) ··· 1214 1050 return rc; 1215 1051 } 1216 1052 1053 + static int efx_tc_flower_replace_lhs(struct efx_nic *efx, 1054 + struct flow_cls_offload *tc, 1055 + struct flow_rule *fr, 1056 + struct efx_tc_match *match, 1057 + struct efx_rep *efv, 1058 + struct net_device *net_dev) 1059 + { 1060 + struct netlink_ext_ack *extack = tc->common.extack; 1061 + struct efx_tc_lhs_rule *rule, *old; 1062 + int rc; 1063 + 1064 + if (tc->common.chain_index) { 1065 + NL_SET_ERR_MSG_MOD(extack, "LHS rule only allowed in chain 0"); 1066 + return -EOPNOTSUPP; 1067 + } 1068 + 1069 + if (match->mask.ct_state_trk && match->value.ct_state_trk) { 1070 + NL_SET_ERR_MSG_MOD(extack, "LHS rule can never match +trk"); 1071 + return -EOPNOTSUPP; 1072 + } 1073 + /* LHS rules are always -trk, so we don't need to match on that */ 1074 + match->mask.ct_state_trk = 0; 1075 + match->value.ct_state_trk = 0; 1076 + 1077 + rc = efx_mae_match_check_caps_lhs(efx, &match->mask, extack); 1078 + if (rc) 1079 + return rc; 1080 + 1081 + rule = kzalloc(sizeof(*rule), GFP_USER); 1082 + if (!rule) 1083 + return -ENOMEM; 1084 + rule->cookie = tc->cookie; 1085 + old = rhashtable_lookup_get_insert_fast(&efx->tc->lhs_rule_ht, 1086 + &rule->linkage, 1087 + efx_tc_lhs_rule_ht_params); 1088 + if (old) { 1089 + netif_dbg(efx, drv, efx->net_dev, 1090 + "Already offloaded rule (cookie %lx)\n", tc->cookie); 1091 + rc = -EEXIST; 1092 + NL_SET_ERR_MSG_MOD(extack, "Rule already offloaded"); 1093 + goto release; 1094 + } 1095 + 1096 + /* Parse actions */ 1097 + /* See note in efx_tc_flower_replace() regarding passed net_dev 1098 + * (used for efx_tc_get_recirc_id()). 1099 + */ 1100 + rc = efx_tc_flower_handle_lhs_actions(efx, tc, fr, efx->net_dev, rule); 1101 + if (rc) 1102 + goto release; 1103 + 1104 + rule->match = *match; 1105 + 1106 + rc = efx_mae_insert_lhs_rule(efx, rule, EFX_TC_PRIO_TC); 1107 + if (rc) { 1108 + NL_SET_ERR_MSG_MOD(extack, "Failed to insert rule in hw"); 1109 + goto release; 1110 + } 1111 + netif_dbg(efx, drv, efx->net_dev, 1112 + "Successfully parsed lhs rule (cookie %lx)\n", 1113 + tc->cookie); 1114 + return 0; 1115 + 1116 + release: 1117 + efx_tc_flower_release_lhs_actions(efx, &rule->lhs_act); 1118 + if (!old) 1119 + rhashtable_remove_fast(&efx->tc->lhs_rule_ht, &rule->linkage, 1120 + efx_tc_lhs_rule_ht_params); 1121 + kfree(rule); 1122 + return rc; 1123 + } 1124 + 1217 1125 static int efx_tc_flower_replace(struct efx_nic *efx, 1218 1126 struct net_device *net_dev, 1219 1127 struct flow_cls_offload *tc, ··· 1340 1104 NL_SET_ERR_MSG_MOD(extack, "Ingress enc_key matches not supported"); 1341 1105 return -EOPNOTSUPP; 1342 1106 } 1107 + 1108 + if (efx_tc_rule_is_lhs_rule(fr, &match)) 1109 + return efx_tc_flower_replace_lhs(efx, tc, fr, &match, efv, 1110 + net_dev); 1343 1111 1344 1112 /* chain_index 0 is always recirc_id 0 (and does not appear in recirc_ht). 1345 1113 * Conveniently, match.rid == NULL and match.value.recirc_id == 0 owing ··· 1752 1512 struct flow_cls_offload *tc) 1753 1513 { 1754 1514 struct netlink_ext_ack *extack = tc->common.extack; 1515 + struct efx_tc_lhs_rule *lhs_rule; 1755 1516 struct efx_tc_flow_rule *rule; 1517 + 1518 + lhs_rule = rhashtable_lookup_fast(&efx->tc->lhs_rule_ht, &tc->cookie, 1519 + efx_tc_lhs_rule_ht_params); 1520 + if (lhs_rule) { 1521 + /* Remove it from HW */ 1522 + efx_mae_remove_lhs_rule(efx, lhs_rule); 1523 + /* Delete it from SW */ 1524 + efx_tc_flower_release_lhs_actions(efx, &lhs_rule->lhs_act); 1525 + rhashtable_remove_fast(&efx->tc->lhs_rule_ht, &lhs_rule->linkage, 1526 + efx_tc_lhs_rule_ht_params); 1527 + if (lhs_rule->match.encap) 1528 + efx_tc_flower_release_encap_match(efx, lhs_rule->match.encap); 1529 + netif_dbg(efx, drv, efx->net_dev, "Removed (lhs) filter %lx\n", 1530 + lhs_rule->cookie); 1531 + kfree(lhs_rule); 1532 + return 0; 1533 + } 1756 1534 1757 1535 rule = rhashtable_lookup_fast(&efx->tc->match_action_ht, &tc->cookie, 1758 1536 efx_tc_match_action_ht_params); ··· 2138 1880 kfree(rid); 2139 1881 } 2140 1882 1883 + static void efx_tc_lhs_free(void *ptr, void *arg) 1884 + { 1885 + struct efx_tc_lhs_rule *rule = ptr; 1886 + struct efx_nic *efx = arg; 1887 + 1888 + netif_err(efx, drv, efx->net_dev, 1889 + "tc lhs_rule %lx still present at teardown, removing\n", 1890 + rule->cookie); 1891 + 1892 + if (rule->lhs_act.zone) 1893 + efx_tc_ct_unregister_zone(efx, rule->lhs_act.zone); 1894 + if (rule->lhs_act.count) 1895 + efx_tc_flower_put_counter_index(efx, rule->lhs_act.count); 1896 + efx_mae_remove_lhs_rule(efx, rule); 1897 + 1898 + kfree(rule); 1899 + } 1900 + 2141 1901 static void efx_tc_flow_free(void *ptr, void *arg) 2142 1902 { 2143 1903 struct efx_tc_flow_rule *rule = ptr; ··· 2202 1926 rc = rhashtable_init(&efx->tc->match_action_ht, &efx_tc_match_action_ht_params); 2203 1927 if (rc < 0) 2204 1928 goto fail_match_action_ht; 1929 + rc = rhashtable_init(&efx->tc->lhs_rule_ht, &efx_tc_lhs_rule_ht_params); 1930 + if (rc < 0) 1931 + goto fail_lhs_rule_ht; 2205 1932 rc = efx_tc_init_conntrack(efx); 2206 1933 if (rc < 0) 2207 1934 goto fail_conntrack; ··· 2227 1948 fail_recirc_ht: 2228 1949 efx_tc_destroy_conntrack(efx); 2229 1950 fail_conntrack: 1951 + rhashtable_destroy(&efx->tc->lhs_rule_ht); 1952 + fail_lhs_rule_ht: 2230 1953 rhashtable_destroy(&efx->tc->match_action_ht); 2231 1954 fail_match_action_ht: 2232 1955 rhashtable_destroy(&efx->tc->encap_match_ht); ··· 2259 1978 MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL); 2260 1979 EFX_WARN_ON_PARANOID(efx->tc->facts.reps.fw_id != 2261 1980 MC_CMD_MAE_ACTION_SET_LIST_ALLOC_OUT_ACTION_SET_LIST_ID_NULL); 1981 + rhashtable_free_and_destroy(&efx->tc->lhs_rule_ht, efx_tc_lhs_free, efx); 2262 1982 rhashtable_free_and_destroy(&efx->tc->match_action_ht, efx_tc_flow_free, 2263 1983 efx); 2264 1984 rhashtable_free_and_destroy(&efx->tc->encap_match_ht,
+16
drivers/net/ethernet/sfc/tc.h
··· 140 140 u32 fw_id; 141 141 }; 142 142 143 + struct efx_tc_lhs_action { 144 + struct efx_tc_recirc_id *rid; 145 + struct efx_tc_ct_zone *zone; 146 + struct efx_tc_counter_index *count; 147 + }; 148 + 143 149 struct efx_tc_flow_rule { 144 150 unsigned long cookie; 145 151 struct rhash_head linkage; 146 152 struct efx_tc_match match; 147 153 struct efx_tc_action_set_list acts; 148 154 struct efx_tc_action_set_list *fallback; /* what to use when unready? */ 155 + u32 fw_id; 156 + }; 157 + 158 + struct efx_tc_lhs_rule { 159 + unsigned long cookie; 160 + struct efx_tc_match match; 161 + struct efx_tc_lhs_action lhs_act; 162 + struct rhash_head linkage; 149 163 u32 fw_id; 150 164 }; 151 165 ··· 222 208 * @encap_ht: Hashtable of TC encap actions 223 209 * @encap_match_ht: Hashtable of TC encap matches 224 210 * @match_action_ht: Hashtable of TC match-action rules 211 + * @lhs_rule_ht: Hashtable of TC left-hand (act ct & goto chain) rules 225 212 * @ct_zone_ht: Hashtable of TC conntrack flowtable bindings 226 213 * @ct_ht: Hashtable of TC conntrack flow entries 227 214 * @neigh_ht: Hashtable of neighbour watches (&struct efx_neigh_binder) ··· 259 244 struct rhashtable encap_ht; 260 245 struct rhashtable encap_match_ht; 261 246 struct rhashtable match_action_ht; 247 + struct rhashtable lhs_rule_ht; 262 248 struct rhashtable ct_zone_ht; 263 249 struct rhashtable ct_ht; 264 250 struct rhashtable neigh_ht;