-
Notifications
You must be signed in to change notification settings - Fork 5
Kernel thread stop reap blocked #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
4fab079
60de82d
783df47
5619bf8
834164a
6ac16fe
6653e29
930c116
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,43 +9,173 @@ | |
| #include <thread.h> | ||
| #include <dosched.h> | ||
| #include <runlist.h> | ||
| #include <readylist.h> | ||
| #include <asid.h> | ||
| #include <stop.h> | ||
| #include <timer.h> | ||
| #include <vm.h> | ||
| #include <cpuint.h> | ||
| #include <id.h> | ||
| #include <alloc.h> | ||
| #include <futex.h> | ||
| #include <intpool.h> | ||
| #include <max.h> | ||
|
|
||
| void H2K_thread_stop(s32_t status, H2K_thread_context *me) | ||
| void H2K_vmblock_finalize_if_done_locked(H2K_vmblock_t *vmblock) | ||
| { | ||
| H2K_vmblock_t *vmblock = me->vmblock; | ||
| H2K_thread_context *parent_context; | ||
| H2K_vmblock_t *parent_vmblock; | ||
|
|
||
| if (vmblock->num_cpus != 0 && vmblock->status == 0) return; | ||
|
|
||
| parent_context = H2K_id_to_context(vmblock->parent); | ||
| if (parent_context != NULL | ||
| && parent_context->status != H2K_STATUS_DEAD) { | ||
| parent_vmblock = parent_context->vmblock; | ||
| H2K_vm_cpuint_post_locked(parent_vmblock, parent_context, H2K_VM_CHILDINT, parent_vmblock->intinfo); | ||
| } else if (vmblock->num_cpus == 0) { | ||
| /* Can't free immediately because H2K_switch reads from *me */ | ||
| /* EJP: I think this is OK now if we dosched(NULL,htnum)? */ | ||
| H2K_mem_alloc_free(vmblock); | ||
| } | ||
| } | ||
|
|
||
| /* See stop.h. The five-step "return a context to its free list" tail shared | ||
| * by reap_one_locked, vm_stop_locked, H2K_thread_stop, and resched's | ||
| * self_reap_locked. */ | ||
| void H2K_free_context_locked(H2K_vmblock_t *vmblock, H2K_thread_context *ctx) | ||
| { | ||
| H2K_asid_table_dec(ctx->ssr_asid); | ||
| H2K_thread_context_clear(ctx); /* preserves vmblock_id */ | ||
| ctx->next = vmblock->free_threads; | ||
| vmblock->free_threads = ctx; | ||
| vmblock->num_cpus--; | ||
| } | ||
|
|
||
| /* Cancel pending waits and return one context to its vmblock's free list. | ||
| * Caller holds BKL. Decrements num_cpus on success. | ||
| * Skips H2K_STATUS_RUNNING contexts: those are executing on another HW thread | ||
| * and must self-reap via the exiting-vmblock path in resched. */ | ||
| static int reap_one_locked(H2K_thread_context *ctx) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this code has the race condition issue that I ran into on my branch. Let's discuss.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, this function seems to duplicate a lot with other thread cancel functionality, duplicating it is likely to cause problems over time with maintenance. |
||
| { | ||
| H2K_vmblock_t *vmblock = ctx->vmblock; | ||
| u8_t s = ctx->status; | ||
|
|
||
| switch (s) { | ||
| case H2K_STATUS_DEAD: | ||
| case H2K_STATUS_RUNNING: | ||
| return 0; | ||
| case H2K_STATUS_BLOCKED: | ||
| H2K_timer_cancel_withlock(ctx); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can this be moved to be common? |
||
| H2K_futex_cancel(ctx); | ||
| break; | ||
| case H2K_STATUS_INTBLOCKED: | ||
| H2K_timer_cancel_withlock(ctx); | ||
| /* TODO: H2K_popup_wait shares INTBLOCKED with H2K_intpool_wait; | ||
| * H2K_intpool_cancel only handles the latter. Distinguish before | ||
| * canceling once popup_wait grows a state bit. */ | ||
| H2K_intpool_cancel(ctx); | ||
| break; | ||
| case H2K_STATUS_READY: | ||
| H2K_ready_remove(ctx); | ||
| H2K_timer_cancel_withlock(ctx); | ||
| break; | ||
| case H2K_STATUS_VMWAIT: | ||
| if (ctx->id.cpuidx < (sizeof(long_bitmask_t) * 8)) | ||
| vmblock->waiting_cpus &= ~(0x1ULL << ctx->id.cpuidx); | ||
| H2K_timer_cancel_withlock(ctx); | ||
| break; | ||
| default: | ||
| return 0; | ||
| } | ||
| H2K_free_context_locked(vmblock, ctx); | ||
| return 1; | ||
| } | ||
|
|
||
| /* Tear down the calling thread's entire vmblock: reap every non-DEAD context, | ||
| * IPI any RUNNING peers so they self-reap, and run the parent-signal / | ||
| * vmblock-free finalizer. Caller holds BKL. POSIX exit() semantics. */ | ||
| static void vm_stop_locked(s32_t status, H2K_thread_context *me) | ||
| { | ||
| H2K_vmblock_t *vmblock = me->vmblock; | ||
| u32_t i; | ||
|
|
||
| vmblock->exiting = 1; | ||
|
|
||
| /* Per-me cleanup. */ | ||
| H2K_timer_cancel_withlock(me); | ||
| H2K_runlist_remove(me); | ||
| H2K_free_context_locked(vmblock, me); | ||
| vmblock->status = status; | ||
|
|
||
| /* Pass 1: reap every non-DEAD, non-RUNNING context immediately. */ | ||
| for (i = 0; i < vmblock->max_cpus; i++) { | ||
| reap_one_locked(&vmblock->contexts[i]); | ||
| } | ||
| /* Pass 2: kick any RUNNING contexts via CLUSTER_RESCHED_INT steered to | ||
| * their HW thread. Each target enters H2K_resched_cluster, sees | ||
| * vmblock->exiting and self-reaps. */ | ||
| for (i = 0; i < vmblock->max_cpus; i++) { | ||
| H2K_thread_context *ctx = &vmblock->contexts[i]; | ||
| if (ctx->status != H2K_STATUS_RUNNING) continue; | ||
| if (ctx == me) continue; | ||
| iassignw(CLUSTER_RESCHED_INT, ~(0x1u << ctx->hthread)); | ||
| cluster_resched_int(); | ||
| } | ||
|
|
||
| H2K_vmblock_finalize_if_done_locked(vmblock); | ||
| } | ||
|
|
||
| void H2K_thread_stop(s32_t status, H2K_thread_context *me) | ||
| { | ||
| H2K_vmblock_t *vmblock = me->vmblock; | ||
| u32_t i; | ||
|
|
||
| BKL_LOCK(&H2K_bkl); | ||
|
|
||
| /* | ||
| * Per-thread cleanup. Main and worker threads share this path: | ||
| * pthread_exit on main must NOT tear down the VM (POSIX semantics -- | ||
| * workers keep running). VM-wide teardown happens only via | ||
| * H2K_vm_stop (H2_TRAP_VM_STOP), which exit()/sys_exit() invokes. | ||
| */ | ||
| H2K_timer_cancel_withlock(me); | ||
| H2K_runlist_remove(me); | ||
| H2K_asid_table_dec(me->ssr_asid); | ||
| H2K_thread_context_clear(me); | ||
| me->next = vmblock->free_threads; | ||
| vmblock->free_threads = me; | ||
| vmblock->num_cpus--; | ||
| H2K_free_context_locked(vmblock, me); | ||
| vmblock->status = status; | ||
|
|
||
| if (status != 0 || vmblock->num_cpus == 0) { // signal parent | ||
| parent_context = H2K_id_to_context(vmblock->parent); | ||
| if (parent_context != NULL | ||
| && parent_context->status != H2K_STATUS_DEAD) { // parent exists | ||
| parent_vmblock = parent_context->vmblock; | ||
| H2K_vm_cpuint_post_locked(parent_vmblock, parent_context, H2K_VM_CHILDINT, parent_vmblock->intinfo); | ||
| } else if (vmblock->num_cpus == 0) { // no parent; just deallocate. | ||
| /* Can't free immediately because H2K_switch reads from *me */ | ||
| /* EJP: I think this is OK now if we dosched(NULL,htnum)? */ | ||
| H2K_mem_alloc_free(vmblock); | ||
| if (!vmblock->exiting && status == 0 && vmblock->num_cpus > 0) { | ||
| /* A thread exited cleanly with siblings remaining (main and | ||
| * workers alike): keep the conservative all-blocked-with-no- | ||
| * armed-timer reaper. A non-zero ctx->timeout means a timer is | ||
| * queued; that thread will be woken when it fires, so do not reap. */ | ||
| int all_blocked = 1; | ||
| for (i = 0; i < vmblock->max_cpus && all_blocked; i++) { | ||
| H2K_thread_context *ctx = &vmblock->contexts[i]; | ||
| u8_t s = ctx->status; | ||
| if (s == H2K_STATUS_DEAD) | ||
| continue; | ||
| if ((s == H2K_STATUS_BLOCKED || s == H2K_STATUS_INTBLOCKED) && ctx->timeout == 0) | ||
| continue; | ||
| all_blocked = 0; | ||
| } | ||
| if (all_blocked) { | ||
| for (i = 0; i < vmblock->max_cpus; i++) { | ||
| reap_one_locked(&vmblock->contexts[i]); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| H2K_vmblock_finalize_if_done_locked(vmblock); | ||
|
|
||
| /* If we dosched(NULL,get_hwtnum()) I think we can remove special cases in free() */ | ||
| H2K_dosched(NULL,get_hwtnum()); | ||
| } | ||
|
|
||
| void H2K_vm_stop(s32_t status, H2K_thread_context *me) | ||
| { | ||
| BKL_LOCK(&H2K_bkl); | ||
| vm_stop_locked(status, me); | ||
| H2K_dosched(NULL, get_hwtnum()); | ||
| /* unreachable */ | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,16 @@ Terminate the current thread with the given status. | |
| */ | ||
| void h2_thread_stop_trap(int status); | ||
|
|
||
| /** | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This allows the process to tear itself down, but doesn't allow you to kill a child VM... that's also a useful thing to be able to do. |
||
| Tear down the entire VM with the given exit status (POSIX exit() semantics). | ||
| All other contexts in the calling thread's vmblock are reaped regardless of | ||
| state, then the parent VM is signaled. Caller does not return. | ||
| @param[in] status Termination status value | ||
| @returns None; Does not return. | ||
| @dependencies None | ||
| */ | ||
| void h2_vm_stop_trap(int status) __attribute__((noreturn)); | ||
|
|
||
| /** | ||
| Obtain the ID of the calling thread | ||
| @returns ID of the calling thread | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thread stop and vm stop should probably be separated (although perhaps one should call the other?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that. Should have kept reading...