SMP - Process is stopped when VM modifies the page tables

- RTS_VMINHIBIT flag is used to stop process while VM is fiddling with
  its pagetables

- more generic way of sending synchronous scheduling events among cpus

- do the x-cpu smp sched calls only if the target process is runnable.
  If it is not, it cannot be running and it cannot become runnable
  this CPU holds the BKL
This commit is contained in:
Tomas Hruby
2010-09-15 14:11:12 +00:00
parent 906a81a1c7
commit 6513d20744
10 changed files with 126 additions and 35 deletions

View File

@@ -1,3 +1,5 @@
#include <assert.h>
#include "smp.h"
#include "interrupt.h"
@@ -7,7 +9,7 @@ unsigned bsp_cpu_id;
PUBLIC struct cpu cpus[CONFIG_MAX_CPUS];
/* flags passed to another cpu along with a sched ipi */
/* info passed to another cpu along with a sched ipi */
struct sched_ipi_data {
volatile u32_t flags;
volatile u32_t data;
@@ -16,6 +18,7 @@ struct sched_ipi_data {
PRIVATE struct sched_ipi_data sched_ipi_data[CONFIG_MAX_CPUS];
#define SCHED_IPI_STOP_PROC 1
#define SCHED_IPI_VM_INHIBIT 2
static volatile unsigned ap_cpus_booted;
@@ -55,34 +58,77 @@ PUBLIC void smp_schedule(unsigned cpu)
arch_send_smp_schedule_ipi(cpu);
}
PUBLIC void smp_schedule_stop_proc(struct proc * p)
/*
* tell another cpu about a task to do and return only after the cpu acks that
* the task is finished. Also wait before it finishes task sent by another cpu
* to the same one.
*/
PRIVATE void smp_schedule_sync(struct proc * p, unsigned task)
{
unsigned cpu = p->p_cpu;
sched_ipi_data[cpu].flags |= SCHED_IPI_STOP_PROC;
/*
* if some other cpu made a request to the same cpu, wait until it is
* done before proceeding
*/
if ((volatile unsigned)sched_ipi_data[cpu].flags != 0) {
BKL_UNLOCK();
while ((volatile unsigned)sched_ipi_data[cpu].flags != 0);
BKL_LOCK();
}
sched_ipi_data[cpu].flags |= task;
sched_ipi_data[cpu].data = (u32_t) p;
arch_send_smp_schedule_ipi(cpu);
/* wait until the destination cpu finishes its job */
BKL_UNLOCK();
while ((volatile unsigned)sched_ipi_data[cpu].flags != 0);
BKL_LOCK();
}
PUBLIC void smp_schedule_stop_proc(struct proc * p)
{
if (proc_is_runnable(p))
smp_schedule_sync(p, SCHED_IPI_STOP_PROC);
else
RTS_SET(p, RTS_PROC_STOP);
assert(RTS_ISSET(p, RTS_PROC_STOP));
}
PUBLIC void smp_schedule_vminhibit(struct proc * p)
{
if (proc_is_runnable(p))
smp_schedule_sync(p, SCHED_IPI_VM_INHIBIT);
else
RTS_SET(p, RTS_VMINHIBIT);
assert(RTS_ISSET(p, RTS_VMINHIBIT));
}
PUBLIC void smp_ipi_sched_handler(void)
{
struct proc * p;
struct proc * curr;
unsigned mycpu = cpuid;
unsigned flgs;
ipi_ack();
p = get_cpu_var(mycpu, proc_ptr);
curr = get_cpu_var(mycpu, proc_ptr);
flgs = sched_ipi_data[mycpu].flags;
if (flgs & SCHED_IPI_STOP_PROC) {
RTS_SET((struct proc *)sched_ipi_data[mycpu].data, RTS_PROC_STOP);
if (flgs) {
struct proc * p;
p = (struct proc *)sched_ipi_data[mycpu].data;
if (flgs & SCHED_IPI_STOP_PROC) {
RTS_SET(p, RTS_PROC_STOP);
}
if (flgs & SCHED_IPI_VM_INHIBIT) {
RTS_SET(p, RTS_VMINHIBIT);
}
}
else if (p->p_endpoint != IDLE) {
RTS_SET(p, RTS_PREEMPTED);
else if (curr->p_endpoint != IDLE) {
RTS_SET(curr, RTS_PREEMPTED);
}
sched_ipi_data[cpuid].flags = 0;
}

View File

@@ -59,7 +59,10 @@ _PROTOTYPE(void smp_ipi_halt_handler, (void));
_PROTOTYPE(void smp_ipi_sched_handler, (void));
_PROTOTYPE(void smp_schedule, (unsigned cpu));
/* stop a processes on a different cpu */
_PROTOTYPE(void smp_schedule_stop_proc, (struct proc * p));
/* stop a process on a different cpu because its adress space is being changed */
_PROTOTYPE(void smp_schedule_vminhibit, (struct proc * p));
_PROTOTYPE(void arch_send_smp_schedule_ipi, (unsigned cpu));
_PROTOTYPE(void arch_smp_halt_cpu, (void));

View File

@@ -56,13 +56,13 @@ PUBLIC int do_runctl(struct proc * caller, message * m_ptr)
/* check if we must stop a process on a different CPU */
if (rp->p_cpu != cpuid) {
smp_schedule_stop_proc(rp);
assert(RTS_ISSET(rp, RTS_PROC_STOP));
break;
}
#endif
RTS_SET(rp, RTS_PROC_STOP);
break;
case RC_RESUME:
assert(RTS_ISSET(rp, RTS_PROC_STOP));
RTS_UNSET(rp, RTS_PROC_STOP);
break;
default:

View File

@@ -140,6 +140,23 @@ PUBLIC int do_vmctl(struct proc * caller, message * m_ptr)
return arch_phys_map_reply(m_ptr->SVMCTL_VALUE,
(vir_bytes) m_ptr->SVMCTL_MAP_VIR_ADDR);
}
case VMCTL_VMINHIBIT_SET:
/* check if we must stop a process on a different CPU */
#if CONFIG_SMP
if (p->p_cpu != cpuid) {
smp_schedule_vminhibit(p);
} else
#endif
RTS_SET(p, RTS_VMINHIBIT);
return OK;
case VMCTL_VMINHIBIT_CLEAR:
assert(RTS_ISSET(p, RTS_VMINHIBIT));
/*
* the processes is certainly not runnable, no need to tell its
* cpu
*/
RTS_UNSET(p, RTS_VMINHIBIT);
return OK;
}
/* Try architecture-specific vmctls. */