[Nouveau] [PATCH 4/4] drm/nouveau/acpi: fix lockup with PCIe runtime PM

Emil Velikov emil.l.velikov at gmail.com
Mon May 30 12:41:28 UTC 2016


On 30 May 2016 at 12:23, Peter Wu <peter at lekensteyn.nl> wrote:
> On Mon, May 30, 2016 at 11:48:34AM +0100, Emil Velikov wrote:
>> On 27 May 2016 at 22:31, Peter Wu <peter at lekensteyn.nl> wrote:
>> > On Fri, May 27, 2016 at 02:01:39PM +0100, Emil Velikov wrote:
>> >> Hi Peter,
>> >>
>> >> On 24 May 2016 at 23:53, Peter Wu <peter at lekensteyn.nl> wrote:
>> >> > Since "PCI: Add runtime PM support for PCIe ports", the parent PCIe port
>> >> > can be runtime-suspended which disables power resources via ACPI. This
>> >> > is incompatible with DSM, resulting in a GPU device which is still in D3
>> >> > and locks up the kernel on resume.
>> >> >
>> >> > Mirror the behavior of Windows 8 and newer[1] (as observed via an AMLi
>> >> > debugger trace) and stop using the DSM functions for D3cold when power
>> >> > resources are available on the parent PCIe port.
>> >> >
>> >> >  [1]: https://msdn.microsoft.com/windows/hardware/drivers/bringup/firmware-requirements-for-d3cold
>> >> >
>> >> > Signed-off-by: Peter Wu <peter at lekensteyn.nl>
>> >> > ---
>> >> >  drivers/gpu/drm/nouveau/nouveau_acpi.c | 34 ++++++++++++++++++++++++++++++----
>> >> >  1 file changed, 30 insertions(+), 4 deletions(-)
>> >> >
>> >> > diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
>> >> > index df9f73e..e469df7 100644
>> >> > --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
>> >> > +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
>> >> > @@ -46,6 +46,7 @@ static struct nouveau_dsm_priv {
>> >> >         bool dsm_detected;
>> >> >         bool optimus_detected;
>> >> >         bool optimus_flags_detected;
>> >> > +       bool optimus_skip_dsm;
>> >> >         acpi_handle dhandle;
>> >> >         acpi_handle rom_handle;
>> >> >  } nouveau_dsm_priv;
>> >> > @@ -212,8 +213,26 @@ static const struct vga_switcheroo_handler nouveau_dsm_handler = {
>> >> >         .get_client_id = nouveau_dsm_get_client_id,
>> >> >  };
>> >> >
>> >> > +/* Firmware supporting Windows 8 or later do not use _DSM to put the device into
>> >> > + * D3cold, they instead rely on disabling power resources on the parent. */
>> >> > +static bool nouveau_pr3_present(struct pci_dev *pdev)
>> >> > +{
>> >> > +       struct pci_dev *parent_pdev = pci_upstream_bridge(pdev);
>> >> > +       struct acpi_device *ad;
>> >> > +
>> >> > +       if (!parent_pdev)
>> >> > +               return false;
>> >> > +
>> >> > +       ad = ACPI_COMPANION(&parent_pdev->dev);
>> >> > +       if (!ad)
>> >> > +               return false;
>> >> > +
>> >> > +       return ad->power.flags.power_resources;
>> >> > +}
>> >> > +
>> >> >  static void nouveau_dsm_pci_probe(struct pci_dev *pdev, bool *has_mux,
>> >> > -                                 bool *has_opt, bool *has_opt_flags)
>> >> > +                                 bool *has_opt, bool *has_opt_flags,
>> >> > +                                 bool *has_power_resources)
>> >> >  {
>> >> >         acpi_handle dhandle;
>> >> >         bool supports_mux;
>> >> > @@ -238,6 +257,7 @@ static void nouveau_dsm_pci_probe(struct pci_dev *pdev, bool *has_mux,
>> >> >         *has_mux = supports_mux;
>> >> >         *has_opt = !!optimus_funcs;
>> >> >         *has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS);
>> >> > +       *has_power_resources = false;
>> >> >
>> >> >         if (optimus_funcs) {
>> >> >                 uint32_t result;
>> >> > @@ -247,6 +267,8 @@ static void nouveau_dsm_pci_probe(struct pci_dev *pdev, bool *has_mux,
>> >> >                          (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
>> >> >                          (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
>> >> >                          (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
>> >> > +
>> >> > +               *has_power_resources = nouveau_pr3_present(pdev);
>> >> >         }
>> >> >  }
>> >> >
>> >> > @@ -258,6 +280,7 @@ static bool nouveau_dsm_detect(void)
>> >> >         bool has_mux = false;
>> >> >         bool has_optimus = false;
>> >> >         bool has_optimus_flags = false;
>> >> > +       bool has_power_resources = false;
>> >> >         int vga_count = 0;
>> >> >         bool guid_valid;
>> >> >         bool ret = false;
>> >> > @@ -273,14 +296,14 @@ static bool nouveau_dsm_detect(void)
>> >> >                 vga_count++;
>> >> >
>> >> >                 nouveau_dsm_pci_probe(pdev, &has_mux, &has_optimus,
>> >> > -                                     &has_optimus_flags);
>> >> > +                                     &has_optimus_flags, &has_power_resources);
>> >> >         }
>> >> >
>> >> >         while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
>> >> >                 vga_count++;
>> >> >
>> >> >                 nouveau_dsm_pci_probe(pdev, &has_mux, &has_optimus,
>> >> > -                                     &has_optimus_flags);
>> >> > +                                     &has_optimus_flags, &has_power_resources);
>> >> >         }
>> >> >
>> >> This and earlier patch break things in a subtle way.
>> >>
>> >> Namely: upon the second (and any later) call into the
>> >> nouveau_dsm_pci_probe() function, the had_foo flags are reset. Thus
>> >> only the specifics of the _final_ device are being used (at a later
>> >> stage). IMHO one should change that to "_any_ device", which will
>> >> match the original code and the actual intent further down in the
>> >> file.
>> >
>> > The flags are only reset if any of the MUX or Optimus handles are found.
>> > If both are missing, the flags are not overridden. This is from patch 1:
>> >
>> > +       /* Does not look like a Nvidia device. */
>> > +       if (!supports_mux && !supports_opt)
>> > +               return;
>> >
>> This is precisely what I'm saying, and I think it's wrong/strange. If
>> you've detected that device A support_{X,Y}, you'll reset the
>> support_{X,Y} flag anyway if device B is present... (continues further
>> down)
>
> The flags will only be reset when device B supports at least one
> function.
>
Indeed. Seems like I completely misread the code on multiple
occasions. Sorry about the noise.

>> > The reason why later calls override early ones is because some Optimus
>> > laptops have the _DSM method on both the Intel GPU (00:02.0) and the
>> > Nvidia one (01:00.0).
>> >
>> I agree with Lukas idea that one could/should be checking for nvidia
>> devices (perhaps in nouveau_dsm_pci_probe() or just before calling it
>> ?).
>
> That could break PM on at least two Acer laptops. The Acer Travelmate
> 8472TG from 2011 (acpidump[1]) has two DSM on the Nvidia and Intel ACPI
> handles:
>
>  - Nvidia: supports MXM methods only.
>  - Intel: supports the older Nvidia UUID (for toggling power and
>    possibly other things).
>
>  [1]: https://github.com/Bumblebee-Project/bbswitch/issues/4#issuecomment-219988501
>
> There is also an Acer Aspire 5742G which possibly breaks (linked in the
> above issue), but that could be a configuration issue that disabled
> Optimus in BIOS (unconfirmed).
>
> If it matters, both of these laptops have a MXMX method (Select Display
> Data Channel), but their MXMI (Return Specification Support Level) and
> MXMS (Return MXM Structure) functions are disfunctional. There is also a
> MXDS function on both ACPI handles, but these are not hooked to the WMI
> interface for some reason. No idea of Acer has hacked up some drivers to
> work with this, outside these models I do not know others that are also
> affected by this issue.
>
/me takes a sigh "Why Acer why ..." :-)

>> > The previous detection method would fail in this scenario:
>> >  1. One device reports support for X and Y (has_x = 1, has_y = 1). Write
>> >     ACPI handle A to nouveau_dsm_priv.dhandle.
>> >  2. Another device reports support for X only (has_x = 1). Write
>> >     ACPI handle B to nouveau_dsm_priv.dhandle.
>> >  3. End result: has_x = 1, has_y = 1, dhandle = B. But ACPI handle B
>> >     does not really support Y!
>>
>> ...  so to avoid the above case and preserve the original ideas ('do
>> not discard earlier device caps' and 'Optimus takes precedence over
>> DSM v1') one could do the following:
>>
>>  - decouple the "feature check" and "set the dhandle"
>>
>>  - pick the 'ideal' one based on the feature set provided. if multiple
>> pick one based on $insert_heuristics
>>  - set the dhandle
>>
>> What do you think ?
>
> The dhandle is only set when at least one valid DSM was found on the
> device. The dhandle assignment could indeed be moved to the caller,
> making it more obvious that the dhandle is only valid when the
> capabilities are detected (this does not have a functional change
> though). I'll do it in the next version.

That will be amazing, thanks.
Emil


More information about the Nouveau mailing list