xref: /openbmc/linux/kernel/trace/trace_hwlat.c (revision 0330f7aa8ee63d0c435c0cb4e47ea06235ee4b7f)
1e7c15cd8SSteven Rostedt (Red Hat) /*
2e7c15cd8SSteven Rostedt (Red Hat)  * trace_hwlatdetect.c - A simple Hardware Latency detector.
3e7c15cd8SSteven Rostedt (Red Hat)  *
4e7c15cd8SSteven Rostedt (Red Hat)  * Use this tracer to detect large system latencies induced by the behavior of
5e7c15cd8SSteven Rostedt (Red Hat)  * certain underlying system hardware or firmware, independent of Linux itself.
6e7c15cd8SSteven Rostedt (Red Hat)  * The code was developed originally to detect the presence of SMIs on Intel
7e7c15cd8SSteven Rostedt (Red Hat)  * and AMD systems, although there is no dependency upon x86 herein.
8e7c15cd8SSteven Rostedt (Red Hat)  *
9e7c15cd8SSteven Rostedt (Red Hat)  * The classical example usage of this tracer is in detecting the presence of
10e7c15cd8SSteven Rostedt (Red Hat)  * SMIs or System Management Interrupts on Intel and AMD systems. An SMI is a
11e7c15cd8SSteven Rostedt (Red Hat)  * somewhat special form of hardware interrupt spawned from earlier CPU debug
12e7c15cd8SSteven Rostedt (Red Hat)  * modes in which the (BIOS/EFI/etc.) firmware arranges for the South Bridge
13e7c15cd8SSteven Rostedt (Red Hat)  * LPC (or other device) to generate a special interrupt under certain
14e7c15cd8SSteven Rostedt (Red Hat)  * circumstances, for example, upon expiration of a special SMI timer device,
15e7c15cd8SSteven Rostedt (Red Hat)  * due to certain external thermal readings, on certain I/O address accesses,
16e7c15cd8SSteven Rostedt (Red Hat)  * and other situations. An SMI hits a special CPU pin, triggers a special
17e7c15cd8SSteven Rostedt (Red Hat)  * SMI mode (complete with special memory map), and the OS is unaware.
18e7c15cd8SSteven Rostedt (Red Hat)  *
19e7c15cd8SSteven Rostedt (Red Hat)  * Although certain hardware-inducing latencies are necessary (for example,
20e7c15cd8SSteven Rostedt (Red Hat)  * a modern system often requires an SMI handler for correct thermal control
21e7c15cd8SSteven Rostedt (Red Hat)  * and remote management) they can wreak havoc upon any OS-level performance
22e7c15cd8SSteven Rostedt (Red Hat)  * guarantees toward low-latency, especially when the OS is not even made
23e7c15cd8SSteven Rostedt (Red Hat)  * aware of the presence of these interrupts. For this reason, we need a
24e7c15cd8SSteven Rostedt (Red Hat)  * somewhat brute force mechanism to detect these interrupts. In this case,
25e7c15cd8SSteven Rostedt (Red Hat)  * we do it by hogging all of the CPU(s) for configurable timer intervals,
26e7c15cd8SSteven Rostedt (Red Hat)  * sampling the built-in CPU timer, looking for discontiguous readings.
27e7c15cd8SSteven Rostedt (Red Hat)  *
28e7c15cd8SSteven Rostedt (Red Hat)  * WARNING: This implementation necessarily introduces latencies. Therefore,
29e7c15cd8SSteven Rostedt (Red Hat)  *          you should NEVER use this tracer while running in a production
30e7c15cd8SSteven Rostedt (Red Hat)  *          environment requiring any kind of low-latency performance
31e7c15cd8SSteven Rostedt (Red Hat)  *          guarantee(s).
32e7c15cd8SSteven Rostedt (Red Hat)  *
33e7c15cd8SSteven Rostedt (Red Hat)  * Copyright (C) 2008-2009 Jon Masters, Red Hat, Inc. <jcm@redhat.com>
34e7c15cd8SSteven Rostedt (Red Hat)  * Copyright (C) 2013-2016 Steven Rostedt, Red Hat, Inc. <srostedt@redhat.com>
35e7c15cd8SSteven Rostedt (Red Hat)  *
36e7c15cd8SSteven Rostedt (Red Hat)  * Includes useful feedback from Clark Williams <clark@redhat.com>
37e7c15cd8SSteven Rostedt (Red Hat)  *
38e7c15cd8SSteven Rostedt (Red Hat)  * This file is licensed under the terms of the GNU General Public
39e7c15cd8SSteven Rostedt (Red Hat)  * License version 2. This program is licensed "as is" without any
40e7c15cd8SSteven Rostedt (Red Hat)  * warranty of any kind, whether express or implied.
41e7c15cd8SSteven Rostedt (Red Hat)  */
42e7c15cd8SSteven Rostedt (Red Hat) #include <linux/kthread.h>
43e7c15cd8SSteven Rostedt (Red Hat) #include <linux/tracefs.h>
44e7c15cd8SSteven Rostedt (Red Hat) #include <linux/uaccess.h>
45*0330f7aaSSteven Rostedt (Red Hat) #include <linux/cpumask.h>
46e7c15cd8SSteven Rostedt (Red Hat) #include <linux/delay.h>
47e7c15cd8SSteven Rostedt (Red Hat) #include "trace.h"
48e7c15cd8SSteven Rostedt (Red Hat) 
49e7c15cd8SSteven Rostedt (Red Hat) static struct trace_array	*hwlat_trace;
50e7c15cd8SSteven Rostedt (Red Hat) 
51e7c15cd8SSteven Rostedt (Red Hat) #define U64STR_SIZE		22			/* 20 digits max */
52e7c15cd8SSteven Rostedt (Red Hat) 
53e7c15cd8SSteven Rostedt (Red Hat) #define BANNER			"hwlat_detector: "
54e7c15cd8SSteven Rostedt (Red Hat) #define DEFAULT_SAMPLE_WINDOW	1000000			/* 1s */
55e7c15cd8SSteven Rostedt (Red Hat) #define DEFAULT_SAMPLE_WIDTH	500000			/* 0.5s */
56e7c15cd8SSteven Rostedt (Red Hat) #define DEFAULT_LAT_THRESHOLD	10			/* 10us */
57e7c15cd8SSteven Rostedt (Red Hat) 
58e7c15cd8SSteven Rostedt (Red Hat) /* sampling thread*/
59e7c15cd8SSteven Rostedt (Red Hat) static struct task_struct *hwlat_kthread;
60e7c15cd8SSteven Rostedt (Red Hat) 
61e7c15cd8SSteven Rostedt (Red Hat) static struct dentry *hwlat_sample_width;	/* sample width us */
62e7c15cd8SSteven Rostedt (Red Hat) static struct dentry *hwlat_sample_window;	/* sample window us */
63e7c15cd8SSteven Rostedt (Red Hat) 
64e7c15cd8SSteven Rostedt (Red Hat) /* Save the previous tracing_thresh value */
65e7c15cd8SSteven Rostedt (Red Hat) static unsigned long save_tracing_thresh;
66e7c15cd8SSteven Rostedt (Red Hat) 
67e7c15cd8SSteven Rostedt (Red Hat) /* If the user changed threshold, remember it */
68e7c15cd8SSteven Rostedt (Red Hat) static u64 last_tracing_thresh = DEFAULT_LAT_THRESHOLD * NSEC_PER_USEC;
69e7c15cd8SSteven Rostedt (Red Hat) 
70e7c15cd8SSteven Rostedt (Red Hat) /* Individual latency samples are stored here when detected. */
71e7c15cd8SSteven Rostedt (Red Hat) struct hwlat_sample {
72e7c15cd8SSteven Rostedt (Red Hat) 	u64		seqnum;		/* unique sequence */
73e7c15cd8SSteven Rostedt (Red Hat) 	u64		duration;	/* delta */
74e7c15cd8SSteven Rostedt (Red Hat) 	u64		outer_duration;	/* delta (outer loop) */
75e7c15cd8SSteven Rostedt (Red Hat) 	struct timespec	timestamp;	/* wall time */
76e7c15cd8SSteven Rostedt (Red Hat) };
77e7c15cd8SSteven Rostedt (Red Hat) 
78e7c15cd8SSteven Rostedt (Red Hat) /* keep the global state somewhere. */
79e7c15cd8SSteven Rostedt (Red Hat) static struct hwlat_data {
80e7c15cd8SSteven Rostedt (Red Hat) 
81e7c15cd8SSteven Rostedt (Red Hat) 	struct mutex lock;		/* protect changes */
82e7c15cd8SSteven Rostedt (Red Hat) 
83e7c15cd8SSteven Rostedt (Red Hat) 	u64	count;			/* total since reset */
84e7c15cd8SSteven Rostedt (Red Hat) 
85e7c15cd8SSteven Rostedt (Red Hat) 	u64	sample_window;		/* total sampling window (on+off) */
86e7c15cd8SSteven Rostedt (Red Hat) 	u64	sample_width;		/* active sampling portion of window */
87e7c15cd8SSteven Rostedt (Red Hat) 
88e7c15cd8SSteven Rostedt (Red Hat) } hwlat_data = {
89e7c15cd8SSteven Rostedt (Red Hat) 	.sample_window		= DEFAULT_SAMPLE_WINDOW,
90e7c15cd8SSteven Rostedt (Red Hat) 	.sample_width		= DEFAULT_SAMPLE_WIDTH,
91e7c15cd8SSteven Rostedt (Red Hat) };
92e7c15cd8SSteven Rostedt (Red Hat) 
93e7c15cd8SSteven Rostedt (Red Hat) static void trace_hwlat_sample(struct hwlat_sample *sample)
94e7c15cd8SSteven Rostedt (Red Hat) {
95e7c15cd8SSteven Rostedt (Red Hat) 	struct trace_array *tr = hwlat_trace;
96e7c15cd8SSteven Rostedt (Red Hat) 	struct trace_event_call *call = &event_hwlat;
97e7c15cd8SSteven Rostedt (Red Hat) 	struct ring_buffer *buffer = tr->trace_buffer.buffer;
98e7c15cd8SSteven Rostedt (Red Hat) 	struct ring_buffer_event *event;
99e7c15cd8SSteven Rostedt (Red Hat) 	struct hwlat_entry *entry;
100e7c15cd8SSteven Rostedt (Red Hat) 	unsigned long flags;
101e7c15cd8SSteven Rostedt (Red Hat) 	int pc;
102e7c15cd8SSteven Rostedt (Red Hat) 
103e7c15cd8SSteven Rostedt (Red Hat) 	pc = preempt_count();
104e7c15cd8SSteven Rostedt (Red Hat) 	local_save_flags(flags);
105e7c15cd8SSteven Rostedt (Red Hat) 
106e7c15cd8SSteven Rostedt (Red Hat) 	event = trace_buffer_lock_reserve(buffer, TRACE_HWLAT, sizeof(*entry),
107e7c15cd8SSteven Rostedt (Red Hat) 					  flags, pc);
108e7c15cd8SSteven Rostedt (Red Hat) 	if (!event)
109e7c15cd8SSteven Rostedt (Red Hat) 		return;
110e7c15cd8SSteven Rostedt (Red Hat) 	entry	= ring_buffer_event_data(event);
111e7c15cd8SSteven Rostedt (Red Hat) 	entry->seqnum			= sample->seqnum;
112e7c15cd8SSteven Rostedt (Red Hat) 	entry->duration			= sample->duration;
113e7c15cd8SSteven Rostedt (Red Hat) 	entry->outer_duration		= sample->outer_duration;
114e7c15cd8SSteven Rostedt (Red Hat) 	entry->timestamp		= sample->timestamp;
115e7c15cd8SSteven Rostedt (Red Hat) 
116e7c15cd8SSteven Rostedt (Red Hat) 	if (!call_filter_check_discard(call, entry, buffer, event))
117e7c15cd8SSteven Rostedt (Red Hat) 		__buffer_unlock_commit(buffer, event);
118e7c15cd8SSteven Rostedt (Red Hat) }
119e7c15cd8SSteven Rostedt (Red Hat) 
120e7c15cd8SSteven Rostedt (Red Hat) /* Macros to encapsulate the time capturing infrastructure */
121e7c15cd8SSteven Rostedt (Red Hat) #define time_type	u64
122e7c15cd8SSteven Rostedt (Red Hat) #define time_get()	trace_clock_local()
123e7c15cd8SSteven Rostedt (Red Hat) #define time_to_us(x)	div_u64(x, 1000)
124e7c15cd8SSteven Rostedt (Red Hat) #define time_sub(a, b)	((a) - (b))
125e7c15cd8SSteven Rostedt (Red Hat) #define init_time(a, b)	(a = b)
126e7c15cd8SSteven Rostedt (Red Hat) #define time_u64(a)	a
127e7c15cd8SSteven Rostedt (Red Hat) 
128e7c15cd8SSteven Rostedt (Red Hat) /**
129e7c15cd8SSteven Rostedt (Red Hat)  * get_sample - sample the CPU TSC and look for likely hardware latencies
130e7c15cd8SSteven Rostedt (Red Hat)  *
131e7c15cd8SSteven Rostedt (Red Hat)  * Used to repeatedly capture the CPU TSC (or similar), looking for potential
132e7c15cd8SSteven Rostedt (Red Hat)  * hardware-induced latency. Called with interrupts disabled and with
133e7c15cd8SSteven Rostedt (Red Hat)  * hwlat_data.lock held.
134e7c15cd8SSteven Rostedt (Red Hat)  */
135e7c15cd8SSteven Rostedt (Red Hat) static int get_sample(void)
136e7c15cd8SSteven Rostedt (Red Hat) {
137e7c15cd8SSteven Rostedt (Red Hat) 	struct trace_array *tr = hwlat_trace;
138e7c15cd8SSteven Rostedt (Red Hat) 	time_type start, t1, t2, last_t2;
139e7c15cd8SSteven Rostedt (Red Hat) 	s64 diff, total, last_total = 0;
140e7c15cd8SSteven Rostedt (Red Hat) 	u64 sample = 0;
141e7c15cd8SSteven Rostedt (Red Hat) 	u64 thresh = tracing_thresh;
142e7c15cd8SSteven Rostedt (Red Hat) 	u64 outer_sample = 0;
143e7c15cd8SSteven Rostedt (Red Hat) 	int ret = -1;
144e7c15cd8SSteven Rostedt (Red Hat) 
145e7c15cd8SSteven Rostedt (Red Hat) 	do_div(thresh, NSEC_PER_USEC); /* modifies interval value */
146e7c15cd8SSteven Rostedt (Red Hat) 
147e7c15cd8SSteven Rostedt (Red Hat) 	init_time(last_t2, 0);
148e7c15cd8SSteven Rostedt (Red Hat) 	start = time_get(); /* start timestamp */
149e7c15cd8SSteven Rostedt (Red Hat) 
150e7c15cd8SSteven Rostedt (Red Hat) 	do {
151e7c15cd8SSteven Rostedt (Red Hat) 
152e7c15cd8SSteven Rostedt (Red Hat) 		t1 = time_get();	/* we'll look for a discontinuity */
153e7c15cd8SSteven Rostedt (Red Hat) 		t2 = time_get();
154e7c15cd8SSteven Rostedt (Red Hat) 
155e7c15cd8SSteven Rostedt (Red Hat) 		if (time_u64(last_t2)) {
156e7c15cd8SSteven Rostedt (Red Hat) 			/* Check the delta from outer loop (t2 to next t1) */
157e7c15cd8SSteven Rostedt (Red Hat) 			diff = time_to_us(time_sub(t1, last_t2));
158e7c15cd8SSteven Rostedt (Red Hat) 			/* This shouldn't happen */
159e7c15cd8SSteven Rostedt (Red Hat) 			if (diff < 0) {
160e7c15cd8SSteven Rostedt (Red Hat) 				pr_err(BANNER "time running backwards\n");
161e7c15cd8SSteven Rostedt (Red Hat) 				goto out;
162e7c15cd8SSteven Rostedt (Red Hat) 			}
163e7c15cd8SSteven Rostedt (Red Hat) 			if (diff > outer_sample)
164e7c15cd8SSteven Rostedt (Red Hat) 				outer_sample = diff;
165e7c15cd8SSteven Rostedt (Red Hat) 		}
166e7c15cd8SSteven Rostedt (Red Hat) 		last_t2 = t2;
167e7c15cd8SSteven Rostedt (Red Hat) 
168e7c15cd8SSteven Rostedt (Red Hat) 		total = time_to_us(time_sub(t2, start)); /* sample width */
169e7c15cd8SSteven Rostedt (Red Hat) 
170e7c15cd8SSteven Rostedt (Red Hat) 		/* Check for possible overflows */
171e7c15cd8SSteven Rostedt (Red Hat) 		if (total < last_total) {
172e7c15cd8SSteven Rostedt (Red Hat) 			pr_err("Time total overflowed\n");
173e7c15cd8SSteven Rostedt (Red Hat) 			break;
174e7c15cd8SSteven Rostedt (Red Hat) 		}
175e7c15cd8SSteven Rostedt (Red Hat) 		last_total = total;
176e7c15cd8SSteven Rostedt (Red Hat) 
177e7c15cd8SSteven Rostedt (Red Hat) 		/* This checks the inner loop (t1 to t2) */
178e7c15cd8SSteven Rostedt (Red Hat) 		diff = time_to_us(time_sub(t2, t1));     /* current diff */
179e7c15cd8SSteven Rostedt (Red Hat) 
180e7c15cd8SSteven Rostedt (Red Hat) 		/* This shouldn't happen */
181e7c15cd8SSteven Rostedt (Red Hat) 		if (diff < 0) {
182e7c15cd8SSteven Rostedt (Red Hat) 			pr_err(BANNER "time running backwards\n");
183e7c15cd8SSteven Rostedt (Red Hat) 			goto out;
184e7c15cd8SSteven Rostedt (Red Hat) 		}
185e7c15cd8SSteven Rostedt (Red Hat) 
186e7c15cd8SSteven Rostedt (Red Hat) 		if (diff > sample)
187e7c15cd8SSteven Rostedt (Red Hat) 			sample = diff; /* only want highest value */
188e7c15cd8SSteven Rostedt (Red Hat) 
189e7c15cd8SSteven Rostedt (Red Hat) 	} while (total <= hwlat_data.sample_width);
190e7c15cd8SSteven Rostedt (Red Hat) 
191e7c15cd8SSteven Rostedt (Red Hat) 	ret = 0;
192e7c15cd8SSteven Rostedt (Red Hat) 
193e7c15cd8SSteven Rostedt (Red Hat) 	/* If we exceed the threshold value, we have found a hardware latency */
194e7c15cd8SSteven Rostedt (Red Hat) 	if (sample > thresh || outer_sample > thresh) {
195e7c15cd8SSteven Rostedt (Red Hat) 		struct hwlat_sample s;
196e7c15cd8SSteven Rostedt (Red Hat) 
197e7c15cd8SSteven Rostedt (Red Hat) 		ret = 1;
198e7c15cd8SSteven Rostedt (Red Hat) 
199e7c15cd8SSteven Rostedt (Red Hat) 		hwlat_data.count++;
200e7c15cd8SSteven Rostedt (Red Hat) 		s.seqnum = hwlat_data.count;
201e7c15cd8SSteven Rostedt (Red Hat) 		s.duration = sample;
202e7c15cd8SSteven Rostedt (Red Hat) 		s.outer_duration = outer_sample;
203e7c15cd8SSteven Rostedt (Red Hat) 		s.timestamp = CURRENT_TIME;
204e7c15cd8SSteven Rostedt (Red Hat) 		trace_hwlat_sample(&s);
205e7c15cd8SSteven Rostedt (Red Hat) 
206e7c15cd8SSteven Rostedt (Red Hat) 		/* Keep a running maximum ever recorded hardware latency */
207e7c15cd8SSteven Rostedt (Red Hat) 		if (sample > tr->max_latency)
208e7c15cd8SSteven Rostedt (Red Hat) 			tr->max_latency = sample;
209e7c15cd8SSteven Rostedt (Red Hat) 	}
210e7c15cd8SSteven Rostedt (Red Hat) 
211e7c15cd8SSteven Rostedt (Red Hat) out:
212e7c15cd8SSteven Rostedt (Red Hat) 	return ret;
213e7c15cd8SSteven Rostedt (Red Hat) }
214e7c15cd8SSteven Rostedt (Red Hat) 
215*0330f7aaSSteven Rostedt (Red Hat) static struct cpumask save_cpumask;
216*0330f7aaSSteven Rostedt (Red Hat) static bool disable_migrate;
217*0330f7aaSSteven Rostedt (Red Hat) 
218*0330f7aaSSteven Rostedt (Red Hat) static void move_to_next_cpu(void)
219*0330f7aaSSteven Rostedt (Red Hat) {
220*0330f7aaSSteven Rostedt (Red Hat) 	static struct cpumask *current_mask;
221*0330f7aaSSteven Rostedt (Red Hat) 	int next_cpu;
222*0330f7aaSSteven Rostedt (Red Hat) 
223*0330f7aaSSteven Rostedt (Red Hat) 	if (disable_migrate)
224*0330f7aaSSteven Rostedt (Red Hat) 		return;
225*0330f7aaSSteven Rostedt (Red Hat) 
226*0330f7aaSSteven Rostedt (Red Hat) 	/* Just pick the first CPU on first iteration */
227*0330f7aaSSteven Rostedt (Red Hat) 	if (!current_mask) {
228*0330f7aaSSteven Rostedt (Red Hat) 		current_mask = &save_cpumask;
229*0330f7aaSSteven Rostedt (Red Hat) 		get_online_cpus();
230*0330f7aaSSteven Rostedt (Red Hat) 		cpumask_and(current_mask, cpu_online_mask, tracing_buffer_mask);
231*0330f7aaSSteven Rostedt (Red Hat) 		put_online_cpus();
232*0330f7aaSSteven Rostedt (Red Hat) 		next_cpu = cpumask_first(current_mask);
233*0330f7aaSSteven Rostedt (Red Hat) 		goto set_affinity;
234*0330f7aaSSteven Rostedt (Red Hat) 	}
235*0330f7aaSSteven Rostedt (Red Hat) 
236*0330f7aaSSteven Rostedt (Red Hat) 	/*
237*0330f7aaSSteven Rostedt (Red Hat) 	 * If for some reason the user modifies the CPU affinity
238*0330f7aaSSteven Rostedt (Red Hat) 	 * of this thread, than stop migrating for the duration
239*0330f7aaSSteven Rostedt (Red Hat) 	 * of the current test.
240*0330f7aaSSteven Rostedt (Red Hat) 	 */
241*0330f7aaSSteven Rostedt (Red Hat) 	if (!cpumask_equal(current_mask, &current->cpus_allowed))
242*0330f7aaSSteven Rostedt (Red Hat) 		goto disable;
243*0330f7aaSSteven Rostedt (Red Hat) 
244*0330f7aaSSteven Rostedt (Red Hat) 	get_online_cpus();
245*0330f7aaSSteven Rostedt (Red Hat) 	cpumask_and(current_mask, cpu_online_mask, tracing_buffer_mask);
246*0330f7aaSSteven Rostedt (Red Hat) 	next_cpu = cpumask_next(smp_processor_id(), current_mask);
247*0330f7aaSSteven Rostedt (Red Hat) 	put_online_cpus();
248*0330f7aaSSteven Rostedt (Red Hat) 
249*0330f7aaSSteven Rostedt (Red Hat) 	if (next_cpu >= nr_cpu_ids)
250*0330f7aaSSteven Rostedt (Red Hat) 		next_cpu = cpumask_first(current_mask);
251*0330f7aaSSteven Rostedt (Red Hat) 
252*0330f7aaSSteven Rostedt (Red Hat)  set_affinity:
253*0330f7aaSSteven Rostedt (Red Hat) 	if (next_cpu >= nr_cpu_ids) /* Shouldn't happen! */
254*0330f7aaSSteven Rostedt (Red Hat) 		goto disable;
255*0330f7aaSSteven Rostedt (Red Hat) 
256*0330f7aaSSteven Rostedt (Red Hat) 	cpumask_clear(current_mask);
257*0330f7aaSSteven Rostedt (Red Hat) 	cpumask_set_cpu(next_cpu, current_mask);
258*0330f7aaSSteven Rostedt (Red Hat) 
259*0330f7aaSSteven Rostedt (Red Hat) 	sched_setaffinity(0, current_mask);
260*0330f7aaSSteven Rostedt (Red Hat) 	return;
261*0330f7aaSSteven Rostedt (Red Hat) 
262*0330f7aaSSteven Rostedt (Red Hat)  disable:
263*0330f7aaSSteven Rostedt (Red Hat) 	disable_migrate = true;
264*0330f7aaSSteven Rostedt (Red Hat) }
265*0330f7aaSSteven Rostedt (Red Hat) 
266e7c15cd8SSteven Rostedt (Red Hat) /*
267e7c15cd8SSteven Rostedt (Red Hat)  * kthread_fn - The CPU time sampling/hardware latency detection kernel thread
268e7c15cd8SSteven Rostedt (Red Hat)  *
269e7c15cd8SSteven Rostedt (Red Hat)  * Used to periodically sample the CPU TSC via a call to get_sample. We
270e7c15cd8SSteven Rostedt (Red Hat)  * disable interrupts, which does (intentionally) introduce latency since we
271e7c15cd8SSteven Rostedt (Red Hat)  * need to ensure nothing else might be running (and thus preempting).
272e7c15cd8SSteven Rostedt (Red Hat)  * Obviously this should never be used in production environments.
273e7c15cd8SSteven Rostedt (Red Hat)  *
274e7c15cd8SSteven Rostedt (Red Hat)  * Currently this runs on which ever CPU it was scheduled on, but most
275e7c15cd8SSteven Rostedt (Red Hat)  * real-world hardware latency situations occur across several CPUs,
276e7c15cd8SSteven Rostedt (Red Hat)  * but we might later generalize this if we find there are any actualy
277e7c15cd8SSteven Rostedt (Red Hat)  * systems with alternate SMI delivery or other hardware latencies.
278e7c15cd8SSteven Rostedt (Red Hat)  */
279e7c15cd8SSteven Rostedt (Red Hat) static int kthread_fn(void *data)
280e7c15cd8SSteven Rostedt (Red Hat) {
281e7c15cd8SSteven Rostedt (Red Hat) 	u64 interval;
282e7c15cd8SSteven Rostedt (Red Hat) 
283e7c15cd8SSteven Rostedt (Red Hat) 	while (!kthread_should_stop()) {
284e7c15cd8SSteven Rostedt (Red Hat) 
285*0330f7aaSSteven Rostedt (Red Hat) 		move_to_next_cpu();
286*0330f7aaSSteven Rostedt (Red Hat) 
287e7c15cd8SSteven Rostedt (Red Hat) 		local_irq_disable();
288e7c15cd8SSteven Rostedt (Red Hat) 		get_sample();
289e7c15cd8SSteven Rostedt (Red Hat) 		local_irq_enable();
290e7c15cd8SSteven Rostedt (Red Hat) 
291e7c15cd8SSteven Rostedt (Red Hat) 		mutex_lock(&hwlat_data.lock);
292e7c15cd8SSteven Rostedt (Red Hat) 		interval = hwlat_data.sample_window - hwlat_data.sample_width;
293e7c15cd8SSteven Rostedt (Red Hat) 		mutex_unlock(&hwlat_data.lock);
294e7c15cd8SSteven Rostedt (Red Hat) 
295e7c15cd8SSteven Rostedt (Red Hat) 		do_div(interval, USEC_PER_MSEC); /* modifies interval value */
296e7c15cd8SSteven Rostedt (Red Hat) 
297e7c15cd8SSteven Rostedt (Red Hat) 		/* Always sleep for at least 1ms */
298e7c15cd8SSteven Rostedt (Red Hat) 		if (interval < 1)
299e7c15cd8SSteven Rostedt (Red Hat) 			interval = 1;
300e7c15cd8SSteven Rostedt (Red Hat) 
301e7c15cd8SSteven Rostedt (Red Hat) 		if (msleep_interruptible(interval))
302e7c15cd8SSteven Rostedt (Red Hat) 			break;
303e7c15cd8SSteven Rostedt (Red Hat) 	}
304e7c15cd8SSteven Rostedt (Red Hat) 
305e7c15cd8SSteven Rostedt (Red Hat) 	return 0;
306e7c15cd8SSteven Rostedt (Red Hat) }
307e7c15cd8SSteven Rostedt (Red Hat) 
308e7c15cd8SSteven Rostedt (Red Hat) /**
309e7c15cd8SSteven Rostedt (Red Hat)  * start_kthread - Kick off the hardware latency sampling/detector kthread
310e7c15cd8SSteven Rostedt (Red Hat)  *
311e7c15cd8SSteven Rostedt (Red Hat)  * This starts the kernel thread that will sit and sample the CPU timestamp
312e7c15cd8SSteven Rostedt (Red Hat)  * counter (TSC or similar) and look for potential hardware latencies.
313e7c15cd8SSteven Rostedt (Red Hat)  */
314e7c15cd8SSteven Rostedt (Red Hat) static int start_kthread(struct trace_array *tr)
315e7c15cd8SSteven Rostedt (Red Hat) {
316e7c15cd8SSteven Rostedt (Red Hat) 	struct task_struct *kthread;
317e7c15cd8SSteven Rostedt (Red Hat) 
318e7c15cd8SSteven Rostedt (Red Hat) 	kthread = kthread_create(kthread_fn, NULL, "hwlatd");
319e7c15cd8SSteven Rostedt (Red Hat) 	if (IS_ERR(kthread)) {
320e7c15cd8SSteven Rostedt (Red Hat) 		pr_err(BANNER "could not start sampling thread\n");
321e7c15cd8SSteven Rostedt (Red Hat) 		return -ENOMEM;
322e7c15cd8SSteven Rostedt (Red Hat) 	}
323e7c15cd8SSteven Rostedt (Red Hat) 	hwlat_kthread = kthread;
324e7c15cd8SSteven Rostedt (Red Hat) 	wake_up_process(kthread);
325e7c15cd8SSteven Rostedt (Red Hat) 
326e7c15cd8SSteven Rostedt (Red Hat) 	return 0;
327e7c15cd8SSteven Rostedt (Red Hat) }
328e7c15cd8SSteven Rostedt (Red Hat) 
329e7c15cd8SSteven Rostedt (Red Hat) /**
330e7c15cd8SSteven Rostedt (Red Hat)  * stop_kthread - Inform the hardware latency samping/detector kthread to stop
331e7c15cd8SSteven Rostedt (Red Hat)  *
332e7c15cd8SSteven Rostedt (Red Hat)  * This kicks the running hardware latency sampling/detector kernel thread and
333e7c15cd8SSteven Rostedt (Red Hat)  * tells it to stop sampling now. Use this on unload and at system shutdown.
334e7c15cd8SSteven Rostedt (Red Hat)  */
335e7c15cd8SSteven Rostedt (Red Hat) static void stop_kthread(void)
336e7c15cd8SSteven Rostedt (Red Hat) {
337e7c15cd8SSteven Rostedt (Red Hat) 	if (!hwlat_kthread)
338e7c15cd8SSteven Rostedt (Red Hat) 		return;
339e7c15cd8SSteven Rostedt (Red Hat) 	kthread_stop(hwlat_kthread);
340e7c15cd8SSteven Rostedt (Red Hat) 	hwlat_kthread = NULL;
341e7c15cd8SSteven Rostedt (Red Hat) }
342e7c15cd8SSteven Rostedt (Red Hat) 
343e7c15cd8SSteven Rostedt (Red Hat) /*
344e7c15cd8SSteven Rostedt (Red Hat)  * hwlat_read - Wrapper read function for reading both window and width
345e7c15cd8SSteven Rostedt (Red Hat)  * @filp: The active open file structure
346e7c15cd8SSteven Rostedt (Red Hat)  * @ubuf: The userspace provided buffer to read value into
347e7c15cd8SSteven Rostedt (Red Hat)  * @cnt: The maximum number of bytes to read
348e7c15cd8SSteven Rostedt (Red Hat)  * @ppos: The current "file" position
349e7c15cd8SSteven Rostedt (Red Hat)  *
350e7c15cd8SSteven Rostedt (Red Hat)  * This function provides a generic read implementation for the global state
351e7c15cd8SSteven Rostedt (Red Hat)  * "hwlat_data" structure filesystem entries.
352e7c15cd8SSteven Rostedt (Red Hat)  */
353e7c15cd8SSteven Rostedt (Red Hat) static ssize_t hwlat_read(struct file *filp, char __user *ubuf,
354e7c15cd8SSteven Rostedt (Red Hat) 			  size_t cnt, loff_t *ppos)
355e7c15cd8SSteven Rostedt (Red Hat) {
356e7c15cd8SSteven Rostedt (Red Hat) 	char buf[U64STR_SIZE];
357e7c15cd8SSteven Rostedt (Red Hat) 	u64 *entry = filp->private_data;
358e7c15cd8SSteven Rostedt (Red Hat) 	u64 val;
359e7c15cd8SSteven Rostedt (Red Hat) 	int len;
360e7c15cd8SSteven Rostedt (Red Hat) 
361e7c15cd8SSteven Rostedt (Red Hat) 	if (!entry)
362e7c15cd8SSteven Rostedt (Red Hat) 		return -EFAULT;
363e7c15cd8SSteven Rostedt (Red Hat) 
364e7c15cd8SSteven Rostedt (Red Hat) 	if (cnt > sizeof(buf))
365e7c15cd8SSteven Rostedt (Red Hat) 		cnt = sizeof(buf);
366e7c15cd8SSteven Rostedt (Red Hat) 
367e7c15cd8SSteven Rostedt (Red Hat) 	val = *entry;
368e7c15cd8SSteven Rostedt (Red Hat) 
369e7c15cd8SSteven Rostedt (Red Hat) 	len = snprintf(buf, sizeof(buf), "%llu\n", val);
370e7c15cd8SSteven Rostedt (Red Hat) 
371e7c15cd8SSteven Rostedt (Red Hat) 	return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
372e7c15cd8SSteven Rostedt (Red Hat) }
373e7c15cd8SSteven Rostedt (Red Hat) 
374e7c15cd8SSteven Rostedt (Red Hat) /**
375e7c15cd8SSteven Rostedt (Red Hat)  * hwlat_width_write - Write function for "width" entry
376e7c15cd8SSteven Rostedt (Red Hat)  * @filp: The active open file structure
377e7c15cd8SSteven Rostedt (Red Hat)  * @ubuf: The user buffer that contains the value to write
378e7c15cd8SSteven Rostedt (Red Hat)  * @cnt: The maximum number of bytes to write to "file"
379e7c15cd8SSteven Rostedt (Red Hat)  * @ppos: The current position in @file
380e7c15cd8SSteven Rostedt (Red Hat)  *
381e7c15cd8SSteven Rostedt (Red Hat)  * This function provides a write implementation for the "width" interface
382e7c15cd8SSteven Rostedt (Red Hat)  * to the hardware latency detector. It can be used to configure
383e7c15cd8SSteven Rostedt (Red Hat)  * for how many us of the total window us we will actively sample for any
384e7c15cd8SSteven Rostedt (Red Hat)  * hardware-induced latency periods. Obviously, it is not possible to
385e7c15cd8SSteven Rostedt (Red Hat)  * sample constantly and have the system respond to a sample reader, or,
386e7c15cd8SSteven Rostedt (Red Hat)  * worse, without having the system appear to have gone out to lunch. It
387e7c15cd8SSteven Rostedt (Red Hat)  * is enforced that width is less that the total window size.
388e7c15cd8SSteven Rostedt (Red Hat)  */
389e7c15cd8SSteven Rostedt (Red Hat) static ssize_t
390e7c15cd8SSteven Rostedt (Red Hat) hwlat_width_write(struct file *filp, const char __user *ubuf,
391e7c15cd8SSteven Rostedt (Red Hat) 		  size_t cnt, loff_t *ppos)
392e7c15cd8SSteven Rostedt (Red Hat) {
393e7c15cd8SSteven Rostedt (Red Hat) 	u64 val;
394e7c15cd8SSteven Rostedt (Red Hat) 	int err;
395e7c15cd8SSteven Rostedt (Red Hat) 
396e7c15cd8SSteven Rostedt (Red Hat) 	err = kstrtoull_from_user(ubuf, cnt, 10, &val);
397e7c15cd8SSteven Rostedt (Red Hat) 	if (err)
398e7c15cd8SSteven Rostedt (Red Hat) 		return err;
399e7c15cd8SSteven Rostedt (Red Hat) 
400e7c15cd8SSteven Rostedt (Red Hat) 	mutex_lock(&hwlat_data.lock);
401e7c15cd8SSteven Rostedt (Red Hat) 	if (val < hwlat_data.sample_window)
402e7c15cd8SSteven Rostedt (Red Hat) 		hwlat_data.sample_width = val;
403e7c15cd8SSteven Rostedt (Red Hat) 	else
404e7c15cd8SSteven Rostedt (Red Hat) 		err = -EINVAL;
405e7c15cd8SSteven Rostedt (Red Hat) 	mutex_unlock(&hwlat_data.lock);
406e7c15cd8SSteven Rostedt (Red Hat) 
407e7c15cd8SSteven Rostedt (Red Hat) 	if (err)
408e7c15cd8SSteven Rostedt (Red Hat) 		return err;
409e7c15cd8SSteven Rostedt (Red Hat) 
410e7c15cd8SSteven Rostedt (Red Hat) 	return cnt;
411e7c15cd8SSteven Rostedt (Red Hat) }
412e7c15cd8SSteven Rostedt (Red Hat) 
413e7c15cd8SSteven Rostedt (Red Hat) /**
414e7c15cd8SSteven Rostedt (Red Hat)  * hwlat_window_write - Write function for "window" entry
415e7c15cd8SSteven Rostedt (Red Hat)  * @filp: The active open file structure
416e7c15cd8SSteven Rostedt (Red Hat)  * @ubuf: The user buffer that contains the value to write
417e7c15cd8SSteven Rostedt (Red Hat)  * @cnt: The maximum number of bytes to write to "file"
418e7c15cd8SSteven Rostedt (Red Hat)  * @ppos: The current position in @file
419e7c15cd8SSteven Rostedt (Red Hat)  *
420e7c15cd8SSteven Rostedt (Red Hat)  * This function provides a write implementation for the "window" interface
421e7c15cd8SSteven Rostedt (Red Hat)  * to the hardware latency detetector. The window is the total time
422e7c15cd8SSteven Rostedt (Red Hat)  * in us that will be considered one sample period. Conceptually, windows
423e7c15cd8SSteven Rostedt (Red Hat)  * occur back-to-back and contain a sample width period during which
424e7c15cd8SSteven Rostedt (Red Hat)  * actual sampling occurs. Can be used to write a new total window size. It
425e7c15cd8SSteven Rostedt (Red Hat)  * is enfoced that any value written must be greater than the sample width
426e7c15cd8SSteven Rostedt (Red Hat)  * size, or an error results.
427e7c15cd8SSteven Rostedt (Red Hat)  */
428e7c15cd8SSteven Rostedt (Red Hat) static ssize_t
429e7c15cd8SSteven Rostedt (Red Hat) hwlat_window_write(struct file *filp, const char __user *ubuf,
430e7c15cd8SSteven Rostedt (Red Hat) 		   size_t cnt, loff_t *ppos)
431e7c15cd8SSteven Rostedt (Red Hat) {
432e7c15cd8SSteven Rostedt (Red Hat) 	u64 val;
433e7c15cd8SSteven Rostedt (Red Hat) 	int err;
434e7c15cd8SSteven Rostedt (Red Hat) 
435e7c15cd8SSteven Rostedt (Red Hat) 	err = kstrtoull_from_user(ubuf, cnt, 10, &val);
436e7c15cd8SSteven Rostedt (Red Hat) 	if (err)
437e7c15cd8SSteven Rostedt (Red Hat) 		return err;
438e7c15cd8SSteven Rostedt (Red Hat) 
439e7c15cd8SSteven Rostedt (Red Hat) 	mutex_lock(&hwlat_data.lock);
440e7c15cd8SSteven Rostedt (Red Hat) 	if (hwlat_data.sample_width < val)
441e7c15cd8SSteven Rostedt (Red Hat) 		hwlat_data.sample_window = val;
442e7c15cd8SSteven Rostedt (Red Hat) 	else
443e7c15cd8SSteven Rostedt (Red Hat) 		err = -EINVAL;
444e7c15cd8SSteven Rostedt (Red Hat) 	mutex_unlock(&hwlat_data.lock);
445e7c15cd8SSteven Rostedt (Red Hat) 
446e7c15cd8SSteven Rostedt (Red Hat) 	if (err)
447e7c15cd8SSteven Rostedt (Red Hat) 		return err;
448e7c15cd8SSteven Rostedt (Red Hat) 
449e7c15cd8SSteven Rostedt (Red Hat) 	return cnt;
450e7c15cd8SSteven Rostedt (Red Hat) }
451e7c15cd8SSteven Rostedt (Red Hat) 
452e7c15cd8SSteven Rostedt (Red Hat) static const struct file_operations width_fops = {
453e7c15cd8SSteven Rostedt (Red Hat) 	.open		= tracing_open_generic,
454e7c15cd8SSteven Rostedt (Red Hat) 	.read		= hwlat_read,
455e7c15cd8SSteven Rostedt (Red Hat) 	.write		= hwlat_width_write,
456e7c15cd8SSteven Rostedt (Red Hat) };
457e7c15cd8SSteven Rostedt (Red Hat) 
458e7c15cd8SSteven Rostedt (Red Hat) static const struct file_operations window_fops = {
459e7c15cd8SSteven Rostedt (Red Hat) 	.open		= tracing_open_generic,
460e7c15cd8SSteven Rostedt (Red Hat) 	.read		= hwlat_read,
461e7c15cd8SSteven Rostedt (Red Hat) 	.write		= hwlat_window_write,
462e7c15cd8SSteven Rostedt (Red Hat) };
463e7c15cd8SSteven Rostedt (Red Hat) 
464e7c15cd8SSteven Rostedt (Red Hat) /**
465e7c15cd8SSteven Rostedt (Red Hat)  * init_tracefs - A function to initialize the tracefs interface files
466e7c15cd8SSteven Rostedt (Red Hat)  *
467e7c15cd8SSteven Rostedt (Red Hat)  * This function creates entries in tracefs for "hwlat_detector".
468e7c15cd8SSteven Rostedt (Red Hat)  * It creates the hwlat_detector directory in the tracing directory,
469e7c15cd8SSteven Rostedt (Red Hat)  * and within that directory is the count, width and window files to
470e7c15cd8SSteven Rostedt (Red Hat)  * change and view those values.
471e7c15cd8SSteven Rostedt (Red Hat)  */
472e7c15cd8SSteven Rostedt (Red Hat) static int init_tracefs(void)
473e7c15cd8SSteven Rostedt (Red Hat) {
474e7c15cd8SSteven Rostedt (Red Hat) 	struct dentry *d_tracer;
475e7c15cd8SSteven Rostedt (Red Hat) 	struct dentry *top_dir;
476e7c15cd8SSteven Rostedt (Red Hat) 
477e7c15cd8SSteven Rostedt (Red Hat) 	d_tracer = tracing_init_dentry();
478e7c15cd8SSteven Rostedt (Red Hat) 	if (IS_ERR(d_tracer))
479e7c15cd8SSteven Rostedt (Red Hat) 		return -ENOMEM;
480e7c15cd8SSteven Rostedt (Red Hat) 
481e7c15cd8SSteven Rostedt (Red Hat) 	top_dir = tracefs_create_dir("hwlat_detector", d_tracer);
482e7c15cd8SSteven Rostedt (Red Hat) 	if (!top_dir)
483e7c15cd8SSteven Rostedt (Red Hat) 		return -ENOMEM;
484e7c15cd8SSteven Rostedt (Red Hat) 
485e7c15cd8SSteven Rostedt (Red Hat) 	hwlat_sample_window = tracefs_create_file("window", 0640,
486e7c15cd8SSteven Rostedt (Red Hat) 						  top_dir,
487e7c15cd8SSteven Rostedt (Red Hat) 						  &hwlat_data.sample_window,
488e7c15cd8SSteven Rostedt (Red Hat) 						  &window_fops);
489e7c15cd8SSteven Rostedt (Red Hat) 	if (!hwlat_sample_window)
490e7c15cd8SSteven Rostedt (Red Hat) 		goto err;
491e7c15cd8SSteven Rostedt (Red Hat) 
492e7c15cd8SSteven Rostedt (Red Hat) 	hwlat_sample_width = tracefs_create_file("width", 0644,
493e7c15cd8SSteven Rostedt (Red Hat) 						 top_dir,
494e7c15cd8SSteven Rostedt (Red Hat) 						 &hwlat_data.sample_width,
495e7c15cd8SSteven Rostedt (Red Hat) 						 &width_fops);
496e7c15cd8SSteven Rostedt (Red Hat) 	if (!hwlat_sample_width)
497e7c15cd8SSteven Rostedt (Red Hat) 		goto err;
498e7c15cd8SSteven Rostedt (Red Hat) 
499e7c15cd8SSteven Rostedt (Red Hat) 	return 0;
500e7c15cd8SSteven Rostedt (Red Hat) 
501e7c15cd8SSteven Rostedt (Red Hat)  err:
502e7c15cd8SSteven Rostedt (Red Hat) 	tracefs_remove_recursive(top_dir);
503e7c15cd8SSteven Rostedt (Red Hat) 	return -ENOMEM;
504e7c15cd8SSteven Rostedt (Red Hat) }
505e7c15cd8SSteven Rostedt (Red Hat) 
506e7c15cd8SSteven Rostedt (Red Hat) static void hwlat_tracer_start(struct trace_array *tr)
507e7c15cd8SSteven Rostedt (Red Hat) {
508e7c15cd8SSteven Rostedt (Red Hat) 	int err;
509e7c15cd8SSteven Rostedt (Red Hat) 
510e7c15cd8SSteven Rostedt (Red Hat) 	err = start_kthread(tr);
511e7c15cd8SSteven Rostedt (Red Hat) 	if (err)
512e7c15cd8SSteven Rostedt (Red Hat) 		pr_err(BANNER "Cannot start hwlat kthread\n");
513e7c15cd8SSteven Rostedt (Red Hat) }
514e7c15cd8SSteven Rostedt (Red Hat) 
515e7c15cd8SSteven Rostedt (Red Hat) static void hwlat_tracer_stop(struct trace_array *tr)
516e7c15cd8SSteven Rostedt (Red Hat) {
517e7c15cd8SSteven Rostedt (Red Hat) 	stop_kthread();
518e7c15cd8SSteven Rostedt (Red Hat) }
519e7c15cd8SSteven Rostedt (Red Hat) 
520e7c15cd8SSteven Rostedt (Red Hat) static bool hwlat_busy;
521e7c15cd8SSteven Rostedt (Red Hat) 
522e7c15cd8SSteven Rostedt (Red Hat) static int hwlat_tracer_init(struct trace_array *tr)
523e7c15cd8SSteven Rostedt (Red Hat) {
524e7c15cd8SSteven Rostedt (Red Hat) 	/* Only allow one instance to enable this */
525e7c15cd8SSteven Rostedt (Red Hat) 	if (hwlat_busy)
526e7c15cd8SSteven Rostedt (Red Hat) 		return -EBUSY;
527e7c15cd8SSteven Rostedt (Red Hat) 
528e7c15cd8SSteven Rostedt (Red Hat) 	hwlat_trace = tr;
529e7c15cd8SSteven Rostedt (Red Hat) 
530*0330f7aaSSteven Rostedt (Red Hat) 	disable_migrate = false;
531e7c15cd8SSteven Rostedt (Red Hat) 	hwlat_data.count = 0;
532e7c15cd8SSteven Rostedt (Red Hat) 	tr->max_latency = 0;
533e7c15cd8SSteven Rostedt (Red Hat) 	save_tracing_thresh = tracing_thresh;
534e7c15cd8SSteven Rostedt (Red Hat) 
535e7c15cd8SSteven Rostedt (Red Hat) 	/* tracing_thresh is in nsecs, we speak in usecs */
536e7c15cd8SSteven Rostedt (Red Hat) 	if (!tracing_thresh)
537e7c15cd8SSteven Rostedt (Red Hat) 		tracing_thresh = last_tracing_thresh;
538e7c15cd8SSteven Rostedt (Red Hat) 
539e7c15cd8SSteven Rostedt (Red Hat) 	if (tracer_tracing_is_on(tr))
540e7c15cd8SSteven Rostedt (Red Hat) 		hwlat_tracer_start(tr);
541e7c15cd8SSteven Rostedt (Red Hat) 
542e7c15cd8SSteven Rostedt (Red Hat) 	hwlat_busy = true;
543e7c15cd8SSteven Rostedt (Red Hat) 
544e7c15cd8SSteven Rostedt (Red Hat) 	return 0;
545e7c15cd8SSteven Rostedt (Red Hat) }
546e7c15cd8SSteven Rostedt (Red Hat) 
547e7c15cd8SSteven Rostedt (Red Hat) static void hwlat_tracer_reset(struct trace_array *tr)
548e7c15cd8SSteven Rostedt (Red Hat) {
549e7c15cd8SSteven Rostedt (Red Hat) 	stop_kthread();
550e7c15cd8SSteven Rostedt (Red Hat) 
551e7c15cd8SSteven Rostedt (Red Hat) 	/* the tracing threshold is static between runs */
552e7c15cd8SSteven Rostedt (Red Hat) 	last_tracing_thresh = tracing_thresh;
553e7c15cd8SSteven Rostedt (Red Hat) 
554e7c15cd8SSteven Rostedt (Red Hat) 	tracing_thresh = save_tracing_thresh;
555e7c15cd8SSteven Rostedt (Red Hat) 	hwlat_busy = false;
556e7c15cd8SSteven Rostedt (Red Hat) }
557e7c15cd8SSteven Rostedt (Red Hat) 
558e7c15cd8SSteven Rostedt (Red Hat) static struct tracer hwlat_tracer __read_mostly =
559e7c15cd8SSteven Rostedt (Red Hat) {
560e7c15cd8SSteven Rostedt (Red Hat) 	.name		= "hwlat",
561e7c15cd8SSteven Rostedt (Red Hat) 	.init		= hwlat_tracer_init,
562e7c15cd8SSteven Rostedt (Red Hat) 	.reset		= hwlat_tracer_reset,
563e7c15cd8SSteven Rostedt (Red Hat) 	.start		= hwlat_tracer_start,
564e7c15cd8SSteven Rostedt (Red Hat) 	.stop		= hwlat_tracer_stop,
565e7c15cd8SSteven Rostedt (Red Hat) 	.allow_instances = true,
566e7c15cd8SSteven Rostedt (Red Hat) };
567e7c15cd8SSteven Rostedt (Red Hat) 
568e7c15cd8SSteven Rostedt (Red Hat) __init static int init_hwlat_tracer(void)
569e7c15cd8SSteven Rostedt (Red Hat) {
570e7c15cd8SSteven Rostedt (Red Hat) 	int ret;
571e7c15cd8SSteven Rostedt (Red Hat) 
572e7c15cd8SSteven Rostedt (Red Hat) 	mutex_init(&hwlat_data.lock);
573e7c15cd8SSteven Rostedt (Red Hat) 
574e7c15cd8SSteven Rostedt (Red Hat) 	ret = register_tracer(&hwlat_tracer);
575e7c15cd8SSteven Rostedt (Red Hat) 	if (ret)
576e7c15cd8SSteven Rostedt (Red Hat) 		return ret;
577e7c15cd8SSteven Rostedt (Red Hat) 
578e7c15cd8SSteven Rostedt (Red Hat) 	init_tracefs();
579e7c15cd8SSteven Rostedt (Red Hat) 
580e7c15cd8SSteven Rostedt (Red Hat) 	return 0;
581e7c15cd8SSteven Rostedt (Red Hat) }
582e7c15cd8SSteven Rostedt (Red Hat) late_initcall(init_hwlat_tracer);
583