4.18-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Ben Skeggs <bske...@redhat.com>

[ Upstream commit e04cfdc9b7398c60dbc70212415ea63b6c6a93ae ]

If a HPD pulse signalling the need to retrain the link occurs between
the KMS driver releasing the output and the supervisor interrupt that
finishes the teardown, it was possible get a NULL-ptr deref.

Avoid this by marking the link as inactive earlier.

Signed-off-by: Ben Skeggs <bske...@redhat.com>
Signed-off-by: Sasha Levin <alexander.le...@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>
---
 drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c   |   17 ++++++++++++-----
 drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c |    6 +++---
 drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c |    2 ++
 drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h |    3 ++-
 4 files changed, 19 insertions(+), 9 deletions(-)

--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
@@ -412,14 +412,10 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 da
 }
 
 static void
-nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior)
+nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior)
 {
        struct nvkm_dp *dp = nvkm_dp(outp);
 
-       /* Prevent link from being retrained if sink sends an IRQ. */
-       atomic_set(&dp->lt.done, 0);
-       ior->dp.nr = 0;
-
        /* Execute DisableLT script from DP Info Table. */
        nvbios_init(&ior->disp->engine.subdev, dp->info.script[4],
                init.outp = &dp->outp.info;
@@ -428,6 +424,16 @@ nvkm_dp_release(struct nvkm_outp *outp,
        );
 }
 
+static void
+nvkm_dp_release(struct nvkm_outp *outp)
+{
+       struct nvkm_dp *dp = nvkm_dp(outp);
+
+       /* Prevent link from being retrained if sink sends an IRQ. */
+       atomic_set(&dp->lt.done, 0);
+       dp->outp.ior->dp.nr = 0;
+}
+
 static int
 nvkm_dp_acquire(struct nvkm_outp *outp)
 {
@@ -576,6 +582,7 @@ nvkm_dp_func = {
        .fini = nvkm_dp_fini,
        .acquire = nvkm_dp_acquire,
        .release = nvkm_dp_release,
+       .disable = nvkm_dp_disable,
 };
 
 static int
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
@@ -501,11 +501,11 @@ nv50_disp_super_2_0(struct nv50_disp *di
        nv50_disp_super_ied_off(head, ior, 2);
 
        /* If we're shutting down the OR's only active head, execute
-        * the output path's release function.
+        * the output path's disable function.
         */
        if (ior->arm.head == (1 << head->id)) {
-               if ((outp = ior->arm.outp) && outp->func->release)
-                       outp->func->release(outp, ior);
+               if ((outp = ior->arm.outp) && outp->func->disable)
+                       outp->func->disable(outp, ior);
        }
 }
 
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
@@ -93,6 +93,8 @@ nvkm_outp_release(struct nvkm_outp *outp
        if (ior) {
                outp->acquired &= ~user;
                if (!outp->acquired) {
+                       if (outp->func->release && outp->ior)
+                               outp->func->release(outp);
                        outp->ior->asy.outp = NULL;
                        outp->ior = NULL;
                }
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
@@ -41,7 +41,8 @@ struct nvkm_outp_func {
        void (*init)(struct nvkm_outp *);
        void (*fini)(struct nvkm_outp *);
        int (*acquire)(struct nvkm_outp *);
-       void (*release)(struct nvkm_outp *, struct nvkm_ior *);
+       void (*release)(struct nvkm_outp *);
+       void (*disable)(struct nvkm_outp *, struct nvkm_ior *);
 };
 
 #define OUTP_MSG(o,l,f,a...) do {                                              
\


Reply via email to