diff mbox

[BUGFIX] PCI/PM: Fix proc config reg access for D3cold and bridge suspending

Message ID 1351128963-10347-1-git-send-email-ying.huang@intel.com (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show

Commit Message

Huang, Ying Oct. 25, 2012, 1:36 a.m. UTC
In

  https://bugzilla.kernel.org/show_bug.cgi?id=48981

Peter reported that /proc/bus/pci/??/??.? does not works for 3.6.
This is This is because the device configuration space registers will
be 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.

Cc: stable@vger.kernel.org
Reported-by: Peter <lekensteyn@gmail.com>
Signed-off-by: Huang Ying <ying.huang@intel.com>
---
 drivers/pci/pci-sysfs.c |   34 ----------------------------------
 drivers/pci/pci.c       |   32 ++++++++++++++++++++++++++++++++
 drivers/pci/pci.h       |    2 ++
 drivers/pci/proc.c      |    8 ++++++++
 4 files changed, 42 insertions(+), 34 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Rafael Wysocki Oct. 25, 2012, 8:27 p.m. UTC | #1
On Thursday, October 25, 2012 09:36:03 AM Huang Ying wrote:
> In
> 
>   https://bugzilla.kernel.org/show_bug.cgi?id=48981
> 
> Peter reported that /proc/bus/pci/??/??.? does not works for 3.6.
> This is This is because the device configuration space registers will
> be 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.
> 
> Cc: stable@vger.kernel.org
> Reported-by: Peter <lekensteyn@gmail.com>
> Signed-off-by: Huang Ying <ying.huang@intel.com>

Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

> ---
>  drivers/pci/pci-sysfs.c |   34 ----------------------------------
>  drivers/pci/pci.c       |   32 ++++++++++++++++++++++++++++++++
>  drivers/pci/pci.h       |    2 ++
>  drivers/pci/proc.c      |    8 ++++++++
>  4 files changed, 42 insertions(+), 34 deletions(-)
> 
> --- a/drivers/pci/pci-sysfs.c
> +++ b/drivers/pci/pci-sysfs.c
> @@ -458,40 +458,6 @@ boot_vga_show(struct device *dev, struct
>  }
>  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,
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -1858,6 +1858,38 @@ bool pci_dev_run_wake(struct pci_dev *de
>  }
>  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.
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -72,6 +72,8 @@ extern void pci_disable_enabled_device(s
>  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);
> --- a/drivers/pci/proc.c
> +++ b/drivers/pci/proc.c
> @@ -76,6 +76,8 @@ proc_bus_pci_read(struct file *file, cha
>  	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, cha
>  		cnt--;
>  	}
>  
> +	pci_config_pm_runtime_put(dev);
> +
>  	*ppos = pos;
>  	return nbytes;
>  }
> @@ -146,6 +150,8 @@ proc_bus_pci_write(struct file *file, co
>  	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, co
>  		cnt--;
>  	}
>  
> +	pci_config_pm_runtime_put(dev);
> +
>  	*ppos = pos;
>  	i_size_write(ino, dp->size);
>  	return nbytes;
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
Bjorn Helgaas Nov. 2, 2012, 4:38 p.m. UTC | #2
On Wed, Oct 24, 2012 at 7:36 PM, Huang Ying <ying.huang@intel.com> wrote:
> In
>
>   https://bugzilla.kernel.org/show_bug.cgi?id=48981
>
> Peter reported that /proc/bus/pci/??/??.? does not works for 3.6.
> This is This is because the device configuration space registers will
> be 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.
>
> Cc: stable@vger.kernel.org
> Reported-by: Peter <lekensteyn@gmail.com>

Is this bug the same as the one originally reported by Forrest Loomis
(original reporter of bug 48981)?  And
https://bugzilla.kernel.org/show_bug.cgi?id=49031, reported by Micael
Dias (Rafael marked 49031 as a duplicate of 48981)?

If so, I'll mention Forrest and Micael and bug 49031 here as well.

> Signed-off-by: Huang Ying <ying.huang@intel.com>
> ---
>  drivers/pci/pci-sysfs.c |   34 ----------------------------------
>  drivers/pci/pci.c       |   32 ++++++++++++++++++++++++++++++++
>  drivers/pci/pci.h       |    2 ++
>  drivers/pci/proc.c      |    8 ++++++++
>  4 files changed, 42 insertions(+), 34 deletions(-)
>
> --- a/drivers/pci/pci-sysfs.c
> +++ b/drivers/pci/pci-sysfs.c
> @@ -458,40 +458,6 @@ boot_vga_show(struct device *dev, struct
>  }
>  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,
> --- a/drivers/pci/pci.c
> +++ b/drivers/pci/pci.c
> @@ -1858,6 +1858,38 @@ bool pci_dev_run_wake(struct pci_dev *de
>  }
>  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.
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -72,6 +72,8 @@ extern void pci_disable_enabled_device(s
>  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);
> --- a/drivers/pci/proc.c
> +++ b/drivers/pci/proc.c
> @@ -76,6 +76,8 @@ proc_bus_pci_read(struct file *file, cha
>         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, cha
>                 cnt--;
>         }
>
> +       pci_config_pm_runtime_put(dev);
> +
>         *ppos = pos;
>         return nbytes;
>  }
> @@ -146,6 +150,8 @@ proc_bus_pci_write(struct file *file, co
>         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, co
>                 cnt--;
>         }
>
> +       pci_config_pm_runtime_put(dev);
> +
>         *ppos = pos;
>         i_size_write(ino, dp->size);
>         return nbytes;
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael Wysocki Nov. 2, 2012, 8:25 p.m. UTC | #3
On Friday, November 02, 2012 10:38:43 AM Bjorn Helgaas wrote:
> On Wed, Oct 24, 2012 at 7:36 PM, Huang Ying <ying.huang@intel.com> wrote:
> > In
> >
> >   https://bugzilla.kernel.org/show_bug.cgi?id=48981
> >
> > Peter reported that /proc/bus/pci/??/??.? does not works for 3.6.
> > This is This is because the device configuration space registers will
> > be 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.
> >
> > Cc: stable@vger.kernel.org
> > Reported-by: Peter <lekensteyn@gmail.com>
> 
> Is this bug the same as the one originally reported by Forrest Loomis
> (original reporter of bug 48981)?  And
> https://bugzilla.kernel.org/show_bug.cgi?id=49031, reported by Micael
> Dias (Rafael marked 49031 as a duplicate of 48981)?
> 
> If so, I'll mention Forrest and Micael and bug 49031 here as well.

No, it's not.  That one turned out to be a duplicate of bko#48981.

Thanks,
Rafael


> > Signed-off-by: Huang Ying <ying.huang@intel.com>
> > ---
> >  drivers/pci/pci-sysfs.c |   34 ----------------------------------
> >  drivers/pci/pci.c       |   32 ++++++++++++++++++++++++++++++++
> >  drivers/pci/pci.h       |    2 ++
> >  drivers/pci/proc.c      |    8 ++++++++
> >  4 files changed, 42 insertions(+), 34 deletions(-)
> >
> > --- a/drivers/pci/pci-sysfs.c
> > +++ b/drivers/pci/pci-sysfs.c
> > @@ -458,40 +458,6 @@ boot_vga_show(struct device *dev, struct
> >  }
> >  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,
> > --- a/drivers/pci/pci.c
> > +++ b/drivers/pci/pci.c
> > @@ -1858,6 +1858,38 @@ bool pci_dev_run_wake(struct pci_dev *de
> >  }
> >  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.
> > --- a/drivers/pci/pci.h
> > +++ b/drivers/pci/pci.h
> > @@ -72,6 +72,8 @@ extern void pci_disable_enabled_device(s
> >  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);
> > --- a/drivers/pci/proc.c
> > +++ b/drivers/pci/proc.c
> > @@ -76,6 +76,8 @@ proc_bus_pci_read(struct file *file, cha
> >         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, cha
> >                 cnt--;
> >         }
> >
> > +       pci_config_pm_runtime_put(dev);
> > +
> >         *ppos = pos;
> >         return nbytes;
> >  }
> > @@ -146,6 +150,8 @@ proc_bus_pci_write(struct file *file, co
> >         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, co
> >                 cnt--;
> >         }
> >
> > +       pci_config_pm_runtime_put(dev);
> > +
> >         *ppos = pos;
> >         i_size_write(ino, dp->size);
> >         return nbytes;
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
Bjorn Helgaas Nov. 5, 2012, 10:10 p.m. UTC | #4
On Fri, Nov 2, 2012 at 2:25 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Friday, November 02, 2012 10:38:43 AM Bjorn Helgaas wrote:
>> On Wed, Oct 24, 2012 at 7:36 PM, Huang Ying <ying.huang@intel.com> wrote:
>> > In
>> >
>> >   https://bugzilla.kernel.org/show_bug.cgi?id=48981
>> >
>> > Peter reported that /proc/bus/pci/??/??.? does not works for 3.6.
>> > This is This is because the device configuration space registers will
>> > be 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.
>> >
>> > Cc: stable@vger.kernel.org
>> > Reported-by: Peter <lekensteyn@gmail.com>
>>
>> Is this bug the same as the one originally reported by Forrest Loomis
>> (original reporter of bug 48981)?  And
>> https://bugzilla.kernel.org/show_bug.cgi?id=49031, reported by Micael
>> Dias (Rafael marked 49031 as a duplicate of 48981)?
>>
>> If so, I'll mention Forrest and Micael and bug 49031 here as well.
>
> No, it's not.  That one turned out to be a duplicate of bko#48981.

I applied this to my for-linus branch as v3.7 material.  Thanks!
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -458,40 +458,6 @@  boot_vga_show(struct device *dev, struct
 }
 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,
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1858,6 +1858,38 @@  bool pci_dev_run_wake(struct pci_dev *de
 }
 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.
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -72,6 +72,8 @@  extern void pci_disable_enabled_device(s
 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);
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -76,6 +76,8 @@  proc_bus_pci_read(struct file *file, cha
 	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, cha
 		cnt--;
 	}
 
+	pci_config_pm_runtime_put(dev);
+
 	*ppos = pos;
 	return nbytes;
 }
@@ -146,6 +150,8 @@  proc_bus_pci_write(struct file *file, co
 	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, co
 		cnt--;
 	}
 
+	pci_config_pm_runtime_put(dev);
+
 	*ppos = pos;
 	i_size_write(ino, dp->size);
 	return nbytes;