Commit 1c51b50c authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman Committed by Bjorn Helgaas

PCI/MSI: Export MSI mode using attributes, not kobjects

The PCI MSI sysfs code is a mess with kobjects for things that don't really
need to be kobjects.  This patch creates attributes dynamically for the MSI
interrupts instead of using kobjects.

Note, this removes a directory from sysfs.  Old MSI kobjects:

  pci_device
     └── msi_irqs
         └── 40
             └── mode

New MSI attributes:

  pci_device
     └── msi_irqs
         └── 40

As there was only one file "mode" with the kobject model, the interrupt
number is now a file that returns the "mode" of the interrupt (msi vs.
msix).
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
parent 6ce4eac1
...@@ -70,18 +70,15 @@ Date: September, 2011 ...@@ -70,18 +70,15 @@ Date: September, 2011
Contact: Neil Horman <nhorman@tuxdriver.com> Contact: Neil Horman <nhorman@tuxdriver.com>
Description: Description:
The /sys/devices/.../msi_irqs directory contains a variable set The /sys/devices/.../msi_irqs directory contains a variable set
of sub-directories, with each sub-directory being named after a of files, with each file being named after a corresponding msi
corresponding msi irq vector allocated to that device. Each irq vector allocated to that device.
numbered sub-directory N contains attributes of that irq.
Note that this directory is not created for device drivers which
do not support msi irqs
What: /sys/bus/pci/devices/.../msi_irqs/<N>/mode What: /sys/bus/pci/devices/.../msi_irqs/<N>
Date: September 2011 Date: September 2011
Contact: Neil Horman <nhorman@tuxdriver.com> Contact: Neil Horman <nhorman@tuxdriver.com>
Description: Description:
This attribute indicates the mode that the irq vector named by This attribute indicates the mode that the irq vector named by
the parent directory is in (msi vs. msix) the file is in (msi vs. msix)
What: /sys/bus/pci/devices/.../remove What: /sys/bus/pci/devices/.../remove
Date: January 2009 Date: January 2009
......
...@@ -363,6 +363,9 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) ...@@ -363,6 +363,9 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg)
static void free_msi_irqs(struct pci_dev *dev) static void free_msi_irqs(struct pci_dev *dev)
{ {
struct msi_desc *entry, *tmp; struct msi_desc *entry, *tmp;
struct attribute **msi_attrs;
struct device_attribute *dev_attr;
int count = 0;
list_for_each_entry(entry, &dev->msi_list, list) { list_for_each_entry(entry, &dev->msi_list, list) {
int i, nvec; int i, nvec;
...@@ -398,6 +401,22 @@ static void free_msi_irqs(struct pci_dev *dev) ...@@ -398,6 +401,22 @@ static void free_msi_irqs(struct pci_dev *dev)
list_del(&entry->list); list_del(&entry->list);
kfree(entry); kfree(entry);
} }
if (dev->msi_irq_groups) {
sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups);
msi_attrs = dev->msi_irq_groups[0]->attrs;
list_for_each_entry(entry, &dev->msi_list, list) {
dev_attr = container_of(msi_attrs[count],
struct device_attribute, attr);
kfree(dev_attr->attr.name);
kfree(dev_attr);
++count;
}
kfree(msi_attrs);
kfree(dev->msi_irq_groups[0]);
kfree(dev->msi_irq_groups);
dev->msi_irq_groups = NULL;
}
} }
static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) static struct msi_desc *alloc_msi_entry(struct pci_dev *dev)
...@@ -471,94 +490,95 @@ void pci_restore_msi_state(struct pci_dev *dev) ...@@ -471,94 +490,95 @@ void pci_restore_msi_state(struct pci_dev *dev)
} }
EXPORT_SYMBOL_GPL(pci_restore_msi_state); EXPORT_SYMBOL_GPL(pci_restore_msi_state);
static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr,
#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) char *buf)
{ {
return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi"); struct pci_dev *pdev = to_pci_dev(dev);
} struct msi_desc *entry;
unsigned long irq;
static ssize_t msi_irq_attr_show(struct kobject *kobj, int retval;
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);
static struct attribute *msi_irq_default_attrs[] = {
&mode_attribute.attr,
NULL
};
static void msi_kobj_release(struct kobject *kobj) retval = kstrtoul(attr->attr.name, 10, &irq);
{ if (retval)
struct msi_desc *entry = to_msi_desc(kobj); return retval;
pci_dev_put(entry->dev); list_for_each_entry(entry, &pdev->msi_list, list) {
if (entry->irq == irq) {
return sprintf(buf, "%s\n",
entry->msi_attrib.is_msix ? "msix" : "msi");
}
}
return -ENODEV;
} }
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) static int populate_msi_sysfs(struct pci_dev *pdev)
{ {
struct attribute **msi_attrs;
struct attribute *msi_attr;
struct device_attribute *msi_dev_attr;
struct attribute_group *msi_irq_group;
const struct attribute_group **msi_irq_groups;
struct msi_desc *entry; struct msi_desc *entry;
struct kobject *kobj; int ret = -ENOMEM;
int ret; int num_msi = 0;
int count = 0; int count = 0;
pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); /* Determine how many msi entries we have */
if (!pdev->msi_kset) list_for_each_entry(entry, &pdev->msi_list, list) {
return -ENOMEM; ++num_msi;
}
if (!num_msi)
return 0;
/* Dynamically create the MSI attributes for the PCI device */
msi_attrs = kzalloc(sizeof(void *) * (num_msi + 1), GFP_KERNEL);
if (!msi_attrs)
return -ENOMEM;
list_for_each_entry(entry, &pdev->msi_list, list) { list_for_each_entry(entry, &pdev->msi_list, list) {
kobj = &entry->kobj; char *name = kmalloc(20, GFP_KERNEL);
kobj->kset = pdev->msi_kset; msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
pci_dev_get(pdev); if (!msi_dev_attr)
ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, goto error_attrs;
"%u", entry->irq); sprintf(name, "%d", entry->irq);
if (ret) sysfs_attr_init(&msi_dev_attr->attr);
goto out_unroll; msi_dev_attr->attr.name = name;
msi_dev_attr->attr.mode = S_IRUGO;
count++; msi_dev_attr->show = msi_mode_show;
msi_attrs[count] = &msi_dev_attr->attr;
++count;
} }
msi_irq_group = kzalloc(sizeof(*msi_irq_group), GFP_KERNEL);
if (!msi_irq_group)
goto error_attrs;
msi_irq_group->name = "msi_irqs";
msi_irq_group->attrs = msi_attrs;
msi_irq_groups = kzalloc(sizeof(void *) * 2, GFP_KERNEL);
if (!msi_irq_groups)
goto error_irq_group;
msi_irq_groups[0] = msi_irq_group;
ret = sysfs_create_groups(&pdev->dev.kobj, msi_irq_groups);
if (ret)
goto error_irq_groups;
pdev->msi_irq_groups = msi_irq_groups;
return 0; return 0;
out_unroll: error_irq_groups:
list_for_each_entry(entry, &pdev->msi_list, list) { kfree(msi_irq_groups);
if (!count) error_irq_group:
break; kfree(msi_irq_group);
kobject_del(&entry->kobj); error_attrs:
kobject_put(&entry->kobj); count = 0;
count--; msi_attr = msi_attrs[count];
while (msi_attr) {
msi_dev_attr = container_of(msi_attr, struct device_attribute, attr);
kfree(msi_attr->name);
kfree(msi_dev_attr);
++count;
msi_attr = msi_attrs[count];
} }
return ret; return ret;
} }
...@@ -925,8 +945,6 @@ void pci_disable_msi(struct pci_dev *dev) ...@@ -925,8 +945,6 @@ void pci_disable_msi(struct pci_dev *dev)
pci_msi_shutdown(dev); pci_msi_shutdown(dev);
free_msi_irqs(dev); free_msi_irqs(dev);
kset_unregister(dev->msi_kset);
dev->msi_kset = NULL;
} }
EXPORT_SYMBOL(pci_disable_msi); EXPORT_SYMBOL(pci_disable_msi);
...@@ -1023,8 +1041,6 @@ void pci_disable_msix(struct pci_dev *dev) ...@@ -1023,8 +1041,6 @@ void pci_disable_msix(struct pci_dev *dev)
pci_msix_shutdown(dev); pci_msix_shutdown(dev);
free_msi_irqs(dev); free_msi_irqs(dev);
kset_unregister(dev->msi_kset);
dev->msi_kset = NULL;
} }
EXPORT_SYMBOL(pci_disable_msix); EXPORT_SYMBOL(pci_disable_msix);
......
...@@ -351,7 +351,7 @@ struct pci_dev { ...@@ -351,7 +351,7 @@ struct pci_dev {
struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */ struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
#ifdef CONFIG_PCI_MSI #ifdef CONFIG_PCI_MSI
struct list_head msi_list; struct list_head msi_list;
struct kset *msi_kset; const struct attribute_group **msi_irq_groups;
#endif #endif
struct pci_vpd *vpd; struct pci_vpd *vpd;
#ifdef CONFIG_PCI_ATS #ifdef CONFIG_PCI_ATS
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment