summaryrefslogtreecommitdiffstats
path: root/tools/KSE
diff options
context:
space:
mode:
authormini <mini@FreeBSD.org>2002-09-19 02:15:27 +0000
committermini <mini@FreeBSD.org>2002-09-19 02:15:27 +0000
commiteabad0146ba86c11bd57572fa5840033cb10a901 (patch)
tree4d865be52a5b027ec6061f0e670654a9fe7cb8f3 /tools/KSE
parentb87a363d1b0ac49ecbd8a11a92862276236dffbe (diff)
downloadFreeBSD-src-eabad0146ba86c11bd57572fa5840033cb10a901.zip
FreeBSD-src-eabad0146ba86c11bd57572fa5840033cb10a901.tar.gz
Update the KSE test utility to the new KSE API.
Diffstat (limited to 'tools/KSE')
-rw-r--r--tools/KSE/ksetest/Makefile19
-rw-r--r--tools/KSE/ksetest/kse_asm.S221
-rw-r--r--tools/KSE/ksetest/kse_threads_test.c561
3 files changed, 482 insertions, 319 deletions
diff --git a/tools/KSE/ksetest/Makefile b/tools/KSE/ksetest/Makefile
index f7bb415..5c0a743 100644
--- a/tools/KSE/ksetest/Makefile
+++ b/tools/KSE/ksetest/Makefile
@@ -1,20 +1,7 @@
# $FreeBSD$
-# @(#)Makefile 8.1 (Berkeley) 6/2/93
-
PROG= ksetest
-SRCS= kse_threads_test.c kse_asm.S
-#
-# To support "lazy" ps for non root/wheel users
-# add -DLAZY_PS to the cflags. This helps
-# keep ps from being an unnecessary load
-# on large systems.
-#
-CFLAGS=-static -g -O
-#WARNS= 0
-#DPADD= ${LIBM} ${LIBKVM}
-#LDADD= -lm -lkvm
-#BINGRP= kmem
-#BINMODE=2555
-NOMAN=yes
+NOMAN=
+CFLAGS+= -g
+SRCS= kse_asm.S kse_threads_test.c
.include <bsd.prog.mk>
diff --git a/tools/KSE/ksetest/kse_asm.S b/tools/KSE/ksetest/kse_asm.S
index a12358c..ae6ac7e 100644
--- a/tools/KSE/ksetest/kse_asm.S
+++ b/tools/KSE/ksetest/kse_asm.S
@@ -1,88 +1,149 @@
-/*-
+/*
+ * Copyright (c) 2002 Jonathan Mini <mini@freebsd.org>.
+ * Copyright (c) 2001 Daniel Eischen <deischen@freebsd.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DANIEL EISCHEN AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
* $FreeBSD$
*/
-#include <machine/asmacros.h>
-
-/*****************************************************************************/
-/* Scheduling */
-/*****************************************************************************/
-
- .data
-
- .text
-
-#define TF_GS -0x04 /* don't laugh! */
-#define TF_FS 0x00
-#define TF_ES 0x04
-#define TF_DS 0x08
-#define TF_EDI 0x0c
-#define TF_ESI 0x10
-#define TF_EBP 0x14
-#define TF_ISP 0x18
-#define TF_EBX 0x1c
-#define TF_EDX 0x20
-#define TF_ECX 0x24
-#define TF_EAX 0x28
-#define TF_TRAPNO 0x2c
-#define TF_ERR 0x30
-#define TF_EIP 0x34
-#define TF_CS 0x38
-#define TF_EFLAGS 0x3c
-#define TF_ESP 0x40
-#define TF_SS 0x44
+#include <machine/asm.h>
+__FBSDID("$FreeBSD$");
/*
- * savethread
+ * Where do we define these?
*/
-GEN_ENTRY(savethread)
-
- /* Switch to new thread. First, save context as needed. */
- pushl %edx
- movl 8(%esp), %edx /* get context area */
-
- movl %eax,TF_EAX(%edx)
- movl %ebx,TF_EBX(%edx)
- movl %ecx,TF_ECX(%edx)
- popl %eax /* get dx off the stack again */
- movl %eax,TF_EDX(%edx)
- movl (%esp),%eax /* get the return address */
- movl %eax,TF_EIP(%edx)
- movl %esp,TF_ESP(%edx)
- movl %esp,TF_ISP(%edx) /* XXX */
- movl %ebp,TF_EBP(%edx)
- movl %esi,TF_ESI(%edx)
- movl %edi,TF_EDI(%edx)
- movl %cs,TF_CS(%edx)
- movl %ds,TF_DS(%edx)
- movl %es,TF_ES(%edx)
- movl %fs,TF_FS(%edx)
- movl %gs,TF_GS(%edx)
- ret
-
-
-GEN_ENTRY(loadthread)
- mov 4(%esp), %edx /* get context area */
+#define MC_SIZE 640 /* sizeof mcontext_t */
+#define UC_MC_OFFSET 16 /* offset to mcontext from ucontext */
+#define UC_MC_LEN_OFFSET 96 /* offset to mc_len from mcontext */
+#define MC_LEN_OFFSET 80 /* offset to mc_len from mcontext */
+#define MC_FP_REGS_OFFSET 96 /* offset to FP regs from mcontext */
+#define MC_FP_CW_OFFSET 96 /* offset to FP control word */
+#define MC_OWNEDFP_OFFSET 88 /* offset to mc_ownedfp from mcontext */
+#define KM_STACK_SP_OFFSET 32 /* offset to km_stack.ss_sp */
+#define KM_STACK_SIZE_OFFSET 36 /* offset to km_stack.ss_sp */
+#define KM_FUNC_OFFSET 28 /* offset to km_func */
+
+/*
+ * int uts_to_thread(thread_mailbox *tdp, thread_mailbox **curthreadp);
+ *
+ * Does not return on success, returns -1 otherwise.
+ */
+ENTRY(uts_to_thread)
+ movl 4(%esp), %edx /* get address of thread_mailbox */
+ /* .. ucontext_t is at offset 0 */
+ cmpl $0, %edx /* check for null pointer */
+ jne 1f
+ movl $-1, %eax
+ jmp 5f
+1: cmpl $MC_SIZE, UC_MC_LEN_OFFSET(%edx) /* is context valid? */
+ je 2f
+ movl $-1, %eax /* bzzzt, invalid context */
+ jmp 5f
+2: movl 8(%esp), %eax /* get address of curthreadp */
+ movl %edx, (%eax) /* we're now the current thread */
+ /*
+ * From here on, we don't touch the old stack.
+ */
+ addl $UC_MC_OFFSET, %edx /* add offset to mcontext */
+ movl 4(%edx), %gs
+ movl 8(%edx), %fs
+ movl 12(%edx), %es
+ movl 16(%edx), %ds
+ movl 76(%edx), %ss
+ movl 20(%edx), %edi
+ movl 24(%edx), %esi
+ movl 28(%edx), %ebp
+ movl 72(%edx), %esp /* switch to context defined stack */
+ subl $4, %esp /* leave space for the return address */
+ movl 60(%edx), %eax /* put return address at top of stack */
+ movl %eax, (%esp)
+ cmpl $0, MC_OWNEDFP_OFFSET(%edx) /* are FP regs valid? */
+ jz 3f
+ frstor MC_FP_REGS_OFFSET(%edx) /* restore FP regs */
+ jmp 4f
+3: fninit
+ fldcw MC_FP_CW_OFFSET(%edx)
+4: movl 48(%edx), %eax /* restore ax, bx, cx */
+ movl 36(%edx), %ebx
+ movl 44(%edx), %ecx
+ pushl 68(%edx) /* flags on stack */
+ pushl 40(%edx) /* %edx on stack */
+ popl %edx /* %edx off stack */
+ popf /* flags off stack */
+5: ret /* %eip off stack */
-/* movl TF_ISP(%edx), %esp */ /* select which is correct */
- movl TF_ESP(%edx), %esp /* get the new stack online */
- movl TF_EBP(%edx), %ebp
- movl TF_EIP(%edx),%eax
- push %eax /* return adddress */
-#if 0
- movl TF_CS(%edx), %cs
- movl TF_DS(%edx), %ds
- movl TF_ES(%edx), %es
- movl TF_FS(%edx), %fs
- movl TF_GS(%edx), %gs
-#endif
- movl TF_ESI(%edx), %esi
- movl TF_EDI(%edx), %edi
+/*
+ * int thread_to_uts(thread_mailbox *tm, kse_mailbox *km);
+ *
+ * Does not return on success, returns -1 otherwise.
+ */
+ENTRY(thread_to_uts)
+ movl 4(%esp), %eax /* get address of context */
+ cmpl $0, %eax /* check for null pointer */
+ jne 1f
+ movl $-1, %eax
+ jmp 2f
+1: pushl %edx /* save value of edx */
+ movl %eax, %edx /* get address of context */
+ addl $UC_MC_OFFSET, %edx /* add offset to mcontext */
+ movl %gs, 4(%edx)
+ movl %fs, 8(%edx)
+ movl %es, 12(%edx)
+ movl %ds, 16(%edx)
+ movl %edi, 20(%edx)
+ movl %esi, 24(%edx)
+ movl %ebp, 28(%edx)
+ movl %ebx, 36(%edx)
+ movl $0, 48(%edx) /* store successful return in eax */
+ popl %eax /* get saved value of edx */
+ movl %eax, 40(%edx) /* save edx */
+ movl %ecx, 44(%edx)
+ movl (%esp), %eax /* get return address */
+ movl %eax, 60(%edx) /* save return address */
+ movl %ss, 76(%edx)
+ /*
+ * Don't save floating point registers here.
+ *
+ * This is an explicit call to get the current context, so
+ * the caller is done with the floating point registers.
+ * Contexts formed by involuntary switches, such as signal delivery,
+ * have floating point registers saved by the kernel.
+ */
+ fnstcw MC_FP_CW_OFFSET(%edx)
+ movl $0, MC_OWNEDFP_OFFSET(%edx) /* no FP */
+ lahf /* get eflags */
+ movl %eax, 68(%edx) /* store eflags */
+ movl %esp, %eax /* setcontext pushes the return */
+ addl $4, %eax /* address onto the top of the */
+ movl %eax, 72(%edx) /* stack; account for this */
+ movl $MC_SIZE, MC_LEN_OFFSET(%edx) /* context is now valid */
+ movl 8(%esp), %edx /* get address of mailbox */
+ movl KM_STACK_SP_OFFSET(%edx), %eax /* get bottom of stack */
+ addl KM_STACK_SIZE_OFFSET(%edx), %eax /* add length */
+ movl %eax, %esp /* switch to the uts's stack */
+ pushl %edx /* push the address of the mailbox */
+ pushl KM_FUNC_OFFSET(%edx) /* .. the uts can return to itself */
+ pushl KM_FUNC_OFFSET(%edx) /* push the address of the uts func */
+2: ret
- movl TF_EDX(%edx), %eax
- pushl %eax
- movl TF_ECX(%edx), %ecx
- movl TF_EBX(%edx), %ebx
- movl TF_EAX(%edx), %eax
- popl %edx
- ret
diff --git a/tools/KSE/ksetest/kse_threads_test.c b/tools/KSE/ksetest/kse_threads_test.c
index fb4f22e..09f8786 100644
--- a/tools/KSE/ksetest/kse_threads_test.c
+++ b/tools/KSE/ksetest/kse_threads_test.c
@@ -1,275 +1,390 @@
-/*
+/*-
+ * Copyright (c) 2002 Jonathan Mini (mini@freebsd.org).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
* $FreeBSD$
*/
-#define _KERNEL
-#include <stdio.h>
-#include <stdlib.h>
-#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/signalvar.h>
+#include <sys/sysctl.h>
#include <sys/kse.h>
-#include <sys/queue.h>
-
-/*#define DEBUG*/
-/*************************************************************
- * These should probably be in a .h file
- **************************************************************/
-typedef void thread_fn(void *arg);
-
-struct user_thread {
- struct thread_mailbox mbox;
- char *stack;
- int stack_size;
- TAILQ_ENTRY(user_thread) runq_next;
-};
-
-struct per_kse {
- struct kse_mailbox mbox;
- struct user_thread *curthread;
-};
-/*************************************************************
- * Debug stuff
- **************************************************************/
-#ifdef DEBUG
-
-jmp_buf jb3;
-#define DUMPREGS(desc) do {_setjmp(jb3); printjb(jb3, desc); } while (0)
-
-char *regname[] = {"%eip","%ebx","%esp","%ebp",
- "%esi","%edi","fpcr","MSK0",
- "MSK1","MSK2","MSK3"};
-
-
-static
-printjb(struct _jmp_buf *jb, char *desc)
-{
-
- int i;
- printf("jb (%s) is at 0x%x\n", desc, jb);
- for( i = 0; i< _JBLEN-4; i++) {
- printf("jb[%d] (%s) = 0x%x\n", i, regname[i], jb[0]._jb[i]);
- }
-}
+#include <sys/ucontext.h>
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <time.h>
+#include <unistd.h>
+
+#undef TRACE_UTS
+
+#ifdef TRACE_UTS
+#define UPFMT(fmt...) pfmt(#fmt)
+#define UPSTR(s) pstr(s)
+#define UPCHAR(c) pchar(c)
#else
-#define DUMPREGS(desc) do {} while (0)
+#define UPFMT(fmt...) /* Nothing. */
+#define UPSTR(s) /* Nothing. */
+#define UPCHAR(c) /* Nothing. */
#endif
-/*************************************************************
- * Globals
- **************************************************************/
-struct per_kse first_kse; /* for NOW cheat and make it global */
-TAILQ_HEAD(, user_thread) runqueue = TAILQ_HEAD_INITIALIZER(runqueue);
-/*************************************************************
- * Implementation parameters
- **************************************************************/
-#define T_STACKSIZE (16*4096) /* thread stacksize */
-#define K_STACKSIZE (1*4096) /* KSE (UTS) stacksize */
-
-/*************************************************************
- * UTS funcions.
- * Simple round_robin for now.
- **************************************************************/
-static void
-runq_insert(struct user_thread *thread)
-{
- TAILQ_INSERT_TAIL(&runqueue, thread, runq_next);
-}
+#define MAIN_STACK_SIZE (1024 * 1024)
+#define THREAD_STACK_SIZE (32 * 1024)
-static struct user_thread *
-select_thread(void)
-{
- struct user_thread *thread;
+static struct kse_mailbox uts_mb;
+static struct thread_mailbox *run_queue;
+static struct thread_mailbox *aa;
- if ((thread = TAILQ_FIRST(&runqueue))) {
- TAILQ_REMOVE(&runqueue, thread, runq_next);
- }
- return (thread);
-}
+static int progress = 0;
-/*************************************************************
- * The UTS upcall entrypoint
- * Called once on startup (and left by longjump)
- * and there-after, returned to by the upcall many times.
- **************************************************************/
-void
-UTS(struct kse_mailbox *ke_mbox)
-{
- struct user_thread *thread;
- struct thread_mailbox *completed;
- struct per_kse *ksedata;
- int done = 0;
-
- /**********************************/
- /* UTS upcall starts running here. */
- /**********************************/
- /**********************************/
-
- ksedata = ke_mbox->kmbx_UTS_handle;
- /* If there are returned syscall threads, put them on the run queue */
- if ((completed = ke_mbox->kmbx_completed_threads)) {
- ke_mbox->kmbx_completed_threads = NULL;
- while (completed) {
- thread = completed->UTS_handle;
- completed = completed->next_completed;
- runq_insert(thread);
- }
- }
+static void init_uts(void);
+static void enter_uts(void);
+static void pchar(char c);
+static void pfmt(const char *fmt, ...);
+static void pstr(const char *s);
+static void runq_insert(struct thread_mailbox *tm);
+static struct thread_mailbox *runq_remove(void);
+static void thread_start(const void *func, int arg);
+static void uts(struct kse_mailbox *km);
- /* find highest priority thread and load it */
- if ((thread = select_thread())) {
- ksedata->curthread = thread;
- ke_mbox->kmbx_current_thread = &thread->mbox;
+extern int uts_to_thread(struct thread_mailbox *tdp, struct thread_mailbox **curthreadp);
- /* loads context similar to longjmp() */
- loadthread(&thread->mbox.ctx.tfrm.tf_tf);
- /* NOTREACHED */
- }
- kse_yield(); /* in the kernel it does a thread_exit() */
- /* NOTREACHED */
-}
-
-/*************************************************************
- * Startup mechanism functions
- **************************************************************/
-static int
-kickkse(struct per_kse *ksedata, int newgroup)
+static void
+nano(int len)
{
- char * newstack;
- jmp_buf jb1;
- jmp_buf jb2;
- struct kse_mailbox *mboxaddr;
- struct per_kse *user_UTS_info;
- int err;
-
- newstack = malloc(K_STACKSIZE);
- mboxaddr = &ksedata->mbox;
- mboxaddr->kmbx_stackbase = newstack;
- mboxaddr->kmbx_stacksize = K_STACKSIZE;
- mboxaddr->kmbx_upcall = &UTS;
- mboxaddr->kmbx_UTS_handle = ksedata;
- err = kse_new(mboxaddr, newgroup);
- return(err);
-}
+ struct timespec time_to_sleep;
+ struct timespec time_remaining;
+ time_to_sleep.tv_sec = 0;
+ time_to_sleep.tv_nsec = len * 10000;
+ nanosleep(&time_to_sleep, &time_remaining);
+}
-static int
-startkse(struct per_kse *ksedata)
+void
+aaaa(int c)
{
- return (kickkse(ksedata, 0));
+ for (;;) {
+ pchar(c);
+ nano(1);
+ }
}
-static int
-startksegrp(struct per_kse *ksedata)
+static void
+foof(int sig)
{
- return(kickkse(ksedata, 1));
+ pfmt("\n[%d]\n", sig);
+ thread_start(aaaa, '0' + progress++);
}
-void badreturn()
+void
+spin(int arg)
{
- printf("thread returned when shouldn't\n");
- exit(1);
+ for (;;) enter_uts(); sched_yield();
}
-__inline__ void
-pushontostack(struct user_thread *tcb, int value)
+/*
+ * Test Userland Thread Scheduler (UTS) suite for KSE.
+ */
+int
+main(void)
{
- int *SP;
+ int i;
- SP = (int *)(tcb->mbox.ctx.tfrm.tf_tf.tf_isp);
- *--SP = value;
- tcb->mbox.ctx.tfrm.tf_tf.tf_isp = (int)SP;
+ thread_start(spin, '.');
+ // thread_start(spin);
+ init_uts();
+ for (i = 0;1;i++) {
+// if (i < 1000)
+// thread_start(aaaa, 'a' + (i % 26));
+ pchar('A' + (i % 26));
+ nano(5);
+ }
+ pstr("\n** main() exiting **\n");
+ return (EX_OK);
}
-struct user_thread *
-makethread(thread_fn *fn, int arg1, void *arg2)
+
+/*
+ * Enter the UTS from a thread.
+ */
+static void
+enter_uts(void)
{
- struct user_thread *tcb;
-
- /* We could combine these mallocs */
- tcb = malloc(sizeof *tcb);
- bzero(tcb, sizeof(*tcb));
- tcb->mbox.UTS_handle = tcb; /* back pointer */
-
- /* malloc the thread's stack */
- /* We COULD mmap it with STACK characteristics */
- /* Then we could add a guard page. */
- tcb->stack_size = T_STACKSIZE; /* set the size we want */
- tcb->stack = malloc(tcb->stack_size);
-
- /* Make sure there are good defaults */
- savethread(&tcb->mbox.ctx.tfrm.tf_tf);
-
- /* set the PC to the fn */
- tcb->mbox.ctx.tfrm.tf_tf.tf_eip = (int) fn;
-
- /* Set the stack and push on the args and a dummy return address */
- tcb->mbox.ctx.tfrm.tf_tf.tf_ebp =
- tcb->mbox.ctx.tfrm.tf_tf.tf_isp =
- tcb->mbox.ctx.tfrm.tf_tf.tf_esp =
- (int)(&tcb->stack[tcb->stack_size - 16]);
- pushontostack(tcb, (int)arg2);
- pushontostack(tcb, (int)arg1);
- pushontostack(tcb, (int)&badreturn); /* safety return address */
- return (tcb);
+ struct thread_mailbox *td;
+
+ /* XXX: We should atomically exchange these two. */
+ td = uts_mb.km_curthread;
+ uts_mb.km_curthread = NULL;
+
+ thread_to_uts(td, &uts_mb);
}
-/*************************************************************
- * code for three separate threads. (so we can see if it works)
- *************************************************************/
-static void
-thread1_code(void *arg)
+/*
+ * Initialise threading.
+ */
+static void
+init_uts(void)
{
- for(;;) {
- sleep (1);
- write(1,".",1);
- }
+ struct thread_mailbox *tm;
+ int mib[2];
+ char *p;
+ size_t len;
+
+ /*
+ * Create initial thread.
+ */
+ tm = (struct thread_mailbox *)calloc(1, sizeof(struct thread_mailbox));
+
+ /* Throw us into its context. */
+ getcontext(&tm->tm_context);
+
+ /* Find our stack. */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_USRSTACK;
+ len = sizeof(p);
+ if (sysctl(mib, 2, &p, &len, NULL, 0) == -1)
+ pstr("sysctl(CTL_KER.KERN_USRSTACK) failed.\n");
+ pfmt("main() : 0x%x\n", tm);
+ pfmt("eip -> 0x%x\n", tm->tm_context.uc_mcontext.mc_eip);
+ tm->tm_context.uc_stack.ss_sp = p - MAIN_STACK_SIZE;
+ tm->tm_context.uc_stack.ss_size = MAIN_STACK_SIZE;
+
+ /*
+ * Create KSE mailbox.
+ */
+ p = (char *)malloc(THREAD_STACK_SIZE);
+ bzero(&uts_mb, sizeof(struct kse_mailbox));
+ uts_mb.km_stack.ss_sp = p;
+ uts_mb.km_stack.ss_size = THREAD_STACK_SIZE;
+ uts_mb.km_func = (void *)uts;
+ pfmt("uts() at : 0x%x\n", uts);
+ pfmt("uts stack at : 0x%x - 0x%x\n", p, p + THREAD_STACK_SIZE);
+
+ /*
+ * Start KSE scheduling.
+ */
+ pfmt("kse_new() -> %d\n", kse_new(&uts_mb, 0));
+ uts_mb.km_curthread = tm;
+
+ /*
+ * Arrange to deliver signals via KSE.
+ */
+ signal(SIGURG, foof);
}
-static void
-thread2_code(void *arg)
+/*
+ * Write a single character to stdout, in a thread-safe manner.
+ */
+static void
+pchar(char c)
{
- for(;;) {
- sleep (3);
- write(1,"+",1);
- }
+
+ write(STDOUT_FILENO, &c, 1);
}
-static void
-thread3_code(void *arg)
+/*
+ * Write formatted output to stdout, in a thread-safe manner.
+ *
+ * Recognises the following conversions:
+ * %c -> char
+ * %d -> signed int (base 10)
+ * %s -> string
+ * %u -> unsigned int (base 10)
+ * %x -> unsigned int (base 16)
+ */
+static void
+pfmt(const char *fmt, ...)
{
- for(;;) {
- sleep (5);
- write(1,"=",1);
+ static const char digits[16] = "0123456789abcdef";
+ va_list ap;
+ char buf[10];
+ char *s;
+ unsigned r, u;
+ int c, d;
+
+ va_start(ap, fmt);
+ while ((c = *fmt++)) {
+ if (c == '%') {
+ c = *fmt++;
+ switch (c) {
+ case 'c':
+ pchar(va_arg(ap, int));
+ continue;
+ case 's':
+ pstr(va_arg(ap, char *));
+ continue;
+ case 'd':
+ case 'u':
+ case 'x':
+ r = ((c == 'u') || (c == 'd')) ? 10 : 16;
+ if (c == 'd') {
+ d = va_arg(ap, unsigned);
+ if (d < 0) {
+ pchar('-');
+ u = (unsigned)(d * -1);
+ } else
+ u = (unsigned)d;
+ } else
+ u = va_arg(ap, unsigned);
+ s = buf;
+ do {
+ *s++ = digits[u % r];
+ } while (u /= r);
+ while (--s >= buf)
+ pchar(*s);
+ continue;
+ }
+ }
+ pchar(c);
}
+ va_end(ap);
}
+static void
+pstr(const char *s)
+{
+ write(STDOUT_FILENO, s, strlen(s));
+}
-int main()
+/*
+ * Insert a thread into the run queue.
+ */
+static void
+runq_insert(struct thread_mailbox *tm)
{
- /* set up global structures */
- TAILQ_INIT(&runqueue);
-
- /* define two threads to run, they are runnable but not yet running */
- runq_insert( makethread(&thread1_code, 0, NULL));
- runq_insert( makethread(&thread2_code, 0, NULL));
+ tm->tm_next = run_queue;
+ run_queue = tm;
+}
- /* and one which we will run ourself */
- first_kse.curthread = makethread(&thread3_code, 0, NULL);
+/*
+ * Select and remove a thread from the run queue.
+ */
+static struct thread_mailbox *
+runq_remove(void)
+{
+ struct thread_mailbox *p, *p1;
+
+ if (run_queue == NULL)
+ return (NULL);
+ p1 = NULL;
+ for (p = run_queue; p->tm_next != NULL; p = p->tm_next)
+ p1 = p;
+ if (p1 == NULL)
+ run_queue = NULL;
+ else
+ p1->tm_next = NULL;
+ return (p);
+}
- /* start two KSEs in different KSEGRPs */
- if (startkse(&first_kse)) {
- perror("failed to start KSE");
- exit(1);
+/*
+ * Userland thread scheduler.
+ */
+static void
+uts(struct kse_mailbox *km)
+{
+ struct thread_mailbox *tm, *p;
+ int ret, i;
+
+ UPSTR("\n--uts() start--\n");
+ UPFMT("mailbox -> %x\n", km);
+
+ /*
+ * Insert any processes back from being blocked
+ * in the kernel into the run queue.
+ */
+ p = km->km_completed;
+ uts_mb.km_completed = NULL;
+ UPFMT("km_completed -> 0x%x", p);
+ while ((tm = p) != NULL) {
+ p = tm->tm_next;
+ UPFMT(" 0x%x", p);
+ runq_insert(tm);
+ }
+ UPCHAR('\n');
+
+ /*
+ * Process any signals we've recieved (but only if we have
+ * somewhere to deliver them to).
+ */
+ if ((run_queue != NULL) && SIGNOTEMPTY(km->km_sigscaught)) {
+ for (i = 0;i < _SIG_MAXSIG;i++)
+ if (SIGISMEMBER(km->km_sigscaught, i)) {
+ signalcontext(&run_queue->tm_context, i, foof);
+ break;
+ }
+ bzero(&km->km_sigscaught, sizeof(sigset_t));
}
- /* startksegrp(&second_kse); */ /* we can't do 2 KSEs yet */
- /* One will be sufficient */
+ /*
+ * Pull a thread off the run queue.
+ */
+ p = runq_remove();
+#if 0
+ if ((p == aa) && (progress > 0)) {
+ --progress;
+ signalcontext(&p->tm_context, 1, foof);
+ }
+#endif
- /* we are a thread, start the ball rolling */
- /* let the kernel know we are it */
- first_kse.mbox.kmbx_current_thread = &first_kse.curthread->mbox;
- thread3_code(NULL);
- return 0;
+ /*
+ * Either schedule a thread, or idle if none ready to run.
+ */
+ if (p != NULL) {
+ UPFMT("\n-- uts() scheduling 0x%x--\n", p);
+ UPFMT("eip -> 0x%x progress -> %d\n",
+ p->tm_context.uc_mcontext.mc_eip, progress);
+ UPSTR("curthread set\n");
+ uts_to_thread(p, &km->km_curthread);
+ UPSTR("\n-- uts_to_thread() failed --\n");
+ }
+ kse_yield();
+ pstr("** uts() exiting **\n");
+ exit(EX_SOFTWARE);
}
+/*
+ * Start a thread.
+ */
+static void
+thread_start(const void *func, int arg)
+{
+ struct thread_mailbox *tm;
+ char *p;
+
+ aa = tm = (struct thread_mailbox *)calloc(1, sizeof(struct thread_mailbox));
+ pfmt("thread_start() : 0x%x %x\n", tm, &aa->tm_context);
+ getcontext(&tm->tm_context);
+ p = (char *)malloc(THREAD_STACK_SIZE);
+ tm->tm_context.uc_stack.ss_sp = p;
+ tm->tm_context.uc_stack.ss_size = THREAD_STACK_SIZE;
+ makecontext(&tm->tm_context, func, 2, arg);
+ // setcontext(&tm->tm_context);
+ runq_insert(tm);
+}
OpenPOWER on IntegriCloud