Dave Jones 8c3a2d0
From: Neil Horman <nhorman@tuxdriver.com>
Dave Jones 8c3a2d0
Date: Thu, 6 Oct 2011 18:08:18 +0000 (-0400)
Dave Jones 8c3a2d0
Subject: PCI/sysfs: add per pci device msi[x] irq listing (v5)
Dave Jones 8c3a2d0
X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fjbarnes%2Fpci.git;a=commitdiff_plain;h=933aa5c1f69aa650f59ba783307fc7ed7cc5fafa
Dave Jones 8c3a2d0
Dave Jones 8c3a2d0
PCI/sysfs: add per pci device msi[x] irq listing (v5)
Dave Jones 8c3a2d0
Dave Jones 8c3a2d0
This patch adds a per-pci-device subdirectory in sysfs called:
Dave Jones 8c3a2d0
/sys/bus/pci/devices/<device>/msi_irqs
Dave Jones 8c3a2d0
Dave Jones 8c3a2d0
This sub-directory exports the set of msi vectors allocated by a given
Dave Jones 8c3a2d0
pci device, by creating a numbered sub-directory for each vector beneath
Dave Jones 8c3a2d0
msi_irqs.  For each vector various attributes can be exported.
Dave Jones 8c3a2d0
Currently the only attribute is called mode, which tracks the
Dave Jones 8c3a2d0
operational mode of that vector (msi vs. msix)
Dave Jones 8c3a2d0
Dave Jones 8c3a2d0
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Dave Jones 8c3a2d0
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Dave Jones 8c3a2d0
---
Dave Jones 8c3a2d0
Dave Jones 8c3a2d0
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
Dave Jones 8c3a2d0
index 349ecf2..34f5110 100644
Dave Jones 8c3a2d0
--- a/Documentation/ABI/testing/sysfs-bus-pci
Dave Jones 8c3a2d0
+++ b/Documentation/ABI/testing/sysfs-bus-pci
Dave Jones 8c3a2d0
@@ -66,6 +66,24 @@ Description:
Dave Jones 8c3a2d0
 		re-discover previously removed devices.
Dave Jones 8c3a2d0
 		Depends on CONFIG_HOTPLUG.
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
+What:		/sys/bus/pci/devices/.../msi_irqs/
Dave Jones 8c3a2d0
+Date:		September, 2011
Dave Jones 8c3a2d0
+Contact:	Neil Horman <nhorman@tuxdriver.com>
Dave Jones 8c3a2d0
+Description:
Dave Jones 8c3a2d0
+		The /sys/devices/.../msi_irqs directory contains a variable set
Dave Jones 8c3a2d0
+		of sub-directories, with each sub-directory being named after a
Dave Jones 8c3a2d0
+		corresponding msi irq vector allocated to that device.  Each
Dave Jones 8c3a2d0
+		numbered sub-directory N contains attributes of that irq.
Dave Jones 8c3a2d0
+		Note that this directory is not created for device drivers which
Dave Jones 8c3a2d0
+		do not support msi irqs
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+What:		/sys/bus/pci/devices/.../msi_irqs/<N>/mode
Dave Jones 8c3a2d0
+Date:		September 2011
Dave Jones 8c3a2d0
+Contact:	Neil Horman <nhorman@tuxdriver.com>
Dave Jones 8c3a2d0
+Description:
Dave Jones 8c3a2d0
+		This attribute indicates the mode that the irq vector named by
Dave Jones 8c3a2d0
+		the parent directory is in (msi vs. msix)
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
 What:		/sys/bus/pci/devices/.../remove
Dave Jones 8c3a2d0
 Date:		January 2009
Dave Jones 8c3a2d0
 Contact:	Linux PCI developers <linux-pci@vger.kernel.org>
Dave Jones 8c3a2d0
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
Dave Jones 8c3a2d0
index 2f10328..73613e2 100644
Dave Jones 8c3a2d0
--- a/drivers/pci/msi.c
Dave Jones 8c3a2d0
+++ b/drivers/pci/msi.c
Dave Jones 8c3a2d0
@@ -322,6 +322,8 @@ static void free_msi_irqs(struct pci_dev *dev)
Dave Jones 8c3a2d0
 			if (list_is_last(&entry->list, &dev->msi_list))
Dave Jones 8c3a2d0
 				iounmap(entry->mask_base);
Dave Jones 8c3a2d0
 		}
Dave Jones 8c3a2d0
+		kobject_del(&entry->kobj);
Dave Jones 8c3a2d0
+		kobject_put(&entry->kobj);
Dave Jones 8c3a2d0
 		list_del(&entry->list);
Dave Jones 8c3a2d0
 		kfree(entry);
Dave Jones 8c3a2d0
 	}
Dave Jones 8c3a2d0
@@ -402,6 +404,98 @@ void pci_restore_msi_state(struct pci_dev *dev)
Dave Jones 8c3a2d0
 }
Dave Jones 8c3a2d0
 EXPORT_SYMBOL_GPL(pci_restore_msi_state);
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+#define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr)
Dave Jones 8c3a2d0
+#define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj)
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+struct msi_attribute {
Dave Jones 8c3a2d0
+	struct attribute        attr;
Dave Jones 8c3a2d0
+	ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr,
Dave Jones 8c3a2d0
+			char *buf);
Dave Jones 8c3a2d0
+	ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr,
Dave Jones 8c3a2d0
+			 const char *buf, size_t count);
Dave Jones 8c3a2d0
+};
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr,
Dave Jones 8c3a2d0
+			     char *buf)
Dave Jones 8c3a2d0
+{
Dave Jones 8c3a2d0
+	return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi");
Dave Jones 8c3a2d0
+}
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+static ssize_t msi_irq_attr_show(struct kobject *kobj,
Dave Jones 8c3a2d0
+				 struct attribute *attr, char *buf)
Dave Jones 8c3a2d0
+{
Dave Jones 8c3a2d0
+	struct msi_attribute *attribute = to_msi_attr(attr);
Dave Jones 8c3a2d0
+	struct msi_desc *entry = to_msi_desc(kobj);
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+	if (!attribute->show)
Dave Jones 8c3a2d0
+		return -EIO;
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+	return attribute->show(entry, attribute, buf);
Dave Jones 8c3a2d0
+}
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+static const struct sysfs_ops msi_irq_sysfs_ops = {
Dave Jones 8c3a2d0
+	.show = msi_irq_attr_show,
Dave Jones 8c3a2d0
+};
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+static struct msi_attribute mode_attribute =
Dave Jones 8c3a2d0
+	__ATTR(mode, S_IRUGO, show_msi_mode, NULL);
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+struct attribute *msi_irq_default_attrs[] = {
Dave Jones 8c3a2d0
+	&mode_attribute.attr,
Dave Jones 8c3a2d0
+	NULL
Dave Jones 8c3a2d0
+};
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+void msi_kobj_release(struct kobject *kobj)
Dave Jones 8c3a2d0
+{
Dave Jones 8c3a2d0
+	struct msi_desc *entry = to_msi_desc(kobj);
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+	pci_dev_put(entry->dev);
Dave Jones 8c3a2d0
+}
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+static struct kobj_type msi_irq_ktype = {
Dave Jones 8c3a2d0
+	.release = msi_kobj_release,
Dave Jones 8c3a2d0
+	.sysfs_ops = &msi_irq_sysfs_ops,
Dave Jones 8c3a2d0
+	.default_attrs = msi_irq_default_attrs,
Dave Jones 8c3a2d0
+};
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+static int populate_msi_sysfs(struct pci_dev *pdev)
Dave Jones 8c3a2d0
+{
Dave Jones 8c3a2d0
+	struct msi_desc *entry;
Dave Jones 8c3a2d0
+	struct kobject *kobj;
Dave Jones 8c3a2d0
+	int ret;
Dave Jones 8c3a2d0
+	int count = 0;
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+	pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj);
Dave Jones 8c3a2d0
+	if (!pdev->msi_kset)
Dave Jones 8c3a2d0
+		return -ENOMEM;
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+	list_for_each_entry(entry, &pdev->msi_list, list) {
Dave Jones 8c3a2d0
+		kobj = &entry->kobj;
Dave Jones 8c3a2d0
+		kobj->kset = pdev->msi_kset;
Dave Jones 8c3a2d0
+		pci_dev_get(pdev);
Dave Jones 8c3a2d0
+		ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL,
Dave Jones 8c3a2d0
+				     "%u", entry->irq);
Dave Jones 8c3a2d0
+		if (ret)
Dave Jones 8c3a2d0
+			goto out_unroll;
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+		count++;
Dave Jones 8c3a2d0
+	}
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+	return 0;
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+out_unroll:
Dave Jones 8c3a2d0
+	list_for_each_entry(entry, &pdev->msi_list, list) {
Dave Jones 8c3a2d0
+		if (!count)
Dave Jones 8c3a2d0
+			break;
Dave Jones 8c3a2d0
+		kobject_del(&entry->kobj);
Dave Jones 8c3a2d0
+		kobject_put(&entry->kobj);
Dave Jones 8c3a2d0
+		count--;
Dave Jones 8c3a2d0
+	}
Dave Jones 8c3a2d0
+	return ret;
Dave Jones 8c3a2d0
+}
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
 /**
Dave Jones 8c3a2d0
  * msi_capability_init - configure device's MSI capability structure
Dave Jones 8c3a2d0
  * @dev: pointer to the pci_dev data structure of MSI device function
Dave Jones 8c3a2d0
@@ -453,6 +547,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec)
Dave Jones 8c3a2d0
 		return ret;
Dave Jones 8c3a2d0
 	}
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
+	ret = populate_msi_sysfs(dev);
Dave Jones 8c3a2d0
+	if (ret) {
Dave Jones 8c3a2d0
+		msi_mask_irq(entry, mask, ~mask);
Dave Jones 8c3a2d0
+		free_msi_irqs(dev);
Dave Jones 8c3a2d0
+		return ret;
Dave Jones 8c3a2d0
+	}
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
 	/* Set MSI enabled bits	 */
Dave Jones 8c3a2d0
 	pci_intx_for_msi(dev, 0);
Dave Jones 8c3a2d0
 	msi_set_enable(dev, pos, 1);
Dave Jones 8c3a2d0
@@ -573,6 +674,12 @@ static int msix_capability_init(struct pci_dev *dev,
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
 	msix_program_entries(dev, entries);
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
+	ret = populate_msi_sysfs(dev);
Dave Jones 8c3a2d0
+	if (ret) {
Dave Jones 8c3a2d0
+		ret = 0;
Dave Jones 8c3a2d0
+		goto error;
Dave Jones 8c3a2d0
+	}
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
 	/* Set MSI-X enabled bits and unmask the function */
Dave Jones 8c3a2d0
 	pci_intx_for_msi(dev, 0);
Dave Jones 8c3a2d0
 	dev->msix_enabled = 1;
Dave Jones 8c3a2d0
@@ -731,6 +838,8 @@ void pci_disable_msi(struct pci_dev *dev)
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
 	pci_msi_shutdown(dev);
Dave Jones 8c3a2d0
 	free_msi_irqs(dev);
Dave Jones 8c3a2d0
+	kset_unregister(dev->msi_kset);
Dave Jones 8c3a2d0
+	dev->msi_kset = NULL;
Dave Jones 8c3a2d0
 }
Dave Jones 8c3a2d0
 EXPORT_SYMBOL(pci_disable_msi);
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
@@ -829,6 +938,8 @@ void pci_disable_msix(struct pci_dev *dev)
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
 	pci_msix_shutdown(dev);
Dave Jones 8c3a2d0
 	free_msi_irqs(dev);
Dave Jones 8c3a2d0
+	kset_unregister(dev->msi_kset);
Dave Jones 8c3a2d0
+	dev->msi_kset = NULL;
Dave Jones 8c3a2d0
 }
Dave Jones 8c3a2d0
 EXPORT_SYMBOL(pci_disable_msix);
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
diff --git a/include/linux/msi.h b/include/linux/msi.h
Dave Jones 8c3a2d0
index 05acced..ce93a34 100644
Dave Jones 8c3a2d0
--- a/include/linux/msi.h
Dave Jones 8c3a2d0
+++ b/include/linux/msi.h
Dave Jones 8c3a2d0
@@ -1,6 +1,7 @@
Dave Jones 8c3a2d0
 #ifndef LINUX_MSI_H
Dave Jones 8c3a2d0
 #define LINUX_MSI_H
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
+#include <linux/kobject.h>
Dave Jones 8c3a2d0
 #include <linux/list.h>
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
 struct msi_msg {
Dave Jones 8c3a2d0
@@ -44,6 +45,8 @@ struct msi_desc {
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
 	/* Last set MSI message */
Dave Jones 8c3a2d0
 	struct msi_msg msg;
Dave Jones 8c3a2d0
+
Dave Jones 8c3a2d0
+	struct kobject kobj;
Dave Jones 8c3a2d0
 };
Dave Jones 8c3a2d0
 
Dave Jones 8c3a2d0
 /*
Dave Jones 8c3a2d0
diff --git a/include/linux/pci.h b/include/linux/pci.h
Dave Jones 8c3a2d0
index 7cda65b..84225c7 100644
Dave Jones 8c3a2d0
--- a/include/linux/pci.h
Dave Jones 8c3a2d0
+++ b/include/linux/pci.h
Dave Jones 8c3a2d0
@@ -336,6 +336,7 @@ struct pci_dev {
Dave Jones 8c3a2d0
 	struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
Dave Jones 8c3a2d0
 #ifdef CONFIG_PCI_MSI
Dave Jones 8c3a2d0
 	struct list_head msi_list;
Dave Jones 8c3a2d0
+	struct kset *msi_kset;
Dave Jones 8c3a2d0
 #endif
Dave Jones 8c3a2d0
 	struct pci_vpd *vpd;
Dave Jones 8c3a2d0
 #ifdef CONFIG_PCI_ATS