mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-28 16:56:26 +00:00
82e11e47c1
fsleep() simply implements the recommendations 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. Signed-off-by: Anna-Maria Behnsen <anna-maria@linutronix.de> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Frederic Weisbecker <frederic@kernel.org> Link: https://lore.kernel.org/all/20241014-devel-anna-maria-b4-timers-flseep-v3-7-dc8b907cb62f@linutronix.de
138 lines
4.2 KiB
C
138 lines
4.2 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_DELAY_H
|
|
#define _LINUX_DELAY_H
|
|
|
|
/*
|
|
* Copyright (C) 1993 Linus Torvalds
|
|
*
|
|
* Delay routines, using a pre-computed "loops_per_jiffy" value.
|
|
* Sleep routines using timer list timers or hrtimers.
|
|
*/
|
|
|
|
#include <linux/math.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/jiffies.h>
|
|
|
|
extern unsigned long loops_per_jiffy;
|
|
|
|
#include <asm/delay.h>
|
|
|
|
/*
|
|
* Using udelay() for intervals greater than a few milliseconds can
|
|
* risk overflow for high loops_per_jiffy (high bogomips) machines. The
|
|
* mdelay() provides a wrapper to prevent this. For delays greater
|
|
* than MAX_UDELAY_MS milliseconds, the wrapper is used. Architecture
|
|
* specific values can be defined in asm-???/delay.h as an override.
|
|
* The 2nd mdelay() definition ensures GCC will optimize away the
|
|
* while loop for the common cases where n <= MAX_UDELAY_MS -- Paul G.
|
|
*/
|
|
#ifndef MAX_UDELAY_MS
|
|
#define MAX_UDELAY_MS 5
|
|
#endif
|
|
|
|
#ifndef mdelay
|
|
/**
|
|
* mdelay - Inserting a delay based on milliseconds with busy waiting
|
|
* @n: requested delay in milliseconds
|
|
*
|
|
* See udelay() for basic information about mdelay() and it's variants.
|
|
*
|
|
* Please double check, whether mdelay() is the right way to go or whether a
|
|
* refactoring of the code is the better variant to be able to use msleep()
|
|
* instead.
|
|
*/
|
|
#define mdelay(n) (\
|
|
(__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \
|
|
({unsigned long __ms=(n); while (__ms--) udelay(1000);}))
|
|
#endif
|
|
|
|
#ifndef ndelay
|
|
static inline void ndelay(unsigned long x)
|
|
{
|
|
udelay(DIV_ROUND_UP(x, 1000));
|
|
}
|
|
#define ndelay(x) ndelay(x)
|
|
#endif
|
|
|
|
extern unsigned long lpj_fine;
|
|
void calibrate_delay(void);
|
|
unsigned long calibrate_delay_is_known(void);
|
|
void __attribute__((weak)) calibration_delay_done(void);
|
|
void msleep(unsigned int msecs);
|
|
unsigned long msleep_interruptible(unsigned int msecs);
|
|
void usleep_range_state(unsigned long min, unsigned long max,
|
|
unsigned int state);
|
|
|
|
/**
|
|
* usleep_range - Sleep for an approximate time
|
|
* @min: Minimum time in microseconds to sleep
|
|
* @max: Maximum time in microseconds to sleep
|
|
*
|
|
* For basic information please refere to usleep_range_state().
|
|
*
|
|
* The task will be in the state TASK_UNINTERRUPTIBLE during the sleep.
|
|
*/
|
|
static inline void usleep_range(unsigned long min, unsigned long max)
|
|
{
|
|
usleep_range_state(min, max, TASK_UNINTERRUPTIBLE);
|
|
}
|
|
|
|
/**
|
|
* usleep_range_idle - Sleep for an approximate time with idle time accounting
|
|
* @min: Minimum time in microseconds to sleep
|
|
* @max: Maximum time in microseconds to sleep
|
|
*
|
|
* For basic information please refere to usleep_range_state().
|
|
*
|
|
* The sleeping task has the state TASK_IDLE during the sleep to prevent
|
|
* contribution to the load avarage.
|
|
*/
|
|
static inline void usleep_range_idle(unsigned long min, unsigned long max)
|
|
{
|
|
usleep_range_state(min, max, TASK_IDLE);
|
|
}
|
|
|
|
/**
|
|
* ssleep - wrapper for seconds around msleep
|
|
* @seconds: Requested sleep duration in seconds
|
|
*
|
|
* Please refere to msleep() for detailed information.
|
|
*/
|
|
static inline void ssleep(unsigned int seconds)
|
|
{
|
|
msleep(seconds * 1000);
|
|
}
|
|
|
|
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 < USLEEP_RANGE_UPPER_BOUND)
|
|
usleep_range(usecs, usecs + (usecs >> max_slack_shift));
|
|
else
|
|
msleep(DIV_ROUND_UP(usecs, USEC_PER_MSEC));
|
|
}
|
|
|
|
#endif /* defined(_LINUX_DELAY_H) */
|