mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Sascha Hauer" <s.hauer@pengutronix.de>
To: "Nicolas Dufresne" <nicolas@ndufresne.ca>
Cc: "Sascha Hauer" <s.hauer@pengutronix.de>,
	"Lucas Sinn" <lucas.sinn@wolfvision.net>,
	"Mauro Carvalho Chehab" <mchehab@kernel.org>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Heiko Stuebner" <heiko@sntech.de>,
	"Philipp Zabel" <p.zabel@pengutronix.de>,
	linux-media@vger.kernel.org, linux-rockchip@lists.infradead.org,
	devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v5 2/4] media: rockchip: Add JPEG decoder driver
Date: Mon, 05 Oct 2026 09:40:24 +0000	[thread overview]
Message-ID: <ea747d54-acec-45cd-bcc4-77385812eead@pengutronix.de> (raw)
In-Reply-To: <c28b782fd2de05f4e1fdaa1df96a57f04640f4df.camel@ndufresne.ca>

On 2026-09-29 15:34, Nicolas Dufresne wrote:
> > > > +static int vdpu720_fill_chroma(struct rkjpegd_ctx *ctx,
> > > > +			       struct vb2_v4l2_buffer *dst_buf)
> > > > +{
> > > > +	struct rkjpegd_dev *jpegd = ctx->dev;
> > > > +	struct vb2_buffer *vb = &dst_buf->vb2_buf;
> > > > +	u32 y_size, size;
> > > > +	void *dst_cpu;
> > > > +	int ret;
> > > > +
> > > > +	mutex_lock(&ctx->fmt_lock);
> > > 
> > > I keep seeing this fmt lock over and over and can't get my head around why you
> > > would need that. There is implicit locking in the VIDIOC system that do protect
> > > these. Again, can you in your own word explain your choices ?
> > 
> > It comes with dynamic resolution support. The check for a new resolution
> > has to be done in device_run() to make sure the resolution change takes
> > place on that exact frame. Doing it in buf_queue() would mean we get the
> > frames still in the queue wrong. We can't take vdev_lock in
> > device_run() which is why sashiko continuously stumbled upon missing
> > locking of ctx->dst_fmt.
> > 
> > I cannot judge how propable an actual race or how serious this missing
> > locking is. But yes, the fmt_lock is a direct result of my LLM fighting
> > against Sashiko.
> > 
> > So I could drop dynamic resolution support (which I don't need
> > currently), or we could ignore Sashiko here.
> 
> That's the tougher one to solve I suppose. This is probably the first jpeg
> decoder implementing DYN_RESOLUTION, which is valid, just a feature more recent
> then most jpeg decoders today.
> 
> I was mostly triggered by the fact  called fmt lock, since device_run() only
> occurs while the device is streaming, and the format cannot change while
> streaming.
> 
> On dynamic resolution change, to avoid the need for this locking, I believe you
> want to delay the update of the dst_fmt to when the draining have completed and
> userspace stop streaming. Only then it is safe, as in order to resume decoding,
> userspace will have to call streamon, which will fail if the allocated buffers
> are too small. There is no other validation of the buffer size in place, so
> otherwise you can just do CMD_START and let the HW write all over the place.

Ok, with the next version the fmt_lock will be gone. I'll update dst_fmt
only under vdev_lock. On a format change the decoder stops at that
frame. In g_fmt I can now report either the dst_fmt, or, when the
decoder has stopped, the format of the frame at the head of the queue.

> > > > +	irqreturn_t ret = IRQ_NONE;
> > > > +	u32 status;
> > > > +
> > > > +	/* The registers are only clocked while the device is runtime active. */
> > > > +	if (pm_runtime_get_if_active(jpegd->dev) <= 0)
> > > > +		return IRQ_NONE;
> > > 
> > > Was that sashiko asking you to do that ?
> > 
> > Yes, several times:
> > 
> >   ▎ [High] The interrupt handler reads hardware registers without checking if the device is active via 
> >   ▎ pm_runtime, leading to crashes on spurious interrupts.
> > 
> > 
> >   ▎ [Severity: High]
> >   ▎ This reads the VDPU720_REG_INT register unconditionally upon entry. If a spurious interrupt or an 
> >   ▎ irqpoll event occurs while the device is in a runtime-suspended state (with clocks and power domains 
> >   ▎ gated off), will this read trigger a synchronous external abort on ARM? Should it use 
> >   ▎ pm_runtime_get_if_active() to verify the power state first?
> > 
> >   ▎ If a spurious interrupt fires while the IP block is held in reset, the rkjpegd_vdpu720_irq() handler 
> >   ▎ will run. Because PM runtime is still active, pm_runtime_get_if_active() will succeed, and the handler
> >   ▎ will try to read from the hardware (VDPU720_REG_INT), which can cause a bus stall or kernel panic.
> > 
> > > To start with, if you clock off the
> > > device, you won't get the IRQ. If you clock off the device between the start of
> > > this function and here in a race, you have some bigger problems in your driver.
> > > 
> > > This type of IP is not free-running, its trigger based. When its triggered, it
> > > should be busy and a PM ref should be held. Be careful with sashiko remarks, it
> > > does not differentiate free-running IP from triggered IP.
> > > 
> > > I'm also a little worried that maybe you don't actually understand this, since
> > > its quite possible you simply fed sashiko into your llm to produce this v5.
> > 
> > Ok, shows I have to think a bit more before taking Sashiko things for
> > granted.
> 
> Yes, though I was off a little, what I mean is that for triggered devices like
> this, which don't have known spurious IRQ hw bugs, you want the PM runtime to be
> held for the duration of job, which imply it will be running on IRQ. The method
> you have now would be useful if there was a shared IRQ or some known hardware
> bugs to deal with.

No shared IRQ and no known hardware bugs currently, so I can drop the
runtime PM from the IRQ handler.

> > > > +			rkjpegd_last_buffer_done(ctx, vbuf);
> > > > +			v4l2_m2m_mark_stopped(ctx->fh.m2m_ctx);
> > > > +			v4l2_event_queue_fh(&ctx->fh, &rkjpegd_eos_event);
> > > > +			return;
> > > > +		}
> > > > +
> > > > +		v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
> > > > +		return;
> > > > +	}
> > > > +
> > > > +	rkjpegd_parse_src_buf(ctx, vb);
> > > 
> > > This function can fail, its not clear once you ignore the return value how the
> > > error will propagade to device_run() and produce a matching error dst buffer.
> > 
> > The error travels through src_buf->parsed and rkjpegd_vdpu720_run()
> > bails out with an error when the frame couldn't be parsed. I can make
> > that clearer by returning an error from rkjpegd_parse_src_buf() and
> > setting the variable here instead.
> 
> Not sure I completely followed, but the main point is either drop
> rkjpegd_parse_src_buf() return value and related code if not needed, or use the
> return value to propagate the error.

rkjpegd_parse_src_buf() returns void in this version. Anyway, will
change to return an error and use that value.

> > > > +static void rkjpegd_stop_streaming(struct vb2_queue *vq)
> > > > +{
> > > > +	struct rkjpegd_ctx *ctx = vb2_get_drv_priv(vq);
> > > > +	struct vb2_v4l2_buffer *vbuf;
> > > > +
> > > > +	for (;;) {
> > > > +		if (V4L2_TYPE_IS_OUTPUT(vq->type))
> > > > +			vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
> > > > +		else
> > > > +			vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
> > > > +		if (!vbuf)
> > > > +			break;
> > > > +		if (V4L2_TYPE_IS_CAPTURE(vq->type))
> > > > +			vb2_set_plane_payload(&vbuf->vb2_buf, 0, 0);
> > > > +		v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
> > > > +	}
> > > > +
> > > > +	v4l2_m2m_update_stop_streaming_state(ctx->fh.m2m_ctx, vq);
> > > > +
> > > > +	/* A seek also ends a drain a resolution change had cut short. */
> > > > +	if (V4L2_TYPE_IS_OUTPUT(vq->type))
> > > > +		ctx->fh.m2m_ctx->last_src_buf = NULL;
> > > > +	else
> > > > +		rkjpegd_resume_drain(ctx);
> > > > +
> > > > +	if (V4L2_TYPE_IS_OUTPUT(vq->type) &&
> > > > +	    v4l2_m2m_has_stopped(ctx->fh.m2m_ctx))
> > > > +		v4l2_event_queue_fh(&ctx->fh, &rkjpegd_eos_event);
> > > 
> > > That is strange, STREAMOFF will flush the queues, so adding an event to the
> > > event queue seems odd, I never seen that before.
> > 
> > The same pattern is in the vicodec since [1], was added to mxc-jpeg in
> > [2] and went into this driver from there.
> > 
> > Sending EOS here is deprecated anyway:
> > 
> >       For backwards compatibility, the decoder will signal a ``V4L2_EVENT_EOS``
> >       event when the last frame has been decoded and all frames are ready to be
> >       dequeued. It is a deprecated behavior and the client must not rely on it.
> >       The ``V4L2_BUF_FLAG_LAST`` buffer flag should be used instead.
> > 
> > Maybe we can just drop it for a new driver.
> 
> But that's different, its about sending EOS event when the LAST buffer is queued
> into the capture pool. Driver that strictly want to implement this backward
> compatibility must resort to queuing an empty LAST buffer. Doing this in
> streamoff seems odd, I would need to further dig into. I must admit, I never
> seen that event being used, so I don't really know how it was historically used.
> 
> You can always leave it, I'm just not convince of the usefulness, as we should
> be flushing the queues when we stop streaming.

I'll drop it.

> > > > +module_platform_driver(rkjpegd_driver);
> > > > +
> > > > +MODULE_DESCRIPTION("Rockchip JPEG decoder driver");
> > > > +MODULE_AUTHOR("Lucas Sinn <lucas.sinn@wolfvision.net>");
> > > > +MODULE_LICENSE("GPL");
> > > > +MODULE_IMPORT_NS("DMA_BUF");
> > > 
> > > 
> > > Looking forward your feedback, I'm quite surprised of "your" choices, and how
> > > you possibly have needed this complexity.
> > 
> > Thanks for the thorough and honest review.
> > 
> > When I first worked with v4l2_m2m many years ago I was excited how easy
> > it has become to write such an m2m driver. I am also shocked to see how
> > many possible races sashiko found and through which hoops I had to go to
> > make sashiko happy (I still haven't accomplished that it seems).
> > 
> > Much of the complexity comes from two things: The watchdog and the
> > dynamic resolution handling.
> > 
> > Sashiko claims the watchdog has to protect itself against a race with
> > the irq handler. That of course misses that the watchdog only triggers
> > because the IRQ doesn't come which makes it quite unlikely that the IRQ
> > comes in the precise moment the watchdog triggers.
> 
> That should be solved using the watchdog API alone. On a theoretical basis
> (cause I don't program this every day):
>
> irq_func()
> {
>   if (!cancel_delayed_work(&dev->watchdog_work))
> 		return IRQ_HANDLED;
>   ...
> }
> 
> watchdog_func()
> {
>   reset_so_that_no_possible_irq_occures_passed_this_line();
>   return;
> }
> 
> So in the IRQ handler, you use the delayed work API to guaranty that your
> whatchdog won't be called. If its being called, or has been called, then it does
> an early return.
> 
> For the watchdog, the delayed worker code ensures that once called,
> cancel_delayed_work() will return false. You have to make sure you synchronously
> guaranty that no more IRQ will occur, hense why we operate a HW reset. Its also
> usually the only way to recover a wedged IP.

Done that.

Sascha

--
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |


  reply	other threads:[~2026-10-05  9:40 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-25 11:45 [PATCH v5 0/4] " Sascha Hauer
2026-09-25 11:45 ` [PATCH v5 1/4] media: dt-bindings: Add Rockchip JPEG decoder Sascha Hauer
2026-09-25 13:23   ` Nicolas Dufresne
2026-09-25 11:45 ` [PATCH v5 2/4] media: rockchip: Add JPEG decoder driver Sascha Hauer
2026-09-25 14:31   ` Nicolas Dufresne
2026-09-28 14:23     ` Sascha Hauer
2026-09-29 19:34       ` Nicolas Dufresne
2026-10-05  9:40         ` Sascha Hauer [this message]
2026-09-25 11:45 ` [PATCH v5 3/4] arm64: dts: rockchip: rk3588: Add JPEG decoder node Sascha Hauer
2026-09-25 11:45 ` [PATCH v5 4/4] arm64: dts: rockchip: rk356x: " Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ea747d54-acec-45cd-bcc4-77385812eead@pengutronix.de \
    --to=s.hauer@pengutronix.de \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=heiko@sntech.de \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=lucas.sinn@wolfvision.net \
    --cc=mchehab@kernel.org \
    --cc=nicolas@ndufresne.ca \
    --cc=p.zabel@pengutronix.de \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®