Commit 3c298b84 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/vpd'

- Ensure device is accessible before VPD access via sysfs (Alex Williamson)

- Ensure device doesn't go to a low-power state while we're polling for PME
  (Alex Williamson)

* pci/vpd:
  PCI: Fix runtime PM race with PME polling
  PCI/VPD: Add runtime power management to sysfs interface
parents 93a3241d d3fcd736
Loading
Loading
Loading
Loading
+16 −7
Original line number Diff line number Diff line
@@ -2420,10 +2420,13 @@ static void pci_pme_list_scan(struct work_struct *work)

	mutex_lock(&pci_pme_list_mutex);
	list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
		if (pme_dev->dev->pme_poll) {
			struct pci_dev *bridge;
		struct pci_dev *pdev = pme_dev->dev;

		if (pdev->pme_poll) {
			struct pci_dev *bridge = pdev->bus->self;
			struct device *dev = &pdev->dev;
			int pm_status;

			bridge = pme_dev->dev->bus->self;
			/*
			 * If bridge is in low power state, the
			 * configuration space of subordinate devices
@@ -2431,14 +2434,20 @@ static void pci_pme_list_scan(struct work_struct *work)
			 */
			if (bridge && bridge->current_state != PCI_D0)
				continue;

			/*
			 * If the device is in D3cold it should not be
			 * polled either.
			 * If the device is in a low power state it
			 * should not be polled either.
			 */
			if (pme_dev->dev->current_state == PCI_D3cold)
			pm_status = pm_runtime_get_if_active(dev, true);
			if (!pm_status)
				continue;

			pci_pme_wakeup(pme_dev->dev, NULL);
			if (pdev->current_state != PCI_D3cold)
				pci_pme_wakeup(pdev, NULL);

			if (pm_status > 0)
				pm_runtime_put(dev);
		} else {
			list_del(&pme_dev->list);
			kfree(pme_dev);
+32 −2
Original line number Diff line number Diff line
@@ -275,8 +275,23 @@ static ssize_t vpd_read(struct file *filp, struct kobject *kobj,
			size_t count)
{
	struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
	struct pci_dev *vpd_dev = dev;
	ssize_t ret;

	if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
		vpd_dev = pci_get_func0_dev(dev);
		if (!vpd_dev)
			return -ENODEV;
	}

	pci_config_pm_runtime_get(vpd_dev);
	ret = pci_read_vpd(vpd_dev, off, count, buf);
	pci_config_pm_runtime_put(vpd_dev);

	return pci_read_vpd(dev, off, count, buf);
	if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
		pci_dev_put(vpd_dev);

	return ret;
}

static ssize_t vpd_write(struct file *filp, struct kobject *kobj,
@@ -284,8 +299,23 @@ static ssize_t vpd_write(struct file *filp, struct kobject *kobj,
			 size_t count)
{
	struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
	struct pci_dev *vpd_dev = dev;
	ssize_t ret;

	if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) {
		vpd_dev = pci_get_func0_dev(dev);
		if (!vpd_dev)
			return -ENODEV;
	}

	pci_config_pm_runtime_get(vpd_dev);
	ret = pci_write_vpd(vpd_dev, off, count, buf);
	pci_config_pm_runtime_put(vpd_dev);

	return pci_write_vpd(dev, off, count, buf);
	if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0)
		pci_dev_put(vpd_dev);

	return ret;
}
static BIN_ATTR(vpd, 0600, vpd_read, vpd_write, 0);