This patch brings the virtio_pci driver up-to-date with what I have locally.
It fixes a bug with destroying a virtqueue, switches to kmalloc so we can
support rings > PAGE_SIZE, and fixes a bug with odd sized configuration
entries.
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 85ae096..eb9a8e0 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -1,3 +1,19 @@
+/*
+ * Virtio PCI driver
+ *
+ * This module allows virtio devices to be used over a virtual PCI device.
+ * This can be used with QEMU based VMMs like KVM or Xen.
+ *
+ * Copyright IBM Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
#include <linux/module.h>
#include <linux/list.h>
#include <linux/pci.h>
@@ -17,32 +33,32 @@ MODULE_VERSION("1");
/* Our device structure */
struct virtio_pci_device
{
- /* the virtio device */
struct virtio_device vdev;
- /* the PCI device */
struct pci_dev *pci_dev;
+
/* the IO mapping for the PCI config space */
void *ioaddr;
+ /* a list of queues so we can dispatch IRQs */
spinlock_t lock;
struct list_head virtqueues;
};
struct virtio_pci_vq_info
{
+ /* the actual virtqueue */
+ struct virtqueue *vq;
+
/* the number of entries in the queue */
int num;
- /* the number of pages the device needs for the ring queue */
- int n_pages;
+
/* the index of the queue */
int queue_index;
- /* the struct page of the ring queue */
- struct page *pages;
+
/* the virtual address of the ring queue */
void *queue;
- /* a pointer to the virtqueue */
- struct virtqueue *vq;
- /* the node pointer */
+
+ /* the list node for the virtqueues list */
struct list_head node;
};
@@ -127,12 +143,15 @@ static void vp_get(struct virtio_device *vdev, unsigned
offset,
break;
}
- /* for strange accesses of an odd size, we do not perform any
- * endianness conversion. */
- default:
- ioread8_rep(ioaddr, buf, len);
+ default: {
+ uint8_t *ptr = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ ptr[i] = ioread8(ioaddr + i);
break;
}
+ }
}
/* the config->set() implementation. it's symmetric to the
config->get()
@@ -169,10 +188,15 @@ static void vp_set(struct virtio_device *vdev, unsigned
offset,
iowrite32(val, ioaddr + 4);
break;
}
- default:
- iowrite8_rep(ioaddr, buf, len);
+ default: {
+ const uint8_t *ptr = buf;
+ int i;
+
+ for (i = 0; i < len; i++)
+ iowrite8(ptr[i], ioaddr + i);
break;
}
+ }
}
/* config->{get,set}_status() implementations */
@@ -237,8 +261,8 @@ static struct virtqueue *vp_find_vq(struct virtio_device
*vdev, unsigned index,
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
struct virtio_pci_vq_info *info;
struct virtqueue *vq;
- int err;
u16 num;
+ int err;
/* Select the queue we're interested in */
iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
@@ -257,25 +281,14 @@ static struct virtqueue *vp_find_vq(struct virtio_device
*vdev, unsigned index,
info->queue_index = index;
info->num = num;
- /* determine the memory needed for the queue and provide the memory
- * location to the host */
- info->n_pages = DIV_ROUND_UP(vring_size(num), PAGE_SIZE);
- info->pages = alloc_pages(GFP_KERNEL | __GFP_ZERO,
- get_order(info->n_pages));
- if (info->pages == NULL) {
- err = -ENOMEM;
- goto out_info;
- }
-
- /* FIXME: is this sufficient for info->n_pages > 1? */
- info->queue = kmap(info->pages);
+ info->queue = kmalloc(vring_size(num), GFP_KERNEL | __GFP_ZERO);
if (info->queue == NULL) {
err = -ENOMEM;
- goto out_alloc_pages;
+ goto out_info;
}
/* activate the queue */
- iowrite32(page_to_pfn(info->pages),
+ iowrite32(virt_to_phys(info->queue) >> PAGE_SHIFT,
vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
/* create the vring */
@@ -297,9 +310,7 @@ static struct virtqueue *vp_find_vq(struct virtio_device
*vdev, unsigned index,
out_activate_queue:
iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
- kunmap(info->queue);
-out_alloc_pages:
- __free_pages(info->pages, get_order(info->n_pages));
+ kfree(info->queue);
out_info:
kfree(info);
return ERR_PTR(err);
@@ -319,10 +330,9 @@ static void vp_del_vq(struct virtqueue *vq)
/* Select and deactivate the queue */
iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
- iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
+ iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
- kunmap(info->queue);
- __free_pages(info->pages, get_order(info->n_pages));
+ kfree(info->queue);
kfree(info);
}
@@ -348,10 +358,13 @@ static int __devinit virtio_pci_probe(struct pci_dev
*pci_dev,
if (vp_dev == NULL)
return -ENOMEM;
- vp_dev->pci_dev = pci_dev;
+ snprintf(vp_dev->vdev.dev.bus_id, BUS_ID_SIZE, "virtio%d",
dev_index);
+ vp_dev->vdev.index = dev_index;
+ dev_index++;
+
vp_dev->vdev.dev.parent = &virtio_pci_root;
- vp_dev->vdev.index = dev_index++;
vp_dev->vdev.config = &virtio_pci_config_ops;
+ vp_dev->pci_dev = pci_dev;
INIT_LIST_HEAD(&vp_dev->virtqueues);
spin_lock_init(&vp_dev->lock);
@@ -379,7 +392,7 @@ static int __devinit virtio_pci_probe(struct pci_dev
*pci_dev,
/* register a handler for the queue with the PCI device's interrupt */
err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
- pci_name(vp_dev->pci_dev), vp_dev);
+ vp_dev->vdev.dev.bus_id, vp_dev);
if (err)
goto out_set_drvdata;
diff --git a/include/linux/virtio_pci.h b/include/linux/virtio_pci.h
index b1a1568..4b28409 100644
--- a/include/linux/virtio_pci.h
+++ b/include/linux/virtio_pci.h
@@ -1,3 +1,19 @@
+/*
+ * Virtio PCI driver
+ *
+ * This module allows virtio devices to be used over a virtual PCI device.
+ * This can be used with QEMU based VMMs like KVM or Xen.
+ *
+ * Copyright IBM Corp. 2007
+ *
+ * Authors:
+ * Anthony Liguori <aliguori@us.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ */
+
#ifndef _LINUX_VIRTIO_PCI_H
#define _LINUX_VIRTIO_PCI_H