Commit d76a60ba authored by Anirudh Venkataramanan's avatar Anirudh Venkataramanan Committed by Jeff Kirsher

ice: Add support for VLANs and offloads

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: default avatarAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: default avatarTony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent 2b245cb2
......@@ -23,7 +23,10 @@
#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_ */
......@@ -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,
......
......@@ -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
......
......@@ -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);
......
......@@ -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_ */
......@@ -663,6 +663,35 @@ static int ice_vsi_req_irq_msix(struct ice_vsi *vsi, char *basename)
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 @@ static int ice_vsi_req_irq_msix(struct ice_vsi *vsi, char *basename)
*/
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 @@ ice_vsi_setup(struct ice_pf *pf, enum ice_vsi_type type,
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,
};
......@@ -1217,6 +1217,117 @@ ice_add_mac(struct ice_hw *hw, struct list_head *m_list)
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(struct ice_hw *hw, struct list_head *m_list)
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:
......
......@@ -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_ */
......@@ -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 @@ static void ice_tx_map(struct ice_ring *tx_ring, struct ice_tx_buf *first)
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:
......
......@@ -28,6 +28,12 @@
((((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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment