mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 22:19:30 +02:00
Merge tag 'timers-urgent-2026-09-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull timer fixes from Ingo Molnar: - Fix clockevents replacement race when a broadcast device is replaced which may trigger a BUG() crash (朱恺乾 - Zhu Kaiqian) - Fix potential timerqueue ordering bug when rearming a queued timer with nonzero slack (Andrea Parri) * tag 'timers-urgent-2026-09-13' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: hrtimer: Use hard expiry when updating timers on the same base tick/broadcast: Plug clockevents replacement race
This commit is contained in:
+21
-14
@@ -615,6 +615,24 @@ void clockevents_handle_noop(struct clock_event_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
void __clockevents_exchange_device(struct clock_event_device *old,
|
||||
struct clock_event_device *new)
|
||||
{
|
||||
/*
|
||||
* Caller releases a clock event device. We queue it into the
|
||||
* released list and do a notify add later.
|
||||
*/
|
||||
if (old) {
|
||||
clockevents_switch_state(old, CLOCK_EVT_STATE_DETACHED);
|
||||
list_move(&old->list, &clockevents_released);
|
||||
}
|
||||
|
||||
if (new) {
|
||||
WARN_ON(!clockevent_state_detached(new));
|
||||
clockevents_shutdown(new);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clockevents_exchange_device - release and request clock devices
|
||||
* @old: device to release (can be NULL)
|
||||
@@ -626,20 +644,9 @@ void clockevents_handle_noop(struct clock_event_device *dev)
|
||||
void clockevents_exchange_device(struct clock_event_device *old,
|
||||
struct clock_event_device *new)
|
||||
{
|
||||
/*
|
||||
* Caller releases a clock event device. We queue it into the
|
||||
* released list and do a notify add later.
|
||||
*/
|
||||
if (old) {
|
||||
__clockevents_exchange_device(old, new);
|
||||
if (old)
|
||||
module_put(old->owner);
|
||||
clockevents_switch_state(old, CLOCK_EVT_STATE_DETACHED);
|
||||
list_move(&old->list, &clockevents_released);
|
||||
}
|
||||
|
||||
if (new) {
|
||||
BUG_ON(!clockevent_state_detached(new));
|
||||
clockevents_shutdown(new);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -699,7 +706,7 @@ void tick_offline_cpu(unsigned int cpu)
|
||||
if (cpumask_test_cpu(cpu, dev->cpumask) &&
|
||||
cpumask_weight(dev->cpumask) == 1 &&
|
||||
!tick_is_broadcast_device(dev)) {
|
||||
BUG_ON(!clockevent_state_detached(dev));
|
||||
WARN_ON(!clockevent_state_detached(dev));
|
||||
list_del(&dev->list);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-4
@@ -1263,13 +1263,23 @@ remove_and_enqueue_same_base(struct hrtimer *timer, struct hrtimer_clock_base *b
|
||||
{
|
||||
bool was_first = false;
|
||||
|
||||
/*
|
||||
* Updating the sort key while @timer is queued can temporarily
|
||||
* make the tree inconsistent. This is safe under cpu_base->lock:
|
||||
* no other queue operation can observe that state.
|
||||
* hrtimer_can_update_in_place() either confirms that the new expiry
|
||||
* fits between the neighbours or timerqueue_linked_del() removes the
|
||||
* timer without consulting the expiry.
|
||||
*/
|
||||
hrtimer_set_expires_range_ns(timer, expires, delta_ns);
|
||||
expires = hrtimer_get_expires(timer);
|
||||
|
||||
/* Remove it from the timer queue if active */
|
||||
if (timer->is_queued) {
|
||||
was_first = !timerqueue_linked_prev(&timer->node);
|
||||
|
||||
/* Try to update in place to avoid the de/enqueue dance */
|
||||
if (hrtimer_can_update_in_place(timer, base, expires)) {
|
||||
hrtimer_set_expires_range_ns(timer, expires, delta_ns);
|
||||
trace_hrtimer_start(timer, mode, true);
|
||||
if (was_first)
|
||||
base->expires_next = expires;
|
||||
@@ -1280,9 +1290,6 @@ remove_and_enqueue_same_base(struct hrtimer *timer, struct hrtimer_clock_base *b
|
||||
timerqueue_linked_del(&base->active, &timer->node);
|
||||
}
|
||||
|
||||
/* Set the new expiry time */
|
||||
hrtimer_set_expires_range_ns(timer, expires, delta_ns);
|
||||
|
||||
debug_activate(timer, mode, timer->is_queued);
|
||||
base->cpu_base->active_bases |= 1 << base->index;
|
||||
|
||||
|
||||
@@ -165,23 +165,31 @@ static bool tick_set_oneshot_wakeup_device(struct clock_event_device *newdev,
|
||||
*/
|
||||
void tick_install_broadcast_device(struct clock_event_device *dev, int cpu)
|
||||
{
|
||||
struct clock_event_device *cur = tick_broadcast_device.evtdev;
|
||||
struct clock_event_device *cur;
|
||||
|
||||
if (tick_set_oneshot_wakeup_device(dev, cpu))
|
||||
return;
|
||||
scoped_guard(raw_spinlock_irqsave, &tick_broadcast_lock) {
|
||||
|
||||
if (!tick_check_broadcast_device(cur, dev))
|
||||
return;
|
||||
if (tick_set_oneshot_wakeup_device(dev, cpu))
|
||||
return;
|
||||
|
||||
if (!try_module_get(dev->owner))
|
||||
return;
|
||||
cur = tick_broadcast_device.evtdev;
|
||||
if (!tick_check_broadcast_device(cur, dev))
|
||||
return;
|
||||
|
||||
clockevents_exchange_device(cur, dev);
|
||||
if (!try_module_get(dev->owner))
|
||||
return;
|
||||
|
||||
__clockevents_exchange_device(cur, dev);
|
||||
if (cur)
|
||||
cur->event_handler = clockevents_handle_noop;
|
||||
WRITE_ONCE(tick_broadcast_device.evtdev, dev);
|
||||
if (!cpumask_empty(tick_broadcast_mask))
|
||||
tick_broadcast_start_periodic(dev);
|
||||
}
|
||||
|
||||
/* Module release must be outside of the lock */
|
||||
if (cur)
|
||||
cur->event_handler = clockevents_handle_noop;
|
||||
tick_broadcast_device.evtdev = dev;
|
||||
if (!cpumask_empty(tick_broadcast_mask))
|
||||
tick_broadcast_start_periodic(dev);
|
||||
module_put(cur->owner);
|
||||
|
||||
if (!(dev->features & CLOCK_EVT_FEAT_ONESHOT))
|
||||
return;
|
||||
@@ -1218,7 +1226,7 @@ int tick_broadcast_oneshot_active(void)
|
||||
*/
|
||||
bool tick_broadcast_oneshot_available(void)
|
||||
{
|
||||
struct clock_event_device *bc = tick_broadcast_device.evtdev;
|
||||
struct clock_event_device *bc = READ_ONCE(tick_broadcast_device.evtdev);
|
||||
|
||||
return bc ? bc->features & CLOCK_EVT_FEAT_ONESHOT : false;
|
||||
}
|
||||
@@ -1226,7 +1234,7 @@ bool tick_broadcast_oneshot_available(void)
|
||||
#else
|
||||
int __tick_broadcast_oneshot_control(enum tick_broadcast_state state)
|
||||
{
|
||||
struct clock_event_device *bc = tick_broadcast_device.evtdev;
|
||||
struct clock_event_device *bc = READ_ONCE(tick_broadcast_device.evtdev);
|
||||
|
||||
if (!bc || (bc->features & CLOCK_EVT_FEAT_HRTIMER))
|
||||
return -EBUSY;
|
||||
|
||||
@@ -55,6 +55,8 @@ static inline void clockevent_set_state(struct clock_event_device *dev,
|
||||
}
|
||||
|
||||
extern void clockevents_shutdown(struct clock_event_device *dev);
|
||||
extern void __clockevents_exchange_device(struct clock_event_device *old,
|
||||
struct clock_event_device *new);
|
||||
extern void clockevents_exchange_device(struct clock_event_device *old,
|
||||
struct clock_event_device *new);
|
||||
extern void clockevents_switch_state(struct clock_event_device *dev,
|
||||
|
||||
Reference in New Issue
Block a user