Skip to contentSkip to conversation

Introduce the Counter character device interface

PATCH Superseded William Breathitt Gray 5 participants 21 messages Nov 22, 2020 – Jan 21, 2021

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

v6 of 17 Superseded Latest is v17 →

lore
Share & tools
lore
#0 lore raw

v6 cover Introduce the Counter character device interface

Changes in v6:
- Consolidated the value member of struct counter_event down to a single u64; u64 should be capable of representing all component values
- Removed extension width sysfs attributes; no longer needed when value is always u64
- Implemented COUNTER_COMPONENT_DUMMY to allow timestamp grabs without component data reads
- Implemented events_config() callback; called during COUNTER_CLEAR_WATCHES_IOCTL and COUNTER_LOAD_WATCHES_IOCTL in order to allow devices a chance to adjust (enable/disable IRQ, etc.) for the new events configuration requested by the user
- Simplified example code in Documentation by removing confusing use of poll() call
- Removed redundant ida_simple_remove() from counter_register()
- Renamed devm_counter_unreg() to devm_counter_unregister()
- Renamed functions in counter-sysfs.c to be clearer
- Fixed miscellaneous typos throughout files
- Added more kernel doc comments; I've left some defines without comments if they seemed obvious -- but please let me know if further documentation is needed
- Refactored quad8_irq_handler() to use WARN_ONCE() instead of returning on error; this should prevent interrupts from entering an endless loop
- General refactoring and additional comments for clarity
- Returns EOPNOTSUPP instead of EFAULT now if a Counter watch is added for unsupported component
- Renamed COUNTER_SET_WATCH_IOCTL TO COUNTER_ADD_WATCH_IOCTL to make the use clear
- Reimplemented the parent and id members of struct counter_component as __u8 instead of __u64; it's unlikely we'll ever have a device that supports more than 255 components
- Reimplement __u64 variables in include/uapi/linux/counter.h as __aligned_u64 to prevent 32-bit vs 64-bit alignment issues
- Fixed return value bug in counter_comp_u8_store(); enums set to a value with index > 0 should now work correctly
- Fixed spectre issues in counter-chrdev.c
- Removed redundant get_device() call from counter_register()
- Moved put_device() to after the events_list is freed lest we leak memory

I'm skipping the introduction blurb because it was just a rehashing of information included in the documentation patches within this patchset. Instead I will focus this cover letter on discussions about this patchset and the userspace interface implications.

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 implicitly cast these values via the driver callback parameters.

This question is primarily addressed to David Lechner. I'm somewhat confused about how this setup would look in device drivers. I've gone ahead and refactored the code to support u32 enums, and pushed it to a separate branch on my repository called counter_chrdev_v6_u32_enum:
https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v6_u32_enum

Please check it out and let me know what you think. Is this the support you had in mind? I'm curious to see an example of how would your driver callback functions would look in this case. Is everything works out fine, then I'll submit this branch as v7 of this patchset.

2. How should we handle "raw" timestamps?

Ahmad Fatoum brought up the possibility of returning "raw" timestamps similar to what the network stack offers (see the network stack SOF_TIMESTAMPING_{RAW,SYS}_HARDWARE support).

I'm not very familiar with the networking stack code, but if I understand correctly the SOF_TIMESTAMPING_RAW_HARDWARE timestamps are values returned from the device. If so, I suspect we would be able to support these "raw" timestamps by defining them as Counter Extensions and returning them in struct counter_event elements similar to the other Extension values.

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  |  411 ++++-
 .../userspace-api/ioctl/ioctl-number.rst      |    1 +
 MAINTAINERS                                   |    2 +-
 drivers/counter/104-quad-8.c                  |  778 +++++----
 drivers/counter/Kconfig                       |    6 +-
 drivers/counter/Makefile                      |    1 +
 drivers/counter/counter-chrdev.c              |  476 ++++++
 drivers/counter/counter-chrdev.h              |   16 +
 drivers/counter/counter-core.c                |  183 ++
 drivers/counter/counter-sysfs.c               |  806 +++++++++
 drivers/counter/counter-sysfs.h               |   13 +
 drivers/counter/counter.c                     | 1496 -----------------
 drivers/counter/ftm-quaddec.c                 |   60 +-
 drivers/counter/microchip-tcb-capture.c       |  114 +-
 drivers/counter/stm32-lptimer-cnt.c           |  175 +-
 drivers/counter/stm32-timer-cnt.c             |  145 +-
 drivers/counter/ti-eqep.c                     |  224 +--
 include/linux/counter.h                       |  676 ++++----
 include/linux/counter_enum.h                  |   45 -
 include/uapi/linux/counter.h                  |  105 ++
 22 files changed, 3094 insertions(+), 2689 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.29.2
#1 lore raw

PATCH v6 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]>
Cc Dan Carpenter <[email protected]>
---
 MAINTAINERS                             |    1 -
 drivers/counter/104-quad-8.c            |  476 +++-----
 drivers/counter/Makefile                |    1 +
 drivers/counter/counter-core.c          |  153 +++
 drivers/counter/counter-sysfs.c         |  775 ++++++++++++
 drivers/counter/counter-sysfs.h         |   13 +
 drivers/counter/counter.c               | 1496 -----------------------
 drivers/counter/ftm-quaddec.c           |   60 +-
 drivers/counter/microchip-tcb-capture.c |  114 +-
 drivers/counter/stm32-lptimer-cnt.c     |  175 +--
 drivers/counter/stm32-timer-cnt.c       |  145 +--
 drivers/counter/ti-eqep.c               |  224 ++--
 include/linux/counter.h                 |  674 +++++-----
 include/linux/counter_enum.h            |   45 -
 14 files changed, 1806 insertions(+), 2546 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 913b5eb64e44..5e0d8ca70912 100644
@@ -4453,7 +4453,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 +153 -0

153 lines not on this page · raw message

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

775 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,226 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 +51 -63

114 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 +116 -108

224 lines not on this page · raw message

include/linux/counter.h +321 -353

674 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.29.2

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

#2 lore raw

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

I love your patch! Perhaps something to improve:

Hi William,

I love your patch! Perhaps something to improve:

[auto build test WARNING on stm32/stm32-next]
[also build test WARNING on linux/master linus/master v5.10-rc4 next-20201120] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20201123-043634
base:   https://git.kernel.org/pub/scm/linux/kernel/git/atorgue/stm32.git stm32-next
config: x86_64-randconfig-a011-20201122 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project 3324fd8a7b1ab011513017ed8fd81e06928526d5)

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# install x86_64 cross compiling tool for clang build
# apt-get install binutils-x86-64-linux-gnu
# https://github.com/0day-ci/linux/commit/1bc8c3bb021f3736c23d97f1a965935821f0ac5a
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20201123-043634 git checkout 1bc8c3bb021f3736c23d97f1a965935821f0ac5a
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64

If you fix the issue, kindly add following tag as appropriate

All warnings (new ones prefixed by >>):

>> drivers/counter/counter-sysfs.c:180:7: warning: variable 'err' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
                   if (idx < 0)
                       ^~~~~~~
   drivers/counter/counter-sysfs.c:193:6: note: uninitialized use occurs here
           if (err)
               ^~~
   drivers/counter/counter-sysfs.c:180:3: note: remove the 'if' if its condition is always true
                   if (idx < 0)
                   ^~~~~~~~~~~~
   drivers/counter/counter-sysfs.c:158:9: note: initialize the variable 'err' to silence this warning
           int err;
                  ^
                   = 0

1 warning generated.

vim +180 drivers/counter/counter-sysfs.c

148
149 static ssize_t counter_comp_u8_store(struct device *dev,
150 struct device_attribute *attr,
151 const char *buf, size_t len)
152 {
153 const struct counter_attribute *const a = to_counter_attribute(attr);
154 struct counter_device *const counter = dev_get_drvdata(dev);
155 struct counter_count *const count = a->parent;
156 struct counter_synapse *const synapse = a->comp.priv;
157 const struct counter_available *const avail = a->comp.priv;
158 int err;
159 bool bool_data;
160 int idx;
161 u8 data;
162
163 switch (a->comp.type) {
164 case COUNTER_COMP_BOOL:
165 err = kstrtobool(buf, &bool_data);
166 data = bool_data;
167 break;
168 case COUNTER_COMP_FUNCTION:
169 err = find_in_string_array(&data, count->functions_list,
170 count->num_functions, buf,
171 counter_function_str);
172 break;
173 case COUNTER_COMP_SYNAPSE_ACTION:
174 err = find_in_string_array(&data, synapse->actions_list,
175 synapse->num_actions, buf,
176 counter_synapse_action_str);
177 break;
178 case COUNTER_COMP_ENUM:
179 idx = __sysfs_match_string(avail->strs, avail->num_items, buf);

 > 180			if (idx < 0)

181 return idx;
182 data = idx;
183 break;
184 case COUNTER_COMP_COUNT_MODE:
185 err = find_in_string_array(&data, avail->enums,
186 avail->num_items, buf,
187 counter_count_mode_str);
188 break;
189 default:
190 err = kstrtou8(buf, 0, &data);
191 break;
192 }
193 if (err)
194 return err;
195
196 switch (a->scope) {
197 case COUNTER_SCOPE_DEVICE:
198 err = a->comp.device_u8_write(counter, data);
199 break;
200 case COUNTER_SCOPE_SIGNAL:
201 err = a->comp.signal_u8_write(counter, a->parent, data);
202 break;
203 case COUNTER_SCOPE_COUNT:
204 if (a->comp.type == COUNTER_COMP_SYNAPSE_ACTION)
205 err = a->comp.action_write(counter, count, synapse,
206 data);
207 else
208 err = a->comp.count_u8_write(counter, count, data);
209 break;
210 }
211 if (err)
212 return err;
213
214 return len;
215 }
216

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#3 lore raw

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

In reply to William Breathitt Gray
I love your patch! Perhaps something to improve:

Hi William,

I love your patch! Perhaps something to improve:

[auto build test WARNING on stm32/stm32-next]
[also build test WARNING on linux/master linus/master v5.10-rc5 next-20201124] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20201123-043634
base:   https://git.kernel.org/pub/scm/linux/kernel/git/atorgue/stm32.git stm32-next
config: powerpc-randconfig-r016-20201124 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project df9ae5992889560a8f3c6760b54d5051b47c7bf5)

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# install powerpc cross compiling tool for clang build
# apt-get install binutils-powerpc-linux-gnu
# https://github.com/0day-ci/linux/commit/1bc8c3bb021f3736c23d97f1a965935821f0ac5a
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20201123-043634 git checkout 1bc8c3bb021f3736c23d97f1a965935821f0ac5a
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=powerpc

If you fix the issue, kindly add following tag as appropriate

All warnings (new ones prefixed by >>):

In file included from include/linux/iopoll.h:14:
In file included from include/linux/io.h:13:
In file included from arch/powerpc/include/asm/io.h:604:
arch/powerpc/include/asm/io-defs.h:43:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] DEF_PCI_AC_NORET(insb, (unsigned long p, void *b, unsigned long c), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'

                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:170:1: note: expanded from here
   __do_insb
   ^
   arch/powerpc/include/asm/io.h:541:56: note: expanded from macro '__do_insb'
   #define __do_insb(p, b, n)      readsb((PCI_IO_ADDR)_IO_BASE+(p), (b), (n))
                                          ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:45:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(insw, (unsigned long p, void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:172:1: note: expanded from here
   __do_insw
   ^
   arch/powerpc/include/asm/io.h:542:56: note: expanded from macro '__do_insw'
   #define __do_insw(p, b, n)      readsw((PCI_IO_ADDR)_IO_BASE+(p), (b), (n))
                                          ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:47:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(insl, (unsigned long p, void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:174:1: note: expanded from here
   __do_insl
   ^
   arch/powerpc/include/asm/io.h:543:56: note: expanded from macro '__do_insl'
   #define __do_insl(p, b, n)      readsl((PCI_IO_ADDR)_IO_BASE+(p), (b), (n))
                                          ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:49:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(outsb, (unsigned long p, const void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:176:1: note: expanded from here
   __do_outsb
   ^
   arch/powerpc/include/asm/io.h:544:58: note: expanded from macro '__do_outsb'
   #define __do_outsb(p, b, n)     writesb((PCI_IO_ADDR)_IO_BASE+(p),(b),(n))
                                           ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:51:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(outsw, (unsigned long p, const void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:178:1: note: expanded from here
   __do_outsw
   ^
   arch/powerpc/include/asm/io.h:545:58: note: expanded from macro '__do_outsw'
   #define __do_outsw(p, b, n)     writesw((PCI_IO_ADDR)_IO_BASE+(p),(b),(n))
                                           ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:53:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(outsl, (unsigned long p, const void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:180:1: note: expanded from here
   __do_outsl
   ^
   arch/powerpc/include/asm/io.h:546:58: note: expanded from macro '__do_outsl'
   #define __do_outsl(p, b, n)     writesl((PCI_IO_ADDR)_IO_BASE+(p),(b),(n))
                                           ~~~~~~~~~~~~~~~~~~~~~^
>> drivers/counter/ti-eqep.c:115:17: warning: unused variable 'ti_qep_t2c_functions_map' [-Wunused-const-variable]
   static const u8 ti_qep_t2c_functions_map[] = {
                   ^

13 warnings generated.

vim +/ti_qep_t2c_functions_map +115 drivers/counter/ti-eqep.c

114

 > 115	static const u8 ti_qep_t2c_functions_map[] = {

116 };
117

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#4 lore raw

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

Quoted 138 lines · kernel test robot

Hi William,

I love your patch! Perhaps something to improve:

[auto build test WARNING on stm32/stm32-next]
[also build test WARNING on linux/master linus/master v5.10-rc5 next-20201124] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20201123-043634
base:   https://git.kernel.org/pub/scm/linux/kernel/git/atorgue/stm32.git stm32-next
config: powerpc-randconfig-r016-20201124 (attached as .config)
compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project df9ae5992889560a8f3c6760b54d5051b47c7bf5)

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# install powerpc cross compiling tool for clang build
# apt-get install binutils-powerpc-linux-gnu
# https://github.com/0day-ci/linux/commit/1bc8c3bb021f3736c23d97f1a965935821f0ac5a
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review William-Breathitt-Gray/Introduce-the-Counter-character-device-interface/20201123-043634 git checkout 1bc8c3bb021f3736c23d97f1a965935821f0ac5a
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=powerpc

If you fix the issue, kindly add following tag as appropriate

All warnings (new ones prefixed by >>):

In file included from include/linux/iopoll.h:14:
In file included from include/linux/io.h:13:
In file included from arch/powerpc/include/asm/io.h:604:
arch/powerpc/include/asm/io-defs.h:43:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] DEF_PCI_AC_NORET(insb, (unsigned long p, void *b, unsigned long c), ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'

                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:170:1: note: expanded from here
   __do_insb
   ^
   arch/powerpc/include/asm/io.h:541:56: note: expanded from macro '__do_insb'
   #define __do_insb(p, b, n)      readsb((PCI_IO_ADDR)_IO_BASE+(p), (b), (n))
                                          ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:45:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(insw, (unsigned long p, void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:172:1: note: expanded from here
   __do_insw
   ^
   arch/powerpc/include/asm/io.h:542:56: note: expanded from macro '__do_insw'
   #define __do_insw(p, b, n)      readsw((PCI_IO_ADDR)_IO_BASE+(p), (b), (n))
                                          ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:47:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(insl, (unsigned long p, void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:174:1: note: expanded from here
   __do_insl
   ^
   arch/powerpc/include/asm/io.h:543:56: note: expanded from macro '__do_insl'
   #define __do_insl(p, b, n)      readsl((PCI_IO_ADDR)_IO_BASE+(p), (b), (n))
                                          ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:49:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(outsb, (unsigned long p, const void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:176:1: note: expanded from here
   __do_outsb
   ^
   arch/powerpc/include/asm/io.h:544:58: note: expanded from macro '__do_outsb'
   #define __do_outsb(p, b, n)     writesb((PCI_IO_ADDR)_IO_BASE+(p),(b),(n))
                                           ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:51:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(outsw, (unsigned long p, const void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:178:1: note: expanded from here
   __do_outsw
   ^
   arch/powerpc/include/asm/io.h:545:58: note: expanded from macro '__do_outsw'
   #define __do_outsw(p, b, n)     writesw((PCI_IO_ADDR)_IO_BASE+(p),(b),(n))
                                           ~~~~~~~~~~~~~~~~~~~~~^
   In file included from drivers/counter/ti-eqep.c:15:
   In file included from include/linux/regmap.h:20:
   In file included from include/linux/iopoll.h:14:
   In file included from include/linux/io.h:13:
   In file included from arch/powerpc/include/asm/io.h:604:
   arch/powerpc/include/asm/io-defs.h:53:1: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
   DEF_PCI_AC_NORET(outsl, (unsigned long p, const void *b, unsigned long c),
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   arch/powerpc/include/asm/io.h:601:3: note: expanded from macro 'DEF_PCI_AC_NORET'
                   __do_##name al;                                 \
                   ^~~~~~~~~~~~~~
   <scratch space>:180:1: note: expanded from here
   __do_outsl
   ^
   arch/powerpc/include/asm/io.h:546:58: note: expanded from macro '__do_outsl'
   #define __do_outsl(p, b, n)     writesl((PCI_IO_ADDR)_IO_BASE+(p),(b),(n))
                                           ~~~~~~~~~~~~~~~~~~~~~^
Quoted 1 line
Quoted 1 line

drivers/counter/ti-eqep.c:115:17: warning: unused variable 'ti_qep_t2c_functions_map' [-Wunused-const-variable]

   static const u8 ti_qep_t2c_functions_map[] = {
                   ^

13 warnings generated.

vim +/ti_qep_t2c_functions_map +115 drivers/counter/ti-eqep.c

Quoted 8 lines
   114	
Quoted 1 line

115 static const u8 ti_qep_t2c_functions_map[] = {

   116	};
   117	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

This ti_qep_t2c_functions_map array is left over from some code I was testing. It's not used at all -- I simply forgot to remove it -- so I'll make sure to take it out in the next patchset. I'll give this v6 patchset some more time for people to review and comment before I submit the v7 revision.

William Breathitt Gray

#5 lore raw

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

In reply to William Breathitt Gray

drivers/counter/ti-eqep.c static int ti_eqep_count_write(struct counter_device *counter, · line 116

Quoted 8 lines · William Breathitt Gray
drivers/counter/ti-eqep.c +2 -2
@@ -117,62 +112,95 @@ static int ti_eqep_count_write(struct counter_device *counter,
 	return regmap_write(priv->regmap32, QPOSCNT, val);
 }
 
-static int ti_eqep_function_get(struct counter_device *counter,
-				struct counter_count *count, size_t *function)
+static const u8 ti_qep_t2c_functions_map[] = {
+};

Just a heads-up: this ti_qep_t2c_functions_map array is left over from some code I was testing. It's not used at all -- I simply forgot to remove it -- so I'll make sure to take it out in the next patchset. I'll give this v6 patchset some more time for people to review and comment before I submit the v7 revision.

William Breathitt Gray

reviewer
#6 lore raw

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

In reply to William Breathitt Gray
Quoted 1 line · William Breathitt Gray
  14 files changed, 1806 insertions(+), 2546 deletions(-)

It would be really nice if we could break this down into smaller pieces and start getting it merged. It is really tough to keep reviewing this much code in one patch over and over again.

Here are some initial findings from testing:

drivers/counter/counter-core.c line 25

Quoted 7 lines
drivers/counter/counter-core.c new file +7 -0
+static void counter_device_release(struct device *dev)
+{
+	struct counter_device *const counter = dev_get_drvdata(dev);
+
+	counter_chrdev_remove(counter);
+	ida_simple_remove(&counter_ida, counter->id);
+}

I got the following error after modprobe -r ti-eqep:

[ 1186.045766] ------------[ cut here ]------------
[ 1186.050647] WARNING: CPU: 0 PID: 2625 at lib/refcount.c:28 counter_device_release+0x10/0x24 [counter]
[ 1186.059976] refcount_t: underflow; use-after-free.
[ 1186.064790] Modules linked in: aes_arm_bs(+) crypto_simd cryptd ccm usb_f_mass_storage usb_f_acm u_serial usb_f_ecm rfcomm usb_f_rndis u_ether libcomposite aes_arm aes_generic cmac bnep wl18xx wlcore mac80211 libarc4 sha256_generic libsha256 sha256_arm cfg80211 ti_am335x_adc kfifo_buf omap_aes_driver omap_crypto omap_sham crypto_engine pm33xx ti_emif_sram hci_uart omap_rng btbcm rng_core ti_eqep(-) counter bluetooth c_can_platform c_can ecdh_generic bmp280_spi ecc can_dev libaes bmp280_i2c bmp280 industrialio omap_mailbox musb_dsps wlcore_sdio musb_hdrc udc_core usbcore wkup_m3_ipc at24 omap_wdt phy_am335x watchdog phy_am335x_control ti_am335x_tscadc phy_generic wkup_m3_rproc usb_common cppi41 rtc_omap leds_gpio led_class cpufreq_dt pwm_tiehrpwm autofs4
[ 1186.132376] CPU: 0 PID: 2625 Comm: modprobe Not tainted 5.10.0-rc7bone-counter+ #23
[ 1186.140070] Hardware name: Generic AM33XX (Flattened Device Tree)
[ 1186.146225] [<c0110d70>] (unwind_backtrace) from [<c010b640>] (show_stack+0x10/0x14)
[ 1186.154017] [<c010b640>] (show_stack) from [<c09a0c98>] (dump_stack+0xc4/0xe4)
[ 1186.161285] [<c09a0c98>] (dump_stack) from [<c0137ba0>] (__warn+0xd8/0x100)
[ 1186.168284] [<c0137ba0>] (__warn) from [<c099c8e4>] (warn_slowpath_fmt+0x94/0xbc)
[ 1186.175814] [<c099c8e4>] (warn_slowpath_fmt) from [<bf10b0e8>] (counter_device_release+0x10/0x24 [counter])
[ 1186.185632] [<bf10b0e8>] (counter_device_release [counter]) from [<c0667118>] (device_release+0x30/0xa4)
[ 1186.195163] [<c0667118>] (device_release) from [<c057f73c>] (kobject_put+0x94/0x104)
[ 1186.202944] [<c057f73c>] (kobject_put) from [<c057f73c>] (kobject_put+0x94/0x104)
[ 1186.210472] [<c057f73c>] (kobject_put) from [<bf19004c>] (ti_eqep_remove+0x10/0x30 [ti_eqep])
[ 1186.219047] [<bf19004c>] (ti_eqep_remove [ti_eqep]) from [<c066f390>] (platform_drv_remove+0x24/0x3c)
[ 1186.228313] [<c066f390>] (platform_drv_remove) from [<c066d934>] (device_release_driver_internal+0xfc/0x1d0)
[ 1186.238187] [<c066d934>] (device_release_driver_internal) from [<c066da78>] (driver_detach+0x58/0xa8)
[ 1186.247456] [<c066da78>] (driver_detach) from [<c066c5ec>] (bus_remove_driver+0x4c/0xa0)
[ 1186.255594] [<c066c5ec>] (bus_remove_driver) from [<c01dd150>] (sys_delete_module+0x180/0x264)
[ 1186.264250] [<c01dd150>] (sys_delete_module) from [<c0100080>] (ret_fast_syscall+0x0/0x54)
[ 1186.272551] Exception stack(0xd247ffa8 to 0xd247fff0)
[ 1186.277629] ffa0:                   004fb478 004fb478 004fb4b4 00000800 b3bfcf00 00000000
[ 1186.285847] ffc0: 004fb478 004fb478 004fb478 00000081 00000000 be974900 be974a55 004fb478
[ 1186.294062] ffe0: 004f8f5c be97352c 004ddd97 b6d11d68
[ 1186.299253] ---[ end trace e1c61dea091f1078 ]---
Quoted 35 lines
drivers/counter/counter-sysfs.c new file +35 -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;
+	int idx;
+	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:
+		idx = __sysfs_match_string(avail->strs, avail->num_items, buf);
+		if (idx < 0)
+			return idx;
+		data = idx;
+		break;

drivers/counter/counter-sysfs.c line 193

Quoted 10 lines
drivers/counter/counter-sysfs.c new file +10 -0
+	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;
+	}
+	if (err)

This needs to be if (err < 0). There are cases where the functions above return positive values. (And to be overly safe, it probably wouldn't hurt to use err < 0 everywhere - not just in this function.)

Quoted 22 lines
drivers/counter/counter-sysfs.c new file +22 -0
+		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 v6 1/5] counter: Internalize sysfs interface code

Quoted 7 lines · David Lechner
Quoted 1 line · William Breathitt Gray
  14 files changed, 1806 insertions(+), 2546 deletions(-)

It would be really nice if we could break this down into smaller pieces and start getting it merged. It is really tough to keep reviewing this much code in one patch over and over again.

Yes, this is a pretty massive patch. I could break this across the individual files affected to make it simpler to review, but in the end all those patches would need to end up squashed together before merge again (for the sake of git bisect), so the effort feels somewhat moot.

Luckily, I don't think there will be much change in the next revision since it's looking like it'll mainly be a few bug fixes; hopefully this coming version 7 will be the final revision before merge.

Quoted 32 lines

Here are some initial findings from testing:

drivers/counter/counter-core.c line 25

Quoted 7 lines
drivers/counter/counter-core.c new file +7 -0
+static void counter_device_release(struct device *dev)
+{
+	struct counter_device *const counter = dev_get_drvdata(dev);
+
+	counter_chrdev_remove(counter);
+	ida_simple_remove(&counter_ida, counter->id);
+}

I got the following error after modprobe -r ti-eqep:

[ 1186.045766] ------------[ cut here ]------------
[ 1186.050647] WARNING: CPU: 0 PID: 2625 at lib/refcount.c:28 counter_device_release+0x10/0x24 [counter]
[ 1186.059976] refcount_t: underflow; use-after-free.
[ 1186.064790] Modules linked in: aes_arm_bs(+) crypto_simd cryptd ccm usb_f_mass_storage usb_f_acm u_serial usb_f_ecm rfcomm usb_f_rndis u_ether libcomposite aes_arm aes_generic cmac bnep wl18xx wlcore mac80211 libarc4 sha256_generic libsha256 sha256_arm cfg80211 ti_am335x_adc kfifo_buf omap_aes_driver omap_crypto omap_sham crypto_engine pm33xx ti_emif_sram hci_uart omap_rng btbcm rng_core ti_eqep(-) counter bluetooth c_can_platform c_can ecdh_generic bmp280_spi ecc can_dev libaes bmp280_i2c bmp280 industrialio omap_mailbox musb_dsps wlcore_sdio musb_hdrc udc_core usbcore wkup_m3_ipc at24 omap_wdt phy_am335x watchdog phy_am335x_control ti_am335x_tscadc phy_generic wkup_m3_rproc usb_common cppi41 rtc_omap leds_gpio led_class cpufreq_dt pwm_tiehrpwm autofs4
[ 1186.132376] CPU: 0 PID: 2625 Comm: modprobe Not tainted 5.10.0-rc7bone-counter+ #23
[ 1186.140070] Hardware name: Generic AM33XX (Flattened Device Tree)
[ 1186.146225] [<c0110d70>] (unwind_backtrace) from [<c010b640>] (show_stack+0x10/0x14)
[ 1186.154017] [<c010b640>] (show_stack) from [<c09a0c98>] (dump_stack+0xc4/0xe4)
[ 1186.161285] [<c09a0c98>] (dump_stack) from [<c0137ba0>] (__warn+0xd8/0x100)
[ 1186.168284] [<c0137ba0>] (__warn) from [<c099c8e4>] (warn_slowpath_fmt+0x94/0xbc)
[ 1186.175814] [<c099c8e4>] (warn_slowpath_fmt) from [<bf10b0e8>] (counter_device_release+0x10/0x24 [counter])
[ 1186.185632] [<bf10b0e8>] (counter_device_release [counter]) from [<c0667118>] (device_release+0x30/0xa4)
[ 1186.195163] [<c0667118>] (device_release) from [<c057f73c>] (kobject_put+0x94/0x104)
[ 1186.202944] [<c057f73c>] (kobject_put) from [<c057f73c>] (kobject_put+0x94/0x104)
[ 1186.210472] [<c057f73c>] (kobject_put) from [<bf19004c>] (ti_eqep_remove+0x10/0x30 [ti_eqep])
[ 1186.219047] [<bf19004c>] (ti_eqep_remove [ti_eqep]) from [<c066f390>] (platform_drv_remove+0x24/0x3c)
[ 1186.228313] [<c066f390>] (platform_drv_remove) from [<c066d934>] (device_release_driver_internal+0xfc/0x1d0)
[ 1186.238187] [<c066d934>] (device_release_driver_internal) from [<c066da78>] (driver_detach+0x58/0xa8)
Quoted 8 lines
[ 1186.247456] [<c066da78>] (driver_detach) from [<c066c5ec>] (bus_remove_driver+0x4c/0xa0)
[ 1186.255594] [<c066c5ec>] (bus_remove_driver) from [<c01dd150>] (sys_delete_module+0x180/0x264)
[ 1186.264250] [<c01dd150>] (sys_delete_module) from [<c0100080>] (ret_fast_syscall+0x0/0x54)
[ 1186.272551] Exception stack(0xd247ffa8 to 0xd247fff0)
[ 1186.277629] ffa0:                   004fb478 004fb478 004fb4b4 00000800 b3bfcf00 00000000
[ 1186.285847] ffc0: 004fb478 004fb478 004fb478 00000081 00000000 be974900 be974a55 004fb478
[ 1186.294062] ffe0: 004f8f5c be97352c 004ddd97 b6d11d68
[ 1186.299253] ---[ end trace e1c61dea091f1078 ]---

I noticed that I'm calling counter_chrdev_remove() twice: once in counter_unregister(), and again in counter_device_release(). I suspect this is what's causing the refcount to underflow. I'll test and verify that this is the culprit.

In fact, I don't think I need to define a counter_device_release() callback at all, would I? These cleanup function calls could be moved to counter_unregister() instead.

Quoted 41 lines
Quoted 41 lines
drivers/counter/counter-sysfs.c new file +41 -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;
+	int idx;
+	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:
+		idx = __sysfs_match_string(avail->strs, avail->num_items, buf);
+		if (idx < 0)
+			return idx;
+		data = idx;
+		break;
+	case COUNTER_COMP_COUNT_MODE:
+		err = find_in_string_array(&data, avail->enums,
+					   avail->num_items, buf,
+					   counter_count_mode_str);
+		break;
+	default:
Quoted 8 lines

drivers/counter/counter-sysfs.c line 193

Quoted 4 lines
drivers/counter/counter-sysfs.c new file +4 -0
+		err = kstrtou8(buf, 0, &data);
+		break;
+	}
+	if (err)

This needs to be if (err < 0). There are cases where the functions above return positive values. (And to be overly safe, it probably wouldn't hurt to use err < 0 everywhere - not just in this function.)

Ack.

William Breathitt Gray

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

reviewer
#8 lore raw

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

Quoted 59 lines · William Breathitt Gray
Quoted 7 lines · David Lechner
Quoted 1 line · William Breathitt Gray
   14 files changed, 1806 insertions(+), 2546 deletions(-)

It would be really nice if we could break this down into smaller pieces and start getting it merged. It is really tough to keep reviewing this much code in one patch over and over again.

Yes, this is a pretty massive patch. I could break this across the individual files affected to make it simpler to review, but in the end all those patches would need to end up squashed together before merge again (for the sake of git bisect), so the effort feels somewhat moot.

Luckily, I don't think there will be much change in the next revision since it's looking like it'll mainly be a few bug fixes; hopefully this coming version 7 will be the final revision before merge.

Quoted 32 lines

Here are some initial findings from testing:

drivers/counter/counter-core.c line 25

Quoted 7 lines
drivers/counter/counter-core.c new file +7 -0
+static void counter_device_release(struct device *dev)
+{
+	struct counter_device *const counter = dev_get_drvdata(dev);
+
+	counter_chrdev_remove(counter);
+	ida_simple_remove(&counter_ida, counter->id);
+}

I got the following error after modprobe -r ti-eqep:

[ 1186.045766] ------------[ cut here ]------------
[ 1186.050647] WARNING: CPU: 0 PID: 2625 at lib/refcount.c:28 counter_device_release+0x10/0x24 [counter]
[ 1186.059976] refcount_t: underflow; use-after-free.
[ 1186.064790] Modules linked in: aes_arm_bs(+) crypto_simd cryptd ccm usb_f_mass_storage usb_f_acm u_serial usb_f_ecm rfcomm usb_f_rndis u_ether libcomposite aes_arm aes_generic cmac bnep wl18xx wlcore mac80211 libarc4 sha256_generic libsha256 sha256_arm cfg80211 ti_am335x_adc kfifo_buf omap_aes_driver omap_crypto omap_sham crypto_engine pm33xx ti_emif_sram hci_uart omap_rng btbcm rng_core ti_eqep(-) counter bluetooth c_can_platform c_can ecdh_generic bmp280_spi ecc can_dev libaes bmp280_i2c bmp280 industrialio omap_mailbox musb_dsps wlcore_sdio musb_hdrc udc_core usbcore wkup_m3_ipc at24 omap_wdt phy_am335x watchdog phy_am335x_control ti_am335x_tscadc phy_generic wkup_m3_rproc usb_common cppi41 rtc_omap leds_gpio led_class cpufreq_dt pwm_tiehrpwm autofs4
[ 1186.132376] CPU: 0 PID: 2625 Comm: modprobe Not tainted 5.10.0-rc7bone-counter+ #23
[ 1186.140070] Hardware name: Generic AM33XX (Flattened Device Tree)
[ 1186.146225] [<c0110d70>] (unwind_backtrace) from [<c010b640>] (show_stack+0x10/0x14)
[ 1186.154017] [<c010b640>] (show_stack) from [<c09a0c98>] (dump_stack+0xc4/0xe4)
[ 1186.161285] [<c09a0c98>] (dump_stack) from [<c0137ba0>] (__warn+0xd8/0x100)
[ 1186.168284] [<c0137ba0>] (__warn) from [<c099c8e4>] (warn_slowpath_fmt+0x94/0xbc)
[ 1186.175814] [<c099c8e4>] (warn_slowpath_fmt) from [<bf10b0e8>] (counter_device_release+0x10/0x24 [counter])
[ 1186.185632] [<bf10b0e8>] (counter_device_release [counter]) from [<c0667118>] (device_release+0x30/0xa4)
[ 1186.195163] [<c0667118>] (device_release) from [<c057f73c>] (kobject_put+0x94/0x104)
[ 1186.202944] [<c057f73c>] (kobject_put) from [<c057f73c>] (kobject_put+0x94/0x104)
[ 1186.210472] [<c057f73c>] (kobject_put) from [<bf19004c>] (ti_eqep_remove+0x10/0x30 [ti_eqep])
[ 1186.219047] [<bf19004c>] (ti_eqep_remove [ti_eqep]) from [<c066f390>] (platform_drv_remove+0x24/0x3c)
[ 1186.228313] [<c066f390>] (platform_drv_remove) from [<c066d934>] (device_release_driver_internal+0xfc/0x1d0)
[ 1186.238187] [<c066d934>] (device_release_driver_internal) from [<c066da78>] (driver_detach+0x58/0xa8)
Quoted 8 lines
[ 1186.247456] [<c066da78>] (driver_detach) from [<c066c5ec>] (bus_remove_driver+0x4c/0xa0)
[ 1186.255594] [<c066c5ec>] (bus_remove_driver) from [<c01dd150>] (sys_delete_module+0x180/0x264)
[ 1186.264250] [<c01dd150>] (sys_delete_module) from [<c0100080>] (ret_fast_syscall+0x0/0x54)
[ 1186.272551] Exception stack(0xd247ffa8 to 0xd247fff0)
[ 1186.277629] ffa0:                   004fb478 004fb478 004fb4b4 00000800 b3bfcf00 00000000
[ 1186.285847] ffc0: 004fb478 004fb478 004fb478 00000081 00000000 be974900 be974a55 004fb478
[ 1186.294062] ffe0: 004f8f5c be97352c 004ddd97 b6d11d68
[ 1186.299253] ---[ end trace e1c61dea091f1078 ]---
Quoted 8 lines

I noticed that I'm calling counter_chrdev_remove() twice: once in counter_unregister(), and again in counter_device_release(). I suspect this is what's causing the refcount to underflow. I'll test and verify that this is the culprit.

In fact, I don't think I need to define a counter_device_release() callback at all, would I? These cleanup function calls could be moved to counter_unregister() instead.

As long as a user program keeps a chrdev open, it holds a reference to the device, so I think it needs to be the other way around. (Unless it is impossible to call counter_unregister() before all references have been released - but I don't think that is the case - not 100% sure.)

#9 lore raw

PATCH v6 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/ABI/testing/sysfs-bus-counter  |   9 +-
 Documentation/driver-api/generic-counter.rst | 242 ++++++++++++++-----
 2 files changed, 184 insertions(+), 67 deletions(-)
Documentation/ABI/testing/sysfs-bus-counter +8 -1
index 566bd99fe0a5..1820ce2f9183 100644
@@ -219,7 +219,14 @@ What:		/sys/bus/counter/devices/counterX/signalY/signal
 KernelVersion:	5.2
 Contact:	[email protected]
 Description:
-		Signal data of Signal Y represented as a string.
+		Signal level state of Signal Y. The following signal level
+		states are available:
+
+		low:
+			Low level state.
+
+		high:
+			High level state.
 
 What:		/sys/bus/counter/devices/counterX/signalY/name
 KernelVersion:	5.2
Documentation/driver-api/generic-counter.rst +176 -66

261 lines not on this page · raw message

Signature · 1 line
2.29.2
#10 lore raw

PATCH v6 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.

Cc David Lechner <[email protected]>
Cc Gwendal Grignou <[email protected]>
Cc Dan Carpenter <[email protected]>
---
 MAINTAINERS                      |   1 +
 drivers/counter/Makefile         |   2 +-
 drivers/counter/counter-chrdev.c | 476 +++++++++++++++++++++++++++++++
 drivers/counter/counter-chrdev.h |  16 ++
 drivers/counter/counter-core.c   |  38 ++-
 drivers/counter/counter-sysfs.c  |  51 +++-
 include/linux/counter.h          |  66 +++--
 include/uapi/linux/counter.h     | 105 +++++++
 8 files changed, 711 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 5e0d8ca70912..beda3366b64c 100644
@@ -4453,6 +4453,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 +476 -0

476 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 251627b95a3a..088886e93898 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 */
@@ -33,6 +37,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
@@ -51,7 +57,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) {
@@ -62,18 +67,25 @@ 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)
-		goto err_free_id;
+		goto err_remove_chrdev;
 
 	return 0;
 
+err_remove_chrdev:
+	counter_chrdev_remove(counter);
 err_free_id:
 	put_device(dev);
 	return err;
@@ -92,6 +104,7 @@ void counter_unregister(struct counter_device *const counter)
 		return;
 
 	device_unregister(&counter->dev);
+	counter_chrdev_remove(counter);
 }
 EXPORT_SYMBOL_GPL(counter_unregister);
 
@@ -135,13 +148,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 +41 -10

122 lines not on this page · raw message

include/linux/counter.h +37 -29

117 lines not on this page · raw message

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

105 lines not on this page · raw message

Signature · 1 line
2.29.2
reviewer
#11 lore raw

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

Quoted 34 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.

Cc David Lechner <[email protected]>
Cc Gwendal Grignou <[email protected]>
Cc Dan Carpenter <[email protected]>

---

drivers/counter/counter-chrdev.c new file +13 -0
new file mode 100644
index 000000000000..96fa7fbeef92
 
 
+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)) {

drivers/counter/counter-chrdev.c line 57

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+			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);

As mentioned in my previous review, I think it would be fine to use a mutex here instead of disabling interrupts. In fact, I think copy_to_user() might sleep, in which case we really don't want to call this with interrupts disabled.

Quoted 16 lines
drivers/counter/counter-chrdev.c new file +14 -0
+		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;
+}
+
 
 
+static int counter_add_watch(struct counter_device *const counter,
+			     const unsigned long arg)
+{
+	void __user *const uwatch = (void __user *)arg;
+	struct counter_watch watch;

drivers/counter/counter-chrdev.c line 164

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+	struct counter_comp_node comp_node = {0};
+	size_t parent, id;
+	struct counter_comp *ext;
+	size_t num_ext;
+
+	if (copy_from_user(&watch, uwatch, sizeof(watch)))
+		return -EFAULT;
+
+	/* Dummy components can skip evaluation */
+	if (watch.component.type == COUNTER_COMPONENT_DUMMY)

I think "none" would be a better name than "dummy". Then it just naturally makes sense why we would skip the evaluation.

Quoted 76 lines
drivers/counter/counter-chrdev.c new file +76 -0
+		goto dummy_component;
+
+	parent = watch.component.parent;
+
+	/* Configure parent component info for comp node */
+	switch (watch.component.scope) {
+	case COUNTER_SCOPE_DEVICE:
+		ext = counter->ext;
+		num_ext = counter->num_ext;
+		break;
+	case COUNTER_SCOPE_SIGNAL:
+		if (parent >= counter->num_signals)
+			return -EINVAL;
+		parent = array_index_nospec(parent, counter->num_signals);
+
+		comp_node.parent = counter->signals + parent;
+
+		ext = counter->signals[parent].ext;
+		num_ext = counter->signals[parent].num_ext;
+		break;
+	case COUNTER_SCOPE_COUNT:
+		if (parent >= counter->num_counts)
+			return -EINVAL;
+		parent = array_index_nospec(parent, counter->num_counts);
+
+		comp_node.parent = counter->counts + parent;
+
+		ext = counter->counts[parent].ext;
+		num_ext = counter->counts[parent].num_ext;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	id = watch.component.id;
+
+	/* 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;
+		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 (id >= counter->counts[parent].num_synapses)
+			return -EINVAL;
+		id = array_index_nospec(id, counter->counts[parent].num_synapses);
+
+		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 (id >= num_ext)
+			return -EINVAL;
+		id = array_index_nospec(id, num_ext);
+
+		comp_node.comp = ext[id];

drivers/counter/counter-chrdev.c line 250

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Check if any read callback is set; this is part of a union */
+	if (!comp_node.comp.count_u8_read)
+		return -EOPNOTSUPP;
+
+dummy_component:
+	comp_node.component = watch.component;

In my experiments, I added a events_validate driver callback here to validate each event as it is added. This way the user can know exactly which event caused the problem rather than waiting for the event_config callback.

Quoted 15 lines
drivers/counter/counter-chrdev.c new file +13 -0
+
+	return counter_set_event_node(counter, &watch, &comp_node);
+}
+
 
 
+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 316

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;
+
+	raw_spin_lock_irqsave(&counter->events_lock, flags);
+	counter_events_list_free(&counter->events_list);

Do we need to call the events_config callback here?

Quoted 42 lines
drivers/counter/counter-chrdev.c new file +40 -0
+	raw_spin_unlock_irqrestore(&counter->events_lock, flags);
+	counter_events_list_free(&counter->next_events_list);
+
+	put_device(&counter->dev);
+
+	return 0;
+}
 
 
+/**
+ * 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 = 0;
+
+	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;

drivers/counter/counter-chrdev.c line 461

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+
+	/* 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);
+		if (err)
+			goto exit_early;

It looks like this will skip an event even if just one watch value has an error. This doesn't seem so great.

drivers/counter/counter-chrdev.c line 474

Quoted 13 lines
drivers/counter/counter-chrdev.c new file +13 -0
+
+		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 err;

Interrupt handlers can't really do anything about the error here. It could be nice to instead add an error field to the userspace event struct, then userspace would know when an error occurred and could do something about it. And it also fixes the one-error- skips-all problem I mentioned above.

Quoted 15 lines
drivers/counter/counter-chrdev.c new file +2 -0
+}
+EXPORT_SYMBOL_GPL(counter_push_event);
include/linux/counter.h +4 -1
index 3f3f8ba6c1b4..98cd7c035968 100644
--- a/include/linux/counter.h
 
 
 
+/**
+ * struct counter_event_node - Counter Event node
+ * @l:		list of current watching Counter events
+ * @event:	event that triggers

include/linux/counter.h struct counter_count { · line 187

Quoted 10 lines
include/linux/counter.h +10 -0
+ * @channel:	event channel
+ * @comp_list:	list of components to watch when event triggers
+ */
+struct counter_event_node {
+	struct list_head l;
+	u8 event;
+	u8 channel;
+	struct list_head comp_list;
+};
+

Unless this is needed outside of the drivers/counter/ directory, I would suggest putting it in drivers/counter/counter-chrdev.h instead of include/linux/counter.h.

#12 lore raw

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

Hi David,

I agree with your suggested changes -- just a couple select comments following below.

Quoted 3 lines · David Lechner
Quoted 3 lines
drivers/counter/counter-chrdev.c new file +3 -0
+static int counter_add_watch(struct counter_device *const counter,
+			     const unsigned long arg)
+{

[...]

Quoted 9 lines

drivers/counter/counter-chrdev.c line 250

Quoted 3 lines
drivers/counter/counter-chrdev.c new file +3 -0
+
+dummy_component:
+	comp_node.component = watch.component;

In my experiments, I added a events_validate driver callback here to validate each event as it is added. This way the user can know exactly which event caused the problem rather than waiting for the event_config callback.

Yes, this is a good idea and I have use for this in the 104-quad-8 driver as well. I'm going to name this "watch_validate" however, because I need to validate the requested channel as well as the requested event here (both part of the struct counter_watch).

Quoted 17 lines
Quoted 17 lines
include/linux/counter.h +11 -1
index 3f3f8ba6c1b4..98cd7c035968 100644
--- a/include/linux/counter.h
 
 
 
+/**
+ * struct counter_event_node - Counter Event node
+ * @l:		list of current watching Counter events
+ * @event:	event that triggers
+ * @channel:	event channel
+ * @comp_list:	list of components to watch when event triggers
+ */
+struct counter_event_node {
+	struct list_head l;
+	u8 event;
+	u8 channel;
Quoted 8 lines

include/linux/counter.h struct counter_count { · line 187

Quoted 3 lines
include/linux/counter.h +3 -0
+	struct list_head comp_list;
+};
+

Unless this is needed outside of the drivers/counter/ directory, I would suggest putting it in drivers/counter/counter-chrdev.h instead of include/linux/counter.h.

The "events_list" member of the struct counter_device is a list of struct counter_event_node. The events_configure() callback should parse through this list to determine the current events configuration request. As such, driver authors will need this structure available via include/linux/counter.h so they can parse "events_list".

William Breathitt Gray

#13 lore raw

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

In reply to William Breathitt Gray
Quoted 11 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.

Cc David Lechner <[email protected]>
Cc Gwendal Grignou <[email protected]>
Cc Dan Carpenter <[email protected]>

---

Hello William,

the series looks quite interesting, we have some thoughts... see below:

[...]

Quoted 37 lines
drivers/counter/counter-chrdev.c new file +37 -0
+/**
+ * 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 = 0;
+
+	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;

drivers/counter/counter-chrdev.c line 465

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+
+	/* 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);
+		if (err)
+			goto exit_early;
+
+		ev.watch.component = comp_node->component;
+
+		copied += kfifo_put(&counter->events, ev);

We want to calculate the frequency of some IRQ pulses in user space and counter values with time stamps really fits well here. As the pulses are from a physical system (rotating wheel), they will only change at a certain rate. We want to have the possibility to read from the counter device less often, we intentionally want to skip (meaning miss) events.

When reading we're interested in the newest events. The kfifo implements a "tail" drop FIFO, which means new values are added at the end, and if the FIFO is full, they are dropped. We need a "head" drop FIFO which discards the oldest events, keeping only the recent ones.

As far as we know, kfifo doesn't offer a head drop mode, but I think this can be added.

[...]

include/linux/counter.h struct counter_device { · line 277

Quoted 11 lines

struct counter_device {

include/linux/counter.h +5 -0
 	const char *name;
@@ -270,12 +270,20 @@ struct counter_device {
 
 	int id;
 	struct device dev;
+	struct cdev chrdev;
+	raw_spinlock_t events_lock;
+	struct list_head events_list;
+	struct list_head next_events_list;
+	DECLARE_KFIFO(events, struct counter_event, 64);

Do you plan to make the size of the FIFO configurable?

regards,
Oleksij & Marc

Signature · 9 lines
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

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

Quoted 73 lines · Oleksij Rempel
Quoted 11 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.

Cc David Lechner <[email protected]>
Cc Gwendal Grignou <[email protected]>
Cc Dan Carpenter <[email protected]>

---

Hello William,

the series looks quite interesting, we have some thoughts... see below:

[...]

Quoted 37 lines
drivers/counter/counter-chrdev.c new file +37 -0
+/**
+ * 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 = 0;
+
+	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;

drivers/counter/counter-chrdev.c line 465

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+
+	/* 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);
+		if (err)
+			goto exit_early;
+
+		ev.watch.component = comp_node->component;
+
+		copied += kfifo_put(&counter->events, ev);

We want to calculate the frequency of some IRQ pulses in user space and counter values with time stamps really fits well here. As the pulses are from a physical system (rotating wheel), they will only change at a certain rate. We want to have the possibility to read from the counter device less often, we intentionally want to skip (meaning miss) events.

Quoted 7 lines

When reading we're interested in the newest events. The kfifo implements a "tail" drop FIFO, which means new values are added at the end, and if the FIFO is full, they are dropped. We need a "head" drop FIFO which discards the oldest events, keeping only the recent ones.

As far as we know, kfifo doesn't offer a head drop mode, but I think this can be added.

I'm not sure if kfifo has this kind of mode, but it seems like something that should be there if it is not already -- I imagine this kind of operation mode would be pretty common. Perhaps someone knows how to achieve this and will share here.

Quoted 17 lines

[...]

include/linux/counter.h struct counter_device { · line 277

Quoted 11 lines

struct counter_device {

include/linux/counter.h +5 -0
 	const char *name;
@@ -270,12 +270,20 @@ struct counter_device {
 
 	int id;
 	struct device dev;
+	struct cdev chrdev;
+	raw_spinlock_t events_lock;
+	struct list_head events_list;
+	struct list_head next_events_list;
+	DECLARE_KFIFO(events, struct counter_event, 64);

Do you plan to make the size of the FIFO configurable?

Quoted 2 lines

regards,
Oleksij & Marc

I suppose it wouldn't be a problem to make this a configurable setting; I think I can implement this via kfifo_alloc() and kfifo_free(). How would users control this -- maybe using a sysfs attribute?

William Breathitt Gray

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

#15 lore raw

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

Quoted 79 lines · On Thu, 21 Jan 2021 17:03:11 +0900 William Breathitt Gray…
Quoted 78 lines · Oleksij Rempel
Quoted 11 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.

Cc David Lechner <[email protected]>
Cc Gwendal Grignou <[email protected]>
Cc Dan Carpenter <[email protected]>

---

Hello William,

the series looks quite interesting, we have some thoughts... see below:

[...]

Quoted 37 lines
drivers/counter/counter-chrdev.c new file +37 -0
+/**
+ * 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 = 0;
+
+	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;

drivers/counter/counter-chrdev.c line 465

Quoted 10 lines
drivers/counter/counter-chrdev.c new file +10 -0
+
+	/* 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);
+		if (err)
+			goto exit_early;
+
+		ev.watch.component = comp_node->component;
+
+		copied += kfifo_put(&counter->events, ev);  

We want to calculate the frequency of some IRQ pulses in user space and counter values with time stamps really fits well here. As the pulses are from a physical system (rotating wheel), they will only change at a certain rate. We want to have the possibility to read from the counter device less often, we intentionally want to skip (meaning miss) events.

When reading we're interested in the newest events. The kfifo implements a "tail" drop FIFO, which means new values are added at the end, and if the FIFO is full, they are dropped. We need a "head" drop FIFO which discards the oldest events, keeping only the recent ones.

Quoted 8 lines
Quoted 2 lines

As far as we know, kfifo doesn't offer a head drop mode, but I think this can be added.

I'm not sure if kfifo has this kind of mode, but it seems like something that should be there if it is not already -- I imagine this kind of operation mode would be pretty common. Perhaps someone knows how to achieve this and will share here.

I don't think it does. In IIO we started with a ring (effectively this)
- hopefully no one else remembers the horrible mess I made of it ;), but eventually decided that there were very few usecases that actually demanded it so just switched over to kfifo.

It's actually pretty rare that you aren't better off draining to a backing store of some type.

Not had any demand to bring a ring back to IIO since we dropped it.

Quoted 17 lines
Quoted 17 lines

[...]

include/linux/counter.h struct counter_device { · line 277

Quoted 11 lines

struct counter_device {

include/linux/counter.h +5 -0
 	const char *name;
@@ -270,12 +270,20 @@ struct counter_device {
 
 	int id;
 	struct device dev;
+	struct cdev chrdev;
+	raw_spinlock_t events_lock;
+	struct list_head events_list;
+	struct list_head next_events_list;
+	DECLARE_KFIFO(events, struct counter_event, 64);  

Do you plan to make the size of the FIFO configurable?

Quoted 6 lines
Quoted 2 lines

regards,
Oleksij & Marc

I suppose it wouldn't be a problem to make this a configurable setting; I think I can implement this via kfifo_alloc() and kfifo_free(). How would users control this -- maybe using a sysfs attribute?

Would make sense to do it from sysfs.

Jonathan

Quoted 3 lines

William Breathitt Gray

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

#16 lore raw

PATCH v6 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   |   9 +
 Documentation/driver-api/generic-counter.rst  | 231 ++++++++++++++----
 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 3 files changed, 200 insertions(+), 41 deletions(-)
Documentation/ABI/testing/sysfs-bus-counter +9 -0
index 1820ce2f9183..c8abe7c03a82 100644
@@ -99,6 +99,15 @@ 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/function
 KernelVersion:	5.2
 Contact:	[email protected]
Documentation/driver-api/generic-counter.rst +190 -41

258 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.29.2
#17 lore raw

PATCH v6 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                  | 306 ++++++++++++++----
 drivers/counter/Kconfig                       |   6 +-
 3 files changed, 273 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 +238 -68

800 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.29.2
reviewer
#18 lore raw
In reply to William Breathitt Gray
Quoted 18 lines · William Breathitt Gray

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 implicitly cast these values via the driver callback parameters.

This question is primarily addressed to David Lechner. I'm somewhat confused about how this setup would look in device drivers. I've gone ahead and refactored the code to support u32 enums, and pushed it to a separate branch on my repository called counter_chrdev_v6_u32_enum:
https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v6_u32_enum

Quoted 4 lines

Please check it out and let me know what you think. Is this the support you had in mind? I'm curious to see an example of how would your driver callback functions would look in this case. If everything works out fine, then I'll submit this branch as v7 of this patchset.

I haven't had time to look at this in depth, but just superficially looking at it, it is mostly there. The driver callback would just use the enum type in place of u32. For example:

static int ti_eqep_function_write(struct counter_device *counter,
struct counter_count *count, enum counter_function function)

and the COUNTER_FUNCTION_* constants would be defined as:

enum counter_function {
	COUNTER_FUNCTION_INCREASE,
	...
};

instead of using #define macros.

One advantage I see to using u8, at least in the user API data structures, is that it increases the number of events that fit in the kfifo buffer by a significant factor.

And that is not to say that we couldn't do both: have the user API structs use u8 for enum values and still use u32/strong enum types internally in the callback functions.

Quoted 7 lines

2. How should we handle "raw" timestamps?

Ahmad Fatoum brought up the possibility of returning "raw" timestamps similar to what the network stack offers (see the network stack SOF_TIMESTAMPING_{RAW,SYS}_HARDWARE support).

Quoted 6 lines

I'm not very familiar with the networking stack code, but if I understand correctly the SOF_TIMESTAMPING_RAW_HARDWARE timestamps are values returned from the device. If so, I suspect we would be able to support these "raw" timestamps by defining them as Counter Extensions and returning them in struct counter_event elements similar to the other Extension values.

Is nanosecond resolution good enough? In the TI eQEP driver I considered returning the raw timer value, but quickly realized that it would not be very nice to expect the user code to know the clock rate of the timer. It was very easy to get the clock rate in the kernel and just convert the timer value to nanoseconds before returning it to userspace.

So if there is some specialized case where it can be solved no other way besides using raw timestamps, then sure, include it. Otherwise I think we should stick with nanoseconds for time values when possible.

#19 lore raw
Quoted 41 lines · David Lechner
Quoted 18 lines · William Breathitt Gray

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 implicitly cast these values via the driver callback parameters.

This question is primarily addressed to David Lechner. I'm somewhat confused about how this setup would look in device drivers. I've gone ahead and refactored the code to support u32 enums, and pushed it to a separate branch on my repository called counter_chrdev_v6_u32_enum:
https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v6_u32_enum

Quoted 4 lines

Please check it out and let me know what you think. Is this the support you had in mind? I'm curious to see an example of how would your driver callback functions would look in this case. If everything works out fine, then I'll submit this branch as v7 of this patchset.

I haven't had time to look at this in depth, but just superficially looking at it, it is mostly there. The driver callback would just use the enum type in place of u32. For example:

static int ti_eqep_function_write(struct counter_device *counter,
struct counter_count *count, enum counter_function function)

and the COUNTER_FUNCTION_* constants would be defined as:

enum counter_function {
	COUNTER_FUNCTION_INCREASE,
	...
};

instead of using #define macros.

Quoted 7 lines

One advantage I see to using u8, at least in the user API data structures, is that it increases the number of events that fit in the kfifo buffer by a significant factor.

And that is not to say that we couldn't do both: have the user API structs use u8 for enum values and still use u32/strong enum types internally in the callback functions.

I'm including David Laight because he initially opposed enums in favor of fixed size types when we discussed this in an earlier revision:
https://lkml.org/lkml/2020/5/3/159

However, there have been significant changes to this patchset so the context now is different than those earlier discussions (i.e. we're no longer discussing ioctl calls).

I think reimplementing these constants as enums as described could work. If we do so, should the enum constants be given specific values? For example:

enum counter_function {
COUNTER_FUNCTION_INCREASE = 0,
COUNTER_FUNCTION_DECREASE = 1,
...
};

Quoted 21 lines
Quoted 8 lines

2. How should we handle "raw" timestamps?

Ahmad Fatoum brought up the possibility of returning "raw" timestamps similar to what the network stack offers (see the network stack SOF_TIMESTAMPING_{RAW,SYS}_HARDWARE support).

Quoted 6 lines

I'm not very familiar with the networking stack code, but if I understand correctly the SOF_TIMESTAMPING_RAW_HARDWARE timestamps are values returned from the device. If so, I suspect we would be able to support these "raw" timestamps by defining them as Counter Extensions and returning them in struct counter_event elements similar to the other Extension values.

Is nanosecond resolution good enough? In the TI eQEP driver I considered returning the raw timer value, but quickly realized that it would not be very nice to expect the user code to know the clock rate of the timer. It was very easy to get the clock rate in the kernel and just convert the timer value to nanoseconds before returning it to userspace.

Quoted 3 lines

So if there is some specialized case where it can be solved no other way besides using raw timestamps, then sure, include it. Otherwise I think we should stick with nanoseconds for time values when possible.

Given that the struct counter_event 'timestamp' member serves as the identification vessel for correlating component values to a single event (i.e. component values of a given event will share the same unique timestamp), I believe it's prudent to standardize this timestamp format on the kernel monotonic time as we have currently done so via our ktime_get_ns() call.

There are cases where it is understandably better to use a timestamp provided directly by the hardware (e.g. keeping timestamping close to data collection). For these cases, we can retrieve these "raw" timestamps via a Counter Extension: users would get their "raw" timestamp via the struct counter_event 'value' member, and just treat the 'timestamp' member as a unique event identification number.

William Breathitt Gray

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

reviewer
#20 lore raw
Quoted 62 lines · William Breathitt Gray
Quoted 41 lines · David Lechner
Quoted 18 lines · William Breathitt Gray

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 implicitly cast these values via the driver callback parameters.

This question is primarily addressed to David Lechner. I'm somewhat confused about how this setup would look in device drivers. I've gone ahead and refactored the code to support u32 enums, and pushed it to a separate branch on my repository called counter_chrdev_v6_u32_enum:
https://gitlab.com/vilhelmgray/iio/-/tree/counter_chrdev_v6_u32_enum

Quoted 4 lines

Please check it out and let me know what you think. Is this the support you had in mind? I'm curious to see an example of how would your driver callback functions would look in this case. If everything works out fine, then I'll submit this branch as v7 of this patchset.

I haven't had time to look at this in depth, but just superficially looking at it, it is mostly there. The driver callback would just use the enum type in place of u32. For example:

static int ti_eqep_function_write(struct counter_device *counter,
struct counter_count *count, enum counter_function function)

and the COUNTER_FUNCTION_* constants would be defined as:

enum counter_function {
	COUNTER_FUNCTION_INCREASE,
	...
};

instead of using #define macros.

Quoted 7 lines

One advantage I see to using u8, at least in the user API data structures, is that it increases the number of events that fit in the kfifo buffer by a significant factor.

And that is not to say that we couldn't do both: have the user API structs use u8 for enum values and still use u32/strong enum types internally in the callback functions.

I'm including David Laight because he initially opposed enums in favor of fixed size types when we discussed this in an earlier revision:
https://lkml.org/lkml/2020/5/3/159

However, there have been significant changes to this patchset so the context now is different than those earlier discussions (i.e. we're no longer discussing ioctl calls).

I think reimplementing these constants as enums as described could work. If we do so, should the enum constants be given specific values? For example:

Quoted 5 lines

enum counter_function {
COUNTER_FUNCTION_INCREASE = 0,
COUNTER_FUNCTION_DECREASE = 1,
...
};

I would say no on the explicit values since they don't have any significant meaning.