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