1 /* 2 * This program is free software; you can redistribute it and/or 3 * modify it under the terms of the GNU General Public License as 4 * published by the Free Software Foundation; either version 2 of 5 * the License, or (at your option) any later version. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 15 * MA 02111-1307 USA 16 */ 17 18 #include <common.h> 19 #include <exports.h> 20 21 /* 22 * Author: Arun Dharankar <ADharankar@ATTBI.Com> 23 * 24 * A very simple thread/schedular model: 25 * - only one master thread, and no parent child relation maintained 26 * - parent thread cannot be stopped or deleted 27 * - no permissions or credentials 28 * - no elaborate safety checks 29 * - cooperative multi threading 30 * - Simple round-robin scheduleing with no priorities 31 * - no metering/statistics collection 32 * 33 * Basic idea of implementing this is to allow more than one tests to 34 * execute "simultaneously". 35 * 36 * This may be modified such thread_yield may be called in syscalls, and 37 * timer interrupts. 38 */ 39 40 41 #define MAX_THREADS 8 42 43 #define CTX_SIZE 512 44 #define STK_SIZE 8*1024 45 46 #define STATE_EMPTY 0 47 #define STATE_RUNNABLE 1 48 #define STATE_STOPPED 2 49 #define STATE_TERMINATED 2 50 51 #define MASTER_THREAD 0 52 53 #define RC_FAILURE (-1) 54 #define RC_SUCCESS (0) 55 56 typedef vu_char *jmp_ctx; 57 unsigned long setctxsp (vu_char *sp); 58 int ppc_setjmp(jmp_ctx env); 59 void ppc_longjmp(jmp_ctx env, int val); 60 #define setjmp ppc_setjmp 61 #define longjmp ppc_longjmp 62 63 struct lthread { 64 int state; 65 int retval; 66 char stack[STK_SIZE]; 67 uchar context[CTX_SIZE]; 68 int (*func) (void *); 69 void *arg; 70 }; 71 static volatile struct lthread lthreads[MAX_THREADS]; 72 static volatile int current_tid = MASTER_THREAD; 73 74 75 static uchar dbg = 0; 76 77 #define PDEBUG(fmt, args...) { \ 78 if(dbg != 0) { \ 79 printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\ 80 printf(fmt, ##args); \ 81 printf("\n"); \ 82 } \ 83 } 84 85 static int testthread (void *); 86 static void sched_init (void); 87 static int thread_create (int (*func) (void *), void *arg); 88 static int thread_start (int id); 89 static void thread_yield (void); 90 static int thread_delete (int id); 91 static int thread_join (int *ret); 92 93 #if 0 /* not used yet */ 94 static int thread_stop (int id); 95 #endif /* not used yet */ 96 97 /* An example of schedular test */ 98 99 #define NUMTHREADS 7 100 int sched (int ac, char *av[]) 101 { 102 int i, j; 103 int tid[NUMTHREADS]; 104 int names[NUMTHREADS]; 105 106 app_startup(av); 107 108 sched_init (); 109 110 for (i = 0; i < NUMTHREADS; i++) { 111 names[i] = i; 112 j = thread_create (testthread, (void *) &names[i]); 113 if (j == RC_FAILURE) 114 printf ("schedtest: Failed to create thread %d\n", i); 115 if (j > 0) { 116 printf ("schedtest: Created thread with id %d, name %d\n", 117 j, i); 118 tid[i] = j; 119 } 120 } 121 printf ("schedtest: Threads created\n"); 122 123 printf ("sched_test: function=0x%08x\n", (unsigned)testthread); 124 for (i = 0; i < NUMTHREADS; i++) { 125 printf ("schedtest: Setting thread %d runnable\n", tid[i]); 126 thread_start (tid[i]); 127 thread_yield (); 128 } 129 printf ("schedtest: Started %d threads\n", NUMTHREADS); 130 131 while (1) { 132 printf ("schedtest: Waiting for threads to complete\n"); 133 if (tstc () && getc () == 0x3) { 134 printf ("schedtest: Aborting threads...\n"); 135 for (i = 0; i < NUMTHREADS; i++) { 136 printf ("schedtest: Deleting thread %d\n", tid[i]); 137 thread_delete (tid[i]); 138 } 139 return RC_SUCCESS; 140 } 141 j = -1; 142 i = thread_join (&j); 143 if (i == RC_FAILURE) { 144 printf ("schedtest: No threads pending, " 145 "exiting schedular test\n"); 146 return RC_SUCCESS; 147 } 148 printf ("schedtest: thread is %d returned %d\n", i, j); 149 thread_yield (); 150 } 151 152 return RC_SUCCESS; 153 } 154 155 static int testthread (void *name) 156 { 157 int i; 158 159 printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n", 160 *(int *) name, (unsigned)&i); 161 162 printf ("Thread %02d, i=%d\n", *(int *) name, i); 163 164 for (i = 0; i < 0xffff * (*(int *) name + 1); i++) { 165 if (tstc () && getc () == 0x3) { 166 printf ("testthread: myname %d terminating.\n", 167 *(int *) name); 168 return *(int *) name + 1; 169 } 170 171 if (i % 100 == 0) 172 thread_yield (); 173 } 174 175 printf ("testthread: returning %d, i=0x%x\n", 176 *(int *) name + 1, i); 177 178 return *(int *) name + 1; 179 } 180 181 182 static void sched_init (void) 183 { 184 int i; 185 186 for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) 187 lthreads[i].state = STATE_EMPTY; 188 189 current_tid = MASTER_THREAD; 190 lthreads[current_tid].state = STATE_RUNNABLE; 191 PDEBUG ("sched_init: master context = 0x%08x", 192 (unsigned)lthreads[current_tid].context); 193 return; 194 } 195 196 static void thread_yield (void) 197 { 198 static int i; 199 200 PDEBUG ("thread_yield: current tid=%d", current_tid); 201 202 #define SWITCH(new) \ 203 if(lthreads[new].state == STATE_RUNNABLE) { \ 204 PDEBUG("thread_yield: %d match, ctx=0x%08x", \ 205 new, \ 206 (unsigned)lthreads[current_tid].context); \ 207 if(setjmp(lthreads[current_tid].context) == 0) { \ 208 current_tid = new; \ 209 PDEBUG("thread_yield: tid %d returns 0", \ 210 new); \ 211 longjmp(lthreads[new].context, 1); \ 212 } else { \ 213 PDEBUG("thread_yield: tid %d returns 1", \ 214 new); \ 215 return; \ 216 } \ 217 } 218 219 for (i = current_tid + 1; i < MAX_THREADS; i++) { 220 SWITCH (i); 221 } 222 223 if (current_tid != 0) { 224 for (i = 0; i <= current_tid; i++) { 225 SWITCH (i); 226 } 227 } 228 229 PDEBUG ("thread_yield: returning from thread_yield"); 230 return; 231 } 232 233 static int thread_create (int (*func) (void *), void *arg) 234 { 235 int i; 236 237 for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { 238 if (lthreads[i].state == STATE_EMPTY) { 239 lthreads[i].state = STATE_STOPPED; 240 lthreads[i].func = func; 241 lthreads[i].arg = arg; 242 PDEBUG ("thread_create: returns new tid %d", i); 243 return i; 244 } 245 } 246 247 PDEBUG ("thread_create: returns failure"); 248 return RC_FAILURE; 249 } 250 251 static int thread_delete (int id) 252 { 253 if (id <= MASTER_THREAD || id > MAX_THREADS) 254 return RC_FAILURE; 255 256 if (current_tid == id) 257 return RC_FAILURE; 258 259 lthreads[id].state = STATE_EMPTY; 260 return RC_SUCCESS; 261 } 262 263 static void thread_launcher (void) 264 { 265 PDEBUG ("thread_launcher: invoking func=0x%08x", 266 (unsigned)lthreads[current_tid].func); 267 268 lthreads[current_tid].retval = 269 lthreads[current_tid].func (lthreads[current_tid].arg); 270 271 PDEBUG ("thread_launcher: tid %d terminated", current_tid); 272 273 lthreads[current_tid].state = STATE_TERMINATED; 274 thread_yield (); 275 printf ("thread_launcher: should NEVER get here!\n"); 276 277 return; 278 } 279 280 static int thread_start (int id) 281 { 282 PDEBUG ("thread_start: id=%d", id); 283 if (id <= MASTER_THREAD || id > MAX_THREADS) { 284 return RC_FAILURE; 285 } 286 287 if (lthreads[id].state != STATE_STOPPED) 288 return RC_FAILURE; 289 290 if (setjmp (lthreads[current_tid].context) == 0) { 291 lthreads[id].state = STATE_RUNNABLE; 292 current_tid = id; 293 PDEBUG ("thread_start: to be stack=0%08x", 294 (unsigned)lthreads[id].stack); 295 setctxsp ((vu_char *)<hreads[id].stack[STK_SIZE]); 296 thread_launcher (); 297 } 298 299 PDEBUG ("thread_start: Thread id=%d started, parent returns", id); 300 301 return RC_SUCCESS; 302 } 303 304 #if 0 /* not used so far */ 305 static int thread_stop (int id) 306 { 307 if (id <= MASTER_THREAD || id >= MAX_THREADS) 308 return RC_FAILURE; 309 310 if (current_tid == id) 311 return RC_FAILURE; 312 313 lthreads[id].state = STATE_STOPPED; 314 return RC_SUCCESS; 315 } 316 #endif /* not used so far */ 317 318 static int thread_join (int *ret) 319 { 320 int i, j = 0; 321 322 PDEBUG ("thread_join: *ret = %d", *ret); 323 324 if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) { 325 PDEBUG ("thread_join: invalid tid %d", *ret); 326 return RC_FAILURE; 327 } 328 329 if (*ret == -1) { 330 PDEBUG ("Checking for tid = -1"); 331 while (1) { 332 /* PDEBUG("thread_join: start while-loopn"); */ 333 j = 0; 334 for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { 335 if (lthreads[i].state == STATE_TERMINATED) { 336 *ret = lthreads[i].retval; 337 lthreads[i].state = STATE_EMPTY; 338 /* PDEBUG("thread_join: returning retval %d of tid %d", 339 ret, i); */ 340 return RC_SUCCESS; 341 } 342 343 if (lthreads[i].state != STATE_EMPTY) { 344 PDEBUG ("thread_join: %d used slots tid %d state=%d", 345 j, i, lthreads[i].state); 346 j++; 347 } 348 } 349 if (j == 0) { 350 PDEBUG ("thread_join: all slots empty!"); 351 return RC_FAILURE; 352 } 353 /* PDEBUG("thread_join: yielding"); */ 354 thread_yield (); 355 /* PDEBUG("thread_join: back from yield"); */ 356 } 357 } 358 359 if (lthreads[*ret].state == STATE_TERMINATED) { 360 i = *ret; 361 *ret = lthreads[*ret].retval; 362 lthreads[*ret].state = STATE_EMPTY; 363 PDEBUG ("thread_join: returing %d for tid %d", *ret, i); 364 return RC_SUCCESS; 365 } 366 367 PDEBUG ("thread_join: thread %d is not terminated!", *ret); 368 return RC_FAILURE; 369 } 370