Skip to content

Commit 89f8667

Browse files
fengidrikuba-moo
authored andcommitted
virtio_net: xsk: tx: support xmit xsk buffer
The driver's tx napi is very important for XSK. It is responsible for obtaining data from the XSK queue and sending it out. At the beginning, we need to trigger tx napi. virtnet_free_old_xmit distinguishes three type ptr(skb, xdp frame, xsk buffer) by the last bits of the pointer. Signed-off-by: Xuan Zhuo <[email protected]> Acked-by: Jason Wang <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent 1df5116 commit 89f8667

File tree

1 file changed

+168
-11
lines changed

1 file changed

+168
-11
lines changed

drivers/net/virtio_net.c

Lines changed: 168 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct virtnet_sq_free_stats {
8383
u64 bytes;
8484
u64 napi_packets;
8585
u64 napi_bytes;
86+
u64 xsk;
8687
};
8788

8889
struct virtnet_sq_stats {
@@ -512,11 +513,13 @@ static struct sk_buff *virtnet_skb_append_frag(struct sk_buff *head_skb,
512513
struct sk_buff *curr_skb,
513514
struct page *page, void *buf,
514515
int len, int truesize);
516+
static void virtnet_xsk_completed(struct send_queue *sq, int num);
515517

516518
enum virtnet_xmit_type {
517519
VIRTNET_XMIT_TYPE_SKB,
518520
VIRTNET_XMIT_TYPE_SKB_ORPHAN,
519521
VIRTNET_XMIT_TYPE_XDP,
522+
VIRTNET_XMIT_TYPE_XSK,
520523
};
521524

522525
static int rss_indirection_table_alloc(struct virtio_net_ctrl_rss *rss, u16 indir_table_size)
@@ -541,6 +544,8 @@ static void rss_indirection_table_free(struct virtio_net_ctrl_rss *rss)
541544
/* We use the last two bits of the pointer to distinguish the xmit type. */
542545
#define VIRTNET_XMIT_TYPE_MASK (BIT(0) | BIT(1))
543546

547+
#define VIRTIO_XSK_FLAG_OFFSET 2
548+
544549
static enum virtnet_xmit_type virtnet_xmit_ptr_unpack(void **ptr)
545550
{
546551
unsigned long p = (unsigned long)*ptr;
@@ -563,6 +568,11 @@ static int virtnet_add_outbuf(struct send_queue *sq, int num, void *data,
563568
GFP_ATOMIC);
564569
}
565570

571+
static u32 virtnet_ptr_to_xsk_buff_len(void *ptr)
572+
{
573+
return ((unsigned long)ptr) >> VIRTIO_XSK_FLAG_OFFSET;
574+
}
575+
566576
static void sg_fill_dma(struct scatterlist *sg, dma_addr_t addr, u32 len)
567577
{
568578
sg_dma_address(sg) = addr;
@@ -603,11 +613,27 @@ static void __free_old_xmit(struct send_queue *sq, struct netdev_queue *txq,
603613
stats->bytes += xdp_get_frame_len(frame);
604614
xdp_return_frame(frame);
605615
break;
616+
617+
case VIRTNET_XMIT_TYPE_XSK:
618+
stats->bytes += virtnet_ptr_to_xsk_buff_len(ptr);
619+
stats->xsk++;
620+
break;
606621
}
607622
}
608623
netdev_tx_completed_queue(txq, stats->napi_packets, stats->napi_bytes);
609624
}
610625

626+
static void virtnet_free_old_xmit(struct send_queue *sq,
627+
struct netdev_queue *txq,
628+
bool in_napi,
629+
struct virtnet_sq_free_stats *stats)
630+
{
631+
__free_old_xmit(sq, txq, in_napi, stats);
632+
633+
if (stats->xsk)
634+
virtnet_xsk_completed(sq, stats->xsk);
635+
}
636+
611637
/* Converting between virtqueue no. and kernel tx/rx queue no.
612638
* 0:rx0 1:tx0 2:rx1 3:tx1 ... 2N:rxN 2N+1:txN 2N+2:cvq
613639
*/
@@ -1037,7 +1063,7 @@ static void free_old_xmit(struct send_queue *sq, struct netdev_queue *txq,
10371063
{
10381064
struct virtnet_sq_free_stats stats = {0};
10391065

1040-
__free_old_xmit(sq, txq, in_napi, &stats);
1066+
virtnet_free_old_xmit(sq, txq, in_napi, &stats);
10411067

10421068
/* Avoid overhead when no packets have been processed
10431069
* happens when called speculatively from start_xmit.
@@ -1399,6 +1425,113 @@ static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue
13991425
return err;
14001426
}
14011427

1428+
static void *virtnet_xsk_to_ptr(u32 len)
1429+
{
1430+
unsigned long p;
1431+
1432+
p = len << VIRTIO_XSK_FLAG_OFFSET;
1433+
1434+
return virtnet_xmit_ptr_pack((void *)p, VIRTNET_XMIT_TYPE_XSK);
1435+
}
1436+
1437+
static int virtnet_xsk_xmit_one(struct send_queue *sq,
1438+
struct xsk_buff_pool *pool,
1439+
struct xdp_desc *desc)
1440+
{
1441+
struct virtnet_info *vi;
1442+
dma_addr_t addr;
1443+
1444+
vi = sq->vq->vdev->priv;
1445+
1446+
addr = xsk_buff_raw_get_dma(pool, desc->addr);
1447+
xsk_buff_raw_dma_sync_for_device(pool, addr, desc->len);
1448+
1449+
sg_init_table(sq->sg, 2);
1450+
sg_fill_dma(sq->sg, sq->xsk_hdr_dma_addr, vi->hdr_len);
1451+
sg_fill_dma(sq->sg + 1, addr, desc->len);
1452+
1453+
return virtqueue_add_outbuf_premapped(sq->vq, sq->sg, 2,
1454+
virtnet_xsk_to_ptr(desc->len),
1455+
GFP_ATOMIC);
1456+
}
1457+
1458+
static int virtnet_xsk_xmit_batch(struct send_queue *sq,
1459+
struct xsk_buff_pool *pool,
1460+
unsigned int budget,
1461+
u64 *kicks)
1462+
{
1463+
struct xdp_desc *descs = pool->tx_descs;
1464+
bool kick = false;
1465+
u32 nb_pkts, i;
1466+
int err;
1467+
1468+
budget = min_t(u32, budget, sq->vq->num_free);
1469+
1470+
nb_pkts = xsk_tx_peek_release_desc_batch(pool, budget);
1471+
if (!nb_pkts)
1472+
return 0;
1473+
1474+
for (i = 0; i < nb_pkts; i++) {
1475+
err = virtnet_xsk_xmit_one(sq, pool, &descs[i]);
1476+
if (unlikely(err)) {
1477+
xsk_tx_completed(sq->xsk_pool, nb_pkts - i);
1478+
break;
1479+
}
1480+
1481+
kick = true;
1482+
}
1483+
1484+
if (kick && virtqueue_kick_prepare(sq->vq) && virtqueue_notify(sq->vq))
1485+
(*kicks)++;
1486+
1487+
return i;
1488+
}
1489+
1490+
static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
1491+
int budget)
1492+
{
1493+
struct virtnet_info *vi = sq->vq->vdev->priv;
1494+
struct virtnet_sq_free_stats stats = {};
1495+
struct net_device *dev = vi->dev;
1496+
u64 kicks = 0;
1497+
int sent;
1498+
1499+
/* Avoid to wakeup napi meanless, so call __free_old_xmit instead of
1500+
* free_old_xmit().
1501+
*/
1502+
__free_old_xmit(sq, netdev_get_tx_queue(dev, sq - vi->sq), true, &stats);
1503+
1504+
if (stats.xsk)
1505+
xsk_tx_completed(sq->xsk_pool, stats.xsk);
1506+
1507+
sent = virtnet_xsk_xmit_batch(sq, pool, budget, &kicks);
1508+
1509+
if (!is_xdp_raw_buffer_queue(vi, sq - vi->sq))
1510+
check_sq_full_and_disable(vi, vi->dev, sq);
1511+
1512+
u64_stats_update_begin(&sq->stats.syncp);
1513+
u64_stats_add(&sq->stats.packets, stats.packets);
1514+
u64_stats_add(&sq->stats.bytes, stats.bytes);
1515+
u64_stats_add(&sq->stats.kicks, kicks);
1516+
u64_stats_add(&sq->stats.xdp_tx, sent);
1517+
u64_stats_update_end(&sq->stats.syncp);
1518+
1519+
if (xsk_uses_need_wakeup(pool))
1520+
xsk_set_tx_need_wakeup(pool);
1521+
1522+
return sent;
1523+
}
1524+
1525+
static void xsk_wakeup(struct send_queue *sq)
1526+
{
1527+
if (napi_if_scheduled_mark_missed(&sq->napi))
1528+
return;
1529+
1530+
local_bh_disable();
1531+
virtqueue_napi_schedule(&sq->napi, sq->vq);
1532+
local_bh_enable();
1533+
}
1534+
14021535
static int virtnet_xsk_wakeup(struct net_device *dev, u32 qid, u32 flag)
14031536
{
14041537
struct virtnet_info *vi = netdev_priv(dev);
@@ -1412,14 +1545,19 @@ static int virtnet_xsk_wakeup(struct net_device *dev, u32 qid, u32 flag)
14121545

14131546
sq = &vi->sq[qid];
14141547

1415-
if (napi_if_scheduled_mark_missed(&sq->napi))
1416-
return 0;
1548+
xsk_wakeup(sq);
1549+
return 0;
1550+
}
14171551

1418-
local_bh_disable();
1419-
virtqueue_napi_schedule(&sq->napi, sq->vq);
1420-
local_bh_enable();
1552+
static void virtnet_xsk_completed(struct send_queue *sq, int num)
1553+
{
1554+
xsk_tx_completed(sq->xsk_pool, num);
14211555

1422-
return 0;
1556+
/* If this is called by rx poll, start_xmit and xdp xmit we should
1557+
* wakeup the tx napi to consume the xsk tx queue, because the tx
1558+
* interrupt may not be triggered.
1559+
*/
1560+
xsk_wakeup(sq);
14231561
}
14241562

14251563
static int __virtnet_xdp_xmit_one(struct virtnet_info *vi,
@@ -1535,8 +1673,8 @@ static int virtnet_xdp_xmit(struct net_device *dev,
15351673
}
15361674

15371675
/* Free up any pending old buffers before queueing new ones. */
1538-
__free_old_xmit(sq, netdev_get_tx_queue(dev, sq - vi->sq),
1539-
false, &stats);
1676+
virtnet_free_old_xmit(sq, netdev_get_tx_queue(dev, sq - vi->sq),
1677+
false, &stats);
15401678

15411679
for (i = 0; i < n; i++) {
15421680
struct xdp_frame *xdpf = frames[i];
@@ -2993,7 +3131,7 @@ static int virtnet_poll_tx(struct napi_struct *napi, int budget)
29933131
struct virtnet_info *vi = sq->vq->vdev->priv;
29943132
unsigned int index = vq2txq(sq->vq);
29953133
struct netdev_queue *txq;
2996-
int opaque;
3134+
int opaque, xsk_done = 0;
29973135
bool done;
29983136

29993137
if (unlikely(is_xdp_raw_buffer_queue(vi, index))) {
@@ -3005,7 +3143,11 @@ static int virtnet_poll_tx(struct napi_struct *napi, int budget)
30053143
txq = netdev_get_tx_queue(vi->dev, index);
30063144
__netif_tx_lock(txq, raw_smp_processor_id());
30073145
virtqueue_disable_cb(sq->vq);
3008-
free_old_xmit(sq, txq, !!budget);
3146+
3147+
if (sq->xsk_pool)
3148+
xsk_done = virtnet_xsk_xmit(sq, sq->xsk_pool, budget);
3149+
else
3150+
free_old_xmit(sq, txq, !!budget);
30093151

30103152
if (sq->vq->num_free >= 2 + MAX_SKB_FRAGS) {
30113153
if (netif_tx_queue_stopped(txq)) {
@@ -3016,6 +3158,11 @@ static int virtnet_poll_tx(struct napi_struct *napi, int budget)
30163158
netif_tx_wake_queue(txq);
30173159
}
30183160

3161+
if (xsk_done >= budget) {
3162+
__netif_tx_unlock(txq);
3163+
return budget;
3164+
}
3165+
30193166
opaque = virtqueue_enable_cb_prepare(sq->vq);
30203167

30213168
done = napi_complete_done(napi, 0);
@@ -6058,6 +6205,12 @@ static void free_receive_page_frags(struct virtnet_info *vi)
60586205

60596206
static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf)
60606207
{
6208+
struct virtnet_info *vi = vq->vdev->priv;
6209+
struct send_queue *sq;
6210+
int i = vq2rxq(vq);
6211+
6212+
sq = &vi->sq[i];
6213+
60616214
switch (virtnet_xmit_ptr_unpack(&buf)) {
60626215
case VIRTNET_XMIT_TYPE_SKB:
60636216
case VIRTNET_XMIT_TYPE_SKB_ORPHAN:
@@ -6067,6 +6220,10 @@ static void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf)
60676220
case VIRTNET_XMIT_TYPE_XDP:
60686221
xdp_return_frame(buf);
60696222
break;
6223+
6224+
case VIRTNET_XMIT_TYPE_XSK:
6225+
xsk_tx_completed(sq->xsk_pool, 1);
6226+
break;
60706227
}
60716228
}
60726229

0 commit comments

Comments
 (0)