Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 05 Feb 2002 20:19:46 -0500
From:      Glenn Gombert <ggombert@imatowns.com>
To:        Julian Elischer <julian@elischer.org>, arch@freebsd.org
Subject:   Re: simple KSE test program
Message-ID:  <3.0.6.32.20020205201946.00d9eef8@imatowns.com>
In-Reply-To: <Pine.BSF.4.21.0201240046440.25428-100000@InterJet.elischer .org>

next in thread | previous in thread | raw e-mail | index | archive | help


I have a kernel running with all the latest kse changes from cvsup10
running on my dual PIII system (with SMP support commented out of the
kernel config file) and it appears to be running most things well in
'uni-processor' mode. I am trying to get the protoype test program compiled
below and was wondering if you had a compiled version working yet? Or shall
I post mine as soon as I get it working.....

Glenn G. 

At 01:41 AM 1/24/2002 -0800, Julian Elischer wrote:
>
>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
>
Glenn Gombert
ggombert@imatowns.com

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?3.0.6.32.20020205201946.00d9eef8>