* 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>
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)
{
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];