diff mbox

[RFC,v2,19/19] virtio: add a vbus transport

Message ID 20090409163221.32740.38373.stgit@dev.haskins.net (mailing list archive)
State Accepted
Headers show

Commit Message

Gregory Haskins April 9, 2009, 4:32 p.m. UTC
We add a new virtio transport for accessing backends located on vbus.  This
complements the existing transports for virtio-pci, virtio-s390, and
virtio-lguest that already exist.

Signed-off-by: Gregory Haskins <ghaskins@novell.com>
---

 drivers/virtio/Kconfig       |   15 +
 drivers/virtio/Makefile      |    1 
 drivers/virtio/virtio_vbus.c |  496 +++++++++++++++++++++++++++++++++
 include/linux/virtio_vbus.h  |  163 +++++++++++
 kernel/vbus/Kconfig          |    7 
 kernel/vbus/Makefile         |    3 
 kernel/vbus/virtio.c         |  628 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 1313 insertions(+), 0 deletions(-)
 create mode 100644 drivers/virtio/virtio_vbus.c
 create mode 100644 include/linux/virtio_vbus.h
 create mode 100644 kernel/vbus/virtio.c


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Anthony Liguori Aug. 9, 2009, 4:40 p.m. UTC | #1
Gregory Haskins wrote:
> We add a new virtio transport for accessing backends located on vbus.  This
> complements the existing transports for virtio-pci, virtio-s390, and
> virtio-lguest that already exist.
>
> Signed-off-by: Gregory Haskins <ghaskins@novell.com>

Very interesting...

I'm somewhat confused by what you're advocating vbus as.  I'm trying to 
figure out how we converge vbus and virtio and become one big happy 
family :-)

What parts of it do you think are better than virtio?  Should we forget 
about venet and just focus on virtio-net on top of virtio-vbus assuming 
that we can prove venet-tap/virtio-vbus/virtio-net is just as good as 
venet-tap/vbus/venet?

If we can prove that an in-kernel virtio-net 
backend/virtio-pci/virtio-net does just as well as 
venet-tap/virtio-vbus/virtio-net does that mean that vbus is no longer 
needed?

If you concede that the transport mechanisms can be identical, are you 
really advocating the discovering and configuration mechanisms in vbus?  
Is that what we should be focusing on? Do you care only about the host 
mechanisms or do you also require the guest infrastructure to be present?

I think two paravirtual I/O frameworks for KVM is a bad thing.  It 
duplicates a ton of code and will very likely lead to user unhappiness.

Regards,

Anthony Liguori
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Gregory Haskins Aug. 10, 2009, 3:40 p.m. UTC | #2
Anthony Liguori wrote:
> Gregory Haskins wrote:
>> We add a new virtio transport for accessing backends located on vbus. 
>> This
>> complements the existing transports for virtio-pci, virtio-s390, and
>> virtio-lguest that already exist.
>>
>> Signed-off-by: Gregory Haskins <ghaskins@novell.com>
> 
> Very interesting...
> 
> I'm somewhat confused by what you're advocating vbus as.  I'm trying to
> figure out how we converge vbus and virtio and become one big happy
> family :-)
> 
> What parts of it do you think are better than virtio?

I see them as distinctly different, and complimentary: virtio is
primarily a device-interface.  vbus is primarily a bus-model.

While its true that vbus has a rudimentary "device interface" (ala
dev->call(), dev->shm(), etc), it will typically be layered with a more
robust device model (such as virtqueue based virtio-net, or IOQ based
venet).

This is akin to raw PCI.  PCI has the basic mechanics (MMIO, PIO,
interrupts, DMA), but generally devices overlay higher layer constructs
(such as tx/rx rings for ethernet).  vbus is similar in that regard.

virtio, on the other hand, is a fully fleshed device model, but it needs
to be paired up with some other bus-model (such as qemu+pci via
virtio-pci, or linux+vbus via virtio-vbus) to be complete.


> Should we forget
> about venet and just focus on virtio-net on top of virtio-vbus assuming
> that we can prove venet-tap/virtio-vbus/virtio-net is just as good as
> venet-tap/vbus/venet?

I think going forward, I would love to see some community weight behind
virtio-vbus and the standardization of the virtio-X drivers (net, blk,
console, etc) as the default core IO device model for vbus.  If that
were to happen, I would not be inclined to create any new native vbus
drivers for where there is overlap with virtio (net, disk, etc).

I *will*, however, likely use the native vbus interfaces for some of the
device models in the pipeline (such as the real-time controller, and
OFED) because there isn't a virtio equivalent for that, and the vbus
device model was written with them in mind.

And regardless of the final outcome of virtio-vbus as a viable virtio
transport model, I will probably continue to support the native vbus
"venet" protocol for AlacrityVM indefinitely only because I have people
that are using it today (AlacrityVM being one example, but others are in
the works), it is simple, and it works extremely well.

> 
> If we can prove that an in-kernel virtio-net
> backend/virtio-pci/virtio-net does just as well as
> venet-tap/virtio-vbus/virtio-net does that mean that vbus is no longer
> needed?

vbus is primarily about the bus model and resource-containers.  It is
therefore so much more than just 802.x networking and/or KVM.  So no,
obtaining equal network performance with virtio-net sans vbus does not
invalidate vbus as a concept.  It would perhaps purely be disincentive
for anyone to chose venet over virtio-net within a KVM guest who doesn't
have a vested interest in the venet ABI.


> 
> If you concede that the transport mechanisms can be identical, are you
> really advocating the discovering and configuration mechanisms in vbus?

The problem is that they are not truly identical, and they cannot easily
be made identical.

One of the design goals is to create a re-useable, in-kernel device
model with a thin-shim transport between the guest and the bus.  The
transport (called a "vbus-connector") in question must convey a very
small verb namespace (devadd/drop, devopen/close, shmsignal, etc) to use
this bus.  So you have a driver for this transport (e.g. vbus-pcibridge)
but the bus-model itself, device-models, and guest drivers are all reusable.

Another design goal is to create a high-performance, low-latency,
software-to-software optimized IO subsystem.  PCI was designed for
software-to-hardware, so it has assumptions about that environment that
simply do not apply in software-to-software.  Most of these limitations
can be worked-around with KVM surgery and/or creative guest development,
but they are (at best) awkward to express in PCI.

For example, creating a high-performance, synchronous, in-kernel "call"
in PCI is fairly complicated, awkward, and requires a bit of per-device
heap to pull off.  Yet, I need this functionality for things like RT
support (scheduler state changes, etc).

As another example, the PCI model is somewhat resource rigid and
therefore awkward for creating dynamic software objects in response to
guest actions on demand, such as IPC sockets, due to the way MSI works.

As a third example, the PCI device model makes assumptions about
signal-path delivery (MSI to APIC) which may or may not be the optimal
path (e.g. see my priority+aggregation thread with Arnd).

I am open to suggestions on ways to write the "connector" code
differently.  I am sure that some things could be addressed at a
different layer (such as the conversation with Arnd regarding the PV
interrupt controller).

I think, however, that the motivations to PCI'ize the x86-KVM world are
driven more by inertia (its already in qemu and many guests know it)
than by intentional selection (its model is the best to express the
needs of virt).  Therefore, I am trying to put a stake in the ground to
redirect that inertia towards a path that I believe is more amenable to
virt in the long term.  Consider it a long-term investment in the platform.

My approach to date is more akin to how something like USB was
introduced.  The USB designers were not trying to artificially constrain
their design so that it looked like PCI just to avoid needing an OS
driver.  They (presumably) designed a bus that best achieved their
goals, and then people wrote adaption layers (ala PCI-to-USB bridges) to
support them on the OS's/arches that cared.  Eventually most did.

I admit that, as with any new bus (e.g. USB), there will undoubtedly be
some lumps during the initial roll-out phase since it is not yet
universally supported.  But the long term vision is that (assuming it
succeeds as a project) that eventually vbus would be ubiquitous (ala
USB) and it makes for a better bus-model choice for things like
linux-based hypervisors (pci-based kvm (x86/ppc), s390-kvm, lguest, UML,
openvz, and even something like Xen-Dom0, etc), as well as applications
and even some specialized physical systems (Blade systems, clusters, etc).


> Is that what we should be focusing on? Do you care only about the host
> mechanisms or do you also require the guest infrastructure to be present?

I see it as a tightly integrated portion of the stack that sits right
below the virtio layer.

> 
> I think two paravirtual I/O frameworks for KVM is a bad thing.

So hopefully after reading my reply above you can see that I don't think
this is indeed what is being proposed.

> It duplicates a ton of code and will very likely lead to user unhappiness.

I totally agree and I hope we can work together to find a solution that
makes the most sense and eliminates as much duplicate effort as possible.

Kind Regards,
-Greg
diff mbox

Patch

diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 3dd6294..e8562ee 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -23,6 +23,21 @@  config VIRTIO_PCI
 
 	  If unsure, say M.
 
+config VIRTIO_VBUS
+	tristate "VBUS driver for virtio devices (EXPERIMENTAL)"
+	depends on VBUS_DRIVERS && EXPERIMENTAL
+	select VIRTIO
+	select VIRTIO_RING
+	---help---
+	  This drivers provides support for virtio based paravirtual device
+	  drivers over VBUS.  This requires that your VMM has appropriate VBUS
+	  virtio backends.
+
+	  Currently, the ABI is not considered stable so there is no guarantee
+	  that this version of the driver will work with your VMM.
+
+	  If unsure, say M.
+
 config VIRTIO_BALLOON
 	tristate "Virtio balloon driver (EXPERIMENTAL)"
 	select VIRTIO
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 6738c44..0342e42 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -1,4 +1,5 @@ 
 obj-$(CONFIG_VIRTIO) += virtio.o
 obj-$(CONFIG_VIRTIO_RING) += virtio_ring.o
 obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
+obj-$(CONFIG_VIRTIO_VBUS) += virtio_vbus.o
 obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
diff --git a/drivers/virtio/virtio_vbus.c b/drivers/virtio/virtio_vbus.c
new file mode 100644
index 0000000..ebefcf2
--- /dev/null
+++ b/drivers/virtio/virtio_vbus.c
@@ -0,0 +1,496 @@ 
+/*
+ * Virtio VBUS driver
+ *
+ * This module allows virtio devices to be used over a virtual-bus device.
+ *
+ * Copyright: Novell, 2009
+ *
+ * Authors:
+ *  Gregory Haskins <ghaskins@novell.com>
+ *
+ * Derived from virtio-pci, written by
+ *  Anthony Liguori  <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_vbus.h>
+#include <linux/vbus_driver.h>
+#include <linux/spinlock.h>
+
+MODULE_AUTHOR("Gregory Haskins <ghaskins@novell.com>");
+MODULE_DESCRIPTION("virtio-vbus");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1");
+
+struct virtio_vbus_priv {
+	struct virtio_device                   virtio_dev;
+	struct vbus_device_proxy              *vbus_dev;
+	struct {
+		struct virtio_vbus_shm        *shm;
+		struct shm_signal             *signal;
+		struct shm_signal_notifier     notifier;
+	} config;
+};
+
+struct vbus_virtqueue {
+	struct virtqueue           *vq;
+	u64                         index;
+	int                         num;
+	struct virtio_vbus_shm     *shm;
+	size_t                      size;
+	struct shm_signal          *signal;
+	struct shm_signal_notifier  notifier;
+};
+
+static struct virtio_vbus_priv *
+virtio_to_priv(struct virtio_device *virtio_dev)
+{
+	return container_of(virtio_dev, struct virtio_vbus_priv, virtio_dev);
+}
+
+static int
+devcall(struct virtio_vbus_priv *priv, u32 func, void *data, size_t len)
+{
+	struct vbus_device_proxy *dev = priv->vbus_dev;
+
+	return dev->ops->call(dev, func, data, len, 0);
+}
+
+/*
+ * This is called whenever the host signals our config-space shm
+ */
+static void
+config_isr(struct shm_signal_notifier *notifier)
+{
+	struct virtio_vbus_priv *priv = container_of(notifier,
+						     struct virtio_vbus_priv,
+						     config.notifier);
+	struct virtio_driver *drv = container_of(priv->virtio_dev.dev.driver,
+						 struct virtio_driver, driver);
+
+	if (drv && drv->config_changed)
+		drv->config_changed(&priv->virtio_dev);
+}
+
+/*
+ * ------------------
+ * virtio config ops
+ * ------------------
+ */
+
+static u32
+_virtio_get_features(struct virtio_device *dev)
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(dev);
+	u32 features;
+	int ret;
+
+	ret = devcall(priv, VIRTIO_VBUS_FUNC_GET_FEATURES,
+		      &features, sizeof(features));
+	BUG_ON(ret < 0);
+
+	/*
+	 * When someone needs more than 32 feature bits, we'll need to
+	 * steal a bit to indicate that the rest are somewhere else.
+	 */
+	return features;
+}
+
+static void
+_virtio_finalize_features(struct virtio_device *dev)
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(dev);
+	int ret;
+
+	/* Give virtio_ring a chance to accept features. */
+	vring_transport_features(dev);
+
+	/* We only support 32 feature bits. */
+	BUILD_BUG_ON(ARRAY_SIZE(dev->features) != 1);
+
+	ret = devcall(priv, VIRTIO_VBUS_FUNC_FINALIZE_FEATURES,
+		      &dev->features[0], sizeof(dev->features[0]));
+	BUG_ON(ret < 0);
+}
+
+static void
+_virtio_get(struct virtio_device *vdev, unsigned offset,
+	    void *buf, unsigned len)
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(vdev);
+
+	BUG_ON((offset + len) > VIRTIO_VBUS_CONFIGSPACE_LEN);
+	memcpy(buf, &priv->config.shm->data[offset], len);
+}
+
+static void
+_virtio_set(struct virtio_device *vdev, unsigned offset,
+	    const void *buf, unsigned len)
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(vdev);
+	int ret;
+
+	BUG_ON((offset + len) > VIRTIO_VBUS_CONFIGSPACE_LEN);
+	memcpy(&priv->config.shm->data[offset], buf, len);
+
+	ret = shm_signal_inject(priv->config.signal, 0);
+	BUG_ON(ret < 0);
+}
+
+static u8
+_virtio_get_status(struct virtio_device *vdev)
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(vdev);
+	u8 data;
+	int ret;
+
+	ret = devcall(priv, VIRTIO_VBUS_FUNC_GET_STATUS, &data, sizeof(data));
+	BUG_ON(ret < 0);
+
+	return data;
+}
+
+static void
+_virtio_set_status(struct virtio_device *vdev, u8 status)
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(vdev);
+	int ret;
+
+	/* We should never be setting status to 0. */
+	BUG_ON(status == 0);
+
+	ret = devcall(priv, VIRTIO_VBUS_FUNC_SET_STATUS, &status,
+		      sizeof(status));
+	BUG_ON(ret < 0);
+}
+
+static void
+_virtio_reset(struct virtio_device *vdev)
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(vdev);
+	int ret;
+
+	ret = devcall(priv, VIRTIO_VBUS_FUNC_RESET, NULL, 0);
+	BUG_ON(ret < 0);
+}
+
+/*
+ * ------------------
+ * virtqueue ops
+ * ------------------
+ */
+
+static int
+_vq_getlen(struct virtio_vbus_priv *priv, int index)
+{
+	struct virtio_vbus_queryqueue query = {
+		.index = index,
+	};
+	int ret;
+
+	ret = devcall(priv, VIRTIO_VBUS_FUNC_QUERY_QUEUE,
+		      &query, sizeof(query));
+	if (ret < 0)
+		return ret;
+
+	return query.num;
+}
+
+static void
+_vq_kick(struct virtqueue *vq)
+{
+	struct vbus_virtqueue *_vq = vq->priv;
+	int ret;
+
+	ret = shm_signal_inject(_vq->signal, 0);
+	BUG_ON(ret < 0);
+}
+
+/*
+ * This is called whenever the host signals our virtqueue
+ */
+static void
+_vq_isr(struct shm_signal_notifier *notifier)
+{
+	struct vbus_virtqueue *_vq = container_of(notifier,
+						  struct vbus_virtqueue,
+						  notifier);
+	vring_interrupt(0, _vq->vq);
+}
+
+static struct virtqueue *
+_virtio_find_vq(struct virtio_device *vdev, unsigned index,
+	 void (*callback)(struct virtqueue *vq))
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(vdev);
+	struct vbus_device_proxy *dev = priv->vbus_dev;
+	struct vbus_virtqueue *_vq;
+	struct virtqueue *vq;
+	unsigned long ringsize;
+	int num;
+	int ret;
+
+	num = _vq_getlen(priv, index);
+	if (num < 0)
+		return ERR_PTR(num);
+
+	_vq = kmalloc(sizeof(struct vbus_virtqueue), GFP_KERNEL);
+	if (!_vq)
+		return ERR_PTR(-ENOMEM);
+
+	ringsize = vring_size(num, PAGE_SIZE);
+
+	_vq->index = index;
+	_vq->num   = num;
+	_vq->size  = PAGE_ALIGN(sizeof(struct virtio_vbus_shm) + ringsize - 1);
+
+	_vq->shm = alloc_pages_exact(_vq->size, GFP_KERNEL|__GFP_ZERO);
+	if (!_vq->shm) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* initialize the shm with a ring */
+	vq = vring_new_virtqueue(_vq->num, PAGE_SIZE, vdev,
+				 &_vq->shm->data[0],
+				 _vq_kick,
+				 callback);
+	if (!vq) {
+		ret = -ENOMEM;
+		goto out_free;
+	}
+
+	/* register the shm with an id of the vq index + RING_OFFSET */
+	ret = dev->ops->shm(dev, index + VIRTIO_VBUS_RING_OFFSET, 0,
+			    _vq->shm, _vq->size,
+			    &_vq->shm->signal, &_vq->signal, 0);
+	if (ret < 0)
+		goto out_free;
+
+	_vq->notifier.signal = &_vq_isr;
+	_vq->signal->notifier = &_vq->notifier;
+
+	shm_signal_enable(_vq->signal, 0);
+
+	vq->priv = _vq;
+	_vq->vq = vq;
+
+	return vq;
+
+out_free:
+	free_pages_exact(_vq->shm, _vq->size);
+out:
+	if (_vq && _vq->signal)
+		shm_signal_put(_vq->signal);
+	kfree(_vq);
+	return ERR_PTR(ret);
+}
+
+/* the config->del_vq() implementation */
+static void
+_virtio_del_vq(struct virtqueue *vq)
+{
+	struct virtio_vbus_priv *priv = virtio_to_priv(vq->vdev);
+	struct vbus_virtqueue *_vq = vq->priv;
+
+	devcall(priv, VIRTIO_VBUS_FUNC_DEL_QUEUE,
+		&_vq->index, sizeof(_vq->index));
+
+	vring_del_virtqueue(vq);
+
+	shm_signal_put(_vq->signal);
+	free_pages_exact(_vq->shm, _vq->size);
+	kfree(_vq);
+}
+
+/*
+ * ------------------
+ * general setup
+ * ------------------
+ */
+
+static struct virtio_config_ops virtio_vbus_config_ops = {
+	.get		   = _virtio_get,
+	.set		   = _virtio_set,
+	.get_status	   = _virtio_get_status,
+	.set_status	   = _virtio_set_status,
+	.reset		   = _virtio_reset,
+	.find_vq	   = _virtio_find_vq,
+	.del_vq		   = _virtio_del_vq,
+	.get_features	   = _virtio_get_features,
+	.finalize_features = _virtio_finalize_features,
+};
+
+/*
+ * Negotiate vbus transport features.  This is not to be confused with the
+ * higher-level function FUNC_GET/FINALIZE_FEATURES, which is specifically
+ * for the virtio transport
+ */
+static void
+virtio_vbus_negcap(struct virtio_vbus_priv *priv)
+{
+	u64 features = 0; /* We do not have any advanced features to enable */
+	int ret;
+
+	ret = devcall(priv, VIRTIO_VBUS_FUNC_NEG_CAP,
+		      &features, sizeof(features));
+	BUG_ON(ret < 0);
+}
+
+static void
+virtio_vbus_getid(struct virtio_vbus_priv *priv)
+{
+	struct virtio_vbus_id id;
+	int ret;
+
+	ret = devcall(priv, VIRTIO_VBUS_FUNC_GET_ID, &id, sizeof(id));
+	BUG_ON(ret < 0);
+
+	priv->virtio_dev.id.vendor = id.vendor;
+	priv->virtio_dev.id.device = id.device;
+}
+
+static int
+virtio_vbus_initconfig(struct virtio_vbus_priv *priv)
+{
+	struct vbus_device_proxy *vdev = priv->vbus_dev;
+	size_t len;
+	int ret;
+
+	len = sizeof(struct virtio_vbus_shm) + VIRTIO_VBUS_CONFIGSPACE_LEN - 1;
+
+	priv->config.shm = kzalloc(len, GFP_KERNEL);
+	if (!priv->config.shm)
+		return -ENOMEM;
+
+	ret = vdev->ops->shm(vdev, 0, 0,
+			     &priv->config.shm, len,
+			     &priv->config.shm->signal, &priv->config.signal,
+			     0);
+	BUG_ON(ret < 0);
+
+	priv->config.notifier.signal = &config_isr;
+	priv->config.signal->notifier = &priv->config.notifier;
+
+	shm_signal_enable(priv->config.signal, 0);
+
+	return 0;
+}
+
+/* the VBUS probing function */
+static int
+virtio_vbus_probe(struct vbus_device_proxy *vdev)
+{
+	struct virtio_vbus_priv *priv;
+	int ret;
+
+	printk(KERN_INFO "VIRTIO-VBUS: Found new device at %lld\n", vdev->id);
+
+	ret = vdev->ops->open(vdev, VIRTIO_VBUS_ABI_VERSION, 0);
+	if (ret < 0) {
+		printk(KERN_ERR "virtio_vbus: ABI version %d failed with: %d\n",
+		       VIRTIO_VBUS_ABI_VERSION, ret);
+		return ret;
+	}
+
+	priv = kzalloc(sizeof(struct virtio_vbus_priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->virtio_dev.config = &virtio_vbus_config_ops;
+	priv->vbus_dev = vdev;
+
+	/*
+	 * Negotiate for any vbus specific features
+	 */
+	virtio_vbus_negcap(priv);
+
+	/*
+	 * This probe occurs for any "virtio" device on the vbus, so we need
+	 * to hypercall the host to figure out what specific PCI-ID type
+	 * device this is
+	 */
+	virtio_vbus_getid(priv);
+
+	/*
+	 * Map our config-space to the device, and establish a signal-path
+	 * for config-space updates
+	 */
+	virtio_vbus_initconfig(priv);
+
+	/* finally register the virtio device */
+	ret = register_virtio_device(&priv->virtio_dev);
+	if (ret)
+		goto out;
+
+	vdev->priv = priv;
+
+	return 0;
+
+out:
+	kfree(priv);
+	return ret;
+}
+
+#ifdef NOTYET
+/* FIXME: wire this up */
+static void
+virtio_vbus_release(struct virtio_vbus_priv *priv)
+{
+	shm_signal_put(priv->config.signal);
+	kfree(priv->config.shm);
+	kfree(priv);
+}
+
+#endif
+
+static int
+virtio_vbus_remove(struct vbus_device_proxy *vdev)
+{
+	struct virtio_vbus_priv *priv = vdev->priv;
+
+	unregister_virtio_device(&priv->virtio_dev);
+
+	return 0;
+}
+
+/*
+ * Finally, the module stuff
+ */
+
+static struct vbus_driver_ops virtio_vbus_driver_ops = {
+	.probe  = virtio_vbus_probe,
+	.remove = virtio_vbus_remove,
+};
+
+static struct vbus_driver virtio_vbus_driver = {
+	.type   = "virtio",
+	.owner  = THIS_MODULE,
+	.ops    = &virtio_vbus_driver_ops,
+};
+
+static __init int
+virtio_vbus_init_module(void)
+{
+	printk(KERN_INFO "Virtio-VBUS: Copyright (C) 2009 Novell, Gregory Haskins\n");
+	return vbus_driver_register(&virtio_vbus_driver);
+}
+
+static __exit void
+virtio_vbus_cleanup(void)
+{
+	vbus_driver_unregister(&virtio_vbus_driver);
+}
+
+module_init(virtio_vbus_init_module);
+module_exit(virtio_vbus_cleanup);
+
diff --git a/include/linux/virtio_vbus.h b/include/linux/virtio_vbus.h
new file mode 100644
index 0000000..05791bf
--- /dev/null
+++ b/include/linux/virtio_vbus.h
@@ -0,0 +1,163 @@ 
+/*
+ * Copyright 2009 Novell.  All Rights Reserved.
+ *
+ * Virtio VBUS driver
+ *
+ * This module allows virtio devices to be used over a VBUS interface
+ *
+ * Author:
+ *      Gregory Haskins <ghaskins@novell.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _LINUX_VIRTIO_VBUS_H
+#define _LINUX_VIRTIO_VBUS_H
+
+#include <linux/shm_signal.h>
+
+#define VIRTIO_VBUS_ABI_VERSION 1
+
+enum {
+	VIRTIO_VBUS_FUNC_NEG_CAP,
+	VIRTIO_VBUS_FUNC_GET_ID,
+	VIRTIO_VBUS_FUNC_GET_FEATURES,
+	VIRTIO_VBUS_FUNC_FINALIZE_FEATURES,
+	VIRTIO_VBUS_FUNC_GET_STATUS,
+	VIRTIO_VBUS_FUNC_SET_STATUS,
+	VIRTIO_VBUS_FUNC_RESET,
+	VIRTIO_VBUS_FUNC_QUERY_QUEUE,
+	VIRTIO_VBUS_FUNC_DEL_QUEUE,
+};
+
+struct virtio_vbus_id {
+	u16 vendor;
+	u16 device;
+};
+
+struct virtio_vbus_queryqueue {
+	u64 index;  /* in: queue index */
+	u32 num;    /* out: number of entries */
+	u32 pad[0];
+};
+
+#define VIRTIO_VBUS_CONFIGSPACE_LEN 1024
+#define VIRTIO_VBUS_RING_OFFSET     10000 /* shm-index where rings start */
+
+struct virtio_vbus_shm {
+	struct shm_signal_desc signal;
+	char                   data[1];
+};
+
+/*
+ * --------------------------------------------------
+ * Backend support - These components are only needed
+ * for interfacing a virtio-backend to the vbus-backend
+ * --------------------------------------------------
+ */
+
+#include <linux/vbus_device.h>
+
+struct virtio_device_interface;
+struct virtio_connection;
+
+struct virtio_queue_def {
+	int index;
+	int entries;
+};
+
+/*
+ * ----------------------
+ * interface
+ * ----------------------
+ */
+
+struct virtio_device_interface_ops {
+	int (*open)(struct virtio_device_interface *intf,
+		    struct vbus_memctx *ctx,
+		    struct virtio_connection **conn);
+	void (*release)(struct virtio_device_interface *intf);
+};
+
+struct virtio_device_interface {
+	struct virtio_vbus_id id;
+	struct virtio_device_interface_ops *ops;
+	struct virtio_queue_def *queues;
+	struct vbus_device_interface *parent;
+};
+
+/**
+ * virtio_device_interface_register() - register an interface with a bus
+ * @dev:        The device context of the caller
+ * @vbus:       The bus context to register with
+ * @intf:       The interface context to register
+ *
+ * This function is invoked (usually in the context of a device::bus_connect()
+ * callback) to register a interface on a bus.  We make this an explicit
+ * operation instead of implicit on the bus_connect() to facilitate devices
+ * that may present multiple interfaces to a bus.  In those cases, a device
+ * may invoke this function multiple times (one per supported interface).
+ *
+ * Returns: success = 0, <0 = ERRNO
+ *
+ **/
+int virtio_device_interface_register(struct vbus_device *dev,
+				   struct vbus *vbus,
+				   struct virtio_device_interface *intf);
+
+/**
+ * virtio_device_interface_unregister() - unregister an interface with a bus
+ * @intf:       The interface context to unregister
+ *
+ * This function is the converse of interface_register.  It is typically
+ * invoked in the context of a device::bus_disconnect().
+ *
+ * Returns: success = 0, <0 = ERRNO
+ *
+ **/
+int virtio_device_interface_unregister(struct virtio_device_interface *intf);
+
+/*
+ * ----------------------
+ * connection
+ * ----------------------
+ */
+struct virtqueue;
+
+struct virtio_connection_ops {
+	void (*config_changed)(struct virtio_connection *vconn);
+	u8 (*get_status)(struct virtio_connection *vconn);
+	void (*set_status)(struct virtio_connection *vconn, u8 status);
+	void (*reset)(struct virtio_connection *vconn);
+	u32 (*get_features)(struct virtio_connection *vconn);
+	void (*finalize_features)(struct virtio_connection *vconn);
+	void (*add_vq)(struct virtio_connection *vconn, int index,
+		       struct virtqueue *vq);
+	void (*del_vq)(struct virtio_connection *vconn, int index);
+	void (*notify_vq)(struct virtio_connection *vconn, int index);
+	void (*release)(struct virtio_connection *conn);
+};
+
+struct virtio_connection {
+	struct virtio_connection_ops *ops;
+	struct vbus_connection *parent;
+};
+
+int virtio_connection_config_get(struct virtio_connection *vconn,
+				 int offset, void *buf, size_t len);
+
+int virtio_connection_config_set(struct virtio_connection *vconn,
+				 int offset, void *buf, size_t len);
+
+#endif /* _LINUX_VIRTIO_VBUS_H */
diff --git a/kernel/vbus/Kconfig b/kernel/vbus/Kconfig
index b894dd1..5eeced2 100644
--- a/kernel/vbus/Kconfig
+++ b/kernel/vbus/Kconfig
@@ -14,6 +14,13 @@  config VBUS
 
 	If unsure, say N
 
+config VBUS_VIRTIO_BACKEND
+       tristate "Virtio VBUS Backend"
+       depends on VBUS
+       default n
+       help
+        Provides backend support for virtio devices over vbus
+
 config VBUS_DEVICES
        bool "Virtual-Bus Devices"
        depends on VBUS
diff --git a/kernel/vbus/Makefile b/kernel/vbus/Makefile
index 61d0371..c2bd140 100644
--- a/kernel/vbus/Makefile
+++ b/kernel/vbus/Makefile
@@ -1,6 +1,9 @@ 
 obj-$(CONFIG_VBUS) += core.o devclass.o config.o attribute.o map.o client.o
 obj-$(CONFIG_VBUS) += shm-ioq.o
 
+virtio-backend-objs += virtio.o
+obj-$(CONFIG_VBUS_VIRTIO_BACKEND) += virtio-backend.o
+
 vbus-proxy-objs += proxy.o
 obj-$(CONFIG_VBUS_DRIVERS) += vbus-proxy.o
 
diff --git a/kernel/vbus/virtio.c b/kernel/vbus/virtio.c
new file mode 100644
index 0000000..dac5cd4
--- /dev/null
+++ b/kernel/vbus/virtio.c
@@ -0,0 +1,628 @@ 
+/*
+ * Copyright 2009 Novell.  All Rights Reserved.
+ *
+ * Author:
+ *      Gregory Haskins <ghaskins@novell.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ring.h>
+#include <linux/virtio_vbus.h>
+
+MODULE_AUTHOR("Gregory Haskins");
+MODULE_LICENSE("GPL");
+
+#undef PDEBUG
+#ifdef VENETTAP_DEBUG
+#  define PDEBUG(fmt, args...) printk(KERN_DEBUG "virtio-vbus: " fmt, ## args)
+#else
+#  define PDEBUG(fmt, args...)
+#endif
+
+struct _virtio_device_interface {
+	struct virtio_device_interface *vintf;
+	struct vbus_device_interface    intf;
+};
+
+struct _virtio_connection {
+	struct _virtio_device_interface *_vintf;
+	struct virtio_connection        *vconn;
+	struct vbus_connection           conn;
+	struct vbus_memctx              *ctx;
+	struct list_head                 queues;
+
+	struct {
+		struct vbus_shm            *shm;
+		struct shm_signal          *signal;
+		struct shm_signal_notifier  notifier;
+	} config;
+
+	int running:1;
+};
+
+struct _virtio_queue {
+	int                         index;
+	int                         num;
+	struct _virtio_connection  *_vconn;
+	struct virtqueue           *vq;
+
+	struct vbus_shm            *shm;
+	struct shm_signal          *signal;
+	struct shm_signal_notifier  notifier;
+
+	struct list_head            node;
+};
+
+static struct _virtio_device_interface *
+to_vintf(struct vbus_device_interface *intf)
+{
+	return container_of(intf, struct _virtio_device_interface, intf);
+}
+
+static struct _virtio_connection *
+to_vconn(struct vbus_connection *conn)
+{
+	return container_of(conn, struct _virtio_connection, conn);
+}
+
+int virtio_connection_config_get(struct virtio_connection *vconn,
+				 int offset, void *buf, size_t len)
+{
+	struct _virtio_connection *_vconn = to_vconn(vconn->parent);
+	char *data;
+
+	if (!_vconn->config.shm)
+		return -EINVAL;
+
+	if (offset + len > _vconn->config.shm->len)
+		return -EINVAL;
+
+	data = _vconn->config.shm->ptr;
+
+	memcpy(buf, &data[offset], len);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(virtio_connection_config_get);
+
+int virtio_connection_config_set(struct virtio_connection *vconn,
+				 int offset, void *buf, size_t len)
+{
+	struct _virtio_connection *_vconn = to_vconn(vconn->parent);
+	char *data;
+
+	if (!_vconn->config.shm)
+		return -EINVAL;
+
+	if (offset + len > _vconn->config.shm->len)
+		return -EINVAL;
+
+	data = _vconn->config.shm->ptr;
+
+	memcpy(&data[offset], buf, len);
+
+	if (_vconn->config.signal)
+		shm_signal_inject(_vconn->config.signal, 0);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(virtio_connection_config_set);
+
+/*
+ * Negotiate Capabilities - This function is provided so that the
+ * interface may be extended without breaking ABI compatability
+ *
+ * The caller is expected to send down any capabilities they would like
+ * to enable, and the device will OR them with capabilities that it
+ * supports.  This value is then returned so that both sides may
+ * ascertain the lowest-common-denominator of features to enable
+ */
+static int
+_virtio_connection_negcap(struct _virtio_connection *_vconn,
+			  void *data, unsigned long len)
+{
+	struct vbus_memctx *ctx = _vconn->ctx;
+	u64 features;
+	int ret;
+
+	if (len != sizeof(features))
+		return -EINVAL;
+
+	if (_vconn->running)
+		return -EINVAL;
+
+#ifdef NOTYET
+	ret = ctx->ops->copy_from(ctx, &features, data, sizeof(features));
+	if (ret)
+		return -EFAULT;
+#endif
+
+	/*
+	 * right now we dont support any advanced features, so just clear all
+	 * bits
+	 */
+	features = 0;
+
+	ret = ctx->ops->copy_to(ctx, data, &features, sizeof(features));
+	if (ret)
+		return -EFAULT;
+
+	return 0;
+}
+
+static int
+_virtio_connection_getid(struct _virtio_connection *_vconn,
+			 void *data, unsigned long len)
+{
+	struct vbus_memctx *ctx = _vconn->ctx;
+	struct virtio_vbus_id *id = &_vconn->_vintf->vintf->id;
+	int ret;
+
+	if (len != sizeof(*id))
+		return -EINVAL;
+
+	ret = ctx->ops->copy_to(ctx, data, id, sizeof(*id));
+	if (ret)
+		return -EFAULT;
+
+	return 0;
+}
+
+static int
+_virtio_connection_getstatus(struct _virtio_connection *_vconn,
+			     void *data, unsigned long len)
+{
+	struct virtio_connection *vconn = _vconn->vconn;
+	struct vbus_memctx *ctx = _vconn->ctx;
+	u8 val = 0;
+	int ret;
+
+	if (len != sizeof(val))
+		return -EINVAL;
+
+	if (vconn->ops->get_status)
+		val = vconn->ops->get_status(vconn);
+
+	ret = ctx->ops->copy_to(ctx, data, &val, sizeof(val));
+	if (ret)
+		return -EFAULT;
+
+	return 0;
+}
+
+static int
+_virtio_connection_setstatus(struct _virtio_connection *_vconn,
+			     void *data, unsigned long len)
+{
+	struct virtio_connection *vconn = _vconn->vconn;
+	struct vbus_memctx *ctx = _vconn->ctx;
+	u8 val;
+	int ret;
+
+	if (len != sizeof(val))
+		return -EINVAL;
+
+	if (!vconn->ops->set_status)
+		return 0;
+
+	ret = ctx->ops->copy_from(ctx, &val, data, sizeof(val));
+	if (ret)
+		return -EFAULT;
+
+	vconn->ops->set_status(vconn, val);
+
+	return 0;
+}
+
+static int
+_virtio_connection_getfeatures(struct _virtio_connection *_vconn,
+			       void *data, unsigned long len)
+{
+	struct virtio_connection *vconn = _vconn->vconn;
+	struct vbus_memctx *ctx = _vconn->ctx;
+	u32 val = 0;
+	int ret;
+
+	if (len != sizeof(val))
+		return -EINVAL;
+
+	if (vconn->ops->get_features)
+		val = vconn->ops->get_features(vconn);
+
+	ret = ctx->ops->copy_to(ctx, data, &val, sizeof(val));
+	if (ret)
+		return -EFAULT;
+
+	return 0;
+}
+
+static int
+_virtio_connection_finalizefeatures(struct _virtio_connection *_vconn)
+{
+	struct virtio_connection *vconn = _vconn->vconn;
+
+	if (vconn->ops->finalize_features)
+		vconn->ops->finalize_features(vconn);
+
+	return 0;
+}
+
+static int
+_virtio_connection_reset(struct _virtio_connection *_vconn)
+{
+	struct virtio_connection *vconn = _vconn->vconn;
+
+	if (vconn->ops->reset)
+		vconn->ops->reset(vconn);
+
+	return 0;
+}
+
+static struct _virtio_queue *
+_virtio_find_queue(struct _virtio_connection *_vconn, int index)
+{
+	struct _virtio_queue *vq;
+
+	list_for_each_entry(vq, &_vconn->queues, node) {
+		if (vq->index == index)
+			return vq;
+	}
+
+	return NULL;
+}
+
+static int
+_virtio_connection_queryqueue(struct _virtio_connection *_vconn,
+			      void *data, unsigned long len)
+{
+	struct vbus_memctx *ctx = _vconn->ctx;
+	struct virtio_vbus_queryqueue val;
+	struct _virtio_queue *vq;
+	int ret;
+
+	if (len != sizeof(val))
+		return -EINVAL;
+
+	ret = ctx->ops->copy_from(ctx, &val, data, sizeof(val));
+	if (ret)
+		return -EFAULT;
+
+	vq = _virtio_find_queue(_vconn, val.index);
+
+	if (!vq)
+		return -EINVAL;
+
+	if (vq->shm)
+		return -EEXIST;
+
+	val.num = vq->num;
+
+	ret = ctx->ops->copy_to(ctx, data, &val, sizeof(val));
+	if (ret)
+		return -EFAULT;
+
+	return 0;
+}
+
+static int
+_virtio_connection_call(struct vbus_connection *conn,
+		    unsigned long func,
+		    void *data,
+		    unsigned long len,
+		    unsigned long flags)
+{
+	struct _virtio_connection *_vconn = to_vconn(conn);
+	int ret = 0;
+
+	PDEBUG("call -> %d with %p/%d\n", func, data, len);
+
+	switch (func) {
+	case VIRTIO_VBUS_FUNC_NEG_CAP:
+		ret = _virtio_connection_negcap(_vconn, data, len);
+		break;
+	case VIRTIO_VBUS_FUNC_GET_ID:
+		ret = _virtio_connection_getid(_vconn, data, len);
+		break;
+	case VIRTIO_VBUS_FUNC_GET_FEATURES:
+		ret = _virtio_connection_getfeatures(_vconn, data, len);
+		break;
+	case VIRTIO_VBUS_FUNC_FINALIZE_FEATURES:
+		_virtio_connection_finalizefeatures(_vconn);
+		break;
+	case VIRTIO_VBUS_FUNC_GET_STATUS:
+		ret = _virtio_connection_getstatus(_vconn, data, len);
+		break;
+	case VIRTIO_VBUS_FUNC_SET_STATUS:
+		ret = _virtio_connection_setstatus(_vconn, data, len);
+		break;
+	case VIRTIO_VBUS_FUNC_RESET:
+		_virtio_connection_reset(_vconn);
+		break;
+	case VIRTIO_VBUS_FUNC_QUERY_QUEUE:
+		ret = _virtio_connection_queryqueue(_vconn, data, len);
+		break;
+	case VIRTIO_VBUS_FUNC_DEL_QUEUE:
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static void _virtio_config_isr(struct shm_signal_notifier *notifier)
+{
+	struct _virtio_connection *_vconn;
+	struct virtio_connection *vconn;
+
+	_vconn = container_of(notifier, struct _virtio_connection,
+			      config.notifier);
+
+	vconn = _vconn->vconn;
+
+	if (vconn->ops->config_changed)
+		vconn->ops->config_changed(vconn);
+}
+
+static int
+_virtio_connection_open(struct _virtio_connection *_vconn)
+
+{
+	struct virtio_device_interface *vintf = _vconn->_vintf->vintf;
+	struct virtio_connection *vconn;
+	struct virtio_queue_def *def = vintf->queues;
+	int ret;
+
+	ret = vintf->ops->open(vintf, _vconn->ctx, &vconn);
+	if (ret < 0)
+		return ret;
+
+	while (def && def->index != -1) {
+		struct _virtio_queue *vq;
+
+		vq = kzalloc(sizeof(*vq), GFP_KERNEL);
+		if (!vq)
+			return -ENOMEM;
+
+		vq->index  = def->index;
+		vq->num    = def->entries;
+		vq->_vconn = _vconn;
+
+		list_add_tail(&vq->node, &_vconn->queues);
+
+		def++;
+	}
+
+	_vconn->vconn  = vconn;
+	vconn->parent  = &_vconn->conn;
+
+	return 0;
+}
+
+static int
+_virtio_connection_initconfig(struct _virtio_connection *_vconn,
+			      struct vbus_shm *shm,
+			      struct shm_signal *signal)
+{
+	int ret;
+
+	if (_vconn->running)
+		return -EINVAL;
+
+	_vconn->config.signal = signal;
+	_vconn->config.shm = shm;
+	_vconn->config.notifier.signal = &_virtio_config_isr;
+	signal->notifier = &_vconn->config.notifier;
+
+	shm_signal_enable(signal, 0);
+
+	ret = _virtio_connection_open(_vconn);
+	if (ret < 0)
+		return ret;
+
+	_vconn->running = 1;
+
+	return 0;
+}
+
+static void _vq_isr(struct shm_signal_notifier *notifier)
+{
+	struct _virtio_queue *vq;
+
+	vq = container_of(notifier, struct _virtio_queue, notifier);
+
+	vring_interrupt(0, vq->vq);
+}
+
+static void _vq_notify(struct virtqueue *vq)
+{
+	struct _virtio_queue *_vq = vq->priv;
+
+	shm_signal_inject(_vq->signal, 0);
+}
+
+static void _vq_callback(struct virtqueue *vq)
+{
+	struct _virtio_queue *_vq = vq->priv;
+	struct virtio_connection *vconn = _vq->_vconn->vconn;
+
+	vconn->ops->notify_vq(vconn, _vq->index);
+}
+
+static int
+_virtio_connection_shm(struct vbus_connection *conn,
+		   unsigned long id,
+		   struct vbus_shm *shm,
+		   struct shm_signal *signal,
+		   unsigned long flags)
+{
+	struct _virtio_connection *_vconn = to_vconn(conn);
+	struct virtio_connection *vconn = _vconn->vconn;
+	struct _virtio_queue *vq;
+	struct virtio_vbus_shm *_shm = shm->ptr;
+
+	/* All shm connections that we support require a signal */
+	if (!signal)
+		return -EINVAL;
+
+	if (!id)
+		return _virtio_connection_initconfig(_vconn, shm, signal);
+
+	vq = _virtio_find_queue(_vconn, id - VIRTIO_VBUS_RING_OFFSET);
+	if (!vq)
+		return -EINVAL;
+
+	if (vq->shm)
+		return -EEXIST;
+
+	vq->shm = shm;
+	vq->signal = signal;
+
+	vq->notifier.signal = &_vq_isr;
+	signal->notifier = &vq->notifier;
+
+	shm_signal_enable(signal, 0);
+
+	vq->vq = vring_new_virtqueue(vq->num, PAGE_SIZE, NULL,
+				     &_shm->data[0],
+				     _vq_notify, _vq_callback);
+
+	vq->vq->priv = vq;
+
+	vconn->ops->add_vq(vconn, vq->index, vq->vq);
+
+	return 0;
+}
+
+static void
+_virtio_connection_release(struct vbus_connection *conn)
+{
+	struct _virtio_connection *_vconn = to_vconn(conn);
+	struct virtio_connection *vconn = _vconn->vconn;
+	struct _virtio_queue *vq, *tmp;
+
+	vconn->ops->release(vconn);
+
+	list_for_each_entry_safe(vq, tmp, &_vconn->queues, node) {
+		if (vq->vq)
+			vring_del_virtqueue(vq->vq);
+
+		if (vq->shm)
+			vbus_shm_put(vq->shm);
+
+		if (vq->signal)
+			shm_signal_put(vq->signal);
+
+		list_del(&vq->node);
+		kfree(vq);
+	}
+
+	if (_vconn->config.signal)
+		shm_signal_put(_vconn->config.signal);
+
+	if (_vconn->config.shm)
+		vbus_shm_put(_vconn->config.shm);
+
+	kobject_put(&_vconn->_vintf->intf.kobj);
+	vbus_memctx_put(_vconn->ctx);
+
+	kfree(_vconn);
+}
+
+static struct vbus_connection_ops _virtio_connection_ops = {
+	.call    = _virtio_connection_call,
+	.shm     = _virtio_connection_shm,
+	.release = _virtio_connection_release,
+};
+
+static int
+_virtio_intf_open(struct vbus_device_interface *intf,
+		  struct vbus_memctx *ctx,
+		  int version,
+		  struct vbus_connection **conn)
+{
+	struct _virtio_device_interface *_vintf = to_vintf(intf);
+	struct _virtio_connection *_vconn;
+	int ret;
+
+	if (version != VIRTIO_VBUS_ABI_VERSION)
+		return -EINVAL;
+
+	_vconn = kzalloc(sizeof(*_vconn), GFP_KERNEL);
+	if (!_vconn)
+		return -ENOMEM;
+
+	vbus_connection_init(&_vconn->conn, &_virtio_connection_ops);
+	_vconn->_vintf = _vintf;
+	_vconn->ctx    = ctx;
+	INIT_LIST_HEAD(&_vconn->queues);
+
+	vbus_memctx_get(ctx);
+	kobject_get(&intf->kobj);
+
+	*conn         = &_vconn->conn;
+
+	return 0;
+}
+
+static void
+_virtio_intf_release(struct vbus_device_interface *intf)
+{
+	struct _virtio_device_interface *_vintf = to_vintf(intf);
+	struct virtio_device_interface *vintf = _vintf->vintf;
+
+	if (vintf && vintf->ops->release)
+		vintf->ops->release(vintf);
+	kfree(_vintf);
+}
+
+static struct vbus_device_interface_ops _virtio_device_interface_ops = {
+	.open    = _virtio_intf_open,
+	.release = _virtio_intf_release,
+};
+
+int
+virtio_device_interface_register(struct vbus_device *dev,
+				 struct vbus *vbus,
+				 struct virtio_device_interface *vintf)
+{
+	struct _virtio_device_interface *_vintf;
+	struct vbus_device_interface *intf;
+
+	_vintf = kzalloc(sizeof(*_vintf), GFP_KERNEL);
+	if (!_vintf)
+		return -ENOMEM;
+
+	_vintf->vintf = vintf;
+
+	intf = &_vintf->intf;
+
+	intf->name = "0"; /* FIXME */
+	intf->type = "virtio";
+	intf->ops  = &_virtio_device_interface_ops;
+
+	return vbus_device_interface_register(dev, vbus, intf);
+}
+EXPORT_SYMBOL_GPL(virtio_device_interface_register);
+
+int
+virtio_device_interface_unregister(struct virtio_device_interface *intf)
+{
+	return vbus_device_interface_unregister(intf->parent);
+}
+EXPORT_SYMBOL_GPL(virtio_device_interface_unregister);