Deleted Added
full compact
kse_threads_test.c (99073) kse_threads_test.c (103581)
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}