Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 11 Sep 2015 03:54:38 +0000 (UTC)
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r287645 - in head/sys: amd64/amd64 arm/arm arm64/arm64 i386/i386 kern mips/mips powerpc/powerpc sparc64/sparc64 sys x86/include x86/x86
Message-ID:  <201509110354.t8B3scxL083655@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: markj
Date: Fri Sep 11 03:54:37 2015
New Revision: 287645
URL: https://svnweb.freebsd.org/changeset/base/287645

Log:
  Add stack_save_td_running(), a function to trace the kernel stack of a
  running thread.
  
  It is currently implemented only on amd64 and i386; on these
  architectures, it is implemented by raising an NMI on the CPU on which
  the target thread is currently running. Unlike stack_save_td(), it may
  fail, for example if the thread is running in user mode.
  
  This change also modifies the kern.proc.kstack sysctl to use this function,
  so that stacks of running threads are shown in the output of "procstat -kk".
  This is handy for debugging threads that are stuck in a busy loop.
  
  Reviewed by:	bdrewery, jhb, kib
  Sponsored by:	EMC / Isilon Storage Division
  Differential Revision:	https://reviews.freebsd.org/D3256

Modified:
  head/sys/amd64/amd64/trap.c
  head/sys/arm/arm/stack_machdep.c
  head/sys/arm64/arm64/stack_machdep.c
  head/sys/i386/i386/trap.c
  head/sys/kern/kern_proc.c
  head/sys/mips/mips/stack_machdep.c
  head/sys/powerpc/powerpc/stack_machdep.c
  head/sys/sparc64/sparc64/stack_machdep.c
  head/sys/sys/stack.h
  head/sys/x86/include/apicvar.h
  head/sys/x86/include/stack.h
  head/sys/x86/x86/local_apic.c
  head/sys/x86/x86/mp_x86.c
  head/sys/x86/x86/stack_machdep.c

Modified: head/sys/amd64/amd64/trap.c
==============================================================================
--- head/sys/amd64/amd64/trap.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/amd64/amd64/trap.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
 #include "opt_hwpmc_hooks.h"
 #include "opt_isa.h"
 #include "opt_kdb.h"
+#include "opt_stack.h"
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -91,6 +92,7 @@ PMC_SOFT_DEFINE( , , page_fault, write);
 #ifdef SMP
 #include <machine/smp.h>
 #endif
+#include <machine/stack.h>
 #include <machine/tss.h>
 
 #ifdef KDTRACE_HOOKS
@@ -202,17 +204,24 @@ trap(struct trapframe *frame)
 		goto out;
 	}
 
-#ifdef	HWPMC_HOOKS
-	/*
-	 * CPU PMCs interrupt using an NMI.  If the PMC module is
-	 * active, pass the 'rip' value to the PMC module's interrupt
-	 * handler.  A return value of '1' from the handler means that
-	 * the NMI was handled by it and we can return immediately.
-	 */
-	if (type == T_NMI && pmc_intr &&
-	    (*pmc_intr)(PCPU_GET(cpuid), frame))
-		goto out;
+	if (type == T_NMI) {
+#ifdef HWPMC_HOOKS
+		/*
+		 * CPU PMCs interrupt using an NMI.  If the PMC module is
+		 * active, pass the 'rip' value to the PMC module's interrupt
+		 * handler.  A non-zero return value from the handler means that
+		 * the NMI was consumed by it and we can return immediately.
+		 */
+		if (pmc_intr != NULL &&
+		    (*pmc_intr)(PCPU_GET(cpuid), frame) != 0)
+			goto out;
+#endif
+
+#ifdef STACK
+		if (stack_nmi_handler(frame) != 0)
+			goto out;
 #endif
+	}
 
 	if (type == T_MCHK) {
 		mca_intr();

Modified: head/sys/arm/arm/stack_machdep.c
==============================================================================
--- head/sys/arm/arm/stack_machdep.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/arm/arm/stack_machdep.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -71,6 +71,13 @@ stack_save_td(struct stack *st, struct t
 	stack_capture(st, frame);
 }
 
+int
+stack_save_td_running(struct stack *st, struct thread *td)
+{
+
+	return (EOPNOTSUPP);
+}
+
 void
 stack_save(struct stack *st)
 {

Modified: head/sys/arm64/arm64/stack_machdep.c
==============================================================================
--- head/sys/arm64/arm64/stack_machdep.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/arm64/arm64/stack_machdep.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -72,6 +72,13 @@ stack_save_td(struct stack *st, struct t
 	stack_capture(st, &frame);
 }
 
+int
+stack_save_td_running(struct stack *st, struct thread *td)
+{
+
+	return (EOPNOTSUPP);
+}
+
 void
 stack_save(struct stack *st)
 {

Modified: head/sys/i386/i386/trap.c
==============================================================================
--- head/sys/i386/i386/trap.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/i386/i386/trap.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
 #include "opt_isa.h"
 #include "opt_kdb.h"
 #include "opt_npx.h"
+#include "opt_stack.h"
 #include "opt_trap.h"
 
 #include <sys/param.h>
@@ -94,6 +95,7 @@ PMC_SOFT_DEFINE( , , page_fault, write);
 #ifdef SMP
 #include <machine/smp.h>
 #endif
+#include <machine/stack.h>
 #include <machine/tss.h>
 #include <machine/vm86.h>
 
@@ -219,19 +221,26 @@ trap(struct trapframe *frame)
 		goto out;
 	}
 
-#ifdef	HWPMC_HOOKS
-	/*
-	 * CPU PMCs interrupt using an NMI so we check for that first.
-	 * If the HWPMC module is active, 'pmc_hook' will point to
-	 * the function to be called.  A return value of '1' from the
-	 * hook means that the NMI was handled by it and that we can
-	 * return immediately.
-	 */
-	if (type == T_NMI && pmc_intr &&
-	    (*pmc_intr)(PCPU_GET(cpuid), frame))
-	    goto out;
+	if (type == T_NMI) {
+#ifdef HWPMC_HOOKS
+		/*
+		 * CPU PMCs interrupt using an NMI so we check for that first.
+		 * If the HWPMC module is active, 'pmc_hook' will point to
+		 * the function to be called.  A non-zero return value from the
+		 * hook means that the NMI was consumed by it and that we can
+		 * return immediately.
+		 */
+		if (pmc_intr != NULL &&
+		    (*pmc_intr)(PCPU_GET(cpuid), frame) != 0)
+			goto out;
 #endif
 
+#ifdef STACK
+		if (stack_nmi_handler(frame) != 0)
+			goto out;
+#endif
+	}
+
 	if (type == T_MCHK) {
 		mca_intr();
 		goto out;

Modified: head/sys/kern/kern_proc.c
==============================================================================
--- head/sys/kern/kern_proc.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/kern/kern_proc.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -2517,11 +2517,14 @@ repeat:
 		    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))
-			kkstp->kkst_state = KKST_STATE_RUNNING;
-		else {
+		} 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 {
 			kkstp->kkst_state = KKST_STATE_STACKOK;
 			stack_save_td(st, td);
 		}

Modified: head/sys/mips/mips/stack_machdep.c
==============================================================================
--- head/sys/mips/mips/stack_machdep.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/mips/mips/stack_machdep.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -142,6 +142,13 @@ stack_save_td(struct stack *st, struct t
 	stack_capture(st, pc, sp);
 }
 
+int
+stack_save_td_running(struct stack *st, struct thread *td)
+{
+
+	return (EOPNOTSUPP);
+}
+
 void
 stack_save(struct stack *st)
 {

Modified: head/sys/powerpc/powerpc/stack_machdep.c
==============================================================================
--- head/sys/powerpc/powerpc/stack_machdep.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/powerpc/powerpc/stack_machdep.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -98,6 +98,13 @@ stack_save_td(struct stack *st, struct t
 	stack_capture(st, frame);
 }
 
+int
+stack_save_td_running(struct stack *st, struct thread *td)
+{
+
+	return (EOPNOTSUPP);
+}
+
 void
 stack_save(struct stack *st)
 {

Modified: head/sys/sparc64/sparc64/stack_machdep.c
==============================================================================
--- head/sys/sparc64/sparc64/stack_machdep.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/sparc64/sparc64/stack_machdep.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -82,6 +82,13 @@ stack_save_td(struct stack *st, struct t
 	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)
 {

Modified: head/sys/sys/stack.h
==============================================================================
--- head/sys/sys/stack.h	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/sys/stack.h	Fri Sep 11 03:54:37 2015	(r287645)
@@ -56,9 +56,10 @@ void		 stack_ktr(u_int, const char *, in
 #define	CTRSTACK(m, st, depth, cheap)
 #endif
 
-/* MD Routine. */
+/* 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 *);
 
 #endif

Modified: head/sys/x86/include/apicvar.h
==============================================================================
--- head/sys/x86/include/apicvar.h	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/x86/include/apicvar.h	Fri Sep 11 03:54:37 2015	(r287645)
@@ -129,12 +129,14 @@
 #else
 #define	IPI_DYN_FIRST	(APIC_IPI_INTS + 8)
 #endif
-#define	IPI_DYN_LAST	(254)			/* IPIs allocated at runtime */
+#define	IPI_DYN_LAST	(253)			/* 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_STOP_HARD	255			/* Stop CPU with a NMI. */
 
 /*

Modified: head/sys/x86/include/stack.h
==============================================================================
--- head/sys/x86/include/stack.h	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/x86/include/stack.h	Fri Sep 11 03:54:37 2015	(r287645)
@@ -54,4 +54,8 @@ struct i386_frame {
 };
 #endif /* __amd64__ */
 
+#ifdef _KERNEL
+int	stack_nmi_handler(struct trapframe *);
+#endif
+
 #endif /* !_X86_STACK_H */

Modified: head/sys/x86/x86/local_apic.c
==============================================================================
--- head/sys/x86/x86/local_apic.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/x86/x86/local_apic.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -1703,11 +1703,10 @@ native_lapic_ipi_vectored(u_int vector, 
 	icrlo = APIC_DESTMODE_PHY | APIC_TRIGMOD_EDGE | APIC_LEVEL_ASSERT;
 
 	/*
-	 * IPI_STOP_HARD is just a "fake" vector used to send a NMI.
-	 * Use special rules regard NMI if passed, otherwise specify
-	 * the vector.
+	 * NMI IPIs are just fake vectors used to send a NMI.  Use special rules
+	 * regarding NMIs if passed, otherwise specify the vector.
 	 */
-	if (vector == IPI_STOP_HARD)
+	if (vector >= IPI_NMI_FIRST)
 		icrlo |= APIC_DELMODE_NMI;
 	else
 		icrlo |= vector | APIC_DELMODE_FIXED;

Modified: head/sys/x86/x86/mp_x86.c
==============================================================================
--- head/sys/x86/x86/mp_x86.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/x86/x86/mp_x86.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -120,7 +120,7 @@ struct cpu_ops cpu_ops;
  * Local data and functions.
  */
 
-static volatile cpuset_t ipi_nmi_pending;
+static volatile cpuset_t ipi_stop_nmi_pending;
 
 /* used to hold the AP's until we are ready to release them */
 struct mtx ap_boot_mtx;
@@ -894,7 +894,7 @@ ipi_selected(cpuset_t cpus, u_int ipi)
 	 * Set the mask of receiving CPUs for this purpose.
 	 */
 	if (ipi == IPI_STOP_HARD)
-		CPU_OR_ATOMIC(&ipi_nmi_pending, &cpus);
+		CPU_OR_ATOMIC(&ipi_stop_nmi_pending, &cpus);
 
 	while ((cpu = CPU_FFS(&cpus)) != 0) {
 		cpu--;
@@ -917,7 +917,7 @@ ipi_cpu(int cpu, u_int ipi)
 	 * Set the mask of receiving CPUs for this purpose.
 	 */
 	if (ipi == IPI_STOP_HARD)
-		CPU_SET_ATOMIC(cpu, &ipi_nmi_pending);
+		CPU_SET_ATOMIC(cpu, &ipi_stop_nmi_pending);
 
 	CTR3(KTR_SMP, "%s: cpu: %d ipi: %x", __func__, cpu, ipi);
 	ipi_send_cpu(cpu, ipi);
@@ -944,7 +944,7 @@ ipi_all_but_self(u_int ipi)
 	 * Set the mask of receiving CPUs for this purpose.
 	 */
 	if (ipi == IPI_STOP_HARD)
-		CPU_OR_ATOMIC(&ipi_nmi_pending, &other_cpus);
+		CPU_OR_ATOMIC(&ipi_stop_nmi_pending, &other_cpus);
 
 	CTR2(KTR_SMP, "%s: ipi: %x", __func__, ipi);
 	lapic_ipi_vectored(ipi, APIC_IPI_DEST_OTHERS);
@@ -962,10 +962,10 @@ ipi_nmi_handler()
 	 * and should be handled.
 	 */
 	cpuid = PCPU_GET(cpuid);
-	if (!CPU_ISSET(cpuid, &ipi_nmi_pending))
+	if (!CPU_ISSET(cpuid, &ipi_stop_nmi_pending))
 		return (1);
 
-	CPU_CLR_ATOMIC(cpuid, &ipi_nmi_pending);
+	CPU_CLR_ATOMIC(cpuid, &ipi_stop_nmi_pending);
 	cpustop_handler();
 	return (0);
 }

Modified: head/sys/x86/x86/stack_machdep.c
==============================================================================
--- head/sys/x86/x86/stack_machdep.c	Fri Sep 11 03:31:22 2015	(r287644)
+++ head/sys/x86/x86/stack_machdep.c	Fri Sep 11 03:54:37 2015	(r287645)
@@ -1,4 +1,5 @@
 /*-
+ * Copyright (c) 2015 EMC Corporation
  * Copyright (c) 2005 Antoine Brodin
  * All rights reserved.
  *
@@ -29,17 +30,21 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
 #include <sys/proc.h>
 #include <sys/stack.h>
 
-#include <x86/stack.h>
-
 #include <machine/pcb.h>
+#include <machine/smp.h>
 
 #include <vm/vm.h>
 #include <vm/vm_param.h>
 #include <vm/pmap.h>
 
+#include <x86/stack.h>
+
 #ifdef __i386__
 #define	PCB_FP(pcb)	((pcb)->pcb_ebp)
 #define	TF_FP(tf)	((tf)->tf_ebp)
@@ -54,6 +59,14 @@ typedef struct i386_frame *x86_frame_t;
 typedef struct amd64_frame *x86_frame_t;
 #endif
 
+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
+
 static void
 stack_capture(struct thread *td, struct stack *st, register_t fp)
 {
@@ -78,6 +91,24 @@ stack_capture(struct thread *td, struct 
 	}
 }
 
+int
+stack_nmi_handler(struct trapframe *tf)
+{
+
+	/* Don't consume an NMI that wasn't meant for us. */
+	if (nmi_stack == NULL || curthread != nmi_pending)
+		return (0);
+
+	if (INKERNEL(TF_PC(tf)))
+		stack_capture(curthread, nmi_stack, TF_FP(tf));
+	else
+		/* We interrupted a thread in user mode. */
+		nmi_stack->depth = 0;
+
+	atomic_store_rel_ptr((long *)&nmi_pending, (long)NULL);
+	return (1);
+}
+
 void
 stack_save_td(struct stack *st, struct thread *td)
 {
@@ -90,6 +121,39 @@ stack_save_td(struct stack *st, struct t
 	stack_capture(td, st, PCB_FP(td->td_pcb));
 }
 
+int
+stack_save_td_running(struct stack *st, struct thread *td)
+{
+
+	THREAD_LOCK_ASSERT(td, MA_OWNED);
+	MPASS(TD_IS_RUNNING(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);
+
+	if (st->depth == 0)
+		/* We interrupted a thread in user mode. */
+		return (EAGAIN);
+#else
+	KASSERT(0, ("curthread isn't running"));
+#endif
+	return (0);
+}
+
 void
 stack_save(struct stack *st)
 {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201509110354.t8B3scxL083655>