summaryrefslogtreecommitdiffstats
path: root/sys/amd64/amd64
diff options
context:
space:
mode:
Diffstat (limited to 'sys/amd64/amd64')
-rw-r--r--sys/amd64/amd64/apic_vector.S22
-rw-r--r--sys/amd64/amd64/cpu_switch.S190
-rw-r--r--sys/amd64/amd64/machdep.c36
-rw-r--r--sys/amd64/amd64/mp_machdep.c9
-rw-r--r--sys/amd64/amd64/mptable.c9
-rw-r--r--sys/amd64/amd64/pmap.c129
-rw-r--r--sys/amd64/amd64/swtch.s190
7 files changed, 423 insertions, 162 deletions
diff --git a/sys/amd64/amd64/apic_vector.S b/sys/amd64/amd64/apic_vector.S
index e4b6ea5..d91ff1c 100644
--- a/sys/amd64/amd64/apic_vector.S
+++ b/sys/amd64/amd64/apic_vector.S
@@ -3,6 +3,7 @@
* $FreeBSD$
*/
+#include "opt_swtch.h"
#include <machine/apic.h>
#include <machine/smp.h>
@@ -648,7 +649,28 @@ Xrendezvous:
POP_FRAME
iret
+#ifdef LAZY_SWITCH
+/*
+ * Clean up when we lose out on the lazy context switch optimization.
+ * ie: when we are about to release a PTD but a cpu is still borrowing it.
+ */
+ SUPERALIGN_TEXT
+ .globl Xlazypmap
+Xlazypmap:
+ PUSH_FRAME
+ movl $KDSEL, %eax
+ mov %ax, %ds /* use KERNEL data segment */
+ mov %ax, %es
+ movl $KPSEL, %eax
+ mov %ax, %fs
+
+ call pmap_lazyfix_action
+ movl $0, lapic+LA_EOI /* End Of Interrupt to APIC */
+ POP_FRAME
+ iret
+#endif
+
.data
.globl apic_pin_trigger
diff --git a/sys/amd64/amd64/cpu_switch.S b/sys/amd64/amd64/cpu_switch.S
index aaf0ec2..793e634 100644
--- a/sys/amd64/amd64/cpu_switch.S
+++ b/sys/amd64/amd64/cpu_switch.S
@@ -37,30 +37,16 @@
*/
#include "opt_npx.h"
+#include "opt_swtch.h"
#include <machine/asmacros.h>
-#ifdef SMP
-#include <machine/apic.h>
-#include <machine/smptests.h> /* CHEAP_TPR, GRAB_LOPRIO */
-#endif
-
#include "assym.s"
/*****************************************************************************/
/* Scheduling */
/*****************************************************************************/
- .data
-
- .globl panic
-
-#ifdef SWTCH_OPTIM_STATS
- .globl swtch_optim_stats, tlb_flush_count
-swtch_optim_stats: .long 0 /* number of _swtch_optims */
-tlb_flush_count: .long 0
-#endif
-
.text
/*
@@ -68,30 +54,60 @@ tlb_flush_count: .long 0
*
* This is the second half of cpu_swtch(). It is used when the current
* thread is either a dummy or slated to die, and we no longer care
- * about its state.
+ * about its state. This is only a slight optimization and is probably
+ * not worth it anymore. Note that we need to clear the pm_active bits so
+ * we do need the old proc if it still exists.
+ * 0(%esp) = ret
+ * 4(%esp) = oldtd
+ * 8(%esp) = newtd
*/
ENTRY(cpu_throw)
+ movl PCPU(CPUID), %esi
+ movl 4(%esp),%ecx /* Old thread */
+ testl %ecx,%ecx /* no thread? */
+ jz 1f
+ /* release bit from old pm_active */
+ movl TD_PROC(%ecx), %eax /* thread->td_proc */
+ movl P_VMSPACE(%eax), %ebx /* proc->p_vmspace */
+#ifdef SMP
+ lock
+#endif
+ btrl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* clear old */
+1:
+ movl 8(%esp),%ecx /* New thread */
+ movl TD_PCB(%ecx),%edx
+#ifdef SWTCH_OPTIM_STATS
+ incl tlb_flush_count
+#endif
+ movl PCB_CR3(%edx),%eax
+ movl %eax,%cr3 /* new address space */
+ /* set bit in new pm_active */
+ movl TD_PROC(%ecx),%eax
+ movl P_VMSPACE(%eax), %ebx
+#ifdef SMP
+ lock
+#endif
+ btsl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* set new */
jmp sw1
/*
- * cpu_switch()
+ * cpu_switch(old, new)
*
* Save the current thread state, then select the next thread to run
* and load its state.
+ * 0(%esp) = ret
+ * 4(%esp) = oldtd
+ * 8(%esp) = newtd
*/
ENTRY(cpu_switch)
- /* Switch to new thread. First, save context as needed. */
- movl PCPU(CURTHREAD),%ecx
-
- /* If no thread to save, don't save it (XXX shouldn't happen). */
- testl %ecx,%ecx
- jz sw1
+ /* Switch to new thread. First, save context. */
+ movl 4(%esp),%ecx
- movl TD_PROC(%ecx), %eax
- movl P_VMSPACE(%eax), %edx
- movl PCPU(CPUID), %eax
- btrl %eax, VM_PMAP+PM_ACTIVE(%edx)
+#ifdef INVARIANTS
+ testl %ecx,%ecx /* no thread? */
+ jz badsw2 /* no, panic */
+#endif
movl TD_PCB(%ecx),%edx
@@ -125,10 +141,6 @@ ENTRY(cpu_switch)
movl %eax,PCB_DR0(%edx)
1:
-#ifdef SMP
- /* XXX FIXME: we should be saving the local APIC TPR */
-#endif
-
#ifdef DEV_NPX
/* have we used fp, and need a save? */
cmpl %ecx,PCPU(FPCURTHREAD)
@@ -140,56 +152,76 @@ ENTRY(cpu_switch)
1:
#endif
- /* Save is done. Now choose a new thread. */
- /* XXX still trashing space above the old "Top Of Stack". */
-sw1:
+ /* Save is done. Now fire up new thread. Leave old vmspace. */
+ movl %ecx,%edi
+ movl 8(%esp),%ecx /* New thread */
+#ifdef INVARIANTS
+ testl %ecx,%ecx /* no thread? */
+ jz badsw3 /* no, panic */
+#endif
+ movl TD_PCB(%ecx),%edx
+ movl PCPU(CPUID), %esi
-#ifdef SMP
- /*
- * Stop scheduling if smp_active has become zero (for rebooting) and
- * we are not the BSP.
- */
- cmpl $0,smp_active
- jne 1f
- cmpl $0,PCPU(CPUID)
+ /* switch address space */
+ movl PCB_CR3(%edx),%eax
+#ifdef LAZY_SWITCH
+ cmpl $0,lazy_flush_enable
je 1f
- movl PCPU(IDLETHREAD), %eax
- jmp sw1b
+ cmpl %eax,IdlePTD /* Kernel address space? */
+#ifdef SWTCH_OPTIM_STATS
+ je 3f
+#else
+ je sw1
+#endif
1:
+ movl %cr3,%ebx /* The same address space? */
+ cmpl %ebx,%eax
+#ifdef SWTCH_OPTIM_STATS
+ je 2f /* Yes, skip all that cruft */
+#else
+ je sw1
+#endif
#endif
- /*
- * Choose a new thread to schedule. choosethread() returns idlethread
- * if it cannot find another thread to run.
- */
- call choosethread /* Trash ecx, edx; ret eax. */
+#ifdef SWTCH_OPTIM_STATS
+ incl tlb_flush_count
+#endif
+ movl %eax,%cr3 /* new address space */
-#ifdef INVARIANTS
- testl %eax,%eax /* no thread? */
- jz badsw3 /* no, panic */
+ /* Release bit from old pmap->pm_active */
+ movl TD_PROC(%edi), %eax /* oldproc */
+ movl P_VMSPACE(%eax), %ebx
+#ifdef SMP
+ lock
#endif
+ btrl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* clear old */
-sw1b:
- movl %eax,%ecx
- movl TD_PCB(%ecx),%edx
+ /* Set bit in new pmap->pm_active */
+ movl TD_PROC(%ecx),%eax /* newproc */
+ movl P_VMSPACE(%eax), %ebx
+#ifdef SMP
+ lock
+#endif
+ btsl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* set new */
+#ifdef LAZY_SWITCH
#ifdef SWTCH_OPTIM_STATS
+ jmp sw1
+
+2: /* same address space */
incl swtch_optim_stats
-#endif
+ jmp sw1
- /* switch address space */
- movl %cr3,%ebx /* The same address space? */
- cmpl PCB_CR3(%edx),%ebx
- je 4f /* Yes, skip all that cruft */
-#ifdef SWTCH_OPTIM_STATS
- decl swtch_optim_stats
- incl tlb_flush_count
+3: /* kernel address space */
+ incl lazy_flush_count
+#endif
#endif
- movl PCB_CR3(%edx),%ebx /* Tell the CPU about the */
- movl %ebx,%cr3 /* new address space */
-4:
- movl PCPU(CPUID), %esi
+sw1:
+ /*
+ * At this point, we've switched address spaces and are ready
+ * to load up the rest of the next context.
+ */
cmpl $0, PCB_EXT(%edx) /* has pcb extension? */
je 1f /* If not, use the default */
btsl %esi, private_tss /* mark use of private tss */
@@ -221,11 +253,6 @@ sw1b:
movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */
ltr %si
3:
- /* Note in vmspace that this cpu is using it. */
- movl TD_PROC(%ecx),%eax
- movl P_VMSPACE(%eax), %ebx
- movl PCPU(CPUID), %eax
- btsl %eax, VM_PMAP+PM_ACTIVE(%ebx)
/* Restore context. */
movl PCB_EBX(%edx),%ebx
@@ -241,10 +268,6 @@ sw1b:
movl %edx, PCPU(CURPCB)
movl %ecx, PCPU(CURTHREAD) /* into next thread */
-#ifdef SMP
- /* XXX FIXME: we should be restoring the local APIC TPR */
-#endif
-
/*
* Determine the LDT to use and load it if is the default one and
* that is not the current one.
@@ -301,12 +324,23 @@ cpu_switch_load_gs:
ret
#ifdef INVARIANTS
+badsw1:
+ pushal
+ pushl $sw0_1
+ call panic
+sw0_1: .asciz "cpu_throw: no newthread supplied"
+
+badsw2:
+ pushal
+ pushl $sw0_2
+ call panic
+sw0_2: .asciz "cpu_switch: no curthread supplied"
+
badsw3:
pushal
pushl $sw0_3
call panic
-
-sw0_3: .asciz "cpu_switch: choosethread returned NULL"
+sw0_3: .asciz "cpu_switch: no newthread supplied"
#endif
/*
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index b389828..baab5cb 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
@@ -49,6 +49,7 @@
#include "opt_msgbuf.h"
#include "opt_npx.h"
#include "opt_perfmon.h"
+#include "opt_swtch.h"
#include "opt_kstack_pages.h"
#include <sys/param.h>
@@ -151,11 +152,40 @@ int _udatasel, _ucodesel;
u_int atdevbase;
#if defined(SWTCH_OPTIM_STATS)
-extern int swtch_optim_stats;
+int stupid_switch;
+SYSCTL_INT(_debug, OID_AUTO, stupid_switch,
+ CTLFLAG_RW, &stupid_switch, 0, "");
+int swtch_optim_stats;
SYSCTL_INT(_debug, OID_AUTO, swtch_optim_stats,
- CTLFLAG_RD, &swtch_optim_stats, 0, "");
+ CTLFLAG_RW, &swtch_optim_stats, 0, "");
+int tlb_flush_count;
SYSCTL_INT(_debug, OID_AUTO, tlb_flush_count,
- CTLFLAG_RD, &tlb_flush_count, 0, "");
+ CTLFLAG_RW, &tlb_flush_count, 0, "");
+int lazy_flush_count;
+SYSCTL_INT(_debug, OID_AUTO, lazy_flush_count,
+ CTLFLAG_RW, &lazy_flush_count, 0, "");
+int lazy_flush_fixup;
+SYSCTL_INT(_debug, OID_AUTO, lazy_flush_fixup,
+ CTLFLAG_RW, &lazy_flush_fixup, 0, "");
+#ifdef SMP
+int lazy_flush_smpfixup;
+SYSCTL_INT(_debug, OID_AUTO, lazy_flush_smpfixup,
+ CTLFLAG_RW, &lazy_flush_smpfixup, 0, "");
+int lazy_flush_smpipi;
+SYSCTL_INT(_debug, OID_AUTO, lazy_flush_smpipi,
+ CTLFLAG_RW, &lazy_flush_smpipi, 0, "");
+int lazy_flush_smpbadcr3;
+SYSCTL_INT(_debug, OID_AUTO, lazy_flush_smpbadcr3,
+ CTLFLAG_RW, &lazy_flush_smpbadcr3, 0, "");
+int lazy_flush_smpmiss;
+SYSCTL_INT(_debug, OID_AUTO, lazy_flush_smpmiss,
+ CTLFLAG_RW, &lazy_flush_smpmiss, 0, "");
+#endif
+#endif
+#ifdef LAZY_SWITCH
+int lazy_flush_enable = 1;
+SYSCTL_INT(_debug, OID_AUTO, lazy_flush_enable,
+ CTLFLAG_RW, &lazy_flush_enable, 0, "");
#endif
int cold = 1;
diff --git a/sys/amd64/amd64/mp_machdep.c b/sys/amd64/amd64/mp_machdep.c
index 954a7fd..4cc8bf5 100644
--- a/sys/amd64/amd64/mp_machdep.c
+++ b/sys/amd64/amd64/mp_machdep.c
@@ -27,6 +27,7 @@
#include "opt_cpu.h"
#include "opt_kstack_pages.h"
+#include "opt_swtch.h"
#ifdef SMP
#include <machine/smptests.h>
@@ -634,6 +635,12 @@ mp_enable(u_int boot_addr)
setidt(XSTATCLOCK_OFFSET, Xstatclock,
SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+#ifdef LAZY_SWITCH
+ /* install an inter-CPU IPI for lazy pmap release */
+ setidt(XLAZYPMAP_OFFSET, Xlazypmap,
+ SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+#endif
+
/* install an inter-CPU IPI for all-CPU rendezvous */
setidt(XRENDEZVOUS_OFFSET, Xrendezvous,
SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
@@ -2598,7 +2605,7 @@ ap_init(void)
binuptime(PCPU_PTR(switchtime));
PCPU_SET(switchticks, ticks);
- cpu_throw(); /* doesn't return */
+ cpu_throw(NULL, choosethread()); /* doesn't return */
panic("scheduler returned us to %s", __func__);
}
diff --git a/sys/amd64/amd64/mptable.c b/sys/amd64/amd64/mptable.c
index 954a7fd..4cc8bf5 100644
--- a/sys/amd64/amd64/mptable.c
+++ b/sys/amd64/amd64/mptable.c
@@ -27,6 +27,7 @@
#include "opt_cpu.h"
#include "opt_kstack_pages.h"
+#include "opt_swtch.h"
#ifdef SMP
#include <machine/smptests.h>
@@ -634,6 +635,12 @@ mp_enable(u_int boot_addr)
setidt(XSTATCLOCK_OFFSET, Xstatclock,
SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+#ifdef LAZY_SWITCH
+ /* install an inter-CPU IPI for lazy pmap release */
+ setidt(XLAZYPMAP_OFFSET, Xlazypmap,
+ SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
+#endif
+
/* install an inter-CPU IPI for all-CPU rendezvous */
setidt(XRENDEZVOUS_OFFSET, Xrendezvous,
SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
@@ -2598,7 +2605,7 @@ ap_init(void)
binuptime(PCPU_PTR(switchtime));
PCPU_SET(switchticks, ticks);
- cpu_throw(); /* doesn't return */
+ cpu_throw(NULL, choosethread()); /* doesn't return */
panic("scheduler returned us to %s", __func__);
}
diff --git a/sys/amd64/amd64/pmap.c b/sys/amd64/amd64/pmap.c
index 76b45b4..77c53e0 100644
--- a/sys/amd64/amd64/pmap.c
+++ b/sys/amd64/amd64/pmap.c
@@ -102,6 +102,7 @@
#include "opt_pmap.h"
#include "opt_msgbuf.h"
#include "opt_kstack_pages.h"
+#include "opt_swtch.h"
#include <sys/param.h>
#include <sys/systm.h>
@@ -184,6 +185,9 @@ struct pmap kernel_pmap_store;
LIST_HEAD(pmaplist, pmap);
static struct pmaplist allpmaps;
static struct mtx allpmaps_lock;
+#if defined(SMP) && defined(LAZY_SWITCH)
+static struct mtx lazypmap_lock;
+#endif
vm_paddr_t avail_start; /* PA of first available physical page */
vm_paddr_t avail_end; /* PA of last available physical page */
@@ -336,6 +340,9 @@ pmap_bootstrap(firstaddr, loadaddr)
kernel_pmap->pm_active = -1; /* don't allow deactivation */
TAILQ_INIT(&kernel_pmap->pm_pvlist);
LIST_INIT(&allpmaps);
+#if defined(SMP) && defined(LAZY_SWITCH)
+ mtx_init(&lazypmap_lock, "lazypmap", NULL, MTX_SPIN);
+#endif
mtx_init(&allpmaps_lock, "allpmaps", NULL, MTX_SPIN);
mtx_lock_spin(&allpmaps_lock);
LIST_INSERT_HEAD(&allpmaps, kernel_pmap, pm_list);
@@ -1486,6 +1493,121 @@ pmap_allocpte(pmap_t pmap, vm_offset_t va)
* Pmap allocation/deallocation routines.
***************************************************/
+#ifdef LAZY_SWITCH
+#ifdef SMP
+/*
+ * Deal with a SMP shootdown of other users of the pmap that we are
+ * trying to dispose of. This can be a bit hairy.
+ */
+static u_int *lazymask;
+static u_int lazyptd;
+static volatile u_int lazywait;
+
+void pmap_lazyfix_action(void);
+
+void
+pmap_lazyfix_action(void)
+{
+ u_int mymask = PCPU_GET(cpumask);
+
+ if (rcr3() == lazyptd) {
+ load_cr3(PCPU_GET(curpcb)->pcb_cr3);
+#ifdef SWTCH_OPTIM_STATS
+ atomic_add_int(&lazy_flush_smpfixup, 1);
+ } else {
+ if (*lazymask & mymask)
+ lazy_flush_smpbadcr3++;
+ else
+ lazy_flush_smpmiss++;
+#endif
+ }
+ atomic_clear_int(lazymask, mymask);
+ atomic_store_rel_int(&lazywait, 1);
+}
+
+static void
+pmap_lazyfix_self(u_int mymask)
+{
+
+ if (rcr3() == lazyptd) {
+ load_cr3(PCPU_GET(curpcb)->pcb_cr3);
+#ifdef SWTCH_OPTIM_STATS
+ lazy_flush_fixup++;
+ } else {
+ if (*lazymask & mymask)
+ lazy_flush_smpbadcr3++;
+ else
+ lazy_flush_smpmiss++;
+#endif
+ }
+ atomic_clear_int(lazymask, mymask);
+}
+
+
+static void
+pmap_lazyfix(pmap_t pmap)
+{
+ u_int mymask = PCPU_GET(cpumask);
+ u_int mask;
+ register u_int spins;
+
+ while ((mask = pmap->pm_active) != 0) {
+ spins = 50000000;
+ mask = mask & -mask; /* Find least significant set bit */
+ mtx_lock_spin(&lazypmap_lock);
+#ifdef PAE
+ lazyptd = vtophys(pmap->pm_pdpt);
+#else
+ lazyptd = vtophys(pmap->pm_pdir);
+#endif
+ if (mask == mymask) {
+ lazymask = &pmap->pm_active;
+ pmap_lazyfix_self(mymask);
+ } else {
+ atomic_store_rel_int((u_int *)&lazymask,
+ (u_int)&pmap->pm_active);
+ atomic_store_rel_int(&lazywait, 0);
+ ipi_selected(mask, IPI_LAZYPMAP);
+ while (lazywait == 0) {
+ ia32_pause();
+ if (--spins == 0)
+ break;
+ }
+#ifdef SWTCH_OPTIM_STATS
+ lazy_flush_smpipi++;
+#endif
+ }
+ mtx_unlock_spin(&lazypmap_lock);
+ if (spins == 0)
+ printf("pmap_lazyfix: spun for 50000000\n");
+ }
+}
+
+#else /* SMP */
+
+/*
+ * Cleaning up on uniprocessor is easy. For various reasons, we're
+ * unlikely to have to even execute this code, including the fact
+ * that the cleanup is deferred until the parent does a wait(2), which
+ * means that another userland process has run.
+ */
+static void
+pmap_lazyfix(pmap_t pmap)
+{
+ u_int cr3;
+
+ cr3 = vtophys(pmap->pm_pdir);
+ if (cr3 == rcr3()) {
+ load_cr3(PCPU_GET(curpcb)->pcb_cr3);
+ pmap->pm_active &= ~(PCPU_GET(cpumask));
+#ifdef SWTCH_OPTIM_STATS
+ lazy_flush_fixup++;
+#endif
+ }
+}
+#endif /* SMP */
+#endif /* LAZY_SWITCH */
+
/*
* Release any resources held by the given physical map.
* Called when a pmap initialized by pmap_pinit is being released.
@@ -1507,6 +1629,9 @@ pmap_release(pmap_t pmap)
("pmap_release: pmap resident count %ld != 0",
pmap->pm_stats.resident_count));
+#ifdef LAZY_SWITCH
+ pmap_lazyfix(pmap);
+#endif
mtx_lock_spin(&allpmaps_lock);
LIST_REMOVE(pmap, pm_list);
mtx_unlock_spin(&allpmaps_lock);
@@ -3321,9 +3446,10 @@ pmap_activate(struct thread *td)
pmap_t pmap;
u_int32_t cr3;
+ critical_enter();
pmap = vmspace_pmap(td->td_proc->p_vmspace);
#if defined(SMP)
- pmap->pm_active |= PCPU_GET(cpumask);
+ atomic_set_int(&pmap->pm_active, PCPU_GET(cpumask));
#else
pmap->pm_active |= 1;
#endif
@@ -3348,6 +3474,7 @@ pmap_activate(struct thread *td)
#ifdef SWTCH_OPTIM_STATS
tlb_flush_count++;
#endif
+ critical_exit();
}
vm_offset_t
diff --git a/sys/amd64/amd64/swtch.s b/sys/amd64/amd64/swtch.s
index aaf0ec2..793e634 100644
--- a/sys/amd64/amd64/swtch.s
+++ b/sys/amd64/amd64/swtch.s
@@ -37,30 +37,16 @@
*/
#include "opt_npx.h"
+#include "opt_swtch.h"
#include <machine/asmacros.h>
-#ifdef SMP
-#include <machine/apic.h>
-#include <machine/smptests.h> /* CHEAP_TPR, GRAB_LOPRIO */
-#endif
-
#include "assym.s"
/*****************************************************************************/
/* Scheduling */
/*****************************************************************************/
- .data
-
- .globl panic
-
-#ifdef SWTCH_OPTIM_STATS
- .globl swtch_optim_stats, tlb_flush_count
-swtch_optim_stats: .long 0 /* number of _swtch_optims */
-tlb_flush_count: .long 0
-#endif
-
.text
/*
@@ -68,30 +54,60 @@ tlb_flush_count: .long 0
*
* This is the second half of cpu_swtch(). It is used when the current
* thread is either a dummy or slated to die, and we no longer care
- * about its state.
+ * about its state. This is only a slight optimization and is probably
+ * not worth it anymore. Note that we need to clear the pm_active bits so
+ * we do need the old proc if it still exists.
+ * 0(%esp) = ret
+ * 4(%esp) = oldtd
+ * 8(%esp) = newtd
*/
ENTRY(cpu_throw)
+ movl PCPU(CPUID), %esi
+ movl 4(%esp),%ecx /* Old thread */
+ testl %ecx,%ecx /* no thread? */
+ jz 1f
+ /* release bit from old pm_active */
+ movl TD_PROC(%ecx), %eax /* thread->td_proc */
+ movl P_VMSPACE(%eax), %ebx /* proc->p_vmspace */
+#ifdef SMP
+ lock
+#endif
+ btrl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* clear old */
+1:
+ movl 8(%esp),%ecx /* New thread */
+ movl TD_PCB(%ecx),%edx
+#ifdef SWTCH_OPTIM_STATS
+ incl tlb_flush_count
+#endif
+ movl PCB_CR3(%edx),%eax
+ movl %eax,%cr3 /* new address space */
+ /* set bit in new pm_active */
+ movl TD_PROC(%ecx),%eax
+ movl P_VMSPACE(%eax), %ebx
+#ifdef SMP
+ lock
+#endif
+ btsl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* set new */
jmp sw1
/*
- * cpu_switch()
+ * cpu_switch(old, new)
*
* Save the current thread state, then select the next thread to run
* and load its state.
+ * 0(%esp) = ret
+ * 4(%esp) = oldtd
+ * 8(%esp) = newtd
*/
ENTRY(cpu_switch)
- /* Switch to new thread. First, save context as needed. */
- movl PCPU(CURTHREAD),%ecx
-
- /* If no thread to save, don't save it (XXX shouldn't happen). */
- testl %ecx,%ecx
- jz sw1
+ /* Switch to new thread. First, save context. */
+ movl 4(%esp),%ecx
- movl TD_PROC(%ecx), %eax
- movl P_VMSPACE(%eax), %edx
- movl PCPU(CPUID), %eax
- btrl %eax, VM_PMAP+PM_ACTIVE(%edx)
+#ifdef INVARIANTS
+ testl %ecx,%ecx /* no thread? */
+ jz badsw2 /* no, panic */
+#endif
movl TD_PCB(%ecx),%edx
@@ -125,10 +141,6 @@ ENTRY(cpu_switch)
movl %eax,PCB_DR0(%edx)
1:
-#ifdef SMP
- /* XXX FIXME: we should be saving the local APIC TPR */
-#endif
-
#ifdef DEV_NPX
/* have we used fp, and need a save? */
cmpl %ecx,PCPU(FPCURTHREAD)
@@ -140,56 +152,76 @@ ENTRY(cpu_switch)
1:
#endif
- /* Save is done. Now choose a new thread. */
- /* XXX still trashing space above the old "Top Of Stack". */
-sw1:
+ /* Save is done. Now fire up new thread. Leave old vmspace. */
+ movl %ecx,%edi
+ movl 8(%esp),%ecx /* New thread */
+#ifdef INVARIANTS
+ testl %ecx,%ecx /* no thread? */
+ jz badsw3 /* no, panic */
+#endif
+ movl TD_PCB(%ecx),%edx
+ movl PCPU(CPUID), %esi
-#ifdef SMP
- /*
- * Stop scheduling if smp_active has become zero (for rebooting) and
- * we are not the BSP.
- */
- cmpl $0,smp_active
- jne 1f
- cmpl $0,PCPU(CPUID)
+ /* switch address space */
+ movl PCB_CR3(%edx),%eax
+#ifdef LAZY_SWITCH
+ cmpl $0,lazy_flush_enable
je 1f
- movl PCPU(IDLETHREAD), %eax
- jmp sw1b
+ cmpl %eax,IdlePTD /* Kernel address space? */
+#ifdef SWTCH_OPTIM_STATS
+ je 3f
+#else
+ je sw1
+#endif
1:
+ movl %cr3,%ebx /* The same address space? */
+ cmpl %ebx,%eax
+#ifdef SWTCH_OPTIM_STATS
+ je 2f /* Yes, skip all that cruft */
+#else
+ je sw1
+#endif
#endif
- /*
- * Choose a new thread to schedule. choosethread() returns idlethread
- * if it cannot find another thread to run.
- */
- call choosethread /* Trash ecx, edx; ret eax. */
+#ifdef SWTCH_OPTIM_STATS
+ incl tlb_flush_count
+#endif
+ movl %eax,%cr3 /* new address space */
-#ifdef INVARIANTS
- testl %eax,%eax /* no thread? */
- jz badsw3 /* no, panic */
+ /* Release bit from old pmap->pm_active */
+ movl TD_PROC(%edi), %eax /* oldproc */
+ movl P_VMSPACE(%eax), %ebx
+#ifdef SMP
+ lock
#endif
+ btrl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* clear old */
-sw1b:
- movl %eax,%ecx
- movl TD_PCB(%ecx),%edx
+ /* Set bit in new pmap->pm_active */
+ movl TD_PROC(%ecx),%eax /* newproc */
+ movl P_VMSPACE(%eax), %ebx
+#ifdef SMP
+ lock
+#endif
+ btsl %esi, VM_PMAP+PM_ACTIVE(%ebx) /* set new */
+#ifdef LAZY_SWITCH
#ifdef SWTCH_OPTIM_STATS
+ jmp sw1
+
+2: /* same address space */
incl swtch_optim_stats
-#endif
+ jmp sw1
- /* switch address space */
- movl %cr3,%ebx /* The same address space? */
- cmpl PCB_CR3(%edx),%ebx
- je 4f /* Yes, skip all that cruft */
-#ifdef SWTCH_OPTIM_STATS
- decl swtch_optim_stats
- incl tlb_flush_count
+3: /* kernel address space */
+ incl lazy_flush_count
+#endif
#endif
- movl PCB_CR3(%edx),%ebx /* Tell the CPU about the */
- movl %ebx,%cr3 /* new address space */
-4:
- movl PCPU(CPUID), %esi
+sw1:
+ /*
+ * At this point, we've switched address spaces and are ready
+ * to load up the rest of the next context.
+ */
cmpl $0, PCB_EXT(%edx) /* has pcb extension? */
je 1f /* If not, use the default */
btsl %esi, private_tss /* mark use of private tss */
@@ -221,11 +253,6 @@ sw1b:
movl $GPROC0_SEL*8, %esi /* GSEL(entry, SEL_KPL) */
ltr %si
3:
- /* Note in vmspace that this cpu is using it. */
- movl TD_PROC(%ecx),%eax
- movl P_VMSPACE(%eax), %ebx
- movl PCPU(CPUID), %eax
- btsl %eax, VM_PMAP+PM_ACTIVE(%ebx)
/* Restore context. */
movl PCB_EBX(%edx),%ebx
@@ -241,10 +268,6 @@ sw1b:
movl %edx, PCPU(CURPCB)
movl %ecx, PCPU(CURTHREAD) /* into next thread */
-#ifdef SMP
- /* XXX FIXME: we should be restoring the local APIC TPR */
-#endif
-
/*
* Determine the LDT to use and load it if is the default one and
* that is not the current one.
@@ -301,12 +324,23 @@ cpu_switch_load_gs:
ret
#ifdef INVARIANTS
+badsw1:
+ pushal
+ pushl $sw0_1
+ call panic
+sw0_1: .asciz "cpu_throw: no newthread supplied"
+
+badsw2:
+ pushal
+ pushl $sw0_2
+ call panic
+sw0_2: .asciz "cpu_switch: no curthread supplied"
+
badsw3:
pushal
pushl $sw0_3
call panic
-
-sw0_3: .asciz "cpu_switch: choosethread returned NULL"
+sw0_3: .asciz "cpu_switch: no newthread supplied"
#endif
/*
OpenPOWER on IntegriCloud