]> Git Repo - linux.git/blobdiff - net/9p/trans_virtio.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[linux.git] / net / 9p / trans_virtio.c
index 9b550ed9c7110bb8b339d91c7e2f1eb4cd5878e8..e8f046b071821636daf99ae9ef79e88f28ef7a09 100644 (file)
@@ -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);
This page took 0.035435 seconds and 4 git commands to generate.