2021-02-15 10:41:50

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 00/24] iio: core,buffer: add support for multiple IIO buffers per IIO device

Changelog v5 -> v6:
* https://lore.kernel.org/linux-iio/[email protected]/T/#t
* merged series 'iio: kfifo: define a devm_iio_kfifo_buffer_setup helper' into this
https://lore.kernel.org/linux-iio/[email protected]/T/#u
* merged patch 'iio: buffer-dma,adi-axi-adc: introduce devm_iio_dmaengine_buffer_setup()' into this
https://lore.kernel.org/linux-iio/[email protected]/T/#u
* patch 'iio: core: wrap iio device & buffer into struct for character devices'
- moved kfree(ib) to be first in iio_chrdev_release() to make the cleanup
order be reversed as the open() order
* patch 'iio: buffer: group attr count and attr alloc'
- sizeof(struct attribute *) -> sizeof(*attr) in kcalloc()
* patch 'iio: core: merge buffer/ & scan_elements/ attributes'
- minor rework in iio_buffer_register_legacy_sysfs_groups() to
use more 'sizeof(*attrs)' in allocations
- fixed typo 'legacy_buffer_el_group' -> 'legacy_buffer_group'
- updated comment about
'/* we only need to link the legacy buffer groups for the first buffer */'
to
'/* we only need to register the legacy groups for the first buffer */'
* patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
- removed 'buf_name' from iio_device_buffer_getfd();
passing "iio:buffer" name; since it should be a class-name
- removed extra-line in drivers/iio/industrial-core.c
- changed init order in iio_device_buffer_getfd() and matched it with reverse in
iio_buffer_chrdev_release()
- calling put_unused_fd() in iio_device_buffer_getfd() if copy_to_user() fails
* added 'iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls'
* patch ' iio: buffer: introduce support for attaching more IIO buffers'
- now handling iio_device_attach_buffer() error returns
- rename iio_buffer_free_sysfs_and_mask() -> iio_buffers_free_sysfs_and_mask()
- rename iio_buffer_alloc_sysfs_and_mask() -> iio_buffer_alloc_sysfs_and_mask()
* patch 'tools: iio: convert iio_generic_buffer to use new IIO buffer API'
- removed extra close() if getfd() ioctl() errors out
- added comment that the sanity check for buffer0 should not be done
under normal operation
- update message from
"This device does not support buffers"
to
"Device does not have this many buffers"

Changelog v4 -> v5:
* https://lore.kernel.org/linux-iio/[email protected]/T/#t
* patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
don't return -EBUSY in iio_buffer_poll_wrapper(); return 0
__poll_t is unsigned, so returning 0 is the best we can do
Reported-by: kernel test robot <[email protected]>
* patch 'iio: buffer: dmaengine: obtain buffer object from attribute'
removed unused 'indio_dev' variable; seems i missed this initially
* patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
call 'wake_up(buffer->pollq)' in iio_buffer_chrdev_release()

Changelog v3 -> v4:
* https://lore.kernel.org/linux-iio/[email protected]/
* patch 'docs: ioctl-number.rst: reserve IIO subsystem ioctl() space'
remove 'uapi/' from `uapi/linux/iio/*.h`
* patch 'iio: core: register chardev only if needed'
add commit comment about potentially breaking userspace ABI with chardev removal
* patch 'iio: core: rework iio device group creation'
remove NULL re-init in iio_device_unregister_sysfs() ; memory is being free'd
* patch 'iio: buffer: group attr count and attr alloc'
extend commit comment about the 2 or 1 buffer directores
* patch 'iio: core: merge buffer/ & scan_elements/ attributes'
fixed static checker complaints
- removed unused global
- initialize omitted 'ret = -ENOMEM' on error path
- made iio_buffer_unregister_legacy_sysfs_groups() static
* patch 'iio: buffer: wrap all buffer attributes into iio_dev_attr'
- update some omitted unwindings; seems i forgot a few originally
this was showing up when trying to read from buffer1
* add patch 'iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc func'
* patch 'iio: buffer: introduce support for attaching more IIO buffers'
- removed 'iio_dev_opaque->attached_buffers = NULL' after kfree()
- using 'iio_dev_opaque->attached_buffers_cnt' to check that we have buffers
instead of checking 'indio_dev->buffer'
* patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
- replaced -ENOENT with -ENODEV when buffer index is out of range
* add 'iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()'
* add 'iio: buffer: dmaengine: obtain buffer object from attribute'
* add tools/iio patches for new multibuffer logic
tools: iio: make iioutils_get_type() private in iio_utils
tools: iio: privatize globals and functions in iio_generic_buffer.c file
tools: iio: convert iio_generic_buffer to use new IIO buffer API

Changelog v2 -> v3:
* https://lore.kernel.org/linux-iio/[email protected]/
* added commit 'docs: ioctl-number.rst: reserve IIO subsystem ioctl() space'
reserving 'i' 0x90-0x9F ioctls for IIO
I did not see any conflicts with others (in the doc)
- related to this, the new IIO_BUFFER_GET_FD_IOCTL is now at 'i' 0x91
* changed approach for creating sysfs buffer directories;
- they are now created as groups on the IIO device; that also means
that the groups array needs to be krealloc-ed and assign later in
the registration
- merged bufferX/ and scan_elementsX/ directories into a single
bufferX/ directory
- for legacy the buffer/ & scan_elements/ directories are kept; but
they're groups objects have been moved on the iio_dev_opaque object
- internally, the iio_dev_attr type is being extended to hold a
reference for an IIO buffer;
= this is great for scan_elements attributes
= and for the rest of the iio_buffer attributes, it means we need to
wrap them into iio_dev_attr

Changelog v1 -> v2:
* https://lore.kernel.org/linux-iio/[email protected]/
* 'iio: buffer: rework buffer & scan_elements dir creation'
add more doc-strings detailing the reasoning for this change
* 'iio: buffer: re-route scan_elements via it's kobj_type'
move list_del() before the kfree()'s in the list destruction
* 'iio: buffer: introduce support for attaching more IIO buffers'
- changed to 'cnt' variable vs re-using the 'i' for unwinding in
iio_buffer_alloc_sysfs_and_mask()
- removed kfree(old) in iio_device_attach_buffer()
- made iio_device_attach_buffer() an int return; this means that some
follow up patches are needed to make this return value be used;
* 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
- tested ioctl() with a simple C program; attached to comment;
- changed 'i' variable usage to 'sz' for alloc
- changed logic for buffer0; returning FD 0; userspace should know
that the IIO_BUFFER_GET_FD_IOCTL call returns 0 for buffer0;
this is because I can't find a way to determine the FD of the
ioctl() in the kernel; duplicating an ioctl() for buffer0 is also bad;


Alexandru Ardelean (24):
iio: adc: ti_am335x_adc: remove omitted iio_kfifo_free()
iio: kfifo: add devm_iio_kfifo_buffer_setup() helper
iio: make use of devm_iio_kfifo_buffer_setup() helper
iio: accel: sca3000: use devm_iio_kfifo_buffer_setup() helper
iio: kfifo: un-export devm_iio_kfifo_allocate() function
iio: buffer-dma,adi-axi-adc: introduce
devm_iio_dmaengine_buffer_setup()
docs: ioctl-number.rst: reserve IIO subsystem ioctl() space
iio: core: register chardev only if needed
iio: core-trigger: make iio_device_register_trigger_consumer() an int
return
iio: core: rework iio device group creation
iio: buffer: group attr count and attr alloc
iio: core: merge buffer/ & scan_elements/ attributes
iio: add reference to iio buffer on iio_dev_attr
iio: buffer: wrap all buffer attributes into iio_dev_attr
iio: buffer: dmaengine: obtain buffer object from attribute
iio: core: wrap iio device & buffer into struct for character devices
iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc
iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls
iio: buffer: introduce support for attaching more IIO buffers
iio: buffer: add ioctl() to support opening extra buffers for IIO
device
iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()
tools: iio: make iioutils_get_type() private in iio_utils
tools: iio: privatize globals and functions in iio_generic_buffer.c
file
tools: iio: convert iio_generic_buffer to use new IIO buffer API

.../driver-api/driver-model/devres.rst | 3 +-
.../userspace-api/ioctl/ioctl-number.rst | 1 +
drivers/iio/accel/sca3000.c | 19 +-
drivers/iio/accel/ssp_accel_sensor.c | 14 +-
drivers/iio/adc/adi-axi-adc.c | 12 +-
drivers/iio/adc/ina2xx-adc.c | 14 +-
drivers/iio/adc/ti_am335x_adc.c | 24 +-
.../buffer/industrialio-buffer-dmaengine.c | 35 +-
.../buffer/industrialio-triggered-buffer.c | 10 +-
drivers/iio/buffer/kfifo_buf.c | 40 +-
.../cros_ec_sensors/cros_ec_sensors_core.c | 13 +-
drivers/iio/dummy/iio_simple_dummy_buffer.c | 68 +--
drivers/iio/gyro/ssp_gyro_sensor.c | 14 +-
drivers/iio/health/max30100.c | 16 +-
drivers/iio/health/max30102.c | 16 +-
drivers/iio/iio_core.h | 32 +-
drivers/iio/iio_core_trigger.h | 4 +-
.../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +-
.../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +-
.../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +-
drivers/iio/industrialio-buffer.c | 494 ++++++++++++++----
drivers/iio/industrialio-core.c | 113 +++-
drivers/iio/industrialio-event.c | 6 +-
drivers/iio/industrialio-trigger.c | 6 +-
drivers/iio/light/acpi-als.c | 12 +-
drivers/iio/light/apds9960.c | 16 +-
.../staging/iio/impedance-analyzer/ad5933.c | 23 +-
include/linux/iio/buffer-dmaengine.h | 7 +-
include/linux/iio/buffer.h | 4 +-
include/linux/iio/buffer_impl.h | 21 +-
include/linux/iio/iio-opaque.h | 14 +
include/linux/iio/iio.h | 5 -
include/linux/iio/kfifo_buf.h | 7 +-
include/linux/iio/sysfs.h | 3 +
include/uapi/linux/iio/buffer.h | 10 +
tools/iio/Makefile | 1 +
tools/iio/iio_generic_buffer.c | 153 ++++--
tools/iio/iio_utils.c | 18 +-
tools/iio/iio_utils.h | 8 +-
39 files changed, 856 insertions(+), 442 deletions(-)
create mode 100644 include/uapi/linux/iio/buffer.h

--
2.17.1


2021-02-15 10:42:05

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 04/24] iio: accel: sca3000: use devm_iio_kfifo_buffer_setup() helper

This change makes use of the devm_iio_kfifo_buffer_setup() helper, however
the unwind order is changed.
The life-time of the kfifo object is attached to the parent device object.
This is to make the driver a bit more consistent with the other IIO
drivers, even though (as it is now before this change) it shouldn't be a
problem.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/accel/sca3000.c | 19 +++----------------
1 file changed, 3 insertions(+), 16 deletions(-)

diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c
index 194738660523..467b5fcb81db 100644
--- a/drivers/iio/accel/sca3000.c
+++ b/drivers/iio/accel/sca3000.c
@@ -1272,20 +1272,6 @@ static int sca3000_write_event_config(struct iio_dev *indio_dev,
return ret;
}

-static int sca3000_configure_ring(struct iio_dev *indio_dev)
-{
- struct iio_buffer *buffer;
-
- buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
- indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
-
- return 0;
-}
-
static inline
int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)
{
@@ -1479,7 +1465,9 @@ static int sca3000_probe(struct spi_device *spi)
}
indio_dev->modes = INDIO_DIRECT_MODE;

- ret = sca3000_configure_ring(indio_dev);
+ ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &sca3000_ring_setup_ops);
if (ret)
return ret;

@@ -1493,7 +1481,6 @@ static int sca3000_probe(struct spi_device *spi)
if (ret)
return ret;
}
- indio_dev->setup_ops = &sca3000_ring_setup_ops;
ret = sca3000_clean_setup(st);
if (ret)
goto error_free_irq;
--
2.17.1

2021-02-15 10:42:41

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 03/24] iio: make use of devm_iio_kfifo_buffer_setup() helper

All drivers that already call devm_iio_kfifo_allocate() &
iio_device_attach_buffer() are simple to convert to
iio_device_attach_kfifo_buffer() in a single go.

This change does that; the unwind order is preserved.
What is important, is that the devm_iio_kfifo_buffer_setup() be called
after the indio_dev->modes is assigned, to make sure that
INDIO_BUFFER_SOFTWARE flag is set and not overridden by the assignment to
indio_dev->modes.

Also, the INDIO_BUFFER_SOFTWARE has been removed from the assignments of
'indio_dev->modes' because it is set by devm_iio_kfifo_buffer_setup().

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/accel/ssp_accel_sensor.c | 14 ++++-------
drivers/iio/adc/ina2xx-adc.c | 14 +++++------
drivers/iio/adc/ti_am335x_adc.c | 18 ++++-----------
.../cros_ec_sensors/cros_ec_sensors_core.c | 13 ++++-------
drivers/iio/gyro/ssp_gyro_sensor.c | 14 ++++-------
drivers/iio/health/max30100.c | 16 ++++++-------
drivers/iio/health/max30102.c | 16 ++++++-------
.../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +++++------
.../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +++++------
.../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +++++-------
drivers/iio/light/acpi-als.c | 12 ++++------
drivers/iio/light/apds9960.c | 16 ++++++-------
.../staging/iio/impedance-analyzer/ad5933.c | 23 ++++---------------
13 files changed, 74 insertions(+), 124 deletions(-)

diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
index 474477e91b5e..04dcb2b657ee 100644
--- a/drivers/iio/accel/ssp_accel_sensor.c
+++ b/drivers/iio/accel/ssp_accel_sensor.c
@@ -96,7 +96,6 @@ static int ssp_accel_probe(struct platform_device *pdev)
int ret;
struct iio_dev *indio_dev;
struct ssp_sensor_data *spd;
- struct iio_buffer *buffer;

indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
if (!indio_dev)
@@ -109,18 +108,15 @@ static int ssp_accel_probe(struct platform_device *pdev)

indio_dev->name = ssp_accel_device_name;
indio_dev->info = &ssp_accel_iio_info;
- indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = ssp_acc_channels;
indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
indio_dev->available_scan_masks = ssp_accel_scan_mask;

- buffer = devm_iio_kfifo_allocate(&pdev->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
-
- indio_dev->setup_ops = &ssp_accel_buffer_ops;
+ ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &ssp_accel_buffer_ops);
+ if (ret)
+ return ret;

platform_set_drvdata(pdev, indio_dev);

diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index b573ec60a8b8..2ae54258b221 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -953,7 +953,6 @@ static int ina2xx_probe(struct i2c_client *client,
{
struct ina2xx_chip_info *chip;
struct iio_dev *indio_dev;
- struct iio_buffer *buffer;
unsigned int val;
enum ina2xx_ids type;
int ret;
@@ -1017,7 +1016,7 @@ static int ina2xx_probe(struct i2c_client *client,
return ret;
}

- indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->modes = INDIO_DIRECT_MODE;
if (id->driver_data == ina226) {
indio_dev->channels = ina226_channels;
indio_dev->num_channels = ARRAY_SIZE(ina226_channels);
@@ -1028,13 +1027,12 @@ static int ina2xx_probe(struct i2c_client *client,
indio_dev->info = &ina219_info;
}
indio_dev->name = id->name;
- indio_dev->setup_ops = &ina2xx_setup_ops;

- buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
+ ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &ina2xx_setup_ops);
+ if (ret)
+ return ret;

return iio_device_register(indio_dev);
}
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index e946903b0993..855cc2d64ac8 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -385,24 +385,16 @@ static int tiadc_iio_buffered_hardware_setup(struct device *dev,
unsigned long flags,
const struct iio_buffer_setup_ops *setup_ops)
{
- struct iio_buffer *buffer;
int ret;

- buffer = devm_iio_kfifo_allocate(dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
-
- ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
- flags, indio_dev->name, indio_dev);
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ setup_ops);
if (ret)
return ret;

- indio_dev->setup_ops = setup_ops;
- indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
-
- return 0;
+ return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
+ flags, indio_dev->name, indio_dev);
}

static const char * const chan_name_ain[] = {
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
index c833ec0ef214..f8824afe595e 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -334,14 +334,11 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
* We can not use trigger here, as events are generated
* as soon as sample_frequency is set.
*/
- struct iio_buffer *buffer;
-
- buffer = devm_iio_kfifo_allocate(dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
- indio_dev->modes = INDIO_BUFFER_SOFTWARE;
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ NULL);
+ if (ret)
+ return ret;

ret = cros_ec_sensorhub_register_push_data(
sensor_hub, sensor_platform->sensor_num,
diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
index ac7c170a20de..46ed12771d2f 100644
--- a/drivers/iio/gyro/ssp_gyro_sensor.c
+++ b/drivers/iio/gyro/ssp_gyro_sensor.c
@@ -96,7 +96,6 @@ static int ssp_gyro_probe(struct platform_device *pdev)
int ret;
struct iio_dev *indio_dev;
struct ssp_sensor_data *spd;
- struct iio_buffer *buffer;

indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
if (!indio_dev)
@@ -109,18 +108,15 @@ static int ssp_gyro_probe(struct platform_device *pdev)

indio_dev->name = ssp_gyro_name;
indio_dev->info = &ssp_gyro_iio_info;
- indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = ssp_gyro_channels;
indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
indio_dev->available_scan_masks = ssp_gyro_scan_mask;

- buffer = devm_iio_kfifo_allocate(&pdev->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
-
- indio_dev->setup_ops = &ssp_gyro_buffer_ops;
+ ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &ssp_gyro_buffer_ops);
+ if (ret)
+ return ret;

platform_set_drvdata(pdev, indio_dev);

diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
index 38aa2030f3c6..36ba7611d9ce 100644
--- a/drivers/iio/health/max30100.c
+++ b/drivers/iio/health/max30100.c
@@ -418,7 +418,6 @@ static int max30100_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max30100_data *data;
- struct iio_buffer *buffer;
struct iio_dev *indio_dev;
int ret;

@@ -426,19 +425,18 @@ static int max30100_probe(struct i2c_client *client,
if (!indio_dev)
return -ENOMEM;

- buffer = devm_iio_kfifo_allocate(&client->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
-
indio_dev->name = MAX30100_DRV_NAME;
indio_dev->channels = max30100_channels;
indio_dev->info = &max30100_info;
indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
indio_dev->available_scan_masks = max30100_scan_masks;
- indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
- indio_dev->setup_ops = &max30100_buffer_setup_ops;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &max30100_buffer_setup_ops);
+ if (ret)
+ return ret;

data = iio_priv(indio_dev);
data->indio_dev = indio_dev;
diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index b35557a54ee2..2292876c55e2 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -506,7 +506,6 @@ static int max30102_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max30102_data *data;
- struct iio_buffer *buffer;
struct iio_dev *indio_dev;
int ret;
unsigned int reg;
@@ -515,16 +514,9 @@ static int max30102_probe(struct i2c_client *client,
if (!indio_dev)
return -ENOMEM;

- buffer = devm_iio_kfifo_allocate(&client->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
-
indio_dev->name = MAX30102_DRV_NAME;
indio_dev->info = &max30102_info;
- indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
- indio_dev->setup_ops = &max30102_buffer_setup_ops;
+ indio_dev->modes = INDIO_DIRECT_MODE;

data = iio_priv(indio_dev);
data->indio_dev = indio_dev;
@@ -549,6 +541,12 @@ static int max30102_probe(struct i2c_client *client,
return -ENODEV;
}

+ ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &max30102_buffer_setup_ops);
+ if (ret)
+ return ret;
+
data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(&client->dev, "regmap initialization failed\n");
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
index 3441b0d61c5d..383cc3250342 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
@@ -709,7 +709,6 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
const char *name;
struct inv_icm42600_timestamp *ts;
struct iio_dev *indio_dev;
- struct iio_buffer *buffer;
int ret;

name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name);
@@ -720,23 +719,22 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
if (!indio_dev)
return ERR_PTR(-ENOMEM);

- buffer = devm_iio_kfifo_allocate(dev);
- if (!buffer)
- return ERR_PTR(-ENOMEM);
-
ts = iio_priv(indio_dev);
inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));

iio_device_set_drvdata(indio_dev, st);
indio_dev->name = name;
indio_dev->info = &inv_icm42600_accel_info;
- indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = inv_icm42600_accel_channels;
indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels);
indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks;
- indio_dev->setup_ops = &inv_icm42600_buffer_ops;

- iio_device_attach_buffer(indio_dev, buffer);
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &inv_icm42600_buffer_ops);
+ if (ret)
+ return ERR_PTR(ret);

ret = devm_iio_device_register(dev, indio_dev);
if (ret)
diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
index aee7b9ff4bf4..cec1dd0e0464 100644
--- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
+++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
@@ -720,7 +720,6 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
const char *name;
struct inv_icm42600_timestamp *ts;
struct iio_dev *indio_dev;
- struct iio_buffer *buffer;
int ret;

name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
@@ -731,23 +730,23 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
if (!indio_dev)
return ERR_PTR(-ENOMEM);

- buffer = devm_iio_kfifo_allocate(dev);
- if (!buffer)
- return ERR_PTR(-ENOMEM);
-
ts = iio_priv(indio_dev);
inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));

iio_device_set_drvdata(indio_dev, st);
indio_dev->name = name;
indio_dev->info = &inv_icm42600_gyro_info;
- indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = inv_icm42600_gyro_channels;
indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks;
indio_dev->setup_ops = &inv_icm42600_buffer_ops;

- iio_device_attach_buffer(indio_dev, buffer);
+ ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &inv_icm42600_buffer_ops);
+ if (ret)
+ return ERR_PTR(ret);

ret = devm_iio_device_register(dev, indio_dev);
if (ret)
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index f1103ecedd64..16730a780964 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -739,20 +739,17 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {

int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
{
- struct iio_buffer *buffer;
- int i;
+ int i, ret;

for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
if (!hw->iio_devs[i])
continue;

- buffer = devm_iio_kfifo_allocate(hw->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(hw->iio_devs[i], buffer);
- hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
- hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
+ ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
+ INDIO_BUFFER_SOFTWARE,
+ &st_lsm6dsx_buffer_ops);
+ if (ret)
+ return ret;
}

return 0;
diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
index 2be7180e2cbf..f8e547fd35e7 100644
--- a/drivers/iio/light/acpi-als.c
+++ b/drivers/iio/light/acpi-als.c
@@ -165,7 +165,7 @@ static int acpi_als_add(struct acpi_device *device)
{
struct acpi_als *als;
struct iio_dev *indio_dev;
- struct iio_buffer *buffer;
+ int ret;

indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
if (!indio_dev)
@@ -179,15 +179,13 @@ static int acpi_als_add(struct acpi_device *device)

indio_dev->name = ACPI_ALS_DEVICE_NAME;
indio_dev->info = &acpi_als_info;
- indio_dev->modes = INDIO_BUFFER_SOFTWARE;
indio_dev->channels = acpi_als_channels;
indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);

- buffer = devm_iio_kfifo_allocate(&device->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
+ ret = devm_iio_kfifo_buffer_setup(&device->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE, NULL);
+ if (ret)
+ return ret;

return devm_iio_device_register(&device->dev, indio_dev);
}
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index df0647856e5d..4141c0fa7bc4 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -988,7 +988,6 @@ static int apds9960_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct apds9960_data *data;
- struct iio_buffer *buffer;
struct iio_dev *indio_dev;
int ret;

@@ -996,19 +995,18 @@ static int apds9960_probe(struct i2c_client *client,
if (!indio_dev)
return -ENOMEM;

- buffer = devm_iio_kfifo_allocate(&client->dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
-
indio_dev->info = &apds9960_info;
indio_dev->name = APDS9960_DRV_NAME;
indio_dev->channels = apds9960_channels;
indio_dev->num_channels = ARRAY_SIZE(apds9960_channels);
indio_dev->available_scan_masks = apds9960_scan_masks;
- indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
- indio_dev->setup_ops = &apds9960_buffer_setup_ops;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &apds9960_buffer_setup_ops);
+ if (ret)
+ return ret;

data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
index dba78896ea8f..793918e1c45f 100644
--- a/drivers/staging/iio/impedance-analyzer/ad5933.c
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
@@ -602,23 +602,6 @@ static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = {
.postdisable = ad5933_ring_postdisable,
};

-static int ad5933_register_ring_funcs_and_init(struct device *dev,
- struct iio_dev *indio_dev)
-{
- struct iio_buffer *buffer;
-
- buffer = devm_iio_kfifo_allocate(dev);
- if (!buffer)
- return -ENOMEM;
-
- iio_device_attach_buffer(indio_dev, buffer);
-
- /* Ring buffer functions - here trigger setup related */
- indio_dev->setup_ops = &ad5933_ring_setup_ops;
-
- return 0;
-}
-
static void ad5933_work(struct work_struct *work)
{
struct ad5933_state *st = container_of(work,
@@ -761,11 +744,13 @@ static int ad5933_probe(struct i2c_client *client,

indio_dev->info = &ad5933_info;
indio_dev->name = id->name;
- indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
+ indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ad5933_channels;
indio_dev->num_channels = ARRAY_SIZE(ad5933_channels);

- ret = ad5933_register_ring_funcs_and_init(&client->dev, indio_dev);
+ ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
+ INDIO_BUFFER_SOFTWARE,
+ &ad5933_ring_setup_ops);
if (ret)
return ret;

--
2.17.1

2021-02-15 10:43:12

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 05/24] iio: kfifo: un-export devm_iio_kfifo_allocate() function

At this point all drivers should use devm_iio_kfifo_buffer_setup() instead
of manually allocating via devm_iio_kfifo_allocate() and assigning ops and
modes.

With this change, the devm_iio_kfifo_allocate() will be made private to the
IIO core, since all drivers should call either
devm_iio_kfifo_buffer_setup() or devm_iio_triggered_buffer_setup() to
create a kfifo buffer.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
Documentation/driver-api/driver-model/devres.rst | 1 -
drivers/iio/buffer/kfifo_buf.c | 3 +--
include/linux/iio/kfifo_buf.h | 2 --
3 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 562f5722281e..4b15b3e9358b 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -285,7 +285,6 @@ I2C
IIO
devm_iio_device_alloc()
devm_iio_device_register()
- devm_iio_kfifo_allocate()
devm_iio_kfifo_buffer_setup()
devm_iio_triggered_buffer_setup()
devm_iio_trigger_alloc()
diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c
index 6472c9fa1937..c35a625280b1 100644
--- a/drivers/iio/buffer/kfifo_buf.c
+++ b/drivers/iio/buffer/kfifo_buf.c
@@ -186,7 +186,7 @@ static void devm_iio_kfifo_release(struct device *dev, void *res)
* RETURNS:
* Pointer to allocated iio_buffer on success, NULL on failure.
*/
-struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev)
+static struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev)
{
struct iio_buffer **ptr, *r;

@@ -204,7 +204,6 @@ struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev)

return r;
}
-EXPORT_SYMBOL(devm_iio_kfifo_allocate);

/**
* devm_iio_kfifo_buffer_setup - Allocate a kfifo buffer & attach it to an IIO device
diff --git a/include/linux/iio/kfifo_buf.h b/include/linux/iio/kfifo_buf.h
index 92c411b9ac26..1522896e1daf 100644
--- a/include/linux/iio/kfifo_buf.h
+++ b/include/linux/iio/kfifo_buf.h
@@ -10,8 +10,6 @@ struct device;
struct iio_buffer *iio_kfifo_allocate(void);
void iio_kfifo_free(struct iio_buffer *r);

-struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev);
-
int devm_iio_kfifo_buffer_setup(struct device *dev,
struct iio_dev *indio_dev,
int mode_flags,
--
2.17.1

2021-02-15 10:43:52

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 08/24] iio: core: register chardev only if needed

We only need a chardev if we need to support buffers and/or events.

With this change, a chardev will be created only if an IIO buffer is
attached OR an event_interface is configured.

Otherwise, no chardev will be created, and the IIO device will get
registered with the 'device_add()' call.

Quite a lot of IIO devices don't really need a chardev, so this is a minor
improvement to the IIO core, as the IIO device will take up (slightly)
fewer resources.

In order to not create a chardev, we mostly just need to not initialize the
indio_dev->dev.devt field. If that is un-initialized, cdev_device_add()
behaves like device_add().

This change has a small chance of breaking some userspace ABI, because it
removes un-needed chardevs. While these chardevs (that are being removed)
have always been unusable, it is likely that some scripts may check their
existence (for whatever logic).
And we also hope that before opening these chardevs, userspace would have
already checked for some pre-conditions to make sure that opening these
chardevs makes sense.
For the most part, there is also the hope that it would be easier to change
userspace code than revert this. But in the case that reverting this is
required, it should be easy enough to do it.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-core.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 121d5b098367..66eaefd46cf9 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1761,6 +1761,15 @@ static const struct file_operations iio_buffer_fileops = {
.release = iio_chrdev_release,
};

+static const struct file_operations iio_event_fileops = {
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = iio_ioctl,
+ .compat_ioctl = compat_ptr_ioctl,
+ .open = iio_chrdev_open,
+ .release = iio_chrdev_release,
+};
+
static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
{
int i, j;
@@ -1788,6 +1797,7 @@ static const struct iio_buffer_setup_ops noop_ring_setup_ops;

int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
const char *label;
int ret;

@@ -1807,9 +1817,6 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
if (ret < 0)
return ret;

- /* configure elements for the chrdev */
- indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
-
iio_device_register_debugfs(indio_dev);

ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
@@ -1838,9 +1845,15 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
indio_dev->setup_ops == NULL)
indio_dev->setup_ops = &noop_ring_setup_ops;

- cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
+ if (indio_dev->buffer)
+ cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
+ else if (iio_dev_opaque->event_interface)
+ cdev_init(&indio_dev->chrdev, &iio_event_fileops);

- indio_dev->chrdev.owner = this_mod;
+ if (indio_dev->buffer || iio_dev_opaque->event_interface) {
+ indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
+ indio_dev->chrdev.owner = this_mod;
+ }

ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
if (ret < 0)
--
2.17.1

2021-02-15 10:44:11

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 09/24] iio: core-trigger: make iio_device_register_trigger_consumer() an int return

Oddly enough the noop function is an int-return. This one seems to be void.
This change converts it to int, because we want to change how groups are
registered. With that change this function could error out with -ENOMEM.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/iio_core_trigger.h | 4 +++-
drivers/iio/industrialio-trigger.c | 4 +++-
2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h
index 374816bc3e73..e1a56824e07f 100644
--- a/drivers/iio/iio_core_trigger.h
+++ b/drivers/iio/iio_core_trigger.h
@@ -9,8 +9,10 @@
/**
* iio_device_register_trigger_consumer() - set up an iio_dev to use triggers
* @indio_dev: iio_dev associated with the device that will consume the trigger
+ *
+ * Return 0 if successful, negative otherwise
**/
-void iio_device_register_trigger_consumer(struct iio_dev *indio_dev);
+int iio_device_register_trigger_consumer(struct iio_dev *indio_dev);

/**
* iio_device_unregister_trigger_consumer() - reverse the registration process
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index ea3c9859b258..438d5012e8b8 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -692,10 +692,12 @@ int iio_trigger_validate_own_device(struct iio_trigger *trig,
}
EXPORT_SYMBOL(iio_trigger_validate_own_device);

-void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
+int iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
{
indio_dev->groups[indio_dev->groupcounter++] =
&iio_trigger_consumer_attr_group;
+
+ return 0;
}

void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev)
--
2.17.1

2021-02-15 10:44:38

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 10/24] iio: core: rework iio device group creation

Up until now, the device groups that an IIO device had were limited to 6.
Two of these groups would account for buffer attributes (the buffer/ and
scan_elements/ directories).

Since we want to add multiple buffers per IIO device, this number may not
be enough, when adding a second buffer. So, this change reallocates the
groups array whenever an IIO device group is added, via a
iio_device_register_sysfs_group() helper.

This also means that the groups array should be assigned to
'indio_dev.dev.groups' really late, right before {cdev_}device_add() is
called to do the entire setup.
And we also must take care to free this array when the sysfs resources are
being cleaned up.

With this change we can also move the 'groups' & 'groupcounter' fields to
the iio_dev_opaque object. Up until now, this didn't make a whole lot of
sense (especially since we weren't sure how multibuffer support would look
like in the end).
But doing it now kills one birds with one stone.

An alternative, would be to add a configurable Kconfig symbol
CONFIG_IIO_MAX_BUFFERS_PER_DEVICE (or something like that) and compute a
static maximum of the groups we can support per IIO device. But that would
probably annoy a few people since that would make the system less
configurable.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/iio_core.h | 3 +++
drivers/iio/industrialio-buffer.c | 12 ++++++++++--
drivers/iio/industrialio-core.c | 30 +++++++++++++++++++++++++++---
drivers/iio/industrialio-event.c | 5 ++++-
drivers/iio/industrialio-trigger.c | 6 ++----
include/linux/iio/iio-opaque.h | 4 ++++
include/linux/iio/iio.h | 5 -----
7 files changed, 50 insertions(+), 15 deletions(-)

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index fced02cadcc3..7d5b179c1fe7 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -46,6 +46,9 @@ int __iio_add_chan_devattr(const char *postfix,
struct list_head *attr_list);
void iio_free_chan_devattr_list(struct list_head *attr_list);

+int iio_device_register_sysfs_group(struct iio_dev *indio_dev,
+ const struct attribute_group *group);
+
ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);

/* Event interface flags */
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 2f7426a2f47c..cc846988fdb9 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1287,7 +1287,9 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
buffer->buffer_group.name = "buffer";
buffer->buffer_group.attrs = attr;

- indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group;
+ ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group);
+ if (ret)
+ goto error_free_buffer_attrs;

attrcount = 0;
INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
@@ -1330,14 +1332,20 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,

list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
- indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
+
+ ret = iio_device_register_sysfs_group(indio_dev, &buffer->scan_el_group);
+ if (ret)
+ goto error_free_scan_el_attrs;

return 0;

+error_free_scan_el_attrs:
+ kfree(buffer->scan_el_group.attrs);
error_free_scan_mask:
bitmap_free(buffer->scan_mask);
error_cleanup_dynamic:
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
+error_free_buffer_attrs:
kfree(buffer->buffer_group.attrs);

return ret;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 66eaefd46cf9..7b4f088b6efb 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1452,6 +1452,25 @@ static ssize_t iio_store_timestamp_clock(struct device *dev,
return len;
}

+int iio_device_register_sysfs_group(struct iio_dev *indio_dev,
+ const struct attribute_group *group)
+{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+ const struct attribute_group **new, **old = iio_dev_opaque->groups;
+ unsigned int cnt = iio_dev_opaque->groupcounter;
+
+ new = krealloc(old, sizeof(*new) * (cnt + 2), GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+
+ new[iio_dev_opaque->groupcounter++] = group;
+ new[iio_dev_opaque->groupcounter] = NULL;
+
+ iio_dev_opaque->groups = new;
+
+ return 0;
+}
+
static DEVICE_ATTR(current_timestamp_clock, S_IRUGO | S_IWUSR,
iio_show_timestamp_clock, iio_store_timestamp_clock);

@@ -1525,8 +1544,10 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
if (clk)
iio_dev_opaque->chan_attr_group.attrs[attrn++] = clk;

- indio_dev->groups[indio_dev->groupcounter++] =
- &iio_dev_opaque->chan_attr_group;
+ ret = iio_device_register_sysfs_group(indio_dev,
+ &iio_dev_opaque->chan_attr_group);
+ if (ret)
+ goto error_clear_attrs;

return 0;

@@ -1543,6 +1564,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
iio_free_chan_devattr_list(&iio_dev_opaque->channel_attr_list);
kfree(iio_dev_opaque->chan_attr_group.attrs);
iio_dev_opaque->chan_attr_group.attrs = NULL;
+ kfree(iio_dev_opaque->groups);
}

static void iio_dev_release(struct device *device)
@@ -1592,7 +1614,6 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
ALIGN(sizeof(struct iio_dev_opaque), IIO_ALIGN);

dev->dev.parent = parent;
- dev->dev.groups = dev->groups;
dev->dev.type = &iio_device_type;
dev->dev.bus = &iio_bus_type;
device_initialize(&dev->dev);
@@ -1855,6 +1876,9 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
indio_dev->chrdev.owner = this_mod;
}

+ /* assign device groups now; they should be all registered now */
+ indio_dev->dev.groups = iio_dev_opaque->groups;
+
ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
if (ret < 0)
goto error_unreg_eventset;
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 7e532117ac55..ea8947cc21e4 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -544,7 +544,10 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)
/* Add all elements from the list. */
list_for_each_entry(p, &ev_int->dev_attr_list, l)
ev_int->group.attrs[attrn++] = &p->dev_attr.attr;
- indio_dev->groups[indio_dev->groupcounter++] = &ev_int->group;
+
+ ret = iio_device_register_sysfs_group(indio_dev, &ev_int->group);
+ if (ret)
+ goto error_free_setup_event_lines;

ev_int->ioctl_handler.ioctl = iio_event_ioctl;
iio_device_ioctl_handler_register(&iio_dev_opaque->indio_dev,
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index 438d5012e8b8..a035d5c2a445 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -694,10 +694,8 @@ EXPORT_SYMBOL(iio_trigger_validate_own_device);

int iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
{
- indio_dev->groups[indio_dev->groupcounter++] =
- &iio_trigger_consumer_attr_group;
-
- return 0;
+ return iio_device_register_sysfs_group(indio_dev,
+ &iio_trigger_consumer_attr_group);
}

void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev)
diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h
index 07c5a8e52ca8..8ba13a5c7af6 100644
--- a/include/linux/iio/iio-opaque.h
+++ b/include/linux/iio/iio-opaque.h
@@ -12,6 +12,8 @@
* attributes
* @chan_attr_group: group for all attrs in base directory
* @ioctl_handlers: ioctl handlers registered with the core handler
+ * @groups: attribute groups
+ * @groupcounter: index of next attribute group
* @debugfs_dentry: device specific debugfs dentry
* @cached_reg_addr: cached register address for debugfs reads
* @read_buf: read buffer to be used for the initial reg read
@@ -24,6 +26,8 @@ struct iio_dev_opaque {
struct list_head channel_attr_list;
struct attribute_group chan_attr_group;
struct list_head ioctl_handlers;
+ const struct attribute_group **groups;
+ int groupcounter;
#if defined(CONFIG_DEBUG_FS)
struct dentry *debugfs_dentry;
unsigned cached_reg_addr;
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index e4a9822e6495..f8585d01fc76 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -518,8 +518,6 @@ struct iio_buffer_setup_ops {
* @setup_ops: [DRIVER] callbacks to call before and after buffer
* enable/disable
* @chrdev: [INTERN] associated character device
- * @groups: [INTERN] attribute groups
- * @groupcounter: [INTERN] index of next attribute group
* @flags: [INTERN] file ops related flags including busy flag.
* @priv: [DRIVER] reference to driver's private information
* **MUST** be accessed **ONLY** via iio_priv() helper
@@ -556,9 +554,6 @@ struct iio_dev {
struct mutex info_exist_lock;
const struct iio_buffer_setup_ops *setup_ops;
struct cdev chrdev;
-#define IIO_MAX_GROUPS 6
- const struct attribute_group *groups[IIO_MAX_GROUPS + 1];
- int groupcounter;

unsigned long flags;
void *priv;
--
2.17.1

2021-02-15 10:45:08

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 11/24] iio: buffer: group attr count and attr alloc

If we want to merge the attributes of the buffer/ and scan_elements/
directories, we'll need to count all attributes first, then (depending on
the attribute group) either allocate 2 attribute groups, or a single one.
Historically an IIO buffer was described by 2 subdirectories under
/sys/bus/iio/iio:devicesX (i.e. buffer/ and scan_elements/); these subdirs
were actually 2 separate attribute groups on the iio_buffer object.

Moving forward, if we want to allow more than one buffer per IIO device,
keeping 2 subdirectories for each IIO buffer is a bit cumbersome
(especially for userpace ABI). So, we will merge the attributes of these 2
subdirs under a /sys/bus/iio/iio:devicesX/bufferY subdirectory. To do this,
we need to count all attributes first, and then distribute them based on
which buffer this is. For the first buffer, we'll need to also allocate the
legacy 2 attribute groups (for buffer/ and scan_elements/), and also a
/sys/bus/iio/iio:devicesX/buffer0 attribute group.

For buffer1 and above, just a single attribute group will be allocated (the
merged one).

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-buffer.c | 71 ++++++++++++++++---------------
1 file changed, 37 insertions(+), 34 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index cc846988fdb9..01ab3dd0726a 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1257,41 +1257,16 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
{
struct iio_dev_attr *p;
struct attribute **attr;
- int ret, i, attrn, attrcount;
+ int ret, i, attrn, scan_el_attrcount, buffer_attrcount;
const struct iio_chan_spec *channels;

- attrcount = 0;
+ buffer_attrcount = 0;
if (buffer->attrs) {
- while (buffer->attrs[attrcount] != NULL)
- attrcount++;
+ while (buffer->attrs[buffer_attrcount] != NULL)
+ buffer_attrcount++;
}

- attr = kcalloc(attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
- sizeof(struct attribute *), GFP_KERNEL);
- if (!attr)
- return -ENOMEM;
-
- memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs));
- if (!buffer->access->set_length)
- attr[0] = &dev_attr_length_ro.attr;
-
- if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK)
- attr[2] = &dev_attr_watermark_ro.attr;
-
- if (buffer->attrs)
- memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
- sizeof(struct attribute *) * attrcount);
-
- attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL;
-
- buffer->buffer_group.name = "buffer";
- buffer->buffer_group.attrs = attr;
-
- ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group);
- if (ret)
- goto error_free_buffer_attrs;
-
- attrcount = 0;
+ scan_el_attrcount = 0;
INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
channels = indio_dev->channels;
if (channels) {
@@ -1304,7 +1279,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
&channels[i]);
if (ret < 0)
goto error_cleanup_dynamic;
- attrcount += ret;
+ scan_el_attrcount += ret;
if (channels[i].type == IIO_TIMESTAMP)
indio_dev->scan_index_timestamp =
channels[i].scan_index;
@@ -1319,9 +1294,37 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
}
}

+ attr = kcalloc(buffer_attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
+ sizeof(*attr), GFP_KERNEL);
+ if (!attr) {
+ ret = -ENOMEM;
+ goto error_free_scan_mask;
+ }
+
+ memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs));
+ if (!buffer->access->set_length)
+ attr[0] = &dev_attr_length_ro.attr;
+
+ if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK)
+ attr[2] = &dev_attr_watermark_ro.attr;
+
+ if (buffer->attrs)
+ memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
+ sizeof(struct attribute *) * buffer_attrcount);
+
+ buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs);
+ attr[buffer_attrcount] = NULL;
+
+ buffer->buffer_group.name = "buffer";
+ buffer->buffer_group.attrs = attr;
+
+ ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group);
+ if (ret)
+ goto error_free_buffer_attrs;
+
buffer->scan_el_group.name = iio_scan_elements_group_name;

- buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
+ buffer->scan_el_group.attrs = kcalloc(scan_el_attrcount + 1,
sizeof(buffer->scan_el_group.attrs[0]),
GFP_KERNEL);
if (buffer->scan_el_group.attrs == NULL) {
@@ -1341,12 +1344,12 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,

error_free_scan_el_attrs:
kfree(buffer->scan_el_group.attrs);
+error_free_buffer_attrs:
+ kfree(buffer->buffer_group.attrs);
error_free_scan_mask:
bitmap_free(buffer->scan_mask);
error_cleanup_dynamic:
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
-error_free_buffer_attrs:
- kfree(buffer->buffer_group.attrs);

return ret;
}
--
2.17.1

2021-02-15 10:45:12

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 07/24] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space

Currently, only the 'i' 0x90 ioctl() actually exists and is defined in
'include/uapi/linux/iio/events.h'.

It's the IIO_GET_EVENT_FD_IOCTL, which is used to retrieve and FD for
reading events from an IIO device.
We will want to add more ioct() numbers, so with this change the 'i'
0x90-0x9F space is reserved for IIO ioctl() calls.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
Documentation/userspace-api/ioctl/ioctl-number.rst | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index a4c75a28c839..179783aac8dd 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -245,6 +245,7 @@ Code Seq# Include File Comments
'i' 00-3F linux/i2o-dev.h conflict!
'i' 0B-1F linux/ipmi.h conflict!
'i' 80-8F linux/i8k.h
+'i' 90-9F `linux/iio/*.h` IIO
'j' 00-3F linux/joystick.h
'k' 00-0F linux/spi/spidev.h conflict!
'k' 00-05 video/kyro.h conflict!
--
2.17.1

2021-02-15 10:45:12

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 06/24] iio: buffer-dma,adi-axi-adc: introduce devm_iio_dmaengine_buffer_setup()

This change does a conversion of the devm_iio_dmaengine_buffer_alloc() to
devm_iio_dmaengine_buffer_setup(). This will allocate an IIO DMA buffer and
attach it to the IIO device, similar to devm_iio_triggered_buffer_setup()
(though the underlying code is different, the final logic is the same).

Since the only user of the devm_iio_dmaengine_buffer_alloc() was the
adi-axi-adc driver, this change does the replacement in a single go in the
driver.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
.../driver-api/driver-model/devres.rst | 1 +
drivers/iio/adc/adi-axi-adc.c | 12 ++-----
.../buffer/industrialio-buffer-dmaengine.c | 33 +++++++++++++++++--
include/linux/iio/buffer-dmaengine.h | 7 ++--
4 files changed, 38 insertions(+), 15 deletions(-)

diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst
index 4b15b3e9358b..5f8c6c303ff2 100644
--- a/Documentation/driver-api/driver-model/devres.rst
+++ b/Documentation/driver-api/driver-model/devres.rst
@@ -285,6 +285,7 @@ I2C
IIO
devm_iio_device_alloc()
devm_iio_device_register()
+ devm_iio_dmaengine_buffer_setup()
devm_iio_kfifo_buffer_setup()
devm_iio_triggered_buffer_setup()
devm_iio_trigger_alloc()
diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c
index 9109da2d2e15..2e84623f732e 100644
--- a/drivers/iio/adc/adi-axi-adc.c
+++ b/drivers/iio/adc/adi-axi-adc.c
@@ -104,7 +104,6 @@ static unsigned int adi_axi_adc_read(struct adi_axi_adc_state *st,
static int adi_axi_adc_config_dma_buffer(struct device *dev,
struct iio_dev *indio_dev)
{
- struct iio_buffer *buffer;
const char *dma_name;

if (!device_property_present(dev, "dmas"))
@@ -113,15 +112,8 @@ static int adi_axi_adc_config_dma_buffer(struct device *dev,
if (device_property_read_string(dev, "dma-names", &dma_name))
dma_name = "rx";

- buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent,
- dma_name);
- if (IS_ERR(buffer))
- return PTR_ERR(buffer);
-
- indio_dev->modes |= INDIO_BUFFER_HARDWARE;
- iio_device_attach_buffer(indio_dev, buffer);
-
- return 0;
+ return devm_iio_dmaengine_buffer_setup(indio_dev->dev.parent,
+ indio_dev, dma_name);
}

static int adi_axi_adc_read_raw(struct iio_dev *indio_dev,
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index b0cb9a35f5cd..9981896e1495 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -244,7 +244,7 @@ static void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res)
*
* The buffer will be automatically de-allocated once the device gets destroyed.
*/
-struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
+static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
const char *channel)
{
struct iio_buffer **bufferp, *buffer;
@@ -265,7 +265,36 @@ struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,

return buffer;
}
-EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_alloc);
+
+/**
+ * devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device
+ * @dev: Parent device for the buffer
+ * @indio_dev: IIO device to which to attach this buffer.
+ * @channel: DMA channel name, typically "rx".
+ *
+ * This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc()
+ * and attaches it to an IIO device with iio_device_attach_buffer().
+ * It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the
+ * IIO device.
+ */
+int devm_iio_dmaengine_buffer_setup(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel)
+{
+ struct iio_buffer *buffer;
+
+ buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent,
+ channel);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
+
+ indio_dev->modes |= INDIO_BUFFER_HARDWARE;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup);

MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
MODULE_DESCRIPTION("DMA buffer for the IIO framework");
diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h
index 5b502291d6a4..5c355be89814 100644
--- a/include/linux/iio/buffer-dmaengine.h
+++ b/include/linux/iio/buffer-dmaengine.h
@@ -7,10 +7,11 @@
#ifndef __IIO_DMAENGINE_H__
#define __IIO_DMAENGINE_H__

-struct iio_buffer;
+struct iio_dev;
struct device;

-struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev,
- const char *channel);
+int devm_iio_dmaengine_buffer_setup(struct device *dev,
+ struct iio_dev *indio_dev,
+ const char *channel);

#endif
--
2.17.1

2021-02-15 10:45:29

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 13/24] iio: add reference to iio buffer on iio_dev_attr

This change adds a reference to a 'struct iio_buffer' object on the
iio_dev_attr object. This way, we can use the created iio_dev_attr objects
on per-buffer basis (since they're allocated anyway).

A minor downside of this change is that the number of parameters on
__iio_add_chan_devattr() grows by 1. This looks like it could do with a bit
of a re-think.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/iio_core.h | 2 ++
drivers/iio/industrialio-buffer.c | 4 ++++
drivers/iio/industrialio-core.c | 6 ++++++
drivers/iio/industrialio-event.c | 1 +
include/linux/iio/sysfs.h | 3 +++
5 files changed, 16 insertions(+)

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index 7d5b179c1fe7..731f5170d5b9 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/device.h>

+struct iio_buffer;
struct iio_chan_spec;
struct iio_dev;

@@ -43,6 +44,7 @@ int __iio_add_chan_devattr(const char *postfix,
u64 mask,
enum iio_shared_by shared_by,
struct device *dev,
+ struct iio_buffer *buffer,
struct list_head *attr_list);
void iio_free_chan_devattr_list(struct list_head *attr_list);

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 76f0f6a61ebc..e6edec3bcb73 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -447,6 +447,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
0,
IIO_SEPARATE,
&indio_dev->dev,
+ buffer,
&buffer->scan_el_dev_attr_list);
if (ret)
return ret;
@@ -458,6 +459,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
0,
0,
&indio_dev->dev,
+ buffer,
&buffer->scan_el_dev_attr_list);
if (ret)
return ret;
@@ -470,6 +472,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
chan->scan_index,
0,
&indio_dev->dev,
+ buffer,
&buffer->scan_el_dev_attr_list);
else
ret = __iio_add_chan_devattr("en",
@@ -479,6 +482,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
chan->scan_index,
0,
&indio_dev->dev,
+ buffer,
&buffer->scan_el_dev_attr_list);
if (ret)
return ret;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 7b4f088b6efb..31596ae68664 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1114,6 +1114,7 @@ int __iio_add_chan_devattr(const char *postfix,
u64 mask,
enum iio_shared_by shared_by,
struct device *dev,
+ struct iio_buffer *buffer,
struct list_head *attr_list)
{
int ret;
@@ -1129,6 +1130,7 @@ int __iio_add_chan_devattr(const char *postfix,
goto error_iio_dev_attr_free;
iio_attr->c = chan;
iio_attr->address = mask;
+ iio_attr->buffer = buffer;
list_for_each_entry(t, attr_list, l)
if (strcmp(t->dev_attr.attr.name,
iio_attr->dev_attr.attr.name) == 0) {
@@ -1165,6 +1167,7 @@ static int iio_device_add_channel_label(struct iio_dev *indio_dev,
0,
IIO_SEPARATE,
&indio_dev->dev,
+ NULL,
&iio_dev_opaque->channel_attr_list);
if (ret < 0)
return ret;
@@ -1190,6 +1193,7 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev,
i,
shared_by,
&indio_dev->dev,
+ NULL,
&iio_dev_opaque->channel_attr_list);
if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
continue;
@@ -1226,6 +1230,7 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev,
i,
shared_by,
&indio_dev->dev,
+ NULL,
&iio_dev_opaque->channel_attr_list);
kfree(avail_postfix);
if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))
@@ -1322,6 +1327,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
i,
ext_info->shared,
&indio_dev->dev,
+ NULL,
&iio_dev_opaque->channel_attr_list);
i++;
if (ret == -EBUSY && ext_info->shared)
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index ea8947cc21e4..a30e289fc362 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -385,6 +385,7 @@ static int iio_device_add_event(struct iio_dev *indio_dev,

ret = __iio_add_chan_devattr(postfix, chan, show, store,
(i << 16) | spec_index, shared_by, &indio_dev->dev,
+ NULL,
&iio_dev_opaque->event_interface->dev_attr_list);
kfree(postfix);

diff --git a/include/linux/iio/sysfs.h b/include/linux/iio/sysfs.h
index b532c875bc24..e51fba66de4b 100644
--- a/include/linux/iio/sysfs.h
+++ b/include/linux/iio/sysfs.h
@@ -9,6 +9,7 @@
#ifndef _INDUSTRIAL_IO_SYSFS_H_
#define _INDUSTRIAL_IO_SYSFS_H_

+struct iio_buffer;
struct iio_chan_spec;

/**
@@ -17,12 +18,14 @@ struct iio_chan_spec;
* @address: associated register address
* @l: list head for maintaining list of dynamically created attrs
* @c: specification for the underlying channel
+ * @buffer: the IIO buffer to which this attribute belongs to (if any)
*/
struct iio_dev_attr {
struct device_attribute dev_attr;
u64 address;
struct list_head l;
struct iio_chan_spec const *c;
+ struct iio_buffer *buffer;
};

#define to_iio_dev_attr(_dev_attr) \
--
2.17.1

2021-02-15 10:47:12

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 12/24] iio: core: merge buffer/ & scan_elements/ attributes

With this change, we create a new directory for the IIO device called
buffer0, under which both the old buffer/ and scan_elements/ are stored.

This is done to simplify the addition of multiple IIO buffers per IIO
device. Otherwise we would need to add a bufferX/ and scan_elementsX/
directory for each IIO buffer.
With the current way of storing attribute groups, we can't have directories
stored under each other (i.e. scan_elements/ under buffer/), so the best
approach moving forward is to merge their attributes.

The old/legacy buffer/ & scan_elements/ groups are not stored on the opaque
IIO device object. This way the IIO buffer can have just a single
attribute_group object, saving a bit of memory when adding multiple IIO
buffers.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-buffer.c | 115 +++++++++++++++++++++++-------
include/linux/iio/buffer_impl.h | 9 +--
include/linux/iio/iio-opaque.h | 4 ++
3 files changed, 95 insertions(+), 33 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 01ab3dd0726a..76f0f6a61ebc 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1175,8 +1175,6 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
return (ret < 0) ? ret : len;
}

-static const char * const iio_scan_elements_group_name = "scan_elements";
-
static ssize_t iio_buffer_show_watermark(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1252,8 +1250,68 @@ static struct attribute *iio_buffer_attrs[] = {
&dev_attr_data_available.attr,
};

+static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev,
+ struct attribute **buffer_attrs,
+ int buffer_attrcount,
+ int scan_el_attrcount)
+{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+ struct attribute_group *group;
+ struct attribute **attrs;
+ int ret;
+
+ attrs = kcalloc(buffer_attrcount + 1, sizeof(*attrs), GFP_KERNEL);
+ if (!attrs)
+ return -ENOMEM;
+
+ memcpy(attrs, buffer_attrs, buffer_attrcount * sizeof(*attrs));
+
+ group = &iio_dev_opaque->legacy_buffer_group;
+ group->attrs = attrs;
+ group->name = "buffer";
+
+ ret = iio_device_register_sysfs_group(indio_dev, group);
+ if (ret)
+ goto error_free_buffer_attrs;
+
+ attrs = kcalloc(scan_el_attrcount + 1, sizeof(*attrs), GFP_KERNEL);
+ if (!attrs) {
+ ret = -ENOMEM;
+ goto error_free_buffer_attrs;
+ }
+
+ memcpy(attrs, &buffer_attrs[buffer_attrcount],
+ scan_el_attrcount * sizeof(*attrs));
+
+ group = &iio_dev_opaque->legacy_scan_el_group;
+ group->attrs = attrs;
+ group->name = "scan_elements";
+
+ ret = iio_device_register_sysfs_group(indio_dev, group);
+ if (ret)
+ goto error_free_scan_el_attrs;
+
+ return 0;
+
+error_free_buffer_attrs:
+ kfree(iio_dev_opaque->legacy_buffer_group.attrs);
+error_free_scan_el_attrs:
+ kfree(iio_dev_opaque->legacy_scan_el_group.attrs);
+
+ return ret;
+}
+
+static void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev)
+{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+
+ kfree(iio_dev_opaque->legacy_buffer_group.attrs);
+ kfree(iio_dev_opaque->legacy_scan_el_group.attrs);
+}
+
static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
- struct iio_dev *indio_dev)
+ struct iio_dev *indio_dev,
+ int index)
{
struct iio_dev_attr *p;
struct attribute **attr;
@@ -1294,8 +1352,8 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
}
}

- attr = kcalloc(buffer_attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
- sizeof(*attr), GFP_KERNEL);
+ attrn = buffer_attrcount + scan_el_attrcount + ARRAY_SIZE(iio_buffer_attrs);
+ attr = kcalloc(attrn + 1, sizeof(* attr), GFP_KERNEL);
if (!attr) {
ret = -ENOMEM;
goto error_free_scan_mask;
@@ -1313,37 +1371,38 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
sizeof(struct attribute *) * buffer_attrcount);

buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs);
- attr[buffer_attrcount] = NULL;
-
- buffer->buffer_group.name = "buffer";
- buffer->buffer_group.attrs = attr;

- ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group);
- if (ret)
- goto error_free_buffer_attrs;
+ attrn = buffer_attrcount;

- buffer->scan_el_group.name = iio_scan_elements_group_name;
+ list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
+ attr[attrn++] = &p->dev_attr.attr;

- buffer->scan_el_group.attrs = kcalloc(scan_el_attrcount + 1,
- sizeof(buffer->scan_el_group.attrs[0]),
- GFP_KERNEL);
- if (buffer->scan_el_group.attrs == NULL) {
+ buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index);
+ if (!buffer->buffer_group.name) {
ret = -ENOMEM;
- goto error_free_scan_mask;
+ goto error_free_buffer_attrs;
}
- attrn = 0;

- list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
- buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
+ buffer->buffer_group.attrs = attr;

- ret = iio_device_register_sysfs_group(indio_dev, &buffer->scan_el_group);
+ ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group);
if (ret)
- goto error_free_scan_el_attrs;
+ goto error_free_buffer_attr_group_name;
+
+ /* we only need to register the legacy groups for the first buffer */
+ if (index > 0)
+ return 0;
+
+ ret = iio_buffer_register_legacy_sysfs_groups(indio_dev, attr,
+ buffer_attrcount,
+ scan_el_attrcount);
+ if (ret)
+ goto error_free_buffer_attr_group_name;

return 0;

-error_free_scan_el_attrs:
- kfree(buffer->scan_el_group.attrs);
+error_free_buffer_attr_group_name:
+ kfree(buffer->buffer_group.name);
error_free_buffer_attrs:
kfree(buffer->buffer_group.attrs);
error_free_scan_mask:
@@ -1372,14 +1431,14 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
if (!buffer)
return 0;

- return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev);
+ return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 0);
}

static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
{
bitmap_free(buffer->scan_mask);
+ kfree(buffer->buffer_group.name);
kfree(buffer->buffer_group.attrs);
- kfree(buffer->scan_el_group.attrs);
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
}

@@ -1390,6 +1449,8 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
if (!buffer)
return;

+ iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
+
__iio_buffer_free_sysfs_and_mask(buffer);
}

diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index a63dc07b7350..3e555e58475b 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -100,14 +100,11 @@ struct iio_buffer {
/* @scan_el_dev_attr_list: List of scan element related attributes. */
struct list_head scan_el_dev_attr_list;

- /* @buffer_group: Attributes of the buffer group. */
- struct attribute_group buffer_group;
-
/*
- * @scan_el_group: Attribute group for those attributes not
- * created from the iio_chan_info array.
+ * @buffer_group: Attributes of the new buffer group.
+ * Includes scan elements attributes.
*/
- struct attribute_group scan_el_group;
+ struct attribute_group buffer_group;

/* @attrs: Standard attributes of the buffer. */
const struct attribute **attrs;
diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h
index 8ba13a5c7af6..1a9310b0145f 100644
--- a/include/linux/iio/iio-opaque.h
+++ b/include/linux/iio/iio-opaque.h
@@ -14,6 +14,8 @@
* @ioctl_handlers: ioctl handlers registered with the core handler
* @groups: attribute groups
* @groupcounter: index of next attribute group
+ * @legacy_scan_el_group: attribute group for legacy scan elements attribute group
+ * @legacy_buffer_group: attribute group for legacy buffer attributes group
* @debugfs_dentry: device specific debugfs dentry
* @cached_reg_addr: cached register address for debugfs reads
* @read_buf: read buffer to be used for the initial reg read
@@ -28,6 +30,8 @@ struct iio_dev_opaque {
struct list_head ioctl_handlers;
const struct attribute_group **groups;
int groupcounter;
+ struct attribute_group legacy_scan_el_group;
+ struct attribute_group legacy_buffer_group;
#if defined(CONFIG_DEBUG_FS)
struct dentry *debugfs_dentry;
unsigned cached_reg_addr;
--
2.17.1

2021-02-15 10:47:14

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 14/24] iio: buffer: wrap all buffer attributes into iio_dev_attr

This change wraps all buffer attributes into iio_dev_attr objects, and
assigns a reference to the IIO buffer they belong to.

With the addition of multiple IIO buffers per one IIO device, we need a way
to know which IIO buffer is being enabled/disabled/controlled.

We know that all buffer attributes are device_attributes. So we can wrap
them with a iio_dev_attr types. In the iio_dev_attr type, we can also hold
a reference to an IIO buffer.
So, we end up being able to allocate wrapped attributes for all buffer
attributes (even the one from other drivers).

The neat part with this mechanism, is that we don't need to add any extra
cleanup, because these attributes are being added to a dynamic list that
will get cleaned up via iio_free_chan_devattr_list().

With this change, the 'buffer->scan_el_dev_attr_list' list is being renamed
to 'buffer->buffer_attr_list', effectively merging (or finalizing the
merge) of the buffer/ & scan_elements/ attributes internally.

Accessing these new buffer attributes can now be done via
'to_iio_dev_attr(attr)->buffer' inside the show/store handlers.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-buffer.c | 76 ++++++++++++++++++++-----------
include/linux/iio/buffer_impl.h | 4 +-
2 files changed, 52 insertions(+), 28 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index e6edec3bcb73..8dc140f13b99 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -253,8 +253,7 @@ static ssize_t iio_scan_el_show(struct device *dev,
char *buf)
{
int ret;
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;

/* Ensure ret is 0 or 1. */
ret = !!test_bit(to_iio_dev_attr(attr)->address,
@@ -367,8 +366,8 @@ static ssize_t iio_scan_el_store(struct device *dev,
int ret;
bool state;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ struct iio_buffer *buffer = this_attr->buffer;

ret = strtobool(buf, &state);
if (ret < 0)
@@ -402,8 +401,7 @@ static ssize_t iio_scan_el_ts_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;

return sprintf(buf, "%d\n", buffer->scan_timestamp);
}
@@ -415,7 +413,7 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,
{
int ret;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
bool state;

ret = strtobool(buf, &state);
@@ -448,7 +446,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
IIO_SEPARATE,
&indio_dev->dev,
buffer,
- &buffer->scan_el_dev_attr_list);
+ &buffer->buffer_attr_list);
if (ret)
return ret;
attrcount++;
@@ -460,7 +458,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
0,
&indio_dev->dev,
buffer,
- &buffer->scan_el_dev_attr_list);
+ &buffer->buffer_attr_list);
if (ret)
return ret;
attrcount++;
@@ -473,7 +471,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
0,
&indio_dev->dev,
buffer,
- &buffer->scan_el_dev_attr_list);
+ &buffer->buffer_attr_list);
else
ret = __iio_add_chan_devattr("en",
chan,
@@ -483,7 +481,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
0,
&indio_dev->dev,
buffer,
- &buffer->scan_el_dev_attr_list);
+ &buffer->buffer_attr_list);
if (ret)
return ret;
attrcount++;
@@ -495,8 +493,7 @@ static ssize_t iio_buffer_read_length(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;

return sprintf(buf, "%d\n", buffer->length);
}
@@ -506,7 +503,7 @@ static ssize_t iio_buffer_write_length(struct device *dev,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
unsigned int val;
int ret;

@@ -538,8 +535,7 @@ static ssize_t iio_buffer_show_enable(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;

return sprintf(buf, "%d\n", iio_buffer_is_active(buffer));
}
@@ -1154,7 +1150,7 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
int ret;
bool requested_state;
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
bool inlist;

ret = strtobool(buf, &requested_state);
@@ -1183,8 +1179,7 @@ static ssize_t iio_buffer_show_watermark(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;

return sprintf(buf, "%u\n", buffer->watermark);
}
@@ -1195,7 +1190,7 @@ static ssize_t iio_buffer_store_watermark(struct device *dev,
size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
unsigned int val;
int ret;

@@ -1228,8 +1223,7 @@ static ssize_t iio_dma_show_data_available(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;

return sprintf(buf, "%zu\n", iio_buffer_data_available(buffer));
}
@@ -1254,6 +1248,26 @@ static struct attribute *iio_buffer_attrs[] = {
&dev_attr_data_available.attr,
};

+#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
+
+static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
+ struct attribute *attr)
+{
+ struct device_attribute *dattr = to_dev_attr(attr);
+ struct iio_dev_attr *iio_attr;
+
+ iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
+ if (!iio_attr)
+ return NULL;
+
+ iio_attr->buffer = buffer;
+ memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
+
+ list_add(&iio_attr->l, &buffer->buffer_attr_list);
+
+ return &iio_attr->dev_attr.attr;
+}
+
static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev,
struct attribute **buffer_attrs,
int buffer_attrcount,
@@ -1329,7 +1343,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
}

scan_el_attrcount = 0;
- INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
+ INIT_LIST_HEAD(&buffer->buffer_attr_list);
channels = indio_dev->channels;
if (channels) {
/* new magic */
@@ -1376,9 +1390,19 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,

buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs);

- attrn = buffer_attrcount;
+ for (i = 0; i < buffer_attrcount; i++) {
+ struct attribute *wrapped;
+
+ wrapped = iio_buffer_wrap_attr(buffer, attr[i]);
+ if (!wrapped) {
+ ret = -ENOMEM;
+ goto error_free_scan_mask;
+ }
+ attr[i] = wrapped;
+ }

- list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
+ attrn = 0;
+ list_for_each_entry(p, &buffer->buffer_attr_list, l)
attr[attrn++] = &p->dev_attr.attr;

buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index);
@@ -1412,7 +1436,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
error_free_scan_mask:
bitmap_free(buffer->scan_mask);
error_cleanup_dynamic:
- iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
+ iio_free_chan_devattr_list(&buffer->buffer_attr_list);

return ret;
}
@@ -1443,7 +1467,7 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
bitmap_free(buffer->scan_mask);
kfree(buffer->buffer_group.name);
kfree(buffer->buffer_group.attrs);
- iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
+ iio_free_chan_devattr_list(&buffer->buffer_attr_list);
}

void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index 3e555e58475b..41044320e581 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -97,8 +97,8 @@ struct iio_buffer {
/* @scan_timestamp: Does the scan mode include a timestamp. */
bool scan_timestamp;

- /* @scan_el_dev_attr_list: List of scan element related attributes. */
- struct list_head scan_el_dev_attr_list;
+ /* @buffer_attr_list: List of buffer attributes. */
+ struct list_head buffer_attr_list;

/*
* @buffer_group: Attributes of the new buffer group.
--
2.17.1

2021-02-15 10:47:39

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 16/24] iio: core: wrap iio device & buffer into struct for character devices

In order to keep backwards compatibility with the current chardev
mechanism, and in order to add support for multiple buffers per IIO device,
we need to pass both the IIO device & IIO buffer to the chardev.

This is particularly needed for the iio_buffer_read_outer() function, where
we need to pass another buffer object than 'indio_dev->buffer'.

Since we'll also open some chardevs via anon inodes, we can pass extra
buffers in that function by assigning another object to the
iio_dev_buffer_pair object.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/iio_core.h | 5 +++++
drivers/iio/industrialio-buffer.c | 10 ++++++----
drivers/iio/industrialio-core.c | 18 ++++++++++++++++--
3 files changed, 27 insertions(+), 6 deletions(-)

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index 731f5170d5b9..87868fff7d37 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -18,6 +18,11 @@ struct iio_dev;

extern struct device_type iio_device_type;

+struct iio_dev_buffer_pair {
+ struct iio_dev *indio_dev;
+ struct iio_buffer *buffer;
+};
+
#define IIO_IOCTL_UNHANDLED 1
struct iio_ioctl_handler {
struct list_head entry;
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 8dc140f13b99..83ea15e330a4 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -104,8 +104,9 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
size_t n, loff_t *f_ps)
{
- struct iio_dev *indio_dev = filp->private_data;
- struct iio_buffer *rb = indio_dev->buffer;
+ struct iio_dev_buffer_pair *ib = filp->private_data;
+ struct iio_buffer *rb = ib->buffer;
+ struct iio_dev *indio_dev = ib->indio_dev;
DEFINE_WAIT_FUNC(wait, woken_wake_function);
size_t datum_size;
size_t to_wait;
@@ -170,8 +171,9 @@ ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
__poll_t iio_buffer_poll(struct file *filp,
struct poll_table_struct *wait)
{
- struct iio_dev *indio_dev = filp->private_data;
- struct iio_buffer *rb = indio_dev->buffer;
+ struct iio_dev_buffer_pair *ib = filp->private_data;
+ struct iio_buffer *rb = ib->buffer;
+ struct iio_dev *indio_dev = ib->indio_dev;

if (!indio_dev->info || rb == NULL)
return 0;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 31596ae68664..1d500ea246af 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1703,13 +1703,24 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
{
struct iio_dev *indio_dev = container_of(inode->i_cdev,
struct iio_dev, chrdev);
+ struct iio_dev_buffer_pair *ib;

if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags))
return -EBUSY;

iio_device_get(indio_dev);

- filp->private_data = indio_dev;
+ ib = kmalloc(sizeof(*ib), GFP_KERNEL);
+ if (!ib) {
+ iio_device_put(indio_dev);
+ clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
+ return -ENOMEM;
+ }
+
+ ib->indio_dev = indio_dev;
+ ib->buffer = indio_dev->buffer;
+
+ filp->private_data = ib;

return 0;
}
@@ -1723,8 +1734,10 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
*/
static int iio_chrdev_release(struct inode *inode, struct file *filp)
{
+ struct iio_dev_buffer_pair *ib = filp->private_data;
struct iio_dev *indio_dev = container_of(inode->i_cdev,
struct iio_dev, chrdev);
+ kfree(ib);
clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
iio_device_put(indio_dev);

@@ -1746,7 +1759,8 @@ void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h)

static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
- struct iio_dev *indio_dev = filp->private_data;
+ struct iio_dev_buffer_pair *ib = filp->private_data;
+ struct iio_dev *indio_dev = ib->indio_dev;
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
struct iio_ioctl_handler *h;
int ret = -ENODEV;
--
2.17.1

2021-02-15 10:48:47

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

With this change, an ioctl() call is added to open a character device for a
buffer. The ioctl() number is 'i' 0x91, which follows the
IIO_GET_EVENT_FD_IOCTL ioctl.

The ioctl() will return an FD for the requested buffer index. The indexes
are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
variable).

Since there doesn't seem to be a sane way to return the FD for buffer0 to
be the same FD for the /dev/iio:deviceX, this ioctl() will return another
FD for buffer0 (or the first buffer). This duplicate FD will be able to
access the same buffer object (for buffer0) as accessing directly the
/dev/iio:deviceX chardev.

Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
index for each buffer (and the count) can be deduced from the
'/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
bufferY folders).

Used following C code to test this:
-------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h"
#include <errno.h>

#define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)

int main(int argc, char *argv[])
{
int fd;
int fd1;
int ret;

if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
return -1;
}

fprintf(stderr, "Using FD %d\n", fd);

fd1 = atoi(argv[1]);

ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
if (ret < 0) {
fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
close(fd);
return -1;
}

fprintf(stderr, "Got FD %d\n", fd1);

close(fd1);
close(fd);

return 0;
}
-------------------------------------------------------------------

Results are:
-------------------------------------------------------------------
# ./test 0
Using FD 3
Got FD 4

# ./test 1
Using FD 3
Got FD 4

# ./test 2
Using FD 3
Got FD 4

# ./test 3
Using FD 3
Got FD 4

# ls /sys/bus/iio/devices/iio\:device0
buffer buffer0 buffer1 buffer2 buffer3 dev
in_voltage_sampling_frequency in_voltage_scale
in_voltage_scale_available
name of_node power scan_elements subsystem uevent
-------------------------------------------------------------------

iio:device0 has some fake kfifo buffers attached to an IIO device.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/iio_core.h | 12 +--
drivers/iio/industrialio-buffer.c | 144 ++++++++++++++++++++++++++++--
include/linux/iio/buffer_impl.h | 5 ++
include/linux/iio/iio-opaque.h | 2 +
include/uapi/linux/iio/buffer.h | 10 +++
5 files changed, 162 insertions(+), 11 deletions(-)
create mode 100644 include/uapi/linux/iio/buffer.h

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index 7990c759f1f5..062fe16c6c49 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -64,16 +64,16 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);
#ifdef CONFIG_IIO_BUFFER
struct poll_table_struct;

-__poll_t iio_buffer_poll(struct file *filp,
- struct poll_table_struct *wait);
-ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
- size_t n, loff_t *f_ps);
+__poll_t iio_buffer_poll_wrapper(struct file *filp,
+ struct poll_table_struct *wait);
+ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf,
+ size_t n, loff_t *f_ps);

int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev);

-#define iio_buffer_poll_addr (&iio_buffer_poll)
-#define iio_buffer_read_outer_addr (&iio_buffer_read_outer)
+#define iio_buffer_poll_addr (&iio_buffer_poll_wrapper)
+#define iio_buffer_read_outer_addr (&iio_buffer_read_wrapper)

void iio_disable_all_buffers(struct iio_dev *indio_dev);
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 7b5827b91280..4848932d4394 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -9,9 +9,11 @@
* - Better memory allocation techniques?
* - Alternative access techniques?
*/
+#include <linux/anon_inodes.h>
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/device.h>
+#include <linux/file.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
@@ -89,7 +91,7 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
}

/**
- * iio_buffer_read_outer() - chrdev read for buffer access
+ * iio_buffer_read() - chrdev read for buffer access
* @filp: File structure pointer for the char device
* @buf: Destination buffer for iio buffer read
* @n: First n bytes to read
@@ -101,8 +103,8 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
* Return: negative values corresponding to error codes or ret != 0
* for ending the reading activity
**/
-ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
- size_t n, loff_t *f_ps)
+static ssize_t iio_buffer_read(struct file *filp, char __user *buf,
+ size_t n, loff_t *f_ps)
{
struct iio_dev_buffer_pair *ib = filp->private_data;
struct iio_buffer *rb = ib->buffer;
@@ -168,8 +170,8 @@ ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
* Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading
* or 0 for other cases
*/
-__poll_t iio_buffer_poll(struct file *filp,
- struct poll_table_struct *wait)
+static __poll_t iio_buffer_poll(struct file *filp,
+ struct poll_table_struct *wait)
{
struct iio_dev_buffer_pair *ib = filp->private_data;
struct iio_buffer *rb = ib->buffer;
@@ -184,6 +186,32 @@ __poll_t iio_buffer_poll(struct file *filp,
return 0;
}

+ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf,
+ size_t n, loff_t *f_ps)
+{
+ struct iio_dev_buffer_pair *ib = filp->private_data;
+ struct iio_buffer *rb = ib->buffer;
+
+ /* check if buffer was opened through new API */
+ if (test_bit(IIO_BUSY_BIT_POS, &rb->flags))
+ return -EBUSY;
+
+ return iio_buffer_read(filp, buf, n, f_ps);
+}
+
+__poll_t iio_buffer_poll_wrapper(struct file *filp,
+ struct poll_table_struct *wait)
+{
+ struct iio_dev_buffer_pair *ib = filp->private_data;
+ struct iio_buffer *rb = ib->buffer;
+
+ /* check if buffer was opened through new API */
+ if (test_bit(IIO_BUSY_BIT_POS, &rb->flags))
+ return 0;
+
+ return iio_buffer_poll(filp, wait);
+}
+
/**
* iio_buffer_wakeup_poll - Wakes up the buffer waitqueue
* @indio_dev: The IIO device
@@ -1343,6 +1371,96 @@ static void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev)
kfree(iio_dev_opaque->legacy_scan_el_group.attrs);
}

+static int iio_buffer_chrdev_release(struct inode *inode, struct file *filep)
+{
+ struct iio_dev_buffer_pair *ib = filep->private_data;
+ struct iio_dev *indio_dev = ib->indio_dev;
+ struct iio_buffer *buffer = ib->buffer;
+
+ wake_up(&buffer->pollq);
+
+ kfree(ib);
+ clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
+ iio_device_put(indio_dev);
+
+ return 0;
+}
+
+static const struct file_operations iio_buffer_chrdev_fileops = {
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+ .read = iio_buffer_read,
+ .poll = iio_buffer_poll,
+ .release = iio_buffer_chrdev_release,
+};
+
+static long iio_device_buffer_getfd(struct iio_dev *indio_dev, unsigned long arg)
+{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+ int __user *ival = (int __user *)arg;
+ struct iio_dev_buffer_pair *ib;
+ struct iio_buffer *buffer;
+ int fd, idx, ret;
+
+ if (copy_from_user(&idx, ival, sizeof(idx)))
+ return -EFAULT;
+
+ if (idx >= iio_dev_opaque->attached_buffers_cnt)
+ return -ENODEV;
+
+ iio_device_get(indio_dev);
+
+ buffer = iio_dev_opaque->attached_buffers[idx];
+
+ if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
+ ret = -EBUSY;
+ goto error_iio_dev_put;
+ }
+
+ ib = kzalloc(sizeof(*ib), GFP_KERNEL);
+ if (!ib) {
+ ret = -ENOMEM;
+ goto error_clear_busy_bit;
+ }
+
+ ib->indio_dev = indio_dev;
+ ib->buffer = buffer;
+
+ fd = anon_inode_getfd("iio:buffer", &iio_buffer_chrdev_fileops,
+ ib, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ ret = fd;
+ goto error_free_ib;
+ }
+
+ if (copy_to_user(ival, &fd, sizeof(fd))) {
+ put_unused_fd(fd);
+ ret = -EFAULT;
+ goto error_free_ib;
+ }
+
+ return fd;
+
+error_free_ib:
+ kfree(ib);
+error_clear_busy_bit:
+ clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
+error_iio_dev_put:
+ iio_device_put(indio_dev);
+ return ret;
+}
+
+static long iio_device_buffer_ioctl(struct iio_dev *indio_dev, struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case IIO_BUFFER_GET_FD_IOCTL:
+ return iio_device_buffer_getfd(indio_dev, arg);
+ default:
+ return IIO_IOCTL_UNHANDLED;
+ }
+}
+
static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
struct iio_dev *indio_dev,
int index)
@@ -1472,6 +1590,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
struct iio_buffer *buffer;
int unwind_idx;
int ret, i;
+ size_t sz;

channels = indio_dev->channels;
if (channels) {
@@ -1493,6 +1612,18 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
goto error_unwind_sysfs_and_mask;
}
}
+ unwind_idx = iio_dev_opaque->attached_buffers_cnt - 1;
+
+ sz = sizeof(*(iio_dev_opaque->buffer_ioctl_handler));
+ iio_dev_opaque->buffer_ioctl_handler = kzalloc(sz, GFP_KERNEL);
+ if (!iio_dev_opaque->buffer_ioctl_handler) {
+ ret = -ENOMEM;
+ goto error_unwind_sysfs_and_mask;
+ }
+
+ iio_dev_opaque->buffer_ioctl_handler->ioctl = iio_device_buffer_ioctl;
+ iio_device_ioctl_handler_register(indio_dev,
+ iio_dev_opaque->buffer_ioctl_handler);

return 0;

@@ -1514,6 +1645,9 @@ void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev)
if (!iio_dev_opaque->attached_buffers_cnt)
return;

+ iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler);
+ kfree(iio_dev_opaque->buffer_ioctl_handler);
+
iio_buffer_unregister_legacy_sysfs_groups(indio_dev);

for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index 768b90c64412..245b32918ae1 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -6,6 +6,8 @@

#ifdef CONFIG_IIO_BUFFER

+#include <uapi/linux/iio/buffer.h>
+
struct iio_dev;
struct iio_buffer;

@@ -72,6 +74,9 @@ struct iio_buffer {
/** @length: Number of datums in buffer. */
unsigned int length;

+ /** @flags: File ops flags including busy flag. */
+ unsigned long flags;
+
/** @bytes_per_datum: Size of individual datum including timestamp. */
size_t bytes_per_datum;

diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h
index b6ebc04af3e7..32addd5e790e 100644
--- a/include/linux/iio/iio-opaque.h
+++ b/include/linux/iio/iio-opaque.h
@@ -9,6 +9,7 @@
* @event_interface: event chrdevs associated with interrupt lines
* @attached_buffers: array of buffers statically attached by the driver
* @attached_buffers_cnt: number of buffers in the array of statically attached buffers
+ * @buffer_ioctl_handler: ioctl() handler for this IIO device's buffer interface
* @buffer_list: list of all buffers currently attached
* @channel_attr_list: keep track of automatically created channel
* attributes
@@ -28,6 +29,7 @@ struct iio_dev_opaque {
struct iio_event_interface *event_interface;
struct iio_buffer **attached_buffers;
unsigned int attached_buffers_cnt;
+ struct iio_ioctl_handler *buffer_ioctl_handler;
struct list_head buffer_list;
struct list_head channel_attr_list;
struct attribute_group chan_attr_group;
diff --git a/include/uapi/linux/iio/buffer.h b/include/uapi/linux/iio/buffer.h
new file mode 100644
index 000000000000..13939032b3f6
--- /dev/null
+++ b/include/uapi/linux/iio/buffer.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* industrial I/O buffer definitions needed both in and out of kernel
+ */
+
+#ifndef _UAPI_IIO_BUFFER_H_
+#define _UAPI_IIO_BUFFER_H_
+
+#define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
+
+#endif /* _UAPI_IIO_BUFFER_H_ */
--
2.17.1

2021-02-15 10:48:47

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 19/24] iio: buffer: introduce support for attaching more IIO buffers

With this change, calling iio_device_attach_buffer() will actually attach
more buffers.
Right now this doesn't do any validation of whether a buffer is attached
twice; maybe that can be added later (if needed). Attaching a buffer more
than once should yield noticeably bad results.

The first buffer is the legacy buffer, so a reference is kept to it.

At this point, accessing the data for the extra buffers (that are added
after the first one) isn't possible yet.

The iio_device_attach_buffer() is also changed to return an error code,
which for now is -ENOMEM if the array could not be realloc-ed for more
buffers.
To adapt to this new change iio_device_attach_buffer() is called last in
all place where it's called. The realloc failure is a bit difficult to
handle during un-managed calls when unwinding, so it's better to have this
as the last error in the setup_buffer calls.

At this point, no driver should call iio_device_attach_buffer() directly,
it should call one of the {devm_}iio_triggered_buffer_setup() or
devm_iio_kfifo_buffer_setup() or devm_iio_dmaengine_buffer_setup()
functions. This makes iio_device_attach_buffer() a bit easier to handle.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
.../buffer/industrialio-buffer-dmaengine.c | 4 +-
.../buffer/industrialio-triggered-buffer.c | 10 +-
drivers/iio/buffer/kfifo_buf.c | 4 +-
drivers/iio/iio_core.h | 10 +-
drivers/iio/industrialio-buffer.c | 100 ++++++++++++++----
drivers/iio/industrialio-core.c | 12 +--
include/linux/iio/buffer.h | 4 +-
include/linux/iio/buffer_impl.h | 3 +
include/linux/iio/iio-opaque.h | 4 +
9 files changed, 111 insertions(+), 40 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index a64b222289be..d76179878ff9 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -290,9 +290,7 @@ int devm_iio_dmaengine_buffer_setup(struct device *dev,

indio_dev->modes |= INDIO_BUFFER_HARDWARE;

- iio_device_attach_buffer(indio_dev, buffer);
-
- return 0;
+ return iio_device_attach_buffer(indio_dev, buffer);
}
EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup);

diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c
index 92b8aea3e063..b2b1b7d27af4 100644
--- a/drivers/iio/buffer/industrialio-triggered-buffer.c
+++ b/drivers/iio/buffer/industrialio-triggered-buffer.c
@@ -50,8 +50,6 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev,
goto error_ret;
}

- iio_device_attach_buffer(indio_dev, buffer);
-
indio_dev->pollfunc = iio_alloc_pollfunc(h,
thread,
IRQF_ONESHOT,
@@ -72,10 +70,16 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev,

buffer->attrs = buffer_attrs;

+ ret = iio_device_attach_buffer(indio_dev, buffer);
+ if (ret < 0)
+ goto error_dealloc_pollfunc;
+
return 0;

+error_dealloc_pollfunc:
+ iio_dealloc_pollfunc(indio_dev->pollfunc);
error_kfifo_free:
- iio_kfifo_free(indio_dev->buffer);
+ iio_kfifo_free(buffer);
error_ret:
return ret;
}
diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c
index c35a625280b1..34289ce12f20 100644
--- a/drivers/iio/buffer/kfifo_buf.c
+++ b/drivers/iio/buffer/kfifo_buf.c
@@ -235,12 +235,10 @@ int devm_iio_kfifo_buffer_setup(struct device *dev,
if (!buffer)
return -ENOMEM;

- iio_device_attach_buffer(indio_dev, buffer);
-
indio_dev->modes |= mode_flags;
indio_dev->setup_ops = setup_ops;

- return 0;
+ return iio_device_attach_buffer(indio_dev, buffer);
}
EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup);

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index 87868fff7d37..7990c759f1f5 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -69,29 +69,31 @@ __poll_t iio_buffer_poll(struct file *filp,
ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
size_t n, loff_t *f_ps);

-int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
-void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
+int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
+void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev);

#define iio_buffer_poll_addr (&iio_buffer_poll)
#define iio_buffer_read_outer_addr (&iio_buffer_read_outer)

void iio_disable_all_buffers(struct iio_dev *indio_dev);
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
+void iio_buffers_put(struct iio_dev *indio_dev);

#else

#define iio_buffer_poll_addr NULL
#define iio_buffer_read_outer_addr NULL

-static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
+static inline int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
return 0;
}

-static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {}
+static inline void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) {}

static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {}
static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}
+static inline void iio_buffers_put(struct iio_dev *indio_dev) {}

#endif

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 7efa32dd9c61..7b5827b91280 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -193,12 +193,14 @@ __poll_t iio_buffer_poll(struct file *filp,
*/
void iio_buffer_wakeup_poll(struct iio_dev *indio_dev)
{
- struct iio_buffer *buffer = indio_dev->buffer;
-
- if (!buffer)
- return;
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+ struct iio_buffer *buffer;
+ unsigned int i;

- wake_up(&buffer->pollq);
+ for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) {
+ buffer = iio_dev_opaque->attached_buffers[i];
+ wake_up(&buffer->pollq);
+ }
}

void iio_buffer_init(struct iio_buffer *buffer)
@@ -212,6 +214,18 @@ void iio_buffer_init(struct iio_buffer *buffer)
}
EXPORT_SYMBOL(iio_buffer_init);

+void iio_buffers_put(struct iio_dev *indio_dev)
+{
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+ struct iio_buffer *buffer;
+ unsigned int i;
+
+ for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) {
+ buffer = iio_dev_opaque->attached_buffers[i];
+ iio_buffer_put(buffer);
+ }
+}
+
static ssize_t iio_show_scan_index(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -1451,11 +1465,13 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
iio_free_chan_devattr_list(&buffer->buffer_attr_list);
}

-int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
+int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
const struct iio_chan_spec *channels;
- int i;
+ struct iio_buffer *buffer;
+ int unwind_idx;
+ int ret, i;

channels = indio_dev->channels;
if (channels) {
@@ -1466,22 +1482,46 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
indio_dev->masklength = ml;
}

- if (!buffer)
+ if (!iio_dev_opaque->attached_buffers_cnt)
return 0;

- return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 0);
+ for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) {
+ buffer = iio_dev_opaque->attached_buffers[i];
+ ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, i);
+ if (ret) {
+ unwind_idx = i;
+ goto error_unwind_sysfs_and_mask;
+ }
+ }
+
+ return 0;
+
+error_unwind_sysfs_and_mask:
+ for (; unwind_idx >= 0; unwind_idx--) {
+ buffer = iio_dev_opaque->attached_buffers[unwind_idx];
+ __iio_buffer_free_sysfs_and_mask(buffer);
+ }
+ kfree(iio_dev_opaque->attached_buffers);
+ return ret;
}

-void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
+void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev)
{
- struct iio_buffer *buffer = indio_dev->buffer;
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+ struct iio_buffer *buffer;
+ int i;

- if (!buffer)
+ if (!iio_dev_opaque->attached_buffers_cnt)
return;

iio_buffer_unregister_legacy_sysfs_groups(indio_dev);

- __iio_buffer_free_sysfs_and_mask(buffer);
+ for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
+ buffer = iio_dev_opaque->attached_buffers[i];
+ __iio_buffer_free_sysfs_and_mask(buffer);
+ }
+
+ kfree(iio_dev_opaque->attached_buffers);
}

/**
@@ -1599,13 +1639,35 @@ EXPORT_SYMBOL_GPL(iio_buffer_put);
* @indio_dev: The device the buffer should be attached to
* @buffer: The buffer to attach to the device
*
+ * Return 0 if successful, negative if error.
+ *
* This function attaches a buffer to a IIO device. The buffer stays attached to
- * the device until the device is freed. The function should only be called at
- * most once per device.
+ * the device until the device is freed. For legacy reasons, the first attached
+ * buffer will also be assigned to 'indio_dev->buffer'.
*/
-void iio_device_attach_buffer(struct iio_dev *indio_dev,
- struct iio_buffer *buffer)
+int iio_device_attach_buffer(struct iio_dev *indio_dev,
+ struct iio_buffer *buffer)
{
- indio_dev->buffer = iio_buffer_get(buffer);
+ struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+ struct iio_buffer **new, **old = iio_dev_opaque->attached_buffers;
+ unsigned int cnt = iio_dev_opaque->attached_buffers_cnt;
+
+ cnt++;
+
+ new = krealloc(old, sizeof(*new) * cnt, GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+ iio_dev_opaque->attached_buffers = new;
+
+ buffer = iio_buffer_get(buffer);
+
+ /* first buffer is legacy; attach it to the IIO device directly */
+ if (!indio_dev->buffer)
+ indio_dev->buffer = buffer;
+
+ iio_dev_opaque->attached_buffers[cnt - 1] = buffer;
+ iio_dev_opaque->attached_buffers_cnt = cnt;
+
+ return 0;
}
EXPORT_SYMBOL_GPL(iio_device_attach_buffer);
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 1d500ea246af..f7f785431106 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1583,7 +1583,7 @@ static void iio_dev_release(struct device *device)
iio_device_unregister_eventset(indio_dev);
iio_device_unregister_sysfs(indio_dev);

- iio_buffer_put(indio_dev->buffer);
+ iio_buffers_put(indio_dev);

ida_simple_remove(&iio_ida, indio_dev->id);
kfree(iio_dev_opaque);
@@ -1860,7 +1860,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)

iio_device_register_debugfs(indio_dev);

- ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
+ ret = iio_buffers_alloc_sysfs_and_mask(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to create buffer sysfs interfaces\n");
@@ -1886,12 +1886,12 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
indio_dev->setup_ops == NULL)
indio_dev->setup_ops = &noop_ring_setup_ops;

- if (indio_dev->buffer)
+ if (iio_dev_opaque->attached_buffers_cnt)
cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
else if (iio_dev_opaque->event_interface)
cdev_init(&indio_dev->chrdev, &iio_event_fileops);

- if (indio_dev->buffer || iio_dev_opaque->event_interface) {
+ if (iio_dev_opaque->attached_buffers_cnt || iio_dev_opaque->event_interface) {
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
indio_dev->chrdev.owner = this_mod;
}
@@ -1910,7 +1910,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
error_free_sysfs:
iio_device_unregister_sysfs(indio_dev);
error_buffer_free_sysfs:
- iio_buffer_free_sysfs_and_mask(indio_dev);
+ iio_buffers_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs:
iio_device_unregister_debugfs(indio_dev);
return ret;
@@ -1944,7 +1944,7 @@ void iio_device_unregister(struct iio_dev *indio_dev)

mutex_unlock(&indio_dev->info_exist_lock);

- iio_buffer_free_sysfs_and_mask(indio_dev);
+ iio_buffers_free_sysfs_and_mask(indio_dev);
}
EXPORT_SYMBOL(iio_device_unregister);

diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
index 8febc23f5f26..b6928ac5c63d 100644
--- a/include/linux/iio/buffer.h
+++ b/include/linux/iio/buffer.h
@@ -41,7 +41,7 @@ static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev,
bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
const unsigned long *mask);

-void iio_device_attach_buffer(struct iio_dev *indio_dev,
- struct iio_buffer *buffer);
+int iio_device_attach_buffer(struct iio_dev *indio_dev,
+ struct iio_buffer *buffer);

#endif /* _IIO_BUFFER_GENERIC_H_ */
diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index 41044320e581..768b90c64412 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -112,6 +112,9 @@ struct iio_buffer {
/* @demux_bounce: Buffer for doing gather from incoming scan. */
void *demux_bounce;

+ /* @attached_entry: Entry in the devices list of buffers attached by the driver. */
+ struct list_head attached_entry;
+
/* @buffer_list: Entry in the devices list of current buffers. */
struct list_head buffer_list;

diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h
index 1a9310b0145f..b6ebc04af3e7 100644
--- a/include/linux/iio/iio-opaque.h
+++ b/include/linux/iio/iio-opaque.h
@@ -7,6 +7,8 @@
* struct iio_dev_opaque - industrial I/O device opaque information
* @indio_dev: public industrial I/O device information
* @event_interface: event chrdevs associated with interrupt lines
+ * @attached_buffers: array of buffers statically attached by the driver
+ * @attached_buffers_cnt: number of buffers in the array of statically attached buffers
* @buffer_list: list of all buffers currently attached
* @channel_attr_list: keep track of automatically created channel
* attributes
@@ -24,6 +26,8 @@
struct iio_dev_opaque {
struct iio_dev indio_dev;
struct iio_event_interface *event_interface;
+ struct iio_buffer **attached_buffers;
+ unsigned int attached_buffers_cnt;
struct list_head buffer_list;
struct list_head channel_attr_list;
struct attribute_group chan_attr_group;
--
2.17.1

2021-02-15 10:49:12

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 21/24] iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()

The 'dev' variable name usually refers to 'struct device' types. However in
iio_device_alloc() this was used for the 'struct iio_dev' type, which was
sometimes causing minor confusions.

This change renames the variable to 'indio_dev', which is the usual name
used around IIO for 'struct iio_dev' type objects.
It makes grepping a bit easier as well.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-core.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index f7f785431106..bdb55901e97c 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1602,7 +1602,7 @@ struct device_type iio_device_type = {
struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
{
struct iio_dev_opaque *iio_dev_opaque;
- struct iio_dev *dev;
+ struct iio_dev *indio_dev;
size_t alloc_size;

alloc_size = sizeof(struct iio_dev_opaque);
@@ -1615,31 +1615,31 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
if (!iio_dev_opaque)
return NULL;

- dev = &iio_dev_opaque->indio_dev;
- dev->priv = (char *)iio_dev_opaque +
+ indio_dev = &iio_dev_opaque->indio_dev;
+ indio_dev->priv = (char *)iio_dev_opaque +
ALIGN(sizeof(struct iio_dev_opaque), IIO_ALIGN);

- dev->dev.parent = parent;
- dev->dev.type = &iio_device_type;
- dev->dev.bus = &iio_bus_type;
- device_initialize(&dev->dev);
- dev_set_drvdata(&dev->dev, (void *)dev);
- mutex_init(&dev->mlock);
- mutex_init(&dev->info_exist_lock);
+ indio_dev->dev.parent = parent;
+ indio_dev->dev.type = &iio_device_type;
+ indio_dev->dev.bus = &iio_bus_type;
+ device_initialize(&indio_dev->dev);
+ dev_set_drvdata(&indio_dev->dev, (void *)indio_dev);
+ mutex_init(&indio_dev->mlock);
+ mutex_init(&indio_dev->info_exist_lock);
INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list);

- dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
- if (dev->id < 0) {
+ indio_dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);
+ if (indio_dev->id < 0) {
/* cannot use a dev_err as the name isn't available */
pr_err("failed to get device id\n");
kfree(iio_dev_opaque);
return NULL;
}
- dev_set_name(&dev->dev, "iio:device%d", dev->id);
+ dev_set_name(&indio_dev->dev, "iio:device%d", indio_dev->id);
INIT_LIST_HEAD(&iio_dev_opaque->buffer_list);
INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers);

- return dev;
+ return indio_dev;
}
EXPORT_SYMBOL(iio_device_alloc);

--
2.17.1

2021-02-15 10:49:21

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 15/24] iio: buffer: dmaengine: obtain buffer object from attribute

The reference to the IIO buffer object is stored on the attribute object.
So we need to unwind it to obtain it.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/buffer/industrialio-buffer-dmaengine.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 9981896e1495..a64b222289be 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -132,9 +132,9 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
struct dmaengine_buffer *dmaengine_buffer =
- iio_buffer_to_dmaengine_buffer(indio_dev->buffer);
+ iio_buffer_to_dmaengine_buffer(buffer);

return sprintf(buf, "%zu\n", dmaengine_buffer->align);
}
--
2.17.1

2021-02-15 10:50:00

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 23/24] tools: iio: privatize globals and functions in iio_generic_buffer.c file

Mostly a tidy-up.
But also helps to understand the limits of scope of these functions and
globals.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
tools/iio/iio_generic_buffer.c | 31 +++++++++++++++----------------
1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c
index 34d63bcebcd2..7c7240553777 100644
--- a/tools/iio/iio_generic_buffer.c
+++ b/tools/iio/iio_generic_buffer.c
@@ -49,7 +49,7 @@ enum autochan {
* Has the side effect of filling the channels[i].location values used
* in processing the buffer output.
**/
-int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
+static int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
{
int bytes = 0;
int i = 0;
@@ -68,7 +68,7 @@ int size_from_channelarray(struct iio_channel_info *channels, int num_channels)
return bytes;
}

-void print1byte(uint8_t input, struct iio_channel_info *info)
+static void print1byte(uint8_t input, struct iio_channel_info *info)
{
/*
* Shift before conversion to avoid sign extension
@@ -85,7 +85,7 @@ void print1byte(uint8_t input, struct iio_channel_info *info)
}
}

-void print2byte(uint16_t input, struct iio_channel_info *info)
+static void print2byte(uint16_t input, struct iio_channel_info *info)
{
/* First swap if incorrect endian */
if (info->be)
@@ -108,7 +108,7 @@ void print2byte(uint16_t input, struct iio_channel_info *info)
}
}

-void print4byte(uint32_t input, struct iio_channel_info *info)
+static void print4byte(uint32_t input, struct iio_channel_info *info)
{
/* First swap if incorrect endian */
if (info->be)
@@ -131,7 +131,7 @@ void print4byte(uint32_t input, struct iio_channel_info *info)
}
}

-void print8byte(uint64_t input, struct iio_channel_info *info)
+static void print8byte(uint64_t input, struct iio_channel_info *info)
{
/* First swap if incorrect endian */
if (info->be)
@@ -167,9 +167,8 @@ void print8byte(uint64_t input, struct iio_channel_info *info)
* to fill the location offsets.
* @num_channels: number of channels
**/
-void process_scan(char *data,
- struct iio_channel_info *channels,
- int num_channels)
+static void process_scan(char *data, struct iio_channel_info *channels,
+ int num_channels)
{
int k;

@@ -238,7 +237,7 @@ static int enable_disable_all_channels(char *dev_dir_name, int enable)
return 0;
}

-void print_usage(void)
+static void print_usage(void)
{
fprintf(stderr, "Usage: generic_buffer [options]...\n"
"Capture, convert and output data from IIO device buffer\n"
@@ -257,12 +256,12 @@ void print_usage(void)
" -w <n> Set delay between reads in us (event-less mode)\n");
}

-enum autochan autochannels = AUTOCHANNELS_DISABLED;
-char *dev_dir_name = NULL;
-char *buf_dir_name = NULL;
-bool current_trigger_set = false;
+static enum autochan autochannels = AUTOCHANNELS_DISABLED;
+static char *dev_dir_name = NULL;
+static char *buf_dir_name = NULL;
+static bool current_trigger_set = false;

-void cleanup(void)
+static void cleanup(void)
{
int ret;

@@ -294,14 +293,14 @@ void cleanup(void)
}
}

-void sig_handler(int signum)
+static void sig_handler(int signum)
{
fprintf(stderr, "Caught signal %d\n", signum);
cleanup();
exit(-signum);
}

-void register_cleanup(void)
+static void register_cleanup(void)
{
struct sigaction sa = { .sa_handler = sig_handler };
const int signums[] = { SIGINT, SIGTERM, SIGABRT };
--
2.17.1

2021-02-15 10:50:11

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 18/24] iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls

The iio_simple_dummy_configure_buffer() function is essentially a
re-implementation of the iio_triggered_buffer_setup() function.

This change makes use of the iio_triggered_buffer_setup() function. The
reason is so that we don't have to modify the iio_device_attach_buffer()
function in this driver as well.

One minor drawback is that the pollfunc name may not be 100% identical
with the one in the original code, but since it's an example, it should be
a big problem.

This change does a minor re-arranging of the included iio headers, as a
minor tidy-up.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/dummy/iio_simple_dummy_buffer.c | 68 ++-------------------
1 file changed, 6 insertions(+), 62 deletions(-)

diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c
index 5512d5edc707..59aa60d4ca37 100644
--- a/drivers/iio/dummy/iio_simple_dummy_buffer.c
+++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c
@@ -16,9 +16,9 @@
#include <linux/bitmap.h>

#include <linux/iio/iio.h>
-#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
-#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>

#include "iio_simple_dummy.h"

@@ -103,64 +103,9 @@ static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = {

int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
{
- int ret;
- struct iio_buffer *buffer;
-
- /* Allocate a buffer to use - here a kfifo */
- buffer = iio_kfifo_allocate();
- if (!buffer) {
- ret = -ENOMEM;
- goto error_ret;
- }
-
- iio_device_attach_buffer(indio_dev, buffer);
-
- /*
- * Tell the core what device type specific functions should
- * be run on either side of buffer capture enable / disable.
- */
- indio_dev->setup_ops = &iio_simple_dummy_buffer_setup_ops;
-
- /*
- * Configure a polling function.
- * When a trigger event with this polling function connected
- * occurs, this function is run. Typically this grabs data
- * from the device.
- *
- * NULL for the bottom half. This is normally implemented only if we
- * either want to ping a capture now pin (no sleeping) or grab
- * a timestamp as close as possible to a data ready trigger firing.
- *
- * IRQF_ONESHOT ensures irqs are masked such that only one instance
- * of the handler can run at a time.
- *
- * "iio_simple_dummy_consumer%d" formatting string for the irq 'name'
- * as seen under /proc/interrupts. Remaining parameters as per printk.
- */
- indio_dev->pollfunc = iio_alloc_pollfunc(NULL,
- &iio_simple_dummy_trigger_h,
- IRQF_ONESHOT,
- indio_dev,
- "iio_simple_dummy_consumer%d",
- indio_dev->id);
-
- if (!indio_dev->pollfunc) {
- ret = -ENOMEM;
- goto error_free_buffer;
- }
-
- /*
- * Notify the core that this device is capable of buffered capture
- * driven by a trigger.
- */
- indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
-
- return 0;
-
-error_free_buffer:
- iio_kfifo_free(indio_dev->buffer);
-error_ret:
- return ret;
+ return iio_triggered_buffer_setup(indio_dev, NULL,
+ iio_simple_dummy_trigger_h,
+ &iio_simple_dummy_buffer_setup_ops);
}

/**
@@ -169,6 +114,5 @@ int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
*/
void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev)
{
- iio_dealloc_pollfunc(indio_dev->pollfunc);
- iio_kfifo_free(indio_dev->buffer);
+ iio_triggered_buffer_cleanup(indio_dev);
}
--
2.17.1

2021-02-15 10:50:14

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 24/24] tools: iio: convert iio_generic_buffer to use new IIO buffer API

This change makes use of the new IIO buffer API to read data from an IIO
buffer.
It doesn't read the /sys/bus/iio/devices/iio:deviceX/scan_elements dir
anymore, it reads /sys/bus/iio/devices/iio:deviceX/bufferY, where all the
scan_elements have been merged together with the old/classical buffer
attributes.

And it makes use of the new IIO_BUFFER_GET_FD_IOCTL ioctl to get an FD for
the IIO buffer for which to read data from.
It also does a quick sanity check to see that -EBUSY is returned if reading
the chardev after the ioctl() has succeeded.

This was tested with the following cases:
1. Tested buffer0 works with ioctl()
2. Tested that buffer0 can't be opened via /dev/iio:deviceX after ioctl()
This check should be omitted under normal operation; it's being done
here to check that the driver change is sane
3. Moved valid buffer0 to be buffer1, and tested that data comes from it

Signed-off-by: Alexandru Ardelean <[email protected]>
---
tools/iio/Makefile | 1 +
tools/iio/iio_generic_buffer.c | 122 ++++++++++++++++++++++++++-------
tools/iio/iio_utils.c | 13 ++--
tools/iio/iio_utils.h | 4 +-
4 files changed, 107 insertions(+), 33 deletions(-)

diff --git a/tools/iio/Makefile b/tools/iio/Makefile
index 3de763d9ab70..5d12ac4e7f8f 100644
--- a/tools/iio/Makefile
+++ b/tools/iio/Makefile
@@ -27,6 +27,7 @@ include $(srctree)/tools/build/Makefile.include
#
$(OUTPUT)include/linux/iio: ../../include/uapi/linux/iio
mkdir -p $(OUTPUT)include/linux/iio 2>&1 || true
+ ln -sf $(CURDIR)/../../include/uapi/linux/iio/buffer.h $@
ln -sf $(CURDIR)/../../include/uapi/linux/iio/events.h $@
ln -sf $(CURDIR)/../../include/uapi/linux/iio/types.h $@

diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c
index 7c7240553777..2491c54a5e4f 100644
--- a/tools/iio/iio_generic_buffer.c
+++ b/tools/iio/iio_generic_buffer.c
@@ -30,6 +30,8 @@
#include <inttypes.h>
#include <stdbool.h>
#include <signal.h>
+#include <sys/ioctl.h>
+#include <linux/iio/buffer.h>
#include "iio_utils.h"

/**
@@ -197,7 +199,7 @@ static void process_scan(char *data, struct iio_channel_info *channels,
printf("\n");
}

-static int enable_disable_all_channels(char *dev_dir_name, int enable)
+static int enable_disable_all_channels(char *dev_dir_name, int buffer_idx, int enable)
{
const struct dirent *ent;
char scanelemdir[256];
@@ -205,7 +207,7 @@ static int enable_disable_all_channels(char *dev_dir_name, int enable)
int ret;

snprintf(scanelemdir, sizeof(scanelemdir),
- FORMAT_SCAN_ELEMENTS_DIR, dev_dir_name);
+ FORMAT_SCAN_ELEMENTS_DIR, dev_dir_name, buffer_idx);
scanelemdir[sizeof(scanelemdir)-1] = '\0';

dp = opendir(scanelemdir);
@@ -243,6 +245,7 @@ static void print_usage(void)
"Capture, convert and output data from IIO device buffer\n"
" -a Auto-activate all available channels\n"
" -A Force-activate ALL channels\n"
+ " -b <n> The buffer which to open (by index), default 0\n"
" -c <n> Do n conversions, or loop forever if n < 0\n"
" -e Disable wait for event (new data)\n"
" -g Use trigger-less mode\n"
@@ -259,6 +262,7 @@ static void print_usage(void)
static enum autochan autochannels = AUTOCHANNELS_DISABLED;
static char *dev_dir_name = NULL;
static char *buf_dir_name = NULL;
+static int buffer_idx = 0;
static bool current_trigger_set = false;

static void cleanup(void)
@@ -286,7 +290,7 @@ static void cleanup(void)

/* Disable channels if auto-enabled */
if (dev_dir_name && autochannels == AUTOCHANNELS_ACTIVE) {
- ret = enable_disable_all_channels(dev_dir_name, 0);
+ ret = enable_disable_all_channels(dev_dir_name, buffer_idx, 0);
if (ret)
fprintf(stderr, "Failed to disable all channels\n");
autochannels = AUTOCHANNELS_DISABLED;
@@ -333,7 +337,9 @@ int main(int argc, char **argv)
unsigned long long j;
unsigned long toread;
int ret, c;
- int fp = -1;
+ struct stat st;
+ int fd = -1;
+ int buf_fd = -1;

int num_channels = 0;
char *trigger_name = NULL, *device_name = NULL;
@@ -352,7 +358,7 @@ int main(int argc, char **argv)

register_cleanup();

- while ((c = getopt_long(argc, argv, "aAc:egl:n:N:t:T:w:?", longopts,
+ while ((c = getopt_long(argc, argv, "aAb:c:egl:n:N:t:T:w:?", longopts,
NULL)) != -1) {
switch (c) {
case 'a':
@@ -361,7 +367,20 @@ int main(int argc, char **argv)
case 'A':
autochannels = AUTOCHANNELS_ENABLED;
force_autochannels = true;
- break;
+ break;
+ case 'b':
+ errno = 0;
+ buffer_idx = strtoll(optarg, &dummy, 10);
+ if (errno) {
+ ret = -errno;
+ goto error;
+ }
+ if (buffer_idx < 0) {
+ ret = -ERANGE;
+ goto error;
+ }
+
+ break;
case 'c':
errno = 0;
num_loops = strtoll(optarg, &dummy, 10);
@@ -518,7 +537,7 @@ int main(int argc, char **argv)
* Parse the files in scan_elements to identify what channels are
* present
*/
- ret = build_channel_array(dev_dir_name, &channels, &num_channels);
+ ret = build_channel_array(dev_dir_name, buffer_idx, &channels, &num_channels);
if (ret) {
fprintf(stderr, "Problem reading scan element information\n"
"diag %s\n", dev_dir_name);
@@ -535,7 +554,7 @@ int main(int argc, char **argv)
(autochannels == AUTOCHANNELS_ENABLED && force_autochannels)) {
fprintf(stderr, "Enabling all channels\n");

- ret = enable_disable_all_channels(dev_dir_name, 1);
+ ret = enable_disable_all_channels(dev_dir_name, buffer_idx, 1);
if (ret) {
fprintf(stderr, "Failed to enable all channels\n");
goto error;
@@ -544,7 +563,7 @@ int main(int argc, char **argv)
/* This flags that we need to disable the channels again */
autochannels = AUTOCHANNELS_ACTIVE;

- ret = build_channel_array(dev_dir_name, &channels,
+ ret = build_channel_array(dev_dir_name, buffer_idx, &channels,
&num_channels);
if (ret) {
fprintf(stderr, "Problem reading scan element "
@@ -565,7 +584,7 @@ int main(int argc, char **argv)
fprintf(stderr, "Enable channels manually in "
FORMAT_SCAN_ELEMENTS_DIR
"/*_en or pass -a to autoenable channels and "
- "try again.\n", dev_dir_name);
+ "try again.\n", dev_dir_name, buffer_idx);
ret = -ENOENT;
goto error;
}
@@ -576,12 +595,25 @@ int main(int argc, char **argv)
* be built rather than found.
*/
ret = asprintf(&buf_dir_name,
- "%siio:device%d/buffer", iio_dir, dev_num);
+ "%siio:device%d/buffer%d", iio_dir, dev_num, buffer_idx);
if (ret < 0) {
ret = -ENOMEM;
goto error;
}

+ if (stat(buf_dir_name, &st)) {
+ fprintf(stderr, "Could not stat() '%s', got error %d: %s\n",
+ buf_dir_name, errno, strerror(errno));
+ ret = -errno;
+ goto error;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ fprintf(stderr, "File '%s' is not a directory\n", buf_dir_name);
+ ret = -EFAULT;
+ goto error;
+ }
+
if (!notrigger) {
printf("%s %s\n", dev_dir_name, trigger_name);
/*
@@ -598,6 +630,35 @@ int main(int argc, char **argv)
}
}

+ ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num);
+ if (ret < 0) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* Attempt to open non blocking the access dev */
+ fd = open(buffer_access, O_RDONLY | O_NONBLOCK);
+ if (fd == -1) { /* TODO: If it isn't there make the node */
+ ret = -errno;
+ fprintf(stderr, "Failed to open %s\n", buffer_access);
+ goto error;
+ }
+
+ /* specify for which buffer index we want an FD */
+ buf_fd = buffer_idx;
+
+ ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &buf_fd);
+ if (ret == -1 || buf_fd == -1) {
+ ret = -errno;
+ if (ret == -ENODEV || ret == -EINVAL)
+ fprintf(stderr,
+ "Device does not have this many buffers\n");
+ else
+ fprintf(stderr, "Failed to retrieve buffer fd\n");
+
+ goto error;
+ }
+
/* Setup ring buffer parameters */
ret = write_sysfs_int("length", buf_dir_name, buf_len);
if (ret < 0)
@@ -607,7 +668,8 @@ int main(int argc, char **argv)
ret = write_sysfs_int("enable", buf_dir_name, 1);
if (ret < 0) {
fprintf(stderr,
- "Failed to enable buffer: %s\n", strerror(-ret));
+ "Failed to enable buffer '%s': %s\n",
+ buf_dir_name, strerror(-ret));
goto error;
}

@@ -618,24 +680,30 @@ int main(int argc, char **argv)
goto error;
}

- ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num);
- if (ret < 0) {
- ret = -ENOMEM;
- goto error;
+ /**
+ * This check is being done here for sanity reasons, however it
+ * should be omitted under normal operation.
+ * If this is buffer0, we check that we get EBUSY after this point.
+ */
+ if (buffer_idx == 0) {
+ errno = 0;
+ read_size = read(fd, data, 1);
+ if (read_size > -1 || errno != EBUSY) {
+ ret = -EFAULT;
+ perror("Reading from '%s' should not be possible after ioctl()");
+ goto error;
+ }
}

- /* Attempt to open non blocking the access dev */
- fp = open(buffer_access, O_RDONLY | O_NONBLOCK);
- if (fp == -1) { /* TODO: If it isn't there make the node */
- ret = -errno;
- fprintf(stderr, "Failed to open %s\n", buffer_access);
- goto error;
- }
+ /* close now the main chardev FD and let the buffer FD work */
+ if (close(fd) == -1)
+ perror("Failed to close character device file");
+ fd = -1;

for (j = 0; j < num_loops || num_loops < 0; j++) {
if (!noevents) {
struct pollfd pfd = {
- .fd = fp,
+ .fd = buf_fd,
.events = POLLIN,
};

@@ -653,7 +721,7 @@ int main(int argc, char **argv)
toread = 64;
}

- read_size = read(fp, data, toread * scan_size);
+ read_size = read(buf_fd, data, toread * scan_size);
if (read_size < 0) {
if (errno == EAGAIN) {
fprintf(stderr, "nothing available\n");
@@ -670,7 +738,9 @@ int main(int argc, char **argv)
error:
cleanup();

- if (fp >= 0 && close(fp) == -1)
+ if (fd >= 0 && close(fd) == -1)
+ perror("Failed to close character device");
+ if (buf_fd >= 0 && close(buf_fd) == -1)
perror("Failed to close buffer");
free(buffer_access);
free(data);
diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c
index a96002f2c2d5..aadee6d34c74 100644
--- a/tools/iio/iio_utils.c
+++ b/tools/iio/iio_utils.c
@@ -77,6 +77,7 @@ int iioutils_break_up_name(const char *full_name, char **generic_name)
* @mask: output a bit mask for the raw data
* @be: output if data in big endian
* @device_dir: the IIO device directory
+ * @buffer_idx: the IIO buffer index
* @name: the channel name
* @generic_name: the channel type name
*
@@ -85,8 +86,8 @@ int iioutils_break_up_name(const char *full_name, char **generic_name)
static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes,
unsigned int *bits_used, unsigned int *shift,
uint64_t *mask, unsigned int *be,
- const char *device_dir, const char *name,
- const char *generic_name)
+ const char *device_dir, int buffer_idx,
+ const char *name, const char *generic_name)
{
FILE *sysfsfp;
int ret;
@@ -96,7 +97,7 @@ static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes,
unsigned padint;
const struct dirent *ent;

- ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
+ ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx);
if (ret < 0)
return -ENOMEM;

@@ -304,12 +305,13 @@ void bsort_channel_array_by_index(struct iio_channel_info *ci_array, int cnt)
/**
* build_channel_array() - function to figure out what channels are present
* @device_dir: the IIO device directory in sysfs
+ * @buffer_idx: the IIO buffer for this channel array
* @ci_array: output the resulting array of iio_channel_info
* @counter: output the amount of array elements
*
* Returns 0 on success, otherwise a negative error code.
**/
-int build_channel_array(const char *device_dir,
+int build_channel_array(const char *device_dir, int buffer_idx,
struct iio_channel_info **ci_array, int *counter)
{
DIR *dp;
@@ -322,7 +324,7 @@ int build_channel_array(const char *device_dir,
char *filename;

*counter = 0;
- ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
+ ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx);
if (ret < 0)
return -ENOMEM;

@@ -503,6 +505,7 @@ int build_channel_array(const char *device_dir,
&current->mask,
&current->be,
device_dir,
+ buffer_idx,
current->name,
current->generic_name);
if (ret < 0)
diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h
index a5d0aa8a57d3..336752cade4f 100644
--- a/tools/iio/iio_utils.h
+++ b/tools/iio/iio_utils.h
@@ -12,7 +12,7 @@
/* Made up value to limit allocation sizes */
#define IIO_MAX_NAME_LENGTH 64

-#define FORMAT_SCAN_ELEMENTS_DIR "%s/scan_elements"
+#define FORMAT_SCAN_ELEMENTS_DIR "%s/buffer%d"
#define FORMAT_TYPE_FILE "%s_type"

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
@@ -61,7 +61,7 @@ int iioutils_get_param_float(float *output, const char *param_name,
const char *device_dir, const char *name,
const char *generic_name);
void bsort_channel_array_by_index(struct iio_channel_info *ci_array, int cnt);
-int build_channel_array(const char *device_dir,
+int build_channel_array(const char *device_dir, int buffer_idx,
struct iio_channel_info **ci_array, int *counter);
int find_type_by_name(const char *name, const char *type);
int write_sysfs_int(const char *filename, const char *basedir, int val);
--
2.17.1

2021-02-15 10:51:21

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 22/24] tools: iio: make iioutils_get_type() private in iio_utils

This is a bit of a tidy-up, but also helps with extending the
iioutils_get_type() function a bit, as we don't need to use it outside of
the iio_utils.c file. So, we'll need to update it only in one place.

With this change, the 'unsigned' types are updated to 'unsigned int' in the
iioutils_get_type() function definition.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
tools/iio/iio_utils.c | 9 +++++----
tools/iio/iio_utils.h | 4 ----
2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c
index 7399eb7f1378..a96002f2c2d5 100644
--- a/tools/iio/iio_utils.c
+++ b/tools/iio/iio_utils.c
@@ -82,10 +82,11 @@ int iioutils_break_up_name(const char *full_name, char **generic_name)
*
* Returns a value >= 0 on success, otherwise a negative error code.
**/
-int iioutils_get_type(unsigned *is_signed, unsigned *bytes, unsigned *bits_used,
- unsigned *shift, uint64_t *mask, unsigned *be,
- const char *device_dir, const char *name,
- const char *generic_name)
+static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes,
+ unsigned int *bits_used, unsigned int *shift,
+ uint64_t *mask, unsigned int *be,
+ const char *device_dir, const char *name,
+ const char *generic_name)
{
FILE *sysfsfp;
int ret;
diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h
index 74bde4fde2c8..a5d0aa8a57d3 100644
--- a/tools/iio/iio_utils.h
+++ b/tools/iio/iio_utils.h
@@ -57,10 +57,6 @@ static inline int iioutils_check_suffix(const char *str, const char *suffix)
}

int iioutils_break_up_name(const char *full_name, char **generic_name);
-int iioutils_get_type(unsigned *is_signed, unsigned *bytes, unsigned *bits_used,
- unsigned *shift, uint64_t *mask, unsigned *be,
- const char *device_dir, const char *name,
- const char *generic_name);
int iioutils_get_param_float(float *output, const char *param_name,
const char *device_dir, const char *name,
const char *generic_name);
--
2.17.1

2021-02-15 10:52:53

by Alexandru Ardelean

[permalink] [raw]
Subject: [PATCH v6 17/24] iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc

The __iio_buffer_free_sysfs_and_mask() function will be used in
iio_buffer_alloc_sysfs_and_mask() when multiple buffers will be attached to
the IIO device.
This will need to be used to cleanup resources on each buffer, when the
buffers cleanup unwind will occur on the error path.

The move is done in this patch to make the patch that adds multiple buffers
per IIO device a bit cleaner.

Signed-off-by: Alexandru Ardelean <[email protected]>
---
drivers/iio/industrialio-buffer.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 83ea15e330a4..7efa32dd9c61 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1443,6 +1443,14 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
return ret;
}

+static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
+{
+ bitmap_free(buffer->scan_mask);
+ kfree(buffer->buffer_group.name);
+ kfree(buffer->buffer_group.attrs);
+ iio_free_chan_devattr_list(&buffer->buffer_attr_list);
+}
+
int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer = indio_dev->buffer;
@@ -1464,14 +1472,6 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, 0);
}

-static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
-{
- bitmap_free(buffer->scan_mask);
- kfree(buffer->buffer_group.name);
- kfree(buffer->buffer_group.attrs);
- iio_free_chan_devattr_list(&buffer->buffer_attr_list);
-}
-
void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
{
struct iio_buffer *buffer = indio_dev->buffer;
--
2.17.1

2021-02-15 12:13:21

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 03/24] iio: make use of devm_iio_kfifo_buffer_setup() helper

On Mon, 15 Feb 2021 12:40:22 +0200
Alexandru Ardelean <[email protected]> wrote:

> All drivers that already call devm_iio_kfifo_allocate() &
> iio_device_attach_buffer() are simple to convert to
> iio_device_attach_kfifo_buffer() in a single go.
>
> This change does that; the unwind order is preserved.
> What is important, is that the devm_iio_kfifo_buffer_setup() be called
> after the indio_dev->modes is assigned, to make sure that
> INDIO_BUFFER_SOFTWARE flag is set and not overridden by the assignment to
> indio_dev->modes.
>
> Also, the INDIO_BUFFER_SOFTWARE has been removed from the assignments of
> 'indio_dev->modes' because it is set by devm_iio_kfifo_buffer_setup().
>
> Signed-off-by: Alexandru Ardelean <[email protected]>
I 'think' this one is is in the obviously correct category so I've
applied it to the togreg branch of iio.git and pushed out as testing.
Note there is still plenty of time for any additional feedback,
particularly as the CC list was a little sparse.

I've +CCd those who I know are still active maintainers of some
of the affected drivers.

Jonathan

> ---
> drivers/iio/accel/ssp_accel_sensor.c | 14 ++++-------
> drivers/iio/adc/ina2xx-adc.c | 14 +++++------
> drivers/iio/adc/ti_am335x_adc.c | 18 ++++-----------
> .../cros_ec_sensors/cros_ec_sensors_core.c | 13 ++++-------
> drivers/iio/gyro/ssp_gyro_sensor.c | 14 ++++-------
> drivers/iio/health/max30100.c | 16 ++++++-------
> drivers/iio/health/max30102.c | 16 ++++++-------
> .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +++++------
> .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +++++------
> .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +++++-------
> drivers/iio/light/acpi-als.c | 12 ++++------
> drivers/iio/light/apds9960.c | 16 ++++++-------
> .../staging/iio/impedance-analyzer/ad5933.c | 23 ++++---------------
> 13 files changed, 74 insertions(+), 124 deletions(-)
>
> diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
> index 474477e91b5e..04dcb2b657ee 100644
> --- a/drivers/iio/accel/ssp_accel_sensor.c
> +++ b/drivers/iio/accel/ssp_accel_sensor.c
> @@ -96,7 +96,6 @@ static int ssp_accel_probe(struct platform_device *pdev)
> int ret;
> struct iio_dev *indio_dev;
> struct ssp_sensor_data *spd;
> - struct iio_buffer *buffer;
>
> indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
> if (!indio_dev)
> @@ -109,18 +108,15 @@ static int ssp_accel_probe(struct platform_device *pdev)
>
> indio_dev->name = ssp_accel_device_name;
> indio_dev->info = &ssp_accel_iio_info;
> - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> indio_dev->channels = ssp_acc_channels;
> indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
> indio_dev->available_scan_masks = ssp_accel_scan_mask;
>
> - buffer = devm_iio_kfifo_allocate(&pdev->dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> -
> - indio_dev->setup_ops = &ssp_accel_buffer_ops;
> + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &ssp_accel_buffer_ops);
> + if (ret)
> + return ret;
>
> platform_set_drvdata(pdev, indio_dev);
>
> diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
> index b573ec60a8b8..2ae54258b221 100644
> --- a/drivers/iio/adc/ina2xx-adc.c
> +++ b/drivers/iio/adc/ina2xx-adc.c
> @@ -953,7 +953,6 @@ static int ina2xx_probe(struct i2c_client *client,
> {
> struct ina2xx_chip_info *chip;
> struct iio_dev *indio_dev;
> - struct iio_buffer *buffer;
> unsigned int val;
> enum ina2xx_ids type;
> int ret;
> @@ -1017,7 +1016,7 @@ static int ina2xx_probe(struct i2c_client *client,
> return ret;
> }
>
> - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> if (id->driver_data == ina226) {
> indio_dev->channels = ina226_channels;
> indio_dev->num_channels = ARRAY_SIZE(ina226_channels);
> @@ -1028,13 +1027,12 @@ static int ina2xx_probe(struct i2c_client *client,
> indio_dev->info = &ina219_info;
> }
> indio_dev->name = id->name;
> - indio_dev->setup_ops = &ina2xx_setup_ops;
>
> - buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &ina2xx_setup_ops);
> + if (ret)
> + return ret;
>
> return iio_device_register(indio_dev);
> }
> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> index e946903b0993..855cc2d64ac8 100644
> --- a/drivers/iio/adc/ti_am335x_adc.c
> +++ b/drivers/iio/adc/ti_am335x_adc.c
> @@ -385,24 +385,16 @@ static int tiadc_iio_buffered_hardware_setup(struct device *dev,
> unsigned long flags,
> const struct iio_buffer_setup_ops *setup_ops)
> {
> - struct iio_buffer *buffer;
> int ret;
>
> - buffer = devm_iio_kfifo_allocate(dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> -
> - ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
> - flags, indio_dev->name, indio_dev);
> + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + setup_ops);
> if (ret)
> return ret;
>
> - indio_dev->setup_ops = setup_ops;
> - indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
> -
> - return 0;
> + return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
> + flags, indio_dev->name, indio_dev);
> }
>
> static const char * const chan_name_ain[] = {
> diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> index c833ec0ef214..f8824afe595e 100644
> --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> @@ -334,14 +334,11 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
> * We can not use trigger here, as events are generated
> * as soon as sample_frequency is set.
> */
> - struct iio_buffer *buffer;
> -
> - buffer = devm_iio_kfifo_allocate(dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + NULL);
> + if (ret)
> + return ret;
>
> ret = cros_ec_sensorhub_register_push_data(
> sensor_hub, sensor_platform->sensor_num,
> diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
> index ac7c170a20de..46ed12771d2f 100644
> --- a/drivers/iio/gyro/ssp_gyro_sensor.c
> +++ b/drivers/iio/gyro/ssp_gyro_sensor.c
> @@ -96,7 +96,6 @@ static int ssp_gyro_probe(struct platform_device *pdev)
> int ret;
> struct iio_dev *indio_dev;
> struct ssp_sensor_data *spd;
> - struct iio_buffer *buffer;
>
> indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
> if (!indio_dev)
> @@ -109,18 +108,15 @@ static int ssp_gyro_probe(struct platform_device *pdev)
>
> indio_dev->name = ssp_gyro_name;
> indio_dev->info = &ssp_gyro_iio_info;
> - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> indio_dev->channels = ssp_gyro_channels;
> indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
> indio_dev->available_scan_masks = ssp_gyro_scan_mask;
>
> - buffer = devm_iio_kfifo_allocate(&pdev->dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> -
> - indio_dev->setup_ops = &ssp_gyro_buffer_ops;
> + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &ssp_gyro_buffer_ops);
> + if (ret)
> + return ret;
>
> platform_set_drvdata(pdev, indio_dev);
>
> diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
> index 38aa2030f3c6..36ba7611d9ce 100644
> --- a/drivers/iio/health/max30100.c
> +++ b/drivers/iio/health/max30100.c
> @@ -418,7 +418,6 @@ static int max30100_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> struct max30100_data *data;
> - struct iio_buffer *buffer;
> struct iio_dev *indio_dev;
> int ret;
>
> @@ -426,19 +425,18 @@ static int max30100_probe(struct i2c_client *client,
> if (!indio_dev)
> return -ENOMEM;
>
> - buffer = devm_iio_kfifo_allocate(&client->dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> -
> indio_dev->name = MAX30100_DRV_NAME;
> indio_dev->channels = max30100_channels;
> indio_dev->info = &max30100_info;
> indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
> indio_dev->available_scan_masks = max30100_scan_masks;
> - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> - indio_dev->setup_ops = &max30100_buffer_setup_ops;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &max30100_buffer_setup_ops);
> + if (ret)
> + return ret;
>
> data = iio_priv(indio_dev);
> data->indio_dev = indio_dev;
> diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
> index b35557a54ee2..2292876c55e2 100644
> --- a/drivers/iio/health/max30102.c
> +++ b/drivers/iio/health/max30102.c
> @@ -506,7 +506,6 @@ static int max30102_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> struct max30102_data *data;
> - struct iio_buffer *buffer;
> struct iio_dev *indio_dev;
> int ret;
> unsigned int reg;
> @@ -515,16 +514,9 @@ static int max30102_probe(struct i2c_client *client,
> if (!indio_dev)
> return -ENOMEM;
>
> - buffer = devm_iio_kfifo_allocate(&client->dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> -
> indio_dev->name = MAX30102_DRV_NAME;
> indio_dev->info = &max30102_info;
> - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> - indio_dev->setup_ops = &max30102_buffer_setup_ops;
> + indio_dev->modes = INDIO_DIRECT_MODE;
>
> data = iio_priv(indio_dev);
> data->indio_dev = indio_dev;
> @@ -549,6 +541,12 @@ static int max30102_probe(struct i2c_client *client,
> return -ENODEV;
> }
>
> + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &max30102_buffer_setup_ops);
> + if (ret)
> + return ret;
> +
> data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config);
> if (IS_ERR(data->regmap)) {
> dev_err(&client->dev, "regmap initialization failed\n");
> diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> index 3441b0d61c5d..383cc3250342 100644
> --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> @@ -709,7 +709,6 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
> const char *name;
> struct inv_icm42600_timestamp *ts;
> struct iio_dev *indio_dev;
> - struct iio_buffer *buffer;
> int ret;
>
> name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name);
> @@ -720,23 +719,22 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
> if (!indio_dev)
> return ERR_PTR(-ENOMEM);
>
> - buffer = devm_iio_kfifo_allocate(dev);
> - if (!buffer)
> - return ERR_PTR(-ENOMEM);
> -
> ts = iio_priv(indio_dev);
> inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));
>
> iio_device_set_drvdata(indio_dev, st);
> indio_dev->name = name;
> indio_dev->info = &inv_icm42600_accel_info;
> - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> indio_dev->channels = inv_icm42600_accel_channels;
> indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels);
> indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks;
> - indio_dev->setup_ops = &inv_icm42600_buffer_ops;
>
> - iio_device_attach_buffer(indio_dev, buffer);
> + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &inv_icm42600_buffer_ops);
> + if (ret)
> + return ERR_PTR(ret);
>
> ret = devm_iio_device_register(dev, indio_dev);
> if (ret)
> diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> index aee7b9ff4bf4..cec1dd0e0464 100644
> --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> @@ -720,7 +720,6 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> const char *name;
> struct inv_icm42600_timestamp *ts;
> struct iio_dev *indio_dev;
> - struct iio_buffer *buffer;
> int ret;
>
> name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
> @@ -731,23 +730,23 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> if (!indio_dev)
> return ERR_PTR(-ENOMEM);
>
> - buffer = devm_iio_kfifo_allocate(dev);
> - if (!buffer)
> - return ERR_PTR(-ENOMEM);
> -
> ts = iio_priv(indio_dev);
> inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));
>
> iio_device_set_drvdata(indio_dev, st);
> indio_dev->name = name;
> indio_dev->info = &inv_icm42600_gyro_info;
> - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> indio_dev->channels = inv_icm42600_gyro_channels;
> indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
> indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks;
> indio_dev->setup_ops = &inv_icm42600_buffer_ops;
>
> - iio_device_attach_buffer(indio_dev, buffer);
> + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &inv_icm42600_buffer_ops);
> + if (ret)
> + return ERR_PTR(ret);
>
> ret = devm_iio_device_register(dev, indio_dev);
> if (ret)
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> index f1103ecedd64..16730a780964 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> @@ -739,20 +739,17 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
>
> int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
> {
> - struct iio_buffer *buffer;
> - int i;
> + int i, ret;
>
> for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> if (!hw->iio_devs[i])
> continue;
>
> - buffer = devm_iio_kfifo_allocate(hw->dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(hw->iio_devs[i], buffer);
> - hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
> - hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
> + ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
> + INDIO_BUFFER_SOFTWARE,
> + &st_lsm6dsx_buffer_ops);
> + if (ret)
> + return ret;
> }
>
> return 0;
> diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
> index 2be7180e2cbf..f8e547fd35e7 100644
> --- a/drivers/iio/light/acpi-als.c
> +++ b/drivers/iio/light/acpi-als.c
> @@ -165,7 +165,7 @@ static int acpi_als_add(struct acpi_device *device)
> {
> struct acpi_als *als;
> struct iio_dev *indio_dev;
> - struct iio_buffer *buffer;
> + int ret;
>
> indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
> if (!indio_dev)
> @@ -179,15 +179,13 @@ static int acpi_als_add(struct acpi_device *device)
>
> indio_dev->name = ACPI_ALS_DEVICE_NAME;
> indio_dev->info = &acpi_als_info;
> - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> indio_dev->channels = acpi_als_channels;
> indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
>
> - buffer = devm_iio_kfifo_allocate(&device->dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> + ret = devm_iio_kfifo_buffer_setup(&device->dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE, NULL);
> + if (ret)
> + return ret;
>
> return devm_iio_device_register(&device->dev, indio_dev);
> }
> diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
> index df0647856e5d..4141c0fa7bc4 100644
> --- a/drivers/iio/light/apds9960.c
> +++ b/drivers/iio/light/apds9960.c
> @@ -988,7 +988,6 @@ static int apds9960_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> struct apds9960_data *data;
> - struct iio_buffer *buffer;
> struct iio_dev *indio_dev;
> int ret;
>
> @@ -996,19 +995,18 @@ static int apds9960_probe(struct i2c_client *client,
> if (!indio_dev)
> return -ENOMEM;
>
> - buffer = devm_iio_kfifo_allocate(&client->dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> -
> indio_dev->info = &apds9960_info;
> indio_dev->name = APDS9960_DRV_NAME;
> indio_dev->channels = apds9960_channels;
> indio_dev->num_channels = ARRAY_SIZE(apds9960_channels);
> indio_dev->available_scan_masks = apds9960_scan_masks;
> - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> - indio_dev->setup_ops = &apds9960_buffer_setup_ops;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &apds9960_buffer_setup_ops);
> + if (ret)
> + return ret;
>
> data = iio_priv(indio_dev);
> i2c_set_clientdata(client, indio_dev);
> diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
> index dba78896ea8f..793918e1c45f 100644
> --- a/drivers/staging/iio/impedance-analyzer/ad5933.c
> +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
> @@ -602,23 +602,6 @@ static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = {
> .postdisable = ad5933_ring_postdisable,
> };
>
> -static int ad5933_register_ring_funcs_and_init(struct device *dev,
> - struct iio_dev *indio_dev)
> -{
> - struct iio_buffer *buffer;
> -
> - buffer = devm_iio_kfifo_allocate(dev);
> - if (!buffer)
> - return -ENOMEM;
> -
> - iio_device_attach_buffer(indio_dev, buffer);
> -
> - /* Ring buffer functions - here trigger setup related */
> - indio_dev->setup_ops = &ad5933_ring_setup_ops;
> -
> - return 0;
> -}
> -
> static void ad5933_work(struct work_struct *work)
> {
> struct ad5933_state *st = container_of(work,
> @@ -761,11 +744,13 @@ static int ad5933_probe(struct i2c_client *client,
>
> indio_dev->info = &ad5933_info;
> indio_dev->name = id->name;
> - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> + indio_dev->modes = INDIO_DIRECT_MODE;
> indio_dev->channels = ad5933_channels;
> indio_dev->num_channels = ARRAY_SIZE(ad5933_channels);
>
> - ret = ad5933_register_ring_funcs_and_init(&client->dev, indio_dev);
> + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> + INDIO_BUFFER_SOFTWARE,
> + &ad5933_ring_setup_ops);
> if (ret)
> return ret;
>

2021-02-15 12:47:59

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 15/24] iio: buffer: dmaengine: obtain buffer object from attribute

On Mon, 15 Feb 2021 12:40:34 +0200
Alexandru Ardelean <[email protected]> wrote:

> The reference to the IIO buffer object is stored on the attribute object.
> So we need to unwind it to obtain it.
>
> Signed-off-by: Alexandru Ardelean <[email protected]>

This could have done with a little more description of 'why', but
I'm not going to hold up the series to fix it.

Jonathan

> ---
> drivers/iio/buffer/industrialio-buffer-dmaengine.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
> index 9981896e1495..a64b222289be 100644
> --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
> +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
> @@ -132,9 +132,9 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = {
> static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> struct dmaengine_buffer *dmaengine_buffer =
> - iio_buffer_to_dmaengine_buffer(indio_dev->buffer);
> + iio_buffer_to_dmaengine_buffer(buffer);
>
> return sprintf(buf, "%zu\n", dmaengine_buffer->align);
> }

2021-02-15 13:54:57

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 24/24] tools: iio: convert iio_generic_buffer to use new IIO buffer API

On Mon, 15 Feb 2021 12:40:43 +0200
Alexandru Ardelean <[email protected]> wrote:

> This change makes use of the new IIO buffer API to read data from an IIO
> buffer.
> It doesn't read the /sys/bus/iio/devices/iio:deviceX/scan_elements dir
> anymore, it reads /sys/bus/iio/devices/iio:deviceX/bufferY, where all the
> scan_elements have been merged together with the old/classical buffer
> attributes.
>
> And it makes use of the new IIO_BUFFER_GET_FD_IOCTL ioctl to get an FD for
> the IIO buffer for which to read data from.
> It also does a quick sanity check to see that -EBUSY is returned if reading
> the chardev after the ioctl() has succeeded.
>
> This was tested with the following cases:
> 1. Tested buffer0 works with ioctl()
> 2. Tested that buffer0 can't be opened via /dev/iio:deviceX after ioctl()
> This check should be omitted under normal operation; it's being done
> here to check that the driver change is sane
> 3. Moved valid buffer0 to be buffer1, and tested that data comes from it
>
> Signed-off-by: Alexandru Ardelean <[email protected]>

Whole series applied to the togreg branch of iio.git and pushed out as testing
to see what we missed in review.

There is that one patch doing rework of a load of drivers early on in the
series that may get a few more reviews.

Otherwise, reviews on any of this from others still definitely welcome.
I'm not going to push out as a non rebasing tree for at least a couple of
weeks (given merge window is open).

Thanks,

Jonathan

> ---
> tools/iio/Makefile | 1 +
> tools/iio/iio_generic_buffer.c | 122 ++++++++++++++++++++++++++-------
> tools/iio/iio_utils.c | 13 ++--
> tools/iio/iio_utils.h | 4 +-
> 4 files changed, 107 insertions(+), 33 deletions(-)
>
> diff --git a/tools/iio/Makefile b/tools/iio/Makefile
> index 3de763d9ab70..5d12ac4e7f8f 100644
> --- a/tools/iio/Makefile
> +++ b/tools/iio/Makefile
> @@ -27,6 +27,7 @@ include $(srctree)/tools/build/Makefile.include
> #
> $(OUTPUT)include/linux/iio: ../../include/uapi/linux/iio
> mkdir -p $(OUTPUT)include/linux/iio 2>&1 || true
> + ln -sf $(CURDIR)/../../include/uapi/linux/iio/buffer.h $@
> ln -sf $(CURDIR)/../../include/uapi/linux/iio/events.h $@
> ln -sf $(CURDIR)/../../include/uapi/linux/iio/types.h $@
>
> diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c
> index 7c7240553777..2491c54a5e4f 100644
> --- a/tools/iio/iio_generic_buffer.c
> +++ b/tools/iio/iio_generic_buffer.c
> @@ -30,6 +30,8 @@
> #include <inttypes.h>
> #include <stdbool.h>
> #include <signal.h>
> +#include <sys/ioctl.h>
> +#include <linux/iio/buffer.h>
> #include "iio_utils.h"
>
> /**
> @@ -197,7 +199,7 @@ static void process_scan(char *data, struct iio_channel_info *channels,
> printf("\n");
> }
>
> -static int enable_disable_all_channels(char *dev_dir_name, int enable)
> +static int enable_disable_all_channels(char *dev_dir_name, int buffer_idx, int enable)
> {
> const struct dirent *ent;
> char scanelemdir[256];
> @@ -205,7 +207,7 @@ static int enable_disable_all_channels(char *dev_dir_name, int enable)
> int ret;
>
> snprintf(scanelemdir, sizeof(scanelemdir),
> - FORMAT_SCAN_ELEMENTS_DIR, dev_dir_name);
> + FORMAT_SCAN_ELEMENTS_DIR, dev_dir_name, buffer_idx);
> scanelemdir[sizeof(scanelemdir)-1] = '\0';
>
> dp = opendir(scanelemdir);
> @@ -243,6 +245,7 @@ static void print_usage(void)
> "Capture, convert and output data from IIO device buffer\n"
> " -a Auto-activate all available channels\n"
> " -A Force-activate ALL channels\n"
> + " -b <n> The buffer which to open (by index), default 0\n"
> " -c <n> Do n conversions, or loop forever if n < 0\n"
> " -e Disable wait for event (new data)\n"
> " -g Use trigger-less mode\n"
> @@ -259,6 +262,7 @@ static void print_usage(void)
> static enum autochan autochannels = AUTOCHANNELS_DISABLED;
> static char *dev_dir_name = NULL;
> static char *buf_dir_name = NULL;
> +static int buffer_idx = 0;
> static bool current_trigger_set = false;
>
> static void cleanup(void)
> @@ -286,7 +290,7 @@ static void cleanup(void)
>
> /* Disable channels if auto-enabled */
> if (dev_dir_name && autochannels == AUTOCHANNELS_ACTIVE) {
> - ret = enable_disable_all_channels(dev_dir_name, 0);
> + ret = enable_disable_all_channels(dev_dir_name, buffer_idx, 0);
> if (ret)
> fprintf(stderr, "Failed to disable all channels\n");
> autochannels = AUTOCHANNELS_DISABLED;
> @@ -333,7 +337,9 @@ int main(int argc, char **argv)
> unsigned long long j;
> unsigned long toread;
> int ret, c;
> - int fp = -1;
> + struct stat st;
> + int fd = -1;
> + int buf_fd = -1;
>
> int num_channels = 0;
> char *trigger_name = NULL, *device_name = NULL;
> @@ -352,7 +358,7 @@ int main(int argc, char **argv)
>
> register_cleanup();
>
> - while ((c = getopt_long(argc, argv, "aAc:egl:n:N:t:T:w:?", longopts,
> + while ((c = getopt_long(argc, argv, "aAb:c:egl:n:N:t:T:w:?", longopts,
> NULL)) != -1) {
> switch (c) {
> case 'a':
> @@ -361,7 +367,20 @@ int main(int argc, char **argv)
> case 'A':
> autochannels = AUTOCHANNELS_ENABLED;
> force_autochannels = true;
> - break;
> + break;
> + case 'b':
> + errno = 0;
> + buffer_idx = strtoll(optarg, &dummy, 10);
> + if (errno) {
> + ret = -errno;
> + goto error;
> + }
> + if (buffer_idx < 0) {
> + ret = -ERANGE;
> + goto error;
> + }
> +
> + break;
> case 'c':
> errno = 0;
> num_loops = strtoll(optarg, &dummy, 10);
> @@ -518,7 +537,7 @@ int main(int argc, char **argv)
> * Parse the files in scan_elements to identify what channels are
> * present
> */
> - ret = build_channel_array(dev_dir_name, &channels, &num_channels);
> + ret = build_channel_array(dev_dir_name, buffer_idx, &channels, &num_channels);
> if (ret) {
> fprintf(stderr, "Problem reading scan element information\n"
> "diag %s\n", dev_dir_name);
> @@ -535,7 +554,7 @@ int main(int argc, char **argv)
> (autochannels == AUTOCHANNELS_ENABLED && force_autochannels)) {
> fprintf(stderr, "Enabling all channels\n");
>
> - ret = enable_disable_all_channels(dev_dir_name, 1);
> + ret = enable_disable_all_channels(dev_dir_name, buffer_idx, 1);
> if (ret) {
> fprintf(stderr, "Failed to enable all channels\n");
> goto error;
> @@ -544,7 +563,7 @@ int main(int argc, char **argv)
> /* This flags that we need to disable the channels again */
> autochannels = AUTOCHANNELS_ACTIVE;
>
> - ret = build_channel_array(dev_dir_name, &channels,
> + ret = build_channel_array(dev_dir_name, buffer_idx, &channels,
> &num_channels);
> if (ret) {
> fprintf(stderr, "Problem reading scan element "
> @@ -565,7 +584,7 @@ int main(int argc, char **argv)
> fprintf(stderr, "Enable channels manually in "
> FORMAT_SCAN_ELEMENTS_DIR
> "/*_en or pass -a to autoenable channels and "
> - "try again.\n", dev_dir_name);
> + "try again.\n", dev_dir_name, buffer_idx);
> ret = -ENOENT;
> goto error;
> }
> @@ -576,12 +595,25 @@ int main(int argc, char **argv)
> * be built rather than found.
> */
> ret = asprintf(&buf_dir_name,
> - "%siio:device%d/buffer", iio_dir, dev_num);
> + "%siio:device%d/buffer%d", iio_dir, dev_num, buffer_idx);
> if (ret < 0) {
> ret = -ENOMEM;
> goto error;
> }
>
> + if (stat(buf_dir_name, &st)) {
> + fprintf(stderr, "Could not stat() '%s', got error %d: %s\n",
> + buf_dir_name, errno, strerror(errno));
> + ret = -errno;
> + goto error;
> + }
> +
> + if (!S_ISDIR(st.st_mode)) {
> + fprintf(stderr, "File '%s' is not a directory\n", buf_dir_name);
> + ret = -EFAULT;
> + goto error;
> + }
> +
> if (!notrigger) {
> printf("%s %s\n", dev_dir_name, trigger_name);
> /*
> @@ -598,6 +630,35 @@ int main(int argc, char **argv)
> }
> }
>
> + ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num);
> + if (ret < 0) {
> + ret = -ENOMEM;
> + goto error;
> + }
> +
> + /* Attempt to open non blocking the access dev */
> + fd = open(buffer_access, O_RDONLY | O_NONBLOCK);
> + if (fd == -1) { /* TODO: If it isn't there make the node */
> + ret = -errno;
> + fprintf(stderr, "Failed to open %s\n", buffer_access);
> + goto error;
> + }
> +
> + /* specify for which buffer index we want an FD */
> + buf_fd = buffer_idx;
> +
> + ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &buf_fd);
> + if (ret == -1 || buf_fd == -1) {
> + ret = -errno;
> + if (ret == -ENODEV || ret == -EINVAL)
> + fprintf(stderr,
> + "Device does not have this many buffers\n");
> + else
> + fprintf(stderr, "Failed to retrieve buffer fd\n");
> +
> + goto error;
> + }
> +
> /* Setup ring buffer parameters */
> ret = write_sysfs_int("length", buf_dir_name, buf_len);
> if (ret < 0)
> @@ -607,7 +668,8 @@ int main(int argc, char **argv)
> ret = write_sysfs_int("enable", buf_dir_name, 1);
> if (ret < 0) {
> fprintf(stderr,
> - "Failed to enable buffer: %s\n", strerror(-ret));
> + "Failed to enable buffer '%s': %s\n",
> + buf_dir_name, strerror(-ret));
> goto error;
> }
>
> @@ -618,24 +680,30 @@ int main(int argc, char **argv)
> goto error;
> }
>
> - ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num);
> - if (ret < 0) {
> - ret = -ENOMEM;
> - goto error;
> + /**
> + * This check is being done here for sanity reasons, however it
> + * should be omitted under normal operation.
> + * If this is buffer0, we check that we get EBUSY after this point.
> + */
> + if (buffer_idx == 0) {
> + errno = 0;
> + read_size = read(fd, data, 1);
> + if (read_size > -1 || errno != EBUSY) {
> + ret = -EFAULT;
> + perror("Reading from '%s' should not be possible after ioctl()");
> + goto error;
> + }
> }
>
> - /* Attempt to open non blocking the access dev */
> - fp = open(buffer_access, O_RDONLY | O_NONBLOCK);
> - if (fp == -1) { /* TODO: If it isn't there make the node */
> - ret = -errno;
> - fprintf(stderr, "Failed to open %s\n", buffer_access);
> - goto error;
> - }
> + /* close now the main chardev FD and let the buffer FD work */
> + if (close(fd) == -1)
> + perror("Failed to close character device file");
> + fd = -1;
>
> for (j = 0; j < num_loops || num_loops < 0; j++) {
> if (!noevents) {
> struct pollfd pfd = {
> - .fd = fp,
> + .fd = buf_fd,
> .events = POLLIN,
> };
>
> @@ -653,7 +721,7 @@ int main(int argc, char **argv)
> toread = 64;
> }
>
> - read_size = read(fp, data, toread * scan_size);
> + read_size = read(buf_fd, data, toread * scan_size);
> if (read_size < 0) {
> if (errno == EAGAIN) {
> fprintf(stderr, "nothing available\n");
> @@ -670,7 +738,9 @@ int main(int argc, char **argv)
> error:
> cleanup();
>
> - if (fp >= 0 && close(fp) == -1)
> + if (fd >= 0 && close(fd) == -1)
> + perror("Failed to close character device");
> + if (buf_fd >= 0 && close(buf_fd) == -1)
> perror("Failed to close buffer");
> free(buffer_access);
> free(data);
> diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c
> index a96002f2c2d5..aadee6d34c74 100644
> --- a/tools/iio/iio_utils.c
> +++ b/tools/iio/iio_utils.c
> @@ -77,6 +77,7 @@ int iioutils_break_up_name(const char *full_name, char **generic_name)
> * @mask: output a bit mask for the raw data
> * @be: output if data in big endian
> * @device_dir: the IIO device directory
> + * @buffer_idx: the IIO buffer index
> * @name: the channel name
> * @generic_name: the channel type name
> *
> @@ -85,8 +86,8 @@ int iioutils_break_up_name(const char *full_name, char **generic_name)
> static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes,
> unsigned int *bits_used, unsigned int *shift,
> uint64_t *mask, unsigned int *be,
> - const char *device_dir, const char *name,
> - const char *generic_name)
> + const char *device_dir, int buffer_idx,
> + const char *name, const char *generic_name)
> {
> FILE *sysfsfp;
> int ret;
> @@ -96,7 +97,7 @@ static int iioutils_get_type(unsigned int *is_signed, unsigned int *bytes,
> unsigned padint;
> const struct dirent *ent;
>
> - ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
> + ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx);
> if (ret < 0)
> return -ENOMEM;
>
> @@ -304,12 +305,13 @@ void bsort_channel_array_by_index(struct iio_channel_info *ci_array, int cnt)
> /**
> * build_channel_array() - function to figure out what channels are present
> * @device_dir: the IIO device directory in sysfs
> + * @buffer_idx: the IIO buffer for this channel array
> * @ci_array: output the resulting array of iio_channel_info
> * @counter: output the amount of array elements
> *
> * Returns 0 on success, otherwise a negative error code.
> **/
> -int build_channel_array(const char *device_dir,
> +int build_channel_array(const char *device_dir, int buffer_idx,
> struct iio_channel_info **ci_array, int *counter)
> {
> DIR *dp;
> @@ -322,7 +324,7 @@ int build_channel_array(const char *device_dir,
> char *filename;
>
> *counter = 0;
> - ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir);
> + ret = asprintf(&scan_el_dir, FORMAT_SCAN_ELEMENTS_DIR, device_dir, buffer_idx);
> if (ret < 0)
> return -ENOMEM;
>
> @@ -503,6 +505,7 @@ int build_channel_array(const char *device_dir,
> &current->mask,
> &current->be,
> device_dir,
> + buffer_idx,
> current->name,
> current->generic_name);
> if (ret < 0)
> diff --git a/tools/iio/iio_utils.h b/tools/iio/iio_utils.h
> index a5d0aa8a57d3..336752cade4f 100644
> --- a/tools/iio/iio_utils.h
> +++ b/tools/iio/iio_utils.h
> @@ -12,7 +12,7 @@
> /* Made up value to limit allocation sizes */
> #define IIO_MAX_NAME_LENGTH 64
>
> -#define FORMAT_SCAN_ELEMENTS_DIR "%s/scan_elements"
> +#define FORMAT_SCAN_ELEMENTS_DIR "%s/buffer%d"
> #define FORMAT_TYPE_FILE "%s_type"
>
> #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
> @@ -61,7 +61,7 @@ int iioutils_get_param_float(float *output, const char *param_name,
> const char *device_dir, const char *name,
> const char *generic_name);
> void bsort_channel_array_by_index(struct iio_channel_info *ci_array, int cnt);
> -int build_channel_array(const char *device_dir,
> +int build_channel_array(const char *device_dir, int buffer_idx,
> struct iio_channel_info **ci_array, int *counter);
> int find_type_by_name(const char *name, const char *type);
> int write_sysfs_int(const char *filename, const char *basedir, int val);

2021-02-15 14:00:29

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 00/24] iio: core,buffer: add support for multiple IIO buffers per IIO device

On Mon, 15 Feb 2021 12:40:19 +0200
Alexandru Ardelean <[email protected]> wrote:

Hi Alex,

One thought on this that came up whilst reading through it again.
There are several uses for multiple buffers.
1) input vs output buffers
2) A device with separately clocked sampling of different channels.

For case 2, there is at least sometimes a trigger involved
(often data ready) so we probably also need to think long term about how
to support multiple current_triggers in one driver.

I guess that's something for us to figure out when we need it however.

Thanks for you hard work on finally getting this upstream.
+ Lars (and maybe others) for earlier work on this.

Whilst I've applied all these, they are only exposed in the testing branch
of iio.git on kernel.org so far and as such any additional review anyone
else wants to give would be welcome!

Jonathan

> Changelog v5 -> v6:
> * https://lore.kernel.org/linux-iio/[email protected]/T/#t
> * merged series 'iio: kfifo: define a devm_iio_kfifo_buffer_setup helper' into this
> https://lore.kernel.org/linux-iio/[email protected]/T/#u
> * merged patch 'iio: buffer-dma,adi-axi-adc: introduce devm_iio_dmaengine_buffer_setup()' into this
> https://lore.kernel.org/linux-iio/[email protected]/T/#u
> * patch 'iio: core: wrap iio device & buffer into struct for character devices'
> - moved kfree(ib) to be first in iio_chrdev_release() to make the cleanup
> order be reversed as the open() order
> * patch 'iio: buffer: group attr count and attr alloc'
> - sizeof(struct attribute *) -> sizeof(*attr) in kcalloc()
> * patch 'iio: core: merge buffer/ & scan_elements/ attributes'
> - minor rework in iio_buffer_register_legacy_sysfs_groups() to
> use more 'sizeof(*attrs)' in allocations
> - fixed typo 'legacy_buffer_el_group' -> 'legacy_buffer_group'
> - updated comment about
> '/* we only need to link the legacy buffer groups for the first buffer */'
> to
> '/* we only need to register the legacy groups for the first buffer */'
> * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> - removed 'buf_name' from iio_device_buffer_getfd();
> passing "iio:buffer" name; since it should be a class-name
> - removed extra-line in drivers/iio/industrial-core.c
> - changed init order in iio_device_buffer_getfd() and matched it with reverse in
> iio_buffer_chrdev_release()
> - calling put_unused_fd() in iio_device_buffer_getfd() if copy_to_user() fails
> * added 'iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls'
> * patch ' iio: buffer: introduce support for attaching more IIO buffers'
> - now handling iio_device_attach_buffer() error returns
> - rename iio_buffer_free_sysfs_and_mask() -> iio_buffers_free_sysfs_and_mask()
> - rename iio_buffer_alloc_sysfs_and_mask() -> iio_buffer_alloc_sysfs_and_mask()
> * patch 'tools: iio: convert iio_generic_buffer to use new IIO buffer API'
> - removed extra close() if getfd() ioctl() errors out
> - added comment that the sanity check for buffer0 should not be done
> under normal operation
> - update message from
> "This device does not support buffers"
> to
> "Device does not have this many buffers"
>
> Changelog v4 -> v5:
> * https://lore.kernel.org/linux-iio/[email protected]/T/#t
> * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> don't return -EBUSY in iio_buffer_poll_wrapper(); return 0
> __poll_t is unsigned, so returning 0 is the best we can do
> Reported-by: kernel test robot <[email protected]>
> * patch 'iio: buffer: dmaengine: obtain buffer object from attribute'
> removed unused 'indio_dev' variable; seems i missed this initially
> * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> call 'wake_up(buffer->pollq)' in iio_buffer_chrdev_release()
>
> Changelog v3 -> v4:
> * https://lore.kernel.org/linux-iio/[email protected]/
> * patch 'docs: ioctl-number.rst: reserve IIO subsystem ioctl() space'
> remove 'uapi/' from `uapi/linux/iio/*.h`
> * patch 'iio: core: register chardev only if needed'
> add commit comment about potentially breaking userspace ABI with chardev removal
> * patch 'iio: core: rework iio device group creation'
> remove NULL re-init in iio_device_unregister_sysfs() ; memory is being free'd
> * patch 'iio: buffer: group attr count and attr alloc'
> extend commit comment about the 2 or 1 buffer directores
> * patch 'iio: core: merge buffer/ & scan_elements/ attributes'
> fixed static checker complaints
> - removed unused global
> - initialize omitted 'ret = -ENOMEM' on error path
> - made iio_buffer_unregister_legacy_sysfs_groups() static
> * patch 'iio: buffer: wrap all buffer attributes into iio_dev_attr'
> - update some omitted unwindings; seems i forgot a few originally
> this was showing up when trying to read from buffer1
> * add patch 'iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc func'
> * patch 'iio: buffer: introduce support for attaching more IIO buffers'
> - removed 'iio_dev_opaque->attached_buffers = NULL' after kfree()
> - using 'iio_dev_opaque->attached_buffers_cnt' to check that we have buffers
> instead of checking 'indio_dev->buffer'
> * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> - replaced -ENOENT with -ENODEV when buffer index is out of range
> * add 'iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()'
> * add 'iio: buffer: dmaengine: obtain buffer object from attribute'
> * add tools/iio patches for new multibuffer logic
> tools: iio: make iioutils_get_type() private in iio_utils
> tools: iio: privatize globals and functions in iio_generic_buffer.c file
> tools: iio: convert iio_generic_buffer to use new IIO buffer API
>
> Changelog v2 -> v3:
> * https://lore.kernel.org/linux-iio/[email protected]/
> * added commit 'docs: ioctl-number.rst: reserve IIO subsystem ioctl() space'
> reserving 'i' 0x90-0x9F ioctls for IIO
> I did not see any conflicts with others (in the doc)
> - related to this, the new IIO_BUFFER_GET_FD_IOCTL is now at 'i' 0x91
> * changed approach for creating sysfs buffer directories;
> - they are now created as groups on the IIO device; that also means
> that the groups array needs to be krealloc-ed and assign later in
> the registration
> - merged bufferX/ and scan_elementsX/ directories into a single
> bufferX/ directory
> - for legacy the buffer/ & scan_elements/ directories are kept; but
> they're groups objects have been moved on the iio_dev_opaque object
> - internally, the iio_dev_attr type is being extended to hold a
> reference for an IIO buffer;
> = this is great for scan_elements attributes
> = and for the rest of the iio_buffer attributes, it means we need to
> wrap them into iio_dev_attr
>
> Changelog v1 -> v2:
> * https://lore.kernel.org/linux-iio/[email protected]/
> * 'iio: buffer: rework buffer & scan_elements dir creation'
> add more doc-strings detailing the reasoning for this change
> * 'iio: buffer: re-route scan_elements via it's kobj_type'
> move list_del() before the kfree()'s in the list destruction
> * 'iio: buffer: introduce support for attaching more IIO buffers'
> - changed to 'cnt' variable vs re-using the 'i' for unwinding in
> iio_buffer_alloc_sysfs_and_mask()
> - removed kfree(old) in iio_device_attach_buffer()
> - made iio_device_attach_buffer() an int return; this means that some
> follow up patches are needed to make this return value be used;
> * 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> - tested ioctl() with a simple C program; attached to comment;
> - changed 'i' variable usage to 'sz' for alloc
> - changed logic for buffer0; returning FD 0; userspace should know
> that the IIO_BUFFER_GET_FD_IOCTL call returns 0 for buffer0;
> this is because I can't find a way to determine the FD of the
> ioctl() in the kernel; duplicating an ioctl() for buffer0 is also bad;
>
>
> Alexandru Ardelean (24):
> iio: adc: ti_am335x_adc: remove omitted iio_kfifo_free()
> iio: kfifo: add devm_iio_kfifo_buffer_setup() helper
> iio: make use of devm_iio_kfifo_buffer_setup() helper
> iio: accel: sca3000: use devm_iio_kfifo_buffer_setup() helper
> iio: kfifo: un-export devm_iio_kfifo_allocate() function
> iio: buffer-dma,adi-axi-adc: introduce
> devm_iio_dmaengine_buffer_setup()
> docs: ioctl-number.rst: reserve IIO subsystem ioctl() space
> iio: core: register chardev only if needed
> iio: core-trigger: make iio_device_register_trigger_consumer() an int
> return
> iio: core: rework iio device group creation
> iio: buffer: group attr count and attr alloc
> iio: core: merge buffer/ & scan_elements/ attributes
> iio: add reference to iio buffer on iio_dev_attr
> iio: buffer: wrap all buffer attributes into iio_dev_attr
> iio: buffer: dmaengine: obtain buffer object from attribute
> iio: core: wrap iio device & buffer into struct for character devices
> iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc
> iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls
> iio: buffer: introduce support for attaching more IIO buffers
> iio: buffer: add ioctl() to support opening extra buffers for IIO
> device
> iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()
> tools: iio: make iioutils_get_type() private in iio_utils
> tools: iio: privatize globals and functions in iio_generic_buffer.c
> file
> tools: iio: convert iio_generic_buffer to use new IIO buffer API
>
> .../driver-api/driver-model/devres.rst | 3 +-
> .../userspace-api/ioctl/ioctl-number.rst | 1 +
> drivers/iio/accel/sca3000.c | 19 +-
> drivers/iio/accel/ssp_accel_sensor.c | 14 +-
> drivers/iio/adc/adi-axi-adc.c | 12 +-
> drivers/iio/adc/ina2xx-adc.c | 14 +-
> drivers/iio/adc/ti_am335x_adc.c | 24 +-
> .../buffer/industrialio-buffer-dmaengine.c | 35 +-
> .../buffer/industrialio-triggered-buffer.c | 10 +-
> drivers/iio/buffer/kfifo_buf.c | 40 +-
> .../cros_ec_sensors/cros_ec_sensors_core.c | 13 +-
> drivers/iio/dummy/iio_simple_dummy_buffer.c | 68 +--
> drivers/iio/gyro/ssp_gyro_sensor.c | 14 +-
> drivers/iio/health/max30100.c | 16 +-
> drivers/iio/health/max30102.c | 16 +-
> drivers/iio/iio_core.h | 32 +-
> drivers/iio/iio_core_trigger.h | 4 +-
> .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +-
> .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +-
> .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +-
> drivers/iio/industrialio-buffer.c | 494 ++++++++++++++----
> drivers/iio/industrialio-core.c | 113 +++-
> drivers/iio/industrialio-event.c | 6 +-
> drivers/iio/industrialio-trigger.c | 6 +-
> drivers/iio/light/acpi-als.c | 12 +-
> drivers/iio/light/apds9960.c | 16 +-
> .../staging/iio/impedance-analyzer/ad5933.c | 23 +-
> include/linux/iio/buffer-dmaengine.h | 7 +-
> include/linux/iio/buffer.h | 4 +-
> include/linux/iio/buffer_impl.h | 21 +-
> include/linux/iio/iio-opaque.h | 14 +
> include/linux/iio/iio.h | 5 -
> include/linux/iio/kfifo_buf.h | 7 +-
> include/linux/iio/sysfs.h | 3 +
> include/uapi/linux/iio/buffer.h | 10 +
> tools/iio/Makefile | 1 +
> tools/iio/iio_generic_buffer.c | 153 ++++--
> tools/iio/iio_utils.c | 18 +-
> tools/iio/iio_utils.h | 8 +-
> 39 files changed, 856 insertions(+), 442 deletions(-)
> create mode 100644 include/uapi/linux/iio/buffer.h
>

2021-02-15 14:13:27

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [PATCH v6 00/24] iio: core,buffer: add support for multiple IIO buffers per IIO device

On Mon, Feb 15, 2021 at 3:59 PM Jonathan Cameron <[email protected]> wrote:
>
> On Mon, 15 Feb 2021 12:40:19 +0200
> Alexandru Ardelean <[email protected]> wrote:
>
> Hi Alex,
>
> One thought on this that came up whilst reading through it again.
> There are several uses for multiple buffers.
> 1) input vs output buffers
> 2) A device with separately clocked sampling of different channels.

So, ADI typically has use-cases for 1).
Mostly RF transceivers.
Traditionally, these were written (inside the ADI tree) as 2-3 IIO
devices (1 IIO device for each path,1 for TX and 1-2 for RX).
With multi-buffer, they can be a single IIO device with 2-3 IIO buffers.

There are some ADI devices that have channels that can run at different speeds.
But this hasn't been an important use-cases to deal with.
Typically, a common-rate would be picked [for all channels] and that's that.
I think some people find this per-channel rates interesting; but let's see.

>
> For case 2, there is at least sometimes a trigger involved
> (often data ready) so we probably also need to think long term about how
> to support multiple current_triggers in one driver.
>
> I guess that's something for us to figure out when we need it however.
>
> Thanks for you hard work on finally getting this upstream.
> + Lars (and maybe others) for earlier work on this.

Thank you as well.

>
> Whilst I've applied all these, they are only exposed in the testing branch
> of iio.git on kernel.org so far and as such any additional review anyone
> else wants to give would be welcome!

I feel there may be other users stumbling over this multibuffer
support, and try it out.
Often people don't seem to care about multibuffer, until they really need it.
A bit of chicken-n-egg problem.
But after being added, people may see it and start to use it.

>
> Jonathan
>
> > Changelog v5 -> v6:
> > * https://lore.kernel.org/linux-iio/[email protected]/T/#t
> > * merged series 'iio: kfifo: define a devm_iio_kfifo_buffer_setup helper' into this
> > https://lore.kernel.org/linux-iio/[email protected]/T/#u
> > * merged patch 'iio: buffer-dma,adi-axi-adc: introduce devm_iio_dmaengine_buffer_setup()' into this
> > https://lore.kernel.org/linux-iio/[email protected]/T/#u
> > * patch 'iio: core: wrap iio device & buffer into struct for character devices'
> > - moved kfree(ib) to be first in iio_chrdev_release() to make the cleanup
> > order be reversed as the open() order
> > * patch 'iio: buffer: group attr count and attr alloc'
> > - sizeof(struct attribute *) -> sizeof(*attr) in kcalloc()
> > * patch 'iio: core: merge buffer/ & scan_elements/ attributes'
> > - minor rework in iio_buffer_register_legacy_sysfs_groups() to
> > use more 'sizeof(*attrs)' in allocations
> > - fixed typo 'legacy_buffer_el_group' -> 'legacy_buffer_group'
> > - updated comment about
> > '/* we only need to link the legacy buffer groups for the first buffer */'
> > to
> > '/* we only need to register the legacy groups for the first buffer */'
> > * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > - removed 'buf_name' from iio_device_buffer_getfd();
> > passing "iio:buffer" name; since it should be a class-name
> > - removed extra-line in drivers/iio/industrial-core.c
> > - changed init order in iio_device_buffer_getfd() and matched it with reverse in
> > iio_buffer_chrdev_release()
> > - calling put_unused_fd() in iio_device_buffer_getfd() if copy_to_user() fails
> > * added 'iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls'
> > * patch ' iio: buffer: introduce support for attaching more IIO buffers'
> > - now handling iio_device_attach_buffer() error returns
> > - rename iio_buffer_free_sysfs_and_mask() -> iio_buffers_free_sysfs_and_mask()
> > - rename iio_buffer_alloc_sysfs_and_mask() -> iio_buffer_alloc_sysfs_and_mask()
> > * patch 'tools: iio: convert iio_generic_buffer to use new IIO buffer API'
> > - removed extra close() if getfd() ioctl() errors out
> > - added comment that the sanity check for buffer0 should not be done
> > under normal operation
> > - update message from
> > "This device does not support buffers"
> > to
> > "Device does not have this many buffers"
> >
> > Changelog v4 -> v5:
> > * https://lore.kernel.org/linux-iio/[email protected]/T/#t
> > * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > don't return -EBUSY in iio_buffer_poll_wrapper(); return 0
> > __poll_t is unsigned, so returning 0 is the best we can do
> > Reported-by: kernel test robot <[email protected]>
> > * patch 'iio: buffer: dmaengine: obtain buffer object from attribute'
> > removed unused 'indio_dev' variable; seems i missed this initially
> > * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > call 'wake_up(buffer->pollq)' in iio_buffer_chrdev_release()
> >
> > Changelog v3 -> v4:
> > * https://lore.kernel.org/linux-iio/[email protected]/
> > * patch 'docs: ioctl-number.rst: reserve IIO subsystem ioctl() space'
> > remove 'uapi/' from `uapi/linux/iio/*.h`
> > * patch 'iio: core: register chardev only if needed'
> > add commit comment about potentially breaking userspace ABI with chardev removal
> > * patch 'iio: core: rework iio device group creation'
> > remove NULL re-init in iio_device_unregister_sysfs() ; memory is being free'd
> > * patch 'iio: buffer: group attr count and attr alloc'
> > extend commit comment about the 2 or 1 buffer directores
> > * patch 'iio: core: merge buffer/ & scan_elements/ attributes'
> > fixed static checker complaints
> > - removed unused global
> > - initialize omitted 'ret = -ENOMEM' on error path
> > - made iio_buffer_unregister_legacy_sysfs_groups() static
> > * patch 'iio: buffer: wrap all buffer attributes into iio_dev_attr'
> > - update some omitted unwindings; seems i forgot a few originally
> > this was showing up when trying to read from buffer1
> > * add patch 'iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc func'
> > * patch 'iio: buffer: introduce support for attaching more IIO buffers'
> > - removed 'iio_dev_opaque->attached_buffers = NULL' after kfree()
> > - using 'iio_dev_opaque->attached_buffers_cnt' to check that we have buffers
> > instead of checking 'indio_dev->buffer'
> > * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > - replaced -ENOENT with -ENODEV when buffer index is out of range
> > * add 'iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()'
> > * add 'iio: buffer: dmaengine: obtain buffer object from attribute'
> > * add tools/iio patches for new multibuffer logic
> > tools: iio: make iioutils_get_type() private in iio_utils
> > tools: iio: privatize globals and functions in iio_generic_buffer.c file
> > tools: iio: convert iio_generic_buffer to use new IIO buffer API
> >
> > Changelog v2 -> v3:
> > * https://lore.kernel.org/linux-iio/[email protected]/
> > * added commit 'docs: ioctl-number.rst: reserve IIO subsystem ioctl() space'
> > reserving 'i' 0x90-0x9F ioctls for IIO
> > I did not see any conflicts with others (in the doc)
> > - related to this, the new IIO_BUFFER_GET_FD_IOCTL is now at 'i' 0x91
> > * changed approach for creating sysfs buffer directories;
> > - they are now created as groups on the IIO device; that also means
> > that the groups array needs to be krealloc-ed and assign later in
> > the registration
> > - merged bufferX/ and scan_elementsX/ directories into a single
> > bufferX/ directory
> > - for legacy the buffer/ & scan_elements/ directories are kept; but
> > they're groups objects have been moved on the iio_dev_opaque object
> > - internally, the iio_dev_attr type is being extended to hold a
> > reference for an IIO buffer;
> > = this is great for scan_elements attributes
> > = and for the rest of the iio_buffer attributes, it means we need to
> > wrap them into iio_dev_attr
> >
> > Changelog v1 -> v2:
> > * https://lore.kernel.org/linux-iio/[email protected]/
> > * 'iio: buffer: rework buffer & scan_elements dir creation'
> > add more doc-strings detailing the reasoning for this change
> > * 'iio: buffer: re-route scan_elements via it's kobj_type'
> > move list_del() before the kfree()'s in the list destruction
> > * 'iio: buffer: introduce support for attaching more IIO buffers'
> > - changed to 'cnt' variable vs re-using the 'i' for unwinding in
> > iio_buffer_alloc_sysfs_and_mask()
> > - removed kfree(old) in iio_device_attach_buffer()
> > - made iio_device_attach_buffer() an int return; this means that some
> > follow up patches are needed to make this return value be used;
> > * 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > - tested ioctl() with a simple C program; attached to comment;
> > - changed 'i' variable usage to 'sz' for alloc
> > - changed logic for buffer0; returning FD 0; userspace should know
> > that the IIO_BUFFER_GET_FD_IOCTL call returns 0 for buffer0;
> > this is because I can't find a way to determine the FD of the
> > ioctl() in the kernel; duplicating an ioctl() for buffer0 is also bad;
> >
> >
> > Alexandru Ardelean (24):
> > iio: adc: ti_am335x_adc: remove omitted iio_kfifo_free()
> > iio: kfifo: add devm_iio_kfifo_buffer_setup() helper
> > iio: make use of devm_iio_kfifo_buffer_setup() helper
> > iio: accel: sca3000: use devm_iio_kfifo_buffer_setup() helper
> > iio: kfifo: un-export devm_iio_kfifo_allocate() function
> > iio: buffer-dma,adi-axi-adc: introduce
> > devm_iio_dmaengine_buffer_setup()
> > docs: ioctl-number.rst: reserve IIO subsystem ioctl() space
> > iio: core: register chardev only if needed
> > iio: core-trigger: make iio_device_register_trigger_consumer() an int
> > return
> > iio: core: rework iio device group creation
> > iio: buffer: group attr count and attr alloc
> > iio: core: merge buffer/ & scan_elements/ attributes
> > iio: add reference to iio buffer on iio_dev_attr
> > iio: buffer: wrap all buffer attributes into iio_dev_attr
> > iio: buffer: dmaengine: obtain buffer object from attribute
> > iio: core: wrap iio device & buffer into struct for character devices
> > iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc
> > iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls
> > iio: buffer: introduce support for attaching more IIO buffers
> > iio: buffer: add ioctl() to support opening extra buffers for IIO
> > device
> > iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()
> > tools: iio: make iioutils_get_type() private in iio_utils
> > tools: iio: privatize globals and functions in iio_generic_buffer.c
> > file
> > tools: iio: convert iio_generic_buffer to use new IIO buffer API
> >
> > .../driver-api/driver-model/devres.rst | 3 +-
> > .../userspace-api/ioctl/ioctl-number.rst | 1 +
> > drivers/iio/accel/sca3000.c | 19 +-
> > drivers/iio/accel/ssp_accel_sensor.c | 14 +-
> > drivers/iio/adc/adi-axi-adc.c | 12 +-
> > drivers/iio/adc/ina2xx-adc.c | 14 +-
> > drivers/iio/adc/ti_am335x_adc.c | 24 +-
> > .../buffer/industrialio-buffer-dmaengine.c | 35 +-
> > .../buffer/industrialio-triggered-buffer.c | 10 +-
> > drivers/iio/buffer/kfifo_buf.c | 40 +-
> > .../cros_ec_sensors/cros_ec_sensors_core.c | 13 +-
> > drivers/iio/dummy/iio_simple_dummy_buffer.c | 68 +--
> > drivers/iio/gyro/ssp_gyro_sensor.c | 14 +-
> > drivers/iio/health/max30100.c | 16 +-
> > drivers/iio/health/max30102.c | 16 +-
> > drivers/iio/iio_core.h | 32 +-
> > drivers/iio/iio_core_trigger.h | 4 +-
> > .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +-
> > .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +-
> > .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +-
> > drivers/iio/industrialio-buffer.c | 494 ++++++++++++++----
> > drivers/iio/industrialio-core.c | 113 +++-
> > drivers/iio/industrialio-event.c | 6 +-
> > drivers/iio/industrialio-trigger.c | 6 +-
> > drivers/iio/light/acpi-als.c | 12 +-
> > drivers/iio/light/apds9960.c | 16 +-
> > .../staging/iio/impedance-analyzer/ad5933.c | 23 +-
> > include/linux/iio/buffer-dmaengine.h | 7 +-
> > include/linux/iio/buffer.h | 4 +-
> > include/linux/iio/buffer_impl.h | 21 +-
> > include/linux/iio/iio-opaque.h | 14 +
> > include/linux/iio/iio.h | 5 -
> > include/linux/iio/kfifo_buf.h | 7 +-
> > include/linux/iio/sysfs.h | 3 +
> > include/uapi/linux/iio/buffer.h | 10 +
> > tools/iio/Makefile | 1 +
> > tools/iio/iio_generic_buffer.c | 153 ++++--
> > tools/iio/iio_utils.c | 18 +-
> > tools/iio/iio_utils.h | 8 +-
> > 39 files changed, 856 insertions(+), 442 deletions(-)
> > create mode 100644 include/uapi/linux/iio/buffer.h
> >
>

2021-02-16 11:22:47

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 00/24] iio: core,buffer: add support for multiple IIO buffers per IIO device

On Mon, 15 Feb 2021 16:10:45 +0200
Alexandru Ardelean <[email protected]> wrote:

> On Mon, Feb 15, 2021 at 3:59 PM Jonathan Cameron <[email protected]> wrote:
> >
> > On Mon, 15 Feb 2021 12:40:19 +0200
> > Alexandru Ardelean <[email protected]> wrote:
> >
> > Hi Alex,
> >
> > One thought on this that came up whilst reading through it again.
> > There are several uses for multiple buffers.
> > 1) input vs output buffers
> > 2) A device with separately clocked sampling of different channels.
>
> So, ADI typically has use-cases for 1).
> Mostly RF transceivers.
> Traditionally, these were written (inside the ADI tree) as 2-3 IIO
> devices (1 IIO device for each path,1 for TX and 1-2 for RX).
> With multi-buffer, they can be a single IIO device with 2-3 IIO buffers.
>
> There are some ADI devices that have channels that can run at different speeds.
> But this hasn't been an important use-cases to deal with.
> Typically, a common-rate would be picked [for all channels] and that's that.
> I think some people find this per-channel rates interesting; but let's see.

The nasty ones for multiple speeds tend to be sensor hubs and similar
units where there is heavy processing going on.

Often they are taking in a bunch of physical sensors and
1) Shoving the data straight out unprocessed whenever it shows up.
The raw sensors aren't running in sync, so they drift across each other and
other fun things like that.
2) Doing sensor fusion in batches and kicking out an quaternion every now
and then.

They work on the basis of tagging the samples in a fifo with what they actually
are. Often there is very little control of the flow so we've previously ended
up with the same multi IIO device solution, with a common core redirecting the
flows to the right devices (basically acting as an MFD).

Thankfully those cases don't play well with triggers anyway so we mostly
don't expose them.

From memory, cros_ec and st_lsm6dsx are like this. I think there are a few more.


>
> >
> > For case 2, there is at least sometimes a trigger involved
> > (often data ready) so we probably also need to think long term about how
> > to support multiple current_triggers in one driver.
> >
> > I guess that's something for us to figure out when we need it however.
> >
> > Thanks for you hard work on finally getting this upstream.
> > + Lars (and maybe others) for earlier work on this.
>
> Thank you as well.
>
> >
> > Whilst I've applied all these, they are only exposed in the testing branch
> > of iio.git on kernel.org so far and as such any additional review anyone
> > else wants to give would be welcome!
>
> I feel there may be other users stumbling over this multibuffer
> support, and try it out.
> Often people don't seem to care about multibuffer, until they really need it.
> A bit of chicken-n-egg problem.
> But after being added, people may see it and start to use it.

Agreed. I'm fairly confident that the ABI is fine, so I'm not particularly
worried if we need to make further adjustments as other users show up.

Jonathan

>
> >
> > Jonathan
> >
> > > Changelog v5 -> v6:
> > > * https://lore.kernel.org/linux-iio/[email protected]/T/#t
> > > * merged series 'iio: kfifo: define a devm_iio_kfifo_buffer_setup helper' into this
> > > https://lore.kernel.org/linux-iio/[email protected]/T/#u
> > > * merged patch 'iio: buffer-dma,adi-axi-adc: introduce devm_iio_dmaengine_buffer_setup()' into this
> > > https://lore.kernel.org/linux-iio/[email protected]/T/#u
> > > * patch 'iio: core: wrap iio device & buffer into struct for character devices'
> > > - moved kfree(ib) to be first in iio_chrdev_release() to make the cleanup
> > > order be reversed as the open() order
> > > * patch 'iio: buffer: group attr count and attr alloc'
> > > - sizeof(struct attribute *) -> sizeof(*attr) in kcalloc()
> > > * patch 'iio: core: merge buffer/ & scan_elements/ attributes'
> > > - minor rework in iio_buffer_register_legacy_sysfs_groups() to
> > > use more 'sizeof(*attrs)' in allocations
> > > - fixed typo 'legacy_buffer_el_group' -> 'legacy_buffer_group'
> > > - updated comment about
> > > '/* we only need to link the legacy buffer groups for the first buffer */'
> > > to
> > > '/* we only need to register the legacy groups for the first buffer */'
> > > * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > > - removed 'buf_name' from iio_device_buffer_getfd();
> > > passing "iio:buffer" name; since it should be a class-name
> > > - removed extra-line in drivers/iio/industrial-core.c
> > > - changed init order in iio_device_buffer_getfd() and matched it with reverse in
> > > iio_buffer_chrdev_release()
> > > - calling put_unused_fd() in iio_device_buffer_getfd() if copy_to_user() fails
> > > * added 'iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls'
> > > * patch ' iio: buffer: introduce support for attaching more IIO buffers'
> > > - now handling iio_device_attach_buffer() error returns
> > > - rename iio_buffer_free_sysfs_and_mask() -> iio_buffers_free_sysfs_and_mask()
> > > - rename iio_buffer_alloc_sysfs_and_mask() -> iio_buffer_alloc_sysfs_and_mask()
> > > * patch 'tools: iio: convert iio_generic_buffer to use new IIO buffer API'
> > > - removed extra close() if getfd() ioctl() errors out
> > > - added comment that the sanity check for buffer0 should not be done
> > > under normal operation
> > > - update message from
> > > "This device does not support buffers"
> > > to
> > > "Device does not have this many buffers"
> > >
> > > Changelog v4 -> v5:
> > > * https://lore.kernel.org/linux-iio/[email protected]/T/#t
> > > * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > > don't return -EBUSY in iio_buffer_poll_wrapper(); return 0
> > > __poll_t is unsigned, so returning 0 is the best we can do
> > > Reported-by: kernel test robot <[email protected]>
> > > * patch 'iio: buffer: dmaengine: obtain buffer object from attribute'
> > > removed unused 'indio_dev' variable; seems i missed this initially
> > > * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > > call 'wake_up(buffer->pollq)' in iio_buffer_chrdev_release()
> > >
> > > Changelog v3 -> v4:
> > > * https://lore.kernel.org/linux-iio/[email protected]/
> > > * patch 'docs: ioctl-number.rst: reserve IIO subsystem ioctl() space'
> > > remove 'uapi/' from `uapi/linux/iio/*.h`
> > > * patch 'iio: core: register chardev only if needed'
> > > add commit comment about potentially breaking userspace ABI with chardev removal
> > > * patch 'iio: core: rework iio device group creation'
> > > remove NULL re-init in iio_device_unregister_sysfs() ; memory is being free'd
> > > * patch 'iio: buffer: group attr count and attr alloc'
> > > extend commit comment about the 2 or 1 buffer directores
> > > * patch 'iio: core: merge buffer/ & scan_elements/ attributes'
> > > fixed static checker complaints
> > > - removed unused global
> > > - initialize omitted 'ret = -ENOMEM' on error path
> > > - made iio_buffer_unregister_legacy_sysfs_groups() static
> > > * patch 'iio: buffer: wrap all buffer attributes into iio_dev_attr'
> > > - update some omitted unwindings; seems i forgot a few originally
> > > this was showing up when trying to read from buffer1
> > > * add patch 'iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc func'
> > > * patch 'iio: buffer: introduce support for attaching more IIO buffers'
> > > - removed 'iio_dev_opaque->attached_buffers = NULL' after kfree()
> > > - using 'iio_dev_opaque->attached_buffers_cnt' to check that we have buffers
> > > instead of checking 'indio_dev->buffer'
> > > * patch 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > > - replaced -ENOENT with -ENODEV when buffer index is out of range
> > > * add 'iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()'
> > > * add 'iio: buffer: dmaengine: obtain buffer object from attribute'
> > > * add tools/iio patches for new multibuffer logic
> > > tools: iio: make iioutils_get_type() private in iio_utils
> > > tools: iio: privatize globals and functions in iio_generic_buffer.c file
> > > tools: iio: convert iio_generic_buffer to use new IIO buffer API
> > >
> > > Changelog v2 -> v3:
> > > * https://lore.kernel.org/linux-iio/[email protected]/
> > > * added commit 'docs: ioctl-number.rst: reserve IIO subsystem ioctl() space'
> > > reserving 'i' 0x90-0x9F ioctls for IIO
> > > I did not see any conflicts with others (in the doc)
> > > - related to this, the new IIO_BUFFER_GET_FD_IOCTL is now at 'i' 0x91
> > > * changed approach for creating sysfs buffer directories;
> > > - they are now created as groups on the IIO device; that also means
> > > that the groups array needs to be krealloc-ed and assign later in
> > > the registration
> > > - merged bufferX/ and scan_elementsX/ directories into a single
> > > bufferX/ directory
> > > - for legacy the buffer/ & scan_elements/ directories are kept; but
> > > they're groups objects have been moved on the iio_dev_opaque object
> > > - internally, the iio_dev_attr type is being extended to hold a
> > > reference for an IIO buffer;
> > > = this is great for scan_elements attributes
> > > = and for the rest of the iio_buffer attributes, it means we need to
> > > wrap them into iio_dev_attr
> > >
> > > Changelog v1 -> v2:
> > > * https://lore.kernel.org/linux-iio/[email protected]/
> > > * 'iio: buffer: rework buffer & scan_elements dir creation'
> > > add more doc-strings detailing the reasoning for this change
> > > * 'iio: buffer: re-route scan_elements via it's kobj_type'
> > > move list_del() before the kfree()'s in the list destruction
> > > * 'iio: buffer: introduce support for attaching more IIO buffers'
> > > - changed to 'cnt' variable vs re-using the 'i' for unwinding in
> > > iio_buffer_alloc_sysfs_and_mask()
> > > - removed kfree(old) in iio_device_attach_buffer()
> > > - made iio_device_attach_buffer() an int return; this means that some
> > > follow up patches are needed to make this return value be used;
> > > * 'iio: buffer: add ioctl() to support opening extra buffers for IIO device'
> > > - tested ioctl() with a simple C program; attached to comment;
> > > - changed 'i' variable usage to 'sz' for alloc
> > > - changed logic for buffer0; returning FD 0; userspace should know
> > > that the IIO_BUFFER_GET_FD_IOCTL call returns 0 for buffer0;
> > > this is because I can't find a way to determine the FD of the
> > > ioctl() in the kernel; duplicating an ioctl() for buffer0 is also bad;
> > >
> > >
> > > Alexandru Ardelean (24):
> > > iio: adc: ti_am335x_adc: remove omitted iio_kfifo_free()
> > > iio: kfifo: add devm_iio_kfifo_buffer_setup() helper
> > > iio: make use of devm_iio_kfifo_buffer_setup() helper
> > > iio: accel: sca3000: use devm_iio_kfifo_buffer_setup() helper
> > > iio: kfifo: un-export devm_iio_kfifo_allocate() function
> > > iio: buffer-dma,adi-axi-adc: introduce
> > > devm_iio_dmaengine_buffer_setup()
> > > docs: ioctl-number.rst: reserve IIO subsystem ioctl() space
> > > iio: core: register chardev only if needed
> > > iio: core-trigger: make iio_device_register_trigger_consumer() an int
> > > return
> > > iio: core: rework iio device group creation
> > > iio: buffer: group attr count and attr alloc
> > > iio: core: merge buffer/ & scan_elements/ attributes
> > > iio: add reference to iio buffer on iio_dev_attr
> > > iio: buffer: wrap all buffer attributes into iio_dev_attr
> > > iio: buffer: dmaengine: obtain buffer object from attribute
> > > iio: core: wrap iio device & buffer into struct for character devices
> > > iio: buffer: move __iio_buffer_free_sysfs_and_mask() before alloc
> > > iio: dummy: iio_simple_dummy_buffer: use triggered buffer core calls
> > > iio: buffer: introduce support for attaching more IIO buffers
> > > iio: buffer: add ioctl() to support opening extra buffers for IIO
> > > device
> > > iio: core: rename 'dev' -> 'indio_dev' in iio_device_alloc()
> > > tools: iio: make iioutils_get_type() private in iio_utils
> > > tools: iio: privatize globals and functions in iio_generic_buffer.c
> > > file
> > > tools: iio: convert iio_generic_buffer to use new IIO buffer API
> > >
> > > .../driver-api/driver-model/devres.rst | 3 +-
> > > .../userspace-api/ioctl/ioctl-number.rst | 1 +
> > > drivers/iio/accel/sca3000.c | 19 +-
> > > drivers/iio/accel/ssp_accel_sensor.c | 14 +-
> > > drivers/iio/adc/adi-axi-adc.c | 12 +-
> > > drivers/iio/adc/ina2xx-adc.c | 14 +-
> > > drivers/iio/adc/ti_am335x_adc.c | 24 +-
> > > .../buffer/industrialio-buffer-dmaengine.c | 35 +-
> > > .../buffer/industrialio-triggered-buffer.c | 10 +-
> > > drivers/iio/buffer/kfifo_buf.c | 40 +-
> > > .../cros_ec_sensors/cros_ec_sensors_core.c | 13 +-
> > > drivers/iio/dummy/iio_simple_dummy_buffer.c | 68 +--
> > > drivers/iio/gyro/ssp_gyro_sensor.c | 14 +-
> > > drivers/iio/health/max30100.c | 16 +-
> > > drivers/iio/health/max30102.c | 16 +-
> > > drivers/iio/iio_core.h | 32 +-
> > > drivers/iio/iio_core_trigger.h | 4 +-
> > > .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +-
> > > .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +-
> > > .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +-
> > > drivers/iio/industrialio-buffer.c | 494 ++++++++++++++----
> > > drivers/iio/industrialio-core.c | 113 +++-
> > > drivers/iio/industrialio-event.c | 6 +-
> > > drivers/iio/industrialio-trigger.c | 6 +-
> > > drivers/iio/light/acpi-als.c | 12 +-
> > > drivers/iio/light/apds9960.c | 16 +-
> > > .../staging/iio/impedance-analyzer/ad5933.c | 23 +-
> > > include/linux/iio/buffer-dmaengine.h | 7 +-
> > > include/linux/iio/buffer.h | 4 +-
> > > include/linux/iio/buffer_impl.h | 21 +-
> > > include/linux/iio/iio-opaque.h | 14 +
> > > include/linux/iio/iio.h | 5 -
> > > include/linux/iio/kfifo_buf.h | 7 +-
> > > include/linux/iio/sysfs.h | 3 +
> > > include/uapi/linux/iio/buffer.h | 10 +
> > > tools/iio/Makefile | 1 +
> > > tools/iio/iio_generic_buffer.c | 153 ++++--
> > > tools/iio/iio_utils.c | 18 +-
> > > tools/iio/iio_utils.h | 8 +-
> > > 39 files changed, 856 insertions(+), 442 deletions(-)
> > > create mode 100644 include/uapi/linux/iio/buffer.h
> > >
> >

2021-02-17 00:05:16

by Gwendal Grignou

[permalink] [raw]
Subject: Re: [PATCH v6 03/24] iio: make use of devm_iio_kfifo_buffer_setup() helper

Reviewed-by: Gwendal Grignou <[email protected]>

On Mon, Feb 15, 2021 at 4:11 AM Jonathan Cameron <[email protected]> wrote:
>
> On Mon, 15 Feb 2021 12:40:22 +0200
> Alexandru Ardelean <[email protected]> wrote:
>
> > All drivers that already call devm_iio_kfifo_allocate() &
> > iio_device_attach_buffer() are simple to convert to
> > iio_device_attach_kfifo_buffer() in a single go.
> >
> > This change does that; the unwind order is preserved.
> > What is important, is that the devm_iio_kfifo_buffer_setup() be called
> > after the indio_dev->modes is assigned, to make sure that
> > INDIO_BUFFER_SOFTWARE flag is set and not overridden by the assignment to
> > indio_dev->modes.
> >
> > Also, the INDIO_BUFFER_SOFTWARE has been removed from the assignments of
> > 'indio_dev->modes' because it is set by devm_iio_kfifo_buffer_setup().
> >
> > Signed-off-by: Alexandru Ardelean <[email protected]>
> I 'think' this one is is in the obviously correct category so I've
> applied it to the togreg branch of iio.git and pushed out as testing.
> Note there is still plenty of time for any additional feedback,
> particularly as the CC list was a little sparse.
>
> I've +CCd those who I know are still active maintainers of some
> of the affected drivers.
>
> Jonathan
>
> > ---
> > drivers/iio/accel/ssp_accel_sensor.c | 14 ++++-------
> > drivers/iio/adc/ina2xx-adc.c | 14 +++++------
> > drivers/iio/adc/ti_am335x_adc.c | 18 ++++-----------
> > .../cros_ec_sensors/cros_ec_sensors_core.c | 13 ++++-------
> > drivers/iio/gyro/ssp_gyro_sensor.c | 14 ++++-------
> > drivers/iio/health/max30100.c | 16 ++++++-------
> > drivers/iio/health/max30102.c | 16 ++++++-------
> > .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +++++------
> > .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +++++------
> > .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +++++-------
> > drivers/iio/light/acpi-als.c | 12 ++++------
> > drivers/iio/light/apds9960.c | 16 ++++++-------
> > .../staging/iio/impedance-analyzer/ad5933.c | 23 ++++---------------
> > 13 files changed, 74 insertions(+), 124 deletions(-)
> >
> > diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
> > index 474477e91b5e..04dcb2b657ee 100644
> > --- a/drivers/iio/accel/ssp_accel_sensor.c
> > +++ b/drivers/iio/accel/ssp_accel_sensor.c
> > @@ -96,7 +96,6 @@ static int ssp_accel_probe(struct platform_device *pdev)
> > int ret;
> > struct iio_dev *indio_dev;
> > struct ssp_sensor_data *spd;
> > - struct iio_buffer *buffer;
> >
> > indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
> > if (!indio_dev)
> > @@ -109,18 +108,15 @@ static int ssp_accel_probe(struct platform_device *pdev)
> >
> > indio_dev->name = ssp_accel_device_name;
> > indio_dev->info = &ssp_accel_iio_info;
> > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > indio_dev->channels = ssp_acc_channels;
> > indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
> > indio_dev->available_scan_masks = ssp_accel_scan_mask;
> >
> > - buffer = devm_iio_kfifo_allocate(&pdev->dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > -
> > - indio_dev->setup_ops = &ssp_accel_buffer_ops;
> > + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &ssp_accel_buffer_ops);
> > + if (ret)
> > + return ret;
> >
> > platform_set_drvdata(pdev, indio_dev);
> >
> > diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
> > index b573ec60a8b8..2ae54258b221 100644
> > --- a/drivers/iio/adc/ina2xx-adc.c
> > +++ b/drivers/iio/adc/ina2xx-adc.c
> > @@ -953,7 +953,6 @@ static int ina2xx_probe(struct i2c_client *client,
> > {
> > struct ina2xx_chip_info *chip;
> > struct iio_dev *indio_dev;
> > - struct iio_buffer *buffer;
> > unsigned int val;
> > enum ina2xx_ids type;
> > int ret;
> > @@ -1017,7 +1016,7 @@ static int ina2xx_probe(struct i2c_client *client,
> > return ret;
> > }
> >
> > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > if (id->driver_data == ina226) {
> > indio_dev->channels = ina226_channels;
> > indio_dev->num_channels = ARRAY_SIZE(ina226_channels);
> > @@ -1028,13 +1027,12 @@ static int ina2xx_probe(struct i2c_client *client,
> > indio_dev->info = &ina219_info;
> > }
> > indio_dev->name = id->name;
> > - indio_dev->setup_ops = &ina2xx_setup_ops;
> >
> > - buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &ina2xx_setup_ops);
> > + if (ret)
> > + return ret;
> >
> > return iio_device_register(indio_dev);
> > }
> > diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> > index e946903b0993..855cc2d64ac8 100644
> > --- a/drivers/iio/adc/ti_am335x_adc.c
> > +++ b/drivers/iio/adc/ti_am335x_adc.c
> > @@ -385,24 +385,16 @@ static int tiadc_iio_buffered_hardware_setup(struct device *dev,
> > unsigned long flags,
> > const struct iio_buffer_setup_ops *setup_ops)
> > {
> > - struct iio_buffer *buffer;
> > int ret;
> >
> > - buffer = devm_iio_kfifo_allocate(dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > -
> > - ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
> > - flags, indio_dev->name, indio_dev);
> > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + setup_ops);
> > if (ret)
> > return ret;
> >
> > - indio_dev->setup_ops = setup_ops;
> > - indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
> > -
> > - return 0;
> > + return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
> > + flags, indio_dev->name, indio_dev);
> > }
> >
> > static const char * const chan_name_ain[] = {
> > diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > index c833ec0ef214..f8824afe595e 100644
> > --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > @@ -334,14 +334,11 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
> > * We can not use trigger here, as events are generated
> > * as soon as sample_frequency is set.
> > */
> > - struct iio_buffer *buffer;
> > -
> > - buffer = devm_iio_kfifo_allocate(dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + NULL);
> > + if (ret)
> > + return ret;
> >
> > ret = cros_ec_sensorhub_register_push_data(
> > sensor_hub, sensor_platform->sensor_num,
> > diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
> > index ac7c170a20de..46ed12771d2f 100644
> > --- a/drivers/iio/gyro/ssp_gyro_sensor.c
> > +++ b/drivers/iio/gyro/ssp_gyro_sensor.c
> > @@ -96,7 +96,6 @@ static int ssp_gyro_probe(struct platform_device *pdev)
> > int ret;
> > struct iio_dev *indio_dev;
> > struct ssp_sensor_data *spd;
> > - struct iio_buffer *buffer;
> >
> > indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
> > if (!indio_dev)
> > @@ -109,18 +108,15 @@ static int ssp_gyro_probe(struct platform_device *pdev)
> >
> > indio_dev->name = ssp_gyro_name;
> > indio_dev->info = &ssp_gyro_iio_info;
> > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > indio_dev->channels = ssp_gyro_channels;
> > indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
> > indio_dev->available_scan_masks = ssp_gyro_scan_mask;
> >
> > - buffer = devm_iio_kfifo_allocate(&pdev->dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > -
> > - indio_dev->setup_ops = &ssp_gyro_buffer_ops;
> > + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &ssp_gyro_buffer_ops);
> > + if (ret)
> > + return ret;
> >
> > platform_set_drvdata(pdev, indio_dev);
> >
> > diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
> > index 38aa2030f3c6..36ba7611d9ce 100644
> > --- a/drivers/iio/health/max30100.c
> > +++ b/drivers/iio/health/max30100.c
> > @@ -418,7 +418,6 @@ static int max30100_probe(struct i2c_client *client,
> > const struct i2c_device_id *id)
> > {
> > struct max30100_data *data;
> > - struct iio_buffer *buffer;
> > struct iio_dev *indio_dev;
> > int ret;
> >
> > @@ -426,19 +425,18 @@ static int max30100_probe(struct i2c_client *client,
> > if (!indio_dev)
> > return -ENOMEM;
> >
> > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > -
> > indio_dev->name = MAX30100_DRV_NAME;
> > indio_dev->channels = max30100_channels;
> > indio_dev->info = &max30100_info;
> > indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
> > indio_dev->available_scan_masks = max30100_scan_masks;
> > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > - indio_dev->setup_ops = &max30100_buffer_setup_ops;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > +
> > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &max30100_buffer_setup_ops);
> > + if (ret)
> > + return ret;
> >
> > data = iio_priv(indio_dev);
> > data->indio_dev = indio_dev;
> > diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
> > index b35557a54ee2..2292876c55e2 100644
> > --- a/drivers/iio/health/max30102.c
> > +++ b/drivers/iio/health/max30102.c
> > @@ -506,7 +506,6 @@ static int max30102_probe(struct i2c_client *client,
> > const struct i2c_device_id *id)
> > {
> > struct max30102_data *data;
> > - struct iio_buffer *buffer;
> > struct iio_dev *indio_dev;
> > int ret;
> > unsigned int reg;
> > @@ -515,16 +514,9 @@ static int max30102_probe(struct i2c_client *client,
> > if (!indio_dev)
> > return -ENOMEM;
> >
> > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > -
> > indio_dev->name = MAX30102_DRV_NAME;
> > indio_dev->info = &max30102_info;
> > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > - indio_dev->setup_ops = &max30102_buffer_setup_ops;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> >
> > data = iio_priv(indio_dev);
> > data->indio_dev = indio_dev;
> > @@ -549,6 +541,12 @@ static int max30102_probe(struct i2c_client *client,
> > return -ENODEV;
> > }
> >
> > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &max30102_buffer_setup_ops);
> > + if (ret)
> > + return ret;
> > +
> > data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config);
> > if (IS_ERR(data->regmap)) {
> > dev_err(&client->dev, "regmap initialization failed\n");
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > index 3441b0d61c5d..383cc3250342 100644
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > @@ -709,7 +709,6 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
> > const char *name;
> > struct inv_icm42600_timestamp *ts;
> > struct iio_dev *indio_dev;
> > - struct iio_buffer *buffer;
> > int ret;
> >
> > name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name);
> > @@ -720,23 +719,22 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
> > if (!indio_dev)
> > return ERR_PTR(-ENOMEM);
> >
> > - buffer = devm_iio_kfifo_allocate(dev);
> > - if (!buffer)
> > - return ERR_PTR(-ENOMEM);
> > -
> > ts = iio_priv(indio_dev);
> > inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));
> >
> > iio_device_set_drvdata(indio_dev, st);
> > indio_dev->name = name;
> > indio_dev->info = &inv_icm42600_accel_info;
> > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > indio_dev->channels = inv_icm42600_accel_channels;
> > indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels);
> > indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks;
> > - indio_dev->setup_ops = &inv_icm42600_buffer_ops;
> >
> > - iio_device_attach_buffer(indio_dev, buffer);
> > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &inv_icm42600_buffer_ops);
> > + if (ret)
> > + return ERR_PTR(ret);
> >
> > ret = devm_iio_device_register(dev, indio_dev);
> > if (ret)
> > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > index aee7b9ff4bf4..cec1dd0e0464 100644
> > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > @@ -720,7 +720,6 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> > const char *name;
> > struct inv_icm42600_timestamp *ts;
> > struct iio_dev *indio_dev;
> > - struct iio_buffer *buffer;
> > int ret;
> >
> > name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
> > @@ -731,23 +730,23 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> > if (!indio_dev)
> > return ERR_PTR(-ENOMEM);
> >
> > - buffer = devm_iio_kfifo_allocate(dev);
> > - if (!buffer)
> > - return ERR_PTR(-ENOMEM);
> > -
> > ts = iio_priv(indio_dev);
> > inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));
> >
> > iio_device_set_drvdata(indio_dev, st);
> > indio_dev->name = name;
> > indio_dev->info = &inv_icm42600_gyro_info;
> > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > indio_dev->channels = inv_icm42600_gyro_channels;
> > indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
> > indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks;
> > indio_dev->setup_ops = &inv_icm42600_buffer_ops;
> >
> > - iio_device_attach_buffer(indio_dev, buffer);
> > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &inv_icm42600_buffer_ops);
> > + if (ret)
> > + return ERR_PTR(ret);
> >
> > ret = devm_iio_device_register(dev, indio_dev);
> > if (ret)
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > index f1103ecedd64..16730a780964 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > @@ -739,20 +739,17 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
> >
> > int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
> > {
> > - struct iio_buffer *buffer;
> > - int i;
> > + int i, ret;
> >
> > for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > if (!hw->iio_devs[i])
> > continue;
> >
> > - buffer = devm_iio_kfifo_allocate(hw->dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(hw->iio_devs[i], buffer);
> > - hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
> > - hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
> > + ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
> > + INDIO_BUFFER_SOFTWARE,
> > + &st_lsm6dsx_buffer_ops);
> > + if (ret)
> > + return ret;
> > }
> >
> > return 0;
> > diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
> > index 2be7180e2cbf..f8e547fd35e7 100644
> > --- a/drivers/iio/light/acpi-als.c
> > +++ b/drivers/iio/light/acpi-als.c
> > @@ -165,7 +165,7 @@ static int acpi_als_add(struct acpi_device *device)
> > {
> > struct acpi_als *als;
> > struct iio_dev *indio_dev;
> > - struct iio_buffer *buffer;
> > + int ret;
> >
> > indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
> > if (!indio_dev)
> > @@ -179,15 +179,13 @@ static int acpi_als_add(struct acpi_device *device)
> >
> > indio_dev->name = ACPI_ALS_DEVICE_NAME;
> > indio_dev->info = &acpi_als_info;
> > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > indio_dev->channels = acpi_als_channels;
> > indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
> >
> > - buffer = devm_iio_kfifo_allocate(&device->dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > + ret = devm_iio_kfifo_buffer_setup(&device->dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE, NULL);
> > + if (ret)
> > + return ret;
> >
> > return devm_iio_device_register(&device->dev, indio_dev);
> > }
> > diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
> > index df0647856e5d..4141c0fa7bc4 100644
> > --- a/drivers/iio/light/apds9960.c
> > +++ b/drivers/iio/light/apds9960.c
> > @@ -988,7 +988,6 @@ static int apds9960_probe(struct i2c_client *client,
> > const struct i2c_device_id *id)
> > {
> > struct apds9960_data *data;
> > - struct iio_buffer *buffer;
> > struct iio_dev *indio_dev;
> > int ret;
> >
> > @@ -996,19 +995,18 @@ static int apds9960_probe(struct i2c_client *client,
> > if (!indio_dev)
> > return -ENOMEM;
> >
> > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > -
> > indio_dev->info = &apds9960_info;
> > indio_dev->name = APDS9960_DRV_NAME;
> > indio_dev->channels = apds9960_channels;
> > indio_dev->num_channels = ARRAY_SIZE(apds9960_channels);
> > indio_dev->available_scan_masks = apds9960_scan_masks;
> > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > - indio_dev->setup_ops = &apds9960_buffer_setup_ops;
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > +
> > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &apds9960_buffer_setup_ops);
> > + if (ret)
> > + return ret;
> >
> > data = iio_priv(indio_dev);
> > i2c_set_clientdata(client, indio_dev);
> > diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
> > index dba78896ea8f..793918e1c45f 100644
> > --- a/drivers/staging/iio/impedance-analyzer/ad5933.c
> > +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
> > @@ -602,23 +602,6 @@ static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = {
> > .postdisable = ad5933_ring_postdisable,
> > };
> >
> > -static int ad5933_register_ring_funcs_and_init(struct device *dev,
> > - struct iio_dev *indio_dev)
> > -{
> > - struct iio_buffer *buffer;
> > -
> > - buffer = devm_iio_kfifo_allocate(dev);
> > - if (!buffer)
> > - return -ENOMEM;
> > -
> > - iio_device_attach_buffer(indio_dev, buffer);
> > -
> > - /* Ring buffer functions - here trigger setup related */
> > - indio_dev->setup_ops = &ad5933_ring_setup_ops;
> > -
> > - return 0;
> > -}
> > -
> > static void ad5933_work(struct work_struct *work)
> > {
> > struct ad5933_state *st = container_of(work,
> > @@ -761,11 +744,13 @@ static int ad5933_probe(struct i2c_client *client,
> >
> > indio_dev->info = &ad5933_info;
> > indio_dev->name = id->name;
> > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > + indio_dev->modes = INDIO_DIRECT_MODE;
> > indio_dev->channels = ad5933_channels;
> > indio_dev->num_channels = ARRAY_SIZE(ad5933_channels);
> >
> > - ret = ad5933_register_ring_funcs_and_init(&client->dev, indio_dev);
> > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > + INDIO_BUFFER_SOFTWARE,
> > + &ad5933_ring_setup_ops);
> > if (ret)
> > return ret;
> >
>

2021-02-18 10:01:22

by Matt Ranostay

[permalink] [raw]
Subject: Re: [PATCH v6 03/24] iio: make use of devm_iio_kfifo_buffer_setup() helper

On Tue, Feb 16, 2021 at 3:46 PM Gwendal Grignou <[email protected]> wrote:
>
> Reviewed-by: Gwendal Grignou <[email protected]>

Looks fine to me as well.

Reviewed-by: Matt Ranostay <[email protected]>

>
> On Mon, Feb 15, 2021 at 4:11 AM Jonathan Cameron <[email protected]> wrote:
> >
> > On Mon, 15 Feb 2021 12:40:22 +0200
> > Alexandru Ardelean <[email protected]> wrote:
> >
> > > All drivers that already call devm_iio_kfifo_allocate() &
> > > iio_device_attach_buffer() are simple to convert to
> > > iio_device_attach_kfifo_buffer() in a single go.
> > >
> > > This change does that; the unwind order is preserved.
> > > What is important, is that the devm_iio_kfifo_buffer_setup() be called
> > > after the indio_dev->modes is assigned, to make sure that
> > > INDIO_BUFFER_SOFTWARE flag is set and not overridden by the assignment to
> > > indio_dev->modes.
> > >
> > > Also, the INDIO_BUFFER_SOFTWARE has been removed from the assignments of
> > > 'indio_dev->modes' because it is set by devm_iio_kfifo_buffer_setup().
> > >
> > > Signed-off-by: Alexandru Ardelean <[email protected]>
> > I 'think' this one is is in the obviously correct category so I've
> > applied it to the togreg branch of iio.git and pushed out as testing.
> > Note there is still plenty of time for any additional feedback,
> > particularly as the CC list was a little sparse.
> >
> > I've +CCd those who I know are still active maintainers of some
> > of the affected drivers.
> >
> > Jonathan
> >
> > > ---
> > > drivers/iio/accel/ssp_accel_sensor.c | 14 ++++-------
> > > drivers/iio/adc/ina2xx-adc.c | 14 +++++------
> > > drivers/iio/adc/ti_am335x_adc.c | 18 ++++-----------
> > > .../cros_ec_sensors/cros_ec_sensors_core.c | 13 ++++-------
> > > drivers/iio/gyro/ssp_gyro_sensor.c | 14 ++++-------
> > > drivers/iio/health/max30100.c | 16 ++++++-------
> > > drivers/iio/health/max30102.c | 16 ++++++-------
> > > .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +++++------
> > > .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +++++------
> > > .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +++++-------
> > > drivers/iio/light/acpi-als.c | 12 ++++------
> > > drivers/iio/light/apds9960.c | 16 ++++++-------
> > > .../staging/iio/impedance-analyzer/ad5933.c | 23 ++++---------------
> > > 13 files changed, 74 insertions(+), 124 deletions(-)
> > >
> > > diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
> > > index 474477e91b5e..04dcb2b657ee 100644
> > > --- a/drivers/iio/accel/ssp_accel_sensor.c
> > > +++ b/drivers/iio/accel/ssp_accel_sensor.c
> > > @@ -96,7 +96,6 @@ static int ssp_accel_probe(struct platform_device *pdev)
> > > int ret;
> > > struct iio_dev *indio_dev;
> > > struct ssp_sensor_data *spd;
> > > - struct iio_buffer *buffer;
> > >
> > > indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
> > > if (!indio_dev)
> > > @@ -109,18 +108,15 @@ static int ssp_accel_probe(struct platform_device *pdev)
> > >
> > > indio_dev->name = ssp_accel_device_name;
> > > indio_dev->info = &ssp_accel_iio_info;
> > > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > > indio_dev->channels = ssp_acc_channels;
> > > indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
> > > indio_dev->available_scan_masks = ssp_accel_scan_mask;
> > >
> > > - buffer = devm_iio_kfifo_allocate(&pdev->dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > -
> > > - indio_dev->setup_ops = &ssp_accel_buffer_ops;
> > > + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &ssp_accel_buffer_ops);
> > > + if (ret)
> > > + return ret;
> > >
> > > platform_set_drvdata(pdev, indio_dev);
> > >
> > > diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
> > > index b573ec60a8b8..2ae54258b221 100644
> > > --- a/drivers/iio/adc/ina2xx-adc.c
> > > +++ b/drivers/iio/adc/ina2xx-adc.c
> > > @@ -953,7 +953,6 @@ static int ina2xx_probe(struct i2c_client *client,
> > > {
> > > struct ina2xx_chip_info *chip;
> > > struct iio_dev *indio_dev;
> > > - struct iio_buffer *buffer;
> > > unsigned int val;
> > > enum ina2xx_ids type;
> > > int ret;
> > > @@ -1017,7 +1016,7 @@ static int ina2xx_probe(struct i2c_client *client,
> > > return ret;
> > > }
> > >
> > > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > if (id->driver_data == ina226) {
> > > indio_dev->channels = ina226_channels;
> > > indio_dev->num_channels = ARRAY_SIZE(ina226_channels);
> > > @@ -1028,13 +1027,12 @@ static int ina2xx_probe(struct i2c_client *client,
> > > indio_dev->info = &ina219_info;
> > > }
> > > indio_dev->name = id->name;
> > > - indio_dev->setup_ops = &ina2xx_setup_ops;
> > >
> > > - buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &ina2xx_setup_ops);
> > > + if (ret)
> > > + return ret;
> > >
> > > return iio_device_register(indio_dev);
> > > }
> > > diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> > > index e946903b0993..855cc2d64ac8 100644
> > > --- a/drivers/iio/adc/ti_am335x_adc.c
> > > +++ b/drivers/iio/adc/ti_am335x_adc.c
> > > @@ -385,24 +385,16 @@ static int tiadc_iio_buffered_hardware_setup(struct device *dev,
> > > unsigned long flags,
> > > const struct iio_buffer_setup_ops *setup_ops)
> > > {
> > > - struct iio_buffer *buffer;
> > > int ret;
> > >
> > > - buffer = devm_iio_kfifo_allocate(dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > -
> > > - ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
> > > - flags, indio_dev->name, indio_dev);
> > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + setup_ops);
> > > if (ret)
> > > return ret;
> > >
> > > - indio_dev->setup_ops = setup_ops;
> > > - indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
> > > -
> > > - return 0;
> > > + return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
> > > + flags, indio_dev->name, indio_dev);
> > > }
> > >
> > > static const char * const chan_name_ain[] = {
> > > diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > > index c833ec0ef214..f8824afe595e 100644
> > > --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > > +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > > @@ -334,14 +334,11 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
> > > * We can not use trigger here, as events are generated
> > > * as soon as sample_frequency is set.
> > > */
> > > - struct iio_buffer *buffer;
> > > -
> > > - buffer = devm_iio_kfifo_allocate(dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + NULL);
> > > + if (ret)
> > > + return ret;
> > >
> > > ret = cros_ec_sensorhub_register_push_data(
> > > sensor_hub, sensor_platform->sensor_num,
> > > diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
> > > index ac7c170a20de..46ed12771d2f 100644
> > > --- a/drivers/iio/gyro/ssp_gyro_sensor.c
> > > +++ b/drivers/iio/gyro/ssp_gyro_sensor.c
> > > @@ -96,7 +96,6 @@ static int ssp_gyro_probe(struct platform_device *pdev)
> > > int ret;
> > > struct iio_dev *indio_dev;
> > > struct ssp_sensor_data *spd;
> > > - struct iio_buffer *buffer;
> > >
> > > indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
> > > if (!indio_dev)
> > > @@ -109,18 +108,15 @@ static int ssp_gyro_probe(struct platform_device *pdev)
> > >
> > > indio_dev->name = ssp_gyro_name;
> > > indio_dev->info = &ssp_gyro_iio_info;
> > > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > > indio_dev->channels = ssp_gyro_channels;
> > > indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
> > > indio_dev->available_scan_masks = ssp_gyro_scan_mask;
> > >
> > > - buffer = devm_iio_kfifo_allocate(&pdev->dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > -
> > > - indio_dev->setup_ops = &ssp_gyro_buffer_ops;
> > > + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &ssp_gyro_buffer_ops);
> > > + if (ret)
> > > + return ret;
> > >
> > > platform_set_drvdata(pdev, indio_dev);
> > >
> > > diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
> > > index 38aa2030f3c6..36ba7611d9ce 100644
> > > --- a/drivers/iio/health/max30100.c
> > > +++ b/drivers/iio/health/max30100.c
> > > @@ -418,7 +418,6 @@ static int max30100_probe(struct i2c_client *client,
> > > const struct i2c_device_id *id)
> > > {
> > > struct max30100_data *data;
> > > - struct iio_buffer *buffer;
> > > struct iio_dev *indio_dev;
> > > int ret;
> > >
> > > @@ -426,19 +425,18 @@ static int max30100_probe(struct i2c_client *client,
> > > if (!indio_dev)
> > > return -ENOMEM;
> > >
> > > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > -
> > > indio_dev->name = MAX30100_DRV_NAME;
> > > indio_dev->channels = max30100_channels;
> > > indio_dev->info = &max30100_info;
> > > indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
> > > indio_dev->available_scan_masks = max30100_scan_masks;
> > > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > > - indio_dev->setup_ops = &max30100_buffer_setup_ops;
> > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > +
> > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &max30100_buffer_setup_ops);
> > > + if (ret)
> > > + return ret;
> > >
> > > data = iio_priv(indio_dev);
> > > data->indio_dev = indio_dev;
> > > diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
> > > index b35557a54ee2..2292876c55e2 100644
> > > --- a/drivers/iio/health/max30102.c
> > > +++ b/drivers/iio/health/max30102.c
> > > @@ -506,7 +506,6 @@ static int max30102_probe(struct i2c_client *client,
> > > const struct i2c_device_id *id)
> > > {
> > > struct max30102_data *data;
> > > - struct iio_buffer *buffer;
> > > struct iio_dev *indio_dev;
> > > int ret;
> > > unsigned int reg;
> > > @@ -515,16 +514,9 @@ static int max30102_probe(struct i2c_client *client,
> > > if (!indio_dev)
> > > return -ENOMEM;
> > >
> > > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > -
> > > indio_dev->name = MAX30102_DRV_NAME;
> > > indio_dev->info = &max30102_info;
> > > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > > - indio_dev->setup_ops = &max30102_buffer_setup_ops;
> > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > >
> > > data = iio_priv(indio_dev);
> > > data->indio_dev = indio_dev;
> > > @@ -549,6 +541,12 @@ static int max30102_probe(struct i2c_client *client,
> > > return -ENODEV;
> > > }
> > >
> > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &max30102_buffer_setup_ops);
> > > + if (ret)
> > > + return ret;
> > > +
> > > data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config);
> > > if (IS_ERR(data->regmap)) {
> > > dev_err(&client->dev, "regmap initialization failed\n");
> > > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > > index 3441b0d61c5d..383cc3250342 100644
> > > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > > @@ -709,7 +709,6 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
> > > const char *name;
> > > struct inv_icm42600_timestamp *ts;
> > > struct iio_dev *indio_dev;
> > > - struct iio_buffer *buffer;
> > > int ret;
> > >
> > > name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name);
> > > @@ -720,23 +719,22 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
> > > if (!indio_dev)
> > > return ERR_PTR(-ENOMEM);
> > >
> > > - buffer = devm_iio_kfifo_allocate(dev);
> > > - if (!buffer)
> > > - return ERR_PTR(-ENOMEM);
> > > -
> > > ts = iio_priv(indio_dev);
> > > inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));
> > >
> > > iio_device_set_drvdata(indio_dev, st);
> > > indio_dev->name = name;
> > > indio_dev->info = &inv_icm42600_accel_info;
> > > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > indio_dev->channels = inv_icm42600_accel_channels;
> > > indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels);
> > > indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks;
> > > - indio_dev->setup_ops = &inv_icm42600_buffer_ops;
> > >
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &inv_icm42600_buffer_ops);
> > > + if (ret)
> > > + return ERR_PTR(ret);
> > >
> > > ret = devm_iio_device_register(dev, indio_dev);
> > > if (ret)
> > > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > > index aee7b9ff4bf4..cec1dd0e0464 100644
> > > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > > @@ -720,7 +720,6 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> > > const char *name;
> > > struct inv_icm42600_timestamp *ts;
> > > struct iio_dev *indio_dev;
> > > - struct iio_buffer *buffer;
> > > int ret;
> > >
> > > name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
> > > @@ -731,23 +730,23 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> > > if (!indio_dev)
> > > return ERR_PTR(-ENOMEM);
> > >
> > > - buffer = devm_iio_kfifo_allocate(dev);
> > > - if (!buffer)
> > > - return ERR_PTR(-ENOMEM);
> > > -
> > > ts = iio_priv(indio_dev);
> > > inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));
> > >
> > > iio_device_set_drvdata(indio_dev, st);
> > > indio_dev->name = name;
> > > indio_dev->info = &inv_icm42600_gyro_info;
> > > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > indio_dev->channels = inv_icm42600_gyro_channels;
> > > indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
> > > indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks;
> > > indio_dev->setup_ops = &inv_icm42600_buffer_ops;
> > >
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &inv_icm42600_buffer_ops);
> > > + if (ret)
> > > + return ERR_PTR(ret);
> > >
> > > ret = devm_iio_device_register(dev, indio_dev);
> > > if (ret)
> > > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > index f1103ecedd64..16730a780964 100644
> > > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > @@ -739,20 +739,17 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
> > >
> > > int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
> > > {
> > > - struct iio_buffer *buffer;
> > > - int i;
> > > + int i, ret;
> > >
> > > for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > if (!hw->iio_devs[i])
> > > continue;
> > >
> > > - buffer = devm_iio_kfifo_allocate(hw->dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(hw->iio_devs[i], buffer);
> > > - hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
> > > - hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
> > > + ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &st_lsm6dsx_buffer_ops);
> > > + if (ret)
> > > + return ret;
> > > }
> > >
> > > return 0;
> > > diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
> > > index 2be7180e2cbf..f8e547fd35e7 100644
> > > --- a/drivers/iio/light/acpi-als.c
> > > +++ b/drivers/iio/light/acpi-als.c
> > > @@ -165,7 +165,7 @@ static int acpi_als_add(struct acpi_device *device)
> > > {
> > > struct acpi_als *als;
> > > struct iio_dev *indio_dev;
> > > - struct iio_buffer *buffer;
> > > + int ret;
> > >
> > > indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
> > > if (!indio_dev)
> > > @@ -179,15 +179,13 @@ static int acpi_als_add(struct acpi_device *device)
> > >
> > > indio_dev->name = ACPI_ALS_DEVICE_NAME;
> > > indio_dev->info = &acpi_als_info;
> > > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > > indio_dev->channels = acpi_als_channels;
> > > indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
> > >
> > > - buffer = devm_iio_kfifo_allocate(&device->dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > + ret = devm_iio_kfifo_buffer_setup(&device->dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE, NULL);
> > > + if (ret)
> > > + return ret;
> > >
> > > return devm_iio_device_register(&device->dev, indio_dev);
> > > }
> > > diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
> > > index df0647856e5d..4141c0fa7bc4 100644
> > > --- a/drivers/iio/light/apds9960.c
> > > +++ b/drivers/iio/light/apds9960.c
> > > @@ -988,7 +988,6 @@ static int apds9960_probe(struct i2c_client *client,
> > > const struct i2c_device_id *id)
> > > {
> > > struct apds9960_data *data;
> > > - struct iio_buffer *buffer;
> > > struct iio_dev *indio_dev;
> > > int ret;
> > >
> > > @@ -996,19 +995,18 @@ static int apds9960_probe(struct i2c_client *client,
> > > if (!indio_dev)
> > > return -ENOMEM;
> > >
> > > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > -
> > > indio_dev->info = &apds9960_info;
> > > indio_dev->name = APDS9960_DRV_NAME;
> > > indio_dev->channels = apds9960_channels;
> > > indio_dev->num_channels = ARRAY_SIZE(apds9960_channels);
> > > indio_dev->available_scan_masks = apds9960_scan_masks;
> > > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > > - indio_dev->setup_ops = &apds9960_buffer_setup_ops;
> > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > +
> > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &apds9960_buffer_setup_ops);
> > > + if (ret)
> > > + return ret;
> > >
> > > data = iio_priv(indio_dev);
> > > i2c_set_clientdata(client, indio_dev);
> > > diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
> > > index dba78896ea8f..793918e1c45f 100644
> > > --- a/drivers/staging/iio/impedance-analyzer/ad5933.c
> > > +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
> > > @@ -602,23 +602,6 @@ static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = {
> > > .postdisable = ad5933_ring_postdisable,
> > > };
> > >
> > > -static int ad5933_register_ring_funcs_and_init(struct device *dev,
> > > - struct iio_dev *indio_dev)
> > > -{
> > > - struct iio_buffer *buffer;
> > > -
> > > - buffer = devm_iio_kfifo_allocate(dev);
> > > - if (!buffer)
> > > - return -ENOMEM;
> > > -
> > > - iio_device_attach_buffer(indio_dev, buffer);
> > > -
> > > - /* Ring buffer functions - here trigger setup related */
> > > - indio_dev->setup_ops = &ad5933_ring_setup_ops;
> > > -
> > > - return 0;
> > > -}
> > > -
> > > static void ad5933_work(struct work_struct *work)
> > > {
> > > struct ad5933_state *st = container_of(work,
> > > @@ -761,11 +744,13 @@ static int ad5933_probe(struct i2c_client *client,
> > >
> > > indio_dev->info = &ad5933_info;
> > > indio_dev->name = id->name;
> > > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > indio_dev->channels = ad5933_channels;
> > > indio_dev->num_channels = ARRAY_SIZE(ad5933_channels);
> > >
> > > - ret = ad5933_register_ring_funcs_and_init(&client->dev, indio_dev);
> > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > + INDIO_BUFFER_SOFTWARE,
> > > + &ad5933_ring_setup_ops);
> > > if (ret)
> > > return ret;
> > >
> >

2021-02-18 16:19:54

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 03/24] iio: make use of devm_iio_kfifo_buffer_setup() helper

On Thu, 18 Feb 2021 00:22:38 -0800
Matt Ranostay <[email protected]> wrote:

> On Tue, Feb 16, 2021 at 3:46 PM Gwendal Grignou <[email protected]> wrote:
> >
> > Reviewed-by: Gwendal Grignou <[email protected]>
>
> Looks fine to me as well.
>
> Reviewed-by: Matt Ranostay <[email protected]>
Thanks. Both tags added.

Jonathan

>
> >
> > On Mon, Feb 15, 2021 at 4:11 AM Jonathan Cameron <[email protected]> wrote:
> > >
> > > On Mon, 15 Feb 2021 12:40:22 +0200
> > > Alexandru Ardelean <[email protected]> wrote:
> > >
> > > > All drivers that already call devm_iio_kfifo_allocate() &
> > > > iio_device_attach_buffer() are simple to convert to
> > > > iio_device_attach_kfifo_buffer() in a single go.
> > > >
> > > > This change does that; the unwind order is preserved.
> > > > What is important, is that the devm_iio_kfifo_buffer_setup() be called
> > > > after the indio_dev->modes is assigned, to make sure that
> > > > INDIO_BUFFER_SOFTWARE flag is set and not overridden by the assignment to
> > > > indio_dev->modes.
> > > >
> > > > Also, the INDIO_BUFFER_SOFTWARE has been removed from the assignments of
> > > > 'indio_dev->modes' because it is set by devm_iio_kfifo_buffer_setup().
> > > >
> > > > Signed-off-by: Alexandru Ardelean <[email protected]>
> > > I 'think' this one is is in the obviously correct category so I've
> > > applied it to the togreg branch of iio.git and pushed out as testing.
> > > Note there is still plenty of time for any additional feedback,
> > > particularly as the CC list was a little sparse.
> > >
> > > I've +CCd those who I know are still active maintainers of some
> > > of the affected drivers.
> > >
> > > Jonathan
> > >
> > > > ---
> > > > drivers/iio/accel/ssp_accel_sensor.c | 14 ++++-------
> > > > drivers/iio/adc/ina2xx-adc.c | 14 +++++------
> > > > drivers/iio/adc/ti_am335x_adc.c | 18 ++++-----------
> > > > .../cros_ec_sensors/cros_ec_sensors_core.c | 13 ++++-------
> > > > drivers/iio/gyro/ssp_gyro_sensor.c | 14 ++++-------
> > > > drivers/iio/health/max30100.c | 16 ++++++-------
> > > > drivers/iio/health/max30102.c | 16 ++++++-------
> > > > .../iio/imu/inv_icm42600/inv_icm42600_accel.c | 14 +++++------
> > > > .../iio/imu/inv_icm42600/inv_icm42600_gyro.c | 13 +++++------
> > > > .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 15 +++++-------
> > > > drivers/iio/light/acpi-als.c | 12 ++++------
> > > > drivers/iio/light/apds9960.c | 16 ++++++-------
> > > > .../staging/iio/impedance-analyzer/ad5933.c | 23 ++++---------------
> > > > 13 files changed, 74 insertions(+), 124 deletions(-)
> > > >
> > > > diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
> > > > index 474477e91b5e..04dcb2b657ee 100644
> > > > --- a/drivers/iio/accel/ssp_accel_sensor.c
> > > > +++ b/drivers/iio/accel/ssp_accel_sensor.c
> > > > @@ -96,7 +96,6 @@ static int ssp_accel_probe(struct platform_device *pdev)
> > > > int ret;
> > > > struct iio_dev *indio_dev;
> > > > struct ssp_sensor_data *spd;
> > > > - struct iio_buffer *buffer;
> > > >
> > > > indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
> > > > if (!indio_dev)
> > > > @@ -109,18 +108,15 @@ static int ssp_accel_probe(struct platform_device *pdev)
> > > >
> > > > indio_dev->name = ssp_accel_device_name;
> > > > indio_dev->info = &ssp_accel_iio_info;
> > > > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > > > indio_dev->channels = ssp_acc_channels;
> > > > indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
> > > > indio_dev->available_scan_masks = ssp_accel_scan_mask;
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(&pdev->dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > -
> > > > - indio_dev->setup_ops = &ssp_accel_buffer_ops;
> > > > + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &ssp_accel_buffer_ops);
> > > > + if (ret)
> > > > + return ret;
> > > >
> > > > platform_set_drvdata(pdev, indio_dev);
> > > >
> > > > diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
> > > > index b573ec60a8b8..2ae54258b221 100644
> > > > --- a/drivers/iio/adc/ina2xx-adc.c
> > > > +++ b/drivers/iio/adc/ina2xx-adc.c
> > > > @@ -953,7 +953,6 @@ static int ina2xx_probe(struct i2c_client *client,
> > > > {
> > > > struct ina2xx_chip_info *chip;
> > > > struct iio_dev *indio_dev;
> > > > - struct iio_buffer *buffer;
> > > > unsigned int val;
> > > > enum ina2xx_ids type;
> > > > int ret;
> > > > @@ -1017,7 +1016,7 @@ static int ina2xx_probe(struct i2c_client *client,
> > > > return ret;
> > > > }
> > > >
> > > > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > > if (id->driver_data == ina226) {
> > > > indio_dev->channels = ina226_channels;
> > > > indio_dev->num_channels = ARRAY_SIZE(ina226_channels);
> > > > @@ -1028,13 +1027,12 @@ static int ina2xx_probe(struct i2c_client *client,
> > > > indio_dev->info = &ina219_info;
> > > > }
> > > > indio_dev->name = id->name;
> > > > - indio_dev->setup_ops = &ina2xx_setup_ops;
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(&indio_dev->dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &ina2xx_setup_ops);
> > > > + if (ret)
> > > > + return ret;
> > > >
> > > > return iio_device_register(indio_dev);
> > > > }
> > > > diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
> > > > index e946903b0993..855cc2d64ac8 100644
> > > > --- a/drivers/iio/adc/ti_am335x_adc.c
> > > > +++ b/drivers/iio/adc/ti_am335x_adc.c
> > > > @@ -385,24 +385,16 @@ static int tiadc_iio_buffered_hardware_setup(struct device *dev,
> > > > unsigned long flags,
> > > > const struct iio_buffer_setup_ops *setup_ops)
> > > > {
> > > > - struct iio_buffer *buffer;
> > > > int ret;
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > -
> > > > - ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
> > > > - flags, indio_dev->name, indio_dev);
> > > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + setup_ops);
> > > > if (ret)
> > > > return ret;
> > > >
> > > > - indio_dev->setup_ops = setup_ops;
> > > > - indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
> > > > -
> > > > - return 0;
> > > > + return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh,
> > > > + flags, indio_dev->name, indio_dev);
> > > > }
> > > >
> > > > static const char * const chan_name_ain[] = {
> > > > diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > > > index c833ec0ef214..f8824afe595e 100644
> > > > --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > > > +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
> > > > @@ -334,14 +334,11 @@ int cros_ec_sensors_core_init(struct platform_device *pdev,
> > > > * We can not use trigger here, as events are generated
> > > > * as soon as sample_frequency is set.
> > > > */
> > > > - struct iio_buffer *buffer;
> > > > -
> > > > - buffer = devm_iio_kfifo_allocate(dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + NULL);
> > > > + if (ret)
> > > > + return ret;
> > > >
> > > > ret = cros_ec_sensorhub_register_push_data(
> > > > sensor_hub, sensor_platform->sensor_num,
> > > > diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
> > > > index ac7c170a20de..46ed12771d2f 100644
> > > > --- a/drivers/iio/gyro/ssp_gyro_sensor.c
> > > > +++ b/drivers/iio/gyro/ssp_gyro_sensor.c
> > > > @@ -96,7 +96,6 @@ static int ssp_gyro_probe(struct platform_device *pdev)
> > > > int ret;
> > > > struct iio_dev *indio_dev;
> > > > struct ssp_sensor_data *spd;
> > > > - struct iio_buffer *buffer;
> > > >
> > > > indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
> > > > if (!indio_dev)
> > > > @@ -109,18 +108,15 @@ static int ssp_gyro_probe(struct platform_device *pdev)
> > > >
> > > > indio_dev->name = ssp_gyro_name;
> > > > indio_dev->info = &ssp_gyro_iio_info;
> > > > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > > > indio_dev->channels = ssp_gyro_channels;
> > > > indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
> > > > indio_dev->available_scan_masks = ssp_gyro_scan_mask;
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(&pdev->dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > -
> > > > - indio_dev->setup_ops = &ssp_gyro_buffer_ops;
> > > > + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &ssp_gyro_buffer_ops);
> > > > + if (ret)
> > > > + return ret;
> > > >
> > > > platform_set_drvdata(pdev, indio_dev);
> > > >
> > > > diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c
> > > > index 38aa2030f3c6..36ba7611d9ce 100644
> > > > --- a/drivers/iio/health/max30100.c
> > > > +++ b/drivers/iio/health/max30100.c
> > > > @@ -418,7 +418,6 @@ static int max30100_probe(struct i2c_client *client,
> > > > const struct i2c_device_id *id)
> > > > {
> > > > struct max30100_data *data;
> > > > - struct iio_buffer *buffer;
> > > > struct iio_dev *indio_dev;
> > > > int ret;
> > > >
> > > > @@ -426,19 +425,18 @@ static int max30100_probe(struct i2c_client *client,
> > > > if (!indio_dev)
> > > > return -ENOMEM;
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > -
> > > > indio_dev->name = MAX30100_DRV_NAME;
> > > > indio_dev->channels = max30100_channels;
> > > > indio_dev->info = &max30100_info;
> > > > indio_dev->num_channels = ARRAY_SIZE(max30100_channels);
> > > > indio_dev->available_scan_masks = max30100_scan_masks;
> > > > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > > > - indio_dev->setup_ops = &max30100_buffer_setup_ops;
> > > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > > +
> > > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &max30100_buffer_setup_ops);
> > > > + if (ret)
> > > > + return ret;
> > > >
> > > > data = iio_priv(indio_dev);
> > > > data->indio_dev = indio_dev;
> > > > diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
> > > > index b35557a54ee2..2292876c55e2 100644
> > > > --- a/drivers/iio/health/max30102.c
> > > > +++ b/drivers/iio/health/max30102.c
> > > > @@ -506,7 +506,6 @@ static int max30102_probe(struct i2c_client *client,
> > > > const struct i2c_device_id *id)
> > > > {
> > > > struct max30102_data *data;
> > > > - struct iio_buffer *buffer;
> > > > struct iio_dev *indio_dev;
> > > > int ret;
> > > > unsigned int reg;
> > > > @@ -515,16 +514,9 @@ static int max30102_probe(struct i2c_client *client,
> > > > if (!indio_dev)
> > > > return -ENOMEM;
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > -
> > > > indio_dev->name = MAX30102_DRV_NAME;
> > > > indio_dev->info = &max30102_info;
> > > > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > > > - indio_dev->setup_ops = &max30102_buffer_setup_ops;
> > > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > >
> > > > data = iio_priv(indio_dev);
> > > > data->indio_dev = indio_dev;
> > > > @@ -549,6 +541,12 @@ static int max30102_probe(struct i2c_client *client,
> > > > return -ENODEV;
> > > > }
> > > >
> > > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &max30102_buffer_setup_ops);
> > > > + if (ret)
> > > > + return ret;
> > > > +
> > > > data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config);
> > > > if (IS_ERR(data->regmap)) {
> > > > dev_err(&client->dev, "regmap initialization failed\n");
> > > > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > > > index 3441b0d61c5d..383cc3250342 100644
> > > > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > > > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c
> > > > @@ -709,7 +709,6 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
> > > > const char *name;
> > > > struct inv_icm42600_timestamp *ts;
> > > > struct iio_dev *indio_dev;
> > > > - struct iio_buffer *buffer;
> > > > int ret;
> > > >
> > > > name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name);
> > > > @@ -720,23 +719,22 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st)
> > > > if (!indio_dev)
> > > > return ERR_PTR(-ENOMEM);
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(dev);
> > > > - if (!buffer)
> > > > - return ERR_PTR(-ENOMEM);
> > > > -
> > > > ts = iio_priv(indio_dev);
> > > > inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr));
> > > >
> > > > iio_device_set_drvdata(indio_dev, st);
> > > > indio_dev->name = name;
> > > > indio_dev->info = &inv_icm42600_accel_info;
> > > > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > > indio_dev->channels = inv_icm42600_accel_channels;
> > > > indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels);
> > > > indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks;
> > > > - indio_dev->setup_ops = &inv_icm42600_buffer_ops;
> > > >
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &inv_icm42600_buffer_ops);
> > > > + if (ret)
> > > > + return ERR_PTR(ret);
> > > >
> > > > ret = devm_iio_device_register(dev, indio_dev);
> > > > if (ret)
> > > > diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > > > index aee7b9ff4bf4..cec1dd0e0464 100644
> > > > --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > > > +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c
> > > > @@ -720,7 +720,6 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> > > > const char *name;
> > > > struct inv_icm42600_timestamp *ts;
> > > > struct iio_dev *indio_dev;
> > > > - struct iio_buffer *buffer;
> > > > int ret;
> > > >
> > > > name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name);
> > > > @@ -731,23 +730,23 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st)
> > > > if (!indio_dev)
> > > > return ERR_PTR(-ENOMEM);
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(dev);
> > > > - if (!buffer)
> > > > - return ERR_PTR(-ENOMEM);
> > > > -
> > > > ts = iio_priv(indio_dev);
> > > > inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr));
> > > >
> > > > iio_device_set_drvdata(indio_dev, st);
> > > > indio_dev->name = name;
> > > > indio_dev->info = &inv_icm42600_gyro_info;
> > > > - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > > indio_dev->channels = inv_icm42600_gyro_channels;
> > > > indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels);
> > > > indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks;
> > > > indio_dev->setup_ops = &inv_icm42600_buffer_ops;
> > > >
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &inv_icm42600_buffer_ops);
> > > > + if (ret)
> > > > + return ERR_PTR(ret);
> > > >
> > > > ret = devm_iio_device_register(dev, indio_dev);
> > > > if (ret)
> > > > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > > index f1103ecedd64..16730a780964 100644
> > > > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > > @@ -739,20 +739,17 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
> > > >
> > > > int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
> > > > {
> > > > - struct iio_buffer *buffer;
> > > > - int i;
> > > > + int i, ret;
> > > >
> > > > for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > > if (!hw->iio_devs[i])
> > > > continue;
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(hw->dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(hw->iio_devs[i], buffer);
> > > > - hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE;
> > > > - hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops;
> > > > + ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i],
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &st_lsm6dsx_buffer_ops);
> > > > + if (ret)
> > > > + return ret;
> > > > }
> > > >
> > > > return 0;
> > > > diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c
> > > > index 2be7180e2cbf..f8e547fd35e7 100644
> > > > --- a/drivers/iio/light/acpi-als.c
> > > > +++ b/drivers/iio/light/acpi-als.c
> > > > @@ -165,7 +165,7 @@ static int acpi_als_add(struct acpi_device *device)
> > > > {
> > > > struct acpi_als *als;
> > > > struct iio_dev *indio_dev;
> > > > - struct iio_buffer *buffer;
> > > > + int ret;
> > > >
> > > > indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als));
> > > > if (!indio_dev)
> > > > @@ -179,15 +179,13 @@ static int acpi_als_add(struct acpi_device *device)
> > > >
> > > > indio_dev->name = ACPI_ALS_DEVICE_NAME;
> > > > indio_dev->info = &acpi_als_info;
> > > > - indio_dev->modes = INDIO_BUFFER_SOFTWARE;
> > > > indio_dev->channels = acpi_als_channels;
> > > > indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels);
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(&device->dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > + ret = devm_iio_kfifo_buffer_setup(&device->dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE, NULL);
> > > > + if (ret)
> > > > + return ret;
> > > >
> > > > return devm_iio_device_register(&device->dev, indio_dev);
> > > > }
> > > > diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
> > > > index df0647856e5d..4141c0fa7bc4 100644
> > > > --- a/drivers/iio/light/apds9960.c
> > > > +++ b/drivers/iio/light/apds9960.c
> > > > @@ -988,7 +988,6 @@ static int apds9960_probe(struct i2c_client *client,
> > > > const struct i2c_device_id *id)
> > > > {
> > > > struct apds9960_data *data;
> > > > - struct iio_buffer *buffer;
> > > > struct iio_dev *indio_dev;
> > > > int ret;
> > > >
> > > > @@ -996,19 +995,18 @@ static int apds9960_probe(struct i2c_client *client,
> > > > if (!indio_dev)
> > > > return -ENOMEM;
> > > >
> > > > - buffer = devm_iio_kfifo_allocate(&client->dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > -
> > > > indio_dev->info = &apds9960_info;
> > > > indio_dev->name = APDS9960_DRV_NAME;
> > > > indio_dev->channels = apds9960_channels;
> > > > indio_dev->num_channels = ARRAY_SIZE(apds9960_channels);
> > > > indio_dev->available_scan_masks = apds9960_scan_masks;
> > > > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > > > - indio_dev->setup_ops = &apds9960_buffer_setup_ops;
> > > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > > +
> > > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &apds9960_buffer_setup_ops);
> > > > + if (ret)
> > > > + return ret;
> > > >
> > > > data = iio_priv(indio_dev);
> > > > i2c_set_clientdata(client, indio_dev);
> > > > diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
> > > > index dba78896ea8f..793918e1c45f 100644
> > > > --- a/drivers/staging/iio/impedance-analyzer/ad5933.c
> > > > +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
> > > > @@ -602,23 +602,6 @@ static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = {
> > > > .postdisable = ad5933_ring_postdisable,
> > > > };
> > > >
> > > > -static int ad5933_register_ring_funcs_and_init(struct device *dev,
> > > > - struct iio_dev *indio_dev)
> > > > -{
> > > > - struct iio_buffer *buffer;
> > > > -
> > > > - buffer = devm_iio_kfifo_allocate(dev);
> > > > - if (!buffer)
> > > > - return -ENOMEM;
> > > > -
> > > > - iio_device_attach_buffer(indio_dev, buffer);
> > > > -
> > > > - /* Ring buffer functions - here trigger setup related */
> > > > - indio_dev->setup_ops = &ad5933_ring_setup_ops;
> > > > -
> > > > - return 0;
> > > > -}
> > > > -
> > > > static void ad5933_work(struct work_struct *work)
> > > > {
> > > > struct ad5933_state *st = container_of(work,
> > > > @@ -761,11 +744,13 @@ static int ad5933_probe(struct i2c_client *client,
> > > >
> > > > indio_dev->info = &ad5933_info;
> > > > indio_dev->name = id->name;
> > > > - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE);
> > > > + indio_dev->modes = INDIO_DIRECT_MODE;
> > > > indio_dev->channels = ad5933_channels;
> > > > indio_dev->num_channels = ARRAY_SIZE(ad5933_channels);
> > > >
> > > > - ret = ad5933_register_ring_funcs_and_init(&client->dev, indio_dev);
> > > > + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev,
> > > > + INDIO_BUFFER_SOFTWARE,
> > > > + &ad5933_ring_setup_ops);
> > > > if (ret)
> > > > return ret;
> > > >
> > >

2021-02-28 08:02:40

by Lars-Peter Clausen

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> [...]
> /**
> * iio_buffer_wakeup_poll - Wakes up the buffer waitqueue
> * @indio_dev: The IIO device
> @@ -1343,6 +1371,96 @@ static void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev)
> kfree(iio_dev_opaque->legacy_scan_el_group.attrs);
> }
>
> [...]
> +static long iio_device_buffer_getfd(struct iio_dev *indio_dev, unsigned long arg)
> +{
> + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
> + int __user *ival = (int __user *)arg;
> + struct iio_dev_buffer_pair *ib;
> + struct iio_buffer *buffer;
> + int fd, idx, ret;
> +
> + if (copy_from_user(&idx, ival, sizeof(idx)))
> + return -EFAULT;

If we only want to pass an int, we can pass that directly, no need to
pass it as a pointer.

int fd = arg;

> +
> + if (idx >= iio_dev_opaque->attached_buffers_cnt)
> + return -ENODEV;
> +
> + iio_device_get(indio_dev);
> +
> + buffer = iio_dev_opaque->attached_buffers[idx];
> +
> + if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
> + ret = -EBUSY;
> + goto error_iio_dev_put;
> + }
> +
> + ib = kzalloc(sizeof(*ib), GFP_KERNEL);
> + if (!ib) {
> + ret = -ENOMEM;
> + goto error_clear_busy_bit;
> + }
> +
> + ib->indio_dev = indio_dev;
> + ib->buffer = buffer;
> +
> + fd = anon_inode_getfd("iio:buffer", &iio_buffer_chrdev_fileops,
> + ib, O_RDWR | O_CLOEXEC);

I wonder if we need to allow to pass flags, like e.g. O_NONBLOCK.

Something like
https://elixir.bootlin.com/linux/latest/source/fs/signalfd.c#L288

> + if (fd < 0) {
> + ret = fd;
> + goto error_free_ib;
> + }
> +
> + if (copy_to_user(ival, &fd, sizeof(fd))) {
> + put_unused_fd(fd);
> + ret = -EFAULT;
> + goto error_free_ib;
> + }

Here we copy back the fd, but also return it. Just return is probably
enough.

> +
> + return fd;
> +
> +error_free_ib:
> + kfree(ib);
> +error_clear_busy_bit:
> + clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> +error_iio_dev_put:
> + iio_device_put(indio_dev);
> + return ret;
> +}
> [...]
> diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h
> index b6ebc04af3e7..32addd5e790e 100644
> --- a/include/linux/iio/iio-opaque.h
> +++ b/include/linux/iio/iio-opaque.h
> @@ -9,6 +9,7 @@
> * @event_interface: event chrdevs associated with interrupt lines
> * @attached_buffers: array of buffers statically attached by the driver
> * @attached_buffers_cnt: number of buffers in the array of statically attached buffers
> + * @buffer_ioctl_handler: ioctl() handler for this IIO device's buffer interface
> * @buffer_list: list of all buffers currently attached
> * @channel_attr_list: keep track of automatically created channel
> * attributes
> @@ -28,6 +29,7 @@ struct iio_dev_opaque {
> struct iio_event_interface *event_interface;
> struct iio_buffer **attached_buffers;
> unsigned int attached_buffers_cnt;
> + struct iio_ioctl_handler *buffer_ioctl_handler;

Can we just embedded this struct so we do not have to
allocate/deallocate it?

> struct list_head buffer_list;
> struct list_head channel_attr_list;
> struct attribute_group chan_attr_group;


2021-02-28 08:31:02

by Lars-Peter Clausen

[permalink] [raw]
Subject: Re: [PATCH v6 19/24] iio: buffer: introduce support for attaching more IIO buffers

On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> static ssize_t iio_show_scan_index(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> @@ -1451,11 +1465,13 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
> iio_free_chan_devattr_list(&buffer->buffer_attr_list);
> }
>
> -int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> +int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> {
> [...]
> +error_unwind_sysfs_and_mask:
> + for (; unwind_idx >= 0; unwind_idx--) {
> + buffer = iio_dev_opaque->attached_buffers[unwind_idx];
> + __iio_buffer_free_sysfs_and_mask(buffer);
> + }
> + kfree(iio_dev_opaque->attached_buffers);
> + return ret;
> }
>
> -void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> +void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev)
> {
> [...]
> + for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
> + buffer = iio_dev_opaque->attached_buffers[i];
> + __iio_buffer_free_sysfs_and_mask(buffer);
> + }
> +
> + kfree(iio_dev_opaque->attached_buffers);
> }
> [...]
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 1d500ea246af..f7f785431106 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -1583,7 +1583,7 @@ static void iio_dev_release(struct device *device)
> iio_device_unregister_eventset(indio_dev);
> iio_device_unregister_sysfs(indio_dev);
>
> - iio_buffer_put(indio_dev->buffer);
> + iio_buffers_put(indio_dev);
We do call kfree(iio_dev_opaque->attached_buffers) before we get here. I
think we need to keep the array around, otherwise we end of up with a
use after free.

2021-02-28 08:54:26

by Lars-Peter Clausen

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> With this change, an ioctl() call is added to open a character device for a
> buffer. The ioctl() number is 'i' 0x91, which follows the
> IIO_GET_EVENT_FD_IOCTL ioctl.
>
> The ioctl() will return an FD for the requested buffer index. The indexes
> are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
> variable).
>
> Since there doesn't seem to be a sane way to return the FD for buffer0 to
> be the same FD for the /dev/iio:deviceX, this ioctl() will return another
> FD for buffer0 (or the first buffer). This duplicate FD will be able to
> access the same buffer object (for buffer0) as accessing directly the
> /dev/iio:deviceX chardev.
>
> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
> index for each buffer (and the count) can be deduced from the
> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
> bufferY folders).
>
> Used following C code to test this:
> -------------------------------------------------------------------
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <sys/ioctl.h>
> #include <fcntl.h"
> #include <errno.h>
>
> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
>
> int main(int argc, char *argv[])
> {
> int fd;
> int fd1;
> int ret;
>
> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
> return -1;
> }
>
> fprintf(stderr, "Using FD %d\n", fd);
>
> fd1 = atoi(argv[1]);
>
> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> if (ret < 0) {
> fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
> close(fd);
> return -1;
> }
>
> fprintf(stderr, "Got FD %d\n", fd1);
>
> close(fd1);
> close(fd);
>
> return 0;
> }
> -------------------------------------------------------------------
>
> Results are:
> -------------------------------------------------------------------
> # ./test 0
> Using FD 3
> Got FD 4
>
> # ./test 1
> Using FD 3
> Got FD 4
>
> # ./test 2
> Using FD 3
> Got FD 4
>
> # ./test 3
> Using FD 3
> Got FD 4
>
> # ls /sys/bus/iio/devices/iio\:device0
> buffer buffer0 buffer1 buffer2 buffer3 dev
> in_voltage_sampling_frequency in_voltage_scale
> in_voltage_scale_available
> name of_node power scan_elements subsystem uevent
> -------------------------------------------------------------------
>
> iio:device0 has some fake kfifo buffers attached to an IIO device.

For me there is one major problem with this approach. We only allow one
application to open /dev/iio:deviceX at a time. This means we can't have
different applications access different buffers of the same device. I
believe this is a circuital feature.

It is possible to open the chardev, get the annonfd, close the chardev
and keep the annonfd open. Then the next application can do the same and
get access to a different buffer. But this has room for race conditions
when two applications try this at the very same time.

We need to somehow address this.

I'm also not much of a fan of using ioctls to create annon fds. In part
because all the standard mechanisms for access control no longer work.

2021-02-28 14:40:38

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sun, 28 Feb 2021 09:51:38 +0100
Lars-Peter Clausen <[email protected]> wrote:

> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > With this change, an ioctl() call is added to open a character device for a
> > buffer. The ioctl() number is 'i' 0x91, which follows the
> > IIO_GET_EVENT_FD_IOCTL ioctl.
> >
> > The ioctl() will return an FD for the requested buffer index. The indexes
> > are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
> > variable).
> >
> > Since there doesn't seem to be a sane way to return the FD for buffer0 to
> > be the same FD for the /dev/iio:deviceX, this ioctl() will return another
> > FD for buffer0 (or the first buffer). This duplicate FD will be able to
> > access the same buffer object (for buffer0) as accessing directly the
> > /dev/iio:deviceX chardev.
> >
> > Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
> > index for each buffer (and the count) can be deduced from the
> > '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
> > bufferY folders).
> >
> > Used following C code to test this:
> > -------------------------------------------------------------------
> >
> > #include <stdio.h>
> > #include <stdlib.h>
> > #include <unistd.h>
> > #include <sys/ioctl.h>
> > #include <fcntl.h"
> > #include <errno.h>
> >
> > #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> >
> > int main(int argc, char *argv[])
> > {
> > int fd;
> > int fd1;
> > int ret;
> >
> > if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
> > return -1;
> > }
> >
> > fprintf(stderr, "Using FD %d\n", fd);
> >
> > fd1 = atoi(argv[1]);
> >
> > ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > if (ret < 0) {
> > fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
> > close(fd);
> > return -1;
> > }
> >
> > fprintf(stderr, "Got FD %d\n", fd1);
> >
> > close(fd1);
> > close(fd);
> >
> > return 0;
> > }
> > -------------------------------------------------------------------
> >
> > Results are:
> > -------------------------------------------------------------------
> > # ./test 0
> > Using FD 3
> > Got FD 4
> >
> > # ./test 1
> > Using FD 3
> > Got FD 4
> >
> > # ./test 2
> > Using FD 3
> > Got FD 4
> >
> > # ./test 3
> > Using FD 3
> > Got FD 4
> >
> > # ls /sys/bus/iio/devices/iio\:device0
> > buffer buffer0 buffer1 buffer2 buffer3 dev
> > in_voltage_sampling_frequency in_voltage_scale
> > in_voltage_scale_available
> > name of_node power scan_elements subsystem uevent
> > -------------------------------------------------------------------
> >
> > iio:device0 has some fake kfifo buffers attached to an IIO device.
>
> For me there is one major problem with this approach. We only allow one
> application to open /dev/iio:deviceX at a time. This means we can't have
> different applications access different buffers of the same device. I
> believe this is a circuital feature.

Thats not quite true (I think - though I've not tested it). What we don't
allow is for multiple processes to access them in an unaware fashion.
My assumption is we can rely on fork + fd passing via appropriate sockets.

>
> It is possible to open the chardev, get the annonfd, close the chardev
> and keep the annonfd open. Then the next application can do the same and
> get access to a different buffer. But this has room for race conditions
> when two applications try this at the very same time.
>
> We need to somehow address this.

I'd count this as a bug :). It could be safely done in a particular custom
system but in general it opens a can of worm.

>
> I'm also not much of a fan of using ioctls to create annon fds. In part
> because all the standard mechanisms for access control no longer work.

The inability to trivially have multiple processes open the anon fds
without care is one of the things I like most about them.

IIO drivers and interfaces really aren't designed for multiple unaware
processes to access them. We don't have per process controls for device
wide sysfs attributes etc. In general, it would be hard to
do due to the complexity of modeling all the interactions between the
different interfaces (events / buffers / sysfs access) in a generic fashion.

As such, the model, in my head at least, is that we only want a single
process to ever be responsible for access control. That process can then
assign access to children or via a deliberate action (I think passing the
anon fd over a unix socket should work for example). The intent being
that it is also responsible for mediating access to infrastructure that
multiple child processes all want to access.

As such, having one chrdev isn't a disadvantage because only one process
should ever open it at a time. This same process also handles the
resource / control mediation. Therefore we should only have one file
exposed for all the standard access control mechanisms.

Jonathan

2021-02-28 15:54:20

by Lars-Peter Clausen

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> On Sun, 28 Feb 2021 09:51:38 +0100
> Lars-Peter Clausen <[email protected]> wrote:
>
>> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
>>> With this change, an ioctl() call is added to open a character device for a
>>> buffer. The ioctl() number is 'i' 0x91, which follows the
>>> IIO_GET_EVENT_FD_IOCTL ioctl.
>>>
>>> The ioctl() will return an FD for the requested buffer index. The indexes
>>> are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
>>> variable).
>>>
>>> Since there doesn't seem to be a sane way to return the FD for buffer0 to
>>> be the same FD for the /dev/iio:deviceX, this ioctl() will return another
>>> FD for buffer0 (or the first buffer). This duplicate FD will be able to
>>> access the same buffer object (for buffer0) as accessing directly the
>>> /dev/iio:deviceX chardev.
>>>
>>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
>>> index for each buffer (and the count) can be deduced from the
>>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
>>> bufferY folders).
>>>
>>> Used following C code to test this:
>>> -------------------------------------------------------------------
>>>
>>> #include <stdio.h>
>>> #include <stdlib.h>
>>> #include <unistd.h>
>>> #include <sys/ioctl.h>
>>> #include <fcntl.h"
>>> #include <errno.h>
>>>
>>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
>>>
>>> int main(int argc, char *argv[])
>>> {
>>> int fd;
>>> int fd1;
>>> int ret;
>>>
>>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
>>> fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
>>> return -1;
>>> }
>>>
>>> fprintf(stderr, "Using FD %d\n", fd);
>>>
>>> fd1 = atoi(argv[1]);
>>>
>>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
>>> if (ret < 0) {
>>> fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
>>> close(fd);
>>> return -1;
>>> }
>>>
>>> fprintf(stderr, "Got FD %d\n", fd1);
>>>
>>> close(fd1);
>>> close(fd);
>>>
>>> return 0;
>>> }
>>> -------------------------------------------------------------------
>>>
>>> Results are:
>>> -------------------------------------------------------------------
>>> # ./test 0
>>> Using FD 3
>>> Got FD 4
>>>
>>> # ./test 1
>>> Using FD 3
>>> Got FD 4
>>>
>>> # ./test 2
>>> Using FD 3
>>> Got FD 4
>>>
>>> # ./test 3
>>> Using FD 3
>>> Got FD 4
>>>
>>> # ls /sys/bus/iio/devices/iio\:device0
>>> buffer buffer0 buffer1 buffer2 buffer3 dev
>>> in_voltage_sampling_frequency in_voltage_scale
>>> in_voltage_scale_available
>>> name of_node power scan_elements subsystem uevent
>>> -------------------------------------------------------------------
>>>
>>> iio:device0 has some fake kfifo buffers attached to an IIO device.
>> For me there is one major problem with this approach. We only allow one
>> application to open /dev/iio:deviceX at a time. This means we can't have
>> different applications access different buffers of the same device. I
>> believe this is a circuital feature.
> Thats not quite true (I think - though I've not tested it). What we don't
> allow is for multiple processes to access them in an unaware fashion.
> My assumption is we can rely on fork + fd passing via appropriate sockets.
>
>> It is possible to open the chardev, get the annonfd, close the chardev
>> and keep the annonfd open. Then the next application can do the same and
>> get access to a different buffer. But this has room for race conditions
>> when two applications try this at the very same time.
>>
>> We need to somehow address this.
> I'd count this as a bug :). It could be safely done in a particular custom
> system but in general it opens a can of worm.
>
>> I'm also not much of a fan of using ioctls to create annon fds. In part
>> because all the standard mechanisms for access control no longer work.
> The inability to trivially have multiple processes open the anon fds
> without care is one of the things I like most about them.
>
> IIO drivers and interfaces really aren't designed for multiple unaware
> processes to access them. We don't have per process controls for device
> wide sysfs attributes etc. In general, it would be hard to
> do due to the complexity of modeling all the interactions between the
> different interfaces (events / buffers / sysfs access) in a generic fashion.
>
> As such, the model, in my head at least, is that we only want a single
> process to ever be responsible for access control. That process can then
> assign access to children or via a deliberate action (I think passing the
> anon fd over a unix socket should work for example). The intent being
> that it is also responsible for mediating access to infrastructure that
> multiple child processes all want to access.
>
> As such, having one chrdev isn't a disadvantage because only one process
> should ever open it at a time. This same process also handles the
> resource / control mediation. Therefore we should only have one file
> exposed for all the standard access control mechanisms.
>
Hm, I see your point, but I'm not convinced.

Having to have explicit synchronization makes it difficult to mix and
match. E.g. at ADI a popular use case for testing was to run some signal
generator application on the TX buffer and some signal analyzer
application on the RX buffer.

Both can be launched independently and there can be different types of
generator and analyzer applications. Having to have a 3rd application to
arbitrate access makes this quite cumbersome. And I'm afraid that in
reality people might just stick with the two devices model just to avoid
this restriction.

- Lars

2021-02-28 19:11:57

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sun, Feb 28, 2021 at 9:58 AM Lars-Peter Clausen <[email protected]> wrote:
>
> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > [...]
> > /**
> > * iio_buffer_wakeup_poll - Wakes up the buffer waitqueue
> > * @indio_dev: The IIO device
> > @@ -1343,6 +1371,96 @@ static void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev)
> > kfree(iio_dev_opaque->legacy_scan_el_group.attrs);
> > }
> >
> > [...]
> > +static long iio_device_buffer_getfd(struct iio_dev *indio_dev, unsigned long arg)
> > +{
> > + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
> > + int __user *ival = (int __user *)arg;
> > + struct iio_dev_buffer_pair *ib;
> > + struct iio_buffer *buffer;
> > + int fd, idx, ret;
> > +
> > + if (copy_from_user(&idx, ival, sizeof(idx)))
> > + return -EFAULT;
>
> If we only want to pass an int, we can pass that directly, no need to
> pass it as a pointer.
>
> int fd = arg;

I guess I can simplify this a bit.

>
> > +
> > + if (idx >= iio_dev_opaque->attached_buffers_cnt)
> > + return -ENODEV;
> > +
> > + iio_device_get(indio_dev);
> > +
> > + buffer = iio_dev_opaque->attached_buffers[idx];
> > +
> > + if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
> > + ret = -EBUSY;
> > + goto error_iio_dev_put;
> > + }
> > +
> > + ib = kzalloc(sizeof(*ib), GFP_KERNEL);
> > + if (!ib) {
> > + ret = -ENOMEM;
> > + goto error_clear_busy_bit;
> > + }
> > +
> > + ib->indio_dev = indio_dev;
> > + ib->buffer = buffer;
> > +
> > + fd = anon_inode_getfd("iio:buffer", &iio_buffer_chrdev_fileops,
> > + ib, O_RDWR | O_CLOEXEC);
>
> I wonder if we need to allow to pass flags, like e.g. O_NONBLOCK.
>
> Something like
> https://elixir.bootlin.com/linux/latest/source/fs/signalfd.c#L288

Umm, no idea.
I guess we could.
Would a syscall make more sense than an ioctl() here?
I guess for the ioctl() case we would need to change the type (of the
data) to some sort of

struct iio_buffer_get_fd {
unsigned int buffer_idx;
unsigned int fd_flags;
};

Or do we just let userspace use fcntl() to change flags to O_NONBLOCK ?

>
> > + if (fd < 0) {
> > + ret = fd;
> > + goto error_free_ib;
> > + }
> > +
> > + if (copy_to_user(ival, &fd, sizeof(fd))) {
> > + put_unused_fd(fd);
> > + ret = -EFAULT;
> > + goto error_free_ib;
> > + }
>
> Here we copy back the fd, but also return it. Just return is probably
> enough.

Hmm.
Yes, it is a bit duplicate.
But it is a good point. Maybe we should return 0 to userspace.

>
> > +
> > + return fd;
> > +
> > +error_free_ib:
> > + kfree(ib);
> > +error_clear_busy_bit:
> > + clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > +error_iio_dev_put:
> > + iio_device_put(indio_dev);
> > + return ret;
> > +}
> > [...]
> > diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h
> > index b6ebc04af3e7..32addd5e790e 100644
> > --- a/include/linux/iio/iio-opaque.h
> > +++ b/include/linux/iio/iio-opaque.h
> > @@ -9,6 +9,7 @@
> > * @event_interface: event chrdevs associated with interrupt lines
> > * @attached_buffers: array of buffers statically attached by the driver
> > * @attached_buffers_cnt: number of buffers in the array of statically attached buffers
> > + * @buffer_ioctl_handler: ioctl() handler for this IIO device's buffer interface
> > * @buffer_list: list of all buffers currently attached
> > * @channel_attr_list: keep track of automatically created channel
> > * attributes
> > @@ -28,6 +29,7 @@ struct iio_dev_opaque {
> > struct iio_event_interface *event_interface;
> > struct iio_buffer **attached_buffers;
> > unsigned int attached_buffers_cnt;
> > + struct iio_ioctl_handler *buffer_ioctl_handler;
>
> Can we just embedded this struct so we do not have to
> allocate/deallocate it?

Unfortunately we can't.
The type of ' struct iio_ioctl_handler ' is stored in iio_core.h.
So, we don't know the size here. So we need to keep it as a pointer
and allocate it.
It is a bit of an unfortunate consequence of the design of this
generic ioctl() registering.

>
> > struct list_head buffer_list;
> > struct list_head channel_attr_list;
> > struct attribute_group chan_attr_group;
>
>

2021-02-28 19:34:55

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sun, 28 Feb 2021 16:51:51 +0100
Lars-Peter Clausen <[email protected]> wrote:

> On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > On Sun, 28 Feb 2021 09:51:38 +0100
> > Lars-Peter Clausen <[email protected]> wrote:
> >
> >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> >>> With this change, an ioctl() call is added to open a character device for a
> >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> >>>
> >>> The ioctl() will return an FD for the requested buffer index. The indexes
> >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
> >>> variable).
> >>>
> >>> Since there doesn't seem to be a sane way to return the FD for buffer0 to
> >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return another
> >>> FD for buffer0 (or the first buffer). This duplicate FD will be able to
> >>> access the same buffer object (for buffer0) as accessing directly the
> >>> /dev/iio:deviceX chardev.
> >>>
> >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
> >>> index for each buffer (and the count) can be deduced from the
> >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
> >>> bufferY folders).
> >>>
> >>> Used following C code to test this:
> >>> -------------------------------------------------------------------
> >>>
> >>> #include <stdio.h>
> >>> #include <stdlib.h>
> >>> #include <unistd.h>
> >>> #include <sys/ioctl.h>
> >>> #include <fcntl.h"
> >>> #include <errno.h>
> >>>
> >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> >>>
> >>> int main(int argc, char *argv[])
> >>> {
> >>> int fd;
> >>> int fd1;
> >>> int ret;
> >>>
> >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> >>> fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
> >>> return -1;
> >>> }
> >>>
> >>> fprintf(stderr, "Using FD %d\n", fd);
> >>>
> >>> fd1 = atoi(argv[1]);
> >>>
> >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> >>> if (ret < 0) {
> >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
> >>> close(fd);
> >>> return -1;
> >>> }
> >>>
> >>> fprintf(stderr, "Got FD %d\n", fd1);
> >>>
> >>> close(fd1);
> >>> close(fd);
> >>>
> >>> return 0;
> >>> }
> >>> -------------------------------------------------------------------
> >>>
> >>> Results are:
> >>> -------------------------------------------------------------------
> >>> # ./test 0
> >>> Using FD 3
> >>> Got FD 4
> >>>
> >>> # ./test 1
> >>> Using FD 3
> >>> Got FD 4
> >>>
> >>> # ./test 2
> >>> Using FD 3
> >>> Got FD 4
> >>>
> >>> # ./test 3
> >>> Using FD 3
> >>> Got FD 4
> >>>
> >>> # ls /sys/bus/iio/devices/iio\:device0
> >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> >>> in_voltage_sampling_frequency in_voltage_scale
> >>> in_voltage_scale_available
> >>> name of_node power scan_elements subsystem uevent
> >>> -------------------------------------------------------------------
> >>>
> >>> iio:device0 has some fake kfifo buffers attached to an IIO device.
> >> For me there is one major problem with this approach. We only allow one
> >> application to open /dev/iio:deviceX at a time. This means we can't have
> >> different applications access different buffers of the same device. I
> >> believe this is a circuital feature.
> > Thats not quite true (I think - though I've not tested it). What we don't
> > allow is for multiple processes to access them in an unaware fashion.
> > My assumption is we can rely on fork + fd passing via appropriate sockets.
> >
> >> It is possible to open the chardev, get the annonfd, close the chardev
> >> and keep the annonfd open. Then the next application can do the same and
> >> get access to a different buffer. But this has room for race conditions
> >> when two applications try this at the very same time.
> >>
> >> We need to somehow address this.
> > I'd count this as a bug :). It could be safely done in a particular custom
> > system but in general it opens a can of worm.
> >
> >> I'm also not much of a fan of using ioctls to create annon fds. In part
> >> because all the standard mechanisms for access control no longer work.
> > The inability to trivially have multiple processes open the anon fds
> > without care is one of the things I like most about them.
> >
> > IIO drivers and interfaces really aren't designed for multiple unaware
> > processes to access them. We don't have per process controls for device
> > wide sysfs attributes etc. In general, it would be hard to
> > do due to the complexity of modeling all the interactions between the
> > different interfaces (events / buffers / sysfs access) in a generic fashion.
> >
> > As such, the model, in my head at least, is that we only want a single
> > process to ever be responsible for access control. That process can then
> > assign access to children or via a deliberate action (I think passing the
> > anon fd over a unix socket should work for example). The intent being
> > that it is also responsible for mediating access to infrastructure that
> > multiple child processes all want to access.
> >
> > As such, having one chrdev isn't a disadvantage because only one process
> > should ever open it at a time. This same process also handles the
> > resource / control mediation. Therefore we should only have one file
> > exposed for all the standard access control mechanisms.
> >
> Hm, I see your point, but I'm not convinced.
>
> Having to have explicit synchronization makes it difficult to mix and
> match. E.g. at ADI a popular use case for testing was to run some signal
> generator application on the TX buffer and some signal analyzer
> application on the RX buffer.
>
> Both can be launched independently and there can be different types of
> generator and analyzer applications. Having to have a 3rd application to
> arbitrate access makes this quite cumbersome. And I'm afraid that in
> reality people might just stick with the two devices model just to avoid
> this restriction.

I'd argue that's a problem best tackled in a library - though it's a bit
fiddly. It ought to be possible to make it invisible that this level
of sharing is going on. The management process you describe would probably
be a thread running inside the first process to try and access a given device.
A second process failing to open the file with -EBUSY then connects to
appropriate socket (via path in /tmp or similar) and asks for the FD.
There are race conditions that might make it fail, but a retry loop should
deal with those.

I agree people might just stick to a two device model and if the devices
are independent enough I'm not sure that is the wrong way to approach the
problem. It represents the independence and that the driver is being careful
that it both can and is safely handle independent simultaneous accessors.
We are always going to have some drivers doing that anyway because they've
already been doing that for years.

J

>
> - Lars
>

2021-02-28 19:35:56

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [PATCH v6 19/24] iio: buffer: introduce support for attaching more IIO buffers

On Sun, Feb 28, 2021 at 10:31 AM Lars-Peter Clausen <[email protected]> wrote:
>
> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > static ssize_t iio_show_scan_index(struct device *dev,
> > struct device_attribute *attr,
> > char *buf)
> > @@ -1451,11 +1465,13 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
> > iio_free_chan_devattr_list(&buffer->buffer_attr_list);
> > }
> >
> > -int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> > +int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> > {
> > [...]
> > +error_unwind_sysfs_and_mask:
> > + for (; unwind_idx >= 0; unwind_idx--) {
> > + buffer = iio_dev_opaque->attached_buffers[unwind_idx];
> > + __iio_buffer_free_sysfs_and_mask(buffer);
> > + }
> > + kfree(iio_dev_opaque->attached_buffers);
> > + return ret;
> > }
> >
> > -void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> > +void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev)
> > {
> > [...]
> > + for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
> > + buffer = iio_dev_opaque->attached_buffers[i];
> > + __iio_buffer_free_sysfs_and_mask(buffer);
> > + }
> > +
> > + kfree(iio_dev_opaque->attached_buffers);
> > }
> > [...]
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > index 1d500ea246af..f7f785431106 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -1583,7 +1583,7 @@ static void iio_dev_release(struct device *device)
> > iio_device_unregister_eventset(indio_dev);
> > iio_device_unregister_sysfs(indio_dev);
> >
> > - iio_buffer_put(indio_dev->buffer);
> > + iio_buffers_put(indio_dev);
> We do call kfree(iio_dev_opaque->attached_buffers) before we get here. I
> think we need to keep the array around, otherwise we end of up with a
> use after free.
>

Good catch.
Will send an update here.

2021-02-28 20:34:50

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sun, Feb 28, 2021 at 5:54 PM Lars-Peter Clausen <[email protected]> wrote:
>
> On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > On Sun, 28 Feb 2021 09:51:38 +0100
> > Lars-Peter Clausen <[email protected]> wrote:
> >
> >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> >>> With this change, an ioctl() call is added to open a character device for a
> >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> >>>
> >>> The ioctl() will return an FD for the requested buffer index. The indexes
> >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
> >>> variable).
> >>>
> >>> Since there doesn't seem to be a sane way to return the FD for buffer0 to
> >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return another
> >>> FD for buffer0 (or the first buffer). This duplicate FD will be able to
> >>> access the same buffer object (for buffer0) as accessing directly the
> >>> /dev/iio:deviceX chardev.
> >>>
> >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
> >>> index for each buffer (and the count) can be deduced from the
> >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
> >>> bufferY folders).
> >>>
> >>> Used following C code to test this:
> >>> -------------------------------------------------------------------
> >>>
> >>> #include <stdio.h>
> >>> #include <stdlib.h>
> >>> #include <unistd.h>
> >>> #include <sys/ioctl.h>
> >>> #include <fcntl.h"
> >>> #include <errno.h>
> >>>
> >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> >>>
> >>> int main(int argc, char *argv[])
> >>> {
> >>> int fd;
> >>> int fd1;
> >>> int ret;
> >>>
> >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> >>> fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
> >>> return -1;
> >>> }
> >>>
> >>> fprintf(stderr, "Using FD %d\n", fd);
> >>>
> >>> fd1 = atoi(argv[1]);
> >>>
> >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> >>> if (ret < 0) {
> >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
> >>> close(fd);
> >>> return -1;
> >>> }
> >>>
> >>> fprintf(stderr, "Got FD %d\n", fd1);
> >>>
> >>> close(fd1);
> >>> close(fd);
> >>>
> >>> return 0;
> >>> }
> >>> -------------------------------------------------------------------
> >>>
> >>> Results are:
> >>> -------------------------------------------------------------------
> >>> # ./test 0
> >>> Using FD 3
> >>> Got FD 4
> >>>
> >>> # ./test 1
> >>> Using FD 3
> >>> Got FD 4
> >>>
> >>> # ./test 2
> >>> Using FD 3
> >>> Got FD 4
> >>>
> >>> # ./test 3
> >>> Using FD 3
> >>> Got FD 4
> >>>
> >>> # ls /sys/bus/iio/devices/iio\:device0
> >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> >>> in_voltage_sampling_frequency in_voltage_scale
> >>> in_voltage_scale_available
> >>> name of_node power scan_elements subsystem uevent
> >>> -------------------------------------------------------------------
> >>>
> >>> iio:device0 has some fake kfifo buffers attached to an IIO device.
> >> For me there is one major problem with this approach. We only allow one
> >> application to open /dev/iio:deviceX at a time. This means we can't have
> >> different applications access different buffers of the same device. I
> >> believe this is a circuital feature.
> > Thats not quite true (I think - though I've not tested it). What we don't
> > allow is for multiple processes to access them in an unaware fashion.
> > My assumption is we can rely on fork + fd passing via appropriate sockets.
> >
> >> It is possible to open the chardev, get the annonfd, close the chardev
> >> and keep the annonfd open. Then the next application can do the same and
> >> get access to a different buffer. But this has room for race conditions
> >> when two applications try this at the very same time.
> >>
> >> We need to somehow address this.
> > I'd count this as a bug :). It could be safely done in a particular custom
> > system but in general it opens a can of worm.

I'll take a look at this.

> >
> >> I'm also not much of a fan of using ioctls to create annon fds. In part
> >> because all the standard mechanisms for access control no longer work.
> > The inability to trivially have multiple processes open the anon fds
> > without care is one of the things I like most about them.
> >
> > IIO drivers and interfaces really aren't designed for multiple unaware
> > processes to access them. We don't have per process controls for device
> > wide sysfs attributes etc. In general, it would be hard to
> > do due to the complexity of modeling all the interactions between the
> > different interfaces (events / buffers / sysfs access) in a generic fashion.
> >
> > As such, the model, in my head at least, is that we only want a single
> > process to ever be responsible for access control. That process can then
> > assign access to children or via a deliberate action (I think passing the
> > anon fd over a unix socket should work for example). The intent being
> > that it is also responsible for mediating access to infrastructure that
> > multiple child processes all want to access.
> >
> > As such, having one chrdev isn't a disadvantage because only one process
> > should ever open it at a time. This same process also handles the
> > resource / control mediation. Therefore we should only have one file
> > exposed for all the standard access control mechanisms.
> >
> Hm, I see your point, but I'm not convinced.
>
> Having to have explicit synchronization makes it difficult to mix and
> match. E.g. at ADI a popular use case for testing was to run some signal
> generator application on the TX buffer and some signal analyzer
> application on the RX buffer.
>
> Both can be launched independently and there can be different types of
> generator and analyzer applications. Having to have a 3rd application to
> arbitrate access makes this quite cumbersome. And I'm afraid that in
> reality people might just stick with the two devices model just to avoid
> this restriction.

I'm neutral on this part of the debate.
I feel this may be some older discussion being refreshed here (it's
just a personal feeling).

I can try to accommodate a solution if something (else) is agreed.
Though at this point it may be a little slower.
I'm no longer an ADI employee, so it may take me a little longer to
test some things.

>
> - Lars
>

2021-03-06 17:03:36

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
<[email protected]> wrote:
>
> On Sun, 28 Feb 2021 16:51:51 +0100
> Lars-Peter Clausen <[email protected]> wrote:
>
> > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > Lars-Peter Clausen <[email protected]> wrote:
> > >
> > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > >>> With this change, an ioctl() call is added to open a character device for a
> > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > >>>
> > >>> The ioctl() will return an FD for the requested buffer index. The indexes
> > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
> > >>> variable).
> > >>>
> > >>> Since there doesn't seem to be a sane way to return the FD for buffer0 to
> > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return another
> > >>> FD for buffer0 (or the first buffer). This duplicate FD will be able to
> > >>> access the same buffer object (for buffer0) as accessing directly the
> > >>> /dev/iio:deviceX chardev.
> > >>>
> > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
> > >>> index for each buffer (and the count) can be deduced from the
> > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
> > >>> bufferY folders).
> > >>>
> > >>> Used following C code to test this:
> > >>> -------------------------------------------------------------------
> > >>>
> > >>> #include <stdio.h>
> > >>> #include <stdlib.h>
> > >>> #include <unistd.h>
> > >>> #include <sys/ioctl.h>
> > >>> #include <fcntl.h"
> > >>> #include <errno.h>
> > >>>
> > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > >>>
> > >>> int main(int argc, char *argv[])
> > >>> {
> > >>> int fd;
> > >>> int fd1;
> > >>> int ret;
> > >>>
> > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > >>> fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
> > >>> return -1;
> > >>> }
> > >>>
> > >>> fprintf(stderr, "Using FD %d\n", fd);
> > >>>
> > >>> fd1 = atoi(argv[1]);
> > >>>
> > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > >>> if (ret < 0) {
> > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
> > >>> close(fd);
> > >>> return -1;
> > >>> }
> > >>>
> > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > >>>
> > >>> close(fd1);
> > >>> close(fd);
> > >>>
> > >>> return 0;
> > >>> }
> > >>> -------------------------------------------------------------------
> > >>>
> > >>> Results are:
> > >>> -------------------------------------------------------------------
> > >>> # ./test 0
> > >>> Using FD 3
> > >>> Got FD 4
> > >>>
> > >>> # ./test 1
> > >>> Using FD 3
> > >>> Got FD 4
> > >>>
> > >>> # ./test 2
> > >>> Using FD 3
> > >>> Got FD 4
> > >>>
> > >>> # ./test 3
> > >>> Using FD 3
> > >>> Got FD 4
> > >>>
> > >>> # ls /sys/bus/iio/devices/iio\:device0
> > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > >>> in_voltage_sampling_frequency in_voltage_scale
> > >>> in_voltage_scale_available
> > >>> name of_node power scan_elements subsystem uevent
> > >>> -------------------------------------------------------------------
> > >>>
> > >>> iio:device0 has some fake kfifo buffers attached to an IIO device.
> > >> For me there is one major problem with this approach. We only allow one
> > >> application to open /dev/iio:deviceX at a time. This means we can't have
> > >> different applications access different buffers of the same device. I
> > >> believe this is a circuital feature.
> > > Thats not quite true (I think - though I've not tested it). What we don't
> > > allow is for multiple processes to access them in an unaware fashion.
> > > My assumption is we can rely on fork + fd passing via appropriate sockets.
> > >
> > >> It is possible to open the chardev, get the annonfd, close the chardev
> > >> and keep the annonfd open. Then the next application can do the same and
> > >> get access to a different buffer. But this has room for race conditions
> > >> when two applications try this at the very same time.
> > >>
> > >> We need to somehow address this.
> > > I'd count this as a bug :). It could be safely done in a particular custom
> > > system but in general it opens a can of worm.
> > >
> > >> I'm also not much of a fan of using ioctls to create annon fds. In part
> > >> because all the standard mechanisms for access control no longer work.
> > > The inability to trivially have multiple processes open the anon fds
> > > without care is one of the things I like most about them.
> > >
> > > IIO drivers and interfaces really aren't designed for multiple unaware
> > > processes to access them. We don't have per process controls for device
> > > wide sysfs attributes etc. In general, it would be hard to
> > > do due to the complexity of modeling all the interactions between the
> > > different interfaces (events / buffers / sysfs access) in a generic fashion.
> > >
> > > As such, the model, in my head at least, is that we only want a single
> > > process to ever be responsible for access control. That process can then
> > > assign access to children or via a deliberate action (I think passing the
> > > anon fd over a unix socket should work for example). The intent being
> > > that it is also responsible for mediating access to infrastructure that
> > > multiple child processes all want to access.
> > >
> > > As such, having one chrdev isn't a disadvantage because only one process
> > > should ever open it at a time. This same process also handles the
> > > resource / control mediation. Therefore we should only have one file
> > > exposed for all the standard access control mechanisms.
> > >
> > Hm, I see your point, but I'm not convinced.
> >
> > Having to have explicit synchronization makes it difficult to mix and
> > match. E.g. at ADI a popular use case for testing was to run some signal
> > generator application on the TX buffer and some signal analyzer
> > application on the RX buffer.
> >
> > Both can be launched independently and there can be different types of
> > generator and analyzer applications. Having to have a 3rd application to
> > arbitrate access makes this quite cumbersome. And I'm afraid that in
> > reality people might just stick with the two devices model just to avoid
> > this restriction.
>
> I'd argue that's a problem best tackled in a library - though it's a bit
> fiddly. It ought to be possible to make it invisible that this level
> of sharing is going on. The management process you describe would probably
> be a thread running inside the first process to try and access a given device.
> A second process failing to open the file with -EBUSY then connects to
> appropriate socket (via path in /tmp or similar) and asks for the FD.
> There are race conditions that might make it fail, but a retry loop should
> deal with those.
>
> I agree people might just stick to a two device model and if the devices
> are independent enough I'm not sure that is the wrong way to approach the
> problem. It represents the independence and that the driver is being careful
> that it both can and is safely handle independent simultaneous accessors.
> We are always going to have some drivers doing that anyway because they've
> already been doing that for years.
>

This is the last of the 3 patches that I need to re-spin after Lars' review.
I have a good handle on the small stuff.

I'm not sure about the race-condition about which Lars was talking about.
I mean, I get the problem, but is it a problem that we should fix in the kernel?

I'm sensing that Jonathan's preference is to keep things mostly as the
current implementation.
I'll probably leave this alone for a few days.
And I'll prepare some patches for the tweaks Lars suggested (adding
O_NONBLOCK and doing things a bit differently with the FD).
I'll send those in the next few days.

> J
>
> >
> > - Lars
> >
>

2021-03-07 15:03:44

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sat, 6 Mar 2021 19:00:52 +0200
Alexandru Ardelean <[email protected]> wrote:

> On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
> <[email protected]> wrote:
> >
> > On Sun, 28 Feb 2021 16:51:51 +0100
> > Lars-Peter Clausen <[email protected]> wrote:
> >
> > > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > > Lars-Peter Clausen <[email protected]> wrote:
> > > >
> > > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > > >>> With this change, an ioctl() call is added to open a character device for a
> > > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > > >>>
> > > >>> The ioctl() will return an FD for the requested buffer index. The indexes
> > > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
> > > >>> variable).
> > > >>>
> > > >>> Since there doesn't seem to be a sane way to return the FD for buffer0 to
> > > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return another
> > > >>> FD for buffer0 (or the first buffer). This duplicate FD will be able to
> > > >>> access the same buffer object (for buffer0) as accessing directly the
> > > >>> /dev/iio:deviceX chardev.
> > > >>>
> > > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
> > > >>> index for each buffer (and the count) can be deduced from the
> > > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
> > > >>> bufferY folders).
> > > >>>
> > > >>> Used following C code to test this:
> > > >>> -------------------------------------------------------------------
> > > >>>
> > > >>> #include <stdio.h>
> > > >>> #include <stdlib.h>
> > > >>> #include <unistd.h>
> > > >>> #include <sys/ioctl.h>
> > > >>> #include <fcntl.h"
> > > >>> #include <errno.h>
> > > >>>
> > > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > > >>>
> > > >>> int main(int argc, char *argv[])
> > > >>> {
> > > >>> int fd;
> > > >>> int fd1;
> > > >>> int ret;
> > > >>>
> > > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > > >>> fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
> > > >>> return -1;
> > > >>> }
> > > >>>
> > > >>> fprintf(stderr, "Using FD %d\n", fd);
> > > >>>
> > > >>> fd1 = atoi(argv[1]);
> > > >>>
> > > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > > >>> if (ret < 0) {
> > > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
> > > >>> close(fd);
> > > >>> return -1;
> > > >>> }
> > > >>>
> > > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > > >>>
> > > >>> close(fd1);
> > > >>> close(fd);
> > > >>>
> > > >>> return 0;
> > > >>> }
> > > >>> -------------------------------------------------------------------
> > > >>>
> > > >>> Results are:
> > > >>> -------------------------------------------------------------------
> > > >>> # ./test 0
> > > >>> Using FD 3
> > > >>> Got FD 4
> > > >>>
> > > >>> # ./test 1
> > > >>> Using FD 3
> > > >>> Got FD 4
> > > >>>
> > > >>> # ./test 2
> > > >>> Using FD 3
> > > >>> Got FD 4
> > > >>>
> > > >>> # ./test 3
> > > >>> Using FD 3
> > > >>> Got FD 4
> > > >>>
> > > >>> # ls /sys/bus/iio/devices/iio\:device0
> > > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > > >>> in_voltage_sampling_frequency in_voltage_scale
> > > >>> in_voltage_scale_available
> > > >>> name of_node power scan_elements subsystem uevent
> > > >>> -------------------------------------------------------------------
> > > >>>
> > > >>> iio:device0 has some fake kfifo buffers attached to an IIO device.
> > > >> For me there is one major problem with this approach. We only allow one
> > > >> application to open /dev/iio:deviceX at a time. This means we can't have
> > > >> different applications access different buffers of the same device. I
> > > >> believe this is a circuital feature.
> > > > Thats not quite true (I think - though I've not tested it). What we don't
> > > > allow is for multiple processes to access them in an unaware fashion.
> > > > My assumption is we can rely on fork + fd passing via appropriate sockets.
> > > >
> > > >> It is possible to open the chardev, get the annonfd, close the chardev
> > > >> and keep the annonfd open. Then the next application can do the same and
> > > >> get access to a different buffer. But this has room for race conditions
> > > >> when two applications try this at the very same time.
> > > >>
> > > >> We need to somehow address this.
> > > > I'd count this as a bug :). It could be safely done in a particular custom
> > > > system but in general it opens a can of worm.
> > > >
> > > >> I'm also not much of a fan of using ioctls to create annon fds. In part
> > > >> because all the standard mechanisms for access control no longer work.
> > > > The inability to trivially have multiple processes open the anon fds
> > > > without care is one of the things I like most about them.
> > > >
> > > > IIO drivers and interfaces really aren't designed for multiple unaware
> > > > processes to access them. We don't have per process controls for device
> > > > wide sysfs attributes etc. In general, it would be hard to
> > > > do due to the complexity of modeling all the interactions between the
> > > > different interfaces (events / buffers / sysfs access) in a generic fashion.
> > > >
> > > > As such, the model, in my head at least, is that we only want a single
> > > > process to ever be responsible for access control. That process can then
> > > > assign access to children or via a deliberate action (I think passing the
> > > > anon fd over a unix socket should work for example). The intent being
> > > > that it is also responsible for mediating access to infrastructure that
> > > > multiple child processes all want to access.
> > > >
> > > > As such, having one chrdev isn't a disadvantage because only one process
> > > > should ever open it at a time. This same process also handles the
> > > > resource / control mediation. Therefore we should only have one file
> > > > exposed for all the standard access control mechanisms.
> > > >
> > > Hm, I see your point, but I'm not convinced.
> > >
> > > Having to have explicit synchronization makes it difficult to mix and
> > > match. E.g. at ADI a popular use case for testing was to run some signal
> > > generator application on the TX buffer and some signal analyzer
> > > application on the RX buffer.
> > >
> > > Both can be launched independently and there can be different types of
> > > generator and analyzer applications. Having to have a 3rd application to
> > > arbitrate access makes this quite cumbersome. And I'm afraid that in
> > > reality people might just stick with the two devices model just to avoid
> > > this restriction.
> >
> > I'd argue that's a problem best tackled in a library - though it's a bit
> > fiddly. It ought to be possible to make it invisible that this level
> > of sharing is going on. The management process you describe would probably
> > be a thread running inside the first process to try and access a given device.
> > A second process failing to open the file with -EBUSY then connects to
> > appropriate socket (via path in /tmp or similar) and asks for the FD.
> > There are race conditions that might make it fail, but a retry loop should
> > deal with those.
> >
> > I agree people might just stick to a two device model and if the devices
> > are independent enough I'm not sure that is the wrong way to approach the
> > problem. It represents the independence and that the driver is being careful
> > that it both can and is safely handle independent simultaneous accessors.
> > We are always going to have some drivers doing that anyway because they've
> > already been doing that for years.
> >
>
> This is the last of the 3 patches that I need to re-spin after Lars' review.
> I have a good handle on the small stuff.
>
> I'm not sure about the race-condition about which Lars was talking about.
> I mean, I get the problem, but is it a problem that we should fix in the kernel?
>
> I'm sensing that Jonathan's preference is to keep things mostly as the
> current implementation.
> I'll probably leave this alone for a few days.

Wise move :) Whilst I'm currently falling on the side of leaving the
current situation fo the ioctl, I'm not sure the discussion has completely
finished.

I'm not keen to delay this series too much because other stuff will back
up behind it. For now I'm in two minds on whether to back out the series
(on basis it's easy enough to bring back until churn gets us) or rely
on us being able to make any tweaks in a safe fashion.

Note we will have a little time to tweak the interface either way
as there aren't any mainline drivers using it yet. As a result
I'm open to other proposals until this becomes ABI that we have to
support for ever.

Ideally I'd have lots of time and mock up the userspace library handling
that I think would hid most of the magic needed, but who knows when I'll
get time for that...

> And I'll prepare some patches for the tweaks Lars suggested (adding
> O_NONBLOCK and doing things a bit differently with the FD).
> I'll send those in the next few days.

Great.

Jonathan

>
> > J
> >
> > >
> > > - Lars
> > >
> >

2021-03-13 18:48:59

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sun, 7 Mar 2021 12:13:08 +0000
Jonathan Cameron <[email protected]> wrote:

> On Sat, 6 Mar 2021 19:00:52 +0200
> Alexandru Ardelean <[email protected]> wrote:
>
> > On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
> > <[email protected]> wrote:
> > >
> > > On Sun, 28 Feb 2021 16:51:51 +0100
> > > Lars-Peter Clausen <[email protected]> wrote:
> > >
> > > > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > >
> > > > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > > > >>> With this change, an ioctl() call is added to open a character device for a
> > > > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > > > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > > > >>>
> > > > >>> The ioctl() will return an FD for the requested buffer index. The indexes
> > > > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY (i.e. the Y
> > > > >>> variable).
> > > > >>>
> > > > >>> Since there doesn't seem to be a sane way to return the FD for buffer0 to
> > > > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return another
> > > > >>> FD for buffer0 (or the first buffer). This duplicate FD will be able to
> > > > >>> access the same buffer object (for buffer0) as accessing directly the
> > > > >>> /dev/iio:deviceX chardev.
> > > > >>>
> > > > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl() implemented, as the
> > > > >>> index for each buffer (and the count) can be deduced from the
> > > > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the number of
> > > > >>> bufferY folders).
> > > > >>>
> > > > >>> Used following C code to test this:
> > > > >>> -------------------------------------------------------------------
> > > > >>>
> > > > >>> #include <stdio.h>
> > > > >>> #include <stdlib.h>
> > > > >>> #include <unistd.h>
> > > > >>> #include <sys/ioctl.h>
> > > > >>> #include <fcntl.h"
> > > > >>> #include <errno.h>
> > > > >>>
> > > > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > > > >>>
> > > > >>> int main(int argc, char *argv[])
> > > > >>> {
> > > > >>> int fd;
> > > > >>> int fd1;
> > > > >>> int ret;
> > > > >>>
> > > > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > > > >>> fprintf(stderr, "Error open() %d errno %d\n",fd, errno);
> > > > >>> return -1;
> > > > >>> }
> > > > >>>
> > > > >>> fprintf(stderr, "Using FD %d\n", fd);
> > > > >>>
> > > > >>> fd1 = atoi(argv[1]);
> > > > >>>
> > > > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > > > >>> if (ret < 0) {
> > > > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno %d\n", fd1, ret, errno);
> > > > >>> close(fd);
> > > > >>> return -1;
> > > > >>> }
> > > > >>>
> > > > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > > > >>>
> > > > >>> close(fd1);
> > > > >>> close(fd);
> > > > >>>
> > > > >>> return 0;
> > > > >>> }
> > > > >>> -------------------------------------------------------------------
> > > > >>>
> > > > >>> Results are:
> > > > >>> -------------------------------------------------------------------
> > > > >>> # ./test 0
> > > > >>> Using FD 3
> > > > >>> Got FD 4
> > > > >>>
> > > > >>> # ./test 1
> > > > >>> Using FD 3
> > > > >>> Got FD 4
> > > > >>>
> > > > >>> # ./test 2
> > > > >>> Using FD 3
> > > > >>> Got FD 4
> > > > >>>
> > > > >>> # ./test 3
> > > > >>> Using FD 3
> > > > >>> Got FD 4
> > > > >>>
> > > > >>> # ls /sys/bus/iio/devices/iio\:device0
> > > > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > > > >>> in_voltage_sampling_frequency in_voltage_scale
> > > > >>> in_voltage_scale_available
> > > > >>> name of_node power scan_elements subsystem uevent
> > > > >>> -------------------------------------------------------------------
> > > > >>>
> > > > >>> iio:device0 has some fake kfifo buffers attached to an IIO device.
> > > > >> For me there is one major problem with this approach. We only allow one
> > > > >> application to open /dev/iio:deviceX at a time. This means we can't have
> > > > >> different applications access different buffers of the same device. I
> > > > >> believe this is a circuital feature.
> > > > > Thats not quite true (I think - though I've not tested it). What we don't
> > > > > allow is for multiple processes to access them in an unaware fashion.
> > > > > My assumption is we can rely on fork + fd passing via appropriate sockets.
> > > > >
> > > > >> It is possible to open the chardev, get the annonfd, close the chardev
> > > > >> and keep the annonfd open. Then the next application can do the same and
> > > > >> get access to a different buffer. But this has room for race conditions
> > > > >> when two applications try this at the very same time.
> > > > >>
> > > > >> We need to somehow address this.
> > > > > I'd count this as a bug :). It could be safely done in a particular custom
> > > > > system but in general it opens a can of worm.
> > > > >
> > > > >> I'm also not much of a fan of using ioctls to create annon fds. In part
> > > > >> because all the standard mechanisms for access control no longer work.
> > > > > The inability to trivially have multiple processes open the anon fds
> > > > > without care is one of the things I like most about them.
> > > > >
> > > > > IIO drivers and interfaces really aren't designed for multiple unaware
> > > > > processes to access them. We don't have per process controls for device
> > > > > wide sysfs attributes etc. In general, it would be hard to
> > > > > do due to the complexity of modeling all the interactions between the
> > > > > different interfaces (events / buffers / sysfs access) in a generic fashion.
> > > > >
> > > > > As such, the model, in my head at least, is that we only want a single
> > > > > process to ever be responsible for access control. That process can then
> > > > > assign access to children or via a deliberate action (I think passing the
> > > > > anon fd over a unix socket should work for example). The intent being
> > > > > that it is also responsible for mediating access to infrastructure that
> > > > > multiple child processes all want to access.
> > > > >
> > > > > As such, having one chrdev isn't a disadvantage because only one process
> > > > > should ever open it at a time. This same process also handles the
> > > > > resource / control mediation. Therefore we should only have one file
> > > > > exposed for all the standard access control mechanisms.
> > > > >
> > > > Hm, I see your point, but I'm not convinced.
> > > >
> > > > Having to have explicit synchronization makes it difficult to mix and
> > > > match. E.g. at ADI a popular use case for testing was to run some signal
> > > > generator application on the TX buffer and some signal analyzer
> > > > application on the RX buffer.
> > > >
> > > > Both can be launched independently and there can be different types of
> > > > generator and analyzer applications. Having to have a 3rd application to
> > > > arbitrate access makes this quite cumbersome. And I'm afraid that in
> > > > reality people might just stick with the two devices model just to avoid
> > > > this restriction.
> > >
> > > I'd argue that's a problem best tackled in a library - though it's a bit
> > > fiddly. It ought to be possible to make it invisible that this level
> > > of sharing is going on. The management process you describe would probably
> > > be a thread running inside the first process to try and access a given device.
> > > A second process failing to open the file with -EBUSY then connects to
> > > appropriate socket (via path in /tmp or similar) and asks for the FD.
> > > There are race conditions that might make it fail, but a retry loop should
> > > deal with those.
> > >
> > > I agree people might just stick to a two device model and if the devices
> > > are independent enough I'm not sure that is the wrong way to approach the
> > > problem. It represents the independence and that the driver is being careful
> > > that it both can and is safely handle independent simultaneous accessors.
> > > We are always going to have some drivers doing that anyway because they've
> > > already been doing that for years.
> > >
> >
> > This is the last of the 3 patches that I need to re-spin after Lars' review.
> > I have a good handle on the small stuff.
> >
> > I'm not sure about the race-condition about which Lars was talking about.
> > I mean, I get the problem, but is it a problem that we should fix in the kernel?
> >
> > I'm sensing that Jonathan's preference is to keep things mostly as the
> > current implementation.
> > I'll probably leave this alone for a few days.
>
> Wise move :) Whilst I'm currently falling on the side of leaving the
> current situation fo the ioctl, I'm not sure the discussion has completely
> finished.
>
> I'm not keen to delay this series too much because other stuff will back
> up behind it. For now I'm in two minds on whether to back out the series
> (on basis it's easy enough to bring back until churn gets us) or rely
> on us being able to make any tweaks in a safe fashion.
>
> Note we will have a little time to tweak the interface either way
> as there aren't any mainline drivers using it yet. As a result
> I'm open to other proposals until this becomes ABI that we have to
> support for ever.
>
> Ideally I'd have lots of time and mock up the userspace library handling
> that I think would hid most of the magic needed, but who knows when I'll
> get time for that...
So just to update. My current plan is to leave this series in place as it
is (with fixes etc as they come).

If we need to we can always block off the ioctl later this cycle and rethink.

Jonathan

>
> > And I'll prepare some patches for the tweaks Lars suggested (adding
> > O_NONBLOCK and doing things a bit differently with the FD).
> > I'll send those in the next few days.
>
> Great.
>
> Jonathan
>
> >
> > > J
> > >
> > > >
> > > > - Lars
> > > >
> > >
>

2021-03-15 10:00:21

by Nuno Sa

[permalink] [raw]
Subject: RE: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device


> -----Original Message-----
> From: Alexandru Ardelean <[email protected]>
> Sent: Saturday, March 6, 2021 6:01 PM
> To: Jonathan Cameron <[email protected]>
> Cc: Lars-Peter Clausen <[email protected]>; zzzzArdelean,
> zzzzAlexandru <[email protected]>; LKML <linux-
> [email protected]>; linux-iio <[email protected]>;
> Hennerich, Michael <[email protected]>; Jonathan
> Cameron <[email protected]>; Sa, Nuno <[email protected]>;
> Bogdan, Dragos <[email protected]>
> Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening
> extra buffers for IIO device
>
> [External]
>
> On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
> <[email protected]> wrote:
> >
> > On Sun, 28 Feb 2021 16:51:51 +0100
> > Lars-Peter Clausen <[email protected]> wrote:
> >
> > > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > > Lars-Peter Clausen <[email protected]> wrote:
> > > >
> > > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > > >>> With this change, an ioctl() call is added to open a character
> device for a
> > > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > > >>>
> > > >>> The ioctl() will return an FD for the requested buffer index.
> The indexes
> > > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY
> (i.e. the Y
> > > >>> variable).
> > > >>>
> > > >>> Since there doesn't seem to be a sane way to return the FD for
> buffer0 to
> > > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return
> another
> > > >>> FD for buffer0 (or the first buffer). This duplicate FD will be
> able to
> > > >>> access the same buffer object (for buffer0) as accessing
> directly the
> > > >>> /dev/iio:deviceX chardev.
> > > >>>
> > > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl()
> implemented, as the
> > > >>> index for each buffer (and the count) can be deduced from
> the
> > > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the
> number of
> > > >>> bufferY folders).
> > > >>>
> > > >>> Used following C code to test this:
> > > >>> -------------------------------------------------------------------
> > > >>>
> > > >>> #include <stdio.h>
> > > >>> #include <stdlib.h>
> > > >>> #include <unistd.h>
> > > >>> #include <sys/ioctl.h>
> > > >>> #include <fcntl.h"
> > > >>> #include <errno.h>
> > > >>>
> > > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > > >>>
> > > >>> int main(int argc, char *argv[])
> > > >>> {
> > > >>> int fd;
> > > >>> int fd1;
> > > >>> int ret;
> > > >>>
> > > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > > >>> fprintf(stderr, "Error open() %d errno %d\n",fd,
> errno);
> > > >>> return -1;
> > > >>> }
> > > >>>
> > > >>> fprintf(stderr, "Using FD %d\n", fd);
> > > >>>
> > > >>> fd1 = atoi(argv[1]);
> > > >>>
> > > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > > >>> if (ret < 0) {
> > > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno
> %d\n", fd1, ret, errno);
> > > >>> close(fd);
> > > >>> return -1;
> > > >>> }
> > > >>>
> > > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > > >>>
> > > >>> close(fd1);
> > > >>> close(fd);
> > > >>>
> > > >>> return 0;
> > > >>> }
> > > >>> -------------------------------------------------------------------
> > > >>>
> > > >>> Results are:
> > > >>> -------------------------------------------------------------------
> > > >>> # ./test 0
> > > >>> Using FD 3
> > > >>> Got FD 4
> > > >>>
> > > >>> # ./test 1
> > > >>> Using FD 3
> > > >>> Got FD 4
> > > >>>
> > > >>> # ./test 2
> > > >>> Using FD 3
> > > >>> Got FD 4
> > > >>>
> > > >>> # ./test 3
> > > >>> Using FD 3
> > > >>> Got FD 4
> > > >>>
> > > >>> # ls /sys/bus/iio/devices/iio\:device0
> > > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > > >>> in_voltage_sampling_frequency in_voltage_scale
> > > >>> in_voltage_scale_available
> > > >>> name of_node power scan_elements subsystem uevent
> > > >>> -------------------------------------------------------------------
> > > >>>
> > > >>> iio:device0 has some fake kfifo buffers attached to an IIO
> device.
> > > >> For me there is one major problem with this approach. We only
> allow one
> > > >> application to open /dev/iio:deviceX at a time. This means we
> can't have
> > > >> different applications access different buffers of the same
> device. I
> > > >> believe this is a circuital feature.
> > > > Thats not quite true (I think - though I've not tested it). What we
> don't
> > > > allow is for multiple processes to access them in an unaware
> fashion.
> > > > My assumption is we can rely on fork + fd passing via appropriate
> sockets.
> > > >
> > > >> It is possible to open the chardev, get the annonfd, close the
> chardev
> > > >> and keep the annonfd open. Then the next application can do
> the same and
> > > >> get access to a different buffer. But this has room for race
> conditions
> > > >> when two applications try this at the very same time.
> > > >>
> > > >> We need to somehow address this.
> > > > I'd count this as a bug :). It could be safely done in a particular
> custom
> > > > system but in general it opens a can of worm.
> > > >
> > > >> I'm also not much of a fan of using ioctls to create annon fds. In
> part
> > > >> because all the standard mechanisms for access control no
> longer work.
> > > > The inability to trivially have multiple processes open the anon
> fds
> > > > without care is one of the things I like most about them.
> > > >
> > > > IIO drivers and interfaces really aren't designed for multiple
> unaware
> > > > processes to access them. We don't have per process controls
> for device
> > > > wide sysfs attributes etc. In general, it would be hard to
> > > > do due to the complexity of modeling all the interactions
> between the
> > > > different interfaces (events / buffers / sysfs access) in a generic
> fashion.
> > > >
> > > > As such, the model, in my head at least, is that we only want a
> single
> > > > process to ever be responsible for access control. That process
> can then
> > > > assign access to children or via a deliberate action (I think passing
> the
> > > > anon fd over a unix socket should work for example). The intent
> being
> > > > that it is also responsible for mediating access to infrastructure
> that
> > > > multiple child processes all want to access.
> > > >
> > > > As such, having one chrdev isn't a disadvantage because only one
> process
> > > > should ever open it at a time. This same process also handles the
> > > > resource / control mediation. Therefore we should only have
> one file
> > > > exposed for all the standard access control mechanisms.
> > > >
> > > Hm, I see your point, but I'm not convinced.
> > >
> > > Having to have explicit synchronization makes it difficult to mix and
> > > match. E.g. at ADI a popular use case for testing was to run some
> signal
> > > generator application on the TX buffer and some signal analyzer
> > > application on the RX buffer.
> > >
> > > Both can be launched independently and there can be different
> types of
> > > generator and analyzer applications. Having to have a 3rd
> application to
> > > arbitrate access makes this quite cumbersome. And I'm afraid that
> in
> > > reality people might just stick with the two devices model just to
> avoid
> > > this restriction.
> >
> > I'd argue that's a problem best tackled in a library - though it's a bit
> > fiddly. It ought to be possible to make it invisible that this level
> > of sharing is going on. The management process you describe would
> probably
> > be a thread running inside the first process to try and access a given
> device.
> > A second process failing to open the file with -EBUSY then connects
> to
> > appropriate socket (via path in /tmp or similar) and asks for the FD.
> > There are race conditions that might make it fail, but a retry loop
> should
> > deal with those.
> >
> > I agree people might just stick to a two device model and if the
> devices
> > are independent enough I'm not sure that is the wrong way to
> approach the
> > problem. It represents the independence and that the driver is
> being careful
> > that it both can and is safely handle independent simultaneous
> accessors.
> > We are always going to have some drivers doing that anyway
> because they've
> > already been doing that for years.
> >
>
> This is the last of the 3 patches that I need to re-spin after Lars' review.
> I have a good handle on the small stuff.
>
> I'm not sure about the race-condition about which Lars was talking
> about.
> I mean, I get the problem, but is it a problem that we should fix in the
> kernel?

Hi all,

FWIW, I think that this really depends on the chosen ABI. If we do use
the ioctl to return the buffer fd and just allow one app to hold the chardev
at a time, I agree with Alex that this is not really a race and is just something
that userspace needs to deal with....

That said and giving my superficial (I did not really read the full series) piece on this,
I get both Lars and Jonathan points and, personally, it feels that the most natural thing
would be to have a chardev per buffer...

On the other hand, AFAIC, events are also being handled in the same chardev as
buffers, which makes things harder in terms of consistency... Events are per device
and not per buffers right? My point is that, to have a chardev per buffer, it would make
sense to detach events from the buffer stuff and that seems to be not doable without
breaking ABI (we would probably need to assume that events and buffer0 are on the
same chardev).

- Nuno Sá

2021-03-20 17:52:21

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Mon, 15 Mar 2021 09:58:08 +0000
"Sa, Nuno" <[email protected]> wrote:

> > -----Original Message-----
> > From: Alexandru Ardelean <[email protected]>
> > Sent: Saturday, March 6, 2021 6:01 PM
> > To: Jonathan Cameron <[email protected]>
> > Cc: Lars-Peter Clausen <[email protected]>; zzzzArdelean,
> > zzzzAlexandru <[email protected]>; LKML <linux-
> > [email protected]>; linux-iio <[email protected]>;
> > Hennerich, Michael <[email protected]>; Jonathan
> > Cameron <[email protected]>; Sa, Nuno <[email protected]>;
> > Bogdan, Dragos <[email protected]>
> > Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening
> > extra buffers for IIO device
> >
> > [External]
> >
> > On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
> > <[email protected]> wrote:
> > >
> > > On Sun, 28 Feb 2021 16:51:51 +0100
> > > Lars-Peter Clausen <[email protected]> wrote:
> > >
> > > > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > >
> > > > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > > > >>> With this change, an ioctl() call is added to open a character
> > device for a
> > > > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > > > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > > > >>>
> > > > >>> The ioctl() will return an FD for the requested buffer index.
> > The indexes
> > > > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY
> > (i.e. the Y
> > > > >>> variable).
> > > > >>>
> > > > >>> Since there doesn't seem to be a sane way to return the FD for
> > buffer0 to
> > > > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return
> > another
> > > > >>> FD for buffer0 (or the first buffer). This duplicate FD will be
> > able to
> > > > >>> access the same buffer object (for buffer0) as accessing
> > directly the
> > > > >>> /dev/iio:deviceX chardev.
> > > > >>>
> > > > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl()
> > implemented, as the
> > > > >>> index for each buffer (and the count) can be deduced from
> > the
> > > > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the
> > number of
> > > > >>> bufferY folders).
> > > > >>>
> > > > >>> Used following C code to test this:
> > > > >>> -------------------------------------------------------------------
> > > > >>>
> > > > >>> #include <stdio.h>
> > > > >>> #include <stdlib.h>
> > > > >>> #include <unistd.h>
> > > > >>> #include <sys/ioctl.h>
> > > > >>> #include <fcntl.h"
> > > > >>> #include <errno.h>
> > > > >>>
> > > > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > > > >>>
> > > > >>> int main(int argc, char *argv[])
> > > > >>> {
> > > > >>> int fd;
> > > > >>> int fd1;
> > > > >>> int ret;
> > > > >>>
> > > > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > > > >>> fprintf(stderr, "Error open() %d errno %d\n",fd,
> > errno);
> > > > >>> return -1;
> > > > >>> }
> > > > >>>
> > > > >>> fprintf(stderr, "Using FD %d\n", fd);
> > > > >>>
> > > > >>> fd1 = atoi(argv[1]);
> > > > >>>
> > > > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > > > >>> if (ret < 0) {
> > > > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno
> > %d\n", fd1, ret, errno);
> > > > >>> close(fd);
> > > > >>> return -1;
> > > > >>> }
> > > > >>>
> > > > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > > > >>>
> > > > >>> close(fd1);
> > > > >>> close(fd);
> > > > >>>
> > > > >>> return 0;
> > > > >>> }
> > > > >>> -------------------------------------------------------------------
> > > > >>>
> > > > >>> Results are:
> > > > >>> -------------------------------------------------------------------
> > > > >>> # ./test 0
> > > > >>> Using FD 3
> > > > >>> Got FD 4
> > > > >>>
> > > > >>> # ./test 1
> > > > >>> Using FD 3
> > > > >>> Got FD 4
> > > > >>>
> > > > >>> # ./test 2
> > > > >>> Using FD 3
> > > > >>> Got FD 4
> > > > >>>
> > > > >>> # ./test 3
> > > > >>> Using FD 3
> > > > >>> Got FD 4
> > > > >>>
> > > > >>> # ls /sys/bus/iio/devices/iio\:device0
> > > > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > > > >>> in_voltage_sampling_frequency in_voltage_scale
> > > > >>> in_voltage_scale_available
> > > > >>> name of_node power scan_elements subsystem uevent
> > > > >>> -------------------------------------------------------------------
> > > > >>>
> > > > >>> iio:device0 has some fake kfifo buffers attached to an IIO
> > device.
> > > > >> For me there is one major problem with this approach. We only
> > allow one
> > > > >> application to open /dev/iio:deviceX at a time. This means we
> > can't have
> > > > >> different applications access different buffers of the same
> > device. I
> > > > >> believe this is a circuital feature.
> > > > > Thats not quite true (I think - though I've not tested it). What we
> > don't
> > > > > allow is for multiple processes to access them in an unaware
> > fashion.
> > > > > My assumption is we can rely on fork + fd passing via appropriate
> > sockets.
> > > > >
> > > > >> It is possible to open the chardev, get the annonfd, close the
> > chardev
> > > > >> and keep the annonfd open. Then the next application can do
> > the same and
> > > > >> get access to a different buffer. But this has room for race
> > conditions
> > > > >> when two applications try this at the very same time.
> > > > >>
> > > > >> We need to somehow address this.
> > > > > I'd count this as a bug :). It could be safely done in a particular
> > custom
> > > > > system but in general it opens a can of worm.
> > > > >
> > > > >> I'm also not much of a fan of using ioctls to create annon fds. In
> > part
> > > > >> because all the standard mechanisms for access control no
> > longer work.
> > > > > The inability to trivially have multiple processes open the anon
> > fds
> > > > > without care is one of the things I like most about them.
> > > > >
> > > > > IIO drivers and interfaces really aren't designed for multiple
> > unaware
> > > > > processes to access them. We don't have per process controls
> > for device
> > > > > wide sysfs attributes etc. In general, it would be hard to
> > > > > do due to the complexity of modeling all the interactions
> > between the
> > > > > different interfaces (events / buffers / sysfs access) in a generic
> > fashion.
> > > > >
> > > > > As such, the model, in my head at least, is that we only want a
> > single
> > > > > process to ever be responsible for access control. That process
> > can then
> > > > > assign access to children or via a deliberate action (I think passing
> > the
> > > > > anon fd over a unix socket should work for example). The intent
> > being
> > > > > that it is also responsible for mediating access to infrastructure
> > that
> > > > > multiple child processes all want to access.
> > > > >
> > > > > As such, having one chrdev isn't a disadvantage because only one
> > process
> > > > > should ever open it at a time. This same process also handles the
> > > > > resource / control mediation. Therefore we should only have
> > one file
> > > > > exposed for all the standard access control mechanisms.
> > > > >
> > > > Hm, I see your point, but I'm not convinced.
> > > >
> > > > Having to have explicit synchronization makes it difficult to mix and
> > > > match. E.g. at ADI a popular use case for testing was to run some
> > signal
> > > > generator application on the TX buffer and some signal analyzer
> > > > application on the RX buffer.
> > > >
> > > > Both can be launched independently and there can be different
> > types of
> > > > generator and analyzer applications. Having to have a 3rd
> > application to
> > > > arbitrate access makes this quite cumbersome. And I'm afraid that
> > in
> > > > reality people might just stick with the two devices model just to
> > avoid
> > > > this restriction.
> > >
> > > I'd argue that's a problem best tackled in a library - though it's a bit
> > > fiddly. It ought to be possible to make it invisible that this level
> > > of sharing is going on. The management process you describe would
> > probably
> > > be a thread running inside the first process to try and access a given
> > device.
> > > A second process failing to open the file with -EBUSY then connects
> > to
> > > appropriate socket (via path in /tmp or similar) and asks for the FD.
> > > There are race conditions that might make it fail, but a retry loop
> > should
> > > deal with those.
> > >
> > > I agree people might just stick to a two device model and if the
> > devices
> > > are independent enough I'm not sure that is the wrong way to
> > approach the
> > > problem. It represents the independence and that the driver is
> > being careful
> > > that it both can and is safely handle independent simultaneous
> > accessors.
> > > We are always going to have some drivers doing that anyway
> > because they've
> > > already been doing that for years.
> > >
> >
> > This is the last of the 3 patches that I need to re-spin after Lars' review.
> > I have a good handle on the small stuff.
> >
> > I'm not sure about the race-condition about which Lars was talking
> > about.
> > I mean, I get the problem, but is it a problem that we should fix in the
> > kernel?
>
> Hi all,
>
> FWIW, I think that this really depends on the chosen ABI. If we do use
> the ioctl to return the buffer fd and just allow one app to hold the chardev
> at a time, I agree with Alex that this is not really a race and is just something
> that userspace needs to deal with....
>
> That said and giving my superficial (I did not really read the full series) piece on this,
> I get both Lars and Jonathan points and, personally, it feels that the most natural thing
> would be to have a chardev per buffer...
>
> On the other hand, AFAIC, events are also being handled in the same chardev as
> buffers, which makes things harder in terms of consistency... Events are per device
> and not per buffers right? My point is that, to have a chardev per buffer, it would make
> sense to detach events from the buffer stuff and that seems to be not doable without
> breaking ABI (we would probably need to assume that events and buffer0 are on the
> same chardev).

Events are interesting as there is no particular reason to assume the driver
handling buffer0 is the right one to deal with them. It might just as easily
be the case that they are of interest to a process that is concerned with buffer1.

To add a bit more flavour to my earlier comments.

I'm still concerned that if we did do multiple /dev/* files it would allow code
to think it has complete control over the device when it really doesn't.
Events are just one aspect of that.

We have had discussions in the past about allowing multiple userspace consumers
for a single buffer, but the conclusion there was that was a job for userspace
(daemon or similar) software which can deal with control inter dependencies etc.

There are already potential messy corners we don't handle for userspace
iio buffers vs in kernel users (what happens if they both try to control the
sampling frequency?) I'm not keen to broaden this problem set.
If a device genuinely has separate control and pipelines for different
buffers then we are probably better representing that cleanly as
an mfd type layer and two separate IIO devices. Its effectively the
same a multi chip package.

A more classic multibuffer usecase is the one where you have related
datastreams that run at different rates (often happens in devices with
tagged FIFO elements). These are tightly coupled but we need to split
the data stream (or add tagging to our FIFOs.). Another case would be
DMA based device that puts channels into buffers that are entirely
separate in memory address rather than interleaved.

So I still need to put together a PoC, but it feels like there are various
software models that will give the illusion of there being separate
/dev/* files, but with an aspect of control being possible.

1. Daemon, if present that can hand off chardevs to who needs them
2. Library to make the first user of the buffer responsible for providing
service to other users. Yes there are races, but I don't think they
are hard to deal in normal usecases. (retry loops)

Jonathan


>
> - Nuno Sá

2021-03-21 17:48:22

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sat, 20 Mar 2021 17:41:00 +0000
Jonathan Cameron <[email protected]> wrote:

> On Mon, 15 Mar 2021 09:58:08 +0000
> "Sa, Nuno" <[email protected]> wrote:
>
> > > -----Original Message-----
> > > From: Alexandru Ardelean <[email protected]>
> > > Sent: Saturday, March 6, 2021 6:01 PM
> > > To: Jonathan Cameron <[email protected]>
> > > Cc: Lars-Peter Clausen <[email protected]>; zzzzArdelean,
> > > zzzzAlexandru <[email protected]>; LKML <linux-
> > > [email protected]>; linux-iio <[email protected]>;
> > > Hennerich, Michael <[email protected]>; Jonathan
> > > Cameron <[email protected]>; Sa, Nuno <[email protected]>;
> > > Bogdan, Dragos <[email protected]>
> > > Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening
> > > extra buffers for IIO device
> > >
> > > [External]
> > >
> > > On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
> > > <[email protected]> wrote:
> > > >
> > > > On Sun, 28 Feb 2021 16:51:51 +0100
> > > > Lars-Peter Clausen <[email protected]> wrote:
> > > >
> > > > > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > > > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > > >
> > > > > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > > > > >>> With this change, an ioctl() call is added to open a character
> > > device for a
> > > > > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > > > > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > > > > >>>
> > > > > >>> The ioctl() will return an FD for the requested buffer index.
> > > The indexes
> > > > > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY
> > > (i.e. the Y
> > > > > >>> variable).
> > > > > >>>
> > > > > >>> Since there doesn't seem to be a sane way to return the FD for
> > > buffer0 to
> > > > > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return
> > > another
> > > > > >>> FD for buffer0 (or the first buffer). This duplicate FD will be
> > > able to
> > > > > >>> access the same buffer object (for buffer0) as accessing
> > > directly the
> > > > > >>> /dev/iio:deviceX chardev.
> > > > > >>>
> > > > > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl()
> > > implemented, as the
> > > > > >>> index for each buffer (and the count) can be deduced from
> > > the
> > > > > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the
> > > number of
> > > > > >>> bufferY folders).
> > > > > >>>
> > > > > >>> Used following C code to test this:
> > > > > >>> -------------------------------------------------------------------
> > > > > >>>
> > > > > >>> #include <stdio.h>
> > > > > >>> #include <stdlib.h>
> > > > > >>> #include <unistd.h>
> > > > > >>> #include <sys/ioctl.h>
> > > > > >>> #include <fcntl.h"
> > > > > >>> #include <errno.h>
> > > > > >>>
> > > > > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > > > > >>>
> > > > > >>> int main(int argc, char *argv[])
> > > > > >>> {
> > > > > >>> int fd;
> > > > > >>> int fd1;
> > > > > >>> int ret;
> > > > > >>>
> > > > > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > > > > >>> fprintf(stderr, "Error open() %d errno %d\n",fd,
> > > errno);
> > > > > >>> return -1;
> > > > > >>> }
> > > > > >>>
> > > > > >>> fprintf(stderr, "Using FD %d\n", fd);
> > > > > >>>
> > > > > >>> fd1 = atoi(argv[1]);
> > > > > >>>
> > > > > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > > > > >>> if (ret < 0) {
> > > > > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno
> > > %d\n", fd1, ret, errno);
> > > > > >>> close(fd);
> > > > > >>> return -1;
> > > > > >>> }
> > > > > >>>
> > > > > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > > > > >>>
> > > > > >>> close(fd1);
> > > > > >>> close(fd);
> > > > > >>>
> > > > > >>> return 0;
> > > > > >>> }
> > > > > >>> -------------------------------------------------------------------
> > > > > >>>
> > > > > >>> Results are:
> > > > > >>> -------------------------------------------------------------------
> > > > > >>> # ./test 0
> > > > > >>> Using FD 3
> > > > > >>> Got FD 4
> > > > > >>>
> > > > > >>> # ./test 1
> > > > > >>> Using FD 3
> > > > > >>> Got FD 4
> > > > > >>>
> > > > > >>> # ./test 2
> > > > > >>> Using FD 3
> > > > > >>> Got FD 4
> > > > > >>>
> > > > > >>> # ./test 3
> > > > > >>> Using FD 3
> > > > > >>> Got FD 4
> > > > > >>>
> > > > > >>> # ls /sys/bus/iio/devices/iio\:device0
> > > > > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > > > > >>> in_voltage_sampling_frequency in_voltage_scale
> > > > > >>> in_voltage_scale_available
> > > > > >>> name of_node power scan_elements subsystem uevent
> > > > > >>> -------------------------------------------------------------------
> > > > > >>>
> > > > > >>> iio:device0 has some fake kfifo buffers attached to an IIO
> > > device.
> > > > > >> For me there is one major problem with this approach. We only
> > > allow one
> > > > > >> application to open /dev/iio:deviceX at a time. This means we
> > > can't have
> > > > > >> different applications access different buffers of the same
> > > device. I
> > > > > >> believe this is a circuital feature.
> > > > > > Thats not quite true (I think - though I've not tested it). What we
> > > don't
> > > > > > allow is for multiple processes to access them in an unaware
> > > fashion.
> > > > > > My assumption is we can rely on fork + fd passing via appropriate
> > > sockets.
> > > > > >
> > > > > >> It is possible to open the chardev, get the annonfd, close the
> > > chardev
> > > > > >> and keep the annonfd open. Then the next application can do
> > > the same and
> > > > > >> get access to a different buffer. But this has room for race
> > > conditions
> > > > > >> when two applications try this at the very same time.
> > > > > >>
> > > > > >> We need to somehow address this.
> > > > > > I'd count this as a bug :). It could be safely done in a particular
> > > custom
> > > > > > system but in general it opens a can of worm.
> > > > > >
> > > > > >> I'm also not much of a fan of using ioctls to create annon fds. In
> > > part
> > > > > >> because all the standard mechanisms for access control no
> > > longer work.
> > > > > > The inability to trivially have multiple processes open the anon
> > > fds
> > > > > > without care is one of the things I like most about them.
> > > > > >
> > > > > > IIO drivers and interfaces really aren't designed for multiple
> > > unaware
> > > > > > processes to access them. We don't have per process controls
> > > for device
> > > > > > wide sysfs attributes etc. In general, it would be hard to
> > > > > > do due to the complexity of modeling all the interactions
> > > between the
> > > > > > different interfaces (events / buffers / sysfs access) in a generic
> > > fashion.
> > > > > >
> > > > > > As such, the model, in my head at least, is that we only want a
> > > single
> > > > > > process to ever be responsible for access control. That process
> > > can then
> > > > > > assign access to children or via a deliberate action (I think passing
> > > the
> > > > > > anon fd over a unix socket should work for example). The intent
> > > being
> > > > > > that it is also responsible for mediating access to infrastructure
> > > that
> > > > > > multiple child processes all want to access.
> > > > > >
> > > > > > As such, having one chrdev isn't a disadvantage because only one
> > > process
> > > > > > should ever open it at a time. This same process also handles the
> > > > > > resource / control mediation. Therefore we should only have
> > > one file
> > > > > > exposed for all the standard access control mechanisms.
> > > > > >
> > > > > Hm, I see your point, but I'm not convinced.
> > > > >
> > > > > Having to have explicit synchronization makes it difficult to mix and
> > > > > match. E.g. at ADI a popular use case for testing was to run some
> > > signal
> > > > > generator application on the TX buffer and some signal analyzer
> > > > > application on the RX buffer.
> > > > >
> > > > > Both can be launched independently and there can be different
> > > types of
> > > > > generator and analyzer applications. Having to have a 3rd
> > > application to
> > > > > arbitrate access makes this quite cumbersome. And I'm afraid that
> > > in
> > > > > reality people might just stick with the two devices model just to
> > > avoid
> > > > > this restriction.
> > > >
> > > > I'd argue that's a problem best tackled in a library - though it's a bit
> > > > fiddly. It ought to be possible to make it invisible that this level
> > > > of sharing is going on. The management process you describe would
> > > probably
> > > > be a thread running inside the first process to try and access a given
> > > device.
> > > > A second process failing to open the file with -EBUSY then connects
> > > to
> > > > appropriate socket (via path in /tmp or similar) and asks for the FD.
> > > > There are race conditions that might make it fail, but a retry loop
> > > should
> > > > deal with those.
> > > >
> > > > I agree people might just stick to a two device model and if the
> > > devices
> > > > are independent enough I'm not sure that is the wrong way to
> > > approach the
> > > > problem. It represents the independence and that the driver is
> > > being careful
> > > > that it both can and is safely handle independent simultaneous
> > > accessors.
> > > > We are always going to have some drivers doing that anyway
> > > because they've
> > > > already been doing that for years.
> > > >
> > >
> > > This is the last of the 3 patches that I need to re-spin after Lars' review.
> > > I have a good handle on the small stuff.
> > >
> > > I'm not sure about the race-condition about which Lars was talking
> > > about.
> > > I mean, I get the problem, but is it a problem that we should fix in the
> > > kernel?
> >
> > Hi all,
> >
> > FWIW, I think that this really depends on the chosen ABI. If we do use
> > the ioctl to return the buffer fd and just allow one app to hold the chardev
> > at a time, I agree with Alex that this is not really a race and is just something
> > that userspace needs to deal with....
> >
> > That said and giving my superficial (I did not really read the full series) piece on this,
> > I get both Lars and Jonathan points and, personally, it feels that the most natural thing
> > would be to have a chardev per buffer...
> >
> > On the other hand, AFAIC, events are also being handled in the same chardev as
> > buffers, which makes things harder in terms of consistency... Events are per device
> > and not per buffers right? My point is that, to have a chardev per buffer, it would make
> > sense to detach events from the buffer stuff and that seems to be not doable without
> > breaking ABI (we would probably need to assume that events and buffer0 are on the
> > same chardev).
>
> Events are interesting as there is no particular reason to assume the driver
> handling buffer0 is the right one to deal with them. It might just as easily
> be the case that they are of interest to a process that is concerned with buffer1.
>
> To add a bit more flavour to my earlier comments.
>
> I'm still concerned that if we did do multiple /dev/* files it would allow code
> to think it has complete control over the device when it really doesn't.
> Events are just one aspect of that.
>
> We have had discussions in the past about allowing multiple userspace consumers
> for a single buffer, but the conclusion there was that was a job for userspace
> (daemon or similar) software which can deal with control inter dependencies etc.
>
> There are already potential messy corners we don't handle for userspace
> iio buffers vs in kernel users (what happens if they both try to control the
> sampling frequency?) I'm not keen to broaden this problem set.
> If a device genuinely has separate control and pipelines for different
> buffers then we are probably better representing that cleanly as
> an mfd type layer and two separate IIO devices. Its effectively the
> same a multi chip package.
>
> A more classic multibuffer usecase is the one where you have related
> datastreams that run at different rates (often happens in devices with
> tagged FIFO elements). These are tightly coupled but we need to split
> the data stream (or add tagging to our FIFOs.). Another case would be
> DMA based device that puts channels into buffers that are entirely
> separate in memory address rather than interleaved.
>
> So I still need to put together a PoC, but it feels like there are various
> software models that will give the illusion of there being separate
> /dev/* files, but with an aspect of control being possible.
>
> 1. Daemon, if present that can hand off chardevs to who needs them
> 2. Library to make the first user of the buffer responsible for providing
> service to other users. Yes there are races, but I don't think they
> are hard to deal in normal usecases. (retry loops)

Hi Nuno / Others,

Nuno's mention of things being similar for the event anon
FD to the situation for the buffer anon FDs made me realise there was
a horrible short cut to a proof of concept that didn't require me
to wire up a multiple buffer device.

Upshot, is that I've just sent out a (definitely not for merging)
hacked up version of the iio_event_monitor that can act as server
or client. The idea is that the socket handling looks a bit
like what I'd expect to see hidden away in a library so as to
allow

1) Client 1 is after buffer 3.
It tries to open the /dev/iio\:deviceX chrdev and succeeds.
It spins up a thread with a listening socket for /tmp/iio\:deviceX-magic
Continues in main thread to request buffer 3.
2) Client 2 is after buffer 2
I tries to open the /dev/iio\:deviceX chrdev and fails.
It sleeps a moment (reduces chance of race with client 1)
It opens a connection to the socket via /tmp/iio\:deviceX-magic
Sends a request for the buffer 2 FD.
Thread in Client 1 calls the ioctl to get the buffer 2 FD which
it then sends on to Client 2 which can use it as if it had
requested it directly.

We might want to have a generic server version as well that doesn't
itself make use of any of the buffers as keeps the model more symmetric
and reduce common corner cases.

Anyhow the code I put together is terrible, but I wasn't 100% sure
there weren't any issues passing anon fd file handles and this shows
that at least in theory the approach I proposed above works.

Test is something like
./iio_events_network /dev/iio\:device1
./iio_events_network -c

Then make some events happen (I was using the dummy driver and
the event generator associated with that).
The server in this PoC just quits after handling off the FD.

Jonathan

>
> Jonathan
>
>
> >
> > - Nuno Sá
>

2021-03-23 09:55:04

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Sun, Mar 21, 2021 at 7:37 PM Jonathan Cameron
<[email protected]> wrote:
>
> On Sat, 20 Mar 2021 17:41:00 +0000
> Jonathan Cameron <[email protected]> wrote:
>
> > On Mon, 15 Mar 2021 09:58:08 +0000
> > "Sa, Nuno" <[email protected]> wrote:
> >
> > > > -----Original Message-----
> > > > From: Alexandru Ardelean <[email protected]>
> > > > Sent: Saturday, March 6, 2021 6:01 PM
> > > > To: Jonathan Cameron <[email protected]>
> > > > Cc: Lars-Peter Clausen <[email protected]>; zzzzArdelean,
> > > > zzzzAlexandru <[email protected]>; LKML <linux-
> > > > [email protected]>; linux-iio <[email protected]>;
> > > > Hennerich, Michael <[email protected]>; Jonathan
> > > > Cameron <[email protected]>; Sa, Nuno <[email protected]>;
> > > > Bogdan, Dragos <[email protected]>
> > > > Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening
> > > > extra buffers for IIO device
> > > >
> > > > [External]
> > > >
> > > > On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
> > > > <[email protected]> wrote:
> > > > >
> > > > > On Sun, 28 Feb 2021 16:51:51 +0100
> > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > >
> > > > > > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > > > > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > > > >
> > > > > > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > > > > > >>> With this change, an ioctl() call is added to open a character
> > > > device for a
> > > > > > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > > > > > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > > > > > >>>
> > > > > > >>> The ioctl() will return an FD for the requested buffer index.
> > > > The indexes
> > > > > > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY
> > > > (i.e. the Y
> > > > > > >>> variable).
> > > > > > >>>
> > > > > > >>> Since there doesn't seem to be a sane way to return the FD for
> > > > buffer0 to
> > > > > > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return
> > > > another
> > > > > > >>> FD for buffer0 (or the first buffer). This duplicate FD will be
> > > > able to
> > > > > > >>> access the same buffer object (for buffer0) as accessing
> > > > directly the
> > > > > > >>> /dev/iio:deviceX chardev.
> > > > > > >>>
> > > > > > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl()
> > > > implemented, as the
> > > > > > >>> index for each buffer (and the count) can be deduced from
> > > > the
> > > > > > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the
> > > > number of
> > > > > > >>> bufferY folders).
> > > > > > >>>
> > > > > > >>> Used following C code to test this:
> > > > > > >>> -------------------------------------------------------------------
> > > > > > >>>
> > > > > > >>> #include <stdio.h>
> > > > > > >>> #include <stdlib.h>
> > > > > > >>> #include <unistd.h>
> > > > > > >>> #include <sys/ioctl.h>
> > > > > > >>> #include <fcntl.h"
> > > > > > >>> #include <errno.h>
> > > > > > >>>
> > > > > > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > > > > > >>>
> > > > > > >>> int main(int argc, char *argv[])
> > > > > > >>> {
> > > > > > >>> int fd;
> > > > > > >>> int fd1;
> > > > > > >>> int ret;
> > > > > > >>>
> > > > > > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > > > > > >>> fprintf(stderr, "Error open() %d errno %d\n",fd,
> > > > errno);
> > > > > > >>> return -1;
> > > > > > >>> }
> > > > > > >>>
> > > > > > >>> fprintf(stderr, "Using FD %d\n", fd);
> > > > > > >>>
> > > > > > >>> fd1 = atoi(argv[1]);
> > > > > > >>>
> > > > > > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > > > > > >>> if (ret < 0) {
> > > > > > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno
> > > > %d\n", fd1, ret, errno);
> > > > > > >>> close(fd);
> > > > > > >>> return -1;
> > > > > > >>> }
> > > > > > >>>
> > > > > > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > > > > > >>>
> > > > > > >>> close(fd1);
> > > > > > >>> close(fd);
> > > > > > >>>
> > > > > > >>> return 0;
> > > > > > >>> }
> > > > > > >>> -------------------------------------------------------------------
> > > > > > >>>
> > > > > > >>> Results are:
> > > > > > >>> -------------------------------------------------------------------
> > > > > > >>> # ./test 0
> > > > > > >>> Using FD 3
> > > > > > >>> Got FD 4
> > > > > > >>>
> > > > > > >>> # ./test 1
> > > > > > >>> Using FD 3
> > > > > > >>> Got FD 4
> > > > > > >>>
> > > > > > >>> # ./test 2
> > > > > > >>> Using FD 3
> > > > > > >>> Got FD 4
> > > > > > >>>
> > > > > > >>> # ./test 3
> > > > > > >>> Using FD 3
> > > > > > >>> Got FD 4
> > > > > > >>>
> > > > > > >>> # ls /sys/bus/iio/devices/iio\:device0
> > > > > > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > > > > > >>> in_voltage_sampling_frequency in_voltage_scale
> > > > > > >>> in_voltage_scale_available
> > > > > > >>> name of_node power scan_elements subsystem uevent
> > > > > > >>> -------------------------------------------------------------------
> > > > > > >>>
> > > > > > >>> iio:device0 has some fake kfifo buffers attached to an IIO
> > > > device.
> > > > > > >> For me there is one major problem with this approach. We only
> > > > allow one
> > > > > > >> application to open /dev/iio:deviceX at a time. This means we
> > > > can't have
> > > > > > >> different applications access different buffers of the same
> > > > device. I
> > > > > > >> believe this is a circuital feature.
> > > > > > > Thats not quite true (I think - though I've not tested it). What we
> > > > don't
> > > > > > > allow is for multiple processes to access them in an unaware
> > > > fashion.
> > > > > > > My assumption is we can rely on fork + fd passing via appropriate
> > > > sockets.
> > > > > > >
> > > > > > >> It is possible to open the chardev, get the annonfd, close the
> > > > chardev
> > > > > > >> and keep the annonfd open. Then the next application can do
> > > > the same and
> > > > > > >> get access to a different buffer. But this has room for race
> > > > conditions
> > > > > > >> when two applications try this at the very same time.
> > > > > > >>
> > > > > > >> We need to somehow address this.
> > > > > > > I'd count this as a bug :). It could be safely done in a particular
> > > > custom
> > > > > > > system but in general it opens a can of worm.
> > > > > > >
> > > > > > >> I'm also not much of a fan of using ioctls to create annon fds. In
> > > > part
> > > > > > >> because all the standard mechanisms for access control no
> > > > longer work.
> > > > > > > The inability to trivially have multiple processes open the anon
> > > > fds
> > > > > > > without care is one of the things I like most about them.
> > > > > > >
> > > > > > > IIO drivers and interfaces really aren't designed for multiple
> > > > unaware
> > > > > > > processes to access them. We don't have per process controls
> > > > for device
> > > > > > > wide sysfs attributes etc. In general, it would be hard to
> > > > > > > do due to the complexity of modeling all the interactions
> > > > between the
> > > > > > > different interfaces (events / buffers / sysfs access) in a generic
> > > > fashion.
> > > > > > >
> > > > > > > As such, the model, in my head at least, is that we only want a
> > > > single
> > > > > > > process to ever be responsible for access control. That process
> > > > can then
> > > > > > > assign access to children or via a deliberate action (I think passing
> > > > the
> > > > > > > anon fd over a unix socket should work for example). The intent
> > > > being
> > > > > > > that it is also responsible for mediating access to infrastructure
> > > > that
> > > > > > > multiple child processes all want to access.
> > > > > > >
> > > > > > > As such, having one chrdev isn't a disadvantage because only one
> > > > process
> > > > > > > should ever open it at a time. This same process also handles the
> > > > > > > resource / control mediation. Therefore we should only have
> > > > one file
> > > > > > > exposed for all the standard access control mechanisms.
> > > > > > >
> > > > > > Hm, I see your point, but I'm not convinced.
> > > > > >
> > > > > > Having to have explicit synchronization makes it difficult to mix and
> > > > > > match. E.g. at ADI a popular use case for testing was to run some
> > > > signal
> > > > > > generator application on the TX buffer and some signal analyzer
> > > > > > application on the RX buffer.
> > > > > >
> > > > > > Both can be launched independently and there can be different
> > > > types of
> > > > > > generator and analyzer applications. Having to have a 3rd
> > > > application to
> > > > > > arbitrate access makes this quite cumbersome. And I'm afraid that
> > > > in
> > > > > > reality people might just stick with the two devices model just to
> > > > avoid
> > > > > > this restriction.
> > > > >
> > > > > I'd argue that's a problem best tackled in a library - though it's a bit
> > > > > fiddly. It ought to be possible to make it invisible that this level
> > > > > of sharing is going on. The management process you describe would
> > > > probably
> > > > > be a thread running inside the first process to try and access a given
> > > > device.
> > > > > A second process failing to open the file with -EBUSY then connects
> > > > to
> > > > > appropriate socket (via path in /tmp or similar) and asks for the FD.
> > > > > There are race conditions that might make it fail, but a retry loop
> > > > should
> > > > > deal with those.
> > > > >
> > > > > I agree people might just stick to a two device model and if the
> > > > devices
> > > > > are independent enough I'm not sure that is the wrong way to
> > > > approach the
> > > > > problem. It represents the independence and that the driver is
> > > > being careful
> > > > > that it both can and is safely handle independent simultaneous
> > > > accessors.
> > > > > We are always going to have some drivers doing that anyway
> > > > because they've
> > > > > already been doing that for years.
> > > > >
> > > >
> > > > This is the last of the 3 patches that I need to re-spin after Lars' review.
> > > > I have a good handle on the small stuff.
> > > >
> > > > I'm not sure about the race-condition about which Lars was talking
> > > > about.
> > > > I mean, I get the problem, but is it a problem that we should fix in the
> > > > kernel?
> > >
> > > Hi all,
> > >
> > > FWIW, I think that this really depends on the chosen ABI. If we do use
> > > the ioctl to return the buffer fd and just allow one app to hold the chardev
> > > at a time, I agree with Alex that this is not really a race and is just something
> > > that userspace needs to deal with....
> > >
> > > That said and giving my superficial (I did not really read the full series) piece on this,
> > > I get both Lars and Jonathan points and, personally, it feels that the most natural thing
> > > would be to have a chardev per buffer...
> > >
> > > On the other hand, AFAIC, events are also being handled in the same chardev as
> > > buffers, which makes things harder in terms of consistency... Events are per device
> > > and not per buffers right? My point is that, to have a chardev per buffer, it would make
> > > sense to detach events from the buffer stuff and that seems to be not doable without
> > > breaking ABI (we would probably need to assume that events and buffer0 are on the
> > > same chardev).
> >
> > Events are interesting as there is no particular reason to assume the driver
> > handling buffer0 is the right one to deal with them. It might just as easily
> > be the case that they are of interest to a process that is concerned with buffer1.
> >
> > To add a bit more flavour to my earlier comments.
> >
> > I'm still concerned that if we did do multiple /dev/* files it would allow code
> > to think it has complete control over the device when it really doesn't.
> > Events are just one aspect of that.
> >
> > We have had discussions in the past about allowing multiple userspace consumers
> > for a single buffer, but the conclusion there was that was a job for userspace
> > (daemon or similar) software which can deal with control inter dependencies etc.
> >
> > There are already potential messy corners we don't handle for userspace
> > iio buffers vs in kernel users (what happens if they both try to control the
> > sampling frequency?) I'm not keen to broaden this problem set.
> > If a device genuinely has separate control and pipelines for different
> > buffers then we are probably better representing that cleanly as
> > an mfd type layer and two separate IIO devices. Its effectively the
> > same a multi chip package.
> >
> > A more classic multibuffer usecase is the one where you have related
> > datastreams that run at different rates (often happens in devices with
> > tagged FIFO elements). These are tightly coupled but we need to split
> > the data stream (or add tagging to our FIFOs.). Another case would be
> > DMA based device that puts channels into buffers that are entirely
> > separate in memory address rather than interleaved.
> >
> > So I still need to put together a PoC, but it feels like there are various
> > software models that will give the illusion of there being separate
> > /dev/* files, but with an aspect of control being possible.
> >
> > 1. Daemon, if present that can hand off chardevs to who needs them
> > 2. Library to make the first user of the buffer responsible for providing
> > service to other users. Yes there are races, but I don't think they
> > are hard to deal in normal usecases. (retry loops)
>
> Hi Nuno / Others,
>
> Nuno's mention of things being similar for the event anon
> FD to the situation for the buffer anon FDs made me realise there was
> a horrible short cut to a proof of concept that didn't require me
> to wire up a multiple buffer device.
>
> Upshot, is that I've just sent out a (definitely not for merging)
> hacked up version of the iio_event_monitor that can act as server
> or client. The idea is that the socket handling looks a bit
> like what I'd expect to see hidden away in a library so as to
> allow
>
> 1) Client 1 is after buffer 3.
> It tries to open the /dev/iio\:deviceX chrdev and succeeds.
> It spins up a thread with a listening socket for /tmp/iio\:deviceX-magic
> Continues in main thread to request buffer 3.
> 2) Client 2 is after buffer 2
> I tries to open the /dev/iio\:deviceX chrdev and fails.
> It sleeps a moment (reduces chance of race with client 1)
> It opens a connection to the socket via /tmp/iio\:deviceX-magic
> Sends a request for the buffer 2 FD.
> Thread in Client 1 calls the ioctl to get the buffer 2 FD which
> it then sends on to Client 2 which can use it as if it had
> requested it directly.
>
> We might want to have a generic server version as well that doesn't
> itself make use of any of the buffers as keeps the model more symmetric
> and reduce common corner cases.
>
> Anyhow the code I put together is terrible, but I wasn't 100% sure
> there weren't any issues passing anon fd file handles and this shows
> that at least in theory the approach I proposed above works.
>
> Test is something like
> ./iio_events_network /dev/iio\:device1
> ./iio_events_network -c
>
> Then make some events happen (I was using the dummy driver and
> the event generator associated with that).
> The server in this PoC just quits after handling off the FD.

The whole code looks good functionally.
If there are any race issues [as discussed here], they can be handled
in the server code.
And if this is the model we try to enforce/propose in userspace, then
all should be fine.


Continuing a bit with the original IIO buffer ioctl(), I talked to
Lars a bit over IRC.
And there was an idea/suggestion to maybe use a struct to pass more
information to the buffer FD.

So, right now the ioctl() just returns an FD.
Would it be worth to extend this to a struct?
What I'm worried about is that it opens the discussion to add more
stuff to that struct.

so now, it would be:

struct iio_buffer_ioctl_data {
__u32 fd;
__u32 flags; // flags for the new FD, which maybe we
could also pass via fcntl()
}

anything else that we would need?

>
> Jonathan
>
> >
> > Jonathan
> >
> >
> > >
> > > - Nuno Sá
> >
>

2021-03-23 11:38:22

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Tue, 23 Mar 2021 11:51:04 +0200
Alexandru Ardelean <[email protected]> wrote:

> On Sun, Mar 21, 2021 at 7:37 PM Jonathan Cameron
> <[email protected]> wrote:
> >
> > On Sat, 20 Mar 2021 17:41:00 +0000
> > Jonathan Cameron <[email protected]> wrote:
> >
> > > On Mon, 15 Mar 2021 09:58:08 +0000
> > > "Sa, Nuno" <[email protected]> wrote:
> > >
> > > > > -----Original Message-----
> > > > > From: Alexandru Ardelean <[email protected]>
> > > > > Sent: Saturday, March 6, 2021 6:01 PM
> > > > > To: Jonathan Cameron <[email protected]>
> > > > > Cc: Lars-Peter Clausen <[email protected]>; zzzzArdelean,
> > > > > zzzzAlexandru <[email protected]>; LKML <linux-
> > > > > [email protected]>; linux-iio <[email protected]>;
> > > > > Hennerich, Michael <[email protected]>; Jonathan
> > > > > Cameron <[email protected]>; Sa, Nuno <[email protected]>;
> > > > > Bogdan, Dragos <[email protected]>
> > > > > Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening
> > > > > extra buffers for IIO device
> > > > >
> > > > > [External]
> > > > >
> > > > > On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
> > > > > <[email protected]> wrote:
> > > > > >
> > > > > > On Sun, 28 Feb 2021 16:51:51 +0100
> > > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > > >
> > > > > > > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > > > > > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > > > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > > > > >
> > > > > > > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > > > > > > >>> With this change, an ioctl() call is added to open a character
> > > > > device for a
> > > > > > > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > > > > > > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > > > > > > >>>
> > > > > > > >>> The ioctl() will return an FD for the requested buffer index.
> > > > > The indexes
> > > > > > > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY
> > > > > (i.e. the Y
> > > > > > > >>> variable).
> > > > > > > >>>
> > > > > > > >>> Since there doesn't seem to be a sane way to return the FD for
> > > > > buffer0 to
> > > > > > > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return
> > > > > another
> > > > > > > >>> FD for buffer0 (or the first buffer). This duplicate FD will be
> > > > > able to
> > > > > > > >>> access the same buffer object (for buffer0) as accessing
> > > > > directly the
> > > > > > > >>> /dev/iio:deviceX chardev.
> > > > > > > >>>
> > > > > > > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl()
> > > > > implemented, as the
> > > > > > > >>> index for each buffer (and the count) can be deduced from
> > > > > the
> > > > > > > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the
> > > > > number of
> > > > > > > >>> bufferY folders).
> > > > > > > >>>
> > > > > > > >>> Used following C code to test this:
> > > > > > > >>> -------------------------------------------------------------------
> > > > > > > >>>
> > > > > > > >>> #include <stdio.h>
> > > > > > > >>> #include <stdlib.h>
> > > > > > > >>> #include <unistd.h>
> > > > > > > >>> #include <sys/ioctl.h>
> > > > > > > >>> #include <fcntl.h"
> > > > > > > >>> #include <errno.h>
> > > > > > > >>>
> > > > > > > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > > > > > > >>>
> > > > > > > >>> int main(int argc, char *argv[])
> > > > > > > >>> {
> > > > > > > >>> int fd;
> > > > > > > >>> int fd1;
> > > > > > > >>> int ret;
> > > > > > > >>>
> > > > > > > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > > > > > > >>> fprintf(stderr, "Error open() %d errno %d\n",fd,
> > > > > errno);
> > > > > > > >>> return -1;
> > > > > > > >>> }
> > > > > > > >>>
> > > > > > > >>> fprintf(stderr, "Using FD %d\n", fd);
> > > > > > > >>>
> > > > > > > >>> fd1 = atoi(argv[1]);
> > > > > > > >>>
> > > > > > > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > > > > > > >>> if (ret < 0) {
> > > > > > > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno
> > > > > %d\n", fd1, ret, errno);
> > > > > > > >>> close(fd);
> > > > > > > >>> return -1;
> > > > > > > >>> }
> > > > > > > >>>
> > > > > > > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > > > > > > >>>
> > > > > > > >>> close(fd1);
> > > > > > > >>> close(fd);
> > > > > > > >>>
> > > > > > > >>> return 0;
> > > > > > > >>> }
> > > > > > > >>> -------------------------------------------------------------------
> > > > > > > >>>
> > > > > > > >>> Results are:
> > > > > > > >>> -------------------------------------------------------------------
> > > > > > > >>> # ./test 0
> > > > > > > >>> Using FD 3
> > > > > > > >>> Got FD 4
> > > > > > > >>>
> > > > > > > >>> # ./test 1
> > > > > > > >>> Using FD 3
> > > > > > > >>> Got FD 4
> > > > > > > >>>
> > > > > > > >>> # ./test 2
> > > > > > > >>> Using FD 3
> > > > > > > >>> Got FD 4
> > > > > > > >>>
> > > > > > > >>> # ./test 3
> > > > > > > >>> Using FD 3
> > > > > > > >>> Got FD 4
> > > > > > > >>>
> > > > > > > >>> # ls /sys/bus/iio/devices/iio\:device0
> > > > > > > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > > > > > > >>> in_voltage_sampling_frequency in_voltage_scale
> > > > > > > >>> in_voltage_scale_available
> > > > > > > >>> name of_node power scan_elements subsystem uevent
> > > > > > > >>> -------------------------------------------------------------------
> > > > > > > >>>
> > > > > > > >>> iio:device0 has some fake kfifo buffers attached to an IIO
> > > > > device.
> > > > > > > >> For me there is one major problem with this approach. We only
> > > > > allow one
> > > > > > > >> application to open /dev/iio:deviceX at a time. This means we
> > > > > can't have
> > > > > > > >> different applications access different buffers of the same
> > > > > device. I
> > > > > > > >> believe this is a circuital feature.
> > > > > > > > Thats not quite true (I think - though I've not tested it). What we
> > > > > don't
> > > > > > > > allow is for multiple processes to access them in an unaware
> > > > > fashion.
> > > > > > > > My assumption is we can rely on fork + fd passing via appropriate
> > > > > sockets.
> > > > > > > >
> > > > > > > >> It is possible to open the chardev, get the annonfd, close the
> > > > > chardev
> > > > > > > >> and keep the annonfd open. Then the next application can do
> > > > > the same and
> > > > > > > >> get access to a different buffer. But this has room for race
> > > > > conditions
> > > > > > > >> when two applications try this at the very same time.
> > > > > > > >>
> > > > > > > >> We need to somehow address this.
> > > > > > > > I'd count this as a bug :). It could be safely done in a particular
> > > > > custom
> > > > > > > > system but in general it opens a can of worm.
> > > > > > > >
> > > > > > > >> I'm also not much of a fan of using ioctls to create annon fds. In
> > > > > part
> > > > > > > >> because all the standard mechanisms for access control no
> > > > > longer work.
> > > > > > > > The inability to trivially have multiple processes open the anon
> > > > > fds
> > > > > > > > without care is one of the things I like most about them.
> > > > > > > >
> > > > > > > > IIO drivers and interfaces really aren't designed for multiple
> > > > > unaware
> > > > > > > > processes to access them. We don't have per process controls
> > > > > for device
> > > > > > > > wide sysfs attributes etc. In general, it would be hard to
> > > > > > > > do due to the complexity of modeling all the interactions
> > > > > between the
> > > > > > > > different interfaces (events / buffers / sysfs access) in a generic
> > > > > fashion.
> > > > > > > >
> > > > > > > > As such, the model, in my head at least, is that we only want a
> > > > > single
> > > > > > > > process to ever be responsible for access control. That process
> > > > > can then
> > > > > > > > assign access to children or via a deliberate action (I think passing
> > > > > the
> > > > > > > > anon fd over a unix socket should work for example). The intent
> > > > > being
> > > > > > > > that it is also responsible for mediating access to infrastructure
> > > > > that
> > > > > > > > multiple child processes all want to access.
> > > > > > > >
> > > > > > > > As such, having one chrdev isn't a disadvantage because only one
> > > > > process
> > > > > > > > should ever open it at a time. This same process also handles the
> > > > > > > > resource / control mediation. Therefore we should only have
> > > > > one file
> > > > > > > > exposed for all the standard access control mechanisms.
> > > > > > > >
> > > > > > > Hm, I see your point, but I'm not convinced.
> > > > > > >
> > > > > > > Having to have explicit synchronization makes it difficult to mix and
> > > > > > > match. E.g. at ADI a popular use case for testing was to run some
> > > > > signal
> > > > > > > generator application on the TX buffer and some signal analyzer
> > > > > > > application on the RX buffer.
> > > > > > >
> > > > > > > Both can be launched independently and there can be different
> > > > > types of
> > > > > > > generator and analyzer applications. Having to have a 3rd
> > > > > application to
> > > > > > > arbitrate access makes this quite cumbersome. And I'm afraid that
> > > > > in
> > > > > > > reality people might just stick with the two devices model just to
> > > > > avoid
> > > > > > > this restriction.
> > > > > >
> > > > > > I'd argue that's a problem best tackled in a library - though it's a bit
> > > > > > fiddly. It ought to be possible to make it invisible that this level
> > > > > > of sharing is going on. The management process you describe would
> > > > > probably
> > > > > > be a thread running inside the first process to try and access a given
> > > > > device.
> > > > > > A second process failing to open the file with -EBUSY then connects
> > > > > to
> > > > > > appropriate socket (via path in /tmp or similar) and asks for the FD.
> > > > > > There are race conditions that might make it fail, but a retry loop
> > > > > should
> > > > > > deal with those.
> > > > > >
> > > > > > I agree people might just stick to a two device model and if the
> > > > > devices
> > > > > > are independent enough I'm not sure that is the wrong way to
> > > > > approach the
> > > > > > problem. It represents the independence and that the driver is
> > > > > being careful
> > > > > > that it both can and is safely handle independent simultaneous
> > > > > accessors.
> > > > > > We are always going to have some drivers doing that anyway
> > > > > because they've
> > > > > > already been doing that for years.
> > > > > >
> > > > >
> > > > > This is the last of the 3 patches that I need to re-spin after Lars' review.
> > > > > I have a good handle on the small stuff.
> > > > >
> > > > > I'm not sure about the race-condition about which Lars was talking
> > > > > about.
> > > > > I mean, I get the problem, but is it a problem that we should fix in the
> > > > > kernel?
> > > >
> > > > Hi all,
> > > >
> > > > FWIW, I think that this really depends on the chosen ABI. If we do use
> > > > the ioctl to return the buffer fd and just allow one app to hold the chardev
> > > > at a time, I agree with Alex that this is not really a race and is just something
> > > > that userspace needs to deal with....
> > > >
> > > > That said and giving my superficial (I did not really read the full series) piece on this,
> > > > I get both Lars and Jonathan points and, personally, it feels that the most natural thing
> > > > would be to have a chardev per buffer...
> > > >
> > > > On the other hand, AFAIC, events are also being handled in the same chardev as
> > > > buffers, which makes things harder in terms of consistency... Events are per device
> > > > and not per buffers right? My point is that, to have a chardev per buffer, it would make
> > > > sense to detach events from the buffer stuff and that seems to be not doable without
> > > > breaking ABI (we would probably need to assume that events and buffer0 are on the
> > > > same chardev).
> > >
> > > Events are interesting as there is no particular reason to assume the driver
> > > handling buffer0 is the right one to deal with them. It might just as easily
> > > be the case that they are of interest to a process that is concerned with buffer1.
> > >
> > > To add a bit more flavour to my earlier comments.
> > >
> > > I'm still concerned that if we did do multiple /dev/* files it would allow code
> > > to think it has complete control over the device when it really doesn't.
> > > Events are just one aspect of that.
> > >
> > > We have had discussions in the past about allowing multiple userspace consumers
> > > for a single buffer, but the conclusion there was that was a job for userspace
> > > (daemon or similar) software which can deal with control inter dependencies etc.
> > >
> > > There are already potential messy corners we don't handle for userspace
> > > iio buffers vs in kernel users (what happens if they both try to control the
> > > sampling frequency?) I'm not keen to broaden this problem set.
> > > If a device genuinely has separate control and pipelines for different
> > > buffers then we are probably better representing that cleanly as
> > > an mfd type layer and two separate IIO devices. Its effectively the
> > > same a multi chip package.
> > >
> > > A more classic multibuffer usecase is the one where you have related
> > > datastreams that run at different rates (often happens in devices with
> > > tagged FIFO elements). These are tightly coupled but we need to split
> > > the data stream (or add tagging to our FIFOs.). Another case would be
> > > DMA based device that puts channels into buffers that are entirely
> > > separate in memory address rather than interleaved.
> > >
> > > So I still need to put together a PoC, but it feels like there are various
> > > software models that will give the illusion of there being separate
> > > /dev/* files, but with an aspect of control being possible.
> > >
> > > 1. Daemon, if present that can hand off chardevs to who needs them
> > > 2. Library to make the first user of the buffer responsible for providing
> > > service to other users. Yes there are races, but I don't think they
> > > are hard to deal in normal usecases. (retry loops)
> >
> > Hi Nuno / Others,
> >
> > Nuno's mention of things being similar for the event anon
> > FD to the situation for the buffer anon FDs made me realise there was
> > a horrible short cut to a proof of concept that didn't require me
> > to wire up a multiple buffer device.
> >
> > Upshot, is that I've just sent out a (definitely not for merging)
> > hacked up version of the iio_event_monitor that can act as server
> > or client. The idea is that the socket handling looks a bit
> > like what I'd expect to see hidden away in a library so as to
> > allow
> >
> > 1) Client 1 is after buffer 3.
> > It tries to open the /dev/iio\:deviceX chrdev and succeeds.
> > It spins up a thread with a listening socket for /tmp/iio\:deviceX-magic
> > Continues in main thread to request buffer 3.
> > 2) Client 2 is after buffer 2
> > I tries to open the /dev/iio\:deviceX chrdev and fails.
> > It sleeps a moment (reduces chance of race with client 1)
> > It opens a connection to the socket via /tmp/iio\:deviceX-magic
> > Sends a request for the buffer 2 FD.
> > Thread in Client 1 calls the ioctl to get the buffer 2 FD which
> > it then sends on to Client 2 which can use it as if it had
> > requested it directly.
> >
> > We might want to have a generic server version as well that doesn't
> > itself make use of any of the buffers as keeps the model more symmetric
> > and reduce common corner cases.
> >
> > Anyhow the code I put together is terrible, but I wasn't 100% sure
> > there weren't any issues passing anon fd file handles and this shows
> > that at least in theory the approach I proposed above works.
> >
> > Test is something like
> > ./iio_events_network /dev/iio\:device1
> > ./iio_events_network -c
> >
> > Then make some events happen (I was using the dummy driver and
> > the event generator associated with that).
> > The server in this PoC just quits after handling off the FD.
>
> The whole code looks good functionally.
> If there are any race issues [as discussed here], they can be handled
> in the server code.
> And if this is the model we try to enforce/propose in userspace, then
> all should be fine.
>
>
> Continuing a bit with the original IIO buffer ioctl(), I talked to
> Lars a bit over IRC.
> And there was an idea/suggestion to maybe use a struct to pass more
> information to the buffer FD.
>
> So, right now the ioctl() just returns an FD.
> Would it be worth to extend this to a struct?
> What I'm worried about is that it opens the discussion to add more
> stuff to that struct.
>
> so now, it would be:
>
> struct iio_buffer_ioctl_data {
> __u32 fd;
> __u32 flags; // flags for the new FD, which maybe we
> could also pass via fcntl()
> }
>
> anything else that we would need?

I have a vague recollection that is is almost always worth adding
some padding to such ioctl data coming out of the kernel. Gives
flexibility to safely add more stuff later without userspace
failing to allocate enough space etc.

I'm curious though, because this feels backwards. I'd expect the
flags to be more useful passed into the ioctl? i.e. request
a non blocking FD? Might want to mirror that back again of course.

Jonathan


>
> >
> > Jonathan
> >
> > >
> > > Jonathan
> > >
> > >
> > > >
> > > > - Nuno S?
> > >
> >

2021-03-24 10:58:04

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On Tue, Mar 23, 2021 at 1:35 PM Jonathan Cameron
<[email protected]> wrote:
>
> On Tue, 23 Mar 2021 11:51:04 +0200
> Alexandru Ardelean <[email protected]> wrote:
>
> > On Sun, Mar 21, 2021 at 7:37 PM Jonathan Cameron
> > <[email protected]> wrote:
> > >
> > > On Sat, 20 Mar 2021 17:41:00 +0000
> > > Jonathan Cameron <[email protected]> wrote:
> > >
> > > > On Mon, 15 Mar 2021 09:58:08 +0000
> > > > "Sa, Nuno" <[email protected]> wrote:
> > > >
> > > > > > -----Original Message-----
> > > > > > From: Alexandru Ardelean <[email protected]>
> > > > > > Sent: Saturday, March 6, 2021 6:01 PM
> > > > > > To: Jonathan Cameron <[email protected]>
> > > > > > Cc: Lars-Peter Clausen <[email protected]>; zzzzArdelean,
> > > > > > zzzzAlexandru <[email protected]>; LKML <linux-
> > > > > > [email protected]>; linux-iio <[email protected]>;
> > > > > > Hennerich, Michael <[email protected]>; Jonathan
> > > > > > Cameron <[email protected]>; Sa, Nuno <[email protected]>;
> > > > > > Bogdan, Dragos <[email protected]>
> > > > > > Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening
> > > > > > extra buffers for IIO device
> > > > > >
> > > > > > [External]
> > > > > >
> > > > > > On Sun, Feb 28, 2021 at 9:00 PM Jonathan Cameron
> > > > > > <[email protected]> wrote:
> > > > > > >
> > > > > > > On Sun, 28 Feb 2021 16:51:51 +0100
> > > > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > > > >
> > > > > > > > On 2/28/21 3:34 PM, Jonathan Cameron wrote:
> > > > > > > > > On Sun, 28 Feb 2021 09:51:38 +0100
> > > > > > > > > Lars-Peter Clausen <[email protected]> wrote:
> > > > > > > > >
> > > > > > > > >> On 2/15/21 11:40 AM, Alexandru Ardelean wrote:
> > > > > > > > >>> With this change, an ioctl() call is added to open a character
> > > > > > device for a
> > > > > > > > >>> buffer. The ioctl() number is 'i' 0x91, which follows the
> > > > > > > > >>> IIO_GET_EVENT_FD_IOCTL ioctl.
> > > > > > > > >>>
> > > > > > > > >>> The ioctl() will return an FD for the requested buffer index.
> > > > > > The indexes
> > > > > > > > >>> are the same from the /sys/iio/devices/iio:deviceX/bufferY
> > > > > > (i.e. the Y
> > > > > > > > >>> variable).
> > > > > > > > >>>
> > > > > > > > >>> Since there doesn't seem to be a sane way to return the FD for
> > > > > > buffer0 to
> > > > > > > > >>> be the same FD for the /dev/iio:deviceX, this ioctl() will return
> > > > > > another
> > > > > > > > >>> FD for buffer0 (or the first buffer). This duplicate FD will be
> > > > > > able to
> > > > > > > > >>> access the same buffer object (for buffer0) as accessing
> > > > > > directly the
> > > > > > > > >>> /dev/iio:deviceX chardev.
> > > > > > > > >>>
> > > > > > > > >>> Also, there is no IIO_BUFFER_GET_BUFFER_COUNT ioctl()
> > > > > > implemented, as the
> > > > > > > > >>> index for each buffer (and the count) can be deduced from
> > > > > > the
> > > > > > > > >>> '/sys/bus/iio/devices/iio:deviceX/bufferY' folders (i.e the
> > > > > > number of
> > > > > > > > >>> bufferY folders).
> > > > > > > > >>>
> > > > > > > > >>> Used following C code to test this:
> > > > > > > > >>> -------------------------------------------------------------------
> > > > > > > > >>>
> > > > > > > > >>> #include <stdio.h>
> > > > > > > > >>> #include <stdlib.h>
> > > > > > > > >>> #include <unistd.h>
> > > > > > > > >>> #include <sys/ioctl.h>
> > > > > > > > >>> #include <fcntl.h"
> > > > > > > > >>> #include <errno.h>
> > > > > > > > >>>
> > > > > > > > >>> #define IIO_BUFFER_GET_FD_IOCTL _IOWR('i', 0x91, int)
> > > > > > > > >>>
> > > > > > > > >>> int main(int argc, char *argv[])
> > > > > > > > >>> {
> > > > > > > > >>> int fd;
> > > > > > > > >>> int fd1;
> > > > > > > > >>> int ret;
> > > > > > > > >>>
> > > > > > > > >>> if ((fd = open("/dev/iio:device0", O_RDWR))<0) {
> > > > > > > > >>> fprintf(stderr, "Error open() %d errno %d\n",fd,
> > > > > > errno);
> > > > > > > > >>> return -1;
> > > > > > > > >>> }
> > > > > > > > >>>
> > > > > > > > >>> fprintf(stderr, "Using FD %d\n", fd);
> > > > > > > > >>>
> > > > > > > > >>> fd1 = atoi(argv[1]);
> > > > > > > > >>>
> > > > > > > > >>> ret = ioctl(fd, IIO_BUFFER_GET_FD_IOCTL, &fd1);
> > > > > > > > >>> if (ret < 0) {
> > > > > > > > >>> fprintf(stderr, "Error for buffer %d ioctl() %d errno
> > > > > > %d\n", fd1, ret, errno);
> > > > > > > > >>> close(fd);
> > > > > > > > >>> return -1;
> > > > > > > > >>> }
> > > > > > > > >>>
> > > > > > > > >>> fprintf(stderr, "Got FD %d\n", fd1);
> > > > > > > > >>>
> > > > > > > > >>> close(fd1);
> > > > > > > > >>> close(fd);
> > > > > > > > >>>
> > > > > > > > >>> return 0;
> > > > > > > > >>> }
> > > > > > > > >>> -------------------------------------------------------------------
> > > > > > > > >>>
> > > > > > > > >>> Results are:
> > > > > > > > >>> -------------------------------------------------------------------
> > > > > > > > >>> # ./test 0
> > > > > > > > >>> Using FD 3
> > > > > > > > >>> Got FD 4
> > > > > > > > >>>
> > > > > > > > >>> # ./test 1
> > > > > > > > >>> Using FD 3
> > > > > > > > >>> Got FD 4
> > > > > > > > >>>
> > > > > > > > >>> # ./test 2
> > > > > > > > >>> Using FD 3
> > > > > > > > >>> Got FD 4
> > > > > > > > >>>
> > > > > > > > >>> # ./test 3
> > > > > > > > >>> Using FD 3
> > > > > > > > >>> Got FD 4
> > > > > > > > >>>
> > > > > > > > >>> # ls /sys/bus/iio/devices/iio\:device0
> > > > > > > > >>> buffer buffer0 buffer1 buffer2 buffer3 dev
> > > > > > > > >>> in_voltage_sampling_frequency in_voltage_scale
> > > > > > > > >>> in_voltage_scale_available
> > > > > > > > >>> name of_node power scan_elements subsystem uevent
> > > > > > > > >>> -------------------------------------------------------------------
> > > > > > > > >>>
> > > > > > > > >>> iio:device0 has some fake kfifo buffers attached to an IIO
> > > > > > device.
> > > > > > > > >> For me there is one major problem with this approach. We only
> > > > > > allow one
> > > > > > > > >> application to open /dev/iio:deviceX at a time. This means we
> > > > > > can't have
> > > > > > > > >> different applications access different buffers of the same
> > > > > > device. I
> > > > > > > > >> believe this is a circuital feature.
> > > > > > > > > Thats not quite true (I think - though I've not tested it). What we
> > > > > > don't
> > > > > > > > > allow is for multiple processes to access them in an unaware
> > > > > > fashion.
> > > > > > > > > My assumption is we can rely on fork + fd passing via appropriate
> > > > > > sockets.
> > > > > > > > >
> > > > > > > > >> It is possible to open the chardev, get the annonfd, close the
> > > > > > chardev
> > > > > > > > >> and keep the annonfd open. Then the next application can do
> > > > > > the same and
> > > > > > > > >> get access to a different buffer. But this has room for race
> > > > > > conditions
> > > > > > > > >> when two applications try this at the very same time.
> > > > > > > > >>
> > > > > > > > >> We need to somehow address this.
> > > > > > > > > I'd count this as a bug :). It could be safely done in a particular
> > > > > > custom
> > > > > > > > > system but in general it opens a can of worm.
> > > > > > > > >
> > > > > > > > >> I'm also not much of a fan of using ioctls to create annon fds. In
> > > > > > part
> > > > > > > > >> because all the standard mechanisms for access control no
> > > > > > longer work.
> > > > > > > > > The inability to trivially have multiple processes open the anon
> > > > > > fds
> > > > > > > > > without care is one of the things I like most about them.
> > > > > > > > >
> > > > > > > > > IIO drivers and interfaces really aren't designed for multiple
> > > > > > unaware
> > > > > > > > > processes to access them. We don't have per process controls
> > > > > > for device
> > > > > > > > > wide sysfs attributes etc. In general, it would be hard to
> > > > > > > > > do due to the complexity of modeling all the interactions
> > > > > > between the
> > > > > > > > > different interfaces (events / buffers / sysfs access) in a generic
> > > > > > fashion.
> > > > > > > > >
> > > > > > > > > As such, the model, in my head at least, is that we only want a
> > > > > > single
> > > > > > > > > process to ever be responsible for access control. That process
> > > > > > can then
> > > > > > > > > assign access to children or via a deliberate action (I think passing
> > > > > > the
> > > > > > > > > anon fd over a unix socket should work for example). The intent
> > > > > > being
> > > > > > > > > that it is also responsible for mediating access to infrastructure
> > > > > > that
> > > > > > > > > multiple child processes all want to access.
> > > > > > > > >
> > > > > > > > > As such, having one chrdev isn't a disadvantage because only one
> > > > > > process
> > > > > > > > > should ever open it at a time. This same process also handles the
> > > > > > > > > resource / control mediation. Therefore we should only have
> > > > > > one file
> > > > > > > > > exposed for all the standard access control mechanisms.
> > > > > > > > >
> > > > > > > > Hm, I see your point, but I'm not convinced.
> > > > > > > >
> > > > > > > > Having to have explicit synchronization makes it difficult to mix and
> > > > > > > > match. E.g. at ADI a popular use case for testing was to run some
> > > > > > signal
> > > > > > > > generator application on the TX buffer and some signal analyzer
> > > > > > > > application on the RX buffer.
> > > > > > > >
> > > > > > > > Both can be launched independently and there can be different
> > > > > > types of
> > > > > > > > generator and analyzer applications. Having to have a 3rd
> > > > > > application to
> > > > > > > > arbitrate access makes this quite cumbersome. And I'm afraid that
> > > > > > in
> > > > > > > > reality people might just stick with the two devices model just to
> > > > > > avoid
> > > > > > > > this restriction.
> > > > > > >
> > > > > > > I'd argue that's a problem best tackled in a library - though it's a bit
> > > > > > > fiddly. It ought to be possible to make it invisible that this level
> > > > > > > of sharing is going on. The management process you describe would
> > > > > > probably
> > > > > > > be a thread running inside the first process to try and access a given
> > > > > > device.
> > > > > > > A second process failing to open the file with -EBUSY then connects
> > > > > > to
> > > > > > > appropriate socket (via path in /tmp or similar) and asks for the FD.
> > > > > > > There are race conditions that might make it fail, but a retry loop
> > > > > > should
> > > > > > > deal with those.
> > > > > > >
> > > > > > > I agree people might just stick to a two device model and if the
> > > > > > devices
> > > > > > > are independent enough I'm not sure that is the wrong way to
> > > > > > approach the
> > > > > > > problem. It represents the independence and that the driver is
> > > > > > being careful
> > > > > > > that it both can and is safely handle independent simultaneous
> > > > > > accessors.
> > > > > > > We are always going to have some drivers doing that anyway
> > > > > > because they've
> > > > > > > already been doing that for years.
> > > > > > >
> > > > > >
> > > > > > This is the last of the 3 patches that I need to re-spin after Lars' review.
> > > > > > I have a good handle on the small stuff.
> > > > > >
> > > > > > I'm not sure about the race-condition about which Lars was talking
> > > > > > about.
> > > > > > I mean, I get the problem, but is it a problem that we should fix in the
> > > > > > kernel?
> > > > >
> > > > > Hi all,
> > > > >
> > > > > FWIW, I think that this really depends on the chosen ABI. If we do use
> > > > > the ioctl to return the buffer fd and just allow one app to hold the chardev
> > > > > at a time, I agree with Alex that this is not really a race and is just something
> > > > > that userspace needs to deal with....
> > > > >
> > > > > That said and giving my superficial (I did not really read the full series) piece on this,
> > > > > I get both Lars and Jonathan points and, personally, it feels that the most natural thing
> > > > > would be to have a chardev per buffer...
> > > > >
> > > > > On the other hand, AFAIC, events are also being handled in the same chardev as
> > > > > buffers, which makes things harder in terms of consistency... Events are per device
> > > > > and not per buffers right? My point is that, to have a chardev per buffer, it would make
> > > > > sense to detach events from the buffer stuff and that seems to be not doable without
> > > > > breaking ABI (we would probably need to assume that events and buffer0 are on the
> > > > > same chardev).
> > > >
> > > > Events are interesting as there is no particular reason to assume the driver
> > > > handling buffer0 is the right one to deal with them. It might just as easily
> > > > be the case that they are of interest to a process that is concerned with buffer1.
> > > >
> > > > To add a bit more flavour to my earlier comments.
> > > >
> > > > I'm still concerned that if we did do multiple /dev/* files it would allow code
> > > > to think it has complete control over the device when it really doesn't.
> > > > Events are just one aspect of that.
> > > >
> > > > We have had discussions in the past about allowing multiple userspace consumers
> > > > for a single buffer, but the conclusion there was that was a job for userspace
> > > > (daemon or similar) software which can deal with control inter dependencies etc.
> > > >
> > > > There are already potential messy corners we don't handle for userspace
> > > > iio buffers vs in kernel users (what happens if they both try to control the
> > > > sampling frequency?) I'm not keen to broaden this problem set.
> > > > If a device genuinely has separate control and pipelines for different
> > > > buffers then we are probably better representing that cleanly as
> > > > an mfd type layer and two separate IIO devices. Its effectively the
> > > > same a multi chip package.
> > > >
> > > > A more classic multibuffer usecase is the one where you have related
> > > > datastreams that run at different rates (often happens in devices with
> > > > tagged FIFO elements). These are tightly coupled but we need to split
> > > > the data stream (or add tagging to our FIFOs.). Another case would be
> > > > DMA based device that puts channels into buffers that are entirely
> > > > separate in memory address rather than interleaved.
> > > >
> > > > So I still need to put together a PoC, but it feels like there are various
> > > > software models that will give the illusion of there being separate
> > > > /dev/* files, but with an aspect of control being possible.
> > > >
> > > > 1. Daemon, if present that can hand off chardevs to who needs them
> > > > 2. Library to make the first user of the buffer responsible for providing
> > > > service to other users. Yes there are races, but I don't think they
> > > > are hard to deal in normal usecases. (retry loops)
> > >
> > > Hi Nuno / Others,
> > >
> > > Nuno's mention of things being similar for the event anon
> > > FD to the situation for the buffer anon FDs made me realise there was
> > > a horrible short cut to a proof of concept that didn't require me
> > > to wire up a multiple buffer device.
> > >
> > > Upshot, is that I've just sent out a (definitely not for merging)
> > > hacked up version of the iio_event_monitor that can act as server
> > > or client. The idea is that the socket handling looks a bit
> > > like what I'd expect to see hidden away in a library so as to
> > > allow
> > >
> > > 1) Client 1 is after buffer 3.
> > > It tries to open the /dev/iio\:deviceX chrdev and succeeds.
> > > It spins up a thread with a listening socket for /tmp/iio\:deviceX-magic
> > > Continues in main thread to request buffer 3.
> > > 2) Client 2 is after buffer 2
> > > I tries to open the /dev/iio\:deviceX chrdev and fails.
> > > It sleeps a moment (reduces chance of race with client 1)
> > > It opens a connection to the socket via /tmp/iio\:deviceX-magic
> > > Sends a request for the buffer 2 FD.
> > > Thread in Client 1 calls the ioctl to get the buffer 2 FD which
> > > it then sends on to Client 2 which can use it as if it had
> > > requested it directly.
> > >
> > > We might want to have a generic server version as well that doesn't
> > > itself make use of any of the buffers as keeps the model more symmetric
> > > and reduce common corner cases.
> > >
> > > Anyhow the code I put together is terrible, but I wasn't 100% sure
> > > there weren't any issues passing anon fd file handles and this shows
> > > that at least in theory the approach I proposed above works.
> > >
> > > Test is something like
> > > ./iio_events_network /dev/iio\:device1
> > > ./iio_events_network -c
> > >
> > > Then make some events happen (I was using the dummy driver and
> > > the event generator associated with that).
> > > The server in this PoC just quits after handling off the FD.
> >
> > The whole code looks good functionally.
> > If there are any race issues [as discussed here], they can be handled
> > in the server code.
> > And if this is the model we try to enforce/propose in userspace, then
> > all should be fine.
> >
> >
> > Continuing a bit with the original IIO buffer ioctl(), I talked to
> > Lars a bit over IRC.
> > And there was an idea/suggestion to maybe use a struct to pass more
> > information to the buffer FD.
> >
> > So, right now the ioctl() just returns an FD.
> > Would it be worth to extend this to a struct?
> > What I'm worried about is that it opens the discussion to add more
> > stuff to that struct.
> >
> > so now, it would be:
> >
> > struct iio_buffer_ioctl_data {
> > __u32 fd;
> > __u32 flags; // flags for the new FD, which maybe we
> > could also pass via fcntl()
> > }
> >
> > anything else that we would need?
>
> I have a vague recollection that is is almost always worth adding
> some padding to such ioctl data coming out of the kernel. Gives
> flexibility to safely add more stuff later without userspace
> failing to allocate enough space etc.
>
> I'm curious though, because this feels backwards. I'd expect the
> flags to be more useful passed into the ioctl? i.e. request
> a non blocking FD? Might want to mirror that back again of course.

Personally, I don't know.
I don't have any experiences on this.

So, then I'll do a change to this ioctl() to use a struct.
We can probably add some reserved space?

struct iio_buffer_ioctl_data {
__u32 fd;
__u32 flags;
__u32 reserved1;
__u32 reserved2;
}

Lars was giving me some articles about ioctls.
One idea was to maybe consider making them multiples of 64 bits.

But reading through one of the docs here:
https://www.kernel.org/doc/html/latest/driver-api/ioctl.html#interface-versions
it discourages to do interface versions.

But I guess if we plan ahead with some reserved space, it might be
somewhat fine.

I'm still a little green on this stuff.

>
> Jonathan
>
>
> >
> > >
> > > Jonathan
> > >
> > > >
> > > > Jonathan
> > > >
> > > >
> > > > >
> > > > > - Nuno Sá
> > > >
> > >
>

2021-03-27 12:03:35

by Lars-Peter Clausen

[permalink] [raw]
Subject: Re: [PATCH v6 20/24] iio: buffer: add ioctl() to support opening extra buffers for IIO device

On 3/24/21 10:10 AM, Alexandru Ardelean wrote:
> On Tue, Mar 23, 2021 at 1:35 PM Jonathan Cameron
> <[email protected]> wrote:
>>
[..]
>>>
>>> Continuing a bit with the original IIO buffer ioctl(), I talked to
>>> Lars a bit over IRC.
>>> And there was an idea/suggestion to maybe use a struct to pass more
>>> information to the buffer FD.
>>>
>>> So, right now the ioctl() just returns an FD.
>>> Would it be worth to extend this to a struct?
>>> What I'm worried about is that it opens the discussion to add more
>>> stuff to that struct.
>>>
>>> so now, it would be:
>>>
>>> struct iio_buffer_ioctl_data {
>>> __u32 fd;
>>> __u32 flags; // flags for the new FD, which maybe we
>>> could also pass via fcntl()
>>> }
>>>
>>> anything else that we would need?
>>
>> I have a vague recollection that is is almost always worth adding
>> some padding to such ioctl data coming out of the kernel. Gives
>> flexibility to safely add more stuff later without userspace
>> failing to allocate enough space etc.
>>
>> I'm curious though, because this feels backwards. I'd expect the
>> flags to be more useful passed into the ioctl? i.e. request
>> a non blocking FD? Might want to mirror that back again of course.
>

The struct can be used for both. Passing flags and buffer number in and fd out.

> Personally, I don't know.
> I don't have any experiences on this.
>
> So, then I'll do a change to this ioctl() to use a struct.
> We can probably add some reserved space?
>
> struct iio_buffer_ioctl_data {
> __u32 fd;
> __u32 flags;
> __u32 reserved1;
> __u32 reserved2;
> }

What to make sure of when using reserved fields is to check that they are 0.
And reject the ioctl if they are not. This is the only way to ensure that old
applications will continue to work if the struct is updated.

>
> Lars was giving me some articles about ioctls.
> One idea was to maybe consider making them multiples of 64 bits.
>
> But reading through one of the docs here:
> https://www.kernel.org/doc/html/latest/driver-api/ioctl.html#interface-versions
> it discourages to do interface versions.
>
> But I guess if we plan ahead with some reserved space, it might be
> somewhat fine.
>
> I'm still a little green on this stuff.
>
>>

2021-04-01 07:42:15

by Marek Szyprowski

[permalink] [raw]
Subject: Re: [PATCH v6 14/24] iio: buffer: wrap all buffer attributes into iio_dev_attr

Hi

On 15.02.2021 11:40, Alexandru Ardelean wrote:
> This change wraps all buffer attributes into iio_dev_attr objects, and
> assigns a reference to the IIO buffer they belong to.
>
> With the addition of multiple IIO buffers per one IIO device, we need a way
> to know which IIO buffer is being enabled/disabled/controlled.
>
> We know that all buffer attributes are device_attributes. So we can wrap
> them with a iio_dev_attr types. In the iio_dev_attr type, we can also hold
> a reference to an IIO buffer.
> So, we end up being able to allocate wrapped attributes for all buffer
> attributes (even the one from other drivers).
>
> The neat part with this mechanism, is that we don't need to add any extra
> cleanup, because these attributes are being added to a dynamic list that
> will get cleaned up via iio_free_chan_devattr_list().
>
> With this change, the 'buffer->scan_el_dev_attr_list' list is being renamed
> to 'buffer->buffer_attr_list', effectively merging (or finalizing the
> merge) of the buffer/ & scan_elements/ attributes internally.
>
> Accessing these new buffer attributes can now be done via
> 'to_iio_dev_attr(attr)->buffer' inside the show/store handlers.
>
> Signed-off-by: Alexandru Ardelean <[email protected]>

This patch landed recently in linux-next as commit 15097c7a1adc ("iio:
buffer: wrap all buffer attributes into iio_dev_attr"). Sadly it causes
a regression and triggers the lock debuging warning:

ak8975 9-000c: mounting matrix not found: using identity...
ak8975 9-000c: supply vdd not found, using dummy regulator
ak8975 9-000c: supply vid not found, using dummy regulator
BUG: key cf40d08c has not been registered!
------------[ cut here ]------------
WARNING: CPU: 2 PID: 1 at kernel/locking/lockdep.c:4686
__kernfs_create_file+0x7c/0xfc
DEBUG_LOCKS_WARN_ON(1)
Modules linked in:
CPU: 2 PID: 1 Comm: swapper/0 Not tainted 5.12.0-rc2-00153-g15097c7a1adc
#2828
Hardware name: Samsung Exynos (Flattened Device Tree)
[<c011170c>] (unwind_backtrace) from [<c010cf74>] (show_stack+0x10/0x14)
[<c010cf74>] (show_stack) from [<c0b48650>] (dump_stack+0xa4/0xc4)
[<c0b48650>] (dump_stack) from [<c01274c8>] (__warn+0x118/0x11c)
[<c01274c8>] (__warn) from [<c0127544>] (warn_slowpath_fmt+0x78/0xbc)
[<c0127544>] (warn_slowpath_fmt) from [<c038db90>]
(__kernfs_create_file+0x7c/0xfc)
[<c038db90>] (__kernfs_create_file) from [<c038e870>]
(sysfs_add_file_mode_ns+0xa0/0x1cc)
[<c038e870>] (sysfs_add_file_mode_ns) from [<c038f4e0>]
(internal_create_group+0x138/0x3f4)
[<c038f4e0>] (internal_create_group) from [<c038fd64>]
(internal_create_groups+0x48/0x88)
[<c038fd64>] (internal_create_groups) from [<c06a8754>]
(device_add+0x2e4/0x7ec)
[<c06a8754>] (device_add) from [<c02e46a0>] (cdev_device_add+0x48/0x80)
[<c02e46a0>] (cdev_device_add) from [<c08db1a4>]
(__iio_device_register+0x670/0x7c0)
[<c08db1a4>] (__iio_device_register) from [<c08e2efc>]
(ak8975_probe+0x3a4/0x584)
[<c08e2efc>] (ak8975_probe) from [<c082e120>] (i2c_device_probe+0x234/0x2a4)
[<c082e120>] (i2c_device_probe) from [<c06abea4>] (really_probe+0x1d4/0x4ec)
[<c06abea4>] (really_probe) from [<c06ac234>]
(driver_probe_device+0x78/0x1d8)
[<c06ac234>] (driver_probe_device) from [<c06ac74c>]
(device_driver_attach+0x58/0x60)
[<c06ac74c>] (device_driver_attach) from [<c06ac850>]
(__driver_attach+0xfc/0x160)
[<c06ac850>] (__driver_attach) from [<c06a9e58>]
(bus_for_each_dev+0x6c/0xb8)
[<c06a9e58>] (bus_for_each_dev) from [<c06aaf90>]
(bus_add_driver+0x170/0x20c)
[<c06aaf90>] (bus_add_driver) from [<c06ad6c8>] (driver_register+0x78/0x10c)
[<c06ad6c8>] (driver_register) from [<c082f0d4>]
(i2c_register_driver+0x3c/0xac)
[<c082f0d4>] (i2c_register_driver) from [<c0102434>]
(do_one_initcall+0x88/0x430)
[<c0102434>] (do_one_initcall) from [<c11010d4>]
(kernel_init_freeable+0x190/0x1e0)
[<c11010d4>] (kernel_init_freeable) from [<c0b4c564>]
(kernel_init+0x8/0x118)
[<c0b4c564>] (kernel_init) from [<c010011c>] (ret_from_fork+0x14/0x38)
Exception stack(0xc1d09fb0 to 0xc1d09ff8)
9fa0:                                     00000000 00000000 00000000
00000000
9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000
9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
irq event stamp: 485619
hardirqs last  enabled at (485619): [<c01a51fc>] console_unlock+0x500/0x648
hardirqs last disabled at (485618): [<c01a51e0>] console_unlock+0x4e4/0x648
softirqs last  enabled at (484774): [<c0101790>] __do_softirq+0x528/0x63c
softirqs last disabled at (484769): [<c0130a38>] irq_exit+0x1f4/0x1fc
---[ end trace 21850020cbfb3350 ]---

The above warning is probably caused by copying struct device_attribute
objects in iio_buffer_wrap_attr() without re-initializing
spinlocks/mutexes. Locks debuging depends on the spinlock/mutex
initializers, which register them (as absolute value of the pointer to
them) to the debugging engine. After copying the structures, the objects
don't match the pointers they were registered. I didn't have time to
analyze it further and find which object/lock is triggering this though.

> ---
> drivers/iio/industrialio-buffer.c | 76 ++++++++++++++++++++-----------
> include/linux/iio/buffer_impl.h | 4 +-
> 2 files changed, 52 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index e6edec3bcb73..8dc140f13b99 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -253,8 +253,7 @@ static ssize_t iio_scan_el_show(struct device *dev,
> char *buf)
> {
> int ret;
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
>
> /* Ensure ret is 0 or 1. */
> ret = !!test_bit(to_iio_dev_attr(attr)->address,
> @@ -367,8 +366,8 @@ static ssize_t iio_scan_el_store(struct device *dev,
> int ret;
> bool state;
> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> + struct iio_buffer *buffer = this_attr->buffer;
>
> ret = strtobool(buf, &state);
> if (ret < 0)
> @@ -402,8 +401,7 @@ static ssize_t iio_scan_el_ts_show(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> {
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
>
> return sprintf(buf, "%d\n", buffer->scan_timestamp);
> }
> @@ -415,7 +413,7 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,
> {
> int ret;
> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> bool state;
>
> ret = strtobool(buf, &state);
> @@ -448,7 +446,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> IIO_SEPARATE,
> &indio_dev->dev,
> buffer,
> - &buffer->scan_el_dev_attr_list);
> + &buffer->buffer_attr_list);
> if (ret)
> return ret;
> attrcount++;
> @@ -460,7 +458,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> 0,
> &indio_dev->dev,
> buffer,
> - &buffer->scan_el_dev_attr_list);
> + &buffer->buffer_attr_list);
> if (ret)
> return ret;
> attrcount++;
> @@ -473,7 +471,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> 0,
> &indio_dev->dev,
> buffer,
> - &buffer->scan_el_dev_attr_list);
> + &buffer->buffer_attr_list);
> else
> ret = __iio_add_chan_devattr("en",
> chan,
> @@ -483,7 +481,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> 0,
> &indio_dev->dev,
> buffer,
> - &buffer->scan_el_dev_attr_list);
> + &buffer->buffer_attr_list);
> if (ret)
> return ret;
> attrcount++;
> @@ -495,8 +493,7 @@ static ssize_t iio_buffer_read_length(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> {
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
>
> return sprintf(buf, "%d\n", buffer->length);
> }
> @@ -506,7 +503,7 @@ static ssize_t iio_buffer_write_length(struct device *dev,
> const char *buf, size_t len)
> {
> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> unsigned int val;
> int ret;
>
> @@ -538,8 +535,7 @@ static ssize_t iio_buffer_show_enable(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> {
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
>
> return sprintf(buf, "%d\n", iio_buffer_is_active(buffer));
> }
> @@ -1154,7 +1150,7 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
> int ret;
> bool requested_state;
> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> bool inlist;
>
> ret = strtobool(buf, &requested_state);
> @@ -1183,8 +1179,7 @@ static ssize_t iio_buffer_show_watermark(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> {
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
>
> return sprintf(buf, "%u\n", buffer->watermark);
> }
> @@ -1195,7 +1190,7 @@ static ssize_t iio_buffer_store_watermark(struct device *dev,
> size_t len)
> {
> struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> unsigned int val;
> int ret;
>
> @@ -1228,8 +1223,7 @@ static ssize_t iio_dma_show_data_available(struct device *dev,
> struct device_attribute *attr,
> char *buf)
> {
> - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> - struct iio_buffer *buffer = indio_dev->buffer;
> + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
>
> return sprintf(buf, "%zu\n", iio_buffer_data_available(buffer));
> }
> @@ -1254,6 +1248,26 @@ static struct attribute *iio_buffer_attrs[] = {
> &dev_attr_data_available.attr,
> };
>
> +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
> +
> +static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
> + struct attribute *attr)
> +{
> + struct device_attribute *dattr = to_dev_attr(attr);
> + struct iio_dev_attr *iio_attr;
> +
> + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
> + if (!iio_attr)
> + return NULL;
> +
> + iio_attr->buffer = buffer;
> + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
> +
> + list_add(&iio_attr->l, &buffer->buffer_attr_list);
> +
> + return &iio_attr->dev_attr.attr;
> +}
> +
> static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev,
> struct attribute **buffer_attrs,
> int buffer_attrcount,
> @@ -1329,7 +1343,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
> }
>
> scan_el_attrcount = 0;
> - INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
> + INIT_LIST_HEAD(&buffer->buffer_attr_list);
> channels = indio_dev->channels;
> if (channels) {
> /* new magic */
> @@ -1376,9 +1390,19 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
>
> buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs);
>
> - attrn = buffer_attrcount;
> + for (i = 0; i < buffer_attrcount; i++) {
> + struct attribute *wrapped;
> +
> + wrapped = iio_buffer_wrap_attr(buffer, attr[i]);
> + if (!wrapped) {
> + ret = -ENOMEM;
> + goto error_free_scan_mask;
> + }
> + attr[i] = wrapped;
> + }
>
> - list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
> + attrn = 0;
> + list_for_each_entry(p, &buffer->buffer_attr_list, l)
> attr[attrn++] = &p->dev_attr.attr;
>
> buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index);
> @@ -1412,7 +1436,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
> error_free_scan_mask:
> bitmap_free(buffer->scan_mask);
> error_cleanup_dynamic:
> - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
> + iio_free_chan_devattr_list(&buffer->buffer_attr_list);
>
> return ret;
> }
> @@ -1443,7 +1467,7 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
> bitmap_free(buffer->scan_mask);
> kfree(buffer->buffer_group.name);
> kfree(buffer->buffer_group.attrs);
> - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
> + iio_free_chan_devattr_list(&buffer->buffer_attr_list);
> }
>
> void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
> index 3e555e58475b..41044320e581 100644
> --- a/include/linux/iio/buffer_impl.h
> +++ b/include/linux/iio/buffer_impl.h
> @@ -97,8 +97,8 @@ struct iio_buffer {
> /* @scan_timestamp: Does the scan mode include a timestamp. */
> bool scan_timestamp;
>
> - /* @scan_el_dev_attr_list: List of scan element related attributes. */
> - struct list_head scan_el_dev_attr_list;
> + /* @buffer_attr_list: List of buffer attributes. */
> + struct list_head buffer_attr_list;
>
> /*
> * @buffer_group: Attributes of the new buffer group.

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland

2021-04-01 08:29:35

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v6 14/24] iio: buffer: wrap all buffer attributes into iio_dev_attr

On Thu, 1 Apr 2021 09:39:47 +0200
Marek Szyprowski <[email protected]> wrote:

> Hi
>
> On 15.02.2021 11:40, Alexandru Ardelean wrote:
> > This change wraps all buffer attributes into iio_dev_attr objects, and
> > assigns a reference to the IIO buffer they belong to.
> >
> > With the addition of multiple IIO buffers per one IIO device, we need a way
> > to know which IIO buffer is being enabled/disabled/controlled.
> >
> > We know that all buffer attributes are device_attributes. So we can wrap
> > them with a iio_dev_attr types. In the iio_dev_attr type, we can also hold
> > a reference to an IIO buffer.
> > So, we end up being able to allocate wrapped attributes for all buffer
> > attributes (even the one from other drivers).
> >
> > The neat part with this mechanism, is that we don't need to add any extra
> > cleanup, because these attributes are being added to a dynamic list that
> > will get cleaned up via iio_free_chan_devattr_list().
> >
> > With this change, the 'buffer->scan_el_dev_attr_list' list is being renamed
> > to 'buffer->buffer_attr_list', effectively merging (or finalizing the
> > merge) of the buffer/ & scan_elements/ attributes internally.
> >
> > Accessing these new buffer attributes can now be done via
> > 'to_iio_dev_attr(attr)->buffer' inside the show/store handlers.
> >
> > Signed-off-by: Alexandru Ardelean <[email protected]>
>
> This patch landed recently in linux-next as commit 15097c7a1adc ("iio:
> buffer: wrap all buffer attributes into iio_dev_attr"). Sadly it causes
> a regression and triggers the lock debuging warning:
>
> ak8975 9-000c: mounting matrix not found: using identity...
> ak8975 9-000c: supply vdd not found, using dummy regulator
> ak8975 9-000c: supply vid not found, using dummy regulator
> BUG: key cf40d08c has not been registered!
> ------------[ cut here ]------------
> WARNING: CPU: 2 PID: 1 at kernel/locking/lockdep.c:4686
> __kernfs_create_file+0x7c/0xfc
> DEBUG_LOCKS_WARN_ON(1)
> Modules linked in:
> CPU: 2 PID: 1 Comm: swapper/0 Not tainted 5.12.0-rc2-00153-g15097c7a1adc
> #2828
> Hardware name: Samsung Exynos (Flattened Device Tree)
> [<c011170c>] (unwind_backtrace) from [<c010cf74>] (show_stack+0x10/0x14)
> [<c010cf74>] (show_stack) from [<c0b48650>] (dump_stack+0xa4/0xc4)
> [<c0b48650>] (dump_stack) from [<c01274c8>] (__warn+0x118/0x11c)
> [<c01274c8>] (__warn) from [<c0127544>] (warn_slowpath_fmt+0x78/0xbc)
> [<c0127544>] (warn_slowpath_fmt) from [<c038db90>]
> (__kernfs_create_file+0x7c/0xfc)
> [<c038db90>] (__kernfs_create_file) from [<c038e870>]
> (sysfs_add_file_mode_ns+0xa0/0x1cc)
> [<c038e870>] (sysfs_add_file_mode_ns) from [<c038f4e0>]
> (internal_create_group+0x138/0x3f4)
> [<c038f4e0>] (internal_create_group) from [<c038fd64>]
> (internal_create_groups+0x48/0x88)
> [<c038fd64>] (internal_create_groups) from [<c06a8754>]
> (device_add+0x2e4/0x7ec)
> [<c06a8754>] (device_add) from [<c02e46a0>] (cdev_device_add+0x48/0x80)
> [<c02e46a0>] (cdev_device_add) from [<c08db1a4>]
> (__iio_device_register+0x670/0x7c0)
> [<c08db1a4>] (__iio_device_register) from [<c08e2efc>]
> (ak8975_probe+0x3a4/0x584)
> [<c08e2efc>] (ak8975_probe) from [<c082e120>] (i2c_device_probe+0x234/0x2a4)
> [<c082e120>] (i2c_device_probe) from [<c06abea4>] (really_probe+0x1d4/0x4ec)
> [<c06abea4>] (really_probe) from [<c06ac234>]
> (driver_probe_device+0x78/0x1d8)
> [<c06ac234>] (driver_probe_device) from [<c06ac74c>]
> (device_driver_attach+0x58/0x60)
> [<c06ac74c>] (device_driver_attach) from [<c06ac850>]
> (__driver_attach+0xfc/0x160)
> [<c06ac850>] (__driver_attach) from [<c06a9e58>]
> (bus_for_each_dev+0x6c/0xb8)
> [<c06a9e58>] (bus_for_each_dev) from [<c06aaf90>]
> (bus_add_driver+0x170/0x20c)
> [<c06aaf90>] (bus_add_driver) from [<c06ad6c8>] (driver_register+0x78/0x10c)
> [<c06ad6c8>] (driver_register) from [<c082f0d4>]
> (i2c_register_driver+0x3c/0xac)
> [<c082f0d4>] (i2c_register_driver) from [<c0102434>]
> (do_one_initcall+0x88/0x430)
> [<c0102434>] (do_one_initcall) from [<c11010d4>]
> (kernel_init_freeable+0x190/0x1e0)
> [<c11010d4>] (kernel_init_freeable) from [<c0b4c564>]
> (kernel_init+0x8/0x118)
> [<c0b4c564>] (kernel_init) from [<c010011c>] (ret_from_fork+0x14/0x38)
> Exception stack(0xc1d09fb0 to 0xc1d09ff8)
> 9fa0:???????????????????????????????????? 00000000 00000000 00000000
> 00000000
> 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
> 00000000
> 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
> irq event stamp: 485619
> hardirqs last? enabled at (485619): [<c01a51fc>] console_unlock+0x500/0x648
> hardirqs last disabled at (485618): [<c01a51e0>] console_unlock+0x4e4/0x648
> softirqs last? enabled at (484774): [<c0101790>] __do_softirq+0x528/0x63c
> softirqs last disabled at (484769): [<c0130a38>] irq_exit+0x1f4/0x1fc
> ---[ end trace 21850020cbfb3350 ]---
>
> The above warning is probably caused by copying struct device_attribute
> objects in iio_buffer_wrap_attr() without re-initializing
> spinlocks/mutexes. Locks debuging depends on the spinlock/mutex
> initializers, which register them (as absolute value of the pointer to
> them) to the debugging engine. After copying the structures, the objects
> don't match the pointers they were registered. I didn't have time to
> analyze it further and find which object/lock is triggering this though.

Thanks, interestingly there aren't any spinlocks or mutexes in attributes,
but there are lock_class_keys. Looks like we need to call sysfs_attr_init()
and I guess we are currently missing it on this path.

Alex, can you take a look? If not I'll get to it sometime over the weekend
most likely.

Jonathan


>
> > ---
> > drivers/iio/industrialio-buffer.c | 76 ++++++++++++++++++++-----------
> > include/linux/iio/buffer_impl.h | 4 +-
> > 2 files changed, 52 insertions(+), 28 deletions(-)
> >
> > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > index e6edec3bcb73..8dc140f13b99 100644
> > --- a/drivers/iio/industrialio-buffer.c
> > +++ b/drivers/iio/industrialio-buffer.c
> > @@ -253,8 +253,7 @@ static ssize_t iio_scan_el_show(struct device *dev,
> > char *buf)
> > {
> > int ret;
> > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> >
> > /* Ensure ret is 0 or 1. */
> > ret = !!test_bit(to_iio_dev_attr(attr)->address,
> > @@ -367,8 +366,8 @@ static ssize_t iio_scan_el_store(struct device *dev,
> > int ret;
> > bool state;
> > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> > + struct iio_buffer *buffer = this_attr->buffer;
> >
> > ret = strtobool(buf, &state);
> > if (ret < 0)
> > @@ -402,8 +401,7 @@ static ssize_t iio_scan_el_ts_show(struct device *dev,
> > struct device_attribute *attr,
> > char *buf)
> > {
> > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> >
> > return sprintf(buf, "%d\n", buffer->scan_timestamp);
> > }
> > @@ -415,7 +413,7 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,
> > {
> > int ret;
> > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > bool state;
> >
> > ret = strtobool(buf, &state);
> > @@ -448,7 +446,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> > IIO_SEPARATE,
> > &indio_dev->dev,
> > buffer,
> > - &buffer->scan_el_dev_attr_list);
> > + &buffer->buffer_attr_list);
> > if (ret)
> > return ret;
> > attrcount++;
> > @@ -460,7 +458,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> > 0,
> > &indio_dev->dev,
> > buffer,
> > - &buffer->scan_el_dev_attr_list);
> > + &buffer->buffer_attr_list);
> > if (ret)
> > return ret;
> > attrcount++;
> > @@ -473,7 +471,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> > 0,
> > &indio_dev->dev,
> > buffer,
> > - &buffer->scan_el_dev_attr_list);
> > + &buffer->buffer_attr_list);
> > else
> > ret = __iio_add_chan_devattr("en",
> > chan,
> > @@ -483,7 +481,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> > 0,
> > &indio_dev->dev,
> > buffer,
> > - &buffer->scan_el_dev_attr_list);
> > + &buffer->buffer_attr_list);
> > if (ret)
> > return ret;
> > attrcount++;
> > @@ -495,8 +493,7 @@ static ssize_t iio_buffer_read_length(struct device *dev,
> > struct device_attribute *attr,
> > char *buf)
> > {
> > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> >
> > return sprintf(buf, "%d\n", buffer->length);
> > }
> > @@ -506,7 +503,7 @@ static ssize_t iio_buffer_write_length(struct device *dev,
> > const char *buf, size_t len)
> > {
> > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > unsigned int val;
> > int ret;
> >
> > @@ -538,8 +535,7 @@ static ssize_t iio_buffer_show_enable(struct device *dev,
> > struct device_attribute *attr,
> > char *buf)
> > {
> > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> >
> > return sprintf(buf, "%d\n", iio_buffer_is_active(buffer));
> > }
> > @@ -1154,7 +1150,7 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
> > int ret;
> > bool requested_state;
> > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > bool inlist;
> >
> > ret = strtobool(buf, &requested_state);
> > @@ -1183,8 +1179,7 @@ static ssize_t iio_buffer_show_watermark(struct device *dev,
> > struct device_attribute *attr,
> > char *buf)
> > {
> > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> >
> > return sprintf(buf, "%u\n", buffer->watermark);
> > }
> > @@ -1195,7 +1190,7 @@ static ssize_t iio_buffer_store_watermark(struct device *dev,
> > size_t len)
> > {
> > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > unsigned int val;
> > int ret;
> >
> > @@ -1228,8 +1223,7 @@ static ssize_t iio_dma_show_data_available(struct device *dev,
> > struct device_attribute *attr,
> > char *buf)
> > {
> > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > - struct iio_buffer *buffer = indio_dev->buffer;
> > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> >
> > return sprintf(buf, "%zu\n", iio_buffer_data_available(buffer));
> > }
> > @@ -1254,6 +1248,26 @@ static struct attribute *iio_buffer_attrs[] = {
> > &dev_attr_data_available.attr,
> > };
> >
> > +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
> > +
> > +static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
> > + struct attribute *attr)
> > +{
> > + struct device_attribute *dattr = to_dev_attr(attr);
> > + struct iio_dev_attr *iio_attr;
> > +
> > + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
> > + if (!iio_attr)
> > + return NULL;
> > +
> > + iio_attr->buffer = buffer;
> > + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
> > +
> > + list_add(&iio_attr->l, &buffer->buffer_attr_list);
> > +
> > + return &iio_attr->dev_attr.attr;
> > +}
> > +
> > static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev,
> > struct attribute **buffer_attrs,
> > int buffer_attrcount,
> > @@ -1329,7 +1343,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
> > }
> >
> > scan_el_attrcount = 0;
> > - INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
> > + INIT_LIST_HEAD(&buffer->buffer_attr_list);
> > channels = indio_dev->channels;
> > if (channels) {
> > /* new magic */
> > @@ -1376,9 +1390,19 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
> >
> > buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs);
> >
> > - attrn = buffer_attrcount;
> > + for (i = 0; i < buffer_attrcount; i++) {
> > + struct attribute *wrapped;
> > +
> > + wrapped = iio_buffer_wrap_attr(buffer, attr[i]);
> > + if (!wrapped) {
> > + ret = -ENOMEM;
> > + goto error_free_scan_mask;
> > + }
> > + attr[i] = wrapped;
> > + }
> >
> > - list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
> > + attrn = 0;
> > + list_for_each_entry(p, &buffer->buffer_attr_list, l)
> > attr[attrn++] = &p->dev_attr.attr;
> >
> > buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index);
> > @@ -1412,7 +1436,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
> > error_free_scan_mask:
> > bitmap_free(buffer->scan_mask);
> > error_cleanup_dynamic:
> > - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
> > + iio_free_chan_devattr_list(&buffer->buffer_attr_list);
> >
> > return ret;
> > }
> > @@ -1443,7 +1467,7 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
> > bitmap_free(buffer->scan_mask);
> > kfree(buffer->buffer_group.name);
> > kfree(buffer->buffer_group.attrs);
> > - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
> > + iio_free_chan_devattr_list(&buffer->buffer_attr_list);
> > }
> >
> > void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> > diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
> > index 3e555e58475b..41044320e581 100644
> > --- a/include/linux/iio/buffer_impl.h
> > +++ b/include/linux/iio/buffer_impl.h
> > @@ -97,8 +97,8 @@ struct iio_buffer {
> > /* @scan_timestamp: Does the scan mode include a timestamp. */
> > bool scan_timestamp;
> >
> > - /* @scan_el_dev_attr_list: List of scan element related attributes. */
> > - struct list_head scan_el_dev_attr_list;
> > + /* @buffer_attr_list: List of buffer attributes. */
> > + struct list_head buffer_attr_list;
> >
> > /*
> > * @buffer_group: Attributes of the new buffer group.
>
> Best regards

2021-04-01 11:12:39

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [PATCH v6 14/24] iio: buffer: wrap all buffer attributes into iio_dev_attr

On Thu, Apr 1, 2021 at 11:29 AM Jonathan Cameron
<[email protected]> wrote:
>
> On Thu, 1 Apr 2021 09:39:47 +0200
> Marek Szyprowski <[email protected]> wrote:
>
> > Hi
> >
> > On 15.02.2021 11:40, Alexandru Ardelean wrote:
> > > This change wraps all buffer attributes into iio_dev_attr objects, and
> > > assigns a reference to the IIO buffer they belong to.
> > >
> > > With the addition of multiple IIO buffers per one IIO device, we need a way
> > > to know which IIO buffer is being enabled/disabled/controlled.
> > >
> > > We know that all buffer attributes are device_attributes. So we can wrap
> > > them with a iio_dev_attr types. In the iio_dev_attr type, we can also hold
> > > a reference to an IIO buffer.
> > > So, we end up being able to allocate wrapped attributes for all buffer
> > > attributes (even the one from other drivers).
> > >
> > > The neat part with this mechanism, is that we don't need to add any extra
> > > cleanup, because these attributes are being added to a dynamic list that
> > > will get cleaned up via iio_free_chan_devattr_list().
> > >
> > > With this change, the 'buffer->scan_el_dev_attr_list' list is being renamed
> > > to 'buffer->buffer_attr_list', effectively merging (or finalizing the
> > > merge) of the buffer/ & scan_elements/ attributes internally.
> > >
> > > Accessing these new buffer attributes can now be done via
> > > 'to_iio_dev_attr(attr)->buffer' inside the show/store handlers.
> > >
> > > Signed-off-by: Alexandru Ardelean <[email protected]>
> >
> > This patch landed recently in linux-next as commit 15097c7a1adc ("iio:
> > buffer: wrap all buffer attributes into iio_dev_attr"). Sadly it causes
> > a regression and triggers the lock debuging warning:
> >
> > ak8975 9-000c: mounting matrix not found: using identity...
> > ak8975 9-000c: supply vdd not found, using dummy regulator
> > ak8975 9-000c: supply vid not found, using dummy regulator
> > BUG: key cf40d08c has not been registered!
> > ------------[ cut here ]------------
> > WARNING: CPU: 2 PID: 1 at kernel/locking/lockdep.c:4686
> > __kernfs_create_file+0x7c/0xfc
> > DEBUG_LOCKS_WARN_ON(1)
> > Modules linked in:
> > CPU: 2 PID: 1 Comm: swapper/0 Not tainted 5.12.0-rc2-00153-g15097c7a1adc
> > #2828
> > Hardware name: Samsung Exynos (Flattened Device Tree)
> > [<c011170c>] (unwind_backtrace) from [<c010cf74>] (show_stack+0x10/0x14)
> > [<c010cf74>] (show_stack) from [<c0b48650>] (dump_stack+0xa4/0xc4)
> > [<c0b48650>] (dump_stack) from [<c01274c8>] (__warn+0x118/0x11c)
> > [<c01274c8>] (__warn) from [<c0127544>] (warn_slowpath_fmt+0x78/0xbc)
> > [<c0127544>] (warn_slowpath_fmt) from [<c038db90>]
> > (__kernfs_create_file+0x7c/0xfc)
> > [<c038db90>] (__kernfs_create_file) from [<c038e870>]
> > (sysfs_add_file_mode_ns+0xa0/0x1cc)
> > [<c038e870>] (sysfs_add_file_mode_ns) from [<c038f4e0>]
> > (internal_create_group+0x138/0x3f4)
> > [<c038f4e0>] (internal_create_group) from [<c038fd64>]
> > (internal_create_groups+0x48/0x88)
> > [<c038fd64>] (internal_create_groups) from [<c06a8754>]
> > (device_add+0x2e4/0x7ec)
> > [<c06a8754>] (device_add) from [<c02e46a0>] (cdev_device_add+0x48/0x80)
> > [<c02e46a0>] (cdev_device_add) from [<c08db1a4>]
> > (__iio_device_register+0x670/0x7c0)
> > [<c08db1a4>] (__iio_device_register) from [<c08e2efc>]
> > (ak8975_probe+0x3a4/0x584)
> > [<c08e2efc>] (ak8975_probe) from [<c082e120>] (i2c_device_probe+0x234/0x2a4)
> > [<c082e120>] (i2c_device_probe) from [<c06abea4>] (really_probe+0x1d4/0x4ec)
> > [<c06abea4>] (really_probe) from [<c06ac234>]
> > (driver_probe_device+0x78/0x1d8)
> > [<c06ac234>] (driver_probe_device) from [<c06ac74c>]
> > (device_driver_attach+0x58/0x60)
> > [<c06ac74c>] (device_driver_attach) from [<c06ac850>]
> > (__driver_attach+0xfc/0x160)
> > [<c06ac850>] (__driver_attach) from [<c06a9e58>]
> > (bus_for_each_dev+0x6c/0xb8)
> > [<c06a9e58>] (bus_for_each_dev) from [<c06aaf90>]
> > (bus_add_driver+0x170/0x20c)
> > [<c06aaf90>] (bus_add_driver) from [<c06ad6c8>] (driver_register+0x78/0x10c)
> > [<c06ad6c8>] (driver_register) from [<c082f0d4>]
> > (i2c_register_driver+0x3c/0xac)
> > [<c082f0d4>] (i2c_register_driver) from [<c0102434>]
> > (do_one_initcall+0x88/0x430)
> > [<c0102434>] (do_one_initcall) from [<c11010d4>]
> > (kernel_init_freeable+0x190/0x1e0)
> > [<c11010d4>] (kernel_init_freeable) from [<c0b4c564>]
> > (kernel_init+0x8/0x118)
> > [<c0b4c564>] (kernel_init) from [<c010011c>] (ret_from_fork+0x14/0x38)
> > Exception stack(0xc1d09fb0 to 0xc1d09ff8)
> > 9fa0: 00000000 00000000 00000000
> > 00000000
> > 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000
> > 00000000
> > 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
> > irq event stamp: 485619
> > hardirqs last enabled at (485619): [<c01a51fc>] console_unlock+0x500/0x648
> > hardirqs last disabled at (485618): [<c01a51e0>] console_unlock+0x4e4/0x648
> > softirqs last enabled at (484774): [<c0101790>] __do_softirq+0x528/0x63c
> > softirqs last disabled at (484769): [<c0130a38>] irq_exit+0x1f4/0x1fc
> > ---[ end trace 21850020cbfb3350 ]---
> >
> > The above warning is probably caused by copying struct device_attribute
> > objects in iio_buffer_wrap_attr() without re-initializing
> > spinlocks/mutexes. Locks debuging depends on the spinlock/mutex
> > initializers, which register them (as absolute value of the pointer to
> > them) to the debugging engine. After copying the structures, the objects
> > don't match the pointers they were registered. I didn't have time to
> > analyze it further and find which object/lock is triggering this though.
>
> Thanks, interestingly there aren't any spinlocks or mutexes in attributes,
> but there are lock_class_keys. Looks like we need to call sysfs_attr_init()
> and I guess we are currently missing it on this path.
>
> Alex, can you take a look? If not I'll get to it sometime over the weekend
> most likely.

Will take a look.

>
> Jonathan
>
>
> >
> > > ---
> > > drivers/iio/industrialio-buffer.c | 76 ++++++++++++++++++++-----------
> > > include/linux/iio/buffer_impl.h | 4 +-
> > > 2 files changed, 52 insertions(+), 28 deletions(-)
> > >
> > > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > > index e6edec3bcb73..8dc140f13b99 100644
> > > --- a/drivers/iio/industrialio-buffer.c
> > > +++ b/drivers/iio/industrialio-buffer.c
> > > @@ -253,8 +253,7 @@ static ssize_t iio_scan_el_show(struct device *dev,
> > > char *buf)
> > > {
> > > int ret;
> > > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > >
> > > /* Ensure ret is 0 or 1. */
> > > ret = !!test_bit(to_iio_dev_attr(attr)->address,
> > > @@ -367,8 +366,8 @@ static ssize_t iio_scan_el_store(struct device *dev,
> > > int ret;
> > > bool state;
> > > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
> > > + struct iio_buffer *buffer = this_attr->buffer;
> > >
> > > ret = strtobool(buf, &state);
> > > if (ret < 0)
> > > @@ -402,8 +401,7 @@ static ssize_t iio_scan_el_ts_show(struct device *dev,
> > > struct device_attribute *attr,
> > > char *buf)
> > > {
> > > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > >
> > > return sprintf(buf, "%d\n", buffer->scan_timestamp);
> > > }
> > > @@ -415,7 +413,7 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,
> > > {
> > > int ret;
> > > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > > bool state;
> > >
> > > ret = strtobool(buf, &state);
> > > @@ -448,7 +446,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> > > IIO_SEPARATE,
> > > &indio_dev->dev,
> > > buffer,
> > > - &buffer->scan_el_dev_attr_list);
> > > + &buffer->buffer_attr_list);
> > > if (ret)
> > > return ret;
> > > attrcount++;
> > > @@ -460,7 +458,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> > > 0,
> > > &indio_dev->dev,
> > > buffer,
> > > - &buffer->scan_el_dev_attr_list);
> > > + &buffer->buffer_attr_list);
> > > if (ret)
> > > return ret;
> > > attrcount++;
> > > @@ -473,7 +471,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> > > 0,
> > > &indio_dev->dev,
> > > buffer,
> > > - &buffer->scan_el_dev_attr_list);
> > > + &buffer->buffer_attr_list);
> > > else
> > > ret = __iio_add_chan_devattr("en",
> > > chan,
> > > @@ -483,7 +481,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
> > > 0,
> > > &indio_dev->dev,
> > > buffer,
> > > - &buffer->scan_el_dev_attr_list);
> > > + &buffer->buffer_attr_list);
> > > if (ret)
> > > return ret;
> > > attrcount++;
> > > @@ -495,8 +493,7 @@ static ssize_t iio_buffer_read_length(struct device *dev,
> > > struct device_attribute *attr,
> > > char *buf)
> > > {
> > > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > >
> > > return sprintf(buf, "%d\n", buffer->length);
> > > }
> > > @@ -506,7 +503,7 @@ static ssize_t iio_buffer_write_length(struct device *dev,
> > > const char *buf, size_t len)
> > > {
> > > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > > unsigned int val;
> > > int ret;
> > >
> > > @@ -538,8 +535,7 @@ static ssize_t iio_buffer_show_enable(struct device *dev,
> > > struct device_attribute *attr,
> > > char *buf)
> > > {
> > > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > >
> > > return sprintf(buf, "%d\n", iio_buffer_is_active(buffer));
> > > }
> > > @@ -1154,7 +1150,7 @@ static ssize_t iio_buffer_store_enable(struct device *dev,
> > > int ret;
> > > bool requested_state;
> > > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > > bool inlist;
> > >
> > > ret = strtobool(buf, &requested_state);
> > > @@ -1183,8 +1179,7 @@ static ssize_t iio_buffer_show_watermark(struct device *dev,
> > > struct device_attribute *attr,
> > > char *buf)
> > > {
> > > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > >
> > > return sprintf(buf, "%u\n", buffer->watermark);
> > > }
> > > @@ -1195,7 +1190,7 @@ static ssize_t iio_buffer_store_watermark(struct device *dev,
> > > size_t len)
> > > {
> > > struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > > unsigned int val;
> > > int ret;
> > >
> > > @@ -1228,8 +1223,7 @@ static ssize_t iio_dma_show_data_available(struct device *dev,
> > > struct device_attribute *attr,
> > > char *buf)
> > > {
> > > - struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> > > - struct iio_buffer *buffer = indio_dev->buffer;
> > > + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer;
> > >
> > > return sprintf(buf, "%zu\n", iio_buffer_data_available(buffer));
> > > }
> > > @@ -1254,6 +1248,26 @@ static struct attribute *iio_buffer_attrs[] = {
> > > &dev_attr_data_available.attr,
> > > };
> > >
> > > +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
> > > +
> > > +static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
> > > + struct attribute *attr)
> > > +{
> > > + struct device_attribute *dattr = to_dev_attr(attr);
> > > + struct iio_dev_attr *iio_attr;
> > > +
> > > + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
> > > + if (!iio_attr)
> > > + return NULL;
> > > +
> > > + iio_attr->buffer = buffer;
> > > + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
> > > +
> > > + list_add(&iio_attr->l, &buffer->buffer_attr_list);
> > > +
> > > + return &iio_attr->dev_attr.attr;
> > > +}
> > > +
> > > static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev,
> > > struct attribute **buffer_attrs,
> > > int buffer_attrcount,
> > > @@ -1329,7 +1343,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
> > > }
> > >
> > > scan_el_attrcount = 0;
> > > - INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
> > > + INIT_LIST_HEAD(&buffer->buffer_attr_list);
> > > channels = indio_dev->channels;
> > > if (channels) {
> > > /* new magic */
> > > @@ -1376,9 +1390,19 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
> > >
> > > buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs);
> > >
> > > - attrn = buffer_attrcount;
> > > + for (i = 0; i < buffer_attrcount; i++) {
> > > + struct attribute *wrapped;
> > > +
> > > + wrapped = iio_buffer_wrap_attr(buffer, attr[i]);
> > > + if (!wrapped) {
> > > + ret = -ENOMEM;
> > > + goto error_free_scan_mask;
> > > + }
> > > + attr[i] = wrapped;
> > > + }
> > >
> > > - list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
> > > + attrn = 0;
> > > + list_for_each_entry(p, &buffer->buffer_attr_list, l)
> > > attr[attrn++] = &p->dev_attr.attr;
> > >
> > > buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index);
> > > @@ -1412,7 +1436,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
> > > error_free_scan_mask:
> > > bitmap_free(buffer->scan_mask);
> > > error_cleanup_dynamic:
> > > - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
> > > + iio_free_chan_devattr_list(&buffer->buffer_attr_list);
> > >
> > > return ret;
> > > }
> > > @@ -1443,7 +1467,7 @@ static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer)
> > > bitmap_free(buffer->scan_mask);
> > > kfree(buffer->buffer_group.name);
> > > kfree(buffer->buffer_group.attrs);
> > > - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
> > > + iio_free_chan_devattr_list(&buffer->buffer_attr_list);
> > > }
> > >
> > > void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> > > diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
> > > index 3e555e58475b..41044320e581 100644
> > > --- a/include/linux/iio/buffer_impl.h
> > > +++ b/include/linux/iio/buffer_impl.h
> > > @@ -97,8 +97,8 @@ struct iio_buffer {
> > > /* @scan_timestamp: Does the scan mode include a timestamp. */
> > > bool scan_timestamp;
> > >
> > > - /* @scan_el_dev_attr_list: List of scan element related attributes. */
> > > - struct list_head scan_el_dev_attr_list;
> > > + /* @buffer_attr_list: List of buffer attributes. */
> > > + struct list_head buffer_attr_list;
> > >
> > > /*
> > > * @buffer_group: Attributes of the new buffer group.
> >
> > Best regards
>

2022-09-09 08:27:51

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [PATCH v6 14/24] iio: buffer: wrap all buffer attributes into iio_dev_attr

Hi dee Ho peeps!

Disclaimer - I have no HW to test this using real in-tree drivers. If
someone has a device with a variant of bmc150 or adxl372 or - it'd be
nice to see if reading hwfifo_watermark_max or hwfifo_watermark_min
works with the v6.0-rc4. Maybe I am misreading code and have my own
issues - in which case I apologize already now and go to the corner
while being deeply ashamed :)

On 2/15/21 12:40, Alexandru Ardelean wrote:
> This change wraps all buffer attributes into iio_dev_attr objects, and
> assigns a reference to the IIO buffer they belong to.
>
> With the addition of multiple IIO buffers per one IIO device, we need a way
> to know which IIO buffer is being enabled/disabled/controlled.
>
> We know that all buffer attributes are device_attributes.

I think this assumption is slightly unsafe. I see few drivers adding
IIO_CONST_ATTRs in attribute groups. For example the bmc150 and adxl372
add the hwfifo_watermark_min and hwfifo_watermark_max.

Long story short (or the other way around?)

I was developing a driver for ROHM/Kionix KX022A accelerometer. I did a
random pick and chose the bmc150-core as a reference how others have
implemented the accel drivers. During the testing I noticed that using
IIO_CONST_ATTRs for triggered buffers seem to cause access to somewhere
it shouldn't... Oops.

Reading the code allows me to assume the problem is wrapping the
attributes to IIO_DEV_ATTRs.

static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
+ struct attribute *attr)
+{
+ struct device_attribute *dattr = to_dev_attr(attr);
+ struct iio_dev_attr *iio_attr;
+
+ iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
+ if (!iio_attr)
+ return NULL;
+
+ iio_attr->buffer = buffer;
+ memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));

This copy does assume all attributes are device_attrs, and does not take
into account that IIO_CONST_ATTRS have the string stored in a struct
iio_const_attr which is containing the dev_attr. Eg, copying in the
iio_buffer_wrap_attr() does not copy the string - and later invoking the
'show' callback goes reading something else than the mentioned string
because the pointer is not copied.

I believe problem has been there for a while now - introduced by this:
15097c7a1adc ("iio: buffer: wrap all buffer attributes into
iio_dev_attr") at Feb 2021.

I don't have a fix for you as I am not sure what would be the correct
fix. We _could_ go through all the IIO drivers and ensure none used
IIO_CONST_ATTRs for triggered buffers. This would IMHO be just a
workaround to the real problem - which is assumption that all attributes
in the attribute group are dev_attrs. Such silent assumptions are
fragile as we see :)

If we opt treating all attributes in the group as device_attrs also in
the future, then (in my not always so humble opinion) the APIs should
force this - but I'm not sure how to do that.

Best regards
-- Matti

--
Matti Vaittinen
Linux kernel developer at ROHM Semiconductors
Oulu Finland

~~ When things go utterly wrong vim users can always type :help! ~~

2022-09-19 09:01:11

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [RFT] potential bug with IIO_CONST_ATTR usage with triggered buffers

On 9/9/22 11:12, Vaittinen, Matti wrote:
> Hi dee Ho peeps!
>
> Disclaimer - I have no HW to test this using real in-tree drivers. If
> someone has a device with a variant of bmc150 or adxl372 or - it'd be
> nice to see if reading hwfifo_watermark_max or hwfifo_watermark_min
> works with the v6.0-rc4. Maybe I am misreading code and have my own
> issues - in which case I apologize already now and go to the corner
> while being deeply ashamed :)

I would like to add at least the at91-sama5d2_adc (conditonally
registers the IIO_CONST_ATTR for triggered-buffer) to the list of
devices that could be potentially tested. I hope some of these devices
had a user who could either make us worried and verify my assumption -
or make me ashamed but rest of us relieved :) Eg - I second my request
for testing this - and add potential owners of at91-sama5d2_adc to the list.

> On 2/15/21 12:40, Alexandru Ardelean wrote:
>> This change wraps all buffer attributes into iio_dev_attr objects, and
>> assigns a reference to the IIO buffer they belong to.
>>
>> With the addition of multiple IIO buffers per one IIO device, we need a way
>> to know which IIO buffer is being enabled/disabled/controlled.
>>
>> We know that all buffer attributes are device_attributes.
>
> I think this assumption is slightly unsafe. I see few drivers adding
> IIO_CONST_ATTRs in attribute groups. For example the bmc150 and adxl372
> add the hwfifo_watermark_min and hwfifo_watermark_max.
>

and at91-sama5d2_adc

//snip

>I noticed that using
> IIO_CONST_ATTRs for triggered buffers seem to cause access to somewhere
> it shouldn't... Oops.
>
> Reading the code allows me to assume the problem is wrapping the
> attributes to IIO_DEV_ATTRs.
>
> static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
> + struct attribute *attr)
> +{
> + struct device_attribute *dattr = to_dev_attr(attr);
> + struct iio_dev_attr *iio_attr;
> +
> + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
> + if (!iio_attr)
> + return NULL;
> +
> + iio_attr->buffer = buffer;
> + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
>
> This copy does assume all attributes are device_attrs, and does not take
> into account that IIO_CONST_ATTRS have the string stored in a struct
> iio_const_attr which is containing the dev_attr. Eg, copying in the
> iio_buffer_wrap_attr() does not copy the string - and later invoking the
> 'show' callback goes reading something else than the mentioned string
> because the pointer is not copied.

Yours,
-- Matti

--
Matti Vaittinen
Linux kernel developer at ROHM Semiconductors
Oulu Finland

~~ When things go utterly wrong vim users can always type :help! ~~

2022-09-19 16:12:01

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [RFT] potential bug with IIO_CONST_ATTR usage with triggered buffers

On Mon, 19 Sep 2022 08:52:38 +0000
"Vaittinen, Matti" <[email protected]> wrote:

> On 9/9/22 11:12, Vaittinen, Matti wrote:
> > Hi dee Ho peeps!
> >
> > Disclaimer - I have no HW to test this using real in-tree drivers. If
> > someone has a device with a variant of bmc150 or adxl372 or - it'd be
> > nice to see if reading hwfifo_watermark_max or hwfifo_watermark_min
> > works with the v6.0-rc4. Maybe I am misreading code and have my own
> > issues - in which case I apologize already now and go to the corner
> > while being deeply ashamed :)
>
> I would like to add at least the at91-sama5d2_adc (conditonally
> registers the IIO_CONST_ATTR for triggered-buffer) to the list of
> devices that could be potentially tested. I hope some of these devices
> had a user who could either make us worried and verify my assumption -
> or make me ashamed but rest of us relieved :) Eg - I second my request
> for testing this - and add potential owners of at91-sama5d2_adc to the list.
>
> > On 2/15/21 12:40, Alexandru Ardelean wrote:
> >> This change wraps all buffer attributes into iio_dev_attr objects, and
> >> assigns a reference to the IIO buffer they belong to.
> >>
> >> With the addition of multiple IIO buffers per one IIO device, we need a way
> >> to know which IIO buffer is being enabled/disabled/controlled.
> >>
> >> We know that all buffer attributes are device_attributes.
> >
> > I think this assumption is slightly unsafe. I see few drivers adding
> > IIO_CONST_ATTRs in attribute groups. For example the bmc150 and adxl372
> > add the hwfifo_watermark_min and hwfifo_watermark_max.
> >
>
> and at91-sama5d2_adc
>
> //snip
>
> >I noticed that using
> > IIO_CONST_ATTRs for triggered buffers seem to cause access to somewhere
> > it shouldn't... Oops.
> >
> > Reading the code allows me to assume the problem is wrapping the
> > attributes to IIO_DEV_ATTRs.
> >
> > static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
> > + struct attribute *attr)
> > +{
> > + struct device_attribute *dattr = to_dev_attr(attr);
> > + struct iio_dev_attr *iio_attr;
> > +
> > + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
> > + if (!iio_attr)
> > + return NULL;
> > +
> > + iio_attr->buffer = buffer;
> > + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
> >
> > This copy does assume all attributes are device_attrs, and does not take
> > into account that IIO_CONST_ATTRS have the string stored in a struct
> > iio_const_attr which is containing the dev_attr. Eg, copying in the
> > iio_buffer_wrap_attr() does not copy the string - and later invoking the
> > 'show' callback goes reading something else than the mentioned string
> > because the pointer is not copied.
>
> Yours,
> -- Matti
Hi Matti,

+CC Alexandru on a current email address.

I saw this whilst travelling and completely forgot about when
I was back to normal - so great you sent a follow up!

Anyhow, your reasoning seems correct and it would be easy enough
to add such a case to iio/dummy/iio_simple_dummy_buffer.c and
provide a clear test for the problem.

As to solutions. The quickest is probably to switch these const attrs
over to a non const form and add a comment to the header to say they are
unsuitable for use with buffers.

An alternative would be to make it 'safe' by making the data layouts
match up.

struct iio_attr {
struct device_attribute dev_attr;
union {
u64 address;
const char *string;
};
struct list_head l;
struct iio_chan_spec const *c;
struct iio_buffer *buffer;
};

#define iio_dev_attr iio_attr
#define iio_const_attr iio_attr

Looking at this raises another potential problem.
Where is the address copied over for attributes using IIO_DEVICE_ATTR()?
Maybe I'm just missing it somewhere. Grepping suggests we've been
lucky and there are no users of that field in buffer attributes.

Detecting the problem you found is going to be inherently tricky - though maybe
could rely on the naming of the attributes passed in (iio_const...)
and some scripting magic.

Longer term, it's this sort of thing that motivates protections / runnable
CI self tests with, for example, the roadtest framework that I'm hoping
will be available upstream soonish!

Would you like to send patches given you identified the problem?

If not I'm happy to fix these up. My grepping identified the same 3 cases
you found.

Jonathan

2022-09-19 18:09:28

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [RFT] potential bug with IIO_CONST_ATTR usage with triggered buffers

On Mon, 19 Sep 2022 16:32:14 +0100
Jonathan Cameron <[email protected]> wrote:

> On Mon, 19 Sep 2022 08:52:38 +0000
> "Vaittinen, Matti" <[email protected]> wrote:
>
> > On 9/9/22 11:12, Vaittinen, Matti wrote:
> > > Hi dee Ho peeps!
> > >
> > > Disclaimer - I have no HW to test this using real in-tree drivers. If
> > > someone has a device with a variant of bmc150 or adxl372 or - it'd be
> > > nice to see if reading hwfifo_watermark_max or hwfifo_watermark_min
> > > works with the v6.0-rc4. Maybe I am misreading code and have my own
> > > issues - in which case I apologize already now and go to the corner
> > > while being deeply ashamed :)
> >
> > I would like to add at least the at91-sama5d2_adc (conditonally
> > registers the IIO_CONST_ATTR for triggered-buffer) to the list of
> > devices that could be potentially tested. I hope some of these devices
> > had a user who could either make us worried and verify my assumption -
> > or make me ashamed but rest of us relieved :) Eg - I second my request
> > for testing this - and add potential owners of at91-sama5d2_adc to the list.
> >
> > > On 2/15/21 12:40, Alexandru Ardelean wrote:
> > >> This change wraps all buffer attributes into iio_dev_attr objects, and
> > >> assigns a reference to the IIO buffer they belong to.
> > >>
> > >> With the addition of multiple IIO buffers per one IIO device, we need a way
> > >> to know which IIO buffer is being enabled/disabled/controlled.
> > >>
> > >> We know that all buffer attributes are device_attributes.
> > >
> > > I think this assumption is slightly unsafe. I see few drivers adding
> > > IIO_CONST_ATTRs in attribute groups. For example the bmc150 and adxl372
> > > add the hwfifo_watermark_min and hwfifo_watermark_max.
> > >
> >
> > and at91-sama5d2_adc
> >
> > //snip
> >
> > >I noticed that using
> > > IIO_CONST_ATTRs for triggered buffers seem to cause access to somewhere
> > > it shouldn't... Oops.
> > >
> > > Reading the code allows me to assume the problem is wrapping the
> > > attributes to IIO_DEV_ATTRs.
> > >
> > > static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
> > > + struct attribute *attr)
> > > +{
> > > + struct device_attribute *dattr = to_dev_attr(attr);
> > > + struct iio_dev_attr *iio_attr;
> > > +
> > > + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
> > > + if (!iio_attr)
> > > + return NULL;
> > > +
> > > + iio_attr->buffer = buffer;
> > > + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
> > >
> > > This copy does assume all attributes are device_attrs, and does not take
> > > into account that IIO_CONST_ATTRS have the string stored in a struct
> > > iio_const_attr which is containing the dev_attr. Eg, copying in the
> > > iio_buffer_wrap_attr() does not copy the string - and later invoking the
> > > 'show' callback goes reading something else than the mentioned string
> > > because the pointer is not copied.
> >
> > Yours,
> > -- Matti
> Hi Matti,
>
> +CC Alexandru on a current email address.
>
> I saw this whilst travelling and completely forgot about when
> I was back to normal - so great you sent a follow up!
>
> Anyhow, your reasoning seems correct and it would be easy enough
> to add such a case to iio/dummy/iio_simple_dummy_buffer.c and
> provide a clear test for the problem.
>
> As to solutions. The quickest is probably to switch these const attrs
> over to a non const form and add a comment to the header to say they are
> unsuitable for use with buffers.

Thinking a little more on this - all / (most?) of the users pass a null terminated
array of struct device_attribute * to *iio_triggered_buffer_setup_ext()

That's then assigned to buffer->attrs.
We could add an additional pointer to the struct iio_buffer to take
a null terminated array of struct iio_dev_attr *
and change the signature of that function to take one of those, thus
preventing us using iio_const_attr structures for this.

Then we can wrap those just fine in the code you highlighted and assign the
result into buffer->attrs.

We'd need to precede that change with fixes that just switch the
iio_const_attr uses over to iio_dev_attr but changing this would ensure no
accidental reintroductions of the problem in future drivers (typically
as a result of someone forward porting a driver that is out of tree).

I think this combination of fix then prevent future problems is what
I would prefer.

Jonathan



>
> An alternative would be to make it 'safe' by making the data layouts
> match up.
>
> struct iio_attr {
> struct device_attribute dev_attr;
> union {
> u64 address;
> const char *string;
> };
> struct list_head l;
> struct iio_chan_spec const *c;
> struct iio_buffer *buffer;
> };
>
> #define iio_dev_attr iio_attr
> #define iio_const_attr iio_attr
>
> Looking at this raises another potential problem.
> Where is the address copied over for attributes using IIO_DEVICE_ATTR()?
> Maybe I'm just missing it somewhere. Grepping suggests we've been
> lucky and there are no users of that field in buffer attributes.
>
> Detecting the problem you found is going to be inherently tricky - though maybe
> could rely on the naming of the attributes passed in (iio_const...)
> and some scripting magic.
>
> Longer term, it's this sort of thing that motivates protections / runnable
> CI self tests with, for example, the roadtest framework that I'm hoping
> will be available upstream soonish!
>
> Would you like to send patches given you identified the problem?
>
> If not I'm happy to fix these up. My grepping identified the same 3 cases
> you found.
>
> Jonathan
>

2022-09-19 18:13:28

by Matti Vaittinen

[permalink] [raw]
Subject: Re: [RFT] potential bug with IIO_CONST_ATTR usage with triggered buffers

On 9/19/22 20:18, Jonathan Cameron wrote:
> On Mon, 19 Sep 2022 16:32:14 +0100
> Jonathan Cameron <[email protected]> wrote:
>
>> On Mon, 19 Sep 2022 08:52:38 +0000
>> "Vaittinen, Matti" <[email protected]> wrote:
>>
>>> On 9/9/22 11:12, Vaittinen, Matti wrote:
>>>> Hi dee Ho peeps!
>>>>
>>>> Disclaimer - I have no HW to test this using real in-tree drivers. If
>>>> someone has a device with a variant of bmc150 or adxl372 or - it'd be
>>>> nice to see if reading hwfifo_watermark_max or hwfifo_watermark_min
>>>> works with the v6.0-rc4. Maybe I am misreading code and have my own
>>>> issues - in which case I apologize already now and go to the corner
>>>> while being deeply ashamed :)
>>>
>>> I would like to add at least the at91-sama5d2_adc (conditonally
>>> registers the IIO_CONST_ATTR for triggered-buffer) to the list of
>>> devices that could be potentially tested. I hope some of these devices
>>> had a user who could either make us worried and verify my assumption -
>>> or make me ashamed but rest of us relieved :) Eg - I second my request
>>> for testing this - and add potential owners of at91-sama5d2_adc to the list.
>>>
>>>> On 2/15/21 12:40, Alexandru Ardelean wrote:
>>>>> This change wraps all buffer attributes into iio_dev_attr objects, and
>>>>> assigns a reference to the IIO buffer they belong to.
>>>>>
>>>>> With the addition of multiple IIO buffers per one IIO device, we need a way
>>>>> to know which IIO buffer is being enabled/disabled/controlled.
>>>>>
>>>>> We know that all buffer attributes are device_attributes.
>>>>
>>>> I think this assumption is slightly unsafe. I see few drivers adding
>>>> IIO_CONST_ATTRs in attribute groups. For example the bmc150 and adxl372
>>>> add the hwfifo_watermark_min and hwfifo_watermark_max.
>>>>
>>>
>>> and at91-sama5d2_adc
>>>
>>> //snip
>>>
>>>> I noticed that using
>>>> IIO_CONST_ATTRs for triggered buffers seem to cause access to somewhere
>>>> it shouldn't... Oops.
>>>>
>>>> Reading the code allows me to assume the problem is wrapping the
>>>> attributes to IIO_DEV_ATTRs.
>>>>
>>>> static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
>>>> + struct attribute *attr)
>>>> +{
>>>> + struct device_attribute *dattr = to_dev_attr(attr);
>>>> + struct iio_dev_attr *iio_attr;
>>>> +
>>>> + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
>>>> + if (!iio_attr)
>>>> + return NULL;
>>>> +
>>>> + iio_attr->buffer = buffer;
>>>> + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
>>>>
>>>> This copy does assume all attributes are device_attrs, and does not take
>>>> into account that IIO_CONST_ATTRS have the string stored in a struct
>>>> iio_const_attr which is containing the dev_attr. Eg, copying in the
>>>> iio_buffer_wrap_attr() does not copy the string - and later invoking the
>>>> 'show' callback goes reading something else than the mentioned string
>>>> because the pointer is not copied.
>>>
>>> Yours,
>>> -- Matti
>> Hi Matti,
>>
>> +CC Alexandru on a current email address.
>>
>> I saw this whilst travelling and completely forgot about when
>> I was back to normal - so great you sent a follow up!

I was also participating at ELCE last week so didn't do much of emails/code.

>>
>> Anyhow, your reasoning seems correct and it would be easy enough
>> to add such a case to iio/dummy/iio_simple_dummy_buffer.c and
>> provide a clear test for the problem.
>>
>> As to solutions. The quickest is probably to switch these const attrs
>> over to a non const form and add a comment to the header to say they are
>> unsuitable for use with buffers.
>
> Thinking a little more on this - all / (most?) of the users pass a null terminated
> array of struct device_attribute * to *iio_triggered_buffer_setup_ext()
>
> That's then assigned to buffer->attrs.
> We could add an additional pointer to the struct iio_buffer to take
> a null terminated array of struct iio_dev_attr *
> and change the signature of that function to take one of those, thus
> preventing us using iio_const_attr structures for this.

Yes. I would also rather see pointer to array of struct iio_dev_attr *
if we continue keeping the assumption that attrs are of type iio_dev_attr.

>
> Then we can wrap those just fine in the code you highlighted and assign the
> result into buffer->attrs.
>
> We'd need to precede that change with fixes that just switch the
> iio_const_attr uses over to iio_dev_attr but changing this would ensure no
> accidental reintroductions of the problem in future drivers (typically
> as a result of someone forward porting a driver that is out of tree).

Again I do agree. Besides change of const_attrs is necessary in any case
if we don't change the wrapping.

>>
>> Would you like to send patches given you identified the problem?

I am in any case about to send couple of patches to IIO. The devm-helper
usage (v2 - I sent v1 from my other email address (mazziesaccount) - but
I am the same person :] ) and a new accelerometer driver. So, I can look
also at this change while I am at it if you're busy).

>> If not I'm happy to fix these up. My grepping identified the same 3 cases
>> you found.

Feel free to patch this if you wish. Just please let me know if you take
care of this so we don't do double the work :)

Yours
-- Matti


--
Matti Vaittinen
Linux kernel developer at ROHM Semiconductors
Oulu Finland

~~ When things go utterly wrong vim users can always type :help! ~~

2022-09-24 14:16:56

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [RFT] potential bug with IIO_CONST_ATTR usage with triggered buffers

On Mon, 19 Sep 2022 18:06:37 +0000
"Vaittinen, Matti" <[email protected]> wrote:

> On 9/19/22 20:18, Jonathan Cameron wrote:
> > On Mon, 19 Sep 2022 16:32:14 +0100
> > Jonathan Cameron <[email protected]> wrote:
> >
> >> On Mon, 19 Sep 2022 08:52:38 +0000
> >> "Vaittinen, Matti" <[email protected]> wrote:
> >>
> >>> On 9/9/22 11:12, Vaittinen, Matti wrote:
> >>>> Hi dee Ho peeps!
> >>>>
> >>>> Disclaimer - I have no HW to test this using real in-tree drivers. If
> >>>> someone has a device with a variant of bmc150 or adxl372 or - it'd be
> >>>> nice to see if reading hwfifo_watermark_max or hwfifo_watermark_min
> >>>> works with the v6.0-rc4. Maybe I am misreading code and have my own
> >>>> issues - in which case I apologize already now and go to the corner
> >>>> while being deeply ashamed :)
> >>>
> >>> I would like to add at least the at91-sama5d2_adc (conditonally
> >>> registers the IIO_CONST_ATTR for triggered-buffer) to the list of
> >>> devices that could be potentially tested. I hope some of these devices
> >>> had a user who could either make us worried and verify my assumption -
> >>> or make me ashamed but rest of us relieved :) Eg - I second my request
> >>> for testing this - and add potential owners of at91-sama5d2_adc to the list.
> >>>
> >>>> On 2/15/21 12:40, Alexandru Ardelean wrote:
> >>>>> This change wraps all buffer attributes into iio_dev_attr objects, and
> >>>>> assigns a reference to the IIO buffer they belong to.
> >>>>>
> >>>>> With the addition of multiple IIO buffers per one IIO device, we need a way
> >>>>> to know which IIO buffer is being enabled/disabled/controlled.
> >>>>>
> >>>>> We know that all buffer attributes are device_attributes.
> >>>>
> >>>> I think this assumption is slightly unsafe. I see few drivers adding
> >>>> IIO_CONST_ATTRs in attribute groups. For example the bmc150 and adxl372
> >>>> add the hwfifo_watermark_min and hwfifo_watermark_max.
> >>>>
> >>>
> >>> and at91-sama5d2_adc
> >>>
> >>> //snip
> >>>
> >>>> I noticed that using
> >>>> IIO_CONST_ATTRs for triggered buffers seem to cause access to somewhere
> >>>> it shouldn't... Oops.
> >>>>
> >>>> Reading the code allows me to assume the problem is wrapping the
> >>>> attributes to IIO_DEV_ATTRs.
> >>>>
> >>>> static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
> >>>> + struct attribute *attr)
> >>>> +{
> >>>> + struct device_attribute *dattr = to_dev_attr(attr);
> >>>> + struct iio_dev_attr *iio_attr;
> >>>> +
> >>>> + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
> >>>> + if (!iio_attr)
> >>>> + return NULL;
> >>>> +
> >>>> + iio_attr->buffer = buffer;
> >>>> + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
> >>>>
> >>>> This copy does assume all attributes are device_attrs, and does not take
> >>>> into account that IIO_CONST_ATTRS have the string stored in a struct
> >>>> iio_const_attr which is containing the dev_attr. Eg, copying in the
> >>>> iio_buffer_wrap_attr() does not copy the string - and later invoking the
> >>>> 'show' callback goes reading something else than the mentioned string
> >>>> because the pointer is not copied.
> >>>
> >>> Yours,
> >>> -- Matti
> >> Hi Matti,
> >>
> >> +CC Alexandru on a current email address.
> >>
> >> I saw this whilst travelling and completely forgot about when
> >> I was back to normal - so great you sent a follow up!
>
> I was also participating at ELCE last week so didn't do much of emails/code.
>
> >>
> >> Anyhow, your reasoning seems correct and it would be easy enough
> >> to add such a case to iio/dummy/iio_simple_dummy_buffer.c and
> >> provide a clear test for the problem.
> >>
> >> As to solutions. The quickest is probably to switch these const attrs
> >> over to a non const form and add a comment to the header to say they are
> >> unsuitable for use with buffers.
> >
> > Thinking a little more on this - all / (most?) of the users pass a null terminated
> > array of struct device_attribute * to *iio_triggered_buffer_setup_ext()
> >
> > That's then assigned to buffer->attrs.
> > We could add an additional pointer to the struct iio_buffer to take
> > a null terminated array of struct iio_dev_attr *
> > and change the signature of that function to take one of those, thus
> > preventing us using iio_const_attr structures for this.
>
> Yes. I would also rather see pointer to array of struct iio_dev_attr *
> if we continue keeping the assumption that attrs are of type iio_dev_attr.
>
> >
> > Then we can wrap those just fine in the code you highlighted and assign the
> > result into buffer->attrs.
> >
> > We'd need to precede that change with fixes that just switch the
> > iio_const_attr uses over to iio_dev_attr but changing this would ensure no
> > accidental reintroductions of the problem in future drivers (typically
> > as a result of someone forward porting a driver that is out of tree).
>
> Again I do agree. Besides change of const_attrs is necessary in any case
> if we don't change the wrapping.
>
> >>
> >> Would you like to send patches given you identified the problem?
>
> I am in any case about to send couple of patches to IIO. The devm-helper
> usage (v2 - I sent v1 from my other email address (mazziesaccount) - but
> I am the same person :] ) and a new accelerometer driver. So, I can look
> also at this change while I am at it if you're busy).
>
> >> If not I'm happy to fix these up. My grepping identified the same 3 cases
> >> you found.
>
> Feel free to patch this if you wish. Just please let me know if you take
> care of this so we don't do double the work :)

I'm never one to turn down a volunteer, so I'll leave these for you :)

Plenty of other things on the todo list that I can be getting on with.

Jonathan

>
> Yours
> -- Matti
>
>

2022-09-25 13:37:29

by Alexandru Ardelean

[permalink] [raw]
Subject: Re: [RFT] potential bug with IIO_CONST_ATTR usage with triggered buffers

On Sat, Sep 24, 2022 at 4:49 PM Jonathan Cameron <[email protected]> wrote:
>
> On Mon, 19 Sep 2022 18:06:37 +0000
> "Vaittinen, Matti" <[email protected]> wrote:
>
> > On 9/19/22 20:18, Jonathan Cameron wrote:
> > > On Mon, 19 Sep 2022 16:32:14 +0100
> > > Jonathan Cameron <[email protected]> wrote:
> > >
> > >> On Mon, 19 Sep 2022 08:52:38 +0000
> > >> "Vaittinen, Matti" <[email protected]> wrote:
> > >>
> > >>> On 9/9/22 11:12, Vaittinen, Matti wrote:
> > >>>> Hi dee Ho peeps!
> > >>>>
> > >>>> Disclaimer - I have no HW to test this using real in-tree drivers. If
> > >>>> someone has a device with a variant of bmc150 or adxl372 or - it'd be
> > >>>> nice to see if reading hwfifo_watermark_max or hwfifo_watermark_min
> > >>>> works with the v6.0-rc4. Maybe I am misreading code and have my own
> > >>>> issues - in which case I apologize already now and go to the corner
> > >>>> while being deeply ashamed :)
> > >>>
> > >>> I would like to add at least the at91-sama5d2_adc (conditonally
> > >>> registers the IIO_CONST_ATTR for triggered-buffer) to the list of
> > >>> devices that could be potentially tested. I hope some of these devices
> > >>> had a user who could either make us worried and verify my assumption -
> > >>> or make me ashamed but rest of us relieved :) Eg - I second my request
> > >>> for testing this - and add potential owners of at91-sama5d2_adc to the list.
> > >>>
> > >>>> On 2/15/21 12:40, Alexandru Ardelean wrote:
> > >>>>> This change wraps all buffer attributes into iio_dev_attr objects, and
> > >>>>> assigns a reference to the IIO buffer they belong to.
> > >>>>>
> > >>>>> With the addition of multiple IIO buffers per one IIO device, we need a way
> > >>>>> to know which IIO buffer is being enabled/disabled/controlled.
> > >>>>>
> > >>>>> We know that all buffer attributes are device_attributes.
> > >>>>
> > >>>> I think this assumption is slightly unsafe. I see few drivers adding
> > >>>> IIO_CONST_ATTRs in attribute groups. For example the bmc150 and adxl372
> > >>>> add the hwfifo_watermark_min and hwfifo_watermark_max.


Took me a while to get to this and read in-depth.
Yep.
Apologies.
I omitted the IIO_CONST_ATTRs when I did that change.

> > >>>>
> > >>>
> > >>> and at91-sama5d2_adc
> > >>>
> > >>> //snip
> > >>>
> > >>>> I noticed that using
> > >>>> IIO_CONST_ATTRs for triggered buffers seem to cause access to somewhere
> > >>>> it shouldn't... Oops.
> > >>>>
> > >>>> Reading the code allows me to assume the problem is wrapping the
> > >>>> attributes to IIO_DEV_ATTRs.
> > >>>>
> > >>>> static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
> > >>>> + struct attribute *attr)
> > >>>> +{
> > >>>> + struct device_attribute *dattr = to_dev_attr(attr);
> > >>>> + struct iio_dev_attr *iio_attr;
> > >>>> +
> > >>>> + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
> > >>>> + if (!iio_attr)
> > >>>> + return NULL;
> > >>>> +
> > >>>> + iio_attr->buffer = buffer;
> > >>>> + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
> > >>>>
> > >>>> This copy does assume all attributes are device_attrs, and does not take
> > >>>> into account that IIO_CONST_ATTRS have the string stored in a struct
> > >>>> iio_const_attr which is containing the dev_attr. Eg, copying in the
> > >>>> iio_buffer_wrap_attr() does not copy the string - and later invoking the
> > >>>> 'show' callback goes reading something else than the mentioned string
> > >>>> because the pointer is not copied.
> > >>>
> > >>> Yours,
> > >>> -- Matti
> > >> Hi Matti,
> > >>
> > >> +CC Alexandru on a current email address.
> > >>
> > >> I saw this whilst travelling and completely forgot about when
> > >> I was back to normal - so great you sent a follow up!
> >
> > I was also participating at ELCE last week so didn't do much of emails/code.
> >
> > >>
> > >> Anyhow, your reasoning seems correct and it would be easy enough
> > >> to add such a case to iio/dummy/iio_simple_dummy_buffer.c and
> > >> provide a clear test for the problem.
> > >>
> > >> As to solutions. The quickest is probably to switch these const attrs
> > >> over to a non const form and add a comment to the header to say they are
> > >> unsuitable for use with buffers.
> > >
> > > Thinking a little more on this - all / (most?) of the users pass a null terminated
> > > array of struct device_attribute * to *iio_triggered_buffer_setup_ext()
> > >
> > > That's then assigned to buffer->attrs.
> > > We could add an additional pointer to the struct iio_buffer to take
> > > a null terminated array of struct iio_dev_attr *
> > > and change the signature of that function to take one of those, thus
> > > preventing us using iio_const_attr structures for this.
> >
> > Yes. I would also rather see pointer to array of struct iio_dev_attr *
> > if we continue keeping the assumption that attrs are of type iio_dev_attr.
> >
> > >
> > > Then we can wrap those just fine in the code you highlighted and assign the
> > > result into buffer->attrs.
> > >
> > > We'd need to precede that change with fixes that just switch the
> > > iio_const_attr uses over to iio_dev_attr but changing this would ensure no
> > > accidental reintroductions of the problem in future drivers (typically
> > > as a result of someone forward porting a driver that is out of tree).
> >
> > Again I do agree. Besides change of const_attrs is necessary in any case
> > if we don't change the wrapping.
> >
> > >>
> > >> Would you like to send patches given you identified the problem?
> >
> > I am in any case about to send couple of patches to IIO. The devm-helper
> > usage (v2 - I sent v1 from my other email address (mazziesaccount) - but
> > I am the same person :] ) and a new accelerometer driver. So, I can look
> > also at this change while I am at it if you're busy).
> >
> > >> If not I'm happy to fix these up. My grepping identified the same 3 cases
> > >> you found.
> >
> > Feel free to patch this if you wish. Just please let me know if you take
> > care of this so we don't do double the work :)
>
> I'm never one to turn down a volunteer, so I'll leave these for you :)
>
> Plenty of other things on the todo list that I can be getting on with.
>
> Jonathan
>
> >
> > Yours
> > -- Matti
> >
> >
>

2022-10-06 09:19:22

by Claudiu Beznea

[permalink] [raw]
Subject: Re: [RFT] potential bug with IIO_CONST_ATTR usage with triggered buffers

On 19.09.2022 11:52, Vaittinen, Matti wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>
> On 9/9/22 11:12, Vaittinen, Matti wrote:
>> Hi dee Ho peeps!
>>
>> Disclaimer - I have no HW to test this using real in-tree drivers. If
>> someone has a device with a variant of bmc150 or adxl372 or - it'd be
>> nice to see if reading hwfifo_watermark_max or hwfifo_watermark_min
>> works with the v6.0-rc4.

I've checked it on sama5d2_xplained board on v6.0 and it returns (null) for
both hwfifo_watermark_max and hwfifo_watermark_min:

# cat hwfifo_watermark_max
(null)
# cat hwfifo_watermark_min
(null)


With your series at [1] I have:
# cat hwfifo_watermark_max
128
# cat hwfifo_watermark_min
2

Thank you,
Claudiu Beznea

[1] https://patchwork.kernel.org/project/linux-iio/list/?series=682707

> Maybe I am misreading code and have my own
>> issues - in which case I apologize already now and go to the corner
>> while being deeply ashamed :)
>
> I would like to add at least the at91-sama5d2_adc (conditonally
> registers the IIO_CONST_ATTR for triggered-buffer) to the list of
> devices that could be potentially tested. I hope some of these devices
> had a user who could either make us worried and verify my assumption -
> or make me ashamed but rest of us relieved :) Eg - I second my request
> for testing this - and add potential owners of at91-sama5d2_adc to the list.
>
>> On 2/15/21 12:40, Alexandru Ardelean wrote:
>>> This change wraps all buffer attributes into iio_dev_attr objects, and
>>> assigns a reference to the IIO buffer they belong to.
>>>
>>> With the addition of multiple IIO buffers per one IIO device, we need a way
>>> to know which IIO buffer is being enabled/disabled/controlled.
>>>
>>> We know that all buffer attributes are device_attributes.
>>
>> I think this assumption is slightly unsafe. I see few drivers adding
>> IIO_CONST_ATTRs in attribute groups. For example the bmc150 and adxl372
>> add the hwfifo_watermark_min and hwfifo_watermark_max.
>>
>
> and at91-sama5d2_adc
>
> //snip
>
>> I noticed that using
>> IIO_CONST_ATTRs for triggered buffers seem to cause access to somewhere
>> it shouldn't... Oops.
>>
>> Reading the code allows me to assume the problem is wrapping the
>> attributes to IIO_DEV_ATTRs.
>>
>> static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer,
>> + struct attribute *attr)
>> +{
>> + struct device_attribute *dattr = to_dev_attr(attr);
>> + struct iio_dev_attr *iio_attr;
>> +
>> + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL);
>> + if (!iio_attr)
>> + return NULL;
>> +
>> + iio_attr->buffer = buffer;
>> + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr));
>>
>> This copy does assume all attributes are device_attrs, and does not take
>> into account that IIO_CONST_ATTRS have the string stored in a struct
>> iio_const_attr which is containing the dev_attr. Eg, copying in the
>> iio_buffer_wrap_attr() does not copy the string - and later invoking the
>> 'show' callback goes reading something else than the mentioned string
>> because the pointer is not copied.
>
> Yours,
> -- Matti
>
> --
> Matti Vaittinen
> Linux kernel developer at ROHM Semiconductors
> Oulu Finland
>
> ~~ When things go utterly wrong vim users can always type :help! ~~