(MST, is this what you were thinking?)

This uses the previously-unused field for "queue_notify".  This contains
the offset from the notification area given by the VIRTIO_PCI_CAP_NOTIFY_CFG
header.

(A device can still make them all overlap if it wants, since the queue
index is written: it can still distinguish different notifications).

Cc: "Michael S. Tsirkin" <m...@redhat.com>
Signed-off-by: Rusty Russell <ru...@rustcorp.com.au>
---
 drivers/virtio/virtio_pci.c     |   61 +++++++++++++++++++++++++++------------
 include/uapi/linux/virtio_pci.h |    2 +-
 2 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/drivers/virtio/virtio_pci.c b/drivers/virtio/virtio_pci.c
index f252afe..d492361 100644
--- a/drivers/virtio/virtio_pci.c
+++ b/drivers/virtio/virtio_pci.c
@@ -37,11 +37,15 @@ struct virtio_pci_device {
        struct virtio_pci_common_cfg __iomem *common;
        /* Where to read and clear interrupt */
        u8 __iomem *isr;
-       /* Write the virtqueue index here to notify device of activity. */
-       __le16 __iomem *notify;
+       /* Write the vq index here to notify device of activity. */
+       void __iomem *notify_base;
        /* Device-specific data. */
        void __iomem *device;
 
+       /* So we can sanity-check accesses. */
+       size_t notify_len;
+       size_t device_len;
+
        /* a list of queues so we can dispatch IRQs */
        spinlock_t lock;
        struct list_head virtqueues;
@@ -84,6 +88,9 @@ struct virtio_pci_vq_info {
        /* the list node for the virtqueues list */
        struct list_head node;
 
+       /* Notify area for this vq. */
+       u16 __iomem *notify;
+
        /* MSI-X vector (or none) */
        unsigned msix_vector;
 };
@@ -240,11 +247,11 @@ static void vp_reset(struct virtio_device *vdev)
 /* the notify function used when creating a virt queue */
 static void vp_notify(struct virtqueue *vq)
 {
-       struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
+       struct virtio_pci_vq_info *info = vq->priv;
 
-       /* we write the queue's selector into the notification register to
-        * signal the other end */
-       iowrite16(vq->index, vp_dev->notify);
+       /* we write the queue selector into the notification register
+        * to signal the other end */
+       iowrite16(vq->index, info->notify);
 }
 
 /* Handle a configuration change: Tell driver if it wants to know. */
@@ -460,7 +467,7 @@ static struct virtqueue *setup_vq(struct virtio_device 
*vdev, unsigned index,
        struct virtio_pci_vq_info *info;
        struct virtqueue *vq;
        u16 num;
-       int err;
+       int err, off;
 
        if (index >= ioread16(&vp_dev->common->num_queues))
                return ERR_PTR(-ENOENT);
@@ -492,6 +499,17 @@ static struct virtqueue *setup_vq(struct virtio_device 
*vdev, unsigned index,
 
        info->msix_vector = msix_vec;
 
+       /* get offset of notification byte for this virtqueue */
+       off = ioread16(&vp_dev->common->queue_notify);
+       if (off > vp_dev->notify_len) {
+               dev_warn(&vp_dev->pci_dev->dev,
+                        "bad notification offset %u for queue %u (> %u)",
+                        off, index, vp_dev->notify_len);
+               err = -EINVAL;
+               goto out_info;
+       }
+       info->notify = vp_dev->notify_base + off;
+
        info->queue = alloc_virtqueue_pages(&num);
        if (info->queue == NULL) {
                err = -ENOMEM;
@@ -787,7 +805,8 @@ static void virtio_pci_release_dev(struct device *_d)
         */
 }
 
-static void __iomem *map_capability(struct pci_dev *dev, int off, size_t 
expect)
+static void __iomem *map_capability(struct pci_dev *dev, int off, size_t 
minlen,
+                                   size_t *len)
 {
        u8 bar;
        u32 offset, length;
@@ -800,13 +819,16 @@ static void __iomem *map_capability(struct pci_dev *dev, 
int off, size_t expect)
        pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, 
length),
                             &length);
 
-       if (length < expect) {
+       if (length < minlen) {
                dev_err(&dev->dev,
-                       "virtio_pci: small capability len %u (%u expected)\n",
-                       length, expect);
+                       "virtio_pci: small capability len %u (%zu expected)\n",
+                       length, minlen);
                return NULL;
        }
 
+       if (len)
+               *len = length;
+
        /* We want uncachable mapping, even if bar is cachable. */
        p = pci_iomap_range(dev, bar, offset, length, PAGE_SIZE, true);
        if (!p)
@@ -883,16 +905,19 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
 
        err = -EINVAL;
        vp_dev->common = map_capability(pci_dev, common,
-                                       sizeof(struct virtio_pci_common_cfg));
+                                       sizeof(struct virtio_pci_common_cfg),
+                                       NULL);
        if (!vp_dev->common)
                goto out_req_regions;
-       vp_dev->isr = map_capability(pci_dev, isr, sizeof(u8));
+       vp_dev->isr = map_capability(pci_dev, isr, sizeof(u8), NULL);
        if (!vp_dev->isr)
                goto out_map_common;
-       vp_dev->notify = map_capability(pci_dev, notify, sizeof(u16));
-       if (!vp_dev->notify)
+       vp_dev->notify_base = map_capability(pci_dev, notify, sizeof(u8),
+                                            &vp_dev->notify_len);
+       if (!vp_dev->notify_len)
                goto out_map_isr;
-       vp_dev->device = map_capability(pci_dev, device, 0);
+       vp_dev->device = map_capability(pci_dev, device, 0,
+                                       &vp_dev->device_len);
        if (!vp_dev->device)
                goto out_map_notify;
 
@@ -917,7 +942,7 @@ out_set_drvdata:
        pci_set_drvdata(pci_dev, NULL);
        pci_iounmap(pci_dev, vp_dev->device);
 out_map_notify:
-       pci_iounmap(pci_dev, vp_dev->notify);
+       pci_iounmap(pci_dev, vp_dev->notify_base);
 out_map_isr:
        pci_iounmap(pci_dev, vp_dev->isr);
 out_map_common:
@@ -940,7 +965,7 @@ static void virtio_pci_remove(struct pci_dev *pci_dev)
        vp_del_vqs(&vp_dev->vdev);
        pci_set_drvdata(pci_dev, NULL);
        pci_iounmap(pci_dev, vp_dev->device);
-       pci_iounmap(pci_dev, vp_dev->notify);
+       pci_iounmap(pci_dev, vp_dev->notify_base);
        pci_iounmap(pci_dev, vp_dev->isr);
        pci_iounmap(pci_dev, vp_dev->common);
        pci_release_regions(pci_dev);
diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h
index b334cd9..23b90cb 100644
--- a/include/uapi/linux/virtio_pci.h
+++ b/include/uapi/linux/virtio_pci.h
@@ -144,13 +144,13 @@ struct virtio_pci_common_cfg {
        __le16 num_queues;              /* read-only */
        __u8 device_status;             /* read-write */
        __u8 unused1;
-       __le16 unused2;
 
        /* About a specific virtqueue. */
        __le16 queue_select;    /* read-write */
        __le16 queue_size;      /* read-write, power of 2. */
        __le16 queue_msix_vector;/* read-write */
        __le16 queue_enable;    /* read-write */
+       __le16 queue_notify;    /* read-only */
        __le64 queue_desc;      /* read-write */
        __le64 queue_avail;     /* read-write */
        __le64 queue_used;      /* read-write */
-- 
1.7.10.4

_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/virtualization

Reply via email to