Commit 66686c2a authored by Glauber de Oliveira Costa's avatar Glauber de Oliveira Costa Committed by Rusty Russell

lguest: per-vcpu lguest task management

lguest uses tasks to control its running behaviour (like sending
breaks, controlling halted state, etc). In a per-vcpu environment,
each vcpu will have its own underlying task. So this patch
makes the infrastructure for that possible
Signed-off-by: default avatarGlauber de Oliveira Costa <gcosta@redhat.com>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent fc708b3e
...@@ -197,7 +197,7 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) ...@@ -197,7 +197,7 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
return -ERESTARTSYS; return -ERESTARTSYS;
/* If Waker set break_out, return to Launcher. */ /* If Waker set break_out, return to Launcher. */
if (lg->break_out) if (cpu->break_out)
return -EAGAIN; return -EAGAIN;
/* Check if there are any interrupts which can be delivered /* Check if there are any interrupts which can be delivered
...@@ -217,7 +217,7 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user) ...@@ -217,7 +217,7 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
/* If the Guest asked to be stopped, we sleep. The Guest's /* If the Guest asked to be stopped, we sleep. The Guest's
* clock timer or LHCALL_BREAK from the Waker will wake us. */ * clock timer or LHCALL_BREAK from the Waker will wake us. */
if (lg->halted) { if (cpu->halted) {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
schedule(); schedule();
continue; continue;
......
...@@ -88,7 +88,7 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args) ...@@ -88,7 +88,7 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args)
break; break;
case LHCALL_HALT: case LHCALL_HALT:
/* Similarly, this sets the halted flag for run_guest(). */ /* Similarly, this sets the halted flag for run_guest(). */
lg->halted = 1; cpu->halted = 1;
break; break;
case LHCALL_NOTIFY: case LHCALL_NOTIFY:
lg->pending_notify = args->arg1; lg->pending_notify = args->arg1;
......
...@@ -161,11 +161,11 @@ void maybe_do_interrupt(struct lg_cpu *cpu) ...@@ -161,11 +161,11 @@ void maybe_do_interrupt(struct lg_cpu *cpu)
return; return;
/* If they're halted, interrupts restart them. */ /* If they're halted, interrupts restart them. */
if (lg->halted) { if (cpu->halted) {
/* Re-enable interrupts. */ /* Re-enable interrupts. */
if (put_user(X86_EFLAGS_IF, &lg->lguest_data->irq_enabled)) if (put_user(X86_EFLAGS_IF, &lg->lguest_data->irq_enabled))
kill_guest(lg, "Re-enabling interrupts"); kill_guest(lg, "Re-enabling interrupts");
lg->halted = 0; cpu->halted = 0;
} else { } else {
/* Otherwise we check if they have interrupts disabled. */ /* Otherwise we check if they have interrupts disabled. */
u32 irq_enabled; u32 irq_enabled;
...@@ -497,8 +497,8 @@ static enum hrtimer_restart clockdev_fn(struct hrtimer *timer) ...@@ -497,8 +497,8 @@ static enum hrtimer_restart clockdev_fn(struct hrtimer *timer)
/* Remember the first interrupt is the timer interrupt. */ /* Remember the first interrupt is the timer interrupt. */
set_bit(0, cpu->irqs_pending); set_bit(0, cpu->irqs_pending);
/* If the Guest is actually stopped, we need to wake it up. */ /* If the Guest is actually stopped, we need to wake it up. */
if (cpu->lg->halted) if (cpu->halted)
wake_up_process(cpu->lg->tsk); wake_up_process(cpu->tsk);
return HRTIMER_NORESTART; return HRTIMER_NORESTART;
} }
......
...@@ -43,6 +43,8 @@ struct lguest; ...@@ -43,6 +43,8 @@ struct lguest;
struct lg_cpu { struct lg_cpu {
unsigned int id; unsigned int id;
struct lguest *lg; struct lguest *lg;
struct task_struct *tsk;
struct mm_struct *mm; /* == tsk->mm, but that becomes NULL on exit */
/* At end of a page shared mapped over lguest_pages in guest. */ /* At end of a page shared mapped over lguest_pages in guest. */
unsigned long regs_page; unsigned long regs_page;
...@@ -55,6 +57,11 @@ struct lg_cpu { ...@@ -55,6 +57,11 @@ struct lg_cpu {
/* Virtual clock device */ /* Virtual clock device */
struct hrtimer hrt; struct hrtimer hrt;
/* Do we need to stop what we're doing and return to userspace? */
int break_out;
wait_queue_head_t break_wq;
int halted;
/* Pending virtual interrupts */ /* Pending virtual interrupts */
DECLARE_BITMAP(irqs_pending, LGUEST_IRQS); DECLARE_BITMAP(irqs_pending, LGUEST_IRQS);
...@@ -65,8 +72,6 @@ struct lg_cpu { ...@@ -65,8 +72,6 @@ struct lg_cpu {
struct lguest struct lguest
{ {
struct lguest_data __user *lguest_data; struct lguest_data __user *lguest_data;
struct task_struct *tsk;
struct mm_struct *mm; /* == tsk->mm, but that becomes NULL on exit */
struct lg_cpu cpus[NR_CPUS]; struct lg_cpu cpus[NR_CPUS];
unsigned int nr_cpus; unsigned int nr_cpus;
...@@ -76,15 +81,10 @@ struct lguest ...@@ -76,15 +81,10 @@ struct lguest
void __user *mem_base; void __user *mem_base;
unsigned long kernel_address; unsigned long kernel_address;
u32 cr2; u32 cr2;
int halted;
int ts; int ts;
u32 esp1; u32 esp1;
u8 ss1; u8 ss1;
/* Do we need to stop what we're doing and return to userspace? */
int break_out;
wait_queue_head_t break_wq;
/* Bitmap of what has changed: see CHANGED_* above. */ /* Bitmap of what has changed: see CHANGED_* above. */
int changed; int changed;
struct lguest_pages *last_pages; struct lguest_pages *last_pages;
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* LHREQ_BREAK and the value "1" to /dev/lguest to do this. Once the Launcher * LHREQ_BREAK and the value "1" to /dev/lguest to do this. Once the Launcher
* has done whatever needs attention, it writes LHREQ_BREAK and "0" to release * has done whatever needs attention, it writes LHREQ_BREAK and "0" to release
* the Waker. */ * the Waker. */
static int break_guest_out(struct lguest *lg, const unsigned long __user *input) static int break_guest_out(struct lg_cpu *cpu, const unsigned long __user*input)
{ {
unsigned long on; unsigned long on;
...@@ -22,14 +22,14 @@ static int break_guest_out(struct lguest *lg, const unsigned long __user *input) ...@@ -22,14 +22,14 @@ static int break_guest_out(struct lguest *lg, const unsigned long __user *input)
return -EFAULT; return -EFAULT;
if (on) { if (on) {
lg->break_out = 1; cpu->break_out = 1;
/* Pop it out of the Guest (may be running on different CPU) */ /* Pop it out of the Guest (may be running on different CPU) */
wake_up_process(lg->tsk); wake_up_process(cpu->tsk);
/* Wait for them to reset it */ /* Wait for them to reset it */
return wait_event_interruptible(lg->break_wq, !lg->break_out); return wait_event_interruptible(cpu->break_wq, !cpu->break_out);
} else { } else {
lg->break_out = 0; cpu->break_out = 0;
wake_up(&lg->break_wq); wake_up(&cpu->break_wq);
return 0; return 0;
} }
} }
...@@ -69,7 +69,7 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o) ...@@ -69,7 +69,7 @@ static ssize_t read(struct file *file, char __user *user, size_t size,loff_t*o)
cpu = &lg->cpus[cpu_id]; cpu = &lg->cpus[cpu_id];
/* If you're not the task which owns the Guest, go away. */ /* If you're not the task which owns the Guest, go away. */
if (current != lg->tsk) if (current != cpu->tsk)
return -EPERM; return -EPERM;
/* If the guest is already dead, we indicate why */ /* If the guest is already dead, we indicate why */
...@@ -119,6 +119,18 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip) ...@@ -119,6 +119,18 @@ static int lg_cpu_start(struct lg_cpu *cpu, unsigned id, unsigned long start_ip)
* address. */ * address. */
lguest_arch_setup_regs(cpu, start_ip); lguest_arch_setup_regs(cpu, start_ip);
/* Initialize the queue for the waker to wait on */
init_waitqueue_head(&cpu->break_wq);
/* We keep a pointer to the Launcher task (ie. current task) for when
* other Guests want to wake this one (inter-Guest I/O). */
cpu->tsk = current;
/* We need to keep a pointer to the Launcher's memory map, because if
* the Launcher dies we need to clean it up. If we don't keep a
* reference, it is destroyed before close() is called. */
cpu->mm = get_task_mm(cpu->tsk);
return 0; return 0;
} }
...@@ -180,17 +192,6 @@ static int initialize(struct file *file, const unsigned long __user *input) ...@@ -180,17 +192,6 @@ static int initialize(struct file *file, const unsigned long __user *input)
if (err) if (err)
goto free_regs; goto free_regs;
/* We keep a pointer to the Launcher task (ie. current task) for when
* other Guests want to wake this one (inter-Guest I/O). */
lg->tsk = current;
/* We need to keep a pointer to the Launcher's memory map, because if
* the Launcher dies we need to clean it up. If we don't keep a
* reference, it is destroyed before close() is called. */
lg->mm = get_task_mm(lg->tsk);
/* Initialize the queue for the waker to wait on */
init_waitqueue_head(&lg->break_wq);
/* We remember which CPU's pages this Guest used last, for optimization /* We remember which CPU's pages this Guest used last, for optimization
* when the same Guest runs on the same CPU twice. */ * when the same Guest runs on the same CPU twice. */
lg->last_pages = NULL; lg->last_pages = NULL;
...@@ -246,7 +247,7 @@ static ssize_t write(struct file *file, const char __user *in, ...@@ -246,7 +247,7 @@ static ssize_t write(struct file *file, const char __user *in,
return -ENOENT; return -ENOENT;
/* If you're not the task which owns the Guest, you can only break */ /* If you're not the task which owns the Guest, you can only break */
if (lg && current != lg->tsk && req != LHREQ_BREAK) if (lg && current != cpu->tsk && req != LHREQ_BREAK)
return -EPERM; return -EPERM;
switch (req) { switch (req) {
...@@ -255,7 +256,7 @@ static ssize_t write(struct file *file, const char __user *in, ...@@ -255,7 +256,7 @@ static ssize_t write(struct file *file, const char __user *in,
case LHREQ_IRQ: case LHREQ_IRQ:
return user_send_irq(cpu, input); return user_send_irq(cpu, input);
case LHREQ_BREAK: case LHREQ_BREAK:
return break_guest_out(lg, input); return break_guest_out(cpu, input);
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -280,17 +281,19 @@ static int close(struct inode *inode, struct file *file) ...@@ -280,17 +281,19 @@ static int close(struct inode *inode, struct file *file)
/* We need the big lock, to protect from inter-guest I/O and other /* We need the big lock, to protect from inter-guest I/O and other
* Launchers initializing guests. */ * Launchers initializing guests. */
mutex_lock(&lguest_lock); mutex_lock(&lguest_lock);
/* Free up the shadow page tables for the Guest. */
free_guest_pagetable(lg);
for (i = 0; i < lg->nr_cpus; i++) { for (i = 0; i < lg->nr_cpus; i++) {
/* Cancels the hrtimer set via LHCALL_SET_CLOCKEVENT. */ /* Cancels the hrtimer set via LHCALL_SET_CLOCKEVENT. */
hrtimer_cancel(&lg->cpus[i].hrt); hrtimer_cancel(&lg->cpus[i].hrt);
/* We can free up the register page we allocated. */ /* We can free up the register page we allocated. */
free_page(lg->cpus[i].regs_page); free_page(lg->cpus[i].regs_page);
/* Now all the memory cleanups are done, it's safe to release
* the Launcher's memory management structure. */
mmput(lg->cpus[i].mm);
} }
/* Free up the shadow page tables for the Guest. */
free_guest_pagetable(lg);
/* Now all the memory cleanups are done, it's safe to release the
* Launcher's memory management structure. */
mmput(lg->mm);
/* If lg->dead doesn't contain an error code it will be NULL or a /* If lg->dead doesn't contain an error code it will be NULL or a
* kmalloc()ed string, either of which is ok to hand to kfree(). */ * kmalloc()ed string, either of which is ok to hand to kfree(). */
if (!IS_ERR(lg->dead)) if (!IS_ERR(lg->dead))
......
Markdown is supported
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