@@ -275,6 +275,9 @@ static void free_msi_irqs(struct pci_dev *dev)
{
struct msi_desc *entry, *tmp;
+ if (dev->msix_dir.name)
+ sysfs_remove_group(&dev->dev.kobj, &dev->msix_dir);
+
list_for_each_entry(entry, &dev->msi_list, list) {
int i, nvec;
if (!entry->irq)
@@ -447,13 +450,12 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned pos,
}
static int msix_setup_entries(struct pci_dev *dev, unsigned pos,
- void __iomem *base, struct msix_entry *entries,
- int nvec)
+ void __iomem *base, struct msix_entry *entries)
{
struct msi_desc *entry;
int i;
- for (i = 0; i < nvec; i++) {
+ for (i = 0; i < dev->nr_irqs; i++) {
entry = alloc_msi_entry(dev);
if (!entry) {
if (!i)
@@ -495,6 +497,64 @@ static void msix_program_entries(struct pci_dev *dev,
}
}
+struct pci_msix_attribute {
+ struct device_attribute da;
+ unsigned irq;
+ char name[8]; /* current max is 5: "2047\0" */
+};
+
+static ssize_t
+pci_msix_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_msix_attribute *msix_attr;
+ msix_attr = container_of(attr, struct pci_msix_attribute, da);
+ return snprintf(buf, PAGE_SIZE, "%u\n", msix_attr->irq);
+}
+
+#define kobj_to_pci_dev(obj) to_pci_dev(container_of(obj, struct device, kobj))
+
+static int msix_populate(struct dentry *dentry, struct attribute_group *grp)
+{
+ struct pci_dev *pdev = container_of(grp, struct pci_dev, msix_dir);
+ unsigned i, nr_irqs = pdev->nr_irqs;
+ struct pci_msix_attribute *attr;
+ struct attribute **array;
+ struct msi_desc *desc;
+
+ array = kmalloc((nr_irqs + 1) * sizeof(void *), GFP_KERNEL);
+ if (!array)
+ return -ENOMEM;
+ attr = kmalloc(nr_irqs * sizeof(*attr), GFP_KERNEL);
+ if (!attr)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_irqs; i++)
+ array[i] = &attr[i].da.attr;
+ array[i] = NULL;
+ grp->attrs = array;
+
+ list_for_each_entry(desc, &pdev->msi_list, list) {
+ attr->irq = desc->irq;
+ snprintf(attr->name, sizeof(attr->name), "%u",
+ desc->msi_attrib.entry_nr);
+ attr->da.attr.name = (const char *)&attr->name;
+ attr->da.attr.mode = 0444;
+ attr->da.show = pci_msix_show;
+ attr++;
+ }
+
+ return sysfs_populate_group(dentry, grp);
+}
+
+static void msix_depopulate(struct dentry *dentry, struct attribute_group *grp)
+{
+ sysfs_depopulate_group(dentry, grp);
+
+ kfree(grp->attrs[0]);
+ kfree(grp->attrs);
+ grp->attrs = NULL;
+}
+
/**
* msix_capability_init - configure device's MSI-X capability
* @dev: pointer to the pci_dev data structure of MSI-X device function
@@ -524,10 +584,19 @@ static int msix_capability_init(struct pci_dev *dev,
if (!base)
return -ENOMEM;
- ret = msix_setup_entries(dev, pos, base, entries, nvec);
+ dev->nr_irqs = nvec;
+
+ dev->msix_dir.name = "irqs";
+ dev->msix_dir.populate = msix_populate;
+ dev->msix_dir.depopulate = msix_depopulate;
+ ret = sysfs_create_group(&dev->dev.kobj, &dev->msix_dir);
if (ret)
return ret;
+ ret = msix_setup_entries(dev, pos, base, entries);
+ if (ret)
+ goto error;
+
ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
if (ret)
goto error;
@@ -102,6 +102,8 @@ static
int group_populate(struct dentry *dentry, struct sysfs_dirent *sd)
{
struct attribute_group *grp = sd->s_dir.data;
+ if (grp->populate)
+ return grp->populate(dentry, grp);
return sysfs_populate_group(dentry, grp);
}
@@ -109,7 +111,10 @@ static
void group_depopulate(struct dentry *dentry, struct sysfs_dirent *sd)
{
struct attribute_group *grp = sd->s_dir.data;
- sysfs_depopulate_group(dentry, grp);
+ if (grp->depopulate)
+ grp->depopulate(dentry, grp);
+ else
+ sysfs_depopulate_group(dentry, grp);
}
static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
@@ -291,6 +291,8 @@ 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 attribute_group msix_dir;
+ unsigned short nr_irqs;
#endif
struct pci_vpd *vpd;
#ifdef CONFIG_PCI_IOV
@@ -19,6 +19,7 @@
struct kobject;
struct module;
+struct dentry;
/* FIXME
* The *owner field is no longer used.
@@ -36,6 +37,10 @@ struct attribute_group {
mode_t (*is_visible)(struct kobject *,
struct attribute *, int);
struct attribute **attrs;
+ int (*populate)(struct dentry *,
+ struct attribute_group *);
+ void (*depopulate)(struct dentry *,
+ struct attribute_group *);
};
@@ -115,6 +120,10 @@ int sysfs_update_group(struct kobject *kobj,
const struct attribute_group *grp);
void sysfs_remove_group(struct kobject *kobj,
const struct attribute_group *grp);
+int sysfs_populate_group(struct dentry *dentry,
+ const struct attribute_group *grp);
+void sysfs_depopulate_group(struct dentry *dentry,
+ const struct attribute_group *grp);
int sysfs_add_file_to_group(struct kobject *kobj,
const struct attribute *attr, const char *group);
void sysfs_remove_file_from_group(struct kobject *kobj,