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