Re: [Nouveau] [PATCH] drm/nouveau: Intercept ACPI_VIDEO_NOTIFY_PROBE

2016-11-10 Thread Hans de Goede

Hi,

On 10-11-16 11:58, Peter Wu wrote:

On Wed, Nov 09, 2016 at 06:17:44PM +0100, Hans de Goede wrote:

Various notebooks with nvidia GPUs generate an ACPI_VIDEO_NOTIFY_PROBE
acpi-video event when an external device gets plugged in (and again on
modesets on that connector), the default behavior in the acpi-video
driver for this is to send a KEY_SWITCHVIDEOMODE evdev event, which
causes e.g. gnome-settings-daemon to ask us to rescan the connectors
(good), but also causes g-s-d to switch to mirror mode on a newly plugged
monitor rather then using the monitor to extend the desktop (bad)
as KEY_SWITCHVIDEOMODE is supposed to switch between extend the desktop
vs mirror mode.


Can confirm this behavior on my Clevo P651RA laptop with GTX 965M, it
shows a weird keystroke on console on inserting a monitor. On the Plasma
desktop, it shows a notification "no outputs detected". Only after
running "xrandr", the external monitor would be picked up.


Good, thank you for testing.


More troublesome are the repeated ACPI_VIDEO_NOTIFY_PROBE events on
changing the mode on the connector, which cause g-s-d to switch
between mirror/extend mode, which causes a new ACPI_VIDEO_NOTIFY_PROBE
event and we end up with an endless loop.

This commit fixes this by adding an acpi notifier block handler to
nouveau_display.c to intercept ACPI_VIDEO_NOTIFY_PROBE and:

1) Wake-up runtime suspended GPUs and call drm_helper_hpd_irq_event()
   on them, this is necessary in some cases for the GPU to detect connector
   hotplug events while runtime suspended
2) Return NOTIFY_BAD to stop acpi-video from emitting a bogus
   KEY_SWITCHVIDEOMODE key-press event

There already is another acpi notifier block handler registered in
drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c, but that is not
suitable since that one gets unregistered on runtime suspend, and
we also want to intercept ACPI_VIDEO_NOTIFY_PROBE when runtime suspended.


From a quick look, it looks like a suitable mechanism except for the
removal on runtime suspend.


That is not the only problem, there also is no way to get to the
drm_device pointer that deep in the nvkm object tree.



Based on commit logs for core/core/event.c, the event system seems
initially used for communication between drm and nouveau, later this was
extended to userspace notifications. For some reason, nvkm_acpi_init is
called through this user.c path:

  lspci-14658 [001] d..1 35887.344592: p_nvkm_acpi_init_0: 
(nvkm_acpi_init+0x0/0x20 [nouveau])
   lspci-14658 [001] d..1 35887.344597: 
 => nvkm_udevice_init
 => nvkm_object_init
 => nvkm_object_init
 => nvkm_client_init
 => nvkm_client_resume
 => nvif_client_resume
 => nouveau_do_resume
 => nouveau_pmops_runtime_resume
 => pci_pm_runtime_resume

runtime suspend follows similar code path with resume -> suspend and
init -> fini, but somehow I also see this weird path just before resume:

   lspci-14658 [001] d..1 35887.176959: p_nvkm_acpi_fini_0: 
(nvkm_acpi_fini+0x0/0x20 [nouveau])
   lspci-14658 [001] d..1 35887.176974: 
 => nvkm_device_init
 => nvkm_udevice_init

This was observed on kernel 4.8.6-1-ARCH using kprobe tracing.


Yes I noticed that too, this is indeed weird.


> Hopefully

Ben can clarify this situation. One other comment below.


Signed-off-by: Hans de Goede 
---
Note that ACPI_VIDEO_NOTIFY_PROBE currently is a private define in
drivers/acpi/acpi_video.c, since it is passed to acpi_notifier_call_chain()
it really should be in a public header, so I've submitted a patch to
the acpi subsys to move it to include/acpi/video.h . In the mean time
this patch defines it with a #ifndef guard to allow merging without
introducing inter subsys dependencies. I will submit a follow up patch
removing the #ifndef block once both patches are in Linus' tree.
---
 drivers/gpu/drm/nouveau/nouveau_display.c | 61 +++
 drivers/gpu/drm/nouveau/nouveau_drv.h |  6 +++
 2 files changed, 67 insertions(+)

diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c 
b/drivers/gpu/drm/nouveau/nouveau_display.c
index afbf557..6cd6723 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -24,6 +24,7 @@
  *
  */

+#include 
 #include 
 #include 

@@ -42,6 +43,8 @@
 #include 
 #include 

+
+
 static int
 nouveau_display_vblank_handler(struct nvif_notify *notify)
 {
@@ -358,6 +361,55 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = {
}  \
 } while(0)

+#ifdef CONFIG_ACPI
+
+/*
+ * Hans de Goede: This define belongs in acpi/video.h, I've submitted a patch
+ * to the acpi subsys to move it there from drivers/acpi/acpi_video.c .
+ * This should be dropped once that is merged.
+ */
+#ifndef ACPI_VIDEO_NOTIFY_PROBE
+#define ACPI_VIDEO_NOTIFY_PROBE0x81
+#endif
+
+static void
+nouveau_display_acpi_work(struc

Re: [Nouveau] [PATCH] drm/nouveau: Intercept ACPI_VIDEO_NOTIFY_PROBE

2016-11-10 Thread Peter Wu
On Wed, Nov 09, 2016 at 06:17:44PM +0100, Hans de Goede wrote:
> Various notebooks with nvidia GPUs generate an ACPI_VIDEO_NOTIFY_PROBE
> acpi-video event when an external device gets plugged in (and again on
> modesets on that connector), the default behavior in the acpi-video
> driver for this is to send a KEY_SWITCHVIDEOMODE evdev event, which
> causes e.g. gnome-settings-daemon to ask us to rescan the connectors
> (good), but also causes g-s-d to switch to mirror mode on a newly plugged
> monitor rather then using the monitor to extend the desktop (bad)
> as KEY_SWITCHVIDEOMODE is supposed to switch between extend the desktop
> vs mirror mode.

Can confirm this behavior on my Clevo P651RA laptop with GTX 965M, it
shows a weird keystroke on console on inserting a monitor. On the Plasma
desktop, it shows a notification "no outputs detected". Only after
running "xrandr", the external monitor would be picked up.

> More troublesome are the repeated ACPI_VIDEO_NOTIFY_PROBE events on
> changing the mode on the connector, which cause g-s-d to switch
> between mirror/extend mode, which causes a new ACPI_VIDEO_NOTIFY_PROBE
> event and we end up with an endless loop.
> 
> This commit fixes this by adding an acpi notifier block handler to
> nouveau_display.c to intercept ACPI_VIDEO_NOTIFY_PROBE and:
> 
> 1) Wake-up runtime suspended GPUs and call drm_helper_hpd_irq_event()
>on them, this is necessary in some cases for the GPU to detect connector
>hotplug events while runtime suspended
> 2) Return NOTIFY_BAD to stop acpi-video from emitting a bogus
>KEY_SWITCHVIDEOMODE key-press event
> 
> There already is another acpi notifier block handler registered in
> drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c, but that is not
> suitable since that one gets unregistered on runtime suspend, and
> we also want to intercept ACPI_VIDEO_NOTIFY_PROBE when runtime suspended.

From a quick look, it looks like a suitable mechanism except for the
removal on runtime suspend.

Based on commit logs for core/core/event.c, the event system seems
initially used for communication between drm and nouveau, later this was
extended to userspace notifications. For some reason, nvkm_acpi_init is
called through this user.c path:

  lspci-14658 [001] d..1 35887.344592: p_nvkm_acpi_init_0: 
(nvkm_acpi_init+0x0/0x20 [nouveau])
   lspci-14658 [001] d..1 35887.344597: 
 => nvkm_udevice_init
 => nvkm_object_init
 => nvkm_object_init
 => nvkm_client_init
 => nvkm_client_resume
 => nvif_client_resume
 => nouveau_do_resume
 => nouveau_pmops_runtime_resume
 => pci_pm_runtime_resume

runtime suspend follows similar code path with resume -> suspend and
init -> fini, but somehow I also see this weird path just before resume:

   lspci-14658 [001] d..1 35887.176959: p_nvkm_acpi_fini_0: 
(nvkm_acpi_fini+0x0/0x20 [nouveau])
   lspci-14658 [001] d..1 35887.176974: 
 => nvkm_device_init
 => nvkm_udevice_init

This was observed on kernel 4.8.6-1-ARCH using kprobe tracing. Hopefully
Ben can clarify this situation. One other comment below.

> Signed-off-by: Hans de Goede 
> ---
> Note that ACPI_VIDEO_NOTIFY_PROBE currently is a private define in
> drivers/acpi/acpi_video.c, since it is passed to acpi_notifier_call_chain()
> it really should be in a public header, so I've submitted a patch to
> the acpi subsys to move it to include/acpi/video.h . In the mean time
> this patch defines it with a #ifndef guard to allow merging without
> introducing inter subsys dependencies. I will submit a follow up patch
> removing the #ifndef block once both patches are in Linus' tree.
> ---
>  drivers/gpu/drm/nouveau/nouveau_display.c | 61 
> +++
>  drivers/gpu/drm/nouveau/nouveau_drv.h |  6 +++
>  2 files changed, 67 insertions(+)
> 
> diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c 
> b/drivers/gpu/drm/nouveau/nouveau_display.c
> index afbf557..6cd6723 100644
> --- a/drivers/gpu/drm/nouveau/nouveau_display.c
> +++ b/drivers/gpu/drm/nouveau/nouveau_display.c
> @@ -24,6 +24,7 @@
>   *
>   */
>  
> +#include 
>  #include 
>  #include 
>  
> @@ -42,6 +43,8 @@
>  #include 
>  #include 
>  
> +
> +
>  static int
>  nouveau_display_vblank_handler(struct nvif_notify *notify)
>  {
> @@ -358,6 +361,55 @@ static struct nouveau_drm_prop_enum_list dither_depth[] 
> = {
>   }  \
>  } while(0)
>  
> +#ifdef CONFIG_ACPI
> +
> +/*
> + * Hans de Goede: This define belongs in acpi/video.h, I've submitted a patch
> + * to the acpi subsys to move it there from drivers/acpi/acpi_video.c .
> + * This should be dropped once that is merged.
> + */
> +#ifndef ACPI_VIDEO_NOTIFY_PROBE
> +#define ACPI_VIDEO_NOTIFY_PROBE  0x81
> +#endif
> +
> +static void
> +nouveau_display_acpi_work(struct work_struct *work)
> +{
> + struct nouveau_drm *drm = container_of(work, typeof(*d

[Nouveau] [PATCH] drm/nouveau: Intercept ACPI_VIDEO_NOTIFY_PROBE

2016-11-09 Thread Hans de Goede
Various notebooks with nvidia GPUs generate an ACPI_VIDEO_NOTIFY_PROBE
acpi-video event when an external device gets plugged in (and again on
modesets on that connector), the default behavior in the acpi-video
driver for this is to send a KEY_SWITCHVIDEOMODE evdev event, which
causes e.g. gnome-settings-daemon to ask us to rescan the connectors
(good), but also causes g-s-d to switch to mirror mode on a newly plugged
monitor rather then using the monitor to extend the desktop (bad)
as KEY_SWITCHVIDEOMODE is supposed to switch between extend the desktop
vs mirror mode.

More troublesome are the repeated ACPI_VIDEO_NOTIFY_PROBE events on
changing the mode on the connector, which cause g-s-d to switch
between mirror/extend mode, which causes a new ACPI_VIDEO_NOTIFY_PROBE
event and we end up with an endless loop.

This commit fixes this by adding an acpi notifier block handler to
nouveau_display.c to intercept ACPI_VIDEO_NOTIFY_PROBE and:

1) Wake-up runtime suspended GPUs and call drm_helper_hpd_irq_event()
   on them, this is necessary in some cases for the GPU to detect connector
   hotplug events while runtime suspended
2) Return NOTIFY_BAD to stop acpi-video from emitting a bogus
   KEY_SWITCHVIDEOMODE key-press event

There already is another acpi notifier block handler registered in
drivers/gpu/drm/nouveau/nvkm/engine/device/acpi.c, but that is not
suitable since that one gets unregistered on runtime suspend, and
we also want to intercept ACPI_VIDEO_NOTIFY_PROBE when runtime suspended.

Signed-off-by: Hans de Goede 
---
Note that ACPI_VIDEO_NOTIFY_PROBE currently is a private define in
drivers/acpi/acpi_video.c, since it is passed to acpi_notifier_call_chain()
it really should be in a public header, so I've submitted a patch to
the acpi subsys to move it to include/acpi/video.h . In the mean time
this patch defines it with a #ifndef guard to allow merging without
introducing inter subsys dependencies. I will submit a follow up patch
removing the #ifndef block once both patches are in Linus' tree.
---
 drivers/gpu/drm/nouveau/nouveau_display.c | 61 +++
 drivers/gpu/drm/nouveau/nouveau_drv.h |  6 +++
 2 files changed, 67 insertions(+)

diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c 
b/drivers/gpu/drm/nouveau/nouveau_display.c
index afbf557..6cd6723 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -24,6 +24,7 @@
  *
  */
 
+#include 
 #include 
 #include 
 
@@ -42,6 +43,8 @@
 #include 
 #include 
 
+
+
 static int
 nouveau_display_vblank_handler(struct nvif_notify *notify)
 {
@@ -358,6 +361,55 @@ static struct nouveau_drm_prop_enum_list dither_depth[] = {
}  \
 } while(0)
 
+#ifdef CONFIG_ACPI
+
+/*
+ * Hans de Goede: This define belongs in acpi/video.h, I've submitted a patch
+ * to the acpi subsys to move it there from drivers/acpi/acpi_video.c .
+ * This should be dropped once that is merged.
+ */
+#ifndef ACPI_VIDEO_NOTIFY_PROBE
+#define ACPI_VIDEO_NOTIFY_PROBE0x81
+#endif
+
+static void
+nouveau_display_acpi_work(struct work_struct *work)
+{
+   struct nouveau_drm *drm = container_of(work, typeof(*drm), acpi_work);
+
+   pm_runtime_get_sync(drm->dev->dev);
+
+   drm_helper_hpd_irq_event(drm->dev);
+
+   pm_runtime_mark_last_busy(drm->dev->dev);
+   pm_runtime_put_sync(drm->dev->dev);
+}
+
+static int
+nouveau_display_acpi_ntfy(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+   struct nouveau_drm *drm = container_of(nb, typeof(*drm), acpi_nb);
+   struct acpi_bus_event *info = data;
+
+   if (!strcmp(info->device_class, ACPI_VIDEO_CLASS)) {
+   if (info->type == ACPI_VIDEO_NOTIFY_PROBE) {
+   /*
+* This may be the only indication we receive of a
+* connector hotplug on a runtime suspended GPU,
+* schedule acpi_work to check.
+*/
+   schedule_work(&drm->acpi_work);
+
+   /* acpi-video should not generate keypresses for this */
+   return NOTIFY_BAD;
+   }
+   }
+
+   return NOTIFY_DONE;
+}
+#endif
+
 int
 nouveau_display_init(struct drm_device *dev)
 {
@@ -537,6 +589,12 @@ nouveau_display_create(struct drm_device *dev)
}
 
nouveau_backlight_init(dev);
+#ifdef CONFIG_ACPI
+   INIT_WORK(&drm->acpi_work, nouveau_display_acpi_work);
+   drm->acpi_nb.notifier_call = nouveau_display_acpi_ntfy;
+   register_acpi_notifier(&drm->acpi_nb);
+#endif
+
return 0;
 
 vblank_err:
@@ -552,6 +610,9 @@ nouveau_display_destroy(struct drm_device *dev)
 {
struct nouveau_display *disp = nouveau_display(dev);
 
+#ifdef CONFIG_ACPI
+   unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb);
+#endif
nouveau_