[PATCH v2 07/15] timers: Adjust flseep() to reflect reality

Anna-Maria Behnsen posted 15 patches 2 months, 2 weeks ago
There is a newer version of this series
[PATCH v2 07/15] timers: Adjust flseep() to reflect reality
Posted by Anna-Maria Behnsen 2 months, 2 weeks ago
fsleep() simply implements the recommondations of the outdated
documentation in "Documentation/timers/timers-howto.rst". This should be a
user friendly interface to choose always the best timeout function
approach:

- udelay() for very short sleep durations shorter than 10 microseconds
- usleep_range() for sleep durations until 20 milliseconds
- msleep() for the others

The actual implementation has several problems:

- It does not take into account that HZ resolution also has an impact on
  granularity of jiffies and has also an impact on the granularity of the
  buckets of timer wheel levels. This means that accuracy for the timeout
  does not have an upper limit. When executing fsleep(20000) on a HZ=100
  system, the possible additional slack will be 50% as the granularity of
  the buckets in the lowest level is 10 milliseconds.

- The upper limit of usleep_range() is twice the requested timeout. When no
  other interrupts occur in this range, the maximum value is used. This
  means that the requested sleep length has then an additional delay of
  100%.

Change the thresholds for the decisions in fsleep() to make sure the
maximum slack which is added to the sleep duration is 25%.

Note: Outdated documentation will be updated in a followup patch.

Cc: Heiner Kallweit <hkallweit1@gmail.com>
Cc: David S. Miller <davem@davemloft.net>
Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
---
 include/linux/delay.h | 29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)

diff --git a/include/linux/delay.h b/include/linux/delay.h
index 23623fa79768..b49a63c85c43 100644
--- a/include/linux/delay.h
+++ b/include/linux/delay.h
@@ -11,6 +11,7 @@
 
 #include <linux/math.h>
 #include <linux/sched.h>
+#include <linux/jiffies.h>
 
 extern unsigned long loops_per_jiffy;
 
@@ -102,15 +103,35 @@ static inline void ssleep(unsigned int seconds)
 	msleep(seconds * 1000);
 }
 
-/* see Documentation/timers/timers-howto.rst for the thresholds */
+static const unsigned int max_slack_shift = 2;
+#define USLEEP_RANGE_UPPER_BOUND	((TICK_NSEC << max_slack_shift) / NSEC_PER_USEC)
+
+/**
+ * fsleep - flexible sleep which autoselects the best mechanism
+ * @usecs:	requested sleep duration in microseconds
+ *
+ * flseep() selects the best mechanism that will provide maximum 25% slack
+ * to the requested sleep duration. Therefore it uses:
+ *
+ * * udelay() loop for sleep durations <= 10 microseconds to avoid hrtimer
+ *   overhead for really short sleep durations.
+ * * usleep_range() for sleep durations which would lead with the usage of
+ *   msleep() to a slack larger than 25%. This depends on the granularity of
+ *   jiffies.
+ * * msleep() for all other sleep durations.
+ *
+ * Note: When %CONFIG_HIGH_RES_TIMERS is not set, all sleeps are processed with
+ * the granularity of jiffies and the slack might exceed 25% especially for
+ * short sleep durations.
+ */
 static inline void fsleep(unsigned long usecs)
 {
 	if (usecs <= 10)
 		udelay(usecs);
-	else if (usecs <= 20000)
-		usleep_range(usecs, 2 * usecs);
+	else if (usecs < USLEEP_RANGE_UPPER_BOUND)
+		usleep_range(usecs, usecs + (usecs >> max_slack_shift));
 	else
-		msleep(DIV_ROUND_UP(usecs, 1000));
+		msleep(DIV_ROUND_UP(usecs, USEC_PER_MSEC));
 }
 
 #endif /* defined(_LINUX_DELAY_H) */

-- 
2.39.2
Re: [PATCH v2 07/15] timers: Adjust flseep() to reflect reality
Posted by Frederic Weisbecker 1 month, 3 weeks ago
Le Wed, Sep 11, 2024 at 07:13:33AM +0200, Anna-Maria Behnsen a écrit :
> fsleep() simply implements the recommondations of the outdated

recommendations*

> documentation in "Documentation/timers/timers-howto.rst". This should be a
> user friendly interface to choose always the best timeout function
> approach:
> 
> - udelay() for very short sleep durations shorter than 10 microseconds
> - usleep_range() for sleep durations until 20 milliseconds
> - msleep() for the others
> 
> The actual implementation has several problems:
> 
> - It does not take into account that HZ resolution also has an impact on
>   granularity of jiffies and has also an impact on the granularity of the
>   buckets of timer wheel levels. This means that accuracy for the timeout
>   does not have an upper limit. When executing fsleep(20000) on a HZ=100
>   system, the possible additional slack will be 50% as the granularity of
>   the buckets in the lowest level is 10 milliseconds.
> 
> - The upper limit of usleep_range() is twice the requested timeout. When no
>   other interrupts occur in this range, the maximum value is used. This
>   means that the requested sleep length has then an additional delay of
>   100%.
> 
> Change the thresholds for the decisions in fsleep() to make sure the
> maximum slack which is added to the sleep duration is 25%.
> 
> Note: Outdated documentation will be updated in a followup patch.
> 
> Cc: Heiner Kallweit <hkallweit1@gmail.com>
> Cc: David S. Miller <davem@davemloft.net>
> Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de>
> ---
>  include/linux/delay.h | 29 +++++++++++++++++++++++++----
>  1 file changed, 25 insertions(+), 4 deletions(-)
> 
> diff --git a/include/linux/delay.h b/include/linux/delay.h
> index 23623fa79768..b49a63c85c43 100644
> --- a/include/linux/delay.h
> +++ b/include/linux/delay.h
> @@ -11,6 +11,7 @@
>  
>  #include <linux/math.h>
>  #include <linux/sched.h>
> +#include <linux/jiffies.h>
>  
>  extern unsigned long loops_per_jiffy;
>  
> @@ -102,15 +103,35 @@ static inline void ssleep(unsigned int seconds)
>  	msleep(seconds * 1000);
>  }
>  
> -/* see Documentation/timers/timers-howto.rst for the thresholds */
> +static const unsigned int max_slack_shift = 2;

Should it be a define? (such const variables are usually for arrays).

Anyway:

Reviewed-by: Frederic Weisbecker <frederic@kernel.org>