summaryrefslogtreecommitdiff
path: root/drivers/net/virtio_net.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/virtio_net.c')
-rw-r--r--drivers/net/virtio_net.c71
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);