/* Inits a thread for us, though we won't use it. Just a hack to get into
* _M mode. Note this requests one vcore for us */
struct uthread dummy = {0};
- uthread_2ls_init(&dummy, &ghetto_sched_ops);
+ uthread_2ls_init(&dummy, &ghetto_sched_ops, NULL, NULL);
uthread_mcp_init();
/* Reset the blockon to be the spinner... This is really shitty. Any
/* Inits a thread for us, though we won't use it. Just a hack to get into
* _M mode. Note this requests one vcore for us */
struct uthread dummy = {0};
- uthread_2ls_init(&dummy, &ghetto_sched_ops);
+ uthread_2ls_init(&dummy, &ghetto_sched_ops, NULL, NULL);
uthread_mcp_init();
/* Reset the blockon to be the spinner... This is really shitty. Any
* blocking calls after we become an MCP and before this will fail. This is
int register_ev_handler(unsigned int ev_type, handle_event_t handler,
void *data)
{
+ /* Nasty uthread code assumes this was malloced */
struct ev_handler *new_h = malloc(sizeof(struct ev_handler));
+
if (!new_h)
return -1;
new_h->func = handler;
handle_event_t func;
void *data;
};
+extern struct ev_handler *ev_handlers[];
int register_ev_handler(unsigned int ev_type, handle_event_t handler,
void *data);
int deregister_ev_handler(unsigned int ev_type, handle_event_t handler,
/* Low-level _S code calls this for basic uthreading without a 2LS */
void uthread_lib_init(void);
-/* Call this, passing it a uthread representing thread0, from your 2LS init
- * routines. When it returns, you're in _M mode (thread0 on vcore0) */
-void uthread_2ls_init(struct uthread *uthread, struct schedule_ops *ops);
+/* Call this from your 2LS init routines. Pass it a uthread representing
+ * thread0, your 2LS ops, and your syscall handler + data.
+ *
+ * When it returns, you're in _M mode (thread0 on vcore0) */
+void uthread_2ls_init(struct uthread *uthread, struct schedule_ops *ops,
+ void (*handle_sysc)(struct event_msg *, unsigned int,
+ void *),
+ void *data);
/* Call this to become an mcp capable of worling with uthreads. */
void uthread_mcp_init(void);
static struct thread0_info thread0_info;
static struct event_queue *sysc_evq;
-static void thread0_handle_syscall(struct event_msg *ev_msg,
- unsigned int ev_type, void *data)
+void thread0_handle_syscall(struct event_msg *ev_msg,
+ unsigned int ev_type, void *data)
{
thread0_info.is_blocked = FALSE;
}
/* we don't care about the message, so don't bother with a UCQ */
sysc_evq = get_eventq(EV_MBOX_BITMAP);
sysc_evq->ev_flags = EVENT_INDIR | EVENT_WAKEUP;
- register_ev_handler(EV_SYSCALL, thread0_handle_syscall, 0);
}
/* Thread0 scheduler ops (for processes that haven't linked in a full 2LS) */
vcore_change_to_m();
}
-/* The real 2LS calls this, passing in a uthread representing thread0. */
-void uthread_2ls_init(struct uthread *uthread, struct schedule_ops *ops)
+/* The real 2LS calls this, passing in a uthread representing thread0, its 2LS
+ * ops, and its syscall handling routine. (NULL is fine).
+ *
+ * When we're called, thread0 has it's handler installed. We need to remove it
+ * and install our own (if they want one). All of the actual changes must be
+ * done atomically with respect to syscalls (i.e., no syscalls in between). If
+ * the user did something like have an outstanding async syscall while we're in
+ * here, then they'll crash hard. */
+void uthread_2ls_init(struct uthread *uthread, struct schedule_ops *ops,
+ void (*handle_sysc)(struct event_msg *, unsigned int,
+ void *),
+ void *data)
{
+ struct ev_handler *old_h, *new_h = NULL;
+
+ if (handle_sysc) {
+ new_h = malloc(sizeof(struct ev_handler));
+ assert(new_h);
+ new_h->func = handle_sysc;
+ new_h->data = data;
+ new_h->next = NULL;
+ }
uthread_init_thread0(uthread);
/* We need to *atomically* change the current_uthread and the schedule_ops
* to the new 2LSs thread0 and ops, such that there is no moment when only
* previously). */
uthread_track_thread0(uthread);
sched_ops = ops;
+ /* Racy, but we shouldn't be concurrent */
+ old_h = ev_handlers[EV_SYSCALL];
+ ev_handlers[EV_SYSCALL] = new_h;
cmb();
__vcore_context = FALSE;
enable_notifs(0); /* will trigger a self_notif if we missed a notif */
+ free(old_h);
}
/* Helper: tells the kernel our SCP is capable of going into vcore context on
/* Use the thread0 sched's uth */
extern struct uthread *thread0_uth;
extern void thread0_lib_init(void);
+ extern void thread0_handle_syscall(struct event_msg *ev_msg,
+ unsigned int ev_type, void *data);
int ret;
/* Only run once, but make sure that vcore_lib_init() has run already. */
sizeof(struct uthread));
assert(!ret);
memset(thread0_uth, 0, sizeof(struct uthread)); /* aggressively 0 for bugs*/
- /* Init the 2LS, which sets up current_uthread, before thread0 lib */
- uthread_2ls_init(thread0_uth, &thread0_2ls_ops);
+ /* Need to do thread0_lib_init() first, since it sets up evq's referred to
+ * in thread0_handle_syscall(). */
thread0_lib_init();
+ uthread_2ls_init(thread0_uth, &thread0_2ls_ops, thread0_handle_syscall,
+ NULL);
+ /* Switch our errno/errstr functions to be uthread-aware. See glibc's
+ * errno.c for more info. */
+ ros_errno_loc = __ros_errno_loc;
+ ros_errstr_loc = __ros_errstr_loc;
+ register_ev_handler(EV_EVENT, handle_ev_ev, 0);
+ /* Now that we're ready (I hope) to operate as an MCP, we tell the kernel.
+ * We must set vcctx and blockon atomically with respect to syscalls,
+ * meaning no syscalls in between. */
+ cmb();
scp_vcctx_ready();
/* Change our blockon from glibc's internal one to the regular one, which
* uses vcore context and works for SCPs (with or without 2LS) and MCPs.
ros_syscall_blockon = __ros_uth_syscall_blockon;
cmb();
init_posix_signals();
- /* Switch our errno/errstr functions to be uthread-aware. See glibc's
- * errno.c for more info. */
- ros_errno_loc = __ros_errno_loc;
- ros_errstr_loc = __ros_errstr_loc;
- register_ev_handler(EV_EVENT, handle_ev_ev, 0);
/* Accept diagnostic events. Other parts of the program/libraries can
* register handlers to run. You can kick these with "notify PID 9". */
enable_kevent(EV_FREE_APPLE_PIE, 0, EVENT_IPI | EVENT_WAKEUP |
* private preference. Also note that enable_kevent() is just an example,
* and you probably want to use parts of event.c to do what you want. */
enable_kevent(EV_USER_IPI, 0, EVENT_IPI | EVENT_VCORE_PRIVATE);
-
- /* Handle syscall events. */
- register_ev_handler(EV_SYSCALL, pth_handle_syscall, 0);
/* Set up the per-vcore structs to track outstanding syscalls */
sysc_mgmt = malloc(sizeof(struct sysc_mgmt) * max_vcores());
assert(sysc_mgmt);
}
#endif
/* Sched ops is set by 2ls_init */
- uthread_2ls_init((struct uthread*)t, &pthread_sched_ops);
+ uthread_2ls_init((struct uthread*)t, &pthread_sched_ops, pth_handle_syscall,
+ NULL);
atomic_init(&threads_total, 1); /* one for thread0 */
}
thread0->stacktop = (void*)USTACKTOP;
/* for lack of a better vcore, might as well send to 0 */
sysc_evq = setup_sysc_evq(0);
- register_ev_handler(EV_SYSCALL, vmm_handle_syscall, 0);
- uthread_2ls_init((struct uthread*)thread0, &vmm_sched_ops);
+ uthread_2ls_init((struct uthread*)thread0, &vmm_sched_ops,
+ vmm_handle_syscall, NULL);
}
/* The scheduling policy is encapsulated in the next few functions (from here