genirq/msi: Move MSI sysfs handling from PCI to MSI core
Move PCI's MSI sysfs code to the irq core so that other busses such as platform can reuse it. Signed-off-by: Barry Song <song.bao.hua@hisilicon.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20210813035628.6844-2-21cnbao@gmail.com
This commit is contained in:
parent
88ffe2d0a5
commit
2f170814bd
@ -363,9 +363,7 @@ static void free_msi_irqs(struct pci_dev *dev)
|
|||||||
{
|
{
|
||||||
struct list_head *msi_list = dev_to_msi_list(&dev->dev);
|
struct list_head *msi_list = dev_to_msi_list(&dev->dev);
|
||||||
struct msi_desc *entry, *tmp;
|
struct msi_desc *entry, *tmp;
|
||||||
struct attribute **msi_attrs;
|
int i;
|
||||||
struct device_attribute *dev_attr;
|
|
||||||
int i, count = 0;
|
|
||||||
|
|
||||||
for_each_pci_msi_entry(entry, dev)
|
for_each_pci_msi_entry(entry, dev)
|
||||||
if (entry->irq)
|
if (entry->irq)
|
||||||
@ -385,18 +383,7 @@ static void free_msi_irqs(struct pci_dev *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dev->msi_irq_groups) {
|
if (dev->msi_irq_groups) {
|
||||||
sysfs_remove_groups(&dev->dev.kobj, dev->msi_irq_groups);
|
msi_destroy_sysfs(&dev->dev, dev->msi_irq_groups);
|
||||||
msi_attrs = dev->msi_irq_groups[0]->attrs;
|
|
||||||
while (msi_attrs[count]) {
|
|
||||||
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;
|
dev->msi_irq_groups = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,102 +463,6 @@ 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,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
struct msi_desc *entry;
|
|
||||||
unsigned long irq;
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = kstrtoul(attr->attr.name, 10, &irq);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
entry = irq_get_msi_desc(irq);
|
|
||||||
if (!entry)
|
|
||||||
return -ENODEV;
|
|
||||||
|
|
||||||
return sysfs_emit(buf, "%s\n",
|
|
||||||
entry->msi_attrib.is_msix ? "msix" : "msi");
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
int ret = -ENOMEM;
|
|
||||||
int num_msi = 0;
|
|
||||||
int count = 0;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Determine how many msi entries we have */
|
|
||||||
for_each_pci_msi_entry(entry, pdev)
|
|
||||||
num_msi += entry->nvec_used;
|
|
||||||
if (!num_msi)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Dynamically create the MSI attributes for the PCI device */
|
|
||||||
msi_attrs = kcalloc(num_msi + 1, sizeof(void *), GFP_KERNEL);
|
|
||||||
if (!msi_attrs)
|
|
||||||
return -ENOMEM;
|
|
||||||
for_each_pci_msi_entry(entry, pdev) {
|
|
||||||
for (i = 0; i < entry->nvec_used; i++) {
|
|
||||||
msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
|
|
||||||
if (!msi_dev_attr)
|
|
||||||
goto error_attrs;
|
|
||||||
msi_attrs[count] = &msi_dev_attr->attr;
|
|
||||||
|
|
||||||
sysfs_attr_init(&msi_dev_attr->attr);
|
|
||||||
msi_dev_attr->attr.name = kasprintf(GFP_KERNEL, "%d",
|
|
||||||
entry->irq + i);
|
|
||||||
if (!msi_dev_attr->attr.name)
|
|
||||||
goto error_attrs;
|
|
||||||
msi_dev_attr->attr.mode = S_IRUGO;
|
|
||||||
msi_dev_attr->show = msi_mode_show;
|
|
||||||
++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 = kcalloc(2, sizeof(void *), 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;
|
|
||||||
|
|
||||||
error_irq_groups:
|
|
||||||
kfree(msi_irq_groups);
|
|
||||||
error_irq_group:
|
|
||||||
kfree(msi_irq_group);
|
|
||||||
error_attrs:
|
|
||||||
count = 0;
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
kfree(msi_attrs);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct msi_desc *
|
static struct msi_desc *
|
||||||
msi_setup_entry(struct pci_dev *dev, int nvec, struct irq_affinity *affd)
|
msi_setup_entry(struct pci_dev *dev, int nvec, struct irq_affinity *affd)
|
||||||
{
|
{
|
||||||
@ -667,9 +558,11 @@ static int msi_capability_init(struct pci_dev *dev, int nvec,
|
|||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = populate_msi_sysfs(dev);
|
dev->msi_irq_groups = msi_populate_sysfs(&dev->dev);
|
||||||
if (ret)
|
if (IS_ERR(dev->msi_irq_groups)) {
|
||||||
|
ret = PTR_ERR(dev->msi_irq_groups);
|
||||||
goto err;
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set MSI enabled bits */
|
/* Set MSI enabled bits */
|
||||||
pci_intx_for_msi(dev, 0);
|
pci_intx_for_msi(dev, 0);
|
||||||
@ -834,9 +727,11 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries,
|
|||||||
|
|
||||||
msix_update_entries(dev, entries);
|
msix_update_entries(dev, entries);
|
||||||
|
|
||||||
ret = populate_msi_sysfs(dev);
|
dev->msi_irq_groups = msi_populate_sysfs(&dev->dev);
|
||||||
if (ret)
|
if (IS_ERR(dev->msi_irq_groups)) {
|
||||||
|
ret = PTR_ERR(dev->msi_irq_groups);
|
||||||
goto out_free;
|
goto out_free;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set MSI-X enabled bits and unmask the function */
|
/* Set MSI-X enabled bits and unmask the function */
|
||||||
pci_intx_for_msi(dev, 0);
|
pci_intx_for_msi(dev, 0);
|
||||||
|
@ -239,6 +239,10 @@ void __pci_write_msi_msg(struct msi_desc *entry, struct msi_msg *msg);
|
|||||||
void pci_msi_mask_irq(struct irq_data *data);
|
void pci_msi_mask_irq(struct irq_data *data);
|
||||||
void pci_msi_unmask_irq(struct irq_data *data);
|
void pci_msi_unmask_irq(struct irq_data *data);
|
||||||
|
|
||||||
|
const struct attribute_group **msi_populate_sysfs(struct device *dev);
|
||||||
|
void msi_destroy_sysfs(struct device *dev,
|
||||||
|
const struct attribute_group **msi_irq_groups);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The arch hooks to setup up msi irqs. Default functions are implemented
|
* The arch hooks to setup up msi irqs. Default functions are implemented
|
||||||
* as weak symbols so that they /can/ be overriden by architecture specific
|
* as weak symbols so that they /can/ be overriden by architecture specific
|
||||||
|
134
kernel/irq/msi.c
134
kernel/irq/msi.c
@ -14,6 +14,7 @@
|
|||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/msi.h>
|
#include <linux/msi.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
|
||||||
#include "internals.h"
|
#include "internals.h"
|
||||||
|
|
||||||
@ -71,6 +72,139 @@ void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(get_cached_msi_msg);
|
EXPORT_SYMBOL_GPL(get_cached_msi_msg);
|
||||||
|
|
||||||
|
static ssize_t msi_mode_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
char *buf)
|
||||||
|
{
|
||||||
|
struct msi_desc *entry;
|
||||||
|
bool is_msix = false;
|
||||||
|
unsigned long irq;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
retval = kstrtoul(attr->attr.name, 10, &irq);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
entry = irq_get_msi_desc(irq);
|
||||||
|
if (!entry)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (dev_is_pci(dev))
|
||||||
|
is_msix = entry->msi_attrib.is_msix;
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%s\n", is_msix ? "msix" : "msi");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* msi_populate_sysfs - Populate msi_irqs sysfs entries for devices
|
||||||
|
* @dev: The device(PCI, platform etc) who will get sysfs entries
|
||||||
|
*
|
||||||
|
* Return attribute_group ** so that specific bus MSI can save it to
|
||||||
|
* somewhere during initilizing msi irqs. If devices has no MSI irq,
|
||||||
|
* return NULL; if it fails to populate sysfs, return ERR_PTR
|
||||||
|
*/
|
||||||
|
const struct attribute_group **msi_populate_sysfs(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct attribute_group **msi_irq_groups;
|
||||||
|
struct attribute **msi_attrs, *msi_attr;
|
||||||
|
struct device_attribute *msi_dev_attr;
|
||||||
|
struct attribute_group *msi_irq_group;
|
||||||
|
struct msi_desc *entry;
|
||||||
|
int ret = -ENOMEM;
|
||||||
|
int num_msi = 0;
|
||||||
|
int count = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Determine how many msi entries we have */
|
||||||
|
for_each_msi_entry(entry, dev)
|
||||||
|
num_msi += entry->nvec_used;
|
||||||
|
if (!num_msi)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Dynamically create the MSI attributes for the device */
|
||||||
|
msi_attrs = kcalloc(num_msi + 1, sizeof(void *), GFP_KERNEL);
|
||||||
|
if (!msi_attrs)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
for_each_msi_entry(entry, dev) {
|
||||||
|
for (i = 0; i < entry->nvec_used; i++) {
|
||||||
|
msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
|
||||||
|
if (!msi_dev_attr)
|
||||||
|
goto error_attrs;
|
||||||
|
msi_attrs[count] = &msi_dev_attr->attr;
|
||||||
|
|
||||||
|
sysfs_attr_init(&msi_dev_attr->attr);
|
||||||
|
msi_dev_attr->attr.name = kasprintf(GFP_KERNEL, "%d",
|
||||||
|
entry->irq + i);
|
||||||
|
if (!msi_dev_attr->attr.name)
|
||||||
|
goto error_attrs;
|
||||||
|
msi_dev_attr->attr.mode = 0444;
|
||||||
|
msi_dev_attr->show = msi_mode_show;
|
||||||
|
++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 = kcalloc(2, sizeof(void *), GFP_KERNEL);
|
||||||
|
if (!msi_irq_groups)
|
||||||
|
goto error_irq_group;
|
||||||
|
msi_irq_groups[0] = msi_irq_group;
|
||||||
|
|
||||||
|
ret = sysfs_create_groups(&dev->kobj, msi_irq_groups);
|
||||||
|
if (ret)
|
||||||
|
goto error_irq_groups;
|
||||||
|
|
||||||
|
return msi_irq_groups;
|
||||||
|
|
||||||
|
error_irq_groups:
|
||||||
|
kfree(msi_irq_groups);
|
||||||
|
error_irq_group:
|
||||||
|
kfree(msi_irq_group);
|
||||||
|
error_attrs:
|
||||||
|
count = 0;
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
kfree(msi_attrs);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* msi_destroy_sysfs - Destroy msi_irqs sysfs entries for devices
|
||||||
|
* @dev: The device(PCI, platform etc) who will remove sysfs entries
|
||||||
|
* @msi_irq_groups: attribute_group for device msi_irqs entries
|
||||||
|
*/
|
||||||
|
void msi_destroy_sysfs(struct device *dev, const struct attribute_group **msi_irq_groups)
|
||||||
|
{
|
||||||
|
struct device_attribute *dev_attr;
|
||||||
|
struct attribute **msi_attrs;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
if (msi_irq_groups) {
|
||||||
|
sysfs_remove_groups(&dev->kobj, msi_irq_groups);
|
||||||
|
msi_attrs = msi_irq_groups[0]->attrs;
|
||||||
|
while (msi_attrs[count]) {
|
||||||
|
dev_attr = container_of(msi_attrs[count],
|
||||||
|
struct device_attribute, attr);
|
||||||
|
kfree(dev_attr->attr.name);
|
||||||
|
kfree(dev_attr);
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
kfree(msi_attrs);
|
||||||
|
kfree(msi_irq_groups[0]);
|
||||||
|
kfree(msi_irq_groups);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
|
#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
|
||||||
static inline void irq_chip_write_msi_msg(struct irq_data *data,
|
static inline void irq_chip_write_msi_msg(struct irq_data *data,
|
||||||
struct msi_msg *msg)
|
struct msi_msg *msg)
|
||||||
|
Loading…
Reference in New Issue
Block a user