From 72a51555bbc4ff9b8193b5b904f5444f871e81a6 Mon Sep 17 00:00:00 2001 From: David Tang Date: Tue, 2 Jun 2026 23:40:42 +0800 Subject: [PATCH 1/2] Use IS_ERR() for kthread_create() error handling kthread_create() returns ERR_PTR() on failure rather than NULL. Replace the NULL check with IS_ERR() and propagate the error code via PTR_ERR(). This prevents wake_up_process() from being called on an invalid task pointer when thread creation fails. --- videobuf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/videobuf.c b/videobuf.c index dd5298a..fff4c80 100644 --- a/videobuf.c +++ b/videobuf.c @@ -70,8 +70,9 @@ static int vcam_start_streaming(struct vb2_queue *q, unsigned int count) /* Try to start kernel thread */ dev->sub_thr_id = kthread_create(submitter_thread, dev, "vcam_submitter"); - if (!dev->sub_thr_id) { + if (IS_ERR(dev->sub_thr_id)) { pr_err("Failed to create kernel thread\n"); + dev->sub_thr_id = NULL; return -ECANCELED; } From 10ead390a4fc1ee98bc44453e71558733d60104a Mon Sep 17 00:00:00 2001 From: David Tang Date: Tue, 2 Jun 2026 23:40:48 +0800 Subject: [PATCH 2/2] Move vb2_buffer_done() out of spinlock vcam_stop_streaming() completes queued buffers while holding out_q_slock with IRQs disabled. vb2_buffer_done() must not be called in atomic context as it internally invokes wake_up() on the done wait queue. Move all active buffers to a temporary list under the spinlock and release the lock before calling vb2_buffer_done(). This ensures buffer completion is always invoked from a preemptible context. --- videobuf.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/videobuf.c b/videobuf.c index fff4c80..7d63b0d 100644 --- a/videobuf.c +++ b/videobuf.c @@ -86,22 +86,25 @@ static void vcam_stop_streaming(struct vb2_queue *vb2_q) struct vcam_device *dev = vb2_q->drv_priv; struct vcam_out_queue *q = &dev->vcam_out_vidq; unsigned long flags = 0; + LIST_HEAD(tmp_list); - /* Stop running threads */ if (dev->sub_thr_id) kthread_stop(dev->sub_thr_id); - dev->sub_thr_id = NULL; - /* Empty buffer queue */ + + /* Collect buffers under spinlock, return them to vb2 outside it. + * vb2_buffer_done() must not be called while holding a spinlock. */ spin_lock_irqsave(&dev->out_q_slock, flags); - while (!list_empty(&q->active)) { + list_splice_init(&q->active, &tmp_list); + spin_unlock_irqrestore(&dev->out_q_slock, flags); + + while (!list_empty(&tmp_list)) { struct vcam_out_buffer *buf = - list_entry(q->active.next, struct vcam_out_buffer, list); + list_entry(tmp_list.next, struct vcam_out_buffer, list); list_del(&buf->list); vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); pr_debug("Throwing out buffer\n"); } - spin_unlock_irqrestore(&dev->out_q_slock, flags); } static void vcam_outbuf_lock(struct vb2_queue *vq)