i40e: Flow Director sideband accounting
authorJoseph Gasparakis <joseph.gasparakis@intel.com>
Wed, 12 Feb 2014 01:45:30 +0000 (01:45 +0000)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Fri, 7 Mar 2014 01:54:04 +0000 (17:54 -0800)
This patch completes implementation of the ethtool ntuple
rule management interface.  It adds the get, update and delete
interface reset.

Change-ID: Ida7f481d9ee4e405ed91340b858eabb18a52fdb5
Signed-off-by: Joseph Gasparakis <joseph.gasparakis@intel.com>
Signed-off-by: Anjali Singhai Jain <anjali.singhai@intel.com>
Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: Catherine Sullivan <catherine.sullivan@intel.com>
Tested-by: Kavindya Deegala <kavindya.s.deegala@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/i40e/i40e.h
drivers/net/ethernet/intel/i40e/i40e_debugfs.c
drivers/net/ethernet/intel/i40e/i40e_ethtool.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/i40e/i40e_txrx.c

index 72dae4d97b43e37690fe74818b818d3a9a1e14dc..ba77fca628afe40618d515f07c5d52f49cfbe211 100644 (file)
@@ -152,8 +152,18 @@ struct i40e_lump_tracking {
 };
 
 #define I40E_DEFAULT_ATR_SAMPLE_RATE   20
-#define I40E_FDIR_MAX_RAW_PACKET_LOOKUP 512
-struct i40e_fdir_data {
+#define I40E_FDIR_MAX_RAW_PACKET_SIZE   512
+struct i40e_fdir_filter {
+       struct hlist_node fdir_node;
+       /* filter ipnut set */
+       u8 flow_type;
+       u8 ip4_proto;
+       __be32 dst_ip[4];
+       __be32 src_ip[4];
+       __be16 src_port;
+       __be16 dst_port;
+       __be32 sctp_v_tag;
+       /* filter control */
        u16 q_index;
        u8  flex_off;
        u8  pctype;
@@ -162,7 +172,6 @@ struct i40e_fdir_data {
        u8  fd_status;
        u16 cnt_index;
        u32 fd_id;
-       u8  *raw_packet;
 };
 
 #define I40E_ETH_P_LLDP                        0x88cc
@@ -210,6 +219,9 @@ struct i40e_pf {
        u8 atr_sample_rate;
        bool wol_en;
 
+       struct hlist_head fdir_filter_list;
+       u16 fdir_pf_active_filters;
+
 #ifdef CONFIG_I40E_VXLAN
        __be16  vxlan_ports[I40E_MAX_PF_UDP_OFFLOAD_PORTS];
        u16 pending_vxlan_bitmap;
@@ -534,9 +546,10 @@ struct rtnl_link_stats64 *i40e_get_vsi_stats_struct(struct i40e_vsi *vsi);
 int i40e_fetch_switch_configuration(struct i40e_pf *pf,
                                    bool printconfig);
 
-int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
+int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet,
                             struct i40e_pf *pf, bool add);
-
+int i40e_add_del_fdir(struct i40e_vsi *vsi,
+                     struct i40e_fdir_filter *input, bool add);
 void i40e_set_ethtool_ops(struct net_device *netdev);
 struct i40e_mac_filter *i40e_add_filter(struct i40e_vsi *vsi,
                                        u8 *macaddr, s16 vlan,
index da22c3fa2c004a23adb8545ee01058d3dd0b2e3f..57fc86496f301d31b144ac61669d7479a621eb9b 100644 (file)
@@ -1663,21 +1663,22 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                desc = NULL;
        } else if ((strncmp(cmd_buf, "add fd_filter", 13) == 0) ||
                   (strncmp(cmd_buf, "rem fd_filter", 13) == 0)) {
-               struct i40e_fdir_data fd_data;
+               struct i40e_fdir_filter fd_data;
                u16 packet_len, i, j = 0;
                char *asc_packet;
+               u8 *raw_packet;
                bool add = false;
                int ret;
 
-               asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP,
+               asc_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE,
                                     GFP_KERNEL);
                if (!asc_packet)
                        goto command_write_done;
 
-               fd_data.raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP,
-                                            GFP_KERNEL);
+               raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE,
+                                    GFP_KERNEL);
 
-               if (!fd_data.raw_packet) {
+               if (!raw_packet) {
                        kfree(asc_packet);
                        asc_packet = NULL;
                        goto command_write_done;
@@ -1698,36 +1699,36 @@ static ssize_t i40e_dbg_command_write(struct file *filp,
                                 cnt);
                        kfree(asc_packet);
                        asc_packet = NULL;
-                       kfree(fd_data.raw_packet);
+                       kfree(raw_packet);
                        goto command_write_done;
                }
 
                /* fix packet length if user entered 0 */
                if (packet_len == 0)
-                       packet_len = I40E_FDIR_MAX_RAW_PACKET_LOOKUP;
+                       packet_len = I40E_FDIR_MAX_RAW_PACKET_SIZE;
 
                /* make sure to check the max as well */
                packet_len = min_t(u16,
-                                  packet_len, I40E_FDIR_MAX_RAW_PACKET_LOOKUP);
+                                  packet_len, I40E_FDIR_MAX_RAW_PACKET_SIZE);
 
                for (i = 0; i < packet_len; i++) {
                        sscanf(&asc_packet[j], "%2hhx ",
-                              &fd_data.raw_packet[i]);
+                              &raw_packet[i]);
                        j += 3;
                }
                dev_info(&pf->pdev->dev, "FD raw packet dump\n");
                print_hex_dump(KERN_INFO, "FD raw packet: ",
                               DUMP_PREFIX_OFFSET, 16, 1,
-                              fd_data.raw_packet, packet_len, true);
-               ret = i40e_program_fdir_filter(&fd_data, pf, add);
+                              raw_packet, packet_len, true);
+               ret = i40e_program_fdir_filter(&fd_data, raw_packet, pf, add);
                if (!ret) {
                        dev_info(&pf->pdev->dev, "Filter command send Status : Success\n");
                } else {
                        dev_info(&pf->pdev->dev,
                                 "Filter command send failed %d\n", ret);
                }
-               kfree(fd_data.raw_packet);
-               fd_data.raw_packet = NULL;
+               kfree(raw_packet);
+               raw_packet = NULL;
                kfree(asc_packet);
                asc_packet = NULL;
        } else if (strncmp(cmd_buf, "fd-atr off", 10) == 0) {
index b1d7d8c5cb9b64a362a614f8a412698723582ae8..8d46c6ee4cdd95e830e4f562788e3015b80a5073 100644 (file)
@@ -62,6 +62,9 @@ static const struct i40e_stats i40e_gstrings_net_stats[] = {
        I40E_NETDEV_STAT(rx_crc_errors),
 };
 
+static int i40e_add_del_fdir_ethtool(struct i40e_vsi *vsi,
+                                    struct ethtool_rxnfc *cmd, bool add);
+
 /* These PF_STATs might look like duplicates of some NETDEV_STATs,
  * but they are separate.  This device supports Virtualization, and
  * as such might have several netdevs supporting VMDq and FCoE going
@@ -1111,6 +1114,84 @@ static int i40e_get_rss_hash_opts(struct i40e_pf *pf, struct ethtool_rxnfc *cmd)
        return 0;
 }
 
+/**
+ * i40e_get_ethtool_fdir_all - Populates the rule count of a command
+ * @pf: Pointer to the physical function struct
+ * @cmd: The command to get or set Rx flow classification rules
+ * @rule_locs: Array of used rule locations
+ *
+ * This function populates both the total and actual rule count of
+ * the ethtool flow classification command
+ *
+ * Returns 0 on success or -EMSGSIZE if entry not found
+ **/
+static int i40e_get_ethtool_fdir_all(struct i40e_pf *pf,
+                                    struct ethtool_rxnfc *cmd,
+                                    u32 *rule_locs)
+{
+       struct i40e_fdir_filter *rule;
+       struct hlist_node *node2;
+       int cnt = 0;
+
+       /* report total rule count */
+       cmd->data = pf->hw.fdir_shared_filter_count +
+                   pf->fdir_pf_filter_count;
+
+       hlist_for_each_entry_safe(rule, node2,
+                                 &pf->fdir_filter_list, fdir_node) {
+               if (cnt == cmd->rule_cnt)
+                       return -EMSGSIZE;
+
+               rule_locs[cnt] = rule->fd_id;
+               cnt++;
+       }
+
+       cmd->rule_cnt = cnt;
+
+       return 0;
+}
+
+/**
+ * i40e_get_ethtool_fdir_entry - Look up a filter based on Rx flow
+ * @pf: Pointer to the physical function struct
+ * @cmd: The command to get or set Rx flow classification rules
+ *
+ * This function looks up a filter based on the Rx flow classification
+ * command and fills the flow spec info for it if found
+ *
+ * Returns 0 on success or -EINVAL if filter not found
+ **/
+static int i40e_get_ethtool_fdir_entry(struct i40e_pf *pf,
+                                      struct ethtool_rxnfc *cmd)
+{
+       struct ethtool_rx_flow_spec *fsp =
+                       (struct ethtool_rx_flow_spec *)&cmd->fs;
+       struct i40e_fdir_filter *rule = NULL;
+       struct hlist_node *node2;
+
+       /* report total rule count */
+       cmd->data = pf->hw.fdir_shared_filter_count +
+                   pf->fdir_pf_filter_count;
+
+       hlist_for_each_entry_safe(rule, node2,
+                                 &pf->fdir_filter_list, fdir_node) {
+               if (fsp->location <= rule->fd_id)
+                       break;
+       }
+
+       if (!rule || fsp->location != rule->fd_id)
+               return -EINVAL;
+
+       fsp->flow_type = rule->flow_type;
+       fsp->h_u.tcp_ip4_spec.psrc = rule->src_port;
+       fsp->h_u.tcp_ip4_spec.pdst = rule->dst_port;
+       fsp->h_u.tcp_ip4_spec.ip4src = rule->src_ip[0];
+       fsp->h_u.tcp_ip4_spec.ip4dst = rule->dst_ip[0];
+       fsp->ring_cookie = rule->q_index;
+
+       return 0;
+}
+
 /**
  * i40e_get_rxnfc - command to get RX flow classification rules
  * @netdev: network interface device structure
@@ -1135,15 +1216,15 @@ static int i40e_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
                ret = i40e_get_rss_hash_opts(pf, cmd);
                break;
        case ETHTOOL_GRXCLSRLCNT:
-               cmd->rule_cnt = 10;
+               cmd->rule_cnt = pf->fdir_pf_active_filters;
                ret = 0;
                break;
        case ETHTOOL_GRXCLSRULE:
-               ret = 0;
+               ret = i40e_get_ethtool_fdir_entry(pf, cmd);
                break;
        case ETHTOOL_GRXCLSRLALL:
-               cmd->data = 500;
-               ret = 0;
+               ret = i40e_get_ethtool_fdir_all(pf, cmd, rule_locs);
+               break;
        default:
                break;
        }
@@ -1274,289 +1355,158 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc)
        return 0;
 }
 
-#define IP_HEADER_OFFSET 14
-#define I40E_UDPIP_DUMMY_PACKET_LEN 42
 /**
- * i40e_add_del_fdir_udpv4 - Add/Remove UDPv4 Flow Director filters for
- * a specific flow spec
- * @vsi: pointer to the targeted VSI
- * @fd_data: the flow director data required from the FDir descriptor
- * @ethtool_rx_flow_spec: the flow spec
- * @add: true adds a filter, false removes it
+ * i40e_update_ethtool_fdir_entry - Updates the fdir filter entry
+ * @vsi: Pointer to the targeted VSI
+ * @input: The filter to update or NULL to indicate deletion
+ * @sw_idx: Software index to the filter
+ * @cmd: The command to get or set Rx flow classification rules
  *
- * Returns 0 if the filters were successfully added or removed
+ * This function updates (or deletes) a Flow Director entry from
+ * the hlist of the corresponding PF
+ *
+ * Returns 0 on success
  **/
-static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
-                                  struct i40e_fdir_data *fd_data,
-                                  struct ethtool_rx_flow_spec *fsp, bool add)
+static int i40e_update_ethtool_fdir_entry(struct i40e_vsi *vsi,
+                                         struct i40e_fdir_filter *input,
+                                         u16 sw_idx,
+                                         struct ethtool_rxnfc *cmd)
 {
+       struct i40e_fdir_filter *rule, *parent;
        struct i40e_pf *pf = vsi->back;
-       struct udphdr *udp;
-       struct iphdr *ip;
-       bool err = false;
-       int ret;
-       int i;
-       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};
-
-       memcpy(fd_data->raw_packet, packet, I40E_UDPIP_DUMMY_PACKET_LEN);
-
-       ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET);
-       udp = (struct udphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET
-             + sizeof(struct iphdr));
+       struct hlist_node *node2;
+       int err = -EINVAL;
 
-       ip->saddr = fsp->h_u.tcp_ip4_spec.ip4src;
-       ip->daddr = fsp->h_u.tcp_ip4_spec.ip4dst;
-       udp->source = fsp->h_u.tcp_ip4_spec.psrc;
-       udp->dest = fsp->h_u.tcp_ip4_spec.pdst;
+       parent = NULL;
+       rule = NULL;
 
-       for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP;
-            i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) {
-               fd_data->pctype = i;
-               ret = i40e_program_fdir_filter(fd_data, pf, add);
-
-               if (ret) {
-                       dev_info(&pf->pdev->dev,
-                                "Filter command send failed for PCTYPE %d (ret = %d)\n",
-                                fd_data->pctype, ret);
-                       err = true;
-               } else {
-                       dev_info(&pf->pdev->dev,
-                                "Filter OK for PCTYPE %d (ret = %d)\n",
-                                fd_data->pctype, ret);
-               }
+       hlist_for_each_entry_safe(rule, node2,
+                                 &pf->fdir_filter_list, fdir_node) {
+               /* hash found, or no matching entry */
+               if (rule->fd_id >= sw_idx)
+                       break;
+               parent = rule;
        }
 
-       return err ? -EOPNOTSUPP : 0;
-}
-
-#define I40E_TCPIP_DUMMY_PACKET_LEN 54
-/**
- * i40e_add_del_fdir_tcpv4 - Add/Remove TCPv4 Flow Director filters for
- * a specific flow spec
- * @vsi: pointer to the targeted VSI
- * @fd_data: the flow director data required from the FDir descriptor
- * @ethtool_rx_flow_spec: the flow spec
- * @add: true adds a filter, false removes it
- *
- * Returns 0 if the filters were successfully added or removed
- **/
-static int i40e_add_del_fdir_tcpv4(struct i40e_vsi *vsi,
-                                  struct i40e_fdir_data *fd_data,
-                                  struct ethtool_rx_flow_spec *fsp, bool add)
-{
-       struct i40e_pf *pf = vsi->back;
-       struct tcphdr *tcp;
-       struct iphdr *ip;
-       bool err = false;
-       int ret;
-       /* Dummy packet */
-       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};
-
-       memcpy(fd_data->raw_packet, packet, I40E_TCPIP_DUMMY_PACKET_LEN);
-
-       ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET);
-       tcp = (struct tcphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET
-             + sizeof(struct iphdr));
-
-       ip->daddr = fsp->h_u.tcp_ip4_spec.ip4dst;
-       tcp->dest = fsp->h_u.tcp_ip4_spec.pdst;
-       ip->saddr = fsp->h_u.tcp_ip4_spec.ip4src;
-       tcp->source = fsp->h_u.tcp_ip4_spec.psrc;
-
-       if (add) {
-               if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
-                       dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n");
-                       pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+       /* if there is an old rule occupying our place remove it */
+       if (rule && (rule->fd_id == sw_idx)) {
+               if (!input || (rule->fd_id != input->fd_id)) {
+                       cmd->fs.flow_type = rule->flow_type;
+                       err = i40e_add_del_fdir_ethtool(vsi, cmd, false);
                }
+
+               hlist_del(&rule->fdir_node);
+               kfree(rule);
+               pf->fdir_pf_active_filters--;
        }
 
-       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN;
-       ret = i40e_program_fdir_filter(fd_data, pf, add);
+       /* If no input this was a delete, err should be 0 if a rule was
+        * successfully found and removed from the list else -EINVAL
+        */
+       if (!input)
+               return err;
 
-       if (ret) {
-               dev_info(&pf->pdev->dev,
-                        "Filter command send failed for PCTYPE %d (ret = %d)\n",
-                        fd_data->pctype, ret);
-               err = true;
-       } else {
-               dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n",
-                        fd_data->pctype, ret);
-       }
+       /* initialize node and set software index */
+       INIT_HLIST_NODE(&input->fdir_node);
 
-       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
+       /* add filter to the list */
+       if (parent)
+               hlist_add_after(&parent->fdir_node, &input->fdir_node);
+       else
+               hlist_add_head(&input->fdir_node,
+                              &pf->fdir_filter_list);
 
-       ret = i40e_program_fdir_filter(fd_data, pf, add);
-       if (ret) {
-               dev_info(&pf->pdev->dev,
-                        "Filter command send failed for PCTYPE %d (ret = %d)\n",
-                        fd_data->pctype, ret);
-               err = true;
-       } else {
-               dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n",
-                         fd_data->pctype, ret);
-       }
+       /* update counts */
+       pf->fdir_pf_active_filters++;
 
-       return err ? -EOPNOTSUPP : 0;
+       return 0;
 }
 
 /**
- * i40e_add_del_fdir_sctpv4 - Add/Remove SCTPv4 Flow Director filters for
- * a specific flow spec
- * @vsi: pointer to the targeted VSI
- * @fd_data: the flow director data required from the FDir descriptor
- * @ethtool_rx_flow_spec: the flow spec
- * @add: true adds a filter, false removes it
+ * i40e_del_fdir_entry - Deletes a Flow Director filter entry
+ * @vsi: Pointer to the targeted VSI
+ * @cmd: The command to get or set Rx flow classification rules
  *
- * Returns 0 if the filters were successfully added or removed
- **/
-static int i40e_add_del_fdir_sctpv4(struct i40e_vsi *vsi,
-                                   struct i40e_fdir_data *fd_data,
-                                   struct ethtool_rx_flow_spec *fsp, bool add)
-{
-       return -EOPNOTSUPP;
-}
-
-#define I40E_IP_DUMMY_PACKET_LEN 34
-/**
- * i40e_add_del_fdir_ipv4 - 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
- * @fsp: the ethtool flow spec
- * @add: true adds a filter, false removes it
+ * The function removes a Flow Director filter entry from the
+ * hlist of the corresponding PF
  *
- * Returns 0 if the filters were successfully added or removed
- **/
-static int i40e_add_del_fdir_ipv4(struct i40e_vsi *vsi,
-                                 struct i40e_fdir_data *fd_data,
-                                 struct ethtool_rx_flow_spec *fsp, bool add)
+ * Returns 0 on success
+ */
+static int i40e_del_fdir_entry(struct i40e_vsi *vsi,
+                              struct ethtool_rxnfc *cmd)
 {
+       struct ethtool_rx_flow_spec *fsp =
+               (struct ethtool_rx_flow_spec *)&cmd->fs;
        struct i40e_pf *pf = vsi->back;
-       struct iphdr *ip;
-       bool err = false;
-       int ret;
-       int i;
-       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};
+       int ret = 0;
 
-       memcpy(fd_data->raw_packet, packet, I40E_IP_DUMMY_PACKET_LEN);
-       ip = (struct iphdr *)(fd_data->raw_packet + IP_HEADER_OFFSET);
-
-       ip->saddr = fsp->h_u.usr_ip4_spec.ip4src;
-       ip->daddr = fsp->h_u.usr_ip4_spec.ip4dst;
-       ip->protocol = fsp->h_u.usr_ip4_spec.proto;
-
-       for (i = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
-            i <= I40E_FILTER_PCTYPE_FRAG_IPV4; i++) {
-               fd_data->pctype = i;
-               ret = i40e_program_fdir_filter(fd_data, pf, add);
-
-               if (ret) {
-                       dev_info(&pf->pdev->dev,
-                                "Filter command send failed for PCTYPE %d (ret = %d)\n",
-                                fd_data->pctype, ret);
-                       err = true;
-               } else {
-                       dev_info(&pf->pdev->dev,
-                                "Filter OK for PCTYPE %d (ret = %d)\n",
-                                fd_data->pctype, ret);
-               }
-       }
+       ret = i40e_update_ethtool_fdir_entry(vsi, NULL, fsp->location, cmd);
 
-       return err ? -EOPNOTSUPP : 0;
+       return ret;
 }
 
 /**
- * i40e_add_del_fdir_ethtool - Add/Remove Flow Director filters for
- * a specific flow spec based on their protocol
+ * i40e_add_del_fdir_ethtool - Add/Remove Flow Director filters
  * @vsi: pointer to the targeted VSI
  * @cmd: command to get or set RX flow classification rules
  * @add: true adds a filter, false removes it
  *
- * Returns 0 if the filters were successfully added or removed
+ * Add/Remove Flow Director filters for a specific flow spec based on their
+ * protocol.  Returns 0 if the filters were successfully added or removed.
  **/
 static int i40e_add_del_fdir_ethtool(struct i40e_vsi *vsi,
-                       struct ethtool_rxnfc *cmd, bool add)
+                                    struct ethtool_rxnfc *cmd, bool add)
 {
-       struct i40e_fdir_data fd_data;
-       int ret = -EINVAL;
+       struct ethtool_rx_flow_spec *fsp;
+       struct i40e_fdir_filter *input;
        struct i40e_pf *pf;
-       struct ethtool_rx_flow_spec *fsp =
-               (struct ethtool_rx_flow_spec *)&cmd->fs;
+       int ret = -EINVAL;
 
        if (!vsi)
                return -EINVAL;
 
+       fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
        pf = vsi->back;
 
-       if ((fsp->ring_cookie != RX_CLS_FLOW_DISC) &&
-           (fsp->ring_cookie >= vsi->num_queue_pairs))
+       if (fsp->location >= (pf->hw.func_caps.fd_filters_best_effort +
+                             pf->hw.func_caps.fd_filters_guaranteed)) {
                return -EINVAL;
+       }
 
-       /* Populate the Flow Director that we have at the moment
-        * and allocate the raw packet buffer for the calling functions
-        */
-       fd_data.raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_LOOKUP,
-                                    GFP_KERNEL);
+       if ((fsp->ring_cookie >= vsi->num_queue_pairs) && add)
+               return -EINVAL;
 
-       if (!fd_data.raw_packet) {
-               dev_info(&pf->pdev->dev, "Could not allocate memory\n");
-               return -ENOMEM;
-       }
+       input = kzalloc(sizeof(*input), GFP_KERNEL);
 
-       fd_data.q_index = fsp->ring_cookie;
-       fd_data.flex_off = 0;
-       fd_data.pctype = 0;
-       fd_data.dest_vsi = vsi->id;
-       fd_data.dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX;
-       fd_data.fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID;
-       fd_data.cnt_index = 0;
-       fd_data.fd_id = 0;
+       if (!input)
+               return -ENOMEM;
 
-       switch (fsp->flow_type & ~FLOW_EXT) {
-       case TCP_V4_FLOW:
-               ret = i40e_add_del_fdir_tcpv4(vsi, &fd_data, fsp, add);
-               break;
-       case UDP_V4_FLOW:
-               ret = i40e_add_del_fdir_udpv4(vsi, &fd_data, fsp, add);
-               break;
-       case SCTP_V4_FLOW:
-               ret = i40e_add_del_fdir_sctpv4(vsi, &fd_data, fsp, add);
-               break;
-       case IPV4_FLOW:
-               ret = i40e_add_del_fdir_ipv4(vsi, &fd_data, fsp, add);
-               break;
-       case IP_USER_FLOW:
-               switch (fsp->h_u.usr_ip4_spec.proto) {
-               case IPPROTO_TCP:
-                       ret = i40e_add_del_fdir_tcpv4(vsi, &fd_data, fsp, add);
-                       break;
-               case IPPROTO_UDP:
-                       ret = i40e_add_del_fdir_udpv4(vsi, &fd_data, fsp, add);
-                       break;
-               case IPPROTO_SCTP:
-                       ret = i40e_add_del_fdir_sctpv4(vsi, &fd_data, fsp, add);
-                       break;
-               default:
-                       ret = i40e_add_del_fdir_ipv4(vsi, &fd_data, fsp, add);
-                       break;
-               }
-               break;
-       default:
-               dev_info(&pf->pdev->dev, "Could not specify spec type\n");
-               ret = -EINVAL;
+       input->fd_id = fsp->location;
+
+       input->q_index = fsp->ring_cookie;
+       input->flex_off = 0;
+       input->pctype = 0;
+       input->dest_vsi = vsi->id;
+       input->dest_ctl = I40E_FILTER_PROGRAM_DESC_DEST_DIRECT_PACKET_QINDEX;
+       input->fd_status = I40E_FILTER_PROGRAM_DESC_FD_STATUS_FD_ID;
+       input->cnt_index = 0;
+       input->flow_type = fsp->flow_type;
+       input->ip4_proto = fsp->h_u.usr_ip4_spec.proto;
+       input->src_port = fsp->h_u.tcp_ip4_spec.psrc;
+       input->dst_port = fsp->h_u.tcp_ip4_spec.pdst;
+       input->src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
+       input->dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
+
+       ret = i40e_add_del_fdir(vsi, input, add);
+       if (ret) {
+               kfree(input);
+               return ret;
        }
 
-       kfree(fd_data.raw_packet);
-       fd_data.raw_packet = NULL;
+       if (!ret && add)
+               i40e_update_ethtool_fdir_entry(vsi, input, fsp->location, NULL);
+       else
+               kfree(input);
 
        return ret;
 }
@@ -1583,7 +1533,7 @@ static int i40e_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
                ret = i40e_add_del_fdir_ethtool(vsi, cmd, true);
                break;
        case ETHTOOL_SRXCLSRLDEL:
-               ret = i40e_add_del_fdir_ethtool(vsi, cmd, false);
+               ret = i40e_del_fdir_entry(vsi, cmd);
                break;
        default:
                break;
index 53f3ed2df7961c6623ff80c3a7672d9cc7f39562..fa296b8098f9443c9e3d0e84444373dc0e62701e 100644 (file)
@@ -2420,6 +2420,25 @@ static void i40e_set_vsi_rx_mode(struct i40e_vsi *vsi)
                i40e_set_rx_mode(vsi->netdev);
 }
 
+/**
+ * i40e_fdir_filter_restore - Restore the Sideband Flow Director filters
+ * @vsi: Pointer to the targeted VSI
+ *
+ * This function replays the hlist on the hw where all the SB Flow Director
+ * filters were saved.
+ **/
+static void i40e_fdir_filter_restore(struct i40e_vsi *vsi)
+{
+       struct i40e_fdir_filter *filter;
+       struct i40e_pf *pf = vsi->back;
+       struct hlist_node *node;
+
+       hlist_for_each_entry_safe(filter, node,
+                                 &pf->fdir_filter_list, fdir_node) {
+               i40e_add_del_fdir(vsi, filter, true);
+       }
+}
+
 /**
  * i40e_vsi_configure - Set up the VSI for action
  * @vsi: the VSI being configured
@@ -2431,6 +2450,8 @@ static int i40e_vsi_configure(struct i40e_vsi *vsi)
        i40e_set_vsi_rx_mode(vsi);
        i40e_restore_vlan(vsi);
        i40e_vsi_config_dcb_rings(vsi);
+       if (vsi->type == I40E_VSI_FDIR)
+               i40e_fdir_filter_restore(vsi);
        err = i40e_vsi_configure_tx(vsi);
        if (!err)
                err = i40e_vsi_configure_rx(vsi);
@@ -4267,6 +4288,26 @@ err_setup_tx:
        return err;
 }
 
+/**
+ * i40e_fdir_filter_exit - Cleans up the Flow Director accounting
+ * @pf: Pointer to pf
+ *
+ * This function destroys the hlist where all the Flow Director
+ * filters were saved.
+ **/
+static void i40e_fdir_filter_exit(struct i40e_pf *pf)
+{
+       struct i40e_fdir_filter *filter;
+       struct hlist_node *node2;
+
+       hlist_for_each_entry_safe(filter, node2,
+                                 &pf->fdir_filter_list, fdir_node) {
+               hlist_del(&filter->fdir_node);
+               kfree(filter);
+       }
+       pf->fdir_pf_active_filters = 0;
+}
+
 /**
  * i40e_close - Disables a network interface
  * @netdev: network interface device structure
@@ -5131,9 +5172,9 @@ static void i40e_fdir_sb_setup(struct i40e_pf *pf)
                err = i40e_up_complete(vsi);
                if (err)
                        goto err_up_complete;
+               clear_bit(__I40E_NEEDS_RESTART, &vsi->state);
        }
 
-       clear_bit(__I40E_NEEDS_RESTART, &vsi->state);
        return;
 
 err_up_complete:
@@ -5156,6 +5197,7 @@ static void i40e_fdir_teardown(struct i40e_pf *pf)
 {
        int i;
 
+       i40e_fdir_filter_exit(pf);
        for (i = 0; i < pf->hw.func_caps.num_vsis; i++) {
                if (pf->vsi[i] && pf->vsi[i]->type == I40E_VSI_FDIR) {
                        i40e_vsi_release(pf->vsi[i]);
index 19af4ce0a4fe655001c60a43b02339bb188ba3aa..7a9fa4ec1a3219b9e33cb3e1566f7e8a65faab1b 100644 (file)
@@ -39,11 +39,12 @@ static inline __le64 build_ctob(u32 td_cmd, u32 td_offset, unsigned int size,
 #define I40E_TXD_CMD (I40E_TX_DESC_CMD_EOP | I40E_TX_DESC_CMD_RS)
 /**
  * i40e_program_fdir_filter - Program a Flow Director filter
- * @fdir_input: Packet data that will be filter parameters
+ * @fdir_data: Packet data that will be filter parameters
+ * @raw_packet: the pre-allocated packet buffer for FDir
  * @pf: The pf pointer
  * @add: True for add/update, False for remove
  **/
-int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
+int i40e_program_fdir_filter(struct i40e_fdir_filter *fdir_data, u8 *raw_packet,
                             struct i40e_pf *pf, bool add)
 {
        struct i40e_filter_program_desc *fdir_desc;
@@ -68,8 +69,8 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
        tx_ring = vsi->tx_rings[0];
        dev = tx_ring->dev;
 
-       dma = dma_map_single(dev, fdir_data->raw_packet,
-                            I40E_FDIR_MAX_RAW_PACKET_LOOKUP, DMA_TO_DEVICE);
+       dma = dma_map_single(dev, raw_packet,
+                            I40E_FDIR_MAX_RAW_PACKET_SIZE, DMA_TO_DEVICE);
        if (dma_mapping_error(dev, dma))
                goto dma_fail;
 
@@ -132,14 +133,14 @@ int i40e_program_fdir_filter(struct i40e_fdir_data *fdir_data,
        tx_ring->next_to_use = (i + 1 < tx_ring->count) ? i + 1 : 0;
 
        /* record length, and DMA address */
-       dma_unmap_len_set(tx_buf, len, I40E_FDIR_MAX_RAW_PACKET_LOOKUP);
+       dma_unmap_len_set(tx_buf, len, I40E_FDIR_MAX_RAW_PACKET_SIZE);
        dma_unmap_addr_set(tx_buf, dma, dma);
 
        tx_desc->buffer_addr = cpu_to_le64(dma);
        td_cmd = I40E_TXD_CMD | I40E_TX_DESC_CMD_DUMMY;
 
        tx_desc->cmd_type_offset_bsz =
-               build_ctob(td_cmd, 0, I40E_FDIR_MAX_RAW_PACKET_LOOKUP, 0);
+               build_ctob(td_cmd, 0, I40E_FDIR_MAX_RAW_PACKET_SIZE, 0);
 
        /* set the timestamp */
        tx_buf->time_stamp = jiffies;
@@ -161,6 +162,270 @@ 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
+ * @raw_packet: the pre-allocated packet buffer for FDir
+ * @add: true adds a filter, false removes it
+ *
+ * Returns 0 if the filters were successfully added or removed
+ **/
+static int i40e_add_del_fdir_udpv4(struct i40e_vsi *vsi,
+                                  struct i40e_fdir_filter *fd_data,
+                                  u8 *raw_packet, bool add)
+{
+       struct i40e_pf *pf = vsi->back;
+       struct udphdr *udp;
+       struct iphdr *ip;
+       bool err = false;
+       int ret;
+       int i;
+       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};
+
+       memcpy(raw_packet, packet, I40E_UDPIP_DUMMY_PACKET_LEN);
+
+       ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
+       udp = (struct udphdr *)(raw_packet + IP_HEADER_OFFSET
+             + sizeof(struct iphdr));
+
+       ip->daddr = fd_data->dst_ip[0];
+       udp->dest = fd_data->dst_port;
+       ip->saddr = fd_data->src_ip[0];
+       udp->source = fd_data->src_port;
+
+       for (i = I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP;
+            i <= I40E_FILTER_PCTYPE_NONF_IPV4_UDP; i++) {
+               fd_data->pctype = i;
+               ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+
+               if (ret) {
+                       dev_info(&pf->pdev->dev,
+                                "Filter command send failed for PCTYPE %d (ret = %d)\n",
+                                fd_data->pctype, ret);
+                       err = true;
+               } else {
+                       dev_info(&pf->pdev->dev,
+                                "Filter OK for PCTYPE %d (ret = %d)\n",
+                                fd_data->pctype, ret);
+               }
+       }
+
+       return err ? -EOPNOTSUPP : 0;
+}
+
+#define I40E_TCPIP_DUMMY_PACKET_LEN 54
+/**
+ * i40e_add_del_fdir_tcpv4 - Add/Remove TCPv4 filters
+ * @vsi: pointer to the targeted VSI
+ * @fd_data: the flow director data required for the FDir descriptor
+ * @raw_packet: the pre-allocated packet buffer for FDir
+ * @add: true adds a filter, false removes it
+ *
+ * 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,
+                                  u8 *raw_packet, bool add)
+{
+       struct i40e_pf *pf = vsi->back;
+       struct tcphdr *tcp;
+       struct iphdr *ip;
+       bool err = false;
+       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};
+
+       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[0];
+       tcp->dest = fd_data->dst_port;
+       ip->saddr = fd_data->src_ip[0];
+       tcp->source = fd_data->src_port;
+
+       if (add) {
+               if (pf->flags & I40E_FLAG_FD_ATR_ENABLED) {
+                       dev_info(&pf->pdev->dev, "Forcing ATR off, sideband rules for TCP/IPv4 flow being applied\n");
+                       pf->flags &= ~I40E_FLAG_FD_ATR_ENABLED;
+               }
+       }
+
+       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN;
+       ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+
+       if (ret) {
+               dev_info(&pf->pdev->dev,
+                        "Filter command send failed for PCTYPE %d (ret = %d)\n",
+                        fd_data->pctype, ret);
+               err = true;
+       } else {
+               dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n",
+                        fd_data->pctype, ret);
+       }
+
+       fd_data->pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP;
+
+       ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+       if (ret) {
+               dev_info(&pf->pdev->dev,
+                        "Filter command send failed for PCTYPE %d (ret = %d)\n",
+                        fd_data->pctype, ret);
+               err = true;
+       } else {
+               dev_info(&pf->pdev->dev, "Filter OK for PCTYPE %d (ret = %d)\n",
+                         fd_data->pctype, ret);
+       }
+
+       return err ? -EOPNOTSUPP : 0;
+}
+
+/**
+ * i40e_add_del_fdir_sctpv4 - 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
+ * @raw_packet: the pre-allocated packet buffer for FDir
+ * @add: true adds a filter, false removes it
+ *
+ * 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,
+                                   u8 *raw_packet, bool add)
+{
+       return -EOPNOTSUPP;
+}
+
+#define I40E_IP_DUMMY_PACKET_LEN 34
+/**
+ * i40e_add_del_fdir_ipv4 - 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
+ * @raw_packet: the pre-allocated packet buffer for FDir
+ * @add: true adds a filter, false removes it
+ *
+ * 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,
+                                 u8 *raw_packet, bool add)
+{
+       struct i40e_pf *pf = vsi->back;
+       struct iphdr *ip;
+       bool err = false;
+       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};
+
+       memcpy(raw_packet, packet, I40E_IP_DUMMY_PACKET_LEN);
+       ip = (struct iphdr *)(raw_packet + IP_HEADER_OFFSET);
+
+       ip->saddr = fd_data->src_ip[0];
+       ip->daddr = fd_data->dst_ip[0];
+       ip->protocol = 0;
+
+       for (i = I40E_FILTER_PCTYPE_NONF_IPV4_OTHER;
+            i <= I40E_FILTER_PCTYPE_FRAG_IPV4; i++) {
+               fd_data->pctype = i;
+               ret = i40e_program_fdir_filter(fd_data, raw_packet, pf, add);
+
+               if (ret) {
+                       dev_info(&pf->pdev->dev,
+                                "Filter command send failed for PCTYPE %d (ret = %d)\n",
+                                fd_data->pctype, ret);
+                       err = true;
+               } else {
+                       dev_info(&pf->pdev->dev,
+                                "Filter OK for PCTYPE %d (ret = %d)\n",
+                                fd_data->pctype, ret);
+               }
+       }
+
+       return err ? -EOPNOTSUPP : 0;
+}
+
+/**
+ * i40e_add_del_fdir - Build raw packets to add/del fdir filter
+ * @vsi: pointer to the targeted VSI
+ * @cmd: command to get or set RX flow classification rules
+ * @add: true adds a filter, false removes it
+ *
+ **/
+int i40e_add_del_fdir(struct i40e_vsi *vsi,
+                     struct i40e_fdir_filter *input, bool add)
+{
+       struct i40e_pf *pf = vsi->back;
+       u8 *raw_packet;
+       int ret;
+
+       /* Populate the Flow Director that we have at the moment
+        * and allocate the raw packet buffer for the calling functions
+        */
+       raw_packet = kzalloc(I40E_FDIR_MAX_RAW_PACKET_SIZE, GFP_KERNEL);
+       if (!raw_packet)
+               return -ENOMEM;
+
+       switch (input->flow_type & ~FLOW_EXT) {
+       case TCP_V4_FLOW:
+               ret = i40e_add_del_fdir_tcpv4(vsi, input, raw_packet,
+                                             add);
+               break;
+       case UDP_V4_FLOW:
+               ret = i40e_add_del_fdir_udpv4(vsi, input, raw_packet,
+                                             add);
+               break;
+       case SCTP_V4_FLOW:
+               ret = i40e_add_del_fdir_sctpv4(vsi, input, raw_packet,
+                                              add);
+               break;
+       case IPV4_FLOW:
+               ret = i40e_add_del_fdir_ipv4(vsi, input, raw_packet,
+                                            add);
+               break;
+       case IP_USER_FLOW:
+               switch (input->ip4_proto) {
+               case IPPROTO_TCP:
+                       ret = i40e_add_del_fdir_tcpv4(vsi, input,
+                                                     raw_packet, add);
+                       break;
+               case IPPROTO_UDP:
+                       ret = i40e_add_del_fdir_udpv4(vsi, input,
+                                                     raw_packet, add);
+                       break;
+               case IPPROTO_SCTP:
+                       ret = i40e_add_del_fdir_sctpv4(vsi, input,
+                                                      raw_packet, add);
+                       break;
+               default:
+                       ret = i40e_add_del_fdir_ipv4(vsi, input,
+                                                    raw_packet, add);
+                       break;
+               }
+               break;
+       default:
+               dev_info(&pf->pdev->dev, "Could not specify spec type %d",
+                        input->flow_type);
+               ret = -EINVAL;
+       }
+
+       kfree(raw_packet);
+       return ret;
+}
+
 /**
  * i40e_fd_handle_status - check the Programming Status for FD
  * @rx_ring: the Rx ring for this descriptor