From: Rusty Russell <rusty-8n+1lVoiYb80n/F98K4Iww@public.gmane.org>
To: virtualization
<virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org>
Cc: Stephen Rothwell <sfr-3FnU+UHB4dNDw9hX6IcOSA@public.gmane.org>,
Xen Mailing List
<xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR@public.gmane.org>,
"jmk-zzFmDc4TPjtKvsKVC3L/VUEOCMrvLtNR@public.gmane.org"
<jmk-zzFmDc4TPjtKvsKVC3L/VUEOCMrvLtNR@public.gmane.org>,
kvm-devel
<kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org>,
Christian Borntraeger
<cborntra-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>,
Latchesar Ionkov <lionkov-YOWKrPYUwWM@public.gmane.org>,
Suzanne McIntosh
<skranjac-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org>,
Martin Schwidefsky
<schwidefsky-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org>
Subject: [PATCH] Lguest implemention of virtio draft III
Date: Sat, 16 Jun 2007 23:28:34 +1000 [thread overview]
Message-ID: <1182000514.6237.273.camel@localhost.localdomain> (raw)
In-Reply-To: <1181999920.6237.263.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
This is a bonus patch for those wondering how a virtio implementation
can look. I have two, this is the more efficient one (needs some
modification for inter-guest though: it assumes the other end does all
the accessing of our memory. It's currently tacked on to the existing
lguest I/O mechanism as a demonstration, rather than replacing it.
It shows that it's possible to implement virtio without internal
locking.
Userspace server-side code isn't included.
===
This allows zero-copy from guest <-> host. It uses a page of
descriptors, a page to say what descriptors to use, and a page to say
what's been used: one each set for inbufs and one for outbufs.
TODO:
1) More polishing
2) Get rid of old I/O
3) Inter-guest I/O implementation
Signed-off-by: Rusty Russell <rusty-8n+1lVoiYb80n/F98K4Iww@public.gmane.org>
---
drivers/lguest/Makefile | 2
drivers/lguest/hypercalls.c | 4
drivers/lguest/lguest_virtio.c | 511 +++++++++++++++++++++++++++++++++++++++
include/linux/lguest.h | 3
include/linux/lguest_launcher.h | 24 +
6 files changed, 948 insertions(+), 5 deletions(-)
--- a/drivers/lguest/Makefile
+++ b/drivers/lguest/Makefile
@@ -1,5 +1,5 @@
# Guest requires the paravirt_ops replacement and the bus driver.
-obj-$(CONFIG_LGUEST_GUEST) += lguest.o lguest_asm.o lguest_bus.o
+obj-$(CONFIG_LGUEST_GUEST) += lguest.o lguest_asm.o lguest_bus.o lguest_virtio.o
# Host requires the other files, which can be a module.
obj-$(CONFIG_LGUEST) += lg.o
===================================================================
--- a/drivers/lguest/hypercalls.c
+++ b/drivers/lguest/hypercalls.c
@@ -86,6 +86,10 @@ static void do_hcall(struct lguest *lg,
break;
case LHCALL_HALT:
lg->halted = 1;
+ break;
+ case LHCALL_NOTIFY:
+ lg->pending_key = regs->edx << PAGE_SHIFT;
+ lg->dma_is_pending = 1;
break;
default:
kill_guest(lg, "Bad hypercall %li\n", regs->eax);
===================================================================
--- /dev/null
+++ b/drivers/lguest/lguest_virtio.c
@@ -0,0 +1,511 @@
+/* Descriptor-based virtio backend using lguest. */
+
+/* FIXME: Put "running" in shared page so other side really doesn't
+ * send us interrupts. Then we would never need to "fail" restart.
+ * If there are more buffers when we set "running", simply ping other
+ * side. It would interrupt us back again.
+ */
+#define DEBUG
+#include <linux/lguest.h>
+#include <linux/lguest_bus.h>
+#include <linux/virtio.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+
+#define NUM_DESCS (PAGE_SIZE / sizeof(struct lguest_desc))
+
+#ifdef DEBUG
+/* For development, we want to crash whenever the other side is bad. */
+#define BAD_SIDE(lgv, fmt...) \
+ do { dev_err(lgv->vdev.dev, fmt); BUG(); } while(0)
+#define START_USE(di) \
+ do { if ((di)->in_use) panic("in_use = %i\n", (di)->in_use); (di)->in_use = __LINE__; mb(); } while(0)
+#define END_USE(di) \
+ do { BUG_ON(!(di)->in_use); (di)->in_use = 0; mb(); } while(0)
+#else
+#define BAD_SIDE(lgv, fmt...) \
+ do { dev_err(lgv->vdev.dev, fmt); (lgv)->broken = true; } while(0)
+#define START_USE(di)
+#define END_USE(di)
+#endif
+
+/* FIXME: make the device mem layout a struct, not a set of pointers */
+struct desc_info
+{
+ /* Page of descriptors. */
+ struct lguest_desc *desc;
+ /* How we tell other side what buffers are available. */
+ unsigned int *avail_idx;
+ unsigned int *available;
+ /* How other side tells us what's used. */
+ unsigned int *used_idx;
+ struct lguest_used *used;
+
+ /* Number of free buffers */
+ unsigned int num_free;
+ /* Head of free buffer list. */
+ unsigned int free_head;
+ /* Number we've added since last sync. */
+ unsigned int num_added;
+
+ /* Last used index we've seen. */
+ unsigned int last_used_idx;
+
+ /* Unless they told us to stop */
+ bool running;
+
+#ifdef DEBUG
+ /* They're supposed to lock for us. */
+ unsigned int in_use;
+#endif
+
+ /* Tokens for callbacks. */
+ void *data[NUM_DESCS];
+};
+
+/* FIXME: When doing this for real, vdev will go straight into lguest_device */
+struct lguest_virtio_device
+{
+ struct virtio_device vdev;
+ struct lguest_device *lg;
+ void *priv;
+
+ /* Other side has made a mess, don't try any more. */
+ bool broken;
+
+ struct desc_info in, out;
+};
+
+static inline struct lguest_virtio_device *
+vdev_to_lgv(struct virtio_device *vdev)
+{
+ return container_of(vdev, struct lguest_virtio_device, vdev);
+}
+
+static unsigned long add_buf(struct desc_info *di,
+ const struct scatterlist *sg,
+ unsigned int num,
+ void *data)
+{
+ unsigned int i, head, uninitialized_var(prev);
+
+ BUG_ON(data == NULL);
+ START_USE(di);
+
+ if (di->num_free < num) {
+ pr_debug("Can't add buf len %i - avail = %i\n", num,
+ di->num_free);
+ END_USE(di);
+ return -ENOSPC;
+ }
+
+ /* We're about to use some buffers from the free list. */
+ di->num_free -= num;
+
+ head = di->free_head;
+ for (i = di->free_head; num; i = di->desc[i].next, num--) {
+ di->desc[i].flags |= LGUEST_DESC_F_NEXT;
+ di->desc[i].pfn = page_to_pfn(sg[0].page);
+ di->desc[i].offset = sg[0].offset;
+ di->desc[i].len = sg[0].length;
+ prev = i;
+ sg++;
+ }
+ /* Last one doesn't continue. */
+ di->desc[prev].flags &= ~LGUEST_DESC_F_NEXT;
+
+ /* Update free pointer */
+ di->free_head = i;
+
+ di->data[head] = data;
+
+ /* Make sure it's all visible to other side before setting head. */
+ wmb();
+ di->desc[head].flags |= LGUEST_DESC_F_HEAD;
+
+ /* Put it in available array for advertising. */
+ di->available[(*di->avail_idx + di->num_added++) % NUM_DESCS] = head;
+
+ pr_debug("Added buffer head %i\n", head);
+ END_USE(di);
+ return head;
+}
+
+static unsigned long lguest_add_outbuf(struct virtio_device *vdev,
+ const struct scatterlist sg[],
+ unsigned int num,
+ void *data)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ BUG_ON(num > NUM_DESCS);
+ BUG_ON(num == 0);
+
+ return add_buf(&lgv->out, sg, num, data);
+}
+
+static unsigned long lguest_add_inbuf(struct virtio_device *vdev,
+ struct scatterlist sg[],
+ unsigned int num,
+ void *data)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ BUG_ON(num > NUM_DESCS);
+ BUG_ON(num == 0);
+
+ return add_buf(&lgv->in, sg, num, data);
+}
+
+static void lguest_sync(struct virtio_device *vdev, enum virtio_dir inout)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ if (inout & VIRTIO_IN)
+ START_USE(&lgv->in);
+ if (inout & VIRTIO_OUT)
+ START_USE(&lgv->out);
+ /* LGUEST_DESC_F_HEAD needs to be set before we say they're avail. */
+ wmb();
+
+ if (inout & VIRTIO_IN) {
+ *lgv->in.avail_idx += lgv->in.num_added;
+ lgv->in.num_added = 0;
+ }
+ if (inout & VIRTIO_OUT) {
+ *lgv->out.avail_idx += lgv->out.num_added;
+ lgv->out.num_added = 0;
+ }
+
+ /* Prod other side to tell it about changes. */
+ hcall(LHCALL_NOTIFY, lguest_devices[lgv->lg->index].pfn, 0, 0);
+ if (inout & VIRTIO_IN)
+ END_USE(&lgv->in);
+ if (inout & VIRTIO_OUT)
+ END_USE(&lgv->out);
+}
+
+static void detach_buf(struct desc_info *di, int id)
+{
+ unsigned int i;
+
+ BUG_ON(id >= NUM_DESCS);
+ BUG_ON(!(di->desc[id].flags & LGUEST_DESC_F_HEAD));
+
+ di->desc[id].flags &= ~LGUEST_DESC_F_HEAD;
+ /* Make sure other side has seen that it's detached. */
+ wmb();
+
+ /* Put back on free list: find end */
+ for (i = id; di->desc[i].flags&LGUEST_DESC_F_NEXT; i=di->desc[i].next)
+ di->num_free++;
+
+ di->desc[i].next = di->free_head;
+ di->free_head = id;
+ /* Plus final descriptor */
+ di->num_free++;
+}
+
+static void lguest_detach_outbuf(struct virtio_device *vdev, unsigned long id)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ START_USE(&lgv->out);
+ detach_buf(&lgv->out, id);
+ END_USE(&lgv->out);
+}
+
+static void lguest_detach_inbuf(struct virtio_device *vdev, unsigned long id)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ START_USE(&lgv->in);
+ detach_buf(&lgv->in, id);
+ END_USE(&lgv->in);
+}
+
+static bool more_used(struct desc_info *di)
+{
+ return di->last_used_idx != *di->used_idx;
+}
+
+static void *get_buf(struct desc_info *di, struct lguest_virtio_device *lgv,
+ unsigned int *len)
+{
+ unsigned int id;
+
+ START_USE(di);
+
+ if (!more_used(di)) {
+ END_USE(di);
+ return NULL;
+ }
+
+ /* Don't let them make us do infinite work. */
+ if (unlikely(*di->used_idx > di->last_used_idx + NUM_DESCS)) {
+ BAD_SIDE(lgv, "Too many descriptors");
+ return NULL;
+ }
+
+ id = di->used[di->last_used_idx%NUM_DESCS].id;
+ *len = di->used[di->last_used_idx%NUM_DESCS].len;
+
+ if (unlikely(id >= NUM_DESCS)) {
+ BAD_SIDE(lgv, "id %u out of range\n", id);
+ return NULL;
+ }
+ if (unlikely(!(di->desc[id].flags & LGUEST_DESC_F_HEAD))) {
+ BAD_SIDE(lgv, "id %u is not a head!\n", id);
+ return NULL;
+ }
+
+ detach_buf(di, id);
+ di->last_used_idx++;
+ BUG_ON(!di->data[id]);
+ END_USE(di);
+ return di->data[id];
+}
+
+static void *lguest_get_outbuf(struct virtio_device *vdev, unsigned int *len)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ return get_buf(&lgv->out, lgv, len);
+}
+
+static void *lguest_get_inbuf(struct virtio_device *vdev, unsigned int *len)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ return get_buf(&lgv->in, lgv, len);
+}
+
+static bool lguest_restart_in(struct virtio_device *vdev)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ START_USE(&lgv->in);
+ BUG_ON(lgv->in.running);
+
+ if (likely(!more_used(&lgv->in)) || unlikely(lgv->broken))
+ lgv->in.running = true;
+
+ END_USE(&lgv->in);
+ return lgv->in.running;
+}
+
+static bool lguest_restart_out(struct virtio_device *vdev)
+{
+ struct lguest_virtio_device *lgv = vdev_to_lgv(vdev);
+
+ START_USE(&lgv->out);
+ BUG_ON(lgv->out.running);
+
+ if (likely(!more_used(&lgv->in)) || unlikely(lgv->broken))
+ lgv->in.running = true;
+
+ END_USE(&lgv->out);
+ return lgv->in.running;
+}
+
+static irqreturn_t lguest_virtio_interrupt(int irq, void *_lgv)
+{
+ struct lguest_virtio_device *lgv = _lgv;
+
+ if (unlikely(lgv->broken))
+ return IRQ_HANDLED;
+
+ if (lgv->out.running && more_used(&lgv->out))
+ lgv->out.running = lgv->vdev.driver_ops->out(&lgv->vdev);
+
+ if (lgv->in.running && more_used(&lgv->in))
+ lgv->in.running = lgv->vdev.driver_ops->in(&lgv->vdev);
+
+ return IRQ_HANDLED;
+}
+
+static struct virtio_ops lguest_virtio_ops = {
+ .add_outbuf = lguest_add_outbuf,
+ .add_inbuf = lguest_add_inbuf,
+ .sync = lguest_sync,
+ .detach_outbuf = lguest_detach_outbuf,
+ .detach_inbuf = lguest_detach_inbuf,
+ .get_outbuf = lguest_get_outbuf,
+ .get_inbuf = lguest_get_inbuf,
+ .restart_in = lguest_restart_in,
+ .restart_out = lguest_restart_out,
+};
+
+static struct lguest_virtio_device *lg_new_virtio(struct lguest_device *lgdev)
+{
+ struct lguest_virtio_device *lgv;
+ void *mem;
+ unsigned int i;
+
+ lgv = kmalloc(sizeof(*lgv), GFP_KERNEL);
+ if (!lgv)
+ return NULL;
+
+ memset(lgv, 0, sizeof(*lgv));
+
+ lgdev->private = lgv;
+ lgv->lg = lgdev;
+
+ /* Device mem is input pages followed by output pages */
+ mem = lguest_map(lguest_devices[lgdev->index].pfn<<PAGE_SHIFT, 6);
+ if (!mem)
+ goto free_lgv;
+ lgv->in.desc = mem;
+ lgv->in.avail_idx = mem + PAGE_SIZE;
+ lgv->in.available = (void *)(lgv->in.avail_idx + 1);
+ lgv->in.used_idx = mem + PAGE_SIZE*2;
+ lgv->in.used = (void *)(lgv->in.used_idx + 1);
+ lgv->out.desc = mem + PAGE_SIZE*3;
+ lgv->out.avail_idx = mem + PAGE_SIZE*4;
+ lgv->out.available = (void *)(lgv->out.avail_idx + 1);
+ lgv->out.used_idx = mem + PAGE_SIZE*5;
+ lgv->out.used = (void *)(lgv->out.used_idx + 1);
+
+ lgv->in.last_used_idx = lgv->out.last_used_idx = 0;
+ lgv->in.num_added = lgv->out.num_added = 0;
+ lgv->in.running = lgv->out.running = true;
+
+ /* Put everything in free lists. */
+ lgv->in.num_free = lgv->out.num_free = NUM_DESCS;
+ for (i = 0; i < NUM_DESCS-1; i++) {
+ lgv->in.desc[i].next = i+1;
+ lgv->out.desc[i].next = i+1;
+ }
+
+ lgv->vdev.ops = &lguest_virtio_ops;
+ lgv->vdev.dev = &lgdev->dev;
+ lgv->broken = false;
+ return lgv;
+
+free_lgv:
+ kfree(lgv);
+ return NULL;;
+}
+
+static void lg_destroy_virtio(struct lguest_virtio_device *lgv)
+{
+ lguest_unmap(lgv->in.desc);
+ kfree(lgv);
+}
+
+/* It's nice to have the name for the interrupt, so we do this separately
+ * from lg_new_virtio(). */
+static int lg_setup_interrupt(struct lguest_virtio_device *lgv,
+ const char *name)
+{
+ int irqf;
+
+ if (lguest_devices[lgv->lg->index].features&LGUEST_DEVICE_F_RANDOMNESS)
+ irqf = IRQF_SAMPLE_RANDOM;
+ else
+ irqf = 0;
+
+ return request_irq(lgdev_irq(lgv->lg), lguest_virtio_interrupt, irqf,
+ name, lgv);
+}
+
+/* Example network driver code. */
+#include <linux/virtio_net.h>
+#include <linux/etherdevice.h>
+
+static int lguest_virtnet_probe(struct lguest_device *lgdev)
+{
+ struct lguest_virtio_device *lgv;
+ struct net_device *dev;
+ u8 mac[ETH_ALEN];
+ int err;
+
+ lgv = lg_new_virtio(lgdev);
+ if (!lgv)
+ return -ENOMEM;
+
+ random_ether_addr(mac);
+ lgv->priv = dev = virtnet_probe(&lgv->vdev, mac);
+ if (IS_ERR(lgv->priv)) {
+ err = PTR_ERR(lgv->priv);
+ goto destroy;
+ }
+ err = lg_setup_interrupt(lgv, dev->name);
+ if (err)
+ goto unprobe;
+ return 0;
+
+unprobe:
+ virtnet_remove(dev);
+destroy:
+ lg_destroy_virtio(lgv);
+ return err;
+}
+
+static struct lguest_driver lguest_virtnet_drv = {
+ .name = "lguestvirtnet",
+ .owner = THIS_MODULE,
+ .device_type = LGUEST_DEVICE_T_VIRTNET,
+ .probe = lguest_virtnet_probe,
+};
+
+static __init int lguest_virtnet_init(void)
+{
+ return register_lguest_driver(&lguest_virtnet_drv);
+}
+device_initcall(lguest_virtnet_init);
+
+/* Example block driver code. */
+#include <linux/virtio_blk.h>
+#include <linux/genhd.h>
+#include <linux/blkdev.h>
+static int lguest_virtblk_probe(struct lguest_device *lgdev)
+{
+ struct lguest_virtio_device *lgv;
+ struct gendisk *disk;
+ unsigned long sectors;
+ int err;
+
+ lgv = lg_new_virtio(lgdev);
+ if (!lgv)
+ return -ENOMEM;
+
+ /* Page is initially used to pass capacity. */
+ sectors = *(unsigned long *)lgv->in.desc;
+ *(unsigned long *)lgv->in.desc = 0;
+
+ lgv->priv = disk = virtblk_probe(&lgv->vdev);
+ if (IS_ERR(lgv->priv)) {
+ err = PTR_ERR(lgv->priv);
+ goto destroy;
+ }
+ set_capacity(disk, sectors);
+ blk_queue_max_hw_segments(disk->queue, NUM_DESCS-1);
+
+ err = lg_setup_interrupt(lgv, disk->disk_name);
+ if (err)
+ goto unprobe;
+ add_disk(disk);
+ return 0;
+
+unprobe:
+ virtblk_remove(disk);
+destroy:
+ lg_destroy_virtio(lgv);
+ return err;
+}
+
+static struct lguest_driver lguest_virtblk_drv = {
+ .name = "lguestvirtblk",
+ .owner = THIS_MODULE,
+ .device_type = LGUEST_DEVICE_T_VIRTBLK,
+ .probe = lguest_virtblk_probe,
+};
+
+static __init int lguest_virtblk_init(void)
+{
+ return register_lguest_driver(&lguest_virtblk_drv);
+}
+device_initcall(lguest_virtblk_init);
+
+MODULE_LICENSE("GPL");
===================================================================
--- a/include/linux/lguest.h
+++ b/include/linux/lguest.h
@@ -23,6 +23,9 @@
#define LHCALL_SET_PTE 14
#define LHCALL_SET_PMD 15
#define LHCALL_LOAD_TLS 16
+
+/* Experimental hcalls for new I/O */
+#define LHCALL_NOTIFY 100 /* pfn */
#define LG_CLOCK_MIN_DELTA 100UL
#define LG_CLOCK_MAX_DELTA ULONG_MAX
===================================================================
--- a/include/linux/lguest_launcher.h
+++ b/include/linux/lguest_launcher.h
@@ -44,6 +44,8 @@ struct lguest_device_desc {
#define LGUEST_DEVICE_T_CONSOLE 1
#define LGUEST_DEVICE_T_NET 2
#define LGUEST_DEVICE_T_BLOCK 3
+#define LGUEST_DEVICE_T_VIRTNET 8
+#define LGUEST_DEVICE_T_VIRTBLK 9
u16 features;
#define LGUEST_NET_F_NOCSUM 0x4000 /* Don't bother checksumming */
@@ -70,4 +72,26 @@ enum lguest_req
LHREQ_IRQ, /* + irq */
LHREQ_BREAK, /* + on/off flag (on blocks until someone does off) */
};
+
+/* This marks a buffer as being the start (and active) */
+#define LGUEST_DESC_F_HEAD 1
+/* This marks a buffer as continuing via the next field. */
+#define LGUEST_DESC_F_NEXT 2
+
+/* Virtio descriptors */
+struct lguest_desc
+{
+ unsigned long pfn;
+ unsigned long len;
+ u16 offset;
+ u16 flags;
+ /* We chain unused descriptors via this, too */
+ u32 next;
+};
+
+struct lguest_used
+{
+ unsigned int id;
+ unsigned int len;
+};
#endif /* _ASM_LGUEST_USER */
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
next prev parent reply other threads:[~2007-06-16 13:28 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-06-07 12:02 [PATCH RFC 0/3] Virtio draft II Rusty Russell
2007-06-07 12:04 ` [PATCH RFC 1/3] Virtio draft II: virtio.h Rusty Russell
[not found] ` <1181217867.14054.195.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-07 12:05 ` [PATCH RFC 2/3] Virtio draft II: example block driver Rusty Russell
[not found] ` <1181217920.14054.196.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-07 12:07 ` [PATCH RFC 3/3] Virtio draft II: example net driver Rusty Russell
2007-06-07 12:19 ` [PATCH RFC 2/3] Virtio draft II: example block driver Avi Kivity
[not found] ` <4667F7C0.3070604-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
2007-06-08 1:11 ` Rusty Russell
[not found] ` <1181217762.14054.192.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-16 13:12 ` [PATCH RFC 0/3] Virtio draft III Rusty Russell
[not found] ` <1181999552.6237.255.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-16 13:14 ` [PATCH RFC 1/3] Virtio draft III: virtio.h Rusty Russell
[not found] ` <1181999669.6237.257.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-16 13:17 ` [PATCH RFC 2/3] Virtio draft III: example net driver Rusty Russell
[not found] ` <1181999825.6237.260.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-16 13:18 ` [PATCH RFC 3/3] Virtio draft III: example block driver Rusty Russell
[not found] ` <1181999920.6237.263.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-16 13:28 ` Rusty Russell [this message]
[not found] ` <1182000514.6237.273.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-16 13:50 ` [PATCH] Lguest implemention of virtio draft III Arnd Bergmann
2007-06-17 14:25 ` [PATCH RFC 3/3] Virtio draft III: example block driver Avi Kivity
[not found] ` <46754451.2010305-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
2007-06-18 8:08 ` Rusty Russell
[not found] ` <1182154095.19064.24.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-18 9:09 ` Avi Kivity
[not found] ` <46764BB5.6070704-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
2007-06-19 6:27 ` Rusty Russell
[not found] ` <1182234466.19064.51.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-19 8:34 ` Avi Kivity
2007-06-25 15:26 ` [PATCH RFC 2/3] Virtio draft III: example net driver Brian King
[not found] ` <467FDEAD.4030204-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
2007-06-25 19:33 ` Christoph Hellwig
[not found] ` <20070625193304.GB25736-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org>
2007-06-25 21:54 ` Avi Kivity
[not found] ` <46803999.4040500-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
2007-06-25 22:13 ` Brian King
[not found] ` <46803E0E.7080103-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org>
2007-06-25 22:20 ` Avi Kivity
2007-06-28 11:20 ` Rusty Russell
[not found] ` <1183029641.12401.36.camel-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>
2007-06-28 15:55 ` Brian King
2007-06-17 14:14 ` [PATCH RFC 0/3] Virtio draft III Avi Kivity
[not found] ` <467541DF.5060907-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
2007-06-18 7:48 ` [Xen-devel] " Rusty Russell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1182000514.6237.273.camel@localhost.localdomain \
--to=rusty-8n+1lvoiyb80n/f98k4iww@public.gmane.org \
--cc=cborntra-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org \
--cc=jmk-zzFmDc4TPjtKvsKVC3L/VUEOCMrvLtNR@public.gmane.org \
--cc=kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org \
--cc=lionkov-YOWKrPYUwWM@public.gmane.org \
--cc=schwidefsky-tA70FqPdS9bQT0dZR+AlfA@public.gmane.org \
--cc=sfr-3FnU+UHB4dNDw9hX6IcOSA@public.gmane.org \
--cc=skranjac-r/Jw6+rmf7HQT0dZR+AlfA@public.gmane.org \
--cc=virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org \
--cc=xen-devel-GuqFBffKawuULHF6PoxzQEEOCMrvLtNR@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox