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