소스 검색

[net/9p]: Introduce basic flow-control for VirtIO transport.

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>
Venkateswararao Jujjuri (JV) 14 년 전
부모
커밋
68da9ba4ee
1개의 변경된 파일22개의 추가작업 그리고 1개의 파일을 삭제
  1. 22 1
      net/9p/trans_virtio.c

+ 22 - 1
net/9p/trans_virtio.c

@@ -43,6 +43,7 @@
 #include <net/9p/client.h>
 #include <net/9p/client.h>
 #include <net/9p/transport.h>
 #include <net/9p/transport.h>
 #include <linux/scatterlist.h>
 #include <linux/scatterlist.h>
+#include <linux/swap.h>
 #include <linux/virtio.h>
 #include <linux/virtio.h>
 #include <linux/virtio_9p.h>
 #include <linux/virtio_9p.h>
 #include "trans_common.h"
 #include "trans_common.h"
@@ -51,6 +52,8 @@
 
 
 /* a single mutex to manage channel initialization and attachment */
 /* a single mutex to manage channel initialization and attachment */
 static DEFINE_MUTEX(virtio_9p_lock);
 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
  * struct virtio_chan - per-instance transport information
@@ -78,7 +81,10 @@ struct virtio_chan {
 	struct virtqueue *vq;
 	struct virtqueue *vq;
 	int ring_bufs_avail;
 	int ring_bufs_avail;
 	wait_queue_head_t *vc_wq;
 	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. */
 	/* Scatterlist: can be too big for stack. */
 	struct scatterlist sg[VIRTQUEUE_NUM];
 	struct scatterlist sg[VIRTQUEUE_NUM];
 
 
@@ -159,8 +165,11 @@ static void req_done(struct virtqueue *vq)
 		req = p9_tag_lookup(chan->client, rc->tag);
 		req = p9_tag_lookup(chan->client, rc->tag);
 		if (req->tc->private) {
 		if (req->tc->private) {
 			struct trans_rpage_info *rp = req->tc->private;
 			struct trans_rpage_info *rp = req->tc->private;
+			int p = rp->rp_nr_pages;
 			/*Release pages */
 			/*Release pages */
 			p9_release_req_pages(rp);
 			p9_release_req_pages(rp);
+			atomic_sub(p, &vp_pinned);
+			wake_up(&vp_wq);
 			if (rp->rp_alloc)
 			if (rp->rp_alloc)
 				kfree(rp);
 				kfree(rp);
 			req->tc->private = NULL;
 			req->tc->private = NULL;
@@ -269,6 +278,14 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
 		int rpinfo_size = sizeof(struct trans_rpage_info) +
 		int rpinfo_size = sizeof(struct trans_rpage_info) +
 			sizeof(struct page *) * nr_pages;
 			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)) {
 		if (rpinfo_size <= (req->tc->capacity - req->tc->size)) {
 			/* We can use sdata */
 			/* We can use sdata */
 			req->tc->private = req->tc->sdata + req->tc->size;
 			req->tc->private = req->tc->sdata + req->tc->size;
@@ -291,6 +308,8 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
 			if (rpinfo->rp_alloc)
 			if (rpinfo->rp_alloc)
 				kfree(rpinfo);
 				kfree(rpinfo);
 			return err;
 			return err;
+		} else {
+			atomic_add(rpinfo->rp_nr_pages, &vp_pinned);
 		}
 		}
 	}
 	}
 
 
@@ -452,6 +471,8 @@ static int p9_virtio_probe(struct virtio_device *vdev)
 	}
 	}
 	init_waitqueue_head(chan->vc_wq);
 	init_waitqueue_head(chan->vc_wq);
 	chan->ring_bufs_avail = 1;
 	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);
 	mutex_lock(&virtio_9p_lock);
 	list_add_tail(&chan->chan_list, &virtio_chan_list);
 	list_add_tail(&chan->chan_list, &virtio_chan_list);