trace_irqsoff.c 18.2 KB
Newer Older
1
/*
2
 * trace irqs off critical timings
3
4
5
6
7
8
9
 *
 * 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
10
 *  Copyright (C) 2004 Nadia Yvette Chambers
11
12
13
14
15
16
17
18
19
20
21
 */
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ftrace.h>

#include "trace.h"

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

22
23
static DEFINE_PER_CPU(int, tracing_cpu);

24
static DEFINE_RAW_SPINLOCK(max_trace_lock);
25

26
27
28
29
30
31
32
enum {
	TRACER_IRQS_OFF		= (1 << 1),
	TRACER_PREEMPT_OFF	= (1 << 2),
};

static int trace_type __read_mostly;

33
static int save_flags;
34

35
36
37
static void stop_irqsoff_tracer(struct trace_array *tr, int graph);
static int start_irqsoff_tracer(struct trace_array *tr, int graph);

38
#ifdef CONFIG_PREEMPT_TRACER
Ingo Molnar's avatar
Ingo Molnar committed
39
static inline int
40
41
42
43
44
45
46
47
48
preempt_trace(void)
{
	return ((trace_type & TRACER_PREEMPT_OFF) && preempt_count());
}
#else
# define preempt_trace() (0)
#endif

#ifdef CONFIG_IRQSOFF_TRACER
Ingo Molnar's avatar
Ingo Molnar committed
49
static inline int
50
51
52
53
54
55
56
57
58
irq_trace(void)
{
	return ((trace_type & TRACER_IRQS_OFF) &&
		irqs_disabled());
}
#else
# define irq_trace() (0)
#endif

59
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
60
static int irqsoff_display_graph(struct trace_array *tr, int set);
61
# define is_graph(tr) ((tr)->trace_flags & TRACE_ITER_DISPLAY_GRAPH)
62
63
64
65
66
#else
static inline int irqsoff_display_graph(struct trace_array *tr, int set)
{
	return -EINVAL;
}
67
# define is_graph(tr) false
68
69
#endif

70
71
72
73
74
/*
 * 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
Lucas De Marchi's avatar
Lucas De Marchi committed
75
 * and what happens together happens separately as well, so this doesn't
76
77
78
79
 * decrease the validity of the maximum found:
 */
static __cacheline_aligned_in_smp	unsigned long max_sequence;

80
#ifdef CONFIG_FUNCTION_TRACER
81
/*
82
83
84
85
86
87
88
89
90
91
92
 * Prologue for the preempt and irqs off function tracers.
 *
 * Returns 1 if it is OK to continue, and data->disabled is
 *            incremented.
 *         0 if the trace is to be ignored, and data->disabled
 *            is kept the same.
 *
 * Note, this function is also used outside this ifdef but
 *  inside the #ifdef of the function graph tracer below.
 *  This is OK, since the function graph tracer is
 *  dependent on the function tracer.
93
 */
94
95
96
static int func_prolog_dec(struct trace_array *tr,
			   struct trace_array_cpu **data,
			   unsigned long *flags)
97
98
99
100
{
	long disabled;
	int cpu;

101
102
103
104
105
106
107
108
	/*
	 * 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)))
109
		return 0;
110

111
	local_save_flags(*flags);
112
113
114
115
116
117
	/*
	 * Slight chance to get a false positive on tracing_cpu,
	 * although I'm starting to think there isn't a chance.
	 * Leave this for now just to be paranoid.
	 */
	if (!irqs_disabled_flags(*flags) && !preempt_count())
118
		return 0;
119

120
	*data = per_cpu_ptr(tr->trace_buffer.data, cpu);
121
	disabled = atomic_inc_return(&(*data)->disabled);
122
123

	if (likely(disabled == 1))
124
125
126
127
128
129
130
131
132
133
134
		return 1;

	atomic_dec(&(*data)->disabled);

	return 0;
}

/*
 * irqsoff uses its own tracer function to keep the overhead down:
 */
static void
135
irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip,
136
		    struct ftrace_ops *op, struct pt_regs *pt_regs)
137
138
139
140
141
142
143
144
145
{
	struct trace_array *tr = irqsoff_trace;
	struct trace_array_cpu *data;
	unsigned long flags;

	if (!func_prolog_dec(tr, &data, &flags))
		return;

	trace_function(tr, ip, parent_ip, flags, preempt_count());
146
147
148

	atomic_dec(&data->disabled);
}
149
#endif /* CONFIG_FUNCTION_TRACER */
150

151
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
152
static int irqsoff_display_graph(struct trace_array *tr, int set)
153
154
155
{
	int cpu;

156
	if (!(is_graph(tr) ^ set))
157
158
159
160
161
162
163
		return 0;

	stop_irqsoff_tracer(irqsoff_trace, !set);

	for_each_possible_cpu(cpu)
		per_cpu(tracing_cpu, cpu) = 0;

164
	tr->max_latency = 0;
165
	tracing_reset_online_cpus(&irqsoff_trace->trace_buffer);
166
167
168
169
170
171
172
173
174
175
176
177

	return start_irqsoff_tracer(irqsoff_trace, set);
}

static int irqsoff_graph_entry(struct ftrace_graph_ent *trace)
{
	struct trace_array *tr = irqsoff_trace;
	struct trace_array_cpu *data;
	unsigned long flags;
	int ret;
	int pc;

178
179
180
181
182
183
184
185
186
187
188
189
	if (ftrace_graph_ignore_func(trace))
		return 0;
	/*
	 * Do not trace a function if it's filtered by set_graph_notrace.
	 * Make the index of ret stack negative to indicate that it should
	 * ignore further functions.  But it needs its own ret stack entry
	 * to recover the original index in order to continue tracing after
	 * returning from the function.
	 */
	if (ftrace_graph_notrace_addr(trace->func))
		return 1;

190
	if (!func_prolog_dec(tr, &data, &flags))
191
192
		return 0;

193
194
	pc = preempt_count();
	ret = __trace_graph_entry(tr, trace, flags, pc);
195
	atomic_dec(&data->disabled);
196

197
198
199
200
201
202
203
204
205
206
	return ret;
}

static void irqsoff_graph_return(struct ftrace_graph_ret *trace)
{
	struct trace_array *tr = irqsoff_trace;
	struct trace_array_cpu *data;
	unsigned long flags;
	int pc;

207
	if (!func_prolog_dec(tr, &data, &flags))
208
209
		return;

210
211
	pc = preempt_count();
	__trace_graph_return(tr, trace, flags, pc);
212
213
214
215
216
	atomic_dec(&data->disabled);
}

static void irqsoff_trace_open(struct trace_iterator *iter)
{
217
	if (is_graph(iter->tr))
218
219
220
221
222
223
224
225
226
227
228
		graph_trace_open(iter);

}

static void irqsoff_trace_close(struct trace_iterator *iter)
{
	if (iter->private)
		graph_trace_close(iter);
}

#define GRAPH_TRACER_FLAGS (TRACE_GRAPH_PRINT_CPU | \
229
230
231
			    TRACE_GRAPH_PRINT_PROC | \
			    TRACE_GRAPH_PRINT_ABS_TIME | \
			    TRACE_GRAPH_PRINT_DURATION)
232
233
234
235
236
237
238

static enum print_line_t irqsoff_print_line(struct trace_iterator *iter)
{
	/*
	 * In graph mode call the graph tracer output function,
	 * otherwise go with the TRACE_FN event handler
	 */
239
	if (is_graph(iter->tr))
240
		return print_graph_function_flags(iter, GRAPH_TRACER_FLAGS);
241
242
243
244
245
246

	return TRACE_TYPE_UNHANDLED;
}

static void irqsoff_print_header(struct seq_file *s)
{
247
248
249
	struct trace_array *tr = irqsoff_trace;

	if (is_graph(tr))
250
251
		print_graph_headers_flags(s, GRAPH_TRACER_FLAGS);
	else
252
253
254
255
256
257
258
259
		trace_default_header(s);
}

static void
__trace_function(struct trace_array *tr,
		 unsigned long ip, unsigned long parent_ip,
		 unsigned long flags, int pc)
{
260
	if (is_graph(tr))
261
262
		trace_graph_function(tr, ip, parent_ip, flags, pc);
	else
263
264
265
266
267
268
		trace_function(tr, ip, parent_ip, flags, pc);
}

#else
#define __trace_function trace_function

269
#ifdef CONFIG_FUNCTION_TRACER
270
271
272
273
static int irqsoff_graph_entry(struct ftrace_graph_ent *trace)
{
	return -1;
}
274
#endif
275
276
277
278
279
280
281
282

static enum print_line_t irqsoff_print_line(struct trace_iterator *iter)
{
	return TRACE_TYPE_UNHANDLED;
}

static void irqsoff_trace_open(struct trace_iterator *iter) { }
static void irqsoff_trace_close(struct trace_iterator *iter) { }
283
284

#ifdef CONFIG_FUNCTION_TRACER
285
static void irqsoff_graph_return(struct ftrace_graph_ret *trace) { }
286
287
288
289
290
291
292
293
294
295
static void irqsoff_print_header(struct seq_file *s)
{
	trace_default_header(s);
}
#else
static void irqsoff_print_header(struct seq_file *s)
{
	trace_latency_header(s);
}
#endif /* CONFIG_FUNCTION_TRACER */
296
297
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */

298
299
300
/*
 * Should this new latency be reported/recorded?
 */
301
static bool report_latency(struct trace_array *tr, u64 delta)
302
303
304
{
	if (tracing_thresh) {
		if (delta < tracing_thresh)
305
			return false;
306
	} else {
307
		if (delta <= tr->max_latency)
308
			return false;
309
	}
310
	return true;
311
312
}

Ingo Molnar's avatar
Ingo Molnar committed
313
static void
314
315
316
317
318
check_critical_timing(struct trace_array *tr,
		      struct trace_array_cpu *data,
		      unsigned long parent_ip,
		      int cpu)
{
319
	u64 T0, T1, delta;
320
	unsigned long flags;
321
	int pc;
322
323

	T0 = data->preempt_timestamp;
324
	T1 = ftrace_now(cpu);
325
326
327
328
	delta = T1-T0;

	local_save_flags(flags);

329
330
	pc = preempt_count();

331
	if (!report_latency(tr, delta))
332
333
		goto out;

334
	raw_spin_lock_irqsave(&max_trace_lock, flags);
335

336
	/* check if we are still the max latency */
337
	if (!report_latency(tr, delta))
338
339
		goto out_unlock;

340
	__trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);
341
342
	/* Skip 5 functions to get to the irq/preempt enable function */
	__trace_stack(tr, flags, 5, pc);
343
344

	if (data->critical_sequence != max_sequence)
345
		goto out_unlock;
346
347
348

	data->critical_end = parent_ip;

349
	if (likely(!is_tracing_stopped())) {
350
		tr->max_latency = delta;
351
352
		update_max_tr_single(tr, current, cpu);
	}
353
354
355

	max_sequence++;

356
out_unlock:
357
	raw_spin_unlock_irqrestore(&max_trace_lock, flags);
358

359
360
out:
	data->critical_sequence = max_sequence;
361
	data->preempt_timestamp = ftrace_now(cpu);
362
	__trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);
363
364
}

Ingo Molnar's avatar
Ingo Molnar committed
365
static inline void
366
367
368
369
370
371
372
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;

373
	if (!tracer_enabled || !tracing_is_enabled())
374
375
		return;

376
377
378
	cpu = raw_smp_processor_id();

	if (per_cpu(tracing_cpu, cpu))
379
380
		return;

381
	data = per_cpu_ptr(tr->trace_buffer.data, cpu);
382

383
	if (unlikely(!data) || atomic_read(&data->disabled))
384
385
386
387
388
		return;

	atomic_inc(&data->disabled);

	data->critical_sequence = max_sequence;
389
	data->preempt_timestamp = ftrace_now(cpu);
390
	data->critical_start = parent_ip ? : ip;
391
392

	local_save_flags(flags);
393

394
	__trace_function(tr, ip, parent_ip, flags, preempt_count());
395

396
	per_cpu(tracing_cpu, cpu) = 1;
397

398
399
400
	atomic_dec(&data->disabled);
}

Ingo Molnar's avatar
Ingo Molnar committed
401
static inline void
402
403
404
405
406
407
408
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;

409
	cpu = raw_smp_processor_id();
410
	/* Always clear the tracing cpu on stopping the trace */
411
412
	if (unlikely(per_cpu(tracing_cpu, cpu)))
		per_cpu(tracing_cpu, cpu) = 0;
413
414
415
	else
		return;

416
	if (!tracer_enabled || !tracing_is_enabled())
417
418
		return;

419
	data = per_cpu_ptr(tr->trace_buffer.data, cpu);
420

421
	if (unlikely(!data) ||
422
423
424
425
	    !data->critical_start || atomic_read(&data->disabled))
		return;

	atomic_inc(&data->disabled);
426

427
	local_save_flags(flags);
428
	__trace_function(tr, ip, parent_ip, flags, preempt_count());
429
	check_critical_timing(tr, data, parent_ip ? : ip, cpu);
430
431
432
433
	data->critical_start = 0;
	atomic_dec(&data->disabled);
}

434
/* start and stop critical timings used to for stoppage (in idle) */
Ingo Molnar's avatar
Ingo Molnar committed
435
void start_critical_timings(void)
436
{
437
	if (preempt_trace() || irq_trace())
438
439
		start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
Ingo Molnar's avatar
Ingo Molnar committed
440
EXPORT_SYMBOL_GPL(start_critical_timings);
441

Ingo Molnar's avatar
Ingo Molnar committed
442
void stop_critical_timings(void)
443
{
444
	if (preempt_trace() || irq_trace())
445
446
		stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
Ingo Molnar's avatar
Ingo Molnar committed
447
EXPORT_SYMBOL_GPL(stop_critical_timings);
448

449
#ifdef CONFIG_IRQSOFF_TRACER
450
#ifdef CONFIG_PROVE_LOCKING
Ingo Molnar's avatar
Ingo Molnar committed
451
void time_hardirqs_on(unsigned long a0, unsigned long a1)
452
{
453
	if (!preempt_trace() && irq_trace())
454
455
456
		stop_critical_timing(a0, a1);
}

Ingo Molnar's avatar
Ingo Molnar committed
457
void time_hardirqs_off(unsigned long a0, unsigned long a1)
458
{
459
	if (!preempt_trace() && irq_trace())
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
		start_critical_timing(a0, a1);
}

#else /* !CONFIG_PROVE_LOCKING */

/*
 * Stubs:
 */

void trace_softirqs_on(unsigned long ip)
{
}

void trace_softirqs_off(unsigned long ip)
{
}

Ingo Molnar's avatar
Ingo Molnar committed
477
inline void print_irqtrace_events(struct task_struct *curr)
478
479
480
481
482
483
{
}

/*
 * We are only interested in hardirq on/off events:
 */
Ingo Molnar's avatar
Ingo Molnar committed
484
void trace_hardirqs_on(void)
485
{
486
	if (ipipe_root_p && !preempt_trace() && irq_trace())
487
488
489
490
		stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
EXPORT_SYMBOL(trace_hardirqs_on);

Ingo Molnar's avatar
Ingo Molnar committed
491
void trace_hardirqs_off(void)
492
{
493
	if (ipipe_root_p && !preempt_trace() && irq_trace())
494
495
496
497
		start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
}
EXPORT_SYMBOL(trace_hardirqs_off);

498
__visible void trace_hardirqs_on_caller(unsigned long caller_addr)
499
{
500
	if (ipipe_root_p && !preempt_trace() && irq_trace())
501
502
503
504
		stop_critical_timing(CALLER_ADDR0, caller_addr);
}
EXPORT_SYMBOL(trace_hardirqs_on_caller);

505
506
507
508
509
510
__visible void trace_hardirqs_on_virt_caller(unsigned long caller_addr)
{
	if (ipipe_root_p && !raw_irqs_disabled())
		trace_hardirqs_on_caller(caller_addr);
}

511
__visible void trace_hardirqs_off_caller(unsigned long caller_addr)
512
{
513
	if (ipipe_root_p && !preempt_trace() && irq_trace())
514
515
516
517
518
		start_critical_timing(CALLER_ADDR0, caller_addr);
}
EXPORT_SYMBOL(trace_hardirqs_off_caller);

#endif /* CONFIG_PROVE_LOCKING */
519
520
521
#endif /*  CONFIG_IRQSOFF_TRACER */

#ifdef CONFIG_PREEMPT_TRACER
Ingo Molnar's avatar
Ingo Molnar committed
522
void trace_preempt_on(unsigned long a0, unsigned long a1)
523
{
524
	if (preempt_trace() && !irq_trace())
525
		stop_critical_timing(a0, a1);
526
527
}

Ingo Molnar's avatar
Ingo Molnar committed
528
void trace_preempt_off(unsigned long a0, unsigned long a1)
529
{
530
	if (preempt_trace() && !irq_trace())
531
		start_critical_timing(a0, a1);
532
533
}
#endif /* CONFIG_PREEMPT_TRACER */
534

535
536
537
#ifdef CONFIG_FUNCTION_TRACER
static bool function_enabled;

538
static int register_irqsoff_function(struct trace_array *tr, int graph, int set)
539
{
540
	int ret;
541

542
	/* 'set' is set if TRACE_ITER_FUNCTION is about to be set */
543
	if (function_enabled || (!set && !(tr->trace_flags & TRACE_ITER_FUNCTION)))
544
545
546
		return 0;

	if (graph)
547
548
		ret = register_ftrace_graph(&irqsoff_graph_return,
					    &irqsoff_graph_entry);
549
	else
550
		ret = register_ftrace_function(tr->ops);
551
552
553
554
555
556
557

	if (!ret)
		function_enabled = true;

	return ret;
}

558
static void unregister_irqsoff_function(struct trace_array *tr, int graph)
559
560
561
562
563
564
565
{
	if (!function_enabled)
		return;

	if (graph)
		unregister_ftrace_graph();
	else
566
		unregister_ftrace_function(tr->ops);
567
568
569
570

	function_enabled = false;
}

571
static int irqsoff_function_set(struct trace_array *tr, u32 mask, int set)
572
{
573
574
575
	if (!(mask & TRACE_ITER_FUNCTION))
		return 0;

576
	if (set)
577
		register_irqsoff_function(tr, is_graph(tr), 1);
578
	else
579
		unregister_irqsoff_function(tr, is_graph(tr));
580
581
582
583
584
	return 1;
}
#else
static int register_irqsoff_function(struct trace_array *tr, int graph, int set)
{
585
	return 0;
586
}
587
588
589
590
591
592
static void unregister_irqsoff_function(struct trace_array *tr, int graph) { }
static inline int irqsoff_function_set(struct trace_array *tr, u32 mask, int set)
{
	return 0;
}
#endif /* CONFIG_FUNCTION_TRACER */
593

594
static int irqsoff_flag_changed(struct trace_array *tr, u32 mask, int set)
595
{
596
597
	struct tracer *tracer = tr->current_trace;

598
599
	if (irqsoff_function_set(tr, mask, set))
		return 0;
600

601
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
602
603
	if (mask & TRACE_ITER_DISPLAY_GRAPH)
		return irqsoff_display_graph(tr, set);
604
#endif
605
606
607
608
609
610
611
612

	return trace_keep_overwrite(tracer, mask, set);
}

static int start_irqsoff_tracer(struct trace_array *tr, int graph)
{
	int ret;

613
	ret = register_irqsoff_function(tr, graph, 0);
614
615

	if (!ret && tracing_is_enabled())
616
		tracer_enabled = 1;
617
	else
618
		tracer_enabled = 0;
619
620

	return ret;
621
622
}

623
static void stop_irqsoff_tracer(struct trace_array *tr, int graph)
624
625
{
	tracer_enabled = 0;
626

627
	unregister_irqsoff_function(tr, graph);
628
629
}

630
631
632
static bool irqsoff_busy;

static int __irqsoff_tracer_init(struct trace_array *tr)
633
{
634
635
636
	if (irqsoff_busy)
		return -EBUSY;

637
	save_flags = tr->trace_flags;
638
639

	/* non overwrite screws up the latency tracers */
640
641
	set_tracer_flag(tr, TRACE_ITER_OVERWRITE, 1);
	set_tracer_flag(tr, TRACE_ITER_LATENCY_FMT, 1);
642

643
	tr->max_latency = 0;
644
	irqsoff_trace = tr;
645
	/* make sure that the tracer is visible */
646
	smp_wmb();
647

648
649
650
651
	ftrace_init_array_ops(tr, irqsoff_tracer_call);

	/* Only toplevel instance supports graph tracing */
	if (start_irqsoff_tracer(tr, (tr->flags & TRACE_ARRAY_FL_GLOBAL &&
652
				      is_graph(tr))))
653
		printk(KERN_ERR "failed to start irqsoff tracer\n");
654
655
656

	irqsoff_busy = true;
	return 0;
657
658
659
660
}

static void irqsoff_tracer_reset(struct trace_array *tr)
{
661
662
663
	int lat_flag = save_flags & TRACE_ITER_LATENCY_FMT;
	int overwrite_flag = save_flags & TRACE_ITER_OVERWRITE;

664
	stop_irqsoff_tracer(tr, is_graph(tr));
665

666
667
	set_tracer_flag(tr, TRACE_ITER_LATENCY_FMT, lat_flag);
	set_tracer_flag(tr, TRACE_ITER_OVERWRITE, overwrite_flag);
668
	ftrace_reset_array_ops(tr);
669
670

	irqsoff_busy = false;
671
672
}

673
674
675
676
677
678
679
680
static void irqsoff_tracer_start(struct trace_array *tr)
{
	tracer_enabled = 1;
}

static void irqsoff_tracer_stop(struct trace_array *tr)
{
	tracer_enabled = 0;
681
682
}

683
#ifdef CONFIG_IRQSOFF_TRACER
684
static int irqsoff_tracer_init(struct trace_array *tr)
685
686
687
{
	trace_type = TRACER_IRQS_OFF;

688
	return __irqsoff_tracer_init(tr);
689
}
690
691
692
693
694
static struct tracer irqsoff_tracer __read_mostly =
{
	.name		= "irqsoff",
	.init		= irqsoff_tracer_init,
	.reset		= irqsoff_tracer_reset,
695
696
	.start		= irqsoff_tracer_start,
	.stop		= irqsoff_tracer_stop,
697
	.print_max	= true,
698
699
	.print_header   = irqsoff_print_header,
	.print_line     = irqsoff_print_line,
700
	.flag_changed	= irqsoff_flag_changed,
Steven Rostedt's avatar
Steven Rostedt committed
701
702
703
#ifdef CONFIG_FTRACE_SELFTEST
	.selftest    = trace_selftest_startup_irqsoff,
#endif
704
705
	.open           = irqsoff_trace_open,
	.close          = irqsoff_trace_close,
706
	.allow_instances = true,
707
	.use_max_tr	= true,
708
};
709
710
711
712
713
714
# define register_irqsoff(trace) register_tracer(&trace)
#else
# define register_irqsoff(trace) do { } while (0)
#endif

#ifdef CONFIG_PREEMPT_TRACER
715
static int preemptoff_tracer_init(struct trace_array *tr)
716
717
718
{
	trace_type = TRACER_PREEMPT_OFF;

719
	return __irqsoff_tracer_init(tr);
720
721
722
723
724
725
726
}

static struct tracer preemptoff_tracer __read_mostly =
{
	.name		= "preemptoff",
	.init		= preemptoff_tracer_init,
	.reset		= irqsoff_tracer_reset,
727
728
	.start		= irqsoff_tracer_start,
	.stop		= irqsoff_tracer_stop,
729
	.print_max	= true,
730
731
	.print_header   = irqsoff_print_header,
	.print_line     = irqsoff_print_line,
732
	.flag_changed	= irqsoff_flag_changed,
Steven Rostedt's avatar
Steven Rostedt committed
733
734
735
#ifdef CONFIG_FTRACE_SELFTEST
	.selftest    = trace_selftest_startup_preemptoff,
#endif
736
737
	.open		= irqsoff_trace_open,
	.close		= irqsoff_trace_close,
738
	.allow_instances = true,
739
	.use_max_tr	= true,
740
741
742
743
744
745
746
747
748
};
# 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)

749
static int preemptirqsoff_tracer_init(struct trace_array *tr)
750
751
752
{
	trace_type = TRACER_IRQS_OFF | TRACER_PREEMPT_OFF;

753
	return __irqsoff_tracer_init(tr);
754
755
756
757
758
759
760
}

static struct tracer preemptirqsoff_tracer __read_mostly =
{
	.name		= "preemptirqsoff",
	.init		= preemptirqsoff_tracer_init,
	.reset		= irqsoff_tracer_reset,
761
762
	.start		= irqsoff_tracer_start,
	.stop		= irqsoff_tracer_stop,
763
	.print_max	= true,
764
765
	.print_header   = irqsoff_print_header,
	.print_line     = irqsoff_print_line,
766
	.flag_changed	= irqsoff_flag_changed,
Steven Rostedt's avatar
Steven Rostedt committed
767
768
769
#ifdef CONFIG_FTRACE_SELFTEST
	.selftest    = trace_selftest_startup_preemptirqsoff,
#endif
770
771
	.open		= irqsoff_trace_open,
	.close		= irqsoff_trace_close,
772
	.allow_instances = true,
773
	.use_max_tr	= true,
774
775
776
777
778
779
};

# define register_preemptirqsoff(trace) register_tracer(&trace)
#else
# define register_preemptirqsoff(trace) do { } while (0)
#endif
780
781
782

__init static int init_irqsoff_tracer(void)
{
783
784
785
	register_irqsoff(irqsoff_tracer);
	register_preemptoff(preemptoff_tracer);
	register_preemptirqsoff(preemptirqsoff_tracer);
786
787
788

	return 0;
}
789
core_initcall(init_irqsoff_tracer);