forked from Minki/linux
PCI/PM: Fix proc config reg access for D3cold and bridge suspending
In https://bugzilla.kernel.org/show_bug.cgi?id=48981 Peter reported that /proc/bus/pci/??/??.? does not work for 3.6. This is because the device configuration space registers are not accessible if the corresponding parent bridge is suspended or the device is put into D3cold state. This is the same as /sys/bus/pci/devices/0000:??:??.?/config access issue. So the function used to solve sysfs issue is used to solve this issue. This patch moves pci_config_pm_runtime_get()/_put() from pci/pci-sysfs.c to pci/pci.c and makes them extern so they can be used by both the sysfs and proc paths. [bhelgaas: changelog, references, reporters] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=48981 Reference: https://bugzilla.kernel.org/show_bug.cgi?id=49031 Reported-by: Forrest Loomis <cybercyst@gmail.com> Reported-by: Peter <lekensteyn@gmail.com> Reported-by: Micael Dias <kam1kaz3@gmail.com> Signed-off-by: Huang Ying <ying.huang@intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> CC: stable@vger.kernel.org # v3.6+
This commit is contained in:
parent
3ff2de9ba1
commit
b3c32c4f95
@ -458,40 +458,6 @@ boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
}
|
||||
struct device_attribute vga_attr = __ATTR_RO(boot_vga);
|
||||
|
||||
static void
|
||||
pci_config_pm_runtime_get(struct pci_dev *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *parent = dev->parent;
|
||||
|
||||
if (parent)
|
||||
pm_runtime_get_sync(parent);
|
||||
pm_runtime_get_noresume(dev);
|
||||
/*
|
||||
* pdev->current_state is set to PCI_D3cold during suspending,
|
||||
* so wait until suspending completes
|
||||
*/
|
||||
pm_runtime_barrier(dev);
|
||||
/*
|
||||
* Only need to resume devices in D3cold, because config
|
||||
* registers are still accessible for devices suspended but
|
||||
* not in D3cold.
|
||||
*/
|
||||
if (pdev->current_state == PCI_D3cold)
|
||||
pm_runtime_resume(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
pci_config_pm_runtime_put(struct pci_dev *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *parent = dev->parent;
|
||||
|
||||
pm_runtime_put(dev);
|
||||
if (parent)
|
||||
pm_runtime_put_sync(parent);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
pci_read_config(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
|
@ -1858,6 +1858,38 @@ bool pci_dev_run_wake(struct pci_dev *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pci_dev_run_wake);
|
||||
|
||||
void pci_config_pm_runtime_get(struct pci_dev *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *parent = dev->parent;
|
||||
|
||||
if (parent)
|
||||
pm_runtime_get_sync(parent);
|
||||
pm_runtime_get_noresume(dev);
|
||||
/*
|
||||
* pdev->current_state is set to PCI_D3cold during suspending,
|
||||
* so wait until suspending completes
|
||||
*/
|
||||
pm_runtime_barrier(dev);
|
||||
/*
|
||||
* Only need to resume devices in D3cold, because config
|
||||
* registers are still accessible for devices suspended but
|
||||
* not in D3cold.
|
||||
*/
|
||||
if (pdev->current_state == PCI_D3cold)
|
||||
pm_runtime_resume(dev);
|
||||
}
|
||||
|
||||
void pci_config_pm_runtime_put(struct pci_dev *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device *parent = dev->parent;
|
||||
|
||||
pm_runtime_put(dev);
|
||||
if (parent)
|
||||
pm_runtime_put_sync(parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_pm_init - Initialize PM functions of given PCI device
|
||||
* @dev: PCI device to handle.
|
||||
|
@ -72,6 +72,8 @@ extern void pci_disable_enabled_device(struct pci_dev *dev);
|
||||
extern int pci_finish_runtime_suspend(struct pci_dev *dev);
|
||||
extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
|
||||
extern void pci_wakeup_bus(struct pci_bus *bus);
|
||||
extern void pci_config_pm_runtime_get(struct pci_dev *dev);
|
||||
extern void pci_config_pm_runtime_put(struct pci_dev *dev);
|
||||
extern void pci_pm_init(struct pci_dev *dev);
|
||||
extern void platform_pci_wakeup_init(struct pci_dev *dev);
|
||||
extern void pci_allocate_cap_save_buffers(struct pci_dev *dev);
|
||||
|
@ -76,6 +76,8 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
|
||||
if (!access_ok(VERIFY_WRITE, buf, cnt))
|
||||
return -EINVAL;
|
||||
|
||||
pci_config_pm_runtime_get(dev);
|
||||
|
||||
if ((pos & 1) && cnt) {
|
||||
unsigned char val;
|
||||
pci_user_read_config_byte(dev, pos, &val);
|
||||
@ -121,6 +123,8 @@ proc_bus_pci_read(struct file *file, char __user *buf, size_t nbytes, loff_t *pp
|
||||
cnt--;
|
||||
}
|
||||
|
||||
pci_config_pm_runtime_put(dev);
|
||||
|
||||
*ppos = pos;
|
||||
return nbytes;
|
||||
}
|
||||
@ -146,6 +150,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
|
||||
if (!access_ok(VERIFY_READ, buf, cnt))
|
||||
return -EINVAL;
|
||||
|
||||
pci_config_pm_runtime_get(dev);
|
||||
|
||||
if ((pos & 1) && cnt) {
|
||||
unsigned char val;
|
||||
__get_user(val, buf);
|
||||
@ -191,6 +197,8 @@ proc_bus_pci_write(struct file *file, const char __user *buf, size_t nbytes, lof
|
||||
cnt--;
|
||||
}
|
||||
|
||||
pci_config_pm_runtime_put(dev);
|
||||
|
||||
*ppos = pos;
|
||||
i_size_write(ino, dp->size);
|
||||
return nbytes;
|
||||
|
Loading…
Reference in New Issue
Block a user