| #include <arch/arch.h> | 
 | #include <stdbool.h> | 
 | #include <errno.h> | 
 | #include <vcore.h> | 
 | #include <mcs.h> | 
 | #include <sys/param.h> | 
 | #include <parlib.h> | 
 | #include <unistd.h> | 
 | #include <stdlib.h> | 
 | #include <sys/mman.h> | 
 | #include <stdio.h> | 
 | #include <event.h> | 
 | #include <uthread.h> | 
 | #include <ucq.h> | 
 | #include <ros/arch/membar.h> | 
 | #include <printf-ext.h> | 
 |  | 
 | /* starting with 1 since we alloc vcore0's stacks and TLS in vcore_init(). */ | 
 | static size_t _max_vcores_ever_wanted = 1; | 
 | atomic_t nr_new_vcores_wanted; | 
 | atomic_t vc_req_being_handled; | 
 |  | 
 | bool vc_initialized = FALSE; | 
 | __thread struct syscall __vcore_one_sysc = {.flags = (atomic_t)SC_DONE, 0}; | 
 |  | 
 | /* Per vcore entery function used when reentering at the top of a vcore's stack */ | 
 | static __thread void (*__vcore_reentry_func)(void) = NULL; | 
 |  | 
 | /* TODO: probably don't want to dealloc.  Considering caching */ | 
 | static void free_transition_tls(int id) | 
 | { | 
 | 	if (get_vcpd_tls_desc(id)) { | 
 | 		/* Note we briefly have no TLS desc in VCPD.  This is fine so long as | 
 | 		 * that vcore doesn't get started fresh before we put in a new desc */ | 
 | 		free_tls(get_vcpd_tls_desc(id)); | 
 | 		set_vcpd_tls_desc(id, NULL); | 
 | 	} | 
 | } | 
 |  | 
 | static int allocate_transition_tls(int id) | 
 | { | 
 | 	/* We want to free and then reallocate the tls rather than simply  | 
 | 	 * reinitializing it because its size may have changed.  TODO: not sure if | 
 | 	 * this is right.  0-ing is one thing, but freeing and reallocating can be | 
 | 	 * expensive, esp if syscalls are involved.  Check out glibc's | 
 | 	 * allocatestack.c for what might work. */ | 
 | 	free_transition_tls(id); | 
 |  | 
 | 	void *tcb = allocate_tls(); | 
 | 	if (!tcb) { | 
 | 		errno = ENOMEM; | 
 | 		return -1; | 
 | 	} | 
 | 	set_vcpd_tls_desc(id, tcb); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void free_transition_stack(int id) | 
 | { | 
 | 	// don't actually free stacks | 
 | } | 
 |  | 
 | static int allocate_transition_stack(int id) | 
 | { | 
 | 	struct preempt_data *vcpd = vcpd_of(id); | 
 | 	if (vcpd->transition_stack) | 
 | 		return 0; // reuse old stack | 
 |  | 
 | 	void* stackbot = mmap(0, TRANSITION_STACK_SIZE, | 
 | 	                      PROT_READ|PROT_WRITE|PROT_EXEC, | 
 | 	                      MAP_POPULATE|MAP_ANONYMOUS, -1, 0); | 
 |  | 
 | 	if(stackbot == MAP_FAILED) | 
 | 		return -1; // errno set by mmap | 
 |  | 
 | 	vcpd->transition_stack = (uintptr_t)stackbot + TRANSITION_STACK_SIZE; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | void vcore_init(void) | 
 | { | 
 | 	uintptr_t mmap_block; | 
 | 	/* Note this is racy, but okay.  The first time through, we are _S */ | 
 | 	init_once_racy(return); | 
 |  | 
 | 	/* Need to alloc vcore0's transition stuff here (technically, just the TLS) | 
 | 	 * so that schedulers can use vcore0's transition TLS before it comes up in | 
 | 	 * vcore_entry() */ | 
 | 	if(allocate_transition_stack(0) || allocate_transition_tls(0)) | 
 | 		goto vcore_init_fail; | 
 |  | 
 | 	/* Initialize our VCPD event queues' ucqs, two pages per ucq, 4 per vcore */ | 
 | 	mmap_block = (uintptr_t)mmap(0, PGSIZE * 4 * max_vcores(), | 
 | 	                             PROT_WRITE | PROT_READ, | 
 | 	                             MAP_POPULATE | MAP_ANONYMOUS, -1, 0); | 
 | 	/* Yeah, this doesn't fit in the error-handling scheme, but this whole | 
 | 	 * system doesn't really handle failure, and needs a rewrite involving less | 
 | 	 * mmaps/munmaps. */ | 
 | 	assert(mmap_block); | 
 | 	/* Note we may end up doing vcore 0's elsewhere, for _Ss, or else have a | 
 | 	 * separate ev_q for that. */ | 
 | 	for (int i = 0; i < max_vcores(); i++) { | 
 | 		/* four pages total for both ucqs from the big block (2 pages each) */ | 
 | 		ucq_init_raw(&vcpd_of(i)->ev_mbox_public.ev_msgs, | 
 | 		             mmap_block + (4 * i    ) * PGSIZE, | 
 | 		             mmap_block + (4 * i + 1) * PGSIZE); | 
 | 		ucq_init_raw(&vcpd_of(i)->ev_mbox_private.ev_msgs, | 
 | 		             mmap_block + (4 * i + 2) * PGSIZE, | 
 | 		             mmap_block + (4 * i + 3) * PGSIZE); | 
 | 	} | 
 | 	atomic_init(&vc_req_being_handled, 0); | 
 | 	assert(!in_vcore_context()); | 
 | 	/* no longer need to enable notifs on vcore 0, it is set like that by | 
 | 	 * default (so you drop into vcore context immediately on transtioning to | 
 | 	 * _M) */ | 
 | 	vc_initialized = TRUE; | 
 | 	return; | 
 | vcore_init_fail: | 
 | 	assert(0); | 
 | } | 
 |  | 
 | /* Helper functions used to reenter at the top of a vcore's stack for an | 
 |  * arbitrary function */ | 
 | static void __attribute__((noinline, noreturn))  | 
 | __vcore_reenter() | 
 | { | 
 |   __vcore_reentry_func(); | 
 |   assert(0); | 
 | } | 
 |  | 
 | void vcore_reenter(void (*entry_func)(void)) | 
 | { | 
 |   assert(in_vcore_context()); | 
 |   struct preempt_data *vcpd = vcpd_of(vcore_id()); | 
 |  | 
 |   __vcore_reentry_func = entry_func; | 
 |   set_stack_pointer((void*)vcpd->transition_stack); | 
 |   cmb(); | 
 |   __vcore_reenter(); | 
 | } | 
 |  | 
 | /* This gets called in glibc before calling the programs 'main'.  Need to set | 
 |  * ourselves up so that thread0 is a uthread, and then register basic signals to | 
 |  * go to vcore 0. */ | 
 | void vcore_event_init(void) | 
 | { | 
 | 	register_printf_specifier('r', printf_errstr, printf_errstr_info); | 
 | 	/* set up our thread0 as a uthread */ | 
 | 	uthread_slim_init(); | 
 | 	/* TODO: register for other kevents/signals and whatnot (can probably reuse | 
 | 	 * the simple ev_q).  Could also do this via explicit functions from the | 
 | 	 * program. */ | 
 | } | 
 |  | 
 | /* Helper, picks some sane defaults and changes the process into an MCP */ | 
 | void vcore_change_to_m(void) | 
 | { | 
 | 	int ret; | 
 | 	__procdata.res_req[RES_CORES].amt_wanted = 1; | 
 | 	__procdata.res_req[RES_CORES].amt_wanted_min = 1;	/* whatever */ | 
 | 	assert(!in_multi_mode()); | 
 | 	assert(!in_vcore_context()); | 
 | 	ret = sys_change_to_m(); | 
 | 	assert(!ret); | 
 | 	assert(in_multi_mode()); | 
 | 	assert(!in_vcore_context()); | 
 | } | 
 |  | 
 | /* Returns -1 with errno set on error, or 0 on success.  This does not return | 
 |  * the number of cores actually granted (though some parts of the kernel do | 
 |  * internally). | 
 |  * | 
 |  * This tries to get "more vcores", based on the number we currently have. | 
 |  * We'll probably need smarter 2LSs in the future that just directly set | 
 |  * amt_wanted.  What happens is we can have a bunch of 2LS vcore contexts | 
 |  * trying to get "another vcore", which currently means more than num_vcores(). | 
 |  * If you have someone ask for two more, and then someone else ask for one more, | 
 |  * how many you ultimately ask for depends on if the kernel heard you and | 
 |  * adjusted num_vcores in between the two calls.  Or maybe your amt_wanted | 
 |  * already was num_vcores + 5, so neither call is telling the kernel anything | 
 |  * new.  It comes down to "one more than I have" vs "one more than I've already | 
 |  * asked for". | 
 |  * | 
 |  * So for now, this will keep the older behavior (one more than I have).  It | 
 |  * will try to accumulate any concurrent requests, and adjust amt_wanted up. | 
 |  * Interleaving, repetitive calls (everyone asking for one more) may get | 
 |  * ignored. | 
 |  * | 
 |  * Note the doesn't block or anything (despite the min number requested is | 
 |  * 1), since the kernel won't block the call. | 
 |  * | 
 |  * There are a few concurrency concerns.  We have _max_vcores_ever_wanted, | 
 |  * initialization of new vcore stacks/TLSs, making sure we don't ask for too | 
 |  * many (minor point), and most importantly not asking the kernel for too much | 
 |  * or otherwise miscommunicating our desires to the kernel.  Remember, the | 
 |  * kernel wants just one answer from the process about what it wants, and it is | 
 |  * up to the process to figure that out. | 
 |  * | 
 |  * So we basically have one thread do the submitting/prepping/bookkeeping, and | 
 |  * other threads come in just update the number wanted and make sure someone | 
 |  * is sorting things out.  This will perform a bit better too, since only one | 
 |  * vcore makes syscalls (which hammer the proc_lock).  This essentially has | 
 |  * cores submit work, and one core does the work (like Eric's old delta | 
 |  * functions). | 
 |  * | 
 |  * There's a slight semantic change: this will return 0 (success) for the | 
 |  * non-submitters, and 0 if we submitted.  -1 only if the submitter had some | 
 |  * non-kernel failure. | 
 |  * | 
 |  * Also, beware that this (like the old version) doesn't protect with races on | 
 |  * num_vcores().  num_vcores() is how many you have now or very soon (accounting | 
 |  * for messages in flight that will take your cores), not how many you told the | 
 |  * kernel you want. */ | 
 | int vcore_request(long nr_new_vcores) | 
 | { | 
 | 	long nr_to_prep_now, nr_vcores_wanted; | 
 |  | 
 | 	assert(vc_initialized); | 
 | 	/* Early sanity checks */ | 
 | 	if ((nr_new_vcores < 0) || (nr_new_vcores + num_vcores() > max_vcores())) | 
 | 		return -1;	/* consider ERRNO */ | 
 | 	/* Post our desires (ROS atomic_add() conflicts with glibc) */ | 
 | 	atomic_fetch_and_add(&nr_new_vcores_wanted, nr_new_vcores); | 
 | try_handle_it: | 
 | 	cmb();	/* inc before swap.  the atomic is a CPU mb() */ | 
 | 	if (atomic_swap(&vc_req_being_handled, 1)) { | 
 | 		/* We got a 1 back, so someone else is already working on it */ | 
 | 		return 0; | 
 | 	} | 
 | 	/* So now we're the ones supposed to handle things.  This does things in the | 
 | 	 * "increment based on the number we have", vs "increment on the number we | 
 | 	 * said we want". | 
 | 	 * | 
 | 	 * Figure out how many we have, though this is racy.  Yields/preempts/grants | 
 | 	 * will change this over time, and we may end up asking for less than we | 
 | 	 * had. */ | 
 | 	nr_vcores_wanted = num_vcores(); | 
 | 	/* Pull all of the vcores wanted into our local variable, where we'll deal | 
 | 	 * with prepping/requesting that many vcores.  Keep doing this til we think | 
 | 	 * no more are wanted. */ | 
 | 	while ((nr_to_prep_now = atomic_swap(&nr_new_vcores_wanted, 0))) { | 
 | 		nr_vcores_wanted += nr_to_prep_now; | 
 | 		/* Don't bother prepping or asking for more than we can ever get */ | 
 | 		nr_vcores_wanted = MIN(nr_vcores_wanted, max_vcores()); | 
 | 		/* Make sure all we might ask for are prepped */ | 
 | 		for (long i = _max_vcores_ever_wanted; i < nr_vcores_wanted; i++) { | 
 | 			if (allocate_transition_stack(i) || allocate_transition_tls(i)) { | 
 | 				atomic_set(&vc_req_being_handled, 0);	/* unlock and bail out*/ | 
 | 				return -1; | 
 | 			} | 
 | 			_max_vcores_ever_wanted++;	/* done in the loop to handle failures*/ | 
 | 		} | 
 | 	} | 
 | 	cmb();	/* force a reread of num_vcores() */ | 
 | 	/* Update amt_wanted if we now want *more* than what the kernel already | 
 | 	 * knows.  See notes in the func doc. */ | 
 | 	if (nr_vcores_wanted > __procdata.res_req[RES_CORES].amt_wanted) | 
 | 		__procdata.res_req[RES_CORES].amt_wanted = nr_vcores_wanted; | 
 | 	/* If num_vcores isn't what we want, we can poke the ksched.  Due to some | 
 | 	 * races with yield, our desires may be old.  Not a big deal; any vcores | 
 | 	 * that pop up will just end up yielding (or get preempt messages.)  */ | 
 | 	if (nr_vcores_wanted > num_vcores()) | 
 | 		sys_poke_ksched(0, RES_CORES);	/* 0 -> poke for ourselves */ | 
 | 	/* Unlock, (which lets someone else work), and check to see if more work | 
 | 	 * needs to be done.  If so, we'll make sure it gets handled. */ | 
 | 	atomic_set(&vc_req_being_handled, 0);	/* unlock, to allow others to try */ | 
 | 	wrmb(); | 
 | 	/* check for any that might have come in while we were out */ | 
 | 	if (atomic_read(&nr_new_vcores_wanted)) | 
 | 		goto try_handle_it; | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* This can return, if you failed to yield due to a concurrent event.  Note | 
 |  * we're atomicly setting the CAN_RCV flag, and aren't bothering with CASing | 
 |  * (either with the kernel or uthread's handle_indirs()).  We don't particularly | 
 |  * care what other code does - we intend to set those flags no matter what. */ | 
 | void vcore_yield(bool preempt_pending) | 
 | { | 
 | 	unsigned long old_nr; | 
 | 	uint32_t vcoreid = vcore_id(); | 
 | 	struct preempt_data *vcpd = vcpd_of(vcoreid); | 
 | 	__sync_fetch_and_and(&vcpd->flags, ~VC_CAN_RCV_MSG); | 
 | 	/* no wrmb() necessary, handle_events() has an mb() if it is checking */ | 
 | 	/* Clears notif pending and tries to handle events.  This is an optimization | 
 | 	 * to avoid the yield syscall if we have an event pending.  If there is one, | 
 | 	 * we want to unwind and return to the 2LS loop, where we may not want to | 
 | 	 * yield anymore. | 
 | 	 * Note that the kernel only cares about CAN_RCV_MSG for the desired vcore, | 
 | 	 * not for a FALLBACK.  */ | 
 | 	if (handle_events(vcoreid)) { | 
 | 		__sync_fetch_and_or(&vcpd->flags, VC_CAN_RCV_MSG); | 
 | 		return; | 
 | 	} | 
 | 	/* If we are yielding since we don't want the core, tell the kernel we want | 
 | 	 * one less vcore (vc_yield assumes a dumb 2LS). | 
 | 	 * | 
 | 	 * If yield fails (slight race), we may end up having more vcores than | 
 | 	 * amt_wanted for a while, and might lose one later on (after a | 
 | 	 * preempt/timeslicing) - the 2LS will have to notice eventually if it | 
 | 	 * actually needs more vcores (which it already needs to do).  amt_wanted | 
 | 	 * could even be 0. | 
 | 	 * | 
 | 	 * In general, any time userspace decrements or sets to 0, it could get | 
 | 	 * preempted, so the kernel will still give us at least one, until the last | 
 | 	 * vcore properly yields without missing a message (and becomes a WAITING | 
 | 	 * proc, which the ksched will not give cores to). | 
 | 	 * | 
 | 	 * I think it's possible for userspace to do this (lock, read amt_wanted, | 
 | 	 * check all message queues for all vcores, subtract amt_wanted (not set to | 
 | 	 * 0), unlock) so long as every event handler +1s the amt wanted, but that's | 
 | 	 * a huge pain, and we already have event handling code making sure a | 
 | 	 * process can't sleep (transition to WAITING) if a message arrives (can't | 
 | 	 * yield if notif_pending, can't go WAITING without yielding, and the event | 
 | 	 * posting the notif_pending will find the online VC or be delayed by | 
 | 	 * spinlock til the proc is WAITING). */ | 
 | 	if (!preempt_pending) { | 
 | 		do { | 
 | 			old_nr = __procdata.res_req[RES_CORES].amt_wanted; | 
 | 			if (old_nr == 0) | 
 | 				break; | 
 | 		} while (!__sync_bool_compare_and_swap( | 
 | 		             &__procdata.res_req[RES_CORES].amt_wanted, | 
 | 		             old_nr, old_nr - 1)); | 
 | 	} | 
 | 	/* We can probably yield.  This may pop back up if notif_pending became set | 
 | 	 * by the kernel after we cleared it and we lost the race. */ | 
 | 	sys_yield(preempt_pending); | 
 | 	__sync_fetch_and_or(&vcpd->flags, VC_CAN_RCV_MSG); | 
 | } | 
 |  | 
 | /* Enables notifs, and deals with missed notifs by self notifying.  This should | 
 |  * be rare, so the syscall overhead isn't a big deal.  The other alternative | 
 |  * would be to uthread_yield(), which would require us to revert some uthread | 
 |  * interface changes. */ | 
 | void enable_notifs(uint32_t vcoreid) | 
 | { | 
 | 	__enable_notifs(vcoreid); | 
 | 	wrmb();	/* need to read after the write that enabled notifs */ | 
 | 	/* Note we could get migrated before executing this.  If that happens, our | 
 | 	 * vcore had gone into vcore context (which is what we wanted), and this | 
 | 	 * self_notify to our old vcore is spurious and harmless. */ | 
 | 	if (vcpd_of(vcoreid)->notif_pending) | 
 | 		sys_self_notify(vcoreid, EV_NONE, 0, TRUE); | 
 | } | 
 |  | 
 | /* Helper to disable notifs.  It simply checks to make sure we disabled uthread | 
 |  * migration, which is a common mistake. */ | 
 | void disable_notifs(uint32_t vcoreid) | 
 | { | 
 | 	if (!in_vcore_context() && current_uthread) | 
 | 		assert(current_uthread->flags & UTHREAD_DONT_MIGRATE); | 
 | 	__disable_notifs(vcoreid); | 
 | } | 
 |  | 
 | /* Like smp_idle(), this will put the core in a state that it can only be woken | 
 |  * up by an IPI.  In the future, we may halt or something.  This will return if | 
 |  * an event was pending (could be the one you were waiting for). */ | 
 | void vcore_idle(void) | 
 | { | 
 | 	uint32_t vcoreid = vcore_id(); | 
 | 	if (handle_events(vcoreid)) | 
 | 		return; | 
 | 	enable_notifs(vcoreid); | 
 | 	while (1) { | 
 | 		cpu_relax(); | 
 | 	} | 
 | } | 
 |  | 
 | /* Helper, that actually makes sure a vcore is running.  Call this is you really | 
 |  * want vcoreid.  More often, you'll want to call the regular version. */ | 
 | static void __ensure_vcore_runs(uint32_t vcoreid) | 
 | { | 
 | 	if (vcore_is_preempted(vcoreid)) { | 
 | 		printd("[vcore]: VC %d changing to VC %d\n", vcore_id(), vcoreid); | 
 | 		/* Note that at this moment, the vcore could still be mapped (we're | 
 | 		 * racing with __preempt.  If that happens, we'll just fail the | 
 | 		 * sys_change_vcore(), and next time __ensure runs we'll get it. */ | 
 | 		/* We want to recover them from preemption.  Since we know they have | 
 | 		 * notifs disabled, they will need to be directly restarted, so we can | 
 | 		 * skip the other logic and cut straight to the sys_change_vcore() */ | 
 | 		sys_change_vcore(vcoreid, FALSE); | 
 | 	} | 
 | } | 
 |  | 
 | /* Helper, looks for any preempted vcores, making sure each of them runs at some | 
 |  * point.  This is pretty heavy-weight, and should be used to help get out of | 
 |  * weird deadlocks (spinning in vcore context, waiting on another vcore).  If | 
 |  * you might know which vcore you are waiting on, use ensure_vc_runs. */ | 
 | static void __ensure_all_run(void) | 
 | { | 
 | 	for (int i = 0; i < max_vcores(); i++) | 
 | 		__ensure_vcore_runs(i); | 
 | } | 
 |  | 
 | /* Makes sure a vcore is running.  If it is preempted, we'll switch to | 
 |  * it.  This will return, either immediately if the vcore is running, or later | 
 |  * when someone preempt-recovers us. | 
 |  * | 
 |  * If you pass in your own vcoreid, this will make sure all other preempted | 
 |  * vcores run. */ | 
 | void ensure_vcore_runs(uint32_t vcoreid) | 
 | { | 
 | 	/* if the vcoreid is ourselves, make sure everyone else is running */ | 
 | 	if (vcoreid == vcore_id()) { | 
 | 		__ensure_all_run(); | 
 | 		return; | 
 | 	} | 
 | 	__ensure_vcore_runs(vcoreid); | 
 | } | 
 |  | 
 | #define NR_RELAX_SPINS 1000 | 
 | /* If you are spinning in vcore context and it is likely that you don't know who | 
 |  * you are waiting on, call this.  It will spin for a bit before firing up the | 
 |  * potentially expensive __ensure_all_run().  Don't call this from uthread | 
 |  * context.  sys_change_vcore will probably mess you up. */ | 
 | void cpu_relax_vc(uint32_t vcoreid) | 
 | { | 
 | 	unsigned int spun = 0; | 
 | 	assert(in_vcore_context()); | 
 | 	if (spun++ >= NR_RELAX_SPINS) { | 
 | 		/* if vcoreid == vcore_id(), this might be expensive */ | 
 | 		ensure_vcore_runs(vcoreid); | 
 | 		spun = 0; | 
 | 	} | 
 | 	cpu_relax(); | 
 | } | 
 |  | 
 | /* Check with the kernel to determine what vcore we are.  Normally, you should | 
 |  * never call this, since your vcoreid is stored in your TLS.  Also, if you call | 
 |  * it from a uthread, you could get migrated, so you should drop into some form | 
 |  * of vcore context (DONT_MIGRATE on) */ | 
 | uint32_t get_vcoreid(void) | 
 | { | 
 | 	if (!in_vcore_context()) { | 
 | 		assert(current_uthread); | 
 | 		assert(current_uthread->flags & UTHREAD_DONT_MIGRATE); | 
 | 	} | 
 | 	return __get_vcoreid(); | 
 | } | 
 |  | 
 | /* Debugging helper.  Pass in the string you want printed if your vcoreid is | 
 |  * wrong, and pass in what vcoreid you think you are.  Don't call from uthread | 
 |  * context unless migrations are disabled.  Will print some stuff and return | 
 |  * FALSE if you were wrong. */ | 
 | bool check_vcoreid(const char *str, uint32_t vcoreid) | 
 | { | 
 | 	uint32_t kvcoreid = get_vcoreid(); | 
 | 	if (vcoreid != kvcoreid) { | 
 | 		ros_debug("%s: VC %d thought it was VC %d\n", str, kvcoreid, vcoreid); | 
 | 		return FALSE; | 
 | 	} | 
 | 	return TRUE; | 
 | } |