Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: lianfeng.ouyang <lianfeng.ouyang@starfivetech.com>
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v9 2/2] hwrng: starfive: rework clk/reset teardown order for JHB100
Date: Wed, 21 Oct 2026 08:57:05 +0000	[thread overview]
Message-ID: <sashiko-outbox-179994@kernel.org> (raw)
In-Reply-To: <20261021083712.3032-3-lianfeng.ouyang@starfivetech.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] hwrng: starfive: unqueued need_reseed stalls non-blocking starfive_trng_read()
- [Medium] hwrng: starfive: unbalanced enable_irq() in starfive_trng_init()
--

commit a998deb7a1356bd18fe1c15b59933eacef0ded76
Author: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>

hwrng: starfive: rework clk/reset teardown order for JHB100

This commit reworks clock and reset power-down ordering in the StarFive TRNG
driver to support the JHB100 SoC alongside JH7110. It also updates runtime PM
handling, moves SEU-triggered reseeding to a workqueue, and serializes
command execution with a mutex.

> diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
> index aee12caab5780..8678b35ecd4a5 100644
> --- a/drivers/char/hw_random/jh7110-trng.c
> +++ b/drivers/char/hw_random/jh7110-trng.c

[ ... ]

> @@ -120,276 +157,496 @@ MODULE_PARM_DESC(autoage, "Auto-reseeding after specified timer countdowns to 0:
>  				" 0 - disable timer\n"
>  				" other - reload value for internal timer");
>  
> -static inline int starfive_trng_wait_idle(struct starfive_trng *trng)
> +static bool starfive_trng_idle(struct starfive_trng *trng, bool wait)
>  {
> -	u32 stat;
> +	u32 stat, busy = STARFIVE_STAT_RAND_GENERATING | STARFIVE_STAT_RAND_SEEDING;
>  
> -	return readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat,
> -					  !(stat & (STARFIVE_STAT_RAND_GENERATING |
> -						    STARFIVE_STAT_RAND_SEEDING)),
> -					  10, 100000);
> +	if (!wait)
> +		return !(readl_relaxed(trng->base + STARFIVE_STAT) & busy);
> +
> +	return !readl_relaxed_poll_timeout(trng->base + STARFIVE_STAT, stat,
> +					   !(stat & busy),
> +					   10, 100000);
>  }
>  
> -static inline void starfive_trng_irq_mask_clear(struct starfive_trng *trng)
> +static void starfive_trng_irq_clear(struct starfive_trng *trng)
>  {
> -	/* clear register: ISTAT */
> -	u32 data = readl(trng->base + STARFIVE_ISTAT);
> +	u32 istat = readl(trng->base + STARFIVE_ISTAT);
> +
> +	/* Do not lose an LFSR lockup that has not been serviced yet. */
> +	if (istat & STARFIVE_ISTAT_LFSR_LOCKUP)
> +		WRITE_ONCE(trng->need_reseed, true);
>  
> -	writel(data, trng->base + STARFIVE_ISTAT);
> +	writel(istat, trng->base + STARFIVE_ISTAT);
>  }

[ ... ]

> +static int starfive_trng_init(struct hwrng *rng)
> +{
> +	struct starfive_trng *trng = to_trng(rng);
> +	int ret;
> +
> +	ret = pm_runtime_resume_and_get(trng->dev);
> +	if (ret < 0) {
> +		dev_err(trng->dev, "Failed to wake device for init: %d\n", ret);
> +		return ret;
> +	}
> +
> +	mutex_lock(&trng->lock);
> +
> +	starfive_trng_irq_clear(trng);
> +	starfive_trng_hw_setup(trng);
> +
> +	enable_irq(trng->irq);
> +	writel(STARFIVE_IE_ALL, trng->base + STARFIVE_IE);
> +
> +	ret = starfive_trng_reseed(trng);
> +	if (ret) {
> +		writel(0, trng->base + STARFIVE_IE);
> +		disable_irq(trng->irq);
> +		starfive_trng_irq_clear(trng);
> +	} else {
> +		trng->initialized = true;
> +	}

[Severity: Medium]
Can starfive_trng_init() in drivers/char/hw_random/jh7110-trng.c cause an
unbalanced enable_irq(trng->irq) call when the RNG is deselected and quickly
reselected via /sys/class/misc/hw_random/rng_current?

In drivers/char/hw_random/core.c, deselecting the current RNG in
drop_current_rng() drops rng->ref to 0 and schedules rng->cleanup_work
asynchronously via cleanup_rng() -> schedule_work(&rng->cleanup_work).

If the RNG is selected again via rng_current_store() -> set_current_rng() ->
hwrng_init() before cleanup_rng_work() acquires rng_mutex, hwrng_init() sees
rng->ref == 0, invokes starfive_trng_init(), and sets rng->ref back to 1.
When cleanup_rng_work() subsequently runs, it skips starfive_trng_cleanup():

drivers/char/hw_random/core.c:cleanup_rng_work() {
	...
	/* Skip if rng has been reinitialized. */
	if (kref_read(&rng->ref)) {
		mutex_unlock(&rng_mutex);
		return;
	}

	if (rng->cleanup)
		rng->cleanup(rng);
	...
}

In that case, starfive_trng_init() runs while trng->initialized is still true
and trng->irq is already enabled, so calling enable_irq(trng->irq)
unconditionally triggers an unbalanced IRQ enable warning in __enable_irq().

Furthermore, if starfive_trng_reseed() fails during that re-initialization,
the error path disables trng->irq without clearing trng->initialized, and
when cleanup_rng_work() later runs for the failed init,
starfive_trng_cleanup() will call disable_irq(trng->irq) a second time and
leave TRNG interrupts permanently disabled.

Should starfive_trng_init() check trng->initialized before enabling trng->irq
(and clear trng->initialized on the error path)?

[ ... ]

>  static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
>  {
>  	struct starfive_trng *trng = to_trng(rng);
> -	int ret;
> -
> -	pm_runtime_get_sync(trng->dev);
> +	unsigned long timeout;
> +	int ret, retry = 1;
>  
> -	if (trng->mode == PRNG_256BIT)
> -		max = min_t(size_t, max, (STARFIVE_RAND_LEN * 8));
> -	else
> -		max = min_t(size_t, max, (STARFIVE_RAND_LEN * 4));
> +	ret = pm_runtime_resume_and_get(trng->dev);
> +	if (ret < 0) {
> +		dev_warn_ratelimited(trng->dev, "Failed to wake device for read: %d\n", ret);
> +		return ret;
> +	}
>  
>  	if (wait) {
> -		ret = starfive_trng_wait_idle(trng);
> -		if (ret) {
> -			ret = -ETIMEDOUT;
> +		mutex_lock(&trng->lock);
> +		timeout = msecs_to_jiffies(STARFIVE_RANDGEN_TIMEOUT_MS);
> +	} else {
> +		/* allow up to 1 jiffy for wait == 0 */
> +		if (!mutex_trylock(&trng->lock)) {
> +			ret = 0;
>  			goto out_put;
>  		}
> +		timeout = STARFIVE_RANDGEN_NOWAIT_TIMEOUT;
>  	}
>  
> -	ret = starfive_trng_cmd(trng, STARFIVE_CTRL_GENE_RANDNUM, wait);
> -	if (ret)
> -		goto out_put;
> +	for (;;) {
> +		/*
> +		 * A pending LFSR lockup taints the next sample. Blocking
> +		 * callers reseed here; non-blocking callers return no data,
> +		 * the reseed work is already queued.
> +		 */
> +		if (READ_ONCE(trng->need_reseed)) {
> +			if (!wait) {
> +				ret = 0;
> +				goto out_unlock;
> +			}
> +			ret = starfive_trng_reseed(trng);
> +			if (ret)
> +				goto out_unlock;
> +		}

[Severity: Medium]
Could non-blocking calls to starfive_trng_read() (wait == false) in
drivers/char/hw_random/jh7110-trng.c stall indefinitely if trng->need_reseed
is set without trng->work being queued?

In starfive_trng_irq_clear(), if STARFIVE_ISTAT_LFSR_LOCKUP is set in
STARFIVE_ISTAT, trng->need_reseed is set to true and the hardware interrupt
status bit is cleared without queuing trng->work:

drivers/char/hw_random/jh7110-trng.c:starfive_trng_irq_clear() {
	u32 istat = readl(trng->base + STARFIVE_ISTAT);

	/* Do not lose an LFSR lockup that has not been serviced yet. */
	if (istat & STARFIVE_ISTAT_LFSR_LOCKUP)
		WRITE_ONCE(trng->need_reseed, true);

	writel(istat, trng->base + STARFIVE_ISTAT);
}

During runtime suspend and resume, starfive_trng_suspend() masks STARFIVE_IE
before calling starfive_trng_irq_clear(), and starfive_trng_resume() calls
starfive_trng_irq_clear() before re-enabling STARFIVE_IE_ALL:

drivers/char/hw_random/jh7110-trng.c:starfive_trng_suspend() {
	...
	writel(0, trng->base + STARFIVE_IE);
	starfive_trng_irq_clear(trng);
	...
}

drivers/char/hw_random/jh7110-trng.c:starfive_trng_resume() {
	...
	if (trng->initialized) {
		starfive_trng_irq_clear(trng);
		writel(STARFIVE_IE_ALL, trng->base + STARFIVE_IE);
	}
	...
}

If STARFIVE_ISTAT_LFSR_LOCKUP is observed and cleared in either path,
starfive_trng_irq() will not fire for that event and trng->work is never
queued. Similarly, if a previous reseed attempt in starfive_trng_reseed_work()
or starfive_trng_read() failed, trng->need_reseed remains true after
trng->work is no longer pending.

Because the !wait path in starfive_trng_read() assumes trng->work is already
queued and returns 0 immediately, wouldn't subsequent non-blocking reads keep
returning 0 until a blocking read occurs? Should the !wait path (or
starfive_trng_irq_clear() / starfive_trng_resume()) queue trng->work when
trng->need_reseed is true?

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261021083712.3032-1-lianfeng.ouyang@starfivetech.com?part=2

  reply	other threads:[~2026-10-21  8:57 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21  8:37 [PATCH v9 0/2] hwrng: starfive: add JHB100 support and fix clk/reset teardown lianfeng.ouyang
2026-10-21  8:37 ` [PATCH v9 1/2] dt-bindings: rng: starfive,jh7110-trng: add jhb100, drop jh8100 lianfeng.ouyang
2026-10-21  8:37 ` [PATCH v9 2/2] hwrng: starfive: rework clk/reset teardown order for JHB100 lianfeng.ouyang
2026-10-21  8:57   ` sashiko-bot [this message]
2026-10-21 16:14   ` Uwe Kleine-König
2026-10-22  2:30     ` 回复: " Lianfeng Ouyang

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=sashiko-outbox-179994@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=lianfeng.ouyang@starfivetech.com \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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