Re: [PATCH v10] ath9k: let sleep be interrupted when unregistering hwrng

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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(&current_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);




[Index of Archives]     [Linux Kernel]     [Kernel Development Newbies]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Hiking]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux