virtio: introduce an API to set affinity for a virtqueue
[pandora-kernel.git] / drivers / virtio / virtio_pci.c
index d902464..f5dfe6b 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];
@@ -276,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,
@@ -293,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,
@@ -311,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;
@@ -606,6 +622,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,
@@ -617,6 +662,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)