1c3bb5c6aSEdward Cree // SPDX-License-Identifier: GPL-2.0-only
2c3bb5c6aSEdward Cree /****************************************************************************
3c3bb5c6aSEdward Cree * Driver for Solarflare network controllers and boards
4c3bb5c6aSEdward Cree * Copyright 2023, Advanced Micro Devices, Inc.
5c3bb5c6aSEdward Cree *
6c3bb5c6aSEdward Cree * This program is free software; you can redistribute it and/or modify it
7c3bb5c6aSEdward Cree * under the terms of the GNU General Public License version 2 as published
8c3bb5c6aSEdward Cree * by the Free Software Foundation, incorporated herein by reference.
9c3bb5c6aSEdward Cree */
10c3bb5c6aSEdward Cree
11c3bb5c6aSEdward Cree #include "tc_conntrack.h"
12c3bb5c6aSEdward Cree #include "tc.h"
13c3bb5c6aSEdward Cree #include "mae.h"
14c3bb5c6aSEdward Cree
15c3bb5c6aSEdward Cree static int efx_tc_flow_block(enum tc_setup_type type, void *type_data,
16c3bb5c6aSEdward Cree void *cb_priv);
17c3bb5c6aSEdward Cree
18c3bb5c6aSEdward Cree static const struct rhashtable_params efx_tc_ct_zone_ht_params = {
19c3bb5c6aSEdward Cree .key_len = offsetof(struct efx_tc_ct_zone, linkage),
20c3bb5c6aSEdward Cree .key_offset = 0,
21c3bb5c6aSEdward Cree .head_offset = offsetof(struct efx_tc_ct_zone, linkage),
22c3bb5c6aSEdward Cree };
23c3bb5c6aSEdward Cree
241909387fSEdward Cree static const struct rhashtable_params efx_tc_ct_ht_params = {
251909387fSEdward Cree .key_len = offsetof(struct efx_tc_ct_entry, linkage),
261909387fSEdward Cree .key_offset = 0,
271909387fSEdward Cree .head_offset = offsetof(struct efx_tc_ct_entry, linkage),
281909387fSEdward Cree };
291909387fSEdward Cree
efx_tc_ct_zone_free(void * ptr,void * arg)30c3bb5c6aSEdward Cree static void efx_tc_ct_zone_free(void *ptr, void *arg)
31c3bb5c6aSEdward Cree {
32c3bb5c6aSEdward Cree struct efx_tc_ct_zone *zone = ptr;
33c3bb5c6aSEdward Cree struct efx_nic *efx = zone->efx;
34c3bb5c6aSEdward Cree
35c3bb5c6aSEdward Cree netif_err(efx, drv, efx->net_dev,
36c3bb5c6aSEdward Cree "tc ct_zone %u still present at teardown, removing\n",
37c3bb5c6aSEdward Cree zone->zone);
38c3bb5c6aSEdward Cree
39c3bb5c6aSEdward Cree nf_flow_table_offload_del_cb(zone->nf_ft, efx_tc_flow_block, zone);
40c3bb5c6aSEdward Cree kfree(zone);
41c3bb5c6aSEdward Cree }
42c3bb5c6aSEdward Cree
efx_tc_ct_free(void * ptr,void * arg)431909387fSEdward Cree static void efx_tc_ct_free(void *ptr, void *arg)
441909387fSEdward Cree {
451909387fSEdward Cree struct efx_tc_ct_entry *conn = ptr;
461909387fSEdward Cree struct efx_nic *efx = arg;
471909387fSEdward Cree
481909387fSEdward Cree netif_err(efx, drv, efx->net_dev,
491909387fSEdward Cree "tc ct_entry %lx still present at teardown\n",
501909387fSEdward Cree conn->cookie);
511909387fSEdward Cree
521909387fSEdward Cree /* We can release the counter, but we can't remove the CT itself
531909387fSEdward Cree * from hardware because the table meta is already gone.
541909387fSEdward Cree */
551909387fSEdward Cree efx_tc_flower_release_counter(efx, conn->cnt);
561909387fSEdward Cree kfree(conn);
571909387fSEdward Cree }
581909387fSEdward Cree
efx_tc_init_conntrack(struct efx_nic * efx)59c3bb5c6aSEdward Cree int efx_tc_init_conntrack(struct efx_nic *efx)
60c3bb5c6aSEdward Cree {
61c3bb5c6aSEdward Cree int rc;
62c3bb5c6aSEdward Cree
63c3bb5c6aSEdward Cree rc = rhashtable_init(&efx->tc->ct_zone_ht, &efx_tc_ct_zone_ht_params);
64c3bb5c6aSEdward Cree if (rc < 0)
651909387fSEdward Cree goto fail_ct_zone_ht;
661909387fSEdward Cree rc = rhashtable_init(&efx->tc->ct_ht, &efx_tc_ct_ht_params);
671909387fSEdward Cree if (rc < 0)
681909387fSEdward Cree goto fail_ct_ht;
69c3bb5c6aSEdward Cree return 0;
701909387fSEdward Cree fail_ct_ht:
711909387fSEdward Cree rhashtable_destroy(&efx->tc->ct_zone_ht);
721909387fSEdward Cree fail_ct_zone_ht:
731909387fSEdward Cree return rc;
74c3bb5c6aSEdward Cree }
75c3bb5c6aSEdward Cree
7629416025SEdward Cree /* Only call this in init failure teardown.
7729416025SEdward Cree * Normal exit should fini instead as there may be entries in the table.
7829416025SEdward Cree */
efx_tc_destroy_conntrack(struct efx_nic * efx)7929416025SEdward Cree void efx_tc_destroy_conntrack(struct efx_nic *efx)
8029416025SEdward Cree {
8129416025SEdward Cree rhashtable_destroy(&efx->tc->ct_ht);
8229416025SEdward Cree rhashtable_destroy(&efx->tc->ct_zone_ht);
8329416025SEdward Cree }
8429416025SEdward Cree
efx_tc_fini_conntrack(struct efx_nic * efx)85c3bb5c6aSEdward Cree void efx_tc_fini_conntrack(struct efx_nic *efx)
86c3bb5c6aSEdward Cree {
87c3bb5c6aSEdward Cree rhashtable_free_and_destroy(&efx->tc->ct_zone_ht, efx_tc_ct_zone_free, NULL);
881909387fSEdward Cree rhashtable_free_and_destroy(&efx->tc->ct_ht, efx_tc_ct_free, efx);
891909387fSEdward Cree }
901909387fSEdward Cree
911909387fSEdward Cree #define EFX_NF_TCP_FLAG(flg) cpu_to_be16(be32_to_cpu(TCP_FLAG_##flg) >> 16)
921909387fSEdward Cree
efx_tc_ct_parse_match(struct efx_nic * efx,struct flow_rule * fr,struct efx_tc_ct_entry * conn)931909387fSEdward Cree static int efx_tc_ct_parse_match(struct efx_nic *efx, struct flow_rule *fr,
941909387fSEdward Cree struct efx_tc_ct_entry *conn)
951909387fSEdward Cree {
961909387fSEdward Cree struct flow_dissector *dissector = fr->match.dissector;
971909387fSEdward Cree unsigned char ipv = 0;
981909387fSEdward Cree bool tcp = false;
991909387fSEdward Cree
1001909387fSEdward Cree if (flow_rule_match_key(fr, FLOW_DISSECTOR_KEY_CONTROL)) {
1011909387fSEdward Cree struct flow_match_control fm;
1021909387fSEdward Cree
1031909387fSEdward Cree flow_rule_match_control(fr, &fm);
1041909387fSEdward Cree if (IS_ALL_ONES(fm.mask->addr_type))
1051909387fSEdward Cree switch (fm.key->addr_type) {
1061909387fSEdward Cree case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
1071909387fSEdward Cree ipv = 4;
1081909387fSEdward Cree break;
1091909387fSEdward Cree case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
1101909387fSEdward Cree ipv = 6;
1111909387fSEdward Cree break;
1121909387fSEdward Cree default:
1131909387fSEdward Cree break;
1141909387fSEdward Cree }
1151909387fSEdward Cree }
1161909387fSEdward Cree
1171909387fSEdward Cree if (!ipv) {
1181909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1191909387fSEdward Cree "Conntrack missing ipv specification\n");
1201909387fSEdward Cree return -EOPNOTSUPP;
1211909387fSEdward Cree }
1221909387fSEdward Cree
1231909387fSEdward Cree if (dissector->used_keys &
1241909387fSEdward Cree ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) |
1251909387fSEdward Cree BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) |
1261909387fSEdward Cree BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
1271909387fSEdward Cree BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
1281909387fSEdward Cree BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) |
1291909387fSEdward Cree BIT_ULL(FLOW_DISSECTOR_KEY_TCP) |
1301909387fSEdward Cree BIT_ULL(FLOW_DISSECTOR_KEY_META))) {
1311909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1321909387fSEdward Cree "Unsupported conntrack keys %#llx\n",
1331909387fSEdward Cree dissector->used_keys);
1341909387fSEdward Cree return -EOPNOTSUPP;
1351909387fSEdward Cree }
1361909387fSEdward Cree
1371909387fSEdward Cree if (flow_rule_match_key(fr, FLOW_DISSECTOR_KEY_BASIC)) {
1381909387fSEdward Cree struct flow_match_basic fm;
1391909387fSEdward Cree
1401909387fSEdward Cree flow_rule_match_basic(fr, &fm);
1411909387fSEdward Cree if (!IS_ALL_ONES(fm.mask->n_proto)) {
1421909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1431909387fSEdward Cree "Conntrack eth_proto is not exact-match; mask %04x\n",
1441909387fSEdward Cree ntohs(fm.mask->n_proto));
1451909387fSEdward Cree return -EOPNOTSUPP;
1461909387fSEdward Cree }
1471909387fSEdward Cree conn->eth_proto = fm.key->n_proto;
1481909387fSEdward Cree if (conn->eth_proto != (ipv == 4 ? htons(ETH_P_IP)
1491909387fSEdward Cree : htons(ETH_P_IPV6))) {
1501909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1511909387fSEdward Cree "Conntrack eth_proto is not IPv%u, is %04x\n",
1521909387fSEdward Cree ipv, ntohs(conn->eth_proto));
1531909387fSEdward Cree return -EOPNOTSUPP;
1541909387fSEdward Cree }
1551909387fSEdward Cree if (!IS_ALL_ONES(fm.mask->ip_proto)) {
1561909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1571909387fSEdward Cree "Conntrack ip_proto is not exact-match; mask %02x\n",
1581909387fSEdward Cree fm.mask->ip_proto);
1591909387fSEdward Cree return -EOPNOTSUPP;
1601909387fSEdward Cree }
1611909387fSEdward Cree conn->ip_proto = fm.key->ip_proto;
1621909387fSEdward Cree switch (conn->ip_proto) {
1631909387fSEdward Cree case IPPROTO_TCP:
1641909387fSEdward Cree tcp = true;
1651909387fSEdward Cree break;
1661909387fSEdward Cree case IPPROTO_UDP:
1671909387fSEdward Cree break;
1681909387fSEdward Cree default:
1691909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1701909387fSEdward Cree "Conntrack ip_proto not TCP or UDP, is %02x\n",
1711909387fSEdward Cree conn->ip_proto);
1721909387fSEdward Cree return -EOPNOTSUPP;
1731909387fSEdward Cree }
1741909387fSEdward Cree } else {
1751909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1761909387fSEdward Cree "Conntrack missing eth_proto, ip_proto\n");
1771909387fSEdward Cree return -EOPNOTSUPP;
1781909387fSEdward Cree }
1791909387fSEdward Cree
1801909387fSEdward Cree if (ipv == 4 && flow_rule_match_key(fr, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
1811909387fSEdward Cree struct flow_match_ipv4_addrs fm;
1821909387fSEdward Cree
1831909387fSEdward Cree flow_rule_match_ipv4_addrs(fr, &fm);
1841909387fSEdward Cree if (!IS_ALL_ONES(fm.mask->src)) {
1851909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1861909387fSEdward Cree "Conntrack ipv4.src is not exact-match; mask %08x\n",
1871909387fSEdward Cree ntohl(fm.mask->src));
1881909387fSEdward Cree return -EOPNOTSUPP;
1891909387fSEdward Cree }
1901909387fSEdward Cree conn->src_ip = fm.key->src;
1911909387fSEdward Cree if (!IS_ALL_ONES(fm.mask->dst)) {
1921909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
1931909387fSEdward Cree "Conntrack ipv4.dst is not exact-match; mask %08x\n",
1941909387fSEdward Cree ntohl(fm.mask->dst));
1951909387fSEdward Cree return -EOPNOTSUPP;
1961909387fSEdward Cree }
1971909387fSEdward Cree conn->dst_ip = fm.key->dst;
1981909387fSEdward Cree } else if (ipv == 6 && flow_rule_match_key(fr, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
1991909387fSEdward Cree struct flow_match_ipv6_addrs fm;
2001909387fSEdward Cree
2011909387fSEdward Cree flow_rule_match_ipv6_addrs(fr, &fm);
2021909387fSEdward Cree if (!efx_ipv6_addr_all_ones(&fm.mask->src)) {
2031909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
2041909387fSEdward Cree "Conntrack ipv6.src is not exact-match; mask %pI6\n",
2051909387fSEdward Cree &fm.mask->src);
2061909387fSEdward Cree return -EOPNOTSUPP;
2071909387fSEdward Cree }
2081909387fSEdward Cree conn->src_ip6 = fm.key->src;
2091909387fSEdward Cree if (!efx_ipv6_addr_all_ones(&fm.mask->dst)) {
2101909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
2111909387fSEdward Cree "Conntrack ipv6.dst is not exact-match; mask %pI6\n",
2121909387fSEdward Cree &fm.mask->dst);
2131909387fSEdward Cree return -EOPNOTSUPP;
2141909387fSEdward Cree }
2151909387fSEdward Cree conn->dst_ip6 = fm.key->dst;
2161909387fSEdward Cree } else {
2171909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
2181909387fSEdward Cree "Conntrack missing IPv%u addrs\n", ipv);
2191909387fSEdward Cree return -EOPNOTSUPP;
2201909387fSEdward Cree }
2211909387fSEdward Cree
2221909387fSEdward Cree if (flow_rule_match_key(fr, FLOW_DISSECTOR_KEY_PORTS)) {
2231909387fSEdward Cree struct flow_match_ports fm;
2241909387fSEdward Cree
2251909387fSEdward Cree flow_rule_match_ports(fr, &fm);
2261909387fSEdward Cree if (!IS_ALL_ONES(fm.mask->src)) {
2271909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
2281909387fSEdward Cree "Conntrack ports.src is not exact-match; mask %04x\n",
2291909387fSEdward Cree ntohs(fm.mask->src));
2301909387fSEdward Cree return -EOPNOTSUPP;
2311909387fSEdward Cree }
2321909387fSEdward Cree conn->l4_sport = fm.key->src;
2331909387fSEdward Cree if (!IS_ALL_ONES(fm.mask->dst)) {
2341909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
2351909387fSEdward Cree "Conntrack ports.dst is not exact-match; mask %04x\n",
2361909387fSEdward Cree ntohs(fm.mask->dst));
2371909387fSEdward Cree return -EOPNOTSUPP;
2381909387fSEdward Cree }
2391909387fSEdward Cree conn->l4_dport = fm.key->dst;
2401909387fSEdward Cree } else {
2411909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev, "Conntrack missing L4 ports\n");
2421909387fSEdward Cree return -EOPNOTSUPP;
2431909387fSEdward Cree }
2441909387fSEdward Cree
2451909387fSEdward Cree if (flow_rule_match_key(fr, FLOW_DISSECTOR_KEY_TCP)) {
2461909387fSEdward Cree __be16 tcp_interesting_flags;
2471909387fSEdward Cree struct flow_match_tcp fm;
2481909387fSEdward Cree
2491909387fSEdward Cree if (!tcp) {
2501909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
2511909387fSEdward Cree "Conntrack matching on TCP keys but ipproto is not tcp\n");
2521909387fSEdward Cree return -EOPNOTSUPP;
2531909387fSEdward Cree }
2541909387fSEdward Cree flow_rule_match_tcp(fr, &fm);
2551909387fSEdward Cree tcp_interesting_flags = EFX_NF_TCP_FLAG(SYN) |
2561909387fSEdward Cree EFX_NF_TCP_FLAG(RST) |
2571909387fSEdward Cree EFX_NF_TCP_FLAG(FIN);
2581909387fSEdward Cree /* If any of the tcp_interesting_flags is set, we always
2591909387fSEdward Cree * inhibit CT lookup in LHS (so SW can update CT table).
2601909387fSEdward Cree */
2611909387fSEdward Cree if (fm.key->flags & tcp_interesting_flags) {
2621909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
2631909387fSEdward Cree "Unsupported conntrack tcp.flags %04x/%04x\n",
2641909387fSEdward Cree ntohs(fm.key->flags), ntohs(fm.mask->flags));
2651909387fSEdward Cree return -EOPNOTSUPP;
2661909387fSEdward Cree }
2671909387fSEdward Cree /* Other TCP flags cannot be filtered at CT */
2681909387fSEdward Cree if (fm.mask->flags & ~tcp_interesting_flags) {
2691909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
2701909387fSEdward Cree "Unsupported conntrack tcp.flags %04x/%04x\n",
2711909387fSEdward Cree ntohs(fm.key->flags), ntohs(fm.mask->flags));
2721909387fSEdward Cree return -EOPNOTSUPP;
2731909387fSEdward Cree }
2741909387fSEdward Cree }
2751909387fSEdward Cree
2761909387fSEdward Cree return 0;
2771909387fSEdward Cree }
2781909387fSEdward Cree
efx_tc_ct_replace(struct efx_tc_ct_zone * ct_zone,struct flow_cls_offload * tc)2791909387fSEdward Cree static int efx_tc_ct_replace(struct efx_tc_ct_zone *ct_zone,
2801909387fSEdward Cree struct flow_cls_offload *tc)
2811909387fSEdward Cree {
2821909387fSEdward Cree struct flow_rule *fr = flow_cls_offload_flow_rule(tc);
2831909387fSEdward Cree struct efx_tc_ct_entry *conn, *old;
2841909387fSEdward Cree struct efx_nic *efx = ct_zone->efx;
2851909387fSEdward Cree const struct flow_action_entry *fa;
2861909387fSEdward Cree struct efx_tc_counter *cnt;
2871909387fSEdward Cree int rc, i;
2881909387fSEdward Cree
2891909387fSEdward Cree if (WARN_ON(!efx->tc))
2901909387fSEdward Cree return -ENETDOWN;
2911909387fSEdward Cree if (WARN_ON(!efx->tc->up))
2921909387fSEdward Cree return -ENETDOWN;
2931909387fSEdward Cree
2941909387fSEdward Cree conn = kzalloc(sizeof(*conn), GFP_USER);
2951909387fSEdward Cree if (!conn)
2961909387fSEdward Cree return -ENOMEM;
2971909387fSEdward Cree conn->cookie = tc->cookie;
2981909387fSEdward Cree old = rhashtable_lookup_get_insert_fast(&efx->tc->ct_ht,
2991909387fSEdward Cree &conn->linkage,
3001909387fSEdward Cree efx_tc_ct_ht_params);
301*fc21f083SEdward Cree if (IS_ERR(old)) {
302*fc21f083SEdward Cree rc = PTR_ERR(old);
303*fc21f083SEdward Cree goto release;
304*fc21f083SEdward Cree } else if (old) {
3051909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
3061909387fSEdward Cree "Already offloaded conntrack (cookie %lx)\n", tc->cookie);
3071909387fSEdward Cree rc = -EEXIST;
3081909387fSEdward Cree goto release;
3091909387fSEdward Cree }
3101909387fSEdward Cree
3111909387fSEdward Cree /* Parse match */
3121909387fSEdward Cree conn->zone = ct_zone;
3131909387fSEdward Cree rc = efx_tc_ct_parse_match(efx, fr, conn);
3141909387fSEdward Cree if (rc)
3151909387fSEdward Cree goto release;
3161909387fSEdward Cree
3171909387fSEdward Cree /* Parse actions */
3181909387fSEdward Cree flow_action_for_each(i, fa, &fr->action) {
3191909387fSEdward Cree switch (fa->id) {
3201909387fSEdward Cree case FLOW_ACTION_CT_METADATA:
3211909387fSEdward Cree conn->mark = fa->ct_metadata.mark;
3221909387fSEdward Cree if (memchr_inv(fa->ct_metadata.labels, 0, sizeof(fa->ct_metadata.labels))) {
3231909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
3241909387fSEdward Cree "Setting CT label not supported\n");
3251909387fSEdward Cree rc = -EOPNOTSUPP;
3261909387fSEdward Cree goto release;
3271909387fSEdward Cree }
3281909387fSEdward Cree break;
3291909387fSEdward Cree default:
3301909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
3311909387fSEdward Cree "Unhandled action %u for conntrack\n", fa->id);
3321909387fSEdward Cree rc = -EOPNOTSUPP;
3331909387fSEdward Cree goto release;
3341909387fSEdward Cree }
3351909387fSEdward Cree }
3361909387fSEdward Cree
3371909387fSEdward Cree /* fill in defaults for unmangled values */
3381909387fSEdward Cree conn->nat_ip = conn->dnat ? conn->dst_ip : conn->src_ip;
3391909387fSEdward Cree conn->l4_natport = conn->dnat ? conn->l4_dport : conn->l4_sport;
3401909387fSEdward Cree
3411909387fSEdward Cree cnt = efx_tc_flower_allocate_counter(efx, EFX_TC_COUNTER_TYPE_CT);
3421909387fSEdward Cree if (IS_ERR(cnt)) {
3431909387fSEdward Cree rc = PTR_ERR(cnt);
3441909387fSEdward Cree goto release;
3451909387fSEdward Cree }
3461909387fSEdward Cree conn->cnt = cnt;
3471909387fSEdward Cree
3481909387fSEdward Cree rc = efx_mae_insert_ct(efx, conn);
3491909387fSEdward Cree if (rc) {
3501909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev,
3511909387fSEdward Cree "Failed to insert conntrack, %d\n", rc);
3521909387fSEdward Cree goto release;
3531909387fSEdward Cree }
3541909387fSEdward Cree mutex_lock(&ct_zone->mutex);
3551909387fSEdward Cree list_add_tail(&conn->list, &ct_zone->cts);
3561909387fSEdward Cree mutex_unlock(&ct_zone->mutex);
3571909387fSEdward Cree return 0;
3581909387fSEdward Cree release:
3591909387fSEdward Cree if (conn->cnt)
3601909387fSEdward Cree efx_tc_flower_release_counter(efx, conn->cnt);
3611909387fSEdward Cree if (!old)
3621909387fSEdward Cree rhashtable_remove_fast(&efx->tc->ct_ht, &conn->linkage,
3631909387fSEdward Cree efx_tc_ct_ht_params);
3641909387fSEdward Cree kfree(conn);
3651909387fSEdward Cree return rc;
3661909387fSEdward Cree }
3671909387fSEdward Cree
3681909387fSEdward Cree /* Caller must follow with efx_tc_ct_remove_finish() after RCU grace period! */
efx_tc_ct_remove(struct efx_nic * efx,struct efx_tc_ct_entry * conn)3691909387fSEdward Cree static void efx_tc_ct_remove(struct efx_nic *efx, struct efx_tc_ct_entry *conn)
3701909387fSEdward Cree {
3711909387fSEdward Cree int rc;
3721909387fSEdward Cree
3731909387fSEdward Cree /* Remove it from HW */
3741909387fSEdward Cree rc = efx_mae_remove_ct(efx, conn);
3751909387fSEdward Cree /* Delete it from SW */
3761909387fSEdward Cree rhashtable_remove_fast(&efx->tc->ct_ht, &conn->linkage,
3771909387fSEdward Cree efx_tc_ct_ht_params);
3781909387fSEdward Cree if (rc) {
3791909387fSEdward Cree netif_err(efx, drv, efx->net_dev,
3801909387fSEdward Cree "Failed to remove conntrack %lx from hw, rc %d\n",
3811909387fSEdward Cree conn->cookie, rc);
3821909387fSEdward Cree } else {
3831909387fSEdward Cree netif_dbg(efx, drv, efx->net_dev, "Removed conntrack %lx\n",
3841909387fSEdward Cree conn->cookie);
3851909387fSEdward Cree }
3861909387fSEdward Cree }
3871909387fSEdward Cree
efx_tc_ct_remove_finish(struct efx_nic * efx,struct efx_tc_ct_entry * conn)3881909387fSEdward Cree static void efx_tc_ct_remove_finish(struct efx_nic *efx, struct efx_tc_ct_entry *conn)
3891909387fSEdward Cree {
3901909387fSEdward Cree /* Remove related CT counter. This is delayed after the conn object we
3911909387fSEdward Cree * are working with has been successfully removed. This protects the
3921909387fSEdward Cree * counter from being used-after-free inside efx_tc_ct_stats.
3931909387fSEdward Cree */
3941909387fSEdward Cree efx_tc_flower_release_counter(efx, conn->cnt);
3951909387fSEdward Cree kfree(conn);
3961909387fSEdward Cree }
3971909387fSEdward Cree
efx_tc_ct_destroy(struct efx_tc_ct_zone * ct_zone,struct flow_cls_offload * tc)3981909387fSEdward Cree static int efx_tc_ct_destroy(struct efx_tc_ct_zone *ct_zone,
3991909387fSEdward Cree struct flow_cls_offload *tc)
4001909387fSEdward Cree {
4011909387fSEdward Cree struct efx_nic *efx = ct_zone->efx;
4021909387fSEdward Cree struct efx_tc_ct_entry *conn;
4031909387fSEdward Cree
4041909387fSEdward Cree conn = rhashtable_lookup_fast(&efx->tc->ct_ht, &tc->cookie,
4051909387fSEdward Cree efx_tc_ct_ht_params);
4061909387fSEdward Cree if (!conn) {
4071909387fSEdward Cree netif_warn(efx, drv, efx->net_dev,
4081909387fSEdward Cree "Conntrack %lx not found to remove\n", tc->cookie);
4091909387fSEdward Cree return -ENOENT;
4101909387fSEdward Cree }
4111909387fSEdward Cree
4121909387fSEdward Cree mutex_lock(&ct_zone->mutex);
4131909387fSEdward Cree list_del(&conn->list);
4141909387fSEdward Cree efx_tc_ct_remove(efx, conn);
4151909387fSEdward Cree mutex_unlock(&ct_zone->mutex);
4161909387fSEdward Cree synchronize_rcu();
4171909387fSEdward Cree efx_tc_ct_remove_finish(efx, conn);
4181909387fSEdward Cree return 0;
4191909387fSEdward Cree }
4201909387fSEdward Cree
efx_tc_ct_stats(struct efx_tc_ct_zone * ct_zone,struct flow_cls_offload * tc)4211909387fSEdward Cree static int efx_tc_ct_stats(struct efx_tc_ct_zone *ct_zone,
4221909387fSEdward Cree struct flow_cls_offload *tc)
4231909387fSEdward Cree {
4241909387fSEdward Cree struct efx_nic *efx = ct_zone->efx;
4251909387fSEdward Cree struct efx_tc_ct_entry *conn;
4261909387fSEdward Cree struct efx_tc_counter *cnt;
4271909387fSEdward Cree
4281909387fSEdward Cree rcu_read_lock();
4291909387fSEdward Cree conn = rhashtable_lookup_fast(&efx->tc->ct_ht, &tc->cookie,
4301909387fSEdward Cree efx_tc_ct_ht_params);
4311909387fSEdward Cree if (!conn) {
4321909387fSEdward Cree netif_warn(efx, drv, efx->net_dev,
4331909387fSEdward Cree "Conntrack %lx not found for stats\n", tc->cookie);
4341909387fSEdward Cree rcu_read_unlock();
4351909387fSEdward Cree return -ENOENT;
4361909387fSEdward Cree }
4371909387fSEdward Cree
4381909387fSEdward Cree cnt = conn->cnt;
4391909387fSEdward Cree spin_lock_bh(&cnt->lock);
4401909387fSEdward Cree /* Report only last use */
4411909387fSEdward Cree flow_stats_update(&tc->stats, 0, 0, 0, cnt->touched,
4421909387fSEdward Cree FLOW_ACTION_HW_STATS_DELAYED);
4431909387fSEdward Cree spin_unlock_bh(&cnt->lock);
4441909387fSEdward Cree rcu_read_unlock();
4451909387fSEdward Cree
4461909387fSEdward Cree return 0;
447c3bb5c6aSEdward Cree }
448c3bb5c6aSEdward Cree
efx_tc_flow_block(enum tc_setup_type type,void * type_data,void * cb_priv)449c3bb5c6aSEdward Cree static int efx_tc_flow_block(enum tc_setup_type type, void *type_data,
450c3bb5c6aSEdward Cree void *cb_priv)
451c3bb5c6aSEdward Cree {
4521909387fSEdward Cree struct flow_cls_offload *tcb = type_data;
4531909387fSEdward Cree struct efx_tc_ct_zone *ct_zone = cb_priv;
4541909387fSEdward Cree
4551909387fSEdward Cree if (type != TC_SETUP_CLSFLOWER)
4561909387fSEdward Cree return -EOPNOTSUPP;
4571909387fSEdward Cree
4581909387fSEdward Cree switch (tcb->command) {
4591909387fSEdward Cree case FLOW_CLS_REPLACE:
4601909387fSEdward Cree return efx_tc_ct_replace(ct_zone, tcb);
4611909387fSEdward Cree case FLOW_CLS_DESTROY:
4621909387fSEdward Cree return efx_tc_ct_destroy(ct_zone, tcb);
4631909387fSEdward Cree case FLOW_CLS_STATS:
4641909387fSEdward Cree return efx_tc_ct_stats(ct_zone, tcb);
4651909387fSEdward Cree default:
4661909387fSEdward Cree break;
4675cce7814SYang Li }
4681909387fSEdward Cree
469c3bb5c6aSEdward Cree return -EOPNOTSUPP;
470c3bb5c6aSEdward Cree }
471c3bb5c6aSEdward Cree
efx_tc_ct_register_zone(struct efx_nic * efx,u16 zone,struct nf_flowtable * ct_ft)472c3bb5c6aSEdward Cree struct efx_tc_ct_zone *efx_tc_ct_register_zone(struct efx_nic *efx, u16 zone,
473c3bb5c6aSEdward Cree struct nf_flowtable *ct_ft)
474c3bb5c6aSEdward Cree {
475c3bb5c6aSEdward Cree struct efx_tc_ct_zone *ct_zone, *old;
476c3bb5c6aSEdward Cree int rc;
477c3bb5c6aSEdward Cree
478c3bb5c6aSEdward Cree ct_zone = kzalloc(sizeof(*ct_zone), GFP_USER);
479c3bb5c6aSEdward Cree if (!ct_zone)
480c3bb5c6aSEdward Cree return ERR_PTR(-ENOMEM);
481c3bb5c6aSEdward Cree ct_zone->zone = zone;
482c3bb5c6aSEdward Cree old = rhashtable_lookup_get_insert_fast(&efx->tc->ct_zone_ht,
483c3bb5c6aSEdward Cree &ct_zone->linkage,
484c3bb5c6aSEdward Cree efx_tc_ct_zone_ht_params);
485c3bb5c6aSEdward Cree if (old) {
486c3bb5c6aSEdward Cree /* don't need our new entry */
487c3bb5c6aSEdward Cree kfree(ct_zone);
488*fc21f083SEdward Cree if (IS_ERR(old)) /* oh dear, it's actually an error */
489*fc21f083SEdward Cree return ERR_CAST(old);
490c3bb5c6aSEdward Cree if (!refcount_inc_not_zero(&old->ref))
491c3bb5c6aSEdward Cree return ERR_PTR(-EAGAIN);
492c3bb5c6aSEdward Cree /* existing entry found */
493c3bb5c6aSEdward Cree WARN_ON_ONCE(old->nf_ft != ct_ft);
494c3bb5c6aSEdward Cree netif_dbg(efx, drv, efx->net_dev,
495c3bb5c6aSEdward Cree "Found existing ct_zone for %u\n", zone);
496c3bb5c6aSEdward Cree return old;
497c3bb5c6aSEdward Cree }
498c3bb5c6aSEdward Cree ct_zone->nf_ft = ct_ft;
499c3bb5c6aSEdward Cree ct_zone->efx = efx;
5001909387fSEdward Cree INIT_LIST_HEAD(&ct_zone->cts);
5011909387fSEdward Cree mutex_init(&ct_zone->mutex);
502c3bb5c6aSEdward Cree rc = nf_flow_table_offload_add_cb(ct_ft, efx_tc_flow_block, ct_zone);
503c3bb5c6aSEdward Cree netif_dbg(efx, drv, efx->net_dev, "Adding new ct_zone for %u, rc %d\n",
504c3bb5c6aSEdward Cree zone, rc);
505c3bb5c6aSEdward Cree if (rc < 0)
506c3bb5c6aSEdward Cree goto fail;
507c3bb5c6aSEdward Cree refcount_set(&ct_zone->ref, 1);
508c3bb5c6aSEdward Cree return ct_zone;
509c3bb5c6aSEdward Cree fail:
510c3bb5c6aSEdward Cree rhashtable_remove_fast(&efx->tc->ct_zone_ht, &ct_zone->linkage,
511c3bb5c6aSEdward Cree efx_tc_ct_zone_ht_params);
512c3bb5c6aSEdward Cree kfree(ct_zone);
513c3bb5c6aSEdward Cree return ERR_PTR(rc);
514c3bb5c6aSEdward Cree }
515c3bb5c6aSEdward Cree
efx_tc_ct_unregister_zone(struct efx_nic * efx,struct efx_tc_ct_zone * ct_zone)516c3bb5c6aSEdward Cree void efx_tc_ct_unregister_zone(struct efx_nic *efx,
517c3bb5c6aSEdward Cree struct efx_tc_ct_zone *ct_zone)
518c3bb5c6aSEdward Cree {
5191909387fSEdward Cree struct efx_tc_ct_entry *conn, *next;
5201909387fSEdward Cree
521c3bb5c6aSEdward Cree if (!refcount_dec_and_test(&ct_zone->ref))
522c3bb5c6aSEdward Cree return; /* still in use */
523c3bb5c6aSEdward Cree nf_flow_table_offload_del_cb(ct_zone->nf_ft, efx_tc_flow_block, ct_zone);
524c3bb5c6aSEdward Cree rhashtable_remove_fast(&efx->tc->ct_zone_ht, &ct_zone->linkage,
525c3bb5c6aSEdward Cree efx_tc_ct_zone_ht_params);
5261909387fSEdward Cree mutex_lock(&ct_zone->mutex);
5271909387fSEdward Cree list_for_each_entry(conn, &ct_zone->cts, list)
5281909387fSEdward Cree efx_tc_ct_remove(efx, conn);
5291909387fSEdward Cree synchronize_rcu();
5301909387fSEdward Cree /* need to use _safe because efx_tc_ct_remove_finish() frees conn */
5311909387fSEdward Cree list_for_each_entry_safe(conn, next, &ct_zone->cts, list)
5321909387fSEdward Cree efx_tc_ct_remove_finish(efx, conn);
5331909387fSEdward Cree mutex_unlock(&ct_zone->mutex);
5341909387fSEdward Cree mutex_destroy(&ct_zone->mutex);
535c3bb5c6aSEdward Cree netif_dbg(efx, drv, efx->net_dev, "Removed ct_zone for %u\n",
536c3bb5c6aSEdward Cree ct_zone->zone);
537c3bb5c6aSEdward Cree kfree(ct_zone);
538c3bb5c6aSEdward Cree }
539