diff options
| author | Neil Horman <nhorman@tuxdriver.com> | 2011-10-06 14:08:18 -0400 | 
|---|---|---|
| committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2011-12-05 10:21:44 -0800 | 
| commit | b50cac55bf859d5b2fdcc1803a553a251b703456 (patch) | |
| tree | bd0f0c9342f173754db228834ccb0f35fd3dff20 /drivers/pci/msi.c | |
| parent | 8e8da023f5af71662867729db5547dc54786093c (diff) | |
| download | olio-linux-3.10-b50cac55bf859d5b2fdcc1803a553a251b703456.tar.xz olio-linux-3.10-b50cac55bf859d5b2fdcc1803a553a251b703456.zip  | |
PCI/sysfs: add per pci device msi[x] irq listing (v5)
This patch adds a per-pci-device subdirectory in sysfs called:
/sys/bus/pci/devices/<device>/msi_irqs
This sub-directory exports the set of msi vectors allocated by a given
pci device, by creating a numbered sub-directory for each vector beneath
msi_irqs.  For each vector various attributes can be exported.
Currently the only attribute is called mode, which tracks the
operational mode of that vector (msi vs. msix)
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/msi.c')
| -rw-r--r-- | drivers/pci/msi.c | 111 | 
1 files changed, 111 insertions, 0 deletions
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 0e6d04d7ba4..e6b6b9c6702 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -323,6 +323,8 @@ static void free_msi_irqs(struct pci_dev *dev)  			if (list_is_last(&entry->list, &dev->msi_list))  				iounmap(entry->mask_base);  		} +		kobject_del(&entry->kobj); +		kobject_put(&entry->kobj);  		list_del(&entry->list);  		kfree(entry);  	} @@ -403,6 +405,98 @@ void pci_restore_msi_state(struct pci_dev *dev)  }  EXPORT_SYMBOL_GPL(pci_restore_msi_state); + +#define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr) +#define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj) + +struct msi_attribute { +	struct attribute        attr; +	ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr, +			char *buf); +	ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr, +			 const char *buf, size_t count); +}; + +static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr, +			     char *buf) +{ +	return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi"); +} + +static ssize_t msi_irq_attr_show(struct kobject *kobj, +				 struct attribute *attr, char *buf) +{ +	struct msi_attribute *attribute = to_msi_attr(attr); +	struct msi_desc *entry = to_msi_desc(kobj); + +	if (!attribute->show) +		return -EIO; + +	return attribute->show(entry, attribute, buf); +} + +static const struct sysfs_ops msi_irq_sysfs_ops = { +	.show = msi_irq_attr_show, +}; + +static struct msi_attribute mode_attribute = +	__ATTR(mode, S_IRUGO, show_msi_mode, NULL); + + +struct attribute *msi_irq_default_attrs[] = { +	&mode_attribute.attr, +	NULL +}; + +void msi_kobj_release(struct kobject *kobj) +{ +	struct msi_desc *entry = to_msi_desc(kobj); + +	pci_dev_put(entry->dev); +} + +static struct kobj_type msi_irq_ktype = { +	.release = msi_kobj_release, +	.sysfs_ops = &msi_irq_sysfs_ops, +	.default_attrs = msi_irq_default_attrs, +}; + +static int populate_msi_sysfs(struct pci_dev *pdev) +{ +	struct msi_desc *entry; +	struct kobject *kobj; +	int ret; +	int count = 0; + +	pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); +	if (!pdev->msi_kset) +		return -ENOMEM; + +	list_for_each_entry(entry, &pdev->msi_list, list) { +		kobj = &entry->kobj; +		kobj->kset = pdev->msi_kset; +		pci_dev_get(pdev); +		ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, +				     "%u", entry->irq); +		if (ret) +			goto out_unroll; + +		count++; +	} + +	return 0; + +out_unroll: +	list_for_each_entry(entry, &pdev->msi_list, list) { +		if (!count) +			break; +		kobject_del(&entry->kobj); +		kobject_put(&entry->kobj); +		count--; +	} +	return ret; +} +  /**   * msi_capability_init - configure device's MSI capability structure   * @dev: pointer to the pci_dev data structure of MSI device function @@ -454,6 +548,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)  		return ret;  	} +	ret = populate_msi_sysfs(dev); +	if (ret) { +		msi_mask_irq(entry, mask, ~mask); +		free_msi_irqs(dev); +		return ret; +	} +  	/* Set MSI enabled bits	 */  	pci_intx_for_msi(dev, 0);  	msi_set_enable(dev, pos, 1); @@ -574,6 +675,12 @@ static int msix_capability_init(struct pci_dev *dev,  	msix_program_entries(dev, entries); +	ret = populate_msi_sysfs(dev); +	if (ret) { +		ret = 0; +		goto error; +	} +  	/* Set MSI-X enabled bits and unmask the function */  	pci_intx_for_msi(dev, 0);  	dev->msix_enabled = 1; @@ -732,6 +839,8 @@ void pci_disable_msi(struct pci_dev *dev)  	pci_msi_shutdown(dev);  	free_msi_irqs(dev); +	kset_unregister(dev->msi_kset); +	dev->msi_kset = NULL;  }  EXPORT_SYMBOL(pci_disable_msi); @@ -830,6 +939,8 @@ void pci_disable_msix(struct pci_dev *dev)  	pci_msix_shutdown(dev);  	free_msi_irqs(dev); +	kset_unregister(dev->msi_kset); +	dev->msi_kset = NULL;  }  EXPORT_SYMBOL(pci_disable_msix);  |