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