Commit d1c29cfd authored by David S. Miller's avatar David S. Miller

Merge branch 'ionic-cleanups-and-perf-tuning'

Shannon Nelson says:

====================
ionic: code cleanup and performance tuning

Brett has been performance testing and code tweaking and has
come up with several improvements for our fast path operations.

In a simple single thread / single queue iperf case on a 1500 MTU
connection we see an improvement from 74.2 to 86.7 Gbits/sec.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents df620d7f 217397da
...@@ -113,8 +113,8 @@ static const struct debugfs_reg32 intr_ctrl_regs[] = { ...@@ -113,8 +113,8 @@ static const struct debugfs_reg32 intr_ctrl_regs[] = {
void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq) void ionic_debugfs_add_qcq(struct ionic_lif *lif, struct ionic_qcq *qcq)
{ {
struct dentry *qcq_dentry, *q_dentry, *cq_dentry; struct dentry *qcq_dentry, *q_dentry, *cq_dentry;
struct dentry *intr_dentry, *stats_dentry;
struct ionic_dev *idev = &lif->ionic->idev; struct ionic_dev *idev = &lif->ionic->idev;
struct dentry *intr_dentry, *stats_dentry;
struct debugfs_regset32 *intr_ctrl_regset; struct debugfs_regset32 *intr_ctrl_regset;
struct ionic_intr_info *intr = &qcq->intr; struct ionic_intr_info *intr = &qcq->intr;
struct debugfs_blob_wrapper *desc_blob; struct debugfs_blob_wrapper *desc_blob;
......
...@@ -16,9 +16,10 @@ ...@@ -16,9 +16,10 @@
#define IONIC_MAX_TX_DESC 8192 #define IONIC_MAX_TX_DESC 8192
#define IONIC_MAX_RX_DESC 16384 #define IONIC_MAX_RX_DESC 16384
#define IONIC_MIN_TXRX_DESC 64 #define IONIC_MIN_TXRX_DESC 64
#define IONIC_DEF_TXRX_DESC 4096 #define IONIC_DEF_TXRX_DESC 1024
#define IONIC_RX_FILL_THRESHOLD 16 #define IONIC_RX_FILL_THRESHOLD 16
#define IONIC_RX_FILL_DIV 8 #define IONIC_RX_FILL_DIV 8
#define IONIC_TSO_DESCS_NEEDED 44 /* 64K TSO @1500B */
#define IONIC_LIFS_MAX 1024 #define IONIC_LIFS_MAX 1024
#define IONIC_WATCHDOG_SECS 5 #define IONIC_WATCHDOG_SECS 5
#define IONIC_ITR_COAL_USEC_DEFAULT 64 #define IONIC_ITR_COAL_USEC_DEFAULT 64
...@@ -379,6 +380,7 @@ typedef void (*ionic_cq_done_cb)(void *done_arg); ...@@ -379,6 +380,7 @@ typedef void (*ionic_cq_done_cb)(void *done_arg);
unsigned int ionic_cq_service(struct ionic_cq *cq, unsigned int work_to_do, unsigned int ionic_cq_service(struct ionic_cq *cq, unsigned int work_to_do,
ionic_cq_cb cb, ionic_cq_done_cb done_cb, ionic_cq_cb cb, ionic_cq_done_cb done_cb,
void *done_arg); void *done_arg);
unsigned int ionic_tx_cq_service(struct ionic_cq *cq, unsigned int work_to_do);
int ionic_q_init(struct ionic_lif *lif, struct ionic_dev *idev, int ionic_q_init(struct ionic_lif *lif, struct ionic_dev *idev,
struct ionic_queue *q, unsigned int index, const char *name, struct ionic_queue *q, unsigned int index, const char *name,
......
...@@ -52,15 +52,20 @@ static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q); ...@@ -52,15 +52,20 @@ static void ionic_xdp_unregister_rxq_info(struct ionic_queue *q);
static void ionic_dim_work(struct work_struct *work) static void ionic_dim_work(struct work_struct *work)
{ {
struct dim *dim = container_of(work, struct dim, work); struct dim *dim = container_of(work, struct dim, work);
struct ionic_intr_info *intr;
struct dim_cq_moder cur_moder; struct dim_cq_moder cur_moder;
struct ionic_intr_info *intr;
struct ionic_qcq *qcq; struct ionic_qcq *qcq;
struct ionic_lif *lif; struct ionic_lif *lif;
struct ionic_queue *q;
u32 new_coal; u32 new_coal;
cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
qcq = container_of(dim, struct ionic_qcq, dim); qcq = container_of(dim, struct ionic_qcq, dim);
lif = qcq->q.lif; q = &qcq->q;
if (q->type == IONIC_QTYPE_RXQ)
cur_moder = net_dim_get_rx_moderation(dim->mode, dim->profile_ix);
else
cur_moder = net_dim_get_tx_moderation(dim->mode, dim->profile_ix);
lif = q->lif;
new_coal = ionic_coal_usec_to_hw(lif->ionic, cur_moder.usec); new_coal = ionic_coal_usec_to_hw(lif->ionic, cur_moder.usec);
new_coal = new_coal ? new_coal : 1; new_coal = new_coal ? new_coal : 1;
...@@ -685,7 +690,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type, ...@@ -685,7 +690,7 @@ static int ionic_qcq_alloc(struct ionic_lif *lif, unsigned int type,
} }
INIT_WORK(&new->dim.work, ionic_dim_work); INIT_WORK(&new->dim.work, ionic_dim_work);
new->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; new->dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_CQE;
*qcq = new; *qcq = new;
...@@ -1262,8 +1267,7 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget) ...@@ -1262,8 +1267,7 @@ static int ionic_adminq_napi(struct napi_struct *napi, int budget)
ionic_rx_service, NULL, NULL); ionic_rx_service, NULL, NULL);
if (lif->hwstamp_txq) if (lif->hwstamp_txq)
tx_work = ionic_cq_service(&lif->hwstamp_txq->cq, budget, tx_work = ionic_tx_cq_service(&lif->hwstamp_txq->cq, budget);
ionic_tx_service, NULL, NULL);
work_done = max(max(n_work, a_work), max(rx_work, tx_work)); work_done = max(max(n_work, a_work), max(rx_work, tx_work));
if (work_done < budget && napi_complete_done(napi, work_done)) { if (work_done < budget && napi_complete_done(napi, work_done)) {
......
...@@ -327,7 +327,7 @@ static inline u32 ionic_coal_usec_to_hw(struct ionic *ionic, u32 usecs) ...@@ -327,7 +327,7 @@ static inline u32 ionic_coal_usec_to_hw(struct ionic *ionic, u32 usecs)
static inline bool ionic_txq_hwstamp_enabled(struct ionic_queue *q) static inline bool ionic_txq_hwstamp_enabled(struct ionic_queue *q)
{ {
return unlikely(q->features & IONIC_TXQ_F_HWSTAMP); return q->features & IONIC_TXQ_F_HWSTAMP;
} }
void ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep); void ionic_link_status_check_request(struct ionic_lif *lif, bool can_sleep);
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
#include "ionic_debugfs.h" #include "ionic_debugfs.h"
MODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION); MODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION);
MODULE_AUTHOR("Pensando Systems, Inc"); MODULE_AUTHOR("Shannon Nelson <shannon.nelson@amd.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
static const char *ionic_error_to_str(enum ionic_status_code code) static const char *ionic_error_to_str(enum ionic_status_code code)
......
...@@ -5,13 +5,12 @@ ...@@ -5,13 +5,12 @@
#include <linux/ipv6.h> #include <linux/ipv6.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <net/ip6_checksum.h> #include <net/ip6_checksum.h>
#include <net/netdev_queues.h>
#include "ionic.h" #include "ionic.h"
#include "ionic_lif.h" #include "ionic_lif.h"
#include "ionic_txrx.h" #include "ionic_txrx.h"
static int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs);
static dma_addr_t ionic_tx_map_single(struct ionic_queue *q, static dma_addr_t ionic_tx_map_single(struct ionic_queue *q,
void *data, size_t len); void *data, size_t len);
...@@ -41,8 +40,8 @@ static inline void ionic_rxq_post(struct ionic_queue *q, bool ring_dbell, ...@@ -41,8 +40,8 @@ static inline void ionic_rxq_post(struct ionic_queue *q, bool ring_dbell,
bool ionic_txq_poke_doorbell(struct ionic_queue *q) bool ionic_txq_poke_doorbell(struct ionic_queue *q)
{ {
unsigned long now, then, dif;
struct netdev_queue *netdev_txq; struct netdev_queue *netdev_txq;
unsigned long now, then, dif;
struct net_device *netdev; struct net_device *netdev;
netdev = q->lif->netdev; netdev = q->lif->netdev;
...@@ -100,9 +99,10 @@ bool ionic_rxq_poke_doorbell(struct ionic_queue *q) ...@@ -100,9 +99,10 @@ bool ionic_rxq_poke_doorbell(struct ionic_queue *q)
return true; return true;
} }
static inline struct netdev_queue *q_to_ndq(struct ionic_queue *q) static inline struct netdev_queue *q_to_ndq(struct net_device *netdev,
struct ionic_queue *q)
{ {
return netdev_get_tx_queue(q->lif->netdev, q->index); return netdev_get_tx_queue(netdev, q->index);
} }
static void *ionic_rx_buf_va(struct ionic_buf_info *buf_info) static void *ionic_rx_buf_va(struct ionic_buf_info *buf_info)
...@@ -123,7 +123,6 @@ static unsigned int ionic_rx_buf_size(struct ionic_buf_info *buf_info) ...@@ -123,7 +123,6 @@ static unsigned int ionic_rx_buf_size(struct ionic_buf_info *buf_info)
static int ionic_rx_page_alloc(struct ionic_queue *q, static int ionic_rx_page_alloc(struct ionic_queue *q,
struct ionic_buf_info *buf_info) struct ionic_buf_info *buf_info)
{ {
struct net_device *netdev = q->lif->netdev;
struct ionic_rx_stats *stats; struct ionic_rx_stats *stats;
struct device *dev; struct device *dev;
struct page *page; struct page *page;
...@@ -133,14 +132,14 @@ static int ionic_rx_page_alloc(struct ionic_queue *q, ...@@ -133,14 +132,14 @@ static int ionic_rx_page_alloc(struct ionic_queue *q,
if (unlikely(!buf_info)) { if (unlikely(!buf_info)) {
net_err_ratelimited("%s: %s invalid buf_info in alloc\n", net_err_ratelimited("%s: %s invalid buf_info in alloc\n",
netdev->name, q->name); dev_name(dev), q->name);
return -EINVAL; return -EINVAL;
} }
page = alloc_pages(IONIC_PAGE_GFP_MASK, 0); page = alloc_pages(IONIC_PAGE_GFP_MASK, 0);
if (unlikely(!page)) { if (unlikely(!page)) {
net_err_ratelimited("%s: %s page alloc failed\n", net_err_ratelimited("%s: %s page alloc failed\n",
netdev->name, q->name); dev_name(dev), q->name);
stats->alloc_err++; stats->alloc_err++;
return -ENOMEM; return -ENOMEM;
} }
...@@ -150,7 +149,7 @@ static int ionic_rx_page_alloc(struct ionic_queue *q, ...@@ -150,7 +149,7 @@ static int ionic_rx_page_alloc(struct ionic_queue *q,
if (unlikely(dma_mapping_error(dev, buf_info->dma_addr))) { if (unlikely(dma_mapping_error(dev, buf_info->dma_addr))) {
__free_pages(page, 0); __free_pages(page, 0);
net_err_ratelimited("%s: %s dma map failed\n", net_err_ratelimited("%s: %s dma map failed\n",
netdev->name, q->name); dev_name(dev), q->name);
stats->dma_map_err++; stats->dma_map_err++;
return -EIO; return -EIO;
} }
...@@ -164,12 +163,11 @@ static int ionic_rx_page_alloc(struct ionic_queue *q, ...@@ -164,12 +163,11 @@ static int ionic_rx_page_alloc(struct ionic_queue *q,
static void ionic_rx_page_free(struct ionic_queue *q, static void ionic_rx_page_free(struct ionic_queue *q,
struct ionic_buf_info *buf_info) struct ionic_buf_info *buf_info)
{ {
struct net_device *netdev = q->lif->netdev;
struct device *dev = q->dev; struct device *dev = q->dev;
if (unlikely(!buf_info)) { if (unlikely(!buf_info)) {
net_err_ratelimited("%s: %s invalid buf_info in free\n", net_err_ratelimited("%s: %s invalid buf_info in free\n",
netdev->name, q->name); dev_name(dev), q->name);
return; return;
} }
...@@ -204,14 +202,14 @@ static bool ionic_rx_buf_recycle(struct ionic_queue *q, ...@@ -204,14 +202,14 @@ static bool ionic_rx_buf_recycle(struct ionic_queue *q,
return true; return true;
} }
static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, static struct sk_buff *ionic_rx_frags(struct net_device *netdev,
struct ionic_queue *q,
struct ionic_desc_info *desc_info, struct ionic_desc_info *desc_info,
unsigned int headroom, unsigned int headroom,
unsigned int len, unsigned int len,
unsigned int num_sg_elems, unsigned int num_sg_elems,
bool synced) bool synced)
{ {
struct net_device *netdev = q->lif->netdev;
struct ionic_buf_info *buf_info; struct ionic_buf_info *buf_info;
struct ionic_rx_stats *stats; struct ionic_rx_stats *stats;
struct device *dev = q->dev; struct device *dev = q->dev;
...@@ -228,7 +226,7 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, ...@@ -228,7 +226,7 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q,
skb = napi_get_frags(&q_to_qcq(q)->napi); skb = napi_get_frags(&q_to_qcq(q)->napi);
if (unlikely(!skb)) { if (unlikely(!skb)) {
net_warn_ratelimited("%s: SKB alloc failed on %s!\n", net_warn_ratelimited("%s: SKB alloc failed on %s!\n",
netdev->name, q->name); dev_name(dev), q->name);
stats->alloc_err++; stats->alloc_err++;
return NULL; return NULL;
} }
...@@ -272,13 +270,13 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q, ...@@ -272,13 +270,13 @@ static struct sk_buff *ionic_rx_frags(struct ionic_queue *q,
return skb; return skb;
} }
static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, static struct sk_buff *ionic_rx_copybreak(struct net_device *netdev,
struct ionic_queue *q,
struct ionic_desc_info *desc_info, struct ionic_desc_info *desc_info,
unsigned int headroom, unsigned int headroom,
unsigned int len, unsigned int len,
bool synced) bool synced)
{ {
struct net_device *netdev = q->lif->netdev;
struct ionic_buf_info *buf_info; struct ionic_buf_info *buf_info;
struct ionic_rx_stats *stats; struct ionic_rx_stats *stats;
struct device *dev = q->dev; struct device *dev = q->dev;
...@@ -291,7 +289,7 @@ static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, ...@@ -291,7 +289,7 @@ static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q,
skb = napi_alloc_skb(&q_to_qcq(q)->napi, len); skb = napi_alloc_skb(&q_to_qcq(q)->napi, len);
if (unlikely(!skb)) { if (unlikely(!skb)) {
net_warn_ratelimited("%s: SKB alloc failed on %s!\n", net_warn_ratelimited("%s: SKB alloc failed on %s!\n",
netdev->name, q->name); dev_name(dev), q->name);
stats->alloc_err++; stats->alloc_err++;
return NULL; return NULL;
} }
...@@ -309,7 +307,7 @@ static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q, ...@@ -309,7 +307,7 @@ static struct sk_buff *ionic_rx_copybreak(struct ionic_queue *q,
headroom, len, DMA_FROM_DEVICE); headroom, len, DMA_FROM_DEVICE);
skb_put(skb, len); skb_put(skb, len);
skb->protocol = eth_type_trans(skb, q->lif->netdev); skb->protocol = eth_type_trans(skb, netdev);
return skb; return skb;
} }
...@@ -349,8 +347,7 @@ static void ionic_xdp_tx_desc_clean(struct ionic_queue *q, ...@@ -349,8 +347,7 @@ static void ionic_xdp_tx_desc_clean(struct ionic_queue *q,
desc_info->act = 0; desc_info->act = 0;
} }
static int ionic_xdp_post_frame(struct net_device *netdev, static int ionic_xdp_post_frame(struct ionic_queue *q, struct xdp_frame *frame,
struct ionic_queue *q, struct xdp_frame *frame,
enum xdp_action act, struct page *page, int off, enum xdp_action act, struct page *page, int off,
bool ring_doorbell) bool ring_doorbell)
{ {
...@@ -458,14 +455,16 @@ int ionic_xdp_xmit(struct net_device *netdev, int n, ...@@ -458,14 +455,16 @@ int ionic_xdp_xmit(struct net_device *netdev, int n,
txq_trans_cond_update(nq); txq_trans_cond_update(nq);
if (netif_tx_queue_stopped(nq) || if (netif_tx_queue_stopped(nq) ||
unlikely(ionic_maybe_stop_tx(txq, 1))) { !netif_txq_maybe_stop(q_to_ndq(netdev, txq),
ionic_q_space_avail(txq),
1, 1)) {
__netif_tx_unlock(nq); __netif_tx_unlock(nq);
return -EIO; return -EIO;
} }
space = min_t(int, n, ionic_q_space_avail(txq)); space = min_t(int, n, ionic_q_space_avail(txq));
for (nxmit = 0; nxmit < space ; nxmit++) { for (nxmit = 0; nxmit < space ; nxmit++) {
if (ionic_xdp_post_frame(netdev, txq, xdp_frames[nxmit], if (ionic_xdp_post_frame(txq, xdp_frames[nxmit],
XDP_REDIRECT, XDP_REDIRECT,
virt_to_page(xdp_frames[nxmit]->data), virt_to_page(xdp_frames[nxmit]->data),
0, false)) { 0, false)) {
...@@ -478,7 +477,9 @@ int ionic_xdp_xmit(struct net_device *netdev, int n, ...@@ -478,7 +477,9 @@ int ionic_xdp_xmit(struct net_device *netdev, int n,
ionic_dbell_ring(lif->kern_dbpage, txq->hw_type, ionic_dbell_ring(lif->kern_dbpage, txq->hw_type,
txq->dbval | txq->head_idx); txq->dbval | txq->head_idx);
ionic_maybe_stop_tx(txq, 4); netif_txq_maybe_stop(q_to_ndq(netdev, txq),
ionic_q_space_avail(txq),
4, 4);
__netif_tx_unlock(nq); __netif_tx_unlock(nq);
return nxmit; return nxmit;
...@@ -571,7 +572,9 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, ...@@ -571,7 +572,9 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
txq_trans_cond_update(nq); txq_trans_cond_update(nq);
if (netif_tx_queue_stopped(nq) || if (netif_tx_queue_stopped(nq) ||
unlikely(ionic_maybe_stop_tx(txq, 1))) { !netif_txq_maybe_stop(q_to_ndq(netdev, txq),
ionic_q_space_avail(txq),
1, 1)) {
__netif_tx_unlock(nq); __netif_tx_unlock(nq);
goto out_xdp_abort; goto out_xdp_abort;
} }
...@@ -579,7 +582,7 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats, ...@@ -579,7 +582,7 @@ static bool ionic_run_xdp(struct ionic_rx_stats *stats,
dma_unmap_page(rxq->dev, buf_info->dma_addr, dma_unmap_page(rxq->dev, buf_info->dma_addr,
IONIC_PAGE_SIZE, DMA_FROM_DEVICE); IONIC_PAGE_SIZE, DMA_FROM_DEVICE);
err = ionic_xdp_post_frame(netdev, txq, xdpf, XDP_TX, err = ionic_xdp_post_frame(txq, xdpf, XDP_TX,
buf_info->page, buf_info->page,
buf_info->page_offset, buf_info->page_offset,
true); true);
...@@ -657,9 +660,10 @@ static void ionic_rx_clean(struct ionic_queue *q, ...@@ -657,9 +660,10 @@ static void ionic_rx_clean(struct ionic_queue *q,
headroom = q->xdp_rxq_info ? XDP_PACKET_HEADROOM : 0; headroom = q->xdp_rxq_info ? XDP_PACKET_HEADROOM : 0;
if (len <= q->lif->rx_copybreak) if (len <= q->lif->rx_copybreak)
skb = ionic_rx_copybreak(q, desc_info, headroom, len, !!xdp_prog); skb = ionic_rx_copybreak(netdev, q, desc_info,
headroom, len, !!xdp_prog);
else else
skb = ionic_rx_frags(q, desc_info, headroom, len, skb = ionic_rx_frags(netdev, q, desc_info, headroom, len,
comp->num_sg_elems, !!xdp_prog); comp->num_sg_elems, !!xdp_prog);
if (unlikely(!skb)) { if (unlikely(!skb)) {
...@@ -946,8 +950,7 @@ int ionic_tx_napi(struct napi_struct *napi, int budget) ...@@ -946,8 +950,7 @@ int ionic_tx_napi(struct napi_struct *napi, int budget)
lif = cq->bound_q->lif; lif = cq->bound_q->lif;
idev = &lif->ionic->idev; idev = &lif->ionic->idev;
work_done = ionic_cq_service(cq, budget, work_done = ionic_tx_cq_service(cq, budget);
ionic_tx_service, NULL, NULL);
if (unlikely(!budget)) if (unlikely(!budget))
return budget; return budget;
...@@ -1038,8 +1041,7 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget) ...@@ -1038,8 +1041,7 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget)
txqcq = lif->txqcqs[qi]; txqcq = lif->txqcqs[qi];
txcq = &lif->txqcqs[qi]->cq; txcq = &lif->txqcqs[qi]->cq;
tx_work_done = ionic_cq_service(txcq, IONIC_TX_BUDGET_DEFAULT, tx_work_done = ionic_tx_cq_service(txcq, IONIC_TX_BUDGET_DEFAULT);
ionic_tx_service, NULL, NULL);
if (unlikely(!budget)) if (unlikely(!budget))
return budget; return budget;
...@@ -1082,7 +1084,7 @@ static dma_addr_t ionic_tx_map_single(struct ionic_queue *q, ...@@ -1082,7 +1084,7 @@ static dma_addr_t ionic_tx_map_single(struct ionic_queue *q,
dma_addr = dma_map_single(dev, data, len, DMA_TO_DEVICE); dma_addr = dma_map_single(dev, data, len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr)) { if (dma_mapping_error(dev, dma_addr)) {
net_warn_ratelimited("%s: DMA single map failed on %s!\n", net_warn_ratelimited("%s: DMA single map failed on %s!\n",
q->lif->netdev->name, q->name); dev_name(dev), q->name);
stats->dma_map_err++; stats->dma_map_err++;
return 0; return 0;
} }
...@@ -1100,7 +1102,7 @@ static dma_addr_t ionic_tx_map_frag(struct ionic_queue *q, ...@@ -1100,7 +1102,7 @@ static dma_addr_t ionic_tx_map_frag(struct ionic_queue *q,
dma_addr = skb_frag_dma_map(dev, frag, offset, len, DMA_TO_DEVICE); dma_addr = skb_frag_dma_map(dev, frag, offset, len, DMA_TO_DEVICE);
if (dma_mapping_error(dev, dma_addr)) { if (dma_mapping_error(dev, dma_addr)) {
net_warn_ratelimited("%s: DMA frag map failed on %s!\n", net_warn_ratelimited("%s: DMA frag map failed on %s!\n",
q->lif->netdev->name, q->name); dev_name(dev), q->name);
stats->dma_map_err++; stats->dma_map_err++;
} }
return dma_addr; return dma_addr;
...@@ -1183,7 +1185,6 @@ static void ionic_tx_clean(struct ionic_queue *q, ...@@ -1183,7 +1185,6 @@ static void ionic_tx_clean(struct ionic_queue *q,
struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_tx_stats *stats = q_to_tx_stats(q);
struct ionic_qcq *qcq = q_to_qcq(q); struct ionic_qcq *qcq = q_to_qcq(q);
struct sk_buff *skb = cb_arg; struct sk_buff *skb = cb_arg;
u16 qi;
if (desc_info->xdpf) { if (desc_info->xdpf) {
ionic_xdp_tx_desc_clean(q->partner, desc_info); ionic_xdp_tx_desc_clean(q->partner, desc_info);
...@@ -1200,9 +1201,7 @@ static void ionic_tx_clean(struct ionic_queue *q, ...@@ -1200,9 +1201,7 @@ static void ionic_tx_clean(struct ionic_queue *q,
if (!skb) if (!skb)
return; return;
qi = skb_get_queue_mapping(skb); if (unlikely(ionic_txq_hwstamp_enabled(q))) {
if (ionic_txq_hwstamp_enabled(q)) {
if (cq_info) { if (cq_info) {
struct skb_shared_hwtstamps hwts = {}; struct skb_shared_hwtstamps hwts = {};
__le64 *cq_desc_hwstamp; __le64 *cq_desc_hwstamp;
...@@ -1227,24 +1226,22 @@ static void ionic_tx_clean(struct ionic_queue *q, ...@@ -1227,24 +1226,22 @@ static void ionic_tx_clean(struct ionic_queue *q,
stats->hwstamp_invalid++; stats->hwstamp_invalid++;
} }
} }
} else if (unlikely(__netif_subqueue_stopped(q->lif->netdev, qi))) {
netif_wake_subqueue(q->lif->netdev, qi);
} }
desc_info->bytes = skb->len; desc_info->bytes = skb->len;
stats->clean++; stats->clean++;
dev_consume_skb_any(skb); napi_consume_skb(skb, 1);
} }
bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) static bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info,
unsigned int *total_pkts, unsigned int *total_bytes)
{ {
struct ionic_queue *q = cq->bound_q; struct ionic_queue *q = cq->bound_q;
struct ionic_desc_info *desc_info; struct ionic_desc_info *desc_info;
struct ionic_txq_comp *comp; struct ionic_txq_comp *comp;
int bytes = 0; unsigned int bytes = 0;
int pkts = 0; unsigned int pkts = 0;
u16 index; u16 index;
comp = cq_info->cq_desc + cq->desc_size - sizeof(*comp); comp = cq_info->cq_desc + cq->desc_size - sizeof(*comp);
...@@ -1269,19 +1266,52 @@ bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info) ...@@ -1269,19 +1266,52 @@ bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info)
desc_info->cb_arg = NULL; desc_info->cb_arg = NULL;
} while (index != le16_to_cpu(comp->comp_index)); } while (index != le16_to_cpu(comp->comp_index));
if (pkts && bytes && !ionic_txq_hwstamp_enabled(q)) (*total_pkts) += pkts;
netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes); (*total_bytes) += bytes;
return true; return true;
} }
unsigned int ionic_tx_cq_service(struct ionic_cq *cq, unsigned int work_to_do)
{
struct ionic_cq_info *cq_info;
unsigned int work_done = 0;
unsigned int bytes = 0;
unsigned int pkts = 0;
if (work_to_do == 0)
return 0;
cq_info = &cq->info[cq->tail_idx];
while (ionic_tx_service(cq, cq_info, &pkts, &bytes)) {
if (cq->tail_idx == cq->num_descs - 1)
cq->done_color = !cq->done_color;
cq->tail_idx = (cq->tail_idx + 1) & (cq->num_descs - 1);
cq_info = &cq->info[cq->tail_idx];
if (++work_done >= work_to_do)
break;
}
if (work_done) {
struct ionic_queue *q = cq->bound_q;
if (likely(!ionic_txq_hwstamp_enabled(q)))
netif_txq_completed_wake(q_to_ndq(q->lif->netdev, q),
pkts, bytes,
ionic_q_space_avail(q),
IONIC_TSO_DESCS_NEEDED);
}
return work_done;
}
void ionic_tx_flush(struct ionic_cq *cq) void ionic_tx_flush(struct ionic_cq *cq)
{ {
struct ionic_dev *idev = &cq->lif->ionic->idev; struct ionic_dev *idev = &cq->lif->ionic->idev;
u32 work_done; u32 work_done;
work_done = ionic_cq_service(cq, cq->num_descs, work_done = ionic_tx_cq_service(cq, cq->num_descs);
ionic_tx_service, NULL, NULL);
if (work_done) if (work_done)
ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index, ionic_intr_credits(idev->intr_ctrl, cq->bound_intr->index,
work_done, IONIC_INTR_CRED_RESET_COALESCE); work_done, IONIC_INTR_CRED_RESET_COALESCE);
...@@ -1307,8 +1337,12 @@ void ionic_tx_empty(struct ionic_queue *q) ...@@ -1307,8 +1337,12 @@ void ionic_tx_empty(struct ionic_queue *q)
desc_info->cb_arg = NULL; desc_info->cb_arg = NULL;
} }
if (pkts && bytes && !ionic_txq_hwstamp_enabled(q)) if (likely(!ionic_txq_hwstamp_enabled(q))) {
netdev_tx_completed_queue(q_to_ndq(q), pkts, bytes); struct netdev_queue *ndq = q_to_ndq(q->lif->netdev, q);
netdev_tx_completed_queue(ndq, pkts, bytes);
netdev_tx_reset_queue(ndq);
}
} }
static int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb) static int ionic_tx_tcp_inner_pseudo_csum(struct sk_buff *skb)
...@@ -1356,7 +1390,7 @@ static int ionic_tx_tcp_pseudo_csum(struct sk_buff *skb) ...@@ -1356,7 +1390,7 @@ static int ionic_tx_tcp_pseudo_csum(struct sk_buff *skb)
return 0; return 0;
} }
static void ionic_tx_tso_post(struct ionic_queue *q, static void ionic_tx_tso_post(struct net_device *netdev, struct ionic_queue *q,
struct ionic_desc_info *desc_info, struct ionic_desc_info *desc_info,
struct sk_buff *skb, struct sk_buff *skb,
dma_addr_t addr, u8 nsge, u16 len, dma_addr_t addr, u8 nsge, u16 len,
...@@ -1385,15 +1419,16 @@ static void ionic_tx_tso_post(struct ionic_queue *q, ...@@ -1385,15 +1419,16 @@ static void ionic_tx_tso_post(struct ionic_queue *q,
if (start) { if (start) {
skb_tx_timestamp(skb); skb_tx_timestamp(skb);
if (!ionic_txq_hwstamp_enabled(q)) if (likely(!ionic_txq_hwstamp_enabled(q)))
netdev_tx_sent_queue(q_to_ndq(q), skb->len); netdev_tx_sent_queue(q_to_ndq(netdev, q), skb->len);
ionic_txq_post(q, false, ionic_tx_clean, skb); ionic_txq_post(q, false, ionic_tx_clean, skb);
} else { } else {
ionic_txq_post(q, done, NULL, NULL); ionic_txq_post(q, done, NULL, NULL);
} }
} }
static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) static int ionic_tx_tso(struct net_device *netdev, struct ionic_queue *q,
struct sk_buff *skb)
{ {
struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_tx_stats *stats = q_to_tx_stats(q);
struct ionic_desc_info *desc_info; struct ionic_desc_info *desc_info;
...@@ -1501,7 +1536,7 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb) ...@@ -1501,7 +1536,7 @@ static int ionic_tx_tso(struct ionic_queue *q, struct sk_buff *skb)
seg_rem = min(tso_rem, mss); seg_rem = min(tso_rem, mss);
done = (tso_rem == 0); done = (tso_rem == 0);
/* post descriptor */ /* post descriptor */
ionic_tx_tso_post(q, desc_info, skb, ionic_tx_tso_post(netdev, q, desc_info, skb,
desc_addr, desc_nsge, desc_len, desc_addr, desc_nsge, desc_len,
hdrlen, mss, outer_csum, vlan_tci, has_vlan, hdrlen, mss, outer_csum, vlan_tci, has_vlan,
start, done); start, done);
...@@ -1611,10 +1646,12 @@ static void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb, ...@@ -1611,10 +1646,12 @@ static void ionic_tx_skb_frags(struct ionic_queue *q, struct sk_buff *skb,
stats->frags += skb_shinfo(skb)->nr_frags; stats->frags += skb_shinfo(skb)->nr_frags;
} }
static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) static int ionic_tx(struct net_device *netdev, struct ionic_queue *q,
struct sk_buff *skb)
{ {
struct ionic_desc_info *desc_info = &q->info[q->head_idx]; struct ionic_desc_info *desc_info = &q->info[q->head_idx];
struct ionic_tx_stats *stats = q_to_tx_stats(q); struct ionic_tx_stats *stats = q_to_tx_stats(q);
bool ring_dbell = true;
if (unlikely(ionic_tx_map_skb(q, skb, desc_info))) if (unlikely(ionic_tx_map_skb(q, skb, desc_info)))
return -EIO; return -EIO;
...@@ -1632,16 +1669,22 @@ static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb) ...@@ -1632,16 +1669,22 @@ static int ionic_tx(struct ionic_queue *q, struct sk_buff *skb)
stats->pkts++; stats->pkts++;
stats->bytes += skb->len; stats->bytes += skb->len;
if (!ionic_txq_hwstamp_enabled(q)) if (likely(!ionic_txq_hwstamp_enabled(q))) {
netdev_tx_sent_queue(q_to_ndq(q), skb->len); struct netdev_queue *ndq = q_to_ndq(netdev, q);
ionic_txq_post(q, !netdev_xmit_more(), ionic_tx_clean, skb);
if (unlikely(!ionic_q_has_space(q, MAX_SKB_FRAGS + 1)))
netif_tx_stop_queue(ndq);
ring_dbell = __netdev_tx_sent_queue(ndq, skb->len,
netdev_xmit_more());
}
ionic_txq_post(q, ring_dbell, ionic_tx_clean, skb);
return 0; return 0;
} }
static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb)
{ {
struct ionic_tx_stats *stats = q_to_tx_stats(q); int nr_frags = skb_shinfo(skb)->nr_frags;
bool too_many_frags = false; bool too_many_frags = false;
skb_frag_t *frag; skb_frag_t *frag;
int desc_bufs; int desc_bufs;
...@@ -1657,17 +1700,20 @@ static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) ...@@ -1657,17 +1700,20 @@ static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb)
/* Each desc is mss long max, so a descriptor for each gso_seg */ /* Each desc is mss long max, so a descriptor for each gso_seg */
if (skb_is_gso(skb)) { if (skb_is_gso(skb)) {
ndescs = skb_shinfo(skb)->gso_segs; ndescs = skb_shinfo(skb)->gso_segs;
if (!nr_frags)
return ndescs;
} else { } else {
ndescs = 1; ndescs = 1;
if (skb_shinfo(skb)->nr_frags > q->max_sg_elems) { if (!nr_frags)
return ndescs;
if (unlikely(nr_frags > q->max_sg_elems)) {
too_many_frags = true; too_many_frags = true;
goto linearize; goto linearize;
} }
}
/* If non-TSO, or no frags to check, we're done */
if (!skb_is_gso(skb) || !skb_shinfo(skb)->nr_frags)
return ndescs; return ndescs;
}
/* We need to scan the skb to be sure that none of the MTU sized /* We need to scan the skb to be sure that none of the MTU sized
* packets in the TSO will require more sgs per descriptor than we * packets in the TSO will require more sgs per descriptor than we
...@@ -1715,6 +1761,8 @@ static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) ...@@ -1715,6 +1761,8 @@ static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb)
linearize: linearize:
if (too_many_frags) { if (too_many_frags) {
struct ionic_tx_stats *stats = q_to_tx_stats(q);
err = skb_linearize(skb); err = skb_linearize(skb);
if (err) if (err)
return err; return err;
...@@ -1724,30 +1772,11 @@ static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb) ...@@ -1724,30 +1772,11 @@ static int ionic_tx_descs_needed(struct ionic_queue *q, struct sk_buff *skb)
return ndescs; return ndescs;
} }
static int ionic_maybe_stop_tx(struct ionic_queue *q, int ndescs)
{
int stopped = 0;
if (unlikely(!ionic_q_has_space(q, ndescs))) {
netif_stop_subqueue(q->lif->netdev, q->index);
stopped = 1;
/* Might race with ionic_tx_clean, check again */
smp_rmb();
if (ionic_q_has_space(q, ndescs)) {
netif_wake_subqueue(q->lif->netdev, q->index);
stopped = 0;
}
}
return stopped;
}
static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb, static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb,
struct net_device *netdev) struct net_device *netdev)
{ {
struct ionic_lif *lif = netdev_priv(netdev); struct ionic_lif *lif = netdev_priv(netdev);
struct ionic_queue *q = &lif->hwstamp_txq->q; struct ionic_queue *q;
int err, ndescs; int err, ndescs;
/* Does not stop/start txq, because we post to a separate tx queue /* Does not stop/start txq, because we post to a separate tx queue
...@@ -1755,6 +1784,7 @@ static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb, ...@@ -1755,6 +1784,7 @@ static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb,
* the timestamping queue, it is dropped. * the timestamping queue, it is dropped.
*/ */
q = &lif->hwstamp_txq->q;
ndescs = ionic_tx_descs_needed(q, skb); ndescs = ionic_tx_descs_needed(q, skb);
if (unlikely(ndescs < 0)) if (unlikely(ndescs < 0))
goto err_out_drop; goto err_out_drop;
...@@ -1764,9 +1794,9 @@ static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb, ...@@ -1764,9 +1794,9 @@ static netdev_tx_t ionic_start_hwstamp_xmit(struct sk_buff *skb,
skb_shinfo(skb)->tx_flags |= SKBTX_HW_TSTAMP; skb_shinfo(skb)->tx_flags |= SKBTX_HW_TSTAMP;
if (skb_is_gso(skb)) if (skb_is_gso(skb))
err = ionic_tx_tso(q, skb); err = ionic_tx_tso(netdev, q, skb);
else else
err = ionic_tx(q, skb); err = ionic_tx(netdev, q, skb);
if (err) if (err)
goto err_out_drop; goto err_out_drop;
...@@ -1804,23 +1834,19 @@ netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev) ...@@ -1804,23 +1834,19 @@ netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev)
if (ndescs < 0) if (ndescs < 0)
goto err_out_drop; goto err_out_drop;
if (unlikely(ionic_maybe_stop_tx(q, ndescs))) if (!netif_txq_maybe_stop(q_to_ndq(netdev, q),
ionic_q_space_avail(q),
ndescs, ndescs))
return NETDEV_TX_BUSY; return NETDEV_TX_BUSY;
if (skb_is_gso(skb)) if (skb_is_gso(skb))
err = ionic_tx_tso(q, skb); err = ionic_tx_tso(netdev, q, skb);
else else
err = ionic_tx(q, skb); err = ionic_tx(netdev, q, skb);
if (err) if (err)
goto err_out_drop; goto err_out_drop;
/* Stop the queue if there aren't descriptors for the next packet.
* Since our SG lists per descriptor take care of most of the possible
* fragmentation, we don't need to have many descriptors available.
*/
ionic_maybe_stop_tx(q, 4);
return NETDEV_TX_OK; return NETDEV_TX_OK;
err_out_drop: err_out_drop:
......
...@@ -15,7 +15,6 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget); ...@@ -15,7 +15,6 @@ int ionic_txrx_napi(struct napi_struct *napi, int budget);
netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev); netdev_tx_t ionic_start_xmit(struct sk_buff *skb, struct net_device *netdev);
bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info); bool ionic_rx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info);
bool ionic_tx_service(struct ionic_cq *cq, struct ionic_cq_info *cq_info);
int ionic_xdp_xmit(struct net_device *netdev, int n, struct xdp_frame **xdp, u32 flags); int ionic_xdp_xmit(struct net_device *netdev, int n, struct xdp_frame **xdp, u32 flags);
#endif /* _IONIC_TXRX_H_ */ #endif /* _IONIC_TXRX_H_ */
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