xref: /linux-6.15/kernel/locking/lock_events.c (revision 8788c6c2)
1fb346fd9SWaiman Long /* SPDX-License-Identifier: GPL-2.0 */
2fb346fd9SWaiman Long /*
3fb346fd9SWaiman Long  * This program is free software; you can redistribute it and/or modify
4fb346fd9SWaiman Long  * it under the terms of the GNU General Public License as published by
5fb346fd9SWaiman Long  * the Free Software Foundation; either version 2 of the License, or
6fb346fd9SWaiman Long  * (at your option) any later version.
7fb346fd9SWaiman Long  *
8fb346fd9SWaiman Long  * This program is distributed in the hope that it will be useful,
9fb346fd9SWaiman Long  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10fb346fd9SWaiman Long  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11fb346fd9SWaiman Long  * GNU General Public License for more details.
12fb346fd9SWaiman Long  *
13fb346fd9SWaiman Long  * Authors: Waiman Long <[email protected]>
14fb346fd9SWaiman Long  */
15fb346fd9SWaiman Long 
16fb346fd9SWaiman Long /*
17fb346fd9SWaiman Long  * Collect locking event counts
18fb346fd9SWaiman Long  */
19fb346fd9SWaiman Long #include <linux/debugfs.h>
20fb346fd9SWaiman Long #include <linux/sched.h>
21fb346fd9SWaiman Long #include <linux/sched/clock.h>
22fb346fd9SWaiman Long #include <linux/fs.h>
23fb346fd9SWaiman Long 
24fb346fd9SWaiman Long #include "lock_events.h"
25fb346fd9SWaiman Long 
26fb346fd9SWaiman Long #undef  LOCK_EVENT
27fb346fd9SWaiman Long #define LOCK_EVENT(name)	[LOCKEVENT_ ## name] = #name,
28fb346fd9SWaiman Long 
29fb346fd9SWaiman Long #define LOCK_EVENTS_DIR		"lock_event_counts"
30fb346fd9SWaiman Long 
31fb346fd9SWaiman Long /*
32fb346fd9SWaiman Long  * When CONFIG_LOCK_EVENT_COUNTS is enabled, event counts of different
33fb346fd9SWaiman Long  * types of locks will be reported under the <debugfs>/lock_event_counts/
34fb346fd9SWaiman Long  * directory. See lock_events_list.h for the list of available locking
35fb346fd9SWaiman Long  * events.
36fb346fd9SWaiman Long  *
37fb346fd9SWaiman Long  * Writing to the special ".reset_counts" file will reset all the above
38fb346fd9SWaiman Long  * locking event counts. This is a very slow operation and so should not
39fb346fd9SWaiman Long  * be done frequently.
40fb346fd9SWaiman Long  *
41fb346fd9SWaiman Long  * These event counts are implemented as per-cpu variables which are
42fb346fd9SWaiman Long  * summed and computed whenever the corresponding debugfs files are read. This
43fb346fd9SWaiman Long  * minimizes added overhead making the counts usable even in a production
44fb346fd9SWaiman Long  * environment.
45fb346fd9SWaiman Long  */
46fb346fd9SWaiman Long static const char * const lockevent_names[lockevent_num + 1] = {
47fb346fd9SWaiman Long 
48fb346fd9SWaiman Long #include "lock_events_list.h"
49fb346fd9SWaiman Long 
50fb346fd9SWaiman Long 	[LOCKEVENT_reset_cnts] = ".reset_counts",
51fb346fd9SWaiman Long };
52fb346fd9SWaiman Long 
53fb346fd9SWaiman Long /*
54fb346fd9SWaiman Long  * Per-cpu counts
55fb346fd9SWaiman Long  */
56fb346fd9SWaiman Long DEFINE_PER_CPU(unsigned long, lockevents[lockevent_num]);
57fb346fd9SWaiman Long 
58fb346fd9SWaiman Long /*
59fb346fd9SWaiman Long  * The lockevent_read() function can be overridden.
60fb346fd9SWaiman Long  */
lockevent_read(struct file * file,char __user * user_buf,size_t count,loff_t * ppos)61fb346fd9SWaiman Long ssize_t __weak lockevent_read(struct file *file, char __user *user_buf,
62fb346fd9SWaiman Long 			      size_t count, loff_t *ppos)
63fb346fd9SWaiman Long {
64fb346fd9SWaiman Long 	char buf[64];
65fb346fd9SWaiman Long 	int cpu, id, len;
66fb346fd9SWaiman Long 	u64 sum = 0;
67fb346fd9SWaiman Long 
68fb346fd9SWaiman Long 	/*
69fb346fd9SWaiman Long 	 * Get the counter ID stored in file->f_inode->i_private
70fb346fd9SWaiman Long 	 */
71fb346fd9SWaiman Long 	id = (long)file_inode(file)->i_private;
72fb346fd9SWaiman Long 
73fb346fd9SWaiman Long 	if (id >= lockevent_num)
74fb346fd9SWaiman Long 		return -EBADF;
75fb346fd9SWaiman Long 
76fb346fd9SWaiman Long 	for_each_possible_cpu(cpu)
77fb346fd9SWaiman Long 		sum += per_cpu(lockevents[id], cpu);
78fb346fd9SWaiman Long 	len = snprintf(buf, sizeof(buf) - 1, "%llu\n", sum);
79fb346fd9SWaiman Long 
80fb346fd9SWaiman Long 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
81fb346fd9SWaiman Long }
82fb346fd9SWaiman Long 
83fb346fd9SWaiman Long /*
84fb346fd9SWaiman Long  * Function to handle write request
85fb346fd9SWaiman Long  *
86fb346fd9SWaiman Long  * When idx = reset_cnts, reset all the counts.
87fb346fd9SWaiman Long  */
lockevent_write(struct file * file,const char __user * user_buf,size_t count,loff_t * ppos)88fb346fd9SWaiman Long static ssize_t lockevent_write(struct file *file, const char __user *user_buf,
89fb346fd9SWaiman Long 			   size_t count, loff_t *ppos)
90fb346fd9SWaiman Long {
91fb346fd9SWaiman Long 	int cpu;
92fb346fd9SWaiman Long 
93fb346fd9SWaiman Long 	/*
94fb346fd9SWaiman Long 	 * Get the counter ID stored in file->f_inode->i_private
95fb346fd9SWaiman Long 	 */
96fb346fd9SWaiman Long 	if ((long)file_inode(file)->i_private != LOCKEVENT_reset_cnts)
97fb346fd9SWaiman Long 		return count;
98fb346fd9SWaiman Long 
99fb346fd9SWaiman Long 	for_each_possible_cpu(cpu) {
100fb346fd9SWaiman Long 		int i;
101fb346fd9SWaiman Long 		unsigned long *ptr = per_cpu_ptr(lockevents, cpu);
102fb346fd9SWaiman Long 
103fb346fd9SWaiman Long 		for (i = 0 ; i < lockevent_num; i++)
104fb346fd9SWaiman Long 			WRITE_ONCE(ptr[i], 0);
105fb346fd9SWaiman Long 	}
106fb346fd9SWaiman Long 	return count;
107fb346fd9SWaiman Long }
108fb346fd9SWaiman Long 
109fb346fd9SWaiman Long /*
110fb346fd9SWaiman Long  * Debugfs data structures
111fb346fd9SWaiman Long  */
112fb346fd9SWaiman Long static const struct file_operations fops_lockevent = {
113fb346fd9SWaiman Long 	.read = lockevent_read,
114fb346fd9SWaiman Long 	.write = lockevent_write,
115fb346fd9SWaiman Long 	.llseek = default_llseek,
116fb346fd9SWaiman Long };
117fb346fd9SWaiman Long 
118bf20616fSWaiman Long #ifdef CONFIG_PARAVIRT_SPINLOCKS
119bf20616fSWaiman Long #include <asm/paravirt.h>
120bf20616fSWaiman Long 
skip_lockevent(const char * name)121bf20616fSWaiman Long static bool __init skip_lockevent(const char *name)
122bf20616fSWaiman Long {
123bf20616fSWaiman Long 	static int pv_on __initdata = -1;
124bf20616fSWaiman Long 
125bf20616fSWaiman Long 	if (pv_on < 0)
126bf20616fSWaiman Long 		pv_on = !pv_is_native_spin_unlock();
127bf20616fSWaiman Long 	/*
128bf20616fSWaiman Long 	 * Skip PV qspinlock events on bare metal.
129bf20616fSWaiman Long 	 */
130bf20616fSWaiman Long 	if (!pv_on && !memcmp(name, "pv_", 3))
131bf20616fSWaiman Long 		return true;
132bf20616fSWaiman Long 	return false;
133bf20616fSWaiman Long }
134bf20616fSWaiman Long #else
skip_lockevent(const char * name)135bf20616fSWaiman Long static inline bool skip_lockevent(const char *name)
136bf20616fSWaiman Long {
137bf20616fSWaiman Long 	return false;
138bf20616fSWaiman Long }
139bf20616fSWaiman Long #endif
140bf20616fSWaiman Long 
141fb346fd9SWaiman Long /*
142fb346fd9SWaiman Long  * Initialize debugfs for the locking event counts.
143fb346fd9SWaiman Long  */
init_lockevent_counts(void)144fb346fd9SWaiman Long static int __init init_lockevent_counts(void)
145fb346fd9SWaiman Long {
146fb346fd9SWaiman Long 	struct dentry *d_counts = debugfs_create_dir(LOCK_EVENTS_DIR, NULL);
147fb346fd9SWaiman Long 	int i;
148fb346fd9SWaiman Long 
149*8788c6c2SAtul Kumar Pant 	if (IS_ERR(d_counts))
150fb346fd9SWaiman Long 		goto out;
151fb346fd9SWaiman Long 
152fb346fd9SWaiman Long 	/*
153fb346fd9SWaiman Long 	 * Create the debugfs files
154fb346fd9SWaiman Long 	 *
155fb346fd9SWaiman Long 	 * As reading from and writing to the stat files can be slow, only
156fb346fd9SWaiman Long 	 * root is allowed to do the read/write to limit impact to system
157fb346fd9SWaiman Long 	 * performance.
158fb346fd9SWaiman Long 	 */
159bf20616fSWaiman Long 	for (i = 0; i < lockevent_num; i++) {
160bf20616fSWaiman Long 		if (skip_lockevent(lockevent_names[i]))
161bf20616fSWaiman Long 			continue;
162*8788c6c2SAtul Kumar Pant 		if (IS_ERR(debugfs_create_file(lockevent_names[i], 0400, d_counts,
163*8788c6c2SAtul Kumar Pant 					 (void *)(long)i, &fops_lockevent)))
164fb346fd9SWaiman Long 			goto fail_undo;
165bf20616fSWaiman Long 	}
166fb346fd9SWaiman Long 
167*8788c6c2SAtul Kumar Pant 	if (IS_ERR(debugfs_create_file(lockevent_names[LOCKEVENT_reset_cnts], 0200,
168fb346fd9SWaiman Long 				 d_counts, (void *)(long)LOCKEVENT_reset_cnts,
169*8788c6c2SAtul Kumar Pant 				 &fops_lockevent)))
170fb346fd9SWaiman Long 		goto fail_undo;
171fb346fd9SWaiman Long 
172fb346fd9SWaiman Long 	return 0;
173fb346fd9SWaiman Long fail_undo:
174fb346fd9SWaiman Long 	debugfs_remove_recursive(d_counts);
175fb346fd9SWaiman Long out:
176fb346fd9SWaiman Long 	pr_warn("Could not create '%s' debugfs entries\n", LOCK_EVENTS_DIR);
177fb346fd9SWaiman Long 	return -ENOMEM;
178fb346fd9SWaiman Long }
179fb346fd9SWaiman Long fs_initcall(init_lockevent_counts);
180