162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Virtio PCI driver - common functionality for all device versions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This module allows virtio devices to be used over a virtual PCI device. 662306a36Sopenharmony_ci * This can be used with QEMU based VMMs like KVM or Xen. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright IBM Corp. 2007 962306a36Sopenharmony_ci * Copyright Red Hat, Inc. 2014 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Authors: 1262306a36Sopenharmony_ci * Anthony Liguori <aliguori@us.ibm.com> 1362306a36Sopenharmony_ci * Rusty Russell <rusty@rustcorp.com.au> 1462306a36Sopenharmony_ci * Michael S. Tsirkin <mst@redhat.com> 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "virtio_pci_common.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic bool force_legacy = false; 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_VIRTIO_PCI_LEGACY) 2262306a36Sopenharmony_cimodule_param(force_legacy, bool, 0444); 2362306a36Sopenharmony_ciMODULE_PARM_DESC(force_legacy, 2462306a36Sopenharmony_ci "Force legacy mode for transitional virtio 1 devices"); 2562306a36Sopenharmony_ci#endif 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* wait for pending irq handlers */ 2862306a36Sopenharmony_civoid vp_synchronize_vectors(struct virtio_device *vdev) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 3162306a36Sopenharmony_ci int i; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (vp_dev->intx_enabled) 3462306a36Sopenharmony_ci synchronize_irq(vp_dev->pci_dev->irq); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci for (i = 0; i < vp_dev->msix_vectors; ++i) 3762306a36Sopenharmony_ci synchronize_irq(pci_irq_vector(vp_dev->pci_dev, i)); 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* the notify function used when creating a virt queue */ 4162306a36Sopenharmony_cibool vp_notify(struct virtqueue *vq) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci /* we write the queue's selector into the notification register to 4462306a36Sopenharmony_ci * signal the other end */ 4562306a36Sopenharmony_ci iowrite16(vq->index, (void __iomem *)vq->priv); 4662306a36Sopenharmony_ci return true; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* Handle a configuration change: Tell driver if it wants to know. */ 5062306a36Sopenharmony_cistatic irqreturn_t vp_config_changed(int irq, void *opaque) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = opaque; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci virtio_config_changed(&vp_dev->vdev); 5562306a36Sopenharmony_ci return IRQ_HANDLED; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Notify all virtqueues on an interrupt. */ 5962306a36Sopenharmony_cistatic irqreturn_t vp_vring_interrupt(int irq, void *opaque) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = opaque; 6262306a36Sopenharmony_ci struct virtio_pci_vq_info *info; 6362306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 6462306a36Sopenharmony_ci unsigned long flags; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci spin_lock_irqsave(&vp_dev->lock, flags); 6762306a36Sopenharmony_ci list_for_each_entry(info, &vp_dev->virtqueues, node) { 6862306a36Sopenharmony_ci if (vring_interrupt(irq, info->vq) == IRQ_HANDLED) 6962306a36Sopenharmony_ci ret = IRQ_HANDLED; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci spin_unlock_irqrestore(&vp_dev->lock, flags); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci return ret; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* A small wrapper to also acknowledge the interrupt when it's handled. 7762306a36Sopenharmony_ci * I really need an EIO hook for the vring so I can ack the interrupt once we 7862306a36Sopenharmony_ci * know that we'll be handling the IRQ but before we invoke the callback since 7962306a36Sopenharmony_ci * the callback may notify the host which results in the host attempting to 8062306a36Sopenharmony_ci * raise an interrupt that we would then mask once we acknowledged the 8162306a36Sopenharmony_ci * interrupt. */ 8262306a36Sopenharmony_cistatic irqreturn_t vp_interrupt(int irq, void *opaque) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = opaque; 8562306a36Sopenharmony_ci u8 isr; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* reading the ISR has the effect of also clearing it so it's very 8862306a36Sopenharmony_ci * important to save off the value. */ 8962306a36Sopenharmony_ci isr = ioread8(vp_dev->isr); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* It's definitely not us if the ISR was not high */ 9262306a36Sopenharmony_ci if (!isr) 9362306a36Sopenharmony_ci return IRQ_NONE; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Configuration change? Tell driver if it wants to know. */ 9662306a36Sopenharmony_ci if (isr & VIRTIO_PCI_ISR_CONFIG) 9762306a36Sopenharmony_ci vp_config_changed(irq, opaque); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return vp_vring_interrupt(irq, opaque); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int vp_request_msix_vectors(struct virtio_device *vdev, int nvectors, 10362306a36Sopenharmony_ci bool per_vq_vectors, struct irq_affinity *desc) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 10662306a36Sopenharmony_ci const char *name = dev_name(&vp_dev->vdev.dev); 10762306a36Sopenharmony_ci unsigned int flags = PCI_IRQ_MSIX; 10862306a36Sopenharmony_ci unsigned int i, v; 10962306a36Sopenharmony_ci int err = -ENOMEM; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci vp_dev->msix_vectors = nvectors; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci vp_dev->msix_names = kmalloc_array(nvectors, 11462306a36Sopenharmony_ci sizeof(*vp_dev->msix_names), 11562306a36Sopenharmony_ci GFP_KERNEL); 11662306a36Sopenharmony_ci if (!vp_dev->msix_names) 11762306a36Sopenharmony_ci goto error; 11862306a36Sopenharmony_ci vp_dev->msix_affinity_masks 11962306a36Sopenharmony_ci = kcalloc(nvectors, sizeof(*vp_dev->msix_affinity_masks), 12062306a36Sopenharmony_ci GFP_KERNEL); 12162306a36Sopenharmony_ci if (!vp_dev->msix_affinity_masks) 12262306a36Sopenharmony_ci goto error; 12362306a36Sopenharmony_ci for (i = 0; i < nvectors; ++i) 12462306a36Sopenharmony_ci if (!alloc_cpumask_var(&vp_dev->msix_affinity_masks[i], 12562306a36Sopenharmony_ci GFP_KERNEL)) 12662306a36Sopenharmony_ci goto error; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (desc) { 12962306a36Sopenharmony_ci flags |= PCI_IRQ_AFFINITY; 13062306a36Sopenharmony_ci desc->pre_vectors++; /* virtio config vector */ 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci err = pci_alloc_irq_vectors_affinity(vp_dev->pci_dev, nvectors, 13462306a36Sopenharmony_ci nvectors, flags, desc); 13562306a36Sopenharmony_ci if (err < 0) 13662306a36Sopenharmony_ci goto error; 13762306a36Sopenharmony_ci vp_dev->msix_enabled = 1; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Set the vector used for configuration */ 14062306a36Sopenharmony_ci v = vp_dev->msix_used_vectors; 14162306a36Sopenharmony_ci snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, 14262306a36Sopenharmony_ci "%s-config", name); 14362306a36Sopenharmony_ci err = request_irq(pci_irq_vector(vp_dev->pci_dev, v), 14462306a36Sopenharmony_ci vp_config_changed, 0, vp_dev->msix_names[v], 14562306a36Sopenharmony_ci vp_dev); 14662306a36Sopenharmony_ci if (err) 14762306a36Sopenharmony_ci goto error; 14862306a36Sopenharmony_ci ++vp_dev->msix_used_vectors; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci v = vp_dev->config_vector(vp_dev, v); 15162306a36Sopenharmony_ci /* Verify we had enough resources to assign the vector */ 15262306a36Sopenharmony_ci if (v == VIRTIO_MSI_NO_VECTOR) { 15362306a36Sopenharmony_ci err = -EBUSY; 15462306a36Sopenharmony_ci goto error; 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (!per_vq_vectors) { 15862306a36Sopenharmony_ci /* Shared vector for all VQs */ 15962306a36Sopenharmony_ci v = vp_dev->msix_used_vectors; 16062306a36Sopenharmony_ci snprintf(vp_dev->msix_names[v], sizeof *vp_dev->msix_names, 16162306a36Sopenharmony_ci "%s-virtqueues", name); 16262306a36Sopenharmony_ci err = request_irq(pci_irq_vector(vp_dev->pci_dev, v), 16362306a36Sopenharmony_ci vp_vring_interrupt, 0, vp_dev->msix_names[v], 16462306a36Sopenharmony_ci vp_dev); 16562306a36Sopenharmony_ci if (err) 16662306a36Sopenharmony_ci goto error; 16762306a36Sopenharmony_ci ++vp_dev->msix_used_vectors; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_cierror: 17162306a36Sopenharmony_ci return err; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic struct virtqueue *vp_setup_vq(struct virtio_device *vdev, unsigned int index, 17562306a36Sopenharmony_ci void (*callback)(struct virtqueue *vq), 17662306a36Sopenharmony_ci const char *name, 17762306a36Sopenharmony_ci bool ctx, 17862306a36Sopenharmony_ci u16 msix_vec) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 18162306a36Sopenharmony_ci struct virtio_pci_vq_info *info = kmalloc(sizeof *info, GFP_KERNEL); 18262306a36Sopenharmony_ci struct virtqueue *vq; 18362306a36Sopenharmony_ci unsigned long flags; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* fill out our structure that represents an active queue */ 18662306a36Sopenharmony_ci if (!info) 18762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci vq = vp_dev->setup_vq(vp_dev, info, index, callback, name, ctx, 19062306a36Sopenharmony_ci msix_vec); 19162306a36Sopenharmony_ci if (IS_ERR(vq)) 19262306a36Sopenharmony_ci goto out_info; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci info->vq = vq; 19562306a36Sopenharmony_ci if (callback) { 19662306a36Sopenharmony_ci spin_lock_irqsave(&vp_dev->lock, flags); 19762306a36Sopenharmony_ci list_add(&info->node, &vp_dev->virtqueues); 19862306a36Sopenharmony_ci spin_unlock_irqrestore(&vp_dev->lock, flags); 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci INIT_LIST_HEAD(&info->node); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci vp_dev->vqs[index] = info; 20462306a36Sopenharmony_ci return vq; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciout_info: 20762306a36Sopenharmony_ci kfree(info); 20862306a36Sopenharmony_ci return vq; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic void vp_del_vq(struct virtqueue *vq) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev); 21462306a36Sopenharmony_ci struct virtio_pci_vq_info *info = vp_dev->vqs[vq->index]; 21562306a36Sopenharmony_ci unsigned long flags; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * If it fails during re-enable reset vq. This way we won't rejoin 21962306a36Sopenharmony_ci * info->node to the queue. Prevent unexpected irqs. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci if (!vq->reset) { 22262306a36Sopenharmony_ci spin_lock_irqsave(&vp_dev->lock, flags); 22362306a36Sopenharmony_ci list_del(&info->node); 22462306a36Sopenharmony_ci spin_unlock_irqrestore(&vp_dev->lock, flags); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci vp_dev->del_vq(info); 22862306a36Sopenharmony_ci kfree(info); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* the config->del_vqs() implementation */ 23262306a36Sopenharmony_civoid vp_del_vqs(struct virtio_device *vdev) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 23562306a36Sopenharmony_ci struct virtqueue *vq, *n; 23662306a36Sopenharmony_ci int i; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci list_for_each_entry_safe(vq, n, &vdev->vqs, list) { 23962306a36Sopenharmony_ci if (vp_dev->per_vq_vectors) { 24062306a36Sopenharmony_ci int v = vp_dev->vqs[vq->index]->msix_vector; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (v != VIRTIO_MSI_NO_VECTOR) { 24362306a36Sopenharmony_ci int irq = pci_irq_vector(vp_dev->pci_dev, v); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci irq_set_affinity_hint(irq, NULL); 24662306a36Sopenharmony_ci free_irq(irq, vq); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci vp_del_vq(vq); 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci vp_dev->per_vq_vectors = false; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (vp_dev->intx_enabled) { 25462306a36Sopenharmony_ci free_irq(vp_dev->pci_dev->irq, vp_dev); 25562306a36Sopenharmony_ci vp_dev->intx_enabled = 0; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci for (i = 0; i < vp_dev->msix_used_vectors; ++i) 25962306a36Sopenharmony_ci free_irq(pci_irq_vector(vp_dev->pci_dev, i), vp_dev); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (vp_dev->msix_affinity_masks) { 26262306a36Sopenharmony_ci for (i = 0; i < vp_dev->msix_vectors; i++) 26362306a36Sopenharmony_ci free_cpumask_var(vp_dev->msix_affinity_masks[i]); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (vp_dev->msix_enabled) { 26762306a36Sopenharmony_ci /* Disable the vector used for configuration */ 26862306a36Sopenharmony_ci vp_dev->config_vector(vp_dev, VIRTIO_MSI_NO_VECTOR); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci pci_free_irq_vectors(vp_dev->pci_dev); 27162306a36Sopenharmony_ci vp_dev->msix_enabled = 0; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci vp_dev->msix_vectors = 0; 27562306a36Sopenharmony_ci vp_dev->msix_used_vectors = 0; 27662306a36Sopenharmony_ci kfree(vp_dev->msix_names); 27762306a36Sopenharmony_ci vp_dev->msix_names = NULL; 27862306a36Sopenharmony_ci kfree(vp_dev->msix_affinity_masks); 27962306a36Sopenharmony_ci vp_dev->msix_affinity_masks = NULL; 28062306a36Sopenharmony_ci kfree(vp_dev->vqs); 28162306a36Sopenharmony_ci vp_dev->vqs = NULL; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int vp_find_vqs_msix(struct virtio_device *vdev, unsigned int nvqs, 28562306a36Sopenharmony_ci struct virtqueue *vqs[], vq_callback_t *callbacks[], 28662306a36Sopenharmony_ci const char * const names[], bool per_vq_vectors, 28762306a36Sopenharmony_ci const bool *ctx, 28862306a36Sopenharmony_ci struct irq_affinity *desc) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 29162306a36Sopenharmony_ci u16 msix_vec; 29262306a36Sopenharmony_ci int i, err, nvectors, allocated_vectors, queue_idx = 0; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL); 29562306a36Sopenharmony_ci if (!vp_dev->vqs) 29662306a36Sopenharmony_ci return -ENOMEM; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (per_vq_vectors) { 29962306a36Sopenharmony_ci /* Best option: one for change interrupt, one per vq. */ 30062306a36Sopenharmony_ci nvectors = 1; 30162306a36Sopenharmony_ci for (i = 0; i < nvqs; ++i) 30262306a36Sopenharmony_ci if (names[i] && callbacks[i]) 30362306a36Sopenharmony_ci ++nvectors; 30462306a36Sopenharmony_ci } else { 30562306a36Sopenharmony_ci /* Second best: one for change, shared for all vqs. */ 30662306a36Sopenharmony_ci nvectors = 2; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci err = vp_request_msix_vectors(vdev, nvectors, per_vq_vectors, 31062306a36Sopenharmony_ci per_vq_vectors ? desc : NULL); 31162306a36Sopenharmony_ci if (err) 31262306a36Sopenharmony_ci goto error_find; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci vp_dev->per_vq_vectors = per_vq_vectors; 31562306a36Sopenharmony_ci allocated_vectors = vp_dev->msix_used_vectors; 31662306a36Sopenharmony_ci for (i = 0; i < nvqs; ++i) { 31762306a36Sopenharmony_ci if (!names[i]) { 31862306a36Sopenharmony_ci vqs[i] = NULL; 31962306a36Sopenharmony_ci continue; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (!callbacks[i]) 32362306a36Sopenharmony_ci msix_vec = VIRTIO_MSI_NO_VECTOR; 32462306a36Sopenharmony_ci else if (vp_dev->per_vq_vectors) 32562306a36Sopenharmony_ci msix_vec = allocated_vectors++; 32662306a36Sopenharmony_ci else 32762306a36Sopenharmony_ci msix_vec = VP_MSIX_VQ_VECTOR; 32862306a36Sopenharmony_ci vqs[i] = vp_setup_vq(vdev, queue_idx++, callbacks[i], names[i], 32962306a36Sopenharmony_ci ctx ? ctx[i] : false, 33062306a36Sopenharmony_ci msix_vec); 33162306a36Sopenharmony_ci if (IS_ERR(vqs[i])) { 33262306a36Sopenharmony_ci err = PTR_ERR(vqs[i]); 33362306a36Sopenharmony_ci goto error_find; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!vp_dev->per_vq_vectors || msix_vec == VIRTIO_MSI_NO_VECTOR) 33762306a36Sopenharmony_ci continue; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* allocate per-vq irq if available and necessary */ 34062306a36Sopenharmony_ci snprintf(vp_dev->msix_names[msix_vec], 34162306a36Sopenharmony_ci sizeof *vp_dev->msix_names, 34262306a36Sopenharmony_ci "%s-%s", 34362306a36Sopenharmony_ci dev_name(&vp_dev->vdev.dev), names[i]); 34462306a36Sopenharmony_ci err = request_irq(pci_irq_vector(vp_dev->pci_dev, msix_vec), 34562306a36Sopenharmony_ci vring_interrupt, 0, 34662306a36Sopenharmony_ci vp_dev->msix_names[msix_vec], 34762306a36Sopenharmony_ci vqs[i]); 34862306a36Sopenharmony_ci if (err) 34962306a36Sopenharmony_ci goto error_find; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cierror_find: 35462306a36Sopenharmony_ci vp_del_vqs(vdev); 35562306a36Sopenharmony_ci return err; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic int vp_find_vqs_intx(struct virtio_device *vdev, unsigned int nvqs, 35962306a36Sopenharmony_ci struct virtqueue *vqs[], vq_callback_t *callbacks[], 36062306a36Sopenharmony_ci const char * const names[], const bool *ctx) 36162306a36Sopenharmony_ci{ 36262306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 36362306a36Sopenharmony_ci int i, err, queue_idx = 0; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci vp_dev->vqs = kcalloc(nvqs, sizeof(*vp_dev->vqs), GFP_KERNEL); 36662306a36Sopenharmony_ci if (!vp_dev->vqs) 36762306a36Sopenharmony_ci return -ENOMEM; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci err = request_irq(vp_dev->pci_dev->irq, vp_interrupt, IRQF_SHARED, 37062306a36Sopenharmony_ci dev_name(&vdev->dev), vp_dev); 37162306a36Sopenharmony_ci if (err) 37262306a36Sopenharmony_ci goto out_del_vqs; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci vp_dev->intx_enabled = 1; 37562306a36Sopenharmony_ci vp_dev->per_vq_vectors = false; 37662306a36Sopenharmony_ci for (i = 0; i < nvqs; ++i) { 37762306a36Sopenharmony_ci if (!names[i]) { 37862306a36Sopenharmony_ci vqs[i] = NULL; 37962306a36Sopenharmony_ci continue; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci vqs[i] = vp_setup_vq(vdev, queue_idx++, callbacks[i], names[i], 38262306a36Sopenharmony_ci ctx ? ctx[i] : false, 38362306a36Sopenharmony_ci VIRTIO_MSI_NO_VECTOR); 38462306a36Sopenharmony_ci if (IS_ERR(vqs[i])) { 38562306a36Sopenharmony_ci err = PTR_ERR(vqs[i]); 38662306a36Sopenharmony_ci goto out_del_vqs; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return 0; 39162306a36Sopenharmony_ciout_del_vqs: 39262306a36Sopenharmony_ci vp_del_vqs(vdev); 39362306a36Sopenharmony_ci return err; 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* the config->find_vqs() implementation */ 39762306a36Sopenharmony_ciint vp_find_vqs(struct virtio_device *vdev, unsigned int nvqs, 39862306a36Sopenharmony_ci struct virtqueue *vqs[], vq_callback_t *callbacks[], 39962306a36Sopenharmony_ci const char * const names[], const bool *ctx, 40062306a36Sopenharmony_ci struct irq_affinity *desc) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int err; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Try MSI-X with one vector per queue. */ 40562306a36Sopenharmony_ci err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, true, ctx, desc); 40662306a36Sopenharmony_ci if (!err) 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci /* Fallback: MSI-X with one vector for config, one shared for queues. */ 40962306a36Sopenharmony_ci err = vp_find_vqs_msix(vdev, nvqs, vqs, callbacks, names, false, ctx, desc); 41062306a36Sopenharmony_ci if (!err) 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci /* Is there an interrupt? If not give up. */ 41362306a36Sopenharmony_ci if (!(to_vp_device(vdev)->pci_dev->irq)) 41462306a36Sopenharmony_ci return err; 41562306a36Sopenharmony_ci /* Finally fall back to regular interrupts. */ 41662306a36Sopenharmony_ci return vp_find_vqs_intx(vdev, nvqs, vqs, callbacks, names, ctx); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciconst char *vp_bus_name(struct virtio_device *vdev) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return pci_name(vp_dev->pci_dev); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci/* Setup the affinity for a virtqueue: 42762306a36Sopenharmony_ci * - force the affinity for per vq vector 42862306a36Sopenharmony_ci * - OR over all affinities for shared MSI 42962306a36Sopenharmony_ci * - ignore the affinity request if we're using INTX 43062306a36Sopenharmony_ci */ 43162306a36Sopenharmony_ciint vp_set_vq_affinity(struct virtqueue *vq, const struct cpumask *cpu_mask) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct virtio_device *vdev = vq->vdev; 43462306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 43562306a36Sopenharmony_ci struct virtio_pci_vq_info *info = vp_dev->vqs[vq->index]; 43662306a36Sopenharmony_ci struct cpumask *mask; 43762306a36Sopenharmony_ci unsigned int irq; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!vq->callback) 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (vp_dev->msix_enabled) { 44362306a36Sopenharmony_ci mask = vp_dev->msix_affinity_masks[info->msix_vector]; 44462306a36Sopenharmony_ci irq = pci_irq_vector(vp_dev->pci_dev, info->msix_vector); 44562306a36Sopenharmony_ci if (!cpu_mask) 44662306a36Sopenharmony_ci irq_set_affinity_hint(irq, NULL); 44762306a36Sopenharmony_ci else { 44862306a36Sopenharmony_ci cpumask_copy(mask, cpu_mask); 44962306a36Sopenharmony_ci irq_set_affinity_hint(irq, mask); 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ciconst struct cpumask *vp_get_vq_affinity(struct virtio_device *vdev, int index) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (!vp_dev->per_vq_vectors || 46062306a36Sopenharmony_ci vp_dev->vqs[index]->msix_vector == VIRTIO_MSI_NO_VECTOR) 46162306a36Sopenharmony_ci return NULL; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return pci_irq_get_affinity(vp_dev->pci_dev, 46462306a36Sopenharmony_ci vp_dev->vqs[index]->msix_vector); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 46862306a36Sopenharmony_cistatic int virtio_pci_freeze(struct device *dev) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 47162306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); 47262306a36Sopenharmony_ci int ret; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci ret = virtio_device_freeze(&vp_dev->vdev); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci if (!ret) 47762306a36Sopenharmony_ci pci_disable_device(pci_dev); 47862306a36Sopenharmony_ci return ret; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int virtio_pci_restore(struct device *dev) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 48462306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); 48562306a36Sopenharmony_ci int ret; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = pci_enable_device(pci_dev); 48862306a36Sopenharmony_ci if (ret) 48962306a36Sopenharmony_ci return ret; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci pci_set_master(pci_dev); 49262306a36Sopenharmony_ci return virtio_device_restore(&vp_dev->vdev); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic const struct dev_pm_ops virtio_pci_pm_ops = { 49662306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(virtio_pci_freeze, virtio_pci_restore) 49762306a36Sopenharmony_ci}; 49862306a36Sopenharmony_ci#endif 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci/* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */ 50262306a36Sopenharmony_cistatic const struct pci_device_id virtio_pci_id_table[] = { 50362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) }, 50462306a36Sopenharmony_ci { 0 } 50562306a36Sopenharmony_ci}; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, virtio_pci_id_table); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic void virtio_pci_release_dev(struct device *_d) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct virtio_device *vdev = dev_to_virtio(_d); 51262306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = to_vp_device(vdev); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci /* As struct device is a kobject, it's not safe to 51562306a36Sopenharmony_ci * free the memory (including the reference counter itself) 51662306a36Sopenharmony_ci * until it's release callback. */ 51762306a36Sopenharmony_ci kfree(vp_dev); 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int virtio_pci_probe(struct pci_dev *pci_dev, 52162306a36Sopenharmony_ci const struct pci_device_id *id) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci struct virtio_pci_device *vp_dev, *reg_dev = NULL; 52462306a36Sopenharmony_ci int rc; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* allocate our structure and fill it out */ 52762306a36Sopenharmony_ci vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL); 52862306a36Sopenharmony_ci if (!vp_dev) 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci pci_set_drvdata(pci_dev, vp_dev); 53262306a36Sopenharmony_ci vp_dev->vdev.dev.parent = &pci_dev->dev; 53362306a36Sopenharmony_ci vp_dev->vdev.dev.release = virtio_pci_release_dev; 53462306a36Sopenharmony_ci vp_dev->pci_dev = pci_dev; 53562306a36Sopenharmony_ci INIT_LIST_HEAD(&vp_dev->virtqueues); 53662306a36Sopenharmony_ci spin_lock_init(&vp_dev->lock); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* enable the device */ 53962306a36Sopenharmony_ci rc = pci_enable_device(pci_dev); 54062306a36Sopenharmony_ci if (rc) 54162306a36Sopenharmony_ci goto err_enable_device; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci if (force_legacy) { 54462306a36Sopenharmony_ci rc = virtio_pci_legacy_probe(vp_dev); 54562306a36Sopenharmony_ci /* Also try modern mode if we can't map BAR0 (no IO space). */ 54662306a36Sopenharmony_ci if (rc == -ENODEV || rc == -ENOMEM) 54762306a36Sopenharmony_ci rc = virtio_pci_modern_probe(vp_dev); 54862306a36Sopenharmony_ci if (rc) 54962306a36Sopenharmony_ci goto err_probe; 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci rc = virtio_pci_modern_probe(vp_dev); 55262306a36Sopenharmony_ci if (rc == -ENODEV) 55362306a36Sopenharmony_ci rc = virtio_pci_legacy_probe(vp_dev); 55462306a36Sopenharmony_ci if (rc) 55562306a36Sopenharmony_ci goto err_probe; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci pci_set_master(pci_dev); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci rc = register_virtio_device(&vp_dev->vdev); 56162306a36Sopenharmony_ci reg_dev = vp_dev; 56262306a36Sopenharmony_ci if (rc) 56362306a36Sopenharmony_ci goto err_register; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci return 0; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cierr_register: 56862306a36Sopenharmony_ci if (vp_dev->is_legacy) 56962306a36Sopenharmony_ci virtio_pci_legacy_remove(vp_dev); 57062306a36Sopenharmony_ci else 57162306a36Sopenharmony_ci virtio_pci_modern_remove(vp_dev); 57262306a36Sopenharmony_cierr_probe: 57362306a36Sopenharmony_ci pci_disable_device(pci_dev); 57462306a36Sopenharmony_cierr_enable_device: 57562306a36Sopenharmony_ci if (reg_dev) 57662306a36Sopenharmony_ci put_device(&vp_dev->vdev.dev); 57762306a36Sopenharmony_ci else 57862306a36Sopenharmony_ci kfree(vp_dev); 57962306a36Sopenharmony_ci return rc; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic void virtio_pci_remove(struct pci_dev *pci_dev) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); 58562306a36Sopenharmony_ci struct device *dev = get_device(&vp_dev->vdev.dev); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* 58862306a36Sopenharmony_ci * Device is marked broken on surprise removal so that virtio upper 58962306a36Sopenharmony_ci * layers can abort any ongoing operation. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ci if (!pci_device_is_present(pci_dev)) 59262306a36Sopenharmony_ci virtio_break_device(&vp_dev->vdev); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci pci_disable_sriov(pci_dev); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci unregister_virtio_device(&vp_dev->vdev); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (vp_dev->is_legacy) 59962306a36Sopenharmony_ci virtio_pci_legacy_remove(vp_dev); 60062306a36Sopenharmony_ci else 60162306a36Sopenharmony_ci virtio_pci_modern_remove(vp_dev); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci pci_disable_device(pci_dev); 60462306a36Sopenharmony_ci put_device(dev); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic int virtio_pci_sriov_configure(struct pci_dev *pci_dev, int num_vfs) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct virtio_pci_device *vp_dev = pci_get_drvdata(pci_dev); 61062306a36Sopenharmony_ci struct virtio_device *vdev = &vp_dev->vdev; 61162306a36Sopenharmony_ci int ret; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!(vdev->config->get_status(vdev) & VIRTIO_CONFIG_S_DRIVER_OK)) 61462306a36Sopenharmony_ci return -EBUSY; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (!__virtio_test_bit(vdev, VIRTIO_F_SR_IOV)) 61762306a36Sopenharmony_ci return -EINVAL; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci if (pci_vfs_assigned(pci_dev)) 62062306a36Sopenharmony_ci return -EPERM; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (num_vfs == 0) { 62362306a36Sopenharmony_ci pci_disable_sriov(pci_dev); 62462306a36Sopenharmony_ci return 0; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci ret = pci_enable_sriov(pci_dev, num_vfs); 62862306a36Sopenharmony_ci if (ret < 0) 62962306a36Sopenharmony_ci return ret; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci return num_vfs; 63262306a36Sopenharmony_ci} 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cistatic struct pci_driver virtio_pci_driver = { 63562306a36Sopenharmony_ci .name = "virtio-pci", 63662306a36Sopenharmony_ci .id_table = virtio_pci_id_table, 63762306a36Sopenharmony_ci .probe = virtio_pci_probe, 63862306a36Sopenharmony_ci .remove = virtio_pci_remove, 63962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 64062306a36Sopenharmony_ci .driver.pm = &virtio_pci_pm_ops, 64162306a36Sopenharmony_ci#endif 64262306a36Sopenharmony_ci .sriov_configure = virtio_pci_sriov_configure, 64362306a36Sopenharmony_ci}; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cimodule_pci_driver(virtio_pci_driver); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ciMODULE_AUTHOR("Anthony Liguori <aliguori@us.ibm.com>"); 64862306a36Sopenharmony_ciMODULE_DESCRIPTION("virtio-pci"); 64962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 65062306a36Sopenharmony_ciMODULE_VERSION("1"); 651