1/* 2 * $FreeBSD: head/tools/KSE/ksetest/kse_threads_test.c 99073 2002-06-29 17:39:07Z julian $
| 1/*- 2 * Copyright (c) 2002 Jonathan Mini (mini@freebsd.org). 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/tools/KSE/ksetest/kse_threads_test.c 103581 2002-09-19 02:15:27Z mini $
|
3 */
| 27 */
|
4#define _KERNEL
| |
5
| 28
|
6#include <stdio.h> 7#include <stdlib.h> 8#include <setjmp.h>
| 29#include <sys/types.h> 30#include <sys/signal.h> 31#include <sys/signalvar.h> 32#include <sys/sysctl.h>
|
9#include <sys/kse.h>
| 33#include <sys/kse.h>
|
10#include <sys/queue.h>
| 34#include <sys/ucontext.h>
|
11
| 35
|
12/*#define DEBUG*/ 13/************************************************************* 14 * These should probably be in a .h file 15 **************************************************************/ 16typedef void thread_fn(void *arg);
| 36#include <stdarg.h> 37#include <stddef.h> 38#include <stdlib.h> 39#include <string.h> 40#include <sysexits.h> 41#include <time.h> 42#include <unistd.h>
|
17
| 43
|
18struct user_thread { 19 struct thread_mailbox mbox; 20 char *stack; 21 int stack_size; 22 TAILQ_ENTRY(user_thread) runq_next; 23};
| 44#undef TRACE_UTS
|
24
| 45
|
25struct per_kse { 26 struct kse_mailbox mbox; 27 struct user_thread *curthread; 28}; 29/************************************************************* 30 * Debug stuff 31 **************************************************************/ 32#ifdef DEBUG
| 46#ifdef TRACE_UTS 47#define UPFMT(fmt...) pfmt(#fmt) 48#define UPSTR(s) pstr(s) 49#define UPCHAR(c) pchar(c) 50#else 51#define UPFMT(fmt...) /* Nothing. */ 52#define UPSTR(s) /* Nothing. */ 53#define UPCHAR(c) /* Nothing. */ 54#endif
|
33
| 55
|
34jmp_buf jb3; 35#define DUMPREGS(desc) do {_setjmp(jb3); printjb(jb3, desc); } while (0)
| 56#define MAIN_STACK_SIZE (1024 * 1024) 57#define THREAD_STACK_SIZE (32 * 1024)
|
36
| 58
|
37char *regname[] = {"%eip","%ebx","%esp","%ebp", 38 "%esi","%edi","fpcr","MSK0", 39 "MSK1","MSK2","MSK3"};
| 59static struct kse_mailbox uts_mb; 60static struct thread_mailbox *run_queue; 61static struct thread_mailbox *aa;
|
40
| 62
|
| 63static int progress = 0;
|
41
| 64
|
42static 43printjb(struct _jmp_buf *jb, char *desc) 44{ 45 46 int i; 47 printf("jb (%s) is at 0x%x\n", desc, jb); 48 for( i = 0; i< _JBLEN-4; i++) { 49 printf("jb[%d] (%s) = 0x%x\n", i, regname[i], jb[0]._jb[i]); 50 } 51} 52#else 53#define DUMPREGS(desc) do {} while (0) 54#endif
| 65static void init_uts(void); 66static void enter_uts(void); 67static void pchar(char c); 68static void pfmt(const char *fmt, ...); 69static void pstr(const char *s); 70static void runq_insert(struct thread_mailbox *tm); 71static struct thread_mailbox *runq_remove(void); 72static void thread_start(const void *func, int arg); 73static void uts(struct kse_mailbox *km);
|
55
| 74
|
56/************************************************************* 57 * Globals 58 **************************************************************/ 59struct per_kse first_kse; /* for NOW cheat and make it global */ 60TAILQ_HEAD(, user_thread) runqueue = TAILQ_HEAD_INITIALIZER(runqueue); 61/************************************************************* 62 * Implementation parameters 63 **************************************************************/ 64#define T_STACKSIZE (16*4096) /* thread stacksize */ 65#define K_STACKSIZE (1*4096) /* KSE (UTS) stacksize */
| 75extern int uts_to_thread(struct thread_mailbox *tdp, struct thread_mailbox **curthreadp);
|
66
| 76
|
67/************************************************************* 68 * UTS funcions. 69 * Simple round_robin for now. 70 **************************************************************/
| |
71static void
| 77static void
|
72runq_insert(struct user_thread *thread)
| 78nano(int len)
|
73{
| 79{
|
74 TAILQ_INSERT_TAIL(&runqueue, thread, runq_next); 75}
| 80 struct timespec time_to_sleep; 81 struct timespec time_remaining;
|
76
| 82
|
77static struct user_thread * 78select_thread(void) 79{ 80 struct user_thread *thread; 81 82 if ((thread = TAILQ_FIRST(&runqueue))) { 83 TAILQ_REMOVE(&runqueue, thread, runq_next); 84 } 85 return (thread);
| 83 time_to_sleep.tv_sec = 0; 84 time_to_sleep.tv_nsec = len * 10000; 85 nanosleep(&time_to_sleep, &time_remaining);
|
86} 87
| 86} 87
|
88/************************************************************* 89 * The UTS upcall entrypoint 90 * Called once on startup (and left by longjump) 91 * and there-after, returned to by the upcall many times. 92 **************************************************************/
| |
93void
| 88void
|
94UTS(struct kse_mailbox *ke_mbox)
| 89aaaa(int c)
|
95{
| 90{
|
96 struct user_thread *thread; 97 struct thread_mailbox *completed; 98 struct per_kse *ksedata; 99 int done = 0; 100 101 /**********************************/ 102 /* UTS upcall starts running here. */ 103 /**********************************/ 104 /**********************************/ 105 106 ksedata = ke_mbox->kmbx_UTS_handle; 107 /* If there are returned syscall threads, put them on the run queue */ 108 if ((completed = ke_mbox->kmbx_completed_threads)) { 109 ke_mbox->kmbx_completed_threads = NULL; 110 while (completed) { 111 thread = completed->UTS_handle; 112 completed = completed->next_completed; 113 runq_insert(thread); 114 }
| 91 for (;;) { 92 pchar(c); 93 nano(1);
|
115 }
| 94 }
|
116 117 /* find highest priority thread and load it */ 118 if ((thread = select_thread())) { 119 ksedata->curthread = thread; 120 ke_mbox->kmbx_current_thread = &thread->mbox; 121 122 /* loads context similar to longjmp() */ 123 loadthread(&thread->mbox.ctx.tfrm.tf_tf); 124 /* NOTREACHED */ 125 } 126 kse_yield(); /* in the kernel it does a thread_exit() */ 127 /* NOTREACHED */ 128} 129 130/************************************************************* 131 * Startup mechanism functions 132 **************************************************************/ 133static int 134kickkse(struct per_kse *ksedata, int newgroup) 135{ 136 char * newstack; 137 jmp_buf jb1; 138 jmp_buf jb2; 139 struct kse_mailbox *mboxaddr; 140 struct per_kse *user_UTS_info; 141 int err; 142 143 newstack = malloc(K_STACKSIZE); 144 mboxaddr = &ksedata->mbox; 145 mboxaddr->kmbx_stackbase = newstack; 146 mboxaddr->kmbx_stacksize = K_STACKSIZE; 147 mboxaddr->kmbx_upcall = &UTS; 148 mboxaddr->kmbx_UTS_handle = ksedata; 149 err = kse_new(mboxaddr, newgroup); 150 return(err);
| |
151} 152
| 95} 96
|
153 154static int 155startkse(struct per_kse *ksedata)
| 97static void 98foof(int sig)
|
156{
| 99{
|
157 return (kickkse(ksedata, 0));
| 100 pfmt("\n[%d]\n", sig); 101 thread_start(aaaa, '0' + progress++);
|
158} 159
| 102} 103
|
160static int 161startksegrp(struct per_kse *ksedata)
| 104void 105spin(int arg)
|
162{
| 106{
|
163 return(kickkse(ksedata, 1));
| 107 for (;;) enter_uts(); sched_yield();
|
164} 165
| 108} 109
|
166void badreturn()
| 110/* 111 * Test Userland Thread Scheduler (UTS) suite for KSE. 112 */ 113int 114main(void)
|
167{
| 115{
|
168 printf("thread returned when shouldn't\n"); 169 exit(1);
| 116 int i; 117 118 thread_start(spin, '.'); 119 // thread_start(spin); 120 init_uts(); 121 for (i = 0;1;i++) { 122// if (i < 1000) 123// thread_start(aaaa, 'a' + (i % 26)); 124 pchar('A' + (i % 26)); 125 nano(5); 126 } 127 pstr("\n** main() exiting **\n"); 128 return (EX_OK);
|
170} 171
| 129} 130
|
172__inline__ void 173pushontostack(struct user_thread *tcb, int value)
| 131 132/* 133 * Enter the UTS from a thread. 134 */ 135static void 136enter_uts(void)
|
174{
| 137{
|
175 int *SP;
| 138 struct thread_mailbox *td;
|
176
| 139
|
177 SP = (int *)(tcb->mbox.ctx.tfrm.tf_tf.tf_isp); 178 *--SP = value; 179 tcb->mbox.ctx.tfrm.tf_tf.tf_isp = (int)SP;
| 140 /* XXX: We should atomically exchange these two. */ 141 td = uts_mb.km_curthread; 142 uts_mb.km_curthread = NULL; 143 144 thread_to_uts(td, &uts_mb);
|
180} 181
| 145} 146
|
182struct user_thread * 183makethread(thread_fn *fn, int arg1, void *arg2)
| 147/* 148 * Initialise threading. 149 */ 150static void 151init_uts(void)
|
184{
| 152{
|
185 struct user_thread *tcb;
| 153 struct thread_mailbox *tm; 154 int mib[2]; 155 char *p; 156 size_t len;
|
186
| 157
|
187 /* We could combine these mallocs */ 188 tcb = malloc(sizeof *tcb); 189 bzero(tcb, sizeof(*tcb)); 190 tcb->mbox.UTS_handle = tcb; /* back pointer */
| 158 /* 159 * Create initial thread. 160 */ 161 tm = (struct thread_mailbox *)calloc(1, sizeof(struct thread_mailbox));
|
191
| 162
|
192 /* malloc the thread's stack */ 193 /* We COULD mmap it with STACK characteristics */ 194 /* Then we could add a guard page. */ 195 tcb->stack_size = T_STACKSIZE; /* set the size we want */ 196 tcb->stack = malloc(tcb->stack_size);
| 163 /* Throw us into its context. */ 164 getcontext(&tm->tm_context);
|
197
| 165
|
198 /* Make sure there are good defaults */ 199 savethread(&tcb->mbox.ctx.tfrm.tf_tf);
| 166 /* Find our stack. */ 167 mib[0] = CTL_KERN; 168 mib[1] = KERN_USRSTACK; 169 len = sizeof(p); 170 if (sysctl(mib, 2, &p, &len, NULL, 0) == -1) 171 pstr("sysctl(CTL_KER.KERN_USRSTACK) failed.\n"); 172 pfmt("main() : 0x%x\n", tm); 173 pfmt("eip -> 0x%x\n", tm->tm_context.uc_mcontext.mc_eip); 174 tm->tm_context.uc_stack.ss_sp = p - MAIN_STACK_SIZE; 175 tm->tm_context.uc_stack.ss_size = MAIN_STACK_SIZE;
|
200
| 176
|
201 /* set the PC to the fn */ 202 tcb->mbox.ctx.tfrm.tf_tf.tf_eip = (int) fn;
| 177 /* 178 * Create KSE mailbox. 179 */ 180 p = (char *)malloc(THREAD_STACK_SIZE); 181 bzero(&uts_mb, sizeof(struct kse_mailbox)); 182 uts_mb.km_stack.ss_sp = p; 183 uts_mb.km_stack.ss_size = THREAD_STACK_SIZE; 184 uts_mb.km_func = (void *)uts; 185 pfmt("uts() at : 0x%x\n", uts); 186 pfmt("uts stack at : 0x%x - 0x%x\n", p, p + THREAD_STACK_SIZE);
|
203
| 187
|
204 /* Set the stack and push on the args and a dummy return address */ 205 tcb->mbox.ctx.tfrm.tf_tf.tf_ebp = 206 tcb->mbox.ctx.tfrm.tf_tf.tf_isp = 207 tcb->mbox.ctx.tfrm.tf_tf.tf_esp = 208 (int)(&tcb->stack[tcb->stack_size - 16]); 209 pushontostack(tcb, (int)arg2); 210 pushontostack(tcb, (int)arg1); 211 pushontostack(tcb, (int)&badreturn); /* safety return address */ 212 return (tcb);
| 188 /* 189 * Start KSE scheduling. 190 */ 191 pfmt("kse_new() -> %d\n", kse_new(&uts_mb, 0)); 192 uts_mb.km_curthread = tm; 193 194 /* 195 * Arrange to deliver signals via KSE. 196 */ 197 signal(SIGURG, foof);
|
213} 214
| 198} 199
|
215/************************************************************* 216 * code for three separate threads. (so we can see if it works) 217 *************************************************************/ 218static void 219thread1_code(void *arg)
| 200/* 201 * Write a single character to stdout, in a thread-safe manner. 202 */ 203static void 204pchar(char c)
|
220{
| 205{
|
221 for(;;) { 222 sleep (1); 223 write(1,".",1); 224 }
| 206 207 write(STDOUT_FILENO, &c, 1);
|
225} 226
| 208} 209
|
227static void 228thread2_code(void *arg)
| 210/* 211 * Write formatted output to stdout, in a thread-safe manner. 212 * 213 * Recognises the following conversions: 214 * %c -> char 215 * %d -> signed int (base 10) 216 * %s -> string 217 * %u -> unsigned int (base 10) 218 * %x -> unsigned int (base 16) 219 */ 220static void 221pfmt(const char *fmt, ...)
|
229{
| 222{
|
230 for(;;) { 231 sleep (3); 232 write(1,"+",1);
| 223 static const char digits[16] = "0123456789abcdef"; 224 va_list ap; 225 char buf[10]; 226 char *s; 227 unsigned r, u; 228 int c, d; 229 230 va_start(ap, fmt); 231 while ((c = *fmt++)) { 232 if (c == '%') { 233 c = *fmt++; 234 switch (c) { 235 case 'c': 236 pchar(va_arg(ap, int)); 237 continue; 238 case 's': 239 pstr(va_arg(ap, char *)); 240 continue; 241 case 'd': 242 case 'u': 243 case 'x': 244 r = ((c == 'u') || (c == 'd')) ? 10 : 16; 245 if (c == 'd') { 246 d = va_arg(ap, unsigned); 247 if (d < 0) { 248 pchar('-'); 249 u = (unsigned)(d * -1); 250 } else 251 u = (unsigned)d; 252 } else 253 u = va_arg(ap, unsigned); 254 s = buf; 255 do { 256 *s++ = digits[u % r]; 257 } while (u /= r); 258 while (--s >= buf) 259 pchar(*s); 260 continue; 261 } 262 } 263 pchar(c);
|
233 }
| 264 }
|
| 265 va_end(ap);
|
234} 235
| 266} 267
|
236static void 237thread3_code(void *arg)
| 268static void 269pstr(const char *s)
|
238{
| 270{
|
239 for(;;) { 240 sleep (5); 241 write(1,"=",1); 242 }
| 271 272 write(STDOUT_FILENO, s, strlen(s));
|
243} 244
| 273} 274
|
| 275/* 276 * Insert a thread into the run queue. 277 */ 278static void 279runq_insert(struct thread_mailbox *tm) 280{
|
245
| 281
|
| 282 tm->tm_next = run_queue; 283 run_queue = tm; 284}
|
246
| 285
|
247int main()
| 286/* 287 * Select and remove a thread from the run queue. 288 */ 289static struct thread_mailbox * 290runq_remove(void)
|
248{
| 291{
|
| 292 struct thread_mailbox *p, *p1;
|
249
| 293
|
250 /* set up global structures */ 251 TAILQ_INIT(&runqueue);
| 294 if (run_queue == NULL) 295 return (NULL); 296 p1 = NULL; 297 for (p = run_queue; p->tm_next != NULL; p = p->tm_next) 298 p1 = p; 299 if (p1 == NULL) 300 run_queue = NULL; 301 else 302 p1->tm_next = NULL; 303 return (p); 304}
|
252
| 305
|
253 /* define two threads to run, they are runnable but not yet running */ 254 runq_insert( makethread(&thread1_code, 0, NULL)); 255 runq_insert( makethread(&thread2_code, 0, NULL));
| 306/* 307 * Userland thread scheduler. 308 */ 309static void 310uts(struct kse_mailbox *km) 311{ 312 struct thread_mailbox *tm, *p; 313 int ret, i;
|
256
| 314
|
257 /* and one which we will run ourself */ 258 first_kse.curthread = makethread(&thread3_code, 0, NULL);
| 315 UPSTR("\n--uts() start--\n"); 316 UPFMT("mailbox -> %x\n", km);
|
259
| 317
|
260 /* start two KSEs in different KSEGRPs */ 261 if (startkse(&first_kse)) { 262 perror("failed to start KSE"); 263 exit(1);
| 318 /* 319 * Insert any processes back from being blocked 320 * in the kernel into the run queue. 321 */ 322 p = km->km_completed; 323 uts_mb.km_completed = NULL; 324 UPFMT("km_completed -> 0x%x", p); 325 while ((tm = p) != NULL) { 326 p = tm->tm_next; 327 UPFMT(" 0x%x", p); 328 runq_insert(tm);
|
264 }
| 329 }
|
| 330 UPCHAR('\n');
|
265
| 331
|
266 /* startksegrp(&second_kse); */ /* we can't do 2 KSEs yet */ 267 /* One will be sufficient */
| 332 /* 333 * Process any signals we've recieved (but only if we have 334 * somewhere to deliver them to). 335 */ 336 if ((run_queue != NULL) && SIGNOTEMPTY(km->km_sigscaught)) { 337 for (i = 0;i < _SIG_MAXSIG;i++) 338 if (SIGISMEMBER(km->km_sigscaught, i)) { 339 signalcontext(&run_queue->tm_context, i, foof); 340 break; 341 } 342 bzero(&km->km_sigscaught, sizeof(sigset_t)); 343 }
|
268
| 344
|
269 /* we are a thread, start the ball rolling */ 270 /* let the kernel know we are it */ 271 first_kse.mbox.kmbx_current_thread = &first_kse.curthread->mbox; 272 thread3_code(NULL); 273 return 0;
| 345 /* 346 * Pull a thread off the run queue. 347 */ 348 p = runq_remove(); 349#if 0 350 if ((p == aa) && (progress > 0)) { 351 --progress; 352 signalcontext(&p->tm_context, 1, foof); 353 } 354#endif 355 356 /* 357 * Either schedule a thread, or idle if none ready to run. 358 */ 359 if (p != NULL) { 360 UPFMT("\n-- uts() scheduling 0x%x--\n", p); 361 UPFMT("eip -> 0x%x progress -> %d\n", 362 p->tm_context.uc_mcontext.mc_eip, progress); 363 UPSTR("curthread set\n"); 364 uts_to_thread(p, &km->km_curthread); 365 UPSTR("\n-- uts_to_thread() failed --\n"); 366 } 367 kse_yield(); 368 pstr("** uts() exiting **\n"); 369 exit(EX_SOFTWARE);
|
274} 275
| 370} 371
|
| 372/* 373 * Start a thread. 374 */ 375static void 376thread_start(const void *func, int arg) 377{ 378 struct thread_mailbox *tm; 379 char *p; 380 381 aa = tm = (struct thread_mailbox *)calloc(1, sizeof(struct thread_mailbox)); 382 pfmt("thread_start() : 0x%x %x\n", tm, &aa->tm_context); 383 getcontext(&tm->tm_context); 384 p = (char *)malloc(THREAD_STACK_SIZE); 385 tm->tm_context.uc_stack.ss_sp = p; 386 tm->tm_context.uc_stack.ss_size = THREAD_STACK_SIZE; 387 makecontext(&tm->tm_context, func, 2, arg); 388 // setcontext(&tm->tm_context); 389 runq_insert(tm); 390}
|
| |