mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
Misc timer fixes:
- 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)
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-----BEGIN PGP SIGNATURE-----
iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmqmXaERHG1pbmdvQGtl
cm5lbC5vcmcACgkQEnMQ0APhK1jM9RAAnEShNuh27uYj3oxVgaLo+Dhsk1AYzNnr
WVS/Cz8HfFXMEVnOsT3CibcB6p5wydTHms/8248GZWWUuM1HtL/7zcUHWGFgs72S
WRTcw/ouzvdAKQfxlH2j96uApMWwnWnv9XRfpFel9bgIIK1POL9g0JJmcuFK5uNf
5aYvzkdLv3SKHR0BnrIF4a6jqq1Shf2sZPDXHmJDE/k/He28zvFjiOlIEChxfCfh
qUEzY036hSU0RbAONSbn88bj7dc10/Xuck/iW3WVW8cOqtVxw79biAoumUe9jqwE
hX3B6rvDEPvaEOPDm2PgUlrapFukjfImu7K9rDljbFMX1jF6eb7ZQMk4ftJXL+rM
M0RPCdrS2ZrVOKt3VFIYRH7ZzFNwtE+RHPZSD6lpVgia6xpgi6yY++AzTeCn+VpK
3AmkxMg3xHOLkISyCRUlmtTn3Cis6O7+9+9dEad24dh5mkQM7Tr6nzprYeg3fgpR
z714UKjOUvBNBtxCjdZl5/c/i8mb0IaH4DmT+/V6mIXWoHchbqgw0Or7G7XMm5XM
M1J+4RJrGhhg1eUTb254PWi/OixuXZ8XgcB1wwAiJFMTJY9YqBExKRAdZ+Al1DJP
FgHDEPyvElpeh5XFFqf8Ft9xXOTn1CSEc6G+dCO0MswcikHnw4UnmLGn9z3js2AS
SRlK75Dm49s=
=SyXt
-----END PGP SIGNATURE-----
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:
commit
c874ace034
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user