Commit 12c406a8 authored by Jonathan Austin's avatar Jonathan Austin Committed by Will Deacon
Browse files

kvm tools: remove periodic tick in favour of a polling thread



Currently the only use of the periodic timer tick in kvmtool is to
handle reading from stdin. Though functional, this periodic tick can be
problematic on slow (eg FPGA) platforms and can cause low interactivity or
even stop the execution from progressing at all.

This patch removes the periodic tick in favour of a dedicated thread blocked
waiting for input from the console. In order to reflect the new behaviour,
the old 'kvm__arch_periodic_tick' function is renamed to 'kvm__arch_read_term'.

In making this change it is necessary to actively flush the emulated serial
console's output buffer after the guest writes to it, as otherwise flushing
only happens with terminal input. Similarly, it is no longer necessary to
flush the buffer when we process input.

Signed-off-by: default avatarJonathan Austin <jonathan.austin@arm.com>
Acked-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
Signed-off-by: default avatarPekka Enberg <penberg@kernel.org>
parent 7787f0ff
......@@ -46,7 +46,7 @@ void kvm__arch_delete_ram(struct kvm *kvm)
munmap(kvm->arch.ram_alloc_start, kvm->arch.ram_alloc_size);
}
void kvm__arch_periodic_poll(struct kvm *kvm)
void kvm__arch_read_term(struct kvm *kvm)
{
if (term_readable(0)) {
serial8250__update_consoles(kvm);
......
......@@ -165,13 +165,6 @@ void kvm_run_set_wrapper_sandbox(void)
OPT_END() \
};
static void handle_sigalrm(int sig, siginfo_t *si, void *uc)
{
struct kvm *kvm = si->si_value.sival_ptr;
kvm__arch_periodic_poll(kvm);
}
static void *kvm_cpu_thread(void *arg)
{
char name[16];
......@@ -487,17 +480,11 @@ static struct kvm *kvm_cmd_run_init(int argc, const char **argv)
{
static char real_cmdline[2048], default_name[20];
unsigned int nr_online_cpus;
struct sigaction sa;
struct kvm *kvm = kvm__new();
if (IS_ERR(kvm))
return kvm;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = handle_sigalrm;
sigemptyset(&sa.sa_mask);
sigaction(SIGALRM, &sa, NULL);
nr_online_cpus = sysconf(_SC_NPROCESSORS_ONLN);
kvm->cfg.custom_rootfs_name = "default";
......
......@@ -169,15 +169,6 @@ static void serial8250__receive(struct kvm *kvm, struct serial8250_device *dev,
{
int c;
/*
* If the guest transmitted a full fifo, we clear the
* TEMT/THRE bits to let the kernel escape from the 8250
* interrupt handler. We come here only once a ms, so that
* should give the kernel the desired pause. That also flushes
* the tx fifo to the terminal.
*/
serial8250_flush_tx(kvm, dev);
if (dev->mcr & UART_MCR_LOOP)
return;
......@@ -260,6 +251,7 @@ static bool serial8250_out(struct ioport *ioport, struct kvm *kvm, u16 port,
dev->lsr &= ~UART_LSR_TEMT;
if (dev->txcnt == FIFO_LEN / 2)
dev->lsr &= ~UART_LSR_THRE;
serial8250_flush_tx(kvm, dev);
} else {
/* Should never happpen */
dev->lsr &= ~(UART_LSR_TEMT | UART_LSR_THRE);
......
......@@ -103,7 +103,7 @@ void kvm__arch_delete_ram(struct kvm *kvm);
int kvm__arch_setup_firmware(struct kvm *kvm);
int kvm__arch_free_firmware(struct kvm *kvm);
bool kvm__arch_cpu_supports_vm(void);
void kvm__arch_periodic_poll(struct kvm *kvm);
void kvm__arch_read_term(struct kvm *kvm);
void *guest_flat_to_host(struct kvm *kvm, u64 offset);
u64 host_to_guest_flat(struct kvm *kvm, void *ptr);
......
......@@ -393,56 +393,6 @@ found_kernel:
return ret;
}
#define TIMER_INTERVAL_NS 1000000 /* 1 msec */
/*
* This function sets up a timer that's used to inject interrupts from the
* userspace hypervisor into the guest at periodical intervals. Please note
* that clock interrupt, for example, is not handled here.
*/
int kvm_timer__init(struct kvm *kvm)
{
struct itimerspec its;
struct sigevent sev;
int r;
memset(&sev, 0, sizeof(struct sigevent));
sev.sigev_value.sival_int = 0;
sev.sigev_notify = SIGEV_THREAD_ID;
sev.sigev_signo = SIGALRM;
sev.sigev_value.sival_ptr = kvm;
sev._sigev_un._tid = syscall(__NR_gettid);
r = timer_create(CLOCK_REALTIME, &sev, &kvm->timerid);
if (r < 0)
return r;
its.it_value.tv_sec = TIMER_INTERVAL_NS / 1000000000;
its.it_value.tv_nsec = TIMER_INTERVAL_NS % 1000000000;
its.it_interval.tv_sec = its.it_value.tv_sec;
its.it_interval.tv_nsec = its.it_value.tv_nsec;
r = timer_settime(kvm->timerid, 0, &its, NULL);
if (r < 0) {
timer_delete(kvm->timerid);
return r;
}
return 0;
}
firmware_init(kvm_timer__init);
int kvm_timer__exit(struct kvm *kvm)
{
if (kvm->timerid)
if (timer_delete(kvm->timerid) < 0)
die("timer_delete()");
kvm->timerid = 0;
return 0;
}
firmware_exit(kvm_timer__exit);
void kvm__dump_mem(struct kvm *kvm, unsigned long addr, unsigned long size, int debug_fd)
{
......
......@@ -151,7 +151,7 @@ void kvm__irq_trigger(struct kvm *kvm, int irq)
kvm__irq_line(kvm, irq, 0);
}
void kvm__arch_periodic_poll(struct kvm *kvm)
void kvm__arch_read_term(struct kvm *kvm)
{
/* FIXME: Should register callbacks to platform-specific polls */
spapr_hvcons_poll(kvm);
......
......@@ -25,6 +25,8 @@ bool term_got_escape = false;
int term_fds[TERM_MAX_DEVS][2];
static pthread_t term_poll_thread;
int term_getc(struct kvm *kvm, int term)
{
unsigned char c;
......@@ -91,6 +93,30 @@ bool term_readable(int term)
return poll(&pollfd, 1, 0) > 0;
}
static void *term_poll_thread_loop(void *param)
{
struct pollfd fds[TERM_MAX_DEVS];
struct kvm *kvm = (struct kvm *) param;
int i;
for (i = 0; i < TERM_MAX_DEVS; i++) {
fds[i].fd = term_fds[i][TERM_FD_IN];
fds[i].events = POLLIN;
fds[i].revents = 0;
}
while (1) {
/* Poll with infinite timeout */
if(poll(fds, TERM_MAX_DEVS, -1) < 1)
break;
kvm__arch_read_term(kvm);
}
die("term_poll_thread_loop: error polling device fds %d\n", errno);
return NULL;
}
static void term_cleanup(void)
{
int i;
......@@ -161,6 +187,11 @@ int term_init(struct kvm *kvm)
term.c_lflag &= ~(ICANON | ECHO | ISIG);
tcsetattr(STDIN_FILENO, TCSANOW, &term);
/* Use our own blocking thread to read stdin, don't require a tick */
if(pthread_create(&term_poll_thread, NULL, term_poll_thread_loop,kvm))
die("Unable to create console input poll thread\n");
signal(SIGTERM, term_sig_cleanup);
atexit(term_cleanup);
......
......@@ -374,7 +374,7 @@ int kvm__arch_free_firmware(struct kvm *kvm)
return 0;
}
void kvm__arch_periodic_poll(struct kvm *kvm)
void kvm__arch_read_term(struct kvm *kvm)
{
serial8250__update_consoles(kvm);
virtio_console__inject_interrupt(kvm);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment