um: time-travel: fix time corruption
[ Upstream commit abe4eaa8618bb36c2b33e9cdde0499296a23448c ] In 'basic' time-travel mode (without =inf-cpu or =ext), we still get timer interrupts. These can happen at arbitrary points in time, i.e. while in timer_read(), which pushes time forward just a little bit. Then, if we happen to get the interrupt after calculating the new time to push to, but before actually finishing that, the interrupt will set the time to a value that's incompatible with the forward, and we'll crash because time goes backwards when we do the forwarding. Fix this by reading the time_travel_time, calculating the adjustment, and doing the adjustment all with interrupts disabled. Reported-by: Vincent Whitchurch <Vincent.Whitchurch@axis.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Richard Weinberger <richard@nod.at> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
d8512cc8ac
commit
4f7dad73df
@ -432,9 +432,29 @@ static void time_travel_update_time(unsigned long long next, bool idle)
|
|||||||
time_travel_del_event(&ne);
|
time_travel_del_event(&ne);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void time_travel_update_time_rel(unsigned long long offs)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable interrupts before calculating the new time so
|
||||||
|
* that a real timer interrupt (signal) can't happen at
|
||||||
|
* a bad time e.g. after we read time_travel_time but
|
||||||
|
* before we've completed updating the time.
|
||||||
|
*/
|
||||||
|
local_irq_save(flags);
|
||||||
|
time_travel_update_time(time_travel_time + offs, false);
|
||||||
|
local_irq_restore(flags);
|
||||||
|
}
|
||||||
|
|
||||||
void time_travel_ndelay(unsigned long nsec)
|
void time_travel_ndelay(unsigned long nsec)
|
||||||
{
|
{
|
||||||
time_travel_update_time(time_travel_time + nsec, false);
|
/*
|
||||||
|
* Not strictly needed to use _rel() version since this is
|
||||||
|
* only used in INFCPU/EXT modes, but it doesn't hurt and
|
||||||
|
* is more readable too.
|
||||||
|
*/
|
||||||
|
time_travel_update_time_rel(nsec);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(time_travel_ndelay);
|
EXPORT_SYMBOL(time_travel_ndelay);
|
||||||
|
|
||||||
@ -568,7 +588,11 @@ static void time_travel_set_start(void)
|
|||||||
#define time_travel_time 0
|
#define time_travel_time 0
|
||||||
#define time_travel_ext_waiting 0
|
#define time_travel_ext_waiting 0
|
||||||
|
|
||||||
static inline void time_travel_update_time(unsigned long long ns, bool retearly)
|
static inline void time_travel_update_time(unsigned long long ns, bool idle)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void time_travel_update_time_rel(unsigned long long offs)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -720,9 +744,7 @@ static u64 timer_read(struct clocksource *cs)
|
|||||||
*/
|
*/
|
||||||
if (!irqs_disabled() && !in_interrupt() && !in_softirq() &&
|
if (!irqs_disabled() && !in_interrupt() && !in_softirq() &&
|
||||||
!time_travel_ext_waiting)
|
!time_travel_ext_waiting)
|
||||||
time_travel_update_time(time_travel_time +
|
time_travel_update_time_rel(TIMER_MULTIPLIER);
|
||||||
TIMER_MULTIPLIER,
|
|
||||||
false);
|
|
||||||
return time_travel_time / TIMER_MULTIPLIER;
|
return time_travel_time / TIMER_MULTIPLIER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user