12ae7408fSSathya Perla /* Broadcom NetXtreme-C/E network driver. 22ae7408fSSathya Perla * 32ae7408fSSathya Perla * Copyright (c) 2017 Broadcom Limited 42ae7408fSSathya Perla * 52ae7408fSSathya Perla * This program is free software; you can redistribute it and/or modify 62ae7408fSSathya Perla * it under the terms of the GNU General Public License as published by 72ae7408fSSathya Perla * the Free Software Foundation. 82ae7408fSSathya Perla */ 92ae7408fSSathya Perla 102ae7408fSSathya Perla #include <linux/netdevice.h> 112ae7408fSSathya Perla #include <linux/inetdevice.h> 122ae7408fSSathya Perla #include <linux/if_vlan.h> 132ae7408fSSathya Perla #include <net/flow_dissector.h> 142ae7408fSSathya Perla #include <net/pkt_cls.h> 152ae7408fSSathya Perla #include <net/tc_act/tc_gact.h> 162ae7408fSSathya Perla #include <net/tc_act/tc_skbedit.h> 172ae7408fSSathya Perla #include <net/tc_act/tc_mirred.h> 182ae7408fSSathya Perla #include <net/tc_act/tc_vlan.h> 198c95f773SSathya Perla #include <net/tc_act/tc_tunnel_key.h> 202ae7408fSSathya Perla 212ae7408fSSathya Perla #include "bnxt_hsi.h" 222ae7408fSSathya Perla #include "bnxt.h" 232ae7408fSSathya Perla #include "bnxt_sriov.h" 242ae7408fSSathya Perla #include "bnxt_tc.h" 252ae7408fSSathya Perla #include "bnxt_vfr.h" 262ae7408fSSathya Perla 272ae7408fSSathya Perla #define BNXT_FID_INVALID 0xffff 282ae7408fSSathya Perla #define VLAN_TCI(vid, prio) ((vid) | ((prio) << VLAN_PRIO_SHIFT)) 292ae7408fSSathya Perla 302ae7408fSSathya Perla /* Return the dst fid of the func for flow forwarding 312ae7408fSSathya Perla * For PFs: src_fid is the fid of the PF 322ae7408fSSathya Perla * For VF-reps: src_fid the fid of the VF 332ae7408fSSathya Perla */ 342ae7408fSSathya Perla static u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev) 352ae7408fSSathya Perla { 362ae7408fSSathya Perla struct bnxt *bp; 372ae7408fSSathya Perla 382ae7408fSSathya Perla /* check if dev belongs to the same switch */ 392ae7408fSSathya Perla if (!switchdev_port_same_parent_id(pf_bp->dev, dev)) { 402ae7408fSSathya Perla netdev_info(pf_bp->dev, "dev(ifindex=%d) not on same switch", 412ae7408fSSathya Perla dev->ifindex); 422ae7408fSSathya Perla return BNXT_FID_INVALID; 432ae7408fSSathya Perla } 442ae7408fSSathya Perla 452ae7408fSSathya Perla /* Is dev a VF-rep? */ 46dd4ea1daSSathya Perla if (bnxt_dev_is_vf_rep(dev)) 472ae7408fSSathya Perla return bnxt_vf_rep_get_fid(dev); 482ae7408fSSathya Perla 492ae7408fSSathya Perla bp = netdev_priv(dev); 502ae7408fSSathya Perla return bp->pf.fw_fid; 512ae7408fSSathya Perla } 522ae7408fSSathya Perla 532ae7408fSSathya Perla static int bnxt_tc_parse_redir(struct bnxt *bp, 542ae7408fSSathya Perla struct bnxt_tc_actions *actions, 552ae7408fSSathya Perla const struct tc_action *tc_act) 562ae7408fSSathya Perla { 579f8a739eSCong Wang struct net_device *dev = tcf_mirred_dev(tc_act); 582ae7408fSSathya Perla 592ae7408fSSathya Perla if (!dev) { 609f8a739eSCong Wang netdev_info(bp->dev, "no dev in mirred action"); 612ae7408fSSathya Perla return -EINVAL; 622ae7408fSSathya Perla } 632ae7408fSSathya Perla 642ae7408fSSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_FWD; 652ae7408fSSathya Perla actions->dst_dev = dev; 662ae7408fSSathya Perla return 0; 672ae7408fSSathya Perla } 682ae7408fSSathya Perla 692ae7408fSSathya Perla static void bnxt_tc_parse_vlan(struct bnxt *bp, 702ae7408fSSathya Perla struct bnxt_tc_actions *actions, 712ae7408fSSathya Perla const struct tc_action *tc_act) 722ae7408fSSathya Perla { 732ae7408fSSathya Perla if (tcf_vlan_action(tc_act) == TCA_VLAN_ACT_POP) { 742ae7408fSSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_POP_VLAN; 752ae7408fSSathya Perla } else if (tcf_vlan_action(tc_act) == TCA_VLAN_ACT_PUSH) { 762ae7408fSSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_PUSH_VLAN; 772ae7408fSSathya Perla actions->push_vlan_tci = htons(tcf_vlan_push_vid(tc_act)); 782ae7408fSSathya Perla actions->push_vlan_tpid = tcf_vlan_push_proto(tc_act); 792ae7408fSSathya Perla } 802ae7408fSSathya Perla } 812ae7408fSSathya Perla 828c95f773SSathya Perla static int bnxt_tc_parse_tunnel_set(struct bnxt *bp, 838c95f773SSathya Perla struct bnxt_tc_actions *actions, 848c95f773SSathya Perla const struct tc_action *tc_act) 858c95f773SSathya Perla { 868c95f773SSathya Perla struct ip_tunnel_info *tun_info = tcf_tunnel_info(tc_act); 878c95f773SSathya Perla struct ip_tunnel_key *tun_key = &tun_info->key; 888c95f773SSathya Perla 898c95f773SSathya Perla if (ip_tunnel_info_af(tun_info) != AF_INET) { 908c95f773SSathya Perla netdev_info(bp->dev, "only IPv4 tunnel-encap is supported"); 918c95f773SSathya Perla return -EOPNOTSUPP; 928c95f773SSathya Perla } 938c95f773SSathya Perla 948c95f773SSathya Perla actions->tun_encap_key = *tun_key; 958c95f773SSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP; 968c95f773SSathya Perla return 0; 978c95f773SSathya Perla } 988c95f773SSathya Perla 992ae7408fSSathya Perla static int bnxt_tc_parse_actions(struct bnxt *bp, 1002ae7408fSSathya Perla struct bnxt_tc_actions *actions, 1012ae7408fSSathya Perla struct tcf_exts *tc_exts) 1022ae7408fSSathya Perla { 1032ae7408fSSathya Perla const struct tc_action *tc_act; 1042ae7408fSSathya Perla LIST_HEAD(tc_actions); 1052ae7408fSSathya Perla int rc; 1062ae7408fSSathya Perla 1072ae7408fSSathya Perla if (!tcf_exts_has_actions(tc_exts)) { 1082ae7408fSSathya Perla netdev_info(bp->dev, "no actions"); 1092ae7408fSSathya Perla return -EINVAL; 1102ae7408fSSathya Perla } 1112ae7408fSSathya Perla 1122ae7408fSSathya Perla tcf_exts_to_list(tc_exts, &tc_actions); 1132ae7408fSSathya Perla list_for_each_entry(tc_act, &tc_actions, list) { 1142ae7408fSSathya Perla /* Drop action */ 1152ae7408fSSathya Perla if (is_tcf_gact_shot(tc_act)) { 1162ae7408fSSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_DROP; 1172ae7408fSSathya Perla return 0; /* don't bother with other actions */ 1182ae7408fSSathya Perla } 1192ae7408fSSathya Perla 1202ae7408fSSathya Perla /* Redirect action */ 1212ae7408fSSathya Perla if (is_tcf_mirred_egress_redirect(tc_act)) { 1222ae7408fSSathya Perla rc = bnxt_tc_parse_redir(bp, actions, tc_act); 1232ae7408fSSathya Perla if (rc) 1242ae7408fSSathya Perla return rc; 1252ae7408fSSathya Perla continue; 1262ae7408fSSathya Perla } 1272ae7408fSSathya Perla 1282ae7408fSSathya Perla /* Push/pop VLAN */ 1292ae7408fSSathya Perla if (is_tcf_vlan(tc_act)) { 1302ae7408fSSathya Perla bnxt_tc_parse_vlan(bp, actions, tc_act); 1312ae7408fSSathya Perla continue; 1322ae7408fSSathya Perla } 1338c95f773SSathya Perla 1348c95f773SSathya Perla /* Tunnel encap */ 1358c95f773SSathya Perla if (is_tcf_tunnel_set(tc_act)) { 1368c95f773SSathya Perla rc = bnxt_tc_parse_tunnel_set(bp, actions, tc_act); 1378c95f773SSathya Perla if (rc) 1388c95f773SSathya Perla return rc; 1398c95f773SSathya Perla continue; 1402ae7408fSSathya Perla } 1412ae7408fSSathya Perla 1428c95f773SSathya Perla /* Tunnel decap */ 1438c95f773SSathya Perla if (is_tcf_tunnel_release(tc_act)) { 1448c95f773SSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_DECAP; 1458c95f773SSathya Perla continue; 1468c95f773SSathya Perla } 1478c95f773SSathya Perla } 1488c95f773SSathya Perla 149e9ecc731SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { 150e9ecc731SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) { 151e9ecc731SSathya Perla /* dst_fid is PF's fid */ 152e9ecc731SSathya Perla actions->dst_fid = bp->pf.fw_fid; 153e9ecc731SSathya Perla } else { 154e9ecc731SSathya Perla /* find the FID from dst_dev */ 155e9ecc731SSathya Perla actions->dst_fid = 156e9ecc731SSathya Perla bnxt_flow_get_dst_fid(bp, actions->dst_dev); 157e9ecc731SSathya Perla if (actions->dst_fid == BNXT_FID_INVALID) 1588c95f773SSathya Perla return -EINVAL; 1598c95f773SSathya Perla } 160e9ecc731SSathya Perla } 1618c95f773SSathya Perla 16292425c40SDan Carpenter return 0; 1632ae7408fSSathya Perla } 1642ae7408fSSathya Perla 1652ae7408fSSathya Perla #define GET_KEY(flow_cmd, key_type) \ 1662ae7408fSSathya Perla skb_flow_dissector_target((flow_cmd)->dissector, key_type,\ 1672ae7408fSSathya Perla (flow_cmd)->key) 1682ae7408fSSathya Perla #define GET_MASK(flow_cmd, key_type) \ 1692ae7408fSSathya Perla skb_flow_dissector_target((flow_cmd)->dissector, key_type,\ 1702ae7408fSSathya Perla (flow_cmd)->mask) 1712ae7408fSSathya Perla 1722ae7408fSSathya Perla static int bnxt_tc_parse_flow(struct bnxt *bp, 1732ae7408fSSathya Perla struct tc_cls_flower_offload *tc_flow_cmd, 1742ae7408fSSathya Perla struct bnxt_tc_flow *flow) 1752ae7408fSSathya Perla { 1762ae7408fSSathya Perla struct flow_dissector *dissector = tc_flow_cmd->dissector; 1772ae7408fSSathya Perla u16 addr_type = 0; 1782ae7408fSSathya Perla 1792ae7408fSSathya Perla /* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */ 1802ae7408fSSathya Perla if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 || 1812ae7408fSSathya Perla (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC)) == 0) { 1822ae7408fSSathya Perla netdev_info(bp->dev, "cannot form TC key: used_keys = 0x%x", 1832ae7408fSSathya Perla dissector->used_keys); 1842ae7408fSSathya Perla return -EOPNOTSUPP; 1852ae7408fSSathya Perla } 1862ae7408fSSathya Perla 1872ae7408fSSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_CONTROL)) { 1882ae7408fSSathya Perla struct flow_dissector_key_control *key = 1892ae7408fSSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_CONTROL); 1902ae7408fSSathya Perla 1912ae7408fSSathya Perla addr_type = key->addr_type; 1922ae7408fSSathya Perla } 1932ae7408fSSathya Perla 1942ae7408fSSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC)) { 1952ae7408fSSathya Perla struct flow_dissector_key_basic *key = 1962ae7408fSSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_BASIC); 1972ae7408fSSathya Perla struct flow_dissector_key_basic *mask = 1982ae7408fSSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_BASIC); 1992ae7408fSSathya Perla 2002ae7408fSSathya Perla flow->l2_key.ether_type = key->n_proto; 2012ae7408fSSathya Perla flow->l2_mask.ether_type = mask->n_proto; 2022ae7408fSSathya Perla 2032ae7408fSSathya Perla if (key->n_proto == htons(ETH_P_IP) || 2042ae7408fSSathya Perla key->n_proto == htons(ETH_P_IPV6)) { 2052ae7408fSSathya Perla flow->l4_key.ip_proto = key->ip_proto; 2062ae7408fSSathya Perla flow->l4_mask.ip_proto = mask->ip_proto; 2072ae7408fSSathya Perla } 2082ae7408fSSathya Perla } 2092ae7408fSSathya Perla 2102ae7408fSSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 2112ae7408fSSathya Perla struct flow_dissector_key_eth_addrs *key = 2122ae7408fSSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ETH_ADDRS); 2132ae7408fSSathya Perla struct flow_dissector_key_eth_addrs *mask = 2142ae7408fSSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ETH_ADDRS); 2152ae7408fSSathya Perla 2162ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_ETH_ADDRS; 2172ae7408fSSathya Perla ether_addr_copy(flow->l2_key.dmac, key->dst); 2182ae7408fSSathya Perla ether_addr_copy(flow->l2_mask.dmac, mask->dst); 2192ae7408fSSathya Perla ether_addr_copy(flow->l2_key.smac, key->src); 2202ae7408fSSathya Perla ether_addr_copy(flow->l2_mask.smac, mask->src); 2212ae7408fSSathya Perla } 2222ae7408fSSathya Perla 2232ae7408fSSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN)) { 2242ae7408fSSathya Perla struct flow_dissector_key_vlan *key = 2252ae7408fSSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_VLAN); 2262ae7408fSSathya Perla struct flow_dissector_key_vlan *mask = 2272ae7408fSSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_VLAN); 2282ae7408fSSathya Perla 2292ae7408fSSathya Perla flow->l2_key.inner_vlan_tci = 2302ae7408fSSathya Perla cpu_to_be16(VLAN_TCI(key->vlan_id, key->vlan_priority)); 2312ae7408fSSathya Perla flow->l2_mask.inner_vlan_tci = 2322ae7408fSSathya Perla cpu_to_be16((VLAN_TCI(mask->vlan_id, mask->vlan_priority))); 2332ae7408fSSathya Perla flow->l2_key.inner_vlan_tpid = htons(ETH_P_8021Q); 2342ae7408fSSathya Perla flow->l2_mask.inner_vlan_tpid = htons(0xffff); 2352ae7408fSSathya Perla flow->l2_key.num_vlans = 1; 2362ae7408fSSathya Perla } 2372ae7408fSSathya Perla 2382ae7408fSSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { 2392ae7408fSSathya Perla struct flow_dissector_key_ipv4_addrs *key = 2402ae7408fSSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV4_ADDRS); 2412ae7408fSSathya Perla struct flow_dissector_key_ipv4_addrs *mask = 2422ae7408fSSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV4_ADDRS); 2432ae7408fSSathya Perla 2442ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_IPV4_ADDRS; 2452ae7408fSSathya Perla flow->l3_key.ipv4.daddr.s_addr = key->dst; 2462ae7408fSSathya Perla flow->l3_mask.ipv4.daddr.s_addr = mask->dst; 2472ae7408fSSathya Perla flow->l3_key.ipv4.saddr.s_addr = key->src; 2482ae7408fSSathya Perla flow->l3_mask.ipv4.saddr.s_addr = mask->src; 2492ae7408fSSathya Perla } else if (dissector_uses_key(dissector, 2502ae7408fSSathya Perla FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { 2512ae7408fSSathya Perla struct flow_dissector_key_ipv6_addrs *key = 2522ae7408fSSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV6_ADDRS); 2532ae7408fSSathya Perla struct flow_dissector_key_ipv6_addrs *mask = 2542ae7408fSSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV6_ADDRS); 2552ae7408fSSathya Perla 2562ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_IPV6_ADDRS; 2572ae7408fSSathya Perla flow->l3_key.ipv6.daddr = key->dst; 2582ae7408fSSathya Perla flow->l3_mask.ipv6.daddr = mask->dst; 2592ae7408fSSathya Perla flow->l3_key.ipv6.saddr = key->src; 2602ae7408fSSathya Perla flow->l3_mask.ipv6.saddr = mask->src; 2612ae7408fSSathya Perla } 2622ae7408fSSathya Perla 2632ae7408fSSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_PORTS)) { 2642ae7408fSSathya Perla struct flow_dissector_key_ports *key = 2652ae7408fSSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_PORTS); 2662ae7408fSSathya Perla struct flow_dissector_key_ports *mask = 2672ae7408fSSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_PORTS); 2682ae7408fSSathya Perla 2692ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_PORTS; 2702ae7408fSSathya Perla flow->l4_key.ports.dport = key->dst; 2712ae7408fSSathya Perla flow->l4_mask.ports.dport = mask->dst; 2722ae7408fSSathya Perla flow->l4_key.ports.sport = key->src; 2732ae7408fSSathya Perla flow->l4_mask.ports.sport = mask->src; 2742ae7408fSSathya Perla } 2752ae7408fSSathya Perla 2762ae7408fSSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ICMP)) { 2772ae7408fSSathya Perla struct flow_dissector_key_icmp *key = 2782ae7408fSSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ICMP); 2792ae7408fSSathya Perla struct flow_dissector_key_icmp *mask = 2802ae7408fSSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ICMP); 2812ae7408fSSathya Perla 2822ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_ICMP; 2832ae7408fSSathya Perla flow->l4_key.icmp.type = key->type; 2842ae7408fSSathya Perla flow->l4_key.icmp.code = key->code; 2852ae7408fSSathya Perla flow->l4_mask.icmp.type = mask->type; 2862ae7408fSSathya Perla flow->l4_mask.icmp.code = mask->code; 2872ae7408fSSathya Perla } 2882ae7408fSSathya Perla 2898c95f773SSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ENC_CONTROL)) { 2908c95f773SSathya Perla struct flow_dissector_key_control *key = 2918c95f773SSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_CONTROL); 2928c95f773SSathya Perla 2938c95f773SSathya Perla addr_type = key->addr_type; 2948c95f773SSathya Perla } 2958c95f773SSathya Perla 2968c95f773SSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) { 2978c95f773SSathya Perla struct flow_dissector_key_ipv4_addrs *key = 2988c95f773SSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS); 2998c95f773SSathya Perla struct flow_dissector_key_ipv4_addrs *mask = 3008c95f773SSathya Perla GET_MASK(tc_flow_cmd, 3018c95f773SSathya Perla FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS); 3028c95f773SSathya Perla 3038c95f773SSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS; 3048c95f773SSathya Perla flow->tun_key.u.ipv4.dst = key->dst; 3058c95f773SSathya Perla flow->tun_mask.u.ipv4.dst = mask->dst; 3068c95f773SSathya Perla flow->tun_key.u.ipv4.src = key->src; 3078c95f773SSathya Perla flow->tun_mask.u.ipv4.src = mask->src; 3088c95f773SSathya Perla } else if (dissector_uses_key(dissector, 3098c95f773SSathya Perla FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) { 3108c95f773SSathya Perla return -EOPNOTSUPP; 3118c95f773SSathya Perla } 3128c95f773SSathya Perla 3138c95f773SSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ENC_KEYID)) { 3148c95f773SSathya Perla struct flow_dissector_key_keyid *key = 3158c95f773SSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_KEYID); 3168c95f773SSathya Perla struct flow_dissector_key_keyid *mask = 3178c95f773SSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_KEYID); 3188c95f773SSathya Perla 3198c95f773SSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ID; 3208c95f773SSathya Perla flow->tun_key.tun_id = key32_to_tunnel_id(key->keyid); 3218c95f773SSathya Perla flow->tun_mask.tun_id = key32_to_tunnel_id(mask->keyid); 3228c95f773SSathya Perla } 3238c95f773SSathya Perla 3248c95f773SSathya Perla if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ENC_PORTS)) { 3258c95f773SSathya Perla struct flow_dissector_key_ports *key = 3268c95f773SSathya Perla GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_PORTS); 3278c95f773SSathya Perla struct flow_dissector_key_ports *mask = 3288c95f773SSathya Perla GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ENC_PORTS); 3298c95f773SSathya Perla 3308c95f773SSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_PORTS; 3318c95f773SSathya Perla flow->tun_key.tp_dst = key->dst; 3328c95f773SSathya Perla flow->tun_mask.tp_dst = mask->dst; 3338c95f773SSathya Perla flow->tun_key.tp_src = key->src; 3348c95f773SSathya Perla flow->tun_mask.tp_src = mask->src; 3358c95f773SSathya Perla } 3368c95f773SSathya Perla 3372ae7408fSSathya Perla return bnxt_tc_parse_actions(bp, &flow->actions, tc_flow_cmd->exts); 3382ae7408fSSathya Perla } 3392ae7408fSSathya Perla 3402ae7408fSSathya Perla static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp, __le16 flow_handle) 3412ae7408fSSathya Perla { 342db1d36a2SSathya Perla struct hwrm_cfa_flow_free_input req = { 0 }; 343db1d36a2SSathya Perla int rc; 344db1d36a2SSathya Perla 345db1d36a2SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_FREE, -1, -1); 346db1d36a2SSathya Perla req.flow_handle = flow_handle; 347db1d36a2SSathya Perla 348db1d36a2SSathya Perla rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 349db1d36a2SSathya Perla if (rc) 350db1d36a2SSathya Perla netdev_info(bp->dev, "Error: %s: flow_handle=0x%x rc=%d", 351db1d36a2SSathya Perla __func__, flow_handle, rc); 3526ae777eaSVenkat Duvvuru 3536ae777eaSVenkat Duvvuru if (rc) 3546ae777eaSVenkat Duvvuru rc = -EIO; 355db1d36a2SSathya Perla return rc; 356db1d36a2SSathya Perla } 357db1d36a2SSathya Perla 358db1d36a2SSathya Perla static int ipv6_mask_len(struct in6_addr *mask) 359db1d36a2SSathya Perla { 360db1d36a2SSathya Perla int mask_len = 0, i; 361db1d36a2SSathya Perla 362db1d36a2SSathya Perla for (i = 0; i < 4; i++) 363db1d36a2SSathya Perla mask_len += inet_mask_len(mask->s6_addr32[i]); 364db1d36a2SSathya Perla 365db1d36a2SSathya Perla return mask_len; 366db1d36a2SSathya Perla } 367db1d36a2SSathya Perla 368db1d36a2SSathya Perla static bool is_wildcard(void *mask, int len) 369db1d36a2SSathya Perla { 370db1d36a2SSathya Perla const u8 *p = mask; 371db1d36a2SSathya Perla int i; 372db1d36a2SSathya Perla 373db1d36a2SSathya Perla for (i = 0; i < len; i++) { 374db1d36a2SSathya Perla if (p[i] != 0) 375db1d36a2SSathya Perla return false; 376db1d36a2SSathya Perla } 377db1d36a2SSathya Perla return true; 3782ae7408fSSathya Perla } 3792ae7408fSSathya Perla 380e85a9be9SAndy Gospodarek static bool is_exactmatch(void *mask, int len) 381e85a9be9SAndy Gospodarek { 382e85a9be9SAndy Gospodarek const u8 *p = mask; 383e85a9be9SAndy Gospodarek int i; 384e85a9be9SAndy Gospodarek 385e85a9be9SAndy Gospodarek for (i = 0; i < len; i++) 386e85a9be9SAndy Gospodarek if (p[i] != 0xff) 387e85a9be9SAndy Gospodarek return false; 388e85a9be9SAndy Gospodarek 389e85a9be9SAndy Gospodarek return true; 390e85a9be9SAndy Gospodarek } 391e85a9be9SAndy Gospodarek 392e85a9be9SAndy Gospodarek static bool bits_set(void *key, int len) 393e85a9be9SAndy Gospodarek { 394e85a9be9SAndy Gospodarek const u8 *p = key; 395e85a9be9SAndy Gospodarek int i; 396e85a9be9SAndy Gospodarek 397e85a9be9SAndy Gospodarek for (i = 0; i < len; i++) 398e85a9be9SAndy Gospodarek if (p[i] != 0) 399e85a9be9SAndy Gospodarek return true; 400e85a9be9SAndy Gospodarek 401e85a9be9SAndy Gospodarek return false; 402e85a9be9SAndy Gospodarek } 403e85a9be9SAndy Gospodarek 4042ae7408fSSathya Perla static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow, 4058c95f773SSathya Perla __le16 ref_flow_handle, 4068c95f773SSathya Perla __le32 tunnel_handle, __le16 *flow_handle) 4072ae7408fSSathya Perla { 408db1d36a2SSathya Perla struct hwrm_cfa_flow_alloc_output *resp = bp->hwrm_cmd_resp_addr; 409db1d36a2SSathya Perla struct bnxt_tc_actions *actions = &flow->actions; 410db1d36a2SSathya Perla struct bnxt_tc_l3_key *l3_mask = &flow->l3_mask; 411db1d36a2SSathya Perla struct bnxt_tc_l3_key *l3_key = &flow->l3_key; 412db1d36a2SSathya Perla struct hwrm_cfa_flow_alloc_input req = { 0 }; 413db1d36a2SSathya Perla u16 flow_flags = 0, action_flags = 0; 414db1d36a2SSathya Perla int rc; 415db1d36a2SSathya Perla 416db1d36a2SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_ALLOC, -1, -1); 417db1d36a2SSathya Perla 418db1d36a2SSathya Perla req.src_fid = cpu_to_le16(flow->src_fid); 419db1d36a2SSathya Perla req.ref_flow_handle = ref_flow_handle; 4208c95f773SSathya Perla 4218c95f773SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP || 4228c95f773SSathya Perla actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) { 4238c95f773SSathya Perla req.tunnel_handle = tunnel_handle; 4248c95f773SSathya Perla flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_TUNNEL; 4258c95f773SSathya Perla action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL; 4268c95f773SSathya Perla } 4278c95f773SSathya Perla 428db1d36a2SSathya Perla req.ethertype = flow->l2_key.ether_type; 429db1d36a2SSathya Perla req.ip_proto = flow->l4_key.ip_proto; 430db1d36a2SSathya Perla 431db1d36a2SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_ETH_ADDRS) { 432db1d36a2SSathya Perla memcpy(req.dmac, flow->l2_key.dmac, ETH_ALEN); 433db1d36a2SSathya Perla memcpy(req.smac, flow->l2_key.smac, ETH_ALEN); 434db1d36a2SSathya Perla } 435db1d36a2SSathya Perla 436db1d36a2SSathya Perla if (flow->l2_key.num_vlans > 0) { 437db1d36a2SSathya Perla flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE; 438db1d36a2SSathya Perla /* FW expects the inner_vlan_tci value to be set 439db1d36a2SSathya Perla * in outer_vlan_tci when num_vlans is 1 (which is 440db1d36a2SSathya Perla * always the case in TC.) 441db1d36a2SSathya Perla */ 442db1d36a2SSathya Perla req.outer_vlan_tci = flow->l2_key.inner_vlan_tci; 443db1d36a2SSathya Perla } 444db1d36a2SSathya Perla 445db1d36a2SSathya Perla /* If all IP and L4 fields are wildcarded then this is an L2 flow */ 4467deea450SSunil Challa if (is_wildcard(l3_mask, sizeof(*l3_mask)) && 447db1d36a2SSathya Perla is_wildcard(&flow->l4_mask, sizeof(flow->l4_mask))) { 448db1d36a2SSathya Perla flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2; 449db1d36a2SSathya Perla } else { 450db1d36a2SSathya Perla flow_flags |= flow->l2_key.ether_type == htons(ETH_P_IP) ? 451db1d36a2SSathya Perla CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 : 452db1d36a2SSathya Perla CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6; 453db1d36a2SSathya Perla 454db1d36a2SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV4_ADDRS) { 455db1d36a2SSathya Perla req.ip_dst[0] = l3_key->ipv4.daddr.s_addr; 456db1d36a2SSathya Perla req.ip_dst_mask_len = 457db1d36a2SSathya Perla inet_mask_len(l3_mask->ipv4.daddr.s_addr); 458db1d36a2SSathya Perla req.ip_src[0] = l3_key->ipv4.saddr.s_addr; 459db1d36a2SSathya Perla req.ip_src_mask_len = 460db1d36a2SSathya Perla inet_mask_len(l3_mask->ipv4.saddr.s_addr); 461db1d36a2SSathya Perla } else if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV6_ADDRS) { 462db1d36a2SSathya Perla memcpy(req.ip_dst, l3_key->ipv6.daddr.s6_addr32, 463db1d36a2SSathya Perla sizeof(req.ip_dst)); 464db1d36a2SSathya Perla req.ip_dst_mask_len = 465db1d36a2SSathya Perla ipv6_mask_len(&l3_mask->ipv6.daddr); 466db1d36a2SSathya Perla memcpy(req.ip_src, l3_key->ipv6.saddr.s6_addr32, 467db1d36a2SSathya Perla sizeof(req.ip_src)); 468db1d36a2SSathya Perla req.ip_src_mask_len = 469db1d36a2SSathya Perla ipv6_mask_len(&l3_mask->ipv6.saddr); 470db1d36a2SSathya Perla } 471db1d36a2SSathya Perla } 472db1d36a2SSathya Perla 473db1d36a2SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) { 474db1d36a2SSathya Perla req.l4_src_port = flow->l4_key.ports.sport; 475db1d36a2SSathya Perla req.l4_src_port_mask = flow->l4_mask.ports.sport; 476db1d36a2SSathya Perla req.l4_dst_port = flow->l4_key.ports.dport; 477db1d36a2SSathya Perla req.l4_dst_port_mask = flow->l4_mask.ports.dport; 478db1d36a2SSathya Perla } else if (flow->flags & BNXT_TC_FLOW_FLAGS_ICMP) { 479db1d36a2SSathya Perla /* l4 ports serve as type/code when ip_proto is ICMP */ 480db1d36a2SSathya Perla req.l4_src_port = htons(flow->l4_key.icmp.type); 481db1d36a2SSathya Perla req.l4_src_port_mask = htons(flow->l4_mask.icmp.type); 482db1d36a2SSathya Perla req.l4_dst_port = htons(flow->l4_key.icmp.code); 483db1d36a2SSathya Perla req.l4_dst_port_mask = htons(flow->l4_mask.icmp.code); 484db1d36a2SSathya Perla } 485db1d36a2SSathya Perla req.flags = cpu_to_le16(flow_flags); 486db1d36a2SSathya Perla 487db1d36a2SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_DROP) { 488db1d36a2SSathya Perla action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP; 489db1d36a2SSathya Perla } else { 490db1d36a2SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { 491db1d36a2SSathya Perla action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD; 492db1d36a2SSathya Perla req.dst_fid = cpu_to_le16(actions->dst_fid); 493db1d36a2SSathya Perla } 494db1d36a2SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_PUSH_VLAN) { 495db1d36a2SSathya Perla action_flags |= 496db1d36a2SSathya Perla CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; 497db1d36a2SSathya Perla req.l2_rewrite_vlan_tpid = actions->push_vlan_tpid; 498db1d36a2SSathya Perla req.l2_rewrite_vlan_tci = actions->push_vlan_tci; 499db1d36a2SSathya Perla memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); 500db1d36a2SSathya Perla memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); 501db1d36a2SSathya Perla } 502db1d36a2SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_POP_VLAN) { 503db1d36a2SSathya Perla action_flags |= 504db1d36a2SSathya Perla CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; 505db1d36a2SSathya Perla /* Rewrite config with tpid = 0 implies vlan pop */ 506db1d36a2SSathya Perla req.l2_rewrite_vlan_tpid = 0; 507db1d36a2SSathya Perla memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); 508db1d36a2SSathya Perla memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); 509db1d36a2SSathya Perla } 510db1d36a2SSathya Perla } 511db1d36a2SSathya Perla req.action_flags = cpu_to_le16(action_flags); 512db1d36a2SSathya Perla 513db1d36a2SSathya Perla mutex_lock(&bp->hwrm_cmd_lock); 514db1d36a2SSathya Perla rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 515db1d36a2SSathya Perla if (!rc) 516db1d36a2SSathya Perla *flow_handle = resp->flow_handle; 517db1d36a2SSathya Perla mutex_unlock(&bp->hwrm_cmd_lock); 518db1d36a2SSathya Perla 5196ae777eaSVenkat Duvvuru if (rc == HWRM_ERR_CODE_RESOURCE_ALLOC_ERROR) 5206ae777eaSVenkat Duvvuru rc = -ENOSPC; 5216ae777eaSVenkat Duvvuru else if (rc) 5226ae777eaSVenkat Duvvuru rc = -EIO; 523db1d36a2SSathya Perla return rc; 5242ae7408fSSathya Perla } 5252ae7408fSSathya Perla 5268c95f773SSathya Perla static int hwrm_cfa_decap_filter_alloc(struct bnxt *bp, 5278c95f773SSathya Perla struct bnxt_tc_flow *flow, 5288c95f773SSathya Perla struct bnxt_tc_l2_key *l2_info, 5298c95f773SSathya Perla __le32 ref_decap_handle, 5308c95f773SSathya Perla __le32 *decap_filter_handle) 5318c95f773SSathya Perla { 532f484f678SSathya Perla struct hwrm_cfa_decap_filter_alloc_output *resp = 533f484f678SSathya Perla bp->hwrm_cmd_resp_addr; 534f484f678SSathya Perla struct hwrm_cfa_decap_filter_alloc_input req = { 0 }; 535f484f678SSathya Perla struct ip_tunnel_key *tun_key = &flow->tun_key; 536f484f678SSathya Perla u32 enables = 0; 537f484f678SSathya Perla int rc; 538f484f678SSathya Perla 539f484f678SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_DECAP_FILTER_ALLOC, -1, -1); 540f484f678SSathya Perla 541f484f678SSathya Perla req.flags = cpu_to_le32(CFA_DECAP_FILTER_ALLOC_REQ_FLAGS_OVS_TUNNEL); 542f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE | 543f484f678SSathya Perla CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL; 544f484f678SSathya Perla req.tunnel_type = CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN; 545f484f678SSathya Perla req.ip_protocol = CFA_DECAP_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP; 546f484f678SSathya Perla 547f484f678SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ID) { 548f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_ID; 549f484f678SSathya Perla /* tunnel_id is wrongly defined in hsi defn. as __le32 */ 550f484f678SSathya Perla req.tunnel_id = tunnel_id_to_key32(tun_key->tun_id); 551f484f678SSathya Perla } 552f484f678SSathya Perla 553f484f678SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS) { 554c8fb7b82SSunil Challa enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR; 555f484f678SSathya Perla ether_addr_copy(req.dst_macaddr, l2_info->dmac); 556f484f678SSathya Perla } 557f484f678SSathya Perla if (l2_info->num_vlans) { 558f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_T_IVLAN_VID; 559f484f678SSathya Perla req.t_ivlan_vid = l2_info->inner_vlan_tci; 560f484f678SSathya Perla } 561f484f678SSathya Perla 562f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE; 563f484f678SSathya Perla req.ethertype = htons(ETH_P_IP); 564f484f678SSathya Perla 565f484f678SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS) { 566f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR | 567f484f678SSathya Perla CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR | 568f484f678SSathya Perla CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE; 569f484f678SSathya Perla req.ip_addr_type = CFA_DECAP_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4; 570f484f678SSathya Perla req.dst_ipaddr[0] = tun_key->u.ipv4.dst; 571f484f678SSathya Perla req.src_ipaddr[0] = tun_key->u.ipv4.src; 572f484f678SSathya Perla } 573f484f678SSathya Perla 574f484f678SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_PORTS) { 575f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_PORT; 576f484f678SSathya Perla req.dst_port = tun_key->tp_dst; 577f484f678SSathya Perla } 578f484f678SSathya Perla 579f484f678SSathya Perla /* Eventhough the decap_handle returned by hwrm_cfa_decap_filter_alloc 580f484f678SSathya Perla * is defined as __le32, l2_ctxt_ref_id is defined in HSI as __le16. 581f484f678SSathya Perla */ 582f484f678SSathya Perla req.l2_ctxt_ref_id = (__force __le16)ref_decap_handle; 583f484f678SSathya Perla req.enables = cpu_to_le32(enables); 584f484f678SSathya Perla 585f484f678SSathya Perla mutex_lock(&bp->hwrm_cmd_lock); 586f484f678SSathya Perla rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 587f484f678SSathya Perla if (!rc) 588f484f678SSathya Perla *decap_filter_handle = resp->decap_filter_id; 589f484f678SSathya Perla else 590f484f678SSathya Perla netdev_info(bp->dev, "%s: Error rc=%d", __func__, rc); 591f484f678SSathya Perla mutex_unlock(&bp->hwrm_cmd_lock); 592f484f678SSathya Perla 5936ae777eaSVenkat Duvvuru if (rc) 5946ae777eaSVenkat Duvvuru rc = -EIO; 595f484f678SSathya Perla return rc; 5968c95f773SSathya Perla } 5978c95f773SSathya Perla 5988c95f773SSathya Perla static int hwrm_cfa_decap_filter_free(struct bnxt *bp, 5998c95f773SSathya Perla __le32 decap_filter_handle) 6008c95f773SSathya Perla { 601f484f678SSathya Perla struct hwrm_cfa_decap_filter_free_input req = { 0 }; 602f484f678SSathya Perla int rc; 603f484f678SSathya Perla 604f484f678SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_DECAP_FILTER_FREE, -1, -1); 605f484f678SSathya Perla req.decap_filter_id = decap_filter_handle; 606f484f678SSathya Perla 607f484f678SSathya Perla rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 608f484f678SSathya Perla if (rc) 609f484f678SSathya Perla netdev_info(bp->dev, "%s: Error rc=%d", __func__, rc); 6106ae777eaSVenkat Duvvuru 6116ae777eaSVenkat Duvvuru if (rc) 6126ae777eaSVenkat Duvvuru rc = -EIO; 613f484f678SSathya Perla return rc; 6148c95f773SSathya Perla } 6158c95f773SSathya Perla 6168c95f773SSathya Perla static int hwrm_cfa_encap_record_alloc(struct bnxt *bp, 6178c95f773SSathya Perla struct ip_tunnel_key *encap_key, 6188c95f773SSathya Perla struct bnxt_tc_l2_key *l2_info, 6198c95f773SSathya Perla __le32 *encap_record_handle) 6208c95f773SSathya Perla { 621f484f678SSathya Perla struct hwrm_cfa_encap_record_alloc_output *resp = 622f484f678SSathya Perla bp->hwrm_cmd_resp_addr; 623f484f678SSathya Perla struct hwrm_cfa_encap_record_alloc_input req = { 0 }; 624f484f678SSathya Perla struct hwrm_cfa_encap_data_vxlan *encap = 625f484f678SSathya Perla (struct hwrm_cfa_encap_data_vxlan *)&req.encap_data; 626f484f678SSathya Perla struct hwrm_vxlan_ipv4_hdr *encap_ipv4 = 627f484f678SSathya Perla (struct hwrm_vxlan_ipv4_hdr *)encap->l3; 628f484f678SSathya Perla int rc; 629f484f678SSathya Perla 630f484f678SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_ENCAP_RECORD_ALLOC, -1, -1); 631f484f678SSathya Perla 632f484f678SSathya Perla req.encap_type = CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_VXLAN; 633f484f678SSathya Perla 634f484f678SSathya Perla ether_addr_copy(encap->dst_mac_addr, l2_info->dmac); 635f484f678SSathya Perla ether_addr_copy(encap->src_mac_addr, l2_info->smac); 636f484f678SSathya Perla if (l2_info->num_vlans) { 637f484f678SSathya Perla encap->num_vlan_tags = l2_info->num_vlans; 638f484f678SSathya Perla encap->ovlan_tci = l2_info->inner_vlan_tci; 639f484f678SSathya Perla encap->ovlan_tpid = l2_info->inner_vlan_tpid; 640f484f678SSathya Perla } 641f484f678SSathya Perla 642f484f678SSathya Perla encap_ipv4->ver_hlen = 4 << VXLAN_IPV4_HDR_VER_HLEN_VERSION_SFT; 643f484f678SSathya Perla encap_ipv4->ver_hlen |= 5 << VXLAN_IPV4_HDR_VER_HLEN_HEADER_LENGTH_SFT; 644f484f678SSathya Perla encap_ipv4->ttl = encap_key->ttl; 645f484f678SSathya Perla 646f484f678SSathya Perla encap_ipv4->dest_ip_addr = encap_key->u.ipv4.dst; 647f484f678SSathya Perla encap_ipv4->src_ip_addr = encap_key->u.ipv4.src; 648f484f678SSathya Perla encap_ipv4->protocol = IPPROTO_UDP; 649f484f678SSathya Perla 650f484f678SSathya Perla encap->dst_port = encap_key->tp_dst; 651f484f678SSathya Perla encap->vni = tunnel_id_to_key32(encap_key->tun_id); 652f484f678SSathya Perla 653f484f678SSathya Perla mutex_lock(&bp->hwrm_cmd_lock); 654f484f678SSathya Perla rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 655f484f678SSathya Perla if (!rc) 656f484f678SSathya Perla *encap_record_handle = resp->encap_record_id; 657f484f678SSathya Perla else 658f484f678SSathya Perla netdev_info(bp->dev, "%s: Error rc=%d", __func__, rc); 659f484f678SSathya Perla mutex_unlock(&bp->hwrm_cmd_lock); 660f484f678SSathya Perla 6616ae777eaSVenkat Duvvuru if (rc) 6626ae777eaSVenkat Duvvuru rc = -EIO; 663f484f678SSathya Perla return rc; 6648c95f773SSathya Perla } 6658c95f773SSathya Perla 6668c95f773SSathya Perla static int hwrm_cfa_encap_record_free(struct bnxt *bp, 6678c95f773SSathya Perla __le32 encap_record_handle) 6688c95f773SSathya Perla { 669f484f678SSathya Perla struct hwrm_cfa_encap_record_free_input req = { 0 }; 670f484f678SSathya Perla int rc; 671f484f678SSathya Perla 672f484f678SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_ENCAP_RECORD_FREE, -1, -1); 673f484f678SSathya Perla req.encap_record_id = encap_record_handle; 674f484f678SSathya Perla 675f484f678SSathya Perla rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 676f484f678SSathya Perla if (rc) 677f484f678SSathya Perla netdev_info(bp->dev, "%s: Error rc=%d", __func__, rc); 6786ae777eaSVenkat Duvvuru 6796ae777eaSVenkat Duvvuru if (rc) 6806ae777eaSVenkat Duvvuru rc = -EIO; 681f484f678SSathya Perla return rc; 6828c95f773SSathya Perla } 6838c95f773SSathya Perla 6842ae7408fSSathya Perla static int bnxt_tc_put_l2_node(struct bnxt *bp, 6852ae7408fSSathya Perla struct bnxt_tc_flow_node *flow_node) 6862ae7408fSSathya Perla { 6872ae7408fSSathya Perla struct bnxt_tc_l2_node *l2_node = flow_node->l2_node; 688cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 6892ae7408fSSathya Perla int rc; 6902ae7408fSSathya Perla 6912ae7408fSSathya Perla /* remove flow_node from the L2 shared flow list */ 6922ae7408fSSathya Perla list_del(&flow_node->l2_list_node); 6932ae7408fSSathya Perla if (--l2_node->refcount == 0) { 6942ae7408fSSathya Perla rc = rhashtable_remove_fast(&tc_info->l2_table, &l2_node->node, 6952ae7408fSSathya Perla tc_info->l2_ht_params); 6962ae7408fSSathya Perla if (rc) 6972ae7408fSSathya Perla netdev_err(bp->dev, 6982ae7408fSSathya Perla "Error: %s: rhashtable_remove_fast: %d", 6992ae7408fSSathya Perla __func__, rc); 7002ae7408fSSathya Perla kfree_rcu(l2_node, rcu); 7012ae7408fSSathya Perla } 7022ae7408fSSathya Perla return 0; 7032ae7408fSSathya Perla } 7042ae7408fSSathya Perla 7052ae7408fSSathya Perla static struct bnxt_tc_l2_node * 7062ae7408fSSathya Perla bnxt_tc_get_l2_node(struct bnxt *bp, struct rhashtable *l2_table, 7072ae7408fSSathya Perla struct rhashtable_params ht_params, 7082ae7408fSSathya Perla struct bnxt_tc_l2_key *l2_key) 7092ae7408fSSathya Perla { 7102ae7408fSSathya Perla struct bnxt_tc_l2_node *l2_node; 7112ae7408fSSathya Perla int rc; 7122ae7408fSSathya Perla 7132ae7408fSSathya Perla l2_node = rhashtable_lookup_fast(l2_table, l2_key, ht_params); 7142ae7408fSSathya Perla if (!l2_node) { 7152ae7408fSSathya Perla l2_node = kzalloc(sizeof(*l2_node), GFP_KERNEL); 7162ae7408fSSathya Perla if (!l2_node) { 7172ae7408fSSathya Perla rc = -ENOMEM; 7182ae7408fSSathya Perla return NULL; 7192ae7408fSSathya Perla } 7202ae7408fSSathya Perla 7212ae7408fSSathya Perla l2_node->key = *l2_key; 7222ae7408fSSathya Perla rc = rhashtable_insert_fast(l2_table, &l2_node->node, 7232ae7408fSSathya Perla ht_params); 7242ae7408fSSathya Perla if (rc) { 7258c95f773SSathya Perla kfree_rcu(l2_node, rcu); 7262ae7408fSSathya Perla netdev_err(bp->dev, 7272ae7408fSSathya Perla "Error: %s: rhashtable_insert_fast: %d", 7282ae7408fSSathya Perla __func__, rc); 7292ae7408fSSathya Perla return NULL; 7302ae7408fSSathya Perla } 7312ae7408fSSathya Perla INIT_LIST_HEAD(&l2_node->common_l2_flows); 7322ae7408fSSathya Perla } 7332ae7408fSSathya Perla return l2_node; 7342ae7408fSSathya Perla } 7352ae7408fSSathya Perla 7362ae7408fSSathya Perla /* Get the ref_flow_handle for a flow by checking if there are any other 7372ae7408fSSathya Perla * flows that share the same L2 key as this flow. 7382ae7408fSSathya Perla */ 7392ae7408fSSathya Perla static int 7402ae7408fSSathya Perla bnxt_tc_get_ref_flow_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 7412ae7408fSSathya Perla struct bnxt_tc_flow_node *flow_node, 7422ae7408fSSathya Perla __le16 *ref_flow_handle) 7432ae7408fSSathya Perla { 744cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 7452ae7408fSSathya Perla struct bnxt_tc_flow_node *ref_flow_node; 7462ae7408fSSathya Perla struct bnxt_tc_l2_node *l2_node; 7472ae7408fSSathya Perla 7482ae7408fSSathya Perla l2_node = bnxt_tc_get_l2_node(bp, &tc_info->l2_table, 7492ae7408fSSathya Perla tc_info->l2_ht_params, 7502ae7408fSSathya Perla &flow->l2_key); 7512ae7408fSSathya Perla if (!l2_node) 7522ae7408fSSathya Perla return -1; 7532ae7408fSSathya Perla 7542ae7408fSSathya Perla /* If any other flow is using this l2_node, use it's flow_handle 7552ae7408fSSathya Perla * as the ref_flow_handle 7562ae7408fSSathya Perla */ 7572ae7408fSSathya Perla if (l2_node->refcount > 0) { 7582ae7408fSSathya Perla ref_flow_node = list_first_entry(&l2_node->common_l2_flows, 7592ae7408fSSathya Perla struct bnxt_tc_flow_node, 7602ae7408fSSathya Perla l2_list_node); 7612ae7408fSSathya Perla *ref_flow_handle = ref_flow_node->flow_handle; 7622ae7408fSSathya Perla } else { 7632ae7408fSSathya Perla *ref_flow_handle = cpu_to_le16(0xffff); 7642ae7408fSSathya Perla } 7652ae7408fSSathya Perla 7662ae7408fSSathya Perla /* Insert the l2_node into the flow_node so that subsequent flows 7672ae7408fSSathya Perla * with a matching l2 key can use the flow_handle of this flow 7682ae7408fSSathya Perla * as their ref_flow_handle 7692ae7408fSSathya Perla */ 7702ae7408fSSathya Perla flow_node->l2_node = l2_node; 7712ae7408fSSathya Perla list_add(&flow_node->l2_list_node, &l2_node->common_l2_flows); 7722ae7408fSSathya Perla l2_node->refcount++; 7732ae7408fSSathya Perla return 0; 7742ae7408fSSathya Perla } 7752ae7408fSSathya Perla 7762ae7408fSSathya Perla /* After the flow parsing is done, this routine is used for checking 7772ae7408fSSathya Perla * if there are any aspects of the flow that prevent it from being 7782ae7408fSSathya Perla * offloaded. 7792ae7408fSSathya Perla */ 7802ae7408fSSathya Perla static bool bnxt_tc_can_offload(struct bnxt *bp, struct bnxt_tc_flow *flow) 7812ae7408fSSathya Perla { 7822ae7408fSSathya Perla /* If L4 ports are specified then ip_proto must be TCP or UDP */ 7832ae7408fSSathya Perla if ((flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) && 7842ae7408fSSathya Perla (flow->l4_key.ip_proto != IPPROTO_TCP && 7852ae7408fSSathya Perla flow->l4_key.ip_proto != IPPROTO_UDP)) { 7862ae7408fSSathya Perla netdev_info(bp->dev, "Cannot offload non-TCP/UDP (%d) ports", 7872ae7408fSSathya Perla flow->l4_key.ip_proto); 7882ae7408fSSathya Perla return false; 7892ae7408fSSathya Perla } 7902ae7408fSSathya Perla 791e85a9be9SAndy Gospodarek /* Currently source/dest MAC cannot be partial wildcard */ 792e85a9be9SAndy Gospodarek if (bits_set(&flow->l2_key.smac, sizeof(flow->l2_key.smac)) && 793e85a9be9SAndy Gospodarek !is_exactmatch(flow->l2_mask.smac, sizeof(flow->l2_mask.smac))) { 794e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for Source MAC\n"); 795e85a9be9SAndy Gospodarek return false; 796e85a9be9SAndy Gospodarek } 797e85a9be9SAndy Gospodarek if (bits_set(&flow->l2_key.dmac, sizeof(flow->l2_key.dmac)) && 798e85a9be9SAndy Gospodarek !is_exactmatch(&flow->l2_mask.dmac, sizeof(flow->l2_mask.dmac))) { 799e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for Dest MAC\n"); 800e85a9be9SAndy Gospodarek return false; 801e85a9be9SAndy Gospodarek } 802e85a9be9SAndy Gospodarek 803e85a9be9SAndy Gospodarek /* Currently VLAN fields cannot be partial wildcard */ 804e85a9be9SAndy Gospodarek if (bits_set(&flow->l2_key.inner_vlan_tci, 805e85a9be9SAndy Gospodarek sizeof(flow->l2_key.inner_vlan_tci)) && 806e85a9be9SAndy Gospodarek !is_exactmatch(&flow->l2_mask.inner_vlan_tci, 807e85a9be9SAndy Gospodarek sizeof(flow->l2_mask.inner_vlan_tci))) { 808e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for VLAN TCI\n"); 809e85a9be9SAndy Gospodarek return false; 810e85a9be9SAndy Gospodarek } 811e85a9be9SAndy Gospodarek if (bits_set(&flow->l2_key.inner_vlan_tpid, 812e85a9be9SAndy Gospodarek sizeof(flow->l2_key.inner_vlan_tpid)) && 813e85a9be9SAndy Gospodarek !is_exactmatch(&flow->l2_mask.inner_vlan_tpid, 814e85a9be9SAndy Gospodarek sizeof(flow->l2_mask.inner_vlan_tpid))) { 815e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for VLAN TPID\n"); 816e85a9be9SAndy Gospodarek return false; 817e85a9be9SAndy Gospodarek } 818e85a9be9SAndy Gospodarek 819e85a9be9SAndy Gospodarek /* Currently Ethertype must be set */ 820e85a9be9SAndy Gospodarek if (!is_exactmatch(&flow->l2_mask.ether_type, 821e85a9be9SAndy Gospodarek sizeof(flow->l2_mask.ether_type))) { 822e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for Ethertype\n"); 823e85a9be9SAndy Gospodarek return false; 824e85a9be9SAndy Gospodarek } 825e85a9be9SAndy Gospodarek 8262ae7408fSSathya Perla return true; 8272ae7408fSSathya Perla } 8282ae7408fSSathya Perla 8298c95f773SSathya Perla /* Returns the final refcount of the node on success 8308c95f773SSathya Perla * or a -ve error code on failure 8318c95f773SSathya Perla */ 8328c95f773SSathya Perla static int bnxt_tc_put_tunnel_node(struct bnxt *bp, 8338c95f773SSathya Perla struct rhashtable *tunnel_table, 8348c95f773SSathya Perla struct rhashtable_params *ht_params, 8358c95f773SSathya Perla struct bnxt_tc_tunnel_node *tunnel_node) 8368c95f773SSathya Perla { 8378c95f773SSathya Perla int rc; 8388c95f773SSathya Perla 8398c95f773SSathya Perla if (--tunnel_node->refcount == 0) { 8408c95f773SSathya Perla rc = rhashtable_remove_fast(tunnel_table, &tunnel_node->node, 8418c95f773SSathya Perla *ht_params); 8428c95f773SSathya Perla if (rc) { 8438c95f773SSathya Perla netdev_err(bp->dev, "rhashtable_remove_fast rc=%d", rc); 8448c95f773SSathya Perla rc = -1; 8458c95f773SSathya Perla } 8468c95f773SSathya Perla kfree_rcu(tunnel_node, rcu); 8478c95f773SSathya Perla return rc; 8488c95f773SSathya Perla } else { 8498c95f773SSathya Perla return tunnel_node->refcount; 8508c95f773SSathya Perla } 8518c95f773SSathya Perla } 8528c95f773SSathya Perla 8538c95f773SSathya Perla /* Get (or add) either encap or decap tunnel node from/to the supplied 8548c95f773SSathya Perla * hash table. 8558c95f773SSathya Perla */ 8568c95f773SSathya Perla static struct bnxt_tc_tunnel_node * 8578c95f773SSathya Perla bnxt_tc_get_tunnel_node(struct bnxt *bp, struct rhashtable *tunnel_table, 8588c95f773SSathya Perla struct rhashtable_params *ht_params, 8598c95f773SSathya Perla struct ip_tunnel_key *tun_key) 8608c95f773SSathya Perla { 8618c95f773SSathya Perla struct bnxt_tc_tunnel_node *tunnel_node; 8628c95f773SSathya Perla int rc; 8638c95f773SSathya Perla 8648c95f773SSathya Perla tunnel_node = rhashtable_lookup_fast(tunnel_table, tun_key, *ht_params); 8658c95f773SSathya Perla if (!tunnel_node) { 8668c95f773SSathya Perla tunnel_node = kzalloc(sizeof(*tunnel_node), GFP_KERNEL); 8678c95f773SSathya Perla if (!tunnel_node) { 8688c95f773SSathya Perla rc = -ENOMEM; 8698c95f773SSathya Perla goto err; 8708c95f773SSathya Perla } 8718c95f773SSathya Perla 8728c95f773SSathya Perla tunnel_node->key = *tun_key; 8738c95f773SSathya Perla tunnel_node->tunnel_handle = INVALID_TUNNEL_HANDLE; 8748c95f773SSathya Perla rc = rhashtable_insert_fast(tunnel_table, &tunnel_node->node, 8758c95f773SSathya Perla *ht_params); 8768c95f773SSathya Perla if (rc) { 8778c95f773SSathya Perla kfree_rcu(tunnel_node, rcu); 8788c95f773SSathya Perla goto err; 8798c95f773SSathya Perla } 8808c95f773SSathya Perla } 8818c95f773SSathya Perla tunnel_node->refcount++; 8828c95f773SSathya Perla return tunnel_node; 8838c95f773SSathya Perla err: 8848c95f773SSathya Perla netdev_info(bp->dev, "error rc=%d", rc); 8858c95f773SSathya Perla return NULL; 8868c95f773SSathya Perla } 8878c95f773SSathya Perla 8888c95f773SSathya Perla static int bnxt_tc_get_ref_decap_handle(struct bnxt *bp, 8898c95f773SSathya Perla struct bnxt_tc_flow *flow, 8908c95f773SSathya Perla struct bnxt_tc_l2_key *l2_key, 8918c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node, 8928c95f773SSathya Perla __le32 *ref_decap_handle) 8938c95f773SSathya Perla { 894cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 8958c95f773SSathya Perla struct bnxt_tc_flow_node *ref_flow_node; 8968c95f773SSathya Perla struct bnxt_tc_l2_node *decap_l2_node; 8978c95f773SSathya Perla 8988c95f773SSathya Perla decap_l2_node = bnxt_tc_get_l2_node(bp, &tc_info->decap_l2_table, 8998c95f773SSathya Perla tc_info->decap_l2_ht_params, 9008c95f773SSathya Perla l2_key); 9018c95f773SSathya Perla if (!decap_l2_node) 9028c95f773SSathya Perla return -1; 9038c95f773SSathya Perla 9048c95f773SSathya Perla /* If any other flow is using this decap_l2_node, use it's decap_handle 9058c95f773SSathya Perla * as the ref_decap_handle 9068c95f773SSathya Perla */ 9078c95f773SSathya Perla if (decap_l2_node->refcount > 0) { 9088c95f773SSathya Perla ref_flow_node = 9098c95f773SSathya Perla list_first_entry(&decap_l2_node->common_l2_flows, 9108c95f773SSathya Perla struct bnxt_tc_flow_node, 9118c95f773SSathya Perla decap_l2_list_node); 9128c95f773SSathya Perla *ref_decap_handle = ref_flow_node->decap_node->tunnel_handle; 9138c95f773SSathya Perla } else { 9148c95f773SSathya Perla *ref_decap_handle = INVALID_TUNNEL_HANDLE; 9158c95f773SSathya Perla } 9168c95f773SSathya Perla 9178c95f773SSathya Perla /* Insert the l2_node into the flow_node so that subsequent flows 9188c95f773SSathya Perla * with a matching decap l2 key can use the decap_filter_handle of 9198c95f773SSathya Perla * this flow as their ref_decap_handle 9208c95f773SSathya Perla */ 9218c95f773SSathya Perla flow_node->decap_l2_node = decap_l2_node; 9228c95f773SSathya Perla list_add(&flow_node->decap_l2_list_node, 9238c95f773SSathya Perla &decap_l2_node->common_l2_flows); 9248c95f773SSathya Perla decap_l2_node->refcount++; 9258c95f773SSathya Perla return 0; 9268c95f773SSathya Perla } 9278c95f773SSathya Perla 9288c95f773SSathya Perla static void bnxt_tc_put_decap_l2_node(struct bnxt *bp, 9298c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node) 9308c95f773SSathya Perla { 9318c95f773SSathya Perla struct bnxt_tc_l2_node *decap_l2_node = flow_node->decap_l2_node; 932cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 9338c95f773SSathya Perla int rc; 9348c95f773SSathya Perla 9358c95f773SSathya Perla /* remove flow_node from the decap L2 sharing flow list */ 9368c95f773SSathya Perla list_del(&flow_node->decap_l2_list_node); 9378c95f773SSathya Perla if (--decap_l2_node->refcount == 0) { 9388c95f773SSathya Perla rc = rhashtable_remove_fast(&tc_info->decap_l2_table, 9398c95f773SSathya Perla &decap_l2_node->node, 9408c95f773SSathya Perla tc_info->decap_l2_ht_params); 9418c95f773SSathya Perla if (rc) 9428c95f773SSathya Perla netdev_err(bp->dev, "rhashtable_remove_fast rc=%d", rc); 9438c95f773SSathya Perla kfree_rcu(decap_l2_node, rcu); 9448c95f773SSathya Perla } 9458c95f773SSathya Perla } 9468c95f773SSathya Perla 9478c95f773SSathya Perla static void bnxt_tc_put_decap_handle(struct bnxt *bp, 9488c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node) 9498c95f773SSathya Perla { 9508c95f773SSathya Perla __le32 decap_handle = flow_node->decap_node->tunnel_handle; 951cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 9528c95f773SSathya Perla int rc; 9538c95f773SSathya Perla 9548c95f773SSathya Perla if (flow_node->decap_l2_node) 9558c95f773SSathya Perla bnxt_tc_put_decap_l2_node(bp, flow_node); 9568c95f773SSathya Perla 9578c95f773SSathya Perla rc = bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table, 9588c95f773SSathya Perla &tc_info->decap_ht_params, 9598c95f773SSathya Perla flow_node->decap_node); 9608c95f773SSathya Perla if (!rc && decap_handle != INVALID_TUNNEL_HANDLE) 9618c95f773SSathya Perla hwrm_cfa_decap_filter_free(bp, decap_handle); 9628c95f773SSathya Perla } 9638c95f773SSathya Perla 9648c95f773SSathya Perla static int bnxt_tc_resolve_tunnel_hdrs(struct bnxt *bp, 9658c95f773SSathya Perla struct ip_tunnel_key *tun_key, 966e9ecc731SSathya Perla struct bnxt_tc_l2_key *l2_info) 9678c95f773SSathya Perla { 968952c5719SMichael Chan #ifdef CONFIG_INET 969e9ecc731SSathya Perla struct net_device *real_dst_dev = bp->dev; 9708c95f773SSathya Perla struct flowi4 flow = { {0} }; 9718c95f773SSathya Perla struct net_device *dst_dev; 9728c95f773SSathya Perla struct neighbour *nbr; 9738c95f773SSathya Perla struct rtable *rt; 9748c95f773SSathya Perla int rc; 9758c95f773SSathya Perla 9768c95f773SSathya Perla flow.flowi4_proto = IPPROTO_UDP; 9778c95f773SSathya Perla flow.fl4_dport = tun_key->tp_dst; 9788c95f773SSathya Perla flow.daddr = tun_key->u.ipv4.dst; 9798c95f773SSathya Perla 9808c95f773SSathya Perla rt = ip_route_output_key(dev_net(real_dst_dev), &flow); 9818c95f773SSathya Perla if (IS_ERR(rt)) { 9828c95f773SSathya Perla netdev_info(bp->dev, "no route to %pI4b", &flow.daddr); 9838c95f773SSathya Perla return -EOPNOTSUPP; 9848c95f773SSathya Perla } 9858c95f773SSathya Perla 9868c95f773SSathya Perla /* The route must either point to the real_dst_dev or a dst_dev that 9878c95f773SSathya Perla * uses the real_dst_dev. 9888c95f773SSathya Perla */ 9898c95f773SSathya Perla dst_dev = rt->dst.dev; 9908c95f773SSathya Perla if (is_vlan_dev(dst_dev)) { 991952c5719SMichael Chan #if IS_ENABLED(CONFIG_VLAN_8021Q) 9928c95f773SSathya Perla struct vlan_dev_priv *vlan = vlan_dev_priv(dst_dev); 9938c95f773SSathya Perla 9948c95f773SSathya Perla if (vlan->real_dev != real_dst_dev) { 9958c95f773SSathya Perla netdev_info(bp->dev, 9968c95f773SSathya Perla "dst_dev(%s) doesn't use PF-if(%s)", 9978c95f773SSathya Perla netdev_name(dst_dev), 9988c95f773SSathya Perla netdev_name(real_dst_dev)); 9998c95f773SSathya Perla rc = -EOPNOTSUPP; 10008c95f773SSathya Perla goto put_rt; 10018c95f773SSathya Perla } 10028c95f773SSathya Perla l2_info->inner_vlan_tci = htons(vlan->vlan_id); 10038c95f773SSathya Perla l2_info->inner_vlan_tpid = vlan->vlan_proto; 10048c95f773SSathya Perla l2_info->num_vlans = 1; 1005952c5719SMichael Chan #endif 10068c95f773SSathya Perla } else if (dst_dev != real_dst_dev) { 10078c95f773SSathya Perla netdev_info(bp->dev, 10088c95f773SSathya Perla "dst_dev(%s) for %pI4b is not PF-if(%s)", 10098c95f773SSathya Perla netdev_name(dst_dev), &flow.daddr, 10108c95f773SSathya Perla netdev_name(real_dst_dev)); 10118c95f773SSathya Perla rc = -EOPNOTSUPP; 10128c95f773SSathya Perla goto put_rt; 10138c95f773SSathya Perla } 10148c95f773SSathya Perla 10158c95f773SSathya Perla nbr = dst_neigh_lookup(&rt->dst, &flow.daddr); 10168c95f773SSathya Perla if (!nbr) { 10178c95f773SSathya Perla netdev_info(bp->dev, "can't lookup neighbor for %pI4b", 10188c95f773SSathya Perla &flow.daddr); 10198c95f773SSathya Perla rc = -EOPNOTSUPP; 10208c95f773SSathya Perla goto put_rt; 10218c95f773SSathya Perla } 10228c95f773SSathya Perla 10238c95f773SSathya Perla tun_key->u.ipv4.src = flow.saddr; 10248c95f773SSathya Perla tun_key->ttl = ip4_dst_hoplimit(&rt->dst); 10258c95f773SSathya Perla neigh_ha_snapshot(l2_info->dmac, nbr, dst_dev); 10268c95f773SSathya Perla ether_addr_copy(l2_info->smac, dst_dev->dev_addr); 10278c95f773SSathya Perla neigh_release(nbr); 10288c95f773SSathya Perla ip_rt_put(rt); 10298c95f773SSathya Perla 10308c95f773SSathya Perla return 0; 10318c95f773SSathya Perla put_rt: 10328c95f773SSathya Perla ip_rt_put(rt); 10338c95f773SSathya Perla return rc; 1034952c5719SMichael Chan #else 1035952c5719SMichael Chan return -EOPNOTSUPP; 1036952c5719SMichael Chan #endif 10378c95f773SSathya Perla } 10388c95f773SSathya Perla 10398c95f773SSathya Perla static int bnxt_tc_get_decap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 10408c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node, 10418c95f773SSathya Perla __le32 *decap_filter_handle) 10428c95f773SSathya Perla { 10438c95f773SSathya Perla struct ip_tunnel_key *decap_key = &flow->tun_key; 1044cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 10458c95f773SSathya Perla struct bnxt_tc_l2_key l2_info = { {0} }; 10468c95f773SSathya Perla struct bnxt_tc_tunnel_node *decap_node; 10478c95f773SSathya Perla struct ip_tunnel_key tun_key = { 0 }; 10488c95f773SSathya Perla struct bnxt_tc_l2_key *decap_l2_info; 10498c95f773SSathya Perla __le32 ref_decap_handle; 10508c95f773SSathya Perla int rc; 10518c95f773SSathya Perla 10528c95f773SSathya Perla /* Check if there's another flow using the same tunnel decap. 10538c95f773SSathya Perla * If not, add this tunnel to the table and resolve the other 1054479ca3bfSSriharsha Basavapatna * tunnel header fileds. Ignore src_port in the tunnel_key, 1055479ca3bfSSriharsha Basavapatna * since it is not required for decap filters. 10568c95f773SSathya Perla */ 1057479ca3bfSSriharsha Basavapatna decap_key->tp_src = 0; 10588c95f773SSathya Perla decap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->decap_table, 10598c95f773SSathya Perla &tc_info->decap_ht_params, 10608c95f773SSathya Perla decap_key); 10618c95f773SSathya Perla if (!decap_node) 10628c95f773SSathya Perla return -ENOMEM; 10638c95f773SSathya Perla 10648c95f773SSathya Perla flow_node->decap_node = decap_node; 10658c95f773SSathya Perla 10668c95f773SSathya Perla if (decap_node->tunnel_handle != INVALID_TUNNEL_HANDLE) 10678c95f773SSathya Perla goto done; 10688c95f773SSathya Perla 10698c95f773SSathya Perla /* Resolve the L2 fields for tunnel decap 10708c95f773SSathya Perla * Resolve the route for remote vtep (saddr) of the decap key 10718c95f773SSathya Perla * Find it's next-hop mac addrs 10728c95f773SSathya Perla */ 10738c95f773SSathya Perla tun_key.u.ipv4.dst = flow->tun_key.u.ipv4.src; 10748c95f773SSathya Perla tun_key.tp_dst = flow->tun_key.tp_dst; 1075e9ecc731SSathya Perla rc = bnxt_tc_resolve_tunnel_hdrs(bp, &tun_key, &l2_info); 10768c95f773SSathya Perla if (rc) 10778c95f773SSathya Perla goto put_decap; 10788c95f773SSathya Perla 10798c95f773SSathya Perla decap_l2_info = &decap_node->l2_info; 1080c8fb7b82SSunil Challa /* decap smac is wildcarded */ 10818c95f773SSathya Perla ether_addr_copy(decap_l2_info->dmac, l2_info.smac); 10828c95f773SSathya Perla if (l2_info.num_vlans) { 10838c95f773SSathya Perla decap_l2_info->num_vlans = l2_info.num_vlans; 10848c95f773SSathya Perla decap_l2_info->inner_vlan_tpid = l2_info.inner_vlan_tpid; 10858c95f773SSathya Perla decap_l2_info->inner_vlan_tci = l2_info.inner_vlan_tci; 10868c95f773SSathya Perla } 10878c95f773SSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS; 10888c95f773SSathya Perla 10898c95f773SSathya Perla /* For getting a decap_filter_handle we first need to check if 10908c95f773SSathya Perla * there are any other decap flows that share the same tunnel L2 10918c95f773SSathya Perla * key and if so, pass that flow's decap_filter_handle as the 10928c95f773SSathya Perla * ref_decap_handle for this flow. 10938c95f773SSathya Perla */ 10948c95f773SSathya Perla rc = bnxt_tc_get_ref_decap_handle(bp, flow, decap_l2_info, flow_node, 10958c95f773SSathya Perla &ref_decap_handle); 10968c95f773SSathya Perla if (rc) 10978c95f773SSathya Perla goto put_decap; 10988c95f773SSathya Perla 10998c95f773SSathya Perla /* Issue the hwrm cmd to allocate a decap filter handle */ 11008c95f773SSathya Perla rc = hwrm_cfa_decap_filter_alloc(bp, flow, decap_l2_info, 11018c95f773SSathya Perla ref_decap_handle, 11028c95f773SSathya Perla &decap_node->tunnel_handle); 11038c95f773SSathya Perla if (rc) 11048c95f773SSathya Perla goto put_decap_l2; 11058c95f773SSathya Perla 11068c95f773SSathya Perla done: 11078c95f773SSathya Perla *decap_filter_handle = decap_node->tunnel_handle; 11088c95f773SSathya Perla return 0; 11098c95f773SSathya Perla 11108c95f773SSathya Perla put_decap_l2: 11118c95f773SSathya Perla bnxt_tc_put_decap_l2_node(bp, flow_node); 11128c95f773SSathya Perla put_decap: 11138c95f773SSathya Perla bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table, 11148c95f773SSathya Perla &tc_info->decap_ht_params, 11158c95f773SSathya Perla flow_node->decap_node); 11168c95f773SSathya Perla return rc; 11178c95f773SSathya Perla } 11188c95f773SSathya Perla 11198c95f773SSathya Perla static void bnxt_tc_put_encap_handle(struct bnxt *bp, 11208c95f773SSathya Perla struct bnxt_tc_tunnel_node *encap_node) 11218c95f773SSathya Perla { 11228c95f773SSathya Perla __le32 encap_handle = encap_node->tunnel_handle; 1123cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 11248c95f773SSathya Perla int rc; 11258c95f773SSathya Perla 11268c95f773SSathya Perla rc = bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table, 11278c95f773SSathya Perla &tc_info->encap_ht_params, encap_node); 11288c95f773SSathya Perla if (!rc && encap_handle != INVALID_TUNNEL_HANDLE) 11298c95f773SSathya Perla hwrm_cfa_encap_record_free(bp, encap_handle); 11308c95f773SSathya Perla } 11318c95f773SSathya Perla 11328c95f773SSathya Perla /* Lookup the tunnel encap table and check if there's an encap_handle 11338c95f773SSathya Perla * alloc'd already. 11348c95f773SSathya Perla * If not, query L2 info via a route lookup and issue an encap_record_alloc 11358c95f773SSathya Perla * cmd to FW. 11368c95f773SSathya Perla */ 11378c95f773SSathya Perla static int bnxt_tc_get_encap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 11388c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node, 11398c95f773SSathya Perla __le32 *encap_handle) 11408c95f773SSathya Perla { 11418c95f773SSathya Perla struct ip_tunnel_key *encap_key = &flow->actions.tun_encap_key; 1142cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 11438c95f773SSathya Perla struct bnxt_tc_tunnel_node *encap_node; 11448c95f773SSathya Perla int rc; 11458c95f773SSathya Perla 11468c95f773SSathya Perla /* Check if there's another flow using the same tunnel encap. 11478c95f773SSathya Perla * If not, add this tunnel to the table and resolve the other 11488c95f773SSathya Perla * tunnel header fileds 11498c95f773SSathya Perla */ 11508c95f773SSathya Perla encap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->encap_table, 11518c95f773SSathya Perla &tc_info->encap_ht_params, 11528c95f773SSathya Perla encap_key); 11538c95f773SSathya Perla if (!encap_node) 11548c95f773SSathya Perla return -ENOMEM; 11558c95f773SSathya Perla 11568c95f773SSathya Perla flow_node->encap_node = encap_node; 11578c95f773SSathya Perla 11588c95f773SSathya Perla if (encap_node->tunnel_handle != INVALID_TUNNEL_HANDLE) 11598c95f773SSathya Perla goto done; 11608c95f773SSathya Perla 1161e9ecc731SSathya Perla rc = bnxt_tc_resolve_tunnel_hdrs(bp, encap_key, &encap_node->l2_info); 11628c95f773SSathya Perla if (rc) 11638c95f773SSathya Perla goto put_encap; 11648c95f773SSathya Perla 11658c95f773SSathya Perla /* Allocate a new tunnel encap record */ 11668c95f773SSathya Perla rc = hwrm_cfa_encap_record_alloc(bp, encap_key, &encap_node->l2_info, 11678c95f773SSathya Perla &encap_node->tunnel_handle); 11688c95f773SSathya Perla if (rc) 11698c95f773SSathya Perla goto put_encap; 11708c95f773SSathya Perla 11718c95f773SSathya Perla done: 11728c95f773SSathya Perla *encap_handle = encap_node->tunnel_handle; 11738c95f773SSathya Perla return 0; 11748c95f773SSathya Perla 11758c95f773SSathya Perla put_encap: 11768c95f773SSathya Perla bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table, 11778c95f773SSathya Perla &tc_info->encap_ht_params, encap_node); 11788c95f773SSathya Perla return rc; 11798c95f773SSathya Perla } 11808c95f773SSathya Perla 11818c95f773SSathya Perla static void bnxt_tc_put_tunnel_handle(struct bnxt *bp, 11828c95f773SSathya Perla struct bnxt_tc_flow *flow, 11838c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node) 11848c95f773SSathya Perla { 11858c95f773SSathya Perla if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 11868c95f773SSathya Perla bnxt_tc_put_decap_handle(bp, flow_node); 11878c95f773SSathya Perla else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) 11888c95f773SSathya Perla bnxt_tc_put_encap_handle(bp, flow_node->encap_node); 11898c95f773SSathya Perla } 11908c95f773SSathya Perla 11918c95f773SSathya Perla static int bnxt_tc_get_tunnel_handle(struct bnxt *bp, 11928c95f773SSathya Perla struct bnxt_tc_flow *flow, 11938c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node, 11948c95f773SSathya Perla __le32 *tunnel_handle) 11958c95f773SSathya Perla { 11968c95f773SSathya Perla if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 11978c95f773SSathya Perla return bnxt_tc_get_decap_handle(bp, flow, flow_node, 11988c95f773SSathya Perla tunnel_handle); 11998c95f773SSathya Perla else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) 12008c95f773SSathya Perla return bnxt_tc_get_encap_handle(bp, flow, flow_node, 12018c95f773SSathya Perla tunnel_handle); 12028c95f773SSathya Perla else 12038c95f773SSathya Perla return 0; 12048c95f773SSathya Perla } 12052ae7408fSSathya Perla static int __bnxt_tc_del_flow(struct bnxt *bp, 12062ae7408fSSathya Perla struct bnxt_tc_flow_node *flow_node) 12072ae7408fSSathya Perla { 1208cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 12092ae7408fSSathya Perla int rc; 12102ae7408fSSathya Perla 12112ae7408fSSathya Perla /* send HWRM cmd to free the flow-id */ 12122ae7408fSSathya Perla bnxt_hwrm_cfa_flow_free(bp, flow_node->flow_handle); 12132ae7408fSSathya Perla 12142ae7408fSSathya Perla mutex_lock(&tc_info->lock); 12152ae7408fSSathya Perla 12168c95f773SSathya Perla /* release references to any tunnel encap/decap nodes */ 12178c95f773SSathya Perla bnxt_tc_put_tunnel_handle(bp, &flow_node->flow, flow_node); 12188c95f773SSathya Perla 12192ae7408fSSathya Perla /* release reference to l2 node */ 12202ae7408fSSathya Perla bnxt_tc_put_l2_node(bp, flow_node); 12212ae7408fSSathya Perla 12222ae7408fSSathya Perla mutex_unlock(&tc_info->lock); 12232ae7408fSSathya Perla 12242ae7408fSSathya Perla rc = rhashtable_remove_fast(&tc_info->flow_table, &flow_node->node, 12252ae7408fSSathya Perla tc_info->flow_ht_params); 12262ae7408fSSathya Perla if (rc) 12272ae7408fSSathya Perla netdev_err(bp->dev, "Error: %s: rhashtable_remove_fast rc=%d", 12282ae7408fSSathya Perla __func__, rc); 12292ae7408fSSathya Perla 12302ae7408fSSathya Perla kfree_rcu(flow_node, rcu); 12312ae7408fSSathya Perla return 0; 12322ae7408fSSathya Perla } 12332ae7408fSSathya Perla 1234e9ecc731SSathya Perla static void bnxt_tc_set_src_fid(struct bnxt *bp, struct bnxt_tc_flow *flow, 1235e9ecc731SSathya Perla u16 src_fid) 1236e9ecc731SSathya Perla { 1237e9ecc731SSathya Perla if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 1238e9ecc731SSathya Perla flow->src_fid = bp->pf.fw_fid; 1239e9ecc731SSathya Perla else 1240e9ecc731SSathya Perla flow->src_fid = src_fid; 1241e9ecc731SSathya Perla } 1242e9ecc731SSathya Perla 12432ae7408fSSathya Perla /* Add a new flow or replace an existing flow. 12442ae7408fSSathya Perla * Notes on locking: 12452ae7408fSSathya Perla * There are essentially two critical sections here. 12462ae7408fSSathya Perla * 1. while adding a new flow 12472ae7408fSSathya Perla * a) lookup l2-key 12482ae7408fSSathya Perla * b) issue HWRM cmd and get flow_handle 12492ae7408fSSathya Perla * c) link l2-key with flow 12502ae7408fSSathya Perla * 2. while deleting a flow 12512ae7408fSSathya Perla * a) unlinking l2-key from flow 12522ae7408fSSathya Perla * A lock is needed to protect these two critical sections. 12532ae7408fSSathya Perla * 12542ae7408fSSathya Perla * The hash-tables are already protected by the rhashtable API. 12552ae7408fSSathya Perla */ 12562ae7408fSSathya Perla static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid, 12572ae7408fSSathya Perla struct tc_cls_flower_offload *tc_flow_cmd) 12582ae7408fSSathya Perla { 12592ae7408fSSathya Perla struct bnxt_tc_flow_node *new_node, *old_node; 1260cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 12612ae7408fSSathya Perla struct bnxt_tc_flow *flow; 12628c95f773SSathya Perla __le32 tunnel_handle = 0; 12632ae7408fSSathya Perla __le16 ref_flow_handle; 12642ae7408fSSathya Perla int rc; 12652ae7408fSSathya Perla 12662ae7408fSSathya Perla /* allocate memory for the new flow and it's node */ 12672ae7408fSSathya Perla new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); 12682ae7408fSSathya Perla if (!new_node) { 12692ae7408fSSathya Perla rc = -ENOMEM; 12702ae7408fSSathya Perla goto done; 12712ae7408fSSathya Perla } 12722ae7408fSSathya Perla new_node->cookie = tc_flow_cmd->cookie; 12732ae7408fSSathya Perla flow = &new_node->flow; 12742ae7408fSSathya Perla 12752ae7408fSSathya Perla rc = bnxt_tc_parse_flow(bp, tc_flow_cmd, flow); 12762ae7408fSSathya Perla if (rc) 12772ae7408fSSathya Perla goto free_node; 1278e9ecc731SSathya Perla 1279e9ecc731SSathya Perla bnxt_tc_set_src_fid(bp, flow, src_fid); 12802ae7408fSSathya Perla 12812ae7408fSSathya Perla if (!bnxt_tc_can_offload(bp, flow)) { 12822ae7408fSSathya Perla rc = -ENOSPC; 12832ae7408fSSathya Perla goto free_node; 12842ae7408fSSathya Perla } 12852ae7408fSSathya Perla 12862ae7408fSSathya Perla /* If a flow exists with the same cookie, delete it */ 12872ae7408fSSathya Perla old_node = rhashtable_lookup_fast(&tc_info->flow_table, 12882ae7408fSSathya Perla &tc_flow_cmd->cookie, 12892ae7408fSSathya Perla tc_info->flow_ht_params); 12902ae7408fSSathya Perla if (old_node) 12912ae7408fSSathya Perla __bnxt_tc_del_flow(bp, old_node); 12922ae7408fSSathya Perla 12932ae7408fSSathya Perla /* Check if the L2 part of the flow has been offloaded already. 12942ae7408fSSathya Perla * If so, bump up it's refcnt and get it's reference handle. 12952ae7408fSSathya Perla */ 12962ae7408fSSathya Perla mutex_lock(&tc_info->lock); 12972ae7408fSSathya Perla rc = bnxt_tc_get_ref_flow_handle(bp, flow, new_node, &ref_flow_handle); 12982ae7408fSSathya Perla if (rc) 12992ae7408fSSathya Perla goto unlock; 13002ae7408fSSathya Perla 13018c95f773SSathya Perla /* If the flow involves tunnel encap/decap, get tunnel_handle */ 13028c95f773SSathya Perla rc = bnxt_tc_get_tunnel_handle(bp, flow, new_node, &tunnel_handle); 13032ae7408fSSathya Perla if (rc) 13042ae7408fSSathya Perla goto put_l2; 13052ae7408fSSathya Perla 13068c95f773SSathya Perla /* send HWRM cmd to alloc the flow */ 13078c95f773SSathya Perla rc = bnxt_hwrm_cfa_flow_alloc(bp, flow, ref_flow_handle, 13088c95f773SSathya Perla tunnel_handle, &new_node->flow_handle); 13098c95f773SSathya Perla if (rc) 13108c95f773SSathya Perla goto put_tunnel; 13118c95f773SSathya Perla 13125a84acbeSSathya Perla flow->lastused = jiffies; 13135a84acbeSSathya Perla spin_lock_init(&flow->stats_lock); 13142ae7408fSSathya Perla /* add new flow to flow-table */ 13152ae7408fSSathya Perla rc = rhashtable_insert_fast(&tc_info->flow_table, &new_node->node, 13162ae7408fSSathya Perla tc_info->flow_ht_params); 13172ae7408fSSathya Perla if (rc) 13182ae7408fSSathya Perla goto hwrm_flow_free; 13192ae7408fSSathya Perla 13202ae7408fSSathya Perla mutex_unlock(&tc_info->lock); 13212ae7408fSSathya Perla return 0; 13222ae7408fSSathya Perla 13232ae7408fSSathya Perla hwrm_flow_free: 13242ae7408fSSathya Perla bnxt_hwrm_cfa_flow_free(bp, new_node->flow_handle); 13258c95f773SSathya Perla put_tunnel: 13268c95f773SSathya Perla bnxt_tc_put_tunnel_handle(bp, flow, new_node); 13272ae7408fSSathya Perla put_l2: 13282ae7408fSSathya Perla bnxt_tc_put_l2_node(bp, new_node); 13292ae7408fSSathya Perla unlock: 13302ae7408fSSathya Perla mutex_unlock(&tc_info->lock); 13312ae7408fSSathya Perla free_node: 13328c95f773SSathya Perla kfree_rcu(new_node, rcu); 13332ae7408fSSathya Perla done: 13342ae7408fSSathya Perla netdev_err(bp->dev, "Error: %s: cookie=0x%lx error=%d", 13352ae7408fSSathya Perla __func__, tc_flow_cmd->cookie, rc); 13362ae7408fSSathya Perla return rc; 13372ae7408fSSathya Perla } 13382ae7408fSSathya Perla 13392ae7408fSSathya Perla static int bnxt_tc_del_flow(struct bnxt *bp, 13402ae7408fSSathya Perla struct tc_cls_flower_offload *tc_flow_cmd) 13412ae7408fSSathya Perla { 1342cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 13432ae7408fSSathya Perla struct bnxt_tc_flow_node *flow_node; 13442ae7408fSSathya Perla 13452ae7408fSSathya Perla flow_node = rhashtable_lookup_fast(&tc_info->flow_table, 13462ae7408fSSathya Perla &tc_flow_cmd->cookie, 13472ae7408fSSathya Perla tc_info->flow_ht_params); 1348b9ecc340SSriharsha Basavapatna if (!flow_node) 13492ae7408fSSathya Perla return -EINVAL; 13502ae7408fSSathya Perla 13512ae7408fSSathya Perla return __bnxt_tc_del_flow(bp, flow_node); 13522ae7408fSSathya Perla } 13532ae7408fSSathya Perla 13542ae7408fSSathya Perla static int bnxt_tc_get_flow_stats(struct bnxt *bp, 13552ae7408fSSathya Perla struct tc_cls_flower_offload *tc_flow_cmd) 13562ae7408fSSathya Perla { 13575a84acbeSSathya Perla struct bnxt_tc_flow_stats stats, *curr_stats, *prev_stats; 1358cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 1359d7bc7305SSathya Perla struct bnxt_tc_flow_node *flow_node; 13605a84acbeSSathya Perla struct bnxt_tc_flow *flow; 13615a84acbeSSathya Perla unsigned long lastused; 1362d7bc7305SSathya Perla 1363d7bc7305SSathya Perla flow_node = rhashtable_lookup_fast(&tc_info->flow_table, 1364d7bc7305SSathya Perla &tc_flow_cmd->cookie, 1365d7bc7305SSathya Perla tc_info->flow_ht_params); 1366b9ecc340SSriharsha Basavapatna if (!flow_node) 1367d7bc7305SSathya Perla return -1; 1368d7bc7305SSathya Perla 13695a84acbeSSathya Perla flow = &flow_node->flow; 13705a84acbeSSathya Perla curr_stats = &flow->stats; 13715a84acbeSSathya Perla prev_stats = &flow->prev_stats; 13725a84acbeSSathya Perla 13735a84acbeSSathya Perla spin_lock(&flow->stats_lock); 13745a84acbeSSathya Perla stats.packets = curr_stats->packets - prev_stats->packets; 13755a84acbeSSathya Perla stats.bytes = curr_stats->bytes - prev_stats->bytes; 13765a84acbeSSathya Perla *prev_stats = *curr_stats; 13775a84acbeSSathya Perla lastused = flow->lastused; 13785a84acbeSSathya Perla spin_unlock(&flow->stats_lock); 13795a84acbeSSathya Perla 13805a84acbeSSathya Perla tcf_exts_stats_update(tc_flow_cmd->exts, stats.bytes, stats.packets, 13815a84acbeSSathya Perla lastused); 13825a84acbeSSathya Perla return 0; 13835a84acbeSSathya Perla } 13845a84acbeSSathya Perla 13855a84acbeSSathya Perla static int 13865a84acbeSSathya Perla bnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, int num_flows, 13875a84acbeSSathya Perla struct bnxt_tc_stats_batch stats_batch[]) 13885a84acbeSSathya Perla { 13895a84acbeSSathya Perla struct hwrm_cfa_flow_stats_output *resp = bp->hwrm_cmd_resp_addr; 13905a84acbeSSathya Perla struct hwrm_cfa_flow_stats_input req = { 0 }; 13915a84acbeSSathya Perla __le16 *req_flow_handles = &req.flow_handle_0; 13925a84acbeSSathya Perla int rc, i; 13935a84acbeSSathya Perla 13945a84acbeSSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_STATS, -1, -1); 13955a84acbeSSathya Perla req.num_flows = cpu_to_le16(num_flows); 13965a84acbeSSathya Perla for (i = 0; i < num_flows; i++) { 13975a84acbeSSathya Perla struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node; 13985a84acbeSSathya Perla 13995a84acbeSSathya Perla req_flow_handles[i] = flow_node->flow_handle; 14005a84acbeSSathya Perla } 14015a84acbeSSathya Perla 14025a84acbeSSathya Perla mutex_lock(&bp->hwrm_cmd_lock); 14035a84acbeSSathya Perla rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 14045a84acbeSSathya Perla if (!rc) { 14055a84acbeSSathya Perla __le64 *resp_packets = &resp->packet_0; 14065a84acbeSSathya Perla __le64 *resp_bytes = &resp->byte_0; 14075a84acbeSSathya Perla 14085a84acbeSSathya Perla for (i = 0; i < num_flows; i++) { 14095a84acbeSSathya Perla stats_batch[i].hw_stats.packets = 14105a84acbeSSathya Perla le64_to_cpu(resp_packets[i]); 14115a84acbeSSathya Perla stats_batch[i].hw_stats.bytes = 14125a84acbeSSathya Perla le64_to_cpu(resp_bytes[i]); 14135a84acbeSSathya Perla } 14145a84acbeSSathya Perla } else { 14155a84acbeSSathya Perla netdev_info(bp->dev, "error rc=%d", rc); 14165a84acbeSSathya Perla } 14175a84acbeSSathya Perla mutex_unlock(&bp->hwrm_cmd_lock); 14186ae777eaSVenkat Duvvuru 14196ae777eaSVenkat Duvvuru if (rc) 14206ae777eaSVenkat Duvvuru rc = -EIO; 14215a84acbeSSathya Perla return rc; 14225a84acbeSSathya Perla } 14235a84acbeSSathya Perla 14245a84acbeSSathya Perla /* Add val to accum while handling a possible wraparound 14255a84acbeSSathya Perla * of val. Eventhough val is of type u64, its actual width 14265a84acbeSSathya Perla * is denoted by mask and will wrap-around beyond that width. 14275a84acbeSSathya Perla */ 14285a84acbeSSathya Perla static void accumulate_val(u64 *accum, u64 val, u64 mask) 14295a84acbeSSathya Perla { 14305a84acbeSSathya Perla #define low_bits(x, mask) ((x) & (mask)) 14315a84acbeSSathya Perla #define high_bits(x, mask) ((x) & ~(mask)) 14325a84acbeSSathya Perla bool wrapped = val < low_bits(*accum, mask); 14335a84acbeSSathya Perla 14345a84acbeSSathya Perla *accum = high_bits(*accum, mask) + val; 14355a84acbeSSathya Perla if (wrapped) 14365a84acbeSSathya Perla *accum += (mask + 1); 14375a84acbeSSathya Perla } 14385a84acbeSSathya Perla 14395a84acbeSSathya Perla /* The HW counters' width is much less than 64bits. 14405a84acbeSSathya Perla * Handle possible wrap-around while updating the stat counters 14415a84acbeSSathya Perla */ 14425a84acbeSSathya Perla static void bnxt_flow_stats_accum(struct bnxt_tc_info *tc_info, 14435a84acbeSSathya Perla struct bnxt_tc_flow_stats *acc_stats, 14445a84acbeSSathya Perla struct bnxt_tc_flow_stats *hw_stats) 14455a84acbeSSathya Perla { 14465a84acbeSSathya Perla accumulate_val(&acc_stats->bytes, hw_stats->bytes, tc_info->bytes_mask); 14475a84acbeSSathya Perla accumulate_val(&acc_stats->packets, hw_stats->packets, 14485a84acbeSSathya Perla tc_info->packets_mask); 14495a84acbeSSathya Perla } 14505a84acbeSSathya Perla 14515a84acbeSSathya Perla static int 14525a84acbeSSathya Perla bnxt_tc_flow_stats_batch_update(struct bnxt *bp, int num_flows, 14535a84acbeSSathya Perla struct bnxt_tc_stats_batch stats_batch[]) 14545a84acbeSSathya Perla { 1455cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 14565a84acbeSSathya Perla int rc, i; 14575a84acbeSSathya Perla 14585a84acbeSSathya Perla rc = bnxt_hwrm_cfa_flow_stats_get(bp, num_flows, stats_batch); 1459d7bc7305SSathya Perla if (rc) 1460d7bc7305SSathya Perla return rc; 1461d7bc7305SSathya Perla 14625a84acbeSSathya Perla for (i = 0; i < num_flows; i++) { 14635a84acbeSSathya Perla struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node; 14645a84acbeSSathya Perla struct bnxt_tc_flow *flow = &flow_node->flow; 14655a84acbeSSathya Perla 14665a84acbeSSathya Perla spin_lock(&flow->stats_lock); 14675a84acbeSSathya Perla bnxt_flow_stats_accum(tc_info, &flow->stats, 14685a84acbeSSathya Perla &stats_batch[i].hw_stats); 14695a84acbeSSathya Perla if (flow->stats.packets != flow->prev_stats.packets) 14705a84acbeSSathya Perla flow->lastused = jiffies; 14715a84acbeSSathya Perla spin_unlock(&flow->stats_lock); 14725a84acbeSSathya Perla } 14735a84acbeSSathya Perla 14742ae7408fSSathya Perla return 0; 14752ae7408fSSathya Perla } 14762ae7408fSSathya Perla 14775a84acbeSSathya Perla static int 14785a84acbeSSathya Perla bnxt_tc_flow_stats_batch_prep(struct bnxt *bp, 14795a84acbeSSathya Perla struct bnxt_tc_stats_batch stats_batch[], 14805a84acbeSSathya Perla int *num_flows) 14815a84acbeSSathya Perla { 1482cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 14835a84acbeSSathya Perla struct rhashtable_iter *iter = &tc_info->iter; 14845a84acbeSSathya Perla void *flow_node; 14855a84acbeSSathya Perla int rc, i; 14865a84acbeSSathya Perla 148797a6ec4aSTom Herbert rhashtable_walk_start(iter); 14885a84acbeSSathya Perla 14895a84acbeSSathya Perla rc = 0; 14905a84acbeSSathya Perla for (i = 0; i < BNXT_FLOW_STATS_BATCH_MAX; i++) { 14915a84acbeSSathya Perla flow_node = rhashtable_walk_next(iter); 14925a84acbeSSathya Perla if (IS_ERR(flow_node)) { 14935a84acbeSSathya Perla i = 0; 14945a84acbeSSathya Perla if (PTR_ERR(flow_node) == -EAGAIN) { 14955a84acbeSSathya Perla continue; 14965a84acbeSSathya Perla } else { 14975a84acbeSSathya Perla rc = PTR_ERR(flow_node); 14985a84acbeSSathya Perla goto done; 14995a84acbeSSathya Perla } 15005a84acbeSSathya Perla } 15015a84acbeSSathya Perla 15025a84acbeSSathya Perla /* No more flows */ 15035a84acbeSSathya Perla if (!flow_node) 15045a84acbeSSathya Perla goto done; 15055a84acbeSSathya Perla 15065a84acbeSSathya Perla stats_batch[i].flow_node = flow_node; 15075a84acbeSSathya Perla } 15085a84acbeSSathya Perla done: 15095a84acbeSSathya Perla rhashtable_walk_stop(iter); 15105a84acbeSSathya Perla *num_flows = i; 15115a84acbeSSathya Perla return rc; 15125a84acbeSSathya Perla } 15135a84acbeSSathya Perla 15145a84acbeSSathya Perla void bnxt_tc_flow_stats_work(struct bnxt *bp) 15155a84acbeSSathya Perla { 1516cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 15175a84acbeSSathya Perla int num_flows, rc; 15185a84acbeSSathya Perla 15195a84acbeSSathya Perla num_flows = atomic_read(&tc_info->flow_table.nelems); 15205a84acbeSSathya Perla if (!num_flows) 15215a84acbeSSathya Perla return; 15225a84acbeSSathya Perla 15235a84acbeSSathya Perla rhashtable_walk_enter(&tc_info->flow_table, &tc_info->iter); 15245a84acbeSSathya Perla 15255a84acbeSSathya Perla for (;;) { 15265a84acbeSSathya Perla rc = bnxt_tc_flow_stats_batch_prep(bp, tc_info->stats_batch, 15275a84acbeSSathya Perla &num_flows); 15285a84acbeSSathya Perla if (rc) { 15295a84acbeSSathya Perla if (rc == -EAGAIN) 15305a84acbeSSathya Perla continue; 15315a84acbeSSathya Perla break; 15325a84acbeSSathya Perla } 15335a84acbeSSathya Perla 15345a84acbeSSathya Perla if (!num_flows) 15355a84acbeSSathya Perla break; 15365a84acbeSSathya Perla 15375a84acbeSSathya Perla bnxt_tc_flow_stats_batch_update(bp, num_flows, 15385a84acbeSSathya Perla tc_info->stats_batch); 15395a84acbeSSathya Perla } 15405a84acbeSSathya Perla 15415a84acbeSSathya Perla rhashtable_walk_exit(&tc_info->iter); 15425a84acbeSSathya Perla } 15435a84acbeSSathya Perla 15442ae7408fSSathya Perla int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, 15452ae7408fSSathya Perla struct tc_cls_flower_offload *cls_flower) 15462ae7408fSSathya Perla { 15472ae7408fSSathya Perla int rc = 0; 15482ae7408fSSathya Perla 15492ae7408fSSathya Perla switch (cls_flower->command) { 15502ae7408fSSathya Perla case TC_CLSFLOWER_REPLACE: 15512ae7408fSSathya Perla rc = bnxt_tc_add_flow(bp, src_fid, cls_flower); 15522ae7408fSSathya Perla break; 15532ae7408fSSathya Perla 15542ae7408fSSathya Perla case TC_CLSFLOWER_DESTROY: 15552ae7408fSSathya Perla rc = bnxt_tc_del_flow(bp, cls_flower); 15562ae7408fSSathya Perla break; 15572ae7408fSSathya Perla 15582ae7408fSSathya Perla case TC_CLSFLOWER_STATS: 15592ae7408fSSathya Perla rc = bnxt_tc_get_flow_stats(bp, cls_flower); 15602ae7408fSSathya Perla break; 15612ae7408fSSathya Perla } 15622ae7408fSSathya Perla return rc; 15632ae7408fSSathya Perla } 15642ae7408fSSathya Perla 15652ae7408fSSathya Perla static const struct rhashtable_params bnxt_tc_flow_ht_params = { 15662ae7408fSSathya Perla .head_offset = offsetof(struct bnxt_tc_flow_node, node), 15672ae7408fSSathya Perla .key_offset = offsetof(struct bnxt_tc_flow_node, cookie), 15682ae7408fSSathya Perla .key_len = sizeof(((struct bnxt_tc_flow_node *)0)->cookie), 15692ae7408fSSathya Perla .automatic_shrinking = true 15702ae7408fSSathya Perla }; 15712ae7408fSSathya Perla 15722ae7408fSSathya Perla static const struct rhashtable_params bnxt_tc_l2_ht_params = { 15732ae7408fSSathya Perla .head_offset = offsetof(struct bnxt_tc_l2_node, node), 15742ae7408fSSathya Perla .key_offset = offsetof(struct bnxt_tc_l2_node, key), 15752ae7408fSSathya Perla .key_len = BNXT_TC_L2_KEY_LEN, 15762ae7408fSSathya Perla .automatic_shrinking = true 15772ae7408fSSathya Perla }; 15782ae7408fSSathya Perla 15798c95f773SSathya Perla static const struct rhashtable_params bnxt_tc_decap_l2_ht_params = { 15808c95f773SSathya Perla .head_offset = offsetof(struct bnxt_tc_l2_node, node), 15818c95f773SSathya Perla .key_offset = offsetof(struct bnxt_tc_l2_node, key), 15828c95f773SSathya Perla .key_len = BNXT_TC_L2_KEY_LEN, 15838c95f773SSathya Perla .automatic_shrinking = true 15848c95f773SSathya Perla }; 15858c95f773SSathya Perla 15868c95f773SSathya Perla static const struct rhashtable_params bnxt_tc_tunnel_ht_params = { 15878c95f773SSathya Perla .head_offset = offsetof(struct bnxt_tc_tunnel_node, node), 15888c95f773SSathya Perla .key_offset = offsetof(struct bnxt_tc_tunnel_node, key), 15898c95f773SSathya Perla .key_len = sizeof(struct ip_tunnel_key), 15908c95f773SSathya Perla .automatic_shrinking = true 15918c95f773SSathya Perla }; 15928c95f773SSathya Perla 15932ae7408fSSathya Perla /* convert counter width in bits to a mask */ 15942ae7408fSSathya Perla #define mask(width) ((u64)~0 >> (64 - (width))) 15952ae7408fSSathya Perla 15962ae7408fSSathya Perla int bnxt_init_tc(struct bnxt *bp) 15972ae7408fSSathya Perla { 1598cd66358eSSathya Perla struct bnxt_tc_info *tc_info; 15992ae7408fSSathya Perla int rc; 16002ae7408fSSathya Perla 16018c95f773SSathya Perla if (bp->hwrm_spec_code < 0x10803) { 16022ae7408fSSathya Perla netdev_warn(bp->dev, 16032ae7408fSSathya Perla "Firmware does not support TC flower offload.\n"); 16042ae7408fSSathya Perla return -ENOTSUPP; 16052ae7408fSSathya Perla } 1606cd66358eSSathya Perla 1607cd66358eSSathya Perla tc_info = kzalloc(sizeof(*tc_info), GFP_KERNEL); 1608cd66358eSSathya Perla if (!tc_info) 1609cd66358eSSathya Perla return -ENOMEM; 16102ae7408fSSathya Perla mutex_init(&tc_info->lock); 16112ae7408fSSathya Perla 16122ae7408fSSathya Perla /* Counter widths are programmed by FW */ 16132ae7408fSSathya Perla tc_info->bytes_mask = mask(36); 16142ae7408fSSathya Perla tc_info->packets_mask = mask(28); 16152ae7408fSSathya Perla 16162ae7408fSSathya Perla tc_info->flow_ht_params = bnxt_tc_flow_ht_params; 16172ae7408fSSathya Perla rc = rhashtable_init(&tc_info->flow_table, &tc_info->flow_ht_params); 16182ae7408fSSathya Perla if (rc) 1619cd66358eSSathya Perla goto free_tc_info; 16202ae7408fSSathya Perla 16212ae7408fSSathya Perla tc_info->l2_ht_params = bnxt_tc_l2_ht_params; 16222ae7408fSSathya Perla rc = rhashtable_init(&tc_info->l2_table, &tc_info->l2_ht_params); 16232ae7408fSSathya Perla if (rc) 16242ae7408fSSathya Perla goto destroy_flow_table; 16252ae7408fSSathya Perla 16268c95f773SSathya Perla tc_info->decap_l2_ht_params = bnxt_tc_decap_l2_ht_params; 16278c95f773SSathya Perla rc = rhashtable_init(&tc_info->decap_l2_table, 16288c95f773SSathya Perla &tc_info->decap_l2_ht_params); 16298c95f773SSathya Perla if (rc) 16308c95f773SSathya Perla goto destroy_l2_table; 16318c95f773SSathya Perla 16328c95f773SSathya Perla tc_info->decap_ht_params = bnxt_tc_tunnel_ht_params; 16338c95f773SSathya Perla rc = rhashtable_init(&tc_info->decap_table, 16348c95f773SSathya Perla &tc_info->decap_ht_params); 16358c95f773SSathya Perla if (rc) 16368c95f773SSathya Perla goto destroy_decap_l2_table; 16378c95f773SSathya Perla 16388c95f773SSathya Perla tc_info->encap_ht_params = bnxt_tc_tunnel_ht_params; 16398c95f773SSathya Perla rc = rhashtable_init(&tc_info->encap_table, 16408c95f773SSathya Perla &tc_info->encap_ht_params); 16418c95f773SSathya Perla if (rc) 16428c95f773SSathya Perla goto destroy_decap_table; 16438c95f773SSathya Perla 16442ae7408fSSathya Perla tc_info->enabled = true; 16452ae7408fSSathya Perla bp->dev->hw_features |= NETIF_F_HW_TC; 16462ae7408fSSathya Perla bp->dev->features |= NETIF_F_HW_TC; 1647cd66358eSSathya Perla bp->tc_info = tc_info; 16482ae7408fSSathya Perla return 0; 16492ae7408fSSathya Perla 16508c95f773SSathya Perla destroy_decap_table: 16518c95f773SSathya Perla rhashtable_destroy(&tc_info->decap_table); 16528c95f773SSathya Perla destroy_decap_l2_table: 16538c95f773SSathya Perla rhashtable_destroy(&tc_info->decap_l2_table); 16548c95f773SSathya Perla destroy_l2_table: 16558c95f773SSathya Perla rhashtable_destroy(&tc_info->l2_table); 16562ae7408fSSathya Perla destroy_flow_table: 16572ae7408fSSathya Perla rhashtable_destroy(&tc_info->flow_table); 1658cd66358eSSathya Perla free_tc_info: 1659cd66358eSSathya Perla kfree(tc_info); 16602ae7408fSSathya Perla return rc; 16612ae7408fSSathya Perla } 16622ae7408fSSathya Perla 16632ae7408fSSathya Perla void bnxt_shutdown_tc(struct bnxt *bp) 16642ae7408fSSathya Perla { 1665cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 16662ae7408fSSathya Perla 1667cd66358eSSathya Perla if (!bnxt_tc_flower_enabled(bp)) 16682ae7408fSSathya Perla return; 16692ae7408fSSathya Perla 16702ae7408fSSathya Perla rhashtable_destroy(&tc_info->flow_table); 16712ae7408fSSathya Perla rhashtable_destroy(&tc_info->l2_table); 16728c95f773SSathya Perla rhashtable_destroy(&tc_info->decap_l2_table); 16738c95f773SSathya Perla rhashtable_destroy(&tc_info->decap_table); 16748c95f773SSathya Perla rhashtable_destroy(&tc_info->encap_table); 1675cd66358eSSathya Perla kfree(tc_info); 1676cd66358eSSathya Perla bp->tc_info = NULL; 16772ae7408fSSathya Perla } 1678