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> 1990f90624SVenkat Duvvuru #include <net/tc_act/tc_pedit.h> 208c95f773SSathya Perla #include <net/tc_act/tc_tunnel_key.h> 21627c89d0SSriharsha Basavapatna #include <net/vxlan.h> 222ae7408fSSathya Perla 232ae7408fSSathya Perla #include "bnxt_hsi.h" 242ae7408fSSathya Perla #include "bnxt.h" 252ae7408fSSathya Perla #include "bnxt_sriov.h" 262ae7408fSSathya Perla #include "bnxt_tc.h" 272ae7408fSSathya Perla #include "bnxt_vfr.h" 282ae7408fSSathya Perla 292ae7408fSSathya Perla #define BNXT_FID_INVALID 0xffff 302ae7408fSSathya Perla #define VLAN_TCI(vid, prio) ((vid) | ((prio) << VLAN_PRIO_SHIFT)) 312ae7408fSSathya Perla 32e32d4e60SVenkat Duvvuru #define is_vlan_pcp_wildcarded(vlan_tci_mask) \ 33e32d4e60SVenkat Duvvuru ((ntohs(vlan_tci_mask) & VLAN_PRIO_MASK) == 0x0000) 34e32d4e60SVenkat Duvvuru #define is_vlan_pcp_exactmatch(vlan_tci_mask) \ 35e32d4e60SVenkat Duvvuru ((ntohs(vlan_tci_mask) & VLAN_PRIO_MASK) == VLAN_PRIO_MASK) 36e32d4e60SVenkat Duvvuru #define is_vlan_pcp_zero(vlan_tci) \ 37e32d4e60SVenkat Duvvuru ((ntohs(vlan_tci) & VLAN_PRIO_MASK) == 0x0000) 38e32d4e60SVenkat Duvvuru #define is_vid_exactmatch(vlan_tci_mask) \ 39e32d4e60SVenkat Duvvuru ((ntohs(vlan_tci_mask) & VLAN_VID_MASK) == VLAN_VID_MASK) 40e32d4e60SVenkat Duvvuru 4190f90624SVenkat Duvvuru static bool is_wildcard(void *mask, int len); 4290f90624SVenkat Duvvuru static bool is_exactmatch(void *mask, int len); 432ae7408fSSathya Perla /* Return the dst fid of the func for flow forwarding 442ae7408fSSathya Perla * For PFs: src_fid is the fid of the PF 452ae7408fSSathya Perla * For VF-reps: src_fid the fid of the VF 462ae7408fSSathya Perla */ 472ae7408fSSathya Perla static u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev) 482ae7408fSSathya Perla { 492ae7408fSSathya Perla struct bnxt *bp; 502ae7408fSSathya Perla 512ae7408fSSathya Perla /* check if dev belongs to the same switch */ 5252d5254aSFlorian Fainelli if (!netdev_port_same_parent_id(pf_bp->dev, dev)) { 539a005c38SJonathan Lemon netdev_info(pf_bp->dev, "dev(ifindex=%d) not on same switch\n", 542ae7408fSSathya Perla dev->ifindex); 552ae7408fSSathya Perla return BNXT_FID_INVALID; 562ae7408fSSathya Perla } 572ae7408fSSathya Perla 582ae7408fSSathya Perla /* Is dev a VF-rep? */ 59dd4ea1daSSathya Perla if (bnxt_dev_is_vf_rep(dev)) 602ae7408fSSathya Perla return bnxt_vf_rep_get_fid(dev); 612ae7408fSSathya Perla 622ae7408fSSathya Perla bp = netdev_priv(dev); 632ae7408fSSathya Perla return bp->pf.fw_fid; 642ae7408fSSathya Perla } 652ae7408fSSathya Perla 662ae7408fSSathya Perla static int bnxt_tc_parse_redir(struct bnxt *bp, 672ae7408fSSathya Perla struct bnxt_tc_actions *actions, 6873867881SPablo Neira Ayuso const struct flow_action_entry *act) 692ae7408fSSathya Perla { 7073867881SPablo Neira Ayuso struct net_device *dev = act->dev; 712ae7408fSSathya Perla 722ae7408fSSathya Perla if (!dev) { 739a005c38SJonathan Lemon netdev_info(bp->dev, "no dev in mirred action\n"); 742ae7408fSSathya Perla return -EINVAL; 752ae7408fSSathya Perla } 762ae7408fSSathya Perla 772ae7408fSSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_FWD; 782ae7408fSSathya Perla actions->dst_dev = dev; 792ae7408fSSathya Perla return 0; 802ae7408fSSathya Perla } 812ae7408fSSathya Perla 828c6ec361SDavide Caratti static int bnxt_tc_parse_vlan(struct bnxt *bp, 832ae7408fSSathya Perla struct bnxt_tc_actions *actions, 8473867881SPablo Neira Ayuso const struct flow_action_entry *act) 852ae7408fSSathya Perla { 8673867881SPablo Neira Ayuso switch (act->id) { 8773867881SPablo Neira Ayuso case FLOW_ACTION_VLAN_POP: 882ae7408fSSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_POP_VLAN; 898c6ec361SDavide Caratti break; 9073867881SPablo Neira Ayuso case FLOW_ACTION_VLAN_PUSH: 912ae7408fSSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_PUSH_VLAN; 9273867881SPablo Neira Ayuso actions->push_vlan_tci = htons(act->vlan.vid); 9373867881SPablo Neira Ayuso actions->push_vlan_tpid = act->vlan.proto; 948c6ec361SDavide Caratti break; 958c6ec361SDavide Caratti default: 968c6ec361SDavide Caratti return -EOPNOTSUPP; 972ae7408fSSathya Perla } 988c6ec361SDavide Caratti return 0; 992ae7408fSSathya Perla } 1002ae7408fSSathya Perla 1018c95f773SSathya Perla static int bnxt_tc_parse_tunnel_set(struct bnxt *bp, 1028c95f773SSathya Perla struct bnxt_tc_actions *actions, 10373867881SPablo Neira Ayuso const struct flow_action_entry *act) 1048c95f773SSathya Perla { 10573867881SPablo Neira Ayuso const struct ip_tunnel_info *tun_info = act->tunnel; 10673867881SPablo Neira Ayuso const struct ip_tunnel_key *tun_key = &tun_info->key; 1078c95f773SSathya Perla 1088c95f773SSathya Perla if (ip_tunnel_info_af(tun_info) != AF_INET) { 1099a005c38SJonathan Lemon netdev_info(bp->dev, "only IPv4 tunnel-encap is supported\n"); 1108c95f773SSathya Perla return -EOPNOTSUPP; 1118c95f773SSathya Perla } 1128c95f773SSathya Perla 1138c95f773SSathya Perla actions->tun_encap_key = *tun_key; 1148c95f773SSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP; 1158c95f773SSathya Perla return 0; 1168c95f773SSathya Perla } 1178c95f773SSathya Perla 1189b9eb518SSomnath Kotur /* Key & Mask from the stack comes unaligned in multiple iterations of 4 bytes 1199b9eb518SSomnath Kotur * each(u32). 12090f90624SVenkat Duvvuru * This routine consolidates such multiple unaligned values into one 12190f90624SVenkat Duvvuru * field each for Key & Mask (for src and dst macs separately) 12290f90624SVenkat Duvvuru * For example, 12390f90624SVenkat Duvvuru * Mask/Key Offset Iteration 12490f90624SVenkat Duvvuru * ========== ====== ========= 12590f90624SVenkat Duvvuru * dst mac 0xffffffff 0 1 12690f90624SVenkat Duvvuru * dst mac 0x0000ffff 4 2 12790f90624SVenkat Duvvuru * 12890f90624SVenkat Duvvuru * src mac 0xffff0000 4 1 12990f90624SVenkat Duvvuru * src mac 0xffffffff 8 2 13090f90624SVenkat Duvvuru * 13190f90624SVenkat Duvvuru * The above combination coming from the stack will be consolidated as 13290f90624SVenkat Duvvuru * Mask/Key 13390f90624SVenkat Duvvuru * ============== 13490f90624SVenkat Duvvuru * src mac: 0xffffffffffff 13590f90624SVenkat Duvvuru * dst mac: 0xffffffffffff 13690f90624SVenkat Duvvuru */ 13790f90624SVenkat Duvvuru static void bnxt_set_l2_key_mask(u32 part_key, u32 part_mask, 13890f90624SVenkat Duvvuru u8 *actual_key, u8 *actual_mask) 13990f90624SVenkat Duvvuru { 14090f90624SVenkat Duvvuru u32 key = get_unaligned((u32 *)actual_key); 14190f90624SVenkat Duvvuru u32 mask = get_unaligned((u32 *)actual_mask); 14290f90624SVenkat Duvvuru 14390f90624SVenkat Duvvuru part_key &= part_mask; 14490f90624SVenkat Duvvuru part_key |= key & ~part_mask; 14590f90624SVenkat Duvvuru 14690f90624SVenkat Duvvuru put_unaligned(mask | part_mask, (u32 *)actual_mask); 14790f90624SVenkat Duvvuru put_unaligned(part_key, (u32 *)actual_key); 14890f90624SVenkat Duvvuru } 14990f90624SVenkat Duvvuru 15090f90624SVenkat Duvvuru static int 15190f90624SVenkat Duvvuru bnxt_fill_l2_rewrite_fields(struct bnxt_tc_actions *actions, 15290f90624SVenkat Duvvuru u16 *eth_addr, u16 *eth_addr_mask) 15390f90624SVenkat Duvvuru { 15490f90624SVenkat Duvvuru u16 *p; 15590f90624SVenkat Duvvuru int j; 15690f90624SVenkat Duvvuru 15790f90624SVenkat Duvvuru if (unlikely(bnxt_eth_addr_key_mask_invalid(eth_addr, eth_addr_mask))) 15890f90624SVenkat Duvvuru return -EINVAL; 15990f90624SVenkat Duvvuru 16090f90624SVenkat Duvvuru if (!is_wildcard(ð_addr_mask[0], ETH_ALEN)) { 16190f90624SVenkat Duvvuru if (!is_exactmatch(ð_addr_mask[0], ETH_ALEN)) 16290f90624SVenkat Duvvuru return -EINVAL; 16390f90624SVenkat Duvvuru /* FW expects dmac to be in u16 array format */ 16490f90624SVenkat Duvvuru p = eth_addr; 16590f90624SVenkat Duvvuru for (j = 0; j < 3; j++) 16690f90624SVenkat Duvvuru actions->l2_rewrite_dmac[j] = cpu_to_be16(*(p + j)); 16790f90624SVenkat Duvvuru } 16890f90624SVenkat Duvvuru 1693128aad1SVenkat Duvvuru if (!is_wildcard(ð_addr_mask[ETH_ALEN / 2], ETH_ALEN)) { 1703128aad1SVenkat Duvvuru if (!is_exactmatch(ð_addr_mask[ETH_ALEN / 2], ETH_ALEN)) 17190f90624SVenkat Duvvuru return -EINVAL; 17290f90624SVenkat Duvvuru /* FW expects smac to be in u16 array format */ 17390f90624SVenkat Duvvuru p = ð_addr[ETH_ALEN / 2]; 17490f90624SVenkat Duvvuru for (j = 0; j < 3; j++) 17590f90624SVenkat Duvvuru actions->l2_rewrite_smac[j] = cpu_to_be16(*(p + j)); 17690f90624SVenkat Duvvuru } 17790f90624SVenkat Duvvuru 17890f90624SVenkat Duvvuru return 0; 17990f90624SVenkat Duvvuru } 18090f90624SVenkat Duvvuru 18190f90624SVenkat Duvvuru static int 18290f90624SVenkat Duvvuru bnxt_tc_parse_pedit(struct bnxt *bp, struct bnxt_tc_actions *actions, 1839b9eb518SSomnath Kotur struct flow_action_entry *act, int act_idx, u8 *eth_addr, 18490f90624SVenkat Duvvuru u8 *eth_addr_mask) 18590f90624SVenkat Duvvuru { 1869b9eb518SSomnath Kotur size_t offset_of_ip6_daddr = offsetof(struct ipv6hdr, daddr); 1879b9eb518SSomnath Kotur size_t offset_of_ip6_saddr = offsetof(struct ipv6hdr, saddr); 1889b9eb518SSomnath Kotur u32 mask, val, offset, idx; 18990f90624SVenkat Duvvuru u8 htype; 19090f90624SVenkat Duvvuru 19190f90624SVenkat Duvvuru offset = act->mangle.offset; 19290f90624SVenkat Duvvuru htype = act->mangle.htype; 1939b9eb518SSomnath Kotur mask = ~act->mangle.mask; 1949b9eb518SSomnath Kotur val = act->mangle.val; 1959b9eb518SSomnath Kotur 19690f90624SVenkat Duvvuru switch (htype) { 19790f90624SVenkat Duvvuru case FLOW_ACT_MANGLE_HDR_TYPE_ETH: 19890f90624SVenkat Duvvuru if (offset > PEDIT_OFFSET_SMAC_LAST_4_BYTES) { 19990f90624SVenkat Duvvuru netdev_err(bp->dev, 20090f90624SVenkat Duvvuru "%s: eth_hdr: Invalid pedit field\n", 20190f90624SVenkat Duvvuru __func__); 20290f90624SVenkat Duvvuru return -EINVAL; 20390f90624SVenkat Duvvuru } 20490f90624SVenkat Duvvuru actions->flags |= BNXT_TC_ACTION_FLAG_L2_REWRITE; 20590f90624SVenkat Duvvuru 20690f90624SVenkat Duvvuru bnxt_set_l2_key_mask(val, mask, ð_addr[offset], 20790f90624SVenkat Duvvuru ð_addr_mask[offset]); 20890f90624SVenkat Duvvuru break; 2099b9eb518SSomnath Kotur case FLOW_ACT_MANGLE_HDR_TYPE_IP4: 2109b9eb518SSomnath Kotur actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE; 2119b9eb518SSomnath Kotur actions->nat.l3_is_ipv4 = true; 2129b9eb518SSomnath Kotur if (offset == offsetof(struct iphdr, saddr)) { 2139b9eb518SSomnath Kotur actions->nat.src_xlate = true; 2149b9eb518SSomnath Kotur actions->nat.l3.ipv4.saddr.s_addr = htonl(val); 2159b9eb518SSomnath Kotur } else if (offset == offsetof(struct iphdr, daddr)) { 2169b9eb518SSomnath Kotur actions->nat.src_xlate = false; 2179b9eb518SSomnath Kotur actions->nat.l3.ipv4.daddr.s_addr = htonl(val); 2189b9eb518SSomnath Kotur } else { 2199b9eb518SSomnath Kotur netdev_err(bp->dev, 2209b9eb518SSomnath Kotur "%s: IPv4_hdr: Invalid pedit field\n", 2219b9eb518SSomnath Kotur __func__); 2229b9eb518SSomnath Kotur return -EINVAL; 2239b9eb518SSomnath Kotur } 2249b9eb518SSomnath Kotur 2259b9eb518SSomnath Kotur netdev_dbg(bp->dev, "nat.src_xlate = %d src IP: %pI4 dst ip : %pI4\n", 2269b9eb518SSomnath Kotur actions->nat.src_xlate, &actions->nat.l3.ipv4.saddr, 2279b9eb518SSomnath Kotur &actions->nat.l3.ipv4.daddr); 2289b9eb518SSomnath Kotur break; 2299b9eb518SSomnath Kotur 2309b9eb518SSomnath Kotur case FLOW_ACT_MANGLE_HDR_TYPE_IP6: 2319b9eb518SSomnath Kotur actions->flags |= BNXT_TC_ACTION_FLAG_NAT_XLATE; 2329b9eb518SSomnath Kotur actions->nat.l3_is_ipv4 = false; 2339b9eb518SSomnath Kotur if (offset >= offsetof(struct ipv6hdr, saddr) && 2349b9eb518SSomnath Kotur offset < offset_of_ip6_daddr) { 2359b9eb518SSomnath Kotur /* 16 byte IPv6 address comes in 4 iterations of 2369b9eb518SSomnath Kotur * 4byte chunks each 2379b9eb518SSomnath Kotur */ 2389b9eb518SSomnath Kotur actions->nat.src_xlate = true; 2399b9eb518SSomnath Kotur idx = (offset - offset_of_ip6_saddr) / 4; 2409b9eb518SSomnath Kotur /* First 4bytes will be copied to idx 0 and so on */ 2419b9eb518SSomnath Kotur actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val); 2429b9eb518SSomnath Kotur } else if (offset >= offset_of_ip6_daddr && 2439b9eb518SSomnath Kotur offset < offset_of_ip6_daddr + 16) { 2449b9eb518SSomnath Kotur actions->nat.src_xlate = false; 2459b9eb518SSomnath Kotur idx = (offset - offset_of_ip6_daddr) / 4; 2469b9eb518SSomnath Kotur actions->nat.l3.ipv6.saddr.s6_addr32[idx] = htonl(val); 2479b9eb518SSomnath Kotur } else { 2489b9eb518SSomnath Kotur netdev_err(bp->dev, 2499b9eb518SSomnath Kotur "%s: IPv6_hdr: Invalid pedit field\n", 2509b9eb518SSomnath Kotur __func__); 2519b9eb518SSomnath Kotur return -EINVAL; 2529b9eb518SSomnath Kotur } 2539b9eb518SSomnath Kotur break; 2549b9eb518SSomnath Kotur case FLOW_ACT_MANGLE_HDR_TYPE_TCP: 2559b9eb518SSomnath Kotur case FLOW_ACT_MANGLE_HDR_TYPE_UDP: 2569b9eb518SSomnath Kotur /* HW does not support L4 rewrite alone without L3 2579b9eb518SSomnath Kotur * rewrite 2589b9eb518SSomnath Kotur */ 2599b9eb518SSomnath Kotur if (!(actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE)) { 2609b9eb518SSomnath Kotur netdev_err(bp->dev, 2619b9eb518SSomnath Kotur "Need to specify L3 rewrite as well\n"); 2629b9eb518SSomnath Kotur return -EINVAL; 2639b9eb518SSomnath Kotur } 2649b9eb518SSomnath Kotur if (actions->nat.src_xlate) 2659b9eb518SSomnath Kotur actions->nat.l4.ports.sport = htons(val); 2669b9eb518SSomnath Kotur else 2679b9eb518SSomnath Kotur actions->nat.l4.ports.dport = htons(val); 2689b9eb518SSomnath Kotur netdev_dbg(bp->dev, "actions->nat.sport = %d dport = %d\n", 2699b9eb518SSomnath Kotur actions->nat.l4.ports.sport, 2709b9eb518SSomnath Kotur actions->nat.l4.ports.dport); 2719b9eb518SSomnath Kotur break; 27290f90624SVenkat Duvvuru default: 27390f90624SVenkat Duvvuru netdev_err(bp->dev, "%s: Unsupported pedit hdr type\n", 27490f90624SVenkat Duvvuru __func__); 27590f90624SVenkat Duvvuru return -EINVAL; 27690f90624SVenkat Duvvuru } 27790f90624SVenkat Duvvuru return 0; 27890f90624SVenkat Duvvuru } 27990f90624SVenkat Duvvuru 2802ae7408fSSathya Perla static int bnxt_tc_parse_actions(struct bnxt *bp, 2812ae7408fSSathya Perla struct bnxt_tc_actions *actions, 282319a1d19SJiri Pirko struct flow_action *flow_action, 283319a1d19SJiri Pirko struct netlink_ext_ack *extack) 2842ae7408fSSathya Perla { 28590f90624SVenkat Duvvuru /* Used to store the L2 rewrite mask for dmac (6 bytes) followed by 28690f90624SVenkat Duvvuru * smac (6 bytes) if rewrite of both is specified, otherwise either 28790f90624SVenkat Duvvuru * dmac or smac 28890f90624SVenkat Duvvuru */ 28990f90624SVenkat Duvvuru u16 eth_addr_mask[ETH_ALEN] = { 0 }; 29090f90624SVenkat Duvvuru /* Used to store the L2 rewrite key for dmac (6 bytes) followed by 29190f90624SVenkat Duvvuru * smac (6 bytes) if rewrite of both is specified, otherwise either 29290f90624SVenkat Duvvuru * dmac or smac 29390f90624SVenkat Duvvuru */ 29490f90624SVenkat Duvvuru u16 eth_addr[ETH_ALEN] = { 0 }; 29573867881SPablo Neira Ayuso struct flow_action_entry *act; 296244cd96aSCong Wang int i, rc; 2972ae7408fSSathya Perla 29873867881SPablo Neira Ayuso if (!flow_action_has_entries(flow_action)) { 2999a005c38SJonathan Lemon netdev_info(bp->dev, "no actions\n"); 3002ae7408fSSathya Perla return -EINVAL; 3012ae7408fSSathya Perla } 3022ae7408fSSathya Perla 303319a1d19SJiri Pirko if (!flow_action_basic_hw_stats_types_check(flow_action, extack)) 304319a1d19SJiri Pirko return -EOPNOTSUPP; 305319a1d19SJiri Pirko 30673867881SPablo Neira Ayuso flow_action_for_each(i, act, flow_action) { 30773867881SPablo Neira Ayuso switch (act->id) { 30873867881SPablo Neira Ayuso case FLOW_ACTION_DROP: 3092ae7408fSSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_DROP; 3102ae7408fSSathya Perla return 0; /* don't bother with other actions */ 31173867881SPablo Neira Ayuso case FLOW_ACTION_REDIRECT: 31273867881SPablo Neira Ayuso rc = bnxt_tc_parse_redir(bp, actions, act); 3132ae7408fSSathya Perla if (rc) 3142ae7408fSSathya Perla return rc; 31573867881SPablo Neira Ayuso break; 31673867881SPablo Neira Ayuso case FLOW_ACTION_VLAN_POP: 31773867881SPablo Neira Ayuso case FLOW_ACTION_VLAN_PUSH: 31873867881SPablo Neira Ayuso case FLOW_ACTION_VLAN_MANGLE: 31973867881SPablo Neira Ayuso rc = bnxt_tc_parse_vlan(bp, actions, act); 3208c6ec361SDavide Caratti if (rc) 3218c6ec361SDavide Caratti return rc; 32273867881SPablo Neira Ayuso break; 32373867881SPablo Neira Ayuso case FLOW_ACTION_TUNNEL_ENCAP: 32473867881SPablo Neira Ayuso rc = bnxt_tc_parse_tunnel_set(bp, actions, act); 3258c95f773SSathya Perla if (rc) 3268c95f773SSathya Perla return rc; 32773867881SPablo Neira Ayuso break; 32873867881SPablo Neira Ayuso case FLOW_ACTION_TUNNEL_DECAP: 3298c95f773SSathya Perla actions->flags |= BNXT_TC_ACTION_FLAG_TUNNEL_DECAP; 33073867881SPablo Neira Ayuso break; 33190f90624SVenkat Duvvuru /* Packet edit: L2 rewrite, NAT, NAPT */ 33290f90624SVenkat Duvvuru case FLOW_ACTION_MANGLE: 3339b9eb518SSomnath Kotur rc = bnxt_tc_parse_pedit(bp, actions, act, i, 33490f90624SVenkat Duvvuru (u8 *)eth_addr, 33590f90624SVenkat Duvvuru (u8 *)eth_addr_mask); 33690f90624SVenkat Duvvuru if (rc) 33790f90624SVenkat Duvvuru return rc; 33890f90624SVenkat Duvvuru break; 33973867881SPablo Neira Ayuso default: 34073867881SPablo Neira Ayuso break; 3418c95f773SSathya Perla } 3428c95f773SSathya Perla } 3438c95f773SSathya Perla 34490f90624SVenkat Duvvuru if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) { 34590f90624SVenkat Duvvuru rc = bnxt_fill_l2_rewrite_fields(actions, eth_addr, 34690f90624SVenkat Duvvuru eth_addr_mask); 34790f90624SVenkat Duvvuru if (rc) 34890f90624SVenkat Duvvuru return rc; 34990f90624SVenkat Duvvuru } 35090f90624SVenkat Duvvuru 351e9ecc731SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { 352e9ecc731SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) { 353e9ecc731SSathya Perla /* dst_fid is PF's fid */ 354e9ecc731SSathya Perla actions->dst_fid = bp->pf.fw_fid; 355e9ecc731SSathya Perla } else { 356e9ecc731SSathya Perla /* find the FID from dst_dev */ 357e9ecc731SSathya Perla actions->dst_fid = 358e9ecc731SSathya Perla bnxt_flow_get_dst_fid(bp, actions->dst_dev); 359e9ecc731SSathya Perla if (actions->dst_fid == BNXT_FID_INVALID) 3608c95f773SSathya Perla return -EINVAL; 3618c95f773SSathya Perla } 362e9ecc731SSathya Perla } 3638c95f773SSathya Perla 36492425c40SDan Carpenter return 0; 3652ae7408fSSathya Perla } 3662ae7408fSSathya Perla 3672ae7408fSSathya Perla static int bnxt_tc_parse_flow(struct bnxt *bp, 368f9e30088SPablo Neira Ayuso struct flow_cls_offload *tc_flow_cmd, 3692ae7408fSSathya Perla struct bnxt_tc_flow *flow) 3702ae7408fSSathya Perla { 371f9e30088SPablo Neira Ayuso struct flow_rule *rule = flow_cls_offload_flow_rule(tc_flow_cmd); 3728f256622SPablo Neira Ayuso struct flow_dissector *dissector = rule->match.dissector; 3732ae7408fSSathya Perla 3742ae7408fSSathya Perla /* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */ 3752ae7408fSSathya Perla if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 || 3762ae7408fSSathya Perla (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC)) == 0) { 3779a005c38SJonathan Lemon netdev_info(bp->dev, "cannot form TC key: used_keys = 0x%x\n", 3782ae7408fSSathya Perla dissector->used_keys); 3792ae7408fSSathya Perla return -EOPNOTSUPP; 3802ae7408fSSathya Perla } 3812ae7408fSSathya Perla 3828f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { 3838f256622SPablo Neira Ayuso struct flow_match_basic match; 3842ae7408fSSathya Perla 3858f256622SPablo Neira Ayuso flow_rule_match_basic(rule, &match); 3868f256622SPablo Neira Ayuso flow->l2_key.ether_type = match.key->n_proto; 3878f256622SPablo Neira Ayuso flow->l2_mask.ether_type = match.mask->n_proto; 3882ae7408fSSathya Perla 3898f256622SPablo Neira Ayuso if (match.key->n_proto == htons(ETH_P_IP) || 3908f256622SPablo Neira Ayuso match.key->n_proto == htons(ETH_P_IPV6)) { 3918f256622SPablo Neira Ayuso flow->l4_key.ip_proto = match.key->ip_proto; 3928f256622SPablo Neira Ayuso flow->l4_mask.ip_proto = match.mask->ip_proto; 3932ae7408fSSathya Perla } 3942ae7408fSSathya Perla } 3952ae7408fSSathya Perla 3968f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { 3978f256622SPablo Neira Ayuso struct flow_match_eth_addrs match; 3982ae7408fSSathya Perla 3998f256622SPablo Neira Ayuso flow_rule_match_eth_addrs(rule, &match); 4002ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_ETH_ADDRS; 4018f256622SPablo Neira Ayuso ether_addr_copy(flow->l2_key.dmac, match.key->dst); 4028f256622SPablo Neira Ayuso ether_addr_copy(flow->l2_mask.dmac, match.mask->dst); 4038f256622SPablo Neira Ayuso ether_addr_copy(flow->l2_key.smac, match.key->src); 4048f256622SPablo Neira Ayuso ether_addr_copy(flow->l2_mask.smac, match.mask->src); 4052ae7408fSSathya Perla } 4062ae7408fSSathya Perla 4078f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) { 4088f256622SPablo Neira Ayuso struct flow_match_vlan match; 4092ae7408fSSathya Perla 4108f256622SPablo Neira Ayuso flow_rule_match_vlan(rule, &match); 4112ae7408fSSathya Perla flow->l2_key.inner_vlan_tci = 4128f256622SPablo Neira Ayuso cpu_to_be16(VLAN_TCI(match.key->vlan_id, 4138f256622SPablo Neira Ayuso match.key->vlan_priority)); 4142ae7408fSSathya Perla flow->l2_mask.inner_vlan_tci = 4158f256622SPablo Neira Ayuso cpu_to_be16((VLAN_TCI(match.mask->vlan_id, 4168f256622SPablo Neira Ayuso match.mask->vlan_priority))); 4172ae7408fSSathya Perla flow->l2_key.inner_vlan_tpid = htons(ETH_P_8021Q); 4182ae7408fSSathya Perla flow->l2_mask.inner_vlan_tpid = htons(0xffff); 4192ae7408fSSathya Perla flow->l2_key.num_vlans = 1; 4202ae7408fSSathya Perla } 4212ae7408fSSathya Perla 4228f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { 4238f256622SPablo Neira Ayuso struct flow_match_ipv4_addrs match; 4242ae7408fSSathya Perla 4258f256622SPablo Neira Ayuso flow_rule_match_ipv4_addrs(rule, &match); 4262ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_IPV4_ADDRS; 4278f256622SPablo Neira Ayuso flow->l3_key.ipv4.daddr.s_addr = match.key->dst; 4288f256622SPablo Neira Ayuso flow->l3_mask.ipv4.daddr.s_addr = match.mask->dst; 4298f256622SPablo Neira Ayuso flow->l3_key.ipv4.saddr.s_addr = match.key->src; 4308f256622SPablo Neira Ayuso flow->l3_mask.ipv4.saddr.s_addr = match.mask->src; 4318f256622SPablo Neira Ayuso } else if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { 4328f256622SPablo Neira Ayuso struct flow_match_ipv6_addrs match; 4332ae7408fSSathya Perla 4348f256622SPablo Neira Ayuso flow_rule_match_ipv6_addrs(rule, &match); 4352ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_IPV6_ADDRS; 4368f256622SPablo Neira Ayuso flow->l3_key.ipv6.daddr = match.key->dst; 4378f256622SPablo Neira Ayuso flow->l3_mask.ipv6.daddr = match.mask->dst; 4388f256622SPablo Neira Ayuso flow->l3_key.ipv6.saddr = match.key->src; 4398f256622SPablo Neira Ayuso flow->l3_mask.ipv6.saddr = match.mask->src; 4402ae7408fSSathya Perla } 4412ae7408fSSathya Perla 4428f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { 4438f256622SPablo Neira Ayuso struct flow_match_ports match; 4442ae7408fSSathya Perla 4458f256622SPablo Neira Ayuso flow_rule_match_ports(rule, &match); 4462ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_PORTS; 4478f256622SPablo Neira Ayuso flow->l4_key.ports.dport = match.key->dst; 4488f256622SPablo Neira Ayuso flow->l4_mask.ports.dport = match.mask->dst; 4498f256622SPablo Neira Ayuso flow->l4_key.ports.sport = match.key->src; 4508f256622SPablo Neira Ayuso flow->l4_mask.ports.sport = match.mask->src; 4512ae7408fSSathya Perla } 4522ae7408fSSathya Perla 4538f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ICMP)) { 4548f256622SPablo Neira Ayuso struct flow_match_icmp match; 4552ae7408fSSathya Perla 4568f256622SPablo Neira Ayuso flow_rule_match_icmp(rule, &match); 4572ae7408fSSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_ICMP; 4588f256622SPablo Neira Ayuso flow->l4_key.icmp.type = match.key->type; 4598f256622SPablo Neira Ayuso flow->l4_key.icmp.code = match.key->code; 4608f256622SPablo Neira Ayuso flow->l4_mask.icmp.type = match.mask->type; 4618f256622SPablo Neira Ayuso flow->l4_mask.icmp.code = match.mask->code; 4622ae7408fSSathya Perla } 4632ae7408fSSathya Perla 4648f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS)) { 4658f256622SPablo Neira Ayuso struct flow_match_ipv4_addrs match; 4668c95f773SSathya Perla 4678f256622SPablo Neira Ayuso flow_rule_match_enc_ipv4_addrs(rule, &match); 4688c95f773SSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS; 4698f256622SPablo Neira Ayuso flow->tun_key.u.ipv4.dst = match.key->dst; 4708f256622SPablo Neira Ayuso flow->tun_mask.u.ipv4.dst = match.mask->dst; 4718f256622SPablo Neira Ayuso flow->tun_key.u.ipv4.src = match.key->src; 4728f256622SPablo Neira Ayuso flow->tun_mask.u.ipv4.src = match.mask->src; 4738f256622SPablo Neira Ayuso } else if (flow_rule_match_key(rule, 4748c95f773SSathya Perla FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS)) { 4758c95f773SSathya Perla return -EOPNOTSUPP; 4768c95f773SSathya Perla } 4778c95f773SSathya Perla 4788f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID)) { 4798f256622SPablo Neira Ayuso struct flow_match_enc_keyid match; 4808c95f773SSathya Perla 4818f256622SPablo Neira Ayuso flow_rule_match_enc_keyid(rule, &match); 4828c95f773SSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ID; 4838f256622SPablo Neira Ayuso flow->tun_key.tun_id = key32_to_tunnel_id(match.key->keyid); 4848f256622SPablo Neira Ayuso flow->tun_mask.tun_id = key32_to_tunnel_id(match.mask->keyid); 4858c95f773SSathya Perla } 4868c95f773SSathya Perla 4878f256622SPablo Neira Ayuso if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_PORTS)) { 4888f256622SPablo Neira Ayuso struct flow_match_ports match; 4898c95f773SSathya Perla 4908f256622SPablo Neira Ayuso flow_rule_match_enc_ports(rule, &match); 4918c95f773SSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_PORTS; 4928f256622SPablo Neira Ayuso flow->tun_key.tp_dst = match.key->dst; 4938f256622SPablo Neira Ayuso flow->tun_mask.tp_dst = match.mask->dst; 4948f256622SPablo Neira Ayuso flow->tun_key.tp_src = match.key->src; 4958f256622SPablo Neira Ayuso flow->tun_mask.tp_src = match.mask->src; 4968c95f773SSathya Perla } 4978c95f773SSathya Perla 498319a1d19SJiri Pirko return bnxt_tc_parse_actions(bp, &flow->actions, &rule->action, 499319a1d19SJiri Pirko tc_flow_cmd->common.extack); 5002ae7408fSSathya Perla } 5012ae7408fSSathya Perla 502abd43a13SVenkat Duvvuru static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp, 503abd43a13SVenkat Duvvuru struct bnxt_tc_flow_node *flow_node) 5042ae7408fSSathya Perla { 505db1d36a2SSathya Perla struct hwrm_cfa_flow_free_input req = { 0 }; 506db1d36a2SSathya Perla int rc; 507db1d36a2SSathya Perla 508db1d36a2SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_FREE, -1, -1); 509abd43a13SVenkat Duvvuru if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) 510abd43a13SVenkat Duvvuru req.ext_flow_handle = flow_node->ext_flow_handle; 511abd43a13SVenkat Duvvuru else 512abd43a13SVenkat Duvvuru req.flow_handle = flow_node->flow_handle; 513db1d36a2SSathya Perla 514db1d36a2SSathya Perla rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 515db1d36a2SSathya Perla if (rc) 5169a005c38SJonathan Lemon netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 5176ae777eaSVenkat Duvvuru 518db1d36a2SSathya Perla return rc; 519db1d36a2SSathya Perla } 520db1d36a2SSathya Perla 521db1d36a2SSathya Perla static int ipv6_mask_len(struct in6_addr *mask) 522db1d36a2SSathya Perla { 523db1d36a2SSathya Perla int mask_len = 0, i; 524db1d36a2SSathya Perla 525db1d36a2SSathya Perla for (i = 0; i < 4; i++) 526db1d36a2SSathya Perla mask_len += inet_mask_len(mask->s6_addr32[i]); 527db1d36a2SSathya Perla 528db1d36a2SSathya Perla return mask_len; 529db1d36a2SSathya Perla } 530db1d36a2SSathya Perla 531db1d36a2SSathya Perla static bool is_wildcard(void *mask, int len) 532db1d36a2SSathya Perla { 533db1d36a2SSathya Perla const u8 *p = mask; 534db1d36a2SSathya Perla int i; 535db1d36a2SSathya Perla 536db1d36a2SSathya Perla for (i = 0; i < len; i++) { 537db1d36a2SSathya Perla if (p[i] != 0) 538db1d36a2SSathya Perla return false; 539db1d36a2SSathya Perla } 540db1d36a2SSathya Perla return true; 5412ae7408fSSathya Perla } 5422ae7408fSSathya Perla 543e85a9be9SAndy Gospodarek static bool is_exactmatch(void *mask, int len) 544e85a9be9SAndy Gospodarek { 545e85a9be9SAndy Gospodarek const u8 *p = mask; 546e85a9be9SAndy Gospodarek int i; 547e85a9be9SAndy Gospodarek 548e85a9be9SAndy Gospodarek for (i = 0; i < len; i++) 549e85a9be9SAndy Gospodarek if (p[i] != 0xff) 550e85a9be9SAndy Gospodarek return false; 551e85a9be9SAndy Gospodarek 552e85a9be9SAndy Gospodarek return true; 553e85a9be9SAndy Gospodarek } 554e85a9be9SAndy Gospodarek 555e32d4e60SVenkat Duvvuru static bool is_vlan_tci_allowed(__be16 vlan_tci_mask, 556e32d4e60SVenkat Duvvuru __be16 vlan_tci) 557e32d4e60SVenkat Duvvuru { 558e32d4e60SVenkat Duvvuru /* VLAN priority must be either exactly zero or fully wildcarded and 559e32d4e60SVenkat Duvvuru * VLAN id must be exact match. 560e32d4e60SVenkat Duvvuru */ 561e32d4e60SVenkat Duvvuru if (is_vid_exactmatch(vlan_tci_mask) && 562e32d4e60SVenkat Duvvuru ((is_vlan_pcp_exactmatch(vlan_tci_mask) && 563e32d4e60SVenkat Duvvuru is_vlan_pcp_zero(vlan_tci)) || 564e32d4e60SVenkat Duvvuru is_vlan_pcp_wildcarded(vlan_tci_mask))) 565e32d4e60SVenkat Duvvuru return true; 566e32d4e60SVenkat Duvvuru 567e32d4e60SVenkat Duvvuru return false; 568e32d4e60SVenkat Duvvuru } 569e32d4e60SVenkat Duvvuru 570e85a9be9SAndy Gospodarek static bool bits_set(void *key, int len) 571e85a9be9SAndy Gospodarek { 572e85a9be9SAndy Gospodarek const u8 *p = key; 573e85a9be9SAndy Gospodarek int i; 574e85a9be9SAndy Gospodarek 575e85a9be9SAndy Gospodarek for (i = 0; i < len; i++) 576e85a9be9SAndy Gospodarek if (p[i] != 0) 577e85a9be9SAndy Gospodarek return true; 578e85a9be9SAndy Gospodarek 579e85a9be9SAndy Gospodarek return false; 580e85a9be9SAndy Gospodarek } 581e85a9be9SAndy Gospodarek 5822ae7408fSSathya Perla static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow, 5838c95f773SSathya Perla __le16 ref_flow_handle, 584abd43a13SVenkat Duvvuru __le32 tunnel_handle, 585abd43a13SVenkat Duvvuru struct bnxt_tc_flow_node *flow_node) 5862ae7408fSSathya Perla { 587db1d36a2SSathya Perla struct bnxt_tc_actions *actions = &flow->actions; 588db1d36a2SSathya Perla struct bnxt_tc_l3_key *l3_mask = &flow->l3_mask; 589db1d36a2SSathya Perla struct bnxt_tc_l3_key *l3_key = &flow->l3_key; 590db1d36a2SSathya Perla struct hwrm_cfa_flow_alloc_input req = { 0 }; 5915c209fc8SVenkat Duvvuru struct hwrm_cfa_flow_alloc_output *resp; 592db1d36a2SSathya Perla u16 flow_flags = 0, action_flags = 0; 593db1d36a2SSathya Perla int rc; 594db1d36a2SSathya Perla 595db1d36a2SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_ALLOC, -1, -1); 596db1d36a2SSathya Perla 597db1d36a2SSathya Perla req.src_fid = cpu_to_le16(flow->src_fid); 598db1d36a2SSathya Perla req.ref_flow_handle = ref_flow_handle; 5998c95f773SSathya Perla 60090f90624SVenkat Duvvuru if (actions->flags & BNXT_TC_ACTION_FLAG_L2_REWRITE) { 60190f90624SVenkat Duvvuru memcpy(req.l2_rewrite_dmac, actions->l2_rewrite_dmac, 60290f90624SVenkat Duvvuru ETH_ALEN); 60390f90624SVenkat Duvvuru memcpy(req.l2_rewrite_smac, actions->l2_rewrite_smac, 60490f90624SVenkat Duvvuru ETH_ALEN); 60590f90624SVenkat Duvvuru action_flags |= 60690f90624SVenkat Duvvuru CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; 60790f90624SVenkat Duvvuru } 60890f90624SVenkat Duvvuru 6099b9eb518SSomnath Kotur if (actions->flags & BNXT_TC_ACTION_FLAG_NAT_XLATE) { 6109b9eb518SSomnath Kotur if (actions->nat.l3_is_ipv4) { 6119b9eb518SSomnath Kotur action_flags |= 6129b9eb518SSomnath Kotur CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS; 6139b9eb518SSomnath Kotur 6149b9eb518SSomnath Kotur if (actions->nat.src_xlate) { 6159b9eb518SSomnath Kotur action_flags |= 6169b9eb518SSomnath Kotur CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC; 6179b9eb518SSomnath Kotur /* L3 source rewrite */ 6189b9eb518SSomnath Kotur req.nat_ip_address[0] = 6199b9eb518SSomnath Kotur actions->nat.l3.ipv4.saddr.s_addr; 6209b9eb518SSomnath Kotur /* L4 source port */ 6219b9eb518SSomnath Kotur if (actions->nat.l4.ports.sport) 6229b9eb518SSomnath Kotur req.nat_port = 6239b9eb518SSomnath Kotur actions->nat.l4.ports.sport; 6249b9eb518SSomnath Kotur } else { 6259b9eb518SSomnath Kotur action_flags |= 6269b9eb518SSomnath Kotur CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST; 6279b9eb518SSomnath Kotur /* L3 destination rewrite */ 6289b9eb518SSomnath Kotur req.nat_ip_address[0] = 6299b9eb518SSomnath Kotur actions->nat.l3.ipv4.daddr.s_addr; 6309b9eb518SSomnath Kotur /* L4 destination port */ 6319b9eb518SSomnath Kotur if (actions->nat.l4.ports.dport) 6329b9eb518SSomnath Kotur req.nat_port = 6339b9eb518SSomnath Kotur actions->nat.l4.ports.dport; 6349b9eb518SSomnath Kotur } 6359b9eb518SSomnath Kotur netdev_dbg(bp->dev, 6369b9eb518SSomnath Kotur "req.nat_ip_address: %pI4 src_xlate: %d req.nat_port: %x\n", 6379b9eb518SSomnath Kotur req.nat_ip_address, actions->nat.src_xlate, 6389b9eb518SSomnath Kotur req.nat_port); 6399b9eb518SSomnath Kotur } else { 6409b9eb518SSomnath Kotur if (actions->nat.src_xlate) { 6419b9eb518SSomnath Kotur action_flags |= 6429b9eb518SSomnath Kotur CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC; 6439b9eb518SSomnath Kotur /* L3 source rewrite */ 6449b9eb518SSomnath Kotur memcpy(req.nat_ip_address, 6459b9eb518SSomnath Kotur actions->nat.l3.ipv6.saddr.s6_addr32, 6469b9eb518SSomnath Kotur sizeof(req.nat_ip_address)); 6479b9eb518SSomnath Kotur /* L4 source port */ 6489b9eb518SSomnath Kotur if (actions->nat.l4.ports.sport) 6499b9eb518SSomnath Kotur req.nat_port = 6509b9eb518SSomnath Kotur actions->nat.l4.ports.sport; 6519b9eb518SSomnath Kotur } else { 6529b9eb518SSomnath Kotur action_flags |= 6539b9eb518SSomnath Kotur CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST; 6549b9eb518SSomnath Kotur /* L3 destination rewrite */ 6559b9eb518SSomnath Kotur memcpy(req.nat_ip_address, 6569b9eb518SSomnath Kotur actions->nat.l3.ipv6.daddr.s6_addr32, 6579b9eb518SSomnath Kotur sizeof(req.nat_ip_address)); 6589b9eb518SSomnath Kotur /* L4 destination port */ 6599b9eb518SSomnath Kotur if (actions->nat.l4.ports.dport) 6609b9eb518SSomnath Kotur req.nat_port = 6619b9eb518SSomnath Kotur actions->nat.l4.ports.dport; 6629b9eb518SSomnath Kotur } 6639b9eb518SSomnath Kotur netdev_dbg(bp->dev, 6649b9eb518SSomnath Kotur "req.nat_ip_address: %pI6 src_xlate: %d req.nat_port: %x\n", 6659b9eb518SSomnath Kotur req.nat_ip_address, actions->nat.src_xlate, 6669b9eb518SSomnath Kotur req.nat_port); 6679b9eb518SSomnath Kotur } 6689b9eb518SSomnath Kotur } 6699b9eb518SSomnath Kotur 6708c95f773SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP || 6718c95f773SSathya Perla actions->flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) { 6728c95f773SSathya Perla req.tunnel_handle = tunnel_handle; 6738c95f773SSathya Perla flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_TUNNEL; 6748c95f773SSathya Perla action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL; 6758c95f773SSathya Perla } 6768c95f773SSathya Perla 677db1d36a2SSathya Perla req.ethertype = flow->l2_key.ether_type; 678db1d36a2SSathya Perla req.ip_proto = flow->l4_key.ip_proto; 679db1d36a2SSathya Perla 680db1d36a2SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_ETH_ADDRS) { 681db1d36a2SSathya Perla memcpy(req.dmac, flow->l2_key.dmac, ETH_ALEN); 682db1d36a2SSathya Perla memcpy(req.smac, flow->l2_key.smac, ETH_ALEN); 683db1d36a2SSathya Perla } 684db1d36a2SSathya Perla 685db1d36a2SSathya Perla if (flow->l2_key.num_vlans > 0) { 686db1d36a2SSathya Perla flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE; 687db1d36a2SSathya Perla /* FW expects the inner_vlan_tci value to be set 688db1d36a2SSathya Perla * in outer_vlan_tci when num_vlans is 1 (which is 689db1d36a2SSathya Perla * always the case in TC.) 690db1d36a2SSathya Perla */ 691db1d36a2SSathya Perla req.outer_vlan_tci = flow->l2_key.inner_vlan_tci; 692db1d36a2SSathya Perla } 693db1d36a2SSathya Perla 694db1d36a2SSathya Perla /* If all IP and L4 fields are wildcarded then this is an L2 flow */ 6957deea450SSunil Challa if (is_wildcard(l3_mask, sizeof(*l3_mask)) && 696db1d36a2SSathya Perla is_wildcard(&flow->l4_mask, sizeof(flow->l4_mask))) { 697db1d36a2SSathya Perla flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2; 698db1d36a2SSathya Perla } else { 699db1d36a2SSathya Perla flow_flags |= flow->l2_key.ether_type == htons(ETH_P_IP) ? 700db1d36a2SSathya Perla CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 : 701db1d36a2SSathya Perla CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6; 702db1d36a2SSathya Perla 703db1d36a2SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV4_ADDRS) { 704db1d36a2SSathya Perla req.ip_dst[0] = l3_key->ipv4.daddr.s_addr; 705db1d36a2SSathya Perla req.ip_dst_mask_len = 706db1d36a2SSathya Perla inet_mask_len(l3_mask->ipv4.daddr.s_addr); 707db1d36a2SSathya Perla req.ip_src[0] = l3_key->ipv4.saddr.s_addr; 708db1d36a2SSathya Perla req.ip_src_mask_len = 709db1d36a2SSathya Perla inet_mask_len(l3_mask->ipv4.saddr.s_addr); 710db1d36a2SSathya Perla } else if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV6_ADDRS) { 711db1d36a2SSathya Perla memcpy(req.ip_dst, l3_key->ipv6.daddr.s6_addr32, 712db1d36a2SSathya Perla sizeof(req.ip_dst)); 713db1d36a2SSathya Perla req.ip_dst_mask_len = 714db1d36a2SSathya Perla ipv6_mask_len(&l3_mask->ipv6.daddr); 715db1d36a2SSathya Perla memcpy(req.ip_src, l3_key->ipv6.saddr.s6_addr32, 716db1d36a2SSathya Perla sizeof(req.ip_src)); 717db1d36a2SSathya Perla req.ip_src_mask_len = 718db1d36a2SSathya Perla ipv6_mask_len(&l3_mask->ipv6.saddr); 719db1d36a2SSathya Perla } 720db1d36a2SSathya Perla } 721db1d36a2SSathya Perla 722db1d36a2SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) { 723db1d36a2SSathya Perla req.l4_src_port = flow->l4_key.ports.sport; 724db1d36a2SSathya Perla req.l4_src_port_mask = flow->l4_mask.ports.sport; 725db1d36a2SSathya Perla req.l4_dst_port = flow->l4_key.ports.dport; 726db1d36a2SSathya Perla req.l4_dst_port_mask = flow->l4_mask.ports.dport; 727db1d36a2SSathya Perla } else if (flow->flags & BNXT_TC_FLOW_FLAGS_ICMP) { 728db1d36a2SSathya Perla /* l4 ports serve as type/code when ip_proto is ICMP */ 729db1d36a2SSathya Perla req.l4_src_port = htons(flow->l4_key.icmp.type); 730db1d36a2SSathya Perla req.l4_src_port_mask = htons(flow->l4_mask.icmp.type); 731db1d36a2SSathya Perla req.l4_dst_port = htons(flow->l4_key.icmp.code); 732db1d36a2SSathya Perla req.l4_dst_port_mask = htons(flow->l4_mask.icmp.code); 733db1d36a2SSathya Perla } 734db1d36a2SSathya Perla req.flags = cpu_to_le16(flow_flags); 735db1d36a2SSathya Perla 736db1d36a2SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_DROP) { 737db1d36a2SSathya Perla action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP; 738db1d36a2SSathya Perla } else { 739db1d36a2SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { 740db1d36a2SSathya Perla action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD; 741db1d36a2SSathya Perla req.dst_fid = cpu_to_le16(actions->dst_fid); 742db1d36a2SSathya Perla } 743db1d36a2SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_PUSH_VLAN) { 744db1d36a2SSathya Perla action_flags |= 745db1d36a2SSathya Perla CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; 746db1d36a2SSathya Perla req.l2_rewrite_vlan_tpid = actions->push_vlan_tpid; 747db1d36a2SSathya Perla req.l2_rewrite_vlan_tci = actions->push_vlan_tci; 748db1d36a2SSathya Perla memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); 749db1d36a2SSathya Perla memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); 750db1d36a2SSathya Perla } 751db1d36a2SSathya Perla if (actions->flags & BNXT_TC_ACTION_FLAG_POP_VLAN) { 752db1d36a2SSathya Perla action_flags |= 753db1d36a2SSathya Perla CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; 754db1d36a2SSathya Perla /* Rewrite config with tpid = 0 implies vlan pop */ 755db1d36a2SSathya Perla req.l2_rewrite_vlan_tpid = 0; 756db1d36a2SSathya Perla memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); 757db1d36a2SSathya Perla memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); 758db1d36a2SSathya Perla } 759db1d36a2SSathya Perla } 760db1d36a2SSathya Perla req.action_flags = cpu_to_le16(action_flags); 761db1d36a2SSathya Perla 762db1d36a2SSathya Perla mutex_lock(&bp->hwrm_cmd_lock); 763db1d36a2SSathya Perla rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 7645c209fc8SVenkat Duvvuru if (!rc) { 7655c209fc8SVenkat Duvvuru resp = bnxt_get_hwrm_resp_addr(bp, &req); 766abd43a13SVenkat Duvvuru /* CFA_FLOW_ALLOC response interpretation: 767abd43a13SVenkat Duvvuru * fw with fw with 768abd43a13SVenkat Duvvuru * 16-bit 64-bit 769abd43a13SVenkat Duvvuru * flow handle flow handle 770abd43a13SVenkat Duvvuru * =========== =========== 771abd43a13SVenkat Duvvuru * flow_handle flow handle flow context id 772abd43a13SVenkat Duvvuru * ext_flow_handle INVALID flow handle 773abd43a13SVenkat Duvvuru * flow_id INVALID flow counter id 774abd43a13SVenkat Duvvuru */ 775abd43a13SVenkat Duvvuru flow_node->flow_handle = resp->flow_handle; 776abd43a13SVenkat Duvvuru if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) { 777abd43a13SVenkat Duvvuru flow_node->ext_flow_handle = resp->ext_flow_handle; 778abd43a13SVenkat Duvvuru flow_node->flow_id = resp->flow_id; 779abd43a13SVenkat Duvvuru } 7805c209fc8SVenkat Duvvuru } 781db1d36a2SSathya Perla mutex_unlock(&bp->hwrm_cmd_lock); 782db1d36a2SSathya Perla return rc; 7832ae7408fSSathya Perla } 7842ae7408fSSathya Perla 7858c95f773SSathya Perla static int hwrm_cfa_decap_filter_alloc(struct bnxt *bp, 7868c95f773SSathya Perla struct bnxt_tc_flow *flow, 7878c95f773SSathya Perla struct bnxt_tc_l2_key *l2_info, 7888c95f773SSathya Perla __le32 ref_decap_handle, 7898c95f773SSathya Perla __le32 *decap_filter_handle) 7908c95f773SSathya Perla { 791f484f678SSathya Perla struct hwrm_cfa_decap_filter_alloc_input req = { 0 }; 7925c209fc8SVenkat Duvvuru struct hwrm_cfa_decap_filter_alloc_output *resp; 793f484f678SSathya Perla struct ip_tunnel_key *tun_key = &flow->tun_key; 794f484f678SSathya Perla u32 enables = 0; 795f484f678SSathya Perla int rc; 796f484f678SSathya Perla 797f484f678SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_DECAP_FILTER_ALLOC, -1, -1); 798f484f678SSathya Perla 799f484f678SSathya Perla req.flags = cpu_to_le32(CFA_DECAP_FILTER_ALLOC_REQ_FLAGS_OVS_TUNNEL); 800f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_TYPE | 801f484f678SSathya Perla CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IP_PROTOCOL; 802f484f678SSathya Perla req.tunnel_type = CFA_DECAP_FILTER_ALLOC_REQ_TUNNEL_TYPE_VXLAN; 803f484f678SSathya Perla req.ip_protocol = CFA_DECAP_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP; 804f484f678SSathya Perla 805f484f678SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ID) { 806f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_TUNNEL_ID; 807f484f678SSathya Perla /* tunnel_id is wrongly defined in hsi defn. as __le32 */ 808f484f678SSathya Perla req.tunnel_id = tunnel_id_to_key32(tun_key->tun_id); 809f484f678SSathya Perla } 810f484f678SSathya Perla 811f484f678SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS) { 812c8fb7b82SSunil Challa enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_MACADDR; 813f484f678SSathya Perla ether_addr_copy(req.dst_macaddr, l2_info->dmac); 814f484f678SSathya Perla } 815f484f678SSathya Perla if (l2_info->num_vlans) { 816f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_T_IVLAN_VID; 817f484f678SSathya Perla req.t_ivlan_vid = l2_info->inner_vlan_tci; 818f484f678SSathya Perla } 819f484f678SSathya Perla 820f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_ETHERTYPE; 821f484f678SSathya Perla req.ethertype = htons(ETH_P_IP); 822f484f678SSathya Perla 823f484f678SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_IPV4_ADDRS) { 824f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_SRC_IPADDR | 825f484f678SSathya Perla CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_IPADDR | 826f484f678SSathya Perla CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_IPADDR_TYPE; 827f484f678SSathya Perla req.ip_addr_type = CFA_DECAP_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV4; 828f484f678SSathya Perla req.dst_ipaddr[0] = tun_key->u.ipv4.dst; 829f484f678SSathya Perla req.src_ipaddr[0] = tun_key->u.ipv4.src; 830f484f678SSathya Perla } 831f484f678SSathya Perla 832f484f678SSathya Perla if (flow->flags & BNXT_TC_FLOW_FLAGS_TUNL_PORTS) { 833f484f678SSathya Perla enables |= CFA_DECAP_FILTER_ALLOC_REQ_ENABLES_DST_PORT; 834f484f678SSathya Perla req.dst_port = tun_key->tp_dst; 835f484f678SSathya Perla } 836f484f678SSathya Perla 837f484f678SSathya Perla /* Eventhough the decap_handle returned by hwrm_cfa_decap_filter_alloc 838f484f678SSathya Perla * is defined as __le32, l2_ctxt_ref_id is defined in HSI as __le16. 839f484f678SSathya Perla */ 840f484f678SSathya Perla req.l2_ctxt_ref_id = (__force __le16)ref_decap_handle; 841f484f678SSathya Perla req.enables = cpu_to_le32(enables); 842f484f678SSathya Perla 843f484f678SSathya Perla mutex_lock(&bp->hwrm_cmd_lock); 844f484f678SSathya Perla rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 8455c209fc8SVenkat Duvvuru if (!rc) { 8465c209fc8SVenkat Duvvuru resp = bnxt_get_hwrm_resp_addr(bp, &req); 847f484f678SSathya Perla *decap_filter_handle = resp->decap_filter_id; 8485c209fc8SVenkat Duvvuru } else { 8499a005c38SJonathan Lemon netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 8505c209fc8SVenkat Duvvuru } 851f484f678SSathya Perla mutex_unlock(&bp->hwrm_cmd_lock); 852f484f678SSathya Perla 853f484f678SSathya Perla return rc; 8548c95f773SSathya Perla } 8558c95f773SSathya Perla 8568c95f773SSathya Perla static int hwrm_cfa_decap_filter_free(struct bnxt *bp, 8578c95f773SSathya Perla __le32 decap_filter_handle) 8588c95f773SSathya Perla { 859f484f678SSathya Perla struct hwrm_cfa_decap_filter_free_input req = { 0 }; 860f484f678SSathya Perla int rc; 861f484f678SSathya Perla 862f484f678SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_DECAP_FILTER_FREE, -1, -1); 863f484f678SSathya Perla req.decap_filter_id = decap_filter_handle; 864f484f678SSathya Perla 865f484f678SSathya Perla rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 866f484f678SSathya Perla if (rc) 8679a005c38SJonathan Lemon netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 8686ae777eaSVenkat Duvvuru 869f484f678SSathya Perla return rc; 8708c95f773SSathya Perla } 8718c95f773SSathya Perla 8728c95f773SSathya Perla static int hwrm_cfa_encap_record_alloc(struct bnxt *bp, 8738c95f773SSathya Perla struct ip_tunnel_key *encap_key, 8748c95f773SSathya Perla struct bnxt_tc_l2_key *l2_info, 8758c95f773SSathya Perla __le32 *encap_record_handle) 8768c95f773SSathya Perla { 877f484f678SSathya Perla struct hwrm_cfa_encap_record_alloc_input req = { 0 }; 8785c209fc8SVenkat Duvvuru struct hwrm_cfa_encap_record_alloc_output *resp; 879f484f678SSathya Perla struct hwrm_cfa_encap_data_vxlan *encap = 880f484f678SSathya Perla (struct hwrm_cfa_encap_data_vxlan *)&req.encap_data; 881f484f678SSathya Perla struct hwrm_vxlan_ipv4_hdr *encap_ipv4 = 882f484f678SSathya Perla (struct hwrm_vxlan_ipv4_hdr *)encap->l3; 883f484f678SSathya Perla int rc; 884f484f678SSathya Perla 885f484f678SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_ENCAP_RECORD_ALLOC, -1, -1); 886f484f678SSathya Perla 887f484f678SSathya Perla req.encap_type = CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_VXLAN; 888f484f678SSathya Perla 889f484f678SSathya Perla ether_addr_copy(encap->dst_mac_addr, l2_info->dmac); 890f484f678SSathya Perla ether_addr_copy(encap->src_mac_addr, l2_info->smac); 891f484f678SSathya Perla if (l2_info->num_vlans) { 892f484f678SSathya Perla encap->num_vlan_tags = l2_info->num_vlans; 893f484f678SSathya Perla encap->ovlan_tci = l2_info->inner_vlan_tci; 894f484f678SSathya Perla encap->ovlan_tpid = l2_info->inner_vlan_tpid; 895f484f678SSathya Perla } 896f484f678SSathya Perla 897f484f678SSathya Perla encap_ipv4->ver_hlen = 4 << VXLAN_IPV4_HDR_VER_HLEN_VERSION_SFT; 898f484f678SSathya Perla encap_ipv4->ver_hlen |= 5 << VXLAN_IPV4_HDR_VER_HLEN_HEADER_LENGTH_SFT; 899f484f678SSathya Perla encap_ipv4->ttl = encap_key->ttl; 900f484f678SSathya Perla 901f484f678SSathya Perla encap_ipv4->dest_ip_addr = encap_key->u.ipv4.dst; 902f484f678SSathya Perla encap_ipv4->src_ip_addr = encap_key->u.ipv4.src; 903f484f678SSathya Perla encap_ipv4->protocol = IPPROTO_UDP; 904f484f678SSathya Perla 905f484f678SSathya Perla encap->dst_port = encap_key->tp_dst; 906f484f678SSathya Perla encap->vni = tunnel_id_to_key32(encap_key->tun_id); 907f484f678SSathya Perla 908f484f678SSathya Perla mutex_lock(&bp->hwrm_cmd_lock); 909f484f678SSathya Perla rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 9105c209fc8SVenkat Duvvuru if (!rc) { 9115c209fc8SVenkat Duvvuru resp = bnxt_get_hwrm_resp_addr(bp, &req); 912f484f678SSathya Perla *encap_record_handle = resp->encap_record_id; 9135c209fc8SVenkat Duvvuru } else { 9149a005c38SJonathan Lemon netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 9155c209fc8SVenkat Duvvuru } 916f484f678SSathya Perla mutex_unlock(&bp->hwrm_cmd_lock); 917f484f678SSathya Perla 918f484f678SSathya Perla return rc; 9198c95f773SSathya Perla } 9208c95f773SSathya Perla 9218c95f773SSathya Perla static int hwrm_cfa_encap_record_free(struct bnxt *bp, 9228c95f773SSathya Perla __le32 encap_record_handle) 9238c95f773SSathya Perla { 924f484f678SSathya Perla struct hwrm_cfa_encap_record_free_input req = { 0 }; 925f484f678SSathya Perla int rc; 926f484f678SSathya Perla 927f484f678SSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_ENCAP_RECORD_FREE, -1, -1); 928f484f678SSathya Perla req.encap_record_id = encap_record_handle; 929f484f678SSathya Perla 930f484f678SSathya Perla rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 931f484f678SSathya Perla if (rc) 9329a005c38SJonathan Lemon netdev_info(bp->dev, "%s: Error rc=%d\n", __func__, rc); 9336ae777eaSVenkat Duvvuru 934f484f678SSathya Perla return rc; 9358c95f773SSathya Perla } 9368c95f773SSathya Perla 9372ae7408fSSathya Perla static int bnxt_tc_put_l2_node(struct bnxt *bp, 9382ae7408fSSathya Perla struct bnxt_tc_flow_node *flow_node) 9392ae7408fSSathya Perla { 9402ae7408fSSathya Perla struct bnxt_tc_l2_node *l2_node = flow_node->l2_node; 941cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 9422ae7408fSSathya Perla int rc; 9432ae7408fSSathya Perla 9442ae7408fSSathya Perla /* remove flow_node from the L2 shared flow list */ 9452ae7408fSSathya Perla list_del(&flow_node->l2_list_node); 9462ae7408fSSathya Perla if (--l2_node->refcount == 0) { 9472ae7408fSSathya Perla rc = rhashtable_remove_fast(&tc_info->l2_table, &l2_node->node, 9482ae7408fSSathya Perla tc_info->l2_ht_params); 9492ae7408fSSathya Perla if (rc) 9502ae7408fSSathya Perla netdev_err(bp->dev, 9519a005c38SJonathan Lemon "Error: %s: rhashtable_remove_fast: %d\n", 9522ae7408fSSathya Perla __func__, rc); 9532ae7408fSSathya Perla kfree_rcu(l2_node, rcu); 9542ae7408fSSathya Perla } 9552ae7408fSSathya Perla return 0; 9562ae7408fSSathya Perla } 9572ae7408fSSathya Perla 9582ae7408fSSathya Perla static struct bnxt_tc_l2_node * 9592ae7408fSSathya Perla bnxt_tc_get_l2_node(struct bnxt *bp, struct rhashtable *l2_table, 9602ae7408fSSathya Perla struct rhashtable_params ht_params, 9612ae7408fSSathya Perla struct bnxt_tc_l2_key *l2_key) 9622ae7408fSSathya Perla { 9632ae7408fSSathya Perla struct bnxt_tc_l2_node *l2_node; 9642ae7408fSSathya Perla int rc; 9652ae7408fSSathya Perla 9662ae7408fSSathya Perla l2_node = rhashtable_lookup_fast(l2_table, l2_key, ht_params); 9672ae7408fSSathya Perla if (!l2_node) { 9682ae7408fSSathya Perla l2_node = kzalloc(sizeof(*l2_node), GFP_KERNEL); 9692ae7408fSSathya Perla if (!l2_node) { 9702ae7408fSSathya Perla rc = -ENOMEM; 9712ae7408fSSathya Perla return NULL; 9722ae7408fSSathya Perla } 9732ae7408fSSathya Perla 9742ae7408fSSathya Perla l2_node->key = *l2_key; 9752ae7408fSSathya Perla rc = rhashtable_insert_fast(l2_table, &l2_node->node, 9762ae7408fSSathya Perla ht_params); 9772ae7408fSSathya Perla if (rc) { 9788c95f773SSathya Perla kfree_rcu(l2_node, rcu); 9792ae7408fSSathya Perla netdev_err(bp->dev, 9809a005c38SJonathan Lemon "Error: %s: rhashtable_insert_fast: %d\n", 9812ae7408fSSathya Perla __func__, rc); 9822ae7408fSSathya Perla return NULL; 9832ae7408fSSathya Perla } 9842ae7408fSSathya Perla INIT_LIST_HEAD(&l2_node->common_l2_flows); 9852ae7408fSSathya Perla } 9862ae7408fSSathya Perla return l2_node; 9872ae7408fSSathya Perla } 9882ae7408fSSathya Perla 9892ae7408fSSathya Perla /* Get the ref_flow_handle for a flow by checking if there are any other 9902ae7408fSSathya Perla * flows that share the same L2 key as this flow. 9912ae7408fSSathya Perla */ 9922ae7408fSSathya Perla static int 9932ae7408fSSathya Perla bnxt_tc_get_ref_flow_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 9942ae7408fSSathya Perla struct bnxt_tc_flow_node *flow_node, 9952ae7408fSSathya Perla __le16 *ref_flow_handle) 9962ae7408fSSathya Perla { 997cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 9982ae7408fSSathya Perla struct bnxt_tc_flow_node *ref_flow_node; 9992ae7408fSSathya Perla struct bnxt_tc_l2_node *l2_node; 10002ae7408fSSathya Perla 10012ae7408fSSathya Perla l2_node = bnxt_tc_get_l2_node(bp, &tc_info->l2_table, 10022ae7408fSSathya Perla tc_info->l2_ht_params, 10032ae7408fSSathya Perla &flow->l2_key); 10042ae7408fSSathya Perla if (!l2_node) 10052ae7408fSSathya Perla return -1; 10062ae7408fSSathya Perla 10072ae7408fSSathya Perla /* If any other flow is using this l2_node, use it's flow_handle 10082ae7408fSSathya Perla * as the ref_flow_handle 10092ae7408fSSathya Perla */ 10102ae7408fSSathya Perla if (l2_node->refcount > 0) { 10112ae7408fSSathya Perla ref_flow_node = list_first_entry(&l2_node->common_l2_flows, 10122ae7408fSSathya Perla struct bnxt_tc_flow_node, 10132ae7408fSSathya Perla l2_list_node); 10142ae7408fSSathya Perla *ref_flow_handle = ref_flow_node->flow_handle; 10152ae7408fSSathya Perla } else { 10162ae7408fSSathya Perla *ref_flow_handle = cpu_to_le16(0xffff); 10172ae7408fSSathya Perla } 10182ae7408fSSathya Perla 10192ae7408fSSathya Perla /* Insert the l2_node into the flow_node so that subsequent flows 10202ae7408fSSathya Perla * with a matching l2 key can use the flow_handle of this flow 10212ae7408fSSathya Perla * as their ref_flow_handle 10222ae7408fSSathya Perla */ 10232ae7408fSSathya Perla flow_node->l2_node = l2_node; 10242ae7408fSSathya Perla list_add(&flow_node->l2_list_node, &l2_node->common_l2_flows); 10252ae7408fSSathya Perla l2_node->refcount++; 10262ae7408fSSathya Perla return 0; 10272ae7408fSSathya Perla } 10282ae7408fSSathya Perla 10292ae7408fSSathya Perla /* After the flow parsing is done, this routine is used for checking 10302ae7408fSSathya Perla * if there are any aspects of the flow that prevent it from being 10312ae7408fSSathya Perla * offloaded. 10322ae7408fSSathya Perla */ 10332ae7408fSSathya Perla static bool bnxt_tc_can_offload(struct bnxt *bp, struct bnxt_tc_flow *flow) 10342ae7408fSSathya Perla { 10352ae7408fSSathya Perla /* If L4 ports are specified then ip_proto must be TCP or UDP */ 10362ae7408fSSathya Perla if ((flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) && 10372ae7408fSSathya Perla (flow->l4_key.ip_proto != IPPROTO_TCP && 10382ae7408fSSathya Perla flow->l4_key.ip_proto != IPPROTO_UDP)) { 10399a005c38SJonathan Lemon netdev_info(bp->dev, "Cannot offload non-TCP/UDP (%d) ports\n", 10402ae7408fSSathya Perla flow->l4_key.ip_proto); 10412ae7408fSSathya Perla return false; 10422ae7408fSSathya Perla } 10432ae7408fSSathya Perla 1044e85a9be9SAndy Gospodarek /* Currently source/dest MAC cannot be partial wildcard */ 1045e85a9be9SAndy Gospodarek if (bits_set(&flow->l2_key.smac, sizeof(flow->l2_key.smac)) && 1046e85a9be9SAndy Gospodarek !is_exactmatch(flow->l2_mask.smac, sizeof(flow->l2_mask.smac))) { 1047e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for Source MAC\n"); 1048e85a9be9SAndy Gospodarek return false; 1049e85a9be9SAndy Gospodarek } 1050e85a9be9SAndy Gospodarek if (bits_set(&flow->l2_key.dmac, sizeof(flow->l2_key.dmac)) && 1051e85a9be9SAndy Gospodarek !is_exactmatch(&flow->l2_mask.dmac, sizeof(flow->l2_mask.dmac))) { 1052e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for Dest MAC\n"); 1053e85a9be9SAndy Gospodarek return false; 1054e85a9be9SAndy Gospodarek } 1055e85a9be9SAndy Gospodarek 1056e85a9be9SAndy Gospodarek /* Currently VLAN fields cannot be partial wildcard */ 1057e85a9be9SAndy Gospodarek if (bits_set(&flow->l2_key.inner_vlan_tci, 1058e85a9be9SAndy Gospodarek sizeof(flow->l2_key.inner_vlan_tci)) && 1059e32d4e60SVenkat Duvvuru !is_vlan_tci_allowed(flow->l2_mask.inner_vlan_tci, 1060e32d4e60SVenkat Duvvuru flow->l2_key.inner_vlan_tci)) { 1061e32d4e60SVenkat Duvvuru netdev_info(bp->dev, "Unsupported VLAN TCI\n"); 1062e85a9be9SAndy Gospodarek return false; 1063e85a9be9SAndy Gospodarek } 1064e85a9be9SAndy Gospodarek if (bits_set(&flow->l2_key.inner_vlan_tpid, 1065e85a9be9SAndy Gospodarek sizeof(flow->l2_key.inner_vlan_tpid)) && 1066e85a9be9SAndy Gospodarek !is_exactmatch(&flow->l2_mask.inner_vlan_tpid, 1067e85a9be9SAndy Gospodarek sizeof(flow->l2_mask.inner_vlan_tpid))) { 1068e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for VLAN TPID\n"); 1069e85a9be9SAndy Gospodarek return false; 1070e85a9be9SAndy Gospodarek } 1071e85a9be9SAndy Gospodarek 1072e85a9be9SAndy Gospodarek /* Currently Ethertype must be set */ 1073e85a9be9SAndy Gospodarek if (!is_exactmatch(&flow->l2_mask.ether_type, 1074e85a9be9SAndy Gospodarek sizeof(flow->l2_mask.ether_type))) { 1075e85a9be9SAndy Gospodarek netdev_info(bp->dev, "Wildcard match unsupported for Ethertype\n"); 1076e85a9be9SAndy Gospodarek return false; 1077e85a9be9SAndy Gospodarek } 1078e85a9be9SAndy Gospodarek 10792ae7408fSSathya Perla return true; 10802ae7408fSSathya Perla } 10812ae7408fSSathya Perla 10828c95f773SSathya Perla /* Returns the final refcount of the node on success 10838c95f773SSathya Perla * or a -ve error code on failure 10848c95f773SSathya Perla */ 10858c95f773SSathya Perla static int bnxt_tc_put_tunnel_node(struct bnxt *bp, 10868c95f773SSathya Perla struct rhashtable *tunnel_table, 10878c95f773SSathya Perla struct rhashtable_params *ht_params, 10888c95f773SSathya Perla struct bnxt_tc_tunnel_node *tunnel_node) 10898c95f773SSathya Perla { 10908c95f773SSathya Perla int rc; 10918c95f773SSathya Perla 10928c95f773SSathya Perla if (--tunnel_node->refcount == 0) { 10938c95f773SSathya Perla rc = rhashtable_remove_fast(tunnel_table, &tunnel_node->node, 10948c95f773SSathya Perla *ht_params); 10958c95f773SSathya Perla if (rc) { 10969a005c38SJonathan Lemon netdev_err(bp->dev, "rhashtable_remove_fast rc=%d\n", rc); 10978c95f773SSathya Perla rc = -1; 10988c95f773SSathya Perla } 10998c95f773SSathya Perla kfree_rcu(tunnel_node, rcu); 11008c95f773SSathya Perla return rc; 11018c95f773SSathya Perla } else { 11028c95f773SSathya Perla return tunnel_node->refcount; 11038c95f773SSathya Perla } 11048c95f773SSathya Perla } 11058c95f773SSathya Perla 11068c95f773SSathya Perla /* Get (or add) either encap or decap tunnel node from/to the supplied 11078c95f773SSathya Perla * hash table. 11088c95f773SSathya Perla */ 11098c95f773SSathya Perla static struct bnxt_tc_tunnel_node * 11108c95f773SSathya Perla bnxt_tc_get_tunnel_node(struct bnxt *bp, struct rhashtable *tunnel_table, 11118c95f773SSathya Perla struct rhashtable_params *ht_params, 11128c95f773SSathya Perla struct ip_tunnel_key *tun_key) 11138c95f773SSathya Perla { 11148c95f773SSathya Perla struct bnxt_tc_tunnel_node *tunnel_node; 11158c95f773SSathya Perla int rc; 11168c95f773SSathya Perla 11178c95f773SSathya Perla tunnel_node = rhashtable_lookup_fast(tunnel_table, tun_key, *ht_params); 11188c95f773SSathya Perla if (!tunnel_node) { 11198c95f773SSathya Perla tunnel_node = kzalloc(sizeof(*tunnel_node), GFP_KERNEL); 11208c95f773SSathya Perla if (!tunnel_node) { 11218c95f773SSathya Perla rc = -ENOMEM; 11228c95f773SSathya Perla goto err; 11238c95f773SSathya Perla } 11248c95f773SSathya Perla 11258c95f773SSathya Perla tunnel_node->key = *tun_key; 11268c95f773SSathya Perla tunnel_node->tunnel_handle = INVALID_TUNNEL_HANDLE; 11278c95f773SSathya Perla rc = rhashtable_insert_fast(tunnel_table, &tunnel_node->node, 11288c95f773SSathya Perla *ht_params); 11298c95f773SSathya Perla if (rc) { 11308c95f773SSathya Perla kfree_rcu(tunnel_node, rcu); 11318c95f773SSathya Perla goto err; 11328c95f773SSathya Perla } 11338c95f773SSathya Perla } 11348c95f773SSathya Perla tunnel_node->refcount++; 11358c95f773SSathya Perla return tunnel_node; 11368c95f773SSathya Perla err: 11379a005c38SJonathan Lemon netdev_info(bp->dev, "error rc=%d\n", rc); 11388c95f773SSathya Perla return NULL; 11398c95f773SSathya Perla } 11408c95f773SSathya Perla 11418c95f773SSathya Perla static int bnxt_tc_get_ref_decap_handle(struct bnxt *bp, 11428c95f773SSathya Perla struct bnxt_tc_flow *flow, 11438c95f773SSathya Perla struct bnxt_tc_l2_key *l2_key, 11448c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node, 11458c95f773SSathya Perla __le32 *ref_decap_handle) 11468c95f773SSathya Perla { 1147cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 11488c95f773SSathya Perla struct bnxt_tc_flow_node *ref_flow_node; 11498c95f773SSathya Perla struct bnxt_tc_l2_node *decap_l2_node; 11508c95f773SSathya Perla 11518c95f773SSathya Perla decap_l2_node = bnxt_tc_get_l2_node(bp, &tc_info->decap_l2_table, 11528c95f773SSathya Perla tc_info->decap_l2_ht_params, 11538c95f773SSathya Perla l2_key); 11548c95f773SSathya Perla if (!decap_l2_node) 11558c95f773SSathya Perla return -1; 11568c95f773SSathya Perla 11578c95f773SSathya Perla /* If any other flow is using this decap_l2_node, use it's decap_handle 11588c95f773SSathya Perla * as the ref_decap_handle 11598c95f773SSathya Perla */ 11608c95f773SSathya Perla if (decap_l2_node->refcount > 0) { 11618c95f773SSathya Perla ref_flow_node = 11628c95f773SSathya Perla list_first_entry(&decap_l2_node->common_l2_flows, 11638c95f773SSathya Perla struct bnxt_tc_flow_node, 11648c95f773SSathya Perla decap_l2_list_node); 11658c95f773SSathya Perla *ref_decap_handle = ref_flow_node->decap_node->tunnel_handle; 11668c95f773SSathya Perla } else { 11678c95f773SSathya Perla *ref_decap_handle = INVALID_TUNNEL_HANDLE; 11688c95f773SSathya Perla } 11698c95f773SSathya Perla 11708c95f773SSathya Perla /* Insert the l2_node into the flow_node so that subsequent flows 11718c95f773SSathya Perla * with a matching decap l2 key can use the decap_filter_handle of 11728c95f773SSathya Perla * this flow as their ref_decap_handle 11738c95f773SSathya Perla */ 11748c95f773SSathya Perla flow_node->decap_l2_node = decap_l2_node; 11758c95f773SSathya Perla list_add(&flow_node->decap_l2_list_node, 11768c95f773SSathya Perla &decap_l2_node->common_l2_flows); 11778c95f773SSathya Perla decap_l2_node->refcount++; 11788c95f773SSathya Perla return 0; 11798c95f773SSathya Perla } 11808c95f773SSathya Perla 11818c95f773SSathya Perla static void bnxt_tc_put_decap_l2_node(struct bnxt *bp, 11828c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node) 11838c95f773SSathya Perla { 11848c95f773SSathya Perla struct bnxt_tc_l2_node *decap_l2_node = flow_node->decap_l2_node; 1185cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 11868c95f773SSathya Perla int rc; 11878c95f773SSathya Perla 11888c95f773SSathya Perla /* remove flow_node from the decap L2 sharing flow list */ 11898c95f773SSathya Perla list_del(&flow_node->decap_l2_list_node); 11908c95f773SSathya Perla if (--decap_l2_node->refcount == 0) { 11918c95f773SSathya Perla rc = rhashtable_remove_fast(&tc_info->decap_l2_table, 11928c95f773SSathya Perla &decap_l2_node->node, 11938c95f773SSathya Perla tc_info->decap_l2_ht_params); 11948c95f773SSathya Perla if (rc) 11959a005c38SJonathan Lemon netdev_err(bp->dev, "rhashtable_remove_fast rc=%d\n", rc); 11968c95f773SSathya Perla kfree_rcu(decap_l2_node, rcu); 11978c95f773SSathya Perla } 11988c95f773SSathya Perla } 11998c95f773SSathya Perla 12008c95f773SSathya Perla static void bnxt_tc_put_decap_handle(struct bnxt *bp, 12018c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node) 12028c95f773SSathya Perla { 12038c95f773SSathya Perla __le32 decap_handle = flow_node->decap_node->tunnel_handle; 1204cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 12058c95f773SSathya Perla int rc; 12068c95f773SSathya Perla 12078c95f773SSathya Perla if (flow_node->decap_l2_node) 12088c95f773SSathya Perla bnxt_tc_put_decap_l2_node(bp, flow_node); 12098c95f773SSathya Perla 12108c95f773SSathya Perla rc = bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table, 12118c95f773SSathya Perla &tc_info->decap_ht_params, 12128c95f773SSathya Perla flow_node->decap_node); 12138c95f773SSathya Perla if (!rc && decap_handle != INVALID_TUNNEL_HANDLE) 12148c95f773SSathya Perla hwrm_cfa_decap_filter_free(bp, decap_handle); 12158c95f773SSathya Perla } 12168c95f773SSathya Perla 12178c95f773SSathya Perla static int bnxt_tc_resolve_tunnel_hdrs(struct bnxt *bp, 12188c95f773SSathya Perla struct ip_tunnel_key *tun_key, 1219e9ecc731SSathya Perla struct bnxt_tc_l2_key *l2_info) 12208c95f773SSathya Perla { 1221952c5719SMichael Chan #ifdef CONFIG_INET 1222e9ecc731SSathya Perla struct net_device *real_dst_dev = bp->dev; 12238c95f773SSathya Perla struct flowi4 flow = { {0} }; 12248c95f773SSathya Perla struct net_device *dst_dev; 12258c95f773SSathya Perla struct neighbour *nbr; 12268c95f773SSathya Perla struct rtable *rt; 12278c95f773SSathya Perla int rc; 12288c95f773SSathya Perla 12298c95f773SSathya Perla flow.flowi4_proto = IPPROTO_UDP; 12308c95f773SSathya Perla flow.fl4_dport = tun_key->tp_dst; 12318c95f773SSathya Perla flow.daddr = tun_key->u.ipv4.dst; 12328c95f773SSathya Perla 12338c95f773SSathya Perla rt = ip_route_output_key(dev_net(real_dst_dev), &flow); 12348c95f773SSathya Perla if (IS_ERR(rt)) { 12359a005c38SJonathan Lemon netdev_info(bp->dev, "no route to %pI4b\n", &flow.daddr); 12368c95f773SSathya Perla return -EOPNOTSUPP; 12378c95f773SSathya Perla } 12388c95f773SSathya Perla 12398c95f773SSathya Perla /* The route must either point to the real_dst_dev or a dst_dev that 12408c95f773SSathya Perla * uses the real_dst_dev. 12418c95f773SSathya Perla */ 12428c95f773SSathya Perla dst_dev = rt->dst.dev; 12438c95f773SSathya Perla if (is_vlan_dev(dst_dev)) { 1244952c5719SMichael Chan #if IS_ENABLED(CONFIG_VLAN_8021Q) 12458c95f773SSathya Perla struct vlan_dev_priv *vlan = vlan_dev_priv(dst_dev); 12468c95f773SSathya Perla 12478c95f773SSathya Perla if (vlan->real_dev != real_dst_dev) { 12488c95f773SSathya Perla netdev_info(bp->dev, 12499a005c38SJonathan Lemon "dst_dev(%s) doesn't use PF-if(%s)\n", 12508c95f773SSathya Perla netdev_name(dst_dev), 12518c95f773SSathya Perla netdev_name(real_dst_dev)); 12528c95f773SSathya Perla rc = -EOPNOTSUPP; 12538c95f773SSathya Perla goto put_rt; 12548c95f773SSathya Perla } 12558c95f773SSathya Perla l2_info->inner_vlan_tci = htons(vlan->vlan_id); 12568c95f773SSathya Perla l2_info->inner_vlan_tpid = vlan->vlan_proto; 12578c95f773SSathya Perla l2_info->num_vlans = 1; 1258952c5719SMichael Chan #endif 12598c95f773SSathya Perla } else if (dst_dev != real_dst_dev) { 12608c95f773SSathya Perla netdev_info(bp->dev, 12619a005c38SJonathan Lemon "dst_dev(%s) for %pI4b is not PF-if(%s)\n", 12628c95f773SSathya Perla netdev_name(dst_dev), &flow.daddr, 12638c95f773SSathya Perla netdev_name(real_dst_dev)); 12648c95f773SSathya Perla rc = -EOPNOTSUPP; 12658c95f773SSathya Perla goto put_rt; 12668c95f773SSathya Perla } 12678c95f773SSathya Perla 12688c95f773SSathya Perla nbr = dst_neigh_lookup(&rt->dst, &flow.daddr); 12698c95f773SSathya Perla if (!nbr) { 12709a005c38SJonathan Lemon netdev_info(bp->dev, "can't lookup neighbor for %pI4b\n", 12718c95f773SSathya Perla &flow.daddr); 12728c95f773SSathya Perla rc = -EOPNOTSUPP; 12738c95f773SSathya Perla goto put_rt; 12748c95f773SSathya Perla } 12758c95f773SSathya Perla 12768c95f773SSathya Perla tun_key->u.ipv4.src = flow.saddr; 12778c95f773SSathya Perla tun_key->ttl = ip4_dst_hoplimit(&rt->dst); 12788c95f773SSathya Perla neigh_ha_snapshot(l2_info->dmac, nbr, dst_dev); 12798c95f773SSathya Perla ether_addr_copy(l2_info->smac, dst_dev->dev_addr); 12808c95f773SSathya Perla neigh_release(nbr); 12818c95f773SSathya Perla ip_rt_put(rt); 12828c95f773SSathya Perla 12838c95f773SSathya Perla return 0; 12848c95f773SSathya Perla put_rt: 12858c95f773SSathya Perla ip_rt_put(rt); 12868c95f773SSathya Perla return rc; 1287952c5719SMichael Chan #else 1288952c5719SMichael Chan return -EOPNOTSUPP; 1289952c5719SMichael Chan #endif 12908c95f773SSathya Perla } 12918c95f773SSathya Perla 12928c95f773SSathya Perla static int bnxt_tc_get_decap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 12938c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node, 12948c95f773SSathya Perla __le32 *decap_filter_handle) 12958c95f773SSathya Perla { 12968c95f773SSathya Perla struct ip_tunnel_key *decap_key = &flow->tun_key; 1297cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 12988c95f773SSathya Perla struct bnxt_tc_l2_key l2_info = { {0} }; 12998c95f773SSathya Perla struct bnxt_tc_tunnel_node *decap_node; 13008c95f773SSathya Perla struct ip_tunnel_key tun_key = { 0 }; 13018c95f773SSathya Perla struct bnxt_tc_l2_key *decap_l2_info; 13028c95f773SSathya Perla __le32 ref_decap_handle; 13038c95f773SSathya Perla int rc; 13048c95f773SSathya Perla 13058c95f773SSathya Perla /* Check if there's another flow using the same tunnel decap. 13068c95f773SSathya Perla * If not, add this tunnel to the table and resolve the other 1307479ca3bfSSriharsha Basavapatna * tunnel header fileds. Ignore src_port in the tunnel_key, 1308479ca3bfSSriharsha Basavapatna * since it is not required for decap filters. 13098c95f773SSathya Perla */ 1310479ca3bfSSriharsha Basavapatna decap_key->tp_src = 0; 13118c95f773SSathya Perla decap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->decap_table, 13128c95f773SSathya Perla &tc_info->decap_ht_params, 13138c95f773SSathya Perla decap_key); 13148c95f773SSathya Perla if (!decap_node) 13158c95f773SSathya Perla return -ENOMEM; 13168c95f773SSathya Perla 13178c95f773SSathya Perla flow_node->decap_node = decap_node; 13188c95f773SSathya Perla 13198c95f773SSathya Perla if (decap_node->tunnel_handle != INVALID_TUNNEL_HANDLE) 13208c95f773SSathya Perla goto done; 13218c95f773SSathya Perla 13228c95f773SSathya Perla /* Resolve the L2 fields for tunnel decap 13238c95f773SSathya Perla * Resolve the route for remote vtep (saddr) of the decap key 13248c95f773SSathya Perla * Find it's next-hop mac addrs 13258c95f773SSathya Perla */ 13268c95f773SSathya Perla tun_key.u.ipv4.dst = flow->tun_key.u.ipv4.src; 13278c95f773SSathya Perla tun_key.tp_dst = flow->tun_key.tp_dst; 1328e9ecc731SSathya Perla rc = bnxt_tc_resolve_tunnel_hdrs(bp, &tun_key, &l2_info); 13298c95f773SSathya Perla if (rc) 13308c95f773SSathya Perla goto put_decap; 13318c95f773SSathya Perla 13328c95f773SSathya Perla decap_l2_info = &decap_node->l2_info; 1333c8fb7b82SSunil Challa /* decap smac is wildcarded */ 13348c95f773SSathya Perla ether_addr_copy(decap_l2_info->dmac, l2_info.smac); 13358c95f773SSathya Perla if (l2_info.num_vlans) { 13368c95f773SSathya Perla decap_l2_info->num_vlans = l2_info.num_vlans; 13378c95f773SSathya Perla decap_l2_info->inner_vlan_tpid = l2_info.inner_vlan_tpid; 13388c95f773SSathya Perla decap_l2_info->inner_vlan_tci = l2_info.inner_vlan_tci; 13398c95f773SSathya Perla } 13408c95f773SSathya Perla flow->flags |= BNXT_TC_FLOW_FLAGS_TUNL_ETH_ADDRS; 13418c95f773SSathya Perla 13428c95f773SSathya Perla /* For getting a decap_filter_handle we first need to check if 13438c95f773SSathya Perla * there are any other decap flows that share the same tunnel L2 13448c95f773SSathya Perla * key and if so, pass that flow's decap_filter_handle as the 13458c95f773SSathya Perla * ref_decap_handle for this flow. 13468c95f773SSathya Perla */ 13478c95f773SSathya Perla rc = bnxt_tc_get_ref_decap_handle(bp, flow, decap_l2_info, flow_node, 13488c95f773SSathya Perla &ref_decap_handle); 13498c95f773SSathya Perla if (rc) 13508c95f773SSathya Perla goto put_decap; 13518c95f773SSathya Perla 13528c95f773SSathya Perla /* Issue the hwrm cmd to allocate a decap filter handle */ 13538c95f773SSathya Perla rc = hwrm_cfa_decap_filter_alloc(bp, flow, decap_l2_info, 13548c95f773SSathya Perla ref_decap_handle, 13558c95f773SSathya Perla &decap_node->tunnel_handle); 13568c95f773SSathya Perla if (rc) 13578c95f773SSathya Perla goto put_decap_l2; 13588c95f773SSathya Perla 13598c95f773SSathya Perla done: 13608c95f773SSathya Perla *decap_filter_handle = decap_node->tunnel_handle; 13618c95f773SSathya Perla return 0; 13628c95f773SSathya Perla 13638c95f773SSathya Perla put_decap_l2: 13648c95f773SSathya Perla bnxt_tc_put_decap_l2_node(bp, flow_node); 13658c95f773SSathya Perla put_decap: 13668c95f773SSathya Perla bnxt_tc_put_tunnel_node(bp, &tc_info->decap_table, 13678c95f773SSathya Perla &tc_info->decap_ht_params, 13688c95f773SSathya Perla flow_node->decap_node); 13698c95f773SSathya Perla return rc; 13708c95f773SSathya Perla } 13718c95f773SSathya Perla 13728c95f773SSathya Perla static void bnxt_tc_put_encap_handle(struct bnxt *bp, 13738c95f773SSathya Perla struct bnxt_tc_tunnel_node *encap_node) 13748c95f773SSathya Perla { 13758c95f773SSathya Perla __le32 encap_handle = encap_node->tunnel_handle; 1376cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 13778c95f773SSathya Perla int rc; 13788c95f773SSathya Perla 13798c95f773SSathya Perla rc = bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table, 13808c95f773SSathya Perla &tc_info->encap_ht_params, encap_node); 13818c95f773SSathya Perla if (!rc && encap_handle != INVALID_TUNNEL_HANDLE) 13828c95f773SSathya Perla hwrm_cfa_encap_record_free(bp, encap_handle); 13838c95f773SSathya Perla } 13848c95f773SSathya Perla 13858c95f773SSathya Perla /* Lookup the tunnel encap table and check if there's an encap_handle 13868c95f773SSathya Perla * alloc'd already. 13878c95f773SSathya Perla * If not, query L2 info via a route lookup and issue an encap_record_alloc 13888c95f773SSathya Perla * cmd to FW. 13898c95f773SSathya Perla */ 13908c95f773SSathya Perla static int bnxt_tc_get_encap_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, 13918c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node, 13928c95f773SSathya Perla __le32 *encap_handle) 13938c95f773SSathya Perla { 13948c95f773SSathya Perla struct ip_tunnel_key *encap_key = &flow->actions.tun_encap_key; 1395cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 13968c95f773SSathya Perla struct bnxt_tc_tunnel_node *encap_node; 13978c95f773SSathya Perla int rc; 13988c95f773SSathya Perla 13998c95f773SSathya Perla /* Check if there's another flow using the same tunnel encap. 14008c95f773SSathya Perla * If not, add this tunnel to the table and resolve the other 14018c95f773SSathya Perla * tunnel header fileds 14028c95f773SSathya Perla */ 14038c95f773SSathya Perla encap_node = bnxt_tc_get_tunnel_node(bp, &tc_info->encap_table, 14048c95f773SSathya Perla &tc_info->encap_ht_params, 14058c95f773SSathya Perla encap_key); 14068c95f773SSathya Perla if (!encap_node) 14078c95f773SSathya Perla return -ENOMEM; 14088c95f773SSathya Perla 14098c95f773SSathya Perla flow_node->encap_node = encap_node; 14108c95f773SSathya Perla 14118c95f773SSathya Perla if (encap_node->tunnel_handle != INVALID_TUNNEL_HANDLE) 14128c95f773SSathya Perla goto done; 14138c95f773SSathya Perla 1414e9ecc731SSathya Perla rc = bnxt_tc_resolve_tunnel_hdrs(bp, encap_key, &encap_node->l2_info); 14158c95f773SSathya Perla if (rc) 14168c95f773SSathya Perla goto put_encap; 14178c95f773SSathya Perla 14188c95f773SSathya Perla /* Allocate a new tunnel encap record */ 14198c95f773SSathya Perla rc = hwrm_cfa_encap_record_alloc(bp, encap_key, &encap_node->l2_info, 14208c95f773SSathya Perla &encap_node->tunnel_handle); 14218c95f773SSathya Perla if (rc) 14228c95f773SSathya Perla goto put_encap; 14238c95f773SSathya Perla 14248c95f773SSathya Perla done: 14258c95f773SSathya Perla *encap_handle = encap_node->tunnel_handle; 14268c95f773SSathya Perla return 0; 14278c95f773SSathya Perla 14288c95f773SSathya Perla put_encap: 14298c95f773SSathya Perla bnxt_tc_put_tunnel_node(bp, &tc_info->encap_table, 14308c95f773SSathya Perla &tc_info->encap_ht_params, encap_node); 14318c95f773SSathya Perla return rc; 14328c95f773SSathya Perla } 14338c95f773SSathya Perla 14348c95f773SSathya Perla static void bnxt_tc_put_tunnel_handle(struct bnxt *bp, 14358c95f773SSathya Perla struct bnxt_tc_flow *flow, 14368c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node) 14378c95f773SSathya Perla { 14388c95f773SSathya Perla if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 14398c95f773SSathya Perla bnxt_tc_put_decap_handle(bp, flow_node); 14408c95f773SSathya Perla else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) 14418c95f773SSathya Perla bnxt_tc_put_encap_handle(bp, flow_node->encap_node); 14428c95f773SSathya Perla } 14438c95f773SSathya Perla 14448c95f773SSathya Perla static int bnxt_tc_get_tunnel_handle(struct bnxt *bp, 14458c95f773SSathya Perla struct bnxt_tc_flow *flow, 14468c95f773SSathya Perla struct bnxt_tc_flow_node *flow_node, 14478c95f773SSathya Perla __le32 *tunnel_handle) 14488c95f773SSathya Perla { 14498c95f773SSathya Perla if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 14508c95f773SSathya Perla return bnxt_tc_get_decap_handle(bp, flow, flow_node, 14518c95f773SSathya Perla tunnel_handle); 14528c95f773SSathya Perla else if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_ENCAP) 14538c95f773SSathya Perla return bnxt_tc_get_encap_handle(bp, flow, flow_node, 14548c95f773SSathya Perla tunnel_handle); 14558c95f773SSathya Perla else 14568c95f773SSathya Perla return 0; 14578c95f773SSathya Perla } 14582ae7408fSSathya Perla static int __bnxt_tc_del_flow(struct bnxt *bp, 14592ae7408fSSathya Perla struct bnxt_tc_flow_node *flow_node) 14602ae7408fSSathya Perla { 1461cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 14622ae7408fSSathya Perla int rc; 14632ae7408fSSathya Perla 14642ae7408fSSathya Perla /* send HWRM cmd to free the flow-id */ 1465abd43a13SVenkat Duvvuru bnxt_hwrm_cfa_flow_free(bp, flow_node); 14662ae7408fSSathya Perla 14672ae7408fSSathya Perla mutex_lock(&tc_info->lock); 14682ae7408fSSathya Perla 14698c95f773SSathya Perla /* release references to any tunnel encap/decap nodes */ 14708c95f773SSathya Perla bnxt_tc_put_tunnel_handle(bp, &flow_node->flow, flow_node); 14718c95f773SSathya Perla 14722ae7408fSSathya Perla /* release reference to l2 node */ 14732ae7408fSSathya Perla bnxt_tc_put_l2_node(bp, flow_node); 14742ae7408fSSathya Perla 14752ae7408fSSathya Perla mutex_unlock(&tc_info->lock); 14762ae7408fSSathya Perla 14772ae7408fSSathya Perla rc = rhashtable_remove_fast(&tc_info->flow_table, &flow_node->node, 14782ae7408fSSathya Perla tc_info->flow_ht_params); 14792ae7408fSSathya Perla if (rc) 14809a005c38SJonathan Lemon netdev_err(bp->dev, "Error: %s: rhashtable_remove_fast rc=%d\n", 14812ae7408fSSathya Perla __func__, rc); 14822ae7408fSSathya Perla 14832ae7408fSSathya Perla kfree_rcu(flow_node, rcu); 14842ae7408fSSathya Perla return 0; 14852ae7408fSSathya Perla } 14862ae7408fSSathya Perla 1487abd43a13SVenkat Duvvuru static void bnxt_tc_set_flow_dir(struct bnxt *bp, struct bnxt_tc_flow *flow, 1488abd43a13SVenkat Duvvuru u16 src_fid) 1489abd43a13SVenkat Duvvuru { 14909bf46566SSomnath Kotur flow->l2_key.dir = (bp->pf.fw_fid == src_fid) ? BNXT_DIR_RX : BNXT_DIR_TX; 1491abd43a13SVenkat Duvvuru } 1492abd43a13SVenkat Duvvuru 1493e9ecc731SSathya Perla static void bnxt_tc_set_src_fid(struct bnxt *bp, struct bnxt_tc_flow *flow, 1494e9ecc731SSathya Perla u16 src_fid) 1495e9ecc731SSathya Perla { 1496e9ecc731SSathya Perla if (flow->actions.flags & BNXT_TC_ACTION_FLAG_TUNNEL_DECAP) 1497e9ecc731SSathya Perla flow->src_fid = bp->pf.fw_fid; 1498e9ecc731SSathya Perla else 1499e9ecc731SSathya Perla flow->src_fid = src_fid; 1500e9ecc731SSathya Perla } 1501e9ecc731SSathya Perla 15022ae7408fSSathya Perla /* Add a new flow or replace an existing flow. 15032ae7408fSSathya Perla * Notes on locking: 15042ae7408fSSathya Perla * There are essentially two critical sections here. 15052ae7408fSSathya Perla * 1. while adding a new flow 15062ae7408fSSathya Perla * a) lookup l2-key 15072ae7408fSSathya Perla * b) issue HWRM cmd and get flow_handle 15082ae7408fSSathya Perla * c) link l2-key with flow 15092ae7408fSSathya Perla * 2. while deleting a flow 15102ae7408fSSathya Perla * a) unlinking l2-key from flow 15112ae7408fSSathya Perla * A lock is needed to protect these two critical sections. 15122ae7408fSSathya Perla * 15132ae7408fSSathya Perla * The hash-tables are already protected by the rhashtable API. 15142ae7408fSSathya Perla */ 15152ae7408fSSathya Perla static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid, 1516f9e30088SPablo Neira Ayuso struct flow_cls_offload *tc_flow_cmd) 15172ae7408fSSathya Perla { 15182ae7408fSSathya Perla struct bnxt_tc_flow_node *new_node, *old_node; 1519cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 15202ae7408fSSathya Perla struct bnxt_tc_flow *flow; 15218c95f773SSathya Perla __le32 tunnel_handle = 0; 15222ae7408fSSathya Perla __le16 ref_flow_handle; 15232ae7408fSSathya Perla int rc; 15242ae7408fSSathya Perla 15252ae7408fSSathya Perla /* allocate memory for the new flow and it's node */ 15262ae7408fSSathya Perla new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); 15272ae7408fSSathya Perla if (!new_node) { 15282ae7408fSSathya Perla rc = -ENOMEM; 15292ae7408fSSathya Perla goto done; 15302ae7408fSSathya Perla } 15312ae7408fSSathya Perla new_node->cookie = tc_flow_cmd->cookie; 15322ae7408fSSathya Perla flow = &new_node->flow; 15332ae7408fSSathya Perla 15342ae7408fSSathya Perla rc = bnxt_tc_parse_flow(bp, tc_flow_cmd, flow); 15352ae7408fSSathya Perla if (rc) 15362ae7408fSSathya Perla goto free_node; 1537e9ecc731SSathya Perla 1538e9ecc731SSathya Perla bnxt_tc_set_src_fid(bp, flow, src_fid); 1539685ec6a8SVenkat Duvvuru bnxt_tc_set_flow_dir(bp, flow, flow->src_fid); 1540abd43a13SVenkat Duvvuru 15412ae7408fSSathya Perla if (!bnxt_tc_can_offload(bp, flow)) { 1542b2d69122SSriharsha Basavapatna rc = -EOPNOTSUPP; 154308f8280eSSomnath Kotur kfree_rcu(new_node, rcu); 154408f8280eSSomnath Kotur return rc; 15452ae7408fSSathya Perla } 15462ae7408fSSathya Perla 15472ae7408fSSathya Perla /* If a flow exists with the same cookie, delete it */ 15482ae7408fSSathya Perla old_node = rhashtable_lookup_fast(&tc_info->flow_table, 15492ae7408fSSathya Perla &tc_flow_cmd->cookie, 15502ae7408fSSathya Perla tc_info->flow_ht_params); 15512ae7408fSSathya Perla if (old_node) 15522ae7408fSSathya Perla __bnxt_tc_del_flow(bp, old_node); 15532ae7408fSSathya Perla 15542ae7408fSSathya Perla /* Check if the L2 part of the flow has been offloaded already. 15552ae7408fSSathya Perla * If so, bump up it's refcnt and get it's reference handle. 15562ae7408fSSathya Perla */ 15572ae7408fSSathya Perla mutex_lock(&tc_info->lock); 15582ae7408fSSathya Perla rc = bnxt_tc_get_ref_flow_handle(bp, flow, new_node, &ref_flow_handle); 15592ae7408fSSathya Perla if (rc) 15602ae7408fSSathya Perla goto unlock; 15612ae7408fSSathya Perla 15628c95f773SSathya Perla /* If the flow involves tunnel encap/decap, get tunnel_handle */ 15638c95f773SSathya Perla rc = bnxt_tc_get_tunnel_handle(bp, flow, new_node, &tunnel_handle); 15642ae7408fSSathya Perla if (rc) 15652ae7408fSSathya Perla goto put_l2; 15662ae7408fSSathya Perla 15678c95f773SSathya Perla /* send HWRM cmd to alloc the flow */ 15688c95f773SSathya Perla rc = bnxt_hwrm_cfa_flow_alloc(bp, flow, ref_flow_handle, 1569abd43a13SVenkat Duvvuru tunnel_handle, new_node); 15708c95f773SSathya Perla if (rc) 15718c95f773SSathya Perla goto put_tunnel; 15728c95f773SSathya Perla 15735a84acbeSSathya Perla flow->lastused = jiffies; 15745a84acbeSSathya Perla spin_lock_init(&flow->stats_lock); 15752ae7408fSSathya Perla /* add new flow to flow-table */ 15762ae7408fSSathya Perla rc = rhashtable_insert_fast(&tc_info->flow_table, &new_node->node, 15772ae7408fSSathya Perla tc_info->flow_ht_params); 15782ae7408fSSathya Perla if (rc) 15792ae7408fSSathya Perla goto hwrm_flow_free; 15802ae7408fSSathya Perla 15812ae7408fSSathya Perla mutex_unlock(&tc_info->lock); 15822ae7408fSSathya Perla return 0; 15832ae7408fSSathya Perla 15842ae7408fSSathya Perla hwrm_flow_free: 1585abd43a13SVenkat Duvvuru bnxt_hwrm_cfa_flow_free(bp, new_node); 15868c95f773SSathya Perla put_tunnel: 15878c95f773SSathya Perla bnxt_tc_put_tunnel_handle(bp, flow, new_node); 15882ae7408fSSathya Perla put_l2: 15892ae7408fSSathya Perla bnxt_tc_put_l2_node(bp, new_node); 15902ae7408fSSathya Perla unlock: 15912ae7408fSSathya Perla mutex_unlock(&tc_info->lock); 15922ae7408fSSathya Perla free_node: 15938c95f773SSathya Perla kfree_rcu(new_node, rcu); 15942ae7408fSSathya Perla done: 15959a005c38SJonathan Lemon netdev_err(bp->dev, "Error: %s: cookie=0x%lx error=%d\n", 15962ae7408fSSathya Perla __func__, tc_flow_cmd->cookie, rc); 15972ae7408fSSathya Perla return rc; 15982ae7408fSSathya Perla } 15992ae7408fSSathya Perla 16002ae7408fSSathya Perla static int bnxt_tc_del_flow(struct bnxt *bp, 1601f9e30088SPablo Neira Ayuso struct flow_cls_offload *tc_flow_cmd) 16022ae7408fSSathya Perla { 1603cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 16042ae7408fSSathya Perla struct bnxt_tc_flow_node *flow_node; 16052ae7408fSSathya Perla 16062ae7408fSSathya Perla flow_node = rhashtable_lookup_fast(&tc_info->flow_table, 16072ae7408fSSathya Perla &tc_flow_cmd->cookie, 16082ae7408fSSathya Perla tc_info->flow_ht_params); 1609b9ecc340SSriharsha Basavapatna if (!flow_node) 16102ae7408fSSathya Perla return -EINVAL; 16112ae7408fSSathya Perla 16122ae7408fSSathya Perla return __bnxt_tc_del_flow(bp, flow_node); 16132ae7408fSSathya Perla } 16142ae7408fSSathya Perla 16152ae7408fSSathya Perla static int bnxt_tc_get_flow_stats(struct bnxt *bp, 1616f9e30088SPablo Neira Ayuso struct flow_cls_offload *tc_flow_cmd) 16172ae7408fSSathya Perla { 16185a84acbeSSathya Perla struct bnxt_tc_flow_stats stats, *curr_stats, *prev_stats; 1619cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 1620d7bc7305SSathya Perla struct bnxt_tc_flow_node *flow_node; 16215a84acbeSSathya Perla struct bnxt_tc_flow *flow; 16225a84acbeSSathya Perla unsigned long lastused; 1623d7bc7305SSathya Perla 1624d7bc7305SSathya Perla flow_node = rhashtable_lookup_fast(&tc_info->flow_table, 1625d7bc7305SSathya Perla &tc_flow_cmd->cookie, 1626d7bc7305SSathya Perla tc_info->flow_ht_params); 1627b9ecc340SSriharsha Basavapatna if (!flow_node) 1628d7bc7305SSathya Perla return -1; 1629d7bc7305SSathya Perla 16305a84acbeSSathya Perla flow = &flow_node->flow; 16315a84acbeSSathya Perla curr_stats = &flow->stats; 16325a84acbeSSathya Perla prev_stats = &flow->prev_stats; 16335a84acbeSSathya Perla 16345a84acbeSSathya Perla spin_lock(&flow->stats_lock); 16355a84acbeSSathya Perla stats.packets = curr_stats->packets - prev_stats->packets; 16365a84acbeSSathya Perla stats.bytes = curr_stats->bytes - prev_stats->bytes; 16375a84acbeSSathya Perla *prev_stats = *curr_stats; 16385a84acbeSSathya Perla lastused = flow->lastused; 16395a84acbeSSathya Perla spin_unlock(&flow->stats_lock); 16405a84acbeSSathya Perla 16413b1903efSPablo Neira Ayuso flow_stats_update(&tc_flow_cmd->stats, stats.bytes, stats.packets, 16425a84acbeSSathya Perla lastused); 16435a84acbeSSathya Perla return 0; 16445a84acbeSSathya Perla } 16455a84acbeSSathya Perla 1646abd43a13SVenkat Duvvuru static void bnxt_fill_cfa_stats_req(struct bnxt *bp, 1647abd43a13SVenkat Duvvuru struct bnxt_tc_flow_node *flow_node, 1648abd43a13SVenkat Duvvuru __le16 *flow_handle, __le32 *flow_id) 1649abd43a13SVenkat Duvvuru { 1650abd43a13SVenkat Duvvuru u16 handle; 1651abd43a13SVenkat Duvvuru 1652abd43a13SVenkat Duvvuru if (bp->fw_cap & BNXT_FW_CAP_OVS_64BIT_HANDLE) { 1653abd43a13SVenkat Duvvuru *flow_id = flow_node->flow_id; 1654abd43a13SVenkat Duvvuru 1655abd43a13SVenkat Duvvuru /* If flow_id is used to fetch flow stats then: 1656abd43a13SVenkat Duvvuru * 1. lower 12 bits of flow_handle must be set to all 1s. 1657abd43a13SVenkat Duvvuru * 2. 15th bit of flow_handle must specify the flow 1658abd43a13SVenkat Duvvuru * direction (TX/RX). 1659abd43a13SVenkat Duvvuru */ 16609bf46566SSomnath Kotur if (flow_node->flow.l2_key.dir == BNXT_DIR_RX) 1661abd43a13SVenkat Duvvuru handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_DIR_RX | 1662abd43a13SVenkat Duvvuru CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK; 1663abd43a13SVenkat Duvvuru else 1664abd43a13SVenkat Duvvuru handle = CFA_FLOW_INFO_REQ_FLOW_HANDLE_MAX_MASK; 1665abd43a13SVenkat Duvvuru 1666abd43a13SVenkat Duvvuru *flow_handle = cpu_to_le16(handle); 1667abd43a13SVenkat Duvvuru } else { 1668abd43a13SVenkat Duvvuru *flow_handle = flow_node->flow_handle; 1669abd43a13SVenkat Duvvuru } 1670abd43a13SVenkat Duvvuru } 1671abd43a13SVenkat Duvvuru 16725a84acbeSSathya Perla static int 16735a84acbeSSathya Perla bnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, int num_flows, 16745a84acbeSSathya Perla struct bnxt_tc_stats_batch stats_batch[]) 16755a84acbeSSathya Perla { 16765a84acbeSSathya Perla struct hwrm_cfa_flow_stats_input req = { 0 }; 16775c209fc8SVenkat Duvvuru struct hwrm_cfa_flow_stats_output *resp; 16785a84acbeSSathya Perla __le16 *req_flow_handles = &req.flow_handle_0; 1679abd43a13SVenkat Duvvuru __le32 *req_flow_ids = &req.flow_id_0; 16805a84acbeSSathya Perla int rc, i; 16815a84acbeSSathya Perla 16825a84acbeSSathya Perla bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_STATS, -1, -1); 16835a84acbeSSathya Perla req.num_flows = cpu_to_le16(num_flows); 16845a84acbeSSathya Perla for (i = 0; i < num_flows; i++) { 16855a84acbeSSathya Perla struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node; 16865a84acbeSSathya Perla 1687abd43a13SVenkat Duvvuru bnxt_fill_cfa_stats_req(bp, flow_node, 1688abd43a13SVenkat Duvvuru &req_flow_handles[i], &req_flow_ids[i]); 16895a84acbeSSathya Perla } 16905a84acbeSSathya Perla 16915a84acbeSSathya Perla mutex_lock(&bp->hwrm_cmd_lock); 16925a84acbeSSathya Perla rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); 16935a84acbeSSathya Perla if (!rc) { 16945c209fc8SVenkat Duvvuru __le64 *resp_packets; 16955c209fc8SVenkat Duvvuru __le64 *resp_bytes; 16965c209fc8SVenkat Duvvuru 16975c209fc8SVenkat Duvvuru resp = bnxt_get_hwrm_resp_addr(bp, &req); 16985c209fc8SVenkat Duvvuru resp_packets = &resp->packet_0; 16995c209fc8SVenkat Duvvuru resp_bytes = &resp->byte_0; 17005a84acbeSSathya Perla 17015a84acbeSSathya Perla for (i = 0; i < num_flows; i++) { 17025a84acbeSSathya Perla stats_batch[i].hw_stats.packets = 17035a84acbeSSathya Perla le64_to_cpu(resp_packets[i]); 17045a84acbeSSathya Perla stats_batch[i].hw_stats.bytes = 17055a84acbeSSathya Perla le64_to_cpu(resp_bytes[i]); 17065a84acbeSSathya Perla } 17075a84acbeSSathya Perla } else { 17089a005c38SJonathan Lemon netdev_info(bp->dev, "error rc=%d\n", rc); 17095a84acbeSSathya Perla } 17105a84acbeSSathya Perla mutex_unlock(&bp->hwrm_cmd_lock); 17116ae777eaSVenkat Duvvuru 17125a84acbeSSathya Perla return rc; 17135a84acbeSSathya Perla } 17145a84acbeSSathya Perla 17155a84acbeSSathya Perla /* Add val to accum while handling a possible wraparound 17165a84acbeSSathya Perla * of val. Eventhough val is of type u64, its actual width 17175a84acbeSSathya Perla * is denoted by mask and will wrap-around beyond that width. 17185a84acbeSSathya Perla */ 17195a84acbeSSathya Perla static void accumulate_val(u64 *accum, u64 val, u64 mask) 17205a84acbeSSathya Perla { 17215a84acbeSSathya Perla #define low_bits(x, mask) ((x) & (mask)) 17225a84acbeSSathya Perla #define high_bits(x, mask) ((x) & ~(mask)) 17235a84acbeSSathya Perla bool wrapped = val < low_bits(*accum, mask); 17245a84acbeSSathya Perla 17255a84acbeSSathya Perla *accum = high_bits(*accum, mask) + val; 17265a84acbeSSathya Perla if (wrapped) 17275a84acbeSSathya Perla *accum += (mask + 1); 17285a84acbeSSathya Perla } 17295a84acbeSSathya Perla 17305a84acbeSSathya Perla /* The HW counters' width is much less than 64bits. 17315a84acbeSSathya Perla * Handle possible wrap-around while updating the stat counters 17325a84acbeSSathya Perla */ 17335a84acbeSSathya Perla static void bnxt_flow_stats_accum(struct bnxt_tc_info *tc_info, 17345a84acbeSSathya Perla struct bnxt_tc_flow_stats *acc_stats, 17355a84acbeSSathya Perla struct bnxt_tc_flow_stats *hw_stats) 17365a84acbeSSathya Perla { 17375a84acbeSSathya Perla accumulate_val(&acc_stats->bytes, hw_stats->bytes, tc_info->bytes_mask); 17385a84acbeSSathya Perla accumulate_val(&acc_stats->packets, hw_stats->packets, 17395a84acbeSSathya Perla tc_info->packets_mask); 17405a84acbeSSathya Perla } 17415a84acbeSSathya Perla 17425a84acbeSSathya Perla static int 17435a84acbeSSathya Perla bnxt_tc_flow_stats_batch_update(struct bnxt *bp, int num_flows, 17445a84acbeSSathya Perla struct bnxt_tc_stats_batch stats_batch[]) 17455a84acbeSSathya Perla { 1746cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 17475a84acbeSSathya Perla int rc, i; 17485a84acbeSSathya Perla 17495a84acbeSSathya Perla rc = bnxt_hwrm_cfa_flow_stats_get(bp, num_flows, stats_batch); 1750d7bc7305SSathya Perla if (rc) 1751d7bc7305SSathya Perla return rc; 1752d7bc7305SSathya Perla 17535a84acbeSSathya Perla for (i = 0; i < num_flows; i++) { 17545a84acbeSSathya Perla struct bnxt_tc_flow_node *flow_node = stats_batch[i].flow_node; 17555a84acbeSSathya Perla struct bnxt_tc_flow *flow = &flow_node->flow; 17565a84acbeSSathya Perla 17575a84acbeSSathya Perla spin_lock(&flow->stats_lock); 17585a84acbeSSathya Perla bnxt_flow_stats_accum(tc_info, &flow->stats, 17595a84acbeSSathya Perla &stats_batch[i].hw_stats); 17605a84acbeSSathya Perla if (flow->stats.packets != flow->prev_stats.packets) 17615a84acbeSSathya Perla flow->lastused = jiffies; 17625a84acbeSSathya Perla spin_unlock(&flow->stats_lock); 17635a84acbeSSathya Perla } 17645a84acbeSSathya Perla 17652ae7408fSSathya Perla return 0; 17662ae7408fSSathya Perla } 17672ae7408fSSathya Perla 17685a84acbeSSathya Perla static int 17695a84acbeSSathya Perla bnxt_tc_flow_stats_batch_prep(struct bnxt *bp, 17705a84acbeSSathya Perla struct bnxt_tc_stats_batch stats_batch[], 17715a84acbeSSathya Perla int *num_flows) 17725a84acbeSSathya Perla { 1773cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 17745a84acbeSSathya Perla struct rhashtable_iter *iter = &tc_info->iter; 17755a84acbeSSathya Perla void *flow_node; 17765a84acbeSSathya Perla int rc, i; 17775a84acbeSSathya Perla 177897a6ec4aSTom Herbert rhashtable_walk_start(iter); 17795a84acbeSSathya Perla 17805a84acbeSSathya Perla rc = 0; 17815a84acbeSSathya Perla for (i = 0; i < BNXT_FLOW_STATS_BATCH_MAX; i++) { 17825a84acbeSSathya Perla flow_node = rhashtable_walk_next(iter); 17835a84acbeSSathya Perla if (IS_ERR(flow_node)) { 17845a84acbeSSathya Perla i = 0; 17855a84acbeSSathya Perla if (PTR_ERR(flow_node) == -EAGAIN) { 17865a84acbeSSathya Perla continue; 17875a84acbeSSathya Perla } else { 17885a84acbeSSathya Perla rc = PTR_ERR(flow_node); 17895a84acbeSSathya Perla goto done; 17905a84acbeSSathya Perla } 17915a84acbeSSathya Perla } 17925a84acbeSSathya Perla 17935a84acbeSSathya Perla /* No more flows */ 17945a84acbeSSathya Perla if (!flow_node) 17955a84acbeSSathya Perla goto done; 17965a84acbeSSathya Perla 17975a84acbeSSathya Perla stats_batch[i].flow_node = flow_node; 17985a84acbeSSathya Perla } 17995a84acbeSSathya Perla done: 18005a84acbeSSathya Perla rhashtable_walk_stop(iter); 18015a84acbeSSathya Perla *num_flows = i; 18025a84acbeSSathya Perla return rc; 18035a84acbeSSathya Perla } 18045a84acbeSSathya Perla 18055a84acbeSSathya Perla void bnxt_tc_flow_stats_work(struct bnxt *bp) 18065a84acbeSSathya Perla { 1807cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 18085a84acbeSSathya Perla int num_flows, rc; 18095a84acbeSSathya Perla 18105a84acbeSSathya Perla num_flows = atomic_read(&tc_info->flow_table.nelems); 18115a84acbeSSathya Perla if (!num_flows) 18125a84acbeSSathya Perla return; 18135a84acbeSSathya Perla 18145a84acbeSSathya Perla rhashtable_walk_enter(&tc_info->flow_table, &tc_info->iter); 18155a84acbeSSathya Perla 18165a84acbeSSathya Perla for (;;) { 18175a84acbeSSathya Perla rc = bnxt_tc_flow_stats_batch_prep(bp, tc_info->stats_batch, 18185a84acbeSSathya Perla &num_flows); 18195a84acbeSSathya Perla if (rc) { 18205a84acbeSSathya Perla if (rc == -EAGAIN) 18215a84acbeSSathya Perla continue; 18225a84acbeSSathya Perla break; 18235a84acbeSSathya Perla } 18245a84acbeSSathya Perla 18255a84acbeSSathya Perla if (!num_flows) 18265a84acbeSSathya Perla break; 18275a84acbeSSathya Perla 18285a84acbeSSathya Perla bnxt_tc_flow_stats_batch_update(bp, num_flows, 18295a84acbeSSathya Perla tc_info->stats_batch); 18305a84acbeSSathya Perla } 18315a84acbeSSathya Perla 18325a84acbeSSathya Perla rhashtable_walk_exit(&tc_info->iter); 18335a84acbeSSathya Perla } 18345a84acbeSSathya Perla 18352ae7408fSSathya Perla int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, 1836f9e30088SPablo Neira Ayuso struct flow_cls_offload *cls_flower) 18372ae7408fSSathya Perla { 18382ae7408fSSathya Perla switch (cls_flower->command) { 1839f9e30088SPablo Neira Ayuso case FLOW_CLS_REPLACE: 184083741bb0SJiri Pirko return bnxt_tc_add_flow(bp, src_fid, cls_flower); 1841f9e30088SPablo Neira Ayuso case FLOW_CLS_DESTROY: 184283741bb0SJiri Pirko return bnxt_tc_del_flow(bp, cls_flower); 1843f9e30088SPablo Neira Ayuso case FLOW_CLS_STATS: 184483741bb0SJiri Pirko return bnxt_tc_get_flow_stats(bp, cls_flower); 184583741bb0SJiri Pirko default: 184683741bb0SJiri Pirko return -EOPNOTSUPP; 18472ae7408fSSathya Perla } 18482ae7408fSSathya Perla } 18492ae7408fSSathya Perla 1850627c89d0SSriharsha Basavapatna static int bnxt_tc_setup_indr_block_cb(enum tc_setup_type type, 1851627c89d0SSriharsha Basavapatna void *type_data, void *cb_priv) 1852627c89d0SSriharsha Basavapatna { 1853627c89d0SSriharsha Basavapatna struct bnxt_flower_indr_block_cb_priv *priv = cb_priv; 1854627c89d0SSriharsha Basavapatna struct flow_cls_offload *flower = type_data; 1855627c89d0SSriharsha Basavapatna struct bnxt *bp = priv->bp; 1856627c89d0SSriharsha Basavapatna 1857627c89d0SSriharsha Basavapatna if (flower->common.chain_index) 1858627c89d0SSriharsha Basavapatna return -EOPNOTSUPP; 1859627c89d0SSriharsha Basavapatna 1860627c89d0SSriharsha Basavapatna switch (type) { 1861627c89d0SSriharsha Basavapatna case TC_SETUP_CLSFLOWER: 1862627c89d0SSriharsha Basavapatna return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, flower); 1863627c89d0SSriharsha Basavapatna default: 1864627c89d0SSriharsha Basavapatna return -EOPNOTSUPP; 1865627c89d0SSriharsha Basavapatna } 1866627c89d0SSriharsha Basavapatna } 1867627c89d0SSriharsha Basavapatna 1868627c89d0SSriharsha Basavapatna static struct bnxt_flower_indr_block_cb_priv * 1869627c89d0SSriharsha Basavapatna bnxt_tc_indr_block_cb_lookup(struct bnxt *bp, struct net_device *netdev) 1870627c89d0SSriharsha Basavapatna { 1871627c89d0SSriharsha Basavapatna struct bnxt_flower_indr_block_cb_priv *cb_priv; 1872627c89d0SSriharsha Basavapatna 1873627c89d0SSriharsha Basavapatna /* All callback list access should be protected by RTNL. */ 1874627c89d0SSriharsha Basavapatna ASSERT_RTNL(); 1875627c89d0SSriharsha Basavapatna 1876627c89d0SSriharsha Basavapatna list_for_each_entry(cb_priv, &bp->tc_indr_block_list, list) 1877627c89d0SSriharsha Basavapatna if (cb_priv->tunnel_netdev == netdev) 1878627c89d0SSriharsha Basavapatna return cb_priv; 1879627c89d0SSriharsha Basavapatna 1880627c89d0SSriharsha Basavapatna return NULL; 1881627c89d0SSriharsha Basavapatna } 1882627c89d0SSriharsha Basavapatna 1883627c89d0SSriharsha Basavapatna static void bnxt_tc_setup_indr_rel(void *cb_priv) 1884627c89d0SSriharsha Basavapatna { 1885627c89d0SSriharsha Basavapatna struct bnxt_flower_indr_block_cb_priv *priv = cb_priv; 1886627c89d0SSriharsha Basavapatna 1887627c89d0SSriharsha Basavapatna list_del(&priv->list); 1888627c89d0SSriharsha Basavapatna kfree(priv); 1889627c89d0SSriharsha Basavapatna } 1890627c89d0SSriharsha Basavapatna 1891627c89d0SSriharsha Basavapatna static int bnxt_tc_setup_indr_block(struct net_device *netdev, struct bnxt *bp, 1892627c89d0SSriharsha Basavapatna struct flow_block_offload *f) 1893627c89d0SSriharsha Basavapatna { 1894627c89d0SSriharsha Basavapatna struct bnxt_flower_indr_block_cb_priv *cb_priv; 1895627c89d0SSriharsha Basavapatna struct flow_block_cb *block_cb; 1896627c89d0SSriharsha Basavapatna 1897627c89d0SSriharsha Basavapatna if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 1898627c89d0SSriharsha Basavapatna return -EOPNOTSUPP; 1899627c89d0SSriharsha Basavapatna 1900627c89d0SSriharsha Basavapatna switch (f->command) { 1901627c89d0SSriharsha Basavapatna case FLOW_BLOCK_BIND: 1902627c89d0SSriharsha Basavapatna cb_priv = kmalloc(sizeof(*cb_priv), GFP_KERNEL); 1903627c89d0SSriharsha Basavapatna if (!cb_priv) 1904627c89d0SSriharsha Basavapatna return -ENOMEM; 1905627c89d0SSriharsha Basavapatna 1906627c89d0SSriharsha Basavapatna cb_priv->tunnel_netdev = netdev; 1907627c89d0SSriharsha Basavapatna cb_priv->bp = bp; 1908627c89d0SSriharsha Basavapatna list_add(&cb_priv->list, &bp->tc_indr_block_list); 1909627c89d0SSriharsha Basavapatna 1910627c89d0SSriharsha Basavapatna block_cb = flow_block_cb_alloc(bnxt_tc_setup_indr_block_cb, 1911627c89d0SSriharsha Basavapatna cb_priv, cb_priv, 1912627c89d0SSriharsha Basavapatna bnxt_tc_setup_indr_rel); 1913627c89d0SSriharsha Basavapatna if (IS_ERR(block_cb)) { 1914627c89d0SSriharsha Basavapatna list_del(&cb_priv->list); 1915627c89d0SSriharsha Basavapatna kfree(cb_priv); 1916627c89d0SSriharsha Basavapatna return PTR_ERR(block_cb); 1917627c89d0SSriharsha Basavapatna } 1918627c89d0SSriharsha Basavapatna 1919627c89d0SSriharsha Basavapatna flow_block_cb_add(block_cb, f); 1920627c89d0SSriharsha Basavapatna list_add_tail(&block_cb->driver_list, &bnxt_block_cb_list); 1921627c89d0SSriharsha Basavapatna break; 1922627c89d0SSriharsha Basavapatna case FLOW_BLOCK_UNBIND: 1923627c89d0SSriharsha Basavapatna cb_priv = bnxt_tc_indr_block_cb_lookup(bp, netdev); 1924627c89d0SSriharsha Basavapatna if (!cb_priv) 1925627c89d0SSriharsha Basavapatna return -ENOENT; 1926627c89d0SSriharsha Basavapatna 1927627c89d0SSriharsha Basavapatna block_cb = flow_block_cb_lookup(f->block, 1928627c89d0SSriharsha Basavapatna bnxt_tc_setup_indr_block_cb, 1929627c89d0SSriharsha Basavapatna cb_priv); 1930627c89d0SSriharsha Basavapatna if (!block_cb) 1931627c89d0SSriharsha Basavapatna return -ENOENT; 1932627c89d0SSriharsha Basavapatna 1933627c89d0SSriharsha Basavapatna flow_block_cb_remove(block_cb, f); 1934627c89d0SSriharsha Basavapatna list_del(&block_cb->driver_list); 1935627c89d0SSriharsha Basavapatna break; 1936627c89d0SSriharsha Basavapatna default: 1937627c89d0SSriharsha Basavapatna return -EOPNOTSUPP; 1938627c89d0SSriharsha Basavapatna } 1939627c89d0SSriharsha Basavapatna return 0; 1940627c89d0SSriharsha Basavapatna } 1941627c89d0SSriharsha Basavapatna 1942627c89d0SSriharsha Basavapatna static int bnxt_tc_setup_indr_cb(struct net_device *netdev, void *cb_priv, 1943627c89d0SSriharsha Basavapatna enum tc_setup_type type, void *type_data) 1944627c89d0SSriharsha Basavapatna { 1945627c89d0SSriharsha Basavapatna switch (type) { 1946627c89d0SSriharsha Basavapatna case TC_SETUP_BLOCK: 1947627c89d0SSriharsha Basavapatna return bnxt_tc_setup_indr_block(netdev, cb_priv, type_data); 1948627c89d0SSriharsha Basavapatna default: 1949627c89d0SSriharsha Basavapatna return -EOPNOTSUPP; 1950627c89d0SSriharsha Basavapatna } 1951627c89d0SSriharsha Basavapatna } 1952627c89d0SSriharsha Basavapatna 1953627c89d0SSriharsha Basavapatna static bool bnxt_is_netdev_indr_offload(struct net_device *netdev) 1954627c89d0SSriharsha Basavapatna { 1955627c89d0SSriharsha Basavapatna return netif_is_vxlan(netdev); 1956627c89d0SSriharsha Basavapatna } 1957627c89d0SSriharsha Basavapatna 1958627c89d0SSriharsha Basavapatna static int bnxt_tc_indr_block_event(struct notifier_block *nb, 1959627c89d0SSriharsha Basavapatna unsigned long event, void *ptr) 1960627c89d0SSriharsha Basavapatna { 1961627c89d0SSriharsha Basavapatna struct net_device *netdev; 1962627c89d0SSriharsha Basavapatna struct bnxt *bp; 1963627c89d0SSriharsha Basavapatna int rc; 1964627c89d0SSriharsha Basavapatna 1965627c89d0SSriharsha Basavapatna netdev = netdev_notifier_info_to_dev(ptr); 1966627c89d0SSriharsha Basavapatna if (!bnxt_is_netdev_indr_offload(netdev)) 1967627c89d0SSriharsha Basavapatna return NOTIFY_OK; 1968627c89d0SSriharsha Basavapatna 1969627c89d0SSriharsha Basavapatna bp = container_of(nb, struct bnxt, tc_netdev_nb); 1970627c89d0SSriharsha Basavapatna 1971627c89d0SSriharsha Basavapatna switch (event) { 1972627c89d0SSriharsha Basavapatna case NETDEV_REGISTER: 1973627c89d0SSriharsha Basavapatna rc = __flow_indr_block_cb_register(netdev, bp, 1974627c89d0SSriharsha Basavapatna bnxt_tc_setup_indr_cb, 1975627c89d0SSriharsha Basavapatna bp); 1976627c89d0SSriharsha Basavapatna if (rc) 1977627c89d0SSriharsha Basavapatna netdev_info(bp->dev, 19789a005c38SJonathan Lemon "Failed to register indirect blk: dev: %s\n", 1979627c89d0SSriharsha Basavapatna netdev->name); 1980627c89d0SSriharsha Basavapatna break; 1981627c89d0SSriharsha Basavapatna case NETDEV_UNREGISTER: 1982627c89d0SSriharsha Basavapatna __flow_indr_block_cb_unregister(netdev, 1983627c89d0SSriharsha Basavapatna bnxt_tc_setup_indr_cb, 1984627c89d0SSriharsha Basavapatna bp); 1985627c89d0SSriharsha Basavapatna break; 1986627c89d0SSriharsha Basavapatna } 1987627c89d0SSriharsha Basavapatna 1988627c89d0SSriharsha Basavapatna return NOTIFY_DONE; 1989627c89d0SSriharsha Basavapatna } 1990627c89d0SSriharsha Basavapatna 19912ae7408fSSathya Perla static const struct rhashtable_params bnxt_tc_flow_ht_params = { 19922ae7408fSSathya Perla .head_offset = offsetof(struct bnxt_tc_flow_node, node), 19932ae7408fSSathya Perla .key_offset = offsetof(struct bnxt_tc_flow_node, cookie), 19942ae7408fSSathya Perla .key_len = sizeof(((struct bnxt_tc_flow_node *)0)->cookie), 19952ae7408fSSathya Perla .automatic_shrinking = true 19962ae7408fSSathya Perla }; 19972ae7408fSSathya Perla 19982ae7408fSSathya Perla static const struct rhashtable_params bnxt_tc_l2_ht_params = { 19992ae7408fSSathya Perla .head_offset = offsetof(struct bnxt_tc_l2_node, node), 20002ae7408fSSathya Perla .key_offset = offsetof(struct bnxt_tc_l2_node, key), 20012ae7408fSSathya Perla .key_len = BNXT_TC_L2_KEY_LEN, 20022ae7408fSSathya Perla .automatic_shrinking = true 20032ae7408fSSathya Perla }; 20042ae7408fSSathya Perla 20058c95f773SSathya Perla static const struct rhashtable_params bnxt_tc_decap_l2_ht_params = { 20068c95f773SSathya Perla .head_offset = offsetof(struct bnxt_tc_l2_node, node), 20078c95f773SSathya Perla .key_offset = offsetof(struct bnxt_tc_l2_node, key), 20088c95f773SSathya Perla .key_len = BNXT_TC_L2_KEY_LEN, 20098c95f773SSathya Perla .automatic_shrinking = true 20108c95f773SSathya Perla }; 20118c95f773SSathya Perla 20128c95f773SSathya Perla static const struct rhashtable_params bnxt_tc_tunnel_ht_params = { 20138c95f773SSathya Perla .head_offset = offsetof(struct bnxt_tc_tunnel_node, node), 20148c95f773SSathya Perla .key_offset = offsetof(struct bnxt_tc_tunnel_node, key), 20158c95f773SSathya Perla .key_len = sizeof(struct ip_tunnel_key), 20168c95f773SSathya Perla .automatic_shrinking = true 20178c95f773SSathya Perla }; 20188c95f773SSathya Perla 20192ae7408fSSathya Perla /* convert counter width in bits to a mask */ 20202ae7408fSSathya Perla #define mask(width) ((u64)~0 >> (64 - (width))) 20212ae7408fSSathya Perla 20222ae7408fSSathya Perla int bnxt_init_tc(struct bnxt *bp) 20232ae7408fSSathya Perla { 2024cd66358eSSathya Perla struct bnxt_tc_info *tc_info; 20252ae7408fSSathya Perla int rc; 20262ae7408fSSathya Perla 20278c95f773SSathya Perla if (bp->hwrm_spec_code < 0x10803) { 20282ae7408fSSathya Perla netdev_warn(bp->dev, 20292ae7408fSSathya Perla "Firmware does not support TC flower offload.\n"); 20302ae7408fSSathya Perla return -ENOTSUPP; 20312ae7408fSSathya Perla } 2032cd66358eSSathya Perla 2033cd66358eSSathya Perla tc_info = kzalloc(sizeof(*tc_info), GFP_KERNEL); 2034cd66358eSSathya Perla if (!tc_info) 2035cd66358eSSathya Perla return -ENOMEM; 20362ae7408fSSathya Perla mutex_init(&tc_info->lock); 20372ae7408fSSathya Perla 20382ae7408fSSathya Perla /* Counter widths are programmed by FW */ 20392ae7408fSSathya Perla tc_info->bytes_mask = mask(36); 20402ae7408fSSathya Perla tc_info->packets_mask = mask(28); 20412ae7408fSSathya Perla 20422ae7408fSSathya Perla tc_info->flow_ht_params = bnxt_tc_flow_ht_params; 20432ae7408fSSathya Perla rc = rhashtable_init(&tc_info->flow_table, &tc_info->flow_ht_params); 20442ae7408fSSathya Perla if (rc) 2045cd66358eSSathya Perla goto free_tc_info; 20462ae7408fSSathya Perla 20472ae7408fSSathya Perla tc_info->l2_ht_params = bnxt_tc_l2_ht_params; 20482ae7408fSSathya Perla rc = rhashtable_init(&tc_info->l2_table, &tc_info->l2_ht_params); 20492ae7408fSSathya Perla if (rc) 20502ae7408fSSathya Perla goto destroy_flow_table; 20512ae7408fSSathya Perla 20528c95f773SSathya Perla tc_info->decap_l2_ht_params = bnxt_tc_decap_l2_ht_params; 20538c95f773SSathya Perla rc = rhashtable_init(&tc_info->decap_l2_table, 20548c95f773SSathya Perla &tc_info->decap_l2_ht_params); 20558c95f773SSathya Perla if (rc) 20568c95f773SSathya Perla goto destroy_l2_table; 20578c95f773SSathya Perla 20588c95f773SSathya Perla tc_info->decap_ht_params = bnxt_tc_tunnel_ht_params; 20598c95f773SSathya Perla rc = rhashtable_init(&tc_info->decap_table, 20608c95f773SSathya Perla &tc_info->decap_ht_params); 20618c95f773SSathya Perla if (rc) 20628c95f773SSathya Perla goto destroy_decap_l2_table; 20638c95f773SSathya Perla 20648c95f773SSathya Perla tc_info->encap_ht_params = bnxt_tc_tunnel_ht_params; 20658c95f773SSathya Perla rc = rhashtable_init(&tc_info->encap_table, 20668c95f773SSathya Perla &tc_info->encap_ht_params); 20678c95f773SSathya Perla if (rc) 20688c95f773SSathya Perla goto destroy_decap_table; 20698c95f773SSathya Perla 20702ae7408fSSathya Perla tc_info->enabled = true; 20712ae7408fSSathya Perla bp->dev->hw_features |= NETIF_F_HW_TC; 20722ae7408fSSathya Perla bp->dev->features |= NETIF_F_HW_TC; 2073cd66358eSSathya Perla bp->tc_info = tc_info; 2074627c89d0SSriharsha Basavapatna 2075627c89d0SSriharsha Basavapatna /* init indirect block notifications */ 2076627c89d0SSriharsha Basavapatna INIT_LIST_HEAD(&bp->tc_indr_block_list); 2077627c89d0SSriharsha Basavapatna bp->tc_netdev_nb.notifier_call = bnxt_tc_indr_block_event; 2078627c89d0SSriharsha Basavapatna rc = register_netdevice_notifier(&bp->tc_netdev_nb); 2079627c89d0SSriharsha Basavapatna if (!rc) 20802ae7408fSSathya Perla return 0; 20812ae7408fSSathya Perla 2082627c89d0SSriharsha Basavapatna rhashtable_destroy(&tc_info->encap_table); 2083627c89d0SSriharsha Basavapatna 20848c95f773SSathya Perla destroy_decap_table: 20858c95f773SSathya Perla rhashtable_destroy(&tc_info->decap_table); 20868c95f773SSathya Perla destroy_decap_l2_table: 20878c95f773SSathya Perla rhashtable_destroy(&tc_info->decap_l2_table); 20888c95f773SSathya Perla destroy_l2_table: 20898c95f773SSathya Perla rhashtable_destroy(&tc_info->l2_table); 20902ae7408fSSathya Perla destroy_flow_table: 20912ae7408fSSathya Perla rhashtable_destroy(&tc_info->flow_table); 2092cd66358eSSathya Perla free_tc_info: 2093cd66358eSSathya Perla kfree(tc_info); 20942ae7408fSSathya Perla return rc; 20952ae7408fSSathya Perla } 20962ae7408fSSathya Perla 20972ae7408fSSathya Perla void bnxt_shutdown_tc(struct bnxt *bp) 20982ae7408fSSathya Perla { 2099cd66358eSSathya Perla struct bnxt_tc_info *tc_info = bp->tc_info; 21002ae7408fSSathya Perla 2101cd66358eSSathya Perla if (!bnxt_tc_flower_enabled(bp)) 21022ae7408fSSathya Perla return; 21032ae7408fSSathya Perla 2104627c89d0SSriharsha Basavapatna unregister_netdevice_notifier(&bp->tc_netdev_nb); 21052ae7408fSSathya Perla rhashtable_destroy(&tc_info->flow_table); 21062ae7408fSSathya Perla rhashtable_destroy(&tc_info->l2_table); 21078c95f773SSathya Perla rhashtable_destroy(&tc_info->decap_l2_table); 21088c95f773SSathya Perla rhashtable_destroy(&tc_info->decap_table); 21098c95f773SSathya Perla rhashtable_destroy(&tc_info->encap_table); 2110cd66358eSSathya Perla kfree(tc_info); 2111cd66358eSSathya Perla bp->tc_info = NULL; 21122ae7408fSSathya Perla } 2113