Commit 47e0ab3f authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/msi' into next

* pci/msi:
  PCI/MSI: Make pci_enable_msi/msix() 'nvec' argument type as int
  PCI/MSI: Return -ENOSYS for unimplemented interfaces, not -1
  PCI/MSI: Return msix_capability_init() failure if populate_msi_sysfs() fails
  s390/PCI: Remove superfluous check of MSI type
  s390/PCI: Fix single MSI only check
  PCI/MSI: Export MSI mode using attributes, not kobjects
parents f72e1112 52179dc9
...@@ -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
......
...@@ -129,7 +129,7 @@ call to succeed. ...@@ -129,7 +129,7 @@ call to succeed.
4.2.3 pci_enable_msi_block_auto 4.2.3 pci_enable_msi_block_auto
int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *count) int pci_enable_msi_block_auto(struct pci_dev *dev, int *count)
This variation on pci_enable_msi() call allows a device driver to request This variation on pci_enable_msi() call allows a device driver to request
the maximum possible number of MSIs. The MSI specification only allows the maximum possible number of MSIs. The MSI specification only allows
......
...@@ -407,8 +407,8 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) ...@@ -407,8 +407,8 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
struct msi_msg msg; struct msi_msg msg;
int rc; int rc;
if (type != PCI_CAP_ID_MSIX && type != PCI_CAP_ID_MSI) if (type == PCI_CAP_ID_MSI && nvec > 1)
return -EINVAL; return 1;
msi_vecs = min(nvec, ZPCI_MSI_VEC_MAX); msi_vecs = min(nvec, ZPCI_MSI_VEC_MAX);
msi_vecs = min_t(unsigned int, msi_vecs, CONFIG_PCI_NR_MSI); msi_vecs = min_t(unsigned int, msi_vecs, CONFIG_PCI_NR_MSI);
......
...@@ -372,6 +372,9 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) ...@@ -372,6 +372,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;
...@@ -407,6 +410,22 @@ static void free_msi_irqs(struct pci_dev *dev) ...@@ -407,6 +410,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)
...@@ -480,94 +499,95 @@ void pci_restore_msi_state(struct pci_dev *dev) ...@@ -480,94 +499,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)
return -ENOMEM;
list_for_each_entry(entry, &pdev->msi_list, list) { list_for_each_entry(entry, &pdev->msi_list, list) {
kobj = &entry->kobj; ++num_msi;
kobj->kset = pdev->msi_kset; }
pci_dev_get(pdev); if (!num_msi)
ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, return 0;
"%u", entry->irq);
if (ret)
goto out_unroll;
count++; /* 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) {
char *name = kmalloc(20, GFP_KERNEL);
msi_dev_attr = kzalloc(sizeof(*msi_dev_attr), GFP_KERNEL);
if (!msi_dev_attr)
goto error_attrs;
sprintf(name, "%d", entry->irq);
sysfs_attr_init(&msi_dev_attr->attr);
msi_dev_attr->attr.name = name;
msi_dev_attr->attr.mode = S_IRUGO;
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;
} }
...@@ -738,7 +758,7 @@ static int msix_capability_init(struct pci_dev *dev, ...@@ -738,7 +758,7 @@ static int msix_capability_init(struct pci_dev *dev,
ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX);
if (ret) if (ret)
goto error; goto out_avail;
/* /*
* Some devices require MSI-X to be enabled before we can touch the * Some devices require MSI-X to be enabled before we can touch the
...@@ -751,10 +771,8 @@ static int msix_capability_init(struct pci_dev *dev, ...@@ -751,10 +771,8 @@ static int msix_capability_init(struct pci_dev *dev,
msix_program_entries(dev, entries); msix_program_entries(dev, entries);
ret = populate_msi_sysfs(dev); ret = populate_msi_sysfs(dev);
if (ret) { if (ret)
ret = 0; goto out_free;
goto error;
}
/* 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);
...@@ -765,7 +783,7 @@ static int msix_capability_init(struct pci_dev *dev, ...@@ -765,7 +783,7 @@ static int msix_capability_init(struct pci_dev *dev,
return 0; return 0;
error: out_avail:
if (ret < 0) { if (ret < 0) {
/* /*
* If we had some success, report the number of irqs * If we had some success, report the number of irqs
...@@ -782,6 +800,7 @@ static int msix_capability_init(struct pci_dev *dev, ...@@ -782,6 +800,7 @@ static int msix_capability_init(struct pci_dev *dev,
ret = avail; ret = avail;
} }
out_free:
free_msi_irqs(dev); free_msi_irqs(dev);
return ret; return ret;
...@@ -845,7 +864,7 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) ...@@ -845,7 +864,7 @@ static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type)
* updates the @dev's irq member to the lowest new interrupt number; the * updates the @dev's irq member to the lowest new interrupt number; the
* other interrupt numbers allocated to this device are consecutive. * other interrupt numbers allocated to this device are consecutive.
*/ */
int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) int pci_enable_msi_block(struct pci_dev *dev, int nvec)
{ {
int status, maxvec; int status, maxvec;
u16 msgctl; u16 msgctl;
...@@ -876,7 +895,7 @@ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) ...@@ -876,7 +895,7 @@ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec)
} }
EXPORT_SYMBOL(pci_enable_msi_block); EXPORT_SYMBOL(pci_enable_msi_block);
int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec)
{ {
int ret, nvec; int ret, nvec;
u16 msgctl; u16 msgctl;
...@@ -934,8 +953,6 @@ void pci_disable_msi(struct pci_dev *dev) ...@@ -934,8 +953,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);
...@@ -1032,8 +1049,6 @@ void pci_disable_msix(struct pci_dev *dev) ...@@ -1032,8 +1049,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);
......
...@@ -352,7 +352,7 @@ struct pci_dev { ...@@ -352,7 +352,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
...@@ -1167,15 +1167,15 @@ struct msix_entry { ...@@ -1167,15 +1167,15 @@ struct msix_entry {
#ifndef CONFIG_PCI_MSI #ifndef CONFIG_PCI_MSI
static inline int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) static inline int pci_enable_msi_block(struct pci_dev *dev, int nvec)
{ {
return -1; return -ENOSYS;
} }
static inline int static inline int
pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec) pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec)
{ {
return -1; return -ENOSYS;
} }
static inline void pci_msi_shutdown(struct pci_dev *dev) static inline void pci_msi_shutdown(struct pci_dev *dev)
...@@ -1190,7 +1190,7 @@ static inline int pci_msix_table_size(struct pci_dev *dev) ...@@ -1190,7 +1190,7 @@ static inline int pci_msix_table_size(struct pci_dev *dev)
static inline int pci_enable_msix(struct pci_dev *dev, static inline int pci_enable_msix(struct pci_dev *dev,
struct msix_entry *entries, int nvec) struct msix_entry *entries, int nvec)
{ {
return -1; return -ENOSYS;
} }
static inline void pci_msix_shutdown(struct pci_dev *dev) static inline void pci_msix_shutdown(struct pci_dev *dev)
...@@ -1208,8 +1208,8 @@ static inline int pci_msi_enabled(void) ...@@ -1208,8 +1208,8 @@ static inline int pci_msi_enabled(void)
return 0; return 0;
} }
#else #else
int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec); int pci_enable_msi_block(struct pci_dev *dev, int nvec);
int pci_enable_msi_block_auto(struct pci_dev *dev, unsigned int *maxvec); int pci_enable_msi_block_auto(struct pci_dev *dev, int *maxvec);
void pci_msi_shutdown(struct pci_dev *dev); void pci_msi_shutdown(struct pci_dev *dev);
void pci_disable_msi(struct pci_dev *dev); void pci_disable_msi(struct pci_dev *dev);
int pci_msix_table_size(struct pci_dev *dev); int pci_msix_table_size(struct pci_dev *dev);
......
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