uboot/examples/standalone/sched.c
<<
>>
Prefs
   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
  56typedef vu_char *jmp_ctx;
  57unsigned long setctxsp (vu_char *sp);
  58int ppc_setjmp(jmp_ctx env);
  59void ppc_longjmp(jmp_ctx env, int val);
  60#define setjmp  ppc_setjmp
  61#define longjmp ppc_longjmp
  62
  63struct 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};
  71static volatile struct lthread lthreads[MAX_THREADS];
  72static volatile int current_tid = MASTER_THREAD;
  73
  74
  75static 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
  85static int testthread (void *);
  86static void sched_init (void);
  87static int thread_create (int (*func) (void *), void *arg);
  88static int thread_start (int id);
  89static void thread_yield (void);
  90static int thread_delete (int id);
  91static int thread_join (int *ret);
  92
  93#if 0                                                   /* not used yet */
  94static int thread_stop (int id);
  95#endif                                                  /* not used yet */
  96
  97/* An example of schedular test */
  98
  99#define NUMTHREADS 7
 100int 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
 155static 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
 182static 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
 196static 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
 233static 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
 251static 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
 263static 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
 280static 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 *)&lthreads[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 */
 305static 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
 318static 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