Skip to content

Commit

Permalink
drm/nouveau/disp: fix DP disable race
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ben Skeggs committed Sep 6, 2018
1 parent f6d52b2 commit e04cfdc
Show file tree
Hide file tree
Showing 4 changed files with 19 additions and 9 deletions.
17 changes: 12 additions & 5 deletions drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
Original file line number Diff line number Diff line change
Expand Up @@ -413,14 +413,10 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
}

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;
Expand All @@ -429,6 +425,16 @@ nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior)
);
}

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)
{
Expand Down Expand Up @@ -607,6 +613,7 @@ nvkm_dp_func = {
.fini = nvkm_dp_fini,
.acquire = nvkm_dp_acquire,
.release = nvkm_dp_release,
.disable = nvkm_dp_disable,
};

static int
Expand Down
6 changes: 3 additions & 3 deletions drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,11 +501,11 @@ nv50_disp_super_2_0(struct nv50_disp *disp, struct nvkm_head *head)
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);
}
}

Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ nvkm_outp_release(struct nvkm_outp *outp, u8 user)
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;
}
Expand Down
3 changes: 2 additions & 1 deletion drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,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 { \
Expand Down

0 comments on commit e04cfdc

Please sign in to comment.