mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
Updates for the time/timer core code:
- Rework the initialization of the posix-timer kmem_cache and move the
cache pointer into the timer_data structure to prevent false sharing.
- Switch the alarmtimer code to lock guards.
- Improve the CPU selection criteria in the per CPU validation of the
clocksource watchdog to avoid arbitrary selections (or omissions) on
systems with a small number of CPUs.
- The usual cleanups and improvements
-----BEGIN PGP SIGNATURE-----
iQJHBAABCgAxFiEEQp8+kY+LLUocC4bMphj1TA10mKEFAmgzgwkTHHRnbHhAbGlu
dXRyb25peC5kZQAKCRCmGPVMDXSYofL9D/9aiPT3UNkEVJzzQMIjeghi2foKcyqW
ut+0XH0+8bsFrzNKPs/PL9chIcblamm63FjwKmVxKaTFakP9omGUEMKAXIcy7L10
UWoQ7kSLvN/+3RB4JwOavtkNtdxkcjhDso+pd1VP0t7BQ5EsFRg4zkGHx1+PO/8C
H1URzpfmYLZWBPvIHfvgwFy5PAwwppehDynbxrR8uatg8kLvXUUGQRu/yrOYrqx8
7a/4jFkh75QdsezYOrS6yMjCS0qEeg6l37AW1WLQplZqHxJ4Mmwx9aL890KTQkXO
MZhtcZ1Iqa/7KdDNw1yzaW9T9t5RzND5IwEbBrLVBoeQft+P/Y3Grax5pHh+Gt8u
Sj4+4OiyhxQbhOcKGKjTr5pnHc//+jlm1QyLd3Ri6GL+mZB0JTnfmsFDRkhcWORN
05NcPxfganbJdENStYXZYuEIMXKnp1si8JaEbyI0AfgN8hpWITbVLnMZ65ngdOZ9
ym2HY2+3V5uCPPxegV2HdYX8VfaIRekiJAJ+Ttt3shG3+QflUNVSL4C6tv+lNNqa
PPV543ojVmlYWximdhc9KhT12yevhUWiuoFK50lndgbL9vDs471Ua/KXqrf/um0h
j8n49t8Ioq1Ht7g+olN0P3L+w0qSPM2oMWFnHh6bKTQniBrnkRDOtxQC6FC1pwEI
JVnpRMAAuSmjVQ==
=0+bf
-----END PGP SIGNATURE-----
Merge tag 'timers-core-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer core updates from Thomas Gleixner:
"Updates for the time/timer core code:
- Rework the initialization of the posix-timer kmem_cache and move
the cache pointer into the timer_data structure to prevent false
sharing
- Switch the alarmtimer code to lock guards
- Improve the CPU selection criteria in the per CPU validation of the
clocksource watchdog to avoid arbitrary selections (or omissions)
on systems with a small number of CPUs
- The usual cleanups and improvements"
* tag 'timers-core-2025-05-25' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
tick/nohz: Remove unused tick_nohz_full_add_cpus_to()
clocksource: Fix the CPUs' choice in the watchdog per CPU verification
alarmtimer: Switch spin_{lock,unlock}_irqsave() to guards
alarmtimer: Remove dead return value in clock2alarm()
time/jiffies: Change register_refined_jiffies() to void __init
timers: Remove unused __round_jiffies(_up)
posix-timers: Initialize cache early and move pointer into __timer_data
This commit is contained in:
commit
b1456f6dc1
8 changed files with 46 additions and 121 deletions
|
|
@ -59,7 +59,7 @@
|
|||
/* LATCH is used in the interval timer and ftape setup. */
|
||||
#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
|
||||
|
||||
extern int register_refined_jiffies(long clock_tick_rate);
|
||||
extern void register_refined_jiffies(long clock_tick_rate);
|
||||
|
||||
/* TICK_USEC is the time between ticks in usec assuming SHIFTED_HZ */
|
||||
#define TICK_USEC ((USEC_PER_SEC + HZ/2) / HZ)
|
||||
|
|
|
|||
|
|
@ -195,12 +195,6 @@ static inline bool tick_nohz_full_enabled(void)
|
|||
__ret; \
|
||||
})
|
||||
|
||||
static inline void tick_nohz_full_add_cpus_to(struct cpumask *mask)
|
||||
{
|
||||
if (tick_nohz_full_enabled())
|
||||
cpumask_or(mask, mask, tick_nohz_full_mask);
|
||||
}
|
||||
|
||||
extern void tick_nohz_dep_set(enum tick_dep_bits bit);
|
||||
extern void tick_nohz_dep_clear(enum tick_dep_bits bit);
|
||||
extern void tick_nohz_dep_set_cpu(int cpu, enum tick_dep_bits bit);
|
||||
|
|
@ -281,7 +275,6 @@ extern void __init tick_nohz_full_setup(cpumask_var_t cpumask);
|
|||
#else
|
||||
static inline bool tick_nohz_full_enabled(void) { return false; }
|
||||
static inline bool tick_nohz_full_cpu(int cpu) { return false; }
|
||||
static inline void tick_nohz_full_add_cpus_to(struct cpumask *mask) { }
|
||||
|
||||
static inline void tick_nohz_dep_set_cpu(int cpu, enum tick_dep_bits bit) { }
|
||||
static inline void tick_nohz_dep_clear_cpu(int cpu, enum tick_dep_bits bit) { }
|
||||
|
|
|
|||
|
|
@ -172,12 +172,10 @@ extern void timers_init(void);
|
|||
struct hrtimer;
|
||||
extern enum hrtimer_restart it_real_fn(struct hrtimer *);
|
||||
|
||||
unsigned long __round_jiffies(unsigned long j, int cpu);
|
||||
unsigned long __round_jiffies_relative(unsigned long j, int cpu);
|
||||
unsigned long round_jiffies(unsigned long j);
|
||||
unsigned long round_jiffies_relative(unsigned long j);
|
||||
|
||||
unsigned long __round_jiffies_up(unsigned long j, int cpu);
|
||||
unsigned long __round_jiffies_up_relative(unsigned long j, int cpu);
|
||||
unsigned long round_jiffies_up(unsigned long j);
|
||||
unsigned long round_jiffies_up_relative(unsigned long j);
|
||||
|
|
|
|||
|
|
@ -70,12 +70,10 @@ static DEFINE_SPINLOCK(rtcdev_lock);
|
|||
*/
|
||||
struct rtc_device *alarmtimer_get_rtcdev(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct rtc_device *ret;
|
||||
|
||||
spin_lock_irqsave(&rtcdev_lock, flags);
|
||||
guard(spinlock_irqsave)(&rtcdev_lock);
|
||||
ret = rtcdev;
|
||||
spin_unlock_irqrestore(&rtcdev_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -83,7 +81,6 @@ EXPORT_SYMBOL_GPL(alarmtimer_get_rtcdev);
|
|||
|
||||
static int alarmtimer_rtc_add_device(struct device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct rtc_device *rtc = to_rtc_device(dev);
|
||||
struct platform_device *pdev;
|
||||
int ret = 0;
|
||||
|
|
@ -101,25 +98,18 @@ static int alarmtimer_rtc_add_device(struct device *dev)
|
|||
if (!IS_ERR(pdev))
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
spin_lock_irqsave(&rtcdev_lock, flags);
|
||||
if (!IS_ERR(pdev) && !rtcdev) {
|
||||
if (!try_module_get(rtc->owner)) {
|
||||
scoped_guard(spinlock_irqsave, &rtcdev_lock) {
|
||||
if (!IS_ERR(pdev) && !rtcdev && try_module_get(rtc->owner)) {
|
||||
rtcdev = rtc;
|
||||
/* hold a reference so it doesn't go away */
|
||||
get_device(dev);
|
||||
pdev = NULL;
|
||||
} else {
|
||||
ret = -1;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
rtcdev = rtc;
|
||||
/* hold a reference so it doesn't go away */
|
||||
get_device(dev);
|
||||
pdev = NULL;
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&rtcdev_lock, flags);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +188,7 @@ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
|
|||
struct alarm *alarm = container_of(timer, struct alarm, timer);
|
||||
struct alarm_base *base = &alarm_bases[alarm->type];
|
||||
|
||||
scoped_guard (spinlock_irqsave, &base->lock)
|
||||
scoped_guard(spinlock_irqsave, &base->lock)
|
||||
alarmtimer_dequeue(base, alarm);
|
||||
|
||||
if (alarm->function)
|
||||
|
|
@ -228,17 +218,16 @@ EXPORT_SYMBOL_GPL(alarm_expires_remaining);
|
|||
static int alarmtimer_suspend(struct device *dev)
|
||||
{
|
||||
ktime_t min, now, expires;
|
||||
int i, ret, type;
|
||||
struct rtc_device *rtc;
|
||||
unsigned long flags;
|
||||
struct rtc_time tm;
|
||||
int i, ret, type;
|
||||
|
||||
spin_lock_irqsave(&freezer_delta_lock, flags);
|
||||
min = freezer_delta;
|
||||
expires = freezer_expires;
|
||||
type = freezer_alarmtype;
|
||||
freezer_delta = 0;
|
||||
spin_unlock_irqrestore(&freezer_delta_lock, flags);
|
||||
scoped_guard(spinlock_irqsave, &freezer_delta_lock) {
|
||||
min = freezer_delta;
|
||||
expires = freezer_expires;
|
||||
type = freezer_alarmtype;
|
||||
freezer_delta = 0;
|
||||
}
|
||||
|
||||
rtc = alarmtimer_get_rtcdev();
|
||||
/* If we have no rtcdev, just return */
|
||||
|
|
@ -251,9 +240,8 @@ static int alarmtimer_suspend(struct device *dev)
|
|||
struct timerqueue_node *next;
|
||||
ktime_t delta;
|
||||
|
||||
spin_lock_irqsave(&base->lock, flags);
|
||||
next = timerqueue_getnext(&base->timerqueue);
|
||||
spin_unlock_irqrestore(&base->lock, flags);
|
||||
scoped_guard(spinlock_irqsave, &base->lock)
|
||||
next = timerqueue_getnext(&base->timerqueue);
|
||||
if (!next)
|
||||
continue;
|
||||
delta = ktime_sub(next->expires, base->get_ktime());
|
||||
|
|
@ -352,13 +340,12 @@ EXPORT_SYMBOL_GPL(alarm_init);
|
|||
void alarm_start(struct alarm *alarm, ktime_t start)
|
||||
{
|
||||
struct alarm_base *base = &alarm_bases[alarm->type];
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&base->lock, flags);
|
||||
alarm->node.expires = start;
|
||||
alarmtimer_enqueue(base, alarm);
|
||||
hrtimer_start(&alarm->timer, alarm->node.expires, HRTIMER_MODE_ABS);
|
||||
spin_unlock_irqrestore(&base->lock, flags);
|
||||
scoped_guard(spinlock_irqsave, &base->lock) {
|
||||
alarm->node.expires = start;
|
||||
alarmtimer_enqueue(base, alarm);
|
||||
hrtimer_start(&alarm->timer, alarm->node.expires, HRTIMER_MODE_ABS);
|
||||
}
|
||||
|
||||
trace_alarmtimer_start(alarm, base->get_ktime());
|
||||
}
|
||||
|
|
@ -381,13 +368,11 @@ EXPORT_SYMBOL_GPL(alarm_start_relative);
|
|||
void alarm_restart(struct alarm *alarm)
|
||||
{
|
||||
struct alarm_base *base = &alarm_bases[alarm->type];
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&base->lock, flags);
|
||||
guard(spinlock_irqsave)(&base->lock);
|
||||
hrtimer_set_expires(&alarm->timer, alarm->node.expires);
|
||||
hrtimer_restart(&alarm->timer);
|
||||
alarmtimer_enqueue(base, alarm);
|
||||
spin_unlock_irqrestore(&base->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alarm_restart);
|
||||
|
||||
|
|
@ -401,14 +386,13 @@ EXPORT_SYMBOL_GPL(alarm_restart);
|
|||
int alarm_try_to_cancel(struct alarm *alarm)
|
||||
{
|
||||
struct alarm_base *base = &alarm_bases[alarm->type];
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&base->lock, flags);
|
||||
ret = hrtimer_try_to_cancel(&alarm->timer);
|
||||
if (ret >= 0)
|
||||
alarmtimer_dequeue(base, alarm);
|
||||
spin_unlock_irqrestore(&base->lock, flags);
|
||||
scoped_guard(spinlock_irqsave, &base->lock) {
|
||||
ret = hrtimer_try_to_cancel(&alarm->timer);
|
||||
if (ret >= 0)
|
||||
alarmtimer_dequeue(base, alarm);
|
||||
}
|
||||
|
||||
trace_alarmtimer_cancel(alarm, base->get_ktime());
|
||||
return ret;
|
||||
|
|
@ -479,7 +463,6 @@ EXPORT_SYMBOL_GPL(alarm_forward_now);
|
|||
static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type)
|
||||
{
|
||||
struct alarm_base *base;
|
||||
unsigned long flags;
|
||||
ktime_t delta;
|
||||
|
||||
switch(type) {
|
||||
|
|
@ -498,13 +481,12 @@ static void alarmtimer_freezerset(ktime_t absexp, enum alarmtimer_type type)
|
|||
|
||||
delta = ktime_sub(absexp, base->get_ktime());
|
||||
|
||||
spin_lock_irqsave(&freezer_delta_lock, flags);
|
||||
guard(spinlock_irqsave)(&freezer_delta_lock);
|
||||
if (!freezer_delta || (delta < freezer_delta)) {
|
||||
freezer_delta = delta;
|
||||
freezer_expires = absexp;
|
||||
freezer_alarmtype = type;
|
||||
}
|
||||
spin_unlock_irqrestore(&freezer_delta_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -515,9 +497,9 @@ static enum alarmtimer_type clock2alarm(clockid_t clockid)
|
|||
{
|
||||
if (clockid == CLOCK_REALTIME_ALARM)
|
||||
return ALARM_REALTIME;
|
||||
if (clockid == CLOCK_BOOTTIME_ALARM)
|
||||
return ALARM_BOOTTIME;
|
||||
return -1;
|
||||
|
||||
WARN_ON_ONCE(clockid != CLOCK_BOOTTIME_ALARM);
|
||||
return ALARM_BOOTTIME;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ static void clocksource_verify_choose_cpus(void)
|
|||
{
|
||||
int cpu, i, n = verify_n_cpus;
|
||||
|
||||
if (n < 0) {
|
||||
if (n < 0 || n >= num_online_cpus()) {
|
||||
/* Check all of the CPUs. */
|
||||
cpumask_copy(&cpus_chosen, cpu_online_mask);
|
||||
cpumask_clear_cpu(smp_processor_id(), &cpus_chosen);
|
||||
|
|
|
|||
|
|
@ -75,13 +75,11 @@ struct clocksource * __init __weak clocksource_default_clock(void)
|
|||
|
||||
static struct clocksource refined_jiffies;
|
||||
|
||||
int register_refined_jiffies(long cycles_per_second)
|
||||
void __init register_refined_jiffies(long cycles_per_second)
|
||||
{
|
||||
u64 nsec_per_tick, shift_hz;
|
||||
long cycles_per_tick;
|
||||
|
||||
|
||||
|
||||
refined_jiffies = clocksource_jiffies;
|
||||
refined_jiffies.name = "refined-jiffies";
|
||||
refined_jiffies.rating++;
|
||||
|
|
@ -100,5 +98,4 @@ int register_refined_jiffies(long cycles_per_second)
|
|||
refined_jiffies.mult = ((u32)nsec_per_tick) << JIFFIES_SHIFT;
|
||||
|
||||
__clocksource_register(&refined_jiffies);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,6 @@
|
|||
#include "timekeeping.h"
|
||||
#include "posix-timers.h"
|
||||
|
||||
static struct kmem_cache *posix_timers_cache;
|
||||
|
||||
/*
|
||||
* Timers are managed in a hash table for lockless lookup. The hash key is
|
||||
* constructed from current::signal and the timer ID and the timer is
|
||||
|
|
@ -49,10 +47,12 @@ struct timer_hash_bucket {
|
|||
static struct {
|
||||
struct timer_hash_bucket *buckets;
|
||||
unsigned long mask;
|
||||
} __timer_data __ro_after_init __aligned(2*sizeof(long));
|
||||
struct kmem_cache *cache;
|
||||
} __timer_data __ro_after_init __aligned(4*sizeof(long));
|
||||
|
||||
#define timer_buckets (__timer_data.buckets)
|
||||
#define timer_hashmask (__timer_data.mask)
|
||||
#define timer_buckets (__timer_data.buckets)
|
||||
#define timer_hashmask (__timer_data.mask)
|
||||
#define posix_timers_cache (__timer_data.cache)
|
||||
|
||||
static const struct k_clock * const posix_clocks[];
|
||||
static const struct k_clock *clockid_to_kclock(const clockid_t id);
|
||||
|
|
@ -283,14 +283,6 @@ static int posix_get_hrtimer_res(clockid_t which_clock, struct timespec64 *tp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static __init int init_posix_timers(void)
|
||||
{
|
||||
posix_timers_cache = kmem_cache_create("posix_timers_cache", sizeof(struct k_itimer),
|
||||
__alignof__(struct k_itimer), SLAB_ACCOUNT, NULL);
|
||||
return 0;
|
||||
}
|
||||
__initcall(init_posix_timers);
|
||||
|
||||
/*
|
||||
* The siginfo si_overrun field and the return value of timer_getoverrun(2)
|
||||
* are of type int. Clamp the overrun value to INT_MAX
|
||||
|
|
@ -1556,6 +1548,11 @@ static int __init posixtimer_init(void)
|
|||
unsigned long i, size;
|
||||
unsigned int shift;
|
||||
|
||||
posix_timers_cache = kmem_cache_create("posix_timers_cache",
|
||||
sizeof(struct k_itimer),
|
||||
__alignof__(struct k_itimer),
|
||||
SLAB_ACCOUNT, NULL);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BASE_SMALL))
|
||||
size = 512;
|
||||
else
|
||||
|
|
|
|||
|
|
@ -385,32 +385,6 @@ static unsigned long round_jiffies_common(unsigned long j, int cpu,
|
|||
return time_is_after_jiffies(j) ? j : original;
|
||||
}
|
||||
|
||||
/**
|
||||
* __round_jiffies - function to round jiffies to a full second
|
||||
* @j: the time in (absolute) jiffies that should be rounded
|
||||
* @cpu: the processor number on which the timeout will happen
|
||||
*
|
||||
* __round_jiffies() rounds an absolute time in the future (in jiffies)
|
||||
* up or down to (approximately) full seconds. This is useful for timers
|
||||
* for which the exact time they fire does not matter too much, as long as
|
||||
* they fire approximately every X seconds.
|
||||
*
|
||||
* By rounding these timers to whole seconds, all such timers will fire
|
||||
* at the same time, rather than at various times spread out. The goal
|
||||
* of this is to have the CPU wake up less, which saves power.
|
||||
*
|
||||
* The exact rounding is skewed for each processor to avoid all
|
||||
* processors firing at the exact same time, which could lead
|
||||
* to lock contention or spurious cache line bouncing.
|
||||
*
|
||||
* The return value is the rounded version of the @j parameter.
|
||||
*/
|
||||
unsigned long __round_jiffies(unsigned long j, int cpu)
|
||||
{
|
||||
return round_jiffies_common(j, cpu, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__round_jiffies);
|
||||
|
||||
/**
|
||||
* __round_jiffies_relative - function to round jiffies to a full second
|
||||
* @j: the time in (relative) jiffies that should be rounded
|
||||
|
|
@ -482,22 +456,6 @@ unsigned long round_jiffies_relative(unsigned long j)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(round_jiffies_relative);
|
||||
|
||||
/**
|
||||
* __round_jiffies_up - function to round jiffies up to a full second
|
||||
* @j: the time in (absolute) jiffies that should be rounded
|
||||
* @cpu: the processor number on which the timeout will happen
|
||||
*
|
||||
* This is the same as __round_jiffies() except that it will never
|
||||
* round down. This is useful for timeouts for which the exact time
|
||||
* of firing does not matter too much, as long as they don't fire too
|
||||
* early.
|
||||
*/
|
||||
unsigned long __round_jiffies_up(unsigned long j, int cpu)
|
||||
{
|
||||
return round_jiffies_common(j, cpu, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__round_jiffies_up);
|
||||
|
||||
/**
|
||||
* __round_jiffies_up_relative - function to round jiffies up to a full second
|
||||
* @j: the time in (relative) jiffies that should be rounded
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue