Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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;
+4 −4
Original line number Diff line number Diff line
@@ -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;
}

+7 −7
Original line number Diff line number Diff line
@@ -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;
+28 −25
Original line number Diff line number Diff line
@@ -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))