Skip to content
Snippets Groups Projects
  • Dexuan Cui's avatar
    6ab2e14b
    PCI/PM: Always return devices to D0 when thawing · 6ab2e14b
    Dexuan Cui authored
    commit f2c33cca upstream.
    
    pci_pm_thaw_noirq() is supposed to return the device to D0 and restore its
    configuration registers, but previously it only did that for devices whose
    drivers implemented the new power management ops.
    
    Hibernation, e.g., via "echo disk > /sys/power/state", involves freezing
    devices, creating a hibernation image, thawing devices, writing the image,
    and powering off.  The fact that thawing did not return devices with legacy
    power management to D0 caused errors, e.g., in this path:
    
      pci_pm_thaw_noirq
        if (pci_has_legacy_pm_support(pci_dev)) # true for Mellanox VF driver
          return pci_legacy_resume_early(dev)   # ... legacy PM skips the rest
        pci_set_power_state(pci_dev, PCI_D0)
        pci_restore_state(pci_dev)
      pci_pm_thaw
        if (pci_has_legacy_pm_support(pci_dev))
          pci_legacy_resume
    	drv->resume
    	  mlx4_resume
    	    ...
    	      pci_enable_msix_range
    	        ...
    		  if (dev->current_state != PCI_D0)  # <---
    		    return -EINVAL;
    
    which caused these warnings:
    
      mlx4_core a6d1:00:02.0: INTx is not supported in multi-function mode, aborting
      PM: dpm_run_callback(): pci_pm_thaw+0x0/0xd7 returns -95
      PM: Device a6d1:00:02.0 failed to thaw: error -95
    
    Return devices to D0 and restore config registers for all devices, not just
    those whose drivers support new power management.
    
    [bhelgaas: also call pci_restore_state() before pci_legacy_resume_early(),
    update comment, add stable tag, commit log]
    Link: https://lore.kernel.org/r/KU1P153MB016637CAEAD346F0AA8E3801BFAD0@KU1P153MB0166.APCP153.PROD.OUTLOOK.COM
    
    
    Signed-off-by: default avatarDexuan Cui <decui@microsoft.com>
    Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
    Reviewed-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
    Cc: stable@vger.kernel.org	# v4.13+
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    6ab2e14b
    History
    PCI/PM: Always return devices to D0 when thawing
    Dexuan Cui authored
    commit f2c33cca upstream.
    
    pci_pm_thaw_noirq() is supposed to return the device to D0 and restore its
    configuration registers, but previously it only did that for devices whose
    drivers implemented the new power management ops.
    
    Hibernation, e.g., via "echo disk > /sys/power/state", involves freezing
    devices, creating a hibernation image, thawing devices, writing the image,
    and powering off.  The fact that thawing did not return devices with legacy
    power management to D0 caused errors, e.g., in this path:
    
      pci_pm_thaw_noirq
        if (pci_has_legacy_pm_support(pci_dev)) # true for Mellanox VF driver
          return pci_legacy_resume_early(dev)   # ... legacy PM skips the rest
        pci_set_power_state(pci_dev, PCI_D0)
        pci_restore_state(pci_dev)
      pci_pm_thaw
        if (pci_has_legacy_pm_support(pci_dev))
          pci_legacy_resume
    	drv->resume
    	  mlx4_resume
    	    ...
    	      pci_enable_msix_range
    	        ...
    		  if (dev->current_state != PCI_D0)  # <---
    		    return -EINVAL;
    
    which caused these warnings:
    
      mlx4_core a6d1:00:02.0: INTx is not supported in multi-function mode, aborting
      PM: dpm_run_callback(): pci_pm_thaw+0x0/0xd7 returns -95
      PM: Device a6d1:00:02.0 failed to thaw: error -95
    
    Return devices to D0 and restore config registers for all devices, not just
    those whose drivers support new power management.
    
    [bhelgaas: also call pci_restore_state() before pci_legacy_resume_early(),
    update comment, add stable tag, commit log]
    Link: https://lore.kernel.org/r/KU1P153MB016637CAEAD346F0AA8E3801BFAD0@KU1P153MB0166.APCP153.PROD.OUTLOOK.COM
    
    
    Signed-off-by: default avatarDexuan Cui <decui@microsoft.com>
    Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
    Reviewed-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
    Cc: stable@vger.kernel.org	# v4.13+
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
pci-driver.c 36.38 KiB
/*
 * drivers/pci/pci-driver.c
 *
 * (C) Copyright 2002-2004, 2007 Greg Kroah-Hartman <greg@kroah.com>
 * (C) Copyright 2007 Novell Inc.
 *
 * Released under the GPL v2 only.
 *
 */

#include <linux/pci.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mempolicy.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/cpu.h>
#include <linux/pm_runtime.h>
#include <linux/suspend.h>
#include <linux/kexec.h>
#include "pci.h"

struct pci_dynid {
	struct list_head node;
	struct pci_device_id id;
};

/**
 * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices
 * @drv: target pci driver
 * @vendor: PCI vendor ID
 * @device: PCI device ID
 * @subvendor: PCI subvendor ID
 * @subdevice: PCI subdevice ID
 * @class: PCI class
 * @class_mask: PCI class mask
 * @driver_data: private driver data
 *
 * Adds a new dynamic pci device ID to this driver and causes the
 * driver to probe for all devices again.  @drv must have been
 * registered prior to calling this function.
 *
 * CONTEXT:
 * Does GFP_KERNEL allocation.
 *
 * RETURNS:
 * 0 on success, -errno on failure.
 */
int pci_add_dynid(struct pci_driver *drv,
		  unsigned int vendor, unsigned int device,
		  unsigned int subvendor, unsigned int subdevice,
		  unsigned int class, unsigned int class_mask,
		  unsigned long driver_data)
{
	struct pci_dynid *dynid;

	dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
	if (!dynid)
		return -ENOMEM;

	dynid->id.vendor = vendor;
	dynid->id.device = device;
	dynid->id.subvendor = subvendor;
	dynid->id.subdevice = subdevice;
	dynid->id.class = class;
	dynid->id.class_mask = class_mask;
	dynid->id.driver_data = driver_data;