diff options
Diffstat (limited to 'drivers/net/virtio_net.c')
-rw-r--r-- | drivers/net/virtio_net.c | 58 |
1 files changed, 34 insertions, 24 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 626c27352ae2..9bb9e562b893 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c | |||
@@ -443,12 +443,8 @@ static bool __virtnet_xdp_xmit(struct virtnet_info *vi, | |||
443 | sg_init_one(sq->sg, xdp->data, xdp->data_end - xdp->data); | 443 | sg_init_one(sq->sg, xdp->data, xdp->data_end - xdp->data); |
444 | 444 | ||
445 | err = virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdp->data, GFP_ATOMIC); | 445 | err = virtqueue_add_outbuf(sq->vq, sq->sg, 1, xdp->data, GFP_ATOMIC); |
446 | if (unlikely(err)) { | 446 | if (unlikely(err)) |
447 | struct page *page = virt_to_head_page(xdp->data); | 447 | return false; /* Caller handle free/refcnt */ |
448 | |||
449 | put_page(page); | ||
450 | return false; | ||
451 | } | ||
452 | 448 | ||
453 | return true; | 449 | return true; |
454 | } | 450 | } |
@@ -456,8 +452,18 @@ static bool __virtnet_xdp_xmit(struct virtnet_info *vi, | |||
456 | static int virtnet_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) | 452 | static int virtnet_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) |
457 | { | 453 | { |
458 | struct virtnet_info *vi = netdev_priv(dev); | 454 | struct virtnet_info *vi = netdev_priv(dev); |
459 | bool sent = __virtnet_xdp_xmit(vi, xdp); | 455 | struct receive_queue *rq = vi->rq; |
456 | struct bpf_prog *xdp_prog; | ||
457 | bool sent; | ||
460 | 458 | ||
459 | /* Only allow ndo_xdp_xmit if XDP is loaded on dev, as this | ||
460 | * indicate XDP resources have been successfully allocated. | ||
461 | */ | ||
462 | xdp_prog = rcu_dereference(rq->xdp_prog); | ||
463 | if (!xdp_prog) | ||
464 | return -ENXIO; | ||
465 | |||
466 | sent = __virtnet_xdp_xmit(vi, xdp); | ||
461 | if (!sent) | 467 | if (!sent) |
462 | return -ENOSPC; | 468 | return -ENOSPC; |
463 | return 0; | 469 | return 0; |
@@ -546,8 +552,11 @@ static struct sk_buff *receive_small(struct net_device *dev, | |||
546 | unsigned int buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + | 552 | unsigned int buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + |
547 | SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); | 553 | SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); |
548 | struct page *page = virt_to_head_page(buf); | 554 | struct page *page = virt_to_head_page(buf); |
549 | unsigned int delta = 0, err; | 555 | unsigned int delta = 0; |
550 | struct page *xdp_page; | 556 | struct page *xdp_page; |
557 | bool sent; | ||
558 | int err; | ||
559 | |||
551 | len -= vi->hdr_len; | 560 | len -= vi->hdr_len; |
552 | 561 | ||
553 | rcu_read_lock(); | 562 | rcu_read_lock(); |
@@ -558,7 +567,7 @@ static struct sk_buff *receive_small(struct net_device *dev, | |||
558 | void *orig_data; | 567 | void *orig_data; |
559 | u32 act; | 568 | u32 act; |
560 | 569 | ||
561 | if (unlikely(hdr->hdr.gso_type || hdr->hdr.flags)) | 570 | if (unlikely(hdr->hdr.gso_type)) |
562 | goto err_xdp; | 571 | goto err_xdp; |
563 | 572 | ||
564 | if (unlikely(xdp_headroom < virtnet_get_headroom(vi))) { | 573 | if (unlikely(xdp_headroom < virtnet_get_headroom(vi))) { |
@@ -596,16 +605,19 @@ static struct sk_buff *receive_small(struct net_device *dev, | |||
596 | delta = orig_data - xdp.data; | 605 | delta = orig_data - xdp.data; |
597 | break; | 606 | break; |
598 | case XDP_TX: | 607 | case XDP_TX: |
599 | if (unlikely(!__virtnet_xdp_xmit(vi, &xdp))) | 608 | sent = __virtnet_xdp_xmit(vi, &xdp); |
609 | if (unlikely(!sent)) { | ||
600 | trace_xdp_exception(vi->dev, xdp_prog, act); | 610 | trace_xdp_exception(vi->dev, xdp_prog, act); |
601 | else | 611 | goto err_xdp; |
602 | *xdp_xmit = true; | 612 | } |
613 | *xdp_xmit = true; | ||
603 | rcu_read_unlock(); | 614 | rcu_read_unlock(); |
604 | goto xdp_xmit; | 615 | goto xdp_xmit; |
605 | case XDP_REDIRECT: | 616 | case XDP_REDIRECT: |
606 | err = xdp_do_redirect(dev, &xdp, xdp_prog); | 617 | err = xdp_do_redirect(dev, &xdp, xdp_prog); |
607 | if (!err) | 618 | if (err) |
608 | *xdp_xmit = true; | 619 | goto err_xdp; |
620 | *xdp_xmit = true; | ||
609 | rcu_read_unlock(); | 621 | rcu_read_unlock(); |
610 | goto xdp_xmit; | 622 | goto xdp_xmit; |
611 | default: | 623 | default: |
@@ -677,7 +689,7 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, | |||
677 | struct bpf_prog *xdp_prog; | 689 | struct bpf_prog *xdp_prog; |
678 | unsigned int truesize; | 690 | unsigned int truesize; |
679 | unsigned int headroom = mergeable_ctx_to_headroom(ctx); | 691 | unsigned int headroom = mergeable_ctx_to_headroom(ctx); |
680 | int err; | 692 | bool sent; |
681 | 693 | ||
682 | head_skb = NULL; | 694 | head_skb = NULL; |
683 | 695 | ||
@@ -746,20 +758,18 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, | |||
746 | } | 758 | } |
747 | break; | 759 | break; |
748 | case XDP_TX: | 760 | case XDP_TX: |
749 | if (unlikely(!__virtnet_xdp_xmit(vi, &xdp))) | 761 | sent = __virtnet_xdp_xmit(vi, &xdp); |
762 | if (unlikely(!sent)) { | ||
750 | trace_xdp_exception(vi->dev, xdp_prog, act); | 763 | trace_xdp_exception(vi->dev, xdp_prog, act); |
751 | else | 764 | if (unlikely(xdp_page != page)) |
752 | *xdp_xmit = true; | 765 | put_page(xdp_page); |
766 | goto err_xdp; | ||
767 | } | ||
768 | *xdp_xmit = true; | ||
753 | if (unlikely(xdp_page != page)) | 769 | if (unlikely(xdp_page != page)) |
754 | goto err_xdp; | 770 | goto err_xdp; |
755 | rcu_read_unlock(); | 771 | rcu_read_unlock(); |
756 | goto xdp_xmit; | 772 | goto xdp_xmit; |
757 | case XDP_REDIRECT: | ||
758 | err = xdp_do_redirect(dev, &xdp, xdp_prog); | ||
759 | if (!err) | ||
760 | *xdp_xmit = true; | ||
761 | rcu_read_unlock(); | ||
762 | goto xdp_xmit; | ||
763 | default: | 773 | default: |
764 | bpf_warn_invalid_xdp_action(act); | 774 | bpf_warn_invalid_xdp_action(act); |
765 | case XDP_ABORTED: | 775 | case XDP_ABORTED: |