|
@@ -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"
|
|
@@ -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
|
|
@@ -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];
|
|
|
|
|
@@ -141,34 +147,36 @@ static void req_done(struct virtqueue *vq)
|
|
|
|
|
|
P9_DPRINTK(P9_DEBUG_TRANS, ": request done\n");
|
|
|
|
|
|
- do {
|
|
|
+ while (1) {
|
|
|
spin_lock_irqsave(&chan->lock, flags);
|
|
|
rc = virtqueue_get_buf(chan->vq, &len);
|
|
|
|
|
|
- if (rc != NULL) {
|
|
|
- if (!chan->ring_bufs_avail) {
|
|
|
- chan->ring_bufs_avail = 1;
|
|
|
- wake_up(chan->vc_wq);
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(&chan->lock, flags);
|
|
|
- P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
|
|
- P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n",
|
|
|
- rc->tag);
|
|
|
- req = p9_tag_lookup(chan->client, rc->tag);
|
|
|
- req->status = REQ_STATUS_RCVD;
|
|
|
- if (req->tc->private) {
|
|
|
- struct trans_rpage_info *rp = req->tc->private;
|
|
|
- /*Release pages */
|
|
|
- p9_release_req_pages(rp);
|
|
|
- if (rp->rp_alloc)
|
|
|
- kfree(rp);
|
|
|
- req->tc->private = NULL;
|
|
|
- }
|
|
|
- p9_client_cb(chan->client, req);
|
|
|
- } else {
|
|
|
+ if (rc == NULL) {
|
|
|
spin_unlock_irqrestore(&chan->lock, flags);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ chan->ring_bufs_avail = 1;
|
|
|
+ spin_unlock_irqrestore(&chan->lock, flags);
|
|
|
+ /* Wakeup if anyone waiting for VirtIO ring space. */
|
|
|
+ wake_up(chan->vc_wq);
|
|
|
+ P9_DPRINTK(P9_DEBUG_TRANS, ": rc %p\n", rc);
|
|
|
+ P9_DPRINTK(P9_DEBUG_TRANS, ": lookup tag %d\n", rc->tag);
|
|
|
+ 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;
|
|
|
}
|
|
|
- } while (rc != NULL);
|
|
|
+ req->status = REQ_STATUS_RCVD;
|
|
|
+ p9_client_cb(chan->client, req);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -263,7 +271,6 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req)
|
|
|
|
|
|
P9_DPRINTK(P9_DEBUG_TRANS, "9p debug: virtio request\n");
|
|
|
|
|
|
-req_retry:
|
|
|
req->status = REQ_STATUS_SENT;
|
|
|
|
|
|
if (req->tc->pbuf_size && (req->tc->pubuf && P9_IS_USER_CONTEXT)) {
|
|
@@ -271,6 +278,14 @@ req_retry:
|
|
|
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;
|
|
@@ -293,9 +308,12 @@ req_retry:
|
|
|
if (rpinfo->rp_alloc)
|
|
|
kfree(rpinfo);
|
|
|
return err;
|
|
|
+ } else {
|
|
|
+ atomic_add(rpinfo->rp_nr_pages, &vp_pinned);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+req_retry_pinned:
|
|
|
spin_lock_irqsave(&chan->lock, flags);
|
|
|
|
|
|
/* Handle out VirtIO ring buffers */
|
|
@@ -356,7 +374,7 @@ req_retry:
|
|
|
return err;
|
|
|
|
|
|
P9_DPRINTK(P9_DEBUG_TRANS, "9p:Retry virtio request\n");
|
|
|
- goto req_retry;
|
|
|
+ goto req_retry_pinned;
|
|
|
} else {
|
|
|
spin_unlock_irqrestore(&chan->lock, flags);
|
|
|
P9_DPRINTK(P9_DEBUG_TRANS,
|
|
@@ -453,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);
|