aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/trace/trace_sched_switch.c
blob: 5555b906a6669b2a2c3892626026d8b68b17f87b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
 * trace context switch
 *
 * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
 *
 */
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
#include <linux/marker.h>
#include <linux/ftrace.h>

#include "trace.h"

static struct trace_array	*ctx_trace;
static int __read_mostly	tracer_enabled;

static void
ctx_switch_func(void *__rq, struct task_struct *prev, struct task_struct *next)
{
	struct trace_array *tr = ctx_trace;
	struct trace_array_cpu *data;
	unsigned long flags;
	long disabled;
	int cpu;

	if (!tracer_enabled)
		return;

	local_irq_save(flags);
	cpu = raw_smp_processor_id();
	data = tr->data[cpu];
	disabled = atomic_inc_return(&data->disabled);

	if (likely(disabled == 1)) {
		tracing_sched_switch_trace(tr, data, prev, next, flags);
		if (trace_flags & TRACE_ITER_SCHED_TREE)
			ftrace_all_fair_tasks(__rq, tr, data);
	}

	atomic_dec(&data->disabled);
	local_irq_restore(flags);
}

static void
wakeup_func(void *__rq, struct task_struct *wakee, struct task_struct *curr)
{
	struct trace_array *tr = ctx_trace;
	struct trace_array_cpu *data;
	unsigned long flags;
	long disabled;
	int cpu;

	if (!tracer_enabled)
		return;

	local_irq_save(flags);
	cpu = raw_smp_processor_id();
	data = tr->data[cpu];
	disabled = atomic_inc_return(&data->disabled);

	if (likely(disabled == 1)) {
		tracing_sched_wakeup_trace(tr, data, wakee, curr, flags);
		if (trace_flags & TRACE_ITER_SCHED_TREE)
			ftrace_all_fair_tasks(__rq, tr, data);
	}

	atomic_dec(&data->disabled);
	local_irq_restore(flags);
}

void
ftrace_ctx_switch(void *__rq, struct task_struct *prev,
		  struct task_struct *next)
{
	tracing_record_cmdline(prev);

	/*
	 * If tracer_switch_func only points to the local
	 * switch func, it still needs the ptr passed to it.
	 */
	ctx_switch_func(__rq, prev, next);

	/*
	 * Chain to the wakeup tracer (this is a NOP if disabled):
	 */
	wakeup_sched_switch(prev, next);
}

void
ftrace_wake_up_task(void *__rq, struct task_struct *wakee,
		    struct task_struct *curr)
{
	tracing_record_cmdline(curr);

	wakeup_func(__rq, wakee, curr);

	/*
	 * Chain to the wakeup tracer (this is a NOP if disabled):
	 */
	wakeup_sched_wakeup(wakee, curr);
}

static void sched_switch_reset(struct trace_array *tr)
{
	int cpu;

	tr->time_start = ftrace_now(tr->cpu);

	for_each_online_cpu(cpu)
		tracing_reset(tr->data[cpu]);
}

static void start_sched_trace(struct trace_array *tr)
{
	sched_switch_reset(tr);
	tracer_enabled = 1;
}

static void stop_sched_trace(struct trace_array *tr)
{
	tracer_enabled = 0;
}

static void sched_switch_trace_init(struct trace_array *tr)
{
	ctx_trace = tr;

	if (tr->ctrl)
		start_sched_trace(tr);
}

static void sched_switch_trace_reset(struct trace_array *tr)
{
	if (tr->ctrl)
		stop_sched_trace(tr);
}

static void sched_switch_trace_ctrl_update(struct trace_array *tr)
{
	/* When starting a new trace, reset the buffers */
	if (tr->ctrl)
		start_sched_trace(tr);
	else
		stop_sched_trace(tr);
}

static struct tracer sched_switch_trace __read_mostly =
{
	.name		= "sched_switch",
	.init		= sched_switch_trace_init,
	.reset		= sched_switch_trace_reset,
	.ctrl_update	= sched_switch_trace_ctrl_update,
#ifdef CONFIG_FTRACE_SELFTEST
	.selftest    = trace_selftest_startup_sched_switch,
#endif
};

__init static int init_sched_switch_trace(void)
{
	return register_tracer(&sched_switch_trace);
}
device_initcall(init_sched_switch_trace);