]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
net: aquantia: add ethertype and PCP to rx flow filters
authorDmitry Bogdanov <dmitry.bogdanov@aquantia.com>
Mon, 12 Nov 2018 15:46:07 +0000 (15:46 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 14 Nov 2018 16:48:37 +0000 (08:48 -0800)
L2 EtherType filters allows to filter packet by EtherType field or
both EtherType and User Priority (PCP) field of 802.1Q.
UserPriority (vlan) parameter must be accompanied by mask 0x1FFF. That
is to distinguish VLAN filter from L2 Ethertype filter with
UserPriority since both User Priority and VLAN ID are passed in the
same 'vlan' parameter.

Example:
To add a filter that directs IP4 packess of priority 3 to queue 3:
ethtool -N <ethX> flow-type ether proto 0x800 vlan 0x600 m 0x1FFF \
action 3 loc 16

Signed-off-by: Dmitry Bogdanov <dmitry.bogdanov@aquantia.com>
Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/aquantia/atlantic/aq_filters.c
drivers/net/ethernet/aquantia/atlantic/aq_filters.h
drivers/net/ethernet/aquantia/atlantic/aq_hw.h
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.h

index c5240bc487b45d3440258b54c6aa8c6461056a1f..b987c6717af62c3687a9c5f028540b2575483ef4 100644 (file)
@@ -119,6 +119,30 @@ static int aq_check_approve_fl3l4(struct aq_nic_s *aq_nic,
        return 0;
 }
 
+static int __must_check
+aq_check_approve_fl2(struct aq_nic_s *aq_nic,
+                    struct aq_hw_rx_fltrs_s *rx_fltrs,
+                    struct ethtool_rx_flow_spec *fsp)
+{
+       if (fsp->location < AQ_RX_FIRST_LOC_FETHERT ||
+           fsp->location > AQ_RX_LAST_LOC_FETHERT) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: location must be in range [%d, %d]",
+                          AQ_RX_FIRST_LOC_FETHERT,
+                          AQ_RX_LAST_LOC_FETHERT);
+               return -EINVAL;
+       }
+
+       if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_PRIO_MASK &&
+           fsp->m_u.ether_spec.h_proto == 0U) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: proto (ether_type) parameter must be specfied");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int __must_check
 aq_check_approve_fvlan(struct aq_nic_s *aq_nic,
                       struct aq_hw_rx_fltrs_s *rx_fltrs,
@@ -152,6 +176,8 @@ aq_check_filter(struct aq_nic_s *aq_nic,
        if (fsp->flow_type & FLOW_EXT) {
                if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_VID_MASK) {
                        err = aq_check_approve_fvlan(aq_nic, rx_fltrs, fsp);
+               } else if (be16_to_cpu(fsp->m_ext.vlan_tci) == VLAN_PRIO_MASK) {
+                       err = aq_check_approve_fl2(aq_nic, rx_fltrs, fsp);
                } else {
                        netdev_err(aq_nic->ndev,
                                   "ethtool: invalid vlan mask 0x%x specified",
@@ -161,7 +187,7 @@ aq_check_filter(struct aq_nic_s *aq_nic,
        } else {
                switch (fsp->flow_type & ~FLOW_EXT) {
                case ETHER_FLOW:
-                       err = -EOPNOTSUPP;
+                       err = aq_check_approve_fl2(aq_nic, rx_fltrs, fsp);
                        break;
                case TCP_V4_FLOW:
                case UDP_V4_FLOW:
@@ -210,6 +236,10 @@ aq_rule_is_not_support(struct aq_nic_s *aq_nic,
                netdev_err(aq_nic->ndev,
                           "ethtool: The specified tos tclass are not supported\n");
                rule_is_not_support = true;
+       } else if (fsp->flow_type & FLOW_MAC_EXT) {
+               netdev_err(aq_nic->ndev,
+                          "ethtool: MAC_EXT is not supported");
+               rule_is_not_support = true;
        }
 
        return rule_is_not_support;
@@ -259,6 +289,48 @@ aq_check_rule(struct aq_nic_s *aq_nic,
        return err;
 }
 
+static void aq_set_data_fl2(struct aq_nic_s *aq_nic,
+                           struct aq_rx_filter *aq_rx_fltr,
+                           struct aq_rx_filter_l2 *data, bool add)
+{
+       const struct ethtool_rx_flow_spec *fsp = &aq_rx_fltr->aq_fsp;
+
+       memset(data, 0, sizeof(*data));
+
+       data->location = fsp->location - AQ_RX_FIRST_LOC_FETHERT;
+
+       if (fsp->ring_cookie != RX_CLS_FLOW_DISC)
+               data->queue = fsp->ring_cookie;
+       else
+               data->queue = -1;
+
+       data->ethertype = be16_to_cpu(fsp->h_u.ether_spec.h_proto);
+       data->user_priority_en = be16_to_cpu(fsp->m_ext.vlan_tci)
+                                == VLAN_PRIO_MASK;
+       data->user_priority = (be16_to_cpu(fsp->h_ext.vlan_tci)
+                              & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+}
+
+static int aq_add_del_fether(struct aq_nic_s *aq_nic,
+                            struct aq_rx_filter *aq_rx_fltr, bool add)
+{
+       struct aq_rx_filter_l2 data;
+       struct aq_hw_s *aq_hw = aq_nic->aq_hw;
+       const struct aq_hw_ops *aq_hw_ops = aq_nic->aq_hw_ops;
+
+       aq_set_data_fl2(aq_nic, aq_rx_fltr, &data, add);
+
+       if (unlikely(!aq_hw_ops->hw_filter_l2_set))
+               return -EOPNOTSUPP;
+       if (unlikely(!aq_hw_ops->hw_filter_l2_clear))
+               return -EOPNOTSUPP;
+
+       if (add)
+               return aq_hw_ops->hw_filter_l2_set(aq_hw, &data);
+       else
+               return aq_hw_ops->hw_filter_l2_clear(aq_hw, &data);
+}
+
 static int aq_set_data_fvlan(struct aq_nic_s *aq_nic,
                             struct aq_rx_filter *aq_rx_fltr,
                             struct aq_rx_filter_vlan *aq_vlans, bool add)
@@ -424,13 +496,16 @@ static int aq_add_del_rule(struct aq_nic_s *aq_nic,
                    == VLAN_VID_MASK) {
                        aq_rx_fltr->type = aq_rx_filter_vlan;
                        err = aq_add_del_fvlan(aq_nic, aq_rx_fltr, add);
-               } else {
-                       err = -EINVAL;
+               } else if (be16_to_cpu(aq_rx_fltr->aq_fsp.m_ext.vlan_tci)
+                       == VLAN_PRIO_MASK) {
+                       aq_rx_fltr->type = aq_rx_filter_ethertype;
+                       err = aq_add_del_fether(aq_nic, aq_rx_fltr, add);
                }
        } else {
                switch (aq_rx_fltr->aq_fsp.flow_type & ~FLOW_EXT) {
                case ETHER_FLOW:
-                       err = -EOPNOTSUPP;
+                       aq_rx_fltr->type = aq_rx_filter_ethertype;
+                       err = aq_add_del_fether(aq_nic, aq_rx_fltr, add);
                        break;
                case TCP_V4_FLOW:
                case UDP_V4_FLOW:
index bbaf331a55bb241259d5914f1f8125777cfcf10b..4c57c26fd3f0dc4df1874d29b668ce649cf69084 100644 (file)
@@ -9,6 +9,7 @@
 #include "aq_nic.h"
 
 enum aq_rx_filter_type {
+       aq_rx_filter_ethertype,
        aq_rx_filter_vlan,
        aq_rx_filter_l3l4
 };
index d31474c8a49865a8da31aaa38fc932d94a29029d..b0a48956e22f4b398a0489c0d44bc2c338e96006 100644 (file)
@@ -20,6 +20,8 @@
 
 #define AQ_RX_FIRST_LOC_FVLANID     0U
 #define AQ_RX_LAST_LOC_FVLANID    15U
+#define AQ_RX_FIRST_LOC_FETHERT    16U
+#define AQ_RX_LAST_LOC_FETHERT    31U
 #define AQ_RX_FIRST_LOC_FL3L4     32U
 #define AQ_RX_LAST_LOC_FL3L4      39U
 #define AQ_RX_MAX_RXNFC_LOC       AQ_RX_LAST_LOC_FL3L4
@@ -198,6 +200,12 @@ struct aq_hw_ops {
        int (*hw_filter_l3l4_clear)(struct aq_hw_s *self,
                                    struct aq_rx_filter_l3l4 *data);
 
+       int (*hw_filter_l2_set)(struct aq_hw_s *self,
+                               struct aq_rx_filter_l2 *data);
+
+       int (*hw_filter_l2_clear)(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l2 *data);
+
        int (*hw_filter_vlan_set)(struct aq_hw_s *self,
                                  struct aq_rx_filter_vlan *aq_vlans);
 
index 4ee30fa2e36be9331e67c9c88bd304db38e3a4f4..a8777751d09b2f3e4fc592ba64cd1fcd24b3db31 100644 (file)
@@ -1003,6 +1003,41 @@ static int hw_atl_b0_hw_fl3l4_set(struct aq_hw_s *self,
        return aq_hw_err_from_flags(self);
 }
 
+static int hw_atl_b0_hw_fl2_set(struct aq_hw_s *self,
+                               struct aq_rx_filter_l2 *data)
+{
+       hw_atl_rpf_etht_flr_en_set(self, 1U, data->location);
+       hw_atl_rpf_etht_flr_set(self, data->ethertype, data->location);
+       hw_atl_rpf_etht_user_priority_en_set(self,
+                                            !!data->user_priority_en,
+                                            data->location);
+       if (data->user_priority_en)
+               hw_atl_rpf_etht_user_priority_set(self,
+                                                 data->user_priority,
+                                                 data->location);
+
+       if (data->queue < 0) {
+               hw_atl_rpf_etht_flr_act_set(self, 0U, data->location);
+               hw_atl_rpf_etht_rx_queue_en_set(self, 0U, data->location);
+       } else {
+               hw_atl_rpf_etht_flr_act_set(self, 1U, data->location);
+               hw_atl_rpf_etht_rx_queue_en_set(self, 1U, data->location);
+               hw_atl_rpf_etht_rx_queue_set(self, data->queue, data->location);
+       }
+
+       return aq_hw_err_from_flags(self);
+}
+
+static int hw_atl_b0_hw_fl2_clear(struct aq_hw_s *self,
+                                 struct aq_rx_filter_l2 *data)
+{
+       hw_atl_rpf_etht_flr_en_set(self, 0U, data->location);
+       hw_atl_rpf_etht_flr_set(self, 0U, data->location);
+       hw_atl_rpf_etht_user_priority_en_set(self, 0U, data->location);
+
+       return aq_hw_err_from_flags(self);
+}
+
 /**
  * @brief Set VLAN filter table
  * @details Configure VLAN filter table to accept (and assign the queue) traffic
@@ -1063,6 +1098,8 @@ const struct aq_hw_ops hw_atl_ops_b0 = {
        .hw_ring_rx_init             = hw_atl_b0_hw_ring_rx_init,
        .hw_ring_tx_init             = hw_atl_b0_hw_ring_tx_init,
        .hw_packet_filter_set        = hw_atl_b0_hw_packet_filter_set,
+       .hw_filter_l2_set            = hw_atl_b0_hw_fl2_set,
+       .hw_filter_l2_clear          = hw_atl_b0_hw_fl2_clear,
        .hw_filter_l3l4_set          = hw_atl_b0_hw_fl3l4_set,
        .hw_filter_vlan_set          = hw_atl_b0_hw_vlan_set,
        .hw_multicast_list_set       = hw_atl_b0_hw_multicast_list_set,
index 3c5b814203618d7bcaf3ab5f01c7a25a33e1393d..48278e333462a89a5feb40d3f7864267967a5ab1 100644 (file)
@@ -252,6 +252,14 @@ struct aq_rx_filter_vlan {
        u8 queue;
 };
 
+struct aq_rx_filter_l2 {
+       s8 queue;
+       u8 location;
+       u8 user_priority_en;
+       u8 user_priority;
+       u16 ethertype;
+};
+
 struct aq_rx_filter_l3l4 {
        u32 cmd;
        u8 location;