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

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
Loading
Loading
Loading
Loading
+4 −7
Original line number Original line Diff line number Diff line
@@ -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
+1 −1
Original line number Original line Diff line number Diff line
@@ -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
+2 −2
Original line number Original line Diff line number Diff line
@@ -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);


+98 −83
Original line number Original line Diff line number Diff line
@@ -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)
		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)
}
}
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)
	retval = kstrtoul(attr->attr.name, 10, &irq);
		return -EIO;
	if (retval)
		return retval;


	return attribute->show(entry, attribute, buf);
	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");
		}
		}

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)
{
	struct msi_desc *entry = to_msi_desc(kobj);

	pci_dev_put(entry->dev);
	}
	}

	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);
		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)
	if (ret)
			goto out_unroll;
		goto error_irq_groups;

	pdev->msi_irq_groups = msi_irq_groups;
		count++;
	}


	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,


	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,
	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,


	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,
			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)
 * 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)
}
}
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)


	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)


	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);


+8 −8
Original line number Original line Diff line number Diff line
@@ -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 {




#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)
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)
	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);