diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 10d9761..ec0e1fb 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -118,6 +118,20 @@ static void give_pages(struct virtnet_info *vi, struct page *page)
vi->pages = page;
}
+/*
+ * Give a list of pages threaded through the ->private field back to the page
+ * allocator. To be used by paths that aren't serialized with napi.
+ */
+static void __give_pages(struct page *page)
+{
+ struct page *nextpage;
+ do {
+ nextpage = (struct page *)page->private;
+ __free_page(page);
+ page = nextpage;
+ } while (page);
+}
+
static struct page *get_a_page(struct virtnet_info *vi, gfp_t gfp_mask)
{
struct page *p = vi->pages;@@ -402,19 +416,24 @@ static int add_recvbuf_small(struct virtnet_info *vi, struct sk_buff *skb,
* Allocate an list of pages for "big" receive buffer configurations.
* Pages are chained through ->private.
* May return null if oom.
- * No serialization required.
+ * napi_serialized must be false if we aren't serialized with the napi path.
*/
-static struct page *alloc_recvbuf_big(struct virtnet_info *vi, gfp_t gfp)
+static struct page *alloc_recvbuf_big(struct virtnet_info *vi,
+ bool napi_serialized, gfp_t gfp)
{
struct page *first, *tail = NULL;
int i;
/* Build a list of pages chained through ->private. Built in reverse order */
for (i = 0; i < MAX_SKB_FRAGS + 1; ++i) {
- first = get_a_page(vi, gfp);
+ first = napi_serialized ? get_a_page(vi, gfp) : alloc_page(gfp);
if (!first) {
- if (tail)
- give_pages(vi, tail);
+ if (tail) {
+ if (napi_serialized)
+ give_pages(vi, tail);
+ else
+ __give_pages(tail);
+ }
return NULL;
}
@@ -472,11 +491,18 @@ static int add_recvbuf_big(struct virtnet_info *vi, struct page *first,
/*
* Allocate a page for "mergeable" receive buffer configurations.
* May return NULL if oom.
- * No serialization required.
+ * @napi_serialized must be false if we aren't serialized with the napi path.
*/
-static struct page *alloc_recvbuf_mergeable(struct virtnet_info *vi, gfp_t gfp)
+static struct page *alloc_recvbuf_mergeable(struct virtnet_info *vi,
+ bool napi_serialized, gfp_t gfp)
{
- return get_a_page(vi, gfp);
+ struct page *page;
+ if (napi_serialized)
+ return get_a_page(vi, gfp);
+ page = alloc_page(gfp);
+ if (page)
+ page->private = 0;
+ return page;
}
/*@@ -518,14 +544,14 @@ static bool try_fill_recv(struct virtnet_info *vi, gfp_t gfp)
do {
if (vi->mergeable_rx_bufs) {
struct page *page;
- page = alloc_recvbuf_mergeable(vi, gfp);
+ page = alloc_recvbuf_mergeable(vi, true, gfp);
if (!page)
err = -ENOMEM;
else
err = add_recvbuf_mergeable(vi, page, gfp);
} else if (vi->big_packets) {
struct page *page;
- page = alloc_recvbuf_big(vi, gfp);
+ page = alloc_recvbuf_big(vi, true, gfp);
if (!page)
err = -ENOMEM;
else@@ -575,7 +601,7 @@ static void virtnet_napi_enable(struct virtnet_info *vi)
/*
* Try to fill a "big" or "mergeable" receive queue using batching.
- * Caller must serialize against NAPI.
+ * This call will serialize itself against NAPI.
* Returns false if we failed to finish due to oom.
*/
static bool fill_recvbatch_pages(struct virtnet_info *vi)
@@ -591,9 +617,9 @@ fill_more:
/* Allocate a batch. */
for (i = 0; i < MAX_RX_ALLOCATE_BATCH; i++) {
if (vi->mergeable_rx_bufs)
- page = alloc_recvbuf_mergeable(vi, GFP_KERNEL);
+ page = alloc_recvbuf_mergeable(vi, false, GFP_KERNEL);
else /* vi->big_packets */
- page = alloc_recvbuf_big(vi, GFP_KERNEL);
+ page = alloc_recvbuf_big(vi, false, GFP_KERNEL);
if (!page) {
oom = true;
break;@@ -602,6 +628,7 @@ fill_more:
}
/* Enqueue batch as available. */
+ napi_disable(&vi->napi);
list_for_each_entry_safe(page, npage, &local_list, lru) {
int err;
@@ -622,12 +649,14 @@ fill_more:
}
if (unlikely(vi->num > vi->max))
vi->max = vi->num;
+ virtqueue_kick(vi->rvq);
+ virtnet_napi_enable(vi);
/* Cleanup any remaining entries on the list */
if (unlikely(!list_empty(&local_list))) {
list_for_each_entry_safe(page, npage, &local_list, lru) {
list_del(&page->lru);
- give_pages(vi, page);
+ __give_pages(page);
}
}
@@ -639,7 +668,7 @@ fill_more:
/*
* Try to fill a "small" receive queue using batching.
- * Caller must serialize against NAPI.
+ * This call will serialize itself against NAPI.
* Returns false if we failed to finish due to oom.
*/
static bool fill_recvbatch_small(struct virtnet_info *vi)
@@ -663,6 +692,7 @@ fill_more:
}
/* Enqueue batch as available. */
+ napi_disable(&vi->napi);
list_for_each_safe(pos, npos, &local_list) {
int err;
@@ -682,6 +712,8 @@ fill_more:
}
if (unlikely(vi->num > vi->max))
vi->max = vi->num;
+ virtqueue_kick(vi->rvq);
+ virtnet_napi_enable(vi);
/* Cleanup any remaining entries on the list */
if (unlikely(!list_empty(&local_list))) {@@ -700,7 +732,7 @@ fill_more:
/*
* Refill the receive queues from process context.
- * Caller must serialize against NAPI.
+ * Callee will serialize against NAPI itself.
* Returns false if we failed to allocate due to memory pressure.
*/
static bool try_fill_recvbatch(struct virtnet_info *vi)
@@ -717,10 +749,7 @@ static void refill_work(struct work_struct *work)
bool still_empty;
vi = container_of(work, struct virtnet_info, refill.work);
- napi_disable(&vi->napi);
still_empty = !try_fill_recvbatch(vi);
- virtqueue_kick(vi->rvq);
- virtnet_napi_enable(vi);
/* In theory, this can happen: if we don't get any buffers in
* we will *never* try to fill again. */