]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - drivers/virtio/virtio_pci_common.c
virtio_pci: split vp_try_to_find_vqs into INTx and MSI-X variants
[mirror_ubuntu-zesty-kernel.git] / drivers / virtio / virtio_pci_common.c
index d9a90582796710d7750942be21d1b6c41ac28a8d..186cbab327b8f6ac76ce4cc572a03fdd49410f06 100644 (file)
@@ -37,7 +37,7 @@ void vp_synchronize_vectors(struct virtio_device *vdev)
                synchronize_irq(vp_dev->pci_dev->irq);
 
        for (i = 0; i < vp_dev->msix_vectors; ++i)
-               synchronize_irq(vp_dev->msix_entries[i].vector);
+               synchronize_irq(pci_irq_vector(vp_dev->pci_dev, i));
 }
 
 /* the notify function used when creating a virt queue */
@@ -102,41 +102,6 @@ static irqreturn_t vp_interrupt(int irq, void *opaque)
        return vp_vring_interrupt(irq, opaque);
 }
 
-static void vp_free_vectors(struct virtio_device *vdev)
-{
-       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-       int i;
-
-       if (vp_dev->intx_enabled) {
-               free_irq(vp_dev->pci_dev->irq, vp_dev);
-               vp_dev->intx_enabled = 0;
-       }
-
-       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 */
-               vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR);
-
-               pci_disable_msix(vp_dev->pci_dev);
-               vp_dev->msix_enabled = 0;
-       }
-
-       vp_dev->msix_vectors = 0;
-       vp_dev->msix_used_vectors = 0;
-       kfree(vp_dev->msix_names);
-       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,
                                   bool per_vq_vectors)
 {
@@ -147,10 +112,6 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
 
        vp_dev->msix_vectors = nvectors;
 
-       vp_dev->msix_entries = kmalloc(nvectors * sizeof *vp_dev->msix_entries,
-                                      GFP_KERNEL);
-       if (!vp_dev->msix_entries)
-               goto error;
        vp_dev->msix_names = kmalloc(nvectors * sizeof *vp_dev->msix_names,
                                     GFP_KERNEL);
        if (!vp_dev->msix_names)
@@ -165,12 +126,9 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
                                        GFP_KERNEL))
                        goto error;
 
-       for (i = 0; i < nvectors; ++i)
-               vp_dev->msix_entries[i].entry = i;
-
-       err = pci_enable_msix_exact(vp_dev->pci_dev,
-                                   vp_dev->msix_entries, nvectors);
-       if (err)
+       err = pci_alloc_irq_vectors(vp_dev->pci_dev, nvectors, nvectors,
+                       PCI_IRQ_MSIX);
+       if (err < 0)
                goto error;
        vp_dev->msix_enabled = 1;
 
@@ -178,7 +136,7 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
        v = vp_dev->msix_used_vectors;
        snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
                 "%s-config", name);
-       err = request_irq(vp_dev->msix_entries[v].vector,
+       err = request_irq(pci_irq_vector(vp_dev->pci_dev, v),
                          vp_config_changed, 0, vp_dev->msix_names[v],
                          vp_dev);
        if (err)
@@ -197,7 +155,7 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
                v = vp_dev->msix_used_vectors;
                snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names,
                         "%s-virtqueues", name);
-               err = request_irq(vp_dev->msix_entries[v].vector,
+               err = request_irq(pci_irq_vector(vp_dev->pci_dev, v),
                                  vp_vring_interrupt, 0, vp_dev->msix_names[v],
                                  vp_dev);
                if (err)
@@ -206,19 +164,6 @@ static int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors,
        }
        return 0;
 error:
-       vp_free_vectors(vdev);
-       return err;
-}
-
-static int vp_request_intx(struct virtio_device *vdev)
-{
-       int err;
-       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
-
-       err = request_irq(vp_dev->pci_dev->irq, vp_interrupt,
-                         IRQF_SHARED, dev_name(&vdev->dev), vp_dev);
-       if (!err)
-               vp_dev->intx_enabled = 1;
        return err;
 }
 
@@ -276,67 +221,88 @@ void vp_del_vqs(struct virtio_device *vdev)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        struct virtqueue *vq, *n;
-       struct virtio_pci_vq_info *info;
+       int i;
 
        list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
-               info = vp_dev->vqs[vq->index];
-               if (vp_dev->per_vq_vectors &&
-                       info->msix_vector != VIRTIO_MSI_NO_VECTOR)
-                       free_irq(vp_dev->msix_entries[info->msix_vector].vector,
-                                vq);
+               if (vp_dev->per_vq_vectors) {
+                       int v = vp_dev->vqs[vq->index]->msix_vector;
+
+                       if (v != VIRTIO_MSI_NO_VECTOR)
+                               free_irq(pci_irq_vector(vp_dev->pci_dev, v),
+                                       vq);
+               }
                vp_del_vq(vq);
        }
        vp_dev->per_vq_vectors = false;
 
-       vp_free_vectors(vdev);
+       if (vp_dev->intx_enabled) {
+               free_irq(vp_dev->pci_dev->irq, vp_dev);
+               vp_dev->intx_enabled = 0;
+       }
+
+       for (i = 0; i < vp_dev->msix_used_vectors; ++i)
+               free_irq(pci_irq_vector(vp_dev->pci_dev, i), 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 */
+               vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR);
+
+               pci_free_irq_vectors(vp_dev->pci_dev);
+               vp_dev->msix_enabled = 0;
+       }
+
+       vp_dev->msix_vectors = 0;
+       vp_dev->msix_used_vectors = 0;
+       kfree(vp_dev->msix_names);
+       vp_dev->msix_names = NULL;
+       kfree(vp_dev->msix_affinity_masks);
+       vp_dev->msix_affinity_masks = NULL;
        kfree(vp_dev->vqs);
        vp_dev->vqs = NULL;
 }
 
-static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+static int vp_find_vqs_msix(struct virtio_device *vdev, unsigned nvqs,
                              struct virtqueue *vqs[],
                              vq_callback_t *callbacks[],
                              const char * const names[],
-                             bool use_msix,
                              bool per_vq_vectors)
 {
        struct virtio_pci_device *vp_dev = to_vp_device(vdev);
        u16 msix_vec;
        int i, err, nvectors, allocated_vectors;
 
-       vp_dev->vqs = kmalloc(nvqs * sizeof *vp_dev->vqs, GFP_KERNEL);
+       vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL);
        if (!vp_dev->vqs)
                return -ENOMEM;
 
-       if (!use_msix) {
-               /* Old style: one normal interrupt for change and all vqs. */
-               err = vp_request_intx(vdev);
-               if (err)
-                       goto error_find;
+       if (per_vq_vectors) {
+               /* Best option: one for change interrupt, one per vq. */
+               nvectors = 1;
+               for (i = 0; i < nvqs; ++i)
+                       if (callbacks[i])
+                               ++nvectors;
        } else {
-               if (per_vq_vectors) {
-                       /* Best option: one for change interrupt, one per vq. */
-                       nvectors = 1;
-                       for (i = 0; i < nvqs; ++i)
-                               if (callbacks[i])
-                                       ++nvectors;
-               } else {
-                       /* Second best: one for change, shared for all vqs. */
-                       nvectors = 2;
-               }
-
-               err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors);
-               if (err)
-                       goto error_find;
+               /* Second best: one for change, shared for all vqs. */
+               nvectors = 2;
        }
 
+       err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors);
+       if (err)
+               goto error_find;
+
        vp_dev->per_vq_vectors = per_vq_vectors;
        allocated_vectors = vp_dev->msix_used_vectors;
        for (i = 0; i < nvqs; ++i) {
                if (!names[i]) {
                        vqs[i] = NULL;
                        continue;
-               } else if (!callbacks[i] || !vp_dev->msix_enabled)
+               }
+
+               if (!callbacks[i])
                        msix_vec = VIRTIO_MSI_NO_VECTOR;
                else if (vp_dev->per_vq_vectors)
                        msix_vec = allocated_vectors++;
@@ -356,14 +322,12 @@ static int vp_try_to_find_vqs(struct virtio_device *vdev, unsigned nvqs,
                         sizeof *vp_dev->msix_names,
                         "%s-%s",
                         dev_name(&vp_dev->vdev.dev), names[i]);
-               err = request_irq(vp_dev->msix_entries[msix_vec].vector,
+               err = request_irq(pci_irq_vector(vp_dev->pci_dev, msix_vec),
                                  vring_interrupt, 0,
                                  vp_dev->msix_names[msix_vec],
                                  vqs[i]);
-               if (err) {
-                       vp_del_vq(vqs[i]);
+               if (err)
                        goto error_find;
-               }
        }
        return 0;
 
@@ -372,6 +336,43 @@ error_find:
        return err;
 }
 
+static int vp_find_vqs_intx(struct virtio_device *vdev, unsigned nvqs,
+               struct virtqueue *vqs[], vq_callback_t *callbacks[],
+               const char * const names[])
+{
+       struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+       int i, err;
+
+       vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL);
+       if (!vp_dev->vqs)
+               return -ENOMEM;
+
+       err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED,
+                       dev_name(&vdev->dev), vp_dev);
+       if (err)
+               goto out_del_vqs;
+
+       vp_dev->intx_enabled = 1;
+       vp_dev->per_vq_vectors = false;
+       for (i = 0; i < nvqs; ++i) {
+               if (!names[i]) {
+                       vqs[i] = NULL;
+                       continue;
+               }
+               vqs[i] = vp_setup_vq(vdev, i, callbacks[i], names[i],
+                               VIRTIO_MSI_NO_VECTOR);
+               if (IS_ERR(vqs[i])) {
+                       err = PTR_ERR(vqs[i]);
+                       goto out_del_vqs;
+               }
+       }
+
+       return 0;
+out_del_vqs:
+       vp_del_vqs(vdev);
+       return err;
+}
+
 /* the config->find_vqs() implementation */
 int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
                struct virtqueue *vqs[],
@@ -381,17 +382,15 @@ int vp_find_vqs(struct virtio_device *vdev, unsigned nvqs,
        int err;
 
        /* Try MSI-X with one vector per queue. */
-       err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names, true, true);
+       err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, true);
        if (!err)
                return 0;
        /* Fallback: MSI-X with one vector for config, one shared for queues. */
-       err = vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names,
-                                true, false);
+       err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, false);
        if (!err)
                return 0;
        /* Finally fall back to regular interrupts. */
-       return vp_try_to_find_vqs(vdev, nvqs, vqs, callbacks, names,
-                                 false, false);
+       return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names);
 }
 
 const char *vp_bus_name(struct virtio_device *vdev)
@@ -419,7 +418,7 @@ int vp_set_vq_affinity(struct virtqueue *vq, int cpu)
 
        if (vp_dev->msix_enabled) {
                mask = vp_dev->msix_affinity_masks[info->msix_vector];
-               irq = vp_dev->msix_entries[info->msix_vector].vector;
+               irq = pci_irq_vector(vp_dev->pci_dev, info->msix_vector);
                if (cpu == -1)
                        irq_set_affinity_hint(irq, NULL);
                else {