xref: /linux-6.15/kernel/latencytop.c (revision 1751f872)
1b886d83cSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29745512cSArjan van de Ven /*
39745512cSArjan van de Ven  * latencytop.c: Latency display infrastructure
49745512cSArjan van de Ven  *
59745512cSArjan van de Ven  * (C) Copyright 2008 Intel Corporation
69745512cSArjan van de Ven  * Author: Arjan van de Ven <[email protected]>
79745512cSArjan van de Ven  */
8ad0b0fd5SArjan van de Ven 
9ad0b0fd5SArjan van de Ven /*
10ad0b0fd5SArjan van de Ven  * CONFIG_LATENCYTOP enables a kernel latency tracking infrastructure that is
11ad0b0fd5SArjan van de Ven  * used by the "latencytop" userspace tool. The latency that is tracked is not
12ad0b0fd5SArjan van de Ven  * the 'traditional' interrupt latency (which is primarily caused by something
13ad0b0fd5SArjan van de Ven  * else consuming CPU), but instead, it is the latency an application encounters
14ad0b0fd5SArjan van de Ven  * because the kernel sleeps on its behalf for various reasons.
15ad0b0fd5SArjan van de Ven  *
16ad0b0fd5SArjan van de Ven  * This code tracks 2 levels of statistics:
17ad0b0fd5SArjan van de Ven  * 1) System level latency
18ad0b0fd5SArjan van de Ven  * 2) Per process latency
19ad0b0fd5SArjan van de Ven  *
20ad0b0fd5SArjan van de Ven  * The latency is stored in fixed sized data structures in an accumulated form;
21ad0b0fd5SArjan van de Ven  * if the "same" latency cause is hit twice, this will be tracked as one entry
22ad0b0fd5SArjan van de Ven  * in the data structure. Both the count, total accumulated latency and maximum
23ad0b0fd5SArjan van de Ven  * latency are tracked in this data structure. When the fixed size structure is
24ad0b0fd5SArjan van de Ven  * full, no new causes are tracked until the buffer is flushed by writing to
25ad0b0fd5SArjan van de Ven  * the /proc file; the userspace tool does this on a regular basis.
26ad0b0fd5SArjan van de Ven  *
27ad0b0fd5SArjan van de Ven  * A latency cause is identified by a stringified backtrace at the point that
28ad0b0fd5SArjan van de Ven  * the scheduler gets invoked. The userland tool will use this string to
29ad0b0fd5SArjan van de Ven  * identify the cause of the latency in human readable form.
30ad0b0fd5SArjan van de Ven  *
31ad0b0fd5SArjan van de Ven  * The information is exported via /proc/latency_stats and /proc/<pid>/latency.
32ad0b0fd5SArjan van de Ven  * These files look like this:
33ad0b0fd5SArjan van de Ven  *
34ad0b0fd5SArjan van de Ven  * Latency Top version : v0.1
35ad0b0fd5SArjan van de Ven  * 70 59433 4897 i915_irq_wait drm_ioctl vfs_ioctl do_vfs_ioctl sys_ioctl
36ad0b0fd5SArjan van de Ven  * |    |    |    |
37ad0b0fd5SArjan van de Ven  * |    |    |    +----> the stringified backtrace
38ad0b0fd5SArjan van de Ven  * |    |    +---------> The maximum latency for this entry in microseconds
39ad0b0fd5SArjan van de Ven  * |    +--------------> The accumulated latency for this entry (microseconds)
40ad0b0fd5SArjan van de Ven  * +-------------------> The number of times this entry is hit
41ad0b0fd5SArjan van de Ven  *
42ad0b0fd5SArjan van de Ven  * (note: the average latency is the accumulated latency divided by the number
43ad0b0fd5SArjan van de Ven  * of times)
44ad0b0fd5SArjan van de Ven  */
45ad0b0fd5SArjan van de Ven 
469745512cSArjan van de Ven #include <linux/kallsyms.h>
479745512cSArjan van de Ven #include <linux/seq_file.h>
489745512cSArjan van de Ven #include <linux/notifier.h>
499745512cSArjan van de Ven #include <linux/spinlock.h>
509745512cSArjan van de Ven #include <linux/proc_fs.h>
51cb251765SMel Gorman #include <linux/latencytop.h>
529984de1aSPaul Gortmaker #include <linux/export.h>
539745512cSArjan van de Ven #include <linux/sched.h>
54b17b0153SIngo Molnar #include <linux/sched/debug.h>
553905f9adSIngo Molnar #include <linux/sched/stat.h>
569745512cSArjan van de Ven #include <linux/list.h>
579745512cSArjan van de Ven #include <linux/stacktrace.h>
58988f11e0Sliaohua #include <linux/sysctl.h>
599745512cSArjan van de Ven 
60757455d4SThomas Gleixner static DEFINE_RAW_SPINLOCK(latency_lock);
619745512cSArjan van de Ven 
629745512cSArjan van de Ven #define MAXLR 128
639745512cSArjan van de Ven static struct latency_record latency_record[MAXLR];
649745512cSArjan van de Ven 
659745512cSArjan van de Ven int latencytop_enabled;
669745512cSArjan van de Ven 
67988f11e0Sliaohua #ifdef CONFIG_SYSCTL
sysctl_latencytop(const struct ctl_table * table,int write,void * buffer,size_t * lenp,loff_t * ppos)6878eb4ea2SJoel Granados static int sysctl_latencytop(const struct ctl_table *table, int write, void *buffer,
69988f11e0Sliaohua 		size_t *lenp, loff_t *ppos)
70988f11e0Sliaohua {
71988f11e0Sliaohua 	int err;
72988f11e0Sliaohua 
73988f11e0Sliaohua 	err = proc_dointvec(table, write, buffer, lenp, ppos);
74988f11e0Sliaohua 	if (latencytop_enabled)
75988f11e0Sliaohua 		force_schedstat_enabled();
76988f11e0Sliaohua 
77988f11e0Sliaohua 	return err;
78988f11e0Sliaohua }
79988f11e0Sliaohua 
80*1751f872SJoel Granados static const struct ctl_table latencytop_sysctl[] = {
81988f11e0Sliaohua 	{
82988f11e0Sliaohua 		.procname   = "latencytop",
83988f11e0Sliaohua 		.data       = &latencytop_enabled,
84988f11e0Sliaohua 		.maxlen     = sizeof(int),
85988f11e0Sliaohua 		.mode       = 0644,
86988f11e0Sliaohua 		.proc_handler   = sysctl_latencytop,
87988f11e0Sliaohua 	},
88988f11e0Sliaohua };
89988f11e0Sliaohua #endif
90988f11e0Sliaohua 
clear_tsk_latency_tracing(struct task_struct * p)91e02c9b0dSLin Feng void clear_tsk_latency_tracing(struct task_struct *p)
929745512cSArjan van de Ven {
939745512cSArjan van de Ven 	unsigned long flags;
949745512cSArjan van de Ven 
95757455d4SThomas Gleixner 	raw_spin_lock_irqsave(&latency_lock, flags);
969745512cSArjan van de Ven 	memset(&p->latency_record, 0, sizeof(p->latency_record));
979745512cSArjan van de Ven 	p->latency_record_count = 0;
98757455d4SThomas Gleixner 	raw_spin_unlock_irqrestore(&latency_lock, flags);
999745512cSArjan van de Ven }
1009745512cSArjan van de Ven 
clear_global_latency_tracing(void)1019745512cSArjan van de Ven static void clear_global_latency_tracing(void)
1029745512cSArjan van de Ven {
1039745512cSArjan van de Ven 	unsigned long flags;
1049745512cSArjan van de Ven 
105757455d4SThomas Gleixner 	raw_spin_lock_irqsave(&latency_lock, flags);
1069745512cSArjan van de Ven 	memset(&latency_record, 0, sizeof(latency_record));
107757455d4SThomas Gleixner 	raw_spin_unlock_irqrestore(&latency_lock, flags);
1089745512cSArjan van de Ven }
1099745512cSArjan van de Ven 
1109745512cSArjan van de Ven static void __sched
account_global_scheduler_latency(struct task_struct * tsk,struct latency_record * lat)111eaa1809bSFabian Frederick account_global_scheduler_latency(struct task_struct *tsk,
112eaa1809bSFabian Frederick 				 struct latency_record *lat)
1139745512cSArjan van de Ven {
114b8147511Swuchi 	int firstnonnull = MAXLR;
1159745512cSArjan van de Ven 	int i;
1169745512cSArjan van de Ven 
1179745512cSArjan van de Ven 	/* skip kernel threads for now */
1189745512cSArjan van de Ven 	if (!tsk->mm)
1199745512cSArjan van de Ven 		return;
1209745512cSArjan van de Ven 
1219745512cSArjan van de Ven 	for (i = 0; i < MAXLR; i++) {
12219fb518cSDmitry Adamushko 		int q, same = 1;
12319fb518cSDmitry Adamushko 
1249745512cSArjan van de Ven 		/* Nothing stored: */
1259745512cSArjan van de Ven 		if (!latency_record[i].backtrace[0]) {
1269745512cSArjan van de Ven 			if (firstnonnull > i)
1279745512cSArjan van de Ven 				firstnonnull = i;
1289745512cSArjan van de Ven 			continue;
1299745512cSArjan van de Ven 		}
1309745512cSArjan van de Ven 		for (q = 0; q < LT_BACKTRACEDEPTH; q++) {
13119fb518cSDmitry Adamushko 			unsigned long record = lat->backtrace[q];
13219fb518cSDmitry Adamushko 
13319fb518cSDmitry Adamushko 			if (latency_record[i].backtrace[q] != record) {
1349745512cSArjan van de Ven 				same = 0;
1359745512cSArjan van de Ven 				break;
13619fb518cSDmitry Adamushko 			}
13719fb518cSDmitry Adamushko 
138accddc41SThomas Gleixner 			/* 0 entry marks end of backtrace: */
139accddc41SThomas Gleixner 			if (!record)
1409745512cSArjan van de Ven 				break;
1419745512cSArjan van de Ven 		}
1429745512cSArjan van de Ven 		if (same) {
1439745512cSArjan van de Ven 			latency_record[i].count++;
1449745512cSArjan van de Ven 			latency_record[i].time += lat->time;
1459745512cSArjan van de Ven 			if (lat->time > latency_record[i].max)
1469745512cSArjan van de Ven 				latency_record[i].max = lat->time;
1479745512cSArjan van de Ven 			return;
1489745512cSArjan van de Ven 		}
1499745512cSArjan van de Ven 	}
1509745512cSArjan van de Ven 
1519745512cSArjan van de Ven 	i = firstnonnull;
152b8147511Swuchi 	if (i >= MAXLR)
1539745512cSArjan van de Ven 		return;
1549745512cSArjan van de Ven 
1559745512cSArjan van de Ven 	/* Allocted a new one: */
1569745512cSArjan van de Ven 	memcpy(&latency_record[i], lat, sizeof(struct latency_record));
1579745512cSArjan van de Ven }
1589745512cSArjan van de Ven 
159ad0b0fd5SArjan van de Ven /**
16025985edcSLucas De Marchi  * __account_scheduler_latency - record an occurred latency
16169079443SRandy Dunlap  * @tsk: the task struct of the task hitting the latency
16269079443SRandy Dunlap  * @usecs: the duration of the latency in microseconds
16369079443SRandy Dunlap  * @inter: 1 if the sleep was interruptible, 0 if uninterruptible
164ad0b0fd5SArjan van de Ven  *
165ad0b0fd5SArjan van de Ven  * This function is the main entry point for recording latency entries
166ad0b0fd5SArjan van de Ven  * as called by the scheduler.
167ad0b0fd5SArjan van de Ven  *
168ad0b0fd5SArjan van de Ven  * This function has a few special cases to deal with normal 'non-latency'
169ad0b0fd5SArjan van de Ven  * sleeps: specifically, interruptible sleep longer than 5 msec is skipped
170ad0b0fd5SArjan van de Ven  * since this usually is caused by waiting for events via select() and co.
171ad0b0fd5SArjan van de Ven  *
172ad0b0fd5SArjan van de Ven  * Negative latencies (caused by time going backwards) are also explicitly
173ad0b0fd5SArjan van de Ven  * skipped.
174ad0b0fd5SArjan van de Ven  */
1759745512cSArjan van de Ven void __sched
__account_scheduler_latency(struct task_struct * tsk,int usecs,int inter)176ad0b0fd5SArjan van de Ven __account_scheduler_latency(struct task_struct *tsk, int usecs, int inter)
1779745512cSArjan van de Ven {
1789745512cSArjan van de Ven 	unsigned long flags;
1799745512cSArjan van de Ven 	int i, q;
1809745512cSArjan van de Ven 	struct latency_record lat;
1819745512cSArjan van de Ven 
1829745512cSArjan van de Ven 	/* Long interruptible waits are generally user requested... */
1839745512cSArjan van de Ven 	if (inter && usecs > 5000)
1849745512cSArjan van de Ven 		return;
1859745512cSArjan van de Ven 
186ad0b0fd5SArjan van de Ven 	/* Negative sleeps are time going backwards */
187ad0b0fd5SArjan van de Ven 	/* Zero-time sleeps are non-interesting */
188ad0b0fd5SArjan van de Ven 	if (usecs <= 0)
189ad0b0fd5SArjan van de Ven 		return;
190ad0b0fd5SArjan van de Ven 
1919745512cSArjan van de Ven 	memset(&lat, 0, sizeof(lat));
1929745512cSArjan van de Ven 	lat.count = 1;
1939745512cSArjan van de Ven 	lat.time = usecs;
1949745512cSArjan van de Ven 	lat.max = usecs;
195f9387721SThomas Gleixner 
196f9387721SThomas Gleixner 	stack_trace_save_tsk(tsk, lat.backtrace, LT_BACKTRACEDEPTH, 0);
1979745512cSArjan van de Ven 
198757455d4SThomas Gleixner 	raw_spin_lock_irqsave(&latency_lock, flags);
1999745512cSArjan van de Ven 
2009745512cSArjan van de Ven 	account_global_scheduler_latency(tsk, &lat);
2019745512cSArjan van de Ven 
20238715258SKen Chen 	for (i = 0; i < tsk->latency_record_count; i++) {
2039745512cSArjan van de Ven 		struct latency_record *mylat;
2049745512cSArjan van de Ven 		int same = 1;
20519fb518cSDmitry Adamushko 
2069745512cSArjan van de Ven 		mylat = &tsk->latency_record[i];
2079745512cSArjan van de Ven 		for (q = 0; q < LT_BACKTRACEDEPTH; q++) {
20819fb518cSDmitry Adamushko 			unsigned long record = lat.backtrace[q];
20919fb518cSDmitry Adamushko 
21019fb518cSDmitry Adamushko 			if (mylat->backtrace[q] != record) {
2119745512cSArjan van de Ven 				same = 0;
2129745512cSArjan van de Ven 				break;
21319fb518cSDmitry Adamushko 			}
21419fb518cSDmitry Adamushko 
215accddc41SThomas Gleixner 			/* 0 entry is end of backtrace */
216accddc41SThomas Gleixner 			if (!record)
2179745512cSArjan van de Ven 				break;
2189745512cSArjan van de Ven 		}
2199745512cSArjan van de Ven 		if (same) {
2209745512cSArjan van de Ven 			mylat->count++;
2219745512cSArjan van de Ven 			mylat->time += lat.time;
2229745512cSArjan van de Ven 			if (lat.time > mylat->max)
2239745512cSArjan van de Ven 				mylat->max = lat.time;
2249745512cSArjan van de Ven 			goto out_unlock;
2259745512cSArjan van de Ven 		}
2269745512cSArjan van de Ven 	}
2279745512cSArjan van de Ven 
22838715258SKen Chen 	/*
22938715258SKen Chen 	 * short term hack; if we're > 32 we stop; future we recycle:
23038715258SKen Chen 	 */
23138715258SKen Chen 	if (tsk->latency_record_count >= LT_SAVECOUNT)
23238715258SKen Chen 		goto out_unlock;
23338715258SKen Chen 
2349745512cSArjan van de Ven 	/* Allocated a new one: */
23538715258SKen Chen 	i = tsk->latency_record_count++;
2369745512cSArjan van de Ven 	memcpy(&tsk->latency_record[i], &lat, sizeof(struct latency_record));
2379745512cSArjan van de Ven 
2389745512cSArjan van de Ven out_unlock:
239757455d4SThomas Gleixner 	raw_spin_unlock_irqrestore(&latency_lock, flags);
2409745512cSArjan van de Ven }
2419745512cSArjan van de Ven 
lstats_show(struct seq_file * m,void * v)2429745512cSArjan van de Ven static int lstats_show(struct seq_file *m, void *v)
2439745512cSArjan van de Ven {
2449745512cSArjan van de Ven 	int i;
2459745512cSArjan van de Ven 
2469745512cSArjan van de Ven 	seq_puts(m, "Latency Top version : v0.1\n");
2479745512cSArjan van de Ven 
2489745512cSArjan van de Ven 	for (i = 0; i < MAXLR; i++) {
24934e49d4fSJoe Perches 		struct latency_record *lr = &latency_record[i];
25034e49d4fSJoe Perches 
25134e49d4fSJoe Perches 		if (lr->backtrace[0]) {
2529745512cSArjan van de Ven 			int q;
253ad0b0fd5SArjan van de Ven 			seq_printf(m, "%i %lu %lu",
25434e49d4fSJoe Perches 				   lr->count, lr->time, lr->max);
2559745512cSArjan van de Ven 			for (q = 0; q < LT_BACKTRACEDEPTH; q++) {
25634e49d4fSJoe Perches 				unsigned long bt = lr->backtrace[q];
257accddc41SThomas Gleixner 
25834e49d4fSJoe Perches 				if (!bt)
2599745512cSArjan van de Ven 					break;
260accddc41SThomas Gleixner 
26134e49d4fSJoe Perches 				seq_printf(m, " %ps", (void *)bt);
2629745512cSArjan van de Ven 			}
263eaa1809bSFabian Frederick 			seq_puts(m, "\n");
2649745512cSArjan van de Ven 		}
2659745512cSArjan van de Ven 	}
2669745512cSArjan van de Ven 	return 0;
2679745512cSArjan van de Ven }
2689745512cSArjan van de Ven 
2699745512cSArjan van de Ven static ssize_t
lstats_write(struct file * file,const char __user * buf,size_t count,loff_t * offs)2709745512cSArjan van de Ven lstats_write(struct file *file, const char __user *buf, size_t count,
2719745512cSArjan van de Ven 	     loff_t *offs)
2729745512cSArjan van de Ven {
2739745512cSArjan van de Ven 	clear_global_latency_tracing();
2749745512cSArjan van de Ven 
2759745512cSArjan van de Ven 	return count;
2769745512cSArjan van de Ven }
2779745512cSArjan van de Ven 
lstats_open(struct inode * inode,struct file * filp)2789745512cSArjan van de Ven static int lstats_open(struct inode *inode, struct file *filp)
2799745512cSArjan van de Ven {
2809745512cSArjan van de Ven 	return single_open(filp, lstats_show, NULL);
2819745512cSArjan van de Ven }
2829745512cSArjan van de Ven 
28397a32539SAlexey Dobriyan static const struct proc_ops lstats_proc_ops = {
28497a32539SAlexey Dobriyan 	.proc_open	= lstats_open,
28597a32539SAlexey Dobriyan 	.proc_read	= seq_read,
28697a32539SAlexey Dobriyan 	.proc_write	= lstats_write,
28797a32539SAlexey Dobriyan 	.proc_lseek	= seq_lseek,
28897a32539SAlexey Dobriyan 	.proc_release	= single_release,
2899745512cSArjan van de Ven };
2909745512cSArjan van de Ven 
init_lstats_procfs(void)2919745512cSArjan van de Ven static int __init init_lstats_procfs(void)
2929745512cSArjan van de Ven {
29397a32539SAlexey Dobriyan 	proc_create("latency_stats", 0644, NULL, &lstats_proc_ops);
294988f11e0Sliaohua #ifdef CONFIG_SYSCTL
295988f11e0Sliaohua 	register_sysctl_init("kernel", latencytop_sysctl);
296988f11e0Sliaohua #endif
2979745512cSArjan van de Ven 	return 0;
2989745512cSArjan van de Ven }
299ad0b0fd5SArjan van de Ven device_initcall(init_lstats_procfs);
300