This is a note to let you know that I've just added the patch titled drm/vc4: txp: Protect device resources to the 5.19-stable tree which can be found at: http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary The filename of the patch is: drm-vc4-txp-protect-device-resources.patch and it can be found in the queue-5.19 subdirectory. If you, or anyone else, feels it should not be added to the stable tree, please let <stable@xxxxxxxxxxxxxxx> know about it. commit 1c61fad57e773675290342698d815fd0fb0debff Author: Maxime Ripard <maxime@xxxxxxxxxx> Date: Mon Jul 11 19:39:23 2022 +0200 drm/vc4: txp: Protect device resources [ Upstream commit b7345c9799da578c150fde3072446e4049c39c41 ] Our current code now mixes some resources whose lifetime are tied to the device (clocks, IO mappings, etc.) and some that are tied to the DRM device (encoder, bridge). The device one will be freed at unbind time, but the DRM one will only be freed when the last user of the DRM device closes its file handle. So we end up with a time window during which we can call the encoder hooks, but we don't have access to the underlying resources and device. Let's protect all those sections with drm_dev_enter() and drm_dev_exit() so that we bail out if we are during that window. Acked-by: Thomas Zimmermann <tzimmermann@xxxxxxx> Signed-off-by: Maxime Ripard <maxime@xxxxxxxxxx> Link: https://lore.kernel.org/r/20220711173939.1132294-54-maxime@xxxxxxxxxx Stable-dep-of: 84dfc46594b0 ("drm/panel: use 'select' for Ili9341 panel driver helpers") Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx> diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 3579d487402e..acc72fe1e113 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -15,6 +15,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_drv.h> #include <drm/drm_edid.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_fourcc.h> @@ -275,6 +276,7 @@ static int vc4_txp_connector_atomic_check(struct drm_connector *conn, static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, struct drm_atomic_state *state) { + struct drm_device *drm = conn->dev; struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state, conn); struct vc4_txp *txp = connector_to_vc4_txp(conn); @@ -282,6 +284,7 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, struct drm_display_mode *mode; struct drm_framebuffer *fb; u32 ctrl; + int idx; int i; if (WARN_ON(!conn_state->writeback_job)) @@ -311,6 +314,9 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, */ ctrl |= TXP_ALPHA_INVERT; + if (!drm_dev_enter(drm, &idx)) + return; + gem = drm_fb_cma_get_gem_obj(fb, 0); TXP_WRITE(TXP_DST_PTR, gem->paddr + fb->offsets[0]); TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]); @@ -321,6 +327,8 @@ static void vc4_txp_connector_atomic_commit(struct drm_connector *conn, TXP_WRITE(TXP_DST_CTRL, ctrl); drm_writeback_queue_job(&txp->connector, conn_state); + + drm_dev_exit(idx); } static const struct drm_connector_helper_funcs vc4_txp_connector_helper_funcs = { @@ -353,7 +361,12 @@ static const struct drm_connector_funcs vc4_txp_connector_funcs = { static void vc4_txp_encoder_disable(struct drm_encoder *encoder) { + struct drm_device *drm = encoder->dev; struct vc4_txp *txp = encoder_to_vc4_txp(encoder); + int idx; + + if (!drm_dev_enter(drm, &idx)) + return; if (TXP_READ(TXP_DST_CTRL) & TXP_BUSY) { unsigned long timeout = jiffies + msecs_to_jiffies(1000); @@ -368,6 +381,8 @@ static void vc4_txp_encoder_disable(struct drm_encoder *encoder) } TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN); + + drm_dev_exit(idx); } static const struct drm_encoder_helper_funcs vc4_txp_encoder_helper_funcs = { @@ -452,6 +467,16 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data) struct vc4_txp *txp = data; struct vc4_crtc *vc4_crtc = &txp->base; + /* + * We don't need to protect the register access using + * drm_dev_enter() there because the interrupt handler lifetime + * is tied to the device itself, and not to the DRM device. + * + * So when the device will be gone, one of the first thing we + * will be doing will be to unregister the interrupt handler, + * and then unregister the DRM device. drm_dev_enter() would + * thus always succeed if we are here. + */ TXP_WRITE(TXP_DST_CTRL, TXP_READ(TXP_DST_CTRL) & ~TXP_EI); vc4_crtc_handle_vblank(vc4_crtc); drm_writeback_signal_completion(&txp->connector, 0);