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:
parent
387e1835d1
commit
0ac9b6d4cf
12 changed files with 111 additions and 19 deletions
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
/* 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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
6
kernel/interrupt.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef __INTERRUPT_H__
|
||||
#define __INTERRUPT_H__
|
||||
|
||||
#include "hw_intr.h"
|
||||
|
||||
#endif /* __INTERRUPT_H__ */
|
|
@ -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
|
||||
*/
|
||||
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
|
||||
/*
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
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() */
|
||||
else if (get_cpu_var(rp->p_cpu, cpu_is_idle)) {
|
||||
smp_schedule(rp->p_cpu);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DEBUG_SANITYCHECKS
|
||||
assert(runqueues_ok_local());
|
||||
|
|
19
kernel/smp.c
19
kernel/smp.c
|
@ -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() */
|
||||
}
|
||||
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Loading…
Reference in a new issue