trace_irqsoff.c 10.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * trace irqs off criticall timings
 *
 * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
 * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
 *
 * From code in the latency_tracer, that is:
 *
 *  Copyright (C) 2004-2006 Ingo Molnar
 *  Copyright (C) 2004 William Lee Irwin III
 */
#include <linux/kallsyms.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ftrace.h>
#include <linux/fs.h>

#include "trace.h"

static struct trace_array		*irqsoff_trace __read_mostly;
static int				tracer_enabled __read_mostly;

24 25
static DEFINE_PER_CPU(int, tracing_cpu);

26 27
static DEFINE_SPINLOCK(max_trace_lock);

28 29 30 31 32 33 34 35
enum {
	TRACER_IRQS_OFF		= (1 << 1),
	TRACER_PREEMPT_OFF	= (1 << 2),
};

static int trace_type __read_mostly;

#ifdef CONFIG_PREEMPT_TRACER
I
Ingo Molnar 已提交
36
static inline int
37 38 39 40 41 42 43 44 45
preempt_trace(void)
{
	return ((trace_type & TRACER_PREEMPT_OFF) && preempt_count());
}
#else
# define preempt_trace() (0)
#endif

#ifdef CONFIG_IRQSOFF_TRACER
I
Ingo Molnar 已提交
46
static inline int
47 48 49 50 51 52 53 54 55
irq_trace(void)
{
	return ((trace_type & TRACER_IRQS_OFF) &&
		irqs_disabled());
}
#else
# define irq_trace() (0)
#endif

56 57 58 59 60 61 62 63 64 65 66 67 68 69
/*
 * Sequence count - we record it when starting a measurement and
 * skip the latency if the sequence has changed - some other section
 * did a maximum and could disturb our measurement with serial console
 * printouts, etc. Truly coinciding maximum latencies should be rare
 * and what happens together happens separately as well, so this doesnt
 * decrease the validity of the maximum found:
 */
static __cacheline_aligned_in_smp	unsigned long max_sequence;

#ifdef CONFIG_FTRACE
/*
 * irqsoff uses its own tracer function to keep the overhead down:
 */
I
Ingo Molnar 已提交
70
static void
71 72 73 74 75 76 77 78
irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip)
{
	struct trace_array *tr = irqsoff_trace;
	struct trace_array_cpu *data;
	unsigned long flags;
	long disabled;
	int cpu;

79 80 81 82 83 84 85 86
	/*
	 * Does not matter if we preempt. We test the flags
	 * afterward, to see if irqs are disabled or not.
	 * If we preempt and get a false positive, the flags
	 * test will fail.
	 */
	cpu = raw_smp_processor_id();
	if (likely(!per_cpu(tracing_cpu, cpu)))
87 88 89
		return;

	local_save_flags(flags);
90 91 92
	/* slight chance to get a false positive on tracing_cpu */
	if (!irqs_disabled_flags(flags))
		return;
93 94 95 96 97

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

	if (likely(disabled == 1))
98
		trace_function(tr, data, ip, parent_ip, flags);
99 100 101 102 103 104 105 106 107 108 109 110 111

	atomic_dec(&data->disabled);
}

static struct ftrace_ops trace_ops __read_mostly =
{
	.func = irqsoff_tracer_call,
};
#endif /* CONFIG_FTRACE */

/*
 * Should this new latency be reported/recorded?
 */
I
Ingo Molnar 已提交
112
static int report_latency(cycle_t delta)
113 114 115 116 117 118 119 120 121 122 123
{
	if (tracing_thresh) {
		if (delta < tracing_thresh)
			return 0;
	} else {
		if (delta <= tracing_max_latency)
			return 0;
	}
	return 1;
}

I
Ingo Molnar 已提交
124
static void
125 126 127 128 129 130
check_critical_timing(struct trace_array *tr,
		      struct trace_array_cpu *data,
		      unsigned long parent_ip,
		      int cpu)
{
	unsigned long latency, t0, t1;
131
	cycle_t T0, T1, delta;
132 133 134 135 136 137 138
	unsigned long flags;

	/*
	 * usecs conversion is slow so we try to delay the conversion
	 * as long as possible:
	 */
	T0 = data->preempt_timestamp;
I
Ingo Molnar 已提交
139
	T1 = ftrace_now(cpu);
140 141 142 143 144 145 146
	delta = T1-T0;

	local_save_flags(flags);

	if (!report_latency(delta))
		goto out;

I
Ingo Molnar 已提交
147
	spin_lock_irqsave(&max_trace_lock, flags);
148

149 150 151 152
	/* check if we are still the max latency */
	if (!report_latency(delta))
		goto out_unlock;

153
	trace_function(tr, data, CALLER_ADDR0, parent_ip, flags);
154 155 156 157

	latency = nsecs_to_usecs(delta);

	if (data->critical_sequence != max_sequence)
158
		goto out_unlock;
159 160 161 162 163 164 165 166 167

	tracing_max_latency = delta;
	t0 = nsecs_to_usecs(T0);
	t1 = nsecs_to_usecs(T1);

	data->critical_end = parent_ip;

	update_max_tr_single(tr, current, cpu);

I
Ingo Molnar 已提交
168 169 170
	if (tracing_thresh) {
		printk(KERN_INFO "(%16s-%-5d|#%d):"
			" %lu us critical section violates %lu us threshold.\n",
171 172
				current->comm, current->pid,
				raw_smp_processor_id(),
I
Ingo Molnar 已提交
173 174
				latency, nsecs_to_usecs(tracing_thresh));
	} else {
175
		printk(KERN_INFO "(%16s-%-5d|#%d):"
I
Ingo Molnar 已提交
176
		       " new %lu us maximum-latency critical section.\n",
177 178
				current->comm, current->pid,
				raw_smp_processor_id(),
I
Ingo Molnar 已提交
179 180
				latency);
	}
181 182 183

	max_sequence++;

184
out_unlock:
I
Ingo Molnar 已提交
185
	spin_unlock_irqrestore(&max_trace_lock, flags);
186

187 188
out:
	data->critical_sequence = max_sequence;
I
Ingo Molnar 已提交
189
	data->preempt_timestamp = ftrace_now(cpu);
190
	tracing_reset(data);
191
	trace_function(tr, data, CALLER_ADDR0, parent_ip, flags);
192 193
}

I
Ingo Molnar 已提交
194
static inline void
195 196 197 198 199 200 201 202 203 204
start_critical_timing(unsigned long ip, unsigned long parent_ip)
{
	int cpu;
	struct trace_array *tr = irqsoff_trace;
	struct trace_array_cpu *data;
	unsigned long flags;

	if (likely(!tracer_enabled))
		return;

205 206 207
	if (__get_cpu_var(tracing_cpu))
		return;

208 209 210
	cpu = raw_smp_processor_id();
	data = tr->data[cpu];

I
Ingo Molnar 已提交
211
	if (unlikely(!data) || unlikely(!head_page(data)) ||
212
	    atomic_read(&data->disabled))
213 214 215 216 217
		return;

	atomic_inc(&data->disabled);

	data->critical_sequence = max_sequence;
I
Ingo Molnar 已提交
218
	data->preempt_timestamp = ftrace_now(cpu);
219
	data->critical_start = parent_ip ? : ip;
220 221 222
	tracing_reset(data);

	local_save_flags(flags);
223

224
	trace_function(tr, data, ip, parent_ip, flags);
225

226 227
	__get_cpu_var(tracing_cpu) = 1;

228 229 230
	atomic_dec(&data->disabled);
}

I
Ingo Molnar 已提交
231
static inline void
232 233 234 235 236 237 238
stop_critical_timing(unsigned long ip, unsigned long parent_ip)
{
	int cpu;
	struct trace_array *tr = irqsoff_trace;
	struct trace_array_cpu *data;
	unsigned long flags;

239 240 241 242 243 244 245
	/* Always clear the tracing cpu on stopping the trace */
	if (unlikely(__get_cpu_var(tracing_cpu)))
		__get_cpu_var(tracing_cpu) = 0;
	else
		return;

	if (!tracer_enabled)
246 247 248 249 250
		return;

	cpu = raw_smp_processor_id();
	data = tr->data[cpu];

I
Ingo Molnar 已提交
251
	if (unlikely(!data) || unlikely(!head_page(data)) ||
252 253 254 255 256
	    !data->critical_start || atomic_read(&data->disabled))
		return;

	atomic_inc(&data->disabled);
	local_save_flags(flags);
257
	trace_function(tr, data, ip, parent_ip, flags);
258
	check_critical_timing(tr, data, parent_ip ? : ip, cpu);
259 260 261 262
	data->critical_start = 0;
	atomic_dec(&data->disabled);
}

263
/* start and stop critical timings used to for stoppage (in idle) */
I
Ingo Molnar 已提交
264
void start_critical_timings(void)
265
{
266
	if (preempt_trace() || irq_trace())
267 268 269
		start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}

I
Ingo Molnar 已提交
270
void stop_critical_timings(void)
271
{
272
	if (preempt_trace() || irq_trace())
273 274 275
		stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}

276
#ifdef CONFIG_IRQSOFF_TRACER
277
#ifdef CONFIG_PROVE_LOCKING
I
Ingo Molnar 已提交
278
void time_hardirqs_on(unsigned long a0, unsigned long a1)
279
{
280
	if (!preempt_trace() && irq_trace())
281 282 283
		stop_critical_timing(a0, a1);
}

I
Ingo Molnar 已提交
284
void time_hardirqs_off(unsigned long a0, unsigned long a1)
285
{
286
	if (!preempt_trace() && irq_trace())
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
		start_critical_timing(a0, a1);
}

#else /* !CONFIG_PROVE_LOCKING */

/*
 * Stubs:
 */

void early_boot_irqs_off(void)
{
}

void early_boot_irqs_on(void)
{
}

void trace_softirqs_on(unsigned long ip)
{
}

void trace_softirqs_off(unsigned long ip)
{
}

I
Ingo Molnar 已提交
312
inline void print_irqtrace_events(struct task_struct *curr)
313 314 315 316 317 318
{
}

/*
 * We are only interested in hardirq on/off events:
 */
I
Ingo Molnar 已提交
319
void trace_hardirqs_on(void)
320
{
321
	if (!preempt_trace() && irq_trace())
322 323 324 325
		stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
EXPORT_SYMBOL(trace_hardirqs_on);

I
Ingo Molnar 已提交
326
void trace_hardirqs_off(void)
327
{
328
	if (!preempt_trace() && irq_trace())
329 330 331 332
		start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
EXPORT_SYMBOL(trace_hardirqs_off);

I
Ingo Molnar 已提交
333
void trace_hardirqs_on_caller(unsigned long caller_addr)
334
{
335
	if (!preempt_trace() && irq_trace())
336 337 338 339
		stop_critical_timing(CALLER_ADDR0, caller_addr);
}
EXPORT_SYMBOL(trace_hardirqs_on_caller);

I
Ingo Molnar 已提交
340
void trace_hardirqs_off_caller(unsigned long caller_addr)
341
{
342
	if (!preempt_trace() && irq_trace())
343 344 345 346 347
		start_critical_timing(CALLER_ADDR0, caller_addr);
}
EXPORT_SYMBOL(trace_hardirqs_off_caller);

#endif /* CONFIG_PROVE_LOCKING */
348 349 350
#endif /*  CONFIG_IRQSOFF_TRACER */

#ifdef CONFIG_PREEMPT_TRACER
I
Ingo Molnar 已提交
351
void trace_preempt_on(unsigned long a0, unsigned long a1)
352 353 354 355
{
	stop_critical_timing(a0, a1);
}

I
Ingo Molnar 已提交
356
void trace_preempt_off(unsigned long a0, unsigned long a1)
357 358 359 360
{
	start_critical_timing(a0, a1);
}
#endif /* CONFIG_PREEMPT_TRACER */
361 362 363 364

static void start_irqsoff_tracer(struct trace_array *tr)
{
	register_ftrace_function(&trace_ops);
365
	tracer_enabled = 1;
366 367 368 369 370
}

static void stop_irqsoff_tracer(struct trace_array *tr)
{
	tracer_enabled = 0;
371
	unregister_ftrace_function(&trace_ops);
372 373
}

374
static void __irqsoff_tracer_init(struct trace_array *tr)
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397
{
	irqsoff_trace = tr;
	/* make sure that the tracer is visibel */
	smp_wmb();

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

static void irqsoff_tracer_reset(struct trace_array *tr)
{
	if (tr->ctrl)
		stop_irqsoff_tracer(tr);
}

static void irqsoff_tracer_ctrl_update(struct trace_array *tr)
{
	if (tr->ctrl)
		start_irqsoff_tracer(tr);
	else
		stop_irqsoff_tracer(tr);
}

I
Ingo Molnar 已提交
398
static void irqsoff_tracer_open(struct trace_iterator *iter)
399 400 401 402 403 404
{
	/* stop the trace while dumping */
	if (iter->tr->ctrl)
		stop_irqsoff_tracer(iter->tr);
}

I
Ingo Molnar 已提交
405
static void irqsoff_tracer_close(struct trace_iterator *iter)
406 407 408 409 410
{
	if (iter->tr->ctrl)
		start_irqsoff_tracer(iter->tr);
}

411 412 413 414 415 416 417
#ifdef CONFIG_IRQSOFF_TRACER
static void irqsoff_tracer_init(struct trace_array *tr)
{
	trace_type = TRACER_IRQS_OFF;

	__irqsoff_tracer_init(tr);
}
418 419 420 421 422 423 424 425 426
static struct tracer irqsoff_tracer __read_mostly =
{
	.name		= "irqsoff",
	.init		= irqsoff_tracer_init,
	.reset		= irqsoff_tracer_reset,
	.open		= irqsoff_tracer_open,
	.close		= irqsoff_tracer_close,
	.ctrl_update	= irqsoff_tracer_ctrl_update,
	.print_max	= 1,
S
Steven Rostedt 已提交
427 428 429
#ifdef CONFIG_FTRACE_SELFTEST
	.selftest    = trace_selftest_startup_irqsoff,
#endif
430
};
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
# define register_irqsoff(trace) register_tracer(&trace)
#else
# define register_irqsoff(trace) do { } while (0)
#endif

#ifdef CONFIG_PREEMPT_TRACER
static void preemptoff_tracer_init(struct trace_array *tr)
{
	trace_type = TRACER_PREEMPT_OFF;

	__irqsoff_tracer_init(tr);
}

static struct tracer preemptoff_tracer __read_mostly =
{
	.name		= "preemptoff",
	.init		= preemptoff_tracer_init,
	.reset		= irqsoff_tracer_reset,
	.open		= irqsoff_tracer_open,
	.close		= irqsoff_tracer_close,
	.ctrl_update	= irqsoff_tracer_ctrl_update,
	.print_max	= 1,
S
Steven Rostedt 已提交
453 454 455
#ifdef CONFIG_FTRACE_SELFTEST
	.selftest    = trace_selftest_startup_preemptoff,
#endif
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
};
# define register_preemptoff(trace) register_tracer(&trace)
#else
# define register_preemptoff(trace) do { } while (0)
#endif

#if defined(CONFIG_IRQSOFF_TRACER) && \
	defined(CONFIG_PREEMPT_TRACER)

static void preemptirqsoff_tracer_init(struct trace_array *tr)
{
	trace_type = TRACER_IRQS_OFF | TRACER_PREEMPT_OFF;

	__irqsoff_tracer_init(tr);
}

static struct tracer preemptirqsoff_tracer __read_mostly =
{
	.name		= "preemptirqsoff",
	.init		= preemptirqsoff_tracer_init,
	.reset		= irqsoff_tracer_reset,
	.open		= irqsoff_tracer_open,
	.close		= irqsoff_tracer_close,
	.ctrl_update	= irqsoff_tracer_ctrl_update,
	.print_max	= 1,
S
Steven Rostedt 已提交
481 482 483
#ifdef CONFIG_FTRACE_SELFTEST
	.selftest    = trace_selftest_startup_preemptirqsoff,
#endif
484 485 486 487 488 489
};

# define register_preemptirqsoff(trace) register_tracer(&trace)
#else
# define register_preemptirqsoff(trace) do { } while (0)
#endif
490 491 492

__init static int init_irqsoff_tracer(void)
{
493 494 495
	register_irqsoff(irqsoff_tracer);
	register_preemptoff(preemptoff_tracer);
	register_preemptirqsoff(preemptirqsoff_tracer);
496 497 498 499

	return 0;
}
device_initcall(init_irqsoff_tracer);