forked from mirrors/linux
		
	um: time-travel/signals: fix ndelay() in interrupt
We should be able to ndelay() from any context, even from an
interrupt context! However, this is broken (not functionally,
but locking-wise) in time-travel because we'll get into the
time-travel code and enable interrupts to handle messages on
other time-travel aware subsystems (only virtio for now).
Luckily, I've already reworked the time-travel aware signal
(interrupt) delivery for suspend/resume to have a time travel
handler, which runs directly in the context of the signal and
not from the Linux interrupt.
In order to fix this time-travel issue then, we need to do a
few things:
 1) rework the signal handling code to call time-travel handlers
    (only) if interrupts are disabled but signals aren't blocked,
    instead of marking it only pending there. This is needed to
    not deadlock other communication.
 2) rework time-travel to not enable interrupts while it's
    waiting for a message;
 3) rework time-travel to not (just) disable interrupts but
    rather block signals at a lower level while it needs them
    disabled for communicating with the controller.
Finally, since now we can actually spend even virtual time
in interrupts-disabled sections, the delay warning when we
deliver a time-travel delayed interrupt is no longer valid,
things can (and should) now get delayed.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
			
			
This commit is contained in:
		
							parent
							
								
									33c7d0616a
								
							
						
					
					
						commit
						d6b399a0e0
					
				
					 5 changed files with 96 additions and 33 deletions
				
			
		| 
						 | 
					@ -17,6 +17,7 @@ enum um_irq_type {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct siginfo;
 | 
					struct siginfo;
 | 
				
			||||||
extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
 | 
					extern void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs);
 | 
				
			||||||
 | 
					void sigio_run_timetravel_handlers(void);
 | 
				
			||||||
extern void free_irq_by_fd(int fd);
 | 
					extern void free_irq_by_fd(int fd);
 | 
				
			||||||
extern void deactivate_fd(int fd, int irqnum);
 | 
					extern void deactivate_fd(int fd, int irqnum);
 | 
				
			||||||
extern int deactivate_all_fds(void);
 | 
					extern int deactivate_all_fds(void);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -242,6 +242,9 @@ extern int set_signals_trace(int enable);
 | 
				
			||||||
extern int os_is_signal_stack(void);
 | 
					extern int os_is_signal_stack(void);
 | 
				
			||||||
extern void deliver_alarm(void);
 | 
					extern void deliver_alarm(void);
 | 
				
			||||||
extern void register_pm_wake_signal(void);
 | 
					extern void register_pm_wake_signal(void);
 | 
				
			||||||
 | 
					extern void block_signals_hard(void);
 | 
				
			||||||
 | 
					extern void unblock_signals_hard(void);
 | 
				
			||||||
 | 
					extern void mark_sigio_pending(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* util.c */
 | 
					/* util.c */
 | 
				
			||||||
extern void stack_protections(unsigned long address);
 | 
					extern void stack_protections(unsigned long address);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,7 +123,8 @@ static bool irq_do_timetravel_handler(struct irq_entry *entry,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type t,
 | 
					static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type t,
 | 
				
			||||||
			      struct uml_pt_regs *regs)
 | 
								      struct uml_pt_regs *regs,
 | 
				
			||||||
 | 
								      bool timetravel_handlers_only)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct irq_reg *reg = &entry->reg[t];
 | 
						struct irq_reg *reg = &entry->reg[t];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -136,18 +137,29 @@ static void sigio_reg_handler(int idx, struct irq_entry *entry, enum um_irq_type
 | 
				
			||||||
	if (irq_do_timetravel_handler(entry, t))
 | 
						if (irq_do_timetravel_handler(entry, t))
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (irqs_suspended)
 | 
						/*
 | 
				
			||||||
 | 
						 * If we're called to only run time-travel handlers then don't
 | 
				
			||||||
 | 
						 * actually proceed but mark sigio as pending (if applicable).
 | 
				
			||||||
 | 
						 * For suspend/resume, timetravel_handlers_only may be true
 | 
				
			||||||
 | 
						 * despite time-travel not being configured and used.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (timetravel_handlers_only) {
 | 
				
			||||||
 | 
					#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
 | 
				
			||||||
 | 
							mark_sigio_pending();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	irq_io_loop(reg, regs);
 | 
						irq_io_loop(reg, regs);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
 | 
					static void _sigio_handler(struct uml_pt_regs *regs,
 | 
				
			||||||
 | 
								   bool timetravel_handlers_only)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct irq_entry *irq_entry;
 | 
						struct irq_entry *irq_entry;
 | 
				
			||||||
	int n, i;
 | 
						int n, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (irqs_suspended && !um_irq_timetravel_handler_used())
 | 
						if (timetravel_handlers_only && !um_irq_timetravel_handler_used())
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
| 
						 | 
					@ -172,14 +184,20 @@ void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
 | 
				
			||||||
			irq_entry = os_epoll_get_data_pointer(i);
 | 
								irq_entry = os_epoll_get_data_pointer(i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			for (t = 0; t < NUM_IRQ_TYPES; t++)
 | 
								for (t = 0; t < NUM_IRQ_TYPES; t++)
 | 
				
			||||||
				sigio_reg_handler(i, irq_entry, t, regs);
 | 
									sigio_reg_handler(i, irq_entry, t, regs,
 | 
				
			||||||
 | 
											  timetravel_handlers_only);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!irqs_suspended)
 | 
						if (!timetravel_handlers_only)
 | 
				
			||||||
		free_irqs();
 | 
							free_irqs();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_sigio_handler(regs, irqs_suspended);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static struct irq_entry *get_irq_entry_by_fd(int fd)
 | 
					static struct irq_entry *get_irq_entry_by_fd(int fd)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct irq_entry *walk;
 | 
						struct irq_entry *walk;
 | 
				
			||||||
| 
						 | 
					@ -467,6 +485,11 @@ int um_request_irq_tt(int irq, int fd, enum um_irq_type type,
 | 
				
			||||||
			       devname, dev_id, timetravel_handler);
 | 
								       devname, dev_id, timetravel_handler);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(um_request_irq_tt);
 | 
					EXPORT_SYMBOL(um_request_irq_tt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void sigio_run_timetravel_handlers(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						_sigio_handler(NULL, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef CONFIG_PM_SLEEP
 | 
					#ifdef CONFIG_PM_SLEEP
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,23 +68,15 @@ static void time_travel_handle_message(struct um_timetravel_msg *msg,
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Poll outside the locked section (if we're not called to only read
 | 
						 * We can't unlock here, but interrupt signals with a timetravel_handler
 | 
				
			||||||
	 * the response) so we can get interrupts for e.g. virtio while we're
 | 
						 * (see um_request_irq_tt) get to the timetravel_handler anyway.
 | 
				
			||||||
	 * here, but then we need to lock to not get interrupted between the
 | 
					 | 
				
			||||||
	 * read of the message and write of the ACK.
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (mode != TTMH_READ) {
 | 
						if (mode != TTMH_READ) {
 | 
				
			||||||
		bool disabled = irqs_disabled();
 | 
							BUG_ON(mode == TTMH_IDLE && !irqs_disabled());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		BUG_ON(mode == TTMH_IDLE && !disabled);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (disabled)
 | 
					 | 
				
			||||||
			local_irq_enable();
 | 
					 | 
				
			||||||
		while (os_poll(1, &time_travel_ext_fd) != 0) {
 | 
							while (os_poll(1, &time_travel_ext_fd) != 0) {
 | 
				
			||||||
			/* nothing */
 | 
								/* nothing */
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (disabled)
 | 
					 | 
				
			||||||
			local_irq_disable();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ret = os_read_file(time_travel_ext_fd, msg, sizeof(*msg));
 | 
						ret = os_read_file(time_travel_ext_fd, msg, sizeof(*msg));
 | 
				
			||||||
| 
						 | 
					@ -123,15 +115,15 @@ static u64 time_travel_ext_req(u32 op, u64 time)
 | 
				
			||||||
		.time = time,
 | 
							.time = time,
 | 
				
			||||||
		.seq = mseq,
 | 
							.seq = mseq,
 | 
				
			||||||
	};
 | 
						};
 | 
				
			||||||
	unsigned long flags;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * We need to save interrupts here and only restore when we
 | 
						 * We need to block even the timetravel handlers of SIGIO here and
 | 
				
			||||||
	 * got the ACK - otherwise we can get interrupted and send
 | 
						 * only restore their use when we got the ACK - otherwise we may
 | 
				
			||||||
	 * another request while we're still waiting for an ACK, but
 | 
						 * (will) get interrupted by that, try to queue the IRQ for future
 | 
				
			||||||
	 * the peer doesn't know we got interrupted and will send
 | 
						 * processing and thus send another request while we're still waiting
 | 
				
			||||||
	 * the ACKs in the same order as the message, but we'd need
 | 
						 * for an ACK, but the peer doesn't know we got interrupted and will
 | 
				
			||||||
	 * to see them in the opposite order ...
 | 
						 * send the ACKs in the same order as the message, but we'd need to
 | 
				
			||||||
 | 
						 * see them in the opposite order ...
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * This wouldn't matter *too* much, but some ACKs carry the
 | 
						 * This wouldn't matter *too* much, but some ACKs carry the
 | 
				
			||||||
	 * current time (for UM_TIMETRAVEL_GET) and getting another
 | 
						 * current time (for UM_TIMETRAVEL_GET) and getting another
 | 
				
			||||||
| 
						 | 
					@ -140,7 +132,7 @@ static u64 time_travel_ext_req(u32 op, u64 time)
 | 
				
			||||||
	 * The sequence number assignment that happens here lets us
 | 
						 * The sequence number assignment that happens here lets us
 | 
				
			||||||
	 * debug such message handling issues more easily.
 | 
						 * debug such message handling issues more easily.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	local_irq_save(flags);
 | 
						block_signals_hard();
 | 
				
			||||||
	os_write_file(time_travel_ext_fd, &msg, sizeof(msg));
 | 
						os_write_file(time_travel_ext_fd, &msg, sizeof(msg));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (msg.op != UM_TIMETRAVEL_ACK)
 | 
						while (msg.op != UM_TIMETRAVEL_ACK)
 | 
				
			||||||
| 
						 | 
					@ -152,7 +144,7 @@ static u64 time_travel_ext_req(u32 op, u64 time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (op == UM_TIMETRAVEL_GET)
 | 
						if (op == UM_TIMETRAVEL_GET)
 | 
				
			||||||
		time_travel_set_time(msg.time);
 | 
							time_travel_set_time(msg.time);
 | 
				
			||||||
	local_irq_restore(flags);
 | 
						unblock_signals_hard();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return msg.time;
 | 
						return msg.time;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -352,9 +344,6 @@ void deliver_time_travel_irqs(void)
 | 
				
			||||||
	while ((e = list_first_entry_or_null(&time_travel_irqs,
 | 
						while ((e = list_first_entry_or_null(&time_travel_irqs,
 | 
				
			||||||
					     struct time_travel_event,
 | 
										     struct time_travel_event,
 | 
				
			||||||
					     list))) {
 | 
										     list))) {
 | 
				
			||||||
		WARN(e->time != time_travel_time,
 | 
					 | 
				
			||||||
		     "time moved from %lld to %lld before IRQ delivery\n",
 | 
					 | 
				
			||||||
		     time_travel_time, e->time);
 | 
					 | 
				
			||||||
		list_del(&e->list);
 | 
							list_del(&e->list);
 | 
				
			||||||
		e->pending = false;
 | 
							e->pending = false;
 | 
				
			||||||
		e->fn(e);
 | 
							e->fn(e);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@
 | 
				
			||||||
#include <sysdep/mcontext.h>
 | 
					#include <sysdep/mcontext.h>
 | 
				
			||||||
#include <um_malloc.h>
 | 
					#include <um_malloc.h>
 | 
				
			||||||
#include <sys/ucontext.h>
 | 
					#include <sys/ucontext.h>
 | 
				
			||||||
 | 
					#include <timetravel.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
 | 
					void (*sig_info[NSIG])(int, struct siginfo *, struct uml_pt_regs *) = {
 | 
				
			||||||
	[SIGTRAP]	= relay_signal,
 | 
						[SIGTRAP]	= relay_signal,
 | 
				
			||||||
| 
						 | 
					@ -63,16 +64,29 @@ static void sig_handler_common(int sig, struct siginfo *si, mcontext_t *mc)
 | 
				
			||||||
#define SIGALRM_MASK (1 << SIGALRM_BIT)
 | 
					#define SIGALRM_MASK (1 << SIGALRM_BIT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int signals_enabled;
 | 
					int signals_enabled;
 | 
				
			||||||
 | 
					#ifdef UML_CONFIG_UML_TIME_TRAVEL_SUPPORT
 | 
				
			||||||
 | 
					static int signals_blocked;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define signals_blocked false
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
static unsigned int signals_pending;
 | 
					static unsigned int signals_pending;
 | 
				
			||||||
static unsigned int signals_active = 0;
 | 
					static unsigned int signals_active = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void sig_handler(int sig, struct siginfo *si, mcontext_t *mc)
 | 
					void sig_handler(int sig, struct siginfo *si, mcontext_t *mc)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int enabled;
 | 
						int enabled = signals_enabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enabled = signals_enabled;
 | 
						if ((signals_blocked || !enabled) && (sig == SIGIO)) {
 | 
				
			||||||
	if (!enabled && (sig == SIGIO)) {
 | 
							/*
 | 
				
			||||||
		signals_pending |= SIGIO_MASK;
 | 
							 * In TT_MODE_EXTERNAL, need to still call time-travel
 | 
				
			||||||
 | 
							 * handlers unless signals are also blocked for the
 | 
				
			||||||
 | 
							 * external time message processing. This will mark
 | 
				
			||||||
 | 
							 * signals_pending by itself (only if necessary.)
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (!signals_blocked && time_travel_mode == TT_MODE_EXTERNAL)
 | 
				
			||||||
 | 
								sigio_run_timetravel_handlers();
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								signals_pending |= SIGIO_MASK;
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -363,6 +377,39 @@ int set_signals_trace(int enable)
 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef UML_CONFIG_UML_TIME_TRAVEL_SUPPORT
 | 
				
			||||||
 | 
					void mark_sigio_pending(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						signals_pending |= SIGIO_MASK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void block_signals_hard(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (signals_blocked)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						signals_blocked = 1;
 | 
				
			||||||
 | 
						barrier();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void unblock_signals_hard(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!signals_blocked)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						/* Must be set to 0 before we check the pending bits etc. */
 | 
				
			||||||
 | 
						signals_blocked = 0;
 | 
				
			||||||
 | 
						barrier();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (signals_pending && signals_enabled) {
 | 
				
			||||||
 | 
							/* this is a bit inefficient, but that's not really important */
 | 
				
			||||||
 | 
							block_signals();
 | 
				
			||||||
 | 
							unblock_signals();
 | 
				
			||||||
 | 
						} else if (signals_pending & SIGIO_MASK) {
 | 
				
			||||||
 | 
							/* we need to run time-travel handlers even if not enabled */
 | 
				
			||||||
 | 
							sigio_run_timetravel_handlers();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int os_is_signal_stack(void)
 | 
					int os_is_signal_stack(void)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	stack_t ss;
 | 
						stack_t ss;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue