From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0082F406805 for ; Mon, 13 Jul 2026 12:09:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783944556; cv=none; b=iNwIsxaydnJVcsrJZrBCR1h6Ef0ia8WJ/Ci5xWy8mzHUpQZVVPQt7w/BzK72g1h/WtWB7aRr5wCStagzYf6XKoox7CPoOADeR74mLddJP2ZBttxQDEkO94pAGkVwINMz0Ww205JL2mmyjwq9bvZVOAliQeky7GJE1aGzn1ryAuY= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783944556; c=relaxed/simple; bh=N3uewV9p1wEkq7IBGmE047BkMIsp0re4khks6TNRAIE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Q2yWIVYLpeybW19vkmP4mvlwOleKg6Gxx9ASABuNyeJierOv3ZeeIjw5gNIq/xH3rDXrAx9zonksHFbQv2kYfuz9dY19Qfi75x3mGmQmJRcjIigkh/RarSdwxZUJAemB4VvjQyDQRLhOwuERw3e+dAuMIJBd2ggIH7caonza9Q0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=N/mOfQ3D; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="N/mOfQ3D" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2cacd69a9c0so31667905ad.1 for ; Mon, 13 Jul 2026 05:09:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1783944554; x=1784549354; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=O9YYyee56RYzJH/7lhC6WY6QS3lP6Hx70/PxBzEZ1WM=; b=N/mOfQ3D2FKU41gWawBnyoSlMWiQK75gyErFJfHUsnMpFajxXnSQf+w+Ada+WkSTBg yMSPJ6BRBA/f7sGh40e42zutsruTGOg7/C82yCLM2ZAshtBzg0C5+2GD6xvyJxyS/5H3 iuclQvnHwZYAUwM3DIgbGo2JKaal7kf2iMYnuFH7Y9tvr28CDrmcAMkqOimpEg5b1OwB OZWr8fHESAXBq/fdTxJBZwVbMPJbNJUBwKynnkKv1GfaesS2seThg9/sALsTI7UNA1qA NGuJe3rLky8ssUUmYFXMVQTDcwxnJb1IlmkFi7l0eXVpymfL8h5GHGD4WpaLU616wgie RAVQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783944554; x=1784549354; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=O9YYyee56RYzJH/7lhC6WY6QS3lP6Hx70/PxBzEZ1WM=; b=eODWUs4tTBBHQpV1ffOUXC3W+p190MkQlJjl+Ga0Iqh6ZiNiiUzGIgnnC5JzWrlYpm KeX5aVwDOb9HjVcFrj9UI/hx7wQAQhqsPWP76em5+bA54sdj1OTBQRh+sCs/Me7wbrLc FPw0oTWjR7oUdlGsc8QG5bfEoBsucP56Gm8ypchNDLvSGYYadcElVAs9Jlum+/1R3R03 NHuhsnVxo2DzWKpqxDrYG6BhDJIdfS5dSG/OzL9RJ9wh8KqcmWig2R7c4PpmvSHn2sq1 7y2twZ8k8ufwpJ84VZex3VZ1fh2nO0Lq/0/f+A6oSzIpMYYFhj5+nkSCPnCuK/t2jDPP Nqyg== X-Forwarded-Encrypted: i=1; AHgh+RpTqMSS+wvmTOyFI9/nNj0gBWSoGTfzP8pMMX4Zd4IiwpMwQClgObvZZBcsrc1ejrKvrYJVG09k4uto/os=@vger.kernel.org X-Gm-Message-State: AOJu0YxxWFklulCDpOY0P+szwDaPnOZzNbQL+oHk3cNChodlSBls1MSW qb2NjnOGEvxwMwEbWy9fv/D1qIGXucBhWUWwrTM+a5zgcXbESvnAAldI X-Gm-Gg: AfdE7cnzfmRbGLXIPZOZrlDd8zKPaDjru9um8wN2DcN1nR/nMQAQKvNHC37aYV5qvCx Yo4/MMEx3DQBDy0QHyzfQUn5ez+HwnAATRK5OdxxdeqEEF6ijm3ipi1fhALIYoTL2OQd0wLxE72 XGJQS3XlBUOsngwi10qJpB9bnAEcOe74TQtrTTxK/9iyFfcR/nvu4iUdy6elku1C4aNq3oM6NM+ LBPEVoUADToPiICOAsw1LKpNqLNFB0Ren9z8A+yuyL1FFlahiJUy/uSSI/OcaHj8sY3OZWp1Xo7 soo3Jp1iLjpH0XCN5GqVsAWZzqrO5dO4ZozcYkREYUitCiAjoTTkXxo3szaCBBuWRzzk4Ah0yWa 4G59SEbrUEsPbRi1pTHhzKlCXsGpXLI/HX/hCmOtvE7++xuuCDyHWgOSimgojbDRdIJr3RB6CCg U65g1pkb0V1kDV7zww2YZ5 X-Received: by 2002:a17:90b:58f0:b0:387:e0db:bc23 with SMTP id 98e67ed59e1d1-38dc77b82bemr9015022a91.35.1783944553867; Mon, 13 Jul 2026 05:09:13 -0700 (PDT) Received: from rockpi-5b ([45.112.0.180]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-311747f7293sm67756935eec.3.2026.07.13.05.09.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Jul 2026 05:09:13 -0700 (PDT) From: Anand Moon To: Neil Armstrong , Maarten Lankhorst , Maxime Ripard , Thomas Zimmermann , David Airlie , Simona Vetter , Kevin Hilman , Jerome Brunet , Martin Blumenstingl , Mauro Carvalho Chehab , Greg Kroah-Hartman , dri-devel@lists.freedesktop.org (open list:DRM DRIVERS FOR AMLOGIC SOCS), linux-amlogic@lists.infradead.org (open list:DRM DRIVERS FOR AMLOGIC SOCS), linux-arm-kernel@lists.infradead.org (moderated list:ARM/Amlogic Meson SoC support), linux-kernel@vger.kernel.org (open list), linux-media@vger.kernel.org (open list:MESON VIDEO DECODER DRIVER FOR AMLOGIC SOCS), linux-staging@lists.linux.dev (open list:STAGING SUBSYSTEM) Cc: Anand Moon , Doruk Tan Ozturk , Nicolas Dufresne , Sashiko Subject: [PATCH v7 02/19] media: meson: vdec: Fix STREAMON / STREAMOFF race conditions and session teardown Date: Mon, 13 Jul 2026 17:36:57 +0530 Message-ID: <20260713120840.17427-3-linux.amoon@gmail.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20260713120840.17427-1-linux.amoon@gmail.com> References: <20260713120840.17427-1-linux.amoon@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit The vdec driver suffered from unsafe state transitions and race conditions when handling concurrent STREAMON / STREAMOFF calls and dynamic resolution change (DRC) events. Global context pointers (e.g. core->cur_sess) and session status flags were updated outside proper lock boundaries, allowing parallel threads to corrupt hardware state under load. Address these architectural stability flaws with the following changes: 1. In vdec_start_streaming(), safely encapsulate hardware occupancy evaluations and target session context claims within a secure mutex lock (core->lock). This blocks overlapping multi-threaded STREAMON calls from creating concurrent assignment conflicts. 2. Restructure initialization error path handlers to avoid global memory maps cross-contamination. Segregate buffer flushes cleanly based strictly on the active vb2 queue type (OUTPUT vs CAPTURE) to avoid incorrectly reclaiming undecoded ready blocks. 3. In vdec_stop_streaming(), introduce state-aware tracking to accurately identify DRC conditions. If a resolution modification forces a capture queue reset while the output stream is still live, preserve the driver's inner runtime states and bypass premature hardware power-off sweeps. 4. Enforce strict null-pointer safety limits inside DMA unmapping sequences. Explicitly clear tracking metadata entries (sess->vififo_vaddr = NULL) inside the VIFIFO freeing routines to neutralize accidental double-free risks. Together these changes harden the driver against concurrency bugs, eliminate memory leaks, and ensure predictable session lifecycle management under multi threaded workloads. Cc: Nicolas Dufresne Reported-by: Sashiko Closes: https://lore.kernel.org/all/20260530100841.9CEBA1F00893@smtp.kernel.org/ Signed-off-by: Anand Moon --- drivers/staging/media/meson/vdec/vdec.c | 136 +++++++++++++++++------- 1 file changed, 95 insertions(+), 41 deletions(-) diff --git a/drivers/staging/media/meson/vdec/vdec.c b/drivers/staging/media/meson/vdec/vdec.c index 6ae3471155a87..d1f35fc893de1 100644 --- a/drivers/staging/media/meson/vdec/vdec.c +++ b/drivers/staging/media/meson/vdec/vdec.c @@ -286,11 +286,6 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) struct vb2_v4l2_buffer *buf; int ret; - if (core->cur_sess && core->cur_sess != sess) { - ret = -EBUSY; - goto bufs_done; - } - if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) sess->streamon_out = 1; else @@ -308,9 +303,29 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) } if (sess->status == STATUS_RUNNING || - sess->status == STATUS_NEEDS_RESUME || - sess->status == STATUS_INIT) + sess->status == STATUS_NEEDS_RESUME) + return 0; + + /* + * Secure the core hardware lock before checking availability + * and updating session states to prevent STREAMON race conditions. + */ + mutex_lock(&core->lock); + if (core->cur_sess && core->cur_sess != sess) { + ret = -EBUSY; + mutex_unlock(&core->lock); + goto err_unlock_no_hw; + } + + /* If already half-initialized, do not re-initialize */ + if (sess->status == STATUS_INIT) { + mutex_unlock(&core->lock); return 0; + } + + sess->status = STATUS_INIT; + core->cur_sess = sess; + mutex_unlock(&core->lock); sess->vififo_size = SIZE_VIFIFO; sess->vififo_vaddr = @@ -319,7 +334,7 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) if (!sess->vififo_vaddr) { dev_err(sess->core->dev, "Failed to request VIFIFO buffer\n"); ret = -ENOMEM; - goto bufs_done; + goto err_cleanup_session; } sess->should_stop = 0; @@ -333,33 +348,43 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count) ret = vdec_poweron(sess); if (ret) - goto vififo_free; + goto err_free_vififo; sess->sequence_cap = 0; sess->sequence_out = 0; + if (vdec_codec_needs_recycle(sess)) sess->recycle_thread = kthread_run(vdec_recycle_thread, sess, "vdec_recycle"); - sess->status = STATUS_INIT; - core->cur_sess = sess; schedule_work(&sess->esparser_queue_work); return 0; -vififo_free: - dma_free_coherent(sess->core->dev, sess->vififo_size, - sess->vififo_vaddr, sess->vififo_paddr); -bufs_done: - while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); - while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx))) - v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); - +err_free_vififo: + if (sess->vififo_vaddr) { + dma_free_coherent(sess->core->dev, sess->vififo_size, + sess->vififo_vaddr, sess->vififo_paddr); + sess->vififo_vaddr = NULL; + sess->vififo_paddr = 0; + } +err_cleanup_session: if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) sess->streamon_out = 0; else sess->streamon_cap = 0; + mutex_lock(&core->lock); + if (core->cur_sess == sess) + core->cur_sess = NULL; + if (sess->status != STATUS_NEEDS_RESUME) + sess->status = STATUS_STOPPED; + mutex_unlock(&core->lock); +err_unlock_no_hw: + while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx))) + v4l2_m2m_buf_done(buf, VB2_BUF_STATE_QUEUED); + return ret; } @@ -399,30 +424,13 @@ static void vdec_stop_streaming(struct vb2_queue *q) struct amvdec_codec_ops *codec_ops = sess->fmt_out->codec_ops; struct amvdec_core *core = sess->core; struct vb2_v4l2_buffer *buf; + enum amvdec_status old_status; + bool full_cleanup = false; - if (sess->status == STATUS_RUNNING || - sess->status == STATUS_INIT || - (sess->status == STATUS_NEEDS_RESUME && - (!sess->streamon_out || !sess->streamon_cap))) { - if (vdec_codec_needs_recycle(sess)) - kthread_stop(sess->recycle_thread); - - vdec_poweroff(sess); - vdec_free_canvas(sess); - dma_free_coherent(sess->core->dev, sess->vififo_size, - sess->vififo_vaddr, sess->vififo_paddr); - vdec_reset_timestamps(sess); - vdec_reset_bufs_recycle(sess); - kfree(sess->priv); - sess->priv = NULL; - core->cur_sess = NULL; - sess->status = STATUS_STOPPED; - } - + /* flush buffers to kill background workqueue thread */ if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { while ((buf = v4l2_m2m_src_buf_remove(sess->m2m_ctx))) v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); - sess->streamon_out = 0; } else { /* Drain remaining refs if was still running */ @@ -431,9 +439,55 @@ static void vdec_stop_streaming(struct vb2_queue *q) while ((buf = v4l2_m2m_dst_buf_remove(sess->m2m_ctx))) v4l2_m2m_buf_done(buf, VB2_BUF_STATE_ERROR); - sess->streamon_cap = 0; } + + /* Hold core lock continuously for the state and resource processing */ + mutex_lock(&core->lock); + old_status = sess->status; + + if (old_status == STATUS_RUNNING || old_status == STATUS_INIT || + (old_status == STATUS_NEEDS_RESUME && (!sess->streamon_out || + !sess->streamon_cap))) { + /* + * If it's a DRC event (Capture queue streamoff only), preserve + * the status + */ + if (old_status == STATUS_NEEDS_RESUME && sess->streamon_out) { + full_cleanup = false; + } else { + full_cleanup = true; + sess->status = STATUS_STOPPED; + } + } + + if (full_cleanup) { + if ((q->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || + !sess->streamon_out) && vdec_codec_needs_recycle(sess)) { + kthread_stop(sess->recycle_thread); + } + + vdec_poweroff(sess); + vdec_free_canvas(sess); + + if (sess->vififo_vaddr) { + dma_free_coherent(sess->core->dev, sess->vififo_size, + sess->vififo_vaddr, sess->vififo_paddr); + sess->vififo_vaddr = NULL; + sess->vififo_paddr = 0; + } + + vdec_reset_timestamps(sess); + vdec_reset_bufs_recycle(sess); + core->cur_sess = NULL; + + kfree(sess->priv); + sess->priv = NULL; + } else { + if (sess->status == STATUS_NEEDS_RESUME) + sess->changed_format = 0; + } + mutex_unlock(&core->lock); } static int vdec_vb2_buf_prepare(struct vb2_buffer *vb) -- 2.50.1