Skip to content

Commit

Permalink
[net/9p]: Introduce basic flow-control for VirtIO transport.
Browse files Browse the repository at this point in the history
Recent zerocopy work in the 9P VirtIO transport maps and pins
user buffers into kernel memory for the server to work on them.
Since the user process can initiate this kind of pinning with a simple
read/write call, thousands of IO threads initiated by the user process can
hog the system resources and could result into denial of service.

This patch introduces flow control to avoid that extreme scenario.

The ceiling limit to avoid denial of service attacks is set to relatively
high (nr_free_pagecache_pages()/4) so that it won't interfere with
regular usage, but can step in extreme cases to limit the total system
hang. Since we don't have a global structure to accommodate this variable,
I choose the virtio_chan as the home for this.

Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
Reviewed-by: Badari Pulavarty <pbadari@us.ibm.com>
Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
  • Loading branch information
Venkateswararao Jujjuri (JV) authored and Eric Van Hensbergen committed Mar 22, 2011
1 parent aaf0ef1 commit 68da9ba
Showing 1 changed file with 22 additions and 1 deletion.
23 changes: 22 additions & 1 deletion net/9p/trans_virtio.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <net/9p/client.h>
#include <net/9p/transport.h>
#include <linux/scatterlist.h>
#include <linux/swap.h>
#include <linux/virtio.h>
#include <linux/virtio_9p.h>
#include "trans_common.h"
Expand All @@ -51,6 +52,8 @@

/* a single mutex to manage channel initialization and attachment */
static DEFINE_MUTEX(virtio_9p_lock);
static DECLARE_WAIT_QUEUE_HEAD(vp_wq);
static atomic_t vp_pinned = ATOMIC_INIT(0);

/**
* struct virtio_chan - per-instance transport information
Expand Down Expand Up @@ -78,7 +81,10 @@ struct virtio_chan {
struct virtqueue *vq;
int ring_bufs_avail;
wait_queue_head_t *vc_wq;

/* This is global limit. Since we don't have a global structure,
* will be placing it in each channel.
*/
int p9_max_pages;
/* Scatterlist: can be too big for stack. */
struct scatterlist sg[VIRTQUEUE_NUM];

Expand Down Expand Up @@ -159,8 +165,11 @@ static void req_done(struct virtqueue *vq)
req = p9_tag_lookup(chan->client, rc->tag);
if (req->tc->private) {
struct trans_rpage_info *rp = req->tc->private;
int p = rp->rp_nr_pages;
/*Release pages */
p9_release_req_pages(rp);
atomic_sub(p, &vp_pinned);
wake_up(&vp_wq);
if (rp->rp_alloc)
kfree(rp);
req->tc->private = NULL;
Expand Down Expand Up @@ -269,6 +278,14 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
int rpinfo_size = sizeof(struct trans_rpage_info) +
sizeof(struct page *) * nr_pages;

if (atomic_read(&vp_pinned) >= chan->p9_max_pages) {
err = wait_event_interruptible(vp_wq,
atomic_read(&vp_pinned) < chan->p9_max_pages);
if (err == -ERESTARTSYS)
return err;
P9_DPRINTK(P9_DEBUG_TRANS, "9p: May gup pages now.\n");
}

if (rpinfo_size <= (req->tc->capacity - req->tc->size)) {
/* We can use sdata */
req->tc->private = req->tc->sdata + req->tc->size;
Expand All @@ -291,6 +308,8 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
if (rpinfo->rp_alloc)
kfree(rpinfo);
return err;
} else {
atomic_add(rpinfo->rp_nr_pages, &vp_pinned);
}
}

Expand Down Expand Up @@ -452,6 +471,8 @@ static int p9_virtio_probe(struct virtio_device *vdev)
}
init_waitqueue_head(chan->vc_wq);
chan->ring_bufs_avail = 1;
/* Ceiling limit to avoid denial of service attacks */
chan->p9_max_pages = nr_free_buffer_pages()/4;

mutex_lock(&virtio_9p_lock);
list_add_tail(&chan->chan_list, &virtio_chan_list);
Expand Down

0 comments on commit 68da9ba

Please sign in to comment.