diff options
Diffstat (limited to 'drivers/pci/controller/vmd.c')
| -rw-r--r-- | drivers/pci/controller/vmd.c | 47 | 
1 files changed, 36 insertions, 11 deletions
diff --git a/drivers/pci/controller/vmd.c b/drivers/pci/controller/vmd.c index a5987e52700e..a45e8e59d3d4 100644 --- a/drivers/pci/controller/vmd.c +++ b/drivers/pci/controller/vmd.c @@ -6,6 +6,7 @@  #include <linux/device.h>  #include <linux/interrupt.h> +#include <linux/iommu.h>  #include <linux/irq.h>  #include <linux/kernel.h>  #include <linux/module.h> @@ -18,8 +19,6 @@  #include <linux/rcupdate.h>  #include <asm/irqdomain.h> -#include <asm/device.h> -#include <asm/msi.h>  #define VMD_CFGBAR	0  #define VMD_MEMBAR1	2 @@ -70,6 +69,8 @@ enum vmd_features {  	VMD_FEAT_CAN_BYPASS_MSI_REMAP		= (1 << 4),  }; +static DEFINE_IDA(vmd_instance_ida); +  /*   * Lock for manipulating VMD IRQ lists.   */ @@ -120,6 +121,8 @@ struct vmd_dev {  	struct pci_bus		*bus;  	u8			busn_start;  	u8			first_vec; +	char			*name; +	int			instance;  };  static inline struct vmd_dev *vmd_from_bus(struct pci_bus *bus) @@ -650,7 +653,7 @@ static int vmd_alloc_irqs(struct vmd_dev *vmd)  		INIT_LIST_HEAD(&vmd->irqs[i].irq_list);  		err = devm_request_irq(&dev->dev, pci_irq_vector(dev, i),  				       vmd_irq, IRQF_NO_THREAD, -				       "vmd", &vmd->irqs[i]); +				       vmd->name, &vmd->irqs[i]);  		if (err)  			return err;  	} @@ -761,7 +764,8 @@ static int vmd_enable_domain(struct vmd_dev *vmd, unsigned long features)  	 * acceptable because the guest is usually CPU-limited and MSI  	 * remapping doesn't become a performance bottleneck.  	 */ -	if (!(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) || +	if (iommu_capable(vmd->dev->dev.bus, IOMMU_CAP_INTR_REMAP) || +	    !(features & VMD_FEAT_CAN_BYPASS_MSI_REMAP) ||  	    offset[0] || offset[1]) {  		ret = vmd_alloc_irqs(vmd);  		if (ret) @@ -834,18 +838,32 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)  		return -ENOMEM;  	vmd->dev = dev; +	vmd->instance = ida_simple_get(&vmd_instance_ida, 0, 0, GFP_KERNEL); +	if (vmd->instance < 0) +		return vmd->instance; + +	vmd->name = kasprintf(GFP_KERNEL, "vmd%d", vmd->instance); +	if (!vmd->name) { +		err = -ENOMEM; +		goto out_release_instance; +	} +  	err = pcim_enable_device(dev);  	if (err < 0) -		return err; +		goto out_release_instance;  	vmd->cfgbar = pcim_iomap(dev, VMD_CFGBAR, 0); -	if (!vmd->cfgbar) -		return -ENOMEM; +	if (!vmd->cfgbar) { +		err = -ENOMEM; +		goto out_release_instance; +	}  	pci_set_master(dev);  	if (dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(64)) && -	    dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32))) -		return -ENODEV; +	    dma_set_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32))) { +		err = -ENODEV; +		goto out_release_instance; +	}  	if (features & VMD_FEAT_OFFSET_FIRST_VECTOR)  		vmd->first_vec = 1; @@ -854,11 +872,16 @@ static int vmd_probe(struct pci_dev *dev, const struct pci_device_id *id)  	pci_set_drvdata(dev, vmd);  	err = vmd_enable_domain(vmd, features);  	if (err) -		return err; +		goto out_release_instance;  	dev_info(&vmd->dev->dev, "Bound to PCI domain %04x\n",  		 vmd->sysdata.domain);  	return 0; + + out_release_instance: +	ida_simple_remove(&vmd_instance_ida, vmd->instance); +	kfree(vmd->name); +	return err;  }  static void vmd_cleanup_srcu(struct vmd_dev *vmd) @@ -879,6 +902,8 @@ static void vmd_remove(struct pci_dev *dev)  	vmd_cleanup_srcu(vmd);  	vmd_detach_resources(vmd);  	vmd_remove_irq_domain(vmd); +	ida_simple_remove(&vmd_instance_ida, vmd->instance); +	kfree(vmd->name);  }  #ifdef CONFIG_PM_SLEEP @@ -903,7 +928,7 @@ static int vmd_resume(struct device *dev)  	for (i = 0; i < vmd->msix_count; i++) {  		err = devm_request_irq(dev, pci_irq_vector(pdev, i),  				       vmd_irq, IRQF_NO_THREAD, -				       "vmd", &vmd->irqs[i]); +				       vmd->name, &vmd->irqs[i]);  		if (err)  			return err;  	}  |