Skip to content

Commit e8b5f4d

Browse files
Guoniu Zhougregkh
authored andcommitted
media: nxp: imx8-isi: m2m: Fix streaming cleanup on release
[ Upstream commit 178aa33 ] If streamon/streamoff calls are imbalanced, such as when exiting an application with Ctrl+C when streaming, the m2m usage_count will never reach zero and the ISI channel won't be freed. Besides from that, if the input line width is more than 2K, it will trigger a WARN_ON(): [ 59.222120] ------------[ cut here ]------------ [ 59.226758] WARNING: drivers/media/platform/nxp/imx8-isi/imx8-isi-hw.c:631 at mxc_isi_channel_chain+0xa4/0x120, CPU#4: v4l2-ctl/654 [ 59.238569] Modules linked in: ap1302 [ 59.242231] CPU: 4 UID: 0 PID: 654 Comm: v4l2-ctl Not tainted 6.16.0-rc4-next-20250704-06511-gff0e002d480a-dirty #258 PREEMPT [ 59.253597] Hardware name: NXP i.MX95 15X15 board (DT) [ 59.258720] pstate: 80400009 (Nzcv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 59.265669] pc : mxc_isi_channel_chain+0xa4/0x120 [ 59.270358] lr : mxc_isi_channel_chain+0x44/0x120 [ 59.275047] sp : ffff8000848c3b40 [ 59.278348] x29: ffff8000848c3b40 x28: ffff0000859b4c98 x27: ffff800081939f00 [ 59.285472] x26: 000000000000000a x25: ffff0000859b4cb8 x24: 0000000000000001 [ 59.292597] x23: ffff0000816f4760 x22: ffff0000816f4258 x21: ffff000084ceb780 [ 59.299720] x20: ffff000084342ff8 x19: ffff000084340000 x18: 0000000000000000 [ 59.306845] x17: 0000000000000000 x16: 0000000000000000 x15: 0000ffffdb369e1c [ 59.313969] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ 59.321093] x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000 [ 59.328217] x8 : ffff8000848c3d48 x7 : ffff800081930b30 x6 : ffff800081930b30 [ 59.335340] x5 : ffff0000859b6000 x4 : ffff80008193ae80 x3 : ffff800081022420 [ 59.342464] x2 : ffff0000852f6900 x1 : 0000000000000001 x0 : ffff000084341000 [ 59.349590] Call trace: [ 59.352025] mxc_isi_channel_chain+0xa4/0x120 (P) [ 59.356722] mxc_isi_m2m_streamon+0x160/0x20c [ 59.361072] v4l_streamon+0x24/0x30 [ 59.364556] __video_do_ioctl+0x40c/0x4a0 [ 59.368560] video_usercopy+0x2bc/0x690 [ 59.372382] video_ioctl2+0x18/0x24 [ 59.375857] v4l2_ioctl+0x40/0x60 [ 59.379168] __arm64_sys_ioctl+0xac/0x104 [ 59.383172] invoke_syscall+0x48/0x104 [ 59.386916] el0_svc_common.constprop.0+0xc0/0xe0 [ 59.391613] do_el0_svc+0x1c/0x28 [ 59.394915] el0_svc+0x34/0xf4 [ 59.397966] el0t_64_sync_handler+0xa0/0xe4 [ 59.402143] el0t_64_sync+0x198/0x19c [ 59.405801] ---[ end trace 0000000000000000 ]--- Address this issue by moving the streaming preparation and cleanup to the vb2 .prepare_streaming() and .unprepare_streaming() operations. This also simplifies the driver by allowing direct usage of the v4l2_m2m_ioctl_streamon() and v4l2_m2m_ioctl_streamoff() helpers. Fixes: cf21f32 ("media: nxp: Add i.MX8 ISI driver") Cc: [email protected] Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Guoniu Zhou <[email protected]> Co-developed-by: Laurent Pinchart <[email protected]> Signed-off-by: Laurent Pinchart <[email protected]> Tested-by: Guoniu Zhou <[email protected]> Reviewed-by: Frank Li <[email protected]> Signed-off-by: Hans Verkuil <[email protected]> [ Adjust context ] Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent f0b75b4 commit e8b5f4d

File tree

1 file changed

+92
-132
lines changed

1 file changed

+92
-132
lines changed

drivers/media/platform/nxp/imx8-isi/imx8-isi-m2m.c

Lines changed: 92 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ struct mxc_isi_m2m_ctx_queue_data {
4343
struct v4l2_pix_format_mplane format;
4444
const struct mxc_isi_format_info *info;
4545
u32 sequence;
46-
bool streaming;
4746
};
4847

4948
struct mxc_isi_m2m_ctx {
@@ -236,6 +235,65 @@ static void mxc_isi_m2m_vb2_buffer_queue(struct vb2_buffer *vb2)
236235
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
237236
}
238237

238+
static int mxc_isi_m2m_vb2_prepare_streaming(struct vb2_queue *q)
239+
{
240+
struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
241+
const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format;
242+
const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format;
243+
const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info;
244+
const struct mxc_isi_format_info *out_info = ctx->queues.out.info;
245+
struct mxc_isi_m2m *m2m = ctx->m2m;
246+
int ret;
247+
248+
guard(mutex)(&m2m->lock);
249+
250+
if (m2m->usage_count == INT_MAX)
251+
return -EOVERFLOW;
252+
253+
/*
254+
* Acquire the pipe and initialize the channel with the first user of
255+
* the M2M device.
256+
*/
257+
if (m2m->usage_count == 0) {
258+
bool bypass = cap_pix->width == out_pix->width &&
259+
cap_pix->height == out_pix->height &&
260+
cap_info->encoding == out_info->encoding;
261+
262+
ret = mxc_isi_channel_acquire(m2m->pipe,
263+
&mxc_isi_m2m_frame_write_done,
264+
bypass);
265+
if (ret)
266+
return ret;
267+
268+
mxc_isi_channel_get(m2m->pipe);
269+
}
270+
271+
m2m->usage_count++;
272+
273+
/*
274+
* Allocate resources for the channel, counting how many users require
275+
* buffer chaining.
276+
*/
277+
if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {
278+
ret = mxc_isi_channel_chain(m2m->pipe);
279+
if (ret)
280+
goto err_deinit;
281+
282+
m2m->chained_count++;
283+
ctx->chained = true;
284+
}
285+
286+
return 0;
287+
288+
err_deinit:
289+
if (--m2m->usage_count == 0) {
290+
mxc_isi_channel_put(m2m->pipe);
291+
mxc_isi_channel_release(m2m->pipe);
292+
}
293+
294+
return ret;
295+
}
296+
239297
static int mxc_isi_m2m_vb2_start_streaming(struct vb2_queue *q,
240298
unsigned int count)
241299
{
@@ -265,15 +323,46 @@ static void mxc_isi_m2m_vb2_stop_streaming(struct vb2_queue *q)
265323
}
266324
}
267325

326+
static void mxc_isi_m2m_vb2_unprepare_streaming(struct vb2_queue *q)
327+
{
328+
struct mxc_isi_m2m_ctx *ctx = vb2_get_drv_priv(q);
329+
struct mxc_isi_m2m *m2m = ctx->m2m;
330+
331+
guard(mutex)(&m2m->lock);
332+
333+
/*
334+
* If the last context is this one, reset it to make sure the device
335+
* will be reconfigured when streaming is restarted.
336+
*/
337+
if (m2m->last_ctx == ctx)
338+
m2m->last_ctx = NULL;
339+
340+
/* Free the channel resources if this is the last chained context. */
341+
if (ctx->chained && --m2m->chained_count == 0)
342+
mxc_isi_channel_unchain(m2m->pipe);
343+
ctx->chained = false;
344+
345+
/* Turn off the light with the last user. */
346+
if (--m2m->usage_count == 0) {
347+
mxc_isi_channel_disable(m2m->pipe);
348+
mxc_isi_channel_put(m2m->pipe);
349+
mxc_isi_channel_release(m2m->pipe);
350+
}
351+
352+
WARN_ON(m2m->usage_count < 0);
353+
}
354+
268355
static const struct vb2_ops mxc_isi_m2m_vb2_qops = {
269356
.queue_setup = mxc_isi_m2m_vb2_queue_setup,
270357
.buf_init = mxc_isi_m2m_vb2_buffer_init,
271358
.buf_prepare = mxc_isi_m2m_vb2_buffer_prepare,
272359
.buf_queue = mxc_isi_m2m_vb2_buffer_queue,
273360
.wait_prepare = vb2_ops_wait_prepare,
274361
.wait_finish = vb2_ops_wait_finish,
362+
.prepare_streaming = mxc_isi_m2m_vb2_prepare_streaming,
275363
.start_streaming = mxc_isi_m2m_vb2_start_streaming,
276364
.stop_streaming = mxc_isi_m2m_vb2_stop_streaming,
365+
.unprepare_streaming = mxc_isi_m2m_vb2_unprepare_streaming,
277366
};
278367

279368
static int mxc_isi_m2m_queue_init(void *priv, struct vb2_queue *src_vq,
@@ -483,135 +572,6 @@ static int mxc_isi_m2m_s_fmt_vid(struct file *file, void *fh,
483572
return 0;
484573
}
485574

486-
static int mxc_isi_m2m_streamon(struct file *file, void *fh,
487-
enum v4l2_buf_type type)
488-
{
489-
struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
490-
struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type);
491-
const struct v4l2_pix_format_mplane *out_pix = &ctx->queues.out.format;
492-
const struct v4l2_pix_format_mplane *cap_pix = &ctx->queues.cap.format;
493-
const struct mxc_isi_format_info *cap_info = ctx->queues.cap.info;
494-
const struct mxc_isi_format_info *out_info = ctx->queues.out.info;
495-
struct mxc_isi_m2m *m2m = ctx->m2m;
496-
int ret;
497-
498-
if (q->streaming)
499-
return 0;
500-
501-
mutex_lock(&m2m->lock);
502-
503-
if (m2m->usage_count == INT_MAX) {
504-
ret = -EOVERFLOW;
505-
goto unlock;
506-
}
507-
508-
/*
509-
* Acquire the pipe and initialize the channel with the first user of
510-
* the M2M device.
511-
*/
512-
if (m2m->usage_count == 0) {
513-
bool bypass = cap_pix->width == out_pix->width &&
514-
cap_pix->height == out_pix->height &&
515-
cap_info->encoding == out_info->encoding;
516-
517-
ret = mxc_isi_channel_acquire(m2m->pipe,
518-
&mxc_isi_m2m_frame_write_done,
519-
bypass);
520-
if (ret)
521-
goto unlock;
522-
523-
mxc_isi_channel_get(m2m->pipe);
524-
}
525-
526-
m2m->usage_count++;
527-
528-
/*
529-
* Allocate resources for the channel, counting how many users require
530-
* buffer chaining.
531-
*/
532-
if (!ctx->chained && out_pix->width > MXC_ISI_MAX_WIDTH_UNCHAINED) {
533-
ret = mxc_isi_channel_chain(m2m->pipe);
534-
if (ret)
535-
goto deinit;
536-
537-
m2m->chained_count++;
538-
ctx->chained = true;
539-
}
540-
541-
/*
542-
* Drop the lock to start the stream, as the .device_run() operation
543-
* needs to acquire it.
544-
*/
545-
mutex_unlock(&m2m->lock);
546-
ret = v4l2_m2m_ioctl_streamon(file, fh, type);
547-
if (ret) {
548-
/* Reacquire the lock for the cleanup path. */
549-
mutex_lock(&m2m->lock);
550-
goto unchain;
551-
}
552-
553-
q->streaming = true;
554-
555-
return 0;
556-
557-
unchain:
558-
if (ctx->chained && --m2m->chained_count == 0)
559-
mxc_isi_channel_unchain(m2m->pipe);
560-
ctx->chained = false;
561-
562-
deinit:
563-
if (--m2m->usage_count == 0) {
564-
mxc_isi_channel_put(m2m->pipe);
565-
mxc_isi_channel_release(m2m->pipe);
566-
}
567-
568-
unlock:
569-
mutex_unlock(&m2m->lock);
570-
return ret;
571-
}
572-
573-
static int mxc_isi_m2m_streamoff(struct file *file, void *fh,
574-
enum v4l2_buf_type type)
575-
{
576-
struct mxc_isi_m2m_ctx *ctx = to_isi_m2m_ctx(fh);
577-
struct mxc_isi_m2m_ctx_queue_data *q = mxc_isi_m2m_ctx_qdata(ctx, type);
578-
struct mxc_isi_m2m *m2m = ctx->m2m;
579-
580-
v4l2_m2m_ioctl_streamoff(file, fh, type);
581-
582-
if (!q->streaming)
583-
return 0;
584-
585-
mutex_lock(&m2m->lock);
586-
587-
/*
588-
* If the last context is this one, reset it to make sure the device
589-
* will be reconfigured when streaming is restarted.
590-
*/
591-
if (m2m->last_ctx == ctx)
592-
m2m->last_ctx = NULL;
593-
594-
/* Free the channel resources if this is the last chained context. */
595-
if (ctx->chained && --m2m->chained_count == 0)
596-
mxc_isi_channel_unchain(m2m->pipe);
597-
ctx->chained = false;
598-
599-
/* Turn off the light with the last user. */
600-
if (--m2m->usage_count == 0) {
601-
mxc_isi_channel_disable(m2m->pipe);
602-
mxc_isi_channel_put(m2m->pipe);
603-
mxc_isi_channel_release(m2m->pipe);
604-
}
605-
606-
WARN_ON(m2m->usage_count < 0);
607-
608-
mutex_unlock(&m2m->lock);
609-
610-
q->streaming = false;
611-
612-
return 0;
613-
}
614-
615575
static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = {
616576
.vidioc_querycap = mxc_isi_m2m_querycap,
617577

@@ -632,8 +592,8 @@ static const struct v4l2_ioctl_ops mxc_isi_m2m_ioctl_ops = {
632592
.vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf,
633593
.vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs,
634594

635-
.vidioc_streamon = mxc_isi_m2m_streamon,
636-
.vidioc_streamoff = mxc_isi_m2m_streamoff,
595+
.vidioc_streamon = v4l2_m2m_ioctl_streamon,
596+
.vidioc_streamoff = v4l2_m2m_ioctl_streamoff,
637597

638598
.vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
639599
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,

0 commit comments

Comments
 (0)