Commit 1e007181 authored by Xue Chaojing's avatar Xue Chaojing Committed by David S. Miller

hinic: add LRO support

This patch adds LRO support for the HiNIC driver.
Reported-by: default avatarkbuild test robot <lkp@intel.com>
Reviewed-by: default avatarJesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: default avatarXue Chaojing <xuechaojing@huawei.com>
Reviewed-by: default avatarJesse Brandeburg <jesse.brandeburg@intel.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 600bb031
......@@ -313,6 +313,8 @@ static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int rq_depth,
hw_ioctxt.set_cmdq_depth = HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT;
hw_ioctxt.cmdq_depth = 0;
hw_ioctxt.lro_en = 1;
hw_ioctxt.rq_depth = ilog2(rq_depth);
hw_ioctxt.rx_buf_sz_idx = HINIC_RX_BUF_SZ_IDX;
......
......@@ -50,6 +50,8 @@ enum hinic_port_cmd {
HINIC_PORT_CMD_GET_LINK_STATE = 24,
HINIC_PORT_CMD_SET_LRO = 25,
HINIC_PORT_CMD_SET_RX_CSUM = 26,
HINIC_PORT_CMD_SET_PORT_STATE = 41,
......@@ -62,7 +64,11 @@ enum hinic_port_cmd {
HINIC_PORT_CMD_SET_TSO = 112,
HINIC_PORT_CMD_SET_RQ_IQ_MAP = 115,
HINIC_PORT_CMD_GET_CAP = 170,
HINIC_PORT_CMD_SET_LRO_TIMER = 244,
};
enum hinic_mgmt_msg_cmd {
......@@ -106,7 +112,7 @@ struct hinic_cmd_hw_ioctxt {
u8 set_cmdq_depth;
u8 cmdq_depth;
u8 rsvd2;
u8 lro_en;
u8 rsvd3;
u8 rsvd4;
u8 rsvd5;
......
......@@ -45,6 +45,7 @@
enum io_cmd {
IO_CMD_MODIFY_QUEUE_CTXT = 0,
IO_CMD_CLEAN_QUEUE_CTXT,
};
static void init_db_area_idx(struct hinic_free_db_area *free_db_area)
......@@ -210,6 +211,59 @@ static int write_qp_ctxts(struct hinic_func_to_io *func_to_io, u16 base_qpn,
write_rq_ctxts(func_to_io, base_qpn, num_qps));
}
static int hinic_clean_queue_offload_ctxt(struct hinic_func_to_io *func_to_io,
enum hinic_qp_ctxt_type ctxt_type)
{
struct hinic_hwif *hwif = func_to_io->hwif;
struct hinic_clean_queue_ctxt *ctxt_block;
struct pci_dev *pdev = hwif->pdev;
struct hinic_cmdq_buf cmdq_buf;
u64 out_param = 0;
int err;
err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf);
if (err) {
dev_err(&pdev->dev, "Failed to allocate cmdq buf\n");
return err;
}
ctxt_block = cmdq_buf.buf;
ctxt_block->cmdq_hdr.num_queues = func_to_io->max_qps;
ctxt_block->cmdq_hdr.queue_type = ctxt_type;
ctxt_block->cmdq_hdr.addr_offset = 0;
/* TSO/LRO ctxt size: 0x0:0B; 0x1:160B; 0x2:200B; 0x3:240B */
ctxt_block->ctxt_size = 0x3;
hinic_cpu_to_be32(ctxt_block, sizeof(*ctxt_block));
cmdq_buf.size = sizeof(*ctxt_block);
err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC,
IO_CMD_CLEAN_QUEUE_CTXT,
&cmdq_buf, &out_param);
if (err || out_param) {
dev_err(&pdev->dev, "Failed to clean offload ctxts, err: %d, out_param: 0x%llx\n",
err, out_param);
err = -EFAULT;
}
hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf);
return err;
}
static int hinic_clean_qp_offload_ctxt(struct hinic_func_to_io *func_to_io)
{
/* clean LRO/TSO context space */
return (hinic_clean_queue_offload_ctxt(func_to_io,
HINIC_QP_CTXT_TYPE_SQ) ||
hinic_clean_queue_offload_ctxt(func_to_io,
HINIC_QP_CTXT_TYPE_RQ));
}
/**
* init_qp - Initialize a Queue Pair
* @func_to_io: func to io channel that holds the IO components
......@@ -381,6 +435,12 @@ int hinic_io_create_qps(struct hinic_func_to_io *func_to_io,
goto err_write_qp_ctxts;
}
err = hinic_clean_qp_offload_ctxt(func_to_io);
if (err) {
dev_err(&pdev->dev, "Failed to clean QP contexts space\n");
goto err_write_qp_ctxts;
}
return 0;
err_write_qp_ctxts:
......
......@@ -201,6 +201,11 @@ struct hinic_rq_ctxt {
u32 wq_block_lo_pfn;
};
struct hinic_clean_queue_ctxt {
struct hinic_qp_ctxt_header cmdq_hdr;
u32 ctxt_size;
};
struct hinic_sq_ctxt_block {
struct hinic_qp_ctxt_header hdr;
struct hinic_sq_ctxt sq_ctxt[HINIC_Q_CTXT_MAX];
......
......@@ -219,6 +219,26 @@
#define HINIC_MSS_DEFAULT 0x3E00
#define HINIC_MSS_MIN 0x50
#define RQ_CQE_STATUS_NUM_LRO_SHIFT 16
#define RQ_CQE_STATUS_NUM_LRO_MASK 0xFFU
#define RQ_CQE_STATUS_GET(val, member) (((val) >> \
RQ_CQE_STATUS_##member##_SHIFT) & \
RQ_CQE_STATUS_##member##_MASK)
#define HINIC_GET_RX_NUM_LRO(status) \
RQ_CQE_STATUS_GET(status, NUM_LRO)
#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_SHIFT 0
#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_MASK 0xFFFU
#define RQ_CQE_OFFOLAD_TYPE_GET(val, member) (((val) >> \
RQ_CQE_OFFOLAD_TYPE_##member##_SHIFT) & \
RQ_CQE_OFFOLAD_TYPE_##member##_MASK)
#define HINIC_GET_RX_PKT_TYPE(offload_type) \
RQ_CQE_OFFOLAD_TYPE_GET(offload_type, PKT_TYPE)
enum hinic_l4offload_type {
HINIC_L4_OFF_DISABLE = 0,
HINIC_TCP_OFFLOAD_ENABLE = 1,
......@@ -372,7 +392,7 @@ struct hinic_rq_cqe {
u32 status;
u32 len;
u32 rsvd2;
u32 offload_type;
u32 rsvd3;
u32 rsvd4;
u32 rsvd5;
......
......@@ -62,6 +62,10 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)");
NETIF_MSG_IFUP | \
NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR)
#define HINIC_LRO_MAX_WQE_NUM_DEFAULT 8
#define HINIC_LRO_RX_TIMER_DEFAULT 16
#define VLAN_BITMAP_SIZE(nic_dev) (ALIGN(VLAN_N_VID, 8) / 8)
#define work_to_rx_mode_work(work) \
......@@ -72,6 +76,10 @@ MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)");
static int change_mac_addr(struct net_device *netdev, const u8 *addr);
static int set_features(struct hinic_dev *nic_dev,
netdev_features_t pre_features,
netdev_features_t features, bool force_change);
static void set_link_speed(struct ethtool_link_ksettings *link_ksettings,
enum hinic_speed speed)
{
......@@ -372,6 +380,17 @@ static void free_rxqs(struct hinic_dev *nic_dev)
nic_dev->rxqs = NULL;
}
static int hinic_configure_max_qnum(struct hinic_dev *nic_dev)
{
int err;
err = hinic_set_max_qnum(nic_dev, nic_dev->hwdev->nic_cap.max_qps);
if (err)
return err;
return 0;
}
static int hinic_open(struct net_device *netdev)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
......@@ -401,6 +420,13 @@ static int hinic_open(struct net_device *netdev)
goto err_create_rxqs;
}
err = hinic_configure_max_qnum(nic_dev);
if (err) {
netif_err(nic_dev, drv, nic_dev->netdev,
"Failed to configure the maximum number of queues\n");
goto err_port_state;
}
num_qps = hinic_hwdev_num_qps(nic_dev->hwdev);
netif_set_real_num_tx_queues(netdev, num_qps);
netif_set_real_num_rx_queues(netdev, num_qps);
......@@ -787,6 +813,29 @@ static void hinic_get_stats64(struct net_device *netdev,
stats->tx_errors = nic_tx_stats->tx_dropped;
}
static int hinic_set_features(struct net_device *netdev,
netdev_features_t features)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
return set_features(nic_dev, nic_dev->netdev->features,
features, false);
}
static netdev_features_t hinic_fix_features(struct net_device *netdev,
netdev_features_t features)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
/* If Rx checksum is disabled, then LRO should also be disabled */
if (!(features & NETIF_F_RXCSUM)) {
netif_info(nic_dev, drv, netdev, "disabling LRO as RXCSUM is off\n");
features &= ~NETIF_F_LRO;
}
return features;
}
static const struct net_device_ops hinic_netdev_ops = {
.ndo_open = hinic_open,
.ndo_stop = hinic_close,
......@@ -799,13 +848,16 @@ static const struct net_device_ops hinic_netdev_ops = {
.ndo_start_xmit = hinic_xmit_frame,
.ndo_tx_timeout = hinic_tx_timeout,
.ndo_get_stats64 = hinic_get_stats64,
.ndo_fix_features = hinic_fix_features,
.ndo_set_features = hinic_set_features,
};
static void netdev_features_init(struct net_device *netdev)
{
netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_IP_CSUM |
NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |
NETIF_F_RXCSUM;
NETIF_F_RXCSUM | NETIF_F_LRO;
netdev->vlan_features = netdev->hw_features;
......@@ -878,6 +930,13 @@ static int set_features(struct hinic_dev *nic_dev,
if (changed & NETIF_F_RXCSUM)
err = hinic_set_rx_csum_offload(nic_dev, csum_en);
if (changed & NETIF_F_LRO) {
err = hinic_set_rx_lro_state(nic_dev,
!!(features & NETIF_F_LRO),
HINIC_LRO_RX_TIMER_DEFAULT,
HINIC_LRO_MAX_WQE_NUM_DEFAULT);
}
return err;
}
......
......@@ -439,3 +439,117 @@ int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en)
return 0;
}
int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_rq_num rq_num = { 0 };
u16 out_size = sizeof(rq_num);
int err;
rq_num.func_id = HINIC_HWIF_FUNC_IDX(hwif);
rq_num.num_rqs = num_rqs;
rq_num.rq_depth = ilog2(HINIC_SQ_DEPTH);
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RQ_IQ_MAP,
&rq_num, sizeof(rq_num),
&rq_num, &out_size);
if (err || !out_size || rq_num.status) {
dev_err(&pdev->dev,
"Failed to rxq number, ret = %d\n",
rq_num.status);
return -EINVAL;
}
return 0;
}
static int hinic_set_rx_lro(struct hinic_dev *nic_dev, u8 ipv4_en, u8 ipv6_en,
u8 max_wqe_num)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_hwif *hwif = hwdev->hwif;
struct hinic_lro_config lro_cfg = { 0 };
struct pci_dev *pdev = hwif->pdev;
u16 out_size = sizeof(lro_cfg);
int err;
lro_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif);
lro_cfg.lro_ipv4_en = ipv4_en;
lro_cfg.lro_ipv6_en = ipv6_en;
lro_cfg.lro_max_wqe_num = max_wqe_num;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LRO,
&lro_cfg, sizeof(lro_cfg),
&lro_cfg, &out_size);
if (err || !out_size || lro_cfg.status) {
dev_err(&pdev->dev,
"Failed to set lro offload, ret = %d\n",
lro_cfg.status);
return -EINVAL;
}
return 0;
}
static int hinic_set_rx_lro_timer(struct hinic_dev *nic_dev, u32 timer_value)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
struct hinic_lro_timer lro_timer = { 0 };
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
u16 out_size = sizeof(lro_timer);
int err;
lro_timer.status = 0;
lro_timer.type = 0;
lro_timer.enable = 1;
lro_timer.timer = timer_value;
err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LRO_TIMER,
&lro_timer, sizeof(lro_timer),
&lro_timer, &out_size);
if (lro_timer.status == 0xFF) {
/* For this case, we think status (0xFF) is OK */
lro_timer.status = 0;
dev_dbg(&pdev->dev,
"Set lro timer not supported by the current FW version, it will be 1ms default\n");
}
if (err || !out_size || lro_timer.status) {
dev_err(&pdev->dev,
"Failed to set lro timer, ret = %d\n",
lro_timer.status);
return -EINVAL;
}
return 0;
}
int hinic_set_rx_lro_state(struct hinic_dev *nic_dev, u8 lro_en,
u32 lro_timer, u32 wqe_num)
{
struct hinic_hwdev *hwdev = nic_dev->hwdev;
u8 ipv4_en;
u8 ipv6_en;
int err;
if (!hwdev)
return -EINVAL;
ipv4_en = lro_en ? 1 : 0;
ipv6_en = lro_en ? 1 : 0;
err = hinic_set_rx_lro(nic_dev, ipv4_en, ipv6_en, (u8)wqe_num);
if (err)
return err;
err = hinic_set_rx_lro_timer(nic_dev, lro_timer);
if (err)
return err;
return 0;
}
......@@ -192,6 +192,42 @@ struct hinic_checksum_offload {
u16 rsvd1;
u32 rx_csum_offload;
};
struct hinic_rq_num {
u8 status;
u8 version;
u8 rsvd0[6];
u16 func_id;
u16 rsvd1[33];
u32 num_rqs;
u32 rq_depth;
};
struct hinic_lro_config {
u8 status;
u8 version;
u8 rsvd0[6];
u16 func_id;
u16 rsvd1;
u8 lro_ipv4_en;
u8 lro_ipv6_en;
u8 lro_max_wqe_num;
u8 resv2[13];
};
struct hinic_lro_timer {
u8 status;
u8 version;
u8 rsvd0[6];
u8 type; /* 0: set timer value, 1: get timer value */
u8 enable; /* when set lro time, enable should be 1 */
u16 rsvd1;
u32 timer;
};
int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
u16 vlan_id);
......@@ -220,7 +256,12 @@ int hinic_port_set_func_state(struct hinic_dev *nic_dev,
int hinic_port_get_cap(struct hinic_dev *nic_dev,
struct hinic_port_cap *port_cap);
int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs);
int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state);
int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en);
int hinic_set_rx_lro_state(struct hinic_dev *nic_dev, u8 lro_en,
u32 lro_timer, u32 wqe_num);
#endif
......@@ -45,6 +45,15 @@
#define RX_IRQ_NO_RESEND_TIMER 0
#define HINIC_RX_BUFFER_WRITE 16
#define HINIC_RX_IPV6_PKT 7
#define LRO_PKT_HDR_LEN_IPV4 66
#define LRO_PKT_HDR_LEN_IPV6 86
#define LRO_REPLENISH_THLD 256
#define LRO_PKT_HDR_LEN(cqe) \
(HINIC_GET_RX_PKT_TYPE(be32_to_cpu((cqe)->offload_type)) == \
HINIC_RX_IPV6_PKT ? LRO_PKT_HDR_LEN_IPV6 : LRO_PKT_HDR_LEN_IPV4)
/**
* hinic_rxq_clean_stats - Clean the statistics of specific queue
* @rxq: Logical Rx Queue
......@@ -90,18 +99,12 @@ static void rxq_stats_init(struct hinic_rxq *rxq)
hinic_rxq_clean_stats(rxq);
}
static void rx_csum(struct hinic_rxq *rxq, u16 cons_idx,
static void rx_csum(struct hinic_rxq *rxq, u32 status,
struct sk_buff *skb)
{
struct net_device *netdev = rxq->netdev;
struct hinic_rq_cqe *cqe;
struct hinic_rq *rq;
u32 csum_err;
u32 status;
rq = rxq->rq;
cqe = rq->cqe[cons_idx];
status = be32_to_cpu(cqe->status);
csum_err = HINIC_RQ_CQE_STATUS_GET(status, CSUM_ERR);
if (!(netdev->features & NETIF_F_RXCSUM))
......@@ -321,12 +324,16 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
{
struct hinic_qp *qp = container_of(rxq->rq, struct hinic_qp, rq);
u64 pkt_len = 0, rx_bytes = 0;
struct hinic_rq *rq = rxq->rq;
struct hinic_rq_wqe *rq_wqe;
unsigned int free_wqebbs;
struct hinic_rq_cqe *cqe;
int num_wqes, pkts = 0;
struct hinic_sge sge;
unsigned int status;
struct sk_buff *skb;
u16 ci;
u16 ci, num_lro;
u16 num_wqe = 0;
while (pkts < budget) {
num_wqes = 0;
......@@ -336,11 +343,13 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
if (!rq_wqe)
break;
cqe = rq->cqe[ci];
status = be32_to_cpu(cqe->status);
hinic_rq_get_sge(rxq->rq, rq_wqe, ci, &sge);
rx_unmap_skb(rxq, hinic_sge_to_dma(&sge));
rx_csum(rxq, ci, skb);
rx_csum(rxq, status, skb);
prefetch(skb->data);
......@@ -354,7 +363,7 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
HINIC_RX_BUF_SZ, ci);
}
hinic_rq_put_wqe(rxq->rq, ci,
hinic_rq_put_wqe(rq, ci,
(num_wqes + 1) * HINIC_RQ_WQE_SIZE);
skb_record_rx_queue(skb, qp->q_id);
......@@ -364,6 +373,21 @@ static int rxq_recv(struct hinic_rxq *rxq, int budget)
pkts++;
rx_bytes += pkt_len;
num_lro = HINIC_GET_RX_NUM_LRO(status);
if (num_lro) {
rx_bytes += ((num_lro - 1) *
LRO_PKT_HDR_LEN(cqe));
num_wqe +=
(u16)(pkt_len >> rxq->rx_buff_shift) +
((pkt_len & (rxq->buf_len - 1)) ? 1 : 0);
}
cqe->status = 0;
if (num_wqe >= LRO_REPLENISH_THLD)
break;
}
free_wqebbs = hinic_get_rq_free_wqebbs(rxq->rq);
......@@ -482,6 +506,8 @@ int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq,
rxq->netdev = netdev;
rxq->rq = rq;
rxq->buf_len = HINIC_RX_BUF_SZ;
rxq->rx_buff_shift = ilog2(HINIC_RX_BUF_SZ);
rxq_stats_init(rxq);
......
......@@ -41,6 +41,8 @@ struct hinic_rxq {
struct hinic_rxq_stats rxq_stats;
char *irq_name;
u16 buf_len;
u32 rx_buff_shift;
struct napi_struct napi;
};
......
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