Patchwork Guest Kernel Support for Virtio Memory Regions

login
register
mail settings
Submitter Cam Macdonell
Date Feb. 24, 2010, 5:48 a.m.
Message ID <20100224054823.29241.95367.stgit@st-brides.cs.ualberta.ca>
Download mbox | patch
Permalink /patch/46106/
State New
Headers show

Comments

Cam Macdonell - Feb. 24, 2010, 5:48 a.m.
These are the kernel changes to support passing memory regions via VirtIO that
was emailed earlier.  This patch adds new "device_ops" to the VirtIO device
which are "map" and "unmap".  These operations are based on suggestions from
Christian Borntraeger (but don't blame him for this patch) from the discussions
stemming from the PCI version of my shared memory patch.  Other operations
could certainly be added.

The VirtIO config space is extended with three new vectors to support passing
the memory regions to the guest.  I tried to follow the model of how virtqueues
are passed as much as possible.

I've used this mechanism to share host memory between multiple guests and
modified Alex Graf's Virtio Frame buffer to use it as well.

Comments are welcome,
Cam
---
 drivers/virtio/virtio_pci.c   |   73 +++++++++++++++++++++++++++++++++++++++++
 include/linux/virtio.h        |    9 +++++
 include/linux/virtio_config.h |    7 ++++
 include/linux/virtio_pci.h    |   12 +++++--
 4 files changed, 98 insertions(+), 3 deletions(-)

Patch

diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index 28d9cf7..6af2fe2 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -591,6 +591,78 @@  static struct virtio_config_ops virtio_pci_config_ops = {
 	.finalize_features = vp_finalize_features,
 };
 
+static struct virtmem *setup_mem(struct virtio_device *vdev, unsigned index)
+{
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	struct virtmem *vmem;
+	u32 size;
+	u32 pfn;
+
+	/* Select the memory region we're interested in */
+    printk(KERN_INFO "selecting mem region %d\n", index);
+	iowrite32(index, vp_dev->ioaddr + VIRTIO_PCI_MEM_SEL);
+
+	/* Read memory region size */
+	size = ioread32(vp_dev->ioaddr + VIRTIO_PCI_MEM_SIZE);
+	if (!size) return ERR_PTR(-ENOENT);
+
+    pfn = ioread32(vp_dev->ioaddr + VIRTIO_PCI_MEM_PFN);
+    printk(KERN_INFO "pfn is %#x\n", pfn);
+
+	/* allocate and fill out our structure the represents an active
+	 * mem_region */
+	vmem = kmalloc(sizeof(struct virtmem), GFP_KERNEL);
+	if (!vmem)
+		return ERR_PTR(-ENOMEM);
+
+    vmem->size = size;
+    vmem->phys_addr = pfn << 12;
+
+	return vmem;
+
+}
+
+/* support for VirtIO shared memory */
+static int vp_map_region(struct virtio_device *vdev, unsigned nmem,
+        struct virtmem ** vmem)
+{
+    int i;
+
+    for (i = 0; i < nmem; ++i) {
+        vmem[i] = setup_mem(vdev, i);
+        printk(KERN_INFO "virtio_pci: vp_map_region (%lu, %lu)\n",
+               vmem[i]->phys_addr, vmem[i]->size);
+
+        /* let's map it here and assign the address to 'map_addr' */
+        vmem[i]->map_addr = ioremap_cache(vmem[i]->phys_addr, vmem[i]->size);
+
+        if (vmem[i]->map_addr == NULL) {
+            printk(KERN_INFO "virtio_pci: vp_map_region (%lu, %lu) failed \n",
+                    vmem[i]->phys_addr, vmem[i]->size);
+            return -ENODEV;
+        }
+    }
+
+    /* TODO: compare length to size? */
+
+    return 0;
+}
+
+static void vp_unmap_region(struct virtio_device *vdev, struct virtmem *vmem)
+{
+
+    vmem->map_addr = 0;
+    vmem->size = 0;
+    iounmap(vmem->phys_addr);
+
+    return;
+}
+
+static struct virtio_device_ops virtio_pci_device_ops = {
+    .map_region = vp_map_region,
+    .unmap_region   = vp_unmap_region,
+};
+
 static void virtio_pci_release_dev(struct device *_d)
 {
 	struct virtio_device *dev = container_of(_d, struct virtio_device, dev);
@@ -630,6 +702,7 @@  static int __devinit virtio_pci_probe(struct pci_dev *pci_dev,
 	vp_dev->vdev.dev.parent = virtio_pci_root;
 	vp_dev->vdev.dev.release = virtio_pci_release_dev;
 	vp_dev->vdev.config = &virtio_pci_config_ops;
+	vp_dev->vdev.device = &virtio_pci_device_ops;
 	vp_dev->pci_dev = pci_dev;
 	INIT_LIST_HEAD(&vp_dev->virtqueues);
 	spin_lock_init(&vp_dev->lock);
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index 057a2e0..2b8ea66 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -26,6 +26,14 @@  struct virtqueue {
 	void *priv;
 };
 
+struct virtmem {
+    struct virtio_device *vdev;
+    u32 size;
+    u64 phys_addr;
+    unsigned char * map_addr;
+    void * priv;
+};
+
 /**
  * virtqueue_ops - operations for virtqueue abstraction layer
  * @add_buf: expose buffer to other end
@@ -88,6 +96,7 @@  struct virtio_device {
 	struct device dev;
 	struct virtio_device_id id;
 	struct virtio_config_ops *config;
+	struct virtio_device_ops *device;
 	struct list_head vqs;
 	/* Note that this is a Linux set_bit-style bitmap. */
 	unsigned long features[1];
diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h
index 0093dd7..ff90d3c 100644
--- a/include/linux/virtio_config.h
+++ b/include/linux/virtio_config.h
@@ -96,6 +96,13 @@  struct virtio_config_ops {
 	void (*finalize_features)(struct virtio_device *vdev);
 };
 
+struct virtio_device_ops {
+    int (*map_region) (struct virtio_device *vdev,
+                            unsigned nmem, struct virtmem ** vmem);
+    void (*unmap_region)(struct virtio_device *vdev, struct virtmem *vmem);
+    /* we might need query region and other stuff */
+};
+
 /* If driver didn't advertise the feature, it will never appear. */
 void virtio_check_driver_offered_feature(const struct virtio_device *vdev,
 					 unsigned int fbit);
diff --git a/include/linux/virtio_pci.h b/include/linux/virtio_pci.h
index 9a3d7c4..f3362d2 100644
--- a/include/linux/virtio_pci.h
+++ b/include/linux/virtio_pci.h
@@ -49,15 +49,21 @@ 
 
 /* MSI-X registers: only enabled if MSI-X is enabled. */
 /* A 16-bit vector for configuration changes. */
-#define VIRTIO_MSI_CONFIG_VECTOR        20
+#define VIRTIO_MSI_CONFIG_VECTOR        32
 /* A 16-bit vector for selected queue notifications. */
-#define VIRTIO_MSI_QUEUE_VECTOR         22
+#define VIRTIO_MSI_QUEUE_VECTOR         34
 /* Vector value used to disable MSI for queue */
 #define VIRTIO_MSI_NO_VECTOR            0xffff
 
 /* The remaining space is defined by each driver as the per-driver
  * configuration space */
-#define VIRTIO_PCI_CONFIG(dev)		((dev)->msix_enabled ? 24 : 20)
+#define VIRTIO_PCI_CONFIG(dev)		((dev)->msix_enabled ? 36 : 32)
+
+#define VIRTIO_PCI_MEM_SEL              20
+
+#define VIRTIO_PCI_MEM_SIZE             24
+
+#define VIRTIO_PCI_MEM_PFN              28
 
 /* Virtio ABI version, this must match exactly */
 #define VIRTIO_PCI_ABI_VERSION		0