Date: Thu, 24 Jan 2002 01:41:22 -0800 (PST) From: Julian Elischer <julian@elischer.org> To: arch@freebsd.org Subject: simple KSE test program Message-ID: <Pine.BSF.4.21.0201240046440.25428-100000@InterJet.elischer.org>
next in thread | raw e-mail | index | archive | help
Several people have asked how a KSE program would look. Here is a program I am writing for testing KSEs: I hope to be using it within a day or two. it includes 2 new syscalls (kse_new() and kse_yield()). One function not yet written is the function loadthread() which reads the registers in from the saved context in a user_thread structure, in a similar way to how longjmp() does, but with more to load. I need to add that in a .s file. First I include the kse include file: #ifndef SYS_KSE_H #define SYS_KSE_H /* * This file defines the structures needed for communication between * the userland and the kernel when running a KSE-based threading system. * The only programs that should see this file are the UTS and the kernel. */ /* * Each userland thread has one of these buried in it's * Thread control structure somewhere. */ struct thread_mailbox { struct thread_mailbox *next_completed; unsigned int flags; void *UTS_handle; /* UTS can use this for anything */ mcontext_t ctx; /* thread's saved context goes here. */ /* The ctx field will become a union of types trapframe and mcontext_t aligned accordingly. */ }; /* * You need to supply one of these as the argument to the * kse_new() system call. */ struct kse_mailbox { struct thread_mailbox *current_thread; struct thread_mailbox *completed_threads; unsigned int flags; void *UTS_handle; /* The UTS can use this for anything */ }; #define KEMBXF_CRITICAL 0x00000001 struct kse_global_mailbox { unsigned int flags; }; #define GMBXF_CRITICAL 0x00000001 /* some provisional sycalls: */ int kse_new(struct kse_mailbox *mbx, int new_grp_flag); int kse_exit(void); /* last will clear KSE mode */ int thread_wakeup(struct thread_mailbox *tmbx); /* make it complete */ int kse_wakeup(void); /* wake any idle KSEs */ void kse_yield(void); /* this kse can become idle */ --------------------------------------------------------------- and here is the program. Only the last 4 functions should be in the program itself. All the rest make up the basis of what would be in a library. In fact some of what is done in main() might even be abstracted too. I hope to be testing this within a couple of days (given a few cleanups and loose-ends to fix) ---------- #include <stdio.h> #include <stdlib.h> #include <setjmp.h> #include <sys/kse.h> /************************************************************* * 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; struct user_thread *runq_next; }; struct per_kse { struct kse_mailbox *mbox; struct user_thread *curthread }; /************************************************************* * Debug stuff **************************************************************/ #if 0 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]); } } #endif /************************************************************* * Globals **************************************************************/ struct per_kse first_kse; /* for NOW cheat and make it global */ struct user_thread *runqueue; struct user_thread **runq_last; /************************************************************* * 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) { thread->runq_next = NULL; *runq_last = thread; runq_last = &thread->runq_next; } static struct user_thread * select_thread(void) { struct user_thread *thread; if ((thread = runqueue)) { if ((runqueue = thread->runq_next) == NULL) { runq_last = &runqueue; } } return (thread); } /************************************************************* * The UTS upcall entrypoint * Called once on startup (and left by longjump) * and there-after, returned to by the upcall many times. **************************************************************/ static void UTS(struct _jmp_buf *jb1, struct per_kse *ksedata, int newgroup) { struct kse_mailbox ke_mbox; struct user_thread *thread; struct thread_mailbox *completed; /* Let the caller know where our mailbox is */ ksedata->mbox = &ke_mbox; ke_mbox.UTS_handle = ksedata; if (kse_new(&ke_mbox, newgroup)) { /* initial call returns */ _longjmp(jb1, 1); /* go back to caller's stack and caller */ /* NOTREACHED */ } /**********************************/ /* UTS upcall starts running here. */ /**********************************/ /**********************************/ printf("we are in the UTS with mailbox at 0x%x\n", &ke_mbox); /* If there are returned syscall threads, put them on the run queue */ if ((completed = ke_mbox.completed_threads)) { ke_mbox.completed_threads = NULL; while (completed) { thread = completed->UTS_handle; completed = completed->next_completed; runq_insert(thread); } } /* find highest priority thread and load it */ if ((thread = select_thread())) { ksedata->curthread = thread; ke_mbox.current_thread = &thread->mbox; loadthread(thread) /* loads context similar to longjmp() */ /* NOTREACHED */ } kse_yeild(); /* in the kernel it does a thread_exit() */ /* NOTREACHED */ } /************************************************************* * Startup mechanism functions **************************************************************/ static int; kickkse(struct per_kse *ksedata, int newgroup) { char * newstack; jmp_buf jb1; jmp_buf jb2; struct kse_mailbox *mboxaddr; int i; newstack = malloc(K_STACKSIZE); printf("newstack is at 0x%x-0x%x\n", newstack, newstack + K_STACKSIZE); if (_setjmp(jb1) == 0) { if (_setjmp(jb2) == 0) { jb2[0]._jb[2] = &newstack[K_STACKSIZE - 16]; _longjmp(jb2, 1); } /* running with a different SP */ { /* Get all the rest set up. */ UTS(&jb1[0], ksedata, newgroup); /* NOTREACHED */ } } return(1); } static struct kse_mailbox * startkse(struct per_kse *ksedata) { return (kickkse(ksedata, 0)); } static struct kse_mailbox * startksegrp(struct per_kse *ksedata); { return(kickkse(ksedata, 1)); } void badreturn() { printf("thread returned when shouldn't\n"); exit(1); } __inline__ void pushontostack(struct user_thread *tcb, int value) { int *SP; SP = (int *)(tcb->mbox.ctx.trapframe.tf_isp); *--SP = value; tcb->mbox.ctx.trapframe.tf.tf_isp = (int)SP; } struct user_thread * makethread(thread_fn *fn, int arg1, void *arg2) { 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); /* set the PC to the fn */ tcb->mbox.ctx.trapframe.tf_eip = fn; /* Set the stack and push on the args and a dammy return address */ tcb->mbox.ctx.trapframe.tf_isp = tcb->stack + tcb->stack_size - 16; pushontostack(tcb, (int)arg2); pushontostack(tcb, (int)arg1); pushontostack(tcb, (int)&badreturn); /* safety return address */ } /************************************************************* * code for three separate threads. (so we can see if it works) *************************************************************/ static void thread1_code(void *arg) { for(;;) { sleep (1); write(1,".",1); } } static void thread2_code(void *arg) { for(;;) { sleep (3); write(1,"+",1); } } static void thread3_code(void *arg) { for(;;) { sleep (5); write(1,"=",1); } } static struct thread_mailbox * main() { struct kse_mailbox *k_mbox; /* set up global structures */ runq_last = &runqueue; runqueue = NULL; /* 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)); /* and one which we will run ourself */ firstkse->curthread = makethread(&thread3_code, 0, NULL); /* start two KSEs in different KSEGRPs */ k_mbox = startkse(&first_kse); /* startksegrp(&second_kse); */ /* we can't do 2 KSEs yet */ /* One will be sufficient */ /* we are a thread, start the ball rolling */ /* let the kernel know we are it */ firstkse->mbox->current_thread = curthread->mbox; thread3_code(NULL); } To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-arch" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.4.21.0201240046440.25428-100000>