| From foo@baz Mon Oct 8 17:56:31 CEST 2018 |
| From: Ben Skeggs <bskeggs@redhat.com> |
| Date: Tue, 4 Sep 2018 15:57:09 +1000 |
| Subject: drm/nouveau/disp: fix DP disable race |
| |
| From: Ben Skeggs <bskeggs@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 <bskeggs@redhat.com> |
| Signed-off-by: Sasha Levin <alexander.levin@microsoft.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@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 |
| @@ -436,11 +436,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 { \ |