cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lock_events.c (4443B)


      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 */
     46static 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 */
     56DEFINE_PER_CPU(unsigned long, lockevents[lockevent_num]);
     57
     58/*
     59 * The lockevent_read() function can be overridden.
     60 */
     61ssize_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 */
     88static 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 */
    112static 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
    121static 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
    135static 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 */
    144static 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;
    173fail_undo:
    174	debugfs_remove_recursive(d_counts);
    175out:
    176	pr_warn("Could not create '%s' debugfs entries\n", LOCK_EVENTS_DIR);
    177	return -ENOMEM;
    178}
    179fs_initcall(init_lockevent_counts);