From feff17a550c7120009d8ba9431426135661a731b Mon Sep 17 00:00:00 2001 From: William Breathitt Gray <vilhelm.gray@gmail.com> Date: Wed, 29 Sep 2021 12:16:04 +0900 Subject: counter: Implement events_queue_size sysfs attribute The events_queue_size sysfs attribute provides a way for users to dynamically configure the Counter events queue size for the Counter character device interface. The size is in number of struct counter_event data structures. The number of elements will be rounded-up to a power of 2 due to a requirement of the kfifo_alloc function called during reallocation of the queue. Cc: Oleksij Rempel <o.rempel@pengutronix.de> Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com> Link: https://lore.kernel.org/r/c914b2db2ea0a2637633bcc3e86ded3c94783f2e.1632884256.git.vilhelm.gray@gmail.com Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> --- drivers/counter/counter-sysfs.c | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'drivers/counter') diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c index 97d8d7c2a2b6..1ccd771da25f 100644 --- a/drivers/counter/counter-sysfs.c +++ b/drivers/counter/counter-sysfs.c @@ -3,11 +3,13 @@ * Generic Counter sysfs interface * Copyright (C) 2020 William Breathitt Gray */ +#include <linux/atomic.h> #include <linux/counter.h> #include <linux/device.h> #include <linux/err.h> #include <linux/gfp.h> #include <linux/kernel.h> +#include <linux/kfifo.h> #include <linux/kstrtox.h> #include <linux/list.h> #include <linux/string.h> @@ -783,12 +785,49 @@ static int counter_num_counts_read(struct counter_device *counter, u8 *val) return 0; } +static int counter_events_queue_size_read(struct counter_device *counter, + u64 *val) +{ + *val = kfifo_size(&counter->events); + return 0; +} + +static int counter_events_queue_size_write(struct counter_device *counter, + u64 val) +{ + DECLARE_KFIFO_PTR(events, struct counter_event); + int err = 0; + + /* Ensure chrdev is not opened more than 1 at a time */ + if (!atomic_add_unless(&counter->chrdev_lock, 1, 1)) + return -EBUSY; + + /* Allocate new events queue */ + err = kfifo_alloc(&events, val, GFP_KERNEL); + if (err) + goto exit_early; + + /* Swap in new events queue */ + kfifo_free(&counter->events); + counter->events.kfifo = events.kfifo; + +exit_early: + atomic_dec(&counter->chrdev_lock); + + return err; +} + static struct counter_comp counter_num_signals_comp = COUNTER_COMP_DEVICE_U8("num_signals", counter_num_signals_read, NULL); static struct counter_comp counter_num_counts_comp = COUNTER_COMP_DEVICE_U8("num_counts", counter_num_counts_read, NULL); +static struct counter_comp counter_events_queue_size_comp = + COUNTER_COMP_DEVICE_U64("events_queue_size", + counter_events_queue_size_read, + counter_events_queue_size_write); + static int counter_sysfs_attr_add(struct counter_device *const counter, struct counter_attribute_group *cattr_group) { @@ -827,6 +866,12 @@ static int counter_sysfs_attr_add(struct counter_device *const counter, if (err < 0) return err; + /* Create events_queue_size attribute */ + err = counter_attr_create(dev, cattr_group, + &counter_events_queue_size_comp, scope, NULL); + if (err < 0) + return err; + /* Create an attribute for each extension */ for (i = 0; i < counter->num_ext; i++) { ext = &counter->ext[i]; -- cgit v1.2.3