ice: Add support for VLANs and offloads
authorAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tue, 20 Mar 2018 14:58:15 +0000 (07:58 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Mon, 26 Mar 2018 18:54:49 +0000 (11:54 -0700)
This patch adds support for VLANs. When a VLAN is created a switch filter
is added to direct the VLAN traffic to the corresponding VSI. When a VLAN
is deleted, the filter is deleted as well.

This patch also adds support for the following hardware offloads.
    1) VLAN tag insertion/stripping
    2) Receive Side Scaling (RSS)
    3) Tx checksum and TCP segmentation
    4) Rx checksum

Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: Tony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
drivers/net/ethernet/intel/ice/ice.h
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
drivers/net/ethernet/intel/ice/ice_common.c
drivers/net/ethernet/intel/ice/ice_common.h
drivers/net/ethernet/intel/ice/ice_lan_tx_rx.h
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_switch.c
drivers/net/ethernet/intel/ice/ice_switch.h
drivers/net/ethernet/intel/ice/ice_txrx.c
drivers/net/ethernet/intel/ice/ice_txrx.h

index 6f01395a7f3439bb359dc64ea9a940d5b1763a00..51cc955c7220cd2129c9f8f4f2619bf96dfad5f4 100644 (file)
 #include <linux/delay.h>
 #include <linux/bitmap.h>
 #include <linux/log2.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
 #include <linux/if_bridge.h>
+#include <net/ipv6.h>
 #include "ice_devids.h"
 #include "ice_type.h"
 #include "ice_txrx.h"
@@ -47,6 +50,8 @@
 #define ICE_MAX_SCATTER_RXQS   16
 #define ICE_Q_WAIT_RETRY_LIMIT 10
 #define ICE_Q_WAIT_MAX_RETRY   (5 * ICE_Q_WAIT_RETRY_LIMIT)
+#define ICE_MAX_LG_RSS_QS      256
+#define ICE_MAX_SMALL_RSS_QS   8
 #define ICE_RES_VALID_BIT      0x8000
 #define ICE_RES_MISC_VEC_ID    (ICE_RES_VALID_BIT - 1)
 #define ICE_INVAL_Q_INDEX      0xffff
@@ -62,6 +67,7 @@
 
 #define ICE_TX_DESC(R, i) (&(((struct ice_tx_desc *)((R)->desc))[i]))
 #define ICE_RX_DESC(R, i) (&(((union ice_32b_rx_flex_desc *)((R)->desc))[i]))
+#define ICE_TX_CTX_DESC(R, i) (&(((struct ice_tx_ctx_desc *)((R)->desc))[i]))
 
 #define ice_for_each_txq(vsi, i) \
        for ((i) = 0; (i) < (vsi)->num_txq; (i)++)
@@ -113,6 +119,7 @@ struct ice_vsi {
        irqreturn_t (*irq_handler)(int irq, void *data);
 
        DECLARE_BITMAP(state, __ICE_STATE_NBITS);
+       unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)];
        int num_q_vectors;
        int base_vector;
        enum ice_vsi_type type;
@@ -122,6 +129,13 @@ struct ice_vsi {
        /* Interrupt thresholds */
        u16 work_lmt;
 
+       /* RSS config */
+       u16 rss_table_size;     /* HW RSS table size */
+       u16 rss_size;           /* Allocated RSS queues */
+       u8 *rss_hkey_user;      /* User configured hash keys */
+       u8 *rss_lut_user;       /* User configured lookup table entries */
+       u8 rss_lut_type;        /* used to configure Get/Set RSS LUT AQ call */
+
        u16 max_frame;
        u16 rx_buf_len;
 
@@ -181,6 +195,7 @@ struct ice_pf {
        struct mutex avail_q_mutex;     /* protects access to avail_[rx|tx]qs */
        struct mutex sw_mutex;          /* lock for protecting VSI alloc flow */
        u32 msg_enable;
+       u32 hw_csum_rx_error;
        u32 oicr_idx;           /* Other interrupt cause vector index */
        u32 num_lan_msix;       /* Total MSIX vectors for base driver */
        u32 num_avail_msix;     /* remaining MSIX vectors left unclaimed */
@@ -224,4 +239,8 @@ static inline void ice_irq_dynamic_ena(struct ice_hw *hw, struct ice_vsi *vsi,
        wr32(hw, GLINT_DYN_CTL(vector), val);
 }
 
+int ice_set_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
+int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size);
+void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size);
+
 #endif /* _ICE_H_ */
index c7abcc1dbbcc356aa68eaa2f70a837937ebfe960..051427dcfb3c463b71e92a569d2bfad1fac5b389 100644 (file)
@@ -968,6 +968,60 @@ struct ice_aqc_nvm {
        __le32  addr_low;
 };
 
+/* Get/Set RSS key (indirect 0x0B04/0x0B02) */
+struct ice_aqc_get_set_rss_key {
+#define ICE_AQC_GSET_RSS_KEY_VSI_VALID BIT(15)
+#define ICE_AQC_GSET_RSS_KEY_VSI_ID_S  0
+#define ICE_AQC_GSET_RSS_KEY_VSI_ID_M  (0x3FF << ICE_AQC_GSET_RSS_KEY_VSI_ID_S)
+       __le16 vsi_id;
+       u8 reserved[6];
+       __le32 addr_high;
+       __le32 addr_low;
+};
+
+#define ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE      0x28
+#define ICE_AQC_GET_SET_RSS_KEY_DATA_HASH_KEY_SIZE     0xC
+
+struct ice_aqc_get_set_rss_keys {
+       u8 standard_rss_key[ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE];
+       u8 extended_hash_key[ICE_AQC_GET_SET_RSS_KEY_DATA_HASH_KEY_SIZE];
+};
+
+/* Get/Set RSS LUT (indirect 0x0B05/0x0B03) */
+struct  ice_aqc_get_set_rss_lut {
+#define ICE_AQC_GSET_RSS_LUT_VSI_VALID BIT(15)
+#define ICE_AQC_GSET_RSS_LUT_VSI_ID_S  0
+#define ICE_AQC_GSET_RSS_LUT_VSI_ID_M  (0x1FF << ICE_AQC_GSET_RSS_LUT_VSI_ID_S)
+       __le16 vsi_id;
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_S      0
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_M      \
+                               (0x3 << ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_S)
+
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_VSI     0
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF      1
+#define ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_GLOBAL  2
+
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S       2
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_M       \
+                               (0x3 << ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S)
+
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_128     128
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_128_FLAG 0
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_512     512
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_512_FLAG 1
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_2K      2048
+#define ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_2K_FLAG         2
+
+#define ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_S       4
+#define ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_M       \
+                               (0xF << ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_S)
+
+       __le16 flags;
+       __le32 reserved;
+       __le32 addr_high;
+       __le32 addr_low;
+};
+
 /* Add TX LAN Queues (indirect 0x0C30) */
 struct ice_aqc_add_txqs {
        u8 num_qgrps;
@@ -1089,6 +1143,8 @@ struct ice_aq_desc {
                struct ice_aqc_query_txsched_res query_sched_res;
                struct ice_aqc_add_move_delete_elem add_move_delete_elem;
                struct ice_aqc_nvm nvm;
+               struct ice_aqc_get_set_rss_lut get_set_rss_lut;
+               struct ice_aqc_get_set_rss_key get_set_rss_key;
                struct ice_aqc_add_txqs add_txqs;
                struct ice_aqc_dis_txqs dis_txqs;
                struct ice_aqc_add_get_update_free_vsi vsi_cmd;
@@ -1171,6 +1227,12 @@ enum ice_adminq_opc {
        /* NVM commands */
        ice_aqc_opc_nvm_read                            = 0x0701,
 
+       /* RSS commands */
+       ice_aqc_opc_set_rss_key                         = 0x0B02,
+       ice_aqc_opc_set_rss_lut                         = 0x0B03,
+       ice_aqc_opc_get_rss_key                         = 0x0B04,
+       ice_aqc_opc_get_rss_lut                         = 0x0B05,
+
        /* TX queue handling commands/events */
        ice_aqc_opc_add_txqs                            = 0x0C30,
        ice_aqc_opc_dis_txqs                            = 0x0C31,
index 3d56866366561e3eb0852744a820de1e01cbad37..b4e834359a950c0540a49ef32e842768ef99f226 100644 (file)
@@ -1261,6 +1261,194 @@ void ice_clear_pxe_mode(struct ice_hw *hw)
                ice_aq_clear_pxe_mode(hw);
 }
 
+/**
+ * __ice_aq_get_set_rss_lut
+ * @hw: pointer to the hardware structure
+ * @vsi_id: VSI FW index
+ * @lut_type: LUT table type
+ * @lut: pointer to the LUT buffer provided by the caller
+ * @lut_size: size of the LUT buffer
+ * @glob_lut_idx: global LUT index
+ * @set: set true to set the table, false to get the table
+ *
+ * Internal function to get (0x0B05) or set (0x0B03) RSS look up table
+ */
+static enum ice_status
+__ice_aq_get_set_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+                        u16 lut_size, u8 glob_lut_idx, bool set)
+{
+       struct ice_aqc_get_set_rss_lut *cmd_resp;
+       struct ice_aq_desc desc;
+       enum ice_status status;
+       u16 flags = 0;
+
+       cmd_resp = &desc.params.get_set_rss_lut;
+
+       if (set) {
+               ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_rss_lut);
+               desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+       } else {
+               ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_rss_lut);
+       }
+
+       cmd_resp->vsi_id = cpu_to_le16(((vsi_id <<
+                                        ICE_AQC_GSET_RSS_LUT_VSI_ID_S) &
+                                       ICE_AQC_GSET_RSS_LUT_VSI_ID_M) |
+                                      ICE_AQC_GSET_RSS_LUT_VSI_VALID);
+
+       switch (lut_type) {
+       case ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_VSI:
+       case ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF:
+       case ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_GLOBAL:
+               flags |= ((lut_type << ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_S) &
+                         ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_M);
+               break;
+       default:
+               status = ICE_ERR_PARAM;
+               goto ice_aq_get_set_rss_lut_exit;
+       }
+
+       if (lut_type == ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_GLOBAL) {
+               flags |= ((glob_lut_idx << ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_S) &
+                         ICE_AQC_GSET_RSS_LUT_GLOBAL_IDX_M);
+
+               if (!set)
+                       goto ice_aq_get_set_rss_lut_send;
+       } else if (lut_type == ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF) {
+               if (!set)
+                       goto ice_aq_get_set_rss_lut_send;
+       } else {
+               goto ice_aq_get_set_rss_lut_send;
+       }
+
+       /* LUT size is only valid for Global and PF table types */
+       if (lut_size == ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_128) {
+               flags |= (ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_128_FLAG <<
+                         ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S) &
+                        ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_M;
+       } else if (lut_size == ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_512) {
+               flags |= (ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_512_FLAG <<
+                         ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S) &
+                        ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_M;
+       } else if ((lut_size == ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_2K) &&
+                  (lut_type == ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF)) {
+               flags |= (ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_2K_FLAG <<
+                         ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_S) &
+                        ICE_AQC_GSET_RSS_LUT_TABLE_SIZE_M;
+       } else {
+               status = ICE_ERR_PARAM;
+               goto ice_aq_get_set_rss_lut_exit;
+       }
+
+ice_aq_get_set_rss_lut_send:
+       cmd_resp->flags = cpu_to_le16(flags);
+       status = ice_aq_send_cmd(hw, &desc, lut, lut_size, NULL);
+
+ice_aq_get_set_rss_lut_exit:
+       return status;
+}
+
+/**
+ * ice_aq_get_rss_lut
+ * @hw: pointer to the hardware structure
+ * @vsi_id: VSI FW index
+ * @lut_type: LUT table type
+ * @lut: pointer to the LUT buffer provided by the caller
+ * @lut_size: size of the LUT buffer
+ *
+ * get the RSS lookup table, PF or VSI type
+ */
+enum ice_status
+ice_aq_get_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+                  u16 lut_size)
+{
+       return __ice_aq_get_set_rss_lut(hw, vsi_id, lut_type, lut, lut_size, 0,
+                                       false);
+}
+
+/**
+ * ice_aq_set_rss_lut
+ * @hw: pointer to the hardware structure
+ * @vsi_id: VSI FW index
+ * @lut_type: LUT table type
+ * @lut: pointer to the LUT buffer provided by the caller
+ * @lut_size: size of the LUT buffer
+ *
+ * set the RSS lookup table, PF or VSI type
+ */
+enum ice_status
+ice_aq_set_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+                  u16 lut_size)
+{
+       return __ice_aq_get_set_rss_lut(hw, vsi_id, lut_type, lut, lut_size, 0,
+                                       true);
+}
+
+/**
+ * __ice_aq_get_set_rss_key
+ * @hw: pointer to the hw struct
+ * @vsi_id: VSI FW index
+ * @key: pointer to key info struct
+ * @set: set true to set the key, false to get the key
+ *
+ * get (0x0B04) or set (0x0B02) the RSS key per VSI
+ */
+static enum
+ice_status __ice_aq_get_set_rss_key(struct ice_hw *hw, u16 vsi_id,
+                                   struct ice_aqc_get_set_rss_keys *key,
+                                   bool set)
+{
+       struct ice_aqc_get_set_rss_key *cmd_resp;
+       u16 key_size = sizeof(*key);
+       struct ice_aq_desc desc;
+
+       cmd_resp = &desc.params.get_set_rss_key;
+
+       if (set) {
+               ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_set_rss_key);
+               desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
+       } else {
+               ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_rss_key);
+       }
+
+       cmd_resp->vsi_id = cpu_to_le16(((vsi_id <<
+                                        ICE_AQC_GSET_RSS_KEY_VSI_ID_S) &
+                                       ICE_AQC_GSET_RSS_KEY_VSI_ID_M) |
+                                      ICE_AQC_GSET_RSS_KEY_VSI_VALID);
+
+       return ice_aq_send_cmd(hw, &desc, key, key_size, NULL);
+}
+
+/**
+ * ice_aq_get_rss_key
+ * @hw: pointer to the hw struct
+ * @vsi_id: VSI FW index
+ * @key: pointer to key info struct
+ *
+ * get the RSS key per VSI
+ */
+enum ice_status
+ice_aq_get_rss_key(struct ice_hw *hw, u16 vsi_id,
+                  struct ice_aqc_get_set_rss_keys *key)
+{
+       return __ice_aq_get_set_rss_key(hw, vsi_id, key, false);
+}
+
+/**
+ * ice_aq_set_rss_key
+ * @hw: pointer to the hw struct
+ * @vsi_id: VSI FW index
+ * @keys: pointer to key info struct
+ *
+ * set the RSS key per VSI
+ */
+enum ice_status
+ice_aq_set_rss_key(struct ice_hw *hw, u16 vsi_id,
+                  struct ice_aqc_get_set_rss_keys *keys)
+{
+       return __ice_aq_get_set_rss_key(hw, vsi_id, keys, true);
+}
+
 /**
  * ice_aq_add_lan_txq
  * @hw: pointer to the hardware structure
index 8ed1135bb1898d7a486149eedba8cd315ad6491c..38719ba01d9b5d1dc29c60affbf9b9645f418875 100644 (file)
@@ -33,6 +33,19 @@ enum ice_status ice_get_caps(struct ice_hw *hw);
 enum ice_status
 ice_write_rxq_ctx(struct ice_hw *hw, struct ice_rlan_ctx *rlan_ctx,
                  u32 rxq_index);
+
+enum ice_status
+ice_aq_get_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+                  u16 lut_size);
+enum ice_status
+ice_aq_set_rss_lut(struct ice_hw *hw, u16 vsi_id, u8 lut_type, u8 *lut,
+                  u16 lut_size);
+enum ice_status
+ice_aq_get_rss_key(struct ice_hw *hw, u16 vsi_id,
+                  struct ice_aqc_get_set_rss_keys *keys);
+enum ice_status
+ice_aq_set_rss_key(struct ice_hw *hw, u16 vsi_id,
+                  struct ice_aqc_get_set_rss_keys *keys);
 bool ice_check_sq_alive(struct ice_hw *hw, struct ice_ctl_q_info *cq);
 enum ice_status ice_aq_q_shutdown(struct ice_hw *hw, bool unloading);
 void ice_fill_dflt_direct_cmd_desc(struct ice_aq_desc *desc, u16 opcode);
index e1221b9839d5b0b7b51f45b708f65e6edeadcbd9..57acbcff740bfdb5c015873d3a0072fe83fabf57 100644 (file)
@@ -40,6 +40,65 @@ union ice_32byte_rx_desc {
        } wb; /* writeback */
 };
 
+struct ice_rx_ptype_decoded {
+       u32 ptype:10;
+       u32 known:1;
+       u32 outer_ip:1;
+       u32 outer_ip_ver:2;
+       u32 outer_frag:1;
+       u32 tunnel_type:3;
+       u32 tunnel_end_prot:2;
+       u32 tunnel_end_frag:1;
+       u32 inner_prot:4;
+       u32 payload_layer:3;
+};
+
+enum ice_rx_ptype_outer_ip {
+       ICE_RX_PTYPE_OUTER_L2   = 0,
+       ICE_RX_PTYPE_OUTER_IP   = 1,
+};
+
+enum ice_rx_ptype_outer_ip_ver {
+       ICE_RX_PTYPE_OUTER_NONE = 0,
+       ICE_RX_PTYPE_OUTER_IPV4 = 1,
+       ICE_RX_PTYPE_OUTER_IPV6 = 2,
+};
+
+enum ice_rx_ptype_outer_fragmented {
+       ICE_RX_PTYPE_NOT_FRAG   = 0,
+       ICE_RX_PTYPE_FRAG       = 1,
+};
+
+enum ice_rx_ptype_tunnel_type {
+       ICE_RX_PTYPE_TUNNEL_NONE                = 0,
+       ICE_RX_PTYPE_TUNNEL_IP_IP               = 1,
+       ICE_RX_PTYPE_TUNNEL_IP_GRENAT           = 2,
+       ICE_RX_PTYPE_TUNNEL_IP_GRENAT_MAC       = 3,
+       ICE_RX_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN  = 4,
+};
+
+enum ice_rx_ptype_tunnel_end_prot {
+       ICE_RX_PTYPE_TUNNEL_END_NONE    = 0,
+       ICE_RX_PTYPE_TUNNEL_END_IPV4    = 1,
+       ICE_RX_PTYPE_TUNNEL_END_IPV6    = 2,
+};
+
+enum ice_rx_ptype_inner_prot {
+       ICE_RX_PTYPE_INNER_PROT_NONE            = 0,
+       ICE_RX_PTYPE_INNER_PROT_UDP             = 1,
+       ICE_RX_PTYPE_INNER_PROT_TCP             = 2,
+       ICE_RX_PTYPE_INNER_PROT_SCTP            = 3,
+       ICE_RX_PTYPE_INNER_PROT_ICMP            = 4,
+       ICE_RX_PTYPE_INNER_PROT_TIMESYNC        = 5,
+};
+
+enum ice_rx_ptype_payload_layer {
+       ICE_RX_PTYPE_PAYLOAD_LAYER_NONE = 0,
+       ICE_RX_PTYPE_PAYLOAD_LAYER_PAY2 = 1,
+       ICE_RX_PTYPE_PAYLOAD_LAYER_PAY3 = 2,
+       ICE_RX_PTYPE_PAYLOAD_LAYER_PAY4 = 3,
+};
+
 /* RX Flex Descriptor
  * This descriptor is used instead of the legacy version descriptor when
  * ice_rlan_ctx.adv_desc is set
@@ -88,6 +147,41 @@ union ice_32b_rx_flex_desc {
        } wb; /* writeback */
 };
 
+/* Rx Flex Descriptor NIC Profile
+ * This descriptor corresponds to RxDID 2 which contains
+ * metadata fields for RSS, flow id and timestamp info
+ */
+struct ice_32b_rx_flex_desc_nic {
+       /* Qword 0 */
+       u8 rxdid;
+       u8 mir_id_umb_cast;
+       __le16 ptype_flexi_flags0;
+       __le16 pkt_len;
+       __le16 hdr_len_sph_flex_flags1;
+
+       /* Qword 1 */
+       __le16 status_error0;
+       __le16 l2tag1;
+       __le32 rss_hash;
+
+       /* Qword 2 */
+       __le16 status_error1;
+       u8 flexi_flags2;
+       u8 ts_low;
+       __le16 l2tag2_1st;
+       __le16 l2tag2_2nd;
+
+       /* Qword 3 */
+       __le32 flow_id;
+       union {
+               struct {
+                       __le16 vlan_id;
+                       __le16 flow_id_ipv6;
+               } flex;
+               __le32 ts_high;
+       } flex_ts;
+};
+
 /* Receive Flex Descriptor profile IDs: There are a total
  * of 64 profiles where profile IDs 0/1 are for legacy; and
  * profiles 2-63 are flex profiles that can be programmed
@@ -241,12 +335,56 @@ enum ice_tx_desc_dtype_value {
 enum ice_tx_desc_cmd_bits {
        ICE_TX_DESC_CMD_EOP                     = 0x0001,
        ICE_TX_DESC_CMD_RS                      = 0x0002,
+       ICE_TX_DESC_CMD_IL2TAG1                 = 0x0008,
+       ICE_TX_DESC_CMD_IIPT_IPV6               = 0x0020, /* 2 BITS */
+       ICE_TX_DESC_CMD_IIPT_IPV4               = 0x0040, /* 2 BITS */
+       ICE_TX_DESC_CMD_IIPT_IPV4_CSUM          = 0x0060, /* 2 BITS */
+       ICE_TX_DESC_CMD_L4T_EOFT_TCP            = 0x0100, /* 2 BITS */
+       ICE_TX_DESC_CMD_L4T_EOFT_UDP            = 0x0300, /* 2 BITS */
 };
 
 #define ICE_TXD_QW1_OFFSET_S   16
+#define ICE_TXD_QW1_OFFSET_M   (0x3FFFFULL << ICE_TXD_QW1_OFFSET_S)
+
+enum ice_tx_desc_len_fields {
+       /* Note: These are predefined bit offsets */
+       ICE_TX_DESC_LEN_MACLEN_S        = 0, /* 7 BITS */
+       ICE_TX_DESC_LEN_IPLEN_S = 7, /* 7 BITS */
+       ICE_TX_DESC_LEN_L4_LEN_S        = 14 /* 4 BITS */
+};
+
 #define ICE_TXD_QW1_TX_BUF_SZ_S        34
 #define ICE_TXD_QW1_L2TAG1_S   48
 
+/* Context descriptors */
+struct ice_tx_ctx_desc {
+       __le32 tunneling_params;
+       __le16 l2tag2;
+       __le16 rsvd;
+       __le64 qw1;
+};
+
+#define ICE_TXD_CTX_QW1_CMD_S  4
+#define ICE_TXD_CTX_QW1_CMD_M  (0x7FUL << ICE_TXD_CTX_QW1_CMD_S)
+
+#define ICE_TXD_CTX_QW1_TSO_LEN_S      30
+#define ICE_TXD_CTX_QW1_TSO_LEN_M      \
+                       (0x3FFFFULL << ICE_TXD_CTX_QW1_TSO_LEN_S)
+
+#define ICE_TXD_CTX_QW1_MSS_S  50
+
+enum ice_tx_ctx_desc_cmd_bits {
+       ICE_TX_CTX_DESC_TSO             = 0x01,
+       ICE_TX_CTX_DESC_TSYN            = 0x02,
+       ICE_TX_CTX_DESC_IL2TAG2         = 0x04,
+       ICE_TX_CTX_DESC_IL2TAG2_IL2H    = 0x08,
+       ICE_TX_CTX_DESC_SWTCH_NOTAG     = 0x00,
+       ICE_TX_CTX_DESC_SWTCH_UPLINK    = 0x10,
+       ICE_TX_CTX_DESC_SWTCH_LOCAL     = 0x20,
+       ICE_TX_CTX_DESC_SWTCH_VSI       = 0x30,
+       ICE_TX_CTX_DESC_RESERVED        = 0x40
+};
+
 #define ICE_LAN_TXQ_MAX_QGRPS  127
 #define ICE_LAN_TXQ_MAX_QDIS   1023
 
@@ -289,4 +427,35 @@ struct ice_tlan_ctx {
        u8  pkt_shaper_prof_idx;
        u8  int_q_state;        /* width not needed - internal do not write */
 };
+
+/* macro to make the table lines short */
+#define ICE_PTT(PTYPE, OUTER_IP, OUTER_IP_VER, OUTER_FRAG, T, TE, TEF, I, PL)\
+       {       PTYPE, \
+               1, \
+               ICE_RX_PTYPE_OUTER_##OUTER_IP, \
+               ICE_RX_PTYPE_OUTER_##OUTER_IP_VER, \
+               ICE_RX_PTYPE_##OUTER_FRAG, \
+               ICE_RX_PTYPE_TUNNEL_##T, \
+               ICE_RX_PTYPE_TUNNEL_END_##TE, \
+               ICE_RX_PTYPE_##TEF, \
+               ICE_RX_PTYPE_INNER_PROT_##I, \
+               ICE_RX_PTYPE_PAYLOAD_LAYER_##PL }
+
+#define ICE_PTT_UNUSED_ENTRY(PTYPE) { PTYPE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* shorter macros makes the table fit but are terse */
+#define ICE_RX_PTYPE_NOF               ICE_RX_PTYPE_NOT_FRAG
+
+/* Lookup table mapping the HW PTYPE to the bit field for decoding */
+static const struct ice_rx_ptype_decoded ice_ptype_lkup[] = {
+       /* L2 Packet types */
+       ICE_PTT_UNUSED_ENTRY(0),
+       ICE_PTT(1, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+       ICE_PTT(2, L2, NONE, NOF, NONE, NONE, NOF, NONE, NONE),
+};
+
+static inline struct ice_rx_ptype_decoded ice_decode_rx_desc_ptype(u16 ptype)
+{
+       return ice_ptype_lkup[ptype];
+}
 #endif /* _ICE_LAN_TX_RX_H_ */
index dd07bf862afe6bd547a85299702d719c3c8f2e7f..2124e7ad640c781e67d39cd582a1afc37774261f 100644 (file)
@@ -663,6 +663,35 @@ free_q_irqs:
        return err;
 }
 
+/**
+ * ice_vsi_set_rss_params - Setup RSS capabilities per VSI type
+ * @vsi: the VSI being configured
+ */
+static void ice_vsi_set_rss_params(struct ice_vsi *vsi)
+{
+       struct ice_hw_common_caps *cap;
+       struct ice_pf *pf = vsi->back;
+
+       if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags)) {
+               vsi->rss_size = 1;
+               return;
+       }
+
+       cap = &pf->hw.func_caps.common_cap;
+       switch (vsi->type) {
+       case ICE_VSI_PF:
+               /* PF VSI will inherit RSS instance of PF */
+               vsi->rss_table_size = cap->rss_table_size;
+               vsi->rss_size = min_t(int, num_online_cpus(),
+                                     BIT(cap->rss_table_entry_width));
+               vsi->rss_lut_type = ICE_AQC_GSET_RSS_LUT_TABLE_TYPE_PF;
+               break;
+       default:
+               dev_warn(&pf->pdev->dev, "Unknown VSI type %d\n", vsi->type);
+               break;
+       }
+}
+
 /**
  * ice_vsi_setup_q_map - Setup a VSI queue map
  * @vsi: the VSI being configured
@@ -670,7 +699,8 @@ free_q_irqs:
  */
 static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
 {
-       u16 offset = 0, qmap = 0, pow = 0, qcount;
+       u16 offset = 0, qmap = 0, numq_tc;
+       u16 pow = 0, max_rss = 0, qcount;
        u16 qcount_tx = vsi->alloc_txq;
        u16 qcount_rx = vsi->alloc_rxq;
        bool ena_tc0 = false;
@@ -689,13 +719,7 @@ static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
                vsi->tc_cfg.ena_tc |= 1;
        }
 
-       qcount = qcount_rx / vsi->tc_cfg.numtc;
-
-       /* find higher power-of-2 of qcount */
-       pow = ilog2(qcount);
-
-       if (!is_power_of_2(qcount))
-               pow++;
+       numq_tc = qcount_rx / vsi->tc_cfg.numtc;
 
        /* TC mapping is a function of the number of Rx queues assigned to the
         * VSI for each traffic class and the offset of these queues.
@@ -708,6 +732,26 @@ static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt)
         *
         * Setup number and offset of Rx queues for all TCs for the VSI
         */
+
+       /* qcount will change if RSS is enabled */
+       if (test_bit(ICE_FLAG_RSS_ENA, vsi->back->flags)) {
+               if (vsi->type == ICE_VSI_PF)
+                       max_rss = ICE_MAX_LG_RSS_QS;
+               else
+                       max_rss = ICE_MAX_SMALL_RSS_QS;
+
+               qcount = min_t(int, numq_tc, max_rss);
+               qcount = min_t(int, qcount, vsi->rss_size);
+       } else {
+               qcount = numq_tc;
+       }
+
+       /* find higher power-of-2 of qcount */
+       pow = ilog2(qcount);
+
+       if (!is_power_of_2(qcount))
+               pow++;
+
        for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) {
                if (!(vsi->tc_cfg.ena_tc & BIT(i))) {
                        /* TC is not enabled */
@@ -783,6 +827,33 @@ static void ice_set_dflt_vsi_ctx(struct ice_vsi_ctx *ctxt)
        /* No Outer tag support outer_tag_flags remains to zero */
 }
 
+/**
+ * ice_set_rss_vsi_ctx - Set RSS VSI context before adding a VSI
+ * @ctxt: the VSI context being set
+ * @vsi: the VSI being configured
+ */
+static void ice_set_rss_vsi_ctx(struct ice_vsi_ctx *ctxt, struct ice_vsi *vsi)
+{
+       u8 lut_type, hash_type;
+
+       switch (vsi->type) {
+       case ICE_VSI_PF:
+               /* PF VSI will inherit RSS instance of PF */
+               lut_type = ICE_AQ_VSI_Q_OPT_RSS_LUT_PF;
+               hash_type = ICE_AQ_VSI_Q_OPT_RSS_TPLZ;
+               break;
+       default:
+               dev_warn(&vsi->back->pdev->dev, "Unknown VSI type %d\n",
+                        vsi->type);
+               return;
+       }
+
+       ctxt->info.q_opt_rss = ((lut_type << ICE_AQ_VSI_Q_OPT_RSS_LUT_S) &
+                               ICE_AQ_VSI_Q_OPT_RSS_LUT_M) |
+                               ((hash_type << ICE_AQ_VSI_Q_OPT_RSS_HASH_S) &
+                                ICE_AQ_VSI_Q_OPT_RSS_HASH_M);
+}
+
 /**
  * ice_vsi_add - Create a new VSI or fetch preallocated VSI
  * @vsi: the VSI being configured
@@ -810,6 +881,10 @@ static int ice_vsi_add(struct ice_vsi *vsi)
        if (vsi->vsw->bridge_mode == BRIDGE_MODE_VEB)
                ctxt.info.sw_flags |= ICE_AQ_VSI_SW_FLAG_ALLOW_LB;
 
+       /* Set LUT type and HASH type if RSS is enabled */
+       if (test_bit(ICE_FLAG_RSS_ENA, pf->flags))
+               ice_set_rss_vsi_ctx(&ctxt, vsi);
+
        ctxt.info.sw_id = vsi->port_info->sw_id;
        ice_vsi_setup_q_map(vsi, &ctxt);
 
@@ -1629,6 +1704,10 @@ static void ice_vsi_free_q_vectors(struct ice_vsi *vsi)
  */
 static int ice_cfg_netdev(struct ice_vsi *vsi)
 {
+       netdev_features_t csumo_features;
+       netdev_features_t vlano_features;
+       netdev_features_t dflt_features;
+       netdev_features_t tso_features;
        struct ice_netdev_priv *np;
        struct net_device *netdev;
        u8 mac_addr[ETH_ALEN];
@@ -1642,13 +1721,31 @@ static int ice_cfg_netdev(struct ice_vsi *vsi)
        np = netdev_priv(netdev);
        np->vsi = vsi;
 
+       dflt_features = NETIF_F_SG      |
+                       NETIF_F_HIGHDMA |
+                       NETIF_F_RXHASH;
+
+       csumo_features = NETIF_F_RXCSUM   |
+                        NETIF_F_IP_CSUM  |
+                        NETIF_F_IPV6_CSUM;
+
+       vlano_features = NETIF_F_HW_VLAN_CTAG_FILTER |
+                        NETIF_F_HW_VLAN_CTAG_TX     |
+                        NETIF_F_HW_VLAN_CTAG_RX;
+
+       tso_features = NETIF_F_TSO;
+
        /* set features that user can change */
-       netdev->hw_features = NETIF_F_SG        |
-                             NETIF_F_HIGHDMA   |
-                             NETIF_F_RXHASH;
+       netdev->hw_features = dflt_features | csumo_features |
+                             vlano_features | tso_features;
 
        /* enable features */
        netdev->features |= netdev->hw_features;
+       /* encap and VLAN devices inherit default, csumo and tso features */
+       netdev->hw_enc_features |= dflt_features | csumo_features |
+                                  tso_features;
+       netdev->vlan_features |= dflt_features | csumo_features |
+                                tso_features;
 
        if (vsi->type == ICE_VSI_PF) {
                SET_NETDEV_DEV(netdev, &vsi->back->pdev->dev);
@@ -1862,6 +1959,83 @@ static int ice_vsi_setup_vector_base(struct ice_vsi *vsi)
        return 0;
 }
 
+/**
+ * ice_fill_rss_lut - Fill the RSS lookup table with default values
+ * @lut: Lookup table
+ * @rss_table_size: Lookup table size
+ * @rss_size: Range of queue number for hashing
+ */
+void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size)
+{
+       u16 i;
+
+       for (i = 0; i < rss_table_size; i++)
+               lut[i] = i % rss_size;
+}
+
+/**
+ * ice_vsi_cfg_rss - Configure RSS params for a VSI
+ * @vsi: VSI to be configured
+ */
+static int ice_vsi_cfg_rss(struct ice_vsi *vsi)
+{
+       u8 seed[ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE];
+       struct ice_aqc_get_set_rss_keys *key;
+       struct ice_pf *pf = vsi->back;
+       enum ice_status status;
+       int err = 0;
+       u8 *lut;
+
+       vsi->rss_size = min_t(int, vsi->rss_size, vsi->num_rxq);
+
+       lut = devm_kzalloc(&pf->pdev->dev, vsi->rss_table_size, GFP_KERNEL);
+       if (!lut)
+               return -ENOMEM;
+
+       if (vsi->rss_lut_user)
+               memcpy(lut, vsi->rss_lut_user, vsi->rss_table_size);
+       else
+               ice_fill_rss_lut(lut, vsi->rss_table_size, vsi->rss_size);
+
+       status = ice_aq_set_rss_lut(&pf->hw, vsi->vsi_num, vsi->rss_lut_type,
+                                   lut, vsi->rss_table_size);
+
+       if (status) {
+               dev_err(&vsi->back->pdev->dev,
+                       "set_rss_lut failed, error %d\n", status);
+               err = -EIO;
+               goto ice_vsi_cfg_rss_exit;
+       }
+
+       key = devm_kzalloc(&vsi->back->pdev->dev, sizeof(*key), GFP_KERNEL);
+       if (!key) {
+               err = -ENOMEM;
+               goto ice_vsi_cfg_rss_exit;
+       }
+
+       if (vsi->rss_hkey_user)
+               memcpy(seed, vsi->rss_hkey_user,
+                      ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE);
+       else
+               netdev_rss_key_fill((void *)seed,
+                                   ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE);
+       memcpy(&key->standard_rss_key, seed,
+              ICE_AQC_GET_SET_RSS_KEY_DATA_RSS_KEY_SIZE);
+
+       status = ice_aq_set_rss_key(&pf->hw, vsi->vsi_num, key);
+
+       if (status) {
+               dev_err(&vsi->back->pdev->dev, "set_rss_key failed, error %d\n",
+                       status);
+               err = -EIO;
+       }
+
+       devm_kfree(&pf->pdev->dev, key);
+ice_vsi_cfg_rss_exit:
+       devm_kfree(&pf->pdev->dev, lut);
+       return err;
+}
+
 /**
  * ice_vsi_setup - Set up a VSI by a given type
  * @pf: board private structure
@@ -1897,6 +2071,9 @@ ice_vsi_setup(struct ice_pf *pf, enum ice_vsi_type type,
                goto err_get_qs;
        }
 
+       /* set RSS capabilities */
+       ice_vsi_set_rss_params(vsi);
+
        /* create the VSI */
        ret = ice_vsi_add(vsi);
        if (ret)
@@ -1932,6 +2109,12 @@ ice_vsi_setup(struct ice_pf *pf, enum ice_vsi_type type,
 
                ice_vsi_map_rings_to_vectors(vsi);
 
+               /* Do not exit if configuring RSS had an issue, at least
+                * receive traffic on first queue. Hence no need to capture
+                * return value
+                */
+               if (test_bit(ICE_FLAG_RSS_ENA, pf->flags))
+                       ice_vsi_cfg_rss(vsi);
                break;
        default:
                /* if vsi type is not recognized, clean up the resources and
@@ -1966,6 +2149,140 @@ err_get_qs:
        return NULL;
 }
 
+/**
+ * ice_vsi_add_vlan - Add vsi membership for given vlan
+ * @vsi: the vsi being configured
+ * @vid: vlan id to be added
+ */
+static int ice_vsi_add_vlan(struct ice_vsi *vsi, u16 vid)
+{
+       struct ice_fltr_list_entry *tmp;
+       struct ice_pf *pf = vsi->back;
+       LIST_HEAD(tmp_add_list);
+       enum ice_status status;
+       int err = 0;
+
+       tmp = devm_kzalloc(&pf->pdev->dev, sizeof(*tmp), GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
+       tmp->fltr_info.lkup_type = ICE_SW_LKUP_VLAN;
+       tmp->fltr_info.fltr_act = ICE_FWD_TO_VSI;
+       tmp->fltr_info.flag = ICE_FLTR_TX;
+       tmp->fltr_info.src = vsi->vsi_num;
+       tmp->fltr_info.fwd_id.vsi_id = vsi->vsi_num;
+       tmp->fltr_info.l_data.vlan.vlan_id = vid;
+
+       INIT_LIST_HEAD(&tmp->list_entry);
+       list_add(&tmp->list_entry, &tmp_add_list);
+
+       status = ice_add_vlan(&pf->hw, &tmp_add_list);
+       if (status) {
+               err = -ENODEV;
+               dev_err(&pf->pdev->dev, "Failure Adding VLAN %d on VSI %i\n",
+                       vid, vsi->vsi_num);
+       }
+
+       ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
+       return err;
+}
+
+/**
+ * ice_vlan_rx_add_vid - Add a vlan id filter to HW offload
+ * @netdev: network interface to be adjusted
+ * @proto: unused protocol
+ * @vid: vlan id to be added
+ *
+ * net_device_ops implementation for adding vlan ids
+ */
+static int ice_vlan_rx_add_vid(struct net_device *netdev,
+                              __always_unused __be16 proto, u16 vid)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       int ret = 0;
+
+       if (vid >= VLAN_N_VID) {
+               netdev_err(netdev, "VLAN id requested %d is out of range %d\n",
+                          vid, VLAN_N_VID);
+               return -EINVAL;
+       }
+
+       if (vsi->info.pvid)
+               return -EINVAL;
+
+       /* Add all VLAN ids including 0 to the switch filter. VLAN id 0 is
+        * needed to continue allowing all untagged packets since VLAN prune
+        * list is applied to all packets by the switch
+        */
+       ret = ice_vsi_add_vlan(vsi, vid);
+
+       if (!ret)
+               set_bit(vid, vsi->active_vlans);
+
+       return ret;
+}
+
+/**
+ * ice_vsi_kill_vlan - Remove VSI membership for a given VLAN
+ * @vsi: the VSI being configured
+ * @vid: VLAN id to be removed
+ */
+static void ice_vsi_kill_vlan(struct ice_vsi *vsi, u16 vid)
+{
+       struct ice_fltr_list_entry *list;
+       struct ice_pf *pf = vsi->back;
+       LIST_HEAD(tmp_add_list);
+
+       list = devm_kzalloc(&pf->pdev->dev, sizeof(*list), GFP_KERNEL);
+       if (!list)
+               return;
+
+       list->fltr_info.lkup_type = ICE_SW_LKUP_VLAN;
+       list->fltr_info.fwd_id.vsi_id = vsi->vsi_num;
+       list->fltr_info.fltr_act = ICE_FWD_TO_VSI;
+       list->fltr_info.l_data.vlan.vlan_id = vid;
+       list->fltr_info.flag = ICE_FLTR_TX;
+       list->fltr_info.src = vsi->vsi_num;
+
+       INIT_LIST_HEAD(&list->list_entry);
+       list_add(&list->list_entry, &tmp_add_list);
+
+       if (ice_remove_vlan(&pf->hw, &tmp_add_list))
+               dev_err(&pf->pdev->dev, "Error removing VLAN %d on vsi %i\n",
+                       vid, vsi->vsi_num);
+
+       ice_free_fltr_list(&pf->pdev->dev, &tmp_add_list);
+}
+
+/**
+ * ice_vlan_rx_kill_vid - Remove a vlan id filter from HW offload
+ * @netdev: network interface to be adjusted
+ * @proto: unused protocol
+ * @vid: vlan id to be removed
+ *
+ * net_device_ops implementation for removing vlan ids
+ */
+static int ice_vlan_rx_kill_vid(struct net_device *netdev,
+                               __always_unused __be16 proto, u16 vid)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+
+       if (vsi->info.pvid)
+               return -EINVAL;
+
+       /* return code is ignored as there is nothing a user
+        * can do about failure to remove and a log message was
+        * already printed from the other function
+        */
+       ice_vsi_kill_vlan(vsi, vid);
+
+       clear_bit(vid, vsi->active_vlans);
+
+       return 0;
+}
+
 /**
  * ice_setup_pf_sw - Setup the HW switch on startup or after reset
  * @pf: board private structure
@@ -2047,9 +2364,14 @@ static void ice_determine_q_usage(struct ice_pf *pf)
        q_left_tx = pf->hw.func_caps.common_cap.num_txq;
        q_left_rx = pf->hw.func_caps.common_cap.num_rxq;
 
-       /* initial support for only 1 tx and 1 rx queue */
+       /* initial support for only 1 tx queue */
        pf->num_lan_tx = 1;
-       pf->num_lan_rx = 1;
+
+       /* only 1 rx queue unless RSS is enabled */
+       if (!test_bit(ICE_FLAG_RSS_ENA, pf->flags))
+               pf->num_lan_rx = 1;
+       else
+               pf->num_lan_rx = min_t(int, q_left_rx, num_online_cpus());
 
        pf->q_left_tx = q_left_tx - pf->num_lan_tx;
        pf->q_left_rx = q_left_rx - pf->num_lan_rx;
@@ -2087,6 +2409,9 @@ static void ice_init_pf(struct ice_pf *pf)
        bitmap_zero(pf->avail_rxqs, ICE_MAX_RXQS);
        mutex_unlock(&pf->avail_q_mutex);
 
+       if (pf->hw.func_caps.common_cap.rss_table_size)
+               set_bit(ICE_FLAG_RSS_ENA, pf->flags);
+
        /* setup service timer and periodic service task */
        timer_setup(&pf->serv_tmr, ice_service_timer, 0);
        pf->serv_tmr_period = HZ;
@@ -2483,6 +2808,144 @@ static void __exit ice_module_exit(void)
 }
 module_exit(ice_module_exit);
 
+/**
+ * ice_vsi_manage_vlan_insertion - Manage VLAN insertion for the VSI for Tx
+ * @vsi: the vsi being changed
+ */
+static int ice_vsi_manage_vlan_insertion(struct ice_vsi *vsi)
+{
+       struct device *dev = &vsi->back->pdev->dev;
+       struct ice_hw *hw = &vsi->back->hw;
+       struct ice_vsi_ctx ctxt = { 0 };
+       enum ice_status status;
+
+       /* Here we are configuring the VSI to let the driver add VLAN tags by
+        * setting port_vlan_flags to ICE_AQ_VSI_PVLAN_MODE_ALL. The actual VLAN
+        * tag insertion happens in the Tx hot path, in ice_tx_map.
+        */
+       ctxt.info.port_vlan_flags = ICE_AQ_VSI_PVLAN_MODE_ALL;
+
+       ctxt.info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID);
+       ctxt.vsi_num = vsi->vsi_num;
+
+       status = ice_aq_update_vsi(hw, &ctxt, NULL);
+       if (status) {
+               dev_err(dev, "update VSI for VLAN insert failed, err %d aq_err %d\n",
+                       status, hw->adminq.sq_last_status);
+               return -EIO;
+       }
+
+       vsi->info.port_vlan_flags = ctxt.info.port_vlan_flags;
+       return 0;
+}
+
+/**
+ * ice_vsi_manage_vlan_stripping - Manage VLAN stripping for the VSI for Rx
+ * @vsi: the vsi being changed
+ * @ena: boolean value indicating if this is a enable or disable request
+ */
+static int ice_vsi_manage_vlan_stripping(struct ice_vsi *vsi, bool ena)
+{
+       struct device *dev = &vsi->back->pdev->dev;
+       struct ice_hw *hw = &vsi->back->hw;
+       struct ice_vsi_ctx ctxt = { 0 };
+       enum ice_status status;
+
+       /* Here we are configuring what the VSI should do with the VLAN tag in
+        * the Rx packet. We can either leave the tag in the packet or put it in
+        * the Rx descriptor.
+        */
+       if (ena) {
+               /* Strip VLAN tag from Rx packet and put it in the desc */
+               ctxt.info.port_vlan_flags = ICE_AQ_VSI_PVLAN_EMOD_STR_BOTH;
+       } else {
+               /* Disable stripping. Leave tag in packet */
+               ctxt.info.port_vlan_flags = ICE_AQ_VSI_PVLAN_EMOD_NOTHING;
+       }
+
+       ctxt.info.valid_sections = cpu_to_le16(ICE_AQ_VSI_PROP_VLAN_VALID);
+       ctxt.vsi_num = vsi->vsi_num;
+
+       status = ice_aq_update_vsi(hw, &ctxt, NULL);
+       if (status) {
+               dev_err(dev, "update VSI for VALN strip failed, ena = %d err %d aq_err %d\n",
+                       ena, status, hw->adminq.sq_last_status);
+               return -EIO;
+       }
+
+       vsi->info.port_vlan_flags = ctxt.info.port_vlan_flags;
+       return 0;
+}
+
+/**
+ * ice_set_features - set the netdev feature flags
+ * @netdev: ptr to the netdev being adjusted
+ * @features: the feature set that the stack is suggesting
+ */
+static int ice_set_features(struct net_device *netdev,
+                           netdev_features_t features)
+{
+       struct ice_netdev_priv *np = netdev_priv(netdev);
+       struct ice_vsi *vsi = np->vsi;
+       int ret = 0;
+
+       if ((features & NETIF_F_HW_VLAN_CTAG_RX) &&
+           !(netdev->features & NETIF_F_HW_VLAN_CTAG_RX))
+               ret = ice_vsi_manage_vlan_stripping(vsi, true);
+       else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) &&
+                (netdev->features & NETIF_F_HW_VLAN_CTAG_RX))
+               ret = ice_vsi_manage_vlan_stripping(vsi, false);
+       else if ((features & NETIF_F_HW_VLAN_CTAG_TX) &&
+                !(netdev->features & NETIF_F_HW_VLAN_CTAG_TX))
+               ret = ice_vsi_manage_vlan_insertion(vsi);
+       else if (!(features & NETIF_F_HW_VLAN_CTAG_TX) &&
+                (netdev->features & NETIF_F_HW_VLAN_CTAG_TX))
+               ret = ice_vsi_manage_vlan_insertion(vsi);
+
+       return ret;
+}
+
+/**
+ * ice_vsi_vlan_setup - Setup vlan offload properties on a VSI
+ * @vsi: VSI to setup vlan properties for
+ */
+static int ice_vsi_vlan_setup(struct ice_vsi *vsi)
+{
+       int ret = 0;
+
+       if (vsi->netdev->features & NETIF_F_HW_VLAN_CTAG_RX)
+               ret = ice_vsi_manage_vlan_stripping(vsi, true);
+       if (vsi->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)
+               ret = ice_vsi_manage_vlan_insertion(vsi);
+
+       return ret;
+}
+
+/**
+ * ice_restore_vlan - Reinstate VLANs when vsi/netdev comes back up
+ * @vsi: the VSI being brought back up
+ */
+static int ice_restore_vlan(struct ice_vsi *vsi)
+{
+       int err;
+       u16 vid;
+
+       if (!vsi->netdev)
+               return -EINVAL;
+
+       err = ice_vsi_vlan_setup(vsi);
+       if (err)
+               return err;
+
+       for_each_set_bit(vid, vsi->active_vlans, VLAN_N_VID) {
+               err = ice_vlan_rx_add_vid(vsi->netdev, htons(ETH_P_8021Q), vid);
+               if (err)
+                       break;
+       }
+
+       return err;
+}
+
 /**
  * ice_setup_tx_ctx - setup a struct ice_tlan_ctx instance
  * @ring: The Tx ring to configure
@@ -2735,6 +3198,10 @@ static int ice_vsi_cfg(struct ice_vsi *vsi)
 {
        int err;
 
+       err = ice_restore_vlan(vsi);
+       if (err)
+               return err;
+
        err = ice_vsi_cfg_txqs(vsi);
        if (!err)
                err = ice_vsi_cfg_rxqs(vsi);
@@ -3213,6 +3680,22 @@ static void ice_vsi_close(struct ice_vsi *vsi)
        ice_vsi_free_rx_rings(vsi);
 }
 
+/**
+ * ice_rss_clean - Delete RSS related VSI structures that hold user inputs
+ * @vsi: the VSI being removed
+ */
+static void ice_rss_clean(struct ice_vsi *vsi)
+{
+       struct ice_pf *pf;
+
+       pf = vsi->back;
+
+       if (vsi->rss_hkey_user)
+               devm_kfree(&pf->pdev->dev, vsi->rss_hkey_user);
+       if (vsi->rss_lut_user)
+               devm_kfree(&pf->pdev->dev, vsi->rss_lut_user);
+}
+
 /**
  * ice_vsi_release - Delete a VSI and free its resources
  * @vsi: the VSI being removed
@@ -3233,6 +3716,10 @@ static int ice_vsi_release(struct ice_vsi *vsi)
                vsi->netdev = NULL;
        }
 
+       if (test_bit(ICE_FLAG_RSS_ENA, pf->flags))
+               ice_rss_clean(vsi);
+
+       /* Disable VSI and free resources */
        ice_vsi_dis_irq(vsi);
        ice_vsi_close(vsi);
 
@@ -3254,6 +3741,91 @@ static int ice_vsi_release(struct ice_vsi *vsi)
        return 0;
 }
 
+/**
+ * ice_set_rss - Set RSS keys and lut
+ * @vsi: Pointer to VSI structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ */
+int ice_set_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+       struct ice_pf *pf = vsi->back;
+       struct ice_hw *hw = &pf->hw;
+       enum ice_status status;
+
+       if (seed) {
+               struct ice_aqc_get_set_rss_keys *buf =
+                                 (struct ice_aqc_get_set_rss_keys *)seed;
+
+               status = ice_aq_set_rss_key(hw, vsi->vsi_num, buf);
+
+               if (status) {
+                       dev_err(&pf->pdev->dev,
+                               "Cannot set RSS key, err %d aq_err %d\n",
+                               status, hw->adminq.rq_last_status);
+                       return -EIO;
+               }
+       }
+
+       if (lut) {
+               status = ice_aq_set_rss_lut(hw, vsi->vsi_num,
+                                           vsi->rss_lut_type, lut, lut_size);
+               if (status) {
+                       dev_err(&pf->pdev->dev,
+                               "Cannot set RSS lut, err %d aq_err %d\n",
+                               status, hw->adminq.rq_last_status);
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * ice_get_rss - Get RSS keys and lut
+ * @vsi: Pointer to VSI structure
+ * @seed: Buffer to store the keys
+ * @lut: Buffer to store the lookup table entries
+ * @lut_size: Size of buffer to store the lookup table entries
+ *
+ * Returns 0 on success, negative on failure
+ */
+int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
+{
+       struct ice_pf *pf = vsi->back;
+       struct ice_hw *hw = &pf->hw;
+       enum ice_status status;
+
+       if (seed) {
+               struct ice_aqc_get_set_rss_keys *buf =
+                                 (struct ice_aqc_get_set_rss_keys *)seed;
+
+               status = ice_aq_get_rss_key(hw, vsi->vsi_num, buf);
+               if (status) {
+                       dev_err(&pf->pdev->dev,
+                               "Cannot get RSS key, err %d aq_err %d\n",
+                               status, hw->adminq.rq_last_status);
+                       return -EIO;
+               }
+       }
+
+       if (lut) {
+               status = ice_aq_get_rss_lut(hw, vsi->vsi_num,
+                                           vsi->rss_lut_type, lut, lut_size);
+               if (status) {
+                       dev_err(&pf->pdev->dev,
+                               "Cannot get RSS lut, err %d aq_err %d\n",
+                               status, hw->adminq.rq_last_status);
+                       return -EIO;
+               }
+       }
+
+       return 0;
+}
+
 /**
  * ice_open - Called when a network interface becomes active
  * @netdev: network interface device structure
@@ -3306,4 +3878,7 @@ static const struct net_device_ops ice_netdev_ops = {
        .ndo_open = ice_open,
        .ndo_stop = ice_stop,
        .ndo_start_xmit = ice_start_xmit,
+       .ndo_vlan_rx_add_vid = ice_vlan_rx_add_vid,
+       .ndo_vlan_rx_kill_vid = ice_vlan_rx_kill_vid,
+       .ndo_set_features = ice_set_features,
 };
index 3944dac6cfc6a9e79c22b6cba5c8ef6cb30ecc7e..5624df9eb6fcf3e9244aed2558ba0ce9beedc15c 100644 (file)
@@ -1217,6 +1217,117 @@ ice_add_mac_exit:
        return status;
 }
 
+/**
+ * ice_find_vlan_entry
+ * @hw: pointer to the hardware structure
+ * @vlan_id: VLAN id to search for
+ *
+ * Helper function to search for a VLAN entry using a given VLAN id
+ * Returns pointer to the entry if found.
+ */
+static struct ice_fltr_mgmt_list_entry *
+ice_find_vlan_entry(struct ice_hw *hw, u16 vlan_id)
+{
+       struct ice_fltr_mgmt_list_entry *vlan_list_itr, *vlan_ret = NULL;
+       struct ice_switch_info *sw = hw->switch_info;
+
+       mutex_lock(&sw->vlan_list_lock);
+       list_for_each_entry(vlan_list_itr, &sw->vlan_list_head, list_entry)
+               if (vlan_list_itr->fltr_info.l_data.vlan.vlan_id == vlan_id) {
+                       vlan_ret = vlan_list_itr;
+                       break;
+               }
+
+       mutex_unlock(&sw->vlan_list_lock);
+       return vlan_ret;
+}
+
+/**
+ * ice_add_vlan_internal - Add one VLAN based filter rule
+ * @hw: pointer to the hardware structure
+ * @f_entry: filter entry containing one VLAN information
+ */
+static enum ice_status
+ice_add_vlan_internal(struct ice_hw *hw, struct ice_fltr_list_entry *f_entry)
+{
+       struct ice_fltr_info *new_fltr, *cur_fltr;
+       struct ice_fltr_mgmt_list_entry *v_list_itr;
+       u16 vlan_id;
+
+       new_fltr = &f_entry->fltr_info;
+       /* VLAN id should only be 12 bits */
+       if (new_fltr->l_data.vlan.vlan_id > ICE_MAX_VLAN_ID)
+               return ICE_ERR_PARAM;
+
+       vlan_id = new_fltr->l_data.vlan.vlan_id;
+       v_list_itr = ice_find_vlan_entry(hw, vlan_id);
+       if (!v_list_itr) {
+               u16 vsi_id = ICE_VSI_INVAL_ID;
+               enum ice_status status;
+               u16 vsi_list_id = 0;
+
+               if (new_fltr->fltr_act == ICE_FWD_TO_VSI) {
+                       enum ice_sw_lkup_type lkup_type = new_fltr->lkup_type;
+
+                       /* All VLAN pruning rules use a VSI list.
+                        * Convert the action to forwarding to a VSI list.
+                        */
+                       vsi_id = new_fltr->fwd_id.vsi_id;
+                       status = ice_create_vsi_list_rule(hw, &vsi_id, 1,
+                                                         &vsi_list_id,
+                                                         lkup_type);
+                       if (status)
+                               return status;
+                       new_fltr->fltr_act = ICE_FWD_TO_VSI_LIST;
+                       new_fltr->fwd_id.vsi_list_id = vsi_list_id;
+               }
+
+               status = ice_create_pkt_fwd_rule(hw, f_entry);
+               if (!status && vsi_id != ICE_VSI_INVAL_ID) {
+                       v_list_itr = ice_find_vlan_entry(hw, vlan_id);
+                       if (!v_list_itr)
+                               return ICE_ERR_DOES_NOT_EXIST;
+                       v_list_itr->vsi_list_info =
+                               ice_create_vsi_list_map(hw, &vsi_id, 1,
+                                                       vsi_list_id);
+               }
+
+               return status;
+       }
+
+       cur_fltr = &v_list_itr->fltr_info;
+       return ice_handle_vsi_list_mgmt(hw, v_list_itr, cur_fltr, new_fltr);
+}
+
+/**
+ * ice_add_vlan - Add VLAN based filter rule
+ * @hw: pointer to the hardware structure
+ * @v_list: list of VLAN entries and forwarding information
+ */
+enum ice_status
+ice_add_vlan(struct ice_hw *hw, struct list_head *v_list)
+{
+       struct ice_fltr_list_entry *v_list_itr;
+
+       if (!v_list || !hw)
+               return ICE_ERR_PARAM;
+
+       list_for_each_entry(v_list_itr, v_list, list_entry) {
+               enum ice_status status;
+
+               if (v_list_itr->fltr_info.lkup_type != ICE_SW_LKUP_VLAN)
+                       return ICE_ERR_PARAM;
+
+               status = ice_add_vlan_internal(hw, v_list_itr);
+               if (status) {
+                       v_list_itr->status = ICE_FLTR_STATUS_FW_FAIL;
+                       return status;
+               }
+               v_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS;
+       }
+       return 0;
+}
+
 /**
  * ice_remove_vsi_list_rule
  * @hw: pointer to the hardware structure
@@ -1515,6 +1626,54 @@ ice_remove_mac_exit:
        return status;
 }
 
+/**
+ * ice_remove_vlan_internal - Remove one VLAN based filter rule
+ * @hw: pointer to the hardware structure
+ * @f_entry: filter entry containing one VLAN information
+ */
+static enum ice_status
+ice_remove_vlan_internal(struct ice_hw *hw,
+                        struct ice_fltr_list_entry *f_entry)
+{
+       struct ice_fltr_info *new_fltr;
+       struct ice_fltr_mgmt_list_entry *v_list_elem;
+       u16 vsi_id;
+
+       new_fltr = &f_entry->fltr_info;
+
+       v_list_elem = ice_find_vlan_entry(hw, new_fltr->l_data.vlan.vlan_id);
+       if (!v_list_elem)
+               return ICE_ERR_PARAM;
+
+       vsi_id = f_entry->fltr_info.fwd_id.vsi_id;
+       return ice_handle_rem_vsi_list_mgmt(hw, vsi_id, v_list_elem);
+}
+
+/**
+ * ice_remove_vlan - Remove VLAN based filter rule
+ * @hw: pointer to the hardware structure
+ * @v_list: list of VLAN entries and forwarding information
+ */
+enum ice_status
+ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list)
+{
+       struct ice_fltr_list_entry *v_list_itr;
+       enum ice_status status = 0;
+
+       if (!v_list || !hw)
+               return ICE_ERR_PARAM;
+
+       list_for_each_entry(v_list_itr, v_list, list_entry) {
+               status = ice_remove_vlan_internal(hw, v_list_itr);
+               if (status) {
+                       v_list_itr->status = ICE_FLTR_STATUS_FW_FAIL;
+                       return status;
+               }
+               v_list_itr->status = ICE_FLTR_STATUS_FW_SUCCESS;
+       }
+       return status;
+}
+
 /**
  * ice_add_to_vsi_fltr_list - Add VSI filters to the list
  * @hw: pointer to the hardware structure
@@ -1600,6 +1759,16 @@ ice_remove_vsi_lkup_fltr(struct ice_hw *hw, u16 vsi_id,
                }
                break;
        case ICE_SW_LKUP_VLAN:
+               mutex_lock(&sw->vlan_list_lock);
+               status = ice_add_to_vsi_fltr_list(hw, vsi_id,
+                                                 &sw->vlan_list_head,
+                                                 &remove_list_head);
+               mutex_unlock(&sw->vlan_list_lock);
+               if (!status) {
+                       ice_remove_vlan(hw, &remove_list_head);
+                       goto free_fltr_list;
+               }
+               break;
        case ICE_SW_LKUP_MAC_VLAN:
        case ICE_SW_LKUP_ETHERTYPE:
        case ICE_SW_LKUP_ETHERTYPE_MAC:
index 1b312e7ab2b5cfce42abf839a0e439000d6aea0d..80b4e1951b3272f75f8fee8bf89c1ffc1a7efe67 100644 (file)
@@ -8,6 +8,7 @@
 
 #define ICE_SW_CFG_MAX_BUF_LEN 2048
 #define ICE_DFLT_VSI_INVAL 0xff
+#define ICE_VSI_INVAL_ID 0xffff
 
 /* VSI context structure for add/get/update/free operations */
 struct ice_vsi_ctx {
@@ -152,4 +153,7 @@ enum ice_status ice_get_initial_sw_cfg(struct ice_hw *hw);
 enum ice_status ice_add_mac(struct ice_hw *hw, struct list_head *m_lst);
 enum ice_status ice_remove_mac(struct ice_hw *hw, struct list_head *m_lst);
 void ice_remove_vsi_fltr(struct ice_hw *hw, u16 vsi_id);
+enum ice_status ice_add_vlan(struct ice_hw *hw, struct list_head *m_list);
+enum ice_status ice_remove_vlan(struct ice_hw *hw, struct list_head *v_list);
+
 #endif /* _ICE_SWITCH_H_ */
index 1ccf8e69b85a0d808c21468496d5ea089f99a3c1..6481e3d863749837ff215621d6e1d21a33d574c3 100644 (file)
@@ -796,6 +796,134 @@ static bool ice_is_non_eop(struct ice_ring *rx_ring,
        return true;
 }
 
+/**
+ * ice_ptype_to_htype - get a hash type
+ * @ptype: the ptype value from the descriptor
+ *
+ * Returns a hash type to be used by skb_set_hash
+ */
+static enum pkt_hash_types ice_ptype_to_htype(u8 __always_unused ptype)
+{
+       return PKT_HASH_TYPE_NONE;
+}
+
+/**
+ * ice_rx_hash - set the hash value in the skb
+ * @rx_ring: descriptor ring
+ * @rx_desc: specific descriptor
+ * @skb: pointer to current skb
+ * @rx_ptype: the ptype value from the descriptor
+ */
+static void
+ice_rx_hash(struct ice_ring *rx_ring, union ice_32b_rx_flex_desc *rx_desc,
+           struct sk_buff *skb, u8 rx_ptype)
+{
+       struct ice_32b_rx_flex_desc_nic *nic_mdid;
+       u32 hash;
+
+       if (!(rx_ring->netdev->features & NETIF_F_RXHASH))
+               return;
+
+       if (rx_desc->wb.rxdid != ICE_RXDID_FLEX_NIC)
+               return;
+
+       nic_mdid = (struct ice_32b_rx_flex_desc_nic *)rx_desc;
+       hash = le32_to_cpu(nic_mdid->rss_hash);
+       skb_set_hash(skb, hash, ice_ptype_to_htype(rx_ptype));
+}
+
+/**
+ * ice_rx_csum - Indicate in skb if checksum is good
+ * @vsi: the VSI we care about
+ * @skb: skb currently being received and modified
+ * @rx_desc: the receive descriptor
+ * @ptype: the packet type decoded by hardware
+ *
+ * skb->protocol must be set before this function is called
+ */
+static void ice_rx_csum(struct ice_vsi *vsi, struct sk_buff *skb,
+                       union ice_32b_rx_flex_desc *rx_desc, u8 ptype)
+{
+       struct ice_rx_ptype_decoded decoded;
+       u32 rx_error, rx_status;
+       bool ipv4, ipv6;
+
+       rx_status = le16_to_cpu(rx_desc->wb.status_error0);
+       rx_error = rx_status;
+
+       decoded = ice_decode_rx_desc_ptype(ptype);
+
+       /* Start with CHECKSUM_NONE and by default csum_level = 0 */
+       skb->ip_summed = CHECKSUM_NONE;
+       skb_checksum_none_assert(skb);
+
+       /* check if Rx checksum is enabled */
+       if (!(vsi->netdev->features & NETIF_F_RXCSUM))
+               return;
+
+       /* check if HW has decoded the packet and checksum */
+       if (!(rx_status & BIT(ICE_RX_FLEX_DESC_STATUS0_L3L4P_S)))
+               return;
+
+       if (!(decoded.known && decoded.outer_ip))
+               return;
+
+       ipv4 = (decoded.outer_ip == ICE_RX_PTYPE_OUTER_IP) &&
+              (decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV4);
+       ipv6 = (decoded.outer_ip == ICE_RX_PTYPE_OUTER_IP) &&
+              (decoded.outer_ip_ver == ICE_RX_PTYPE_OUTER_IPV6);
+
+       if (ipv4 && (rx_error & (BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_IPE_S) |
+                                BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S))))
+               goto checksum_fail;
+       else if (ipv6 && (rx_status &
+                (BIT(ICE_RX_FLEX_DESC_STATUS0_IPV6EXADD_S))))
+               goto checksum_fail;
+
+       /* check for L4 errors and handle packets that were not able to be
+        * checksummed due to arrival speed
+        */
+       if (rx_error & BIT(ICE_RX_FLEX_DESC_STATUS0_XSUM_L4E_S))
+               goto checksum_fail;
+
+       /* Only report checksum unnecessary for TCP, UDP, or SCTP */
+       switch (decoded.inner_prot) {
+       case ICE_RX_PTYPE_INNER_PROT_TCP:
+       case ICE_RX_PTYPE_INNER_PROT_UDP:
+       case ICE_RX_PTYPE_INNER_PROT_SCTP:
+               skb->ip_summed = CHECKSUM_UNNECESSARY;
+       default:
+               break;
+       }
+       return;
+
+checksum_fail:
+       vsi->back->hw_csum_rx_error++;
+}
+
+/**
+ * ice_process_skb_fields - Populate skb header fields from Rx descriptor
+ * @rx_ring: rx descriptor ring packet is being transacted on
+ * @rx_desc: pointer to the EOP Rx descriptor
+ * @skb: pointer to current skb being populated
+ * @ptype: the packet type decoded by hardware
+ *
+ * This function checks the ring, descriptor, and packet information in
+ * order to populate the hash, checksum, VLAN, protocol, and
+ * other fields within the skb.
+ */
+static void ice_process_skb_fields(struct ice_ring *rx_ring,
+                                  union ice_32b_rx_flex_desc *rx_desc,
+                                  struct sk_buff *skb, u8 ptype)
+{
+       ice_rx_hash(rx_ring, rx_desc, skb, ptype);
+
+       /* modifies the skb - consumes the enet header */
+       skb->protocol = eth_type_trans(skb, rx_ring->netdev);
+
+       ice_rx_csum(rx_ring->vsi, skb, rx_desc, ptype);
+}
+
 /**
  * ice_receive_skb - Send a completed packet up the stack
  * @rx_ring: rx ring in play
@@ -839,6 +967,7 @@ static int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget)
                struct sk_buff *skb;
                u16 stat_err_bits;
                u16 vlan_tag = 0;
+               u8 rx_ptype;
 
                /* return some buffers to hardware, one at a time is too slow */
                if (cleaned_count >= ICE_RX_BUF_WRITE) {
@@ -882,6 +1011,9 @@ static int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget)
                        continue;
                }
 
+               rx_ptype = le16_to_cpu(rx_desc->wb.ptype_flex_flags0) &
+                       ICE_RX_FLEX_DESC_PTYPE_M;
+
                stat_err_bits = BIT(ICE_RX_FLEX_DESC_STATUS0_L2TAG1P_S);
                if (ice_test_staterr(rx_desc, stat_err_bits))
                        vlan_tag = le16_to_cpu(rx_desc->wb.l2tag1);
@@ -897,6 +1029,9 @@ static int ice_clean_rx_irq(struct ice_ring *rx_ring, int budget)
                /* probably a little skewed due to removing CRC */
                total_rx_bytes += skb->len;
 
+               /* populate checksum, VLAN, and protocol */
+               ice_process_skb_fields(rx_ring, rx_desc, skb, rx_ptype);
+
                /* send completed skb up the stack */
                ice_receive_skb(rx_ring, skb, vlan_tag);
 
@@ -1026,14 +1161,17 @@ static int ice_maybe_stop_tx(struct ice_ring *tx_ring, unsigned int size)
  * ice_tx_map - Build the Tx descriptor
  * @tx_ring: ring to send buffer on
  * @first: first buffer info buffer to use
+ * @off: pointer to struct that holds offload parameters
  *
  * This function loops over the skb data pointed to by *first
  * and gets a physical address for each memory location and programs
  * it and the length into the transmit descriptor.
  */
-static void ice_tx_map(struct ice_ring *tx_ring, struct ice_tx_buf *first)
+static void
+ice_tx_map(struct ice_ring *tx_ring, struct ice_tx_buf *first,
+          struct ice_tx_offload_params *off)
 {
-       u64 td_offset = 0, td_tag = 0, td_cmd = 0;
+       u64 td_offset, td_tag, td_cmd;
        u16 i = tx_ring->next_to_use;
        struct skb_frag_struct *frag;
        unsigned int data_len, size;
@@ -1042,6 +1180,9 @@ static void ice_tx_map(struct ice_ring *tx_ring, struct ice_tx_buf *first)
        struct sk_buff *skb;
        dma_addr_t dma;
 
+       td_tag = off->td_l2tag1;
+       td_cmd = off->td_cmd;
+       td_offset = off->td_offset;
        skb = first->skb;
 
        data_len = skb->data_len;
@@ -1049,6 +1190,12 @@ static void ice_tx_map(struct ice_ring *tx_ring, struct ice_tx_buf *first)
 
        tx_desc = ICE_TX_DESC(tx_ring, i);
 
+       if (first->tx_flags & ICE_TX_FLAGS_HW_VLAN) {
+               td_cmd |= (u64)ICE_TX_DESC_CMD_IL2TAG1;
+               td_tag = (first->tx_flags & ICE_TX_FLAGS_VLAN_M) >>
+                         ICE_TX_FLAGS_VLAN_S;
+       }
+
        dma = dma_map_single(tx_ring->dev, skb->data, size, DMA_TO_DEVICE);
 
        tx_buf = first;
@@ -1169,6 +1316,223 @@ dma_error:
        tx_ring->next_to_use = i;
 }
 
+/**
+ * ice_tx_csum - Enable Tx checksum offloads
+ * @first: pointer to the first descriptor
+ * @off: pointer to struct that holds offload parameters
+ *
+ * Returns 0 or error (negative) if checksum offload can't happen, 1 otherwise.
+ */
+static
+int ice_tx_csum(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
+{
+       u32 l4_len = 0, l3_len = 0, l2_len = 0;
+       struct sk_buff *skb = first->skb;
+       union {
+               struct iphdr *v4;
+               struct ipv6hdr *v6;
+               unsigned char *hdr;
+       } ip;
+       union {
+               struct tcphdr *tcp;
+               unsigned char *hdr;
+       } l4;
+       __be16 frag_off, protocol;
+       unsigned char *exthdr;
+       u32 offset, cmd = 0;
+       u8 l4_proto = 0;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return 0;
+
+       ip.hdr = skb_network_header(skb);
+       l4.hdr = skb_transport_header(skb);
+
+       /* compute outer L2 header size */
+       l2_len = ip.hdr - skb->data;
+       offset = (l2_len / 2) << ICE_TX_DESC_LEN_MACLEN_S;
+
+       if (skb->encapsulation)
+               return -1;
+
+       /* Enable IP checksum offloads */
+       protocol = vlan_get_protocol(skb);
+       if (protocol == htons(ETH_P_IP)) {
+               l4_proto = ip.v4->protocol;
+               /* the stack computes the IP header already, the only time we
+                * need the hardware to recompute it is in the case of TSO.
+                */
+               if (first->tx_flags & ICE_TX_FLAGS_TSO)
+                       cmd |= ICE_TX_DESC_CMD_IIPT_IPV4_CSUM;
+               else
+                       cmd |= ICE_TX_DESC_CMD_IIPT_IPV4;
+
+       } else if (protocol == htons(ETH_P_IPV6)) {
+               cmd |= ICE_TX_DESC_CMD_IIPT_IPV6;
+               exthdr = ip.hdr + sizeof(*ip.v6);
+               l4_proto = ip.v6->nexthdr;
+               if (l4.hdr != exthdr)
+                       ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto,
+                                        &frag_off);
+       } else {
+               return -1;
+       }
+
+       /* compute inner L3 header size */
+       l3_len = l4.hdr - ip.hdr;
+       offset |= (l3_len / 4) << ICE_TX_DESC_LEN_IPLEN_S;
+
+       /* Enable L4 checksum offloads */
+       switch (l4_proto) {
+       case IPPROTO_TCP:
+               /* enable checksum offloads */
+               cmd |= ICE_TX_DESC_CMD_L4T_EOFT_TCP;
+               l4_len = l4.tcp->doff;
+               offset |= l4_len << ICE_TX_DESC_LEN_L4_LEN_S;
+               break;
+       case IPPROTO_UDP:
+               /* enable UDP checksum offload */
+               cmd |= ICE_TX_DESC_CMD_L4T_EOFT_UDP;
+               l4_len = (sizeof(struct udphdr) >> 2);
+               offset |= l4_len << ICE_TX_DESC_LEN_L4_LEN_S;
+               break;
+       case IPPROTO_SCTP:
+       default:
+               if (first->tx_flags & ICE_TX_FLAGS_TSO)
+                       return -1;
+               skb_checksum_help(skb);
+               return 0;
+       }
+
+       off->td_cmd |= cmd;
+       off->td_offset |= offset;
+       return 1;
+}
+
+/**
+ * ice_tx_prepare_vlan_flags - prepare generic TX VLAN tagging flags for HW
+ * @tx_ring: ring to send buffer on
+ * @first: pointer to struct ice_tx_buf
+ *
+ * Checks the skb and set up correspondingly several generic transmit flags
+ * related to VLAN tagging for the HW, such as VLAN, DCB, etc.
+ *
+ * Returns error code indicate the frame should be dropped upon error and the
+ * otherwise returns 0 to indicate the flags has been set properly.
+ */
+static int
+ice_tx_prepare_vlan_flags(struct ice_ring *tx_ring, struct ice_tx_buf *first)
+{
+       struct sk_buff *skb = first->skb;
+       __be16 protocol = skb->protocol;
+
+       if (protocol == htons(ETH_P_8021Q) &&
+           !(tx_ring->netdev->features & NETIF_F_HW_VLAN_CTAG_TX)) {
+               /* when HW VLAN acceleration is turned off by the user the
+                * stack sets the protocol to 8021q so that the driver
+                * can take any steps required to support the SW only
+                * VLAN handling. In our case the driver doesn't need
+                * to take any further steps so just set the protocol
+                * to the encapsulated ethertype.
+                */
+               skb->protocol = vlan_get_protocol(skb);
+               goto out;
+       }
+
+       /* if we have a HW VLAN tag being added, default to the HW one */
+       if (skb_vlan_tag_present(skb)) {
+               first->tx_flags |= skb_vlan_tag_get(skb) << ICE_TX_FLAGS_VLAN_S;
+               first->tx_flags |= ICE_TX_FLAGS_HW_VLAN;
+       } else if (protocol == htons(ETH_P_8021Q)) {
+               struct vlan_hdr *vhdr, _vhdr;
+
+               /* for SW VLAN, check the next protocol and store the tag */
+               vhdr = (struct vlan_hdr *)skb_header_pointer(skb, ETH_HLEN,
+                                                            sizeof(_vhdr),
+                                                            &_vhdr);
+               if (!vhdr)
+                       return -EINVAL;
+
+               first->tx_flags |= ntohs(vhdr->h_vlan_TCI) <<
+                                  ICE_TX_FLAGS_VLAN_S;
+               first->tx_flags |= ICE_TX_FLAGS_SW_VLAN;
+       }
+
+out:
+       return 0;
+}
+
+/**
+ * ice_tso - computes mss and TSO length to prepare for TSO
+ * @first: pointer to struct ice_tx_buf
+ * @off: pointer to struct that holds offload parameters
+ *
+ * Returns 0 or error (negative) if TSO can't happen, 1 otherwise.
+ */
+static
+int ice_tso(struct ice_tx_buf *first, struct ice_tx_offload_params *off)
+{
+       struct sk_buff *skb = first->skb;
+       union {
+               struct iphdr *v4;
+               struct ipv6hdr *v6;
+               unsigned char *hdr;
+       } ip;
+       union {
+               struct tcphdr *tcp;
+               unsigned char *hdr;
+       } l4;
+       u64 cd_mss, cd_tso_len;
+       u32 paylen, l4_start;
+       int err;
+
+       if (skb->ip_summed != CHECKSUM_PARTIAL)
+               return 0;
+
+       if (!skb_is_gso(skb))
+               return 0;
+
+       err = skb_cow_head(skb, 0);
+       if (err < 0)
+               return err;
+
+       ip.hdr = skb_network_header(skb);
+       l4.hdr = skb_transport_header(skb);
+
+       /* initialize outer IP header fields */
+       if (ip.v4->version == 4) {
+               ip.v4->tot_len = 0;
+               ip.v4->check = 0;
+       } else {
+               ip.v6->payload_len = 0;
+       }
+
+       /* determine offset of transport header */
+       l4_start = l4.hdr - skb->data;
+
+       /* remove payload length from checksum */
+       paylen = skb->len - l4_start;
+       csum_replace_by_diff(&l4.tcp->check, (__force __wsum)htonl(paylen));
+
+       /* compute length of segmentation header */
+       off->header_len = (l4.tcp->doff * 4) + l4_start;
+
+       /* update gso_segs and bytecount */
+       first->gso_segs = skb_shinfo(skb)->gso_segs;
+       first->bytecount = (first->gso_segs - 1) * off->header_len;
+
+       cd_tso_len = skb->len - off->header_len;
+       cd_mss = skb_shinfo(skb)->gso_size;
+
+       /* record cdesc_qw1 with TSO parameters */
+       off->cd_qw1 |= ICE_TX_DESC_DTYPE_CTX |
+                        (ICE_TX_CTX_DESC_TSO << ICE_TXD_CTX_QW1_CMD_S) |
+                        (cd_tso_len << ICE_TXD_CTX_QW1_TSO_LEN_S) |
+                        (cd_mss << ICE_TXD_CTX_QW1_MSS_S);
+       first->tx_flags |= ICE_TX_FLAGS_TSO;
+       return 1;
+}
+
 /**
  * ice_txd_use_count  - estimate the number of descriptors needed for Tx
  * @size: transmit request size in bytes
@@ -1322,8 +1686,10 @@ static bool ice_chk_linearize(struct sk_buff *skb, unsigned int count)
 static netdev_tx_t
 ice_xmit_frame_ring(struct sk_buff *skb, struct ice_ring *tx_ring)
 {
+       struct ice_tx_offload_params offload = { 0 };
        struct ice_tx_buf *first;
        unsigned int count;
+       int tso, csum;
 
        count = ice_xmit_desc_count(skb);
        if (ice_chk_linearize(skb, count)) {
@@ -1344,13 +1710,46 @@ ice_xmit_frame_ring(struct sk_buff *skb, struct ice_ring *tx_ring)
                return NETDEV_TX_BUSY;
        }
 
+       offload.tx_ring = tx_ring;
+
        /* record the location of the first descriptor for this packet */
        first = &tx_ring->tx_buf[tx_ring->next_to_use];
        first->skb = skb;
        first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN);
        first->gso_segs = 1;
+       first->tx_flags = 0;
+
+       /* prepare the VLAN tagging flags for Tx */
+       if (ice_tx_prepare_vlan_flags(tx_ring, first))
+               goto out_drop;
+
+       /* set up TSO offload */
+       tso = ice_tso(first, &offload);
+       if (tso < 0)
+               goto out_drop;
+
+       /* always set up Tx checksum offload */
+       csum = ice_tx_csum(first, &offload);
+       if (csum < 0)
+               goto out_drop;
+
+       if (tso || offload.cd_tunnel_params) {
+               struct ice_tx_ctx_desc *cdesc;
+               int i = tx_ring->next_to_use;
+
+               /* grab the next descriptor */
+               cdesc = ICE_TX_CTX_DESC(tx_ring, i);
+               i++;
+               tx_ring->next_to_use = (i < tx_ring->count) ? i : 0;
+
+               /* setup context descriptor */
+               cdesc->tunneling_params = cpu_to_le32(offload.cd_tunnel_params);
+               cdesc->l2tag2 = cpu_to_le16(offload.cd_l2tag2);
+               cdesc->rsvd = cpu_to_le16(0);
+               cdesc->qw1 = cpu_to_le64(offload.cd_qw1);
+       }
 
-       ice_tx_map(tx_ring, first);
+       ice_tx_map(tx_ring, first, &offload);
        return NETDEV_TX_OK;
 
 out_drop:
index 8fa4450514b410ebfaa9cf5db60d35c90064833d..567067b650c4129acafbea94388731f7ad315ba6 100644 (file)
        ((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
        (R)->next_to_clean - (R)->next_to_use - 1)
 
+#define ICE_TX_FLAGS_TSO       BIT(0)
+#define ICE_TX_FLAGS_HW_VLAN   BIT(1)
+#define ICE_TX_FLAGS_SW_VLAN   BIT(2)
+#define ICE_TX_FLAGS_VLAN_M    0xffff0000
+#define ICE_TX_FLAGS_VLAN_S    16
+
 struct ice_tx_buf {
        struct ice_tx_desc *next_to_watch;
        struct sk_buff *skb;
@@ -38,6 +44,17 @@ struct ice_tx_buf {
        DEFINE_DMA_UNMAP_LEN(len);
 };
 
+struct ice_tx_offload_params {
+       u8 header_len;
+       u32 td_cmd;
+       u32 td_offset;
+       u32 td_l2tag1;
+       u16 cd_l2tag2;
+       u32 cd_tunnel_params;
+       u64 cd_qw1;
+       struct ice_ring *tx_ring;
+};
+
 struct ice_rx_buf {
        struct sk_buff *skb;
        dma_addr_t dma;