MP Implmentation 3/4: MAJOR progress on SMP, full userland MP is now working!
authorMatthew Dillon <dillon@dragonflybsd.org>
Thu, 10 Jul 2003 04:47:55 +0000 (04:47 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Thu, 10 Jul 2003 04:47:55 +0000 (04:47 +0000)
A number of issues relating to MP lock operation have been fixed, primarily
that we have to read %cr2 before get_mplock() since get_mplock() may switch
away.  Idlethreads can now safely HLT without any performance detriment.
The userland scheduler has been almost completely rewritten and is now
using an extremely flexible abstraction with a lot of room to grow.  pgeflag
has been removed from mapdev (without per-page invalidation it isn't safe
to use PG_G even on UP).  Necessary locked bus cycles have been added for
the pmap->pm_active field in swtch.s.  CR3 has been unoptimized for the
moment (see comment in swtch.s).  Since the switch code runs without the
MP lock we have to adjust pm_active PRIOR to loading %cr3.
Additional sanity checks have been added to the code (see PARANOID_INVLTLB
and ONLY_ONE_USER_CPU in the code), plus many more in kern_switch.c.
A passive release mechanism has been implemented to optimize P_CURPROC/lwkt
priority shifting when going from user->kernel and kernel->user.
Note: preemptive interrupts don't care due to the way preemption works so
no additional complexity there.  non-locking atomic functions to protect
only against local interrupts have been added.  astpending now uses
non-locking atomic functions to set and clear bits.  private_tss has been
moved to a per-cpu variable.   The LWKT thread module has been considerably
enhanced and cleaned up, including some fixes to handle MPLOCKED vs td_mpcount
races (so eventually we can do MP locking without a pushfl/cli/popfl combo).
stopevent() needs critical section protection, maybe.

46 files changed:
sys/cpu/i386/include/atomic.h
sys/cpu/i386/include/cpu.h
sys/ddb/db_ps.c
sys/i386/i386/genassym.c
sys/i386/i386/globals.s
sys/i386/i386/machdep.c
sys/i386/i386/mp_machdep.c
sys/i386/i386/mplock.s
sys/i386/i386/pmap.c
sys/i386/i386/swtch.s
sys/i386/i386/trap.c
sys/i386/i386/vm_machdep.c
sys/i386/include/atomic.h
sys/i386/include/cpu.h
sys/i386/include/globaldata.h
sys/i386/include/thread.h
sys/i386/isa/ipl.s
sys/i386/isa/npx.c
sys/kern/init_main.c
sys/kern/kern_clock.c
sys/kern/kern_exit.c
sys/kern/kern_fork.c
sys/kern/kern_malloc.c
sys/kern/kern_switch.c
sys/kern/kern_synch.c
sys/kern/lwkt_thread.c
sys/kern/sys_process.c
sys/kern/uipc_mbuf.c
sys/platform/pc32/i386/genassym.c
sys/platform/pc32/i386/globals.s
sys/platform/pc32/i386/machdep.c
sys/platform/pc32/i386/mp_machdep.c
sys/platform/pc32/i386/mplock.s
sys/platform/pc32/i386/pmap.c
sys/platform/pc32/i386/swtch.s
sys/platform/pc32/i386/trap.c
sys/platform/pc32/i386/vm_machdep.c
sys/platform/pc32/include/globaldata.h
sys/platform/pc32/include/thread.h
sys/platform/pc32/isa/ipl.s
sys/platform/pc32/isa/npx.c
sys/platform/vkernel/i386/genassym.c
sys/sys/globaldata.h
sys/sys/proc.h
sys/sys/thread.h
sys/vm/vm_glue.c

index 6fdc1d6..8d9d949 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/include/atomic.h,v 1.9.2.1 2000/07/07 00:38:47 obrien Exp $
- * $DragonFly: src/sys/cpu/i386/include/atomic.h,v 1.2 2003/06/17 04:28:35 dillon Exp $
+ * $DragonFly: src/sys/cpu/i386/include/atomic.h,v 1.3 2003/07/10 04:47:53 dillon Exp $
  */
 #ifndef _MACHINE_ATOMIC_H_
 #define _MACHINE_ATOMIC_H_
 
 /*
  * The assembly is volatilized to demark potential before-and-after side
- * effects if an interrupt or SMP collision were to occur.
+ * effects if an interrupt or SMP collision were to occur.  The primary
+ * atomic instructions are MP safe, the nonlocked instructions are 
+ * local-interrupt-safe (so we don't depend on C 'X |= Y' generating an
+ * atomic instruction).
  */
 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 9)
+
 /* egcs 1.1.2+ version */
 #define ATOMIC_ASM(NAME, TYPE, OP, V)                  \
 static __inline void                                   \
 atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\
+{                                                      \
+       __asm __volatile(MPLOCKED OP                    \
+                        : "=m" (*p)                    \
+                        :  "0" (*p), "ir" (V));        \
+}                                                      \
+static __inline void                                   \
+atomic_##NAME##_##TYPE##_nonlocked(volatile u_##TYPE *p, u_##TYPE v)\
 {                                                      \
        __asm __volatile(MPLOCKED OP                    \
                         : "=m" (*p)                    \
@@ -89,6 +100,7 @@ atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\
 }
 
 #else
+
 /* gcc <= 2.8 version */
 #define ATOMIC_ASM(NAME, TYPE, OP, V)                  \
 static __inline void                                   \
@@ -97,6 +109,13 @@ atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\
        __asm __volatile(MPLOCKED OP                    \
                         : "=m" (*p)                    \
                         : "ir" (V));                   \
+}                                                      \
+static __inline void                                   \
+atomic_##NAME##_##TYPE##_nonlocked(volatile u_##TYPE *p, u_##TYPE v)\
+{                                                      \
+       __asm __volatile(OP                             \
+                        : "=m" (*p)                    \
+                        : "ir" (V));                   \
 }
 #endif
 #endif /* KLD_MODULE */
index 888abb8..71f1bf4 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     from: @(#)cpu.h 5.4 (Berkeley) 5/9/91
  * $FreeBSD: src/sys/i386/include/cpu.h,v 1.43.2.2 2001/06/15 09:37:57 scottl Exp $
- * $DragonFly: src/sys/cpu/i386/include/cpu.h,v 1.6 2003/06/29 05:29:30 dillon Exp $
+ * $DragonFly: src/sys/cpu/i386/include/cpu.h,v 1.7 2003/07/10 04:47:53 dillon Exp $
  */
 
 #ifndef _MACHINE_CPU_H_
  * Preempt the current process if in interrupt from user mode,
  * or after the current trap/syscall if in system mode.
  *
- * XXX: if astpending is later changed to an |= here due to more flags being
- * added, we will have an atomicy problem.  The type of atomicy we need is
- * a non-locked orl.
+ * We do not have to use a locked bus cycle but we do have to use an
+ * atomic instruction because an interrupt on the local cpu can modify
+ * the field.
  */
-#define        need_resched()          do { mycpu->gd_astpending = AST_RESCHED|AST_PENDING; } while (0)
-#define        clear_resched()         do { mycpu->gd_astpending &= ~AST_RESCHED; } while(0)
+#define        need_resched()          \
+    atomic_set_int_nonlocked(&mycpu->gd_astpending, AST_RESCHED|AST_PENDING)
+#define        clear_resched()         \
+    atomic_clear_int_nonlocked(&mycpu->gd_astpending, AST_RESCHED)
 #define        resched_wanted()        (mycpu->gd_astpending & AST_RESCHED)
 
 /*
index 0d3d815..ca2c3b5 100644 (file)
@@ -31,7 +31,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/ddb/db_ps.c,v 1.20 1999/08/28 00:41:09 peter Exp $
- * $DragonFly: src/sys/ddb/db_ps.c,v 1.6 2003/07/08 06:27:23 dillon Exp $
+ * $DragonFly: src/sys/ddb/db_ps.c,v 1.7 2003/07/10 04:47:49 dillon Exp $
  */
 #include <sys/param.h>
 #include <sys/systm.h>
 
 #include <ddb/ddb.h>
 
+static void
+db_more(int *nl)
+{
+       ++*nl;
+       if (*nl == 20) {
+               int c;
+
+               db_printf("--More--");
+               c = cngetc();
+               db_printf("\r");
+               /*
+                * A whole screenfull or just one line?
+                */
+               switch (c) {
+               case '\n':              /* just one line */
+                       *nl = 19;
+                       break;
+               case ' ':
+                       *nl = 0;        /* another screenfull */
+                       break;
+               default:                /* exit */
+                       db_printf("\n");
+                       return;
+               }
+       }
+}
+
 void
 db_ps(dummy1, dummy2, dummy3, dummy4)
        db_expr_t       dummy1;
@@ -64,27 +91,7 @@ db_ps(dummy1, dummy2, dummy3, dummy4)
                /*
                 * XXX just take 20 for now...
                 */
-               if (nl++ == 20) {
-                       int c;
-
-                       db_printf("--More--");
-                       c = cngetc();
-                       db_printf("\r");
-                       /*
-                        * A whole screenfull or just one line?
-                        */
-                       switch (c) {
-                       case '\n':              /* just one line */
-                               nl = 20;
-                               break;
-                       case ' ':
-                               nl = 0;         /* another screenfull */
-                               break;
-                       default:                /* exit */
-                               db_printf("\n");
-                               return;
-                       }
-               }
+               db_more(&nl);
                if (p == NULL) {
                        printf("oops, ran out of processes early!\n");
                        break;
@@ -116,10 +123,13 @@ db_ps(dummy1, dummy2, dummy3, dummy4)
            thread_t td;
            struct globaldata *gd = &CPU_prvspace[cpuidx].mdglobaldata.mi;
 
-           db_printf("cpu %d tdrunqmask %08x\n", gd->gd_cpuid, gd->gd_runqmask);
+           db_printf("cpu %d tdrunqmask %08x curthread %p ast %02x\n",
+                   gd->gd_cpuid, gd->gd_runqmask,
+                   gd->gd_curthread, gd->gd_astpending);
            db_printf("  tdq     thread    pid flags  pri(act)        sp    wmesg comm\n");
            for (np = 0; np < 32; ++np) {
                TAILQ_FOREACH(td, &gd->gd_tdrunq[np], td_threadq) {
+                   db_more(&nl);
                    db_printf("  %3d %p %3d %08x %3d(%3d) %p %8.8s %s\n",
                        np, td, 
                        (td->td_proc ? td->td_proc->p_pid : -1),
@@ -133,6 +143,7 @@ db_ps(dummy1, dummy2, dummy3, dummy4)
            db_printf("\n");
            db_printf("  tdq     thread    pid flags  pri(act)        sp    wmesg comm\n");
            TAILQ_FOREACH(td, &gd->gd_tdallq, td_allq) {
+               db_more(&nl);
                db_printf("  %3d %p %3d %08x %3d(%3d) %p %8.8s %s\n",
                    np, td, 
                    (td->td_proc ? td->td_proc->p_pid : -1),
index a53104e..ca8652f 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     from: @(#)genassym.c    5.11 (Berkeley) 5/10/91
  * $FreeBSD: src/sys/i386/i386/genassym.c,v 1.86.2.3 2002/03/03 05:42:49 nyan Exp $
- * $DragonFly: src/sys/i386/i386/Attic/genassym.c,v 1.22 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/genassym.c,v 1.23 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "opt_user_ldt.h"
@@ -189,6 +189,7 @@ ASSYM(GD_CURTHREAD, offsetof(struct mdglobaldata, mi.gd_curthread));
 ASSYM(GD_REQPRI, offsetof(struct mdglobaldata, mi.gd_reqpri));
 ASSYM(GD_CPUID, offsetof(struct mdglobaldata, mi.gd_cpuid));
 ASSYM(GD_CNT, offsetof(struct mdglobaldata, mi.gd_cnt));
+ASSYM(GD_PRIVATE_TSS, offsetof(struct mdglobaldata, gd_private_tss));
 ASSYM(GD_INTR_NESTING_LEVEL, offsetof(struct mdglobaldata, mi.gd_intr_nesting_level));
 ASSYM(GD_ASTPENDING, offsetof(struct mdglobaldata, mi.gd_astpending));
 
@@ -201,7 +202,6 @@ ASSYM(GD_IPENDING, offsetof(struct mdglobaldata, gd_ipending));
 ASSYM(GD_COMMON_TSS, offsetof(struct mdglobaldata, gd_common_tss));
 ASSYM(GD_COMMON_TSSD, offsetof(struct mdglobaldata, gd_common_tssd));
 ASSYM(GD_TSS_GDT, offsetof(struct mdglobaldata, gd_tss_gdt));
-ASSYM(GD_IDLETHREAD, offsetof(struct mdglobaldata, gd_idlethread));
 ASSYM(GD_NPXTHREAD, offsetof(struct mdglobaldata, gd_npxthread));
 ASSYM(GD_CPU_LOCKID, offsetof(struct mdglobaldata, gd_cpu_lockid));
 ASSYM(GD_OTHER_CPUS, offsetof(struct mdglobaldata, gd_other_cpus));
index 86b5f57..c58310c 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/i386/globals.s,v 1.13.2.1 2000/05/16 06:58:06 dillon Exp $
- * $DragonFly: src/sys/i386/i386/Attic/globals.s,v 1.15 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/globals.s,v 1.16 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "opt_user_ldt.h"
@@ -59,9 +59,8 @@
         * the per-cpu address space, otherwise it's in the data segment.
         */
        .globl  gd_curthread, gd_npxthread, gd_astpending, gd_reqpri
-       .globl  gd_common_tss, gd_idlethread
+       .globl  gd_common_tss
        .set    gd_curthread,globaldata + GD_CURTHREAD
-       .set    gd_idlethread,globaldata + GD_IDLETHREAD
        .set    gd_astpending,globaldata + GD_ASTPENDING
        .set    gd_reqpri,globaldata + GD_REQPRI
        .set    gd_npxthread,globaldata + GD_NPXTHREAD
        .globl  gd_ss_eflags, gd_intr_nesting_level
        .globl  gd_CMAP1, gd_CMAP2, gd_CMAP3, gd_PMAP1
        .globl  gd_CADDR1, gd_CADDR2, gd_CADDR3, gd_PADDR1
-       .globl  gd_ipending, gd_fpending, gd_cnt
+       .globl  gd_ipending, gd_fpending, gd_cnt, gd_private_tss
 
        .set    gd_cpuid,globaldata + GD_CPUID
+       .set    gd_private_tss,globaldata + GD_PRIVATE_TSS
        .set    gd_cpu_lockid,globaldata + GD_CPU_LOCKID
        .set    gd_other_cpus,globaldata + GD_OTHER_CPUS
        .set    gd_ss_eflags,globaldata + GD_SS_EFLAGS
index ff5eee1..851552a 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)machdep.c     7.4 (Berkeley) 6/3/91
  * $FreeBSD: src/sys/i386/i386/machdep.c,v 1.385.2.30 2003/05/31 08:48:05 alc Exp $
- * $DragonFly: src/sys/i386/i386/Attic/machdep.c,v 1.24 2003/07/08 09:57:11 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/machdep.c,v 1.25 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "apm.h"
@@ -946,10 +946,14 @@ cpu_halt(void)
  * and loop or halt as appropriate.  Giant is not held on entry to the thread.
  *
  * The main loop is entered with a critical section held, we must release
- * the critical section before doing anything else.
+ * the critical section before doing anything else.  lwkt_switch() will
+ * check for pending interrupts due to entering and exiting its own 
+ * critical section.
  *
- * Note on cpu_idle_hlt:  On an SMP system this may cause the system to 
- * halt until the next clock tick, even if a thread is ready YYY
+ * Note on cpu_idle_hlt:  On an SMP system we rely on a scheduler IPI
+ * to wake a HLTed cpu up.  However, there are cases where the idlethread
+ * will be entered with the possibility that no IPI will occur and in such
+ * cases lwkt_switch() sets TDF_IDLE_NOHLT.
  */
 static int     cpu_idle_hlt = 1;
 SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW,
@@ -958,18 +962,32 @@ SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW,
 void
 cpu_idle(void)
 {
+       struct thread *td = curthread;
+
        crit_exit();
-       KKASSERT(curthread->td_pri < TDPRI_CRIT);
+       KKASSERT(td->td_pri < TDPRI_CRIT);
        for (;;) {
+               /*
+                * See if there are any LWKTs ready to go.
+                */
                lwkt_switch();
-               __asm __volatile("cli");
-               if (cpu_idle_hlt && !lwkt_runnable()) {
+
+               /*
+                * If we are going to halt call splz unconditionally after
+                * CLIing to catch any interrupt races.  Note that we are
+                * at SPL0 and interrupts are enabled.
+                */
+               if (cpu_idle_hlt && !lwkt_runnable() &&
+                   (td->td_flags & TDF_IDLE_NOHLT) == 0) {
                        /*
                         * We must guarentee that hlt is exactly the instruction
                         * following the sti.
                         */
+                       __asm __volatile("cli");
+                       splz();
                        __asm __volatile("sti; hlt");
                } else {
+                       td->td_flags &= ~TDF_IDLE_NOHLT;
                        __asm __volatile("sti");
                }
        }
@@ -1125,8 +1143,6 @@ union descriptor ldt[NLDT];               /* local descriptor table */
 /* table descriptors - used to load tables by cpu */
 struct region_descriptor r_gdt, r_idt;
 
-int private_tss;                       /* flag indicating private tss */
-
 #if defined(I586_CPU) && !defined(NO_F00F_HACK)
 extern int has_f00f_bug;
 #endif
@@ -1897,8 +1913,7 @@ init386(int first)
        lwkt_set_comm(&thread0, "thread0");
        proc0.p_addr = (void *)thread0.td_kstack;
        proc0.p_thread = &thread0;
-       proc0.p_flag |= P_CURPROC;
-       gd->mi.gd_uprocscheduled = 1;
+       proc0.p_flag |= P_CP_RELEASED;  /* early set.  See also init_main.c */
        thread0.td_proc = &proc0;
        thread0.td_switch = cpu_heavy_switch;   /* YYY eventually LWKT */
        safepri = thread0.td_cpl = SWI_MASK | HWI_MASK;
@@ -1983,7 +1998,6 @@ init386(int first)
        gd->gd_common_tss.tss_esp0 = (int) thread0.td_pcb - 16;
        gd->gd_common_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ;
        gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
-       private_tss = 0;
        gd->gd_tss_gdt = &gdt[GPROC0_SEL].sd;
        gd->gd_common_tssd = *gd->gd_tss_gdt;
        gd->gd_common_tss.tss_ioopt = (sizeof gd->gd_common_tss) << 16;
@@ -2062,15 +2076,14 @@ cpu_gdinit(struct mdglobaldata *gd, int cpu)
        char *sp;
 
        if (cpu)
-               gd->mi.gd_curthread = &gd->gd_idlethread;
+               gd->mi.gd_curthread = &gd->mi.gd_idlethread;
 
-       gd->mi.gd_idletd = &gd->gd_idlethread;
        sp = gd->mi.gd_prvspace->idlestack;
-       lwkt_init_thread(&gd->gd_idlethread, sp, 0, &gd->mi);
-       lwkt_set_comm(&gd->gd_idlethread, "idle_%d", cpu);
-       gd->gd_idlethread.td_switch = cpu_lwkt_switch;
-       gd->gd_idlethread.td_sp -= sizeof(void *);
-       *(void **)gd->gd_idlethread.td_sp = cpu_idle_restore;
+       lwkt_init_thread(&gd->mi.gd_idlethread, sp, 0, &gd->mi);
+       lwkt_set_comm(&gd->mi.gd_idlethread, "idle_%d", cpu);
+       gd->mi.gd_idlethread.td_switch = cpu_lwkt_switch;
+       gd->mi.gd_idlethread.td_sp -= sizeof(void *);
+       *(void **)gd->mi.gd_idlethread.td_sp = cpu_idle_restore;
 }
 
 struct globaldata *
index 48953f2..c4cb9ce 100644 (file)
@@ -23,7 +23,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/i386/mp_machdep.c,v 1.115.2.15 2003/03/14 21:22:35 jhb Exp $
- * $DragonFly: src/sys/i386/i386/Attic/mp_machdep.c,v 1.10 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/mp_machdep.c,v 1.11 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "opt_cpu.h"
@@ -2461,6 +2461,11 @@ ap_init(void)
                smp_active = 1;  /* historic */
        }
 
+       /*
+        * Startup helper thread(s) one per cpu.
+        */
+       sched_thread_init();
+
        /*
         * The idle loop doesn't expect the BGL to be held and while
         * lwkt_switch() normally cleans things up this is a special case
index 086ea48..d9072c3 100644 (file)
@@ -7,7 +7,7 @@
  * ----------------------------------------------------------------------------
  *
  * $FreeBSD: src/sys/i386/i386/mplock.s,v 1.29.2.2 2000/05/16 06:58:06 dillon Exp $
- * $DragonFly: src/sys/i386/i386/Attic/mplock.s,v 1.5 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/mplock.s,v 1.6 2003/07/10 04:47:53 dillon Exp $
  *
  * Functions for locking between CPUs in a SMP system.
  *
 
 #include "assym.s"
 
+/*
+ * YYY Debugging only.  Define this to be paranoid about invalidating the
+ * TLB when we get giant.
+ */
+#undef PARANOID_INVLTLB
+
        .data
        ALIGN_DATA
 #ifdef SMP
@@ -58,6 +64,9 @@ NON_GPROF_ENTRY(cpu_try_mplock)
        movl    $-1,%eax
        lock cmpxchgl %ecx,mp_lock      /* ecx<->mem if eax matches */
        jnz     1f
+#ifdef PARANOID_INVLTLB
+       movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
+#endif
        movl    $1,%eax
        NON_GPROF_RET
 1:
@@ -69,6 +78,11 @@ NON_GPROF_ENTRY(get_mplock)
        cmpl    $0,TD_MPCOUNT(%edx)
        je      1f
        incl    TD_MPCOUNT(%edx)        /* already have it, just ++mpcount */
+#ifdef INVARIANTS
+       movl    PCPU(cpuid),%eax        /* failure */
+       cmpl    %eax,mp_lock
+       jne     4f
+#endif
        NON_GPROF_RET
 1:
        pushfl
@@ -78,6 +92,9 @@ NON_GPROF_ENTRY(get_mplock)
        movl    $-1,%eax
        lock cmpxchgl %ecx,mp_lock      /* ecx<->mem & JZ if eax matches */
        jnz     2f
+#ifdef PARANOID_INVLTLB
+       movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
+#endif
        popfl                           /* success */
        NON_GPROF_RET
 2:
@@ -89,6 +106,11 @@ NON_GPROF_ENTRY(get_mplock)
        addl    $TDPRI_CRIT,TD_PRI(%edx)
        popfl
        call    lwkt_switch             /* will be correct on return */
+#ifdef INVARIANTS
+       movl    PCPU(cpuid),%eax        /* failure */
+       cmpl    %eax,mp_lock
+       jne     4f
+#endif
        movl    PCPU(curthread),%edx
        subl    $TDPRI_CRIT,TD_PRI(%edx)
        NON_GPROF_RET
@@ -98,11 +120,21 @@ NON_GPROF_ENTRY(get_mplock)
        popfl
        NON_GPROF_RET
 
+4:
+       cmpl    $0,panicstr             /* don't double panic */
+       je      badmp_get2
+       NON_GPROF_RET
+
 NON_GPROF_ENTRY(try_mplock)
        movl    PCPU(curthread),%edx
        cmpl    $0,TD_MPCOUNT(%edx)
        je      1f
        incl    TD_MPCOUNT(%edx)        /* already have it, just ++mpcount */
+#ifdef INVARIANTS
+       movl    PCPU(cpuid),%eax        /* failure */
+       cmpl    %eax,mp_lock
+       jne     4b
+#endif
        movl    $1,%eax
        NON_GPROF_RET
 1:
@@ -113,6 +145,9 @@ NON_GPROF_ENTRY(try_mplock)
        lock cmpxchgl %ecx,mp_lock      /* ecx<->mem & JZ if eax matches */
        jnz     2f
        movl    $1,TD_MPCOUNT(%edx)
+#ifdef PARANOID_INVLTLB
+       movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
+#endif
        popfl                           /* success */
        movl    $1,%eax
        NON_GPROF_RET
@@ -159,6 +194,9 @@ NON_GPROF_ENTRY(rel_mplock)
 badmp_get:
        pushl   $bmpsw1
        call    panic
+badmp_get2:
+       pushl   $bmpsw1a
+       call    panic
 badmp_rel:
        pushl   $bmpsw2
        call    panic
@@ -171,6 +209,9 @@ badmp_rel2:
 bmpsw1:
        .asciz  "try/get_mplock(): already have lock! %d %p"
 
+bmpsw1a:
+       .asciz  "try/get_mplock(): failed on count or switch %d %p"
+
 bmpsw2:
        .asciz  "rel_mplock(): mpcount already 0 @ %p %p %p %p %p %p %p %p!"
 
index 3074043..07e351f 100644 (file)
@@ -40,7 +40,7 @@
  *
  *     from:   @(#)pmap.c      7.7 (Berkeley)  5/12/91
  * $FreeBSD: src/sys/i386/i386/pmap.c,v 1.250.2.18 2002/03/06 22:48:53 silby Exp $
- * $DragonFly: src/sys/i386/i386/Attic/pmap.c,v 1.16 2003/07/06 21:23:48 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/pmap.c,v 1.17 2003/07/10 04:47:53 dillon Exp $
  */
 
 /*
@@ -345,11 +345,17 @@ pmap_bootstrap(firstaddr, loadaddr)
        for (i = 0; i < NKPT; i++)
                PTD[i] = 0;
 
-       /* XXX - see also mp_machdep.c */
-       if (ncpus == 1 && (cpu_feature & CPUID_PGE))
+       /*
+        * PG_G is terribly broken on SMP because we IPI invltlb's in some
+        * cases rather then invl1pg.  Actually, I don't even know why it
+        * works under UP because self-referential page table mappings
+        */
+#ifdef SMP
+       pgeflag = 0;
+#else
+       if (cpu_feature & CPUID_PGE)
                pgeflag = PG_G;
-       else
-               pgeflag = 0;
+#endif
        
 /*
  * Initialize the 4MB page size flag
@@ -3101,6 +3107,9 @@ i386_protection_init()
  * address space. Return a pointer to where it is mapped. This
  * routine is intended to be used for mapping device memory,
  * NOT real memory.
+ *
+ * NOTE: we can't use pgeflag unless we invalidate the pages one at
+ * a time.
  */
 void *
 pmap_mapdev(pa, size)
@@ -3120,7 +3129,7 @@ pmap_mapdev(pa, size)
        pa = pa & PG_FRAME;
        for (tmpva = va; size > 0;) {
                pte = (unsigned *)vtopte(tmpva);
-               *pte = pa | PG_RW | PG_V | pgeflag;
+               *pte = pa | PG_RW | PG_V; /* | pgeflag; */
                size -= PAGE_SIZE;
                tmpva += PAGE_SIZE;
                pa += PAGE_SIZE;
@@ -3206,7 +3215,7 @@ pmap_activate(struct proc *p)
 
        pmap = vmspace_pmap(p->p_vmspace);
 #if defined(SMP)
-       pmap->pm_active |= 1 << mycpu->gd_cpuid;
+       atomic_set_int(&pmap->pm_active, 1 << mycpu->gd_cpuid);
 #else
        pmap->pm_active |= 1;
 #endif
index 2280439..e5494ce 100644 (file)
@@ -35,7 +35,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/i386/swtch.s,v 1.89.2.10 2003/01/23 03:36:24 ps Exp $
- * $DragonFly: src/sys/i386/i386/Attic/swtch.s,v 1.22 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/swtch.s,v 1.23 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "npx.h"
 
 #include "assym.s"
 
+#if defined(SMP)
+#define MPLOCKED        lock ;
+#else
+#define MPLOCKED
+#endif
+
        .data
 
        .globl  panic
@@ -84,7 +90,7 @@ ENTRY(cpu_heavy_switch)
        cli
        movl    P_VMSPACE(%ecx), %edx
        movl    PCPU(cpuid), %eax
-       btrl    %eax, VM_PMAP+PM_ACTIVE(%edx)
+       MPLOCKED btrl   %eax, VM_PMAP+PM_ACTIVE(%edx)
 
        /*
         * Save general regs
@@ -134,7 +140,8 @@ ENTRY(cpu_heavy_switch)
 1:
  
        /*
-        * Save the FP state if we have used the FP.
+        * Save the FP state if we have used the FP.  Note that calling
+        * npxsave will NULL out PCPU(npxthread).
         */
 #if NNPX > 0
        movl    P_THREAD(%ecx),%ecx
@@ -243,46 +250,54 @@ ENTRY(cpu_heavy_restore)
        incl    _swtch_optim_stats
 #endif
        /*
-        * Restore the MMU address space
+        * Tell the pmap that our cpu is using the VMSPACE now.  We cannot
+        * safely test/reload %cr3 until after we have set the bit in the
+        * pmap (remember, we do not hold the MP lock in the switch code).
         */
-       movl    %cr3,%ebx
-       cmpl    PCB_CR3(%edx),%ebx
+       movl    P_VMSPACE(%ecx), %ebx
+       movl    PCPU(cpuid), %eax
+       MPLOCKED btsl   %eax, VM_PMAP+PM_ACTIVE(%ebx)
+
+       /*
+        * Restore the MMU address space.  If it is the same as the last
+        * thread we don't have to invalidate the tlb (i.e. reload cr3).
+        * YYY which naturally also means that the PM_ACTIVE bit had better
+        * already have been set before we set it above, check? YYY
+        */
+       movl    %cr3,%eax
+       movl    PCB_CR3(%edx),%ebx
+       cmpl    %eax,%ebx
        je      4f
 #if defined(SWTCH_OPTIM_STATS)
        decl    _swtch_optim_stats
        incl    _tlb_flush_count
 #endif
-       movl    PCB_CR3(%edx),%ebx
        movl    %ebx,%cr3
 4:
-
        /*
         * Deal with the PCB extension, restore the private tss
         */
-       movl    PCPU(cpuid), %esi
-       cmpl    $0, PCB_EXT(%edx)               /* has pcb extension? */
-       je      1f
-       btsl    %esi, private_tss               /* mark use of private tss */
-       movl    PCB_EXT(%edx), %edi             /* new tss descriptor */
-       jmp     2f
-1:
+       movl    PCB_EXT(%edx),%edi      /* check for a PCB extension */
+       movl    $1,%ebx                 /* maybe mark use of a private tss */
+       testl   %edi,%edi
+       jnz     2f
 
        /*
-        * update common_tss.tss_esp0 pointer.  This is the supervisor
-        * stack pointer on entry from user mode.  Since the pcb is
-        * at the top of the supervisor stack esp0 starts just below it.
-        * We leave enough space for vm86 (16 bytes).
-        *
-        * common_tss.tss_esp0 is needed when user mode traps into the
-        * kernel.
+        * Going back to the common_tss.  We may need to update TSS_ESP0
+        * which sets the top of the supervisor stack when entering from
+        * usermode.  The PCB is at the top of the stack but we need another
+        * 16 bytes to take vm86 into account.
         */
        leal    -16(%edx),%ebx
        movl    %ebx, PCPU(common_tss) + TSS_ESP0
 
-       btrl    %esi, private_tss
-       jae     3f
+       cmpl    $0,PCPU(private_tss)    /* don't have to reload if      */
+       je      3f                      /* already using the common TSS */
+
+       subl    %ebx,%ebx               /* unmark use of private tss */
 
        /*
+        * Get the address of the common TSS descriptor for the ltr.
         * There is no way to get the address of a segment-accessed variable
         * so we store a self-referential pointer at the base of the per-cpu
         * data area and add the appropriate offset.
@@ -292,9 +307,10 @@ ENTRY(cpu_heavy_restore)
 
        /*
         * Move the correct TSS descriptor into the GDT slot, then reload
-        * tr.   YYY not sure what is going on here
+        * ltr.
         */
 2:
+       movl    %ebx,PCPU(private_tss)          /* mark/unmark private tss */
        movl    PCPU(tss_gdt), %ebx             /* entry in GDT */
        movl    0(%edi), %eax
        movl    %eax, 0(%ebx)
@@ -303,14 +319,7 @@ ENTRY(cpu_heavy_restore)
        movl    $GPROC0_SEL*8, %esi             /* GSEL(entry, SEL_KPL) */
        ltr     %si
 
-       /*
-        * Tell the pmap that our cpu is using the VMSPACE now.
-        */
 3:
-       movl    P_VMSPACE(%ecx), %ebx
-       movl    PCPU(cpuid), %eax
-       btsl    %eax, VM_PMAP+PM_ACTIVE(%ebx)
-
        /*
         * Restore general registers.
         */
@@ -381,36 +390,11 @@ cpu_switch_load_gs:
 
 CROSSJUMPTARGET(sw1a)
 
-badsw0:
-       pushl   %eax
-       pushl   $sw0_1
-       call    panic
-
-sw0_1: .asciz  "cpu_switch: panic: %p"
-
-#ifdef DIAGNOSTIC
-badsw1:
-       pushl   $sw0_1
-       call    panic
-
-sw0_1: .asciz  "cpu_switch: has wchan"
-
 badsw2:
        pushl   $sw0_2
        call    panic
 
 sw0_2: .asciz  "cpu_switch: not SRUN"
-#endif
-
-#if defined(SMP) && defined(DIAGNOSTIC)
-badsw4:
-       pushl   $sw0_4
-       call    panic
-
-sw0_4: .asciz  "cpu_switch: do not have lock"
-#endif /* SMP && DIAGNOSTIC */
-
-string:        .asciz  "SWITCHING\n"
 
 /*
  * savectx(pcb)
@@ -473,7 +457,7 @@ ENTRY(savectx)
        ret
 
 /*
- * cpu_idle_restore()  (current thread in %eax on entry)
+ * cpu_idle_restore()  (current thread in %eax on entry) (one-time execution)
  *
  *     Don't bother setting up any regs other then %ebp so backtraces
  *     don't die.  This restore function is used to bootstrap into the
@@ -487,8 +471,10 @@ ENTRY(savectx)
  *     cpus.
  */
 ENTRY(cpu_idle_restore)
+       movl    IdlePTD,%ecx
        movl    $0,%ebp
        pushl   $0
+       movl    %ecx,%cr3
 #ifdef SMP
        cmpl    $0,PCPU(cpuid)
        je      1f
@@ -499,7 +485,7 @@ ENTRY(cpu_idle_restore)
        jmp     cpu_idle
 
 /*
- * cpu_kthread_restore()       (current thread is %eax on entry)
+ * cpu_kthread_restore() (current thread is %eax on entry) (one-time execution)
  *
  *     Don't bother setting up any regs other then %ebp so backtraces
  *     don't die.  This restore function is used to bootstrap into an
@@ -510,8 +496,10 @@ ENTRY(cpu_idle_restore)
  *     we can release our critical section and enable interrupts early.
  */
 ENTRY(cpu_kthread_restore)
+       movl    IdlePTD,%ecx
        movl    TD_PCB(%eax),%ebx
        movl    $0,%ebp
+       movl    %ecx,%cr3
        subl    $TDPRI_CRIT,TD_PRI(%eax)
        sti
        popl    %edx            /* kthread exit function */
@@ -554,8 +542,18 @@ ENTRY(cpu_lwkt_switch)
  *     Warning: due to preemption the restore function can be used to 
  *     'return' to the original thread.  Interrupt disablement must be
  *     protected through the switch so we cannot run splz here.
+ *
+ *     YYY we theoretically do not need to load IdlePTD into cr3, but if
+ *     so we need a way to detect when the PTD we are using is being 
+ *     deleted due to a process exiting.
  */
 ENTRY(cpu_lwkt_restore)
+       movl    IdlePTD,%ecx    /* YYY borrow but beware desched/cpuchg/exit */
+       movl    %cr3,%eax
+       cmpl    %ecx,%eax
+       je      1f
+       movl    %ecx,%cr3
+1:
        popfl
        popl    %edi
        popl    %esi
index 8431b05..9542d9a 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)trap.c        7.4 (Berkeley) 5/13/91
  * $FreeBSD: src/sys/i386/i386/trap.c,v 1.147.2.11 2003/02/27 19:09:59 luoqi Exp $
- * $DragonFly: src/sys/i386/i386/Attic/trap.c,v 1.19 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/trap.c,v 1.20 2003/07/10 04:47:53 dillon Exp $
  */
 
 /*
@@ -162,28 +162,19 @@ SYSCTL_INT(_machdep, OID_AUTO, panic_on_nmi, CTLFLAG_RW,
  * point of view of the userland scheduler unless we actually have to
  * switch.
  *
- * usertdsw is called from within a critical section, but the BGL will
- * have already been released by lwkt_switch() so only call MP safe functions
- * that don't block and don't require the BGL!
+ * usertdsw is called from within a critical section and the BGL will still
+ * be held.  This function is NOT called for preemptions, only for switchouts.
  */
 static void
-usertdsw(struct thread *ntd)
+passive_release(struct thread *td)
 {
-       struct thread *td = curthread;
+       struct proc *p = td->td_proc;
 
-       td->td_switch = cpu_heavy_switch;
+       td->td_release = NULL;
        lwkt_setpri_self(TDPRI_KERN_USER);
-#if 0
-       /* 
-        * This is where we might want to catch the P_CURPROC designation
-        * and fix it for *any* switchout rather then just an mi_switch()
-        * switchout (move from mi_switch()?) YYY
-        */
        if (p->p_flag & P_CURPROC) {
-               ...
+               release_curproc(p);
        }
-#endif
-       td->td_switch(ntd);
 }
 
 /*
@@ -195,40 +186,26 @@ usertdsw(struct thread *ntd)
 static __inline void
 userenter(void)
 {
-       struct thread *td;
+       struct thread *td = curthread;
 
-       td = curthread;
-       KASSERT(td->td_switch == cpu_heavy_switch,
-               ("userenter: bad td_switch = %p", td->td_switch));
-#if 0
-       KASSERT(td->td_switch == cpu_heavy_switch || td->td_switch == usertdsw,
-               ("userenter: bad td_switch = %p", td->td_switch));
-#endif
-       td->td_switch = usertdsw;
+       td->td_release = passive_release;
 }
 
-static void
-userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
+static __inline void
+userexit(struct proc *p)
 {
-       int sig, s;
-       struct thread *td = curthread;
+       struct thread *td = p->p_thread;
 
        /*
-        * Post any pending signals
+        * If we did not have to release we should already be P_CURPROC.  If
+        * we did have to release we must acquire P_CURPROC again and then
+        * restore our priority for user return.
         */
-       crit_enter();
-       while ((sig = CURSIG(p)) != 0) {
-               crit_exit();
-               postsig(sig);
-               crit_enter();
-       }
-
-       /*
-        * Set our priority properly and restore our switch function.  If
-        * we did not hit our lazy switch function in the first place we
-        * do not need to restore anything.
-        */
-       if (td->td_switch == cpu_heavy_switch) {
+       if (td->td_release) {
+               td->td_release = NULL;
+               KKASSERT(p->p_flag & P_CURPROC);
+       } else {
+               acquire_curproc(p);
                switch(p->p_rtprio.type) {
                case RTP_PRIO_IDLE:
                        lwkt_setpri_self(TDPRI_USER_IDLE);
@@ -241,21 +218,35 @@ userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
                        lwkt_setpri_self(TDPRI_USER_NORM);
                        break;
                }
-       } else {
-               KKASSERT(td->td_switch == usertdsw);
-               td->td_switch = cpu_heavy_switch;
        }
-       crit_exit();
+}
+
+
+static void
+userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
+{
+       int sig;
+
+       /*
+        * Post any pending signals
+        */
+       while ((sig = CURSIG(p)) != 0) {
+               postsig(sig);
+       }
 
        /*
-        * If a reschedule has been requested we call chooseproc() to locate
-        * the next runnable process.  When we wakeup from that we check
-        * for pending signals again.
+        * If a reschedule has been requested we lwkt_switch().  The
+        * lwkt_switch() will ensure that our current process is released
+        * (see the use of td_release) as well as ensure that any pending
+        * LWKTs get run before we get cpu back.
+        *
+        * YYY though of doreti detects that we were in a user context
+        * it should really just call lwkt_switch()!  and are re-acquisition 
+        * of the current process below will handle userland scheduling
+        * priorities.
         */
        if (resched_wanted()) {
-               uio_yield();
-               while ((sig = CURSIG(p)) != 0)
-                       postsig(sig);
+               lwkt_switch();
        }
 
        /*
@@ -267,18 +258,10 @@ userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
        }
 
        /*
-        * In order to return to userland we need to be the designated
-        * current (user) process on this cpu.  We have to wait for
-        * the userland scheduler to schedule as P_CURPROC.
+        * Post any pending signals XXX
         */
-       s = splhigh();
-       while ((p->p_flag & P_CURPROC) == 0) {
-               p->p_stats->p_ru.ru_nivcsw++;
-               lwkt_deschedule_self();
-               mi_switch();
-       }
-       splx(s);
-       KKASSERT(mycpu->gd_uprocscheduled == 1);
+       while ((sig = CURSIG(p)) != 0)
+               postsig(sig);
 }
 
 #ifdef DEVICE_POLLING
@@ -291,8 +274,14 @@ extern int ether_poll __P((int count));
  * This common code is called from assembly language IDT gate entry
  * routines that prepare a suitable stack frame, and restore this
  * frame after the exception has been processed.
+ *
+ * This function is also called from doreti in an interlock to handle ASTs.
+ * For example:  hardwareint->INTROUTINE->(set ast)->doreti->trap
+ *
+ * NOTE!  We have to retrieve the fault address prior to obtaining the
+ * MP lock because get_mplock() may switch out.  YYY cr2 really ought
+ * to be retrieved by the assembly code, not here.
  */
-
 void
 trap(frame)
        struct trapframe frame;
@@ -302,24 +291,38 @@ trap(frame)
        int i = 0, ucode = 0, type, code;
        vm_offset_t eva;
 
-#ifdef SMP
-       if (panicstr == NULL)
-           KASSERT(curthread->td_mpcount >= 0, ("BADX1 AT %08x %08x", frame.tf_eip, frame.tf_esp));
-#endif
-       get_mplock();
-#ifdef SMP
-       if (panicstr == NULL)
-           KKASSERT(curthread->td_mpcount > 0);
-#endif
-
 #ifdef DDB
        if (db_active) {
                eva = (frame.tf_trapno == T_PAGEFLT ? rcr2() : 0);
+               get_mplock();
                trap_fatal(&frame, eva);
                goto out2;
        }
 #endif
 
+       eva = 0;
+       if (frame.tf_trapno == T_PAGEFLT) {
+               /*
+                * For some Cyrix CPUs, %cr2 is clobbered by interrupts.
+                * This problem is worked around by using an interrupt
+                * gate for the pagefault handler.  We are finally ready
+                * to read %cr2 and then must reenable interrupts.
+                *
+                * XXX this should be in the switch statement, but the
+                * NO_FOOF_HACK and VM86 goto and ifdefs obfuscate the
+                * flow of control too much for this to be obviously
+                * correct.
+                */
+               eva = rcr2();
+               get_mplock();
+               cpu_enable_intr();
+       } else {
+               get_mplock();
+       }
+       /*
+        * MP lock is held at this point
+        */
+
        if (!(frame.tf_eflags & PSL_I)) {
                /*
                 * Buggy application or kernel code has disabled interrupts
@@ -328,36 +331,21 @@ trap(frame)
                 * they are accidentally enabled later.
                 */
                type = frame.tf_trapno;
-               if (ISPL(frame.tf_cs) == SEL_UPL || (frame.tf_eflags & PSL_VM))
+               if (ISPL(frame.tf_cs)==SEL_UPL || (frame.tf_eflags & PSL_VM)) {
                        printf(
                            "pid %ld (%s): trap %d with interrupts disabled\n",
                            (long)curproc->p_pid, curproc->p_comm, type);
-               else if (type != T_BPTFLT && type != T_TRCTRAP)
+               } else if (type != T_BPTFLT && type != T_TRCTRAP) {
                        /*
                         * XXX not quite right, since this may be for a
                         * multiple fault in user mode.
                         */
                        printf("kernel trap %d with interrupts disabled\n",
                            type);
+               }
                cpu_enable_intr();
        }
 
-       eva = 0;
-       if (frame.tf_trapno == T_PAGEFLT) {
-               /*
-                * For some Cyrix CPUs, %cr2 is clobbered by interrupts.
-                * This problem is worked around by using an interrupt
-                * gate for the pagefault handler.  We are finally ready
-                * to read %cr2 and then must reenable interrupts.
-                *
-                * XXX this should be in the switch statement, but the
-                * NO_FOOF_HACK and VM86 goto and ifdefs obfuscate the
-                * flow of control too much for this to be obviously
-                * correct.
-                */
-               eva = rcr2();
-               cpu_enable_intr();
-       }
 
 #ifdef DEVICE_POLLING
        if (poll_in_trap)
@@ -750,6 +738,7 @@ out:
                KASSERT(curthread->td_mpcount == 1, ("badmpcount trap from %p", (void *)frame.tf_eip));
 #endif
        userret(p, &frame, sticks);
+       userexit(p);
 out2:
 #ifdef SMP
        KKASSERT(curthread->td_mpcount > 0);
@@ -1345,6 +1334,7 @@ bad:
         */
        STOPEVENT(p, S_SCX, code);
 
+       userexit(p);
 #ifdef SMP
        /*
         * Release the MP lock if we had to get it
@@ -1374,6 +1364,7 @@ fork_return(p, frame)
        if (KTRPOINT(p->p_thread, KTR_SYSRET))
                ktrsysret(p->p_tracep, SYS_fork, 0, 0);
 #endif
+       userexit(p);
 #ifdef SMP
        KKASSERT(curthread->td_mpcount == 1);
        rel_mplock();
index 42cebf0..e7ff444 100644 (file)
@@ -39,7 +39,7 @@
  *     from: @(#)vm_machdep.c  7.3 (Berkeley) 5/13/91
  *     Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
  * $FreeBSD: src/sys/i386/i386/vm_machdep.c,v 1.132.2.9 2003/01/25 19:02:23 dillon Exp $
- * $DragonFly: src/sys/i386/i386/Attic/vm_machdep.c,v 1.18 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/vm_machdep.c,v 1.19 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "npx.h"
@@ -146,7 +146,7 @@ cpu_fork(p1, p2, flags)
 
 #if NNPX > 0
        /* Ensure that p1's pcb is up to date. */
-       if (npxthread == p1->p_thread)
+       if (mdcpu->gd_npxthread == p1->p_thread)
                npxsave(&p1->p_thread->td_pcb->pcb_save);
 #endif
 
index 4409e0d..e56ff9f 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/include/atomic.h,v 1.9.2.1 2000/07/07 00:38:47 obrien Exp $
- * $DragonFly: src/sys/i386/include/Attic/atomic.h,v 1.2 2003/06/17 04:28:35 dillon Exp $
+ * $DragonFly: src/sys/i386/include/Attic/atomic.h,v 1.3 2003/07/10 04:47:53 dillon Exp $
  */
 #ifndef _MACHINE_ATOMIC_H_
 #define _MACHINE_ATOMIC_H_
 
 /*
  * The assembly is volatilized to demark potential before-and-after side
- * effects if an interrupt or SMP collision were to occur.
+ * effects if an interrupt or SMP collision were to occur.  The primary
+ * atomic instructions are MP safe, the nonlocked instructions are 
+ * local-interrupt-safe (so we don't depend on C 'X |= Y' generating an
+ * atomic instruction).
  */
 #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 9)
+
 /* egcs 1.1.2+ version */
 #define ATOMIC_ASM(NAME, TYPE, OP, V)                  \
 static __inline void                                   \
 atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\
+{                                                      \
+       __asm __volatile(MPLOCKED OP                    \
+                        : "=m" (*p)                    \
+                        :  "0" (*p), "ir" (V));        \
+}                                                      \
+static __inline void                                   \
+atomic_##NAME##_##TYPE##_nonlocked(volatile u_##TYPE *p, u_##TYPE v)\
 {                                                      \
        __asm __volatile(MPLOCKED OP                    \
                         : "=m" (*p)                    \
@@ -89,6 +100,7 @@ atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\
 }
 
 #else
+
 /* gcc <= 2.8 version */
 #define ATOMIC_ASM(NAME, TYPE, OP, V)                  \
 static __inline void                                   \
@@ -97,6 +109,13 @@ atomic_##NAME##_##TYPE(volatile u_##TYPE *p, u_##TYPE v)\
        __asm __volatile(MPLOCKED OP                    \
                         : "=m" (*p)                    \
                         : "ir" (V));                   \
+}                                                      \
+static __inline void                                   \
+atomic_##NAME##_##TYPE##_nonlocked(volatile u_##TYPE *p, u_##TYPE v)\
+{                                                      \
+       __asm __volatile(OP                             \
+                        : "=m" (*p)                    \
+                        : "ir" (V));                   \
 }
 #endif
 #endif /* KLD_MODULE */
index 55ab059..3afae06 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     from: @(#)cpu.h 5.4 (Berkeley) 5/9/91
  * $FreeBSD: src/sys/i386/include/cpu.h,v 1.43.2.2 2001/06/15 09:37:57 scottl Exp $
- * $DragonFly: src/sys/i386/include/Attic/cpu.h,v 1.6 2003/06/29 05:29:30 dillon Exp $
+ * $DragonFly: src/sys/i386/include/Attic/cpu.h,v 1.7 2003/07/10 04:47:53 dillon Exp $
  */
 
 #ifndef _MACHINE_CPU_H_
  * Preempt the current process if in interrupt from user mode,
  * or after the current trap/syscall if in system mode.
  *
- * XXX: if astpending is later changed to an |= here due to more flags being
- * added, we will have an atomicy problem.  The type of atomicy we need is
- * a non-locked orl.
+ * We do not have to use a locked bus cycle but we do have to use an
+ * atomic instruction because an interrupt on the local cpu can modify
+ * the field.
  */
-#define        need_resched()          do { mycpu->gd_astpending = AST_RESCHED|AST_PENDING; } while (0)
-#define        clear_resched()         do { mycpu->gd_astpending &= ~AST_RESCHED; } while(0)
+#define        need_resched()          \
+    atomic_set_int_nonlocked(&mycpu->gd_astpending, AST_RESCHED|AST_PENDING)
+#define        clear_resched()         \
+    atomic_clear_int_nonlocked(&mycpu->gd_astpending, AST_RESCHED)
 #define        resched_wanted()        (mycpu->gd_astpending & AST_RESCHED)
 
 /*
index 25e4c1f..a2c46f5 100644 (file)
@@ -28,7 +28,7 @@
  *     should not include this file.
  *
  * $FreeBSD: src/sys/i386/include/globaldata.h,v 1.11.2.1 2000/05/16 06:58:10 dillon Exp $
- * $DragonFly: src/sys/i386/include/Attic/globaldata.h,v 1.16 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/i386/include/Attic/globaldata.h,v 1.17 2003/07/10 04:47:53 dillon Exp $
  */
 
 #ifndef _MACHINE_GLOBALDATA_H_
@@ -61,7 +61,6 @@
  */
 struct mdglobaldata {
        struct globaldata mi;
-       struct thread   gd_idlethread;
        struct segment_descriptor gd_common_tssd;
        struct segment_descriptor *gd_tss_gdt;
        struct thread   *gd_npxthread;
@@ -69,6 +68,7 @@ struct mdglobaldata {
        int             gd_fpending;    /* fast interrupt pending */
        int             gd_ipending;    /* normal interrupt pending */
        int             gd_currentldt;  /* USER_LDT */
+       int             gd_private_tss;
        u_int           gd_cpu_lockid;
        u_int           gd_other_cpus;
        u_int           gd_ss_eflags;
@@ -108,6 +108,5 @@ struct privatespace {
 extern struct privatespace CPU_prvspace[];
 
 #define mdcpu                  ((struct mdglobaldata *)_get_mycpu())
-#define npxthread       mdcpu->gd_npxthread
 
 #endif
index fef5fad..205fb6a 100644 (file)
  *
  *     Machine independant code should not directly include this file.
  *
- * $DragonFly: src/sys/i386/include/Attic/thread.h,v 1.4 2003/07/08 06:27:27 dillon Exp $
+ * $DragonFly: src/sys/i386/include/Attic/thread.h,v 1.5 2003/07/10 04:47:53 dillon Exp $
  */
 
 #ifndef        _MACHINE_THREAD_H_
 #define        _MACHINE_THREAD_H_
 
-#ifndef _SYS_GLOBALDATA_H_
-#include <sys/globaldata.h>    /* struct globaldata */
-#endif
-
 struct md_thread {
     unsigned int       mtd_cpl;
 };
index 450a029..6a026f3 100644 (file)
@@ -37,7 +37,7 @@
  *     @(#)ipl.s
  *
  * $FreeBSD: src/sys/i386/isa/ipl.s,v 1.32.2.3 2002/05/16 16:03:56 bde Exp $
- * $DragonFly: src/sys/i386/isa/Attic/ipl.s,v 1.7 2003/07/08 06:27:27 dillon Exp $
+ * $DragonFly: src/sys/i386/isa/Attic/ipl.s,v 1.8 2003/07/10 04:47:54 dillon Exp $
  */
 
 
@@ -94,30 +94,32 @@ doreti_next:
        movl    %eax,%ecx               /* cpl being restored */
        notl    %ecx
        cli                             /* disallow YYY remove */
-       testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
-       jne     doreti_fast
-
-       testl   PCPU(ipending),%ecx
-       jne     doreti_intr
 #ifdef SMP
        testl   $AST_IPIQ,PCPU(astpending)
        jnz     doreti_ipiq
 #endif
+       testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
+       jnz     doreti_fast
+
+       testl   PCPU(ipending),%ecx
+       jnz     doreti_intr
        testl   $AST_PENDING,PCPU(astpending) /* any pending ASTs? */
        jz      2f
        testl   $PSL_VM,TF_EFLAGS(%esp)
        jz      1f
        cmpl    $1,in_vm86call          /* YYY make per 'cpu' */
-       jnz     doreti_ast
+       jnz     doreti_ast2
 1:
        testb   $SEL_RPL_MASK,TF_CS(%esp)
-       jnz     doreti_ast
+       jnz     doreti_ast2
 2:
        /*
         * Nothing left to do, finish up.  Interrupts are still disabled.
         */
-4:
        subl    $TDPRI_CRIT,TD_PRI(%ebx)        /* interlocked with cli */
+       testl   %eax,%eax
+       jnz     5f
+       movl    $0,PCPU(reqpri)
 5:
        decl    PCPU(intr_nesting_level)
        MEXITCOUNT
@@ -199,7 +201,7 @@ doreti_intr:
        jnc     doreti_next
        pushl   %eax
        pushl   %ecx
-       subl    $TDPRI_CRIT,TD_PRI(%ebx)
+       subl    $TDPRI_CRIT,TD_PRI(%ebx) /* so we can preempt */
        call    sched_ithd              /* YYY must pull in imasks */
        addl    $TDPRI_CRIT,TD_PRI(%ebx)
        addl    $4,%esp
@@ -208,14 +210,35 @@ doreti_intr:
 
        /*
         * AST pending
+        *
+        * Temporarily back-out our critical section because trap() can be
+        * a long-winded call, and we want to be more syscall-like.  
+        *
+        * YYY If we came in from user mode (doreti_ast1) we can call
+        * lwkt_switch *RIGHT* *NOW* to deal with interrupts more quickly,
+        * but should still fall through to the trap code to properly 
+        * reschedule.
         */
-doreti_ast:
+#if 0
+doreti_ast1:
        andl    $~AST_PENDING,PCPU(astpending)
        sti
        movl    %eax,%esi               /* save cpl (can't use stack) */
        movl    $T_ASTFLT,TF_TRAPNO(%esp)
-       decl    PCPU(intr_nesting_level)
-       call    trap
+       decl    PCPU(intr_nesting_level) /* syscall-like, not interrupt-like */
+       subl    $TDPRI_CRIT,TD_PRI(%ebx)
+       call    lwkt_switch
+       jmp     1f
+#endif
+doreti_ast2:
+       andl    $~AST_PENDING,PCPU(astpending)
+       sti
+       movl    %eax,%esi               /* save cpl (can't use stack) */
+       movl    $T_ASTFLT,TF_TRAPNO(%esp)
+       decl    PCPU(intr_nesting_level) /* syscall-like, not interrupt-like */
+       subl    $TDPRI_CRIT,TD_PRI(%ebx)
+1:     call    trap
+       addl    $TDPRI_CRIT,TD_PRI(%ebx)
        incl    PCPU(intr_nesting_level)
        movl    %esi,%eax               /* restore cpl for loop */
        jmp     doreti_next
@@ -253,18 +276,21 @@ splz_next:
        cli
        movl    %eax,%ecx               /* ecx = ~CPL */
        notl    %ecx
-       testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
-       jne     splz_fast
-
-       testl   PCPU(ipending),%ecx
-       jne     splz_intr
-
 #ifdef SMP
        testl   $AST_IPIQ,PCPU(astpending)
        jnz     splz_ipiq
 #endif
+       testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
+       jnz     splz_fast
+
+       testl   PCPU(ipending),%ecx
+       jnz     splz_intr
 
        subl    $TDPRI_CRIT,TD_PRI(%ebx)
+       testl   %eax,%eax
+       jnz     5f
+       movl    $0,PCPU(reqpri)
+5:
        popl    %ebx
        popfl
        ret
index 7a6c54e..6bd24bf 100644 (file)
@@ -33,7 +33,7 @@
  *
  *     from: @(#)npx.c 7.2 (Berkeley) 5/12/91
  * $FreeBSD: src/sys/i386/isa/npx.c,v 1.80.2.3 2001/10/20 19:04:38 tegge Exp $
- * $DragonFly: src/sys/i386/isa/Attic/npx.c,v 1.8 2003/07/08 06:27:27 dillon Exp $
+ * $DragonFly: src/sys/i386/isa/Attic/npx.c,v 1.9 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_cpu.h"
@@ -525,7 +525,7 @@ void
 npxexit(struct proc *p)
 {
 
-       if (p->p_thread == npxthread)
+       if (p->p_thread == mdcpu->gd_npxthread)
                npxsave(&curthread->td_pcb->pcb_save);
 #ifdef NPX_DEBUG
        if (npx_exists) {
@@ -746,16 +746,16 @@ npx_intr(dummy)
        struct intrframe *frame;
        u_long *exstat;
 
-       if (npxthread == NULL || !npx_exists) {
+       if (mdcpu->gd_npxthread == NULL || !npx_exists) {
                get_mplock();
                printf("npxintr: npxthread = %p, curthread = %p, npx_exists = %d\n",
-                      npxthread, curthread, npx_exists);
+                      mdcpu->gd_npxthread, curthread, npx_exists);
                panic("npxintr from nowhere");
        }
-       if (npxthread != curthread) {
+       if (mdcpu->gd_npxthread != curthread) {
                get_mplock();
                printf("npxintr: npxthread = %p, curthread = %p, npx_exists = %d\n",
-                      npxthread, curthread, npx_exists);
+                      mdcpu->gd_npxthread, curthread, npx_exists);
                panic("npxintr from non-current process");
        }
 
@@ -825,16 +825,16 @@ npxdna()
 
        if (!npx_exists)
                return (0);
-       if (npxthread != NULL) {
+       if (mdcpu->gd_npxthread != NULL) {
                printf("npxdna: npxthread = %p, curthread = %p\n",
-                      npxthread, curthread);
+                      mdcpu->gd_npxthread, curthread);
                panic("npxdna");
        }
        stop_emulating();
        /*
         * Record new context early in case frstor causes an IRQ13.
         */
-       npxthread = curthread;
+       mdcpu->gd_npxthread = curthread;
        exstat = GET_FPU_EXSW_PTR(curthread->td_pcb);
        *exstat = 0;
        /*
@@ -878,7 +878,7 @@ npxsave(addr)
 
        /* fnop(); */
        start_emulating();
-       npxthread = NULL;
+       mdcpu->gd_npxthread = NULL;
 
 #else /* SMP or CPU_ENABLE_SSE */
 
@@ -902,7 +902,7 @@ npxsave(addr)
        fnsave(addr);
        fnop();
        start_emulating();
-       npxthread = NULL;
+       mdcpu->gd_npxthread = NULL;
        cpu_disable_intr();
        icu1_mask = inb(IO_ICU1 + 1);   /* masks may have changed */
        icu2_mask = inb(IO_ICU2 + 1);
index 602507c..fd534bc 100644 (file)
@@ -40,7 +40,7 @@
  *
  *     @(#)init_main.c 8.9 (Berkeley) 1/21/94
  * $FreeBSD: src/sys/kern/init_main.c,v 1.134.2.8 2003/06/06 20:21:32 tegge Exp $
- * $DragonFly: src/sys/kern/init_main.c,v 1.19 2003/07/06 21:23:51 dillon Exp $
+ * $DragonFly: src/sys/kern/init_main.c,v 1.20 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_init_path.h"
@@ -290,7 +290,7 @@ proc0_init(void *dummy __unused)
 
        p->p_sysent = &aout_sysvec;
 
-       p->p_flag = P_INMEM | P_SYSTEM | P_CURPROC;
+       p->p_flag = P_INMEM | P_SYSTEM | P_CP_RELEASED;
        p->p_stat = SRUN;
        p->p_nice = NZERO;
        p->p_rtprio.type = RTP_PRIO_NORMAL;
@@ -530,8 +530,13 @@ start_init(void *dummy)
                 *
                 * Otherwise, return via fork_trampoline() all the way
                 * to user mode as init!
+                *
+                * WARNING!  We may have been moved to another cpu after
+                * acquiring P_CURPROC.  The MP lock will migrate with us
+                * though so we still have to release it.
                 */
                if ((error = execve(&args)) == 0) {
+                       acquire_curproc(p);
                        rel_mplock();
                        return;
                }
index 521496b..faa3d55 100644 (file)
@@ -38,7 +38,7 @@
  *
  *     @(#)kern_clock.c        8.5 (Berkeley) 1/21/94
  * $FreeBSD: src/sys/kern/kern_clock.c,v 1.105.2.10 2002/10/17 13:19:40 maxim Exp $
- * $DragonFly: src/sys/kern/kern_clock.c,v 1.6 2003/06/29 07:37:06 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_clock.c,v 1.7 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_ntp.h"
@@ -475,7 +475,7 @@ statclock(frame)
                if (CLKF_INTR(frame)) {
                        cp_time[CP_INTR]++;
                } else {
-                       if (td == mycpu->gd_idletd)
+                       if (td == &mycpu->gd_idlethread)
                                ++cp_time[CP_IDLE];
                        else
                                ++cp_time[CP_SYS];
index 322b745..5ebc883 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)kern_exit.c 8.7 (Berkeley) 2/12/94
  * $FreeBSD: src/sys/kern/kern_exit.c,v 1.92.2.11 2003/01/13 22:51:16 dillon Exp $
- * $DragonFly: src/sys/kern/kern_exit.c,v 1.15 2003/07/06 21:23:51 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_exit.c,v 1.16 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_compat.h"
@@ -368,7 +368,7 @@ exit1(int rv)
         * Release the P_CURPROC designation on the process so the userland
         * scheduler can work in someone else.
         */
-       relscurproc(p);
+       release_curproc(p);
 
        /*
         * Finally, call machine-dependent code to release the remaining
index 583d502..6b24392 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)kern_fork.c 8.6 (Berkeley) 4/8/94
  * $FreeBSD: src/sys/kern/kern_fork.c,v 1.72.2.13 2003/06/06 20:21:32 tegge Exp $
- * $DragonFly: src/sys/kern/kern_fork.c,v 1.10 2003/07/03 17:24:02 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_fork.c,v 1.11 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_ktrace.h"
@@ -352,8 +352,13 @@ again:
         * Duplicate sub-structures as needed.
         * Increase reference counts on shared objects.
         * The p_stats and p_sigacts substructs are set in vm_fork.
+        *
+        * P_CP_RELEASED indicates that the process is starting out in
+        * the kernel (in the fork trampoline).  The flag will be converted
+        * to P_CURPROC when the new process calls userret() and attempts
+        * to return to userland
         */
-       p2->p_flag = P_INMEM;
+       p2->p_flag = P_INMEM | P_CP_RELEASED;
        if (p1->p_flag & P_PROFIL)
                startprofclock(p2);
        p2->p_ucred = crhold(p1->p_ucred);
index f4a7a07..b240c6b 100644 (file)
@@ -32,7 +32,7 @@
  *
  *     @(#)kern_malloc.c       8.3 (Berkeley) 1/4/94
  * $FreeBSD: src/sys/kern/kern_malloc.c,v 1.64.2.5 2002/03/16 02:19:51 archie Exp $
- * $DragonFly: src/sys/kern/Attic/kern_malloc.c,v 1.5 2003/07/03 17:24:02 dillon Exp $
+ * $DragonFly: src/sys/kern/Attic/kern_malloc.c,v 1.6 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_vm.h"
@@ -45,6 +45,7 @@
 #include <sys/vmmeter.h>
 #include <sys/lock.h>
 #include <sys/thread.h>
+#include <sys/globaldata.h>
 
 #include <vm/vm.h>
 #include <vm/vm_param.h>
index 4c6a158..751e7e0 100644 (file)
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/kern/kern_switch.c,v 1.3.2.1 2000/05/16 06:58:12 dillon Exp $
- * $DragonFly: src/sys/kern/Attic/kern_switch.c,v 1.4 2003/06/30 19:50:31 dillon Exp $
+ * $DragonFly: src/sys/kern/Attic/kern_switch.c,v 1.5 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/queue.h>
 #include <sys/proc.h>
 #include <sys/rtprio.h>
-#include <sys/queue.h>
+#include <sys/thread2.h>
+#include <sys/uio.h>
+#include <machine/ipl.h>
+#include <machine/cpu.h>
+
+/*
+ * debugging only YYY Remove me!   define to schedule user processes only
+ * on the BSP.  Interrupts can still be taken on the APs.
+ */
+#undef ONLY_ONE_USER_CPU       
 
 /*
  * We have NQS (32) run queues per scheduling class.  For the normal
  * the state of all 32 queues and then a ffs() to find the first busy
  * queue.
  */
-struct rq queues[NQS];
-struct rq rtqueues[NQS];
-struct rq idqueues[NQS];
-u_int32_t queuebits;
-u_int32_t rtqueuebits;
-u_int32_t idqueuebits;
+static struct rq queues[NQS];
+static struct rq rtqueues[NQS];
+static struct rq idqueues[NQS];
+static u_int32_t queuebits;
+static u_int32_t rtqueuebits;
+static u_int32_t idqueuebits;
+static u_int32_t curprocmask = -1;
+static u_int32_t rdyprocmask = 0;
+static int      runqcount;
 
 /*
  * Initialize the run queues at boot time.
@@ -66,15 +79,85 @@ rqinit(void *dummy)
                TAILQ_INIT(&rtqueues[i]);
                TAILQ_INIT(&idqueues[i]);
        }
+#ifdef SMP
+       sched_thread_init();
+#else
+       curprocmask &= ~1;
+#endif
 }
 SYSINIT(runqueue, SI_SUB_RUN_QUEUE, SI_ORDER_FIRST, rqinit, NULL)
 
+static int
+test_resched(struct proc *curp, struct proc *newp)
+{
+       if (newp->p_rtprio.type < curp->p_rtprio.type)
+               return(1);
+       if (newp->p_rtprio.type == curp->p_rtprio.type) {
+               if (newp->p_rtprio.type == RTP_PRIO_NORMAL) {
+                       if (newp->p_priority / PPQ <= curp->p_priority / PPQ)
+                               return(1);
+               } else if (newp->p_rtprio.prio < curp->p_rtprio.prio) {
+                       return(1);
+               }
+       }
+       return(0);
+}
+
 /*
- * setrunqueue() examines a process priority and class and inserts it on
- * the tail of it's appropriate run queue (based on class and priority).
- * This sets the queue busy bit.  If no user processes have been scheduled
- * on the LWKT subsystem we schedule this one.
+ * chooseproc() is called when a cpu needs a user process to LWKT schedule.
+ * chooseproc() will select a user process and return it.
+ */
+static
+struct proc *
+chooseproc(void)
+{
+       struct proc *p;
+       struct rq *q;
+       u_int32_t *which;
+       u_int32_t pri;
+
+       if (rtqueuebits) {
+               pri = bsfl(rtqueuebits);
+               q = &rtqueues[pri];
+               which = &rtqueuebits;
+       } else if (queuebits) {
+               pri = bsfl(queuebits);
+               q = &queues[pri];
+               which = &queuebits;
+       } else if (idqueuebits) {
+               pri = bsfl(idqueuebits);
+               q = &idqueues[pri];
+               which = &idqueuebits;
+       } else {
+               return NULL;
+       }
+       p = TAILQ_FIRST(q);
+       KASSERT(p, ("chooseproc: no proc on busy queue"));
+       TAILQ_REMOVE(q, p, p_procq);
+       if (TAILQ_EMPTY(q))
+               *which &= ~(1 << pri);
+       KASSERT((p->p_flag & P_ONRUNQ) != 0, ("not on runq6!"));
+       p->p_flag &= ~P_ONRUNQ;
+       return p;
+}
+
+
+/*
+ * setrunqueue() 'wakes up' a 'user' process, which can mean several things.
+ *
+ * If P_CP_RELEASED is set the user process is under the control of the
+ * LWKT subsystem and we simply wake the thread up.  This is ALWAYS the
+ * case when setrunqueue() is called from wakeup() and, in fact wakeup()
+ * asserts that P_CP_RELEASED is set.
+ *
+ * Note that acquire_curproc() already optimizes making the current process
+ * P_CURPROC, so setrunqueue() does not need to.
+ *
+ * If P_CP_RELEASED is not set we place the process on the run queue and we
+ * signal other cpus in the system that may need to be woken up to service
+ * the new 'user' process.
  *
+ * The associated thread must NOT be scheduled.  
  * The process must be runnable.
  * This must be called at splhigh().
  */
@@ -82,59 +165,69 @@ void
 setrunqueue(struct proc *p)
 {
        struct rq *q;
-       u_int8_t pri;
-       struct globaldata *gd;
+       int pri;
+       int cpuid;
+       u_int32_t mask;
 
+       crit_enter();
        KASSERT(p->p_stat == SRUN, ("setrunqueue: proc not SRUN"));
+       KASSERT((p->p_flag & (P_ONRUNQ|P_CURPROC)) == 0,
+           ("process %d already on runq! flag %08x", p->p_pid, p->p_flag));
+       KKASSERT((p->p_thread->td_flags & TDF_RUNQ) == 0);
 
        /*
-        * If we are already the designated current process just
-        * wakeup the thread.
+        * If we have been released from the userland scheduler we
+        * directly schedule its thread.
         */
-       if (p->p_flag & P_CURPROC) {
-               KASSERT((p->p_flag & P_ONRUNQ) == 0, ("already on runq!"));
+       if (p->p_flag & P_CP_RELEASED) {
                lwkt_schedule(p->p_thread);
+               crit_exit();
                return;
        }
 
-       /*
-        * If the process's cpu is not running any userland processes
-        * then schedule this one's thread.
-        */
-       gd = p->p_thread->td_gd;
-       if (gd->gd_uprocscheduled == 0) {
-               gd->gd_uprocscheduled = 1;
-               p->p_flag |= P_CURPROC;
-               lwkt_schedule(p->p_thread);
-               KASSERT((p->p_flag & P_ONRUNQ) == 0, ("already on runq2!"));
-               return;
-       }
-
-       KASSERT((p->p_flag & P_ONRUNQ) == 0, ("already on runq3!"));
-       p->p_flag |= P_ONRUNQ;
        /*
         * Otherwise place this process on the userland scheduler's run
         * queue for action.
         */
-
+       ++runqcount;
+       p->p_flag |= P_ONRUNQ;
        if (p->p_rtprio.type == RTP_PRIO_NORMAL) {
                pri = p->p_priority >> 2;
                q = &queues[pri];
                queuebits |= 1 << pri;
        } else if (p->p_rtprio.type == RTP_PRIO_REALTIME ||
                   p->p_rtprio.type == RTP_PRIO_FIFO) {
-               pri = p->p_rtprio.prio;
+               pri = (u_int8_t)p->p_rtprio.prio;
                q = &rtqueues[pri];
                rtqueuebits |= 1 << pri;
        } else if (p->p_rtprio.type == RTP_PRIO_IDLE) {
-               pri = p->p_rtprio.prio;
+               pri = (u_int8_t)p->p_rtprio.prio;
                q = &idqueues[pri];
                idqueuebits |= 1 << pri;
        } else {
                panic("setrunqueue: invalid rtprio type");
        }
+       KKASSERT(pri < 32);
        p->p_rqindex = pri;             /* remember the queue index */
        TAILQ_INSERT_TAIL(q, p, p_procq);
+
+       /*
+        * Wakeup other cpus to schedule the newly available thread.
+        * XXX doesn't really have to be in a critical section.
+        * We own giant after all.
+        */
+       if ((mask = ~curprocmask & rdyprocmask & mycpu->gd_other_cpus) != 0) {
+               int count = runqcount;
+               while (mask && count) {
+                       cpuid = bsfl(mask);
+                       KKASSERT((curprocmask & (1 << cpuid)) == 0);
+                       rdyprocmask &= ~(1 << cpuid);
+                       lwkt_schedule(&globaldata_find(cpuid)->gd_schedthread);
+                       --count;
+                       mask &= ~(1 << cpuid);
+               }
+       }
+       crit_exit();
 }
 
 /*
@@ -155,8 +248,11 @@ remrunqueue(struct proc *p)
        u_int32_t *which;
        u_int8_t pri;
 
+       crit_enter();
        KASSERT((p->p_flag & P_ONRUNQ) != 0, ("not on runq4!"));
        p->p_flag &= ~P_ONRUNQ;
+       --runqcount;
+       KKASSERT(runqcount >= 0);
        pri = p->p_rqindex;
        if (p->p_rtprio.type == RTP_PRIO_NORMAL) {
                q = &queues[pri];
@@ -177,42 +273,188 @@ remrunqueue(struct proc *p)
                        ("remrunqueue: remove from empty queue"));
                *which &= ~(1 << pri);
        }
+       crit_exit();
 }
 
 /*
- * chooseproc() is called when a cpu needs a user process to LWKT schedule.
- * chooseproc() will select a user process and return it.
+ * Release the P_CURPROC designation on the CURRENT process only.  This
+ * will allow another userland process to be scheduled and places our
+ * process back on the userland scheduling queue.
  */
-struct proc *
-chooseproc(void)
+void
+release_curproc(struct proc *p)
 {
-       struct proc *p;
-       struct rq *q;
-       u_int32_t *which;
-       u_int32_t pri;
+       int cpuid;
+       struct proc *np;
 
-       if (rtqueuebits) {
-               pri = ffs(rtqueuebits) - 1;
-               q = &rtqueues[pri];
-               which = &rtqueuebits;
-       } else if (queuebits) {
-               pri = ffs(queuebits) - 1;
-               q = &queues[pri];
-               which = &queuebits;
-       } else if (idqueuebits) {
-               pri = ffs(idqueuebits) - 1;
-               q = &idqueues[pri];
-               which = &idqueuebits;
-       } else {
-               return NULL;
+#ifdef ONLY_ONE_USER_CPU
+       KKASSERT(mycpu->gd_cpuid == 0 && p->p_thread->td_cpu == 0);
+#endif
+       crit_enter();
+       clear_resched();
+       cpuid = p->p_thread->td_cpu;
+       p->p_flag |= P_CP_RELEASED;
+       if (p->p_flag & P_CURPROC) {
+               p->p_flag &= ~P_CURPROC;
+               KKASSERT(curprocmask & (1 << cpuid));
+               if ((np = chooseproc()) != NULL) {
+                       np->p_flag |= P_CURPROC;
+                       lwkt_acquire(np->p_thread);
+                       lwkt_schedule(np->p_thread);
+               } else {
+                       curprocmask &= ~(1 << cpuid);
+               }
        }
-       p = TAILQ_FIRST(q);
-       KASSERT(p, ("chooseproc: no proc on busy queue"));
-       TAILQ_REMOVE(q, p, p_procq);
-       if (TAILQ_EMPTY(q))
-               *which &= ~(1 << pri);
-       KASSERT((p->p_flag & P_ONRUNQ) != 0, ("not on runq6!"));
-       p->p_flag &= ~P_ONRUNQ;
-       return p;
+       crit_exit();
 }
 
+/*
+ * Acquire the P_CURPROC designation on the CURRENT process only.  This
+ * function is called prior to returning to userland.  If the system
+ * call or trap did not block and if no reschedule was requested it is
+ * highly likely that the P_CURPROC flag is still set in the proc, and
+ * we do almost nothing here.
+ */
+void
+acquire_curproc(struct proc *p)
+{
+       int cpuid;
+       struct proc *np;
+
+       /*
+        * Short cut, we've already acquired the designation or we never
+        * lost it in the first place.
+        */
+       if ((p->p_flag & P_CURPROC) != 0)
+               return;
+
+       /*
+        * Long cut.  This pulls in a bit of the userland scheduler as 
+        * an optimization.  If our cpu has not scheduled a userland
+        * process we gladly fill the slot, otherwise we choose the best
+        * candidate from the run queue and compare it against ourselves,
+        * scheduling either us or him depending.
+        *
+        * If our cpu's slot isn't free we put ourselves on the userland
+        * run queue and switch away.  We should have P_CURPROC when we
+        * come back.  Note that a cpu change can occur when we come back.
+        *
+        * YYY don't need critical section, we hold giant and no interrupt
+        * will mess w/ this proc?  Or will it?  What about curprocmask?
+        */
+#ifdef ONLY_ONE_USER_CPU
+       KKASSERT(mycpu->gd_cpuid == 0 && p->p_thread->td_cpu == 0);
+#endif
+       crit_enter();
+       p->p_flag &= ~P_CP_RELEASED;
+       while ((p->p_flag & P_CURPROC) == 0) {
+               cpuid = p->p_thread->td_cpu;    /* load/reload cpuid */
+               if ((curprocmask & (1 << cpuid)) == 0) {
+                       curprocmask |= 1 << cpuid;
+                       if ((np = chooseproc()) != NULL) {
+                               KKASSERT((np->p_flag & P_CP_RELEASED) == 0);
+                               if (test_resched(p, np)) {
+                                       np->p_flag |= P_CURPROC;
+                                       lwkt_acquire(np->p_thread);
+                                       lwkt_schedule(np->p_thread);
+                               } else {
+                                       p->p_flag |= P_CURPROC;
+                                       setrunqueue(np);
+                               }
+                       } else {
+                               p->p_flag |= P_CURPROC;
+                       }
+               }
+               if ((p->p_flag & P_CURPROC) == 0) {
+                       lwkt_deschedule_self();
+                       setrunqueue(p);
+                       lwkt_switch();
+                       KKASSERT((p->p_flag & (P_ONRUNQ|P_CURPROC|P_CP_RELEASED)) == P_CURPROC);
+               }
+       }
+       crit_exit();
+}
+
+/*
+ * Yield / synchronous reschedule.  This is a bit tricky because the trap
+ * code might have set a lazy release on the switch function.  The first
+ * thing we do is call lwkt_switch() to resolve the lazy release (if any).
+ * Then, if we are a process, we want to allow another process to run.
+ *
+ * The only way to do that is to acquire and then release P_CURPROC.  We
+ * have to release it because the kernel expects it to be released as a
+ * sanity check when it goes to sleep.
+ *
+ * XXX we need a way to ensure that we wake up eventually from a yield,
+ * even if we are an idprio process.
+ */
+void
+uio_yield(void)
+{
+       struct thread *td = curthread;
+       struct proc *p = td->td_proc;
+
+       lwkt_switch();
+       if (p) {
+               acquire_curproc(p);
+               release_curproc(p);
+       }
+}
+
+
+/*
+ * For SMP systems a user scheduler helper thread is created for each
+ * cpu and is used to allow one cpu to wakeup another for the purposes of
+ * scheduling userland threads from setrunqueue().  UP systems do not
+ * need the helper since there is only one cpu.  We can't use the idle
+ * thread for this because we need to hold the MP lock.  Additionally,
+ * doing things this way allows us to HLT idle cpus on MP systems.
+ */
+
+#ifdef SMP
+
+static void
+sched_thread(void *dummy)
+{
+    int cpuid = mycpu->gd_cpuid;       /* doesn't change */
+    u_int32_t cpumask = 1 << cpuid;    /* doesn't change */
+
+#ifdef ONLY_ONE_USER_CPU
+    KKASSERT(cpuid == 0);
+#endif
+
+    get_mplock();                      /* hold the MP lock */
+    for (;;) {
+       struct proc *np;
+
+       rdyprocmask |= cpumask;
+       lwkt_deschedule_self();         /* interlock */
+       crit_enter();
+       if ((curprocmask & cpumask) == 0 && (np = chooseproc()) != NULL) {
+           curprocmask |= cpumask;
+           np->p_flag |= P_CURPROC;
+           lwkt_acquire(np->p_thread);
+           lwkt_schedule(np->p_thread);
+       }
+       crit_exit();
+       lwkt_switch();
+    }
+}
+
+void
+sched_thread_init(void)
+{
+    int cpuid = mycpu->gd_cpuid;
+
+    lwkt_create(sched_thread, NULL, NULL, &mycpu->gd_schedthread, 
+       TDF_STOPREQ, "usched %d", cpuid);
+    curprocmask &= ~(1 << cpuid);      /* schedule user proc on cpu */
+#ifdef ONLY_ONE_USER_CPU
+    if (cpuid)
+       curprocmask |= 1 << cpuid;      /* DISABLE USER PROCS */
+#endif
+    rdyprocmask |= 1 << cpuid;
+}
+
+#endif
+
index b01f725..d3ac9f9 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)kern_synch.c        8.9 (Berkeley) 5/19/95
  * $FreeBSD: src/sys/kern/kern_synch.c,v 1.87.2.6 2002/10/13 07:29:53 kbyanc Exp $
- * $DragonFly: src/sys/kern/kern_synch.c,v 1.15 2003/07/06 21:23:51 dillon Exp $
+ * $DragonFly: src/sys/kern/kern_synch.c,v 1.16 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_ktrace.h"
@@ -408,6 +408,7 @@ tsleep(ident, priority, wmesg, timo)
                crit_panicints();
                return (0);
        }
+       KKASSERT(td != &mycpu->gd_idlethread);  /* you must be kidding! */
        s = splhigh();
        KASSERT(ident != NULL, ("tsleep: no ident"));
        KASSERT(p == NULL || p->p_stat == SRUN, ("tsleep %p %s %d",
@@ -429,7 +430,7 @@ tsleep(ident, priority, wmesg, timo)
         * A SIGCONT would cause us to be marked as SSLEEP
         * without resuming us, thus we must be ready for sleep
         * when CURSIG is called.  If the wakeup happens while we're
-        * stopped, p->p_wchan will be 0 upon return from CURSIG.
+        * stopped, td->td_wchan will be 0 upon return from CURSIG.
         */
        if (p) {
                if (catch) {
@@ -442,7 +443,7 @@ tsleep(ident, priority, wmesg, timo)
                                p->p_stat = SRUN;
                                goto resume;
                        }
-                       if (p->p_wchan == NULL) {
+                       if (td->td_wchan == NULL) {
                                catch = 0;
                                goto resume;
                        }
@@ -460,6 +461,7 @@ tsleep(ident, priority, wmesg, timo)
                 */
                clrrunnable(p, SSLEEP);
                p->p_stats->p_ru.ru_nvcsw++;
+               KKASSERT(td->td_release || (p->p_flag & P_CURPROC) == 0);
                mi_switch();
                KASSERT(p->p_stat == SRUN, ("tsleep: stat not srun"));
        } else {
@@ -729,7 +731,8 @@ restart:
                                p->p_stat = SRUN;
                                if (p->p_flag & P_INMEM) {
                                        setrunqueue(p);
-                                       maybe_resched(p);
+                                       if (p->p_flag & P_CURPROC)
+                                           maybe_resched(p);
                                } else {
                                        p->p_flag |= P_SWAPINREQ;
                                        wakeup((caddr_t)&proc0);
@@ -758,45 +761,6 @@ wakeup_one(void *ident)
     _wakeup(ident, 1);
 }
 
-/*
- * Release the P_CURPROC designation on a process in order to allow the
- * userland scheduler to schedule another one.  This places a runnable
- * process back on the userland scheduler's run queue.
- *
- * Note that losing P_CURPROC does not effect LWKT scheduling, you can
- * still tsleep/wakeup after having lost P_CURPROC, but userret() will
- * not return to user mode until it gets it back.
- */
-static __inline
-void
-_relscurproc(struct proc *p)
-{
-       struct proc *np;
-
-       crit_enter();
-       if (p->p_flag & P_CURPROC) {
-               p->p_flag &= ~P_CURPROC;
-               lwkt_deschedule_self();
-               if (p->p_stat == SRUN && (p->p_flag & P_INMEM)) {
-                       setrunqueue(p);
-               }
-               if ((np = chooseproc()) != NULL) {
-                       np->p_flag |= P_CURPROC;
-                       lwkt_schedule(np->p_thread);
-               } else {
-                       KKASSERT(mycpu->gd_uprocscheduled == 1);
-                       mycpu->gd_uprocscheduled = 0;
-               }
-       }
-       crit_exit();
-}
-
-void
-relscurproc(struct proc *p)
-{
-       _relscurproc(p);
-}
-
 /*
  * The machine independent parts of mi_switch().
  * Must be called at splstatclock() or higher.
@@ -830,14 +794,6 @@ mi_switch()
        x = splstatclock();
        clear_resched();
 
-       /*
-        * If the process being switched out is the 'current' process then
-        * we have to lose the P_CURPROC designation and choose a new
-        * process.  If the process is not being LWKT managed and it is in
-        * SRUN we have to setrunqueue it.
-        */
-       _relscurproc(p);
-
        /*
         * Check if the process exceeds its cpu resource allocation.
         * If over max, kill it.  Time spent in interrupts is not 
@@ -859,14 +815,15 @@ mi_switch()
        }
 
        /*
-        * Pick a new current process and record its start time.
-        * YYY lwkt_switch() will run the heavy weight process restoration
-        * code, which removes the target thread and process from their
-        * respective run queues to temporarily mimic 5.x behavior.
-        * YYY the userland scheduler should pick only one user process
-        * at a time to run per cpu.
+        * Pick a new current process and record its start time.  If we
+        * are in a SSTOPped state we deschedule ourselves.  YYY this needs
+        * to be cleaned up, remember that LWKTs stay on their run queue
+        * which works differently then the user scheduler which removes
+        * the process from the runq when it runs it.
         */
        mycpu->gd_cnt.v_swtch++;
+       if (p->p_stat == SSTOP)
+               lwkt_deschedule_self();
        lwkt_switch();
 
        splx(x);
@@ -925,7 +882,7 @@ clrrunnable(struct proc *p, int stat)
        s = splhigh();
        switch(p->p_stat) {
        case SRUN:
-               if ((p->p_flag & (P_INMEM|P_CURPROC)) == P_INMEM)
+               if (p->p_flag & P_ONRUNQ)
                        remrunqueue(p);
                break;
        default:
@@ -935,31 +892,6 @@ clrrunnable(struct proc *p, int stat)
        splx(s);
 }
 
-/*
- * yield / synchronous reschedule
- *
- * Simply calling mi_switch() has the effect we want.  mi_switch will
- * deschedule the current thread, make sure the current process is on
- * the run queue, and then choose and reschedule another process.
- */
-void
-uio_yield()
-{
-       struct proc *p = curproc;
-       int s;
-               
-       s = splhigh();
-#if 0
-       KKASSERT(p->p_stat == SRUN);
-       if ((p->p_flag & (P_INMEM|P_CURPROC)) == (P_INMEM|P_CURPROC))
-               setrunqueue(p);
-       lwkt_deschedule_self();
-#endif
-       p->p_stats->p_ru.ru_nivcsw++;
-       mi_switch();
-       splx(s);
-}
-
 /*
  * Compute the priority of a process when running in user mode.
  * Arrange to reschedule if the resulting priority is better
@@ -982,8 +914,7 @@ resetpriority(struct proc *p)
        npq = newpriority / PPQ;
        crit_enter();
        opq = p->p_priority / PPQ;
-       if (p->p_stat == SRUN && (p->p_flag & (P_CURPROC|P_INMEM)) == P_INMEM
-           && opq != npq) {
+       if (p->p_stat == SRUN && (p->p_flag & P_ONRUNQ) && opq != npq) {
                /*
                 * We have to move the process to another queue
                 */
@@ -992,10 +923,10 @@ resetpriority(struct proc *p)
                setrunqueue(p);
        } else {
                /*
-                * Not on a queue or is on the same queue, we can just
-                * set the priority.
-                * YYY P_INMEM?
+                * We can just adjust the priority and it will be picked
+                * up later.
                 */
+               KKASSERT(opq == npq || (p->p_flag & P_ONRUNQ) == 0);
                p->p_priority = newpriority;
        }
        crit_exit();
index b8692fa..50c2a60 100644 (file)
@@ -28,7 +28,7 @@
  *     to use a critical section to avoid problems.  Foreign thread 
  *     scheduling is queued via (async) IPIs.
  *
- * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.17 2003/07/08 09:57:13 dillon Exp $
+ * $DragonFly: src/sys/kern/lwkt_thread.c,v 1.18 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -383,7 +383,8 @@ again:
                    nq = bsrl(rqmask);
                }
                if (ntd == NULL) {
-                   ntd = gd->gd_idletd;
+                   ntd = &gd->gd_idlethread;
+                   ntd->td_flags |= TDF_IDLE_NOHLT;
                } else {
                    TAILQ_REMOVE(&gd->gd_tdrunq[nq], ntd, td_threadq);
                    TAILQ_INSERT_TAIL(&gd->gd_tdrunq[nq], ntd, td_threadq);
@@ -397,12 +398,21 @@ again:
            TAILQ_INSERT_TAIL(&gd->gd_tdrunq[nq], ntd, td_threadq);
 #endif
        } else {
-           ntd = gd->gd_idletd;
+           ntd = &gd->gd_idlethread;
        }
     }
     KASSERT(ntd->td_pri >= TDPRI_CRIT,
        ("priority problem in lwkt_switch %d %d", td->td_pri, ntd->td_pri));
 
+    /*
+     * Passive release (used to transition from user to kernel mode
+     * when we block or switch rather then when we enter the kernel).
+     * This function is NOT called if we are switching into a preemption
+     * or returning from a preemption.
+     */
+    if (td->td_release)
+           td->td_release(td);
+
     /*
      * Do the actual switch.  If the new target does not need the MP lock
      * and we are holding it, release the MP lock.  If the new target requires
@@ -500,12 +510,19 @@ lwkt_preempt(thread_t ntd, int critpri)
        return;
     }
 #ifdef SMP
-    mpheld = MP_LOCK_HELD();
+    /*
+     * note: an interrupt might have occured just as we were transitioning
+     * to the MP lock, with the lock held but our mpcount still 0.  We have
+     * to be sure we restore the same condition when the preemption returns.
+     */
+    mpheld = MP_LOCK_HELD();   /* 0 or 1 */
     if (mpheld && td->td_mpcount == 0)
        panic("lwkt_preempt(): held and no count");
     savecnt = td->td_mpcount;
+    td->td_mpcount += mpheld;
     ntd->td_mpcount += td->td_mpcount;
     if (mpheld == 0 && ntd->td_mpcount && !cpu_try_mplock()) {
+       td->td_mpcount -= mpheld;
        ntd->td_mpcount -= td->td_mpcount;
        ++preempt_miss;
        need_resched();
@@ -519,6 +536,7 @@ lwkt_preempt(thread_t ntd, int critpri)
     td->td_switch(ntd);
     KKASSERT(ntd->td_preempted && (td->td_flags & TDF_PREEMPT_DONE));
 #ifdef SMP
+    td->td_mpcount -= mpheld;
     KKASSERT(savecnt == td->td_mpcount);
     if (mpheld == 0 && MP_LOCK_HELD())
        cpu_rel_mplock();
@@ -549,8 +567,17 @@ lwkt_yield_quick(void)
 {
     thread_t td = curthread;
 
+    /*
+     * gd_reqpri is cleared in splz if the cpl is 0.  If we were to clear
+     * it with a non-zero cpl then we might not wind up calling splz after
+     * a task switch when the critical section is exited even though the
+     * new task could accept the interrupt.  YYY alternative is to have
+     * lwkt_switch() just call splz unconditionally.
+     *
+     * XXX from crit_exit() only called after last crit section is released.
+     * If called directly will run splz() even if in a critical section.
+     */
     if ((td->td_pri & TDPRI_MASK) < mycpu->gd_reqpri) {
-       mycpu->gd_reqpri = 0;
        splz();
     }
 
@@ -694,6 +721,24 @@ lwkt_schedule(thread_t td)
     crit_exit();
 }
 
+void
+lwkt_acquire(thread_t td)
+{
+    struct globaldata *gd;
+
+    gd = td->td_gd;
+    KKASSERT((td->td_flags & TDF_RUNQ) == 0);
+    if (gd != mycpu) {
+       crit_enter();
+       TAILQ_REMOVE(&gd->gd_tdallq, td, td_allq);      /* protected by BGL */
+       gd = mycpu;
+       td->td_gd = gd;
+       td->td_cpu = gd->gd_cpuid;
+       TAILQ_INSERT_TAIL(&gd->gd_tdallq, td, td_allq); /* protected by BGL */
+       crit_exit();
+    }
+}
+
 /*
  * Deschedule a thread.
  *
@@ -863,15 +908,20 @@ lwkt_signal(lwkt_wait_t w)
  * must still release it even if you lose your access to it).
  *
  * YYY for now we use a critical section to prevent IPIs from taking away
- * a token, but we really only need to disable IPIs ?
+ * a token, but do we really only need to disable IPIs ?
  *
  * YYY certain tokens could be made to act like mutexes when performance
  * would be better (e.g. t_cpu == -1).  This is not yet implemented.
  *
- * If the token is owned by another cpu we may have to send an IPI to
+ * YYY the tokens replace 4.x's simplelocks for the most part, but this
+ * means that 4.x does not expect a switch so for now we cannot switch
+ * when waiting for an IPI to be returned.  
+ *
+ * YYY If the token is owned by another cpu we may have to send an IPI to
  * it and then block.   The IPI causes the token to be given away to the
  * requesting cpu, unless it has already changed hands.  Since only the
  * current cpu can give away a token it owns we do not need a memory barrier.
+ * This needs serious optimization.
  */
 
 #ifdef SMP
@@ -882,7 +932,11 @@ lwkt_gettoken_remote(void *arg)
 {
     lwkt_gettoken_req *req = arg;
     if (req->tok->t_cpu == mycpu->gd_cpuid) {
+       if (token_debug)
+           printf("GT(%d,%d) ", req->tok->t_cpu, req->cpu);
        req->tok->t_cpu = req->cpu;
+       req->tok->t_reqcpu = req->cpu;  /* YYY leave owned by target cpu */
+       /* else set reqcpu to point to current cpu for release */
     }
 }
 
@@ -900,12 +954,9 @@ lwkt_gettoken(lwkt_token_t tok)
 
     crit_enter();
 #ifdef INVARIANTS
-    if (token_debug) {
-       printf("gettoken %p %d/%d\n", ((int **)&tok)[-1], (curthread->td_proc?curthread->td_proc->p_pid:-1), curthread->td_pri);
-       if (curthread->td_pri > 2000) {
-           curthread->td_pri = 1000;
-           panic("too HIGH!");
-       }
+    if (curthread->td_pri > 2000) {
+       curthread->td_pri = 1000;
+       panic("too HIGH!");
     }
 #endif
 #ifdef SMP
@@ -917,8 +968,13 @@ lwkt_gettoken(lwkt_token_t tok)
        req.cpu = mycpu->gd_cpuid;
        req.tok = tok;
        dcpu = (volatile int)tok->t_cpu;
+       KKASSERT(dcpu >= 0 && dcpu < ncpus);
+       if (token_debug)
+           printf("REQT%d ", dcpu);
        seq = lwkt_send_ipiq(dcpu, lwkt_gettoken_remote, &req);
        lwkt_wait_ipiq(dcpu, seq);
+       if (token_debug)
+           printf("REQR%d ", tok->t_cpu);
     }
 #endif
     /*
@@ -1005,8 +1061,11 @@ lwkt_regettoken(lwkt_token_t tok)
            req.cpu = mycpu->gd_cpuid;
            req.tok = tok;
            dcpu = (volatile int)tok->t_cpu;
+           KKASSERT(dcpu >= 0 && dcpu < ncpus);
+           printf("REQT%d ", dcpu);
            seq = lwkt_send_ipiq(dcpu, lwkt_gettoken_remote, &req);
            lwkt_wait_ipiq(dcpu, seq);
+           printf("REQR%d ", tok->t_cpu);
        }
 #endif
        ++tok->t_gen;
@@ -1039,7 +1098,9 @@ lwkt_create(void (*func)(void *), void *arg,
     thread_t td;
     va_list ap;
 
-    td = *tdp = lwkt_alloc_thread(template);
+    td = lwkt_alloc_thread(template);
+    if (tdp)
+       *tdp = td;
     cpu_set_thread_handler(td, kthread_exit, func, arg);
     td->td_flags |= TDF_VERBOSE | tdflags;
 #ifdef SMP
@@ -1093,7 +1154,9 @@ kthread_create(void (*func)(void *), void *arg,
     thread_t td;
     va_list ap;
 
-    td = *tdp = lwkt_alloc_thread(NULL);
+    td = lwkt_alloc_thread(NULL);
+    if (tdp)
+       *tdp = td;
     cpu_set_thread_handler(td, kthread_exit, func, arg);
     td->td_flags |= TDF_VERBOSE;
 #ifdef SMP
@@ -1155,23 +1218,31 @@ lwkt_send_ipiq(int dcpu, ipifunc_t func, void *arg)
 {
     lwkt_ipiq_t ip;
     int windex;
+    struct globaldata *gd = mycpu;
 
-    if (dcpu == mycpu->gd_cpuid) {
+    if (dcpu == gd->gd_cpuid) {
        func(arg);
        return(0);
     } 
+    ++gd->gd_intr_nesting_level;
+#ifdef INVARIANTS
+    if (gd->gd_intr_nesting_level > 20)
+       panic("lwkt_send_ipiq: TOO HEAVILY NESTED!");
+#endif
     KKASSERT(curthread->td_pri >= TDPRI_CRIT);
     KKASSERT(dcpu >= 0 && dcpu < ncpus);
     ++ipiq_count;
-    ip = &mycpu->gd_ipiq[dcpu];
+    ip = &gd->gd_ipiq[dcpu];
     if (ip->ip_windex - ip->ip_rindex > MAXCPUFIFO / 2) {
        unsigned int eflags = read_eflags();
+       printf("SEND_IPIQ FIFO FULL\n");
        cpu_enable_intr();
        ++ipiq_fifofull;
        while (ip->ip_windex - ip->ip_rindex > MAXCPUFIFO / 2) {
            KKASSERT(ip->ip_windex - ip->ip_rindex != MAXCPUFIFO - 1);
            lwkt_process_ipiq();
        }
+       printf("SEND_IPIQ FIFO GOOD\n");
        write_eflags(eflags);
     }
     KKASSERT(ip->ip_windex - ip->ip_rindex != MAXCPUFIFO - 1);
@@ -1180,6 +1251,7 @@ lwkt_send_ipiq(int dcpu, ipifunc_t func, void *arg)
     ip->ip_arg[windex] = arg;
     /* YYY memory barrier */
     ++ip->ip_windex;
+    --gd->gd_intr_nesting_level;
     cpu_send_ipiq(dcpu);       /* issues memory barrier if appropriate */
     return(ip->ip_windex);
 }
@@ -1199,6 +1271,7 @@ void
 lwkt_wait_ipiq(int dcpu, int seq)
 {
     lwkt_ipiq_t ip;
+    int maxc = 100000000;
 
     if (dcpu != mycpu->gd_cpuid) {
        KKASSERT(dcpu >= 0 && dcpu < ncpus);
@@ -1211,6 +1284,10 @@ lwkt_wait_ipiq(int dcpu, int seq)
 #if 0
                lwkt_switch();  /* YYY fixme */
 #endif
+               if (--maxc == 0)
+                       printf("LWKT_WAIT_IPIQ WARNING! %d wait %d (%d)\n", mycpu->gd_cpuid, dcpu, ip->ip_rindex - seq);
+               if (maxc < -1000000)
+                       panic("LWKT_WAIT_IPIQ");
            }
            write_eflags(eflags);
        }
index 39c96af..adc250f 100644 (file)
@@ -29,7 +29,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/kern/sys_process.c,v 1.51.2.6 2003/01/08 03:06:45 kan Exp $
- * $DragonFly: src/sys/kern/sys_process.c,v 1.4 2003/06/25 03:55:57 dillon Exp $
+ * $DragonFly: src/sys/kern/sys_process.c,v 1.5 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include <sys/param.h>
@@ -48,6 +48,7 @@
 
 #include <sys/user.h>
 #include <miscfs/procfs/procfs.h>
+#include <sys/thread2.h>
 
 /* use the equivalent procfs code */
 #if 0
@@ -620,13 +621,17 @@ trace_req(p)
  */
 
 void
-stopevent(struct proc *p, unsigned int event, unsigned int val) {
+stopevent(struct proc *p, unsigned int event, unsigned int val) 
+{
        p->p_step = 1;
 
        do {
+               crit_enter();
+               wakeup(&p->p_stype);    /* Wake up any PIOCWAIT'ing procs */
                p->p_xstat = val;
                p->p_stype = event;     /* Which event caused the stop? */
-               wakeup(&p->p_stype);    /* Wake up any PIOCWAIT'ing procs */
                tsleep(&p->p_step, PWAIT, "stopevent", 0);
+               crit_exit();
        } while (p->p_step);
 }
+
index f7bc517..9fae9e0 100644 (file)
@@ -32,7 +32,7 @@
  *
  *     @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94
  * $FreeBSD: src/sys/kern/uipc_mbuf.c,v 1.51.2.24 2003/04/15 06:59:29 silby Exp $
- * $DragonFly: src/sys/kern/uipc_mbuf.c,v 1.4 2003/06/29 03:28:44 dillon Exp $
+ * $DragonFly: src/sys/kern/uipc_mbuf.c,v 1.5 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_param.h"
@@ -46,6 +46,7 @@
 #include <sys/domain.h>
 #include <sys/protosw.h>
 #include <sys/thread.h>
+#include <sys/globaldata.h>
 
 #include <vm/vm.h>
 #include <vm/vm_kern.h>
index 93195f6..15d0205 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     from: @(#)genassym.c    5.11 (Berkeley) 5/10/91
  * $FreeBSD: src/sys/i386/i386/genassym.c,v 1.86.2.3 2002/03/03 05:42:49 nyan Exp $
- * $DragonFly: src/sys/platform/pc32/i386/genassym.c,v 1.22 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/genassym.c,v 1.23 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "opt_user_ldt.h"
@@ -189,6 +189,7 @@ ASSYM(GD_CURTHREAD, offsetof(struct mdglobaldata, mi.gd_curthread));
 ASSYM(GD_REQPRI, offsetof(struct mdglobaldata, mi.gd_reqpri));
 ASSYM(GD_CPUID, offsetof(struct mdglobaldata, mi.gd_cpuid));
 ASSYM(GD_CNT, offsetof(struct mdglobaldata, mi.gd_cnt));
+ASSYM(GD_PRIVATE_TSS, offsetof(struct mdglobaldata, gd_private_tss));
 ASSYM(GD_INTR_NESTING_LEVEL, offsetof(struct mdglobaldata, mi.gd_intr_nesting_level));
 ASSYM(GD_ASTPENDING, offsetof(struct mdglobaldata, mi.gd_astpending));
 
@@ -201,7 +202,6 @@ ASSYM(GD_IPENDING, offsetof(struct mdglobaldata, gd_ipending));
 ASSYM(GD_COMMON_TSS, offsetof(struct mdglobaldata, gd_common_tss));
 ASSYM(GD_COMMON_TSSD, offsetof(struct mdglobaldata, gd_common_tssd));
 ASSYM(GD_TSS_GDT, offsetof(struct mdglobaldata, gd_tss_gdt));
-ASSYM(GD_IDLETHREAD, offsetof(struct mdglobaldata, gd_idlethread));
 ASSYM(GD_NPXTHREAD, offsetof(struct mdglobaldata, gd_npxthread));
 ASSYM(GD_CPU_LOCKID, offsetof(struct mdglobaldata, gd_cpu_lockid));
 ASSYM(GD_OTHER_CPUS, offsetof(struct mdglobaldata, gd_other_cpus));
index d341220..f7a7a07 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/i386/globals.s,v 1.13.2.1 2000/05/16 06:58:06 dillon Exp $
- * $DragonFly: src/sys/platform/pc32/i386/globals.s,v 1.15 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/globals.s,v 1.16 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "opt_user_ldt.h"
@@ -59,9 +59,8 @@
         * the per-cpu address space, otherwise it's in the data segment.
         */
        .globl  gd_curthread, gd_npxthread, gd_astpending, gd_reqpri
-       .globl  gd_common_tss, gd_idlethread
+       .globl  gd_common_tss
        .set    gd_curthread,globaldata + GD_CURTHREAD
-       .set    gd_idlethread,globaldata + GD_IDLETHREAD
        .set    gd_astpending,globaldata + GD_ASTPENDING
        .set    gd_reqpri,globaldata + GD_REQPRI
        .set    gd_npxthread,globaldata + GD_NPXTHREAD
        .globl  gd_ss_eflags, gd_intr_nesting_level
        .globl  gd_CMAP1, gd_CMAP2, gd_CMAP3, gd_PMAP1
        .globl  gd_CADDR1, gd_CADDR2, gd_CADDR3, gd_PADDR1
-       .globl  gd_ipending, gd_fpending, gd_cnt
+       .globl  gd_ipending, gd_fpending, gd_cnt, gd_private_tss
 
        .set    gd_cpuid,globaldata + GD_CPUID
+       .set    gd_private_tss,globaldata + GD_PRIVATE_TSS
        .set    gd_cpu_lockid,globaldata + GD_CPU_LOCKID
        .set    gd_other_cpus,globaldata + GD_OTHER_CPUS
        .set    gd_ss_eflags,globaldata + GD_SS_EFLAGS
index 616cc89..57016d0 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)machdep.c     7.4 (Berkeley) 6/3/91
  * $FreeBSD: src/sys/i386/i386/machdep.c,v 1.385.2.30 2003/05/31 08:48:05 alc Exp $
- * $DragonFly: src/sys/platform/pc32/i386/machdep.c,v 1.24 2003/07/08 09:57:11 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/machdep.c,v 1.25 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "apm.h"
@@ -946,10 +946,14 @@ cpu_halt(void)
  * and loop or halt as appropriate.  Giant is not held on entry to the thread.
  *
  * The main loop is entered with a critical section held, we must release
- * the critical section before doing anything else.
+ * the critical section before doing anything else.  lwkt_switch() will
+ * check for pending interrupts due to entering and exiting its own 
+ * critical section.
  *
- * Note on cpu_idle_hlt:  On an SMP system this may cause the system to 
- * halt until the next clock tick, even if a thread is ready YYY
+ * Note on cpu_idle_hlt:  On an SMP system we rely on a scheduler IPI
+ * to wake a HLTed cpu up.  However, there are cases where the idlethread
+ * will be entered with the possibility that no IPI will occur and in such
+ * cases lwkt_switch() sets TDF_IDLE_NOHLT.
  */
 static int     cpu_idle_hlt = 1;
 SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW,
@@ -958,18 +962,32 @@ SYSCTL_INT(_machdep, OID_AUTO, cpu_idle_hlt, CTLFLAG_RW,
 void
 cpu_idle(void)
 {
+       struct thread *td = curthread;
+
        crit_exit();
-       KKASSERT(curthread->td_pri < TDPRI_CRIT);
+       KKASSERT(td->td_pri < TDPRI_CRIT);
        for (;;) {
+               /*
+                * See if there are any LWKTs ready to go.
+                */
                lwkt_switch();
-               __asm __volatile("cli");
-               if (cpu_idle_hlt && !lwkt_runnable()) {
+
+               /*
+                * If we are going to halt call splz unconditionally after
+                * CLIing to catch any interrupt races.  Note that we are
+                * at SPL0 and interrupts are enabled.
+                */
+               if (cpu_idle_hlt && !lwkt_runnable() &&
+                   (td->td_flags & TDF_IDLE_NOHLT) == 0) {
                        /*
                         * We must guarentee that hlt is exactly the instruction
                         * following the sti.
                         */
+                       __asm __volatile("cli");
+                       splz();
                        __asm __volatile("sti; hlt");
                } else {
+                       td->td_flags &= ~TDF_IDLE_NOHLT;
                        __asm __volatile("sti");
                }
        }
@@ -1125,8 +1143,6 @@ union descriptor ldt[NLDT];               /* local descriptor table */
 /* table descriptors - used to load tables by cpu */
 struct region_descriptor r_gdt, r_idt;
 
-int private_tss;                       /* flag indicating private tss */
-
 #if defined(I586_CPU) && !defined(NO_F00F_HACK)
 extern int has_f00f_bug;
 #endif
@@ -1897,8 +1913,7 @@ init386(int first)
        lwkt_set_comm(&thread0, "thread0");
        proc0.p_addr = (void *)thread0.td_kstack;
        proc0.p_thread = &thread0;
-       proc0.p_flag |= P_CURPROC;
-       gd->mi.gd_uprocscheduled = 1;
+       proc0.p_flag |= P_CP_RELEASED;  /* early set.  See also init_main.c */
        thread0.td_proc = &proc0;
        thread0.td_switch = cpu_heavy_switch;   /* YYY eventually LWKT */
        safepri = thread0.td_cpl = SWI_MASK | HWI_MASK;
@@ -1983,7 +1998,6 @@ init386(int first)
        gd->gd_common_tss.tss_esp0 = (int) thread0.td_pcb - 16;
        gd->gd_common_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL) ;
        gsel_tss = GSEL(GPROC0_SEL, SEL_KPL);
-       private_tss = 0;
        gd->gd_tss_gdt = &gdt[GPROC0_SEL].sd;
        gd->gd_common_tssd = *gd->gd_tss_gdt;
        gd->gd_common_tss.tss_ioopt = (sizeof gd->gd_common_tss) << 16;
@@ -2062,15 +2076,14 @@ cpu_gdinit(struct mdglobaldata *gd, int cpu)
        char *sp;
 
        if (cpu)
-               gd->mi.gd_curthread = &gd->gd_idlethread;
+               gd->mi.gd_curthread = &gd->mi.gd_idlethread;
 
-       gd->mi.gd_idletd = &gd->gd_idlethread;
        sp = gd->mi.gd_prvspace->idlestack;
-       lwkt_init_thread(&gd->gd_idlethread, sp, 0, &gd->mi);
-       lwkt_set_comm(&gd->gd_idlethread, "idle_%d", cpu);
-       gd->gd_idlethread.td_switch = cpu_lwkt_switch;
-       gd->gd_idlethread.td_sp -= sizeof(void *);
-       *(void **)gd->gd_idlethread.td_sp = cpu_idle_restore;
+       lwkt_init_thread(&gd->mi.gd_idlethread, sp, 0, &gd->mi);
+       lwkt_set_comm(&gd->mi.gd_idlethread, "idle_%d", cpu);
+       gd->mi.gd_idlethread.td_switch = cpu_lwkt_switch;
+       gd->mi.gd_idlethread.td_sp -= sizeof(void *);
+       *(void **)gd->mi.gd_idlethread.td_sp = cpu_idle_restore;
 }
 
 struct globaldata *
index 3bdebfd..bee55f9 100644 (file)
@@ -23,7 +23,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/i386/mp_machdep.c,v 1.115.2.15 2003/03/14 21:22:35 jhb Exp $
- * $DragonFly: src/sys/platform/pc32/i386/mp_machdep.c,v 1.10 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/mp_machdep.c,v 1.11 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "opt_cpu.h"
@@ -2461,6 +2461,11 @@ ap_init(void)
                smp_active = 1;  /* historic */
        }
 
+       /*
+        * Startup helper thread(s) one per cpu.
+        */
+       sched_thread_init();
+
        /*
         * The idle loop doesn't expect the BGL to be held and while
         * lwkt_switch() normally cleans things up this is a special case
index 2032f3c..f45bf7b 100644 (file)
@@ -7,7 +7,7 @@
  * ----------------------------------------------------------------------------
  *
  * $FreeBSD: src/sys/i386/i386/mplock.s,v 1.29.2.2 2000/05/16 06:58:06 dillon Exp $
- * $DragonFly: src/sys/platform/pc32/i386/mplock.s,v 1.5 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/mplock.s,v 1.6 2003/07/10 04:47:53 dillon Exp $
  *
  * Functions for locking between CPUs in a SMP system.
  *
 
 #include "assym.s"
 
+/*
+ * YYY Debugging only.  Define this to be paranoid about invalidating the
+ * TLB when we get giant.
+ */
+#undef PARANOID_INVLTLB
+
        .data
        ALIGN_DATA
 #ifdef SMP
@@ -58,6 +64,9 @@ NON_GPROF_ENTRY(cpu_try_mplock)
        movl    $-1,%eax
        lock cmpxchgl %ecx,mp_lock      /* ecx<->mem if eax matches */
        jnz     1f
+#ifdef PARANOID_INVLTLB
+       movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
+#endif
        movl    $1,%eax
        NON_GPROF_RET
 1:
@@ -69,6 +78,11 @@ NON_GPROF_ENTRY(get_mplock)
        cmpl    $0,TD_MPCOUNT(%edx)
        je      1f
        incl    TD_MPCOUNT(%edx)        /* already have it, just ++mpcount */
+#ifdef INVARIANTS
+       movl    PCPU(cpuid),%eax        /* failure */
+       cmpl    %eax,mp_lock
+       jne     4f
+#endif
        NON_GPROF_RET
 1:
        pushfl
@@ -78,6 +92,9 @@ NON_GPROF_ENTRY(get_mplock)
        movl    $-1,%eax
        lock cmpxchgl %ecx,mp_lock      /* ecx<->mem & JZ if eax matches */
        jnz     2f
+#ifdef PARANOID_INVLTLB
+       movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
+#endif
        popfl                           /* success */
        NON_GPROF_RET
 2:
@@ -89,6 +106,11 @@ NON_GPROF_ENTRY(get_mplock)
        addl    $TDPRI_CRIT,TD_PRI(%edx)
        popfl
        call    lwkt_switch             /* will be correct on return */
+#ifdef INVARIANTS
+       movl    PCPU(cpuid),%eax        /* failure */
+       cmpl    %eax,mp_lock
+       jne     4f
+#endif
        movl    PCPU(curthread),%edx
        subl    $TDPRI_CRIT,TD_PRI(%edx)
        NON_GPROF_RET
@@ -98,11 +120,21 @@ NON_GPROF_ENTRY(get_mplock)
        popfl
        NON_GPROF_RET
 
+4:
+       cmpl    $0,panicstr             /* don't double panic */
+       je      badmp_get2
+       NON_GPROF_RET
+
 NON_GPROF_ENTRY(try_mplock)
        movl    PCPU(curthread),%edx
        cmpl    $0,TD_MPCOUNT(%edx)
        je      1f
        incl    TD_MPCOUNT(%edx)        /* already have it, just ++mpcount */
+#ifdef INVARIANTS
+       movl    PCPU(cpuid),%eax        /* failure */
+       cmpl    %eax,mp_lock
+       jne     4b
+#endif
        movl    $1,%eax
        NON_GPROF_RET
 1:
@@ -113,6 +145,9 @@ NON_GPROF_ENTRY(try_mplock)
        lock cmpxchgl %ecx,mp_lock      /* ecx<->mem & JZ if eax matches */
        jnz     2f
        movl    $1,TD_MPCOUNT(%edx)
+#ifdef PARANOID_INVLTLB
+       movl    %cr3,%eax; movl %eax,%cr3       /* YYY check and remove */
+#endif
        popfl                           /* success */
        movl    $1,%eax
        NON_GPROF_RET
@@ -159,6 +194,9 @@ NON_GPROF_ENTRY(rel_mplock)
 badmp_get:
        pushl   $bmpsw1
        call    panic
+badmp_get2:
+       pushl   $bmpsw1a
+       call    panic
 badmp_rel:
        pushl   $bmpsw2
        call    panic
@@ -171,6 +209,9 @@ badmp_rel2:
 bmpsw1:
        .asciz  "try/get_mplock(): already have lock! %d %p"
 
+bmpsw1a:
+       .asciz  "try/get_mplock(): failed on count or switch %d %p"
+
 bmpsw2:
        .asciz  "rel_mplock(): mpcount already 0 @ %p %p %p %p %p %p %p %p!"
 
index 6faa71d..392399b 100644 (file)
@@ -40,7 +40,7 @@
  *
  *     from:   @(#)pmap.c      7.7 (Berkeley)  5/12/91
  * $FreeBSD: src/sys/i386/i386/pmap.c,v 1.250.2.18 2002/03/06 22:48:53 silby Exp $
- * $DragonFly: src/sys/platform/pc32/i386/pmap.c,v 1.16 2003/07/06 21:23:48 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/pmap.c,v 1.17 2003/07/10 04:47:53 dillon Exp $
  */
 
 /*
@@ -345,11 +345,17 @@ pmap_bootstrap(firstaddr, loadaddr)
        for (i = 0; i < NKPT; i++)
                PTD[i] = 0;
 
-       /* XXX - see also mp_machdep.c */
-       if (ncpus == 1 && (cpu_feature & CPUID_PGE))
+       /*
+        * PG_G is terribly broken on SMP because we IPI invltlb's in some
+        * cases rather then invl1pg.  Actually, I don't even know why it
+        * works under UP because self-referential page table mappings
+        */
+#ifdef SMP
+       pgeflag = 0;
+#else
+       if (cpu_feature & CPUID_PGE)
                pgeflag = PG_G;
-       else
-               pgeflag = 0;
+#endif
        
 /*
  * Initialize the 4MB page size flag
@@ -3101,6 +3107,9 @@ i386_protection_init()
  * address space. Return a pointer to where it is mapped. This
  * routine is intended to be used for mapping device memory,
  * NOT real memory.
+ *
+ * NOTE: we can't use pgeflag unless we invalidate the pages one at
+ * a time.
  */
 void *
 pmap_mapdev(pa, size)
@@ -3120,7 +3129,7 @@ pmap_mapdev(pa, size)
        pa = pa & PG_FRAME;
        for (tmpva = va; size > 0;) {
                pte = (unsigned *)vtopte(tmpva);
-               *pte = pa | PG_RW | PG_V | pgeflag;
+               *pte = pa | PG_RW | PG_V; /* | pgeflag; */
                size -= PAGE_SIZE;
                tmpva += PAGE_SIZE;
                pa += PAGE_SIZE;
@@ -3206,7 +3215,7 @@ pmap_activate(struct proc *p)
 
        pmap = vmspace_pmap(p->p_vmspace);
 #if defined(SMP)
-       pmap->pm_active |= 1 << mycpu->gd_cpuid;
+       atomic_set_int(&pmap->pm_active, 1 << mycpu->gd_cpuid);
 #else
        pmap->pm_active |= 1;
 #endif
index 132a0fd..9a1166b 100644 (file)
@@ -35,7 +35,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/i386/swtch.s,v 1.89.2.10 2003/01/23 03:36:24 ps Exp $
- * $DragonFly: src/sys/platform/pc32/i386/swtch.s,v 1.22 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/swtch.s,v 1.23 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "npx.h"
 
 #include "assym.s"
 
+#if defined(SMP)
+#define MPLOCKED        lock ;
+#else
+#define MPLOCKED
+#endif
+
        .data
 
        .globl  panic
@@ -84,7 +90,7 @@ ENTRY(cpu_heavy_switch)
        cli
        movl    P_VMSPACE(%ecx), %edx
        movl    PCPU(cpuid), %eax
-       btrl    %eax, VM_PMAP+PM_ACTIVE(%edx)
+       MPLOCKED btrl   %eax, VM_PMAP+PM_ACTIVE(%edx)
 
        /*
         * Save general regs
@@ -134,7 +140,8 @@ ENTRY(cpu_heavy_switch)
 1:
  
        /*
-        * Save the FP state if we have used the FP.
+        * Save the FP state if we have used the FP.  Note that calling
+        * npxsave will NULL out PCPU(npxthread).
         */
 #if NNPX > 0
        movl    P_THREAD(%ecx),%ecx
@@ -243,46 +250,54 @@ ENTRY(cpu_heavy_restore)
        incl    _swtch_optim_stats
 #endif
        /*
-        * Restore the MMU address space
+        * Tell the pmap that our cpu is using the VMSPACE now.  We cannot
+        * safely test/reload %cr3 until after we have set the bit in the
+        * pmap (remember, we do not hold the MP lock in the switch code).
         */
-       movl    %cr3,%ebx
-       cmpl    PCB_CR3(%edx),%ebx
+       movl    P_VMSPACE(%ecx), %ebx
+       movl    PCPU(cpuid), %eax
+       MPLOCKED btsl   %eax, VM_PMAP+PM_ACTIVE(%ebx)
+
+       /*
+        * Restore the MMU address space.  If it is the same as the last
+        * thread we don't have to invalidate the tlb (i.e. reload cr3).
+        * YYY which naturally also means that the PM_ACTIVE bit had better
+        * already have been set before we set it above, check? YYY
+        */
+       movl    %cr3,%eax
+       movl    PCB_CR3(%edx),%ebx
+       cmpl    %eax,%ebx
        je      4f
 #if defined(SWTCH_OPTIM_STATS)
        decl    _swtch_optim_stats
        incl    _tlb_flush_count
 #endif
-       movl    PCB_CR3(%edx),%ebx
        movl    %ebx,%cr3
 4:
-
        /*
         * Deal with the PCB extension, restore the private tss
         */
-       movl    PCPU(cpuid), %esi
-       cmpl    $0, PCB_EXT(%edx)               /* has pcb extension? */
-       je      1f
-       btsl    %esi, private_tss               /* mark use of private tss */
-       movl    PCB_EXT(%edx), %edi             /* new tss descriptor */
-       jmp     2f
-1:
+       movl    PCB_EXT(%edx),%edi      /* check for a PCB extension */
+       movl    $1,%ebx                 /* maybe mark use of a private tss */
+       testl   %edi,%edi
+       jnz     2f
 
        /*
-        * update common_tss.tss_esp0 pointer.  This is the supervisor
-        * stack pointer on entry from user mode.  Since the pcb is
-        * at the top of the supervisor stack esp0 starts just below it.
-        * We leave enough space for vm86 (16 bytes).
-        *
-        * common_tss.tss_esp0 is needed when user mode traps into the
-        * kernel.
+        * Going back to the common_tss.  We may need to update TSS_ESP0
+        * which sets the top of the supervisor stack when entering from
+        * usermode.  The PCB is at the top of the stack but we need another
+        * 16 bytes to take vm86 into account.
         */
        leal    -16(%edx),%ebx
        movl    %ebx, PCPU(common_tss) + TSS_ESP0
 
-       btrl    %esi, private_tss
-       jae     3f
+       cmpl    $0,PCPU(private_tss)    /* don't have to reload if      */
+       je      3f                      /* already using the common TSS */
+
+       subl    %ebx,%ebx               /* unmark use of private tss */
 
        /*
+        * Get the address of the common TSS descriptor for the ltr.
         * There is no way to get the address of a segment-accessed variable
         * so we store a self-referential pointer at the base of the per-cpu
         * data area and add the appropriate offset.
@@ -292,9 +307,10 @@ ENTRY(cpu_heavy_restore)
 
        /*
         * Move the correct TSS descriptor into the GDT slot, then reload
-        * tr.   YYY not sure what is going on here
+        * ltr.
         */
 2:
+       movl    %ebx,PCPU(private_tss)          /* mark/unmark private tss */
        movl    PCPU(tss_gdt), %ebx             /* entry in GDT */
        movl    0(%edi), %eax
        movl    %eax, 0(%ebx)
@@ -303,14 +319,7 @@ ENTRY(cpu_heavy_restore)
        movl    $GPROC0_SEL*8, %esi             /* GSEL(entry, SEL_KPL) */
        ltr     %si
 
-       /*
-        * Tell the pmap that our cpu is using the VMSPACE now.
-        */
 3:
-       movl    P_VMSPACE(%ecx), %ebx
-       movl    PCPU(cpuid), %eax
-       btsl    %eax, VM_PMAP+PM_ACTIVE(%ebx)
-
        /*
         * Restore general registers.
         */
@@ -381,36 +390,11 @@ cpu_switch_load_gs:
 
 CROSSJUMPTARGET(sw1a)
 
-badsw0:
-       pushl   %eax
-       pushl   $sw0_1
-       call    panic
-
-sw0_1: .asciz  "cpu_switch: panic: %p"
-
-#ifdef DIAGNOSTIC
-badsw1:
-       pushl   $sw0_1
-       call    panic
-
-sw0_1: .asciz  "cpu_switch: has wchan"
-
 badsw2:
        pushl   $sw0_2
        call    panic
 
 sw0_2: .asciz  "cpu_switch: not SRUN"
-#endif
-
-#if defined(SMP) && defined(DIAGNOSTIC)
-badsw4:
-       pushl   $sw0_4
-       call    panic
-
-sw0_4: .asciz  "cpu_switch: do not have lock"
-#endif /* SMP && DIAGNOSTIC */
-
-string:        .asciz  "SWITCHING\n"
 
 /*
  * savectx(pcb)
@@ -473,7 +457,7 @@ ENTRY(savectx)
        ret
 
 /*
- * cpu_idle_restore()  (current thread in %eax on entry)
+ * cpu_idle_restore()  (current thread in %eax on entry) (one-time execution)
  *
  *     Don't bother setting up any regs other then %ebp so backtraces
  *     don't die.  This restore function is used to bootstrap into the
@@ -487,8 +471,10 @@ ENTRY(savectx)
  *     cpus.
  */
 ENTRY(cpu_idle_restore)
+       movl    IdlePTD,%ecx
        movl    $0,%ebp
        pushl   $0
+       movl    %ecx,%cr3
 #ifdef SMP
        cmpl    $0,PCPU(cpuid)
        je      1f
@@ -499,7 +485,7 @@ ENTRY(cpu_idle_restore)
        jmp     cpu_idle
 
 /*
- * cpu_kthread_restore()       (current thread is %eax on entry)
+ * cpu_kthread_restore() (current thread is %eax on entry) (one-time execution)
  *
  *     Don't bother setting up any regs other then %ebp so backtraces
  *     don't die.  This restore function is used to bootstrap into an
@@ -510,8 +496,10 @@ ENTRY(cpu_idle_restore)
  *     we can release our critical section and enable interrupts early.
  */
 ENTRY(cpu_kthread_restore)
+       movl    IdlePTD,%ecx
        movl    TD_PCB(%eax),%ebx
        movl    $0,%ebp
+       movl    %ecx,%cr3
        subl    $TDPRI_CRIT,TD_PRI(%eax)
        sti
        popl    %edx            /* kthread exit function */
@@ -554,8 +542,18 @@ ENTRY(cpu_lwkt_switch)
  *     Warning: due to preemption the restore function can be used to 
  *     'return' to the original thread.  Interrupt disablement must be
  *     protected through the switch so we cannot run splz here.
+ *
+ *     YYY we theoretically do not need to load IdlePTD into cr3, but if
+ *     so we need a way to detect when the PTD we are using is being 
+ *     deleted due to a process exiting.
  */
 ENTRY(cpu_lwkt_restore)
+       movl    IdlePTD,%ecx    /* YYY borrow but beware desched/cpuchg/exit */
+       movl    %cr3,%eax
+       cmpl    %ecx,%eax
+       je      1f
+       movl    %ecx,%cr3
+1:
        popfl
        popl    %edi
        popl    %esi
index b876b91..41d012b 100644 (file)
@@ -36,7 +36,7 @@
  *
  *     from: @(#)trap.c        7.4 (Berkeley) 5/13/91
  * $FreeBSD: src/sys/i386/i386/trap.c,v 1.147.2.11 2003/02/27 19:09:59 luoqi Exp $
- * $DragonFly: src/sys/platform/pc32/i386/trap.c,v 1.19 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/trap.c,v 1.20 2003/07/10 04:47:53 dillon Exp $
  */
 
 /*
@@ -162,28 +162,19 @@ SYSCTL_INT(_machdep, OID_AUTO, panic_on_nmi, CTLFLAG_RW,
  * point of view of the userland scheduler unless we actually have to
  * switch.
  *
- * usertdsw is called from within a critical section, but the BGL will
- * have already been released by lwkt_switch() so only call MP safe functions
- * that don't block and don't require the BGL!
+ * usertdsw is called from within a critical section and the BGL will still
+ * be held.  This function is NOT called for preemptions, only for switchouts.
  */
 static void
-usertdsw(struct thread *ntd)
+passive_release(struct thread *td)
 {
-       struct thread *td = curthread;
+       struct proc *p = td->td_proc;
 
-       td->td_switch = cpu_heavy_switch;
+       td->td_release = NULL;
        lwkt_setpri_self(TDPRI_KERN_USER);
-#if 0
-       /* 
-        * This is where we might want to catch the P_CURPROC designation
-        * and fix it for *any* switchout rather then just an mi_switch()
-        * switchout (move from mi_switch()?) YYY
-        */
        if (p->p_flag & P_CURPROC) {
-               ...
+               release_curproc(p);
        }
-#endif
-       td->td_switch(ntd);
 }
 
 /*
@@ -195,40 +186,26 @@ usertdsw(struct thread *ntd)
 static __inline void
 userenter(void)
 {
-       struct thread *td;
+       struct thread *td = curthread;
 
-       td = curthread;
-       KASSERT(td->td_switch == cpu_heavy_switch,
-               ("userenter: bad td_switch = %p", td->td_switch));
-#if 0
-       KASSERT(td->td_switch == cpu_heavy_switch || td->td_switch == usertdsw,
-               ("userenter: bad td_switch = %p", td->td_switch));
-#endif
-       td->td_switch = usertdsw;
+       td->td_release = passive_release;
 }
 
-static void
-userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
+static __inline void
+userexit(struct proc *p)
 {
-       int sig, s;
-       struct thread *td = curthread;
+       struct thread *td = p->p_thread;
 
        /*
-        * Post any pending signals
+        * If we did not have to release we should already be P_CURPROC.  If
+        * we did have to release we must acquire P_CURPROC again and then
+        * restore our priority for user return.
         */
-       crit_enter();
-       while ((sig = CURSIG(p)) != 0) {
-               crit_exit();
-               postsig(sig);
-               crit_enter();
-       }
-
-       /*
-        * Set our priority properly and restore our switch function.  If
-        * we did not hit our lazy switch function in the first place we
-        * do not need to restore anything.
-        */
-       if (td->td_switch == cpu_heavy_switch) {
+       if (td->td_release) {
+               td->td_release = NULL;
+               KKASSERT(p->p_flag & P_CURPROC);
+       } else {
+               acquire_curproc(p);
                switch(p->p_rtprio.type) {
                case RTP_PRIO_IDLE:
                        lwkt_setpri_self(TDPRI_USER_IDLE);
@@ -241,21 +218,35 @@ userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
                        lwkt_setpri_self(TDPRI_USER_NORM);
                        break;
                }
-       } else {
-               KKASSERT(td->td_switch == usertdsw);
-               td->td_switch = cpu_heavy_switch;
        }
-       crit_exit();
+}
+
+
+static void
+userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
+{
+       int sig;
+
+       /*
+        * Post any pending signals
+        */
+       while ((sig = CURSIG(p)) != 0) {
+               postsig(sig);
+       }
 
        /*
-        * If a reschedule has been requested we call chooseproc() to locate
-        * the next runnable process.  When we wakeup from that we check
-        * for pending signals again.
+        * If a reschedule has been requested we lwkt_switch().  The
+        * lwkt_switch() will ensure that our current process is released
+        * (see the use of td_release) as well as ensure that any pending
+        * LWKTs get run before we get cpu back.
+        *
+        * YYY though of doreti detects that we were in a user context
+        * it should really just call lwkt_switch()!  and are re-acquisition 
+        * of the current process below will handle userland scheduling
+        * priorities.
         */
        if (resched_wanted()) {
-               uio_yield();
-               while ((sig = CURSIG(p)) != 0)
-                       postsig(sig);
+               lwkt_switch();
        }
 
        /*
@@ -267,18 +258,10 @@ userret(struct proc *p, struct trapframe *frame, u_quad_t oticks)
        }
 
        /*
-        * In order to return to userland we need to be the designated
-        * current (user) process on this cpu.  We have to wait for
-        * the userland scheduler to schedule as P_CURPROC.
+        * Post any pending signals XXX
         */
-       s = splhigh();
-       while ((p->p_flag & P_CURPROC) == 0) {
-               p->p_stats->p_ru.ru_nivcsw++;
-               lwkt_deschedule_self();
-               mi_switch();
-       }
-       splx(s);
-       KKASSERT(mycpu->gd_uprocscheduled == 1);
+       while ((sig = CURSIG(p)) != 0)
+               postsig(sig);
 }
 
 #ifdef DEVICE_POLLING
@@ -291,8 +274,14 @@ extern int ether_poll __P((int count));
  * This common code is called from assembly language IDT gate entry
  * routines that prepare a suitable stack frame, and restore this
  * frame after the exception has been processed.
+ *
+ * This function is also called from doreti in an interlock to handle ASTs.
+ * For example:  hardwareint->INTROUTINE->(set ast)->doreti->trap
+ *
+ * NOTE!  We have to retrieve the fault address prior to obtaining the
+ * MP lock because get_mplock() may switch out.  YYY cr2 really ought
+ * to be retrieved by the assembly code, not here.
  */
-
 void
 trap(frame)
        struct trapframe frame;
@@ -302,24 +291,38 @@ trap(frame)
        int i = 0, ucode = 0, type, code;
        vm_offset_t eva;
 
-#ifdef SMP
-       if (panicstr == NULL)
-           KASSERT(curthread->td_mpcount >= 0, ("BADX1 AT %08x %08x", frame.tf_eip, frame.tf_esp));
-#endif
-       get_mplock();
-#ifdef SMP
-       if (panicstr == NULL)
-           KKASSERT(curthread->td_mpcount > 0);
-#endif
-
 #ifdef DDB
        if (db_active) {
                eva = (frame.tf_trapno == T_PAGEFLT ? rcr2() : 0);
+               get_mplock();
                trap_fatal(&frame, eva);
                goto out2;
        }
 #endif
 
+       eva = 0;
+       if (frame.tf_trapno == T_PAGEFLT) {
+               /*
+                * For some Cyrix CPUs, %cr2 is clobbered by interrupts.
+                * This problem is worked around by using an interrupt
+                * gate for the pagefault handler.  We are finally ready
+                * to read %cr2 and then must reenable interrupts.
+                *
+                * XXX this should be in the switch statement, but the
+                * NO_FOOF_HACK and VM86 goto and ifdefs obfuscate the
+                * flow of control too much for this to be obviously
+                * correct.
+                */
+               eva = rcr2();
+               get_mplock();
+               cpu_enable_intr();
+       } else {
+               get_mplock();
+       }
+       /*
+        * MP lock is held at this point
+        */
+
        if (!(frame.tf_eflags & PSL_I)) {
                /*
                 * Buggy application or kernel code has disabled interrupts
@@ -328,36 +331,21 @@ trap(frame)
                 * they are accidentally enabled later.
                 */
                type = frame.tf_trapno;
-               if (ISPL(frame.tf_cs) == SEL_UPL || (frame.tf_eflags & PSL_VM))
+               if (ISPL(frame.tf_cs)==SEL_UPL || (frame.tf_eflags & PSL_VM)) {
                        printf(
                            "pid %ld (%s): trap %d with interrupts disabled\n",
                            (long)curproc->p_pid, curproc->p_comm, type);
-               else if (type != T_BPTFLT && type != T_TRCTRAP)
+               } else if (type != T_BPTFLT && type != T_TRCTRAP) {
                        /*
                         * XXX not quite right, since this may be for a
                         * multiple fault in user mode.
                         */
                        printf("kernel trap %d with interrupts disabled\n",
                            type);
+               }
                cpu_enable_intr();
        }
 
-       eva = 0;
-       if (frame.tf_trapno == T_PAGEFLT) {
-               /*
-                * For some Cyrix CPUs, %cr2 is clobbered by interrupts.
-                * This problem is worked around by using an interrupt
-                * gate for the pagefault handler.  We are finally ready
-                * to read %cr2 and then must reenable interrupts.
-                *
-                * XXX this should be in the switch statement, but the
-                * NO_FOOF_HACK and VM86 goto and ifdefs obfuscate the
-                * flow of control too much for this to be obviously
-                * correct.
-                */
-               eva = rcr2();
-               cpu_enable_intr();
-       }
 
 #ifdef DEVICE_POLLING
        if (poll_in_trap)
@@ -750,6 +738,7 @@ out:
                KASSERT(curthread->td_mpcount == 1, ("badmpcount trap from %p", (void *)frame.tf_eip));
 #endif
        userret(p, &frame, sticks);
+       userexit(p);
 out2:
 #ifdef SMP
        KKASSERT(curthread->td_mpcount > 0);
@@ -1345,6 +1334,7 @@ bad:
         */
        STOPEVENT(p, S_SCX, code);
 
+       userexit(p);
 #ifdef SMP
        /*
         * Release the MP lock if we had to get it
@@ -1374,6 +1364,7 @@ fork_return(p, frame)
        if (KTRPOINT(p->p_thread, KTR_SYSRET))
                ktrsysret(p->p_tracep, SYS_fork, 0, 0);
 #endif
+       userexit(p);
 #ifdef SMP
        KKASSERT(curthread->td_mpcount == 1);
        rel_mplock();
index be9bf6e..5349ace 100644 (file)
@@ -39,7 +39,7 @@
  *     from: @(#)vm_machdep.c  7.3 (Berkeley) 5/13/91
  *     Utah $Hdr: vm_machdep.c 1.16.1.1 89/06/23$
  * $FreeBSD: src/sys/i386/i386/vm_machdep.c,v 1.132.2.9 2003/01/25 19:02:23 dillon Exp $
- * $DragonFly: src/sys/platform/pc32/i386/vm_machdep.c,v 1.18 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/vm_machdep.c,v 1.19 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "npx.h"
@@ -146,7 +146,7 @@ cpu_fork(p1, p2, flags)
 
 #if NNPX > 0
        /* Ensure that p1's pcb is up to date. */
-       if (npxthread == p1->p_thread)
+       if (mdcpu->gd_npxthread == p1->p_thread)
                npxsave(&p1->p_thread->td_pcb->pcb_save);
 #endif
 
index b55a4b0..aaa4309 100644 (file)
@@ -28,7 +28,7 @@
  *     should not include this file.
  *
  * $FreeBSD: src/sys/i386/include/globaldata.h,v 1.11.2.1 2000/05/16 06:58:10 dillon Exp $
- * $DragonFly: src/sys/platform/pc32/include/globaldata.h,v 1.16 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/include/globaldata.h,v 1.17 2003/07/10 04:47:53 dillon Exp $
  */
 
 #ifndef _MACHINE_GLOBALDATA_H_
@@ -61,7 +61,6 @@
  */
 struct mdglobaldata {
        struct globaldata mi;
-       struct thread   gd_idlethread;
        struct segment_descriptor gd_common_tssd;
        struct segment_descriptor *gd_tss_gdt;
        struct thread   *gd_npxthread;
@@ -69,6 +68,7 @@ struct mdglobaldata {
        int             gd_fpending;    /* fast interrupt pending */
        int             gd_ipending;    /* normal interrupt pending */
        int             gd_currentldt;  /* USER_LDT */
+       int             gd_private_tss;
        u_int           gd_cpu_lockid;
        u_int           gd_other_cpus;
        u_int           gd_ss_eflags;
@@ -108,6 +108,5 @@ struct privatespace {
 extern struct privatespace CPU_prvspace[];
 
 #define mdcpu                  ((struct mdglobaldata *)_get_mycpu())
-#define npxthread       mdcpu->gd_npxthread
 
 #endif
index 869bd10..f126d06 100644 (file)
  *
  *     Machine independant code should not directly include this file.
  *
- * $DragonFly: src/sys/platform/pc32/include/thread.h,v 1.4 2003/07/08 06:27:27 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/include/thread.h,v 1.5 2003/07/10 04:47:53 dillon Exp $
  */
 
 #ifndef        _MACHINE_THREAD_H_
 #define        _MACHINE_THREAD_H_
 
-#ifndef _SYS_GLOBALDATA_H_
-#include <sys/globaldata.h>    /* struct globaldata */
-#endif
-
 struct md_thread {
     unsigned int       mtd_cpl;
 };
index b075567..c60b5e9 100644 (file)
@@ -37,7 +37,7 @@
  *     @(#)ipl.s
  *
  * $FreeBSD: src/sys/i386/isa/ipl.s,v 1.32.2.3 2002/05/16 16:03:56 bde Exp $
- * $DragonFly: src/sys/platform/pc32/isa/ipl.s,v 1.7 2003/07/08 06:27:27 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/isa/ipl.s,v 1.8 2003/07/10 04:47:54 dillon Exp $
  */
 
 
@@ -94,30 +94,32 @@ doreti_next:
        movl    %eax,%ecx               /* cpl being restored */
        notl    %ecx
        cli                             /* disallow YYY remove */
-       testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
-       jne     doreti_fast
-
-       testl   PCPU(ipending),%ecx
-       jne     doreti_intr
 #ifdef SMP
        testl   $AST_IPIQ,PCPU(astpending)
        jnz     doreti_ipiq
 #endif
+       testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
+       jnz     doreti_fast
+
+       testl   PCPU(ipending),%ecx
+       jnz     doreti_intr
        testl   $AST_PENDING,PCPU(astpending) /* any pending ASTs? */
        jz      2f
        testl   $PSL_VM,TF_EFLAGS(%esp)
        jz      1f
        cmpl    $1,in_vm86call          /* YYY make per 'cpu' */
-       jnz     doreti_ast
+       jnz     doreti_ast2
 1:
        testb   $SEL_RPL_MASK,TF_CS(%esp)
-       jnz     doreti_ast
+       jnz     doreti_ast2
 2:
        /*
         * Nothing left to do, finish up.  Interrupts are still disabled.
         */
-4:
        subl    $TDPRI_CRIT,TD_PRI(%ebx)        /* interlocked with cli */
+       testl   %eax,%eax
+       jnz     5f
+       movl    $0,PCPU(reqpri)
 5:
        decl    PCPU(intr_nesting_level)
        MEXITCOUNT
@@ -199,7 +201,7 @@ doreti_intr:
        jnc     doreti_next
        pushl   %eax
        pushl   %ecx
-       subl    $TDPRI_CRIT,TD_PRI(%ebx)
+       subl    $TDPRI_CRIT,TD_PRI(%ebx) /* so we can preempt */
        call    sched_ithd              /* YYY must pull in imasks */
        addl    $TDPRI_CRIT,TD_PRI(%ebx)
        addl    $4,%esp
@@ -208,14 +210,35 @@ doreti_intr:
 
        /*
         * AST pending
+        *
+        * Temporarily back-out our critical section because trap() can be
+        * a long-winded call, and we want to be more syscall-like.  
+        *
+        * YYY If we came in from user mode (doreti_ast1) we can call
+        * lwkt_switch *RIGHT* *NOW* to deal with interrupts more quickly,
+        * but should still fall through to the trap code to properly 
+        * reschedule.
         */
-doreti_ast:
+#if 0
+doreti_ast1:
        andl    $~AST_PENDING,PCPU(astpending)
        sti
        movl    %eax,%esi               /* save cpl (can't use stack) */
        movl    $T_ASTFLT,TF_TRAPNO(%esp)
-       decl    PCPU(intr_nesting_level)
-       call    trap
+       decl    PCPU(intr_nesting_level) /* syscall-like, not interrupt-like */
+       subl    $TDPRI_CRIT,TD_PRI(%ebx)
+       call    lwkt_switch
+       jmp     1f
+#endif
+doreti_ast2:
+       andl    $~AST_PENDING,PCPU(astpending)
+       sti
+       movl    %eax,%esi               /* save cpl (can't use stack) */
+       movl    $T_ASTFLT,TF_TRAPNO(%esp)
+       decl    PCPU(intr_nesting_level) /* syscall-like, not interrupt-like */
+       subl    $TDPRI_CRIT,TD_PRI(%ebx)
+1:     call    trap
+       addl    $TDPRI_CRIT,TD_PRI(%ebx)
        incl    PCPU(intr_nesting_level)
        movl    %esi,%eax               /* restore cpl for loop */
        jmp     doreti_next
@@ -253,18 +276,21 @@ splz_next:
        cli
        movl    %eax,%ecx               /* ecx = ~CPL */
        notl    %ecx
-       testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
-       jne     splz_fast
-
-       testl   PCPU(ipending),%ecx
-       jne     splz_intr
-
 #ifdef SMP
        testl   $AST_IPIQ,PCPU(astpending)
        jnz     splz_ipiq
 #endif
+       testl   PCPU(fpending),%ecx     /* check for an unmasked fast int */
+       jnz     splz_fast
+
+       testl   PCPU(ipending),%ecx
+       jnz     splz_intr
 
        subl    $TDPRI_CRIT,TD_PRI(%ebx)
+       testl   %eax,%eax
+       jnz     5f
+       movl    $0,PCPU(reqpri)
+5:
        popl    %ebx
        popfl
        ret
index 38f4407..6c70b49 100644 (file)
@@ -33,7 +33,7 @@
  *
  *     from: @(#)npx.c 7.2 (Berkeley) 5/12/91
  * $FreeBSD: src/sys/i386/isa/npx.c,v 1.80.2.3 2001/10/20 19:04:38 tegge Exp $
- * $DragonFly: src/sys/platform/pc32/isa/npx.c,v 1.8 2003/07/08 06:27:27 dillon Exp $
+ * $DragonFly: src/sys/platform/pc32/isa/npx.c,v 1.9 2003/07/10 04:47:54 dillon Exp $
  */
 
 #include "opt_cpu.h"
@@ -525,7 +525,7 @@ void
 npxexit(struct proc *p)
 {
 
-       if (p->p_thread == npxthread)
+       if (p->p_thread == mdcpu->gd_npxthread)
                npxsave(&curthread->td_pcb->pcb_save);
 #ifdef NPX_DEBUG
        if (npx_exists) {
@@ -746,16 +746,16 @@ npx_intr(dummy)
        struct intrframe *frame;
        u_long *exstat;
 
-       if (npxthread == NULL || !npx_exists) {
+       if (mdcpu->gd_npxthread == NULL || !npx_exists) {
                get_mplock();
                printf("npxintr: npxthread = %p, curthread = %p, npx_exists = %d\n",
-                      npxthread, curthread, npx_exists);
+                      mdcpu->gd_npxthread, curthread, npx_exists);
                panic("npxintr from nowhere");
        }
-       if (npxthread != curthread) {
+       if (mdcpu->gd_npxthread != curthread) {
                get_mplock();
                printf("npxintr: npxthread = %p, curthread = %p, npx_exists = %d\n",
-                      npxthread, curthread, npx_exists);
+                      mdcpu->gd_npxthread, curthread, npx_exists);
                panic("npxintr from non-current process");
        }
 
@@ -825,16 +825,16 @@ npxdna()
 
        if (!npx_exists)
                return (0);
-       if (npxthread != NULL) {
+       if (mdcpu->gd_npxthread != NULL) {
                printf("npxdna: npxthread = %p, curthread = %p\n",
-                      npxthread, curthread);
+                      mdcpu->gd_npxthread, curthread);
                panic("npxdna");
        }
        stop_emulating();
        /*
         * Record new context early in case frstor causes an IRQ13.
         */
-       npxthread = curthread;
+       mdcpu->gd_npxthread = curthread;
        exstat = GET_FPU_EXSW_PTR(curthread->td_pcb);
        *exstat = 0;
        /*
@@ -878,7 +878,7 @@ npxsave(addr)
 
        /* fnop(); */
        start_emulating();
-       npxthread = NULL;
+       mdcpu->gd_npxthread = NULL;
 
 #else /* SMP or CPU_ENABLE_SSE */
 
@@ -902,7 +902,7 @@ npxsave(addr)
        fnsave(addr);
        fnop();
        start_emulating();
-       npxthread = NULL;
+       mdcpu->gd_npxthread = NULL;
        cpu_disable_intr();
        icu1_mask = inb(IO_ICU1 + 1);   /* masks may have changed */
        icu2_mask = inb(IO_ICU2 + 1);
index 6540a45..ffbe768 100644 (file)
@@ -35,7 +35,7 @@
  *
  *     from: @(#)genassym.c    5.11 (Berkeley) 5/10/91
  * $FreeBSD: src/sys/i386/i386/genassym.c,v 1.86.2.3 2002/03/03 05:42:49 nyan Exp $
- * $DragonFly: src/sys/platform/vkernel/i386/genassym.c,v 1.22 2003/07/08 06:27:26 dillon Exp $
+ * $DragonFly: src/sys/platform/vkernel/i386/genassym.c,v 1.23 2003/07/10 04:47:53 dillon Exp $
  */
 
 #include "opt_user_ldt.h"
@@ -189,6 +189,7 @@ ASSYM(GD_CURTHREAD, offsetof(struct mdglobaldata, mi.gd_curthread));
 ASSYM(GD_REQPRI, offsetof(struct mdglobaldata, mi.gd_reqpri));
 ASSYM(GD_CPUID, offsetof(struct mdglobaldata, mi.gd_cpuid));
 ASSYM(GD_CNT, offsetof(struct mdglobaldata, mi.gd_cnt));
+ASSYM(GD_PRIVATE_TSS, offsetof(struct mdglobaldata, gd_private_tss));
 ASSYM(GD_INTR_NESTING_LEVEL, offsetof(struct mdglobaldata, mi.gd_intr_nesting_level));
 ASSYM(GD_ASTPENDING, offsetof(struct mdglobaldata, mi.gd_astpending));
 
@@ -201,7 +202,6 @@ ASSYM(GD_IPENDING, offsetof(struct mdglobaldata, gd_ipending));
 ASSYM(GD_COMMON_TSS, offsetof(struct mdglobaldata, gd_common_tss));
 ASSYM(GD_COMMON_TSSD, offsetof(struct mdglobaldata, gd_common_tssd));
 ASSYM(GD_TSS_GDT, offsetof(struct mdglobaldata, gd_tss_gdt));
-ASSYM(GD_IDLETHREAD, offsetof(struct mdglobaldata, gd_idlethread));
 ASSYM(GD_NPXTHREAD, offsetof(struct mdglobaldata, gd_npxthread));
 ASSYM(GD_CPU_LOCKID, offsetof(struct mdglobaldata, gd_cpu_lockid));
 ASSYM(GD_OTHER_CPUS, offsetof(struct mdglobaldata, gd_other_cpus));
index 3fc415e..26c4c79 100644 (file)
@@ -24,7 +24,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/sys/i386/include/globaldata.h,v 1.11.2.1 2000/05/16 06:58:10 dillon Exp $
- * $DragonFly: src/sys/sys/globaldata.h,v 1.8 2003/07/08 06:27:28 dillon Exp $
+ * $DragonFly: src/sys/sys/globaldata.h,v 1.9 2003/07/10 04:47:55 dillon Exp $
  */
 
 #ifndef _SYS_GLOBALDATA_H_
 #include <sys/time.h>  /* struct timeval */
 #endif
 #ifndef _SYS_VMMETER_H_
-#include <sys/vmmeter.h>
-#endif _SYS_VMMETER_H_
+#include <sys/vmmeter.h> /* struct vmmeter */
+#endif
+#ifndef _SYS_THREAD_H_
+#include <sys/thread.h>        /* struct thread */
+#endif
 
 /*
  * This structure maps out the global data that needs to be kept on a
  * further checks are necessary.  Interrupts are typically managed on a
  * per-processor basis at least until you leave a critical section, but
  * may then be scheduled to other cpus.
- *
- * gd_uprocscheduled indicates that the thread for a user process has been
- * scheduled.  It is used to schedule only one user process at a time in
- * the LWKT subsystem.
  */
 
 struct privatespace;
-struct thread;
-struct lwkt_ipiq;
 
 struct globaldata {
        struct privatespace *gd_prvspace;       /* self-reference */
        struct thread   *gd_curthread;
-       struct thread   *gd_idletd;             /* a bit messy but it works */
        int             gd_tdfreecount;         /* new thread cache */
        int             gd_reqpri;              /* (see note above) */
        TAILQ_HEAD(,thread) gd_tdallq;          /* all threads */
@@ -82,9 +78,10 @@ struct globaldata {
        struct timeval  gd_stattv;
        int             gd_intr_nesting_level;  /* (for fast interrupts) */
        int             gd_astpending;          /* sorta MD but easier here */
-       int             gd_uprocscheduled;
        struct vmmeter  gd_cnt;
        struct lwkt_ipiq *gd_ipiq;
+       struct thread   gd_schedthread;
+       struct thread   gd_idlethread;
        /* extended by <machine/pcpu.h> */
 };
 
index 35b52b4..35642c6 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)proc.h      8.15 (Berkeley) 5/19/95
  * $FreeBSD: src/sys/sys/proc.h,v 1.99.2.9 2003/06/06 20:21:32 tegge Exp $
- * $DragonFly: src/sys/sys/proc.h,v 1.22 2003/07/06 21:23:54 dillon Exp $
+ * $DragonFly: src/sys/sys/proc.h,v 1.23 2003/07/10 04:47:55 dillon Exp $
  */
 
 #ifndef _SYS_PROC_H_
@@ -54,6 +54,7 @@
 #include <sys/ucred.h>
 #include <sys/event.h>                 /* For struct klist */
 #include <sys/thread.h>
+#include <sys/globaldata.h>
 #include <machine/proc.h>              /* Machine-dependent proc substruct. */
 
 /*
@@ -271,9 +272,9 @@ struct      proc {
 #define        P_SWAPINREQ     0x80000 /* Swapin request due to wakeup */
 
 /* Marked a kernel thread */
-#define        P_ONRUNQ        0x100000 /* LWKT scheduled (== not on user sched q) */
+#define        P_ONRUNQ        0x100000 /* on a user scheduling run queue */
 #define        P_KTHREADP      0x200000 /* Process is really a kernel thread */
-#define P_XSLEEP       0x400000 /* process sitting on xwait_t structure */
+#define P_CP_RELEASED  0x400000 /* directly schedule LWKT, ignore user schd */
 
 #define        P_DEADLKTREAT   0x800000 /* lock aquisition - deadlock treatment */
 
@@ -388,6 +389,7 @@ struct pgrp *pgfind __P((pid_t));   /* Find process group by id. */
 struct proc *zpfind __P((pid_t));      /* Find zombie process by id. */
 
 struct vm_zone;
+struct globaldata;
 extern struct vm_zone *proc_zone;
 
 int    enterpgrp __P((struct proc *p, pid_t pgid, int mksess));
@@ -409,6 +411,8 @@ int suser __P((struct thread *td));
 int    suser_proc __P((struct proc *p));
 int    suser_cred __P((struct ucred *cred, int flag));
 void   remrunqueue __P((struct proc *));
+void   release_curproc __P((struct proc *));
+void   acquire_curproc __P((struct proc *));
 void   cpu_heavy_switch __P((struct thread *));
 void   cpu_lwkt_switch __P((struct thread *));
 void   unsleep __P((struct thread *));
@@ -427,8 +431,8 @@ void        cpu_thread_wait __P((struct thread *));
 int    cpu_coredump __P((struct thread *, struct vnode *, struct ucred *));
 void   setsugid __P((void));
 void   faultin __P((struct proc *p));
+void   sched_thread_init(void);
 
-struct proc *  chooseproc __P((void));
 u_int32_t      procrunnable __P((void));
 
 #endif /* _KERNEL */
index e40fee2..3a1468e 100644 (file)
@@ -4,7 +4,7 @@
  *     Implements the architecture independant portion of the LWKT 
  *     subsystem.
  * 
- * $DragonFly: src/sys/sys/thread.h,v 1.20 2003/07/08 06:27:28 dillon Exp $
+ * $DragonFly: src/sys/sys/thread.h,v 1.21 2003/07/10 04:47:55 dillon Exp $
  */
 
 #ifndef _SYS_THREAD_H_
@@ -158,6 +158,7 @@ struct thread {
     int                td_gen;         /* wait queue chasing generation number */
                                /* maybe preempt */
     void       (*td_preemptable)(struct thread *td, int critpri);
+    void       (*td_release)(struct thread *td);
     union {
        struct md_intr_info *intdata;
     } td_info;
@@ -184,12 +185,16 @@ struct thread {
  * Thread flags.  Note that TDF_EXITED is set by the appropriate switchout
  * code when a thread exits, after it has switched to another stack and
  * cleaned up the MMU state.
+ *
+ * LWKT threads stay on their (per-cpu) run queue while running, not to
+ * be confused with user processes which are removed from the user scheduling
+ * run queue while actually running.
  */
 #define TDF_EXITED             0x0001  /* thread finished exiting */
-#define TDF_RUNQ               0x0002  /* on run queue (if not on bglq) */
+#define TDF_RUNQ               0x0002  /* on an LWKT run queue */
 #define TDF_PREEMPT_LOCK       0x0004  /* I have been preempted */
 #define TDF_PREEMPT_DONE       0x0008  /* acknowledge preemption complete */
-#define TDF_BGLQ               0x0010  /* on BGL queue */
+#define TDF_IDLE_NOHLT         0x0010  /* we need to spin */
 
 #define TDF_ONALLQ             0x0100  /* on gd_tdallq */
 #define TDF_ALLOCATED_THREAD   0x0200  /* zalloc allocated thread */
@@ -248,6 +253,7 @@ extern void lwkt_schedule(thread_t td);
 extern void lwkt_schedule_self(void);
 extern void lwkt_deschedule(thread_t td);
 extern void lwkt_deschedule_self(void);
+extern void lwkt_acquire(thread_t td);
 extern void lwkt_yield(void);
 extern void lwkt_yield_quick(void);
 extern void lwkt_hold(thread_t td);
index 868ddb3..29037c2 100644 (file)
@@ -60,7 +60,7 @@
  * rights to redistribute these changes.
  *
  * $FreeBSD: src/sys/vm/vm_glue.c,v 1.94.2.4 2003/01/13 22:51:17 dillon Exp $
- * $DragonFly: src/sys/vm/vm_glue.c,v 1.10 2003/07/03 17:24:04 dillon Exp $
+ * $DragonFly: src/sys/vm/vm_glue.c,v 1.11 2003/07/10 04:47:55 dillon Exp $
  */
 
 #include "opt_vm.h"
@@ -542,7 +542,7 @@ swapout(p)
        (void) splhigh();
        p->p_flag &= ~P_INMEM;
        p->p_flag |= P_SWAPPING;
-       if (p->p_stat == SRUN)
+       if (p->p_flag & P_ONRUNQ)
                remrunqueue(p);
        (void) spl0();