diff options
Diffstat (limited to 'sys/kern/init_smp.c')
-rw-r--r-- | sys/kern/init_smp.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/sys/kern/init_smp.c b/sys/kern/init_smp.c new file mode 100644 index 0000000..b7ed538c --- /dev/null +++ b/sys/kern/init_smp.c @@ -0,0 +1,298 @@ +/* + * Copyright (c) 1996, Peter Wemm <peter@freebsd.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: init_smp.c,v 1.46 1997/04/25 03:10:41 fsmp Exp $ + */ + +#include "opt_smp.h" +#include "opt_smp_autostart.h" +#include "opt_smp_invltlb.h" + +#include <sys/param.h> +#include <sys/filedesc.h> +#include <sys/kernel.h> +#include <sys/sysctl.h> +#include <sys/proc.h> +#include <sys/resourcevar.h> +#include <sys/signalvar.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/sysent.h> +#include <sys/reboot.h> +#include <sys/sysproto.h> +#include <sys/vmmeter.h> +#include <sys/lock.h> + +#include <machine/cpu.h> +#include <machine/smp.h> +#include <machine/smptests.h> /** IGNORE_IDLEPROCS */ + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/vm_prot.h> +#include <vm/pmap.h> +#include <vm/vm_map.h> +#include <sys/user.h> + + +int smp_active = 0; /* is the secondary allowed to run? */ +SYSCTL_INT(_kern, OID_AUTO, smp_active, CTLFLAG_RW, &smp_active, 0, ""); + +int smp_cpus = 0; /* how many cpu's running */ +SYSCTL_INT(_kern, OID_AUTO, smp_cpus, CTLFLAG_RD, &smp_cpus, 0, ""); + +int idle_debug = 0; +SYSCTL_INT(_kern, OID_AUTO, idle_debug, CTLFLAG_RW, &idle_debug, 0, ""); + +#if defined(SMP_INVLTLB) +int invldebug = 0; /* XXX: see bit field definitions where used */ +SYSCTL_INT(_kern, OID_AUTO, invldebug, CTLFLAG_RW, &invldebug, 0, ""); +#endif + +#if defined(IGNORE_IDLEPROCS) +int ignore_idleprocs = 1; +#else +int ignore_idleprocs = 0; +#endif +SYSCTL_INT(_kern, OID_AUTO, ignore_idleprocs, CTLFLAG_RW, &ignore_idleprocs, + 0, ""); + +static void smp_kickoff __P((void *dummy)); +SYSINIT(smpkick, SI_SUB_SMP, SI_ORDER_FIRST, smp_kickoff, NULL) + +static void smp_idleloop __P((void *)); + +void secondary_main __P((void)); + +static int idle_loops = 0; +void boot_unlock __P((void)); + +struct proc *SMPidleproc[NCPU]; +static int cpu_starting = -1; + +static void +smp_kickoff(dummy) + void *dummy; +{ + int rval[2]; /* return from fork */ + struct proc *p; + int i; + + /* + * Create the appropriate number of cpu-idle-eaters + */ + for (i = 0; i < mp_ncpus; i++) { + /* kernel thread*/ + if (fork(&proc0, NULL, rval)) + panic("cannot fork idle process"); + p = pfind(rval[0]); + cpu_set_fork_handler(p, smp_idleloop, NULL); + SMPidleproc[i] = p; + p->p_flag |= P_INMEM | P_SYSTEM | P_IDLEPROC; + sprintf(p->p_comm, "cpuidle%d", i); + + /* + * PRIO_IDLE is the last scheduled of the three + * classes and we choose the lowest priority possible + * for there. + */ + p->p_rtprio.type = RTP_PRIO_IDLE; + p->p_rtprio.prio = RTP_PRIO_MAX; + } + +} + + +#define MSG_CPU_MADEIT \ + printf("SMP: TADA! CPU #%d made it into the scheduler!.\n", \ + cpunumber()) +#define MSG_NEXT_CPU \ + printf("SMP: %d of %d CPU's online. Unlocking next CPU..\n", \ + smp_cpus, mp_ncpus) +#define MSG_FINAL_CPU \ + printf("SMP: All %d CPU's are online!\n", \ + smp_cpus) +#define MSG_TOOMANY_CPU \ + printf("SMP: Hey! Too many cpu's started, %d of %d running!\n", \ + smp_cpus, mp_ncpus) + +/* + * This is run by the secondary processor to kick things off. + * It basically drops into the switch routine to pick the first + * available process to run, which is probably an idle process. + */ + +void +secondary_main() +{ + get_mplock(); + + /* + * Record our ID so we know when we've released the mp_stk. + * We must remain single threaded through this. + */ + cpu_starting = cpunumber(); + smp_cpus++; + + printf("SMP: AP CPU #%d LAUNCHED!! Starting Scheduling...\n", + cpunumber()); + + curproc = NULL; /* ensure no context to save */ + cpu_switch(curproc); /* start first process */ + panic("switch returned!"); +} + + +/* + * The main program loop for the idle process + */ + +static void +smp_idleloop(dummy) +void *dummy; +{ + int dcnt = 0; + + /* + * This code is executed only on startup of the idleprocs + * The fact that this is executed is an indication that the + * idle procs are online and it's safe to kick off the first + * AP cpu. + */ + if ( ++idle_loops == mp_ncpus ) { + printf("SMP: All idle procs online.\n"); + +#if defined(SMP_AUTOSTART) +#error WARNING: this code is broken, remove this line at your own risk! + printf("SMP: Starting 1st AP!\n"); + smp_cpus = 1; + smp_active = mp_ncpus; /* XXX */ + boot_unlock(); +#endif /* SMP_AUTOSTART */ + } + + spl0(); + rel_mplock(); + + while (1) { + /* + * make the optimiser assume nothing about the + * which*qs variables + */ + __asm __volatile("" : : : "memory"); + +#if !defined(SMP_AUTOSTART) + /* + * Alternate code to enable a lockstep startup + * via sysctl instead of automatically. + */ + if (smp_cpus == 0 && smp_active != 0) { + get_mplock(); + printf("SMP: Starting 1st AP!\n"); + smp_cpus = 1; + smp_active = mp_ncpus; /* XXX */ + boot_unlock(); + rel_mplock(); + } +#endif /* !SMP_AUTOSTART */ + + /* + * If smp_active is set to (say) 1, we want cpu id's + * 1,2,etc to freeze here. + */ + if (smp_active && smp_active <= cpunumber()) { + get_mplock(); + printf("SMP: cpu#%d freezing\n", cpunumber()); + wakeup((caddr_t)&smp_active); + rel_mplock(); + + while (smp_active <= cpunumber()) { + __asm __volatile("" : : : "memory"); + } + get_mplock(); + printf("SMP: cpu#%d waking up!\n", cpunumber()); + rel_mplock(); + } + + if (whichqs || whichrtqs || (!ignore_idleprocs && whichidqs)) { + /* grab lock for kernel "entry" */ + get_mplock(); + + /* We need to retest due to the spin lock */ + __asm __volatile("" : : : "memory"); + + if (whichqs || whichrtqs || + (!ignore_idleprocs && whichidqs)) { + splhigh(); + if (curproc) + setrunqueue(curproc); + cnt.v_swtch++; + cpu_switch(curproc); + microtime(&runtime); + + if (cpu_starting != -1 && + cpu_starting == cpunumber()) { + /* + * TADA! we have arrived! unlock the + * next cpu now that we have released + * the single mp_stk. + */ + MSG_CPU_MADEIT; + cpu_starting = -1; + + if (smp_cpus < mp_ncpus) { + MSG_NEXT_CPU; + boot_unlock(); + } else if (smp_cpus > mp_ncpus) { + MSG_TOOMANY_CPU; + panic("too many cpus"); + } else { + MSG_FINAL_CPU; +#if defined(SMP_INVLTLB) + /* + * It's safe to send IPI's now + * that all CPUs are online. + */ + invldebug = 6; +#endif + } + + /* Init local apic for irq's */ + apic_initialize(0); + } + + (void)spl0(); + } + rel_mplock(); + } else { + dcnt++; + if (idle_debug && (dcnt % idle_debug) == 0) { + get_mplock(); + printf("idleproc pid#%d on cpu#%d, lock %08x\n", + curproc->p_pid, cpunumber(), mp_lock); + rel_mplock(); + } + } + } +} |