lguest: per-vcpu lguest task management
authorGlauber de Oliveira Costa <gcosta@redhat.com>
Mon, 7 Jan 2008 13:05:34 +0000 (11:05 -0200)
committerRusty Russell <rusty@rustcorp.com.au>
Wed, 30 Jan 2008 11:50:12 +0000 (22:50 +1100)
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: Glauber de Oliveira Costa <gcosta@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
drivers/lguest/core.c
drivers/lguest/hypercalls.c
drivers/lguest/interrupts_and_traps.c
drivers/lguest/lg.h
drivers/lguest/lguest_user.c

index d8e1ac3..66c3d3b 100644 (file)
@@ -197,7 +197,7 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user)
                        return -ERESTARTSYS;
 
                /* If Waker set break_out, return to Launcher. */
-               if (lg->break_out)
+               if (cpu->break_out)
                        return -EAGAIN;
 
                /* 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)
 
                /* If the Guest asked to be stopped, we sleep.  The Guest's
                 * clock timer or LHCALL_BREAK from the Waker will wake us. */
-               if (lg->halted) {
+               if (cpu->halted) {
                        set_current_state(TASK_INTERRUPTIBLE);
                        schedule();
                        continue;
index 6f8c70a..83323b1 100644 (file)
@@ -88,7 +88,7 @@ static void do_hcall(struct lg_cpu *cpu, struct hcall_args *args)
                break;
        case LHCALL_HALT:
                /* Similarly, this sets the halted flag for run_guest(). */
-               lg->halted = 1;
+               cpu->halted = 1;
                break;
        case LHCALL_NOTIFY:
                lg->pending_notify = args->arg1;
index 306b93c..9c1c479 100644 (file)
@@ -161,11 +161,11 @@ void maybe_do_interrupt(struct lg_cpu *cpu)
                return;
 
        /* If they're halted, interrupts restart them. */
-       if (lg->halted) {
+       if (cpu->halted) {
                /* Re-enable interrupts. */
                if (put_user(X86_EFLAGS_IF, &lg->lguest_data->irq_enabled))
                        kill_guest(lg, "Re-enabling interrupts");
-               lg->halted = 0;
+               cpu->halted = 0;
        } else {
                /* Otherwise we check if they have interrupts disabled. */
                u32 irq_enabled;
@@ -497,8 +497,8 @@ static enum hrtimer_restart clockdev_fn(struct hrtimer *timer)
        /* Remember the first interrupt is the timer interrupt. */
        set_bit(0, cpu->irqs_pending);
        /* If the Guest is actually stopped, we need to wake it up. */
-       if (cpu->lg->halted)
-               wake_up_process(cpu->lg->tsk);
+       if (cpu->halted)
+               wake_up_process(cpu->tsk);
        return HRTIMER_NORESTART;
 }
 
index d08b853..e7123fa 100644 (file)
@@ -43,6 +43,8 @@ struct lguest;
 struct lg_cpu {
        unsigned int id;
        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.  */
        unsigned long regs_page;
@@ -55,6 +57,11 @@ struct lg_cpu {
        /* Virtual clock device */
        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 */
        DECLARE_BITMAP(irqs_pending, LGUEST_IRQS);
 
@@ -65,8 +72,6 @@ struct lg_cpu {
 struct lguest
 {
        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];
        unsigned int nr_cpus;
 
@@ -76,15 +81,10 @@ struct lguest
        void __user *mem_base;
        unsigned long kernel_address;
        u32 cr2;
-       int halted;
        int ts;
        u32 esp1;
        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. */
        int changed;
        struct lguest_pages *last_pages;
index d21d95b..980b355 100644 (file)
@@ -13,7 +13,7 @@
  * 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
  * 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;
 
@@ -22,14 +22,14 @@ static int break_guest_out(struct lguest *lg, const unsigned long __user *input)
                return -EFAULT;
 
        if (on) {
-               lg->break_out = 1;
+               cpu->break_out = 1;
                /* 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 */
-               return wait_event_interruptible(lg->break_wq, !lg->break_out);
+               return wait_event_interruptible(cpu->break_wq, !cpu->break_out);
        } else {
-               lg->break_out = 0;
-               wake_up(&lg->break_wq);
+               cpu->break_out = 0;
+               wake_up(&cpu->break_wq);
                return 0;
        }
 }
@@ -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];
 
        /* If you're not the task which owns the Guest, go away. */
-       if (current != lg->tsk)
+       if (current != cpu->tsk)
                return -EPERM;
 
        /* 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)
         * address. */
        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;
 }
 
@@ -180,17 +192,6 @@ static int initialize(struct file *file, const unsigned long __user *input)
        if (err)
                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
         * when the same Guest runs on the same CPU twice. */
        lg->last_pages = NULL;
@@ -246,7 +247,7 @@ static ssize_t write(struct file *file, const char __user *in,
                return -ENOENT;
 
        /* 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;
 
        switch (req) {
@@ -255,7 +256,7 @@ static ssize_t write(struct file *file, const char __user *in,
        case LHREQ_IRQ:
                return user_send_irq(cpu, input);
        case LHREQ_BREAK:
-               return break_guest_out(lg, input);
+               return break_guest_out(cpu, input);
        default:
                return -EINVAL;
        }
@@ -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
         * Launchers initializing guests. */
        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++) {
                /* Cancels the hrtimer set via LHCALL_SET_CLOCKEVENT. */
                hrtimer_cancel(&lg->cpus[i].hrt);
                /* We can free up the register page we allocated. */
                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
         * kmalloc()ed string, either of which is ok to hand to kfree(). */
        if (!IS_ERR(lg->dead))