summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/ftrace.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/ftrace.c')
-rw-r--r--usr/src/uts/common/os/ftrace.c276
1 files changed, 201 insertions, 75 deletions
diff --git a/usr/src/uts/common/os/ftrace.c b/usr/src/uts/common/os/ftrace.c
index 5a31175701..0645759dfb 100644
--- a/usr/src/uts/common/os/ftrace.c
+++ b/usr/src/uts/common/os/ftrace.c
@@ -2,9 +2,8 @@
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
- * Common Development and Distribution License, Version 1.0 only
- * (the "License"). You may not use this file except in compliance
- * with the License.
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
@@ -20,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 1998-2003 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -49,7 +48,24 @@ int ftrace_atboot = 0;
int ftrace_nent = FTRACE_NENT;
/*
- * The current overall state of the ftrace subsystem.
+ * Global Tracing State:
+ *
+ * NOTREADY(=0)
+ * |
+ * ftrace_init()
+ * |
+ * |
+ * v
+ * +-------->READY-------+
+ * | |
+ * ftrace_stop() ftrace_start()
+ * | |
+ * +---(ENABLED|READY)<--+
+ *
+ * During boot, ftrace_init() is called and the state becomes
+ * READY. If ftrace_atboot is set, ftrace_start() is called at
+ * this time.
+ *
* If FTRACE_READY is set, then tracing can be enabled.
* If FTRACE_ENABLED is set, tracing is enabled on the set of CPUs
* which are currently FTRACE_READY.
@@ -57,16 +73,53 @@ int ftrace_nent = FTRACE_NENT;
static int ftrace_state = 0;
/*
- * Protects assignments to:
+ * Per-CPU Tracing State:
+ *
+ * +-----------------READY<--------------+
+ * | ^ | |
+ * | | ftrace_cpu_fini() |
+ * | | | |
+ * | ftrace_cpu_init() | |
+ * | | v ftrace_cpu_stop()
+ * | NOTREADY(=0) |
+ * | ^ |
+ * ftrace_cpu_start() | |
+ * | ftrace_cpu_fini() |
+ * | | |
+ * +----------->(ENABLED|READY)----------+
+ *
+ */
+
+/*
+ * Locking :
+ *
+ * Trace context code does not use any lock. There is a per-cpu circular trace
+ * buffer that has a head, a tail and a current pointer. Each record of this
+ * buffer is of equal length. Before doing anything, trace context code checks
+ * the per-cpu ENABLED bit. Trace buffer is allocated in non-trace context and
+ * it sets this bit only after allocating and setting up the buffer. So trace
+ * context code can't access the buffer till it is set up completely. The
+ * buffer is freed also in non-trace context. The code that frees the buffer is
+ * executed only after the corresponding cpu is powered off. So when this
+ * happens, no trace context code can be running on it. We only need to make
+ * sure that trace context code is not preempted from the cpu in the middle of
+ * accessing the trace buffer. This can be achieved simply by disabling
+ * interrupts temporarily. This approach makes the least assumption about the
+ * state of the callers of tracing functions.
+ *
+ * A single global lock, ftrace_lock protects assignments to all global and
+ * per-cpu trace variables. It does not protect reading of those in some cases.
+ *
+ * More specifically, it protects assignments to:
+ *
* ftrace_state
* cpu[N]->cpu_ftrace.ftd_state
- * cpu[N]->cpu_ftrace.ftd_cur
* cpu[N]->cpu_ftrace.ftd_first
* cpu[N]->cpu_ftrace.ftd_last
- * Does _not_ protect readers of cpu[N]->cpu_ftrace.ftd_state.
- * Does not protect reading the FTRACE_READY bit in ftrace_state,
- * since non-READY to READY is a stable transition. This is used
- * to ensure ftrace_init() has been called.
+ *
+ * Does _not_ protect reading of cpu[N]->cpu_ftrace.ftd_state
+ * Does _not_ protect cpu[N]->cpu_ftrace.ftd_cur
+ * Does _not_ protect reading of ftrace_state
*/
static kmutex_t ftrace_lock;
@@ -119,11 +172,18 @@ ftrace_cpu_fini(int cpuid)
return;
/*
- * Do not free mutex and the the trace buffer once they are
- * allocated. A thread, preempted from the now powered-off CPU
- * may be holding the mutex and in the middle of adding a trace
- * record.
+ * This cpu is powered off and no code can be executing on it. So
+ * we can simply finish our cleanup. There is no need for a xcall
+ * to make sure that this cpu is out of trace context.
+ *
+ * The cpu structure will be cleared soon. But, for the sake of
+ * debugging, clear our pointers and state.
*/
+ if (ftd->ftd_first != NULL) {
+ kmem_free(ftd->ftd_first,
+ ftrace_nent * sizeof (ftrace_record_t));
+ }
+ bzero(ftd, sizeof (ftrace_data_t));
}
static void
@@ -140,11 +200,19 @@ ftrace_cpu_start(int cpuid)
if (ftd->ftd_first == NULL) {
ftrace_record_t *ptrs;
- mutex_init(&ftd->ftd_mutex, NULL, MUTEX_DEFAULT, NULL);
mutex_exit(&ftrace_lock);
ptrs = kmem_zalloc(ftrace_nent *
sizeof (ftrace_record_t), KM_SLEEP);
mutex_enter(&ftrace_lock);
+ if (ftd->ftd_first != NULL) {
+ /*
+ * Someone else beat us to it. The winner will
+ * set up the pointers and the state.
+ */
+ kmem_free(ptrs,
+ ftrace_nent * sizeof (ftrace_record_t));
+ return;
+ }
ftd->ftd_first = ptrs;
ftd->ftd_last = ptrs + (ftrace_nent - 1);
@@ -232,6 +300,9 @@ ftrace_init(void)
(void) ftrace_start();
}
+/*
+ * Called from uadmin ioctl, or via mp_init_table[] during boot.
+ */
int
ftrace_start(void)
{
@@ -250,6 +321,9 @@ ftrace_start(void)
return (was_enabled);
}
+/*
+ * Called from uadmin ioctl, to stop tracing.
+ */
int
ftrace_stop(void)
{
@@ -269,102 +343,146 @@ ftrace_stop(void)
return (was_enabled);
}
+/*
+ * ftrace_X() functions are called from trace context. All callers of ftrace_X()
+ * tests FTRACE_ENABLED first. Although this is not very accurate, it keeps the
+ * overhead very low when tracing is not enabled.
+ *
+ * gethrtime_unscaled() appears to be safe to be called in trace context. As an
+ * added precaution, we call these before we disable interrupts on this cpu.
+ */
+
void
-ftrace_0(char *str)
+ftrace_0(char *str, caddr_t caller)
{
ftrace_record_t *r;
- struct cpu *cp = CPU;
- ftrace_data_t *ftd = &cp->cpu_ftrace;
-
- if (mutex_tryenter(&ftd->ftd_mutex) == 0) {
- if (CPU_ON_INTR(cp))
- return;
- else
- mutex_enter(&ftd->ftd_mutex);
+ struct cpu *cp;
+ ftrace_data_t *ftd;
+ ftrace_icookie_t cookie;
+ hrtime_t timestamp;
+
+ timestamp = gethrtime_unscaled();
+
+ cookie = ftrace_interrupt_disable();
+
+ cp = CPU;
+ ftd = &cp->cpu_ftrace;
+
+ if (!(ftd->ftd_state & FTRACE_ENABLED)) {
+ ftrace_interrupt_enable(cookie);
+ return;
}
+
r = ftd->ftd_cur;
r->ftr_event = str;
r->ftr_thread = curthread;
- r->ftr_tick = gethrtime_unscaled();
- r->ftr_caller = caller();
+ r->ftr_tick = timestamp;
+ r->ftr_caller = caller;
if (r++ == ftd->ftd_last)
r = ftd->ftd_first;
ftd->ftd_cur = r;
- mutex_exit(&ftd->ftd_mutex);
+
+ ftrace_interrupt_enable(cookie);
}
void
-ftrace_1(char *str, ulong_t arg1)
+ftrace_1(char *str, ulong_t arg1, caddr_t caller)
{
ftrace_record_t *r;
- struct cpu *cp = CPU;
- ftrace_data_t *ftd = &cp->cpu_ftrace;
-
- if (mutex_tryenter(&ftd->ftd_mutex) == 0) {
- if (CPU_ON_INTR(cp))
- return;
- else
- mutex_enter(&ftd->ftd_mutex);
+ struct cpu *cp;
+ ftrace_data_t *ftd;
+ ftrace_icookie_t cookie;
+ hrtime_t timestamp;
+
+ timestamp = gethrtime_unscaled();
+
+ cookie = ftrace_interrupt_disable();
+
+ cp = CPU;
+ ftd = &cp->cpu_ftrace;
+
+ if (!(ftd->ftd_state & FTRACE_ENABLED)) {
+ ftrace_interrupt_enable(cookie);
+ return;
}
+
r = ftd->ftd_cur;
r->ftr_event = str;
r->ftr_thread = curthread;
- r->ftr_tick = gethrtime_unscaled();
- r->ftr_caller = caller();
+ r->ftr_tick = timestamp;
+ r->ftr_caller = caller;
r->ftr_data1 = arg1;
if (r++ == ftd->ftd_last)
r = ftd->ftd_first;
ftd->ftd_cur = r;
- mutex_exit(&ftd->ftd_mutex);
+
+ ftrace_interrupt_enable(cookie);
}
void
-ftrace_2(char *str, ulong_t arg1, ulong_t arg2)
+ftrace_2(char *str, ulong_t arg1, ulong_t arg2, caddr_t caller)
{
ftrace_record_t *r;
- struct cpu *cp = CPU;
- ftrace_data_t *ftd = &cp->cpu_ftrace;
-
- if (mutex_tryenter(&ftd->ftd_mutex) == 0) {
- if (CPU_ON_INTR(cp))
- return;
- else
- mutex_enter(&ftd->ftd_mutex);
+ struct cpu *cp;
+ ftrace_data_t *ftd;
+ ftrace_icookie_t cookie;
+ hrtime_t timestamp;
+
+ timestamp = gethrtime_unscaled();
+
+ cookie = ftrace_interrupt_disable();
+
+ cp = CPU;
+ ftd = &cp->cpu_ftrace;
+
+ if (!(ftd->ftd_state & FTRACE_ENABLED)) {
+ ftrace_interrupt_enable(cookie);
+ return;
}
+
r = ftd->ftd_cur;
r->ftr_event = str;
r->ftr_thread = curthread;
- r->ftr_tick = gethrtime_unscaled();
- r->ftr_caller = caller();
+ r->ftr_tick = timestamp;
+ r->ftr_caller = caller;
r->ftr_data1 = arg1;
r->ftr_data2 = arg2;
if (r++ == ftd->ftd_last)
r = ftd->ftd_first;
ftd->ftd_cur = r;
- mutex_exit(&ftd->ftd_mutex);
+
+ ftrace_interrupt_enable(cookie);
}
void
-ftrace_3(char *str, ulong_t arg1, ulong_t arg2, ulong_t arg3)
+ftrace_3(char *str, ulong_t arg1, ulong_t arg2, ulong_t arg3, caddr_t caller)
{
ftrace_record_t *r;
- struct cpu *cp = CPU;
- ftrace_data_t *ftd = &cp->cpu_ftrace;
-
- if (mutex_tryenter(&ftd->ftd_mutex) == 0) {
- if (CPU_ON_INTR(cp))
- return;
- else
- mutex_enter(&ftd->ftd_mutex);
+ struct cpu *cp;
+ ftrace_data_t *ftd;
+ ftrace_icookie_t cookie;
+ hrtime_t timestamp;
+
+ timestamp = gethrtime_unscaled();
+
+ cookie = ftrace_interrupt_disable();
+
+ cp = CPU;
+ ftd = &cp->cpu_ftrace;
+
+ if (!(ftd->ftd_state & FTRACE_ENABLED)) {
+ ftrace_interrupt_enable(cookie);
+ return;
}
+
r = ftd->ftd_cur;
r->ftr_event = str;
r->ftr_thread = curthread;
- r->ftr_tick = gethrtime_unscaled();
- r->ftr_caller = caller();
+ r->ftr_tick = timestamp;
+ r->ftr_caller = caller;
r->ftr_data1 = arg1;
r->ftr_data2 = arg2;
r->ftr_data3 = arg3;
@@ -372,27 +490,34 @@ ftrace_3(char *str, ulong_t arg1, ulong_t arg2, ulong_t arg3)
if (r++ == ftd->ftd_last)
r = ftd->ftd_first;
ftd->ftd_cur = r;
- mutex_exit(&ftd->ftd_mutex);
+
+ ftrace_interrupt_enable(cookie);
}
void
-ftrace_3_notick(char *str, ulong_t arg1, ulong_t arg2, ulong_t arg3)
+ftrace_3_notick(char *str, ulong_t arg1, ulong_t arg2,
+ ulong_t arg3, caddr_t caller)
{
ftrace_record_t *r;
- struct cpu *cp = CPU;
- ftrace_data_t *ftd = &cp->cpu_ftrace;
-
- if (mutex_tryenter(&ftd->ftd_mutex) == 0) {
- if (CPU_ON_INTR(cp))
- return;
- else
- mutex_enter(&ftd->ftd_mutex);
+ struct cpu *cp;
+ ftrace_data_t *ftd;
+ ftrace_icookie_t cookie;
+
+ cookie = ftrace_interrupt_disable();
+
+ cp = CPU;
+ ftd = &cp->cpu_ftrace;
+
+ if (!(ftd->ftd_state & FTRACE_ENABLED)) {
+ ftrace_interrupt_enable(cookie);
+ return;
}
+
r = ftd->ftd_cur;
r->ftr_event = str;
r->ftr_thread = curthread;
r->ftr_tick = 0;
- r->ftr_caller = caller();
+ r->ftr_caller = caller;
r->ftr_data1 = arg1;
r->ftr_data2 = arg2;
r->ftr_data3 = arg3;
@@ -400,5 +525,6 @@ ftrace_3_notick(char *str, ulong_t arg1, ulong_t arg2, ulong_t arg3)
if (r++ == ftd->ftd_last)
r = ftd->ftd_first;
ftd->ftd_cur = r;
- mutex_exit(&ftd->ftd_mutex);
+
+ ftrace_interrupt_enable(cookie);
}