[PATCH RFC] hwrng: core - Set hwrng_fillfn() kernel thread freezable

From: Thomas Richard (TI)

Date: Mon Apr 27 2026 - 09:59:29 EST


The hwrng_fillfn() kernel thread accesses the RNG device directly. During
suspend or resume sequences, hwrng_fillfn() may attempt to access the RNG
device while it is suspended. To address this, the hwrng_fillfn() kernel
thread is marked as freezable. This ensures it is frozen at the beginning
of the suspend sequence and started at the end of the resume sequence.

Signed-off-by: Thomas Richard (TI) <thomas.richard@xxxxxxxxxxx>
---
The patch is tagged as RFC because there is a drawback, the kernel can take
a while to freeze the hwrng_fillfn() kernel thread. If hwrng_fillfn() is
sleeping [1][2] when the suspend sequence starts, the kernel can't freeze
it until the end of the timeout. If you are not lucky it can take around
10s to freeze it which is not acceptable.

[ 37.364974] Freezing remaining freezable tasks completed (elapsed 8.908 seconds)

[1] https://elixir.bootlin.com/linux/v7.0.1/source/drivers/char/hw_random/core.c#L549
[2] https://elixir.bootlin.com/linux/v7.0.1/source/drivers/char/hw_random/core.c#L690

If you have any idea how to fix this, or maybe it is not the right
solution.

Issue was found while doing suspend-resume on J72S2 EVM board with omap-rng
driver.

echo mem > /sys/power/state
[ 27.922259] PM: suspend entry (deep)
[ 27.927191] Filesystems sync: 0.000 seconds
[ 27.933858] Freezing user space processes
[ 27.939119] Freezing user space processes completed (elapsed 0.001 seconds)
[ 27.946090] OOM killer disabled.
[ 27.949315] Freezing remaining freezable tasks
[ 27.954887] Freezing remaining freezable tasks completed (elapsed 0.001 seconds)
[ 27.963337] GFP mask restricted
[ 27.967069] omap_rng 4e10000.rng: PM: calling platform_pm_suspend @ 195, parent: 4e00000.crypto
[ 27.967072] mmcblk mmc1:9fb0: PM: calling mmc_bus_suspend @ 122, parent: mmc1
[ 27.968636] mmcblk mmc1:9fb0: PM: mmc_bus_suspend returned 0 after 1546 usecs
[ 27.975778] omap_rng 4e10000.rng: PM: platform_pm_suspend returned 0 after 3 usecs
...
[ 33.510667] ti-sci 44083000.system-controller: PM: ti_sci_suspend_noirq returned 0 after 0 usecs
[ 33.510671] SError Interrupt on CPU0, code 0x00000000bf000000 -- SError
[ 33.510681] CPU: 0 UID: 0 PID: 132 Comm: hwrng Tainted: G M W 7.0.0-12695-g8923b7a6e11d #19 PREEMPT
[ 33.510690] Tainted: [M]=MACHINE_CHECK, [W]=WARN
[ 33.510693] Hardware name: Texas Instruments J721S2 EVM (DT)
[ 33.510697] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
[ 33.510701] pc : omap_rng_do_read+0x3c/0xe0
[ 33.510709] lr : omap_rng_do_read+0x58/0xe0
[ 33.510712] sp : ffff80008942be00
[ 33.510713] x29: ffff80008942be00 x28: 0000000000000000 x27: 0000000000000000
[ 33.510719] x26: 0000000000000010 x25: 0000000000000010 x24: ffff0008065644e8
[ 33.510724] x23: ffff8000878b3370 x22: ffff00080148b2c0 x21: 0000000000000000
[ 33.510728] x20: ffff000806564480 x19: 0000000000000064 x18: 0000000000000000
[ 33.510732] x17: 6573752031207265 x16: 7466612030206465 x15: 6e72757465722071
[ 33.510737] x14: ffff0008062c8080 x13: 000031702bc0da42 x12: 0000000000000001
[ 33.510741] x11: 00000000000000c0 x10: 0000000000000b30 x9 : ffff80008942bc80
[ 33.510745] x8 : ffff0008062c8b90 x7 : ffff000b7dfa34c0 x6 : 0000000805ca16c1
[ 33.510749] x5 : 0000000000000000 x4 : ffff800080e17bfc x3 : ffff800087389c68
[ 33.510753] x2 : 0000000000000000 x1 : 0000000000000010 x0 : 000000000000a7c6
[ 33.510759] Kernel panic - not syncing: Asynchronous SError Interrupt
[ 33.510762] CPU: 0 UID: 0 PID: 132 Comm: hwrng Tainted: G M W 7.0.0-12695-g8923b7a6e11d #19 PREEMPT
[ 33.510767] Tainted: [M]=MACHINE_CHECK, [W]=WARN
[ 33.510768] Hardware name: Texas Instruments J721S2 EVM (DT)
[ 33.510770] Call trace:
[ 33.510772] show_stack+0x18/0x24 (C)
[ 33.510780] dump_stack_lvl+0x34/0x8c
[ 33.510788] dump_stack+0x18/0x24
[ 33.510792] vpanic+0x47c/0x4dc
[ 33.510799] do_panic_on_target_cpu+0x0/0x1c
[ 33.510803] add_taint+0x0/0xbc
[ 33.510807] arm64_serror_panic+0x70/0x80
[ 33.510812] do_serror+0x3c/0x70
[ 33.510815] el1h_64_error_handler+0x34/0x50
[ 33.510823] el1h_64_error+0x6c/0x70
[ 33.510827] omap_rng_do_read+0x3c/0xe0 (P)
[ 33.510831] hwrng_fillfn+0x98/0x330
[ 33.510834] kthread+0x130/0x13c
[ 33.510845] ret_from_fork+0x10/0x20
[ 33.510850] SMP: stopping secondary CPUs
[ 33.519442] Kernel Offset: disabled
[ 33.519444] CPU features: 0x04000000,800a0008,00040001,0400421b
[ 33.519448] Memory Limit: none
[ 33.732904] ---[ end Kernel panic - not syncing: Asynchronous SError Interrupt ]---

Best Regards,
Thomas
---
drivers/char/hw_random/core.c | 9 ++++++---
drivers/char/random.c | 2 +-
2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
index aba92d777f72..00e9674b8667 100644
--- a/drivers/char/hw_random/core.c
+++ b/drivers/char/hw_random/core.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/hw_random.h>
#include <linux/kernel.h>
@@ -518,7 +519,9 @@ static int hwrng_fillfn(void *unused)
size_t entropy, entropy_credit = 0; /* in 1/1024 of a bit */
long rc;

- while (!kthread_should_stop()) {
+ set_freezable();
+
+ while (!kthread_freezable_should_stop(NULL)) {
unsigned short quality;
struct hwrng *rng;

@@ -528,9 +531,9 @@ static int hwrng_fillfn(void *unused)
* Keep the task_struct alive until kthread_stop()
* is called to avoid UAF in drop_current_rng().
*/
- while (!kthread_should_stop()) {
+ while (!kthread_freezable_should_stop(NULL)) {
set_current_state(TASK_INTERRUPTIBLE);
- if (!kthread_should_stop())
+ if (!kthread_freezable_should_stop(NULL))
schedule();
}
set_current_state(TASK_RUNNING);
diff --git a/drivers/char/random.c b/drivers/char/random.c
index b4da1fb976c1..cd04f1789379 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -951,7 +951,7 @@ void add_hwgenerator_randomness(const void *buf, size_t len, size_t entropy, boo
* Throttle writing to once every reseed interval, unless we're not yet
* initialized or no entropy is credited.
*/
- if (sleep_after && !kthread_should_stop() && (crng_ready() || !entropy))
+ if (sleep_after && !kthread_freezable_should_stop(NULL) && (crng_ready() || !entropy))
schedule_timeout_interruptible(crng_reseed_interval());
}
EXPORT_SYMBOL_GPL(add_hwgenerator_randomness);

---
base-commit: fd936e2e7cabf854ceb3d0f8bda8f9189985afae
change-id: 20260427-hw-random-set-hwrng-fillfn-kthread-freezable-838d7fcfac29

Best regards,
--
Thomas Richard (TI) <thomas.richard@xxxxxxxxxxx>