.. | .. |
---|
1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
---|
2 | 2 | /* |
---|
3 | | - * trace_hwlatdetect.c - A simple Hardware Latency detector. |
---|
| 3 | + * trace_hwlat.c - A simple Hardware Latency detector. |
---|
4 | 4 | * |
---|
5 | 5 | * Use this tracer to detect large system latencies induced by the behavior of |
---|
6 | 6 | * certain underlying system hardware or firmware, independent of Linux itself. |
---|
.. | .. |
---|
83 | 83 | u64 nmi_total_ts; /* Total time spent in NMIs */ |
---|
84 | 84 | struct timespec64 timestamp; /* wall time */ |
---|
85 | 85 | int nmi_count; /* # NMIs during this sample */ |
---|
| 86 | + int count; /* # of iteratons over threash */ |
---|
86 | 87 | }; |
---|
87 | 88 | |
---|
88 | 89 | /* keep the global state somewhere. */ |
---|
.. | .. |
---|
104 | 105 | { |
---|
105 | 106 | struct trace_array *tr = hwlat_trace; |
---|
106 | 107 | struct trace_event_call *call = &event_hwlat; |
---|
107 | | - struct ring_buffer *buffer = tr->trace_buffer.buffer; |
---|
| 108 | + struct trace_buffer *buffer = tr->array_buffer.buffer; |
---|
108 | 109 | struct ring_buffer_event *event; |
---|
109 | 110 | struct hwlat_entry *entry; |
---|
110 | | - unsigned long flags; |
---|
111 | | - int pc; |
---|
112 | | - |
---|
113 | | - pc = preempt_count(); |
---|
114 | | - local_save_flags(flags); |
---|
115 | 111 | |
---|
116 | 112 | event = trace_buffer_lock_reserve(buffer, TRACE_HWLAT, sizeof(*entry), |
---|
117 | | - flags, pc); |
---|
| 113 | + tracing_gen_ctx()); |
---|
118 | 114 | if (!event) |
---|
119 | 115 | return; |
---|
120 | 116 | entry = ring_buffer_event_data(event); |
---|
.. | .. |
---|
124 | 120 | entry->timestamp = sample->timestamp; |
---|
125 | 121 | entry->nmi_total_ts = sample->nmi_total_ts; |
---|
126 | 122 | entry->nmi_count = sample->nmi_count; |
---|
| 123 | + entry->count = sample->count; |
---|
127 | 124 | |
---|
128 | 125 | if (!call_filter_check_discard(call, entry, buffer, event)) |
---|
129 | 126 | trace_buffer_unlock_commit_nostack(buffer, event); |
---|
.. | .. |
---|
167 | 164 | static int get_sample(void) |
---|
168 | 165 | { |
---|
169 | 166 | struct trace_array *tr = hwlat_trace; |
---|
| 167 | + struct hwlat_sample s; |
---|
170 | 168 | time_type start, t1, t2, last_t2; |
---|
171 | | - s64 diff, total, last_total = 0; |
---|
| 169 | + s64 diff, outer_diff, total, last_total = 0; |
---|
172 | 170 | u64 sample = 0; |
---|
173 | 171 | u64 thresh = tracing_thresh; |
---|
174 | 172 | u64 outer_sample = 0; |
---|
175 | 173 | int ret = -1; |
---|
| 174 | + unsigned int count = 0; |
---|
176 | 175 | |
---|
177 | 176 | do_div(thresh, NSEC_PER_USEC); /* modifies interval value */ |
---|
178 | 177 | |
---|
.. | .. |
---|
186 | 185 | |
---|
187 | 186 | init_time(last_t2, 0); |
---|
188 | 187 | start = time_get(); /* start timestamp */ |
---|
| 188 | + outer_diff = 0; |
---|
189 | 189 | |
---|
190 | 190 | do { |
---|
191 | 191 | |
---|
.. | .. |
---|
194 | 194 | |
---|
195 | 195 | if (time_u64(last_t2)) { |
---|
196 | 196 | /* Check the delta from outer loop (t2 to next t1) */ |
---|
197 | | - diff = time_to_us(time_sub(t1, last_t2)); |
---|
| 197 | + outer_diff = time_to_us(time_sub(t1, last_t2)); |
---|
198 | 198 | /* This shouldn't happen */ |
---|
199 | | - if (diff < 0) { |
---|
| 199 | + if (outer_diff < 0) { |
---|
200 | 200 | pr_err(BANNER "time running backwards\n"); |
---|
201 | 201 | goto out; |
---|
202 | 202 | } |
---|
203 | | - if (diff > outer_sample) |
---|
204 | | - outer_sample = diff; |
---|
| 203 | + if (outer_diff > outer_sample) |
---|
| 204 | + outer_sample = outer_diff; |
---|
205 | 205 | } |
---|
206 | 206 | last_t2 = t2; |
---|
207 | 207 | |
---|
.. | .. |
---|
216 | 216 | |
---|
217 | 217 | /* This checks the inner loop (t1 to t2) */ |
---|
218 | 218 | diff = time_to_us(time_sub(t2, t1)); /* current diff */ |
---|
| 219 | + |
---|
| 220 | + if (diff > thresh || outer_diff > thresh) { |
---|
| 221 | + if (!count) |
---|
| 222 | + ktime_get_real_ts64(&s.timestamp); |
---|
| 223 | + count++; |
---|
| 224 | + } |
---|
219 | 225 | |
---|
220 | 226 | /* This shouldn't happen */ |
---|
221 | 227 | if (diff < 0) { |
---|
.. | .. |
---|
236 | 242 | |
---|
237 | 243 | /* If we exceed the threshold value, we have found a hardware latency */ |
---|
238 | 244 | if (sample > thresh || outer_sample > thresh) { |
---|
239 | | - struct hwlat_sample s; |
---|
| 245 | + u64 latency; |
---|
240 | 246 | |
---|
241 | 247 | ret = 1; |
---|
242 | 248 | |
---|
.. | .. |
---|
248 | 254 | s.seqnum = hwlat_data.count; |
---|
249 | 255 | s.duration = sample; |
---|
250 | 256 | s.outer_duration = outer_sample; |
---|
251 | | - ktime_get_real_ts64(&s.timestamp); |
---|
252 | 257 | s.nmi_total_ts = nmi_total_ts; |
---|
253 | 258 | s.nmi_count = nmi_count; |
---|
| 259 | + s.count = count; |
---|
254 | 260 | trace_hwlat_sample(&s); |
---|
255 | 261 | |
---|
| 262 | + latency = max(sample, outer_sample); |
---|
| 263 | + |
---|
256 | 264 | /* Keep a running maximum ever recorded hardware latency */ |
---|
257 | | - if (sample > tr->max_latency) |
---|
258 | | - tr->max_latency = sample; |
---|
259 | | - if (outer_sample > tr->max_latency) |
---|
260 | | - tr->max_latency = outer_sample; |
---|
| 265 | + if (latency > tr->max_latency) { |
---|
| 266 | + tr->max_latency = latency; |
---|
| 267 | + latency_fsnotify(tr); |
---|
| 268 | + } |
---|
261 | 269 | } |
---|
262 | 270 | |
---|
263 | 271 | out: |
---|
.. | .. |
---|
277 | 285 | return; |
---|
278 | 286 | /* |
---|
279 | 287 | * If for some reason the user modifies the CPU affinity |
---|
280 | | - * of this thread, than stop migrating for the duration |
---|
| 288 | + * of this thread, then stop migrating for the duration |
---|
281 | 289 | * of the current test. |
---|
282 | 290 | */ |
---|
283 | 291 | if (!cpumask_equal(current_mask, current->cpus_ptr)) |
---|
.. | .. |
---|
359 | 367 | return 0; |
---|
360 | 368 | |
---|
361 | 369 | /* Just pick the first CPU on first iteration */ |
---|
362 | | - current_mask = &save_cpumask; |
---|
363 | 370 | get_online_cpus(); |
---|
364 | 371 | cpumask_and(current_mask, cpu_online_mask, tr->tracing_cpumask); |
---|
365 | 372 | put_online_cpus(); |
---|
.. | .. |
---|
526 | 533 | */ |
---|
527 | 534 | static int init_tracefs(void) |
---|
528 | 535 | { |
---|
529 | | - struct dentry *d_tracer; |
---|
| 536 | + int ret; |
---|
530 | 537 | struct dentry *top_dir; |
---|
531 | 538 | |
---|
532 | | - d_tracer = tracing_init_dentry(); |
---|
533 | | - if (IS_ERR(d_tracer)) |
---|
| 539 | + ret = tracing_init_dentry(); |
---|
| 540 | + if (ret) |
---|
534 | 541 | return -ENOMEM; |
---|
535 | 542 | |
---|
536 | | - top_dir = tracefs_create_dir("hwlat_detector", d_tracer); |
---|
| 543 | + top_dir = tracefs_create_dir("hwlat_detector", NULL); |
---|
537 | 544 | if (!top_dir) |
---|
538 | 545 | return -ENOMEM; |
---|
539 | 546 | |
---|
.. | .. |
---|
554 | 561 | return 0; |
---|
555 | 562 | |
---|
556 | 563 | err: |
---|
557 | | - tracefs_remove_recursive(top_dir); |
---|
| 564 | + tracefs_remove(top_dir); |
---|
558 | 565 | return -ENOMEM; |
---|
559 | 566 | } |
---|
560 | 567 | |
---|