Skip to contentSkip to conversation

Introduce the Counter character device interface

PATCH Superseded William Breathitt Gray 3 participants 36 messages Sep 27, 2020 – Oct 25, 2020

Subsystems (9)
Documentation IIO Arm STM32 Counter +5 more subsystems

v5 of 17 Superseded Latest is v17 →

lore
Share & tools
lore
#0 lore raw

v5 cover Introduce the Counter character device interface

Changes in v5:
- Fixed typographical errors in documentation and comments
- Updated flow charts in documentation for clarity
- Moved uapi header to be part of the character device intro patch
- Fix git squash mistake in 104-quad-8.c; remove redundant changes
- Fix git merge mistake in 104-quad-8.c; fix locking race condition
- Minor code cleanup for clarity; adjust whitespace/flow
- Use put_device if device_add fails
- Document sysfs structures
- Rename "owner" symbols to "scope"; more apt name
- Use resource-managed devm_* allocation functions
- Rename *_free functions to *_remove; following common convention
- Rename COUNTER_DATA* to COUNTER_COMP*; more obvious meaning
- Rename various symbol and define names for clarity
- Bring back static ops function; more secure to have static const
- Rename counter_available union members to "enums" and "strs"
- Implement COUNTER_EVENT* constants; event types are now standard
- Implement atomic Counter watches swap; no more racy event config

Over the past couple years we have noticed some shortcomings with the Counter sysfs interface. Although useful in the majority of situations, there are certain use-cases where interacting through sysfs attributes can become cumbersome and inefficient. A desire to support more advanced functionality such as timestamps, multi-axes positioning tables, and other such latency-sensitive applications, has motivated a reevaluation of the Counter subsystem. I believe a character device interface will be helpful for this more niche area of counter device use.

To quell any concerns from the offset: this patchset makes no changes to the existing Counter sysfs userspace interface -- existing userspace applications will continue to work with no modifications necessary. I request that driver maintainers please test their applications to verify that this is true, and report any discrepancies if they arise.

However, this patchset does contain a major reimplementation of the Counter subsystem core and driver API. A reimplementation was necessary in order to separate the sysfs code from the counter device drivers and internalize it as a dedicated component of the core Counter subsystem module. A minor benefit from all of this is that the sysfs interface is now ensured a certain amount of consistency because the translation is performed outside of individual counter device drivers.

Essentially, the reimplementation has enabled counter device drivers to pass and handle data as native C datatypes now rather than the sysfs strings from before.

A high-level view of how a count value is passed down from a counter driver is exemplified by the following. The driver callbacks are first registered to the Counter core component for use by the Counter userspace interface components:

                        +----------------------------+
	                | Counter device driver      |
                        +----------------------------+
                        | Processes data from device |
                        +----------------------------+
                                |
                         -------------------
                        / driver callbacks /
                        -------------------
                                |
                                V
                        +----------------------+
                        | Counter core         |
                        +----------------------+
                        | Routes device driver |
                        | callbacks to the     |
                        | userspace interfaces |
                        +----------------------+
                                |
                         -------------------
                        / driver callbacks /
                        -------------------
                                |
                +---------------+---------------+
                |                               |
                V                               V
        +--------------------+          +---------------------+
        | Counter sysfs      |          | Counter chrdev      |
        +--------------------+          +---------------------+
        | Translates to the  |          | Translates to the   |
        | standard Counter   |          | standard Counter    |
        | sysfs output       |          | character device    |
        +--------------------+          +---------------------+

Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface:

                         ----------------------
                        / Counter device       \
                        +----------------------+
                        | Count register: 0x28 |
                        +----------------------+
                                |
                         -----------------
                        / raw count data /
                        -----------------
                                |
                                V
                        +----------------------------+
                        | Counter device driver      |
                        +----------------------------+
                        | Processes data from device |
                        |----------------------------|
                        | Type: u64                  |
                        | Value: 42                  |
                        +----------------------------+
                                |
                         ----------
                        / u64     /
                        ----------
                                |
                +---------------+---------------+
                |                               |
                V                               V
        +--------------------+          +---------------------+
        | Counter sysfs      |          | Counter chrdev      |
        +--------------------+          +---------------------+
        | Translates to the  |          | Translates to the   |
        | standard Counter   |          | standard Counter    |
        | sysfs output       |          | character device    |
        |--------------------|          |---------------------|
        | Type: const char * |          | Type: u64           |
        | Value: "42"        |          | Value: 42           |
        +--------------------+          +---------------------+
                |                               |
         ---------------                 -----------------------
        / const char * /                / struct counter_event /
        ---------------                 -----------------------
                |                               |
                |                               V
                |                       +-----------+
                |                       | read      |
                |                       +-----------+
                |                       \ Count: 42 /
                |                        -----------
                |
                V
        +--------------------------------------------------+
        | `/sys/bus/counter/devices/counterX/countY/count` |
        +--------------------------------------------------+
        \ Count: "42"                                      /
         --------------------------------------------------

Counter device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.

The following are some questions I have about this patchset:

1. Should standard Counter component data types be defined as u8 or u32?

Many standard Counter component types such COUNTER_COMP_SIGNAL_LEVEL have standard values defined (e.g. COUNTER_SIGNAL_LEVEL_LOW and COUNTER_SIGNAL_LEVEL_HIGH). These values are currently handled by the Counter subsystem code as u8 data types.

If u32 is used for these values instead, C enum structures could be used by driver authors to implicit cast these values via the driver callback parameters; userspace would still use u32 with no issue.

In theory this can work because GCC will treat enums are having a 32-bit size; but I worry about the possibility of build targets that have -fshort-enums enabled, resulting in enums having a size less than 32 bits. Would this be a problem?

2. Should I have reserved members in the userspace structures?

The structures in include/uapi/linux/counter.h are available to userspace applications. Should I reserve space in these structures for future additions and usage? Will endianess and packing be a concern here?

William Breathitt Gray (5):
counter: Internalize sysfs interface code
docs: counter: Update to reflect sysfs internalization
counter: Add character device interface
docs: counter: Document character device interface
counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

 Documentation/ABI/testing/sysfs-bus-counter   |   18 +
 .../ABI/testing/sysfs-bus-counter-104-quad-8  |   32 +
 Documentation/driver-api/generic-counter.rst  |  408 ++++-
 .../userspace-api/ioctl/ioctl-number.rst      |    1 +
 MAINTAINERS                                   |    2 +-
 drivers/counter/104-quad-8.c                  |  775 +++++----
 drivers/counter/Kconfig                       |    6 +-
 drivers/counter/Makefile                      |    1 +
 drivers/counter/counter-chrdev.c              |  451 +++++
 drivers/counter/counter-chrdev.h              |   16 +
 drivers/counter/counter-core.c                |  190 +++
 drivers/counter/counter-sysfs.c               |  862 ++++++++++
 drivers/counter/counter-sysfs.h               |   13 +
 drivers/counter/counter.c                     | 1496 -----------------
 drivers/counter/ftm-quaddec.c                 |   60 +-
 drivers/counter/microchip-tcb-capture.c       |  100 +-
 drivers/counter/stm32-lptimer-cnt.c           |  175 +-
 drivers/counter/stm32-timer-cnt.c             |  145 +-
 drivers/counter/ti-eqep.c                     |  226 +--
 include/linux/counter.h                       |  618 +++----
 include/linux/counter_enum.h                  |   45 -
 include/uapi/linux/counter.h                  |   99 ++
 22 files changed, 3053 insertions(+), 2686 deletions(-)
 create mode 100644 drivers/counter/counter-chrdev.c
 create mode 100644 drivers/counter/counter-chrdev.h
 create mode 100644 drivers/counter/counter-core.c
 create mode 100644 drivers/counter/counter-sysfs.c
 create mode 100644 drivers/counter/counter-sysfs.h
 delete mode 100644 drivers/counter/counter.c
 delete mode 100644 include/linux/counter_enum.h
 create mode 100644 include/uapi/linux/counter.h
Signature · 1 line
2.28.0
#1 lore raw

PATCH v5 1/5 counter: Internalize sysfs interface code

This is a reimplementation of the Generic Counter driver interface. There are no modifications to the Counter subsystem userspace interface, so existing userspace applications should continue to run seamlessly.

Overview
========

The purpose of this patch is to internalize the sysfs interface code among the various counter drivers into a shared module. Counter drivers pass and take data natively (i.e. u8, u64, etc.) and the shared counter module handles the translation between the sysfs interface. This guarantees a standard userspace interface for all counter drivers, and helps generalize the Generic Counter driver ABI in order to support the Generic Counter chrdev interface (introduced in a subsequent patch) without significant changes to the existing counter drivers.

A high-level view of how a count value is passed down from a counter driver is exemplified by the following. The driver callbacks are first registered to the Counter core component for use by the Counter userspace interface components:

                        +----------------------------+
	                | Counter device driver      |
                        +----------------------------+
                        | Processes data from device |
                        +----------------------------+
                                |
                         -------------------
                        / driver callbacks /
                        -------------------
                                |
                                V
                        +----------------------+
                        | Counter core         |
                        +----------------------+
                        | Routes device driver |
                        | callbacks to the     |
                        | userspace interfaces |
                        +----------------------+
                                |
                         -------------------
                        / driver callbacks /
                        -------------------
                                |
                +---------------+
                |
                V
        +--------------------+
        | Counter sysfs      |
        +--------------------+
        | Translates to the  |
        | standard Counter   |
        | sysfs output       |
        +--------------------+

Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface:

                         ----------------------
                        / Counter device       \
                        +----------------------+
                        | Count register: 0x28 |
                        +----------------------+
                                |
                         -----------------
                        / raw count data /
                        -----------------
                                |
                                V
                        +----------------------------+
                        | Counter device driver      |
                        +----------------------------+
                        | Processes data from device |
                        |----------------------------|
                        | Type: u64                  |
                        | Value: 42                  |
                        +----------------------------+
                                |
                         ----------
                        / u64     /
                        ----------
                                |
                +---------------+
                |
                V
        +--------------------+
        | Counter sysfs      |
        +--------------------+
        | Translates to the  |
        | standard Counter   |
        | sysfs output       |
        |--------------------|
        | Type: const char * |
        | Value: "42"        |
        +--------------------+
                |
         ---------------
        / const char * /
        ---------------
                |
                V
        +--------------------------------------------------+
        | `/sys/bus/counter/devices/counterX/countY/count` |
        +--------------------------------------------------+
        \ Count: "42"                                      /
         --------------------------------------------------

There are three primary components involved:

Counter device driver
---------------------
Communicates with the hardware device to read/write data; e.g. counter drivers for quadrature encoders, timers, etc.

Counter core
------------
Registers the counter device driver to the system so that the respective callbacks are called during userspace interaction.

Counter sysfs
-------------
Translates counter data to the standard Counter sysfs interface format and vice versa.

Driver ABI
==========

This reimplementation entails several changes to the driver ABI. In particular, the device driver callbacks are now expected to handle standard C datatypes rather than translating the sysfs I/O directly.

To that end, the struct counter_comp structure is introduced to establish counter extensions for Signals, Synapses, and Counts.

The "type" member specifies the type of high-level data (e.g. BOOL, COUNT_DIRECTION, etc.) handled by this extension. The "*_read" and "*_write" members can then be set by the counter device driver with callbacks to handle that data using native C data types (i.e. u8, u64, etc.).

Convenience macros such as COUNTER_COMP_COUNT_U64 are provided for use by driver authors. In particular, driver authors are expected to use the provided macros for standard Counter subsystem attributes in order to maintain a consistent interface for userspace. For example, a counter device driver may define several standard attributes like so:

struct counter_comp count_ext[] = {
COUNTER_COMP_DIRECTION(count_direction_read), COUNTER_COMP_ENABLE(count_enable_read, count_enable_write), COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
};

This makes it simple to see, add, and modify the attributes that are supported by this driver ("direction", "enable", and "ceiling") and to maintain this code without getting lost in a web of struct braces.

Callbacks must match the function type expected for the respective component or extension. These function types are defined in the struct counter_comp structure as the "*_read" and "*_write" union members.

The corresponding callback prototypes for the extensions above would be:

int count_direction_read(struct counter_device *counter,

			 struct counter_count *count, u8 *direction);
int count_enable_read(struct counter_device *counter,
		      struct counter_count *count, u8 *enable);
int count_enable_write(struct counter_device *counter,
		       struct counter_count *count, u8 enable);
int count_ceiling_read(struct counter_device *counter,
		       struct counter_count *count, u64 *ceiling);
int count_ceiling_write(struct counter_device *counter,
			struct counter_count *count, u64 ceiling);

In this way, driver authors no longer need to mess with sysfs strings and are instead able to focus on what they actually care about doing -- getting data to/from the devices -- while the Generic Counter interface handles the translation of that data for them between the various userspace interfaces (e.g. sysfs and chardev).

Architecture
============

Counter device registration is the same as before: drivers populate a struct counter_device with components and callbacks, then pass the structure to the devm_counter_register function. However, what's different now is how the Counter subsystem code handles this registration internally.

Whereas before callbacks would interact directly with sysfs data, this interaction is now abstracted and instead callbacks interact with native C data types. The counter_comp structure forms the basis for Counter extensions.

The counter-sysfs.c file contains the code to parse through the counter_device structure and register the requested components and extensions. Attributes are created and populated based on type, with respective translation functions to handle the mapping between sysfs and the counter driver callbacks.

The translation performed for each attribute is straightforward: the attribute type and data is parsed from the counter_attribute structure, the respective counter driver read/write callback is called, and sysfs I/O is handled before or after the driver read/write function is called.

Cc Syed Nayyar Waris <[email protected]>
Cc Patrick Havelange <[email protected]>
Cc Kamel Bouhara <[email protected]>
Cc Fabrice Gasnier <[email protected]>
Cc Maxime Coquelin <[email protected]>
Cc Alexandre Torgue <[email protected]>
Cc David Lechner <[email protected]>
---
 MAINTAINERS                             |    1 -
 drivers/counter/104-quad-8.c            |  476 +++-----
 drivers/counter/Makefile                |    1 +
 drivers/counter/counter-core.c          |  160 +++
 drivers/counter/counter-sysfs.c         |  773 ++++++++++++
 drivers/counter/counter-sysfs.h         |   13 +
 drivers/counter/counter.c               | 1496 -----------------------
 drivers/counter/ftm-quaddec.c           |   60 +-
 drivers/counter/microchip-tcb-capture.c |  100 +-
 drivers/counter/stm32-lptimer-cnt.c     |  175 +--
 drivers/counter/stm32-timer-cnt.c       |  145 +--
 drivers/counter/ti-eqep.c               |  226 ++--
 include/linux/counter.h                 |  628 +++++-----
 include/linux/counter_enum.h            |   45 -
 14 files changed, 1758 insertions(+), 2541 deletions(-)
 create mode 100644 drivers/counter/counter-core.c
 create mode 100644 drivers/counter/counter-sysfs.c
 create mode 100644 drivers/counter/counter-sysfs.h
 delete mode 100644 drivers/counter/counter.c
 delete mode 100644 include/linux/counter_enum.h
MAINTAINERS +0 -1
index 924616dc19ff..4969e997061a 100644
@@ -4459,7 +4459,6 @@ F:	Documentation/ABI/testing/sysfs-bus-counter*
 F:	Documentation/driver-api/generic-counter.rst
 F:	drivers/counter/
 F:	include/linux/counter.h
-F:	include/linux/counter_enum.h
 
 CPMAC ETHERNET DRIVER
 M:	Florian Fainelli <[email protected]>
drivers/counter/104-quad-8.c +202 -274

819 lines not on this page · raw message

drivers/counter/Makefile +1 -0
index 0a393f71e481..cbe1d06af6a9 100644
@@ -4,6 +4,7 @@
 #
 
 obj-$(CONFIG_COUNTER) += counter.o
+counter-y := counter-core.o counter-sysfs.o
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
 obj-$(CONFIG_STM32_TIMER_CNT)	+= stm32-timer-cnt.o
drivers/counter/counter-core.c new file +160 -0

160 lines not on this page · raw message

drivers/counter/counter-sysfs.c new file +773 -0

773 lines not on this page · raw message

drivers/counter/counter-sysfs.h new file +13 -0
new file mode 100644
index 000000000000..14fe566aca0e
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Counter sysfs interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _COUNTER_SYSFS_H_
+#define _COUNTER_SYSFS_H_
+
+#include <linux/counter.h>
+
+int counter_sysfs_add(struct counter_device *const counter);
+
+#endif /* _COUNTER_SYSFS_H_ */
drivers/counter/counter.c deleted +0 -1496

1,221 lines not on this page · raw message

drivers/counter/ftm-quaddec.c +24 -36

60 lines not on this page · raw message

drivers/counter/microchip-tcb-capture.c +42 -58

100 lines not on this page · raw message

drivers/counter/stm32-lptimer-cnt.c +92 -83

175 lines not on this page · raw message

drivers/counter/stm32-timer-cnt.c +58 -87

145 lines not on this page · raw message

drivers/counter/ti-eqep.c +117 -109

226 lines not on this page · raw message

include/linux/counter.h +276 -352

628 lines not on this page · raw message

include/linux/counter_enum.h deleted +0 -45

45 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.28.0

Part of the diff is not shown here. View the raw message.

reviewer
#2 lore raw

Re: [PATCH v5 1/5] counter: Internalize sysfs interface code

Quoted 1 line · William Breathitt Gray

This is a reimplementation of the Generic Counter driver interface.

I'll follow up if I find any problems while testing but here are some comments I had from looking over the patch.

Quoted 43 lines
drivers/counter/counter-core.c new file +36 -0
new file mode 100644
index 000000000000..987c6e8277eb
 
 
+/**
+ * counter_register - register Counter to the system
+ * @counter:	pointer to Counter to register
+ *
+ * This function registers a Counter to the system. A sysfs "counter" directory
+ * will be created and populated with sysfs attributes correlating with the
+ * Counter Signals, Synapses, and Counts respectively.
+ */
+int counter_register(struct counter_device *const counter)
+{
+	struct device *const dev = &counter->dev;
+	int err;
+
+	/* Acquire unique ID */
+	counter->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
+	if (counter->id < 0)
+		return counter->id;
+
+	/* Configure device structure for Counter */
+	dev->type = &counter_device_type;
+	dev->bus = &counter_bus_type;
+	if (counter->parent) {
+		dev->parent = counter->parent;
+		dev->of_node = counter->parent->of_node;
+	}
+	dev_set_name(dev, "counter%d", counter->id);
+	device_initialize(dev);> +	dev_set_drvdata(dev, counter);
+
+	/* Add Counter sysfs attributes */
+	err = counter_sysfs_add(counter);
+	if (err)
+		goto err_free_id;
+
+	/* Add device to system */
+	err = device_add(dev);
+	if (err) {

drivers/counter/counter-core.c line 81

Quoted 10 lines
drivers/counter/counter-core.c new file +10 -0
+		put_device(dev);
+		goto err_free_id;
+	}
+
+	return 0;
+
+err_free_id:
+	/* get_device/put_device combo used to free managed resources */
+	get_device(dev);
+	put_device(dev);

I've never seen this in a driver before, so it makes me think this is not the "right way" to do this. After device_initialize() is called, we already should have a reference to dev, so only device_put() is needed.

drivers/counter/counter-core.c

Quoted 1 line
drivers/counter/counter-core.c new file +1 -0
+	ida_simple_remove(&counter_ida, counter->id);

In the case of error after device_initialize() is called, won't this result in ida_simple_remove() being called twice, once here and once in the release callback?

Quoted 10 lines
drivers/counter/counter-core.c new file +10 -0
+	return err;
+}
+EXPORT_SYMBOL_GPL(counter_register);
+
+/**
+ * counter_unregister - unregister Counter from the system
+ * @counter:	pointer to Counter to unregister
+ *
+ * The Counter is unregistered from the system; all allocated memory is freed.
+ */

drivers/counter/counter-core.c line 102

Quoted 10 lines
drivers/counter/counter-core.c new file +10 -0
+void counter_unregister(struct counter_device *const counter)
+{
+	if (!counter)
+		return;
+
+	device_unregister(&counter->dev);
+}
+EXPORT_SYMBOL_GPL(counter_unregister);
+
+static void devm_counter_unreg(struct device *dev, void *res)

To be consistent, it would be nice to spell out unregister.

Quoted 16 lines
drivers/counter/counter-core.c new file +4 -0
+{
+	counter_unregister(*(struct counter_device **)res);
+}
+
drivers/counter/counter-sysfs.c new file +5 -0
new file mode 100644
index 000000000000..e66ed99dd5ea
 
+/**
+ * counter_sysfs_add - Adds Counter sysfs attributes to the device structure
+ * @counter:	Pointer to the Counter device structure
+ *
+ * Counter sysfs attributes are created and added to the respective device

drivers/counter/counter-sysfs.c line 722

Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10 -0
+ * structure for later registration to the system. Resource-managed memory
+ * allocation is performed by this function, and this memory should be freed
+ * when no longer needed (automatically by a device_unregister call, or
+ * manually by a devres_release_all call).
+ */
+int counter_sysfs_add(struct counter_device *const counter)
+{
+	struct device *const dev = &counter->dev;
+	const size_t num_groups = counter->num_signals + counter->num_counts +
+				  1;

It is OK to go past 80 columns, especially for just for a few characters.

Quoted 7 lines
drivers/counter/counter-sysfs.c new file +7 -0
+	struct counter_attribute_group *groups;
+	size_t i, j;
+	int err;
+	struct attribute_group *group;
+	struct counter_attribute *p;
+
+	/* Allocate space for attribute groups (signals, counts, and ext) */

drivers/counter/counter-sysfs.c line 739

Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10 -0
+	groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
+	if (!groups)
+		return -ENOMEM;
+
+	/* Initialize attribute lists */
+	for (i = 0; i < num_groups; i++)
+		INIT_LIST_HEAD(&groups[i].attr_list);
+
+	/* Register Counter device attributes */
+	err = counter_device_register(counter, groups);

This function name is a bit misleading. At first I though we were registering a new counter device (struct device). Maybe counter_sysfs_create_attrs() would be a better name? (I wouldn't mind having all functions in this file having a "counter_sysfs_" prefix for that matter.)

Quoted 7 lines
drivers/counter/ti-eqep.c +0 -0
index 1ff07faef27f..938085dead80 100644

drivers/counter/ti-eqep.c static int ti_eqep_probe(struct platform_device *pdev) · line 420

Quoted 8 lines
drivers/counter/ti-eqep.c +1 -1
@@ -406,7 +414,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
 
  	priv->counter.name = dev_name(dev);
  	priv->counter.parent = dev;
-	priv->counter.ops = &ti_eqep_counter_ops;
+	priv->counter.parent = &ti_eqep_counter_ops;
  	priv->counter.counts = ti_eqep_counts;
  	priv->counter.num_counts = ARRAY_SIZE(ti_eqep_counts);
  	priv->counter.signals = ti_eqep_signals;

This looks like an unintentional change and causes a compile error.

include/linux/counter.h line 11

Quoted 12 lines
include/linux/counter.h +2 -1
index 9dbd5df4cd34..132bfecca5c3 100644
@@ -6,417 +6,195 @@
  #ifndef _COUNTER_H_
  #define _COUNTER_H_
 
-#include <linux/counter_enum.h>
  #include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>

struct list_head is defined in linux/types.h. Is there something else we are using from linux/list.h in this file?

include/linux/counter.h line 13

Quoted 2 lines
include/linux/counter.h +0 -0
  #include <linux/types.h>
 

It would be helpful to have kernel doc comments on everything in this file.

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#3 lore raw

Re: [PATCH v5 1/5] counter: Internalize sysfs interface code

Quoted 55 lines · David Lechner
Quoted 1 line · William Breathitt Gray

This is a reimplementation of the Generic Counter driver interface.

I'll follow up if I find any problems while testing but here are some comments I had from looking over the patch.

Quoted 49 lines
drivers/counter/counter-core.c new file +42 -0
new file mode 100644
index 000000000000..987c6e8277eb
 
 
+/**
+ * counter_register - register Counter to the system
+ * @counter:	pointer to Counter to register
+ *
+ * This function registers a Counter to the system. A sysfs "counter" directory
+ * will be created and populated with sysfs attributes correlating with the
+ * Counter Signals, Synapses, and Counts respectively.
+ */
+int counter_register(struct counter_device *const counter)
+{
+	struct device *const dev = &counter->dev;
+	int err;
+
+	/* Acquire unique ID */
+	counter->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
+	if (counter->id < 0)
+		return counter->id;
+
+	/* Configure device structure for Counter */
+	dev->type = &counter_device_type;
+	dev->bus = &counter_bus_type;
+	if (counter->parent) {
+		dev->parent = counter->parent;
+		dev->of_node = counter->parent->of_node;
+	}
+	dev_set_name(dev, "counter%d", counter->id);
+	device_initialize(dev);> +	dev_set_drvdata(dev, counter);
+
+	/* Add Counter sysfs attributes */
+	err = counter_sysfs_add(counter);
+	if (err)
+		goto err_free_id;
+
+	/* Add device to system */
+	err = device_add(dev);
+	if (err) {
+		put_device(dev);
+		goto err_free_id;
+	}
+
+	return 0;
+
Quoted 8 lines

drivers/counter/counter-core.c line 81

Quoted 4 lines
drivers/counter/counter-core.c new file +4 -0
+err_free_id:
+	/* get_device/put_device combo used to free managed resources */
+	get_device(dev);
+	put_device(dev);

I've never seen this in a driver before, so it makes me think this is not the "right way" to do this. After device_initialize() is called, we already should have a reference to dev, so only device_put() is needed.

I do admit this feels very hacky, but I'm not sure how to handle this more elegantly. The problem is that device_initialize() is not enough by itself -- it just initializes the structure, while device_add() increments the reference count via a call to get_device().

So let's assume counter_sysfs_add() fails and we go to err_free_id before device_add() is called; if we ignore get_device(), the reference count will be 0 at this point. We then call put_device(), which calls kobject_put(), kref_put(), and eventually refcount_dec_and_test().

The refcount_dec_and_test() function returns 0 at this point because the reference count can't be decremented further (refcount is already 0), so release() is never called and we end up leaking all the memory we allocated in counter_sysfs_add().

Is my logic correct here?

Quoted 5 lines

drivers/counter/counter-core.c

Quoted 1 line
drivers/counter/counter-core.c new file +1 -0
+	ida_simple_remove(&counter_ida, counter->id);

In the case of error after device_initialize() is called, won't this result in ida_simple_remove() being called twice, once here and once in the release callback?

I hadn't considered that. I suppose the ida_simple_remove here is not necessary because we will always call ida_simple_remove() in counter_device_release() when we call put_device(). I'll remove this ida_simple_remove() from counter_register() then.

Quoted 14 lines
Quoted 14 lines
drivers/counter/counter-core.c new file +14 -0
+	return err;
+}
+EXPORT_SYMBOL_GPL(counter_register);
+
+/**
+ * counter_unregister - unregister Counter from the system
+ * @counter:	pointer to Counter to unregister
+ *
+ * The Counter is unregistered from the system; all allocated memory is freed.
+ */
+void counter_unregister(struct counter_device *const counter)
+{
+	if (!counter)
+		return;
Quoted 8 lines

drivers/counter/counter-core.c line 102

Quoted 6 lines
drivers/counter/counter-core.c new file +6 -0
+
+	device_unregister(&counter->dev);
+}
+EXPORT_SYMBOL_GPL(counter_unregister);
+
+static void devm_counter_unreg(struct device *dev, void *res)

To be consistent, it would be nice to spell out unregister.

Ack.

Quoted 20 lines
Quoted 20 lines
drivers/counter/counter-core.c new file +4 -0
+{
+	counter_unregister(*(struct counter_device **)res);
+}
+
drivers/counter/counter-sysfs.c new file +9 -0
new file mode 100644
index 000000000000..e66ed99dd5ea
 
+/**
+ * counter_sysfs_add - Adds Counter sysfs attributes to the device structure
+ * @counter:	Pointer to the Counter device structure
+ *
+ * Counter sysfs attributes are created and added to the respective device
+ * structure for later registration to the system. Resource-managed memory
+ * allocation is performed by this function, and this memory should be freed
+ * when no longer needed (automatically by a device_unregister call, or
+ * manually by a devres_release_all call).
Quoted 8 lines

drivers/counter/counter-sysfs.c line 722

Quoted 6 lines

+ */

drivers/counter/counter-sysfs.c new file +5 -0
+int counter_sysfs_add(struct counter_device *const counter)
+{
+	struct device *const dev = &counter->dev;
+	const size_t num_groups = counter->num_signals + counter->num_counts +
+				  1;

It is OK to go past 80 columns, especially for just for a few characters.

Ack.

Quoted 14 lines
Quoted 14 lines
drivers/counter/counter-sysfs.c new file +14 -0
+	struct counter_attribute_group *groups;
+	size_t i, j;
+	int err;
+	struct attribute_group *group;
+	struct counter_attribute *p;
+
+	/* Allocate space for attribute groups (signals, counts, and ext) */
+	groups = devm_kcalloc(dev, num_groups, sizeof(*groups), GFP_KERNEL);
+	if (!groups)
+		return -ENOMEM;
+
+	/* Initialize attribute lists */
+	for (i = 0; i < num_groups; i++)
+		INIT_LIST_HEAD(&groups[i].attr_list);
Quoted 8 lines

drivers/counter/counter-sysfs.c line 739

Quoted 3 lines
drivers/counter/counter-sysfs.c new file +3 -0
+
+	/* Register Counter device attributes */
+	err = counter_device_register(counter, groups);

This function name is a bit misleading. At first I though we were registering a new counter device (struct device). Maybe counter_sysfs_create_attrs() would be a better name? (I wouldn't mind having all functions in this file having a "counter_sysfs_" prefix for that matter.)

Good point, I'll rename these to make it clearer.

Quoted 8 lines
Quoted 8 lines
drivers/counter/ti-eqep.c +0 -0
index 1ff07faef27f..938085dead80 100644
@@ -406,7 +414,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
 
Quoted 9 lines

drivers/counter/ti-eqep.c static int ti_eqep_probe(struct platform_device *pdev) · line 420

Quoted 7 lines
drivers/counter/ti-eqep.c +1 -1
  	priv->counter.name = dev_name(dev);
  	priv->counter.parent = dev;
-	priv->counter.ops = &ti_eqep_counter_ops;
+	priv->counter.parent = &ti_eqep_counter_ops;
  	priv->counter.counts = ti_eqep_counts;
  	priv->counter.num_counts = ARRAY_SIZE(ti_eqep_counts);
  	priv->counter.signals = ti_eqep_signals;

This looks like an unintentional change and causes a compile error.

Yeah, it was an unintentional change. I'll fix this. :-)

Quoted 8 lines
Quoted 8 lines
include/linux/counter.h +0 -0
index 9dbd5df4cd34..132bfecca5c3 100644
@@ -6,417 +6,195 @@
  #ifndef _COUNTER_H_
  #define _COUNTER_H_
 
Quoted 7 lines

include/linux/counter.h line 11

Quoted 4 lines
include/linux/counter.h +2 -1
-#include <linux/counter_enum.h>
  #include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>

struct list_head is defined in linux/types.h. Is there something else we are using from linux/list.h in this file?

I think this was left over from when I had list_add() in this file in an earlier version; I'll remove this header now since it doesn't look necessary.

Quoted 5 lines

include/linux/counter.h line 13

Quoted 2 lines
include/linux/counter.h +0 -0
  #include <linux/types.h>
 

It would be helpful to have kernel doc comments on everything in this file.

Ack.

William Breathitt Gray

reviewer
#4 lore raw

Re: [PATCH v5 1/5] counter: Internalize sysfs interface code

Quoted 65 lines · William Breathitt Gray
Quoted 55 lines · David Lechner
Quoted 1 line · William Breathitt Gray

This is a reimplementation of the Generic Counter driver interface.

I'll follow up if I find any problems while testing but here are some comments I had from looking over the patch.

Quoted 49 lines
drivers/counter/counter-core.c new file +42 -0
new file mode 100644
index 000000000000..987c6e8277eb
 
 
+/**
+ * counter_register - register Counter to the system
+ * @counter:	pointer to Counter to register
+ *
+ * This function registers a Counter to the system. A sysfs "counter" directory
+ * will be created and populated with sysfs attributes correlating with the
+ * Counter Signals, Synapses, and Counts respectively.
+ */
+int counter_register(struct counter_device *const counter)
+{
+	struct device *const dev = &counter->dev;
+	int err;
+
+	/* Acquire unique ID */
+	counter->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
+	if (counter->id < 0)
+		return counter->id;
+
+	/* Configure device structure for Counter */
+	dev->type = &counter_device_type;
+	dev->bus = &counter_bus_type;
+	if (counter->parent) {
+		dev->parent = counter->parent;
+		dev->of_node = counter->parent->of_node;
+	}
+	dev_set_name(dev, "counter%d", counter->id);
+	device_initialize(dev);> +	dev_set_drvdata(dev, counter);
+
+	/* Add Counter sysfs attributes */
+	err = counter_sysfs_add(counter);
+	if (err)
+		goto err_free_id;
+
+	/* Add device to system */
+	err = device_add(dev);
+	if (err) {
+		put_device(dev);
+		goto err_free_id;
+	}
+
+	return 0;
+
Quoted 8 lines

drivers/counter/counter-core.c line 81

Quoted 4 lines
drivers/counter/counter-core.c new file +4 -0
+err_free_id:
+	/* get_device/put_device combo used to free managed resources */
+	get_device(dev);
+	put_device(dev);

I've never seen this in a driver before, so it makes me think this is not the "right way" to do this. After device_initialize() is called, we already should have a reference to dev, so only device_put() is needed.

Quoted 4 lines

I do admit this feels very hacky, but I'm not sure how to handle this more elegantly. The problem is that device_initialize() is not enough by itself -- it just initializes the structure, while device_add() increments the reference count via a call to get_device().

add_device() also releases this reference by calling put_device() in all return paths. The reference count is initialized to 1 in device_initialize().

device_initialize > kobject_init > kobject_init_internal > kref_init

Quoted 6 lines

So let's assume counter_sysfs_add() fails and we go to err_free_id before device_add() is called; if we ignore get_device(), the reference count will be 0 at this point. We then call put_device(), which calls kobject_put(), kref_put(), and eventually refcount_dec_and_test().

Quoted 6 lines

The refcount_dec_and_test() function returns 0 at this point because the reference count can't be decremented further (refcount is already 0), so release() is never called and we end up leaking all the memory we allocated in counter_sysfs_add().

Is my logic correct here?

Not quite. I think you missed a few things I mentioned above. So we never have a ref count of 0 until the very last call to put_device().

#5 lore raw

Re: [PATCH v5 1/5] counter: Internalize sysfs interface code

Quoted 66 lines · David Lechner
Quoted 60 lines · William Breathitt Gray
Quoted 50 lines · David Lechner
Quoted 49 lines · William Breathitt Gray
drivers/counter/counter-core.c new file +42 -0
new file mode 100644
index 000000000000..987c6e8277eb
 
 
+/**
+ * counter_register - register Counter to the system
+ * @counter:	pointer to Counter to register
+ *
+ * This function registers a Counter to the system. A sysfs "counter" directory
+ * will be created and populated with sysfs attributes correlating with the
+ * Counter Signals, Synapses, and Counts respectively.
+ */
+int counter_register(struct counter_device *const counter)
+{
+	struct device *const dev = &counter->dev;
+	int err;
+
+	/* Acquire unique ID */
+	counter->id = ida_simple_get(&counter_ida, 0, 0, GFP_KERNEL);
+	if (counter->id < 0)
+		return counter->id;
+
+	/* Configure device structure for Counter */
+	dev->type = &counter_device_type;
+	dev->bus = &counter_bus_type;
+	if (counter->parent) {
+		dev->parent = counter->parent;
+		dev->of_node = counter->parent->of_node;
+	}
+	dev_set_name(dev, "counter%d", counter->id);
+	device_initialize(dev);> +	dev_set_drvdata(dev, counter);
+
+	/* Add Counter sysfs attributes */
+	err = counter_sysfs_add(counter);
+	if (err)
+		goto err_free_id;
+
+	/* Add device to system */
+	err = device_add(dev);
+	if (err) {
+		put_device(dev);
+		goto err_free_id;
+	}
+
+	return 0;
+
Quoted 8 lines

drivers/counter/counter-core.c line 81

Quoted 4 lines
drivers/counter/counter-core.c new file +4 -0
+err_free_id:
+	/* get_device/put_device combo used to free managed resources */
+	get_device(dev);
+	put_device(dev);

I've never seen this in a driver before, so it makes me think this is not the "right way" to do this. After device_initialize() is called, we already should have a reference to dev, so only device_put() is needed.

Quoted 4 lines

I do admit this feels very hacky, but I'm not sure how to handle this more elegantly. The problem is that device_initialize() is not enough by itself -- it just initializes the structure, while device_add() increments the reference count via a call to get_device().

Quoted 4 lines

add_device() also releases this reference by calling put_device() in all return paths. The reference count is initialized to 1 in device_initialize().

device_initialize > kobject_init > kobject_init_internal > kref_init

You're right, I completely overlooked this: kref_init() initializes the reference count to 1. Therefore, the get_device() call is incorrect and should be be removed.

Quoted 11 lines
Quoted 11 lines

So let's assume counter_sysfs_add() fails and we go to err_free_id before device_add() is called; if we ignore get_device(), the reference count will be 0 at this point. We then call put_device(), which calls kobject_put(), kref_put(), and eventually refcount_dec_and_test().

The refcount_dec_and_test() function returns 0 at this point because the reference count can't be decremented further (refcount is already 0), so release() is never called and we end up leaking all the memory we allocated in counter_sysfs_add().

Quoted 4 lines
Quoted 1 line

Is my logic correct here?

Not quite. I think you missed a few things I mentioned above. So we never have a ref count of 0 until the very last call to put_device().

Ack.

William Breathitt Gray

reviewer
#6 lore raw

Re: [PATCH v5 1/5] counter: Internalize sysfs interface code

In reply to William Breathitt Gray
Quoted 31 lines · William Breathitt Gray
drivers/counter/counter-sysfs.c new file +31 -0
+static ssize_t counter_comp_u8_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t len)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	struct counter_device *const counter = dev_get_drvdata(dev);
+	struct counter_count *const count = a->parent;
+	struct counter_synapse *const synapse = a->comp.priv;
+	const struct counter_available *const avail = a->comp.priv;
+	int err;
+	bool bool_data;
+	u8 data;
+
+	switch (a->comp.type) {
+	case COUNTER_COMP_BOOL:
+		err = kstrtobool(buf, &bool_data);
+		data = bool_data;
+		break;
+	case COUNTER_COMP_FUNCTION:
+		err = find_in_string_array(&data, count->functions_list,
+					   count->num_functions, buf,
+					   counter_function_str);
+		break;
+	case COUNTER_COMP_SYNAPSE_ACTION:
+		err = find_in_string_array(&data, synapse->actions_list,
+					   synapse->num_actions, buf,
+					   counter_synapse_action_str);
+		break;
+	case COUNTER_COMP_ENUM:
+		err = __sysfs_match_string(avail->strs, avail->num_items, buf);
+		data = err;

drivers/counter/counter-sysfs.c line 189

Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10 -0
+		break;
+	case COUNTER_COMP_COUNT_MODE:
+		err = find_in_string_array(&data, avail->enums,
+					   avail->num_items, buf,
+					   counter_count_mode_str);
+		break;
+	default:
+		err = kstrtou8(buf, 0, &data);
+		break;
+	}

In this function, return values are not always errors. So it would make sense to call the err variable ret instead and check for ret < 0 below.

Setting enums to a value with index > 0 fails currently because of this.

Quoted 23 lines
drivers/counter/counter-sysfs.c new file +23 -0
+	if (err)
+		return err;
+
+	switch (a->scope) {
+	case COUNTER_SCOPE_DEVICE:
+		err = a->comp.device_u8_write(counter, data);
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		err = a->comp.signal_u8_write(counter, a->parent, data);
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
+			err = a->comp.action_write(counter, count, synapse,
+						   data);
+		else
+			err = a->comp.count_u8_write(counter, count, data);
+		break;
+	}
+	if (err)
+		return err;
+
+	return len;
+}
#7 lore raw

Re: [PATCH v5 1/5] counter: Internalize sysfs interface code

Quoted 46 lines · David Lechner
Quoted 31 lines · William Breathitt Gray
drivers/counter/counter-sysfs.c new file +31 -0
+static ssize_t counter_comp_u8_store(struct device *dev,
+				     struct device_attribute *attr,
+				     const char *buf, size_t len)
+{
+	const struct counter_attribute *const a = to_counter_attribute(attr);
+	struct counter_device *const counter = dev_get_drvdata(dev);
+	struct counter_count *const count = a->parent;
+	struct counter_synapse *const synapse = a->comp.priv;
+	const struct counter_available *const avail = a->comp.priv;
+	int err;
+	bool bool_data;
+	u8 data;
+
+	switch (a->comp.type) {
+	case COUNTER_COMP_BOOL:
+		err = kstrtobool(buf, &bool_data);
+		data = bool_data;
+		break;
+	case COUNTER_COMP_FUNCTION:
+		err = find_in_string_array(&data, count->functions_list,
+					   count->num_functions, buf,
+					   counter_function_str);
+		break;
+	case COUNTER_COMP_SYNAPSE_ACTION:
+		err = find_in_string_array(&data, synapse->actions_list,
+					   synapse->num_actions, buf,
+					   counter_synapse_action_str);
+		break;
+	case COUNTER_COMP_ENUM:
+		err = __sysfs_match_string(avail->strs, avail->num_items, buf);
+		data = err;

drivers/counter/counter-sysfs.c line 189

Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10 -0
+		break;
+	case COUNTER_COMP_COUNT_MODE:
+		err = find_in_string_array(&data, avail->enums,
+					   avail->num_items, buf,
+					   counter_count_mode_str);
+		break;
+	default:
+		err = kstrtou8(buf, 0, &data);
+		break;
+	}

In this function, return values are not always errors. So it would make sense to call the err variable ret instead and check for ret < 0 below.

Quoted 1 line

Setting enums to a value with index > 0 fails currently because of this.

Thank you for pointing this out, I'll fix this.

William Breathitt Gray

Quoted 24 lines
Quoted 23 lines
drivers/counter/counter-sysfs.c new file +23 -0
+	if (err)
+		return err;
+
+	switch (a->scope) {
+	case COUNTER_SCOPE_DEVICE:
+		err = a->comp.device_u8_write(counter, data);
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		err = a->comp.signal_u8_write(counter, a->parent, data);
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
+			err = a->comp.action_write(counter, count, synapse,
+						   data);
+		else
+			err = a->comp.count_u8_write(counter, count, data);
+		break;
+	}
+	if (err)
+		return err;
+
+	return len;
+}
#8 lore raw

PATCH v5 2/5 docs: counter: Update to reflect sysfs internalization

In reply to William Breathitt Gray

The Counter subsystem architecture and driver implementations have changed in order to handle Counter sysfs interactions in a more consistent way. This patch updates the Generic Counter interface documentation to reflect the changes.

---
 Documentation/driver-api/generic-counter.rst | 242 ++++++++++++++-----
 1 file changed, 176 insertions(+), 66 deletions(-)
Documentation/driver-api/generic-counter.rst +176 -66
index b02c52cd69d6..b842ddbbd8a0 100644
@@ -250,8 +250,8 @@ for defining a counter device.
 .. kernel-doc:: drivers/counter/counter.c
    :export:
 
-Implementation
-==============
+Driver Implementation
+=====================
 
 To support a counter device, a driver must first allocate the available
 Counter Signals via counter_signal structures. These Signals should
@@ -267,25 +267,59 @@ respective counter_count structure. These counter_count structures are
 set to the counts array member of an allocated counter_device structure
 before the Counter is registered to the system.
 
-Driver callbacks should be provided to the counter_device structure via
-a constant counter_ops structure in order to communicate with the
-device: to read and write various Signals and Counts, and to set and get
-the "action mode" and "function mode" for various Synapses and Counts
-respectively.
+Driver callbacks must be provided to the counter_device structure in
+order to communicate with the device: to read and write various Signals
+and Counts, and to set and get the "action mode" and "function mode" for
+various Synapses and Counts respectively.
 
 A defined counter_device structure may be registered to the system by
 passing it to the counter_register function, and unregistered by passing
 it to the counter_unregister function. Similarly, the
-devm_counter_register and devm_counter_unregister functions may be used
-if device memory-managed registration is desired.
-
-Extension sysfs attributes can be created for auxiliary functionality
-and data by passing in defined counter_device_ext, counter_count_ext,
-and counter_signal_ext structures. In these cases, the
-counter_device_ext structure is used for global/miscellaneous exposure
-and configuration of the respective Counter device, while the
-counter_count_ext and counter_signal_ext structures allow for auxiliary
-exposure and configuration of a specific Count or Signal respectively.
+devm_counter_register function may be used if device memory-managed
+registration is desired.
+
+The struct counter_comp structure is used to define counter extensions
+for Signals, Synapses, and Counts.
+
+The "type" member specifies the type of high-level data (e.g. BOOL,
+COUNT_DIRECTION, etc.) handled by this extension. The "`*_read`" and
+"`*_write`" members can then be set by the counter device driver with
+callbacks to handle that data using native C data types (i.e. u8, u64,
+etc.).
+
+Convenience macros such as `COUNTER_COMP_COUNT_U64` are provided for use
+by driver authors. In particular, driver authors are expected to use
+the provided macros for standard Counter subsystem attributes in order
+to maintain a consistent interface for userspace. For example, a counter
+device driver may define several standard attributes like so::
+
+        struct counter_comp count_ext[] = {
+                COUNTER_COMP_DIRECTION(count_direction_read),
+                COUNTER_COMP_ENABLE(count_enable_read, count_enable_write),
+                COUNTER_COMP_CEILING(count_ceiling_read, count_ceiling_write),
+        };
+
+This makes it simple to see, add, and modify the attributes that are
+supported by this driver ("direction", "enable", and "ceiling") and to
+maintain this code without getting lost in a web of struct braces.
+
+Callbacks must match the function type expected for the respective
+component or extension. These function types are defined in the struct
+counter_comp structure as the "`*_read`" and "`*_write`" union members.
+
+The corresponding callback prototypes for the extensions mentioned in
+the previous example above would be::
+
+        int count_direction_read(struct counter_device *counter,
+                                 struct counter_count *count, u8 *direction);
+        int count_enable_read(struct counter_device *counter,
+                              struct counter_count *count, u8 *enable);
+        int count_enable_write(struct counter_device *counter,
+                               struct counter_count *count, u8 enable);
+        int count_ceiling_read(struct counter_device *counter,
+                               struct counter_count *count, u64 *ceiling);
+        int count_ceiling_write(struct counter_device *counter,
+                                struct counter_count *count, u64 ceiling);
 
 Determining the type of extension to create is a matter of scope.
 
@@ -313,52 +347,128 @@ Determining the type of extension to create is a matter of scope.
   chip overheated via a device extension called "error_overtemp":
   /sys/bus/counter/devices/counterX/error_overtemp
 
-Architecture
-============
-
-When the Generic Counter interface counter module is loaded, the
-counter_init function is called which registers a bus_type named
-"counter" to the system. Subsequently, when the module is unloaded, the
-counter_exit function is called which unregisters the bus_type named
-"counter" from the system.
-
-Counter devices are registered to the system via the counter_register
-function, and later removed via the counter_unregister function. The
-counter_register function establishes a unique ID for the Counter
-device and creates a respective sysfs directory, where X is the
-mentioned unique ID:
-
-    /sys/bus/counter/devices/counterX
-
-Sysfs attributes are created within the counterX directory to expose
-functionality, configurations, and data relating to the Counts, Signals,
-and Synapses of the Counter device, as well as options and information
-for the Counter device itself.
-
-Each Signal has a directory created to house its relevant sysfs
-attributes, where Y is the unique ID of the respective Signal:
-
-    /sys/bus/counter/devices/counterX/signalY
-
-Similarly, each Count has a directory created to house its relevant
-sysfs attributes, where Y is the unique ID of the respective Count:
-
-    /sys/bus/counter/devices/counterX/countY
-
-For a more detailed breakdown of the available Generic Counter interface
-sysfs attributes, please refer to the
-Documentation/ABI/testing/sysfs-bus-counter file.
-
-The Signals and Counts associated with the Counter device are registered
-to the system as well by the counter_register function. The
-signal_read/signal_write driver callbacks are associated with their
-respective Signal attributes, while the count_read/count_write and
-function_get/function_set driver callbacks are associated with their
-respective Count attributes; similarly, the same is true for the
-action_get/action_set driver callbacks and their respective Synapse
-attributes. If a driver callback is left undefined, then the respective
-read/write permission is left disabled for the relevant attributes.
-
-Similarly, extension sysfs attributes are created for the defined
-counter_device_ext, counter_count_ext, and counter_signal_ext
-structures that are passed in.
+Subsystem Architecture
+======================
+
+Counter drivers pass and take data natively (i.e. `u8`, `u64`, etc.) and
+the shared counter module handles the translation between the sysfs
+interface. This gurantees a standard userspace interface for all counter
+drivers, and helps generalize the Generic Counter driver ABI in order to
+support the Generic Counter chrdev interface without significant changes
+to the existing counter drivers.
+
+A high-level view of how a count value is passed down from a counter
+driver is exemplified by the following. The driver callbacks are first
+registered to the Counter core component for use by the Counter
+userspace interface components::
+
+        Driver callbacks registration:
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+                        +----------------------------+
+	                | Counter device driver      |
+                        +----------------------------+
+                        | Processes data from device |
+                        +----------------------------+
+                                |
+                         -------------------
+                        / driver callbacks /
+                        -------------------
+                                |
+                                V
+                        +----------------------+
+                        | Counter core         |
+                        +----------------------+
+                        | Routes device driver |
+                        | callbacks to the     |
+                        | userspace interfaces |
+                        +----------------------+
+                                |
+                         -------------------
+                        / driver callbacks /
+                        -------------------
+                                |
+                +---------------+
+                |
+                V
+        +--------------------+
+        | Counter sysfs      |
+        +--------------------+
+        | Translates to the  |
+        | standard Counter   |
+        | sysfs output       |
+        +--------------------+
+
+Thereafter, data can be transferred directly between the Counter device
+driver and Counter userspace interface::
+
+        Count data request:
+        ~~~~~~~~~~~~~~~~~~~
+                         ----------------------
+                        / Counter device       \
+                        +----------------------+
+                        | Count register: 0x28 |
+                        +----------------------+
+                                |
+                         -----------------
+                        / raw count data /
+                        -----------------
+                                |
+                                V
+                        +----------------------------+
+                        | Counter device driver      |
+                        +----------------------------+
+                        | Processes data from device |
+                        |----------------------------|
+                        | Type: u64                  |
+                        | Value: 42                  |
+                        +----------------------------+
+                                |
+                         ----------
+                        / u64     /
+                        ----------
+                                |
+                +---------------+
+                |
+                V
+        +--------------------+
+        | Counter sysfs      |
+        +--------------------+
+        | Translates to the  |
+        | standard Counter   |
+        | sysfs output       |
+        |--------------------|
+        | Type: const char * |
+        | Value: "42"        |
+        +--------------------+
+                |
+         ---------------
+        / const char * /
+        ---------------
+                |
+                V
+        +--------------------------------------------------+
+        | `/sys/bus/counter/devices/counterX/countY/count` |
+        +--------------------------------------------------+
+        \ Count: "42"                                      /
+         --------------------------------------------------
+
+There are three primary components involved:
+
+Counter device driver
+---------------------
+Communicates with the hardware device to read/write data; e.g. counter
+drivers for quadrature encoders, timers, etc.
+
+Counter core
+------------
+Registers the counter device driver to the system so that the respective
+callbacks are called during userspace interaction.
+
+Counter sysfs
+-------------
+Translates counter data to the standard Counter sysfs interface format
+and vice versa.
+
+Please refer to the `Documentation/ABI/testing/sysfs-bus-counter` file
+for a detailed breakdown of the available Generic Counter interface
+sysfs attributes.
Signature · 1 line
2.28.0
#9 lore raw

PATCH v5 3/5 counter: Add character device interface

In reply to William Breathitt Gray

This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.

A high-level view of how a count value is passed down from a counter driver is exemplified by the following. The driver callbacks are first registered to the Counter core component for use by the Counter userspace interface components:

                        +----------------------------+
	                | Counter device driver      |
                        +----------------------------+
                        | Processes data from device |
                        +----------------------------+
                                |
                         -------------------
                        / driver callbacks /
                        -------------------
                                |
                                V
                        +----------------------+
                        | Counter core         |
                        +----------------------+
                        | Routes device driver |
                        | callbacks to the     |
                        | userspace interfaces |
                        +----------------------+
                                |
                         -------------------
                        / driver callbacks /
                        -------------------
                                |
                +---------------+---------------+
                |                               |
                V                               V
        +--------------------+          +---------------------+
        | Counter sysfs      |          | Counter chrdev      |
        +--------------------+          +---------------------+
        | Translates to the  |          | Translates to the   |
        | standard Counter   |          | standard Counter    |
        | sysfs output       |          | character device    |
        +--------------------+          +---------------------+

Thereafter, data can be transferred directly between the Counter device driver and Counter userspace interface:

                         ----------------------
                        / Counter device       \
                        +----------------------+
                        | Count register: 0x28 |
                        +----------------------+
                                |
                         -----------------
                        / raw count data /
                        -----------------
                                |
                                V
                        +----------------------------+
                        | Counter device driver      |
                        +----------------------------+
                        | Processes data from device |
                        |----------------------------|
                        | Type: u64                  |
                        | Value: 42                  |
                        +----------------------------+
                                |
                         ----------
                        / u64     /
                        ----------
                                |
                +---------------+---------------+
                |                               |
                V                               V
        +--------------------+          +---------------------+
        | Counter sysfs      |          | Counter chrdev      |
        +--------------------+          +---------------------+
        | Translates to the  |          | Translates to the   |
        | standard Counter   |          | standard Counter    |
        | sysfs output       |          | character device    |
        |--------------------|          |---------------------|
        | Type: const char * |          | Type: u64           |
        | Value: "42"        |          | Value: 42           |
        +--------------------+          +---------------------+
                |                               |
         ---------------                 -----------------------
        / const char * /                / struct counter_event /
        ---------------                 -----------------------
                |                               |
                |                               V
                |                       +-----------+
                |                       | read      |
                |                       +-----------+
                |                       \ Count: 42 /
                |                        -----------
                |
                V
        +--------------------------------------------------+
        | `/sys/bus/counter/devices/counterX/countY/count` |
        +--------------------------------------------------+
        \ Count: "42"                                      /
         --------------------------------------------------

Counter character device nodes are created under the /dev directory as counterX, where X is the respective counter device id. Defines for the standard Counter data types are exposed via the userspace include/uapi/linux/counter.h file.

Counter events
--------------
Counter device drivers can support Counter events by utilizing the counter_push_event function:

int counter_push_event(struct counter_device *const counter,
const u8 event, const u8 channel);

The event id is specified by the event parameter; the event channel id is specified by the channel parameter. When this function is called, the Counter data associated with the respective event is gathered, and a struct counter_event is generated for each datum and pushed to userspace.

Counter events can be configured by users to report various Counter data of interest. This can be conceptualized as a list of Counter component read calls to perform. For example:

+~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+
| COUNTER_EVENT_OVERFLOW | COUNTER_EVENT_INDEX |
+~~~~~~~~~~~~~~~~~~~~~~~~+~~~~~~~~~~~~~~~~~~~~~~~~+
| Channel 0 | Channel 0 |
+------------------------+------------------------+
| * Count 0 | * Signal 0 |
| * Count 1 | * Signal 0 Extension 0 |
| * Signal 3 | * Extension 4 |
| * Count 4 Extension 2 +------------------------+
| * Signal 5 Extension 0 | Channel 1 |
| +------------------------+
| | * Signal 4 |
| | * Signal 4 Extension 0 |
| | * Count 7 |
+------------------------+------------------------+

When counter_push_event(counter, COUNTER_EVENT_INDEX, 1) is called for example, it will go down the list for the COUNTER_EVENT_INDEX event channel 1 and execute the read callbacks for Signal 4, Signal 4 Extension 0, and Count 4 -- the data returned for each is pushed to a kfifo as a struct counter_event, which userspace can retrieve via a standard read operation on the respective character device node.

Userspace
---------
Userspace applications can configure Counter events via ioctl operations on the Counter character device node. There following ioctl codes are supported and provided by the linux/counter.h userspace header file:

* COUNTER_CLEAR_WATCHES_IOCTL:
Clear all Counter watches from all events

* COUNTER_SET_WATCH_IOCTL:
Set a Counter watch for the specified event

* COUNTER_LOAD_WATCHES_IOCTL:
Activates the Counter watches set earlier

To configure events to gather Counter data, users first populate a struct counter_watch with the relevant event id, event channel id, and the information for the desired Counter component from which to read, and then pass it via the COUNTER_SET_WATCH_IOCTL ioctl command.

The COUNTER_SET_WATCH_IOCTL command will buffer these Counter watches. When ready, the COUNTER_LOAD_WATCHES_IOCTL ioctl command may be used to activate these Counter watches.

Userspace applications can then execute a read operation (optionally calling poll first) on the Counter character device node to retrieve struct counter_event elements with the desired data.

For example, the following userspace code opens /dev/counter0, configures the COUNTER_EVENT_INDEX event channel 0 to gather Count 0 and Count 1, and prints out the data as it becomes available on the character device node:

    #include <fcntl.h>
    #include <linux/counter.h>
    #include <poll.h>
    #include <stdio.h>
    #include <sys/ioctl.h>
    #include <unistd.h>

    struct counter_watch watches[2] = {
            {
                    .event = COUNTER_EVENT_INDEX,
                    .channel = 0,
                    .component.scope = COUNTER_SCOPE_COUNT,
                    .component.parent = 0,
                    .component.type = COUNTER_COMPONENT_COUNT,
            },
            {
                    .event = COUNTER_EVENT_INDEX,
                    .channel = 0,
                    .component.scope = COUNTER_SCOPE_COUNT,
                    .component.parent = 1,
                    .component.type = COUNTER_COMPONENT_COUNT,
            },
    };

    int main(void)
    {
            struct pollfd pfd = { .events = POLLIN };
            struct counter_event event_data[2];

            pfd.fd = open("/dev/counter0", O_RDWR);

            ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
            ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
            ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL);

            for (;;) {
                    poll(&pfd, 1, -1);

                    read(pfd.fd, event_data, sizeof(event_data));

                    printf("Timestamp 0: %llu\nCount 0: %llu\n"
                           "Timestamp 1: %llu\nCount 1: %llu\n",
                           (unsigned long long)event_data[0].timestamp,
                           (unsigned long long)event_data[0].value_u64,
                           (unsigned long long)event_data[1].timestamp,
                           (unsigned long long)event_data[1].value_u64);
            }

            return 0;
    }
Cc David Lechner <[email protected]>
Cc Gwendal Grignou <[email protected]>
---
 MAINTAINERS                      |   1 +
 drivers/counter/Makefile         |   2 +-
 drivers/counter/counter-chrdev.c | 451 +++++++++++++++++++++++++++++++
 drivers/counter/counter-chrdev.h |  16 ++
 drivers/counter/counter-core.c   |  38 ++-
 drivers/counter/counter-sysfs.c  | 109 +++++++-
 include/linux/counter.h          |  48 ++--
 include/uapi/linux/counter.h     |  99 +++++++
 8 files changed, 720 insertions(+), 44 deletions(-)
 create mode 100644 drivers/counter/counter-chrdev.c
 create mode 100644 drivers/counter/counter-chrdev.h
 create mode 100644 include/uapi/linux/counter.h
MAINTAINERS +1 -0
index 4969e997061a..de6685a683c8 100644
@@ -4459,6 +4459,7 @@ F:	Documentation/ABI/testing/sysfs-bus-counter*
 F:	Documentation/driver-api/generic-counter.rst
 F:	drivers/counter/
 F:	include/linux/counter.h
+F:	include/uapi/linux/counter.h
 
 CPMAC ETHERNET DRIVER
 M:	Florian Fainelli <[email protected]>
drivers/counter/Makefile +1 -1
index cbe1d06af6a9..c4870eb5b1dd 100644
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_COUNTER) += counter.o
-counter-y := counter-core.o counter-sysfs.o
+counter-y := counter-core.o counter-sysfs.o counter-chrdev.o
 
 obj-$(CONFIG_104_QUAD_8)	+= 104-quad-8.o
 obj-$(CONFIG_STM32_TIMER_CNT)	+= stm32-timer-cnt.o
drivers/counter/counter-chrdev.c new file +451 -0

451 lines not on this page · raw message

drivers/counter/counter-chrdev.h new file +16 -0
new file mode 100644
index 000000000000..cf5a318fe540
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Counter character device interface
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _COUNTER_CHRDEV_H_
+#define _COUNTER_CHRDEV_H_
+
+#include <linux/counter.h>
+#include <linux/types.h>
+
+int counter_chrdev_add(struct counter_device *const counter,
+		       const dev_t counter_devt);
+void counter_chrdev_remove(struct counter_device *const counter);
+
+#endif /* _COUNTER_CHRDEV_H_ */
drivers/counter/counter-core.c +34 -4
index 987c6e8277eb..e9c01de36fcf 100644
@@ -5,12 +5,16 @@
  */
 #include <linux/counter.h>
 #include <linux/device.h>
+#include <linux/device/bus.h>
 #include <linux/export.h>
+#include <linux/fs.h>
 #include <linux/gfp.h>
 #include <linux/idr.h>
 #include <linux/init.h>
 #include <linux/module.h>
+#include <linux/types.h>
 
+#include "counter-chrdev.h"
 #include "counter-sysfs.h"
 
 /* Provides a unique ID for each counter device */
@@ -32,6 +36,8 @@ static struct bus_type counter_bus_type = {
 	.name = "counter"
 };
 
+static dev_t counter_devt;
+
 /**
  * counter_register - register Counter to the system
  * @counter:	pointer to Counter to register
@@ -50,7 +56,6 @@ int counter_register(struct counter_device *const counter)
 	if (counter->id < 0)
 		return counter->id;
 
-	/* Configure device structure for Counter */
 	dev->type = &counter_device_type;
 	dev->bus = &counter_bus_type;
 	if (counter->parent) {
@@ -61,20 +66,27 @@ int counter_register(struct counter_device *const counter)
 	device_initialize(dev);
 	dev_set_drvdata(dev, counter);
 
+	/* Add Counter character device */
+	err = counter_chrdev_add(counter, counter_devt);
+	if (err)
+		goto err_free_id;
+
 	/* Add Counter sysfs attributes */
 	err = counter_sysfs_add(counter);
 	if (err)
-		goto err_free_id;
+		goto err_remove_chrdev;
 
 	/* Add device to system */
 	err = device_add(dev);
 	if (err) {
 		put_device(dev);
-		goto err_free_id;
+		goto err_remove_chrdev;
 	}
 
 	return 0;
 
+err_remove_chrdev:
+	counter_chrdev_remove(counter);
 err_free_id:
 	/* get_device/put_device combo used to free managed resources */
 	get_device(dev);
@@ -96,6 +108,7 @@ void counter_unregister(struct counter_device *const counter)
 		return;
 
 	device_unregister(&counter->dev);
+	counter_chrdev_remove(counter);
 }
 EXPORT_SYMBOL_GPL(counter_unregister);
 
@@ -142,13 +155,30 @@ int devm_counter_register(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_counter_register);
 
+#define COUNTER_DEV_MAX 256
+
 static int __init counter_init(void)
 {
-	return bus_register(&counter_bus_type);
+	int err;
+
+	err = bus_register(&counter_bus_type);
+	if (err < 0)
+		return err;
+
+	err = alloc_chrdev_region(&counter_devt, 0, COUNTER_DEV_MAX, "counter");
+	if (err < 0)
+		goto err_unregister_bus;
+
+	return 0;
+
+err_unregister_bus:
+	bus_unregister(&counter_bus_type);
+	return err;
 }
 
 static void __exit counter_exit(void)
 {
+	unregister_chrdev_region(counter_devt, COUNTER_DEV_MAX);
 	bus_unregister(&counter_bus_type);
 }
 
drivers/counter/counter-sysfs.c +99 -10

180 lines not on this page · raw message

include/linux/counter.h +19 -29

82 lines not on this page · raw message

include/uapi/linux/counter.h new file +99 -0

99 lines not on this page · raw message

Signature · 1 line
2.28.0
reviewer
#10 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 6 lines · William Breathitt Gray

This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.

Probably don't need to duplicate the full documentation in the commit message.

Quoted 31 lines
drivers/counter/counter-chrdev.c new file +24 -0
new file mode 100644
index 000000000000..2be3846e4105
 
 
+
+static int counter_set_event_node(struct counter_device *const counter,
+				  struct counter_watch *const watch,
+				  const struct counter_comp_node *const cfg)
+{
+	struct counter_event_node *event_node;
+	int err;
+	struct counter_comp_node *comp_node;
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->next_events_list, l)
+		if (event_node->event == watch->event &&
+		    event_node->channel == watch->channel)
+			break;
+
+	/* If event is not already in the list */
+	if (&event_node->l == &counter->next_events_list) {
+		/* Allocate new event node */
+		event_node = kmalloc(sizeof(*event_node), GFP_ATOMIC);
+		if (!event_node)
+			return -ENOMEM;
+
+		/* Configure event node and add to the list */
+		event_node->event = watch->event;

drivers/counter/counter-chrdev.c line 137

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+		event_node->channel = watch->channel;
+		INIT_LIST_HEAD(&event_node->comp_list);
+		list_add(&event_node->l, &counter->next_events_list);
+	}
+
+	/* Check if component watch has already been set before */
+	list_for_each_entry(comp_node, &event_node->comp_list, l)
+		if (comp_node->parent == cfg->parent &&
+		    comp_node->comp.count_u8_read == cfg->comp.count_u8_read)
+			return -EINVAL;

There are already enough things that could cause EINVAL, we could probably skip this duplicate check to make troubleshooting easier.

drivers/counter/counter-chrdev.c line 143

Quoted 6 lines
drivers/counter/counter-chrdev.c new file +6 -0
+
+	/* Allocate component node */
+	comp_node = kmalloc(sizeof(*comp_node), GFP_ATOMIC);
+	if (!comp_node) {
+		err = -ENOMEM;
+		goto err_comp_node;

Since there is only one goto, we could just handle the error and return here instead.

drivers/counter/counter-chrdev.c line 152

Quoted 9 lines
drivers/counter/counter-chrdev.c new file +9 -0
+	}
+	*comp_node = *cfg;
+
+	/* Add component node to event node */
+	list_add_tail(&comp_node->l, &event_node->comp_list);
+
+	return 0;
+
+err_comp_node:

A comment explaining the list_empty() check would be nice. It makes sense if you think about it, but it is not super obvious.

Quoted 22 lines
drivers/counter/counter-chrdev.c new file +22 -0
+	if (list_empty(&event_node->comp_list)) {
+		list_del(&event_node->l);
+		kfree(event_node);
+	}
+	return err;
+}
+
+static int counter_set_watch(struct counter_device *const counter,
+			     const unsigned long arg)
+{
+	void __user *const uwatch = (void __user *)arg;
+	struct counter_watch watch;
+	struct counter_comp_node comp_node;
+	size_t parent, id;
+	struct counter_comp *ext;
+	size_t num_ext;
+
+	if (copy_from_user(&watch, uwatch, sizeof(watch)))
+		return -EFAULT;
+	parent = watch.component.parent;
+	id = watch.component.id;
+

drivers/counter/counter-chrdev.c line 184

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+	/* Configure parent component info for comp node */
+	switch (watch.component.scope) {
+	case COUNTER_SCOPE_DEVICE:
+		comp_node.parent = NULL;
+
+		ext = counter->ext;
+		num_ext = counter->num_ext;
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		if (counter->num_signals < parent + 1)

I think it would be more conventional this way:

	if (parent >= counter->num_signals)

drivers/counter/counter-chrdev.c line 193

Quoted 9 lines
drivers/counter/counter-chrdev.c new file +9 -0
+			return -EINVAL;
+
+		comp_node.parent = counter->signals + parent;
+
+		ext = counter->signals[parent].ext;
+		num_ext = counter->signals[parent].num_ext;
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (counter->num_counts < parent + 1)

Same here.

Quoted 28 lines
drivers/counter/counter-chrdev.c new file +28 -0
+			return -EINVAL;
+
+		comp_node.parent = counter->counts + parent;
+
+		ext = counter->counts[parent].ext;
+		num_ext = counter->counts[parent].num_ext;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Configure component info for comp node */
+	switch (watch.component.type) {
+	case COUNTER_COMPONENT_SIGNAL:
+		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
+		comp_node.comp.signal_u8_read = counter->ops->signal_read;
+		break;
+	case COUNTER_COMPONENT_COUNT:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_U64;
+		comp_node.comp.count_u64_read = counter->ops->count_read;
+		break;
+	case COUNTER_COMPONENT_FUNCTION:

drivers/counter/counter-chrdev.c line 231

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_FUNCTION;
+		comp_node.comp.count_u8_read = counter->ops->function_read;
+		break;
+	case COUNTER_COMPONENT_SYNAPSE_ACTION:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+		if (counter->counts[parent].num_synapses < id + 1)
	if (id >= counter->counts[parent].num_synapses)

drivers/counter/counter-chrdev.c line 239

Quoted 8 lines
drivers/counter/counter-chrdev.c new file +8 -0
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
+		comp_node.comp.action_read = counter->ops->action_read;
+		comp_node.comp.priv = counter->counts[parent].synapses + id;
+		break;
+	case COUNTER_COMPONENT_EXTENSION:
+		if (num_ext < id + 1)
	if (id >= num_ext)

drivers/counter/counter-chrdev.c line 247

Quoted 8 lines
drivers/counter/counter-chrdev.c new file +8 -0
+			return -EINVAL;
+
+		comp_node.comp = ext[id];
+		break;
+	default:
+		return -EINVAL;
+	}
+	if (!comp_node.comp.count_u8_read)

A comment reminding us that count_u8_read is part of a union would be helpful here since this is checking that any read function was set, not just count_u8_read.

drivers/counter/counter-chrdev.c

Quoted 1 line
drivers/counter/counter-chrdev.c new file +1 -0
+		return -EFAULT;

Would EOPNOTSUPP be a better error choice?

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+	comp_node.component = watch.component;
+
+	return counter_set_event_node(counter, &watch, &comp_node);
+}
+
+static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct counter_device *const counter = filp->private_data;
+	raw_spinlock_t *const events_lock = &counter->events_lock;

drivers/counter/counter-chrdev.c line 268

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+	unsigned long flags;
+	struct list_head *const events_list = &counter->events_list;
+	struct list_head *const next_events_list = &counter->next_events_list;
+
+	switch (cmd) {
+	case COUNTER_CLEAR_WATCHES_IOCTL:
+		raw_spin_lock_irqsave(events_lock, flags);
+		counter_events_list_free(events_list);
+		raw_spin_unlock_irqrestore(events_lock, flags);
+		counter_events_list_free(next_events_list);

I think this ioctl is doing too much. If we have to use it for both stopping events and clearing the list accumulated by COUNTER_SET_WATCH_IOCTL, then we have a race condition of no events after clearing watches during the time we are adding new ones and until we load the new ones.

It would probably make more sense to call this ioctl COUNTER_STOP_WATCHES_IOCTL and move counter_events_list_free( next_events_list) to the end of COUNTER_LOAD_WATCHES_IOCTL.

drivers/counter/counter-chrdev.c line 271

Quoted 3 lines
drivers/counter/counter-chrdev.c new file +3 -0
+		break;
+	case COUNTER_SET_WATCH_IOCTL:
+		return counter_set_watch(counter, arg);

Would it make more sense to call this COUNTER_ADD_WATCH_IOCTL since it adds a watch to the list that will be loaded/enabled later?

drivers/counter/counter-chrdev.c line 276

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+	case COUNTER_LOAD_WATCHES_IOCTL:
+		raw_spin_lock_irqsave(events_lock, flags);
+		counter_events_list_free(events_list);
+		list_replace_init(next_events_list, events_list);
+		raw_spin_unlock_irqrestore(events_lock, flags);

For the eQEP driver, we will need a callback here to configure interrupts for the events. Likewise a callback would be needed here (for the case when we are replacing already loaded watches) and in COUNTER_STOP_WATCHES_IOCTL to unconfigure them.

Quoted 16 lines
drivers/counter/counter-chrdev.c new file +16 -0
+		break;
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+static int counter_chrdev_open(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = container_of(inode->i_cdev,
+							    typeof(*counter),
+							    chrdev);
+
+	get_device(&counter->dev);
+	filp->private_data = counter;

drivers/counter/counter-chrdev.c line 302

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+
+	return nonseekable_open(inode, filp);
+}
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = filp->private_data;
+	unsigned long flags;
+
+	put_device(&counter->dev);

put_device() should be at the end of the function in case it is the last reference.

Quoted 48 lines
drivers/counter/counter-chrdev.c new file +47 -0
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+	counter_events_list_free(&counter->events_list);
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	counter_events_list_free(&counter->next_events_list);
+
+	return 0;
+}
+
 
+int counter_push_event(struct counter_device *const counter, const u8 event,
+		       const u8 channel)
+{
+	struct counter_event ev = {0};
+	unsigned int copied = 0;
+	unsigned long flags;
+	struct counter_event_node *event_node;
+	struct counter_comp_node *comp_node;
+	int err;
+
+	ev.timestamp = ktime_get_ns();
+	ev.watch.event = event;
+	ev.watch.channel = channel;
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->events_list, l)
+		if (event_node->event == event &&
+		    event_node->channel == channel)
+			break;
+
+	/* If event is not in the list */
+	if (&event_node->l == &counter->events_list)
+		goto exit_early;
+
+	/* Read and queue relevant comp for userspace */
+	list_for_each_entry(comp_node, &event_node->comp_list, l) {
+		err = counter_get_data(counter, comp_node, &ev.value_u8);
+		if (err)
+			goto err_counter_get_data;
+
+		ev.watch.component = comp_node->component;
+
+		copied += kfifo_put(&counter->events, ev);
+	}
+
+	if (copied)

drivers/counter/counter-chrdev.c line 449

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+		wake_up_poll(&counter->events_wait, EPOLLIN);
+
+exit_early:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+
+	return 0;
+
+err_counter_get_data:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	return err;

This seems slightly redundant. Could just initialize err to 0 and combine the two labels.

drivers/counter/counter-sysfs.c

Quoted 8 lines
drivers/counter/counter-chrdev.c new file +2 -0
+}
+EXPORT_SYMBOL_GPL(counter_push_event);
drivers/counter/counter-sysfs.c +0 -0
index e66ed99dd5ea..cefef61f170d 100644

Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?

Quoted 6 lines
include/uapi/linux/counter.h new file +0 -0
new file mode 100644
index 000000000000..ad9a8686b2b0

include/uapi/linux/counter.h line 10

Quoted 10 lines
include/uapi/linux/counter.h new file +10 -0
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace ABI for Counter character devices
+ * Copyright (C) 2020 William Breathitt Gray
+ */
+#ifndef _UAPI_COUNTER_H_
+#define _UAPI_COUNTER_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>

It would be very helpful to have documentation comments for all of the defines in this file.

include/uapi/linux/counter.h line 20

Quoted 10 lines
include/uapi/linux/counter.h new file +10 -0
+
+#define COUNTER_SCOPE_DEVICE 0
+#define COUNTER_SCOPE_SIGNAL 1
+#define COUNTER_SCOPE_COUNT 2
+
+#define COUNTER_COMPONENT_SIGNAL 0
+#define COUNTER_COMPONENT_COUNT 1
+#define COUNTER_COMPONENT_FUNCTION 2
+#define COUNTER_COMPONENT_SYNAPSE_ACTION 3
+#define COUNTER_COMPONENT_EXTENSION 4

There are probably some cases where we will want to just have an event without reading a component value, so we could use something like COUNTER_SCOPE_NONE and COUNTER_COMPONENT_NONE here.

Quoted 4 lines
include/uapi/linux/counter.h new file +4 -0
+
+/**
+ * struct counter_component - Counter component identification
+ * @scope: component scope (Device, Count, or Signal)

include/uapi/linux/counter.h line 34

Quoted 10 lines
include/uapi/linux/counter.h new file +10 -0
+ * @parent: parent component identification number
+ * @type: component type (Count, extension, etc.)
+ * @id: component identification number
+ */
+struct counter_component {
+	__u8 scope;
+	__u64 parent;
+	__u8 type;
+	__u64 id;
+};

This will have alignment issues, e.g. when 64-bit kernel is used with a 32-bit userspace. It looks like there is a __aligned_u64 type to help with this. But, unless we expect more that 4 billion counter devices, there doesn't seem to be a reason for parent and id to be 64-bit.

include/uapi/linux/counter.h line 38

Quoted 4 lines
include/uapi/linux/counter.h new file +4 -0
+
+#define COUNTER_EVENT_OVERFLOW 0
+#define COUNTER_EVENT_UNDERFLOW 1
+#define COUNTER_EVENT_OVERFLOW_UNDERFLOW 2

Would it make sense to just have individual overflow and underflow events? For example, the Carry-Borrow interrupt on 104-quad-8 could just call counter_push_event() twice.

include/uapi/linux/counter.h line 45

Quoted 7 lines
include/uapi/linux/counter.h new file +7 -0
+#define COUNTER_EVENT_THRESHOLD 3
+#define COUNTER_EVENT_INDEX 4
+
+/**
+ * struct counter_watch - Counter component watch configuration
+ * @event: event that triggers
+ * @channel: event channel

How is channel intended to be used? (The device I am working with I assume would ignore the channel since it is just a single counter.)

include/uapi/linux/counter.h line 52

Quoted 7 lines
include/uapi/linux/counter.h new file +7 -0
+ * @component: component to watch when event triggers
+ */
+struct counter_watch {
+	__u8 event;
+	__u8 channel;
+	struct counter_component component;
+};

To get the most compact layout, usually fields are ordered largest to smallest.

Quoted 10 lines
include/uapi/linux/counter.h new file +10 -0
+
+#define COUNTER_CLEAR_WATCHES_IOCTL _IO(0x3E, 0x00)
+#define COUNTER_SET_WATCH_IOCTL _IOW(0x3E, 0x01, struct counter_watch)
+#define COUNTER_LOAD_WATCHES_IOCTL _IO(0x3E, 0x02)
+
+/**
+ * struct counter_event - Counter event data
+ * @timestamp: best estimate of time of event occurrence, in nanoseconds
+ * @watch: component watch configuration
+ * @value_u8: component value as __u8 data type

include/uapi/linux/counter.h line 72

Quoted 10 lines
include/uapi/linux/counter.h new file +10 -0
+ * @value_u64: component value as __u64 data type
+ */
+struct counter_event {
+	__u64 timestamp;
+	struct counter_watch watch;
+	union {
+		__u8 value_u8;
+		__u64 value_u64;
+	};
+};

Should probably use __aligned_u64 here too.

Quoted 18 lines
include/uapi/linux/counter.h new file +17 -0
+
+#define COUNTER_COUNT_DIRECTION_FORWARD 0
+#define COUNTER_COUNT_DIRECTION_BACKWARD 1
+
+#define COUNTER_COUNT_MODE_NORMAL 0
+#define COUNTER_COUNT_MODE_RANGE_LIMIT 1
+#define COUNTER_COUNT_MODE_NON_RECYCLE 2
+#define COUNTER_COUNT_MODE_MODULO_N 3
+
+#define COUNTER_FUNCTION_INCREASE 0
+#define COUNTER_FUNCTION_DECREASE 1
+#define COUNTER_FUNCTION_PULSE_DIRECTION 2
+#define COUNTER_FUNCTION_QUADRATURE_X1_A 3
+#define COUNTER_FUNCTION_QUADRATURE_X1_B 4
+#define COUNTER_FUNCTION_QUADRATURE_X2_A 5
+#define COUNTER_FUNCTION_QUADRATURE_X2_B 6
+#define COUNTER_FUNCTION_QUADRATURE_X4 7

include/uapi/linux/counter.h line 99

Quoted 10 lines
include/uapi/linux/counter.h new file +10 -0
+
+#define COUNTER_SIGNAL_LEVEL_LOW 0
+#define COUNTER_SIGNAL_LEVEL_HIGH 1
+
+#define COUNTER_SYNAPSE_ACTION_NONE 0
+#define COUNTER_SYNAPSE_ACTION_RISING_EDGE 1
+#define COUNTER_SYNAPSE_ACTION_FALLING_EDGE 2
+#define COUNTER_SYNAPSE_ACTION_BOTH_EDGES 3
+
+#endif /* _UAPI_COUNTER_H_ */

Overall, I think we are getting close. I've started adding events to the TI eQEP driver. You can find the code at [1]. I still have a bit of work to do to add the timer and edge capture events.

[1]: https://github.com/dlech/linux/tree/bone-counter

#11 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 10 lines · David Lechner
Quoted 6 lines · William Breathitt Gray

This patch introduces a character device interface for the Counter subsystem. Device data is exposed through standard character device read operations. Device data is gathered when a Counter event is pushed by the respective Counter device driver. Configuration is handled via ioctl operations on the respective Counter character device node.

Probably don't need to duplicate the full documentation in the commit message.

Ack.

Quoted 36 lines
Quoted 36 lines
drivers/counter/counter-chrdev.c new file +29 -0
new file mode 100644
index 000000000000..2be3846e4105
 
 
+
+static int counter_set_event_node(struct counter_device *const counter,
+				  struct counter_watch *const watch,
+				  const struct counter_comp_node *const cfg)
+{
+	struct counter_event_node *event_node;
+	int err;
+	struct counter_comp_node *comp_node;
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->next_events_list, l)
+		if (event_node->event == watch->event &&
+		    event_node->channel == watch->channel)
+			break;
+
+	/* If event is not already in the list */
+	if (&event_node->l == &counter->next_events_list) {
+		/* Allocate new event node */
+		event_node = kmalloc(sizeof(*event_node), GFP_ATOMIC);
+		if (!event_node)
+			return -ENOMEM;
+
+		/* Configure event node and add to the list */
+		event_node->event = watch->event;
+		event_node->channel = watch->channel;
+		INIT_LIST_HEAD(&event_node->comp_list);
+		list_add(&event_node->l, &counter->next_events_list);
+	}
+
Quoted 8 lines

drivers/counter/counter-chrdev.c line 137

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+	/* Check if component watch has already been set before */
+	list_for_each_entry(comp_node, &event_node->comp_list, l)
+		if (comp_node->parent == cfg->parent &&
+		    comp_node->comp.count_u8_read == cfg->comp.count_u8_read)
+			return -EINVAL;

There are already enough things that could cause EINVAL, we could probably skip this duplicate check to make troubleshooting easier.

I'm not sure it would be safe to remove this check. This prevents duplicate watches from being allocated for the same component -- the rationale is that there's no benefit in repeating a component value grab for the same event. Although a typical user will not have the motivation to do this, a bad actor could purposely keep allocating duplicate watches in order to starve all the available memory.

Quoted 9 lines

drivers/counter/counter-chrdev.c line 143

Quoted 6 lines
drivers/counter/counter-chrdev.c new file +6 -0
+
+	/* Allocate component node */
+	comp_node = kmalloc(sizeof(*comp_node), GFP_ATOMIC);
+	if (!comp_node) {
+		err = -ENOMEM;
+		goto err_comp_node;

Since there is only one goto, we could just handle the error and return here instead.

Ack.

Quoted 5 lines
Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+	}
+	*comp_node = *cfg;
+
+	/* Add component node to event node */
+	list_add_tail(&comp_node->l, &event_node->comp_list);
Quoted 8 lines

drivers/counter/counter-chrdev.c line 152

Quoted 4 lines
drivers/counter/counter-chrdev.c new file +4 -0
+
+	return 0;
+
+err_comp_node:

A comment explaining the list_empty() check would be nice. It makes sense if you think about it, but it is not super obvious.

Ack.

Quoted 35 lines
Quoted 22 lines
drivers/counter/counter-chrdev.c new file +22 -0
+	if (list_empty(&event_node->comp_list)) {
+		list_del(&event_node->l);
+		kfree(event_node);
+	}
+	return err;
+}
+
+static int counter_set_watch(struct counter_device *const counter,
+			     const unsigned long arg)
+{
+	void __user *const uwatch = (void __user *)arg;
+	struct counter_watch watch;
+	struct counter_comp_node comp_node;
+	size_t parent, id;
+	struct counter_comp *ext;
+	size_t num_ext;
+
+	if (copy_from_user(&watch, uwatch, sizeof(watch)))
+		return -EFAULT;
+	parent = watch.component.parent;
+	id = watch.component.id;
+

drivers/counter/counter-chrdev.c line 184

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+	/* Configure parent component info for comp node */
+	switch (watch.component.scope) {
+	case COUNTER_SCOPE_DEVICE:
+		comp_node.parent = NULL;
+
+		ext = counter->ext;
+		num_ext = counter->num_ext;
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		if (counter->num_signals < parent + 1)

I think it would be more conventional this way:

Quoted 1 line
	if (parent >= counter->num_signals)

Ack.

Quoted 11 lines

drivers/counter/counter-chrdev.c line 193

Quoted 9 lines
drivers/counter/counter-chrdev.c new file +9 -0
+			return -EINVAL;
+
+		comp_node.parent = counter->signals + parent;
+
+		ext = counter->signals[parent].ext;
+		num_ext = counter->signals[parent].num_ext;
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (counter->num_counts < parent + 1)

Same here.

Ack.

Quoted 32 lines
Quoted 32 lines
drivers/counter/counter-chrdev.c new file +32 -0
+			return -EINVAL;
+
+		comp_node.parent = counter->counts + parent;
+
+		ext = counter->counts[parent].ext;
+		num_ext = counter->counts[parent].num_ext;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Configure component info for comp node */
+	switch (watch.component.type) {
+	case COUNTER_COMPONENT_SIGNAL:
+		if (watch.component.scope != COUNTER_SCOPE_SIGNAL)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_SIGNAL_LEVEL;
+		comp_node.comp.signal_u8_read = counter->ops->signal_read;
+		break;
+	case COUNTER_COMPONENT_COUNT:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_U64;
+		comp_node.comp.count_u64_read = counter->ops->count_read;
+		break;
+	case COUNTER_COMPONENT_FUNCTION:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_FUNCTION;
Quoted 8 lines

drivers/counter/counter-chrdev.c line 231

Quoted 6 lines
drivers/counter/counter-chrdev.c new file +6 -0
+		comp_node.comp.count_u8_read = counter->ops->function_read;
+		break;
+	case COUNTER_COMPONENT_SYNAPSE_ACTION:
+		if (watch.component.scope != COUNTER_SCOPE_COUNT)
+			return -EINVAL;
+		if (counter->counts[parent].num_synapses < id + 1)
	if (id >= counter->counts[parent].num_synapses)

Ack.

Quoted 10 lines

drivers/counter/counter-chrdev.c line 239

Quoted 8 lines
drivers/counter/counter-chrdev.c new file +8 -0
+			return -EINVAL;
+
+		comp_node.comp.type = COUNTER_COMP_SYNAPSE_ACTION;
+		comp_node.comp.action_read = counter->ops->action_read;
+		comp_node.comp.priv = counter->counts[parent].synapses + id;
+		break;
+	case COUNTER_COMPONENT_EXTENSION:
+		if (num_ext < id + 1)
	if (id >= num_ext)

Ack.

Quoted 4 lines
Quoted 4 lines
drivers/counter/counter-chrdev.c new file +4 -0
+			return -EINVAL;
+
+		comp_node.comp = ext[id];
+		break;
Quoted 8 lines

drivers/counter/counter-chrdev.c line 247

Quoted 4 lines
drivers/counter/counter-chrdev.c new file +4 -0
+	default:
+		return -EINVAL;
+	}
+	if (!comp_node.comp.count_u8_read)

A comment reminding us that count_u8_read is part of a union would be helpful here since this is checking that any read function was set, not just count_u8_read.

Ack.

Quoted 3 lines

drivers/counter/counter-chrdev.c

Quoted 1 line
drivers/counter/counter-chrdev.c new file +1 -0
+		return -EFAULT;

Would EOPNOTSUPP be a better error choice?

You're right, that would be a more apt return.

Quoted 27 lines
Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+	comp_node.component = watch.component;
+
+	return counter_set_event_node(counter, &watch, &comp_node);
+}
+
+static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct counter_device *const counter = filp->private_data;
+	raw_spinlock_t *const events_lock = &counter->events_lock;

drivers/counter/counter-chrdev.c line 268

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+	unsigned long flags;
+	struct list_head *const events_list = &counter->events_list;
+	struct list_head *const next_events_list = &counter->next_events_list;
+
+	switch (cmd) {
+	case COUNTER_CLEAR_WATCHES_IOCTL:
+		raw_spin_lock_irqsave(events_lock, flags);
+		counter_events_list_free(events_list);
+		raw_spin_unlock_irqrestore(events_lock, flags);
+		counter_events_list_free(next_events_list);

I think this ioctl is doing too much. If we have to use it for both stopping events and clearing the list accumulated by COUNTER_SET_WATCH_IOCTL, then we have a race condition of no events after clearing watches during the time we are adding new ones and until we load the new ones.

Quoted 3 lines

It would probably make more sense to call this ioctl COUNTER_STOP_WATCHES_IOCTL and move counter_events_list_free( next_events_list) to the end of COUNTER_LOAD_WATCHES_IOCTL.

I don't think we will necessarily have a race condition here. COUNTER_CLEAR_WATCHES_IOCTL is intended to just clear the watches; e.g. bring us back to a clear state when some sort of job has completely finished and the user is no longer going to watch events for a while (maybe they're adjusting the conveyor for the next job or some similar operation).

I think the scenario you're concerned about is when you need to swap watches in the middle of a job without losing events. In this case, you wouldn't need to use COUNTER_CLEAR_WATCHES_IOCTL at all. Instead, you would just set up the watches via COUNTER_SET_WATCH_IOCTL, and then use COUNTER_LOAD_WATCHES_IOCTL to perform the swap; after COUNTER_LOAD_WATCHES_IOCTL completes, next_events_list is empty (thanks to list_replace_init()) and you're ready for the next set of watches.

Quoted 7 lines

drivers/counter/counter-chrdev.c line 271

Quoted 3 lines
drivers/counter/counter-chrdev.c new file +3 -0
+		break;
+	case COUNTER_SET_WATCH_IOCTL:
+		return counter_set_watch(counter, arg);

Would it make more sense to call this COUNTER_ADD_WATCH_IOCTL since it adds a watch to the list that will be loaded/enabled later?

That's a good point because "set" might easily be confused for a configuration operation. I'll rename this to be clearer.

Quoted 10 lines

drivers/counter/counter-chrdev.c line 276

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+	case COUNTER_LOAD_WATCHES_IOCTL:
+		raw_spin_lock_irqsave(events_lock, flags);
+		counter_events_list_free(events_list);
+		list_replace_init(next_events_list, events_list);
+		raw_spin_unlock_irqrestore(events_lock, flags);

For the eQEP driver, we will need a callback here to configure interrupts for the events. Likewise a callback would be needed here (for the case when we are replacing already loaded watches) and in COUNTER_STOP_WATCHES_IOCTL to unconfigure them.

Ack, I'll add in a callback for interrupt configuration where needed.

Quoted 21 lines
Quoted 21 lines
drivers/counter/counter-chrdev.c new file +21 -0
+		break;
+	default:
+		return -ENOIOCTLCMD;
+	}
+
+	return 0;
+}
+
+static int counter_chrdev_open(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = container_of(inode->i_cdev,
+							    typeof(*counter),
+							    chrdev);
+
+	get_device(&counter->dev);
+	filp->private_data = counter;
+
+	return nonseekable_open(inode, filp);
+}
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
Quoted 8 lines

drivers/counter/counter-chrdev.c line 302

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+{
+	struct counter_device *const counter = filp->private_data;
+	unsigned long flags;
+
+	put_device(&counter->dev);

put_device() should be at the end of the function in case it is the last reference.

put_device() shouldn't affect the counter_device events members, so I don't think there's a difference in this case if it's called at the beginning or end of the counter_chrdev_release function.

Quoted 53 lines
Quoted 53 lines
drivers/counter/counter-chrdev.c new file +52 -0
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+	counter_events_list_free(&counter->events_list);
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	counter_events_list_free(&counter->next_events_list);
+
+	return 0;
+}
+
 
+int counter_push_event(struct counter_device *const counter, const u8 event,
+		       const u8 channel)
+{
+	struct counter_event ev = {0};
+	unsigned int copied = 0;
+	unsigned long flags;
+	struct counter_event_node *event_node;
+	struct counter_comp_node *comp_node;
+	int err;
+
+	ev.timestamp = ktime_get_ns();
+	ev.watch.event = event;
+	ev.watch.channel = channel;
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->events_list, l)
+		if (event_node->event == event &&
+		    event_node->channel == channel)
+			break;
+
+	/* If event is not in the list */
+	if (&event_node->l == &counter->events_list)
+		goto exit_early;
+
+	/* Read and queue relevant comp for userspace */
+	list_for_each_entry(comp_node, &event_node->comp_list, l) {
+		err = counter_get_data(counter, comp_node, &ev.value_u8);
+		if (err)
+			goto err_counter_get_data;
+
+		ev.watch.component = comp_node->component;
+
+		copied += kfifo_put(&counter->events, ev);
+	}
+
+	if (copied)
+		wake_up_poll(&counter->events_wait, EPOLLIN);
+
+exit_early:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+
Quoted 8 lines

drivers/counter/counter-chrdev.c line 449

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+	return 0;
+
+err_counter_get_data:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	return err;

This seems slightly redundant. Could just initialize err to 0 and combine the two labels.

Ack.

Quoted 4 lines
Quoted 2 lines
drivers/counter/counter-chrdev.c new file +2 -0
+}
+EXPORT_SYMBOL_GPL(counter_push_event);
Quoted 8 lines

drivers/counter/counter-sysfs.c

Quoted 4 lines
drivers/counter/counter-sysfs.c +0 -0
index e66ed99dd5ea..cefef61f170d 100644

Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?

Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the extensionZ_name and extensionZ_width sysfs attributes.

In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the value_u8 or value_u64 union member in struct counter_event.

Quoted 11 lines
Quoted 11 lines
include/uapi/linux/counter.h new file +5 -0
new file mode 100644
index 000000000000..ad9a8686b2b0
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Userspace ABI for Counter character devices
+ * Copyright (C) 2020 William Breathitt Gray
+ */
Quoted 8 lines

include/uapi/linux/counter.h line 10

Quoted 5 lines
include/uapi/linux/counter.h new file +5 -0
+#ifndef _UAPI_COUNTER_H_
+#define _UAPI_COUNTER_H_
+
+#include <linux/ioctl.h>
+#include <linux/types.h>

It would be very helpful to have documentation comments for all of the defines in this file.

Ack.

Quoted 7 lines
Quoted 7 lines
include/uapi/linux/counter.h new file +7 -0
+
+#define COUNTER_SCOPE_DEVICE 0
+#define COUNTER_SCOPE_SIGNAL 1
+#define COUNTER_SCOPE_COUNT 2
+
+#define COUNTER_COMPONENT_SIGNAL 0
+#define COUNTER_COMPONENT_COUNT 1
Quoted 8 lines

include/uapi/linux/counter.h line 20

Quoted 3 lines
include/uapi/linux/counter.h new file +3 -0
+#define COUNTER_COMPONENT_FUNCTION 2
+#define COUNTER_COMPONENT_SYNAPSE_ACTION 3
+#define COUNTER_COMPONENT_EXTENSION 4

There are probably some cases where we will want to just have an event without reading a component value, so we could use something like COUNTER_SCOPE_NONE and COUNTER_COMPONENT_NONE here.

I'll come up with a solution for this use-case.

Quoted 13 lines
Quoted 13 lines
include/uapi/linux/counter.h new file +13 -0
+
+/**
+ * struct counter_component - Counter component identification
+ * @scope: component scope (Device, Count, or Signal)
+ * @parent: parent component identification number
+ * @type: component type (Count, extension, etc.)
+ * @id: component identification number
+ */
+struct counter_component {
+	__u8 scope;
+	__u64 parent;
+	__u8 type;
+	__u64 id;
Quoted 8 lines

include/uapi/linux/counter.h line 34

Quoted 1 line
include/uapi/linux/counter.h new file +1 -0
+};

This will have alignment issues, e.g. when 64-bit kernel is used with a 32-bit userspace. It looks like there is a __aligned_u64 type to help with this. But, unless we expect more that 4 billion counter devices, there doesn't seem to be a reason for parent and id to be 64-bit.

Fair point, I'll redefine these id as __u8 since it's unlikely we'll ever have a device that needs more than 255 components.

Quoted 8 lines

include/uapi/linux/counter.h line 38

Quoted 4 lines
include/uapi/linux/counter.h new file +4 -0
+
+#define COUNTER_EVENT_OVERFLOW 0
+#define COUNTER_EVENT_UNDERFLOW 1
+#define COUNTER_EVENT_OVERFLOW_UNDERFLOW 2

Would it make sense to just have individual overflow and underflow events? For example, the Carry-Borrow interrupt on 104-quad-8 could just call counter_push_event() twice.

I can see what you mean, but I think a dedicated OVERFLOW_UNDERFLOW event is useful because of the directional implications of an OVERFLOW or UNDERFLOW event by itself: if we see an OVERFLOW event we can be certain that the counter changed conceptually from a ceiling value to floor value (and vice-versa for an UNDERFLOW event). By providing the OVERFLOW_UNDERFLOW event we have an explicit way to indicate that the device is not concerned with the direction of the change that trigger the event. I think that's useful information to have for a user.

Quoted 10 lines

include/uapi/linux/counter.h line 45

Quoted 7 lines
include/uapi/linux/counter.h new file +7 -0
+#define COUNTER_EVENT_THRESHOLD 3
+#define COUNTER_EVENT_INDEX 4
+
+/**
+ * struct counter_watch - Counter component watch configuration
+ * @event: event that triggers
+ * @channel: event channel

How is channel intended to be used? (The device I am working with I assume would ignore the channel since it is just a single counter.)

Yes, this is for physical devices that have multiple counters ("channels") such as the 104-QUAD-8 which has 8 available on a single PC104 card. For the device you're working on, you would probably just set channel to 0 for all your uses since you only have one counter.

Quoted 9 lines

include/uapi/linux/counter.h line 52

Quoted 7 lines
include/uapi/linux/counter.h new file +7 -0
+ * @component: component to watch when event triggers
+ */
+struct counter_watch {
+	__u8 event;
+	__u8 channel;
+	struct counter_component component;
+};

To get the most compact layout, usually fields are ordered largest to smallest.

Ack.

Quoted 14 lines
Quoted 14 lines
include/uapi/linux/counter.h new file +14 -0
+
+#define COUNTER_CLEAR_WATCHES_IOCTL _IO(0x3E, 0x00)
+#define COUNTER_SET_WATCH_IOCTL _IOW(0x3E, 0x01, struct counter_watch)
+#define COUNTER_LOAD_WATCHES_IOCTL _IO(0x3E, 0x02)
+
+/**
+ * struct counter_event - Counter event data
+ * @timestamp: best estimate of time of event occurrence, in nanoseconds
+ * @watch: component watch configuration
+ * @value_u8: component value as __u8 data type
+ * @value_u64: component value as __u64 data type
+ */
+struct counter_event {
+	__u64 timestamp;
Quoted 8 lines

include/uapi/linux/counter.h line 72

Quoted 6 lines
include/uapi/linux/counter.h new file +6 -0
+	struct counter_watch watch;
+	union {
+		__u8 value_u8;
+		__u64 value_u64;
+	};
+};

Should probably use __aligned_u64 here too.

Ack.

Quoted 35 lines
Quoted 18 lines
include/uapi/linux/counter.h new file +17 -0
+
+#define COUNTER_COUNT_DIRECTION_FORWARD 0
+#define COUNTER_COUNT_DIRECTION_BACKWARD 1
+
+#define COUNTER_COUNT_MODE_NORMAL 0
+#define COUNTER_COUNT_MODE_RANGE_LIMIT 1
+#define COUNTER_COUNT_MODE_NON_RECYCLE 2
+#define COUNTER_COUNT_MODE_MODULO_N 3
+
+#define COUNTER_FUNCTION_INCREASE 0
+#define COUNTER_FUNCTION_DECREASE 1
+#define COUNTER_FUNCTION_PULSE_DIRECTION 2
+#define COUNTER_FUNCTION_QUADRATURE_X1_A 3
+#define COUNTER_FUNCTION_QUADRATURE_X1_B 4
+#define COUNTER_FUNCTION_QUADRATURE_X2_A 5
+#define COUNTER_FUNCTION_QUADRATURE_X2_B 6
+#define COUNTER_FUNCTION_QUADRATURE_X4 7

include/uapi/linux/counter.h line 99

Quoted 10 lines
include/uapi/linux/counter.h new file +10 -0
+
+#define COUNTER_SIGNAL_LEVEL_LOW 0
+#define COUNTER_SIGNAL_LEVEL_HIGH 1
+
+#define COUNTER_SYNAPSE_ACTION_NONE 0
+#define COUNTER_SYNAPSE_ACTION_RISING_EDGE 1
+#define COUNTER_SYNAPSE_ACTION_FALLING_EDGE 2
+#define COUNTER_SYNAPSE_ACTION_BOTH_EDGES 3
+
+#endif /* _UAPI_COUNTER_H_ */

Overall, I think we are getting close. I've started adding events to the TI eQEP driver. You can find the code at [1]. I still have a bit of work to do to add the timer and edge capture events.

Quoted 1 line

[1]: https://github.com/dlech/linux/tree/bone-counter

I just want to mention again that I appreciate all the reviews you send for these patchsets, it really helps to have someone to bounce ideas off of as well an extra set of eyes to catch all the silly bugs that I miss during the revisions. :-)

Thanks,

William Breathitt Gray

reviewer
#12 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 34 lines
Quoted 23 lines
Quoted 6 lines
drivers/counter/counter-chrdev.c new file +5 -0
+static long counter_chrdev_ioctl(struct file *filp, unsigned int cmd,
+				 unsigned long arg)
+{
+	struct counter_device *const counter = filp->private_data;
+	raw_spinlock_t *const events_lock = &counter->events_lock;

drivers/counter/counter-chrdev.c line 268

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+	unsigned long flags;
+	struct list_head *const events_list = &counter->events_list;
+	struct list_head *const next_events_list = &counter->next_events_list;
+
+	switch (cmd) {
+	case COUNTER_CLEAR_WATCHES_IOCTL:
+		raw_spin_lock_irqsave(events_lock, flags);
+		counter_events_list_free(events_list);
+		raw_spin_unlock_irqrestore(events_lock, flags);
+		counter_events_list_free(next_events_list);

I think this ioctl is doing too much. If we have to use it for both stopping events and clearing the list accumulated by COUNTER_SET_WATCH_IOCTL, then we have a race condition of no events after clearing watches during the time we are adding new ones and until we load the new ones.

Quoted 3 lines

It would probably make more sense to call this ioctl COUNTER_STOP_WATCHES_IOCTL and move counter_events_list_free( next_events_list) to the end of COUNTER_LOAD_WATCHES_IOCTL.

I don't think we will necessarily have a race condition here. COUNTER_CLEAR_WATCHES_IOCTL is intended to just clear the watches; e.g. bring us back to a clear state when some sort of job has completely finished and the user is no longer going to watch events for a while (maybe they're adjusting the conveyor for the next job or some similar operation).

Quoted 8 lines

I think the scenario you're concerned about is when you need to swap watches in the middle of a job without losing events. In this case, you wouldn't need to use COUNTER_CLEAR_WATCHES_IOCTL at all. Instead, you would just set up the watches via COUNTER_SET_WATCH_IOCTL, and then use COUNTER_LOAD_WATCHES_IOCTL to perform the swap; after COUNTER_LOAD_WATCHES_IOCTL completes, next_events_list is empty (thanks to list_replace_init()) and you're ready for the next set of watches.

Got it. I think I missed the fact that list_replace_init() clears next_events_list.

Quoted 11 lines
Quoted 10 lines

drivers/counter/counter-chrdev.c line 302

Quoted 7 lines
drivers/counter/counter-chrdev.c new file +7 -0
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = filp->private_data;
+	unsigned long flags;
+
+	put_device(&counter->dev);

put_device() should be at the end of the function in case it is the last reference.

Quoted 4 lines

put_device() shouldn't affect the counter_device events members, so I don't think there's a difference in this case if it's called at the beginning or end of the counter_chrdev_release function.

It isn't possible the some memory allocated with devm_kalloc() could be be referenced after calling put_device() now or in the future?

Quoted 17 lines
Quoted 4 lines
Quoted 2 lines
(quoted diff) +2 -0
+}
+EXPORT_SYMBOL_GPL(counter_push_event);
Quoted 8 lines

drivers/counter/counter-sysfs.c

Quoted 4 lines
drivers/counter/counter-sysfs.c +0 -0
index e66ed99dd5ea..cefef61f170d 100644

Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?

Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the extensionZ_name and extensionZ_width sysfs attributes.

Quoted 7 lines

In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the value_u8 or value_u64 union member in struct counter_event.

Are we sure that all value types will always be CPU-endian unsigned integers? Or should we make an enum to describe the data type instead of just the width?

#13 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 11 lines · David Lechner
Quoted 11 lines
Quoted 10 lines

drivers/counter/counter-chrdev.c line 302

Quoted 7 lines
drivers/counter/counter-chrdev.c new file +7 -0
+
+static int counter_chrdev_release(struct inode *inode, struct file *filp)
+{
+	struct counter_device *const counter = filp->private_data;
+	unsigned long flags;
+
+	put_device(&counter->dev);

put_device() should be at the end of the function in case it is the last reference.

Quoted 7 lines
Quoted 4 lines

put_device() shouldn't affect the counter_device events members, so I don't think there's a difference in this case if it's called at the beginning or end of the counter_chrdev_release function.

It isn't possible the some memory allocated with devm_kalloc() could be be referenced after calling put_device() now or in the future?

You're right, if put_device() is called before then we could end up in a garbage state where the device memory is released but events list has not yet been freed. I'll move put_device() to after the events list is freed then.

Quoted 21 lines
Quoted 13 lines
Quoted 8 lines

drivers/counter/counter-sysfs.c

Quoted 4 lines
drivers/counter/counter-sysfs.c +0 -0
index e66ed99dd5ea..cefef61f170d 100644

Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?

Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the extensionZ_name and extensionZ_width sysfs attributes.

Quoted 7 lines

In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the value_u8 or value_u64 union member in struct counter_event.

Quoted 3 lines

Are we sure that all value types will always be CPU-endian unsigned integers? Or should we make an enum to describe the data type instead of just the width?

It should be safe to assume that the character device interface will only ever return CPU-endian unsigned integers. The device driver should handle the conversion of any strange endianness from the device before the character device interface, while userspace is the one responsible for interpreting the meaning of count in the context of the application.

Let's create a scenario for the sake of example. Suppose we want to use a counter device to track the vertical position of a component moved by a linear actuator. The operator considers some vertical position as the horizon, where anything above would be a positive position and anything below a negative position. The counter device stores its count in big-endian format; but the system CPU expects little-endian.

The flow of data for this scenario would look like the following (where BE = big-endian, LE = little-endian):

+----------+         +---------------+          +--------+
| Raw data | - BE -> | Device driver | -> LE -> | chrdev | - u64 ->
+----------+         +---------------+          +--------+

At this point, the userspace application would read the unsigned integer from the character device and determine how to interpret the position -- whether the count be converted to a signed value to represent a negative physical position.

Whether or not a position should be considered negative is dependent on the user application and context. Because the character device does not know the context of the user application, it should only provide unsigned integers in order to ensure a standard interface for counter devices; userspace will be responsible for interpreting those counts to something meaningful for the context of their applications.

William Breathitt Gray

reviewer
#14 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 51 lines
Quoted 22 lines
Quoted 14 lines
Quoted 9 lines

drivers/counter/counter-sysfs.c

Quoted 5 lines
drivers/counter/counter-sysfs.c +0 -0
index e66ed99dd5ea..cefef61f170d 100644

Not sure why sysfs changes are in the chrdev patch. Are these changes related somehow?

Sorry, I forgot to explain this in the cover letter. The changes here are only useful for the character device interface. These changes introduce the extensionZ_name and extensionZ_width sysfs attributes.

Quoted 7 lines

In the character device interface, extensions are selected by their id number, and the value returned depends on the type of data. The new sysfs attributes introduced here allow users to match the id of an extension with its name, as well as the bit width of the value returned so that the user knows whether to use the value_u8 or value_u64 union member in struct counter_event.

Quoted 3 lines

Are we sure that all value types will always be CPU-endian unsigned integers? Or should we make an enum to describe the data type instead of just the width?

It should be safe to assume that the character device interface will only ever return CPU-endian unsigned integers. The device driver should handle the conversion of any strange endianness from the device before the character device interface, while userspace is the one responsible for interpreting the meaning of count in the context of the application.

Let's create a scenario for the sake of example. Suppose we want to use a counter device to track the vertical position of a component moved by a linear actuator. The operator considers some vertical position as the horizon, where anything above would be a positive position and anything below a negative position. The counter device stores its count in big-endian format; but the system CPU expects little-endian.

The flow of data for this scenario would look like the following (where BE = big-endian, LE = little-endian):

+----------+         +---------------+          +--------+
| Raw data | - BE -> | Device driver | -> LE -> | chrdev | - u64 ->
+----------+         +---------------+          +--------+

At this point, the userspace application would read the unsigned integer from the character device and determine how to interpret the position -- whether the count be converted to a signed value to represent a negative physical position.

Quoted 9 lines

Whether or not a position should be considered negative is dependent on the user application and context. Because the character device does not know the context of the user application, it should only provide unsigned integers in order to ensure a standard interface for counter devices; userspace will be responsible for interpreting those counts to something meaningful for the context of their applications.

William Breathitt Gray

Sounds reasonable to me.

reviewer
#15 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

In reply to William Breathitt Gray
Quoted 38 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +31 -0
new file mode 100644
index 000000000000..2be3846e4105
 
 
+/**
+ * counter_push_event - queue event for userspace reading
+ * @counter:	pointer to Counter structure
+ * @event:	triggered event
+ * @channel:	event channel
+ *
+ * Note: If no one is watching for the respective event, it is silently
+ * discarded.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int counter_push_event(struct counter_device *const counter, const u8 event,
+		       const u8 channel)
+{
+	struct counter_event ev = {0};
+	unsigned int copied = 0;
+	unsigned long flags;
+	struct counter_event_node *event_node;
+	struct counter_comp_node *comp_node;
+	int err;
+
+	ev.timestamp = ktime_get_ns();
+	ev.watch.event = event;
+	ev.watch.channel = channel;
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->events_list, l)
+		if (event_node->event == event &&

drivers/counter/counter-chrdev.c line 430

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+		    event_node->channel == channel)
+			break;
+
+	/* If event is not in the list */
+	if (&event_node->l == &counter->events_list)
+		goto exit_early;
+
+	/* Read and queue relevant comp for userspace */
+	list_for_each_entry(comp_node, &event_node->comp_list, l) {
+		err = counter_get_data(counter, comp_node, &ev.value_u8);

Currently all counter devices are memory mapped devices so calling counter_get_data() here with interrupts disabled is probably OK, but if any counter drivers are added that use I2C/SPI/etc. that will take a long time to read, it would cause problems leaving interrupts disabled here.

Brainstorming: Would it make sense to separate the event from the component value being read? As I mentioned in one of my previous reviews, I think there are some cases where we would just want to know when an event happened and not read any additional data anyway. In the case of a slow communication bus, this would also let us queue the event in the kfifo and notify poll right away and then defer the reads in a workqueue for later.

Quoted 20 lines
drivers/counter/counter-chrdev.c new file +20 -0
+		if (err)
+			goto err_counter_get_data;
+
+		ev.watch.component = comp_node->component;
+
+		copied += kfifo_put(&counter->events, ev);
+	}
+
+	if (copied)
+		wake_up_poll(&counter->events_wait, EPOLLIN);
+
+exit_early:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+
+	return 0;
+
+err_counter_get_data:
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	return err;
+}
#16 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 56 lines · David Lechner
Quoted 38 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +31 -0
new file mode 100644
index 000000000000..2be3846e4105
 
 
+/**
+ * counter_push_event - queue event for userspace reading
+ * @counter:	pointer to Counter structure
+ * @event:	triggered event
+ * @channel:	event channel
+ *
+ * Note: If no one is watching for the respective event, it is silently
+ * discarded.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int counter_push_event(struct counter_device *const counter, const u8 event,
+		       const u8 channel)
+{
+	struct counter_event ev = {0};
+	unsigned int copied = 0;
+	unsigned long flags;
+	struct counter_event_node *event_node;
+	struct counter_comp_node *comp_node;
+	int err;
+
+	ev.timestamp = ktime_get_ns();
+	ev.watch.event = event;
+	ev.watch.channel = channel;
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->events_list, l)
+		if (event_node->event == event &&

drivers/counter/counter-chrdev.c line 430

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+		    event_node->channel == channel)
+			break;
+
+	/* If event is not in the list */
+	if (&event_node->l == &counter->events_list)
+		goto exit_early;
+
+	/* Read and queue relevant comp for userspace */
+	list_for_each_entry(comp_node, &event_node->comp_list, l) {
+		err = counter_get_data(counter, comp_node, &ev.value_u8);

Currently all counter devices are memory mapped devices so calling counter_get_data() here with interrupts disabled is probably OK, but if any counter drivers are added that use I2C/SPI/etc. that will take a long time to read, it would cause problems leaving interrupts disabled here.

Quoted 7 lines

Brainstorming: Would it make sense to separate the event from the component value being read? As I mentioned in one of my previous reviews, I think there are some cases where we would just want to know when an event happened and not read any additional data anyway. In the case of a slow communication bus, this would also let us queue the event in the kfifo and notify poll right away and then defer the reads in a workqueue for later.

I don't see any problems with reporting just an event without any component value attached (e.g. userspace could handle the component reads via sysfs at a later point). We would just need a way to inform userspace that the struct counter_component in the struct counter_watch returned should be ignored.

Perhaps we can add an additional member to struct counter_watch indicating whether it's an empty watch; or alternatively, add a new component scope define to differentiate between an actual component and an empty one (e.g. COUNTER_SCOPE_EVENT). What do you think?

William Breathitt Gray

reviewer
#17 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 71 lines · William Breathitt Gray
Quoted 56 lines · David Lechner
Quoted 38 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +31 -0
new file mode 100644
index 000000000000..2be3846e4105
 
 
+/**
+ * counter_push_event - queue event for userspace reading
+ * @counter:	pointer to Counter structure
+ * @event:	triggered event
+ * @channel:	event channel
+ *
+ * Note: If no one is watching for the respective event, it is silently
+ * discarded.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int counter_push_event(struct counter_device *const counter, const u8 event,
+		       const u8 channel)
+{
+	struct counter_event ev = {0};
+	unsigned int copied = 0;
+	unsigned long flags;
+	struct counter_event_node *event_node;
+	struct counter_comp_node *comp_node;
+	int err;
+
+	ev.timestamp = ktime_get_ns();
+	ev.watch.event = event;
+	ev.watch.channel = channel;
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+
+	/* Search for event in the list */
+	list_for_each_entry(event_node, &counter->events_list, l)
+		if (event_node->event == event &&

drivers/counter/counter-chrdev.c line 430

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+		    event_node->channel == channel)
+			break;
+
+	/* If event is not in the list */
+	if (&event_node->l == &counter->events_list)
+		goto exit_early;
+
+	/* Read and queue relevant comp for userspace */
+	list_for_each_entry(comp_node, &event_node->comp_list, l) {
+		err = counter_get_data(counter, comp_node, &ev.value_u8);

Currently all counter devices are memory mapped devices so calling counter_get_data() here with interrupts disabled is probably OK, but if any counter drivers are added that use I2C/SPI/etc. that will take a long time to read, it would cause problems leaving interrupts disabled here.

Quoted 7 lines

Brainstorming: Would it make sense to separate the event from the component value being read? As I mentioned in one of my previous reviews, I think there are some cases where we would just want to know when an event happened and not read any additional data anyway. In the case of a slow communication bus, this would also let us queue the event in the kfifo and notify poll right away and then defer the reads in a workqueue for later.

I don't see any problems with reporting just an event without any component value attached (e.g. userspace could handle the component reads via sysfs at a later point). We would just need a way to inform userspace that the struct counter_component in the struct counter_watch returned should be ignored.

Quoted 7 lines

Perhaps we can add an additional member to struct counter_watch indicating whether it's an empty watch; or alternatively, add a new component scope define to differentiate between an actual component and an empty one (e.g. COUNTER_SCOPE_EVENT). What do you think?

William Breathitt Gray

I made the same suggestion in one of my other replies - except I called it COUNTER_SCOPE_NONE.

reviewer
#18 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

In reply to William Breathitt Gray
Quoted 21 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +21 -0
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned long flags;
+	unsigned int copied;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events));
+			if (err)
+				return err;
+		}

drivers/counter/counter-chrdev.c line 71

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+
+		raw_spin_lock_irqsave(&counter->events_lock, flags);
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+		if (err)
+			return err;
+	} while (!copied);
+
+	return copied;
+}

All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.

Example:

static ssize_t iio_event_chrdev_read(struct file *filep,

				     char __user *buf,
				     size_t count,
				     loff_t *f_ps)
{
	struct iio_dev *indio_dev = filep->private_data;
	struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
	struct iio_event_interface *ev_int = iio_dev_opaque->event_interface;
	unsigned int copied;
	int ret;

	if (!indio_dev->info)
		return -ENODEV;

	if (count < sizeof(struct iio_event_data))
		return -EINVAL;

	do {
		if (kfifo_is_empty(&ev_int->det_events)) {
			if (filep->f_flags & O_NONBLOCK)
				return -EAGAIN;

			ret = wait_event_interruptible(ev_int->wait,
					!kfifo_is_empty(&ev_int->det_events) ||
					indio_dev->info == NULL);
			if (ret)
				return ret;
			if (indio_dev->info == NULL)
				return -ENODEV;
		}

		if (mutex_lock_interruptible(&ev_int->read_lock))
			return -ERESTARTSYS;
		ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied);
		mutex_unlock(&ev_int->read_lock);

		if (ret)
			return ret;

		/*
		 * If we couldn't read anything from the fifo (a different
		 * thread might have been faster) we either return -EAGAIN if
		 * the file descriptor is non-blocking, otherwise we go back to
		 * sleep and wait for more data to arrive.
		 */
		if (copied == 0 && (filep->f_flags & O_NONBLOCK))
			return -EAGAIN;

	} while (copied == 0);

	return copied;
}
#19 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26 -0
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned long flags;
+	unsigned int copied;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events));
+			if (err)
+				return err;
+		}
+
+		raw_spin_lock_irqsave(&counter->events_lock, flags);
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+		if (err)
Quoted 8 lines

drivers/counter/counter-chrdev.c line 71

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+			return err;
+	} while (!copied);
+
+	return copied;
+}

All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.

The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.

William Breathitt Gray

reviewer
#20 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 37 lines · William Breathitt Gray
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26 -0
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned long flags;
+	unsigned int copied;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events));
+			if (err)
+				return err;
+		}
+
+		raw_spin_lock_irqsave(&counter->events_lock, flags);
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+		if (err)
Quoted 8 lines

drivers/counter/counter-chrdev.c line 71

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+			return err;
+	} while (!copied);
+
+	return copied;
+}

All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.

Quoted 7 lines

The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.

The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.

#21 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 47 lines · David Lechner
Quoted 37 lines · William Breathitt Gray
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26 -0
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned long flags;
+	unsigned int copied;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events));
+			if (err)
+				return err;
+		}
+
+		raw_spin_lock_irqsave(&counter->events_lock, flags);
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+		if (err)
Quoted 8 lines

drivers/counter/counter-chrdev.c line 71

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+			return err;
+	} while (!copied);
+
+	return copied;
+}

All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.

Quoted 7 lines

The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.

Quoted 2 lines

The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.

You're right, it does say in the kfifo.h comments that with only one concurrent reader and one current write, we don't need extra locking to use these macros. Because we only have one kfifo_to_user() operating on counter->events, does that mean we don't need locking at all here for the counter_chrdev_read() function?

William Breathitt Gray

reviewer
#22 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 51 lines · William Breathitt Gray
Quoted 47 lines · David Lechner
Quoted 37 lines · William Breathitt Gray
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26 -0
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned long flags;
+	unsigned int copied;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events));
+			if (err)
+				return err;
+		}
+
+		raw_spin_lock_irqsave(&counter->events_lock, flags);
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+		if (err)
Quoted 8 lines

drivers/counter/counter-chrdev.c line 71

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+			return err;
+	} while (!copied);
+
+	return copied;
+}

All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.

Quoted 7 lines

The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.

Quoted 2 lines

The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.

Quoted 8 lines

You're right, it does say in the kfifo.h comments that with only one concurrent reader and one current write, we don't need extra locking to use these macros. Because we only have one kfifo_to_user() operating on counter->events, does that mean we don't need locking at all here for the counter_chrdev_read() function?

William Breathitt Gray

Even if we have the policy that only one file handle to the chrdev can be open at a time, it is still possible that the it could be read from multiple threads. So it I think it makes sense to keep it just to be safe.

#23 lore raw

Re: [PATCH v5 3/5] counter: Add character device interface

Quoted 58 lines · David Lechner
Quoted 57 lines · William Breathitt Gray
Quoted 47 lines · David Lechner
Quoted 37 lines · William Breathitt Gray
Quoted 27 lines · David Lechner
Quoted 26 lines · William Breathitt Gray
drivers/counter/counter-chrdev.c new file +26 -0
+static ssize_t counter_chrdev_read(struct file *filp, char __user *buf,
+				   size_t len, loff_t *f_ps)
+{
+	struct counter_device *const counter = filp->private_data;
+	int err;
+	unsigned long flags;
+	unsigned int copied;
+
+	if (len < sizeof(struct counter_event))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&counter->events)) {
+			if (filp->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			err = wait_event_interruptible(counter->events_wait,
+					!kfifo_is_empty(&counter->events));
+			if (err)
+				return err;
+		}
+
+		raw_spin_lock_irqsave(&counter->events_lock, flags);
+		err = kfifo_to_user(&counter->events, buf, len, &copied);
+		raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+		if (err)
Quoted 8 lines

drivers/counter/counter-chrdev.c line 71

Quoted 5 lines
drivers/counter/counter-chrdev.c new file +5 -0
+			return err;
+	} while (!copied);
+
+	return copied;
+}

All other uses of kfifo_to_user() I saw use a mutex instead of spin lock. I don't see a reason for disabling interrupts here.

Quoted 7 lines

The Counter character device interface is special in this case because counter->events could be accessed from an interrupt context. This is possible if counter_push_event() is called for an interrupt (as is the case for the 104_quad_8 driver). In this case, we can't use mutex because we can't sleep in an interrupt context, so our only option is to use spin lock.

Quoted 2 lines

The way I understand it, locking is only needed for concurrent readers and locking between reader and writer is not needed.

You're right, it does say in the kfifo.h comments that with only one concurrent reader and one current write, we don't need extra locking to use these macros. Because we only have one kfifo_to_user() operating on counter->events, does that mean we don't need locking at all here for the counter_chrdev_read() function?

Quoted 7 lines
Quoted 2 lines

William Breathitt Gray

Even if we have the policy that only one file handle to the chrdev can be open at a time, it is still possible that the it could be read from multiple threads. So it I think it makes sense to keep it just to be safe.

All right, I'll keep the locks in the code for now to keep it safe in case we have multiple threads reading.

William Breathitt Gray

#24 lore raw

PATCH v5 4/5 docs: counter: Document character device interface

In reply to William Breathitt Gray

This patch adds high-level documentation about the Counter subsystem character device interface.

---
 Documentation/ABI/testing/sysfs-bus-counter   |  18 ++
 Documentation/driver-api/generic-counter.rst  | 228 ++++++++++++++----
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 3 files changed, 206 insertions(+), 41 deletions(-)
Documentation/ABI/testing/sysfs-bus-counter +18 -0
index 566bd99fe0a5..b7fdb14ae891 100644
@@ -99,6 +99,24 @@ Description:
 		Read-only attribute that indicates whether excessive noise is
 		present at the channel Y counter inputs.
 
+What:		/sys/bus/counter/devices/counterX/countY/extensionZ_name
+What:		/sys/bus/counter/devices/counterX/extensionZ_name
+What:		/sys/bus/counter/devices/counterX/signalY/extensionZ_name
+KernelVersion:	5.11
+Contact:	[email protected]
+Description:
+		Read-only attribute that indicates the component name of
+		Extension Z.
+
+What:		/sys/bus/counter/devices/counterX/countY/extensionZ_width
+What:		/sys/bus/counter/devices/counterX/extensionZ_width
+What:		/sys/bus/counter/devices/counterX/signalY/extensionZ_width
+KernelVersion:	5.11
+Contact:	[email protected]
+Description:
+		Read-only attribute that indicates the data width of value of
+		Extension Z.
+
 What:		/sys/bus/counter/devices/counterX/countY/function
 KernelVersion:	5.2
 Contact:	[email protected]
Documentation/driver-api/generic-counter.rst +187 -41

255 lines not on this page · raw message

Documentation/userspace-api/ioctl/ioctl-number.rst +1 -0
index 2a198838fca9..f6e96bb780cd 100644
@@ -88,6 +88,7 @@ Code  Seq#    Include File                                           Comments
                                                                      <http://infiniband.sourceforge.net/>
 0x20  all    drivers/cdrom/cm206.h
 0x22  all    scsi/sg.h
+0x3E  00-0F  linux/counter.h                                         <mailto:[email protected]>
 '!'   00-1F  uapi/linux/seccomp.h
 '#'   00-3F                                                          IEEE 1394 Subsystem
                                                                      Block for the entire subsystem
Signature · 1 line
2.28.0
#25 lore raw

Re: [PATCH v5 4/5] docs: counter: Document character device interface

Hi!

Documentation/driver-api/generic-counter.rst and vice versa. · line 607

Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13 -0
+        int main(void)
+        {
+                struct pollfd pfd = { .events = POLLIN };
+                struct counter_event event_data[2];
+
+                pfd.fd = open("/dev/counter0", O_RDWR);
+
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
+                ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL);
+
+                for (;;) {
+                        poll(&pfd, 1, -1);

Why do poll, when you are doing blocking read?

Quoted 1 line

+ read(pfd.fd, event_data, sizeof(event_data));

Does your new chrdev always guarantee returning complete buffer?

If so, should it behave like that?

Best regards,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
#26 lore raw

Re: [PATCH v5 4/5] docs: counter: Document character device interface

Quoted 18 lines · Pavel Machek

Hi!

Documentation/driver-api/generic-counter.rst and vice versa. · line 607

Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13 -0
+        int main(void)
+        {
+                struct pollfd pfd = { .events = POLLIN };
+                struct counter_event event_data[2];
+
+                pfd.fd = open("/dev/counter0", O_RDWR);
+
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
+                ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL);
+
+                for (;;) {
+                        poll(&pfd, 1, -1);

Why do poll, when you are doing blocking read?

Quoted 11 lines
Quoted 1 line

+ read(pfd.fd, event_data, sizeof(event_data));

Does your new chrdev always guarantee returning complete buffer?

If so, should it behave like that?

Best regards,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

I suppose you're right: a poll() should be redundant now with this version of the character device implementation because buffers will always return complete; so a blocking read() should achieve the same behavior that a poll() with read() would.

I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for poll() in the v6 submission.

William Breathitt Gray

reviewer
#27 lore raw

Re: [PATCH v5 4/5] docs: counter: Document character device interface

Quoted 36 lines · William Breathitt Gray
Quoted 18 lines · Pavel Machek

Hi!

Documentation/driver-api/generic-counter.rst and vice versa. · line 607

Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13 -0
+        int main(void)
+        {
+                struct pollfd pfd = { .events = POLLIN };
+                struct counter_event event_data[2];
+
+                pfd.fd = open("/dev/counter0", O_RDWR);
+
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
+                ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL);
+
+                for (;;) {
+                        poll(&pfd, 1, -1);

Why do poll, when you are doing blocking read?

Quoted 11 lines
Quoted 1 line

+ read(pfd.fd, event_data, sizeof(event_data));

Does your new chrdev always guarantee returning complete buffer?

If so, should it behave like that?

Best regards,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

I suppose you're right: a poll() should be redundant now with this version of the character device implementation because buffers will always return complete; so a blocking read() should achieve the same behavior that a poll() with read() would.

Quoted 6 lines

I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for poll() in the v6 submission.

William Breathitt Gray

I hope that you mean that you will just remove it from the example and not from the chardev. Otherwise it won't be possible to integrate this with an event loop.

#28 lore raw

Re: [PATCH v5 4/5] docs: counter: Document character device interface

Quoted 41 lines · David Lechner
Quoted 40 lines · William Breathitt Gray
Quoted 18 lines · Pavel Machek

Hi!

Documentation/driver-api/generic-counter.rst and vice versa. · line 607

Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13 -0
+        int main(void)
+        {
+                struct pollfd pfd = { .events = POLLIN };
+                struct counter_event event_data[2];
+
+                pfd.fd = open("/dev/counter0", O_RDWR);
+
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
+                ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL);
+
+                for (;;) {
+                        poll(&pfd, 1, -1);

Why do poll, when you are doing blocking read?

Quoted 11 lines
Quoted 1 line

+ read(pfd.fd, event_data, sizeof(event_data));

Does your new chrdev always guarantee returning complete buffer?

If so, should it behave like that?

Best regards,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

I suppose you're right: a poll() should be redundant now with this version of the character device implementation because buffers will always return complete; so a blocking read() should achieve the same behavior that a poll() with read() would.

I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for poll() in the v6 submission.

Quoted 6 lines
Quoted 2 lines

William Breathitt Gray

I hope that you mean that you will just remove it from the example and not from the chardev. Otherwise it won't be possible to integrate this with an event loop.

Would you elaborate a bit further on this? My thought process is that because users must set the Counter Events they want to watch, and only those Counter Events show up in the character device node, a blocking read() would effectively behave the same as poll() with read(); if none of the Counter Events occur, the read() just blocks until one does, thus making the use of a poll() call redundant.

William Breathitt Gray

reviewer
#29 lore raw

Re: [PATCH v5 4/5] docs: counter: Document character device interface

Quoted 49 lines · William Breathitt Gray
Quoted 41 lines · David Lechner
Quoted 40 lines · William Breathitt Gray
Quoted 18 lines · Pavel Machek

Hi!

Documentation/driver-api/generic-counter.rst and vice versa. · line 607

Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13 -0
+        int main(void)
+        {
+                struct pollfd pfd = { .events = POLLIN };
+                struct counter_event event_data[2];
+
+                pfd.fd = open("/dev/counter0", O_RDWR);
+
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
+                ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL);
+
+                for (;;) {
+                        poll(&pfd, 1, -1);

Why do poll, when you are doing blocking read?

Quoted 11 lines
Quoted 1 line

+ read(pfd.fd, event_data, sizeof(event_data));

Does your new chrdev always guarantee returning complete buffer?

If so, should it behave like that?

Best regards,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

I suppose you're right: a poll() should be redundant now with this version of the character device implementation because buffers will always return complete; so a blocking read() should achieve the same behavior that a poll() with read() would.

I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for poll() in the v6 submission.

Quoted 6 lines
Quoted 2 lines

William Breathitt Gray

I hope that you mean that you will just remove it from the example and not from the chardev. Otherwise it won't be possible to integrate this with an event loop.

Quoted 9 lines

Would you elaborate a bit further on this? My thought process is that because users must set the Counter Events they want to watch, and only those Counter Events show up in the character device node, a blocking read() would effectively behave the same as poll() with read(); if none of the Counter Events occur, the read() just blocks until one does, thus making the use of a poll() call redundant.

William Breathitt Gray

If the counter device was the only file descriptor being read, then yes it wouldn't matter. But if we are using this in combination with other file descriptors, then it is common to poll all of the file descriptors using a single syscall to see which one is ready to read rather than doing a non-blocking read on all of the file descriptors, which would result in many unnecessary syscalls.

#30 lore raw

Re: [PATCH v5 4/5] docs: counter: Document character device interface

Quoted 60 lines · David Lechner
Quoted 49 lines · William Breathitt Gray
Quoted 41 lines · David Lechner
Quoted 40 lines · William Breathitt Gray
Quoted 18 lines · Pavel Machek

Hi!

Documentation/driver-api/generic-counter.rst and vice versa. · line 607

Quoted 13 lines
Documentation/driver-api/generic-counter.rst +13 -0
+        int main(void)
+        {
+                struct pollfd pfd = { .events = POLLIN };
+                struct counter_event event_data[2];
+
+                pfd.fd = open("/dev/counter0", O_RDWR);
+
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches);
+                ioctl(pfd.fd, COUNTER_SET_WATCH_IOCTL, watches + 1);
+                ioctl(pfd.fd, COUNTER_LOAD_WATCHES_IOCTL);
+
+                for (;;) {
+                        poll(&pfd, 1, -1);

Why do poll, when you are doing blocking read?

Quoted 11 lines
Quoted 1 line

+ read(pfd.fd, event_data, sizeof(event_data));

Does your new chrdev always guarantee returning complete buffer?

If so, should it behave like that?

Best regards,
Pavel

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

I suppose you're right: a poll() should be redundant now with this version of the character device implementation because buffers will always return complete; so a blocking read() should achieve the same behavior that a poll() with read() would.

I'll give some more time for additional feedback to come in for this version of the patchset, and then likely remove support for poll() in the v6 submission.

Quoted 6 lines
Quoted 2 lines

William Breathitt Gray

I hope that you mean that you will just remove it from the example and not from the chardev. Otherwise it won't be possible to integrate this with an event loop.

Quoted 9 lines

Would you elaborate a bit further on this? My thought process is that because users must set the Counter Events they want to watch, and only those Counter Events show up in the character device node, a blocking read() would effectively behave the same as poll() with read(); if none of the Counter Events occur, the read() just blocks until one does, thus making the use of a poll() call redundant.

William Breathitt Gray

Quoted 6 lines

If the counter device was the only file descriptor being read, then yes it wouldn't matter. But if we are using this in combination with other file descriptors, then it is common to poll all of the file descriptors using a single syscall to see which one is ready to read rather than doing a non-blocking read on all of the file descriptors, which would result in many unnecessary syscalls.

Ah, that's a fair point, my original view was somewhat myopic there. I'll leave poll support in the Counter chrdev then and just simplify the documentation example code to not use it.

William Breathitt Gray

#31 lore raw

PATCH v5 5/5 counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

In reply to William Breathitt Gray

The LSI/CSI LS7266R1 chip provides programmable output via the FLG pins. When interrupts are enabled on the ACCES 104-QUAD-8, they occur whenever FLG1 is active. Four functions are available for the FLG1 signal: Carry, Compare, Carry-Borrow, and Index.

Carry:
Interrupt generated on active low Carry signal. Carry signal toggles every time the respective channel's counter overflows.

Compare:
Interrupt generated on active low Compare signal. Compare signal toggles every time respective channel's preset register is equal to the respective channel's counter.

Carry-Borrow:
Interrupt generated on active low Carry signal and active low Borrow signal. Carry signal toggles every time the respective channel's counter overflows. Borrow signal toggles every time the respective channel's counter underflows.

Index:
Interrupt generated on active high Index signal.

The irq_trigger Count extension is introduced to allow the selection of the desired IRQ trigger function per channel. The irq_trigger_enable Count extension is introduced to allow the enablement of interrupts for a respective channel. Interrupts push Counter events to event channel X, where 'X' is the respective channel whose FLG1 activated.

This patch adds IRQ support for the ACCES 104-QUAD-8. The interrupt line numbers for the devices may be configured via the irq array module parameter.

---
 .../ABI/testing/sysfs-bus-counter-104-quad-8  |  32 ++
 drivers/counter/104-quad-8.c                  | 303 ++++++++++++++----
 drivers/counter/Kconfig                       |   6 +-
 3 files changed, 270 insertions(+), 71 deletions(-)
Documentation/ABI/testing/sysfs-bus-counter-104-quad-8 +32 -0
index eac32180c40d..995bf08d365d 100644
@@ -1,3 +1,35 @@
+What:		/sys/bus/counter/devices/counterX/countY/irq_trigger
+KernelVersion:	5.11
+Contact:	[email protected]
+Description:
+		IRQ trigger function for channel Y. Four trigger functions are
+		available: carry, compare, carry-borrow, and index.
+
+		carry:
+			Interrupt generated on active low Carry signal. Carry
+			signal toggles every time channel Y counter overflows.
+
+		compare:
+			Interrupt generated on active low Compare signal.
+			Compare signal toggles every time channel Y preset
+			register is equal to channel Y counter.
+
+		carry-borrow:
+			Interrupt generated on active low Carry signal and
+			active low Borrow signal. Carry signal toggles every
+			time channel Y counter overflows. Borrow signal toggles
+			every time channel Y counter underflows.
+
+		index:
+			Interrupt generated on active high Index signal.
+
+What:		/sys/bus/counter/devices/counterX/countY/irq_trigger_enable
+KernelVersion:	5.11
+Contact:	[email protected]
+Description:
+		Whether generation of interrupts is enabled for channel Y. Valid
+		attribute values are boolean.
+
 What:		/sys/bus/counter/devices/counterX/signalY/cable_fault
 KernelVersion:	5.7
 Contact:	[email protected]
drivers/counter/104-quad-8.c +235 -68

797 lines not on this page · raw message

drivers/counter/Kconfig +3 -3
index 2de53ab0dd25..bd42df98f522 100644
@@ -23,11 +23,11 @@ config 104_QUAD_8
 	  A counter's respective error flag may be cleared by performing a write
 	  operation on the respective count value attribute. Although the
 	  104-QUAD-8 counters have a 25-bit range, only the lower 24 bits may be
-	  set, either directly or via the counter's preset attribute. Interrupts
-	  are not supported by this driver.
+	  set, either directly or via the counter's preset attribute.
 
 	  The base port addresses for the devices may be configured via the base
-	  array module parameter.
+	  array module parameter. The interrupt line numbers for the devices may
+	  be configured via the irq array module parameter.
 
 config STM32_TIMER_CNT
 	tristate "STM32 Timer encoder counter driver"
Signature · 1 line
2.28.0
reviewer
#32 lore raw

Re: [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

Quoted 20 lines · William Breathitt Gray
drivers/counter/104-quad-8.c +20 -0
+static irqreturn_t quad8_irq_handler(int irq, void *quad8iio)
+{
+	struct quad8_iio *const priv = quad8iio;
+	const unsigned long base = priv->base;
+	unsigned long irq_status;
+	unsigned long channel;
+	u8 event;
+	int err;
+
+	irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
+	if (!irq_status)
+		return IRQ_NONE;
+
+	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
+		switch (priv->irq_trigger[channel]) {
+		case 0:
+			event = COUNTER_EVENT_OVERFLOW;
+				break;
+		case 1:
+			event = COUNTER_EVENT_THRESHOLD;

drivers/counter/104-quad-8.c static struct counter_count quad8_counts[] = { · line 1608

Quoted 10 lines
drivers/counter/104-quad-8.c +10 -0
+				break;
+		case 2:
+			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
+				break;
+		case 3:
+			event = COUNTER_EVENT_INDEX;
+				break;
+		default:
+			/* We should never reach here */
+			return -EINVAL;

This is not a valid return value for an IRQ handler. Maybe WARN_ONCE instead?

drivers/counter/104-quad-8.c static struct counter_count quad8_counts[] = { · line 1612

Quoted 4 lines
drivers/counter/104-quad-8.c +4 -0
+		}
+		err = counter_push_event(&priv->counter, event, channel);
+		if (err)
+			return err;

Same here. Otherwise, I think we could end up with interrupts in an endless loop since the interrupt would never be cleared.

Quoted 8 lines
drivers/counter/104-quad-8.c +8 -0
+	}
+
+	/* Clear pending interrupts on device */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
+
+	return IRQ_HANDLED;
+}
+
#33 lore raw

Re: [PATCH v5 5/5] counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8

Quoted 25 lines · David Lechner
Quoted 24 lines · William Breathitt Gray
drivers/counter/104-quad-8.c +24 -0
+static irqreturn_t quad8_irq_handler(int irq, void *quad8iio)
+{
+	struct quad8_iio *const priv = quad8iio;
+	const unsigned long base = priv->base;
+	unsigned long irq_status;
+	unsigned long channel;
+	u8 event;
+	int err;
+
+	irq_status = inb(base + QUAD8_REG_INTERRUPT_STATUS);
+	if (!irq_status)
+		return IRQ_NONE;
+
+	for_each_set_bit(channel, &irq_status, QUAD8_NUM_COUNTERS) {
+		switch (priv->irq_trigger[channel]) {
+		case 0:
+			event = COUNTER_EVENT_OVERFLOW;
+				break;
+		case 1:
+			event = COUNTER_EVENT_THRESHOLD;
+				break;
+		case 2:
+			event = COUNTER_EVENT_OVERFLOW_UNDERFLOW;
+				break;
Quoted 8 lines

drivers/counter/104-quad-8.c static struct counter_count quad8_counts[] = { · line 1608

Quoted 6 lines
drivers/counter/104-quad-8.c +6 -0
+		case 3:
+			event = COUNTER_EVENT_INDEX;
+				break;
+		default:
+			/* We should never reach here */
+			return -EINVAL;

This is not a valid return value for an IRQ handler. Maybe WARN_ONCE instead?

Ack.

Quoted 7 lines

drivers/counter/104-quad-8.c static struct counter_count quad8_counts[] = { · line 1612

Quoted 4 lines
drivers/counter/104-quad-8.c +4 -0
+		}
+		err = counter_push_event(&priv->counter, event, channel);
+		if (err)
+			return err;

Same here. Otherwise, I think we could end up with interrupts in an endless loop since the interrupt would never be cleared.

Good point, I'll change this and the one above.

William Breathitt Gray

Quoted 9 lines
Quoted 8 lines
drivers/counter/104-quad-8.c +8 -0
+	}
+
+	/* Clear pending interrupts on device */
+	outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base + QUAD8_REG_CHAN_OP);
+
+	return IRQ_HANDLED;
+}
+
reviewer
#34 lore raw
In reply to William Breathitt Gray
Quoted 9 lines · William Breathitt Gray

The following are some questions I have about this patchset:

1. Should standard Counter component data types be defined as u8 or u32?

Many standard Counter component types such COUNTER_COMP_SIGNAL_LEVEL have standard values defined (e.g. COUNTER_SIGNAL_LEVEL_LOW and COUNTER_SIGNAL_LEVEL_HIGH). These values are currently handled by the Counter subsystem code as u8 data types.

Quoted 8 lines

If u32 is used for these values instead, C enum structures could be used by driver authors to implicit cast these values via the driver callback parameters; userspace would still use u32 with no issue.

In theory this can work because GCC will treat enums are having a 32-bit size; but I worry about the possibility of build targets that have -fshort-enums enabled, resulting in enums having a size less than 32 bits. Would this be a problem?

We shouldn't have to worry about userspace programs using -fshort-enums since that would break all kernel interfaces that use enums, not just these - so no one should be using that compiler flag.

So I am in favor of using strongly typed enums with u32 as the "generic" enum member type.

Quoted 8 lines

2. Should I have reserved members in the userspace structures?

The structures in include/uapi/linux/counter.h are available to userspace applications. Should I reserve space in these structures for future additions and usage? Will endianess and packing be a concern here?

Since there doesn't seem to be a large number of counter devices this probably isn't critical. Are there any aspects of counter devices in general that couldn't be described with the proposed structures? For example, could there be components more than two levels deep (i.e. it would need id, parent id and grandparent id to describe fully)?

_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#35 lore raw
Quoted 19 lines · David Lechner
Quoted 9 lines · William Breathitt Gray

The following are some questions I have about this patchset:

1. Should standard Counter component data types be defined as u8 or u32?

Many standard Counter component types such COUNTER_COMP_SIGNAL_LEVEL have standard values defined (e.g. COUNTER_SIGNAL_LEVEL_LOW and COUNTER_SIGNAL_LEVEL_HIGH). These values are currently handled by the Counter subsystem code as u8 data types.

Quoted 8 lines

If u32 is used for these values instead, C enum structures could be used by driver authors to implicit cast these values via the driver callback parameters; userspace would still use u32 with no issue.

In theory this can work because GCC will treat enums are having a 32-bit size; but I worry about the possibility of build targets that have -fshort-enums enabled, resulting in enums having a size less than 32 bits. Would this be a problem?

Quoted 6 lines

We shouldn't have to worry about userspace programs using -fshort-enums since that would break all kernel interfaces that use enums, not just these - so no one should be using that compiler flag.

So I am in favor of using strongly typed enums with u32 as the "generic" enum member type.

That reasoning pacifies my worries. I'll test out strongly typed enums in the next revision of this patchset.

Quoted 8 lines
Quoted 8 lines

2. Should I have reserved members in the userspace structures?

The structures in include/uapi/linux/counter.h are available to userspace applications. Should I reserve space in these structures for future additions and usage? Will endianess and packing be a concern here?

Quoted 6 lines

Since there doesn't seem to be a large number of counter devices this probably isn't critical. Are there any aspects of counter devices in general that couldn't be described with the proposed structures? For example, could there be components more than two levels deep (i.e. it would need id, parent id and grandparent id to describe fully)?

I can't think of any devices that would require more depth, so we probably don't need any additional members added to the userspace structures. The current structure should be flexible enough for future expansion, and any additional functionality we come across can be handled by implementing new extension types as we have for the sysfs attributes.

William Breathitt Gray