| .. | .. | 
|---|
| 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 |   | 
|---|