rr.c revision 111519
1/*-
2 * Copyright (c) 2002 David Xu(davidxu@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: cvs2svn/branches/davidxu/tools/KSE/rr/rr.c 111519 2003-02-26 01:05:10Z davidxu $
27 */
28
29/*
30 * Test Userland Thread Scheduler (UTS) suite for KSE.
31 * Test Userland round roubin.
32 */
33
34#include <sys/types.h>
35#include <sys/signal.h>
36#include <sys/signalvar.h>
37#include <sys/sysctl.h>
38#include <sys/kse.h>
39#include <sys/ucontext.h>
40
41#include <stdarg.h>
42#include <stddef.h>
43#include <stdlib.h>
44#include <string.h>
45#include <sysexits.h>
46#include <time.h>
47#include <unistd.h>
48#include "simplelock.h"
49
50#undef TRACE_UTS
51
52#ifdef TRACE_UTS
53#define	UPFMT(fmt...)	pfmt(#fmt)
54#define	UPSTR(s)	pstr(s)
55#define	UPCHAR(c)	pchar(c)
56#else
57#define	UPFMT(fmt...)	/* Nothing. */
58#define	UPSTR(s)	/* Nothing. */
59#define	UPCHAR(c)	/* Nothing. */
60#endif
61
62#define MAIN_STACK_SIZE			(1024 * 1024)
63#define THREAD_STACK_SIZE		(32 * 1024)
64
65struct uts_runq {
66	struct kse_thr_mailbox	*head;
67	struct simplelock	lock;
68};
69
70struct uts_data {
71	struct kse_mailbox	mb;
72	struct uts_runq		*runq;
73	struct kse_thr_mailbox	*cur_thread;
74};
75
76static struct uts_runq runq1;
77static struct uts_data data1;
78
79static void	init_uts(struct uts_data *data, struct uts_runq *q);
80static void	start_uts(struct uts_data *data, int newgrp);
81static void	enter_uts(struct uts_data *);
82static void	pchar(char c);
83static void	pfmt(const char *fmt, ...);
84static void	pstr(const char *s);
85static void	runq_init(struct uts_runq *q);
86static void	runq_insert(struct uts_runq *q, struct kse_thr_mailbox *tm);
87static struct	kse_thr_mailbox *runq_remove(struct uts_runq *q);
88static struct	kse_thr_mailbox *runq_remove_nolock(struct uts_runq *q);
89static void	thread_start(struct uts_data *data, const void *func, int arg);
90static void	uts(struct kse_mailbox *km);
91
92/* Functions implemented in assembly */
93extern int	uts_to_thread(struct kse_thr_mailbox *tdp,
94			struct kse_thr_mailbox **curthreadp);
95extern int	thread_to_uts(struct kse_thr_mailbox *tm,
96			struct kse_mailbox *km);
97
98void
99deadloop(int c)
100{
101	for (;;) {
102		;
103	}
104}
105
106int
107main(void)
108{
109	runq_init(&runq1);
110	init_uts(&data1, &runq1);
111	thread_start(&data1, deadloop, 0);
112	thread_start(&data1, deadloop, 0);
113	thread_start(&data1, deadloop, 0);
114	start_uts(&data1, 0);
115	pause();
116	pstr("\n** main() exiting **\n");
117	return (EX_OK);
118}
119
120
121/*
122 * Enter the UTS from a thread.
123 */
124static void
125enter_uts(struct uts_data *data)
126{
127	struct kse_thr_mailbox	*td;
128
129	/* XXX: We should atomically exchange these two. */
130	td = data->mb.km_curthread;
131	data->mb.km_curthread = NULL;
132
133	thread_to_uts(td, &data->mb);
134}
135
136/*
137 * Initialise threading.
138 */
139static void
140init_uts(struct uts_data *data, struct uts_runq *q)
141{
142	struct kse_thr_mailbox *tm;
143	int mib[2];
144	char	*p;
145#if 0
146	size_t len;
147#endif
148
149	/*
150	 * Create initial thread.
151	 */
152	tm = (struct kse_thr_mailbox *)calloc(1, sizeof(struct kse_thr_mailbox));
153
154	/* Throw us into its context. */
155	getcontext(&tm->tm_context);
156
157	/* Find our stack. */
158	mib[0] = CTL_KERN;
159	mib[1] = KERN_USRSTACK;
160#if 0
161	len = sizeof(p);
162	if (sysctl(mib, 2, &p, &len, NULL, 0) == -1)
163		pstr("sysctl(CTL_KER.KERN_USRSTACK) failed.\n");
164#endif
165	p = (char *)malloc(MAIN_STACK_SIZE) + MAIN_STACK_SIZE;
166	pfmt("main() : 0x%x\n", tm);
167	pfmt("eip -> 0x%x\n", tm->tm_context.uc_mcontext.mc_eip);
168	tm->tm_context.uc_stack.ss_sp = p - MAIN_STACK_SIZE;
169	tm->tm_context.uc_stack.ss_size = MAIN_STACK_SIZE;
170
171	/*
172	 * Create KSE mailbox.
173	 */
174	p = (char *)malloc(THREAD_STACK_SIZE);
175	bzero(&data->mb, sizeof(struct kse_mailbox));
176	data->mb.km_stack.ss_sp = p;
177	data->mb.km_stack.ss_size = THREAD_STACK_SIZE;
178	data->mb.km_func = (void *)uts;
179	data->mb.km_udata = data;
180	data->cur_thread = tm;
181	data->runq = q;
182	pfmt("uts() at : 0x%x\n", uts);
183	pfmt("uts stack at : 0x%x - 0x%x\n", p, p + THREAD_STACK_SIZE);
184}
185
186static void
187start_uts(struct uts_data *data, int newgrp)
188{
189	/*
190	 * Start KSE scheduling.
191	 */
192	pfmt("kse_create() -> %d\n", kse_create(&data->mb, newgrp));
193	data->mb.km_curthread = data->cur_thread;
194}
195
196/*
197 * Write a single character to stdout, in a thread-safe manner.
198 */
199static void
200pchar(char c)
201{
202
203	write(STDOUT_FILENO, &c, 1);
204}
205
206/*
207 * Write formatted output to stdout, in a thread-safe manner.
208 *
209 * Recognises the following conversions:
210 *	%c	-> char
211 *	%d	-> signed int (base 10)
212 *	%s	-> string
213 *	%u	-> unsigned int (base 10)
214 *	%x	-> unsigned int (base 16)
215 */
216static void
217pfmt(const char *fmt, ...)
218{
219	static const char digits[16] = "0123456789abcdef";
220	va_list	 ap;
221	char buf[10];
222	char *s;
223	unsigned r, u;
224	int c, d;
225
226	va_start(ap, fmt);
227	while ((c = *fmt++)) {
228		if (c == '%') {
229			c = *fmt++;
230			switch (c) {
231			case 'c':
232				pchar(va_arg(ap, int));
233				continue;
234			case 's':
235				pstr(va_arg(ap, char *));
236				continue;
237			case 'd':
238			case 'u':
239			case 'x':
240				r = ((c == 'u') || (c == 'd')) ? 10 : 16;
241				if (c == 'd') {
242					d = va_arg(ap, unsigned);
243					if (d < 0) {
244						pchar('-');
245						u = (unsigned)(d * -1);
246					} else
247						u = (unsigned)d;
248				} else
249					u = va_arg(ap, unsigned);
250				s = buf;
251				do {
252					*s++ = digits[u % r];
253				} while (u /= r);
254				while (--s >= buf)
255					pchar(*s);
256				continue;
257			}
258		}
259		pchar(c);
260	}
261	va_end(ap);
262}
263
264static void
265pstr(const char *s)
266{
267
268	write(STDOUT_FILENO, s, strlen(s));
269}
270
271static void
272runq_init(struct uts_runq *q)
273{
274	q->head = NULL;
275	simplelock_init(&q->lock);
276}
277
278/*
279 * Insert a thread into the run queue.
280 */
281static void
282runq_insert(struct uts_runq *q, struct kse_thr_mailbox *tm)
283{
284	simplelock_lock(&q->lock);
285	tm->tm_next = q->head;
286	q->head = tm;
287	simplelock_unlock(&q->lock);
288}
289
290/*
291 * Select and remove a thread from the run queue.
292 */
293static struct kse_thr_mailbox *
294runq_remove(struct uts_runq *q)
295{
296	struct kse_thr_mailbox *tm;
297
298	simplelock_lock(&q->lock);
299	tm = runq_remove_nolock(q);
300	simplelock_unlock(&q->lock);
301	return tm;
302}
303
304static struct kse_thr_mailbox *
305runq_remove_nolock(struct uts_runq *q)
306{
307	struct kse_thr_mailbox *p, *p1;
308
309	if (q->head == NULL)
310		return (NULL);
311	p1 = NULL;
312	for (p = q->head; p->tm_next != NULL; p = p->tm_next)
313		p1 = p;
314	if (p1 == NULL)
315		q->head = NULL;
316	else
317		p1->tm_next = NULL;
318	return (p);
319}
320
321/*
322 * Userland thread scheduler.
323 */
324static void
325uts(struct kse_mailbox *km)
326{
327	struct kse_thr_mailbox *tm, *p;
328	struct uts_data *data;
329
330	UPSTR("\n--uts() start--\n");
331	UPFMT("mailbox -> %x\n", km);
332
333	/*
334	 * Insert any processes back from being blocked
335	 * in the kernel into the run queue.
336	 */
337	data = km->km_udata;
338	p = km->km_completed;
339	km->km_completed = NULL;
340	UPFMT("km_completed -> 0x%x", p);
341	while ((tm = p) != NULL) {
342		p = tm->tm_next;
343		UPFMT(" 0x%x", p);
344		if (tm->tm_slices <= 0) {
345			tm->tm_slices = 10;
346			pfmt("thread %x exhausted its time slice, reassign it 10 statclock ticks\n", tm);
347		}
348		runq_insert(data->runq, tm);
349	}
350	UPCHAR('\n');
351
352	/*
353	 * Pull a thread off the run queue.
354	 */
355	simplelock_lock(&data->runq->lock);
356	p = runq_remove_nolock(data->runq);
357	simplelock_unlock(&data->runq->lock);
358
359	/*
360	 * Either schedule a thread, or idle if none ready to run.
361	 */
362	if (p != NULL) {
363		UPFMT("\n-- uts() scheduling 0x%x--\n", p);
364		UPFMT("eip -> 0x%x progress -> %d\n",
365		    p->tm_context.uc_mcontext.mc_eip, progress);
366		UPSTR("curthread set\n");
367		uts_to_thread(p, &km->km_curthread);
368		UPSTR("\n-- uts_to_thread() failed --\n");
369	}
370	kse_release(NULL);
371	pstr("** uts() exiting **\n");
372	exit(EX_SOFTWARE);
373}
374
375/*
376 * Start a thread.
377 */
378static struct kse_thr_mailbox *
379thread_create(const void *func, int arg)
380{
381	struct kse_thr_mailbox *tm;
382	char *p;
383
384	tm = (struct kse_thr_mailbox *)calloc(1, sizeof(struct kse_thr_mailbox));
385	getcontext(&tm->tm_context);
386	p = (char *)malloc(THREAD_STACK_SIZE);
387	tm->tm_context.uc_stack.ss_sp = p;
388	tm->tm_context.uc_stack.ss_size = THREAD_STACK_SIZE;
389	makecontext(&tm->tm_context, func, 1, arg);
390	// setcontext(&tm->tm_context);
391	return tm;
392}
393
394static void
395thread_start(struct uts_data *data, const void *func, int arg)
396{
397	struct kse_thr_mailbox *tm;
398	struct kse_thr_mailbox *tm2;
399
400	tm = thread_create(func, arg);
401	tm->tm_slices = 10;
402	tm2 = thread_create(enter_uts, (int)data);
403	tm->tm_context.uc_link = &tm2->tm_context;
404	runq_insert(data->runq, tm);
405	pfmt("thread_start() : 0x%x\n", tm);
406}
407