u-boot/examples/standalone/sched.c
Tom Rini d678a59d2d Revert "Merge patch series "arm: dts: am62-beagleplay: Fix Beagleplay Ethernet""
When bringing in the series 'arm: dts: am62-beagleplay: Fix Beagleplay
Ethernet"' I failed to notice that b4 noticed it was based on next and
so took that as the base commit and merged that part of next to master.

This reverts commit c8ffd1356d, reversing
changes made to 2ee6f3a5f7.

Reported-by: Jonas Karlman <jonas@kwiboo.se>
Signed-off-by: Tom Rini <trini@konsulko.com>
2024-05-19 08:16:36 -06:00

354 lines
8 KiB
C

// SPDX-License-Identifier: GPL-2.0+
#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 *)&lthreads[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;
}