I've made some more changes based on the feedback. I figured if the sysfs entries were removed before the msi_desc structs were deleted from msi_list then we wouldn't have to worry about synchronization. How does it look? What's the decision on the directory name? Thanks, -- Vinnie
>From 89928bd9145228ed7691c593b4f0369dd12b3929 Mon Sep 17 00:00:00 2001 From: Vincent Rizza <vinnie@xxxxxxx> Date: Thu, 13 Nov 2008 10:27:33 +1100 Subject: [PATCH] Add sysfs entry that displays MSI-X IRQs Creates a sysfs entry for each MSI-X vector. The name of the entry is based on the entry_nr attribute in msi_desc.msi_attrib. The file contains the MSI-X vector number corresponding to that entry_nr value. Signed-off-by: Vincent Rizza <vinnie@xxxxxxx> Signed-off-by: Brett Grandbois <brettg@xxxxxxx> Signed-off-by: Ken Sandars <ksandars@xxxxxxx> --- Documentation/ABI/testing/sysfs-bus-pci | 10 +++++ drivers/pci/msi.c | 62 +++++++++++++++++++++++++++++++ include/linux/msi.h | 4 ++ include/linux/pci.h | 1 + 4 files changed, 77 insertions(+), 0 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index ceddcff..9941233 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -9,3 +9,13 @@ Description: that some devices may have malformatted data. If the underlying VPD has a writable section then the corresponding section of this file will be writable. + +What: /sys/bus/pci/devices/.../msix_irq/<IRQ vector number> +Date: November 2008 +Contact: Vincent Rizza <vinnie@xxxxxxx> +Description: + If a pci device uses any MSI-X IRQs a new directory + is created called "msix_irq". The directory contains a + file for each MSI-X IRQ used. The filename is the number + stored in msi_desc.msi_attrib.entry_nr. The files contain + the MSI-X vector corresponding to their entry_nr number. diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 74801f7..c2c43df 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -25,6 +25,31 @@ static int pci_msi_enable = 1; +static ssize_t +pci_msix_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + /* kobj = msix_dir. The parent kobj is needed to get pci_dev */ + struct kobject *p_kobj = kobj->parent; + struct pci_dev *pdev = to_pci_dev(container_of(p_kobj, struct device, + kobj)); + struct msi_desc *entry; + unsigned long msi_index; + int ret = 0; + + ret = strict_strtoul(attr->attr.name, 10, &msi_index); + if (!ret) { + list_for_each_entry(entry, &pdev->msi_list, list) { + if (entry->msi_attrib.entry_nr == msi_index) { + ret = snprintf(buf, PAGE_SIZE, "%d\n", + entry->irq); + break; + } + } + } + + return ret; +} + /* Arch hooks */ int __attribute__ ((weak)) @@ -488,10 +513,32 @@ static int msix_capability_init(struct pci_dev *dev, return avail; } + /* create directory "msix_irq" */ + dev->msix_dir = kobject_create_and_add("msix_irq", &dev->dev.kobj); + if (!dev->msix_dir) { + msi_free_irqs(dev); + return -ENOMEM; + } + i = 0; list_for_each_entry(entry, &dev->msi_list, list) { entries[i].vector = entry->irq; set_irq_msi(entry->irq, entry); + + snprintf(entry->msix_fname, sizeof(entry->msix_fname), "%d", + entry->msi_attrib.entry_nr); + entry->sysfs_entry.attr.name = (const char *) entry->msix_fname; + entry->sysfs_entry.attr.mode = 0444; + entry->sysfs_entry.show = pci_msix_show; + + ret = sysfs_create_file(dev->msix_dir, + &entry->sysfs_entry.attr); + if (ret) { + /* Using name as a flag during clean-up */ + entry->sysfs_entry.attr.name = NULL; + msi_free_irqs(dev); + return ret; + } i++; } /* Set MSI-X enabled bits */ @@ -628,6 +675,14 @@ static int msi_free_irqs(struct pci_dev* dev) list_for_each_entry(entry, &dev->msi_list, list) { if (entry->irq) BUG_ON(irq_has_action(entry->irq)); + /* + * Only remove if it's been created. If the attribute's name is + * set then this particular "entry" corresponds to a sysfs + * file. + */ + if (entry->sysfs_entry.attr.name) + sysfs_remove_file(&dev->dev.kobj, + &entry->sysfs_entry.attr); } arch_teardown_msi_irqs(dev); @@ -645,6 +700,13 @@ static int msi_free_irqs(struct pci_dev* dev) kfree(entry); } + /* Also doesn't exist in MSI mode */ + if (dev->msix_dir) { + sysfs_remove_dir(dev->msix_dir); + kobject_put(dev->msix_dir); + dev->msix_dir = NULL; + } + return 0; } diff --git a/include/linux/msi.h b/include/linux/msi.h index 8f29392..242e068 100644 --- a/include/linux/msi.h +++ b/include/linux/msi.h @@ -33,6 +33,10 @@ struct msi_desc { void __iomem *mask_base; struct pci_dev *dev; + struct kobj_attribute sysfs_entry; + /* filename = IRQ */ + char msix_fname[16]; + /* Last set MSI message */ struct msi_msg msg; }; diff --git a/include/linux/pci.h b/include/linux/pci.h index c75b82b..6b818f7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -234,6 +234,7 @@ struct pci_dev { struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ #ifdef CONFIG_PCI_MSI struct list_head msi_list; + struct kobject *msix_dir; #endif struct pci_vpd *vpd; }; -- 1.5.4.5