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

counter-core.c (6353B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Generic Counter interface
      4 * Copyright (C) 2020 William Breathitt Gray
      5 */
      6#include <linux/cdev.h>
      7#include <linux/counter.h>
      8#include <linux/device.h>
      9#include <linux/device/bus.h>
     10#include <linux/export.h>
     11#include <linux/fs.h>
     12#include <linux/gfp.h>
     13#include <linux/idr.h>
     14#include <linux/init.h>
     15#include <linux/kdev_t.h>
     16#include <linux/module.h>
     17#include <linux/mutex.h>
     18#include <linux/slab.h>
     19#include <linux/types.h>
     20#include <linux/wait.h>
     21
     22#include "counter-chrdev.h"
     23#include "counter-sysfs.h"
     24
     25#define COUNTER_NAME	"counter"
     26
     27/* Provides a unique ID for each counter device */
     28static DEFINE_IDA(counter_ida);
     29
     30struct counter_device_allochelper {
     31	struct counter_device counter;
     32
     33	/*
     34	 * This is cache line aligned to ensure private data behaves like if it
     35	 * were kmalloced separately.
     36	 */
     37	unsigned long privdata[] ____cacheline_aligned;
     38};
     39
     40static void counter_device_release(struct device *dev)
     41{
     42	struct counter_device *const counter =
     43		container_of(dev, struct counter_device, dev);
     44
     45	counter_chrdev_remove(counter);
     46	ida_free(&counter_ida, dev->id);
     47
     48	kfree(container_of(counter, struct counter_device_allochelper, counter));
     49}
     50
     51static struct device_type counter_device_type = {
     52	.name = "counter_device",
     53	.release = counter_device_release,
     54};
     55
     56static struct bus_type counter_bus_type = {
     57	.name = "counter",
     58	.dev_name = "counter",
     59};
     60
     61static dev_t counter_devt;
     62
     63/**
     64 * counter_priv - access counter device private data
     65 * @counter: counter device
     66 *
     67 * Get the counter device private data
     68 */
     69void *counter_priv(const struct counter_device *const counter)
     70{
     71	struct counter_device_allochelper *ch =
     72		container_of(counter, struct counter_device_allochelper, counter);
     73
     74	return &ch->privdata;
     75}
     76EXPORT_SYMBOL_GPL(counter_priv);
     77
     78/**
     79 * counter_alloc - allocate a counter_device
     80 * @sizeof_priv: size of the driver private data
     81 *
     82 * This is part one of counter registration. The structure is allocated
     83 * dynamically to ensure the right lifetime for the embedded struct device.
     84 *
     85 * If this succeeds, call counter_put() to get rid of the counter_device again.
     86 */
     87struct counter_device *counter_alloc(size_t sizeof_priv)
     88{
     89	struct counter_device_allochelper *ch;
     90	struct counter_device *counter;
     91	struct device *dev;
     92	int err;
     93
     94	ch = kzalloc(sizeof(*ch) + sizeof_priv, GFP_KERNEL);
     95	if (!ch)
     96		return NULL;
     97
     98	counter = &ch->counter;
     99	dev = &counter->dev;
    100
    101	/* Acquire unique ID */
    102	err = ida_alloc(&counter_ida, GFP_KERNEL);
    103	if (err < 0)
    104		goto err_ida_alloc;
    105	dev->id = err;
    106
    107	mutex_init(&counter->ops_exist_lock);
    108	dev->type = &counter_device_type;
    109	dev->bus = &counter_bus_type;
    110	dev->devt = MKDEV(MAJOR(counter_devt), dev->id);
    111
    112	err = counter_chrdev_add(counter);
    113	if (err < 0)
    114		goto err_chrdev_add;
    115
    116	device_initialize(dev);
    117
    118	err = dev_set_name(dev, COUNTER_NAME "%d", dev->id);
    119	if (err)
    120		goto err_dev_set_name;
    121
    122	return counter;
    123
    124err_dev_set_name:
    125
    126	counter_chrdev_remove(counter);
    127err_chrdev_add:
    128
    129	ida_free(&counter_ida, dev->id);
    130err_ida_alloc:
    131
    132	kfree(ch);
    133
    134	return NULL;
    135}
    136EXPORT_SYMBOL_GPL(counter_alloc);
    137
    138void counter_put(struct counter_device *counter)
    139{
    140	put_device(&counter->dev);
    141}
    142EXPORT_SYMBOL_GPL(counter_put);
    143
    144/**
    145 * counter_add - complete registration of a counter
    146 * @counter: the counter to add
    147 *
    148 * This is part two of counter registration.
    149 *
    150 * If this succeeds, call counter_unregister() to get rid of the counter_device again.
    151 */
    152int counter_add(struct counter_device *counter)
    153{
    154	int err;
    155	struct device *dev = &counter->dev;
    156
    157	if (counter->parent) {
    158		dev->parent = counter->parent;
    159		dev->of_node = counter->parent->of_node;
    160	}
    161
    162	err = counter_sysfs_add(counter);
    163	if (err < 0)
    164		return err;
    165
    166	/* implies device_add(dev) */
    167	return cdev_device_add(&counter->chrdev, dev);
    168}
    169EXPORT_SYMBOL_GPL(counter_add);
    170
    171/**
    172 * counter_unregister - unregister Counter from the system
    173 * @counter:	pointer to Counter to unregister
    174 *
    175 * The Counter is unregistered from the system.
    176 */
    177void counter_unregister(struct counter_device *const counter)
    178{
    179	if (!counter)
    180		return;
    181
    182	cdev_device_del(&counter->chrdev, &counter->dev);
    183
    184	mutex_lock(&counter->ops_exist_lock);
    185
    186	counter->ops = NULL;
    187	wake_up(&counter->events_wait);
    188
    189	mutex_unlock(&counter->ops_exist_lock);
    190}
    191EXPORT_SYMBOL_GPL(counter_unregister);
    192
    193static void devm_counter_release(void *counter)
    194{
    195	counter_unregister(counter);
    196}
    197
    198static void devm_counter_put(void *counter)
    199{
    200	counter_put(counter);
    201}
    202
    203/**
    204 * devm_counter_alloc - allocate a counter_device
    205 * @dev: the device to register the release callback for
    206 * @sizeof_priv: size of the driver private data
    207 *
    208 * This is the device managed version of counter_add(). It registers a cleanup
    209 * callback to care for calling counter_put().
    210 */
    211struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv)
    212{
    213	struct counter_device *counter;
    214	int err;
    215
    216	counter = counter_alloc(sizeof_priv);
    217	if (!counter)
    218		return NULL;
    219
    220	err = devm_add_action_or_reset(dev, devm_counter_put, counter);
    221	if (err < 0)
    222		return NULL;
    223
    224	return counter;
    225}
    226EXPORT_SYMBOL_GPL(devm_counter_alloc);
    227
    228/**
    229 * devm_counter_add - complete registration of a counter
    230 * @dev: the device to register the release callback for
    231 * @counter: the counter to add
    232 *
    233 * This is the device managed version of counter_add(). It registers a cleanup
    234 * callback to care for calling counter_unregister().
    235 */
    236int devm_counter_add(struct device *dev,
    237		     struct counter_device *const counter)
    238{
    239	int err;
    240
    241	err = counter_add(counter);
    242	if (err < 0)
    243		return err;
    244
    245	return devm_add_action_or_reset(dev, devm_counter_release, counter);
    246}
    247EXPORT_SYMBOL_GPL(devm_counter_add);
    248
    249#define COUNTER_DEV_MAX 256
    250
    251static int __init counter_init(void)
    252{
    253	int err;
    254
    255	err = bus_register(&counter_bus_type);
    256	if (err < 0)
    257		return err;
    258
    259	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX,
    260				  COUNTER_NAME);
    261	if (err < 0)
    262		goto err_unregister_bus;
    263
    264	return 0;
    265
    266err_unregister_bus:
    267	bus_unregister(&counter_bus_type);
    268	return err;
    269}
    270
    271static void __exit counter_exit(void)
    272{
    273	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
    274	bus_unregister(&counter_bus_type);
    275}
    276
    277subsys_initcall(counter_init);
    278module_exit(counter_exit);
    279
    280MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
    281MODULE_DESCRIPTION("Generic Counter interface");
    282MODULE_LICENSE("GPL v2");