From e90698adce9c3f44955d8f500527290b2ed9ffae Mon Sep 17 00:00:00 2001 From: James Morris Date: Thu, 22 Mar 2007 03:19:40 -0400 Subject: [PATCH] lguest: add initial clock event device support (WIP) Add initial support for clock event device, enabling nohz and hrtimer support. NOTE - this is incomplete and probably quite buggy. Signed-off-by: James Morris --- arch/i386/Kconfig | 2 - arch/i386/lguest/core.c | 21 ++++++++++ arch/i386/lguest/hypercalls.c | 4 +- arch/i386/lguest/interrupts_and_traps.c | 4 -- arch/i386/lguest/lg.h | 8 +++- arch/i386/lguest/lguest.c | 65 ++++++++++++++++++++++++++++++- arch/i386/lguest/lguest_user.c | 21 ++++++++++ include/asm-i386/lguest.h | 3 + 8 files changed, 117 insertions(+), 11 deletions(-) diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index b640940..f1f74a1 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -258,7 +258,7 @@ source "arch/i386/Kconfig.cpu" config LGUEST tristate "Linux hypervisor example code" - depends on X86 && PARAVIRT && NET && EXPERIMENTAL && !X86_PAE + depends on X86 && PARAVIRT && NET && EXPERIMENTAL && !X86_PAE && NO_HZ select LGUEST_GUEST select HVC_DRIVER ---help--- diff --git a/arch/i386/lguest/core.c b/arch/i386/lguest/core.c index 3f8e111..c95e8d8 100644 --- a/arch/i386/lguest/core.c +++ b/arch/i386/lguest/core.c @@ -377,6 +377,27 @@ pending_dma: return sizeof(unsigned long)*2; } +static inline int kdelta_ok(ktime_t kdelta) +{ + return (ktime_to_ns(kdelta) >= LG_CLOCK_MIN_DELTA); +} + +void guest_clockevent(struct lguest *lg, const ktime_t __user *u) +{ + ktime_t kdelta; + + lhread(lg, &kdelta, (u32)u, sizeof(kdelta)); + + if (!kdelta_ok(kdelta) && printk_ratelimit()) { + printk(KERN_DEBUG "%s: guest %u small delta %Ld ns\n", + __FUNCTION__, lg->guestid, ktime_to_ns(kdelta)); + + /* kick guest timer immediately */ + set_bit(0, lg->irqs_pending); + } + hrtimer_start(&lg->hrt, kdelta, HRTIMER_MODE_REL); +} + static void adjust_pge(void *on) { if (on) diff --git a/arch/i386/lguest/hypercalls.c b/arch/i386/lguest/hypercalls.c index faf5be0..b6f3948 100644 --- a/arch/i386/lguest/hypercalls.c +++ b/arch/i386/lguest/hypercalls.c @@ -84,7 +84,6 @@ static int do_hcall(struct lguest *lg, s break; } case LHCALL_TIMER_START: - lg->timer_on = 1; if (regs->edx != HZ) kill_guest(lg, "Bad clock speed %i", regs->edx); lg->last_timer = jiffies; @@ -116,6 +115,9 @@ static int do_hcall(struct lguest *lg, s case LHCALL_LOAD_TLS: guest_load_tls(lg, (struct desc_struct __user*)regs->edx); break; + case LHCALL_CLOCKEVENT: + guest_clockevent(lg, (ktime_t __user *)regs->edx); + break; default: kill_guest(lg, "Bad hypercall %i\n", regs->eax); } diff --git a/arch/i386/lguest/interrupts_and_traps.c b/arch/i386/lguest/interrupts_and_traps.c index df5b0eb..320d21b 100644 --- a/arch/i386/lguest/interrupts_and_traps.c +++ b/arch/i386/lguest/interrupts_and_traps.c @@ -71,10 +71,6 @@ void maybe_do_interrupt(struct lguest *l if (!lg->lguest_data) return; - /* If timer has changed, set timer interrupt. */ - if (lg->timer_on && jiffies != lg->last_timer) - set_bit(0, lg->irqs_pending); - /* Mask out any interrupts they have blocked. */ copy_from_user(&irqs, lg->lguest_data->interrupts, sizeof(irqs)); bitmap_andnot(irqs, lg->irqs_pending, irqs, LGUEST_IRQS); diff --git a/arch/i386/lguest/lg.h b/arch/i386/lguest/lg.h index b896b21..b3a2010 100644 --- a/arch/i386/lguest/lg.h +++ b/arch/i386/lguest/lg.h @@ -21,6 +21,9 @@ #include "irq_vectors.h" #define GUEST_DPL 1 +#define LG_CLOCK_MIN_DELTA 100UL +#define LG_CLOCK_MAX_DELTA ULONG_MAX + struct lguest_regs { /* Manually saved part. */ @@ -136,7 +139,6 @@ struct lguest u32 page_offset; u32 cr2; u32 cr3; - int timer_on; int halted; int ts; u32 last_timer; @@ -174,6 +176,9 @@ struct lguest struct desc_struct idt[FIRST_EXTERNAL_VECTOR+LGUEST_IRQS]; struct desc_struct syscall_idt; + /* Virtual clock device */ + struct hrtimer hrt; + /* Pending virtual interrupts */ DECLARE_BITMAP(irqs_pending, LGUEST_IRQS); }; @@ -191,6 +196,7 @@ void lhwrite(struct lguest *lg, u32 addr int lguest_address_ok(const struct lguest *lg, unsigned long addr); int run_guest(struct lguest *lg, char *__user user); int find_free_guest(void); +void guest_clockevent(struct lguest *lg, const ktime_t __user *u); /* interrupts_and_traps.c: */ void maybe_do_interrupt(struct lguest *lg); diff --git a/arch/i386/lguest/lguest.c b/arch/i386/lguest/lguest.c index a0b67e3..d9c5f3f 100644 --- a/arch/i386/lguest/lguest.c +++ b/arch/i386/lguest/lguest.c @@ -26,6 +26,7 @@ #include #include #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include #include +#include "lg.h" /* P:0010 lguest.c contains the code compiled into the kernel when * CONFIG_LGUEST_GUEST=y, ie. whenever CONFIG_LGUEST is set to 'y' or @@ -335,10 +337,15 @@ static fastcall void lguest_write_cr4(un { } +static struct clock_event_device lguest_clockevent; + static void fastcall lguest_time_irq(unsigned int irq, struct irq_desc *desc) { - do_timer(hcall(LHCALL_TIMER_READ, 0, 0, 0)); - update_process_times(user_mode_vm(get_irq_regs())); + unsigned long flags; + + local_irq_save(flags); + lguest_clockevent.event_handler(&lguest_clockevent); + local_irq_restore(flags); } static void disable_lguest_irq(unsigned int irq) @@ -382,11 +389,63 @@ static void lguest_setup_clocksource(voi clocksource_register(&lguest_clock); } +/* XXX TODO: kill host timers via hypercall */ +static void lguest_clockevent_shutdown(void) +{ + +} + +/* XXX TODO: return -ETIME if event is in the past */ +static int lguest_clockevent_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + ktime_t kdelta = ktime_sub(evt->next_event, ktime_get()); + hcall(LHCALL_CLOCKEVENT, __pa(&kdelta), 0, 0); + return 0; +} + +static void lguest_clockevent_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + lguest_clockevent_shutdown(); + break; + + case CLOCK_EVT_MODE_ONESHOT: + break; + + case CLOCK_EVT_MODE_PERIODIC: + BUG(); + } +} + +static struct clock_event_device lguest_clockevent = { + .name = "lguest", + .features = CLOCK_EVT_FEAT_ONESHOT, + .set_next_event = lguest_clockevent_set_next_event, + .set_mode = lguest_clockevent_set_mode, + .rating = INT_MAX, + .mult = 1, + .shift = 0, + .min_delta_ns = LG_CLOCK_MIN_DELTA, + .max_delta_ns = LG_CLOCK_MAX_DELTA, +}; + +/* TODO: make this per-cpu for SMP */ +static void lguest_setup_clockevent(void) +{ + lguest_clockevent.cpumask = cpumask_of_cpu(0); + clockevents_register_device(&lguest_clockevent); +} + static void lguest_time_init(void) { + hcall(LHCALL_TIMER_START,HZ,0,0); lguest_setup_clocksource(); + lguest_setup_clockevent(); set_irq_handler(0, lguest_time_irq); - hcall(LHCALL_TIMER_START,HZ,0,0); } static void __init lguest_init_IRQ(void) diff --git a/arch/i386/lguest/lguest_user.c b/arch/i386/lguest/lguest_user.c index 70c94d1..2afcb72 100644 --- a/arch/i386/lguest/lguest_user.c +++ b/arch/i386/lguest/lguest_user.c @@ -74,6 +74,25 @@ static ssize_t read(struct file *file, c return run_guest(lg, user); } +static enum hrtimer_restart clockdev_fn(struct hrtimer *timer) +{ + struct lguest *lg = container_of(timer, struct lguest, hrt); + + set_bit(0, lg->irqs_pending); + return HRTIMER_NORESTART; +} + +static void init_clockdev(struct lguest *lg) +{ + hrtimer_init(&lg->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + lg->hrt.function = clockdev_fn; +} + +static void kill_clockdev(struct lguest *lg) +{ + hrtimer_cancel(&lg->hrt); +} + /* Take: pfnlimit, pgdir, start, pageoffset. */ static int initialize(struct file *file, const u32 __user *input) { @@ -115,6 +134,7 @@ static int initialize(struct file *file, lg->tsk = current; lg->mm = get_task_mm(current); lg->last_pages = NULL; + init_clockdev(lg); mutex_unlock(&lguest_lock); file->private_data = lg; @@ -164,6 +184,7 @@ static int close(struct inode *inode, st return 0; mutex_lock(&lguest_lock); + kill_clockdev(lg); release_all_dma(lg); free_guest_pagetable(lg); mmput(lg->mm); diff --git a/include/asm-i386/lguest.h b/include/asm-i386/lguest.h index 7e6a289..a326c09 100644 --- a/include/asm-i386/lguest.h +++ b/include/asm-i386/lguest.h @@ -25,6 +25,7 @@ #define LHCALL_SET_PTE 15 #define LHCALL_SET_UNKNOWN_PTE 16 #define LHCALL_SET_PUD 17 #define LHCALL_LOAD_TLS 18 +#define LHCALL_CLOCKEVENT 19 /* G:0040 Our Guest contacts the host in two ways: hypercalls, and the * lguest_data structure. A "hypercall" is named by analogy to a @@ -32,7 +33,7 @@ #define LHCALL_LOAD_TLS 18 * calling speaks to the Host Itself. * * The hcall uses the highest unused trap code (traps 32 and above are - * used by real hardware interrupts). Nineteen hypercalls are + * used by real hardware interrupts). Twenty hypercalls are * available: the hypercall number is put in the %eax register, and the * arguments (when required) are placed in %edx, %ebx and %ecx. If a * return value makes sense, it's returned in %eax. -- 1.4.2.1