Merge branch '40GbE' of git://git.kernel.org/pub/scm/linux/kernel/git/tnguy/net-queue
[platform/kernel/linux-rpi.git] / drivers / net / ethernet / intel / i40e / i40e_txrx.c
index 903d4e8..627794b 100644 (file)
@@ -42,9 +42,6 @@ static void i40e_fdir(struct i40e_ring *tx_ring,
        flex_ptype |= I40E_TXD_FLTR_QW0_PCTYPE_MASK &
                      (fdata->pctype << I40E_TXD_FLTR_QW0_PCTYPE_SHIFT);
 
-       flex_ptype |= I40E_TXD_FLTR_QW0_PCTYPE_MASK &
-                     (fdata->flex_offset << I40E_TXD_FLTR_QW0_FLEXOFF_SHIFT);
-
        /* Use LAN VSI Id if not programmed by user */
        flex_ptype |= I40E_TXD_FLTR_QW0_DEST_VSI_MASK &
                      ((u32)(fdata->dest_vsi ? : pf->vsi[pf->lan_vsi]->id) <<
@@ -160,59 +157,180 @@ dma_fail:
        return -1;
 }
 
-#define IP_HEADER_OFFSET 14
-#define I40E_UDPIP_DUMMY_PACKET_LEN 42
 /**
- * i40e_add_del_fdir_udpv4 - Add/Remove UDPv4 filters
- * @vsi: pointer to the targeted VSI
- * @fd_data: the flow director data required for the FDir descriptor
- * @add: true adds a filter, false removes it
+ * i40e_create_dummy_packet - Constructs dummy packet for HW
+ * @dummy_packet: preallocated space for dummy packet
+ * @ipv4: is layer 3 packet of version 4 or 6
+ * @l4proto: next level protocol used in data portion of l3
+ * @data: filter data
  *
- * Returns 0 if the filters were successfully added or removed
+ * Returns address of layer 4 protocol dummy packet.
+ **/
+static char *i40e_create_dummy_packet(u8 *dummy_packet, bool ipv4, u8 l4proto,
+                                     struct i40e_fdir_filter *data)
+{
+       bool is_vlan = !!data->vlan_tag;
+       struct vlan_hdr vlan;
+       struct ipv6hdr ipv6;
+       struct ethhdr eth;
+       struct iphdr ip;
+       u8 *tmp;
+
+       if (ipv4) {
+               eth.h_proto = cpu_to_be16(ETH_P_IP);
+               ip.protocol = l4proto;
+               ip.version = 0x4;
+               ip.ihl = 0x5;
+
+               ip.daddr = data->dst_ip;
+               ip.saddr = data->src_ip;
+       } else {
+               eth.h_proto = cpu_to_be16(ETH_P_IPV6);
+               ipv6.nexthdr = l4proto;
+               ipv6.version = 0x6;
+
+               memcpy(&ipv6.saddr.in6_u.u6_addr32, data->src_ip6,
+                      sizeof(__be32) * 4);
+               memcpy(&ipv6.daddr.in6_u.u6_addr32, data->dst_ip6,
+                      sizeof(__be32) * 4);
+       }
+
+       if (is_vlan) {
+               vlan.h_vlan_TCI = data->vlan_tag;
+               vlan.h_vlan_encapsulated_proto = eth.h_proto;
+               eth.h_proto = data->vlan_etype;
+       }
+
+       tmp = dummy_packet;
+       memcpy(tmp, &eth, sizeof(eth));
+       tmp += sizeof(eth);
+
+       if (is_vlan) {
+               memcpy(tmp, &vlan, sizeof(vlan));
+               tmp += sizeof(vlan);
+       }
+
+       if (ipv4) {
+               memcpy(tmp, &ip, sizeof(ip));
+               tmp += sizeof(ip);
+       } else {
+               memcpy(tmp, &ipv6, sizeof(ipv6));
+               tmp += sizeof(ipv6);
+       }
+
+       return tmp;
+}
+
+/**
+ * i40e_create_dummy_udp_packet - helper function to create UDP packet
+ * @raw_packet: preallocated space for dummy packet
+ * @ipv4: is layer 3 packet of version 4 or 6
+ * @l4proto: next level protocol used in data portion of l3
+ * @data: filter data
+ *
+ * Helper function to populate udp fields.
  **/
-static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
-                                  struct i40e_fdir_filter *fd_data,
-                                  bool add)
+static void i40e_create_dummy_udp_packet(u8 *raw_packet, bool ipv4, u8 l4proto,
+                                        struct i40e_fdir_filter *data)
 {
-       struct i40e_pf *pf = vsi->back;
        struct udphdr *udp;
-       struct iphdr *ip;
-       u8 *raw_packet;
-       int ret;
-       static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
-               0x45, 0, 0, 0x1c, 0, 0, 0x40, 0, 0x40, 0x11, 0, 0, 0, 0, 0, 0,
-               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+       u8 *tmp;
 
-       raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL);
-       if (!raw_packet)
-               return -ENOMEM;
-       memcpy(raw_packet, packet, I40E_UDPIP_DUMMY_PACKET_LEN);
+       tmp = i40e_create_dummy_packet(raw_packet, ipv4, IPPROTO_UDP, data);
+       udp = (struct udphdr *)(tmp);
+       udp->dest = data->dst_port;
+       udp->source = data->src_port;
+}
+
+/**
+ * i40e_create_dummy_tcp_packet - helper function to create TCP packet
+ * @raw_packet: preallocated space for dummy packet
+ * @ipv4: is layer 3 packet of version 4 or 6
+ * @l4proto: next level protocol used in data portion of l3
+ * @data: filter data
+ *
+ * Helper function to populate tcp fields.
+ **/
+static void i40e_create_dummy_tcp_packet(u8 *raw_packet, bool ipv4, u8 l4proto,
+                                        struct i40e_fdir_filter *data)
+{
+       struct tcphdr *tcp;
+       u8 *tmp;
+       /* Dummy tcp packet */
+       static const char tcp_packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+               0x50, 0x11, 0x0, 0x72, 0, 0, 0, 0};
 
-       ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
-       udp = (struct udphdr *)(raw_packet + IP_HEADER_OFFSET
-             + sizeof(struct iphdr));
+       tmp = i40e_create_dummy_packet(raw_packet, ipv4, IPPROTO_TCP, data);
 
-       ip->daddr = fd_data->dst_ip;
-       udp->dest = fd_data->dst_port;
-       ip->saddr = fd_data->src_ip;
-       udp->source = fd_data->src_port;
+       tcp = (struct tcphdr *)tmp;
+       memcpy(tcp, tcp_packet, sizeof(tcp_packet));
+       tcp->dest = data->dst_port;
+       tcp->source = data->src_port;
+}
+
+/**
+ * i40e_create_dummy_sctp_packet - helper function to create SCTP packet
+ * @raw_packet: preallocated space for dummy packet
+ * @ipv4: is layer 3 packet of version 4 or 6
+ * @l4proto: next level protocol used in data portion of l3
+ * @data: filter data
+ *
+ * Helper function to populate sctp fields.
+ **/
+static void i40e_create_dummy_sctp_packet(u8 *raw_packet, bool ipv4,
+                                         u8 l4proto,
+                                         struct i40e_fdir_filter *data)
+{
+       struct sctphdr *sctp;
+       u8 *tmp;
+
+       tmp = i40e_create_dummy_packet(raw_packet, ipv4, IPPROTO_SCTP, data);
+
+       sctp = (struct sctphdr *)tmp;
+       sctp->dest = data->dst_port;
+       sctp->source = data->src_port;
+}
+
+/**
+ * i40e_prepare_fdir_filter - Prepare and program fdir filter
+ * @pf: physical function to attach filter to
+ * @fd_data: filter data
+ * @add: add or delete filter
+ * @packet_addr: address of dummy packet, used in filtering
+ * @payload_offset: offset from dummy packet address to user defined data
+ * @pctype: Packet type for which filter is used
+ *
+ * Helper function to offset data of dummy packet, program it and
+ * handle errors.
+ **/
+static int i40e_prepare_fdir_filter(struct i40e_pf *pf,
+                                   struct i40e_fdir_filter *fd_data,
+                                   bool add, char *packet_addr,
+                                   int payload_offset, u8 pctype)
+{
+       int ret;
 
        if (fd_data->flex_filter) {
-               u8 *payload = raw_packet + I40E_UDPIP_DUMMY_PACKET_LEN;
+               u8 *payload;
                __be16 pattern = fd_data->flex_word;
                u16 off = fd_data->flex_offset;
 
+               payload = packet_addr + payload_offset;
+
+               /* If user provided vlan, offset payload by vlan header length */
+               if (!!fd_data->vlan_tag)
+                       payload += VLAN_HLEN;
+
                *((__force __be16 *)(payload + off)) = pattern;
        }
 
-       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP;
-       ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+       fd_data->pctype = pctype;
+       ret = i40e_program_fdir_filter(fd_data, packet_addr, pf, add);
        if (ret) {
                dev_info(&pf->pdev->dev,
                         "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n",
                         fd_data->pctype, fd_data->fd_id, ret);
                /* Free the packet buffer since it wasn't added to the ring */
-               kfree(raw_packet);
                return -EOPNOTSUPP;
        } else if (I40E_DEBUG_FD & pf->hw.debug_mask) {
                if (add)
@@ -225,238 +343,243 @@ static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
                                 fd_data->pctype, fd_data->fd_id);
        }
 
-       if (add)
-               pf->fd_udp4_filter_cnt++;
-       else
-               pf->fd_udp4_filter_cnt--;
+       return ret;
+}
 
-       return 0;
+/**
+ * i40e_change_filter_num - Prepare and program fdir filter
+ * @ipv4: is layer 3 packet of version 4 or 6
+ * @add: add or delete filter
+ * @ipv4_filter_num: field to update
+ * @ipv6_filter_num: field to update
+ *
+ * Update filter number field for pf.
+ **/
+static void i40e_change_filter_num(bool ipv4, bool add, u16 *ipv4_filter_num,
+                                  u16 *ipv6_filter_num)
+{
+       if (add) {
+               if (ipv4)
+                       (*ipv4_filter_num)++;
+               else
+                       (*ipv6_filter_num)++;
+       } else {
+               if (ipv4)
+                       (*ipv4_filter_num)--;
+               else
+                       (*ipv6_filter_num)--;
+       }
 }
 
-#define I40E_TCPIP_DUMMY_PACKET_LEN 54
+#define IP_HEADER_OFFSET               14
+#define I40E_UDPIP_DUMMY_PACKET_LEN    42
+#define I40E_UDPIP6_DUMMY_PACKET_LEN   62
 /**
- * i40e_add_del_fdir_tcpv4 - Add/Remove TCPv4 filters
+ * i40e_add_del_fdir_udp - Add/Remove UDP filters
  * @vsi: pointer to the targeted VSI
  * @fd_data: the flow director data required for the FDir descriptor
  * @add: true adds a filter, false removes it
+ * @ipv4: true is v4, false is v6
  *
  * Returns 0 if the filters were successfully added or removed
  **/
-static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
-                                  struct i40e_fdir_filter *fd_data,
-                                  bool add)
+static int i40e_add_del_fdir_udp(struct i40e_vsi *vsi,
+                                struct i40e_fdir_filter *fd_data,
+                                bool add,
+                                bool ipv4)
 {
        struct i40e_pf *pf = vsi->back;
-       struct tcphdr *tcp;
-       struct iphdr *ip;
        u8 *raw_packet;
        int ret;
-       /* Dummy packet */
-       static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
-               0x45, 0, 0, 0x28, 0, 0, 0x40, 0, 0x40, 0x6, 0, 0, 0, 0, 0, 0,
-               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0x11,
-               0x0, 0x72, 0, 0, 0, 0};
 
        raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL);
        if (!raw_packet)
                return -ENOMEM;
-       memcpy(raw_packet, packet, I40E_TCPIP_DUMMY_PACKET_LEN);
-
-       ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
-       tcp = (struct tcphdr *)(raw_packet + IP_HEADER_OFFSET
-             + sizeof(struct iphdr));
 
-       ip->daddr = fd_data->dst_ip;
-       tcp->dest = fd_data->dst_port;
-       ip->saddr = fd_data->src_ip;
-       tcp->source = fd_data->src_port;
+       i40e_create_dummy_udp_packet(raw_packet, ipv4, IPPROTO_UDP, fd_data);
 
-       if (fd_data->flex_filter) {
-               u8 *payload = raw_packet + I40E_TCPIP_DUMMY_PACKET_LEN;
-               __be16 pattern = fd_data->flex_word;
-               u16 off = fd_data->flex_offset;
+       if (ipv4)
+               ret = i40e_prepare_fdir_filter
+                       (pf, fd_data, add, raw_packet,
+                        I40E_UDPIP_DUMMY_PACKET_LEN,
+                        I40E_FILTER_PCTYPE_NONF_IPV4_UDP);
+       else
+               ret = i40e_prepare_fdir_filter
+                       (pf, fd_data, add, raw_packet,
+                        I40E_UDPIP6_DUMMY_PACKET_LEN,
+                        I40E_FILTER_PCTYPE_NONF_IPV6_UDP);
 
-               *((__force __be16 *)(payload + off)) = pattern;
+       if (ret) {
+               kfree(raw_packet);
+               return ret;
        }
 
-       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
-       ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+       i40e_change_filter_num(ipv4, add, &pf->fd_udp4_filter_cnt,
+                              &pf->fd_udp6_filter_cnt);
+
+       return 0;
+}
+
+#define I40E_TCPIP_DUMMY_PACKET_LEN    54
+#define I40E_TCPIP6_DUMMY_PACKET_LEN   74
+/**
+ * i40e_add_del_fdir_tcp - Add/Remove TCPv4 filters
+ * @vsi: pointer to the targeted VSI
+ * @fd_data: the flow director data required for the FDir descriptor
+ * @add: true adds a filter, false removes it
+ * @ipv4: true is v4, false is v6
+ *
+ * Returns 0 if the filters were successfully added or removed
+ **/
+static int i40e_add_del_fdir_tcp(struct i40e_vsi *vsi,
+                                struct i40e_fdir_filter *fd_data,
+                                bool add,
+                                bool ipv4)
+{
+       struct i40e_pf *pf = vsi->back;
+       u8 *raw_packet;
+       int ret;
+
+       raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL);
+       if (!raw_packet)
+               return -ENOMEM;
+
+       i40e_create_dummy_tcp_packet(raw_packet, ipv4, IPPROTO_TCP, fd_data);
+       if (ipv4)
+               ret = i40e_prepare_fdir_filter
+                       (pf, fd_data, add, raw_packet,
+                        I40E_TCPIP_DUMMY_PACKET_LEN,
+                        I40E_FILTER_PCTYPE_NONF_IPV4_TCP);
+       else
+               ret = i40e_prepare_fdir_filter
+                       (pf, fd_data, add, raw_packet,
+                        I40E_TCPIP6_DUMMY_PACKET_LEN,
+                        I40E_FILTER_PCTYPE_NONF_IPV6_TCP);
+
        if (ret) {
-               dev_info(&pf->pdev->dev,
-                        "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n",
-                        fd_data->pctype, fd_data->fd_id, ret);
-               /* Free the packet buffer since it wasn't added to the ring */
                kfree(raw_packet);
-               return -EOPNOTSUPP;
-       } else if (I40E_DEBUG_FD & pf->hw.debug_mask) {
-               if (add)
-                       dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d loc = %d)\n",
-                                fd_data->pctype, fd_data->fd_id);
-               else
-                       dev_info(&pf->pdev->dev,
-                                "Filter deleted for PCTYPE %d loc = %d\n",
-                                fd_data->pctype, fd_data->fd_id);
+               return ret;
        }
 
+       i40e_change_filter_num(ipv4, add, &pf->fd_tcp4_filter_cnt,
+                              &pf->fd_tcp6_filter_cnt);
+
        if (add) {
-               pf->fd_tcp4_filter_cnt++;
                if ((pf->flags & I40E_FLAG_FD_ATR_ENABLED) &&
                    I40E_DEBUG_FD & pf->hw.debug_mask)
                        dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n");
                set_bit(__I40E_FD_ATR_AUTO_DISABLED, pf->state);
-       } else {
-               pf->fd_tcp4_filter_cnt--;
        }
-
        return 0;
 }
 
-#define I40E_SCTPIP_DUMMY_PACKET_LEN 46
+#define I40E_SCTPIP_DUMMY_PACKET_LEN   46
+#define I40E_SCTPIP6_DUMMY_PACKET_LEN  66
 /**
- * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for
+ * i40e_add_del_fdir_sctp - Add/Remove SCTPv4 Flow Director filters for
  * a specific flow spec
  * @vsi: pointer to the targeted VSI
  * @fd_data: the flow director data required for the FDir descriptor
  * @add: true adds a filter, false removes it
+ * @ipv4: true is v4, false is v6
  *
  * Returns 0 if the filters were successfully added or removed
  **/
-static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi,
-                                   struct i40e_fdir_filter *fd_data,
-                                   bool add)
+static int i40e_add_del_fdir_sctp(struct i40e_vsi *vsi,
+                                 struct i40e_fdir_filter *fd_data,
+                                 bool add,
+                                 bool ipv4)
 {
        struct i40e_pf *pf = vsi->back;
-       struct sctphdr *sctp;
-       struct iphdr *ip;
        u8 *raw_packet;
        int ret;
-       /* Dummy packet */
-       static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
-               0x45, 0, 0, 0x20, 0, 0, 0x40, 0, 0x40, 0x84, 0, 0, 0, 0, 0, 0,
-               0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
        raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL);
        if (!raw_packet)
                return -ENOMEM;
-       memcpy(raw_packet, packet, I40E_SCTPIP_DUMMY_PACKET_LEN);
-
-       ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
-       sctp = (struct sctphdr *)(raw_packet + IP_HEADER_OFFSET
-             + sizeof(struct iphdr));
 
-       ip->daddr = fd_data->dst_ip;
-       sctp->dest = fd_data->dst_port;
-       ip->saddr = fd_data->src_ip;
-       sctp->source = fd_data->src_port;
+       i40e_create_dummy_sctp_packet(raw_packet, ipv4, IPPROTO_SCTP, fd_data);
 
-       if (fd_data->flex_filter) {
-               u8 *payload = raw_packet + I40E_SCTPIP_DUMMY_PACKET_LEN;
-               __be16 pattern = fd_data->flex_word;
-               u16 off = fd_data->flex_offset;
-
-               *((__force __be16 *)(payload + off)) = pattern;
-       }
+       if (ipv4)
+               ret = i40e_prepare_fdir_filter
+                       (pf, fd_data, add, raw_packet,
+                        I40E_SCTPIP_DUMMY_PACKET_LEN,
+                        I40E_FILTER_PCTYPE_NONF_IPV4_SCTP);
+       else
+               ret = i40e_prepare_fdir_filter
+                       (pf, fd_data, add, raw_packet,
+                        I40E_SCTPIP6_DUMMY_PACKET_LEN,
+                        I40E_FILTER_PCTYPE_NONF_IPV6_SCTP);
 
-       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_SCTP;
-       ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
        if (ret) {
-               dev_info(&pf->pdev->dev,
-                        "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n",
-                        fd_data->pctype, fd_data->fd_id, ret);
-               /* Free the packet buffer since it wasn't added to the ring */
                kfree(raw_packet);
-               return -EOPNOTSUPP;
-       } else if (I40E_DEBUG_FD & pf->hw.debug_mask) {
-               if (add)
-                       dev_info(&pf->pdev->dev,
-                                "Filter OK for PCTYPE %d loc = %d\n",
-                                fd_data->pctype, fd_data->fd_id);
-               else
-                       dev_info(&pf->pdev->dev,
-                                "Filter deleted for PCTYPE %d loc = %d\n",
-                                fd_data->pctype, fd_data->fd_id);
+               return ret;
        }
 
-       if (add)
-               pf->fd_sctp4_filter_cnt++;
-       else
-               pf->fd_sctp4_filter_cnt--;
+       i40e_change_filter_num(ipv4, add, &pf->fd_sctp4_filter_cnt,
+                              &pf->fd_sctp6_filter_cnt);
 
        return 0;
 }
 
-#define I40E_IP_DUMMY_PACKET_LEN 34
+#define I40E_IP_DUMMY_PACKET_LEN       34
+#define I40E_IP6_DUMMY_PACKET_LEN      54
 /**
- * i40e_add_del_fdir_ipv4 - Add/Remove IPv4 Flow Director filters for
+ * i40e_add_del_fdir_ip - Add/Remove IPv4 Flow Director filters for
  * a specific flow spec
  * @vsi: pointer to the targeted VSI
  * @fd_data: the flow director data required for the FDir descriptor
  * @add: true adds a filter, false removes it
+ * @ipv4: true is v4, false is v6
  *
  * Returns 0 if the filters were successfully added or removed
  **/
-static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi,
-                                 struct i40e_fdir_filter *fd_data,
-                                 bool add)
+static int i40e_add_del_fdir_ip(struct i40e_vsi *vsi,
+                               struct i40e_fdir_filter *fd_data,
+                               bool add,
+                               bool ipv4)
 {
        struct i40e_pf *pf = vsi->back;
-       struct iphdr *ip;
+       int payload_offset;
        u8 *raw_packet;
+       int iter_start;
+       int iter_end;
        int ret;
        int i;
-       static char packet[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x08, 0,
-               0x45, 0, 0, 0x14, 0, 0, 0x40, 0, 0x40, 0x10, 0, 0, 0, 0, 0, 0,
-               0, 0, 0, 0};
 
-       for (i = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
-            i <= I40E_FILTER_PCTYPE_FRAG_IPV4; i++) {
+       if (ipv4) {
+               iter_start = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
+               iter_end = I40E_FILTER_PCTYPE_FRAG_IPV4;
+       } else {
+               iter_start = I40E_FILTER_PCTYPE_NONF_IPV6_OTHER;
+               iter_end = I40E_FILTER_PCTYPE_FRAG_IPV6;
+       }
+
+       for (i = iter_start; i <= iter_end; i++) {
                raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL);
                if (!raw_packet)
                        return -ENOMEM;
-               memcpy(raw_packet, packet, I40E_IP_DUMMY_PACKET_LEN);
-               ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
-
-               ip->saddr = fd_data->src_ip;
-               ip->daddr = fd_data->dst_ip;
-               ip->protocol = 0;
 
-               if (fd_data->flex_filter) {
-                       u8 *payload = raw_packet + I40E_IP_DUMMY_PACKET_LEN;
-                       __be16 pattern = fd_data->flex_word;
-                       u16 off = fd_data->flex_offset;
+               /* IPv6 no header option differs from IPv4 */
+               (void)i40e_create_dummy_packet
+                       (raw_packet, ipv4, (ipv4) ? IPPROTO_IP : IPPROTO_NONE,
+                        fd_data);
 
-                       *((__force __be16 *)(payload + off)) = pattern;
-               }
-
-               fd_data->pctype = i;
-               ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
-               if (ret) {
-                       dev_info(&pf->pdev->dev,
-                                "PCTYPE:%d, Filter command send failed for fd_id:%d (ret = %d)\n",
-                                fd_data->pctype, fd_data->fd_id, ret);
-                       /* The packet buffer wasn't added to the ring so we
-                        * need to free it now.
-                        */
-                       kfree(raw_packet);
-                       return -EOPNOTSUPP;
-               } else if (I40E_DEBUG_FD & pf->hw.debug_mask) {
-                       if (add)
-                               dev_info(&pf->pdev->dev,
-                                        "Filter OK for PCTYPE %d loc = %d\n",
-                                        fd_data->pctype, fd_data->fd_id);
-                       else
-                               dev_info(&pf->pdev->dev,
-                                        "Filter deleted for PCTYPE %d loc = %d\n",
-                                        fd_data->pctype, fd_data->fd_id);
-               }
+               payload_offset = (ipv4) ? I40E_IP_DUMMY_PACKET_LEN :
+                       I40E_IP6_DUMMY_PACKET_LEN;
+               ret = i40e_prepare_fdir_filter(pf, fd_data, add, raw_packet,
+                                              payload_offset, i);
+               if (ret)
+                       goto err;
        }
 
-       if (add)
-               pf->fd_ip4_filter_cnt++;
-       else
-               pf->fd_ip4_filter_cnt--;
+       i40e_change_filter_num(ipv4, add, &pf->fd_ip4_filter_cnt,
+                              &pf->fd_ip6_filter_cnt);
 
        return 0;
+err:
+       kfree(raw_packet);
+       return ret;
 }
 
 /**
@@ -469,37 +592,68 @@ static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi,
 int i40e_add_del_fdir(struct i40e_vsi *vsi,
                      struct i40e_fdir_filter *input, bool add)
 {
+       enum ip_ver { ipv6 = 0, ipv4 = 1 };
        struct i40e_pf *pf = vsi->back;
        int ret;
 
        switch (input->flow_type & ~FLOW_EXT) {
        case TCP_V4_FLOW:
-               ret = i40e_add_del_fdir_tcpv4(vsi, input, add);
+               ret = i40e_add_del_fdir_tcp(vsi, input, add, ipv4);
                break;
        case UDP_V4_FLOW:
-               ret = i40e_add_del_fdir_udpv4(vsi, input, add);
+               ret = i40e_add_del_fdir_udp(vsi, input, add, ipv4);
                break;
        case SCTP_V4_FLOW:
-               ret = i40e_add_del_fdir_sctpv4(vsi, input, add);
+               ret = i40e_add_del_fdir_sctp(vsi, input, add, ipv4);
+               break;
+       case TCP_V6_FLOW:
+               ret = i40e_add_del_fdir_tcp(vsi, input, add, ipv6);
+               break;
+       case UDP_V6_FLOW:
+               ret = i40e_add_del_fdir_udp(vsi, input, add, ipv6);
+               break;
+       case SCTP_V6_FLOW:
+               ret = i40e_add_del_fdir_sctp(vsi, input, add, ipv6);
                break;
        case IP_USER_FLOW:
-               switch (input->ip4_proto) {
+               switch (input->ipl4_proto) {
                case IPPROTO_TCP:
-                       ret = i40e_add_del_fdir_tcpv4(vsi, input, add);
+                       ret = i40e_add_del_fdir_tcp(vsi, input, add, ipv4);
                        break;
                case IPPROTO_UDP:
-                       ret = i40e_add_del_fdir_udpv4(vsi, input, add);
+                       ret = i40e_add_del_fdir_udp(vsi, input, add, ipv4);
                        break;
                case IPPROTO_SCTP:
-                       ret = i40e_add_del_fdir_sctpv4(vsi, input, add);
+                       ret = i40e_add_del_fdir_sctp(vsi, input, add, ipv4);
                        break;
                case IPPROTO_IP:
-                       ret = i40e_add_del_fdir_ipv4(vsi, input, add);
+                       ret = i40e_add_del_fdir_ip(vsi, input, add, ipv4);
                        break;
                default:
                        /* We cannot support masking based on protocol */
                        dev_info(&pf->pdev->dev, "Unsupported IPv4 protocol 0x%02x\n",
-                                input->ip4_proto);
+                                input->ipl4_proto);
+                       return -EINVAL;
+               }
+               break;
+       case IPV6_USER_FLOW:
+               switch (input->ipl4_proto) {
+               case IPPROTO_TCP:
+                       ret = i40e_add_del_fdir_tcp(vsi, input, add, ipv6);
+                       break;
+               case IPPROTO_UDP:
+                       ret = i40e_add_del_fdir_udp(vsi, input, add, ipv6);
+                       break;
+               case IPPROTO_SCTP:
+                       ret = i40e_add_del_fdir_sctp(vsi, input, add, ipv6);
+                       break;
+               case IPPROTO_IP:
+                       ret = i40e_add_del_fdir_ip(vsi, input, add, ipv6);
+                       break;
+               default:
+                       /* We cannot support masking based on protocol */
+                       dev_info(&pf->pdev->dev, "Unsupported IPv6 protocol 0x%02x\n",
+                                input->ipl4_proto);
                        return -EINVAL;
                }
                break;
@@ -1416,6 +1570,17 @@ void i40e_free_rx_resources(struct i40e_ring *rx_ring)
 }
 
 /**
+ * i40e_rx_offset - Return expected offset into page to access data
+ * @rx_ring: Ring we are requesting offset of
+ *
+ * Returns the offset value for ring into the data buffer.
+ */
+static unsigned int i40e_rx_offset(struct i40e_ring *rx_ring)
+{
+       return ring_uses_build_skb(rx_ring) ? I40E_SKB_PAD : 0;
+}
+
+/**
  * i40e_setup_rx_descriptors - Allocate Rx descriptors
  * @rx_ring: Rx descriptor ring (for a specific queue) to setup
  *
@@ -1443,6 +1608,7 @@ int i40e_setup_rx_descriptors(struct i40e_ring *rx_ring)
        rx_ring->next_to_alloc = 0;
        rx_ring->next_to_clean = 0;
        rx_ring->next_to_use = 0;
+       rx_ring->rx_offset = i40e_rx_offset(rx_ring);
 
        /* XDP RX-queue info only needed for RX rings exposed to XDP */
        if (rx_ring->vsi->type == I40E_VSI_MAIN) {
@@ -1478,17 +1644,6 @@ void i40e_release_rx_desc(struct i40e_ring *rx_ring, u32 val)
        writel(val, rx_ring->tail);
 }
 
-/**
- * i40e_rx_offset - Return expected offset into page to access data
- * @rx_ring: Ring we are requesting offset of
- *
- * Returns the offset value for ring into the data buffer.
- */
-static inline unsigned int i40e_rx_offset(struct i40e_ring *rx_ring)
-{
-       return ring_uses_build_skb(rx_ring) ? I40E_SKB_PAD : 0;
-}
-
 static unsigned int i40e_rx_frame_truesize(struct i40e_ring *rx_ring,
                                           unsigned int size)
 {
@@ -1497,8 +1652,8 @@ static unsigned int i40e_rx_frame_truesize(struct i40e_ring *rx_ring,
 #if (PAGE_SIZE < 8192)
        truesize = i40e_rx_pg_size(rx_ring) / 2; /* Must be power-of-2 */
 #else
-       truesize = i40e_rx_offset(rx_ring) ?
-               SKB_DATA_ALIGN(size + i40e_rx_offset(rx_ring)) +
+       truesize = rx_ring->rx_offset ?
+               SKB_DATA_ALIGN(size + rx_ring->rx_offset) +
                SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) :
                SKB_DATA_ALIGN(size);
 #endif
@@ -1549,7 +1704,7 @@ static bool i40e_alloc_mapped_page(struct i40e_ring *rx_ring,
 
        bi->dma = dma;
        bi->page = page;
-       bi->page_offset = i40e_rx_offset(rx_ring);
+       bi->page_offset = rx_ring->rx_offset;
        page_ref_add(page, USHRT_MAX - 1);
        bi->pagecnt_bias = USHRT_MAX;
 
@@ -1809,9 +1964,6 @@ void i40e_process_skb_fields(struct i40e_ring *rx_ring,
  * @skb: pointer to current skb being fixed
  * @rx_desc: pointer to the EOP Rx descriptor
  *
- * Also address the case where we are pulling data in on pages only
- * and as such no data is present in the skb header.
- *
  * In addition if skb is not at least 60 bytes we need to pad it so that
  * it is large enough to qualify as a valid Ethernet frame.
  *
@@ -1844,46 +1996,15 @@ static bool i40e_cleanup_headers(struct i40e_ring *rx_ring, struct sk_buff *skb,
 }
 
 /**
- * i40e_page_is_reusable - check if any reuse is possible
- * @page: page struct to check
- *
- * A page is not reusable if it was allocated under low memory
- * conditions, or it's not in the same NUMA node as this CPU.
- */
-static inline bool i40e_page_is_reusable(struct page *page)
-{
-       return (page_to_nid(page) == numa_mem_id()) &&
-               !page_is_pfmemalloc(page);
-}
-
-/**
- * i40e_can_reuse_rx_page - Determine if this page can be reused by
- * the adapter for another receive
- *
+ * i40e_can_reuse_rx_page - Determine if page can be reused for another Rx
  * @rx_buffer: buffer containing the page
  * @rx_buffer_pgcnt: buffer page refcount pre xdp_do_redirect() call
  *
- * If page is reusable, rx_buffer->page_offset is adjusted to point to
- * an unused region in the page.
- *
- * For small pages, @truesize will be a constant value, half the size
- * of the memory at page.  We'll attempt to alternate between high and
- * low halves of the page, with one half ready for use by the hardware
- * and the other half being consumed by the stack.  We use the page
- * ref count to determine whether the stack has finished consuming the
- * portion of this page that was passed up with a previous packet.  If
- * the page ref count is >1, we'll assume the "other" half page is
- * still busy, and this page cannot be reused.
- *
- * For larger pages, @truesize will be the actual space used by the
- * received packet (adjusted upward to an even multiple of the cache
- * line size).  This will advance through the page by the amount
- * actually consumed by the received packets while there is still
- * space for a buffer.  Each region of larger pages will be used at
- * most once, after which the page will not be reused.
- *
- * In either case, if the page is reusable its refcount is increased.
- **/
+ * If page is reusable, we have a green light for calling i40e_reuse_rx_page,
+ * which will assign the current buffer to the buffer that next_to_alloc is
+ * pointing to; otherwise, the DMA mapping needs to be destroyed and
+ * page freed
+ */
 static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer,
                                   int rx_buffer_pgcnt)
 {
@@ -1891,7 +2012,7 @@ static bool i40e_can_reuse_rx_page(struct i40e_rx_buffer *rx_buffer,
        struct page *page = rx_buffer->page;
 
        /* Is any reuse possible? */
-       if (unlikely(!i40e_page_is_reusable(page)))
+       if (!dev_page_is_reusable(page))
                return false;
 
 #if (PAGE_SIZE < 8192)
@@ -1937,7 +2058,7 @@ static void i40e_add_rx_frag(struct i40e_ring *rx_ring,
 #if (PAGE_SIZE < 8192)
        unsigned int truesize = i40e_rx_pg_size(rx_ring) / 2;
 #else
-       unsigned int truesize = SKB_DATA_ALIGN(size + i40e_rx_offset(rx_ring));
+       unsigned int truesize = SKB_DATA_ALIGN(size + rx_ring->rx_offset);
 #endif
 
        skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, rx_buffer->page,
@@ -2151,25 +2272,13 @@ static void i40e_put_rx_buffer(struct i40e_ring *rx_ring,
  * i40e_is_non_eop - process handling of non-EOP buffers
  * @rx_ring: Rx ring being processed
  * @rx_desc: Rx descriptor for current buffer
- * @skb: Current socket buffer containing buffer in progress
  *
- * This function updates next to clean.  If the buffer is an EOP buffer
- * this function exits returning false, otherwise it will place the
- * sk_buff in the next buffer to be chained and return true indicating
- * that this is in fact a non-EOP buffer.
- **/
+ * If the buffer is an EOP buffer, this function exits returning false,
+ * otherwise return true indicating that this is in fact a non-EOP buffer.
+ */
 static bool i40e_is_non_eop(struct i40e_ring *rx_ring,
-                           union i40e_rx_desc *rx_desc,
-                           struct sk_buff *skb)
+                           union i40e_rx_desc *rx_desc)
 {
-       u32 ntc = rx_ring->next_to_clean + 1;
-
-       /* fetch, update, and store next to clean */
-       ntc = (ntc < rx_ring->count) ? ntc : 0;
-       rx_ring->next_to_clean = ntc;
-
-       prefetch(I40E_RX_DESC(rx_ring, ntc));
-
        /* if we are the last buffer then there is nothing else to do */
 #define I40E_RXD_EOF BIT(I40E_RX_DESC_STATUS_EOF_SHIFT)
        if (likely(i40e_test_staterr(rx_desc, I40E_RXD_EOF)))
@@ -2344,17 +2453,18 @@ static void i40e_inc_ntc(struct i40e_ring *rx_ring)
  **/
 static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
 {
-       unsigned int total_rx_bytes = 0, total_rx_packets = 0;
-       struct sk_buff *skb = rx_ring->skb;
+       unsigned int total_rx_bytes = 0, total_rx_packets = 0, frame_sz = 0;
        u16 cleaned_count = I40E_DESC_UNUSED(rx_ring);
+       unsigned int offset = rx_ring->rx_offset;
+       struct sk_buff *skb = rx_ring->skb;
        unsigned int xdp_xmit = 0;
        bool failure = false;
        struct xdp_buff xdp;
 
 #if (PAGE_SIZE < 8192)
-       xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, 0);
+       frame_sz = i40e_rx_frame_truesize(rx_ring, 0);
 #endif
-       xdp.rxq = &rx_ring->xdp_rxq;
+       xdp_init_buff(&xdp, frame_sz, &rx_ring->xdp_rxq);
 
        while (likely(total_rx_packets < (unsigned int)budget)) {
                struct i40e_rx_buffer *rx_buffer;
@@ -2406,12 +2516,11 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
 
                /* retrieve a buffer from the ring */
                if (!skb) {
-                       xdp.data = page_address(rx_buffer->page) +
-                                  rx_buffer->page_offset;
-                       xdp.data_meta = xdp.data;
-                       xdp.data_hard_start = xdp.data -
-                                             i40e_rx_offset(rx_ring);
-                       xdp.data_end = xdp.data + size;
+                       unsigned char *hard_start;
+
+                       hard_start = page_address(rx_buffer->page) +
+                                    rx_buffer->page_offset - offset;
+                       xdp_prepare_buff(&xdp, hard_start, offset, size, true);
 #if (PAGE_SIZE > 4096)
                        /* At larger PAGE_SIZE, frame_sz depend on len size */
                        xdp.frame_sz = i40e_rx_frame_truesize(rx_ring, size);
@@ -2448,7 +2557,8 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
                i40e_put_rx_buffer(rx_ring, rx_buffer, rx_buffer_pgcnt);
                cleaned_count++;
 
-               if (i40e_is_non_eop(rx_ring, rx_desc, skb))
+               i40e_inc_ntc(rx_ring);
+               if (i40e_is_non_eop(rx_ring, rx_desc))
                        continue;
 
                if (i40e_cleanup_headers(rx_ring, skb, rx_desc)) {