1/*-
2 * Copyright (c) 2014, 2015 Antti Kantee.  All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
14 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#define _lwp_park ___lwp_park60
27#include <sys/cdefs.h>
28
29#include <sys/param.h>
30#include <sys/lwpctl.h>
31#include <sys/lwp.h>
32#include <sys/queue.h>
33#include <sys/time.h>
34#include <sys/tls.h>
35
36#include <assert.h>
37#include <errno.h>
38#include <lwp.h>
39#include <sched.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <unistd.h>
44
45#include <rump/rump.h>
46
47#include <bmk-core/core.h>
48#include <bmk-core/sched.h>
49
50#include <rumprun-base/makelwp.h>
51
52#include "rumprun-private.h"
53
54struct rumprun_lwp {
55	struct bmk_thread *rl_thread;
56	int rl_lwpid;
57	char rl_name[MAXCOMLEN+1];
58	void (*rl_start)(void *);
59	void *rl_arg;
60
61	struct lwpctl rl_lwpctl;
62	int rl_no_parking_hare;	/* a looney tunes reference ... finally! */
63
64	TAILQ_ENTRY(rumprun_lwp) rl_entries;
65};
66static TAILQ_HEAD(, rumprun_lwp) all_lwp = TAILQ_HEAD_INITIALIZER(all_lwp);
67static __thread struct rumprun_lwp *me;
68
69#define FIRST_LWPID 1
70static int curlwpid = FIRST_LWPID;
71
72static struct rumprun_lwp mainthread = {
73	.rl_lwpid = FIRST_LWPID,
74};
75
76static void rumprun_makelwp_tramp(void *);
77
78static ptrdiff_t meoff;
79static void
80assignme(void *tcb, struct rumprun_lwp *value)
81{
82	struct rumprun_lwp **dst = (void *)((uintptr_t)tcb + meoff);
83
84	*dst = value;
85}
86
87int
88_lwp_ctl(int ctl, struct lwpctl **data)
89{
90
91	*data = (struct lwpctl *)&me->rl_lwpctl;
92	return 0;
93}
94
95int
96rumprun_makelwp(void (*start)(void *), void *arg, void *private,
97	void *stack_base, size_t stack_size, unsigned long flag, lwpid_t *lid)
98{
99	struct rumprun_lwp *rl;
100	struct lwp *curlwp, *newlwp;
101
102	rl = calloc(1, sizeof(*rl));
103	if (rl == NULL)
104		return errno;
105	assignme(private, rl);
106
107	curlwp = rump_pub_lwproc_curlwp();
108	if ((errno = rump_pub_lwproc_newlwp(getpid())) != 0) {
109		free(rl);
110		return errno;
111	}
112	newlwp = rump_pub_lwproc_curlwp();
113	rl->rl_start = start;
114	rl->rl_arg = arg;
115	rl->rl_lwpid = ++curlwpid;
116	rl->rl_thread = bmk_sched_create_withtls("lwp", rl, 0,
117	    rumprun_makelwp_tramp, newlwp, stack_base, stack_size, private);
118	if (rl->rl_thread == NULL) {
119		free(rl);
120		rump_pub_lwproc_releaselwp();
121		rump_pub_lwproc_switch(curlwp);
122		return EBUSY; /* ??? */
123	}
124	rump_pub_lwproc_switch(curlwp);
125
126	*lid = rl->rl_lwpid;
127	TAILQ_INSERT_TAIL(&all_lwp, rl, rl_entries);
128
129	return 0;
130}
131
132static void
133rumprun_makelwp_tramp(void *arg)
134{
135
136	rump_pub_lwproc_switch(arg);
137	(me->rl_start)(me->rl_arg);
138}
139
140static struct rumprun_lwp *
141lwpid2rl(lwpid_t lid)
142{
143	struct rumprun_lwp *rl;
144
145	if (lid == 0)
146		return &mainthread;
147	TAILQ_FOREACH(rl, &all_lwp, rl_entries) {
148		if (rl->rl_lwpid == lid)
149			return rl;
150	}
151	return NULL;
152}
153
154int
155_lwp_unpark(lwpid_t lid, const void *hint)
156{
157	struct rumprun_lwp *rl;
158
159	if ((rl = lwpid2rl(lid)) == NULL) {
160		return -1;
161	}
162
163	bmk_sched_wake(rl->rl_thread);
164	return 0;
165}
166
167ssize_t
168_lwp_unpark_all(const lwpid_t *targets, size_t ntargets, const void *hint)
169{
170	ssize_t rv;
171
172	if (targets == NULL)
173		return 1024;
174
175	rv = ntargets;
176	while (ntargets--) {
177		if (_lwp_unpark(*targets, NULL) != 0)
178			rv--;
179		targets++;
180	}
181	//assert(rv >= 0);
182	return rv;
183}
184
185/*
186 * called by the scheduler when a context switch is made
187 * nb. cookie is null when non-lwp threads are being run
188 */
189static void
190schedhook(void *prevcookie, void *nextcookie)
191{
192	struct rumprun_lwp *prev, *next;
193
194	prev = prevcookie;
195	next = nextcookie;
196
197	if (prev && prev->rl_lwpctl.lc_curcpu != LWPCTL_CPU_EXITED) {
198		prev->rl_lwpctl.lc_curcpu = LWPCTL_CPU_NONE;
199	}
200	if (next) {
201		next->rl_lwpctl.lc_curcpu = 0;
202		next->rl_lwpctl.lc_pctr++;
203	}
204}
205
206void
207rumprun_lwp_init(void)
208{
209	void *tcb = bmk_sched_gettcb();
210
211	bmk_sched_set_hook(schedhook);
212
213	meoff = (uintptr_t)&me - (uintptr_t)tcb;
214	assignme(tcb, &mainthread);
215	mainthread.rl_thread = bmk_sched_init_mainlwp(&mainthread);
216
217	TAILQ_INSERT_TAIL(&all_lwp, me, rl_entries);
218}
219
220int
221_lwp_park(clockid_t clock_id, int flags, const struct timespec *ts,
222	lwpid_t unpark, const void *hint, const void *unparkhint)
223{
224	int rv;
225
226	if (unpark)
227		_lwp_unpark(unpark, unparkhint);
228
229	if (me->rl_no_parking_hare) {
230		me->rl_no_parking_hare = 0;
231		return 0;
232	}
233
234	if (ts) {
235		bmk_time_t nsecs = ts->tv_sec*1000*1000*1000 + ts->tv_nsec;
236
237		if (flags & TIMER_ABSTIME) {
238			nsecs -= bmk_platform_cpu_clock_epochoffset();
239		} else {
240			nsecs += bmk_platform_cpu_clock_monotonic();
241		}
242		bmk_sched_blockprepare_timeout(nsecs);
243	} else {
244		bmk_sched_blockprepare();
245	}
246	rv = bmk_sched_block();
247	bmk_assert(rv == 0 || rv == ETIMEDOUT);
248
249	if (rv) {
250		errno = rv;
251		rv = -1;
252	}
253	return rv;
254}
255
256int
257_lwp_exit(void)
258{
259
260	me->rl_lwpctl.lc_curcpu = LWPCTL_CPU_EXITED;
261	rump_pub_lwproc_releaselwp();
262	TAILQ_REMOVE(&all_lwp, me, rl_entries);
263
264	/* could just assign it here, but for symmetry! */
265	assignme(bmk_sched_gettcb(), NULL);
266
267	bmk_sched_exit_withtls();
268
269	return 0;
270}
271
272int
273_lwp_continue(lwpid_t lid)
274{
275	struct rumprun_lwp *rl;
276
277	if ((rl = lwpid2rl(lid)) == NULL) {
278		return ESRCH;
279	}
280
281	bmk_sched_unsuspend(rl->rl_thread);
282	return 0;
283}
284
285int
286_lwp_suspend(lwpid_t lid)
287{
288	struct rumprun_lwp *rl;
289
290	if ((rl = lwpid2rl(lid)) == NULL) {
291		return ESRCH;
292	}
293
294	bmk_sched_suspend(rl->rl_thread);
295	return 0;
296}
297
298int
299_lwp_wakeup(lwpid_t lid)
300{
301	struct rumprun_lwp *rl;
302
303	if ((rl = lwpid2rl(lid)) == NULL)
304		return ESRCH;
305
306	bmk_sched_wake(rl->rl_thread);
307	return 0;
308}
309
310int
311_lwp_setname(lwpid_t lid, const char *name)
312{
313	struct rumprun_lwp *rl;
314
315	if ((rl = lwpid2rl(lid)) == NULL)
316		return ESRCH;
317	strlcpy(rl->rl_name, name, sizeof(rl->rl_name));
318
319	return 0;
320}
321
322lwpid_t
323_lwp_self(void)
324{
325
326	return me->rl_lwpid;
327}
328
329/* XXX: messy.  see sched.h, libc, libpthread, and all over */
330int _sys_sched_yield(void);
331int
332_sys_sched_yield(void)
333{
334
335	bmk_sched_yield();
336	return 0;
337}
338__weak_alias(sched_yield,_sys_sched_yield);
339
340struct tls_tcb *
341_rtld_tls_allocate(void)
342{
343
344	return bmk_sched_tls_alloc();
345}
346
347void
348_rtld_tls_free(struct tls_tcb *arg)
349{
350
351	return bmk_sched_tls_free(arg);
352}
353
354void *
355_lwp_getprivate(void)
356{
357
358	return bmk_sched_gettcb();
359}
360
361void _lwpnullop(void);
362void _lwpnullop(void) { }
363
364int _lwpsuccess(void);
365int _lwpsuccess(void) { return 0; }
366
367void _lwpabort(void);
368void __dead
369_lwpabort(void)
370{
371
372	printf("_lwpabort() called\n");
373	_exit(1);
374}
375
376__strong_alias(_sys_setcontext,_lwpabort);
377__strong_alias(_lwp_kill,_lwpabort);
378
379__strong_alias(__libc_static_tls_setup,_lwpnullop);
380
381int rasctl(void);
382int rasctl(void) { return ENOSYS; }
383
384/*
385 * There is ongoing work to support these in the rump kernel,
386 * so I will just stub them out for now.
387 */
388__strong_alias(_sched_getaffinity,_lwpnullop);
389__strong_alias(_sched_getparam,_lwpnullop);
390__strong_alias(_sched_setaffinity,_lwpnullop);
391__strong_alias(_sched_setparam,_lwpnullop);
392
393/*
394 * Technically, specifying a lower >0 protection level is an error,
395 * but we don't flag that error for now.
396 */
397__strong_alias(_sched_protect,_lwpsuccess);
398