2019-08-25 10:49:19 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
2015-11-02 16:16:37 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2012 - 2014 Cisco Systems
|
|
|
|
|
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef __TIMER_INTERNAL_H__
|
|
|
|
|
#define __TIMER_INTERNAL_H__
|
um: time-travel: Rewrite as an event scheduler
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.
This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)
While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.
Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.
Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-02-13 14:26:45 +01:00
|
|
|
#include <linux/list.h>
|
um: time-travel: rework interrupt handling in ext mode
In external time-travel mode, where time is controlled via the
controller application socket, interrupt handling is a little
tricky. For example on virtio, the following happens:
* we receive a message (that requires an ACK) on the vhost-user socket
* we add a time-travel event to handle the interrupt
(this causes communication on the time socket)
* we ACK the original vhost-user message
* we then handle the interrupt once the event is triggered
This protocol ensures that the sender of the interrupt only continues
to run in the simulation when the time-travel event has been added.
So far, this was only done in the virtio driver, but it was actually
wrong, because only virtqueue interrupts were handled this way, and
config change interrupts were handled immediately. Additionally, the
messages were actually handled in the real Linux interrupt handler,
but Linux interrupt handlers are part of the simulation and shouldn't
run while there's no time event.
To really do this properly and only handle all kinds of interrupts in
the time-travel event when we are scheduled to run in the simulation,
rework this to plug in to the lower interrupt layers in UML directly:
Add a um_request_irq_tt() function that let's a time-travel aware
driver request an interrupt with an additional timetravel_handler()
that is called outside of the context of the simulation, to handle
the message only. It then adds an event to the time-travel calendar
if necessary, and no "real" Linux code runs outside of the time
simulation.
This also hooks in with suspend/resume properly now, since this new
timetravel_handler() can run while Linux is suspended and interrupts
are disabled, and decide to wake up (or not) the system based on the
message it received. Importantly in this case, it ACKs the message
before the system even resumes and interrupts are re-enabled, thus
allowing the simulation to progress properly.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-12-15 10:52:24 +01:00
|
|
|
#include <asm/bug.h>
|
2021-03-05 13:19:55 +01:00
|
|
|
#include <shared/timetravel.h>
|
2015-11-02 16:16:37 +00:00
|
|
|
|
|
|
|
|
#define TIMER_MULTIPLIER 256
|
|
|
|
|
#define TIMER_MIN_DELTA 500
|
|
|
|
|
|
um: time-travel: Rewrite as an event scheduler
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.
This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)
While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.
Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.
Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-02-13 14:26:45 +01:00
|
|
|
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
|
|
|
|
|
struct time_travel_event {
|
|
|
|
|
unsigned long long time;
|
|
|
|
|
void (*fn)(struct time_travel_event *d);
|
|
|
|
|
struct list_head list;
|
|
|
|
|
bool pending, onstack;
|
2019-05-27 10:34:27 +02:00
|
|
|
};
|
|
|
|
|
|
um: Simplify os_idle_sleep() and sleep longer
There really is no reason to pass the amount of time we should
sleep, especially since it's just hard-coded to one second.
Additionally, one second isn't really all that long, and as we
are expecting to be woken up by a signal, we can sleep longer
and avoid doing some work every second, so replace the current
clock_nanosleep() with just an empty select() that can _only_
be woken up by a signal.
We can also remove the deliver_alarm() since we don't need to
do that when we got e.g. SIGIO that woke us up, and if we got
SIGALRM the signal handler will actually (have) run, so it's
just unnecessary extra work.
Similarly, in time-travel mode, just program the wakeup event
from idle to be S64_MAX, which is basically the most you could
ever simulate to. Of course, you should already have an event
in the list that's earlier and will cause a wakeup, normally
that's the regular timer interrupt, though in suspend it may
(later) also be an RTC event. Since actually getting to this
point would be a bug and you can't ever get out again, panic()
on it in the time control code.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-By: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-12-02 20:58:04 +01:00
|
|
|
void time_travel_sleep(void);
|
2019-05-27 10:34:27 +02:00
|
|
|
|
um: time-travel: Rewrite as an event scheduler
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.
This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)
While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.
Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.
Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-02-13 14:26:45 +01:00
|
|
|
static inline void
|
|
|
|
|
time_travel_set_event_fn(struct time_travel_event *e,
|
|
|
|
|
void (*fn)(struct time_travel_event *d))
|
2019-05-27 10:34:27 +02:00
|
|
|
{
|
um: time-travel: Rewrite as an event scheduler
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.
This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)
While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.
Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.
Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-02-13 14:26:45 +01:00
|
|
|
e->fn = fn;
|
2019-09-10 17:03:52 +02:00
|
|
|
}
|
2020-02-13 14:26:47 +01:00
|
|
|
|
|
|
|
|
void __time_travel_propagate_time(void);
|
|
|
|
|
|
|
|
|
|
static inline void time_travel_propagate_time(void)
|
|
|
|
|
{
|
|
|
|
|
if (time_travel_mode == TT_MODE_EXTERNAL)
|
|
|
|
|
__time_travel_propagate_time();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __time_travel_wait_readable(int fd);
|
|
|
|
|
|
|
|
|
|
static inline void time_travel_wait_readable(int fd)
|
|
|
|
|
{
|
|
|
|
|
if (time_travel_mode == TT_MODE_EXTERNAL)
|
|
|
|
|
__time_travel_wait_readable(fd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void time_travel_add_irq_event(struct time_travel_event *e);
|
2021-01-15 13:12:18 +01:00
|
|
|
void time_travel_add_event_rel(struct time_travel_event *e,
|
|
|
|
|
unsigned long long delay_ns);
|
|
|
|
|
bool time_travel_del_event(struct time_travel_event *e);
|
2019-05-27 10:34:27 +02:00
|
|
|
#else
|
um: time-travel: Rewrite as an event scheduler
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.
This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)
While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.
Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.
Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-02-13 14:26:45 +01:00
|
|
|
struct time_travel_event {
|
|
|
|
|
};
|
2019-07-22 09:12:56 +02:00
|
|
|
|
um: Simplify os_idle_sleep() and sleep longer
There really is no reason to pass the amount of time we should
sleep, especially since it's just hard-coded to one second.
Additionally, one second isn't really all that long, and as we
are expecting to be woken up by a signal, we can sleep longer
and avoid doing some work every second, so replace the current
clock_nanosleep() with just an empty select() that can _only_
be woken up by a signal.
We can also remove the deliver_alarm() since we don't need to
do that when we got e.g. SIGIO that woke us up, and if we got
SIGALRM the signal handler will actually (have) run, so it's
just unnecessary extra work.
Similarly, in time-travel mode, just program the wakeup event
from idle to be S64_MAX, which is basically the most you could
ever simulate to. Of course, you should already have an event
in the list that's earlier and will cause a wakeup, normally
that's the regular timer interrupt, though in suspend it may
(later) also be an RTC event. Since actually getting to this
point would be a bug and you can't ever get out again, panic()
on it in the time control code.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Acked-By: Anton Ivanov <anton.ivanov@cambridgegreys.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-12-02 20:58:04 +01:00
|
|
|
static inline void time_travel_sleep(void)
|
2019-09-10 17:03:52 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
um: time-travel: Rewrite as an event scheduler
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.
This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)
While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.
Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.
Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-02-13 14:26:45 +01:00
|
|
|
/* this is a macro so the event/function need not exist */
|
|
|
|
|
#define time_travel_set_event_fn(e, fn) do {} while (0)
|
2020-02-13 14:26:47 +01:00
|
|
|
|
|
|
|
|
static inline void time_travel_propagate_time(void)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void time_travel_wait_readable(int fd)
|
|
|
|
|
{
|
|
|
|
|
}
|
um: time-travel: rework interrupt handling in ext mode
In external time-travel mode, where time is controlled via the
controller application socket, interrupt handling is a little
tricky. For example on virtio, the following happens:
* we receive a message (that requires an ACK) on the vhost-user socket
* we add a time-travel event to handle the interrupt
(this causes communication on the time socket)
* we ACK the original vhost-user message
* we then handle the interrupt once the event is triggered
This protocol ensures that the sender of the interrupt only continues
to run in the simulation when the time-travel event has been added.
So far, this was only done in the virtio driver, but it was actually
wrong, because only virtqueue interrupts were handled this way, and
config change interrupts were handled immediately. Additionally, the
messages were actually handled in the real Linux interrupt handler,
but Linux interrupt handlers are part of the simulation and shouldn't
run while there's no time event.
To really do this properly and only handle all kinds of interrupts in
the time-travel event when we are scheduled to run in the simulation,
rework this to plug in to the lower interrupt layers in UML directly:
Add a um_request_irq_tt() function that let's a time-travel aware
driver request an interrupt with an additional timetravel_handler()
that is called outside of the context of the simulation, to handle
the message only. It then adds an event to the time-travel calendar
if necessary, and no "real" Linux code runs outside of the time
simulation.
This also hooks in with suspend/resume properly now, since this new
timetravel_handler() can run while Linux is suspended and interrupts
are disabled, and decide to wake up (or not) the system based on the
message it received. Importantly in this case, it ACKs the message
before the system even resumes and interrupts are re-enabled, thus
allowing the simulation to progress properly.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-12-15 10:52:24 +01:00
|
|
|
|
|
|
|
|
static inline void time_travel_add_irq_event(struct time_travel_event *e)
|
|
|
|
|
{
|
|
|
|
|
WARN_ON(1);
|
|
|
|
|
}
|
2021-01-15 13:12:18 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* not inlines so the data structure need not exist,
|
|
|
|
|
* cause linker failures
|
|
|
|
|
*/
|
|
|
|
|
extern void time_travel_not_configured(void);
|
|
|
|
|
#define time_travel_add_event_rel(...) time_travel_not_configured()
|
|
|
|
|
#define time_travel_del_event(...) time_travel_not_configured()
|
um: time-travel: Rewrite as an event scheduler
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.
This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)
While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.
Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.
Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-02-13 14:26:45 +01:00
|
|
|
#endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */
|
2020-02-13 14:26:48 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Without CONFIG_UML_TIME_TRAVEL_SUPPORT this is a linker error if used,
|
|
|
|
|
* which is intentional since we really shouldn't link it in that case.
|
|
|
|
|
*/
|
|
|
|
|
void time_travel_ndelay(unsigned long nsec);
|
um: time-travel: Rewrite as an event scheduler
Instead of tracking all the various timer configurations,
modify the time-travel mode to have an event scheduler and
use a timer event on the scheduler to handle the different
timer configurations.
This doesn't change the function right now, but it prepares
the code for having different kinds of events in the future
(i.e. interrupts coming from other devices that are part of
co-simulation.)
While at it, also move time_travel_sleep() to time.c to
reduce the externally visible API surface.
Also, we really should mark time-travel as incompatible with
SMP, even if UML doesn't support SMP yet.
Finally, I noticed a bug while developing this - if we move
time forward due to consuming time while reading the clock,
we might move across the next event and that would cause us
to go backward in time when we then handle that event. Fix
that by invoking the whole event machine in this case, but
in order to simplify this, make reading the clock only cost
something when interrupts are not disabled. Otherwise, we'd
have to hook into the interrupt delivery machinery etc. and
that's somewhat intrusive.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
2020-02-13 14:26:45 +01:00
|
|
|
#endif /* __TIMER_INTERNAL_H__ */
|