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: Glauber de Oliveira Costa <gcosta@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
fc708b3e40
commit
66686c2ab0
|
@ -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)
|
||||||
|
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
/* 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;
|
||||||
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 {
|
||||||
/* 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 {
|
||||||
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
|
||||||
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 @@
|
||||||
* 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)
|
||||||
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)
|
||||||
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)
|
||||||
* 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)
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
/* 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))
|
||||||
|
|
Loading…
Reference in New Issue