]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
tools/kvm/9p: Add support for multiple 9p export dirs
authorAneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Fri, 17 Jun 2011 18:11:22 +0000 (23:41 +0530)
committerPekka Enberg <penberg@kernel.org>
Fri, 17 Jun 2011 20:11:53 +0000 (23:11 +0300)
Acked-by: Sasha Levin <levinsasha928@gmail.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
tools/kvm/include/kvm/virtio-9p.h
tools/kvm/kvm-run.c
tools/kvm/virtio/9p.c

index 905283039b8733bb1b6c42b82c9335f0e40dd1a5..d99bf961d92760450dfdce9d2c3e53ad48953a6a 100644 (file)
@@ -3,6 +3,6 @@
 
 struct kvm;
 
-void virtio_9p__init(struct kvm *kvm, const char *root);
+void virtio_9p__init(struct kvm *kvm, const char *root, const char *tag_name);
 
 #endif
index 6ad55aa1d83c7f6b45c7110dde18bf1cfcd31aff..60fc07b55732279e67914b7469287a1cbbd52a3e 100644 (file)
@@ -69,7 +69,6 @@ static const char *network;
 static const char *host_ip_addr;
 static const char *guest_mac;
 static const char *script;
-static const char *virtio_9p_dir;
 static bool single_step;
 static bool readonly_image[MAX_DISK_IMAGES];
 static bool vnc;
@@ -108,6 +107,29 @@ static int img_name_parser(const struct option *opt, const char *arg, int unset)
        return 0;
 }
 
+static int virtio_9p_rootdir_parser(const struct option *opt, const char *arg, int unset)
+{
+       char *tag_name;
+       char tmp[PATH_MAX];
+
+       /*
+        * 9p dir can be of the form dirname,tag_name or
+        * just dirname. In the later case we use the
+        * default tag name
+        */
+       tag_name = strstr(arg, ",");
+       if (tag_name) {
+               *tag_name = '\0';
+               tag_name++;
+       }
+       if (realpath(arg, tmp))
+               virtio_9p__init(kvm, tmp, tag_name);
+       else
+               die("Failed resolving 9p path");
+       return 0;
+}
+
+
 static const struct option options[] = {
        OPT_GROUP("Basic options:"),
        OPT_INTEGER('c', "cpus", &nrcpus, "Number of CPUs"),
@@ -118,8 +140,8 @@ static const struct option options[] = {
        OPT_INCR('\0', "rng", &virtio_rng,
                        "Enable virtio Random Number Generator"),
        OPT_STRING('\0', "kvm-dev", &kvm_dev, "kvm-dev", "KVM device file"),
-       OPT_STRING('\0', "virtio-9p", &virtio_9p_dir, "root dir",
-                       "Enable 9p over virtio"),
+       OPT_CALLBACK('\0', "virtio-9p", NULL, "dirname,tag_name",
+                    "Enable 9p over virtio", virtio_9p_rootdir_parser),
        OPT_BOOLEAN('\0', "vnc", &vnc, "Enable VNC framebuffer"),
        OPT_BOOLEAN('\0', "sdl", &sdl, "Enable SDL framebuffer"),
 
@@ -520,15 +542,6 @@ int kvm_cmd_run(int argc, const char **argv, const char *prefix)
        if (!script)
                script = DEFAULT_SCRIPT;
 
-       if (virtio_9p_dir) {
-               char tmp[PATH_MAX];
-
-               if (realpath(virtio_9p_dir, tmp))
-                       virtio_9p__init(kvm, tmp);
-               else
-                       die("Failed resolving 9p path");
-       }
-
        symbol__init(vmlinux_filename);
 
        term_init();
index 38a997db79840bacbba8cbb8748f1282ba062068..d4263d26057990ca23d9cb4b4d1e21c34ddb03f0 100644 (file)
 
 #define NUM_VIRT_QUEUES                1
 #define VIRTIO_P9_QUEUE_SIZE   128
-#define        VIRTIO_P9_TAG           "kvm_9p"
+#define        VIRTIO_P9_DEFAULT_TAG   "kvm_9p"
 #define VIRTIO_P9_HDR_LEN      (sizeof(u32)+sizeof(u8)+sizeof(u16))
 #define VIRTIO_P9_MAX_FID      128
 #define VIRTIO_P9_VERSION      "9P2000"
+#define MAX_TAG_LEN            32
+
 
 struct p9_msg {
        u32                     size;
@@ -42,14 +44,10 @@ struct p9_fid {
        int                     fd;
 };
 
-static struct pci_device_header virtio_p9_pci_device = {
-       .vendor_id              = PCI_VENDOR_ID_REDHAT_QUMRANET,
-       .device_id              = PCI_DEVICE_ID_VIRTIO_P9,
-       .header_type            = PCI_HEADER_TYPE_NORMAL,
-       .revision_id            = 0,
-       .class                  = 0x010000,
-       .subsys_vendor_id       = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
-       .subsys_id              = VIRTIO_ID_9P,
+struct p9_dev_job {
+       struct virt_queue               *vq;
+       struct p9_dev                   *p9dev;
+       void                            *job_id;
 };
 
 struct p9_dev {
@@ -63,25 +61,26 @@ struct p9_dev {
        /* virtio queue */
        u16                     queue_selector;
        struct virt_queue       vqs[NUM_VIRT_QUEUES];
-       void                    *jobs[NUM_VIRT_QUEUES];
-
+       struct p9_dev_job       jobs[NUM_VIRT_QUEUES];
        struct p9_fid           fids[VIRTIO_P9_MAX_FID];
        char                    root_dir[PATH_MAX];
+       struct pci_device_header pci_hdr;
 };
 
-static struct p9_dev p9dev;
-
 /* Warning: Immediately use value returned from this function */
-static const char *rel_to_abs(const char *path, char *abs_path)
+static const char *rel_to_abs(struct p9_dev *p9dev,
+                             const char *path, char *abs_path)
 {
-       sprintf(abs_path, "%s/%s", p9dev.root_dir, path);
+       sprintf(abs_path, "%s/%s", p9dev->root_dir, path);
 
        return abs_path;
 }
 
-static bool virtio_p9_dev_in(void *data, unsigned long offset, int size, u32 count)
+static bool virtio_p9_dev_in(struct p9_dev *p9dev, void *data,
+                            unsigned long offset,
+                            int size, u32 count)
 {
-       u8 *config_space = (u8 *) p9dev.config;
+       u8 *config_space = (u8 *) p9dev->config;
 
        if (size != 1 || count != 1)
                return false;
@@ -91,16 +90,19 @@ static bool virtio_p9_dev_in(void *data, unsigned long offset, int size, u32 cou
        return true;
 }
 
-static bool virtio_p9_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
+static bool virtio_p9_pci_io_in(struct ioport *ioport, struct kvm *kvm,
+                               u16 port, void *data, int size, u32 count)
 {
-       unsigned long offset;
        bool ret = true;
+       unsigned long offset;
+       struct p9_dev *p9dev = ioport->priv;
 
-       offset = port - p9dev.base_addr;
+
+       offset = port - p9dev->base_addr;
 
        switch (offset) {
        case VIRTIO_PCI_HOST_FEATURES:
-               ioport__write32(data, p9dev.features);
+               ioport__write32(data, p9dev->features);
                ret = true;
                break;
        case VIRTIO_PCI_GUEST_FEATURES:
@@ -109,21 +111,21 @@ static bool virtio_p9_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port
                ret = false;
                break;
        case VIRTIO_PCI_QUEUE_PFN:
-               ioport__write32(data, p9dev.vqs[p9dev.queue_selector].pfn);
+               ioport__write32(data, p9dev->vqs[p9dev->queue_selector].pfn);
                break;
        case VIRTIO_PCI_QUEUE_NUM:
                ioport__write16(data, VIRTIO_P9_QUEUE_SIZE);
                break;
        case VIRTIO_PCI_STATUS:
-               ioport__write8(data, p9dev.status);
+               ioport__write8(data, p9dev->status);
                break;
        case VIRTIO_PCI_ISR:
-               ioport__write8(data, p9dev.isr);
-               kvm__irq_line(kvm, virtio_p9_pci_device.irq_line, VIRTIO_IRQ_LOW);
-               p9dev.isr = VIRTIO_IRQ_LOW;
+               ioport__write8(data, p9dev->isr);
+               kvm__irq_line(kvm, p9dev->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
+               p9dev->isr = VIRTIO_IRQ_LOW;
                break;
        default:
-               ret = virtio_p9_dev_in(data, offset, size, count);
+               ret = virtio_p9_dev_in(p9dev, data, offset, size, count);
                break;
        };
 
@@ -161,15 +163,15 @@ static void st2qid(struct stat *st, struct p9_qid *qid)
                qid->type       |= P9_QTDIR;
 }
 
-static void close_fid(u32 fid)
+static void close_fid(struct p9_dev *p9dev, u32 fid)
 {
-       if (p9dev.fids[fid].fd > 0) {
-               close(p9dev.fids[fid].fd);
-               p9dev.fids[fid].fd = -1;
+       if (p9dev->fids[fid].fd > 0) {
+               close(p9dev->fids[fid].fd);
+               p9dev->fids[fid].fd = -1;
        }
-       if (p9dev.fids[fid].dir) {
-               closedir(p9dev.fids[fid].dir);
-               p9dev.fids[fid].dir = NULL;
+       if (p9dev->fids[fid].dir) {
+               closedir(p9dev->fids[fid].dir);
+               p9dev->fids[fid].dir = NULL;
        }
 }
 
@@ -182,7 +184,9 @@ static void set_p9msg_hdr(struct p9_msg *msg, u32 size, u8 cmd, u16 tag)
        };
 }
 
-static bool virtio_p9_version(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_version(struct p9_dev *p9dev, struct p9_msg *msg,
+                             u32 len, struct iovec *iov,
+                             int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg = iov[0].iov_base;
        struct p9_rversion *rversion = (struct p9_rversion *)outmsg->msg;
@@ -191,18 +195,21 @@ static bool virtio_p9_version(struct p9_msg *msg, u32 len, struct iovec *iov, u3
        rversion->version.len   = strlen(VIRTIO_P9_VERSION);
        memcpy(&rversion->version.str, VIRTIO_P9_VERSION, rversion->version.len);
 
-       *outlen = VIRTIO_P9_HDR_LEN + rversion->version.len + sizeof(u16) + sizeof(u32);
+       *outlen = VIRTIO_P9_HDR_LEN +
+               rversion->version.len + sizeof(u16) + sizeof(u32);
        set_p9msg_hdr(outmsg, *outlen, P9_RVERSION, msg->tag);
 
        return true;
 }
 
-static bool virtio_p9_clunk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_clunk(struct p9_dev *p9dev, struct p9_msg *msg,
+                           u32 len, struct iovec *iov,
+                           int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg = iov[0].iov_base;
        struct p9_tclunk *tclunk = (struct p9_tclunk *)msg->msg;
 
-       close_fid(tclunk->fid);
+       close_fid(p9dev, tclunk->fid);
 
        *outlen = VIRTIO_P9_HDR_LEN;
        set_p9msg_hdr(outmsg, *outlen, P9_RCLUNK, msg->tag);
@@ -210,12 +217,14 @@ static bool virtio_p9_clunk(struct p9_msg *msg, u32 len, struct iovec *iov, u32
        return true;
 }
 
-static bool virtio_p9_open(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_open(struct p9_dev *p9dev, struct p9_msg *msg,
+                          u32 len, struct iovec *iov,
+                          int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg   = iov[0].iov_base;
        struct p9_topen *topen  = (struct p9_topen *)msg->msg;
        struct p9_ropen *ropen  = (struct p9_ropen *)outmsg->msg;
-       struct p9_fid *new_fid  = &p9dev.fids[topen->fid];
+       struct p9_fid *new_fid  = &p9dev->fids[topen->fid];
        struct stat st;
 
        if (stat(new_fid->abs_path, &st) < 0)
@@ -235,12 +244,14 @@ static bool virtio_p9_open(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *
        return true;
 }
 
-static bool virtio_p9_create(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_create(struct p9_dev *p9dev, struct p9_msg *msg,
+                            u32 len, struct iovec *iov,
+                            int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg           = iov[0].iov_base;
        struct p9_tcreate *tcreate      = (struct p9_tcreate *)msg->msg;
        struct p9_rcreate *rcreate      = (struct p9_rcreate *)outmsg->msg;
-       struct p9_fid *fid              = &p9dev.fids[tcreate->fid];
+       struct p9_fid *fid              = &p9dev->fids[tcreate->fid];
        struct stat st;
        u8 mode;
        u32 perm;
@@ -253,7 +264,7 @@ static bool virtio_p9_create(struct p9_msg *msg, u32 len, struct iovec *iov, u32
 
        sprintf(fid->path, "%s/%.*s", fid->path, tcreate->name.len, (char *)&tcreate->name.str);
 
-       close_fid(tcreate->fid);
+       close_fid(p9dev, tcreate->fid);
 
        if (perm & P9_DMDIR) {
                mkdir(fid->abs_path, perm & 0xFFFF);
@@ -274,18 +285,20 @@ static bool virtio_p9_create(struct p9_msg *msg, u32 len, struct iovec *iov, u32
        return true;
 }
 
-static bool virtio_p9_walk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_walk(struct p9_dev *p9dev, struct p9_msg *msg,
+                          u32 len, struct iovec *iov,
+                          int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg   = iov[0].iov_base;
        struct p9_twalk *twalk  = (struct p9_twalk *)msg->msg;
        struct p9_rwalk *rwalk  = (struct p9_rwalk *)outmsg->msg;
        struct p9_str *str      = twalk->wnames;
-       struct p9_fid *new_fid  = &p9dev.fids[twalk->newfid];
+       struct p9_fid *new_fid  = &p9dev->fids[twalk->newfid];
        u8 i;
 
        rwalk->nwqid = 0;
        if (twalk->nwname) {
-               struct p9_fid *fid = &p9dev.fids[twalk->fid];
+               struct p9_fid *fid = &p9dev->fids[twalk->fid];
 
                for (i = 0; i < twalk->nwname; i++) {
                        char tmp[PATH_MAX] = {0};
@@ -295,7 +308,7 @@ static bool virtio_p9_walk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *
                        /* Format the new path we're 'walk'ing into */
                        sprintf(tmp, "%s/%.*s", fid->path, str->len, (char *)&str->str);
 
-                       if (stat(rel_to_abs(tmp, full_path), &st) < 0)
+                       if (stat(rel_to_abs(p9dev, tmp, full_path), &st) < 0)
                                break;
 
                        st2qid(&st, &rwalk->wqids[i]);
@@ -305,8 +318,8 @@ static bool virtio_p9_walk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *
                        rwalk->nwqid++;
                }
        } else {
-               new_fid->is_dir = p9dev.fids[twalk->fid].is_dir;
-               strcpy(new_fid->path, p9dev.fids[twalk->fid].path);
+               new_fid->is_dir = p9dev->fids[twalk->fid].is_dir;
+               strcpy(new_fid->path, p9dev->fids[twalk->fid].path);
                new_fid->fid    = twalk->newfid;
        }
 
@@ -316,7 +329,9 @@ static bool virtio_p9_walk(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *
        return true;
 }
 
-static bool virtio_p9_attach(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_attach(struct p9_dev *p9dev, struct p9_msg *msg,
+                            u32 len, struct iovec *iov,
+                            int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg = iov[0].iov_base;
        struct p9_rattach *rattach = (struct p9_rattach *)outmsg->msg;
@@ -327,14 +342,14 @@ static bool virtio_p9_attach(struct p9_msg *msg, u32 len, struct iovec *iov, u32
 
        /* Reset everything */
        for (i = 0; i < VIRTIO_P9_MAX_FID; i++)
-               p9dev.fids[i].fid = P9_NOFID;
+               p9dev->fids[i].fid = P9_NOFID;
 
-       if (stat(p9dev.root_dir, &st) < 0)
+       if (stat(p9dev->root_dir, &st) < 0)
                return false;
 
        st2qid(&st, &rattach->qid);
 
-       fid = &p9dev.fids[tattach->fid];
+       fid = &p9dev->fids[tattach->fid];
        fid->fid = tattach->fid;
        fid->is_dir = 1;
        strcpy(fid->path, "/");
@@ -345,7 +360,8 @@ static bool virtio_p9_attach(struct p9_msg *msg, u32 len, struct iovec *iov, u32
        return true;
 }
 
-static u32 virtio_p9_fill_stat(const char *name, struct stat *st, struct p9_rstat *rstat)
+static u32 virtio_p9_fill_stat(struct p9_dev *p9dev, const char *name,
+                              struct stat *st, struct p9_rstat *rstat)
 {
        struct p9_str *str;
 
@@ -375,19 +391,24 @@ static u32 virtio_p9_fill_stat(const char *name, struct stat *st, struct p9_rsta
        str->len = 0;
        str = (void *)str + sizeof(u16);
 
-       /* We subtract a u16 here because rstat->size doesn't include rstat->size itself */
+       /*
+        * We subtract a u16 here because rstat->size
+        * doesn't include rstat->size itself
+        */
        rstat->stat.size = (void *)str - (void *)&rstat->stat - sizeof(u16);
 
        return rstat->stat.size + sizeof(u16);
 }
 
-static bool virtio_p9_read(struct p9_msg *msg, u32 len, struct iovec *iov, int iovcnt, u32 *outlen)
+static bool virtio_p9_read(struct p9_dev *p9dev, struct p9_msg *msg,
+                          u32 len, struct iovec *iov,
+                          int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg   = iov[0].iov_base;
        struct p9_tread *tread  = (struct p9_tread *)msg->msg;
        struct p9_rread *rread  = (struct p9_rread *)outmsg->msg;
        struct p9_rstat *rstat  = (struct p9_rstat *)iov[1].iov_base;
-       struct p9_fid *fid      = &p9dev.fids[tread->fid];
+       struct p9_fid *fid      = &p9dev->fids[tread->fid];
        struct stat st;
 
        rread->count = 0;
@@ -400,8 +421,9 @@ static bool virtio_p9_read(struct p9_msg *msg, u32 len, struct iovec *iov, int i
                while (cur) {
                        u32 read;
 
-                       stat(rel_to_abs(cur->d_name, full_path), &st);
-                       read = virtio_p9_fill_stat(cur->d_name, &st, rstat);
+                       stat(rel_to_abs(p9dev, cur->d_name, full_path), &st);
+                       read = virtio_p9_fill_stat(p9dev, cur->d_name,
+                                                  &st, rstat);
                        rread->count += read;
                        rstat = (void *)rstat + read;
                        cur = readdir(fid->dir);
@@ -409,7 +431,7 @@ static bool virtio_p9_read(struct p9_msg *msg, u32 len, struct iovec *iov, int i
        } else {
                iov[0].iov_base += VIRTIO_P9_HDR_LEN + sizeof(u32);
                iov[0].iov_len -= VIRTIO_P9_HDR_LEN + sizeof(u32);
-               rread->count = preadv(fid->fd, iov, iovcnt, tread->offset);
+               rread->count = preadv(fid->fd, iov, iniovcnt, tread->offset);
                if (rread->count > tread->count)
                        rread->count = tread->count;
        }
@@ -420,31 +442,35 @@ static bool virtio_p9_read(struct p9_msg *msg, u32 len, struct iovec *iov, int i
        return true;
 }
 
-static bool virtio_p9_stat(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_stat(struct p9_dev *p9dev, struct p9_msg *msg,
+                          u32 len, struct iovec *iov,
+                          int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg = iov[0].iov_base;
        struct p9_tstat *tstat = (struct p9_tstat *)msg->msg;
        struct p9_rstat *rstat = (struct p9_rstat *)(outmsg->msg + sizeof(u16));
        struct stat st;
-       struct p9_fid *fid = &p9dev.fids[tstat->fid];
+       struct p9_fid *fid = &p9dev->fids[tstat->fid];
        u32 ret;
 
        if (stat(fid->abs_path, &st) < 0)
                return false;
 
-       ret = virtio_p9_fill_stat(fid->path, &st, rstat);
+       ret = virtio_p9_fill_stat(p9dev, fid->path, &st, rstat);
 
        *outlen = VIRTIO_P9_HDR_LEN + ret + sizeof(u16) * 2;
        set_p9msg_hdr(outmsg, *outlen, P9_RSTAT, msg->tag);
        return true;
 }
 
-static bool virtio_p9_wstat(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_wstat(struct p9_dev *p9dev, struct p9_msg *msg,
+                           u32 len, struct iovec *iov,
+                           int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg = iov[0].iov_base;
        struct p9_twstat *twstat = (struct p9_twstat *)msg->msg;
        struct p9_str *str;
-       struct p9_fid *fid = &p9dev.fids[twstat->fid];
+       struct p9_fid *fid = &p9dev->fids[twstat->fid];
        int res = 0;
 
        if (twstat->stat.length != -1UL)
@@ -466,7 +492,7 @@ static bool virtio_p9_wstat(struct p9_msg *msg, u32 len, struct iovec *iov, u32
                memcpy(new_name + strlen(new_name), &str->str, str->len);
 
                /* fid is reused for the new file */
-               rename(fid->abs_path, rel_to_abs(new_name, full_path));
+               rename(fid->abs_path, rel_to_abs(p9dev, new_name, full_path));
                sprintf(fid->path, "%s", new_name);
        }
 
@@ -476,13 +502,15 @@ static bool virtio_p9_wstat(struct p9_msg *msg, u32 len, struct iovec *iov, u32
        return res == 0;
 }
 
-static bool virtio_p9_remove(struct p9_msg *msg, u32 len, struct iovec *iov, u32 *outlen)
+static bool virtio_p9_remove(struct p9_dev *p9dev, struct p9_msg *msg,
+                            u32 len, struct iovec *iov,
+                            int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg = iov[0].iov_base;
        struct p9_tremove *tremove = (struct p9_tremove *)msg->msg;
-       struct p9_fid *fid = &p9dev.fids[tremove->fid];
+       struct p9_fid *fid = &p9dev->fids[tremove->fid];
 
-       close_fid(tremove->fid);
+       close_fid(p9dev, tremove->fid);
        if (fid->is_dir)
                rmdir(fid->abs_path);
        else
@@ -493,131 +521,138 @@ static bool virtio_p9_remove(struct p9_msg *msg, u32 len, struct iovec *iov, u32
        return true;
 }
 
-static bool virtio_p9_write(struct p9_msg *msg, u32 len, struct iovec *iov, int iovcnt, u32 *outlen)
+static bool virtio_p9_write(struct p9_dev *p9dev, struct p9_msg *msg,
+                           u32 len, struct iovec *iov,
+                           int outiovcnt, int iniovcnt, u32 *outlen)
 {
        struct p9_msg *outmsg;
        struct p9_rwrite *rwrite;
        struct p9_twrite *twrite = (struct p9_twrite *)msg->msg;
-       struct p9_fid *fid = &p9dev.fids[twrite->fid];
+       struct p9_fid *fid = &p9dev->fids[twrite->fid];
 
-       if (iovcnt == 1) {
+       if (outiovcnt == 1) {
                outmsg = iov[0].iov_base;
                rwrite = (struct p9_rwrite *)outmsg->msg;
-               rwrite->count = pwrite(fid->fd, &twrite->data, twrite->count, twrite->offset);
+               rwrite->count = pwrite(fid->fd, &twrite->data,
+                                      twrite->count, twrite->offset);
        } else {
                outmsg = iov[2].iov_base;
                rwrite = (struct p9_rwrite *)outmsg->msg;
-               rwrite->count = pwrite(fid->fd, iov[1].iov_base, twrite->count, twrite->offset);
+               rwrite->count = pwrite(fid->fd, iov[1].iov_base,
+                                      twrite->count, twrite->offset);
        }
-
        *outlen = VIRTIO_P9_HDR_LEN + sizeof(u32);
        set_p9msg_hdr(outmsg, *outlen, P9_RWRITE, msg->tag);
 
        return true;
 }
 
-static bool virtio_p9_do_io_request(struct kvm *kvm, struct virt_queue *queue)
+typedef bool p9_handler(struct p9_dev *p9dev, struct p9_msg *msg,
+                       u32 len, struct iovec *iov,
+                       int outiovcnt, int iniovcnt, u32 *outlen);
+
+static p9_handler *virtio_9p_handler [] = {
+       [P9_TVERSION] = virtio_p9_version,
+       [P9_TATTACH]  = virtio_p9_attach,
+       [P9_TSTAT]    = virtio_p9_stat,
+       [P9_TCLUNK]   = virtio_p9_clunk,
+       [P9_TWALK]    = virtio_p9_walk,
+       [P9_TOPEN]    = virtio_p9_open,
+       [P9_TREAD]    = virtio_p9_read,
+       [P9_TCREATE]  = virtio_p9_create,
+       [P9_TWSTAT]   = virtio_p9_wstat,
+       [P9_TREMOVE]  = virtio_p9_remove,
+       [P9_TWRITE]   = virtio_p9_write,
+};
+
+static bool virtio_p9_do_io_request(struct kvm *kvm, struct p9_dev_job *job)
 {
-       struct iovec iov[VIRTIO_P9_QUEUE_SIZE];
+       u32 len = 0;
        u16 out, in, head;
        struct p9_msg *msg;
-       u32 len = 0;
+       p9_handler *handler;
+       struct virt_queue *vq;
+       struct p9_dev *p9dev;
+       struct iovec iov[VIRTIO_P9_QUEUE_SIZE];
 
-       head            = virt_queue__get_iov(queue, iov, &out, &in, kvm);
-       msg             = iov[0].iov_base;
+       vq = job->vq;
+       p9dev = job->p9dev;
+       head  = virt_queue__get_iov(vq, iov, &out, &in, kvm);
+       msg   = iov[0].iov_base;
 
-       switch (msg->cmd) {
-       case P9_TVERSION:
-               virtio_p9_version(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TATTACH:
-               virtio_p9_attach(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TSTAT:
-               virtio_p9_stat(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TCLUNK:
-               virtio_p9_clunk(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TWALK:
-               virtio_p9_walk(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TOPEN:
-               virtio_p9_open(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TREAD:
-               virtio_p9_read(msg, iov[0].iov_len, iov+1, in, &len);
-               break;
-       case P9_TCREATE:
-               virtio_p9_create(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TWSTAT:
-               virtio_p9_wstat(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TREMOVE:
-               virtio_p9_remove(msg, iov[0].iov_len, iov+1, &len);
-               break;
-       case P9_TWRITE:
-               virtio_p9_write(msg, iov[0].iov_len, iov+1, out, &len);
-               break;
-       default:
+       if (msg->cmd >= ARRAY_SIZE(virtio_9p_handler) ||
+           !virtio_9p_handler[msg->cmd]) {
                printf("Unsupported P9 message type: %u\n", msg->cmd);
-               break;
-       }
-       virt_queue__set_used_elem(queue, head, len);
 
+       } else {
+               handler = virtio_9p_handler[msg->cmd];
+               handler(p9dev, msg, iov[0].iov_len, iov+1, out, in, &len);
+       }
+       virt_queue__set_used_elem(vq, head, len);
        return true;
 }
 
 static void virtio_p9_do_io(struct kvm *kvm, void *param)
 {
-       struct virt_queue *vq = param;
+       struct p9_dev_job *job = (struct p9_dev_job *)param;
+       struct p9_dev *p9dev   = job->p9dev;
+       struct virt_queue *vq  = job->vq;
 
        while (virt_queue__available(vq)) {
-               virtio_p9_do_io_request(kvm, vq);
-               virt_queue__trigger_irq(vq, virtio_p9_pci_device.irq_line, &p9dev.isr, kvm);
+               virtio_p9_do_io_request(kvm, job);
+               virt_queue__trigger_irq(vq, p9dev->pci_hdr.irq_line,
+                                       &p9dev->isr, kvm);
        }
 }
 
-static bool virtio_p9_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
+static bool virtio_p9_pci_io_out(struct ioport *ioport, struct kvm *kvm,
+                                u16 port, void *data, int size, u32 count)
 {
        unsigned long offset;
        bool ret = true;
+       struct p9_dev  *p9dev;
 
-       offset = port - p9dev.base_addr;
+       p9dev = ioport->priv;
+       offset = port - p9dev->base_addr;
 
        switch (offset) {
        case VIRTIO_MSI_QUEUE_VECTOR:
        case VIRTIO_PCI_GUEST_FEATURES:
                break;
        case VIRTIO_PCI_QUEUE_PFN: {
-               struct virt_queue *queue;
                void *p;
+               struct p9_dev_job *job;
+               struct virt_queue *queue;
 
-               queue                   = &p9dev.vqs[p9dev.queue_selector];
+               job                     = &p9dev->jobs[p9dev->queue_selector];
+               queue                   = &p9dev->vqs[p9dev->queue_selector];
                queue->pfn              = ioport__read32(data);
                p                       = guest_pfn_to_host(kvm, queue->pfn);
 
-               vring_init(&queue->vring, VIRTIO_P9_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
-
-               p9dev.jobs[p9dev.queue_selector] = thread_pool__add_job(kvm, virtio_p9_do_io, queue);
+               vring_init(&queue->vring, VIRTIO_P9_QUEUE_SIZE, p,
+                          VIRTIO_PCI_VRING_ALIGN);
 
+               *job                    = (struct p9_dev_job) {
+                       .vq                     = queue,
+                       .p9dev                  = p9dev,
+               };
+               job->job_id = thread_pool__add_job(kvm, virtio_p9_do_io, job);
                break;
        }
        case VIRTIO_PCI_QUEUE_SEL:
-               p9dev.queue_selector    = ioport__read16(data);
+               p9dev->queue_selector   = ioport__read16(data);
                break;
        case VIRTIO_PCI_QUEUE_NOTIFY: {
                u16 queue_index;
                queue_index             = ioport__read16(data);
-               thread_pool__do_job(p9dev.jobs[queue_index]);
+               thread_pool__do_job(p9dev->jobs[queue_index].job_id);
                break;
        }
        case VIRTIO_PCI_STATUS:
-               p9dev.status            = ioport__read8(data);
+               p9dev->status           = ioport__read8(data);
                break;
        case VIRTIO_MSI_CONFIG_VECTOR:
-               p9dev.config_vector     = VIRTIO_MSI_NO_VECTOR;
+               p9dev->config_vector    = VIRTIO_MSI_NO_VECTOR;
                break;
        default:
                ret                     = false;
@@ -632,39 +667,63 @@ static struct ioport_operations virtio_p9_io_ops = {
        .io_out                         = virtio_p9_pci_io_out,
 };
 
-void virtio_9p__init(struct kvm *kvm, const char *root)
+void virtio_9p__init(struct kvm *kvm, const char *root, const char *tag_name)
 {
        u8 pin, line, dev;
        u32 i, root_len;
        u16 p9_base_addr;
+       struct p9_dev *p9dev;
 
-       p9dev.config = calloc(1, sizeof(*p9dev.config) + sizeof(VIRTIO_P9_TAG));
-       if (p9dev.config == NULL)
+       p9dev = calloc(1, sizeof(*p9dev));
+       if (!p9dev)
                return;
+       if (!tag_name)
+               tag_name = VIRTIO_P9_DEFAULT_TAG;
+       p9dev->config = calloc(1, sizeof(*p9dev->config) + strlen(tag_name) + 1);
+       if (p9dev->config == NULL)
+               goto free_p9dev;
 
-       strcpy(p9dev.root_dir, root);
+       strcpy(p9dev->root_dir, root);
        root_len = strlen(root);
-
        /*
         * We prefix the full path in all fids, This allows us to get the
         * absolute path of an fid without playing with strings.
         */
        for (i = 0; i < VIRTIO_P9_MAX_FID; i++) {
-               strcpy(p9dev.fids[i].abs_path, root);
-               p9dev.fids[i].path = p9dev.fids[i].abs_path + root_len;
+               strcpy(p9dev->fids[i].abs_path, root);
+               p9dev->fids[i].path = p9dev->fids[i].abs_path + root_len;
        }
+       p9dev->config->tag_len = strlen(tag_name);
+       if (p9dev->config->tag_len > MAX_TAG_LEN)
+               goto free_p9dev_config;
 
-       p9dev.config->tag_len = strlen(VIRTIO_P9_TAG);
-       memcpy(p9dev.config->tag, VIRTIO_P9_TAG, strlen(VIRTIO_P9_TAG));
-       p9dev.features |= 1 << VIRTIO_9P_MOUNT_TAG;
+       memcpy(p9dev->config->tag, tag_name, strlen(tag_name));
+       p9dev->features |= 1 << VIRTIO_9P_MOUNT_TAG;
 
        if (irq__register_device(VIRTIO_ID_9P, &dev, &pin, &line) < 0)
-               return;
+               goto free_p9dev_config;
+
+       p9_base_addr                    = ioport__register(IOPORT_EMPTY,
+                                                          &virtio_p9_io_ops,
+                                                          IOPORT_SIZE, p9dev);
+       p9dev->base_addr                    = p9_base_addr;
+       p9dev->pci_hdr = (struct pci_device_header) {
+               .vendor_id              = PCI_VENDOR_ID_REDHAT_QUMRANET,
+               .device_id              = PCI_DEVICE_ID_VIRTIO_P9,
+               .header_type            = PCI_HEADER_TYPE_NORMAL,
+               .revision_id            = 0,
+               .class                  = 0x010000,
+               .subsys_vendor_id       = PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
+               .subsys_id              = VIRTIO_ID_9P,
+               .irq_pin                = pin,
+               .irq_line               = line,
+               .bar[0]                 = p9_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
+       };
+       pci__register(&p9dev->pci_hdr, dev);
 
-       virtio_p9_pci_device.irq_pin    = pin;
-       virtio_p9_pci_device.irq_line   = line;
-       p9_base_addr                    = ioport__register(IOPORT_EMPTY, &virtio_p9_io_ops, IOPORT_SIZE, NULL);
-       virtio_p9_pci_device.bar[0]     = p9_base_addr | PCI_BASE_ADDRESS_SPACE_IO;
-       p9dev.base_addr                 = p9_base_addr;
-       pci__register(&virtio_p9_pci_device, dev);
+       return;
+free_p9dev_config:
+       free(p9dev->config);
+free_p9dev:
+       free(p9dev);
 }