diff options
Diffstat (limited to 'drivers/net/virtio_net.c')
| -rw-r--r-- | drivers/net/virtio_net.c | 71 |
1 files changed, 45 insertions, 26 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index daab43ac92ce..dd2c41ef6fbe 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -125,6 +125,7 @@ struct virtnet_rq_stats { u64_stats_t packets; u64_stats_t bytes; u64_stats_t drops; + u64_stats_t alloc_fail; u64_stats_t xdp_packets; u64_stats_t xdp_tx; u64_stats_t xdp_redirects; @@ -173,8 +174,9 @@ static const struct virtnet_stat_desc virtnet_sq_stats_desc_qstat[] = { }; static const struct virtnet_stat_desc virtnet_rq_stats_desc_qstat[] = { - VIRTNET_RQ_STAT_QSTAT("packets", packets), - VIRTNET_RQ_STAT_QSTAT("bytes", bytes), + VIRTNET_RQ_STAT_QSTAT("packets", packets), + VIRTNET_RQ_STAT_QSTAT("bytes", bytes), + VIRTNET_RQ_STAT_QSTAT("alloc_fail", alloc_fail), }; #define VIRTNET_STATS_DESC_CQ(name) \ @@ -441,9 +443,6 @@ struct virtnet_info { /* Work struct for setting rx mode */ struct work_struct rx_mode_work; - /* OK to queue work setting RX mode? */ - bool rx_mode_work_enabled; - /* Does the affinity hint is set for virtqueues? */ bool affinity_hint_set; @@ -710,16 +709,13 @@ static void virtnet_rq_free_buf(struct virtnet_info *vi, static void enable_rx_mode_work(struct virtnet_info *vi) { - rtnl_lock(); - vi->rx_mode_work_enabled = true; - rtnl_unlock(); + if (enable_work(&vi->rx_mode_work) && netif_running(vi->dev)) + schedule_work(&vi->rx_mode_work); } static void disable_rx_mode_work(struct virtnet_info *vi) { - rtnl_lock(); - vi->rx_mode_work_enabled = false; - rtnl_unlock(); + disable_work_sync(&vi->rx_mode_work); } static void virtqueue_napi_schedule(struct napi_struct *napi, @@ -906,6 +902,15 @@ static struct sk_buff *page_to_skb(struct virtnet_info *vi, } BUG_ON(offset >= PAGE_SIZE); + + /* The first frag starts at offset, each next frag is a full page. */ + if (unlikely(DIV_ROUND_UP(offset + len, PAGE_SIZE) > MAX_SKB_FRAGS)) { + pr_debug("%s: rx error: len %u needs more than %d frags\n", + vi->dev->name, len, MAX_SKB_FRAGS); + kfree_skb(skb); + return NULL; + } + while (len) { unsigned int frag_size = min((unsigned)PAGE_SIZE - offset, len); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, offset, @@ -1917,8 +1922,10 @@ static struct sk_buff *receive_small_xdp(struct net_device *dev, } skb = virtnet_build_skb(buf, buflen, xdp.data - buf, len); - if (unlikely(!skb)) + if (unlikely(!skb)) { + u64_stats_inc(&stats->alloc_fail); goto err; + } if (metasize) skb_metadata_set(skb, metasize); @@ -1985,6 +1992,7 @@ static struct sk_buff *receive_small(struct net_device *dev, return skb; } + u64_stats_inc(&stats->alloc_fail); err: u64_stats_inc(&stats->drops); page_pool_put_page(rq->page_pool, page, -1, true); @@ -2016,8 +2024,10 @@ static struct sk_buff *receive_big(struct net_device *dev, skb = page_to_skb(vi, rq, page, 0, len, PAGE_SIZE, 0); u64_stats_add(&stats->bytes, len - vi->hdr_len); - if (unlikely(!skb)) + if (unlikely(!skb)) { + u64_stats_inc(&stats->alloc_fail); goto err; + } return skb; @@ -2298,8 +2308,10 @@ static struct sk_buff *receive_mergeable_xdp(struct net_device *dev, switch (act) { case XDP_PASS: head_skb = build_skb_from_xdp_buff(dev, vi, &xdp, xdp_frags_truesz); - if (unlikely(!head_skb)) + if (unlikely(!head_skb)) { + u64_stats_inc(&stats->alloc_fail); break; + } skb_mark_for_recycle(head_skb); return head_skb; @@ -2414,8 +2426,10 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, head_skb = page_to_skb(vi, rq, page, offset, len, truesize, headroom); curr_skb = head_skb; - if (unlikely(!curr_skb)) + if (unlikely(!curr_skb)) { + u64_stats_inc(&stats->alloc_fail); goto err_skb; + } skb_mark_for_recycle(head_skb); while (--num_buf) { @@ -2444,8 +2458,10 @@ static struct sk_buff *receive_mergeable(struct net_device *dev, truesize = mergeable_ctx_to_truesize(ctx); curr_skb = virtnet_skb_append_frag(rq, head_skb, curr_skb, page, buf, len, truesize); - if (!curr_skb) + if (!curr_skb) { + u64_stats_inc(&stats->alloc_fail); goto err_skb; + } } ewma_pkt_len_add(&rq->mrg_avg_pkt_len, head_skb->len); @@ -2930,12 +2946,14 @@ static int virtnet_receive(struct receive_queue *rq, int budget, u64_stats_set(&stats.packets, packets); if (rq->vq->num_free > min((unsigned int)budget, virtqueue_get_vring_size(rq->vq)) / 2) { - if (!try_fill_recv(vi, rq, GFP_ATOMIC)) + if (!try_fill_recv(vi, rq, GFP_ATOMIC)) { /* We need to retry refilling in the next NAPI poll so * we must return budget to make sure the NAPI is * repolled. */ packets = budget; + u64_stats_inc(&stats.alloc_fail); + } } u64_stats_update_begin(&rq->stats.syncp); @@ -2950,6 +2968,7 @@ static int virtnet_receive(struct receive_queue *rq, int budget, u64_stats_add(&rq->stats.packets, u64_stats_read(&stats.packets)); u64_stats_add(&rq->stats.bytes, u64_stats_read(&stats.bytes)); + u64_stats_add(&rq->stats.alloc_fail, u64_stats_read(&stats.alloc_fail)); u64_stats_update_end(&rq->stats.syncp); @@ -3925,8 +3944,7 @@ static void virtnet_set_rx_mode(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); - if (vi->rx_mode_work_enabled) - schedule_work(&vi->rx_mode_work); + schedule_work(&vi->rx_mode_work); } static int virtnet_vlan_rx_add_vid(struct net_device *dev, @@ -5677,6 +5695,7 @@ static void virtnet_get_base_stats(struct net_device *dev, */ rx->bytes = 0; rx->packets = 0; + rx->alloc_fail = 0; if (vi->device_stats_cap & VIRTIO_NET_STATS_TYPE_RX_BASIC) { rx->hw_drops = 0; @@ -5742,7 +5761,6 @@ static void virtnet_freeze_down(struct virtio_device *vdev) /* Make sure no work handler is accessing the device */ flush_work(&vi->config_work); disable_rx_mode_work(vi); - flush_work(&vi->rx_mode_work); if (netif_running(vi->dev)) { rtnl_lock(); @@ -5772,8 +5790,6 @@ static int virtnet_restore_up(struct virtio_device *vdev) virtio_device_ready(vdev); - enable_rx_mode_work(vi); - if (netif_running(vi->dev)) { rtnl_lock(); err = virtnet_open(vi->dev); @@ -5785,6 +5801,8 @@ static int virtnet_restore_up(struct virtio_device *vdev) netif_tx_lock_bh(vi->dev); netif_device_attach(vi->dev); netif_tx_unlock_bh(vi->dev); + + enable_rx_mode_work(vi); return 0; err_destroy_pools: @@ -6717,7 +6735,8 @@ static void virtnet_set_big_packets(struct virtnet_info *vi, const int mtu) */ if (mtu > ETH_DATA_LEN || guest_gso) { vi->big_packets = true; - vi->big_packets_num_skbfrags = guest_gso ? MAX_SKB_FRAGS : DIV_ROUND_UP(mtu, PAGE_SIZE); + vi->big_packets_num_skbfrags = guest_gso ? MAX_SKB_FRAGS - 1 : + DIV_ROUND_UP(mtu, PAGE_SIZE); } } @@ -6880,6 +6899,7 @@ static int virtnet_probe(struct virtio_device *vdev) INIT_WORK(&vi->config_work, virtnet_config_changed_work); INIT_WORK(&vi->rx_mode_work, virtnet_rx_mode_work); + disable_work(&vi->rx_mode_work); if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) { vi->mergeable_rx_bufs = true; @@ -7038,8 +7058,6 @@ static int virtnet_probe(struct virtio_device *vdev) if (vi->has_rss || vi->has_rss_hash_report) virtnet_init_default_rss(vi); - enable_rx_mode_work(vi); - for (i = 0; i < ARRAY_SIZE(guest_offloads); i++) { unsigned int fbit; @@ -7143,6 +7161,8 @@ static int virtnet_probe(struct virtio_device *vdev) goto free_unregister_netdev; } + enable_rx_mode_work(vi); + pr_debug("virtnet: registered device %s with %d RX and TX vq's\n", dev->name, max_queue_pairs); @@ -7195,7 +7215,6 @@ static void virtnet_remove(struct virtio_device *vdev) /* Make sure no work handler is accessing the device. */ flush_work(&vi->config_work); disable_rx_mode_work(vi); - flush_work(&vi->rx_mode_work); virtnet_free_irq_moder(vi); |
