123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- /* SPDX-License-Identifier: GPL-2.0 */
- /*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * Authors: Waiman Long <[email protected]>
- */
- /*
- * Collect locking event counts
- */
- #include <linux/debugfs.h>
- #include <linux/sched.h>
- #include <linux/sched/clock.h>
- #include <linux/fs.h>
- #include "lock_events.h"
- #undef LOCK_EVENT
- #define LOCK_EVENT(name) [LOCKEVENT_ ## name] = #name,
- #define LOCK_EVENTS_DIR "lock_event_counts"
- /*
- * When CONFIG_LOCK_EVENT_COUNTS is enabled, event counts of different
- * types of locks will be reported under the <debugfs>/lock_event_counts/
- * directory. See lock_events_list.h for the list of available locking
- * events.
- *
- * Writing to the special ".reset_counts" file will reset all the above
- * locking event counts. This is a very slow operation and so should not
- * be done frequently.
- *
- * These event counts are implemented as per-cpu variables which are
- * summed and computed whenever the corresponding debugfs files are read. This
- * minimizes added overhead making the counts usable even in a production
- * environment.
- */
- static const char * const lockevent_names[lockevent_num + 1] = {
- #include "lock_events_list.h"
- [LOCKEVENT_reset_cnts] = ".reset_counts",
- };
- /*
- * Per-cpu counts
- */
- DEFINE_PER_CPU(unsigned long, lockevents[lockevent_num]);
- /*
- * The lockevent_read() function can be overridden.
- */
- ssize_t __weak lockevent_read(struct file *file, char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- char buf[64];
- int cpu, id, len;
- u64 sum = 0;
- /*
- * Get the counter ID stored in file->f_inode->i_private
- */
- id = (long)file_inode(file)->i_private;
- if (id >= lockevent_num)
- return -EBADF;
- for_each_possible_cpu(cpu)
- sum += per_cpu(lockevents[id], cpu);
- len = snprintf(buf, sizeof(buf) - 1, "%llu\n", sum);
- return simple_read_from_buffer(user_buf, count, ppos, buf, len);
- }
- /*
- * Function to handle write request
- *
- * When idx = reset_cnts, reset all the counts.
- */
- static ssize_t lockevent_write(struct file *file, const char __user *user_buf,
- size_t count, loff_t *ppos)
- {
- int cpu;
- /*
- * Get the counter ID stored in file->f_inode->i_private
- */
- if ((long)file_inode(file)->i_private != LOCKEVENT_reset_cnts)
- return count;
- for_each_possible_cpu(cpu) {
- int i;
- unsigned long *ptr = per_cpu_ptr(lockevents, cpu);
- for (i = 0 ; i < lockevent_num; i++)
- WRITE_ONCE(ptr[i], 0);
- }
- return count;
- }
- /*
- * Debugfs data structures
- */
- static const struct file_operations fops_lockevent = {
- .read = lockevent_read,
- .write = lockevent_write,
- .llseek = default_llseek,
- };
- #ifdef CONFIG_PARAVIRT_SPINLOCKS
- #include <asm/paravirt.h>
- static bool __init skip_lockevent(const char *name)
- {
- static int pv_on __initdata = -1;
- if (pv_on < 0)
- pv_on = !pv_is_native_spin_unlock();
- /*
- * Skip PV qspinlock events on bare metal.
- */
- if (!pv_on && !memcmp(name, "pv_", 3))
- return true;
- return false;
- }
- #else
- static inline bool skip_lockevent(const char *name)
- {
- return false;
- }
- #endif
- /*
- * Initialize debugfs for the locking event counts.
- */
- static int __init init_lockevent_counts(void)
- {
- struct dentry *d_counts = debugfs_create_dir(LOCK_EVENTS_DIR, NULL);
- int i;
- if (!d_counts)
- goto out;
- /*
- * Create the debugfs files
- *
- * As reading from and writing to the stat files can be slow, only
- * root is allowed to do the read/write to limit impact to system
- * performance.
- */
- for (i = 0; i < lockevent_num; i++) {
- if (skip_lockevent(lockevent_names[i]))
- continue;
- if (!debugfs_create_file(lockevent_names[i], 0400, d_counts,
- (void *)(long)i, &fops_lockevent))
- goto fail_undo;
- }
- if (!debugfs_create_file(lockevent_names[LOCKEVENT_reset_cnts], 0200,
- d_counts, (void *)(long)LOCKEVENT_reset_cnts,
- &fops_lockevent))
- goto fail_undo;
- return 0;
- fail_undo:
- debugfs_remove_recursive(d_counts);
- out:
- pr_warn("Could not create '%s' debugfs entries\n", LOCK_EVENTS_DIR);
- return -ENOMEM;
- }
- fs_initcall(init_lockevent_counts);
|