All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.