On Tue, Sep 9, 2025 at 9:16 PM Mario Limonciello (AMD)
<supe...@kernel.org> wrote:
>
> In order to unify suspend and hibernate codepaths without code duplication
> the common code should be in common helpers.  Move it from
> pci_pm_suspend_noirq() into a helper.  No intended functional changes.

You should say why you need/want to unify the suspend and hibernate
codepaths because this is kind of relevant for this patch.

It isn't entirely valid to use suspend-specific code in the
hibernate/power down code paths.

Also, I'd consider reordering the series so that this patch goes
immediately before patch [9/12] in which the new function is used for
the first time.

> Tested-by: Eric Naim <dn...@cachyos.org>
> Signed-off-by: Mario Limonciello (AMD) <supe...@kernel.org>
> ---
> v7:
>  * Reword title
> v5:
>  * Split from earlier patches
>  * Add tags
> v4:
>  * 
> https://lore.kernel.org/linux-pci/20250616175019.3471583-1-supe...@kernel.org/
> ---
>  drivers/pci/pci-driver.c | 81 +++++++++++++++++++++++++---------------
>  1 file changed, 51 insertions(+), 30 deletions(-)
>
> diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
> index f201d298d7173..571a3809f163a 100644
> --- a/drivers/pci/pci-driver.c
> +++ b/drivers/pci/pci-driver.c
> @@ -762,6 +762,54 @@ static void pci_pm_complete(struct device *dev)
>
>  #endif /* !CONFIG_PM_SLEEP */
>
> +#if defined(CONFIG_SUSPEND)
> +/**
> + * pci_pm_suspend_noirq_common
> + * @pci_dev: pci device
> + * @skip_bus_pm: pointer to a boolean indicating whether to skip bus PM
> + *
> + * Prepare the device to go into a low power state by saving state and
> + * deciding whether to skip bus PM.
> + *
> + */
> +static void pci_pm_suspend_noirq_common(struct pci_dev *pci_dev, bool 
> *skip_bus_pm)

I guess you want to propagate pci_dev->skip_bus_pm to the parent
bridge if set for pci_dev in the hibernation code path so that bridges
can go into D3hot/cold in that case.

Fair enough, but I'd give the common function a somewhat less neutral
name, like for example pci_pm_noirq_prepare_to_sleep().

> +{
> +       if (!pci_dev->state_saved) {
> +               pci_save_state(pci_dev);
> +
> +               /*
> +                * If the device is a bridge with a child in D0 below it,
> +                * it needs to stay in D0, so check skip_bus_pm to avoid
> +                * putting it into a low-power state in that case.
> +                */
> +               if (!pci_dev->skip_bus_pm && pci_power_manageable(pci_dev))
> +                       pci_prepare_to_sleep(pci_dev);
> +       }
> +
> +       pci_dbg(pci_dev, "PCI PM: Sleep power state: %s\n",
> +               pci_power_name(pci_dev->current_state));
> +
> +       if (pci_dev->current_state == PCI_D0) {
> +               pci_dev->skip_bus_pm = true;
> +               /*
> +                * Per PCI PM r1.2, table 6-1, a bridge must be in D0 if any
> +                * downstream device is in D0, so avoid changing the power 
> state
> +                * of the parent bridge by setting the skip_bus_pm flag for 
> it.
> +                */
> +               if (pci_dev->bus->self)
> +                       pci_dev->bus->self->skip_bus_pm = true;
> +       }
> +
> +       if (pci_dev->skip_bus_pm && pm_suspend_no_platform()) {

And pm_suspend_no_platform() is suspend-specific and in the
hibernation/power down cases it is always false because the platform
is going to take over.

This means that *skip_bus_pm will not be updated in those cases, so
why do you need it in this function in the first place?

> +               pci_dbg(pci_dev, "PCI PM: Skipped\n");
> +               *skip_bus_pm = true;
> +               return;
> +       }
> +
> +       pci_pm_set_unknown_state(pci_dev);

So this should stay outside the common part I think.

> +}
> +#endif /* CONFIG_SUSPEND */
> +
>  #ifdef CONFIG_SUSPEND
>  static void pcie_pme_root_status_cleanup(struct pci_dev *pci_dev)
>  {
> @@ -851,6 +899,7 @@ static int pci_pm_suspend_noirq(struct device *dev)
>  {
>         struct pci_dev *pci_dev = to_pci_dev(dev);
>         const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
> +       bool skip_bus_pm = false;

And it doesn't look like this variable is necessary after all.

>
>         if (dev_pm_skip_suspend(dev))
>                 return 0;
> @@ -881,38 +930,10 @@ static int pci_pm_suspend_noirq(struct device *dev)
>                 }
>         }
>
> -       if (!pci_dev->state_saved) {
> -               pci_save_state(pci_dev);
> -
> -               /*
> -                * If the device is a bridge with a child in D0 below it,
> -                * it needs to stay in D0, so check skip_bus_pm to avoid
> -                * putting it into a low-power state in that case.
> -                */
> -               if (!pci_dev->skip_bus_pm && pci_power_manageable(pci_dev))
> -                       pci_prepare_to_sleep(pci_dev);
> -       }
> -
> -       pci_dbg(pci_dev, "PCI PM: Suspend power state: %s\n",
> -               pci_power_name(pci_dev->current_state));
> +       pci_pm_suspend_noirq_common(pci_dev, &skip_bus_pm);
>
> -       if (pci_dev->current_state == PCI_D0) {
> -               pci_dev->skip_bus_pm = true;
> -               /*
> -                * Per PCI PM r1.2, table 6-1, a bridge must be in D0 if any
> -                * downstream device is in D0, so avoid changing the power 
> state
> -                * of the parent bridge by setting the skip_bus_pm flag for 
> it.
> -                */
> -               if (pci_dev->bus->self)
> -                       pci_dev->bus->self->skip_bus_pm = true;
> -       }
> -
> -       if (pci_dev->skip_bus_pm && pm_suspend_no_platform()) {
> -               pci_dbg(pci_dev, "PCI PM: Skipped\n");
> +       if (skip_bus_pm)
>                 goto Fixup;
> -       }
> -
> -       pci_pm_set_unknown_state(pci_dev);
>
>         /*
>          * Some BIOSes from ASUS have a bug: If a USB EHCI host controller's
> --

Reply via email to