diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-08-31 17:54:18 +0200 | 
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-08-31 18:05:25 +0200 | 
| commit | bbe69aa57a7374b51242b95a54eefcf0d0393b7e (patch) | |
| tree | c45e48d11cc9cb81a57c8c27f7243863b117cec8 /drivers/net/virtio_net.c | |
| parent | a417887637e862b434b293404f2a31ad1f282a58 (diff) | |
| parent | 326ba5010a5429a5a528b268b36a5900d4ab0eba (diff) | |
Merge commit 'v2.6.31-rc8' into core/locking
Merge reason: we were on -rc4, move to -rc8 before applying
              a new batch of locking infrastructure changes.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/net/virtio_net.c')
| -rw-r--r-- | drivers/net/virtio_net.c | 61 | 
1 files changed, 46 insertions, 15 deletions
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 2a6e81d5b579..bbedf03a2124 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -70,6 +70,9 @@ struct virtnet_info  	struct sk_buff_head recv;  	struct sk_buff_head send; +	/* Work struct for refilling if we run low on memory. */ +	struct delayed_work refill; +  	/* Chain pages by the private ptr. */  	struct page *pages;  }; @@ -273,19 +276,22 @@ drop:  	dev_kfree_skb(skb);  } -static void try_fill_recv_maxbufs(struct virtnet_info *vi) +static bool try_fill_recv_maxbufs(struct virtnet_info *vi, gfp_t gfp)  {  	struct sk_buff *skb;  	struct scatterlist sg[2+MAX_SKB_FRAGS];  	int num, err, i; +	bool oom = false;  	sg_init_table(sg, 2+MAX_SKB_FRAGS);  	for (;;) {  		struct virtio_net_hdr *hdr;  		skb = netdev_alloc_skb(vi->dev, MAX_PACKET_LEN + NET_IP_ALIGN); -		if (unlikely(!skb)) +		if (unlikely(!skb)) { +			oom = true;  			break; +		}  		skb_reserve(skb, NET_IP_ALIGN);  		skb_put(skb, MAX_PACKET_LEN); @@ -296,7 +302,7 @@ static void try_fill_recv_maxbufs(struct virtnet_info *vi)  		if (vi->big_packets) {  			for (i = 0; i < MAX_SKB_FRAGS; i++) {  				skb_frag_t *f = &skb_shinfo(skb)->frags[i]; -				f->page = get_a_page(vi, GFP_ATOMIC); +				f->page = get_a_page(vi, gfp);  				if (!f->page)  					break; @@ -325,31 +331,35 @@ static void try_fill_recv_maxbufs(struct virtnet_info *vi)  	if (unlikely(vi->num > vi->max))  		vi->max = vi->num;  	vi->rvq->vq_ops->kick(vi->rvq); +	return !oom;  } -static void try_fill_recv(struct virtnet_info *vi) +/* Returns false if we couldn't fill entirely (OOM). */ +static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp)  {  	struct sk_buff *skb;  	struct scatterlist sg[1];  	int err; +	bool oom = false; -	if (!vi->mergeable_rx_bufs) { -		try_fill_recv_maxbufs(vi); -		return; -	} +	if (!vi->mergeable_rx_bufs) +		return try_fill_recv_maxbufs(vi, gfp);  	for (;;) {  		skb_frag_t *f;  		skb = netdev_alloc_skb(vi->dev, GOOD_COPY_LEN + NET_IP_ALIGN); -		if (unlikely(!skb)) +		if (unlikely(!skb)) { +			oom = true;  			break; +		}  		skb_reserve(skb, NET_IP_ALIGN);  		f = &skb_shinfo(skb)->frags[0]; -		f->page = get_a_page(vi, GFP_ATOMIC); +		f->page = get_a_page(vi, gfp);  		if (!f->page) { +			oom = true;  			kfree_skb(skb);  			break;  		} @@ -373,6 +383,7 @@ static void try_fill_recv(struct virtnet_info *vi)  	if (unlikely(vi->num > vi->max))  		vi->max = vi->num;  	vi->rvq->vq_ops->kick(vi->rvq); +	return !oom;  }  static void skb_recv_done(struct virtqueue *rvq) @@ -385,6 +396,23 @@ static void skb_recv_done(struct virtqueue *rvq)  	}  } +static void refill_work(struct work_struct *work) +{ +	struct virtnet_info *vi; +	bool still_empty; + +	vi = container_of(work, struct virtnet_info, refill.work); +	napi_disable(&vi->napi); +	try_fill_recv(vi, GFP_KERNEL); +	still_empty = (vi->num == 0); +	napi_enable(&vi->napi); + +	/* In theory, this can happen: if we don't get any buffers in +	 * we will *never* try to fill again. */ +	if (still_empty) +		schedule_delayed_work(&vi->refill, HZ/2); +} +  static int virtnet_poll(struct napi_struct *napi, int budget)  {  	struct virtnet_info *vi = container_of(napi, struct virtnet_info, napi); @@ -400,10 +428,10 @@ again:  		received++;  	} -	/* FIXME: If we oom and completely run out of inbufs, we need -	 * to start a timer trying to fill more. */ -	if (vi->num < vi->max / 2) -		try_fill_recv(vi); +	if (vi->num < vi->max / 2) { +		if (!try_fill_recv(vi, GFP_ATOMIC)) +			schedule_delayed_work(&vi->refill, 0); +	}  	/* Out of packets? */  	if (received < budget) { @@ -893,6 +921,7 @@ static int virtnet_probe(struct virtio_device *vdev)  	vi->vdev = vdev;  	vdev->priv = vi;  	vi->pages = NULL; +	INIT_DELAYED_WORK(&vi->refill, refill_work);  	/* If they give us a callback when all buffers are done, we don't need  	 * the timer. */ @@ -941,7 +970,7 @@ static int virtnet_probe(struct virtio_device *vdev)  	}  	/* Last of all, set up some receive buffers. */ -	try_fill_recv(vi); +	try_fill_recv(vi, GFP_KERNEL);  	/* If we didn't even get one input buffer, we're useless. */  	if (vi->num == 0) { @@ -958,6 +987,7 @@ static int virtnet_probe(struct virtio_device *vdev)  unregister:  	unregister_netdev(dev); +	cancel_delayed_work_sync(&vi->refill);  free_vqs:  	vdev->config->del_vqs(vdev);  free: @@ -986,6 +1016,7 @@ static void virtnet_remove(struct virtio_device *vdev)  	BUG_ON(vi->num != 0);  	unregister_netdev(vi->dev); +	cancel_delayed_work_sync(&vi->refill);  	vdev->config->del_vqs(vi->vdev);  | 
