lkml.org 
[lkml]   [2009]   [Apr]   [9]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH v2 19/19] virtio: add a vbus transport
    Date
    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

    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);


    \
     
     \ /
      Last update: 2009-04-09 18:43    [W:0.125 / U:2.572 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site