SMP - trully idle APs

- any cpu can use smp_schedule() to tell another cpu to reschedule

- if an AP is idle, it turns off timer as there is nothing to
  preempt, no need to wakeup just to go back to sleep again

- if a cpu makes a process runnable on an idle cpu, it must wake it up
  to reschedule
This commit is contained in:
Tomas Hruby 2010-09-15 14:10:57 +00:00
parent 387e1835d1
commit 0ac9b6d4cf
12 changed files with 111 additions and 19 deletions

View file

@ -159,7 +159,10 @@ PRIVATE u32_t lapic_tctr0, lapic_tctr1;
PRIVATE unsigned apic_imcrp;
PRIVATE const unsigned nlints = 0;
#define apic_eoi() do { *((volatile u32_t *) lapic_eoi_addr) = 0; } while(0)
PUBLIC void arch_eoi(void)
{
apic_eoi();
}
/*
* FIXME this should be a cpulocal variable but there are some problems with
@ -394,10 +397,6 @@ PUBLIC void ioapic_mask_irq(unsigned irq)
irq_8259_mask(irq);
}
PUBLIC void apic_ipi_sched_handler(void)
{
}
PUBLIC unsigned int apicid(void)
{
return lapic_read(LAPIC_ID);
@ -546,6 +545,13 @@ PUBLIC void lapic_stop_timer(void)
lapic_write(LAPIC_LVTTR, lvtt | APIC_LVTT_MASK);
}
PUBLIC void lapic_restart_timer(void)
{
u32_t lvtt;
lvtt = lapic_read(LAPIC_LVTTR);
lapic_write(LAPIC_LVTTR, lvtt & ~APIC_LVTT_MASK);
}
PUBLIC void lapic_microsec_sleep(unsigned count)
{
lapic_set_timer_one_shot(count);

View file

@ -152,11 +152,14 @@ _PROTOTYPE(int apic_single_cpu_init, (void));
_PROTOTYPE(void lapic_set_timer_periodic, (const unsigned freq));
_PROTOTYPE(void lapic_set_timer_one_shot, (const u32_t value));
_PROTOTYPE(void lapic_stop_timer, (void));
_PROTOTYPE(void lapic_restart_timer, (void));
_PROTOTYPE(void ioapic_set_irq, (unsigned irq));
_PROTOTYPE(void ioapic_unset_irq, (unsigned irq));
/* signal the end of interrupt handler to apic */
#define apic_eoi() do { *((volatile u32_t *) lapic_eoi_addr) = 0; } while(0)
_PROTOTYPE(void ioapic_eoi, (int irq));
_PROTOTYPE(void dump_apic_irq_state, (void));
@ -166,8 +169,6 @@ _PROTOTYPE(void apic_send_ipi, (unsigned vector, unsigned cpu, int type));
_PROTOTYPE(void apic_ipi_sched_intr, (void));
_PROTOTYPE(void apic_ipi_halt_intr, (void));
_PROTOTYPE(void apic_ipi_sched_handler, (void));
#define APIC_IPI_DEST 0
#define APIC_IPI_SELF 1
#define APIC_IPI_TO_ALL 2

View file

@ -70,7 +70,7 @@ ENTRY(lapic_timer_int_handler)
#ifdef CONFIG_SMP
ENTRY(apic_ipi_sched_intr)
lapic_intr(_C_LABEL(apic_ipi_sched_handler))
lapic_intr(_C_LABEL(smp_ipi_sched_handler))
ENTRY(apic_ipi_halt_intr)
lapic_intr(_C_LABEL(smp_ipi_halt_handler))

View file

@ -8,6 +8,7 @@
#include "kernel/clock.h"
#include "kernel/proc.h"
#include "kernel/interrupt.h"
#include <minix/u64.h>
#include "glo.h"
@ -144,6 +145,7 @@ PUBLIC void arch_stop_local_timer(void)
#ifdef CONFIG_APIC
if (lapic_addr) {
lapic_stop_timer();
apic_eoi();
} else
#endif
{
@ -151,6 +153,18 @@ PUBLIC void arch_stop_local_timer(void)
}
}
PUBLIC void arch_restart_local_timer(void)
{
#ifdef CONFIG_APIC
if (lapic_addr) {
lapic_restart_timer();
} else
#endif
{
init_8253A_timer(system_hz);
}
}
PUBLIC int arch_register_local_timer_handler(const irq_handler_t handler)
{
#ifdef CONFIG_APIC
@ -243,7 +257,16 @@ PUBLIC void context_stop(struct proc * p)
PUBLIC void context_stop_idle(void)
{
context_stop(proc_addr(IDLE));
int is_idle;
unsigned cpu = cpuid;
is_idle = get_cpu_var(cpu, cpu_is_idle);
get_cpu_var(cpu, cpu_is_idle) = 0;
context_stop(get_cpulocal_var_ptr(idle_proc));
if (is_idle)
arch_restart_local_timer();
}
PUBLIC u64_t ms_2_cpu_time(unsigned ms)

View file

@ -353,3 +353,8 @@ PUBLIC void arch_smp_halt_cpu(void)
BKL_UNLOCK();
for(;;);
}
PUBLIC void arch_send_smp_schedule_ipi(unsigned cpu)
{
apic_send_ipi(APIC_SMP_SCHED_PROC_VECTOR, cpu, APIC_IPI_DEST);
}

View file

@ -33,6 +33,9 @@ _PROTOTYPE(void i8259_disable,(void));
ioapic_reset_pic(); \
lapic_disable(); \
} while (0)
#ifdef CONFIG_SMP
#define ipi_ack apic_eoi
#endif
#else
/* legacy PIC */

View file

@ -10,7 +10,10 @@ _PROTOTYPE(int app_cpu_init_timer, (unsigned freq));
_PROTOTYPE(int timer_int_handler, (void));
_PROTOTYPE(int arch_init_local_timer, (unsigned freq));
/* sto p the local timer ticking */
_PROTOTYPE(void arch_stop_local_timer, (void));
/* let the time tick again with the original settings after it was stopped */
_PROTOTYPE(void arch_restart_local_timer, (void));
_PROTOTYPE(int arch_register_local_timer_handler, (irq_handler_t handler));
_PROTOTYPE( u64_t ms_2_cpu_time, (unsigned ms));

View file

@ -74,6 +74,7 @@ DECLARE_CPULOCAL(struct proc *, ptproc);
/* CPU private run queues */
DECLARE_CPULOCAL(struct proc *, run_q_head[NR_SCHED_QUEUES]); /* ptrs to ready list headers */
DECLARE_CPULOCAL(struct proc *, run_q_tail[NR_SCHED_QUEUES]); /* ptrs to ready list tails */
DECLARE_CPULOCAL(int, cpu_is_idle); /* let the others know that you are idle */
DECLARE_CPULOCAL(u64_t ,tsc_ctr_switch); /* when did we switched time accounting */

6
kernel/interrupt.h Normal file
View file

@ -0,0 +1,6 @@
#ifndef __INTERRUPT_H__
#define __INTERRUPT_H__
#include "hw_intr.h"
#endif /* __INTERRUPT_H__ */

View file

@ -149,6 +149,7 @@ PUBLIC void proc_init(void)
/* initialize IDLE structures for every CPU */
for (i = 0; i < CONFIG_MAX_CPUS; i++) {
struct proc * ip = get_cpu_var_ptr(i, idle_proc);
ip->p_endpoint = IDLE;
ip->p_priv = &idle_priv;
/* must not let idle ever get scheduled */
ip->p_rts_flags |= RTS_PROC_STOP;
@ -187,6 +188,11 @@ PRIVATE void idle(void)
switch_address_space_idle();
/* we don't need to keep time on APs as it is handled on the BSP */
if (cpuid != bsp_cpu_id)
arch_stop_local_timer();
get_cpulocal_var(cpu_is_idle) = 1;
/* start accounting for the idle time */
context_stop(proc_addr(KERNEL));
halt_cpu();
@ -1277,7 +1283,6 @@ PUBLIC void enqueue(
* process is assigned to.
*/
int q = rp->p_priority; /* scheduling queue to use */
struct proc * p;
struct proc **rdy_head, **rdy_tail;
assert(proc_is_runnable(rp));
@ -1298,16 +1303,29 @@ PUBLIC void enqueue(
rp->p_nextready = NULL; /* mark new end */
}
if (cpuid == rp->p_cpu) {
/*
* enqueueing a process with a higher priority than the current one, it gets
* preempted. The current process must be preemptible. Testing the priority
* also makes sure that a process does not preempt itself
* enqueueing a process with a higher priority than the current one,
* it gets preempted. The current process must be preemptible. Testing
* the priority also makes sure that a process does not preempt itself
*/
struct proc * p;
p = get_cpulocal_var(proc_ptr);
assert(p);
if((p->p_priority > rp->p_priority) &&
(priv(p)->s_flags & PREEMPTIBLE))
RTS_SET(p, RTS_PREEMPTED); /* calls dequeue() */
}
#ifdef CONFIG_SMP
/*
* if the process was enqueued on a different cpu and the cpu is idle, i.e.
* the time is off, we need to wake up that cpu and let it schedule this new
* process
*/
else if (get_cpu_var(rp->p_cpu, cpu_is_idle)) {
smp_schedule(rp->p_cpu);
}
#endif
#if DEBUG_SANITYCHECKS
assert(runqueues_ok_local());

View file

@ -1,4 +1,5 @@
#include "smp.h"
#include "interrupt.h"
unsigned ncpus;
unsigned ht_per_core;
@ -28,7 +29,25 @@ PUBLIC void ap_boot_finished(unsigned cpu)
PUBLIC void smp_ipi_halt_handler(void)
{
ipi_ack();
arch_stop_local_timer();
arch_smp_halt_cpu();
}
PUBLIC void smp_schedule(unsigned cpu)
{
arch_send_smp_schedule_ipi(cpu);
}
PUBLIC void smp_ipi_sched_handler(void)
{
struct proc * p;
ipi_ack();
p = get_cpulocal_var(proc_ptr);
if (p->p_endpoint != IDLE)
RTS_SET(p, RTS_PREEMPTED); /* calls dequeue() */
}

View file

@ -53,7 +53,14 @@ SPINLOCK_DECLARE(boot_lock)
_PROTOTYPE(void wait_for_APs_to_finish_booting, (void));
_PROTOTYPE(void ap_boot_finished, (unsigned cpu));
/* IPI handlers */
_PROTOTYPE(void smp_ipi_halt_handler, (void));
_PROTOTYPE(void smp_ipi_sched_handler, (void));
_PROTOTYPE(void smp_schedule, (unsigned cpu));
_PROTOTYPE(void arch_send_smp_schedule_ipi, (unsigned cpu));
_PROTOTYPE(void arch_smp_halt_cpu, (void));
#endif /* __ASSEMBLY__ */