]> Pileus Git - ~andy/linux/blobdiff - drivers/virtio/virtio_pci.c
virtio: support reserved vqs
[~andy/linux] / drivers / virtio / virtio_pci.c
index 2e03d416b9af5c294803448ffbf5be33a4e5d571..42b20769d122f047d3cdb1d932893244713c1a0d 100644 (file)
@@ -48,6 +48,7 @@ struct virtio_pci_device
        int msix_enabled;
        int intx_enabled;
        struct msix_entry *msix_entries;
+       cpumask_var_t *msix_affinity_masks;
        /* Name strings for interrupts. This size should be enough,
         * and I'm too lazy to allocate each name separately. */
        char (*msix_names)[256];
@@ -79,9 +80,6 @@ struct virtio_pci_vq_info
        /* the number of entries in the queue */
        int num;
 
-       /* the index of the queue */
-       int queue_index;
-
        /* the virtual address of the ring queue */
        void *queue;
 
@@ -202,11 +200,11 @@ static void vp_reset(struct virtio_device *vdev)
 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(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
+       iowrite16(virtqueue_get_queue_index(vq),
+                 vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY);
 }
 
 /* Handle a configuration change: Tell driver if it wants to know. */
@@ -279,6 +277,10 @@ static void vp_free_vectors(struct virtio_device *vdev)
        for (i = 0; i < vp_dev->msix_used_vectors; ++i)
                free_irq(vp_dev->msix_entries[i].vector, vp_dev);
 
+       for (i = 0; i < vp_dev->msix_vectors; i++)
+               if (vp_dev->msix_affinity_masks[i])
+                       free_cpumask_var(vp_dev->msix_affinity_masks[i]);
+
        if (vp_dev->msix_enabled) {
                /* Disable the vector used for configuration */
                iowrite16(VIRTIO_MSI_NO_VECTOR,
@@ -296,6 +298,8 @@ static void vp_free_vectors(struct virtio_device *vdev)
        vp_dev->msix_names = NULL;
        kfree(vp_dev->msix_entries);
        vp_dev->msix_entries = NULL;
+       kfree(vp_dev->msix_affinity_masks);
+       vp_dev->msix_affinity_masks = NULL;
 }
 
 static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
@@ -314,6 +318,15 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
                                     GFP_KERNEL);
        if (!vp_dev->msix_names)
                goto error;
+       vp_dev->msix_affinity_masks
+               = kzalloc(nvectors * sizeof *vp_dev->msix_affinity_masks,
+                         GFP_KERNEL);
+       if (!vp_dev->msix_affinity_masks)
+               goto error;
+       for (i = 0; i < nvectors; ++i)
+               if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i],
+                                       GFP_KERNEL))
+                       goto error;
 
        for (i = 0; i < nvectors; ++i)
                vp_dev->msix_entries[i].entry = i;
@@ -402,7 +415,6 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
        if (!info)
                return ERR_PTR(-ENOMEM);
 
-       info->queue_index = index;
        info->num = num;
        info->msix_vector = msix_vec;
 
@@ -418,7 +430,7 @@ static struct virtqueue *setup_vq(struct virtio_device *vdev, unsigned index,
                  vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
        /* create the vring */
-       vq = vring_new_virtqueue(info->num, VIRTIO_PCI_VRING_ALIGN, vdev,
+       vq = vring_new_virtqueue(index, info->num, VIRTIO_PCI_VRING_ALIGN, vdev,
                                 true, info->queue, vp_notify, callback, name);
        if (!vq) {
                err = -ENOMEM;
@@ -467,7 +479,8 @@ static void vp_del_vq(struct virtqueue *vq)
        list_del(&info->node);
        spin_unlock_irqrestore(&vp_dev->lock, flags);
 
-       iowrite16(info->queue_index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
+       iowrite16(virtqueue_get_queue_index(vq),
+               vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
 
        if (vp_dev->msix_enabled) {
                iowrite16(VIRTIO_MSI_NO_VECTOR,
@@ -542,7 +555,10 @@ static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs,
        vp_dev->per_vq_vectors = per_vq_vectors;
        allocated_vectors = vp_dev->msix_used_vectors;
        for (i = 0; i < nvqs; ++i) {
-               if (!callbacks[i] || !vp_dev->msix_enabled)
+               if (!names[i]) {
+                       vqs[i] = NULL;
+                       continue;
+               } else if (!callbacks[i] || !vp_dev->msix_enabled)
                        msix_vec = VIRTIO_MSI_NO_VECTOR;
                else if (vp_dev->per_vq_vectors)
                        msix_vec = allocated_vectors++;
@@ -609,6 +625,35 @@ static const char *vp_bus_name(struct virtio_device *vdev)
        return pci_name(vp_dev->pci_dev);
 }
 
+/* Setup the affinity for a virtqueue:
+ * - force the affinity for per vq vector
+ * - OR over all affinities for shared MSI
+ * - ignore the affinity request if we're using INTX
+ */
+static int vp_set_vq_affinity(struct virtqueue *vq, int cpu)
+{
+       struct virtio_device *vdev = vq->vdev;
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+       struct virtio_pci_vq_info *info = vq->priv;
+       struct cpumask *mask;
+       unsigned int irq;
+
+       if (!vq->callback)
+               return -EINVAL;
+
+       if (vp_dev->msix_enabled) {
+               mask = vp_dev->msix_affinity_masks[info->msix_vector];
+               irq = vp_dev->msix_entries[info->msix_vector].vector;
+               if (cpu == -1)
+                       irq_set_affinity_hint(irq, NULL);
+               else {
+                       cpumask_set_cpu(cpu, mask);
+                       irq_set_affinity_hint(irq, mask);
+               }
+       }
+       return 0;
+}
+
 static struct virtio_config_ops virtio_pci_config_ops = {
        .get            = vp_get,
        .set            = vp_set,
@@ -620,6 +665,7 @@ static struct virtio_config_ops virtio_pci_config_ops = {
        .get_features   = vp_get_features,
        .finalize_features = vp_finalize_features,
        .bus_name       = vp_bus_name,
+       .set_vq_affinity = vp_set_vq_affinity,
 };
 
 static void virtio_pci_release_dev(struct device *_d)