On 19/07/22 22:11, Jason A. Donenfeld wrote: > diff --git a/include/linux/sched.h b/include/linux/sched.h > index c46f3a63b758..f164098fb614 100644 > --- a/include/linux/sched.h > +++ b/include/linux/sched.h > @@ -1936,6 +1936,7 @@ extern struct task_struct *find_get_task_by_vpid(pid_t nr); > > extern int wake_up_state(struct task_struct *tsk, unsigned int state); > extern int wake_up_process(struct task_struct *tsk); > +extern int wake_up_task_interruptible(struct task_struct *tsk); > extern void wake_up_new_task(struct task_struct *tsk); > > #ifdef CONFIG_SMP > diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h > index cafbe03eed01..56a15f35e7b3 100644 > --- a/include/linux/sched/signal.h > +++ b/include/linux/sched/signal.h > @@ -364,7 +364,7 @@ static inline void clear_notify_signal(void) > static inline bool __set_notify_signal(struct task_struct *task) > { > return !test_and_set_tsk_thread_flag(task, TIF_NOTIFY_SIGNAL) && > - !wake_up_state(task, TASK_INTERRUPTIBLE); > + !wake_up_task_interruptible(task); > } > > /* > diff --git a/kernel/sched/core.c b/kernel/sched/core.c > index da0bf6fe9ecd..b178940185d7 100644 > --- a/kernel/sched/core.c > +++ b/kernel/sched/core.c > @@ -4280,6 +4280,12 @@ int wake_up_process(struct task_struct *p) > } > EXPORT_SYMBOL(wake_up_process); > > +int wake_up_task_interruptible(struct task_struct *p) > +{ > + return try_to_wake_up(p, TASK_INTERRUPTIBLE, 0); > +} > +EXPORT_SYMBOL_GPL(wake_up_task_interruptible); > + > int wake_up_state(struct task_struct *p, unsigned int state) > { > return try_to_wake_up(p, state, 0); > -- > 2.35.1 The sched changes are unfortunate, as I had understood it the alternative would be fixing all sleeping hwrng's to make them have a proper wait pattern that doesn't require being sent a signal to avoid missing events, i.e. instead of hwrng->read(): devm_hwrng_unregister(): schedule_timeout_interruptible(x); set_notify_signal(waiting_reader); do hwrng->read(): devm_hwrng_unregister(): set_current_state(TASK_INTERRUPTIBLE) rng->dying = true; if (!rng->dying) wake_up_process(waiting_reader); schedule_timeout(x); I had initially convinced myself this would be somewhat involved, but writing the above I thought maybe not... The below is applied on top of your v10, would you be able to test whether it actually works? I apologize for telling you to do one thing and then suggesting something else... IMO set_notify_signal() is for interrupting tasks that are in a wait-loop that has nothing to do with the calling code (e.g. task_work, I assume livepatching does that for the same reason), but here it's hwrng core code interrupting a sleeping hwrng device. It does however mean patching up any sleeping hwrng (a quick search tells me there are more, e.g. npcm-rng does readb_poll_timeout()) --- diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index df45c265878e..40a73490bfdc 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -522,9 +522,12 @@ static int hwrng_fillfn(void *unused) break; if (rc <= 0) { - if (kthread_should_stop()) + set_current_state(TASK_INTERRUPTIBLE); + if (kthread_should_stop()) { + __set_current_state(TASK_RUNNING); break; - schedule_timeout_interruptible(HZ * 10); + } + schedule_timeout(HZ * 10); continue; } @@ -581,6 +584,8 @@ int hwrng_register(struct hwrng *rng) init_completion(&rng->cleanup_done); complete(&rng->cleanup_done); + rng->unregistering = false; + if (!current_rng || (!cur_rng_set_by_user && rng->quality > current_rng->quality)) { /* @@ -630,8 +635,10 @@ void hwrng_unregister(struct hwrng *rng) rcu_read_lock(); waiting_reader = xchg(¤t_waiting_reader, UNREGISTERING_READER); - if (waiting_reader && waiting_reader != UNREGISTERING_READER) - set_notify_signal(waiting_reader); + if (waiting_reader && waiting_reader != UNREGISTERING_READER) { + rng->unregistering = true; + wake_up_process(waiting_reader); + } rcu_read_unlock(); old_rng = current_rng; list_del(&rng->list); diff --git a/drivers/net/wireless/ath/ath9k/rng.c b/drivers/net/wireless/ath/ath9k/rng.c index 8980dc36509e..35cac38054b5 100644 --- a/drivers/net/wireless/ath/ath9k/rng.c +++ b/drivers/net/wireless/ath/ath9k/rng.c @@ -75,9 +75,17 @@ static int ath9k_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait) bytes_read += max & 3UL; memzero_explicit(&word, sizeof(word)); } - if (!wait || !max || likely(bytes_read) || fail_stats > 110 || - ((current->flags & PF_KTHREAD) && kthread_should_stop()) || - schedule_timeout_interruptible(ath9k_rng_delay_get(++fail_stats))) + if (!wait || !max || likely(bytes_read) || fail_stats > 110) + break; + + set_current_state(TASK_INTERRUPTIBLE); + if (rng->unregistering || + ((current->flags & PF_KTHREAD) && kthread_should_stop())) { + __set_current_state(TASK_RUNNING); + break; + } + + if (schedule_timeout(ath9k_rng_delay_get(++fail_stats))) break; } diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h index aa1d4da03538..778f10dfa12b 100644 --- a/include/linux/hw_random.h +++ b/include/linux/hw_random.h @@ -45,6 +45,7 @@ struct hwrng { int (*read)(struct hwrng *rng, void *data, size_t max, bool wait); unsigned long priv; unsigned short quality; + int unregistering; /* internal. */ struct list_head list; diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h index 56a15f35e7b3..9b94a8f18b04 100644 --- a/include/linux/sched/signal.h +++ b/include/linux/sched/signal.h @@ -364,7 +364,7 @@ static inline void clear_notify_signal(void) static inline bool __set_notify_signal(struct task_struct *task) { return !test_and_set_tsk_thread_flag(task, TIF_NOTIFY_SIGNAL) && - !wake_up_task_interruptible(task); + !wake_up_state(task, TASK_INTERRUPTIBLE); } /* diff --git a/kernel/sched/core.c b/kernel/sched/core.c index e3e675eef63d..a463dbc92fcd 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -4279,12 +4279,6 @@ int wake_up_process(struct task_struct *p) } EXPORT_SYMBOL(wake_up_process); -int wake_up_task_interruptible(struct task_struct *p) -{ - return try_to_wake_up(p, TASK_INTERRUPTIBLE, 0); -} -EXPORT_SYMBOL_GPL(wake_up_task_interruptible); - int wake_up_state(struct task_struct *p, unsigned int state) { return try_to_wake_up(p, state, 0);