|  | /* | 
|  | * This program is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU General Public License as | 
|  | * published by the Free Software Foundation; either version 2 of | 
|  | * the License, or (at your option) any later version. | 
|  | * | 
|  | * This program is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | * GNU General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License | 
|  | * along with this program; if not, write to the Free Software | 
|  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | 
|  | * MA 02111-1307 USA | 
|  | */ | 
|  |  | 
|  | #include <common.h> | 
|  | #include <exports.h> | 
|  |  | 
|  | /* | 
|  | * Author: Arun Dharankar <ADharankar@ATTBI.Com> | 
|  | * | 
|  | * A very simple thread/schedular model: | 
|  | *   - only one master thread, and no parent child relation maintained | 
|  | *   - parent thread cannot be stopped or deleted | 
|  | *   - no permissions or credentials | 
|  | *   - no elaborate safety checks | 
|  | *   - cooperative multi threading | 
|  | *   - Simple round-robin scheduleing with no priorities | 
|  | *   - no metering/statistics collection | 
|  | * | 
|  | * Basic idea of implementing this is to allow more than one tests to | 
|  | * execute "simultaneously". | 
|  | * | 
|  | * This may be modified such thread_yield may be called in syscalls, and | 
|  | * timer interrupts. | 
|  | */ | 
|  |  | 
|  |  | 
|  | #define MAX_THREADS 8 | 
|  |  | 
|  | #define CTX_SIZE 512 | 
|  | #define STK_SIZE 8*1024 | 
|  |  | 
|  | #define STATE_EMPTY 0 | 
|  | #define STATE_RUNNABLE 1 | 
|  | #define STATE_STOPPED 2 | 
|  | #define STATE_TERMINATED 2 | 
|  |  | 
|  | #define MASTER_THREAD 0 | 
|  |  | 
|  | #define RC_FAILURE	(-1) | 
|  | #define	RC_SUCCESS	(0) | 
|  |  | 
|  | typedef	vu_char *jmp_ctx; | 
|  | unsigned long setctxsp (vu_char *sp); | 
|  | int ppc_setjmp(jmp_ctx env); | 
|  | void ppc_longjmp(jmp_ctx env, int val); | 
|  | #define setjmp	ppc_setjmp | 
|  | #define longjmp	ppc_longjmp | 
|  |  | 
|  | struct lthread { | 
|  | int state; | 
|  | int retval; | 
|  | char stack[STK_SIZE]; | 
|  | uchar context[CTX_SIZE]; | 
|  | int (*func) (void *); | 
|  | void *arg; | 
|  | }; | 
|  | static volatile struct lthread lthreads[MAX_THREADS]; | 
|  | static volatile int current_tid = MASTER_THREAD; | 
|  |  | 
|  |  | 
|  | static uchar dbg = 0; | 
|  |  | 
|  | #define PDEBUG(fmt, args...)	 {					\ | 
|  | if(dbg != 0) {							\ | 
|  | printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\ | 
|  | printf(fmt, ##args);				\ | 
|  | printf("\n");					\ | 
|  | }								\ | 
|  | } | 
|  |  | 
|  | static int testthread (void *); | 
|  | static void sched_init (void); | 
|  | static int thread_create (int (*func) (void *), void *arg); | 
|  | static int thread_start (int id); | 
|  | static void thread_yield (void); | 
|  | static int thread_delete (int id); | 
|  | static int thread_join (int *ret); | 
|  |  | 
|  | #if 0							/* not used yet */ | 
|  | static int thread_stop (int id); | 
|  | #endif							/* not used yet */ | 
|  |  | 
|  | /* An example of schedular test */ | 
|  |  | 
|  | #define NUMTHREADS 7 | 
|  | int sched (int ac, char *av[]) | 
|  | { | 
|  | int i, j; | 
|  | int tid[NUMTHREADS]; | 
|  | int names[NUMTHREADS]; | 
|  |  | 
|  | app_startup(av); | 
|  |  | 
|  | sched_init (); | 
|  |  | 
|  | for (i = 0; i < NUMTHREADS; i++) { | 
|  | names[i] = i; | 
|  | j = thread_create (testthread, (void *) &names[i]); | 
|  | if (j == RC_FAILURE) | 
|  | printf ("schedtest: Failed to create thread %d\n", i); | 
|  | if (j > 0) { | 
|  | printf ("schedtest: Created thread with id %d, name %d\n", | 
|  | j, i); | 
|  | tid[i] = j; | 
|  | } | 
|  | } | 
|  | printf ("schedtest: Threads created\n"); | 
|  |  | 
|  | printf ("sched_test: function=0x%08x\n", (unsigned)testthread); | 
|  | for (i = 0; i < NUMTHREADS; i++) { | 
|  | printf ("schedtest: Setting thread %d runnable\n", tid[i]); | 
|  | thread_start (tid[i]); | 
|  | thread_yield (); | 
|  | } | 
|  | printf ("schedtest: Started %d threads\n", NUMTHREADS); | 
|  |  | 
|  | while (1) { | 
|  | printf ("schedtest: Waiting for threads to complete\n"); | 
|  | if (tstc () && getc () == 0x3) { | 
|  | printf ("schedtest: Aborting threads...\n"); | 
|  | for (i = 0; i < NUMTHREADS; i++) { | 
|  | printf ("schedtest: Deleting thread %d\n", tid[i]); | 
|  | thread_delete (tid[i]); | 
|  | } | 
|  | return RC_SUCCESS; | 
|  | } | 
|  | j = -1; | 
|  | i = thread_join (&j); | 
|  | if (i == RC_FAILURE) { | 
|  | printf ("schedtest: No threads pending, " | 
|  | "exiting schedular test\n"); | 
|  | return RC_SUCCESS; | 
|  | } | 
|  | printf ("schedtest: thread is %d returned %d\n", i, j); | 
|  | thread_yield (); | 
|  | } | 
|  |  | 
|  | return RC_SUCCESS; | 
|  | } | 
|  |  | 
|  | static int testthread (void *name) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n", | 
|  | *(int *) name, (unsigned)&i); | 
|  |  | 
|  | printf ("Thread %02d, i=%d\n", *(int *) name, i); | 
|  |  | 
|  | for (i = 0; i < 0xffff * (*(int *) name + 1); i++) { | 
|  | if (tstc () && getc () == 0x3) { | 
|  | printf ("testthread: myname %d terminating.\n", | 
|  | *(int *) name); | 
|  | return *(int *) name + 1; | 
|  | } | 
|  |  | 
|  | if (i % 100 == 0) | 
|  | thread_yield (); | 
|  | } | 
|  |  | 
|  | printf ("testthread: returning %d, i=0x%x\n", | 
|  | *(int *) name + 1, i); | 
|  |  | 
|  | return *(int *) name + 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void sched_init (void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) | 
|  | lthreads[i].state = STATE_EMPTY; | 
|  |  | 
|  | current_tid = MASTER_THREAD; | 
|  | lthreads[current_tid].state = STATE_RUNNABLE; | 
|  | PDEBUG ("sched_init: master context = 0x%08x", | 
|  | (unsigned)lthreads[current_tid].context); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static void thread_yield (void) | 
|  | { | 
|  | static int i; | 
|  |  | 
|  | PDEBUG ("thread_yield: current tid=%d", current_tid); | 
|  |  | 
|  | #define SWITCH(new) 							\ | 
|  | if(lthreads[new].state == STATE_RUNNABLE) {			\ | 
|  | PDEBUG("thread_yield: %d match, ctx=0x%08x",		\ | 
|  | new,						\ | 
|  | (unsigned)lthreads[current_tid].context);	\ | 
|  | if(setjmp(lthreads[current_tid].context) == 0) {	\ | 
|  | current_tid = new;				\ | 
|  | PDEBUG("thread_yield: tid %d returns 0",	\ | 
|  | new); 					\ | 
|  | longjmp(lthreads[new].context, 1);		\ | 
|  | } else {						\ | 
|  | PDEBUG("thread_yield: tid %d returns 1",	\ | 
|  | new); 					\ | 
|  | return;						\ | 
|  | }							\ | 
|  | } | 
|  |  | 
|  | for (i = current_tid + 1; i < MAX_THREADS; i++) { | 
|  | SWITCH (i); | 
|  | } | 
|  |  | 
|  | if (current_tid != 0) { | 
|  | for (i = 0; i <= current_tid; i++) { | 
|  | SWITCH (i); | 
|  | } | 
|  | } | 
|  |  | 
|  | PDEBUG ("thread_yield: returning from thread_yield"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | static int thread_create (int (*func) (void *), void *arg) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { | 
|  | if (lthreads[i].state == STATE_EMPTY) { | 
|  | lthreads[i].state = STATE_STOPPED; | 
|  | lthreads[i].func = func; | 
|  | lthreads[i].arg = arg; | 
|  | PDEBUG ("thread_create: returns new tid %d", i); | 
|  | return i; | 
|  | } | 
|  | } | 
|  |  | 
|  | PDEBUG ("thread_create: returns failure"); | 
|  | return RC_FAILURE; | 
|  | } | 
|  |  | 
|  | static int thread_delete (int id) | 
|  | { | 
|  | if (id <= MASTER_THREAD || id > MAX_THREADS) | 
|  | return RC_FAILURE; | 
|  |  | 
|  | if (current_tid == id) | 
|  | return RC_FAILURE; | 
|  |  | 
|  | lthreads[id].state = STATE_EMPTY; | 
|  | return RC_SUCCESS; | 
|  | } | 
|  |  | 
|  | static void thread_launcher (void) | 
|  | { | 
|  | PDEBUG ("thread_launcher: invoking func=0x%08x", | 
|  | (unsigned)lthreads[current_tid].func); | 
|  |  | 
|  | lthreads[current_tid].retval = | 
|  | lthreads[current_tid].func (lthreads[current_tid].arg); | 
|  |  | 
|  | PDEBUG ("thread_launcher: tid %d terminated", current_tid); | 
|  |  | 
|  | lthreads[current_tid].state = STATE_TERMINATED; | 
|  | thread_yield (); | 
|  | printf ("thread_launcher: should NEVER get here!\n"); | 
|  |  | 
|  | return; | 
|  | } | 
|  |  | 
|  | static int thread_start (int id) | 
|  | { | 
|  | PDEBUG ("thread_start: id=%d", id); | 
|  | if (id <= MASTER_THREAD || id > MAX_THREADS) { | 
|  | return RC_FAILURE; | 
|  | } | 
|  |  | 
|  | if (lthreads[id].state != STATE_STOPPED) | 
|  | return RC_FAILURE; | 
|  |  | 
|  | if (setjmp (lthreads[current_tid].context) == 0) { | 
|  | lthreads[id].state = STATE_RUNNABLE; | 
|  | current_tid = id; | 
|  | PDEBUG ("thread_start: to be stack=0%08x", | 
|  | (unsigned)lthreads[id].stack); | 
|  | setctxsp ((vu_char *)<hreads[id].stack[STK_SIZE]); | 
|  | thread_launcher (); | 
|  | } | 
|  |  | 
|  | PDEBUG ("thread_start: Thread id=%d started, parent returns", id); | 
|  |  | 
|  | return RC_SUCCESS; | 
|  | } | 
|  |  | 
|  | #if 0	/* not used so far */ | 
|  | static int thread_stop (int id) | 
|  | { | 
|  | if (id <= MASTER_THREAD || id >= MAX_THREADS) | 
|  | return RC_FAILURE; | 
|  |  | 
|  | if (current_tid == id) | 
|  | return RC_FAILURE; | 
|  |  | 
|  | lthreads[id].state = STATE_STOPPED; | 
|  | return RC_SUCCESS; | 
|  | } | 
|  | #endif	/* not used so far */ | 
|  |  | 
|  | static int thread_join (int *ret) | 
|  | { | 
|  | int i, j = 0; | 
|  |  | 
|  | PDEBUG ("thread_join: *ret = %d", *ret); | 
|  |  | 
|  | if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) { | 
|  | PDEBUG ("thread_join: invalid tid %d", *ret); | 
|  | return RC_FAILURE; | 
|  | } | 
|  |  | 
|  | if (*ret == -1) { | 
|  | PDEBUG ("Checking for tid = -1"); | 
|  | while (1) { | 
|  | /* PDEBUG("thread_join: start while-loopn"); */ | 
|  | j = 0; | 
|  | for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { | 
|  | if (lthreads[i].state == STATE_TERMINATED) { | 
|  | *ret = lthreads[i].retval; | 
|  | lthreads[i].state = STATE_EMPTY; | 
|  | /* PDEBUG("thread_join: returning retval %d of tid %d", | 
|  | ret, i); */ | 
|  | return RC_SUCCESS; | 
|  | } | 
|  |  | 
|  | if (lthreads[i].state != STATE_EMPTY) { | 
|  | PDEBUG ("thread_join: %d used slots tid %d state=%d", | 
|  | j, i, lthreads[i].state); | 
|  | j++; | 
|  | } | 
|  | } | 
|  | if (j == 0) { | 
|  | PDEBUG ("thread_join: all slots empty!"); | 
|  | return RC_FAILURE; | 
|  | } | 
|  | /*  PDEBUG("thread_join: yielding"); */ | 
|  | thread_yield (); | 
|  | /*  PDEBUG("thread_join: back from yield"); */ | 
|  | } | 
|  | } | 
|  |  | 
|  | if (lthreads[*ret].state == STATE_TERMINATED) { | 
|  | i = *ret; | 
|  | *ret = lthreads[*ret].retval; | 
|  | lthreads[*ret].state = STATE_EMPTY; | 
|  | PDEBUG ("thread_join: returing %d for tid %d", *ret, i); | 
|  | return RC_SUCCESS; | 
|  | } | 
|  |  | 
|  | PDEBUG ("thread_join: thread %d is not terminated!", *ret); | 
|  | return RC_FAILURE; | 
|  | } |