Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 112d65a5 authored by Ben Skeggs's avatar Ben Skeggs Committed by Greg Kroah-Hartman
Browse files

drm/nouveau/disp: fix DP disable race



[ 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: default avatarBen Skeggs <bskeggs@redhat.com>
Signed-off-by: default avatarSasha Levin <alexander.levin@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 1a255bf1
Loading
Loading
Loading
Loading
+12 −5
Original line number Diff line number Diff line
@@ -412,14 +412,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;
@@ -428,6 +424,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)
{
@@ -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
+3 −3
Original line number Diff line number Diff line
@@ -436,11 +436,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);
	}
}

+2 −0
Original line number Diff line number Diff line
@@ -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;
		}
+2 −1
Original line number Diff line number Diff line
@@ -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 {                                              \