Index: share/man/man9/stack.9 =================================================================== --- share/man/man9/stack.9 +++ share/man/man9/stack.9 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 6, 2017 +.Dd January 24, 2020 .Dt STACK 9 .Os .Sh NAME @@ -65,10 +65,8 @@ .Fn stack_sbuf_print_ddb "struct sbuf sb*" "const struct stack *st" .Ft void .Fn stack_save "struct stack *st" -.Ft void -.Fn stack_save_td "struct stack *st" "struct thread *td" .Ft int -.Fn stack_save_td_running "struct stack *st" "struct thread *td" +.Fn stack_save_td "struct stack *st" "struct thread *td" .Sh DESCRIPTION The .Nm @@ -93,18 +91,15 @@ Memory associated with a trace is freed by calling .Fn stack_destroy . .Pp -A trace of the current kernel thread's call stack may be captured using +A trace of the current thread's kernel call stack may be captured using .Fn stack_save . .Fn stack_save_td -and -.Fn stack_save_td_running -can also be used to capture the stack of a caller-specified thread. -Callers of these functions must own the thread lock of the specified thread. +can be used to capture the kernel stack of a caller-specified thread. +Callers of these functions must own the thread lock of the specified thread, +and the thread's stack must not be swapped out. .Fn stack_save_td -can capture the stack of a kernel thread that is not running or -swapped out at the time of the call. -.Fn stack_save_td_running -can capture the stack of a running kernel thread. +can capture the kernel stack of a running thread, though note that this is +not implemented on all platforms. .Pp .Fn stack_print and @@ -157,11 +152,11 @@ does not contain space to record additional frames, and a non-zero value is returned. .Pp -.Fn stack_save_td_running +.Fn stack_save_td returns 0 when the stack capture was successful and a non-zero error number otherwise. In particular, -.Er EAGAIN +.Er EBUSY is returned if the thread was running in user mode at the time that the capture was attempted, and .Er EOPNOTSUPP Index: sys/amd64/amd64/trap.c =================================================================== --- sys/amd64/amd64/trap.c +++ sys/amd64/amd64/trap.c @@ -228,11 +228,6 @@ (*pmc_intr)(frame) != 0) return; #endif - -#ifdef STACK - if (stack_nmi_handler(frame) != 0) - return; -#endif } if ((frame->tf_rflags & PSL_I) == 0) { Index: sys/arm/arm/stack_machdep.c =================================================================== --- sys/arm/arm/stack_machdep.c +++ sys/arm/arm/stack_machdep.c @@ -68,8 +68,12 @@ { struct unwind_state state; - KASSERT(!TD_IS_SWAPPED(td), ("stack_save_td: swapped")); - KASSERT(!TD_IS_RUNNING(td), ("stack_save_td: running")); + THREAD_LOCK_ASSERT(td, MA_OWNED); + KASSERT(!TD_IS_SWAPPED(td), + ("stack_save_td: thread %p is swapped", td)); + + if (TD_IS_RUNNING(td)) + return (EOPNOTSUPP); state.registers[FP] = td->td_pcb->pcb_regs.sf_r11; state.registers[SP] = td->td_pcb->pcb_regs.sf_sp; @@ -78,14 +82,3 @@ stack_capture(st, &state); } - -int -stack_save_td_running(struct stack *st, struct thread *td) -{ - - if (td == curthread) { - stack_save(st); - return (0); - } - return (EOPNOTSUPP); -} Index: sys/arm64/arm64/stack_machdep.c =================================================================== --- sys/arm64/arm64/stack_machdep.c +++ sys/arm64/arm64/stack_machdep.c @@ -60,10 +60,12 @@ { struct unwind_state frame; - if (TD_IS_SWAPPED(td)) - panic("stack_save_td: swapped"); + THREAD_LOCK_ASSERT(td, MA_OWNED); + KASSERT(!TD_IS_SWAPPED(td), + ("stack_save_td: thread %p is swapped", td)); + if (TD_IS_RUNNING(td)) - panic("stack_save_td: running"); + return (EOPNOTSUPP); frame.sp = td->td_pcb->pcb_sp; frame.fp = td->td_pcb->pcb_x[29]; @@ -71,25 +73,3 @@ stack_capture(st, &frame); } - -int -stack_save_td_running(struct stack *st, struct thread *td) -{ - - return (EOPNOTSUPP); -} - -void -stack_save(struct stack *st) -{ - struct unwind_state frame; - uint64_t sp; - - __asm __volatile("mov %0, sp" : "=&r" (sp)); - - frame.sp = sp; - frame.fp = (uint64_t)__builtin_frame_address(0); - frame.pc = (uint64_t)stack_save; - - stack_capture(st, &frame); -} Index: sys/i386/i386/trap.c =================================================================== --- sys/i386/i386/trap.c +++ sys/i386/i386/trap.c @@ -248,11 +248,6 @@ (*pmc_intr)(frame) != 0) return; #endif - -#ifdef STACK - if (stack_nmi_handler(frame) != 0) - return; -#endif } if (type == T_MCHK) { Index: sys/kern/kern_proc.c =================================================================== --- sys/kern/kern_proc.c +++ sys/kern/kern_proc.c @@ -2669,17 +2669,12 @@ sizeof(kkstp->kkst_trace), SBUF_FIXEDLEN); thread_lock(td); kkstp->kkst_tid = td->td_tid; - if (TD_IS_SWAPPED(td)) { + if (TD_IS_SWAPPED(td)) kkstp->kkst_state = KKST_STATE_SWAPPED; - } else if (TD_IS_RUNNING(td)) { - if (stack_save_td_running(st, td) == 0) - kkstp->kkst_state = KKST_STATE_STACKOK; - else - kkstp->kkst_state = KKST_STATE_RUNNING; - } else { + else if (stack_save_td(st, td) == 0) kkstp->kkst_state = KKST_STATE_STACKOK; - stack_save_td(st, td); - } + else + kkstp->kkst_state = KKST_STATE_RUNNING; thread_unlock(td); PROC_UNLOCK(p); stack_sbuf_print(&sb, st); Index: sys/kern/subr_kdb.c =================================================================== --- sys/kern/subr_kdb.c +++ sys/kern/subr_kdb.c @@ -434,8 +434,8 @@ printf("KDB: stack backtrace of thread %d:\n", td->td_tid); stack_zero(&st); - stack_save_td(&st, td); - stack_print_ddb(&st); + if (stack_save_td(&st, td) == 0) + stack_print_ddb(&st); } #endif } Index: sys/kern/subr_sleepqueue.c =================================================================== --- sys/kern/subr_sleepqueue.c +++ sys/kern/subr_sleepqueue.c @@ -1246,7 +1246,7 @@ goto loop_end; /* Note the td_lock is equal to the sleepq_lock here. */ - stack_save_td(st[stack_idx], td); + (void)stack_save_td(st[stack_idx], td); sbuf_printf(td_infos[stack_idx], "%d: %s %p", td->td_tid, td->td_name, td); Index: sys/kern/subr_smp.c =================================================================== --- sys/kern/subr_smp.c +++ sys/kern/subr_smp.c @@ -871,6 +871,20 @@ mp_setvariables_for_up, NULL); #endif /* SMP */ +void +smp_rendezvous_cpu(int cpu, + void (*setup_func)(void *), + void (*action_func)(void *), + void (*teardown_func)(void *), + void *arg) +{ + cpuset_t cs; + + CPU_ZERO(&cs); + CPU_SET(cpu, &cs); + smp_rendezvous_cpus(cs, setup_func, action_func, teardown_func, arg); +} + void smp_no_rendezvous_barrier(void *dummy) { Index: sys/kern/tty_info.c =================================================================== --- sys/kern/tty_info.c +++ sys/kern/tty_info.c @@ -340,12 +340,8 @@ if (tty_info_kstacks) { if (TD_IS_SWAPPED(td)) sterr = ENOENT; - else if (TD_IS_RUNNING(td)) - sterr = stack_save_td_running(&stack, td); - else { - stack_save_td(&stack, td); - sterr = 0; - } + else + sterr = stack_save_td(&stack, td); } #endif thread_unlock(td); Index: sys/mips/mips/stack_machdep.c =================================================================== --- sys/mips/mips/stack_machdep.c +++ sys/mips/mips/stack_machdep.c @@ -132,32 +132,14 @@ { u_register_t pc, sp; - if (TD_IS_SWAPPED(td)) - panic("stack_save_td: swapped"); + THREAD_LOCK_ASSERT(td, MA_OWNED); + KASSERT(!TD_IS_SWAPPED(td), + ("stack_save_td: thread %p is swapped", td)); + if (TD_IS_RUNNING(td)) - panic("stack_save_td: running"); + return (EOPNOTSUPP); pc = td->td_pcb->pcb_regs.pc; sp = td->td_pcb->pcb_regs.sp; stack_capture(st, pc, sp); } - -int -stack_save_td_running(struct stack *st, struct thread *td) -{ - - return (EOPNOTSUPP); -} - -void -stack_save(struct stack *st) -{ - u_register_t pc, sp; - - if (curthread == NULL) - panic("stack_save: curthread == NULL"); - - pc = curthread->td_pcb->pcb_regs.pc; - sp = curthread->td_pcb->pcb_regs.sp; - stack_capture(st, pc, sp); -} Index: sys/powerpc/powerpc/stack_machdep.c =================================================================== --- sys/powerpc/powerpc/stack_machdep.c +++ sys/powerpc/powerpc/stack_machdep.c @@ -91,27 +91,13 @@ { vm_offset_t frame; - if (TD_IS_SWAPPED(td)) - panic("stack_save_td: swapped"); + THREAD_LOCK_ASSERT(td, MA_OWNED); + KASSERT(!TD_IS_SWAPPED(td), + ("stack_save_td: thread %p is swapped", td)); + if (TD_IS_RUNNING(td)) - panic("stack_save_td: running"); + return (EOPNOTSUPP); frame = td->td_pcb->pcb_sp; stack_capture(st, frame); } - -int -stack_save_td_running(struct stack *st, struct thread *td) -{ - - return (EOPNOTSUPP); -} - -void -stack_save(struct stack *st) -{ - register_t frame; - - frame = (register_t)__builtin_frame_address(0); - stack_capture(st, frame); -} Index: sys/riscv/riscv/stack_machdep.c =================================================================== --- sys/riscv/riscv/stack_machdep.c +++ sys/riscv/riscv/stack_machdep.c @@ -65,10 +65,12 @@ { struct unwind_state frame; - if (TD_IS_SWAPPED(td)) - panic("stack_save_td: swapped"); + THREAD_LOCK_ASSERT(td, MA_OWNED); + KASSERT(!TD_IS_SWAPPED(td), + ("stack_save_td: thread %p is swapped", td)); + if (TD_IS_RUNNING(td)) - panic("stack_save_td: running"); + return (EOPNOTSUPP); frame.sp = td->td_pcb->pcb_sp; frame.fp = td->td_pcb->pcb_s[0]; @@ -76,25 +78,3 @@ stack_capture(st, &frame); } - -int -stack_save_td_running(struct stack *st, struct thread *td) -{ - - return (EOPNOTSUPP); -} - -void -stack_save(struct stack *st) -{ - struct unwind_state frame; - uint64_t sp; - - __asm __volatile("mv %0, sp" : "=&r" (sp)); - - frame.sp = sp; - frame.fp = (uint64_t)__builtin_frame_address(0); - frame.pc = (uint64_t)stack_save; - - stack_capture(st, &frame); -} Index: sys/sparc64/sparc64/stack_machdep.c =================================================================== --- sys/sparc64/sparc64/stack_machdep.c +++ sys/sparc64/sparc64/stack_machdep.c @@ -76,21 +76,16 @@ stack_save_td(struct stack *st, struct thread *td) { - if (TD_IS_SWAPPED(td)) - panic("stack_save_td: swapped"); + THREAD_LOCK_ASSERT(td, MA_OWNED); + KASSERT(!TD_IS_SWAPPED(td), + ("stack_save_td: thread %p is swapped", td)); + if (TD_IS_RUNNING(td)) - panic("stack_save_td: running"); + return (EOPNOTSUPP); stack_capture(st, (struct frame *)(td->td_pcb->pcb_sp + SPOFF)); } -int -stack_save_td_running(struct stack *st, struct thread *td) -{ - - return (EOPNOTSUPP); -} - void stack_save(struct stack *st) { Index: sys/sys/proc.h =================================================================== --- sys/sys/proc.h +++ sys/sys/proc.h @@ -362,7 +362,7 @@ /* LP64 hole */ void *td_emuldata; /* Emulator state data */ int td_lastcpu; /* (t) Last cpu we were on. */ - int td_oncpu; /* (t) Which cpu we are on. */ + int td_oncpu; /* (k) Which cpu we are on. */ void *td_lkpi_task; /* LinuxKPI task struct pointer */ int td_pmcpend; #ifdef EPOCH_TRACE Index: sys/sys/smp.h =================================================================== --- sys/sys/smp.h +++ sys/sys/smp.h @@ -271,6 +271,11 @@ void (*)(void *), void (*)(void *), void *arg); +void smp_rendezvous_cpu(int, + void (*)(void *), + void (*)(void *), + void (*)(void *), + void *arg); void smp_rendezvous_cpus(cpuset_t, void (*)(void *), void (*)(void *), Index: sys/sys/stack.h =================================================================== --- sys/sys/stack.h +++ sys/sys/stack.h @@ -67,7 +67,6 @@ /* MD Routines. */ struct thread; void stack_save(struct stack *); -void stack_save_td(struct stack *, struct thread *); -int stack_save_td_running(struct stack *, struct thread *); +int stack_save_td(struct stack *, struct thread *); #endif Index: sys/x86/include/apicvar.h =================================================================== --- sys/x86/include/apicvar.h +++ sys/x86/include/apicvar.h @@ -129,14 +129,13 @@ #define IPI_STOP (APIC_IPI_INTS + 6) /* Stop CPU until restarted. */ #define IPI_SUSPEND (APIC_IPI_INTS + 7) /* Suspend CPU until restarted. */ #define IPI_DYN_FIRST (APIC_IPI_INTS + 8) -#define IPI_DYN_LAST (253) /* IPIs allocated at runtime */ +#define IPI_DYN_LAST (254) /* IPIs allocated at runtime */ /* * IPI_STOP_HARD does not need to occupy a slot in the IPI vector space since * it is delivered using an NMI anyways. */ -#define IPI_NMI_FIRST 254 -#define IPI_TRACE 254 /* Interrupt for tracing. */ +#define IPI_NMI_FIRST 255 #define IPI_STOP_HARD 255 /* Stop CPU with a NMI. */ /* Index: sys/x86/x86/stack_machdep.c =================================================================== --- sys/x86/x86/stack_machdep.c +++ sys/x86/x86/stack_machdep.c @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include #include @@ -63,16 +65,6 @@ typedef struct amd64_frame *x86_frame_t; #endif -#ifdef STACK -static struct stack *nmi_stack; -static volatile struct thread *nmi_pending; - -#ifdef SMP -static struct mtx nmi_lock; -MTX_SYSINIT(nmi_lock, &nmi_lock, "stack_nmi", MTX_SPIN); -#endif -#endif - static void stack_capture(struct thread *td, struct stack *st, register_t fp) { @@ -97,74 +89,79 @@ } } -int -stack_nmi_handler(struct trapframe *tf) -{ +struct stack_capture_args { + struct thread *td; + struct stack *st; + int error; +}; -#ifdef STACK - /* Don't consume an NMI that wasn't meant for us. */ - if (nmi_stack == NULL || curthread != nmi_pending) - return (0); - - if (!TRAPF_USERMODE(tf) && (TF_FLAGS(tf) & PSL_I) != 0) - stack_capture(curthread, nmi_stack, TF_FP(tf)); - else - /* We were running in usermode or had interrupts disabled. */ - nmi_stack->depth = 0; - - atomic_store_rel_ptr((long *)&nmi_pending, (long)NULL); - return (1); -#else - return (0); -#endif -} - -void -stack_save_td(struct stack *st, struct thread *td) +static void +stack_capture_remote(void *arg) { + struct stack_capture_args *args; + struct thread *td; - if (TD_IS_SWAPPED(td)) - panic("stack_save_td: swapped"); - if (TD_IS_RUNNING(td)) - panic("stack_save_td: running"); + args = arg; + td = args->td; - stack_capture(td, st, PCB_FP(td->td_pcb)); + if (curthread != td) + return; + if (TRAPF_USERMODE(td->td_frame)) { + args->error = EJUSTRETURN; + return; + } + stack_capture(td, args->st, PCB_FP(td->td_pcb)); + args->error = 0; } int -stack_save_td_running(struct stack *st, struct thread *td) +stack_save_td(struct stack *st, struct thread *td) { + struct stack_capture_args args; + int cpuid; -#ifdef STACK THREAD_LOCK_ASSERT(td, MA_OWNED); - MPASS(TD_IS_RUNNING(td)); + KASSERT(!TD_IS_SWAPPED(td), + ("stack_save_td: thread %p is swapped", td)); if (td == curthread) { stack_save(st); return (0); } -#ifdef SMP - mtx_lock_spin(&nmi_lock); - - nmi_stack = st; - nmi_pending = td; - ipi_cpu(td->td_oncpu, IPI_TRACE); - while ((void *)atomic_load_acq_ptr((long *)&nmi_pending) != NULL) - cpu_spinwait(); - nmi_stack = NULL; - - mtx_unlock_spin(&nmi_lock); + args.td = td; + args.st = st; + args.error = ENOENT; + do { + if (!TD_IS_RUNNING(td)) { + stack_capture(td, st, PCB_FP(td->td_pcb)); + return (0); + } + if (td->td_md.md_spinlock_count > 1) + /* + * We may be holding a resource for which td is + * spinning. + */ + return (EBUSY); + + thread_unlock(td); + cpuid = atomic_load_int(&td->td_oncpu); + if (cpuid == NOCPU) { + /* + * Wait for the thread to switch on. + */ + sched_relinquish(curthread); + continue; + } + + smp_rendezvous_cpu(cpuid, NULL, stack_capture_remote, NULL, + &args); + thread_lock(td); + if (args.error == EJUSTRETURN) + return (EBUSY); + } while (args.error != 0); - if (st->depth == 0) - return (EAGAIN); -#else /* !SMP */ - KASSERT(0, ("curthread isn't running")); -#endif /* SMP */ return (0); -#else /* !STACK */ - return (EOPNOTSUPP); -#endif /* STACK */ } void