linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device
@ 2021-02-01 14:50 Alexandru Ardelean
  2021-02-01 14:50 ` [PATCH v3 01/11] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space Alexandru Ardelean
                   ` (10 more replies)
  0 siblings, 11 replies; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:50 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

Changelog v2 -> v3:
* 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

One thing to be determined: if IIO_BUFFER_GET_FD_IOCTL for buffer0 should
return an FD or not.
Right now, it's returning 0.

Alexandru Ardelean (11):
  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: core: wrap iio device & buffer into struct for character devices
  iio: buffer: introduce support for attaching more IIO buffers
  iio: buffer: add ioctl() to support opening extra buffers for IIO
    device

 .../userspace-api/ioctl/ioctl-number.rst      |   1 +
 drivers/iio/iio_core.h                        |  10 +
 drivers/iio/iio_core_trigger.h                |   4 +-
 drivers/iio/industrialio-buffer.c             | 420 +++++++++++++++---
 drivers/iio/industrialio-core.c               |  87 +++-
 drivers/iio/industrialio-event.c              |   6 +-
 drivers/iio/industrialio-trigger.c            |   6 +-
 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/sysfs.h                     |   3 +
 include/uapi/linux/iio/buffer.h               |  10 +
 13 files changed, 487 insertions(+), 104 deletions(-)
 create mode 100644 include/uapi/linux/iio/buffer.h

-- 
2.17.1


^ permalink raw reply	[flat|nested] 39+ messages in thread

* [PATCH v3 01/11] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
@ 2021-02-01 14:50 ` Alexandru Ardelean
  2021-02-04 17:06   ` Jonathan Cameron
  2021-02-01 14:50 ` [PATCH v3 02/11] iio: core: register chardev only if needed Alexandru Ardelean
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:50 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

Currently, only the 'i' 0x90 ioctl() actually exists and is defined in
'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 <alexandru.ardelean@analog.com>
---
 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..9ebde26b7e32 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  `uapi/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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 02/11] iio: core: register chardev only if needed
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
  2021-02-01 14:50 ` [PATCH v3 01/11] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space Alexandru Ardelean
@ 2021-02-01 14:50 ` Alexandru Ardelean
  2021-02-04 17:17   ` Jonathan Cameron
  2021-02-01 14:50 ` [PATCH v3 03/11] iio: core-trigger: make iio_device_register_trigger_consumer() an int return Alexandru Ardelean
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:50 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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().

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 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 7db761afa578..0a6fd299a978 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);
 	int ret;
 
 	if (!indio_dev->info)
@@ -1805,9 +1815,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);
@@ -1836,9 +1843,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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 03/11] iio: core-trigger: make iio_device_register_trigger_consumer() an int return
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
  2021-02-01 14:50 ` [PATCH v3 01/11] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space Alexandru Ardelean
  2021-02-01 14:50 ` [PATCH v3 02/11] iio: core: register chardev only if needed Alexandru Ardelean
@ 2021-02-01 14:50 ` Alexandru Ardelean
  2021-02-01 14:50 ` [PATCH v3 04/11] iio: core: rework iio device group creation Alexandru Ardelean
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:50 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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 <alexandru.ardelean@analog.com>
---
 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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 04/11] iio: core: rework iio device group creation
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
                   ` (2 preceding siblings ...)
  2021-02-01 14:50 ` [PATCH v3 03/11] iio: core-trigger: make iio_device_register_trigger_consumer() an int return Alexandru Ardelean
@ 2021-02-01 14:50 ` Alexandru Ardelean
  2021-02-04 17:32   ` Jonathan Cameron
  2021-02-01 14:50 ` [PATCH v3 05/11] iio: buffer: group attr count and attr alloc Alexandru Ardelean
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:50 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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 <alexandru.ardelean@analog.com>
---
 drivers/iio/iio_core.h             |  3 +++
 drivers/iio/industrialio-buffer.c  | 12 +++++++++--
 drivers/iio/industrialio-core.c    | 32 +++++++++++++++++++++++++++---
 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, 52 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 0a6fd299a978..ccd7aaff6d13 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,9 @@ 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);
+	iio_dev_opaque->groups = NULL;
+	iio_dev_opaque->groupcounter = 0;
 }
 
 static void iio_dev_release(struct device *device)
@@ -1592,7 +1616,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);
@@ -1853,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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 05/11] iio: buffer: group attr count and attr alloc
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
                   ` (3 preceding siblings ...)
  2021-02-01 14:50 ` [PATCH v3 04/11] iio: core: rework iio device group creation Alexandru Ardelean
@ 2021-02-01 14:50 ` Alexandru Ardelean
  2021-02-04 17:49   ` Jonathan Cameron
  2021-02-01 14:51 ` [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes Alexandru Ardelean
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:50 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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.

This change moves the allocation of the buffer/ attributes closer to the
allocation of the scan_elements/ attributes to make grouping easier.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 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..23f22be62cc7 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(struct attribute *), 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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
                   ` (4 preceding siblings ...)
  2021-02-01 14:50 ` [PATCH v3 05/11] iio: buffer: group attr count and attr alloc Alexandru Ardelean
@ 2021-02-01 14:51 ` Alexandru Ardelean
  2021-02-01 20:02   ` kernel test robot
                     ` (2 more replies)
  2021-02-01 14:51 ` [PATCH v3 07/11] iio: add reference to iio buffer on iio_dev_attr Alexandru Ardelean
                   ` (4 subsequent siblings)
  10 siblings, 3 replies; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:51 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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 <alexandru.ardelean@analog.com>
---
 drivers/iio/industrialio-buffer.c | 111 +++++++++++++++++++++++-------
 include/linux/iio/buffer_impl.h   |   9 +--
 include/linux/iio/iio-opaque.h    |   4 ++
 3 files changed, 93 insertions(+), 31 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 23f22be62cc7..f82decf92b7c 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1252,8 +1252,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;
+	int ret;
+
+	group = &iio_dev_opaque->legacy_buffer_group;
+
+	group->attrs = kcalloc(buffer_attrcount + 1,
+			       sizeof(struct attribute *), GFP_KERNEL);
+	if (!group->attrs)
+		return -ENOMEM;
+
+	memcpy(group->attrs, buffer_attrs,
+	       buffer_attrcount * sizeof(struct attribute *));
+	group->name = "buffer";
+
+	ret = iio_device_register_sysfs_group(indio_dev, group);
+	if (ret)
+		goto error_free_buffer_attrs;
+
+	group = &iio_dev_opaque->legacy_scan_el_group;
+
+	group->attrs = kcalloc(scan_el_attrcount + 1,
+			       sizeof(struct attribute *), GFP_KERNEL);
+	if (!group->attrs) {
+		ret = -ENOMEM;
+		goto error_free_buffer_attrs;
+	}
+
+	memcpy(group->attrs, &buffer_attrs[buffer_attrcount],
+	       scan_el_attrcount * sizeof(struct attribute *));
+	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;
+}
+
+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 +1354,8 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
 		}
 	}
 
-	attr = kcalloc(buffer_attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
-		       sizeof(struct attribute *), GFP_KERNEL);
+	attrn = buffer_attrcount + scan_el_attrcount + ARRAY_SIZE(iio_buffer_attrs);
+	attr = kcalloc(attrn + 1, sizeof(struct attribute *), GFP_KERNEL);
 	if (!attr) {
 		ret = -ENOMEM;
 		goto error_free_scan_mask;
@@ -1313,37 +1373,36 @@ 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;
+	attrn = buffer_attrcount;
 
-	ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group);
-	if (ret)
+	list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
+		attr[attrn++] = &p->dev_attr.attr;
+
+	buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index);
+	if (!buffer->buffer_group.name)
 		goto error_free_buffer_attrs;
 
-	buffer->scan_el_group.name = iio_scan_elements_group_name;
+	buffer->buffer_group.attrs = 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) {
-		ret = -ENOMEM;
-		goto error_free_scan_mask;
-	}
-	attrn = 0;
+	ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group);
+	if (ret)
+		goto error_free_buffer_attr_group_name;
 
-	list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
-		buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
+	/* we only need to link the legacy buffer groups for the first buffer */
+	if (index > 0)
+		return 0;
 
-	ret = iio_device_register_sysfs_group(indio_dev, &buffer->scan_el_group);
+	ret = iio_buffer_register_legacy_sysfs_groups(indio_dev, attr,
+						      buffer_attrcount,
+						      scan_el_attrcount);
 	if (ret)
-		goto error_free_scan_el_attrs;
+		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..3e4c3cd248fd 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_el_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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 07/11] iio: add reference to iio buffer on iio_dev_attr
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
                   ` (5 preceding siblings ...)
  2021-02-01 14:51 ` [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes Alexandru Ardelean
@ 2021-02-01 14:51 ` Alexandru Ardelean
  2021-02-04 18:09   ` Jonathan Cameron
  2021-02-01 14:51 ` [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr Alexandru Ardelean
                   ` (3 subsequent siblings)
  10 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:51 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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 <alexandru.ardelean@analog.com>
---
 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 f82decf92b7c..a525e88b302f 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 ccd7aaff6d13..c68130885d83 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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
                   ` (6 preceding siblings ...)
  2021-02-01 14:51 ` [PATCH v3 07/11] iio: add reference to iio buffer on iio_dev_attr Alexandru Ardelean
@ 2021-02-01 14:51 ` Alexandru Ardelean
  2021-02-04 18:23   ` Jonathan Cameron
  2021-02-01 14:51 ` [PATCH v3 09/11] iio: core: wrap iio device & buffer into struct for character devices Alexandru Ardelean
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:51 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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 <alexandru.ardelean@analog.com>
---
 drivers/iio/industrialio-buffer.c | 66 +++++++++++++++++++++----------
 include/linux/iio/buffer_impl.h   |  4 +-
 2 files changed, 48 insertions(+), 22 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index a525e88b302f..49996bed5f4c 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -448,7 +448,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 +460,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 +473,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 +483,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 +495,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 +505,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 +537,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 +1152,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);
@@ -1185,8 +1183,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);
 }
@@ -1197,7 +1194,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;
 
@@ -1230,8 +1227,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));
 }
@@ -1256,6 +1252,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,
@@ -1331,7 +1347,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 */
@@ -1378,9 +1394,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 +1438,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 +1469,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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 09/11] iio: core: wrap iio device & buffer into struct for character devices
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
                   ` (7 preceding siblings ...)
  2021-02-01 14:51 ` [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr Alexandru Ardelean
@ 2021-02-01 14:51 ` Alexandru Ardelean
  2021-02-01 14:51 ` [PATCH v3 10/11] iio: buffer: introduce support for attaching more IIO buffers Alexandru Ardelean
  2021-02-01 14:51 ` [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device Alexandru Ardelean
  10 siblings, 0 replies; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:51 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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 <alexandru.ardelean@analog.com>
---
 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 49996bed5f4c..692f721588e2 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 c68130885d83..8af85838d1c2 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1705,13 +1705,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;
 }
@@ -1725,10 +1736,12 @@ 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);
 	clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
 	iio_device_put(indio_dev);
+	kfree(ib);
 
 	return 0;
 }
@@ -1748,7 +1761,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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 10/11] iio: buffer: introduce support for attaching more IIO buffers
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
                   ` (8 preceding siblings ...)
  2021-02-01 14:51 ` [PATCH v3 09/11] iio: core: wrap iio device & buffer into struct for character devices Alexandru Ardelean
@ 2021-02-01 14:51 ` Alexandru Ardelean
  2021-02-04 18:34   ` Jonathan Cameron
  2021-02-01 14:51 ` [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device Alexandru Ardelean
  10 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:51 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/iio/industrialio-buffer.c | 80 +++++++++++++++++++++++++------
 include/linux/iio/buffer.h        |  4 +-
 include/linux/iio/buffer_impl.h   |  3 ++
 include/linux/iio/iio-opaque.h    |  4 ++
 4 files changed, 74 insertions(+), 17 deletions(-)

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 692f721588e2..a69bb705d173 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1445,11 +1445,21 @@ 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_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
 	struct iio_buffer *buffer = indio_dev->buffer;
 	const struct iio_chan_spec *channels;
-	int i;
+	int unwind_idx;
+	int ret, i;
 
 	channels = indio_dev->channels;
 	if (channels) {
@@ -1463,27 +1473,45 @@ 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, 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;
+		}
+	}
 
-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);
+	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);
+	iio_dev_opaque->attached_buffers = NULL;
+	return ret;
 }
 
 void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
 {
+	struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
 	struct iio_buffer *buffer = indio_dev->buffer;
+	int i;
 
 	if (!buffer)
 		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);
+	iio_dev_opaque->attached_buffers = NULL;
 }
 
 /**
@@ -1601,13 +1629,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/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 3e4c3cd248fd..c909835b6247 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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device
  2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
                   ` (9 preceding siblings ...)
  2021-02-01 14:51 ` [PATCH v3 10/11] iio: buffer: introduce support for attaching more IIO buffers Alexandru Ardelean
@ 2021-02-01 14:51 ` Alexandru Ardelean
  2021-02-04 19:00   ` Jonathan Cameron
  10 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-01 14:51 UTC (permalink / raw)
  To: linux-kernel, linux-iio
  Cc: lars, Michael.Hennerich, jic23, nuno.sa, dragos.bogdan, rafael,
	gregkh, Alexandru Ardelean

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 a 0 FD for the first buffer, as that FD for buffer0
is the same FD as the one used for this ioctl().

For any other extra buffer, this ioctl() will return an anon inode FD that
would access any extra buffer.

Right now, there doesn't seem to be (or I couldn't find) a way for this
ioctl() to return the FD for buffer0 (i.e. to return the same FD as used
for the ioctl()).
So, usespace would need to know that  ioctl(fd,
IIO_GET_EVENT_FD_IOCTL, 0) will return FD 0.
We could also return another FD for buffer 0, but duplicating FDs for the
same IIO buffer sounds problematic.

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 0

 # ./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 <alexandru.ardelean@analog.com>
---
 drivers/iio/industrialio-buffer.c | 118 ++++++++++++++++++++++++++++++
 drivers/iio/industrialio-core.c   |   8 ++
 include/linux/iio/buffer_impl.h   |   5 ++
 include/linux/iio/iio-opaque.h    |   2 +
 include/uapi/linux/iio/buffer.h   |  10 +++
 5 files changed, 143 insertions(+)
 create mode 100644 include/uapi/linux/iio/buffer.h

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index a69bb705d173..119c2b5aa863 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -9,6 +9,7 @@
  * - Better memory allocation techniques?
  * - Alternative access techniques?
  */
+#include <linux/anon_inodes.h>
 #include <linux/kernel.h>
 #include <linux/export.h>
 #include <linux/device.h>
@@ -1333,6 +1334,106 @@ 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;
+
+	clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
+	iio_device_put(indio_dev);
+	kfree(ib);
+
+	return 0;
+}
+
+static const struct file_operations iio_buffer_chrdev_fileops = {
+	.owner = THIS_MODULE,
+	.llseek = noop_llseek,
+	.read = iio_buffer_read_outer_addr,
+	.poll = iio_buffer_poll_addr,
+	.compat_ioctl = compat_ptr_ioctl,
+	.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;
+	char buf_name[sizeof("iio:buffer:xxx")];
+	struct iio_dev_buffer_pair *ib;
+	struct iio_buffer *buffer;
+	int fd, idx;
+
+	if (copy_from_user(&idx, ival, sizeof(idx)))
+		return -EFAULT;
+
+	if (idx == 0) {
+		fd = 0;
+		if (copy_to_user(ival, &fd, sizeof(fd)))
+			return -EFAULT;
+		return 0;
+	}
+
+	if (idx >= iio_dev_opaque->attached_buffers_cnt)
+		return -ENOENT;
+
+	fd = mutex_lock_interruptible(&indio_dev->mlock);
+	if (fd)
+		return fd;
+
+	buffer = iio_dev_opaque->attached_buffers[idx];
+
+	if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
+		fd = -EBUSY;
+		goto error_unlock;
+	}
+
+	iio_device_get(indio_dev);
+
+	ib = kzalloc(sizeof(*ib), GFP_KERNEL);
+	if (!ib) {
+		fd = -ENOMEM;
+		goto error_iio_dev_put;
+	}
+
+	ib->indio_dev = indio_dev;
+	ib->buffer = buffer;
+
+	fd = anon_inode_getfd(buf_name, &iio_buffer_chrdev_fileops,
+			      ib, O_RDWR | O_CLOEXEC);
+	if (fd < 0)
+		goto error_free_ib;
+
+	if (copy_to_user(ival, &fd, sizeof(fd))) {
+		fd = -EFAULT;
+		goto error_free_ib;
+	}
+
+	mutex_unlock(&indio_dev->mlock);
+	return fd;
+
+error_free_ib:
+	kfree(ib);
+error_iio_dev_put:
+	iio_device_put(indio_dev);
+	clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
+error_unlock:
+	mutex_unlock(&indio_dev->mlock);
+	return fd;
+}
+
+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)
@@ -1460,6 +1561,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
 	const struct iio_chan_spec *channels;
 	int unwind_idx;
 	int ret, i;
+	size_t sz;
 
 	channels = indio_dev->channels;
 	if (channels) {
@@ -1481,6 +1583,18 @@ int iio_buffer_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;
 
@@ -1503,6 +1617,10 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
 	if (!buffer)
 		return;
 
+	iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler);
+	kfree(iio_dev_opaque->buffer_ioctl_handler);
+	iio_dev_opaque->buffer_ioctl_handler = NULL;
+
 	iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
 
 	for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 8af85838d1c2..78807b62ff52 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1722,6 +1722,9 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
 	ib->indio_dev = indio_dev;
 	ib->buffer = indio_dev->buffer;
 
+	if (indio_dev->buffer)
+		test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->buffer->flags);
+
 	filp->private_data = ib;
 
 	return 0;
@@ -1739,6 +1742,11 @@ 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);
+	struct iio_buffer *buffer = ib->buffer;
+
+	if (buffer)
+		clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
+
 	clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
 	iio_device_put(indio_dev);
 	kfree(ib);
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 c909835b6247..2c3374d465da 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..de571c83c9f2
--- /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


^ permalink raw reply related	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes
  2021-02-01 14:51 ` [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes Alexandru Ardelean
@ 2021-02-01 20:02   ` kernel test robot
  2021-02-02  6:07   ` Dan Carpenter
  2021-02-03 10:02   ` Andy Shevchenko
  2 siblings, 0 replies; 39+ messages in thread
From: kernel test robot @ 2021-02-01 20:02 UTC (permalink / raw)
  To: Alexandru Ardelean, linux-kernel, linux-iio
  Cc: kbuild-all, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh, Alexandru Ardelean

[-- Attachment #1: Type: text/plain, Size: 2609 bytes --]

Hi Alexandru,

I love your patch! Perhaps something to improve:

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

url:    https://github.com/0day-ci/linux/commits/Alexandru-Ardelean/iio-core-buffer-add-support-for-multiple-IIO-buffers-per-IIO-device/20210201-233550
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: openrisc-randconfig-p001-20210201 (attached as .config)
compiler: or1k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/418ff389a5a48a8a515a106734474e98d6d924ad
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Alexandru-Ardelean/iio-core-buffer-add-support-for-multiple-IIO-buffers-per-IIO-device/20210201-233550
        git checkout 418ff389a5a48a8a515a106734474e98d6d924ad
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=openrisc 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> drivers/iio/industrialio-buffer.c:1306:6: warning: no previous prototype for 'iio_buffer_unregister_legacy_sysfs_groups' [-Wmissing-prototypes]
    1306 | void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/iio/industrialio-buffer.c:1178:27: warning: 'iio_scan_elements_group_name' defined but not used [-Wunused-const-variable=]
    1178 | static const char * const iio_scan_elements_group_name = "scan_elements";
         |                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~


vim +/iio_buffer_unregister_legacy_sysfs_groups +1306 drivers/iio/industrialio-buffer.c

  1305	
> 1306	void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev)
  1307	{
  1308		struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
  1309	
  1310		kfree(iio_dev_opaque->legacy_buffer_group.attrs);
  1311		kfree(iio_dev_opaque->legacy_scan_el_group.attrs);
  1312	}
  1313	

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

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 32377 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes
  2021-02-01 14:51 ` [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes Alexandru Ardelean
  2021-02-01 20:02   ` kernel test robot
@ 2021-02-02  6:07   ` Dan Carpenter
  2021-02-03 10:02   ` Andy Shevchenko
  2 siblings, 0 replies; 39+ messages in thread
From: Dan Carpenter @ 2021-02-02  6:07 UTC (permalink / raw)
  To: kbuild, Alexandru Ardelean, linux-kernel, linux-iio
  Cc: lkp, kbuild-all, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh, Alexandru Ardelean

[-- Attachment #1: Type: text/plain, Size: 8887 bytes --]

Hi Alexandru,

url:    https://github.com/0day-ci/linux/commits/Alexandru-Ardelean/iio-core-buffer-add-support-for-multiple-IIO-buffers-per-IIO-device/20210201-233550
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg
config: arc-randconfig-m031-20210201 (attached as .config)
compiler: arc-elf-gcc (GCC) 9.3.0

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
Reported-by: Dan Carpenter <dan.carpenter@oracle.com>

New smatch warnings:
drivers/iio/industrialio-buffer.c:1413 __iio_buffer_alloc_sysfs_and_mask() error: uninitialized symbol 'ret'.

vim +/ret +1413 drivers/iio/industrialio-buffer.c

e16e0a778fec8a Alexandru Ardelean 2020-09-17  1314  static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer,
418ff389a5a48a Alexandru Ardelean 2021-02-01  1315  					     struct iio_dev *indio_dev,
418ff389a5a48a Alexandru Ardelean 2021-02-01  1316  					     int index)
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1317  {
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1318  	struct iio_dev_attr *p;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1319  	struct attribute **attr;
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1320  	int ret, i, attrn, scan_el_attrcount, buffer_attrcount;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1321  	const struct iio_chan_spec *channels;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1322  
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1323  	buffer_attrcount = 0;
08e7e0adaa1720 Lars-Peter Clausen 2014-11-26  1324  	if (buffer->attrs) {
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1325  		while (buffer->attrs[buffer_attrcount] != NULL)
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1326  			buffer_attrcount++;
08e7e0adaa1720 Lars-Peter Clausen 2014-11-26  1327  	}
08e7e0adaa1720 Lars-Peter Clausen 2014-11-26  1328  
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1329  	scan_el_attrcount = 0;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1330  	INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1331  	channels = indio_dev->channels;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1332  	if (channels) {
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1333  		/* new magic */
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1334  		for (i = 0; i < indio_dev->num_channels; i++) {
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1335  			if (channels[i].scan_index < 0)
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1336  				continue;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1337  
ff3f7e049aef92 Alexandru Ardelean 2020-04-24  1338  			ret = iio_buffer_add_channel_sysfs(indio_dev, buffer,
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1339  							 &channels[i]);
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1340  			if (ret < 0)
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1341  				goto error_cleanup_dynamic;
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1342  			scan_el_attrcount += ret;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1343  			if (channels[i].type == IIO_TIMESTAMP)
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1344  				indio_dev->scan_index_timestamp =
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1345  					channels[i].scan_index;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1346  		}
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1347  		if (indio_dev->masklength && buffer->scan_mask == NULL) {
3862828a903d3c Andy Shevchenko    2019-03-04  1348  			buffer->scan_mask = bitmap_zalloc(indio_dev->masklength,
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1349  							  GFP_KERNEL);
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1350  			if (buffer->scan_mask == NULL) {
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1351  				ret = -ENOMEM;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1352  				goto error_cleanup_dynamic;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1353  			}
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1354  		}
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1355  	}
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1356  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1357  	attrn = buffer_attrcount + scan_el_attrcount + ARRAY_SIZE(iio_buffer_attrs);
418ff389a5a48a Alexandru Ardelean 2021-02-01  1358  	attr = kcalloc(attrn + 1, sizeof(struct attribute *), GFP_KERNEL);
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1359  	if (!attr) {
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1360  		ret = -ENOMEM;
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1361  		goto error_free_scan_mask;
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1362  	}
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1363  
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1364  	memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs));
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1365  	if (!buffer->access->set_length)
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1366  		attr[0] = &dev_attr_length_ro.attr;
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1367  
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1368  	if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK)
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1369  		attr[2] = &dev_attr_watermark_ro.attr;
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1370  
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1371  	if (buffer->attrs)
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1372  		memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1373  		       sizeof(struct attribute *) * buffer_attrcount);
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1374  
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1375  	buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs);
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1376  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1377  	attrn = buffer_attrcount;
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1378  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1379  	list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
418ff389a5a48a Alexandru Ardelean 2021-02-01  1380  		attr[attrn++] = &p->dev_attr.attr;
418ff389a5a48a Alexandru Ardelean 2021-02-01  1381  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1382  	buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index);
418ff389a5a48a Alexandru Ardelean 2021-02-01  1383  	if (!buffer->buffer_group.name)
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1384  		goto error_free_buffer_attrs;

This needs to be "ret = -ENOMEM;"

e5d01923ab9239 Alexandru Ardelean 2021-02-01  1385  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1386  	buffer->buffer_group.attrs = attr;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1387  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1388  	ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group);
418ff389a5a48a Alexandru Ardelean 2021-02-01  1389  	if (ret)
418ff389a5a48a Alexandru Ardelean 2021-02-01  1390  		goto error_free_buffer_attr_group_name;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1391  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1392  	/* we only need to link the legacy buffer groups for the first buffer */
418ff389a5a48a Alexandru Ardelean 2021-02-01  1393  	if (index > 0)
418ff389a5a48a Alexandru Ardelean 2021-02-01  1394  		return 0;
2dca9525701e36 Alexandru Ardelean 2021-02-01  1395  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1396  	ret = iio_buffer_register_legacy_sysfs_groups(indio_dev, attr,
418ff389a5a48a Alexandru Ardelean 2021-02-01  1397  						      buffer_attrcount,
418ff389a5a48a Alexandru Ardelean 2021-02-01  1398  						      scan_el_attrcount);
2dca9525701e36 Alexandru Ardelean 2021-02-01  1399  	if (ret)
418ff389a5a48a Alexandru Ardelean 2021-02-01  1400  		goto error_free_buffer_attr_group_name;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1401  
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1402  	return 0;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1403  
418ff389a5a48a Alexandru Ardelean 2021-02-01  1404  error_free_buffer_attr_group_name:
418ff389a5a48a Alexandru Ardelean 2021-02-01  1405  	kfree(buffer->buffer_group.name);
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1406  error_free_buffer_attrs:
e5d01923ab9239 Alexandru Ardelean 2021-02-01  1407  	kfree(buffer->buffer_group.attrs);
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1408  error_free_scan_mask:
3862828a903d3c Andy Shevchenko    2019-03-04  1409  	bitmap_free(buffer->scan_mask);
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1410  error_cleanup_dynamic:
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1411  	iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1412  
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26 @1413  	return ret;
d967cb6bd4e79c Lars-Peter Clausen 2014-11-26  1414  }

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

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 25323 bytes --]

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes
  2021-02-01 14:51 ` [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes Alexandru Ardelean
  2021-02-01 20:02   ` kernel test robot
  2021-02-02  6:07   ` Dan Carpenter
@ 2021-02-03 10:02   ` Andy Shevchenko
  2021-02-04 13:41     ` Alexandru Ardelean
  2 siblings, 1 reply; 39+ messages in thread
From: Andy Shevchenko @ 2021-02-03 10:02 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Linux Kernel Mailing List, linux-iio, Lars-Peter Clausen,
	Michael Hennerich, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Mon, Feb 1, 2021 at 5:28 PM Alexandru Ardelean
<alexandru.ardelean@analog.com> wrote:
>
> 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.

...

> +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;
> +       int ret;
> +
> +       group = &iio_dev_opaque->legacy_buffer_group;

> +       group->attrs = kcalloc(buffer_attrcount + 1,
> +                              sizeof(struct attribute *), GFP_KERNEL);
> +       if (!group->attrs)
> +               return -ENOMEM;
> +
> +       memcpy(group->attrs, buffer_attrs,
> +              buffer_attrcount * sizeof(struct attribute *));

kmemdup() ?
Perhaps introduce kmemdup_array().

> +       group->name = "buffer";
> +
> +       ret = iio_device_register_sysfs_group(indio_dev, group);
> +       if (ret)
> +               goto error_free_buffer_attrs;
> +
> +       group = &iio_dev_opaque->legacy_scan_el_group;

> +       group->attrs = kcalloc(scan_el_attrcount + 1,
> +                              sizeof(struct attribute *), GFP_KERNEL);
> +       if (!group->attrs) {
> +               ret = -ENOMEM;
> +               goto error_free_buffer_attrs;
> +       }
> +
> +       memcpy(group->attrs, &buffer_attrs[buffer_attrcount],
> +              scan_el_attrcount * sizeof(struct attribute *));

Ditto.

> +       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;
> +}

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes
  2021-02-03 10:02   ` Andy Shevchenko
@ 2021-02-04 13:41     ` Alexandru Ardelean
  2021-02-05 11:07       ` Andy Shevchenko
  0 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-04 13:41 UTC (permalink / raw)
  To: Andy Shevchenko
  Cc: Alexandru Ardelean, Linux Kernel Mailing List, linux-iio,
	Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Wed, Feb 3, 2021 at 12:04 PM Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
>
> On Mon, Feb 1, 2021 at 5:28 PM Alexandru Ardelean
> <alexandru.ardelean@analog.com> wrote:
> >
> > 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.
>
> ...
>
> > +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;
> > +       int ret;
> > +
> > +       group = &iio_dev_opaque->legacy_buffer_group;
>
> > +       group->attrs = kcalloc(buffer_attrcount + 1,
> > +                              sizeof(struct attribute *), GFP_KERNEL);
> > +       if (!group->attrs)
> > +               return -ENOMEM;
> > +
> > +       memcpy(group->attrs, buffer_attrs,
> > +              buffer_attrcount * sizeof(struct attribute *));
>
> kmemdup() ?
> Perhaps introduce kmemdup_array().

doesn't add much benefit from what i can tell;
and it complicates things with the fact that we need to add the extra
null terminator element;
[1] if we kmemdup(buffer_attrcount + 1) , the copy an extra element we
don't need, which needs to be null-ed

>
> > +       group->name = "buffer";
> > +
> > +       ret = iio_device_register_sysfs_group(indio_dev, group);
> > +       if (ret)
> > +               goto error_free_buffer_attrs;
> > +
> > +       group = &iio_dev_opaque->legacy_scan_el_group;
>
> > +       group->attrs = kcalloc(scan_el_attrcount + 1,
> > +                              sizeof(struct attribute *), GFP_KERNEL);
> > +       if (!group->attrs) {
> > +               ret = -ENOMEM;
> > +               goto error_free_buffer_attrs;
> > +       }
> > +
> > +       memcpy(group->attrs, &buffer_attrs[buffer_attrcount],
> > +              scan_el_attrcount * sizeof(struct attribute *));
>
> Ditto.

continuing from [1]
here it may be worse, because kmemdup() would copy 1 element from
undefined memory;

>
> > +       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;
> > +}
>
> --
> With Best Regards,
> Andy Shevchenko

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 01/11] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space
  2021-02-01 14:50 ` [PATCH v3 01/11] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space Alexandru Ardelean
@ 2021-02-04 17:06   ` Jonathan Cameron
  2021-02-05  7:08     ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-04 17:06 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-kernel, linux-iio, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh

On Mon, 1 Feb 2021 16:50:55 +0200
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

> Currently, only the 'i' 0x90 ioctl() actually exists and is defined in
> '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 <alexandru.ardelean@analog.com>
Hi Alex,

Thanks for tidying this up.  My bad from a long time ago to not register
this at the time.

> ---
>  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..9ebde26b7e32 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  `uapi/linux/iio/*.h`                                    IIO

I think the uapi/ bit is effectively implicit. I checked a few of them and the
definitions are in uapi/linux/

>  'j'   00-3F  linux/joystick.h
>  'k'   00-0F  linux/spi/spidev.h                                      conflict!
>  'k'   00-05  video/kyro.h                                            conflict!


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 02/11] iio: core: register chardev only if needed
  2021-02-01 14:50 ` [PATCH v3 02/11] iio: core: register chardev only if needed Alexandru Ardelean
@ 2021-02-04 17:17   ` Jonathan Cameron
  0 siblings, 0 replies; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-04 17:17 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-kernel, linux-iio, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh

On Mon, 1 Feb 2021 16:50:56 +0200
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

> 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().
> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>

Perhaps add a note to this description that it's 'sort' of a breaking ABI
change, but we don't believe it will affect any userspace code as they
likely check there is some point in opening the file before trying to do
so.

If we are wrong we can revert it but I doubt we are - subject to the normal
small fixable scripts people might be using and are happy to fix.

Jonathan

> ---
>  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 7db761afa578..0a6fd299a978 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);
>  	int ret;
>  
>  	if (!indio_dev->info)
> @@ -1805,9 +1815,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);
> @@ -1836,9 +1843,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)


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 04/11] iio: core: rework iio device group creation
  2021-02-01 14:50 ` [PATCH v3 04/11] iio: core: rework iio device group creation Alexandru Ardelean
@ 2021-02-04 17:32   ` Jonathan Cameron
  2021-02-05  7:32     ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-04 17:32 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-kernel, linux-iio, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh

On Mon, 1 Feb 2021 16:50:58 +0200
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

> 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 <alexandru.ardelean@analog.com>
Nice change irrespective of the rest of the series needing it.

Few comments below.

Jonathan

> ---
>  drivers/iio/iio_core.h             |  3 +++
>  drivers/iio/industrialio-buffer.c  | 12 +++++++++--
>  drivers/iio/industrialio-core.c    | 32 +++++++++++++++++++++++++++---
>  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, 52 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 0a6fd299a978..ccd7aaff6d13 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,9 @@ 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);
> +	iio_dev_opaque->groups = NULL;
I can see you are matching style above, but right now I can't see why
we set chan_attr_group.attrs = NULL 
or this new case.

> +	iio_dev_opaque->groupcounter = 0;

or indeed groupcounter.

>  }
>  
>  static void iio_dev_release(struct device *device)
> @@ -1592,7 +1616,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;

Hohum. Nothing to do with this patch but not sure why the iio_dev is called dev here...
I (or someone else) should fix that at somepoint after this is in place as it confused me.


>  	dev->dev.type = &iio_device_type;
>  	dev->dev.bus = &iio_bus_type;
>  	device_initialize(&dev->dev);
> @@ -1853,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;


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 05/11] iio: buffer: group attr count and attr alloc
  2021-02-01 14:50 ` [PATCH v3 05/11] iio: buffer: group attr count and attr alloc Alexandru Ardelean
@ 2021-02-04 17:49   ` Jonathan Cameron
  2021-02-05  8:12     ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-04 17:49 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-kernel, linux-iio, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh

On Mon, 1 Feb 2021 16:50:59 +0200
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

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

Probably want to note why we might want to do 2 or 1 group here as it
sounds weird without knowing where this is going.

> 
> This change moves the allocation of the buffer/ attributes closer to the
> allocation of the scan_elements/ attributes to make grouping easier.
> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
This is fine, but one comment on a possible tidy up for another day inline.

Jonathan

> ---
>  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..23f22be62cc7 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(struct attribute *), 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;

Again a comment for the future  rather than now, but when we are copying
4 items and then looking at whether to change 2 of them it might be cleaner
to just set them directly!  Touch of bit rot here :)

> +
> +	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;
>  }


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 07/11] iio: add reference to iio buffer on iio_dev_attr
  2021-02-01 14:51 ` [PATCH v3 07/11] iio: add reference to iio buffer on iio_dev_attr Alexandru Ardelean
@ 2021-02-04 18:09   ` Jonathan Cameron
  2021-02-05  8:26     ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-04 18:09 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-kernel, linux-iio, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh

On Mon, 1 Feb 2021 16:51:01 +0200
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

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

Could use a bit of macro magic or static inline to keep the old
version set of parameter and have __iio_add_chan_devattr_with_buf 
or similar.  I'm not sure I'd bother given we don't have that many callers.

> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> ---
>  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 f82decf92b7c..a525e88b302f 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 ccd7aaff6d13..c68130885d83 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)				\


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr
  2021-02-01 14:51 ` [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr Alexandru Ardelean
@ 2021-02-04 18:23   ` Jonathan Cameron
  2021-02-05  9:17     ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-04 18:23 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-kernel, linux-iio, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh

On Mon, 1 Feb 2021 16:51:02 +0200
Alexandru Ardelean <alexandru.ardelean@analog.com> 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.

That is going to look a bit odd in any drivers that use it given they
will appear to not be embedded.

There seem to be very few such attributes from a quick grep, so maybe
we may want to unwind this and change all the types.   Might still need
to set .buffer for some of them though (only applying to new drivers as
clearly current ones don't care!)

Looking at what they actually are, some perhaps shouldn't have been in the buffer
directory in the first place (with hindsight!).

Anyhow, aside from that oddity this looks good to me.

Jonathan

> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> ---
>  drivers/iio/industrialio-buffer.c | 66 +++++++++++++++++++++----------
>  include/linux/iio/buffer_impl.h   |  4 +-
>  2 files changed, 48 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index a525e88b302f..49996bed5f4c 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -448,7 +448,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 +460,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 +473,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 +483,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 +495,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 +505,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 +537,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 +1152,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);
> @@ -1185,8 +1183,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);
>  }
> @@ -1197,7 +1194,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;
>  
> @@ -1230,8 +1227,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));
>  }
> @@ -1256,6 +1252,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,
> @@ -1331,7 +1347,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 */
> @@ -1378,9 +1394,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 +1438,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 +1469,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.


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 10/11] iio: buffer: introduce support for attaching more IIO buffers
  2021-02-01 14:51 ` [PATCH v3 10/11] iio: buffer: introduce support for attaching more IIO buffers Alexandru Ardelean
@ 2021-02-04 18:34   ` Jonathan Cameron
  2021-02-05  9:32     ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-04 18:34 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-kernel, linux-iio, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh

On Mon, 1 Feb 2021 16:51:04 +0200
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

> 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.
> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
A few minor comments inline.

Thanks,

J
> ---
>  drivers/iio/industrialio-buffer.c | 80 +++++++++++++++++++++++++------
>  include/linux/iio/buffer.h        |  4 +-
>  include/linux/iio/buffer_impl.h   |  3 ++
>  include/linux/iio/iio-opaque.h    |  4 ++
>  4 files changed, 74 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index 692f721588e2..a69bb705d173 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -1445,11 +1445,21 @@ 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_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
>  	struct iio_buffer *buffer = indio_dev->buffer;
>  	const struct iio_chan_spec *channels;
> -	int i;
> +	int unwind_idx;
> +	int ret, i;
>  
>  	channels = indio_dev->channels;
>  	if (channels) {
> @@ -1463,27 +1473,45 @@ 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, 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;
Gut feeling would be to use i instead of introducing unwind_idx, but it
doesn't really matter if you prefer this.

> +			goto error_unwind_sysfs_and_mask;
> +		}
> +	}
>  
> -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);

could you put this move in a precursor patch. It's making a mess
of the diff and hence this harder to review than it should be.

> +	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);
> +	iio_dev_opaque->attached_buffers = NULL;
> +	return ret;
>  }
>  
>  void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
>  {
> +	struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
>  	struct iio_buffer *buffer = indio_dev->buffer;
> +	int i;
>  
>  	if (!buffer)
>  		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);
> +	iio_dev_opaque->attached_buffers = NULL;

For places where we set freed pointers to NULL, I'd like a comment on why.
Usually it makes me a bit worried we are doing something less than tidy.

>  }
>  
>  /**
> @@ -1601,13 +1629,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/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 3e4c3cd248fd..c909835b6247 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;


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device
  2021-02-01 14:51 ` [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device Alexandru Ardelean
@ 2021-02-04 19:00   ` Jonathan Cameron
  2021-02-05  9:51     ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-04 19:00 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: linux-kernel, linux-iio, lars, Michael.Hennerich, jic23, nuno.sa,
	dragos.bogdan, rafael, gregkh

On Mon, 1 Feb 2021 16:51:05 +0200
Alexandru Ardelean <alexandru.ardelean@analog.com> 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 a 0 FD for the first buffer, as that FD for buffer0
> is the same FD as the one used for this ioctl().

That sounds dangerous as code might just use it without checking properly.
Perhaps take a leaf out of open() and return negative?

What would happen if we just returned an error in this path?

> 
> For any other extra buffer, this ioctl() will return an anon inode FD that
> would access any extra buffer.
> 
> Right now, there doesn't seem to be (or I couldn't find) a way for this
> ioctl() to return the FD for buffer0 (i.e. to return the same FD as used
> for the ioctl()).
> So, usespace would need to know that  ioctl(fd,
> IIO_GET_EVENT_FD_IOCTL, 0) will return FD 0.
> We could also return another FD for buffer 0, but duplicating FDs for the
> same IIO buffer sounds problematic.
> 
> 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 0
> 
>  # ./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.
If you get a chance to add a parameter to the tools/iio/ buffer
program that does much the same as above that would be great.

> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>

Otherwise a few trivials inline.

Good work on bringing this new approach together so quickly.

It's a lot simpler which is good and still gives us a reasonable interface.

If you can update the example code in tree and perhaps add something to
the main IIO docs that would be great.
Documentation/driver-api/iio/buffers.rst I think.

We'll also  (at somepoint) want to do a scrub of the ABI docs to 
either include the new paths or make them less specific to incorporate
both.

Thanks,

Jonathan

> ---
>  drivers/iio/industrialio-buffer.c | 118 ++++++++++++++++++++++++++++++
>  drivers/iio/industrialio-core.c   |   8 ++
>  include/linux/iio/buffer_impl.h   |   5 ++
>  include/linux/iio/iio-opaque.h    |   2 +
>  include/uapi/linux/iio/buffer.h   |  10 +++
>  5 files changed, 143 insertions(+)
>  create mode 100644 include/uapi/linux/iio/buffer.h
> 
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index a69bb705d173..119c2b5aa863 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -9,6 +9,7 @@
>   * - Better memory allocation techniques?
>   * - Alternative access techniques?
>   */
> +#include <linux/anon_inodes.h>
>  #include <linux/kernel.h>
>  #include <linux/export.h>
>  #include <linux/device.h>
> @@ -1333,6 +1334,106 @@ 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;
> +
> +	clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> +	iio_device_put(indio_dev);
> +	kfree(ib);
> +
> +	return 0;
> +}
> +
> +static const struct file_operations iio_buffer_chrdev_fileops = {
> +	.owner = THIS_MODULE,
> +	.llseek = noop_llseek,
> +	.read = iio_buffer_read_outer_addr,
> +	.poll = iio_buffer_poll_addr,
> +	.compat_ioctl = compat_ptr_ioctl,
> +	.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;
> +	char buf_name[sizeof("iio:buffer:xxx")];
> +	struct iio_dev_buffer_pair *ib;
> +	struct iio_buffer *buffer;
> +	int fd, idx;
> +
> +	if (copy_from_user(&idx, ival, sizeof(idx)))
> +		return -EFAULT;
> +
> +	if (idx == 0) {
> +		fd = 0;
> +		if (copy_to_user(ival, &fd, sizeof(fd)))
> +			return -EFAULT;
> +		return 0;
> +	}
> +
> +	if (idx >= iio_dev_opaque->attached_buffers_cnt)
> +		return -ENOENT;
> +
> +	fd = mutex_lock_interruptible(&indio_dev->mlock);

Why mlock?  Might be fine, but this doesn't feel like it is in the normal scope
of that lock.  So perhaps a comment on why it makes sense here.

> +	if (fd)
> +		return fd;
> +
> +	buffer = iio_dev_opaque->attached_buffers[idx];
> +
> +	if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
> +		fd = -EBUSY;
> +		goto error_unlock;
> +	}
> +
> +	iio_device_get(indio_dev);
> +
> +	ib = kzalloc(sizeof(*ib), GFP_KERNEL);
> +	if (!ib) {
> +		fd = -ENOMEM;
> +		goto error_iio_dev_put;
> +	}
> +
> +	ib->indio_dev = indio_dev;
> +	ib->buffer = buffer;
> +
> +	fd = anon_inode_getfd(buf_name, &iio_buffer_chrdev_fileops,
> +			      ib, O_RDWR | O_CLOEXEC);
> +	if (fd < 0)
> +		goto error_free_ib;
> +
> +	if (copy_to_user(ival, &fd, sizeof(fd))) {
> +		fd = -EFAULT;
> +		goto error_free_ib;
> +	}
> +
> +	mutex_unlock(&indio_dev->mlock);
> +	return fd;
> +
> +error_free_ib:
> +	kfree(ib);
> +error_iio_dev_put:
> +	iio_device_put(indio_dev);
> +	clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> +error_unlock:
> +	mutex_unlock(&indio_dev->mlock);
> +	return fd;
> +}
> +
> +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)
> @@ -1460,6 +1561,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
>  	const struct iio_chan_spec *channels;
>  	int unwind_idx;
>  	int ret, i;
> +	size_t sz;
>  
>  	channels = indio_dev->channels;
>  	if (channels) {
> @@ -1481,6 +1583,18 @@ int iio_buffer_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;
>  
> @@ -1503,6 +1617,10 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
>  	if (!buffer)
>  		return;
>  
> +	iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler);
> +	kfree(iio_dev_opaque->buffer_ioctl_handler);
> +	iio_dev_opaque->buffer_ioctl_handler = NULL;
> +
>  	iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
>  
>  	for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 8af85838d1c2..78807b62ff52 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -1722,6 +1722,9 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
>  	ib->indio_dev = indio_dev;
>  	ib->buffer = indio_dev->buffer;
>  
> +	if (indio_dev->buffer)
> +		test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->buffer->flags);
> +

What is this test preventing?  I guess it's keeping the flag for the first
buffer in sync with what other buffers will look at, but does anything check those?
We'll not be able to open this one twice anyway due to the check above.

Perhaps a comment on why this is here?

>  	filp->private_data = ib;
>  
>  	return 0;
> @@ -1739,6 +1742,11 @@ 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);
> +	struct iio_buffer *buffer = ib->buffer;
> +
> +	if (buffer)
> +		clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> +
>  	clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
>  	iio_device_put(indio_dev);
>  	kfree(ib);
> 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 c909835b6247..2c3374d465da 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..de571c83c9f2
> --- /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_ */


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 01/11] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space
  2021-02-04 17:06   ` Jonathan Cameron
@ 2021-02-05  7:08     ` Alexandru Ardelean
  0 siblings, 0 replies; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05  7:08 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Thu, Feb 4, 2021 at 7:13 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Mon, 1 Feb 2021 16:50:55 +0200
> Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
>
> > Currently, only the 'i' 0x90 ioctl() actually exists and is defined in
> > '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 <alexandru.ardelean@analog.com>
> Hi Alex,
>
> Thanks for tidying this up.  My bad from a long time ago to not register
> this at the time.
>
> > ---
> >  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..9ebde26b7e32 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  `uapi/linux/iio/*.h`                                    IIO
>
> I think the uapi/ bit is effectively implicit. I checked a few of them and the
> definitions are in uapi/linux/

ack

>
> >  'j'   00-3F  linux/joystick.h
> >  'k'   00-0F  linux/spi/spidev.h                                      conflict!
> >  'k'   00-05  video/kyro.h                                            conflict!
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 04/11] iio: core: rework iio device group creation
  2021-02-04 17:32   ` Jonathan Cameron
@ 2021-02-05  7:32     ` Alexandru Ardelean
  0 siblings, 0 replies; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05  7:32 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Thu, Feb 4, 2021 at 7:39 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Mon, 1 Feb 2021 16:50:58 +0200
> Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
>
> > 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 <alexandru.ardelean@analog.com>
> Nice change irrespective of the rest of the series needing it.
>
> Few comments below.
>
> Jonathan
>
> > ---
> >  drivers/iio/iio_core.h             |  3 +++
> >  drivers/iio/industrialio-buffer.c  | 12 +++++++++--
> >  drivers/iio/industrialio-core.c    | 32 +++++++++++++++++++++++++++---
> >  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, 52 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 0a6fd299a978..ccd7aaff6d13 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,9 @@ 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);
> > +     iio_dev_opaque->groups = NULL;
> I can see you are matching style above, but right now I can't see why
> we set chan_attr_group.attrs = NULL
> or this new case.
>
> > +     iio_dev_opaque->groupcounter = 0;
>
> or indeed groupcounter.

I'll remove this.
At some point I was maybe thinking that iio_device_unregister_sysfs()
might be called to unregister and re-register.
But we're nowhere close to that.
And I had nothing in mind to do something similar.
Just that the name suggested that this might be an idea.

>
> >  }
> >
> >  static void iio_dev_release(struct device *device)
> > @@ -1592,7 +1616,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;
>
> Hohum. Nothing to do with this patch but not sure why the iio_dev is called dev here...
> I (or someone else) should fix that at somepoint after this is in place as it confused me.

I also noticed this a while ago and wanted to fix it.
But with other patches to work on, I defered it, also because some of
them were overlapping.
Maybe I can squeeze this at the end of this series.

>
>
> >       dev->dev.type = &iio_device_type;
> >       dev->dev.bus = &iio_bus_type;
> >       device_initialize(&dev->dev);
> > @@ -1853,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;
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 05/11] iio: buffer: group attr count and attr alloc
  2021-02-04 17:49   ` Jonathan Cameron
@ 2021-02-05  8:12     ` Alexandru Ardelean
  2021-02-05 12:33       ` Jonathan Cameron
  0 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05  8:12 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Thu, Feb 4, 2021 at 7:57 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Mon, 1 Feb 2021 16:50:59 +0200
> Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
>
> > 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.
>
> Probably want to note why we might want to do 2 or 1 group here as it
> sounds weird without knowing where this is going.

ack

>
> >
> > This change moves the allocation of the buffer/ attributes closer to the
> > allocation of the scan_elements/ attributes to make grouping easier.
> >
> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> This is fine, but one comment on a possible tidy up for another day inline.
>
> Jonathan
>
> > ---
> >  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..23f22be62cc7 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(struct attribute *), 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;
>
> Again a comment for the future  rather than now, but when we are copying
> 4 items and then looking at whether to change 2 of them it might be cleaner
> to just set them directly!  Touch of bit rot here :)

So, I've been on-and-off about how to deal with this one.
I wanted to clean it in various ways using new kernel sysfs APIs.
Maybe, also remove the readonly variants and use the is_visible()
property to set RO/RW modes.
But I also came to the conclusion that this is an idea to address later.
Trying to address this early-on confused me with other overlapping changes.

>
> > +
> > +     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;
> >  }
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 07/11] iio: add reference to iio buffer on iio_dev_attr
  2021-02-04 18:09   ` Jonathan Cameron
@ 2021-02-05  8:26     ` Alexandru Ardelean
  0 siblings, 0 replies; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05  8:26 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Thu, Feb 4, 2021 at 8:16 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Mon, 1 Feb 2021 16:51:01 +0200
> Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
>
> > 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.
>
> Could use a bit of macro magic or static inline to keep the old
> version set of parameter and have __iio_add_chan_devattr_with_buf
> or similar.  I'm not sure I'd bother given we don't have that many callers.

If there isn't a strong opinion to add the
__iio_add_chan_devattr_with_buf() version + macro/inline magic, I'd
probably not do it.
In terms of patch-noise, it's not considerably better, and not
necessarily worse.

When I was thinking about the re-think of __iio_add_chan_devattr, I
was thinking of a way to maybe re-architect this.
Maybe, add some {device_}attribute that act as template [at least for
the R/W functions].
This definitely needs some thinking to make it clean and nice.

>
> >
> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> > ---
> >  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 f82decf92b7c..a525e88b302f 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 ccd7aaff6d13..c68130885d83 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)                           \
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr
  2021-02-04 18:23   ` Jonathan Cameron
@ 2021-02-05  9:17     ` Alexandru Ardelean
  2021-02-05 12:39       ` Jonathan Cameron
  0 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05  9:17 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Thu, Feb 4, 2021 at 8:26 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Mon, 1 Feb 2021 16:51:02 +0200
> Alexandru Ardelean <alexandru.ardelean@analog.com> 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.
>
> That is going to look a bit odd in any drivers that use it given they
> will appear to not be embedded.
>
> There seem to be very few such attributes from a quick grep, so maybe
> we may want to unwind this and change all the types.   Might still need
> to set .buffer for some of them though (only applying to new drivers as
> clearly current ones don't care!)
>
> Looking at what they actually are, some perhaps shouldn't have been in the buffer
> directory in the first place (with hindsight!).
>
> Anyhow, aside from that oddity this looks good to me.

I'm a little vague here.
If there is a suggestion for a change, I may have missed it.

I'm a bit vague on the part of "we may want to unwind this and change
all the types"
Is it referring to something like this patch?
      https://lore.kernel.org/linux-iio/20210122162529.84978-10-alexandru.ardelean@analog.com/
We could do a show/store version that takes an iio_buf_attr or
iio_dev_attr parameter.
But maybe at a later point?
I don't feel it adds much benefit over the current usage of
buffer->attrs, because we need to kmalloc these iio_dev_attr anyways
to store the reference to the iio_buffer.

I would have liked to get rid of these user/external buffer->attrs.
That would have made things easier.

But, it looks like there are several drivers using them.
I usually find them by grepping for iio_triggered_buffer_setup_ext
It's only 5 drivers that provide these attributes.
It used to be a bit easier to find them by grepping
iio_buffer_set_attrs(), but I removed that.


>
> Jonathan
>
> >
> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> > ---
> >  drivers/iio/industrialio-buffer.c | 66 +++++++++++++++++++++----------
> >  include/linux/iio/buffer_impl.h   |  4 +-
> >  2 files changed, 48 insertions(+), 22 deletions(-)
> >
> > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > index a525e88b302f..49996bed5f4c 100644
> > --- a/drivers/iio/industrialio-buffer.c
> > +++ b/drivers/iio/industrialio-buffer.c
> > @@ -448,7 +448,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 +460,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 +473,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 +483,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 +495,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 +505,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 +537,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 +1152,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);
> > @@ -1185,8 +1183,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);
> >  }
> > @@ -1197,7 +1194,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;
> >
> > @@ -1230,8 +1227,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));
> >  }
> > @@ -1256,6 +1252,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,
> > @@ -1331,7 +1347,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 */
> > @@ -1378,9 +1394,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 +1438,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 +1469,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.
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 10/11] iio: buffer: introduce support for attaching more IIO buffers
  2021-02-04 18:34   ` Jonathan Cameron
@ 2021-02-05  9:32     ` Alexandru Ardelean
  0 siblings, 0 replies; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05  9:32 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Thu, Feb 4, 2021 at 8:39 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Mon, 1 Feb 2021 16:51:04 +0200
> Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
>
> > 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.
> >
> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> A few minor comments inline.
>
> Thanks,
>
> J
> > ---
> >  drivers/iio/industrialio-buffer.c | 80 +++++++++++++++++++++++++------
> >  include/linux/iio/buffer.h        |  4 +-
> >  include/linux/iio/buffer_impl.h   |  3 ++
> >  include/linux/iio/iio-opaque.h    |  4 ++
> >  4 files changed, 74 insertions(+), 17 deletions(-)
> >
> > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > index 692f721588e2..a69bb705d173 100644
> > --- a/drivers/iio/industrialio-buffer.c
> > +++ b/drivers/iio/industrialio-buffer.c
> > @@ -1445,11 +1445,21 @@ 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_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
> >       struct iio_buffer *buffer = indio_dev->buffer;
> >       const struct iio_chan_spec *channels;
> > -     int i;
> > +     int unwind_idx;
> > +     int ret, i;
> >
> >       channels = indio_dev->channels;
> >       if (channels) {
> > @@ -1463,27 +1473,45 @@ 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, 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;
> Gut feeling would be to use i instead of introducing unwind_idx, but it
> doesn't really matter if you prefer this.

I thought about leaving 'i' here, but then in the next patch a
block/patch is added that makes using 'i' less obvious/clean.
So, unwind_idx is clearer after more code is added after this block.

>
> > +                     goto error_unwind_sysfs_and_mask;
> > +             }
> > +     }
> >
> > -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);
>
> could you put this move in a precursor patch. It's making a mess
> of the diff and hence this harder to review than it should be.

ack

>
> > +     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);
> > +     iio_dev_opaque->attached_buffers = NULL;
> > +     return ret;
> >  }
> >
> >  void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> >  {
> > +     struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
> >       struct iio_buffer *buffer = indio_dev->buffer;
> > +     int i;
> >
> >       if (!buffer)
> >               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);
> > +     iio_dev_opaque->attached_buffers = NULL;
>
> For places where we set freed pointers to NULL, I'd like a comment on why.
> Usually it makes me a bit worried we are doing something less than tidy.

ack;
will remove;

>
> >  }
> >
> >  /**
> > @@ -1601,13 +1629,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/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 3e4c3cd248fd..c909835b6247 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;
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device
  2021-02-04 19:00   ` Jonathan Cameron
@ 2021-02-05  9:51     ` Alexandru Ardelean
  2021-02-05 12:44       ` Jonathan Cameron
  0 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05  9:51 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Thu, Feb 4, 2021 at 9:06 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Mon, 1 Feb 2021 16:51:05 +0200
> Alexandru Ardelean <alexandru.ardelean@analog.com> 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 a 0 FD for the first buffer, as that FD for buffer0
> > is the same FD as the one used for this ioctl().
>
> That sounds dangerous as code might just use it without checking properly.
> Perhaps take a leaf out of open() and return negative?

So, initially this returned -EBUSY.
But that is also confusing.
And returning 0 isn't great either.

I've been thinking about this for the last few days.
Would it be too bad if return another FD for the same buffer0 ?
Admittedly, you'd be able to access the same buffer0 via 2 FDs, but
coming back to the idea of creating a new API and having to live with
the old one as well, it feels like these 2 FDs for buffer0 are falling
under the same principle.

>
> What would happen if we just returned an error in this path?

We need to find a good error code [if we return an error].
-EBUSY would sound the closest to something correct.

>
> >
> > For any other extra buffer, this ioctl() will return an anon inode FD that
> > would access any extra buffer.
> >
> > Right now, there doesn't seem to be (or I couldn't find) a way for this
> > ioctl() to return the FD for buffer0 (i.e. to return the same FD as used
> > for the ioctl()).
> > So, usespace would need to know that  ioctl(fd,
> > IIO_GET_EVENT_FD_IOCTL, 0) will return FD 0.
> > We could also return another FD for buffer 0, but duplicating FDs for the
> > same IIO buffer sounds problematic.
> >
> > 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 0
> >
> >  # ./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.
> If you get a chance to add a parameter to the tools/iio/ buffer
> program that does much the same as above that would be great.

will take a look

>
> >
> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
>
> Otherwise a few trivials inline.
>
> Good work on bringing this new approach together so quickly.
>
> It's a lot simpler which is good and still gives us a reasonable interface.
>
> If you can update the example code in tree and perhaps add something to
> the main IIO docs that would be great.
> Documentation/driver-api/iio/buffers.rst I think.
>
> We'll also  (at somepoint) want to do a scrub of the ABI docs to
> either include the new paths or make them less specific to incorporate
> both.
>
> Thanks,
>
> Jonathan
>
> > ---
> >  drivers/iio/industrialio-buffer.c | 118 ++++++++++++++++++++++++++++++
> >  drivers/iio/industrialio-core.c   |   8 ++
> >  include/linux/iio/buffer_impl.h   |   5 ++
> >  include/linux/iio/iio-opaque.h    |   2 +
> >  include/uapi/linux/iio/buffer.h   |  10 +++
> >  5 files changed, 143 insertions(+)
> >  create mode 100644 include/uapi/linux/iio/buffer.h
> >
> > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > index a69bb705d173..119c2b5aa863 100644
> > --- a/drivers/iio/industrialio-buffer.c
> > +++ b/drivers/iio/industrialio-buffer.c
> > @@ -9,6 +9,7 @@
> >   * - Better memory allocation techniques?
> >   * - Alternative access techniques?
> >   */
> > +#include <linux/anon_inodes.h>
> >  #include <linux/kernel.h>
> >  #include <linux/export.h>
> >  #include <linux/device.h>
> > @@ -1333,6 +1334,106 @@ 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;
> > +
> > +     clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > +     iio_device_put(indio_dev);
> > +     kfree(ib);
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct file_operations iio_buffer_chrdev_fileops = {
> > +     .owner = THIS_MODULE,
> > +     .llseek = noop_llseek,
> > +     .read = iio_buffer_read_outer_addr,
> > +     .poll = iio_buffer_poll_addr,
> > +     .compat_ioctl = compat_ptr_ioctl,
> > +     .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;
> > +     char buf_name[sizeof("iio:buffer:xxx")];
> > +     struct iio_dev_buffer_pair *ib;
> > +     struct iio_buffer *buffer;
> > +     int fd, idx;
> > +
> > +     if (copy_from_user(&idx, ival, sizeof(idx)))
> > +             return -EFAULT;
> > +
> > +     if (idx == 0) {
> > +             fd = 0;
> > +             if (copy_to_user(ival, &fd, sizeof(fd)))
> > +                     return -EFAULT;
> > +             return 0;
> > +     }
> > +
> > +     if (idx >= iio_dev_opaque->attached_buffers_cnt)
> > +             return -ENOENT;
> > +
> > +     fd = mutex_lock_interruptible(&indio_dev->mlock);
>
> Why mlock?  Might be fine, but this doesn't feel like it is in the normal scope
> of that lock.  So perhaps a comment on why it makes sense here.

Copy+paste from iio_event_getfd()
It does the same thing.
So, I'm a bit vague on this mlock being used here.
Will take a look.

>
> > +     if (fd)
> > +             return fd;
> > +
> > +     buffer = iio_dev_opaque->attached_buffers[idx];
> > +
> > +     if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
> > +             fd = -EBUSY;
> > +             goto error_unlock;
> > +     }
> > +
> > +     iio_device_get(indio_dev);
> > +
> > +     ib = kzalloc(sizeof(*ib), GFP_KERNEL);
> > +     if (!ib) {
> > +             fd = -ENOMEM;
> > +             goto error_iio_dev_put;
> > +     }
> > +
> > +     ib->indio_dev = indio_dev;
> > +     ib->buffer = buffer;
> > +
> > +     fd = anon_inode_getfd(buf_name, &iio_buffer_chrdev_fileops,
> > +                           ib, O_RDWR | O_CLOEXEC);
> > +     if (fd < 0)
> > +             goto error_free_ib;
> > +
> > +     if (copy_to_user(ival, &fd, sizeof(fd))) {
> > +             fd = -EFAULT;
> > +             goto error_free_ib;
> > +     }
> > +
> > +     mutex_unlock(&indio_dev->mlock);
> > +     return fd;
> > +
> > +error_free_ib:
> > +     kfree(ib);
> > +error_iio_dev_put:
> > +     iio_device_put(indio_dev);
> > +     clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > +error_unlock:
> > +     mutex_unlock(&indio_dev->mlock);
> > +     return fd;
> > +}
> > +
> > +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)
> > @@ -1460,6 +1561,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> >       const struct iio_chan_spec *channels;
> >       int unwind_idx;
> >       int ret, i;
> > +     size_t sz;
> >
> >       channels = indio_dev->channels;
> >       if (channels) {
> > @@ -1481,6 +1583,18 @@ int iio_buffer_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;
> >
> > @@ -1503,6 +1617,10 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> >       if (!buffer)
> >               return;
> >
> > +     iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler);
> > +     kfree(iio_dev_opaque->buffer_ioctl_handler);
> > +     iio_dev_opaque->buffer_ioctl_handler = NULL;
> > +
> >       iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
> >
> >       for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > index 8af85838d1c2..78807b62ff52 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -1722,6 +1722,9 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
> >       ib->indio_dev = indio_dev;
> >       ib->buffer = indio_dev->buffer;
> >
> > +     if (indio_dev->buffer)
> > +             test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->buffer->flags);
> > +
>
> What is this test preventing?  I guess it's keeping the flag for the first
> buffer in sync with what other buffers will look at, but does anything check those?
> We'll not be able to open this one twice anyway due to the check above.
>
> Perhaps a comment on why this is here?

Hmm, I admit this is a bit sloppy.
This should technically be an impossible condition, so maybe just
set_bit() would have been enough.
It's making sure that the ioctl() on buffer 0 would have return
-EBUSY; but that was on an older version.

>
> >       filp->private_data = ib;
> >
> >       return 0;
> > @@ -1739,6 +1742,11 @@ 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);
> > +     struct iio_buffer *buffer = ib->buffer;
> > +
> > +     if (buffer)
> > +             clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > +
> >       clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
> >       iio_device_put(indio_dev);
> >       kfree(ib);
> > 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 c909835b6247..2c3374d465da 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..de571c83c9f2
> > --- /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_ */
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes
  2021-02-04 13:41     ` Alexandru Ardelean
@ 2021-02-05 11:07       ` Andy Shevchenko
  0 siblings, 0 replies; 39+ messages in thread
From: Andy Shevchenko @ 2021-02-05 11:07 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Alexandru Ardelean, Linux Kernel Mailing List, linux-iio,
	Lars-Peter Clausen, Michael Hennerich, Jonathan Cameron,
	Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Thu, Feb 4, 2021 at 3:41 PM Alexandru Ardelean
<ardeleanalex@gmail.com> wrote:
> On Wed, Feb 3, 2021 at 12:04 PM Andy Shevchenko
> <andy.shevchenko@gmail.com> wrote:
> > On Mon, Feb 1, 2021 at 5:28 PM Alexandru Ardelean
> > <alexandru.ardelean@analog.com> wrote:

...

> > > +       group->attrs = kcalloc(buffer_attrcount + 1,
> > > +                              sizeof(struct attribute *), GFP_KERNEL);
> > > +       if (!group->attrs)
> > > +               return -ENOMEM;
> > > +
> > > +       memcpy(group->attrs, buffer_attrs,
> > > +              buffer_attrcount * sizeof(struct attribute *));
> >
> > kmemdup() ?
> > Perhaps introduce kmemdup_array().
>
> doesn't add much benefit from what i can tell;
> and it complicates things with the fact that we need to add the extra
> null terminator element;
> [1] if we kmemdup(buffer_attrcount + 1) , the copy an extra element we
> don't need, which needs to be null-ed

Ah, I see now. Thanks for pointing it out!


> > > +       group->attrs = kcalloc(scan_el_attrcount + 1,
> > > +                              sizeof(struct attribute *), GFP_KERNEL);
> > > +       if (!group->attrs) {
> > > +               ret = -ENOMEM;
> > > +               goto error_free_buffer_attrs;
> > > +       }
> > > +
> > > +       memcpy(group->attrs, &buffer_attrs[buffer_attrcount],
> > > +              scan_el_attrcount * sizeof(struct attribute *));
> >
> > Ditto.
>
> continuing from [1]
> here it may be worse, because kmemdup() would copy 1 element from
> undefined memory;


-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 05/11] iio: buffer: group attr count and attr alloc
  2021-02-05  8:12     ` Alexandru Ardelean
@ 2021-02-05 12:33       ` Jonathan Cameron
  0 siblings, 0 replies; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-05 12:33 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman


> > >
> > > +     attr = kcalloc(buffer_attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
> > > +                    sizeof(struct attribute *), 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;  
> >
> > Again a comment for the future  rather than now, but when we are copying
> > 4 items and then looking at whether to change 2 of them it might be cleaner
> > to just set them directly!  Touch of bit rot here :)  
> 
> So, I've been on-and-off about how to deal with this one.
> I wanted to clean it in various ways using new kernel sysfs APIs.
> Maybe, also remove the readonly variants and use the is_visible()
> property to set RO/RW modes.
> But I also came to the conclusion that this is an idea to address later.
> Trying to address this early-on confused me with other overlapping changes.
Absolutely agree. It's not something to do in this series.

Jonathan

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr
  2021-02-05  9:17     ` Alexandru Ardelean
@ 2021-02-05 12:39       ` Jonathan Cameron
  2021-02-05 12:57         ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-05 12:39 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Fri, 5 Feb 2021 11:17:04 +0200
Alexandru Ardelean <ardeleanalex@gmail.com> wrote:

> On Thu, Feb 4, 2021 at 8:26 PM Jonathan Cameron
> <Jonathan.Cameron@huawei.com> wrote:
> >
> > On Mon, 1 Feb 2021 16:51:02 +0200
> > Alexandru Ardelean <alexandru.ardelean@analog.com> 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.  
> >
> > That is going to look a bit odd in any drivers that use it given they
> > will appear to not be embedded.
> >
> > There seem to be very few such attributes from a quick grep, so maybe
> > we may want to unwind this and change all the types.   Might still need
> > to set .buffer for some of them though (only applying to new drivers as
> > clearly current ones don't care!)
> >
> > Looking at what they actually are, some perhaps shouldn't have been in the buffer
> > directory in the first place (with hindsight!).
> >
> > Anyhow, aside from that oddity this looks good to me.  
> 
> I'm a little vague here.
> If there is a suggestion for a change, I may have missed it.

It was vague because I wasn't sure if it it made sense :)
> 
> I'm a bit vague on the part of "we may want to unwind this and change
> all the types"
> Is it referring to something like this patch?
>       https://lore.kernel.org/linux-iio/20210122162529.84978-10-alexandru.ardelean@analog.com/

Exactly, that was what I was wondering about.

> We could do a show/store version that takes an iio_buf_attr or
> iio_dev_attr parameter.
> But maybe at a later point?
> I don't feel it adds much benefit over the current usage of
> buffer->attrs, because we need to kmalloc these iio_dev_attr anyways
> to store the reference to the iio_buffer.
> 
> I would have liked to get rid of these user/external buffer->attrs.
> That would have made things easier.
> 
> But, it looks like there are several drivers using them.
> I usually find them by grepping for iio_triggered_buffer_setup_ext
> It's only 5 drivers that provide these attributes.
> It used to be a bit easier to find them by grepping
> iio_buffer_set_attrs(), but I removed that.

We could look at whether some can be brought into the core.  They tend
to be around hwfifo parameters. Those could be specific to individual
buffers rather than device wide so at least some of them are correctly
placed in the buffer directory (I think - I've argued with myself about
this a few times in the past).

The only oddity we'll get from current approach is callbacks appearing
to access a container structure that they aren't associated with in the
driver.  Its the sort of interface that no one would ever realize was
possible.

Jonathan

> 
> 
> >
> > Jonathan
> >  
> > >
> > > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> > > ---
> > >  drivers/iio/industrialio-buffer.c | 66 +++++++++++++++++++++----------
> > >  include/linux/iio/buffer_impl.h   |  4 +-
> > >  2 files changed, 48 insertions(+), 22 deletions(-)
> > >
> > > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > > index a525e88b302f..49996bed5f4c 100644
> > > --- a/drivers/iio/industrialio-buffer.c
> > > +++ b/drivers/iio/industrialio-buffer.c
> > > @@ -448,7 +448,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 +460,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 +473,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 +483,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 +495,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 +505,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 +537,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 +1152,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);
> > > @@ -1185,8 +1183,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);
> > >  }
> > > @@ -1197,7 +1194,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;
> > >
> > > @@ -1230,8 +1227,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));
> > >  }
> > > @@ -1256,6 +1252,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,
> > > @@ -1331,7 +1347,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 */
> > > @@ -1378,9 +1394,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 +1438,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 +1469,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.  
> >  


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device
  2021-02-05  9:51     ` Alexandru Ardelean
@ 2021-02-05 12:44       ` Jonathan Cameron
  2021-02-05 12:48         ` Alexandru Ardelean
  0 siblings, 1 reply; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-05 12:44 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Fri, 5 Feb 2021 11:51:13 +0200
Alexandru Ardelean <ardeleanalex@gmail.com> wrote:

> On Thu, Feb 4, 2021 at 9:06 PM Jonathan Cameron
> <Jonathan.Cameron@huawei.com> wrote:
> >
> > On Mon, 1 Feb 2021 16:51:05 +0200
> > Alexandru Ardelean <alexandru.ardelean@analog.com> 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 a 0 FD for the first buffer, as that FD for buffer0
> > > is the same FD as the one used for this ioctl().  
> >
> > That sounds dangerous as code might just use it without checking properly.
> > Perhaps take a leaf out of open() and return negative?  
> 
> So, initially this returned -EBUSY.
> But that is also confusing.
> And returning 0 isn't great either.
> 
> I've been thinking about this for the last few days.
> Would it be too bad if return another FD for the same buffer0 ?
> Admittedly, you'd be able to access the same buffer0 via 2 FDs, but
> coming back to the idea of creating a new API and having to live with
> the old one as well, it feels like these 2 FDs for buffer0 are falling
> under the same principle.

It would be in the crazy category if anyone actually tried to read from
both, unless we did something to spin up a clone of the data and that's
getting complex.  I guess we could do something like return errors on
all the original FD activities related to the new one (i.e. poll, read)
That might work. What do you think?

> 
> >
> > What would happen if we just returned an error in this path?  
> 
> We need to find a good error code [if we return an error].
> -EBUSY would sound the closest to something correct.
range error maybe?  or just -EINVAL if we do go this way.

Jonathan

> 
> >  
> > >
> > > For any other extra buffer, this ioctl() will return an anon inode FD that
> > > would access any extra buffer.
> > >
> > > Right now, there doesn't seem to be (or I couldn't find) a way for this
> > > ioctl() to return the FD for buffer0 (i.e. to return the same FD as used
> > > for the ioctl()).
> > > So, usespace would need to know that  ioctl(fd,
> > > IIO_GET_EVENT_FD_IOCTL, 0) will return FD 0.
> > > We could also return another FD for buffer 0, but duplicating FDs for the
> > > same IIO buffer sounds problematic.
> > >
> > > 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 0
> > >
> > >  # ./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.  
> > If you get a chance to add a parameter to the tools/iio/ buffer
> > program that does much the same as above that would be great.  
> 
> will take a look
> 
> >  
> > >
> > > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>  
> >
> > Otherwise a few trivials inline.
> >
> > Good work on bringing this new approach together so quickly.
> >
> > It's a lot simpler which is good and still gives us a reasonable interface.
> >
> > If you can update the example code in tree and perhaps add something to
> > the main IIO docs that would be great.
> > Documentation/driver-api/iio/buffers.rst I think.
> >
> > We'll also  (at somepoint) want to do a scrub of the ABI docs to
> > either include the new paths or make them less specific to incorporate
> > both.
> >
> > Thanks,
> >
> > Jonathan
> >  
> > > ---
> > >  drivers/iio/industrialio-buffer.c | 118 ++++++++++++++++++++++++++++++
> > >  drivers/iio/industrialio-core.c   |   8 ++
> > >  include/linux/iio/buffer_impl.h   |   5 ++
> > >  include/linux/iio/iio-opaque.h    |   2 +
> > >  include/uapi/linux/iio/buffer.h   |  10 +++
> > >  5 files changed, 143 insertions(+)
> > >  create mode 100644 include/uapi/linux/iio/buffer.h
> > >
> > > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > > index a69bb705d173..119c2b5aa863 100644
> > > --- a/drivers/iio/industrialio-buffer.c
> > > +++ b/drivers/iio/industrialio-buffer.c
> > > @@ -9,6 +9,7 @@
> > >   * - Better memory allocation techniques?
> > >   * - Alternative access techniques?
> > >   */
> > > +#include <linux/anon_inodes.h>
> > >  #include <linux/kernel.h>
> > >  #include <linux/export.h>
> > >  #include <linux/device.h>
> > > @@ -1333,6 +1334,106 @@ 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;
> > > +
> > > +     clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > +     iio_device_put(indio_dev);
> > > +     kfree(ib);
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static const struct file_operations iio_buffer_chrdev_fileops = {
> > > +     .owner = THIS_MODULE,
> > > +     .llseek = noop_llseek,
> > > +     .read = iio_buffer_read_outer_addr,
> > > +     .poll = iio_buffer_poll_addr,
> > > +     .compat_ioctl = compat_ptr_ioctl,
> > > +     .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;
> > > +     char buf_name[sizeof("iio:buffer:xxx")];
> > > +     struct iio_dev_buffer_pair *ib;
> > > +     struct iio_buffer *buffer;
> > > +     int fd, idx;
> > > +
> > > +     if (copy_from_user(&idx, ival, sizeof(idx)))
> > > +             return -EFAULT;
> > > +
> > > +     if (idx == 0) {
> > > +             fd = 0;
> > > +             if (copy_to_user(ival, &fd, sizeof(fd)))
> > > +                     return -EFAULT;
> > > +             return 0;
> > > +     }
> > > +
> > > +     if (idx >= iio_dev_opaque->attached_buffers_cnt)
> > > +             return -ENOENT;
> > > +
> > > +     fd = mutex_lock_interruptible(&indio_dev->mlock);  
> >
> > Why mlock?  Might be fine, but this doesn't feel like it is in the normal scope
> > of that lock.  So perhaps a comment on why it makes sense here.  
> 
> Copy+paste from iio_event_getfd()
> It does the same thing.
> So, I'm a bit vague on this mlock being used here.
> Will take a look.

The Event one could well be wrong as well :)

> 
> >  
> > > +     if (fd)
> > > +             return fd;
> > > +
> > > +     buffer = iio_dev_opaque->attached_buffers[idx];
> > > +
> > > +     if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
> > > +             fd = -EBUSY;
> > > +             goto error_unlock;
> > > +     }
> > > +
> > > +     iio_device_get(indio_dev);
> > > +
> > > +     ib = kzalloc(sizeof(*ib), GFP_KERNEL);
> > > +     if (!ib) {
> > > +             fd = -ENOMEM;
> > > +             goto error_iio_dev_put;
> > > +     }
> > > +
> > > +     ib->indio_dev = indio_dev;
> > > +     ib->buffer = buffer;
> > > +
> > > +     fd = anon_inode_getfd(buf_name, &iio_buffer_chrdev_fileops,
> > > +                           ib, O_RDWR | O_CLOEXEC);
> > > +     if (fd < 0)
> > > +             goto error_free_ib;
> > > +
> > > +     if (copy_to_user(ival, &fd, sizeof(fd))) {
> > > +             fd = -EFAULT;
> > > +             goto error_free_ib;
> > > +     }
> > > +
> > > +     mutex_unlock(&indio_dev->mlock);
> > > +     return fd;
> > > +
> > > +error_free_ib:
> > > +     kfree(ib);
> > > +error_iio_dev_put:
> > > +     iio_device_put(indio_dev);
> > > +     clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > +error_unlock:
> > > +     mutex_unlock(&indio_dev->mlock);
> > > +     return fd;
> > > +}
> > > +
> > > +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)
> > > @@ -1460,6 +1561,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> > >       const struct iio_chan_spec *channels;
> > >       int unwind_idx;
> > >       int ret, i;
> > > +     size_t sz;
> > >
> > >       channels = indio_dev->channels;
> > >       if (channels) {
> > > @@ -1481,6 +1583,18 @@ int iio_buffer_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;
> > >
> > > @@ -1503,6 +1617,10 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> > >       if (!buffer)
> > >               return;
> > >
> > > +     iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler);
> > > +     kfree(iio_dev_opaque->buffer_ioctl_handler);
> > > +     iio_dev_opaque->buffer_ioctl_handler = NULL;
> > > +
> > >       iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
> > >
> > >       for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
> > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > > index 8af85838d1c2..78807b62ff52 100644
> > > --- a/drivers/iio/industrialio-core.c
> > > +++ b/drivers/iio/industrialio-core.c
> > > @@ -1722,6 +1722,9 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
> > >       ib->indio_dev = indio_dev;
> > >       ib->buffer = indio_dev->buffer;
> > >
> > > +     if (indio_dev->buffer)
> > > +             test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->buffer->flags);
> > > +  
> >
> > What is this test preventing?  I guess it's keeping the flag for the first
> > buffer in sync with what other buffers will look at, but does anything check those?
> > We'll not be able to open this one twice anyway due to the check above.
> >
> > Perhaps a comment on why this is here?  
> 
> Hmm, I admit this is a bit sloppy.
> This should technically be an impossible condition, so maybe just
> set_bit() would have been enough.
> It's making sure that the ioctl() on buffer 0 would have return
> -EBUSY; but that was on an older version.
> 
> >  
> > >       filp->private_data = ib;
> > >
> > >       return 0;
> > > @@ -1739,6 +1742,11 @@ 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);
> > > +     struct iio_buffer *buffer = ib->buffer;
> > > +
> > > +     if (buffer)
> > > +             clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > +
> > >       clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
> > >       iio_device_put(indio_dev);
> > >       kfree(ib);
> > > 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 c909835b6247..2c3374d465da 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..de571c83c9f2
> > > --- /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_ */  
> >  


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device
  2021-02-05 12:44       ` Jonathan Cameron
@ 2021-02-05 12:48         ` Alexandru Ardelean
  2021-02-06 14:48           ` Jonathan Cameron
  0 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05 12:48 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Fri, Feb 5, 2021 at 2:44 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Fri, 5 Feb 2021 11:51:13 +0200
> Alexandru Ardelean <ardeleanalex@gmail.com> wrote:
>
> > On Thu, Feb 4, 2021 at 9:06 PM Jonathan Cameron
> > <Jonathan.Cameron@huawei.com> wrote:
> > >
> > > On Mon, 1 Feb 2021 16:51:05 +0200
> > > Alexandru Ardelean <alexandru.ardelean@analog.com> 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 a 0 FD for the first buffer, as that FD for buffer0
> > > > is the same FD as the one used for this ioctl().
> > >
> > > That sounds dangerous as code might just use it without checking properly.
> > > Perhaps take a leaf out of open() and return negative?
> >
> > So, initially this returned -EBUSY.
> > But that is also confusing.
> > And returning 0 isn't great either.
> >
> > I've been thinking about this for the last few days.
> > Would it be too bad if return another FD for the same buffer0 ?
> > Admittedly, you'd be able to access the same buffer0 via 2 FDs, but
> > coming back to the idea of creating a new API and having to live with
> > the old one as well, it feels like these 2 FDs for buffer0 are falling
> > under the same principle.
>
> It would be in the crazy category if anyone actually tried to read from
> both, unless we did something to spin up a clone of the data and that's
> getting complex.  I guess we could do something like return errors on
> all the original FD activities related to the new one (i.e. poll, read)
> That might work. What do you think?

Ah, so if someone gets an FD to buffer0 [with this ioctl()], then
return -EBUSY when trying to read from the old API/way?
That could work.

>
> >
> > >
> > > What would happen if we just returned an error in this path?
> >
> > We need to find a good error code [if we return an error].
> > -EBUSY would sound the closest to something correct.
> range error maybe?  or just -EINVAL if we do go this way.
>
> Jonathan
>
> >
> > >
> > > >
> > > > For any other extra buffer, this ioctl() will return an anon inode FD that
> > > > would access any extra buffer.
> > > >
> > > > Right now, there doesn't seem to be (or I couldn't find) a way for this
> > > > ioctl() to return the FD for buffer0 (i.e. to return the same FD as used
> > > > for the ioctl()).
> > > > So, usespace would need to know that  ioctl(fd,
> > > > IIO_GET_EVENT_FD_IOCTL, 0) will return FD 0.
> > > > We could also return another FD for buffer 0, but duplicating FDs for the
> > > > same IIO buffer sounds problematic.
> > > >
> > > > 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 0
> > > >
> > > >  # ./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.
> > > If you get a chance to add a parameter to the tools/iio/ buffer
> > > program that does much the same as above that would be great.
> >
> > will take a look
> >
> > >
> > > >
> > > > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> > >
> > > Otherwise a few trivials inline.
> > >
> > > Good work on bringing this new approach together so quickly.
> > >
> > > It's a lot simpler which is good and still gives us a reasonable interface.
> > >
> > > If you can update the example code in tree and perhaps add something to
> > > the main IIO docs that would be great.
> > > Documentation/driver-api/iio/buffers.rst I think.
> > >
> > > We'll also  (at somepoint) want to do a scrub of the ABI docs to
> > > either include the new paths or make them less specific to incorporate
> > > both.
> > >
> > > Thanks,
> > >
> > > Jonathan
> > >
> > > > ---
> > > >  drivers/iio/industrialio-buffer.c | 118 ++++++++++++++++++++++++++++++
> > > >  drivers/iio/industrialio-core.c   |   8 ++
> > > >  include/linux/iio/buffer_impl.h   |   5 ++
> > > >  include/linux/iio/iio-opaque.h    |   2 +
> > > >  include/uapi/linux/iio/buffer.h   |  10 +++
> > > >  5 files changed, 143 insertions(+)
> > > >  create mode 100644 include/uapi/linux/iio/buffer.h
> > > >
> > > > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > > > index a69bb705d173..119c2b5aa863 100644
> > > > --- a/drivers/iio/industrialio-buffer.c
> > > > +++ b/drivers/iio/industrialio-buffer.c
> > > > @@ -9,6 +9,7 @@
> > > >   * - Better memory allocation techniques?
> > > >   * - Alternative access techniques?
> > > >   */
> > > > +#include <linux/anon_inodes.h>
> > > >  #include <linux/kernel.h>
> > > >  #include <linux/export.h>
> > > >  #include <linux/device.h>
> > > > @@ -1333,6 +1334,106 @@ 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;
> > > > +
> > > > +     clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > > +     iio_device_put(indio_dev);
> > > > +     kfree(ib);
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +static const struct file_operations iio_buffer_chrdev_fileops = {
> > > > +     .owner = THIS_MODULE,
> > > > +     .llseek = noop_llseek,
> > > > +     .read = iio_buffer_read_outer_addr,
> > > > +     .poll = iio_buffer_poll_addr,
> > > > +     .compat_ioctl = compat_ptr_ioctl,
> > > > +     .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;
> > > > +     char buf_name[sizeof("iio:buffer:xxx")];
> > > > +     struct iio_dev_buffer_pair *ib;
> > > > +     struct iio_buffer *buffer;
> > > > +     int fd, idx;
> > > > +
> > > > +     if (copy_from_user(&idx, ival, sizeof(idx)))
> > > > +             return -EFAULT;
> > > > +
> > > > +     if (idx == 0) {
> > > > +             fd = 0;
> > > > +             if (copy_to_user(ival, &fd, sizeof(fd)))
> > > > +                     return -EFAULT;
> > > > +             return 0;
> > > > +     }
> > > > +
> > > > +     if (idx >= iio_dev_opaque->attached_buffers_cnt)
> > > > +             return -ENOENT;
> > > > +
> > > > +     fd = mutex_lock_interruptible(&indio_dev->mlock);
> > >
> > > Why mlock?  Might be fine, but this doesn't feel like it is in the normal scope
> > > of that lock.  So perhaps a comment on why it makes sense here.
> >
> > Copy+paste from iio_event_getfd()
> > It does the same thing.
> > So, I'm a bit vague on this mlock being used here.
> > Will take a look.
>
> The Event one could well be wrong as well :)

Ok, so that may mean: a new ioctl_lock?

>
> >
> > >
> > > > +     if (fd)
> > > > +             return fd;
> > > > +
> > > > +     buffer = iio_dev_opaque->attached_buffers[idx];
> > > > +
> > > > +     if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
> > > > +             fd = -EBUSY;
> > > > +             goto error_unlock;
> > > > +     }
> > > > +
> > > > +     iio_device_get(indio_dev);
> > > > +
> > > > +     ib = kzalloc(sizeof(*ib), GFP_KERNEL);
> > > > +     if (!ib) {
> > > > +             fd = -ENOMEM;
> > > > +             goto error_iio_dev_put;
> > > > +     }
> > > > +
> > > > +     ib->indio_dev = indio_dev;
> > > > +     ib->buffer = buffer;
> > > > +
> > > > +     fd = anon_inode_getfd(buf_name, &iio_buffer_chrdev_fileops,
> > > > +                           ib, O_RDWR | O_CLOEXEC);
> > > > +     if (fd < 0)
> > > > +             goto error_free_ib;
> > > > +
> > > > +     if (copy_to_user(ival, &fd, sizeof(fd))) {
> > > > +             fd = -EFAULT;
> > > > +             goto error_free_ib;
> > > > +     }
> > > > +
> > > > +     mutex_unlock(&indio_dev->mlock);
> > > > +     return fd;
> > > > +
> > > > +error_free_ib:
> > > > +     kfree(ib);
> > > > +error_iio_dev_put:
> > > > +     iio_device_put(indio_dev);
> > > > +     clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > > +error_unlock:
> > > > +     mutex_unlock(&indio_dev->mlock);
> > > > +     return fd;
> > > > +}
> > > > +
> > > > +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)
> > > > @@ -1460,6 +1561,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> > > >       const struct iio_chan_spec *channels;
> > > >       int unwind_idx;
> > > >       int ret, i;
> > > > +     size_t sz;
> > > >
> > > >       channels = indio_dev->channels;
> > > >       if (channels) {
> > > > @@ -1481,6 +1583,18 @@ int iio_buffer_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;
> > > >
> > > > @@ -1503,6 +1617,10 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> > > >       if (!buffer)
> > > >               return;
> > > >
> > > > +     iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler);
> > > > +     kfree(iio_dev_opaque->buffer_ioctl_handler);
> > > > +     iio_dev_opaque->buffer_ioctl_handler = NULL;
> > > > +
> > > >       iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
> > > >
> > > >       for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
> > > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > > > index 8af85838d1c2..78807b62ff52 100644
> > > > --- a/drivers/iio/industrialio-core.c
> > > > +++ b/drivers/iio/industrialio-core.c
> > > > @@ -1722,6 +1722,9 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
> > > >       ib->indio_dev = indio_dev;
> > > >       ib->buffer = indio_dev->buffer;
> > > >
> > > > +     if (indio_dev->buffer)
> > > > +             test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->buffer->flags);
> > > > +
> > >
> > > What is this test preventing?  I guess it's keeping the flag for the first
> > > buffer in sync with what other buffers will look at, but does anything check those?
> > > We'll not be able to open this one twice anyway due to the check above.
> > >
> > > Perhaps a comment on why this is here?
> >
> > Hmm, I admit this is a bit sloppy.
> > This should technically be an impossible condition, so maybe just
> > set_bit() would have been enough.
> > It's making sure that the ioctl() on buffer 0 would have return
> > -EBUSY; but that was on an older version.
> >
> > >
> > > >       filp->private_data = ib;
> > > >
> > > >       return 0;
> > > > @@ -1739,6 +1742,11 @@ 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);
> > > > +     struct iio_buffer *buffer = ib->buffer;
> > > > +
> > > > +     if (buffer)
> > > > +             clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > > +
> > > >       clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
> > > >       iio_device_put(indio_dev);
> > > >       kfree(ib);
> > > > 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 c909835b6247..2c3374d465da 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..de571c83c9f2
> > > > --- /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_ */
> > >
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr
  2021-02-05 12:39       ` Jonathan Cameron
@ 2021-02-05 12:57         ` Alexandru Ardelean
  2021-02-06 14:46           ` Jonathan Cameron
  0 siblings, 1 reply; 39+ messages in thread
From: Alexandru Ardelean @ 2021-02-05 12:57 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Alexandru Ardelean, LKML, linux-iio, Lars-Peter Clausen,
	Hennerich, Michael, Jonathan Cameron, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Fri, Feb 5, 2021 at 2:40 PM Jonathan Cameron
<Jonathan.Cameron@huawei.com> wrote:
>
> On Fri, 5 Feb 2021 11:17:04 +0200
> Alexandru Ardelean <ardeleanalex@gmail.com> wrote:
>
> > On Thu, Feb 4, 2021 at 8:26 PM Jonathan Cameron
> > <Jonathan.Cameron@huawei.com> wrote:
> > >
> > > On Mon, 1 Feb 2021 16:51:02 +0200
> > > Alexandru Ardelean <alexandru.ardelean@analog.com> 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.
> > >
> > > That is going to look a bit odd in any drivers that use it given they
> > > will appear to not be embedded.
> > >
> > > There seem to be very few such attributes from a quick grep, so maybe
> > > we may want to unwind this and change all the types.   Might still need
> > > to set .buffer for some of them though (only applying to new drivers as
> > > clearly current ones don't care!)
> > >
> > > Looking at what they actually are, some perhaps shouldn't have been in the buffer
> > > directory in the first place (with hindsight!).
> > >
> > > Anyhow, aside from that oddity this looks good to me.
> >
> > I'm a little vague here.
> > If there is a suggestion for a change, I may have missed it.
>
> It was vague because I wasn't sure if it it made sense :)
> >
> > I'm a bit vague on the part of "we may want to unwind this and change
> > all the types"
> > Is it referring to something like this patch?
> >       https://lore.kernel.org/linux-iio/20210122162529.84978-10-alexandru.ardelean@analog.com/
>
> Exactly, that was what I was wondering about.

So, from a perspective of API for drivers, it would probably make
sense to have those sort of store/show hook types.
But I am leaning towards maybe moving the HW fifo stuff into IIO core somehow.
Which would make these [new] store/show hooks unneeded [for now at least].

>
> > We could do a show/store version that takes an iio_buf_attr or
> > iio_dev_attr parameter.
> > But maybe at a later point?
> > I don't feel it adds much benefit over the current usage of
> > buffer->attrs, because we need to kmalloc these iio_dev_attr anyways
> > to store the reference to the iio_buffer.
> >
> > I would have liked to get rid of these user/external buffer->attrs.
> > That would have made things easier.
> >
> > But, it looks like there are several drivers using them.
> > I usually find them by grepping for iio_triggered_buffer_setup_ext
> > It's only 5 drivers that provide these attributes.
> > It used to be a bit easier to find them by grepping
> > iio_buffer_set_attrs(), but I removed that.
>
> We could look at whether some can be brought into the core.  They tend
> to be around hwfifo parameters. Those could be specific to individual
> buffers rather than device wide so at least some of them are correctly
> placed in the buffer directory (I think - I've argued with myself about
> this a few times in the past).

I think they could be brought into core.
But they would take some time (because of testing)
These HW Fifo attributes were copied around between drivers and ended
being the same.
So, some IIO core logic would make sense. It's 5 drivers now.

>
> The only oddity we'll get from current approach is callbacks appearing
> to access a container structure that they aren't associated with in the
> driver.  Its the sort of interface that no one would ever realize was
> possible.
>
> Jonathan
>
> >
> >
> > >
> > > Jonathan
> > >
> > > >
> > > > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> > > > ---
> > > >  drivers/iio/industrialio-buffer.c | 66 +++++++++++++++++++++----------
> > > >  include/linux/iio/buffer_impl.h   |  4 +-
> > > >  2 files changed, 48 insertions(+), 22 deletions(-)
> > > >
> > > > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > > > index a525e88b302f..49996bed5f4c 100644
> > > > --- a/drivers/iio/industrialio-buffer.c
> > > > +++ b/drivers/iio/industrialio-buffer.c
> > > > @@ -448,7 +448,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 +460,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 +473,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 +483,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 +495,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 +505,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 +537,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 +1152,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);
> > > > @@ -1185,8 +1183,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);
> > > >  }
> > > > @@ -1197,7 +1194,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;
> > > >
> > > > @@ -1230,8 +1227,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));
> > > >  }
> > > > @@ -1256,6 +1252,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,
> > > > @@ -1331,7 +1347,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 */
> > > > @@ -1378,9 +1394,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 +1438,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 +1469,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.
> > >
>

^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr
  2021-02-05 12:57         ` Alexandru Ardelean
@ 2021-02-06 14:46           ` Jonathan Cameron
  0 siblings, 0 replies; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-06 14:46 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Jonathan Cameron, Alexandru Ardelean, LKML, linux-iio,
	Lars-Peter Clausen, Hennerich, Michael, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Fri, 5 Feb 2021 14:57:29 +0200
Alexandru Ardelean <ardeleanalex@gmail.com> wrote:

> On Fri, Feb 5, 2021 at 2:40 PM Jonathan Cameron
> <Jonathan.Cameron@huawei.com> wrote:
> >
> > On Fri, 5 Feb 2021 11:17:04 +0200
> > Alexandru Ardelean <ardeleanalex@gmail.com> wrote:
> >  
> > > On Thu, Feb 4, 2021 at 8:26 PM Jonathan Cameron
> > > <Jonathan.Cameron@huawei.com> wrote:  
> > > >
> > > > On Mon, 1 Feb 2021 16:51:02 +0200
> > > > Alexandru Ardelean <alexandru.ardelean@analog.com> 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.  
> > > >
> > > > That is going to look a bit odd in any drivers that use it given they
> > > > will appear to not be embedded.
> > > >
> > > > There seem to be very few such attributes from a quick grep, so maybe
> > > > we may want to unwind this and change all the types.   Might still need
> > > > to set .buffer for some of them though (only applying to new drivers as
> > > > clearly current ones don't care!)
> > > >
> > > > Looking at what they actually are, some perhaps shouldn't have been in the buffer
> > > > directory in the first place (with hindsight!).
> > > >
> > > > Anyhow, aside from that oddity this looks good to me.  
> > >
> > > I'm a little vague here.
> > > If there is a suggestion for a change, I may have missed it.  
> >
> > It was vague because I wasn't sure if it it made sense :)  
> > >
> > > I'm a bit vague on the part of "we may want to unwind this and change
> > > all the types"
> > > Is it referring to something like this patch?
> > >       https://lore.kernel.org/linux-iio/20210122162529.84978-10-alexandru.ardelean@analog.com/  
> >
> > Exactly, that was what I was wondering about.  
> 
> So, from a perspective of API for drivers, it would probably make
> sense to have those sort of store/show hook types.
> But I am leaning towards maybe moving the HW fifo stuff into IIO core somehow.
> Which would make these [new] store/show hooks unneeded [for now at least].

Agreed. It probably makes sense to look at pulling these into the core.
When we first did this I was a little unsure how common these would be
but now it's been a while we know they do occur on multiple devices
(even if not that many of them!)

> 
> >  
> > > We could do a show/store version that takes an iio_buf_attr or
> > > iio_dev_attr parameter.
> > > But maybe at a later point?
> > > I don't feel it adds much benefit over the current usage of
> > > buffer->attrs, because we need to kmalloc these iio_dev_attr anyways
> > > to store the reference to the iio_buffer.
> > >
> > > I would have liked to get rid of these user/external buffer->attrs.
> > > That would have made things easier.
> > >
> > > But, it looks like there are several drivers using them.
> > > I usually find them by grepping for iio_triggered_buffer_setup_ext
> > > It's only 5 drivers that provide these attributes.
> > > It used to be a bit easier to find them by grepping
> > > iio_buffer_set_attrs(), but I removed that.  
> >
> > We could look at whether some can be brought into the core.  They tend
> > to be around hwfifo parameters. Those could be specific to individual
> > buffers rather than device wide so at least some of them are correctly
> > placed in the buffer directory (I think - I've argued with myself about
> > this a few times in the past).  
> 
> I think they could be brought into core.
> But they would take some time (because of testing)
> These HW Fifo attributes were copied around between drivers and ended
> being the same.
> So, some IIO core logic would make sense. It's 5 drivers now.

Agree entirely.  This is ready for some tidying up, but not a quick job
as you say because of testing so let us leave it for now.  (better
things to be doing :)

> 
> >
> > The only oddity we'll get from current approach is callbacks appearing
> > to access a container structure that they aren't associated with in the
> > driver.  Its the sort of interface that no one would ever realize was
> > possible.
> >
> > Jonathan
> >  
> > >
> > >  
> > > >
> > > > Jonathan
> > > >  
> > > > >
> > > > > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> > > > > ---
> > > > >  drivers/iio/industrialio-buffer.c | 66 +++++++++++++++++++++----------
> > > > >  include/linux/iio/buffer_impl.h   |  4 +-
> > > > >  2 files changed, 48 insertions(+), 22 deletions(-)
> > > > >
> > > > > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > > > > index a525e88b302f..49996bed5f4c 100644
> > > > > --- a/drivers/iio/industrialio-buffer.c
> > > > > +++ b/drivers/iio/industrialio-buffer.c
> > > > > @@ -448,7 +448,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 +460,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 +473,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 +483,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 +495,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 +505,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 +537,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 +1152,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);
> > > > > @@ -1185,8 +1183,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);
> > > > >  }
> > > > > @@ -1197,7 +1194,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;
> > > > >
> > > > > @@ -1230,8 +1227,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));
> > > > >  }
> > > > > @@ -1256,6 +1252,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,
> > > > > @@ -1331,7 +1347,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 */
> > > > > @@ -1378,9 +1394,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 +1438,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 +1469,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.  
> > > >  
> >  


^ permalink raw reply	[flat|nested] 39+ messages in thread

* Re: [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device
  2021-02-05 12:48         ` Alexandru Ardelean
@ 2021-02-06 14:48           ` Jonathan Cameron
  0 siblings, 0 replies; 39+ messages in thread
From: Jonathan Cameron @ 2021-02-06 14:48 UTC (permalink / raw)
  To: Alexandru Ardelean
  Cc: Jonathan Cameron, Alexandru Ardelean, LKML, linux-iio,
	Lars-Peter Clausen, Hennerich, Michael, Nuno Sá,
	Bogdan, Dragos, Rafael J. Wysocki, Greg Kroah-Hartman

On Fri, 5 Feb 2021 14:48:08 +0200
Alexandru Ardelean <ardeleanalex@gmail.com> wrote:

> On Fri, Feb 5, 2021 at 2:44 PM Jonathan Cameron
> <Jonathan.Cameron@huawei.com> wrote:
> >
> > On Fri, 5 Feb 2021 11:51:13 +0200
> > Alexandru Ardelean <ardeleanalex@gmail.com> wrote:
> >  
> > > On Thu, Feb 4, 2021 at 9:06 PM Jonathan Cameron
> > > <Jonathan.Cameron@huawei.com> wrote:  
> > > >
> > > > On Mon, 1 Feb 2021 16:51:05 +0200
> > > > Alexandru Ardelean <alexandru.ardelean@analog.com> 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 a 0 FD for the first buffer, as that FD for buffer0
> > > > > is the same FD as the one used for this ioctl().  
> > > >
> > > > That sounds dangerous as code might just use it without checking properly.
> > > > Perhaps take a leaf out of open() and return negative?  
> > >
> > > So, initially this returned -EBUSY.
> > > But that is also confusing.
> > > And returning 0 isn't great either.
> > >
> > > I've been thinking about this for the last few days.
> > > Would it be too bad if return another FD for the same buffer0 ?
> > > Admittedly, you'd be able to access the same buffer0 via 2 FDs, but
> > > coming back to the idea of creating a new API and having to live with
> > > the old one as well, it feels like these 2 FDs for buffer0 are falling
> > > under the same principle.  
> >
> > It would be in the crazy category if anyone actually tried to read from
> > both, unless we did something to spin up a clone of the data and that's
> > getting complex.  I guess we could do something like return errors on
> > all the original FD activities related to the new one (i.e. poll, read)
> > That might work. What do you think?  
> 
> Ah, so if someone gets an FD to buffer0 [with this ioctl()], then
> return -EBUSY when trying to read from the old API/way?
> That could work.
Yup. That's the best I have yet come up with as making things sane, whilst
also not breaking the old interface.    If someone is using the new
interface at any point in time, it's reasonable to expect them not
to try and use the old one as well!

Jonathan

> 
> >  
> > >  
> > > >
> > > > What would happen if we just returned an error in this path?  
> > >
> > > We need to find a good error code [if we return an error].
> > > -EBUSY would sound the closest to something correct.  
> > range error maybe?  or just -EINVAL if we do go this way.
> >
> > Jonathan
> >  
> > >  
> > > >  
> > > > >
> > > > > For any other extra buffer, this ioctl() will return an anon inode FD that
> > > > > would access any extra buffer.
> > > > >
> > > > > Right now, there doesn't seem to be (or I couldn't find) a way for this
> > > > > ioctl() to return the FD for buffer0 (i.e. to return the same FD as used
> > > > > for the ioctl()).
> > > > > So, usespace would need to know that  ioctl(fd,
> > > > > IIO_GET_EVENT_FD_IOCTL, 0) will return FD 0.
> > > > > We could also return another FD for buffer 0, but duplicating FDs for the
> > > > > same IIO buffer sounds problematic.
> > > > >
> > > > > 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 0
> > > > >
> > > > >  # ./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.  
> > > > If you get a chance to add a parameter to the tools/iio/ buffer
> > > > program that does much the same as above that would be great.  
> > >
> > > will take a look
> > >  
> > > >  
> > > > >
> > > > > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>  
> > > >
> > > > Otherwise a few trivials inline.
> > > >
> > > > Good work on bringing this new approach together so quickly.
> > > >
> > > > It's a lot simpler which is good and still gives us a reasonable interface.
> > > >
> > > > If you can update the example code in tree and perhaps add something to
> > > > the main IIO docs that would be great.
> > > > Documentation/driver-api/iio/buffers.rst I think.
> > > >
> > > > We'll also  (at somepoint) want to do a scrub of the ABI docs to
> > > > either include the new paths or make them less specific to incorporate
> > > > both.
> > > >
> > > > Thanks,
> > > >
> > > > Jonathan
> > > >  
> > > > > ---
> > > > >  drivers/iio/industrialio-buffer.c | 118 ++++++++++++++++++++++++++++++
> > > > >  drivers/iio/industrialio-core.c   |   8 ++
> > > > >  include/linux/iio/buffer_impl.h   |   5 ++
> > > > >  include/linux/iio/iio-opaque.h    |   2 +
> > > > >  include/uapi/linux/iio/buffer.h   |  10 +++
> > > > >  5 files changed, 143 insertions(+)
> > > > >  create mode 100644 include/uapi/linux/iio/buffer.h
> > > > >
> > > > > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> > > > > index a69bb705d173..119c2b5aa863 100644
> > > > > --- a/drivers/iio/industrialio-buffer.c
> > > > > +++ b/drivers/iio/industrialio-buffer.c
> > > > > @@ -9,6 +9,7 @@
> > > > >   * - Better memory allocation techniques?
> > > > >   * - Alternative access techniques?
> > > > >   */
> > > > > +#include <linux/anon_inodes.h>
> > > > >  #include <linux/kernel.h>
> > > > >  #include <linux/export.h>
> > > > >  #include <linux/device.h>
> > > > > @@ -1333,6 +1334,106 @@ 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;
> > > > > +
> > > > > +     clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > > > +     iio_device_put(indio_dev);
> > > > > +     kfree(ib);
> > > > > +
> > > > > +     return 0;
> > > > > +}
> > > > > +
> > > > > +static const struct file_operations iio_buffer_chrdev_fileops = {
> > > > > +     .owner = THIS_MODULE,
> > > > > +     .llseek = noop_llseek,
> > > > > +     .read = iio_buffer_read_outer_addr,
> > > > > +     .poll = iio_buffer_poll_addr,
> > > > > +     .compat_ioctl = compat_ptr_ioctl,
> > > > > +     .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;
> > > > > +     char buf_name[sizeof("iio:buffer:xxx")];
> > > > > +     struct iio_dev_buffer_pair *ib;
> > > > > +     struct iio_buffer *buffer;
> > > > > +     int fd, idx;
> > > > > +
> > > > > +     if (copy_from_user(&idx, ival, sizeof(idx)))
> > > > > +             return -EFAULT;
> > > > > +
> > > > > +     if (idx == 0) {
> > > > > +             fd = 0;
> > > > > +             if (copy_to_user(ival, &fd, sizeof(fd)))
> > > > > +                     return -EFAULT;
> > > > > +             return 0;
> > > > > +     }
> > > > > +
> > > > > +     if (idx >= iio_dev_opaque->attached_buffers_cnt)
> > > > > +             return -ENOENT;
> > > > > +
> > > > > +     fd = mutex_lock_interruptible(&indio_dev->mlock);  
> > > >
> > > > Why mlock?  Might be fine, but this doesn't feel like it is in the normal scope
> > > > of that lock.  So perhaps a comment on why it makes sense here.  
> > >
> > > Copy+paste from iio_event_getfd()
> > > It does the same thing.
> > > So, I'm a bit vague on this mlock being used here.
> > > Will take a look.  
> >
> > The Event one could well be wrong as well :)  
> 
> Ok, so that may mean: a new ioctl_lock?
> 
> >  
> > >  
> > > >  
> > > > > +     if (fd)
> > > > > +             return fd;
> > > > > +
> > > > > +     buffer = iio_dev_opaque->attached_buffers[idx];
> > > > > +
> > > > > +     if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) {
> > > > > +             fd = -EBUSY;
> > > > > +             goto error_unlock;
> > > > > +     }
> > > > > +
> > > > > +     iio_device_get(indio_dev);
> > > > > +
> > > > > +     ib = kzalloc(sizeof(*ib), GFP_KERNEL);
> > > > > +     if (!ib) {
> > > > > +             fd = -ENOMEM;
> > > > > +             goto error_iio_dev_put;
> > > > > +     }
> > > > > +
> > > > > +     ib->indio_dev = indio_dev;
> > > > > +     ib->buffer = buffer;
> > > > > +
> > > > > +     fd = anon_inode_getfd(buf_name, &iio_buffer_chrdev_fileops,
> > > > > +                           ib, O_RDWR | O_CLOEXEC);
> > > > > +     if (fd < 0)
> > > > > +             goto error_free_ib;
> > > > > +
> > > > > +     if (copy_to_user(ival, &fd, sizeof(fd))) {
> > > > > +             fd = -EFAULT;
> > > > > +             goto error_free_ib;
> > > > > +     }
> > > > > +
> > > > > +     mutex_unlock(&indio_dev->mlock);
> > > > > +     return fd;
> > > > > +
> > > > > +error_free_ib:
> > > > > +     kfree(ib);
> > > > > +error_iio_dev_put:
> > > > > +     iio_device_put(indio_dev);
> > > > > +     clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > > > +error_unlock:
> > > > > +     mutex_unlock(&indio_dev->mlock);
> > > > > +     return fd;
> > > > > +}
> > > > > +
> > > > > +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)
> > > > > @@ -1460,6 +1561,7 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
> > > > >       const struct iio_chan_spec *channels;
> > > > >       int unwind_idx;
> > > > >       int ret, i;
> > > > > +     size_t sz;
> > > > >
> > > > >       channels = indio_dev->channels;
> > > > >       if (channels) {
> > > > > @@ -1481,6 +1583,18 @@ int iio_buffer_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;
> > > > >
> > > > > @@ -1503,6 +1617,10 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
> > > > >       if (!buffer)
> > > > >               return;
> > > > >
> > > > > +     iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler);
> > > > > +     kfree(iio_dev_opaque->buffer_ioctl_handler);
> > > > > +     iio_dev_opaque->buffer_ioctl_handler = NULL;
> > > > > +
> > > > >       iio_buffer_unregister_legacy_sysfs_groups(indio_dev);
> > > > >
> > > > >       for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) {
> > > > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > > > > index 8af85838d1c2..78807b62ff52 100644
> > > > > --- a/drivers/iio/industrialio-core.c
> > > > > +++ b/drivers/iio/industrialio-core.c
> > > > > @@ -1722,6 +1722,9 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp)
> > > > >       ib->indio_dev = indio_dev;
> > > > >       ib->buffer = indio_dev->buffer;
> > > > >
> > > > > +     if (indio_dev->buffer)
> > > > > +             test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->buffer->flags);
> > > > > +  
> > > >
> > > > What is this test preventing?  I guess it's keeping the flag for the first
> > > > buffer in sync with what other buffers will look at, but does anything check those?
> > > > We'll not be able to open this one twice anyway due to the check above.
> > > >
> > > > Perhaps a comment on why this is here?  
> > >
> > > Hmm, I admit this is a bit sloppy.
> > > This should technically be an impossible condition, so maybe just
> > > set_bit() would have been enough.
> > > It's making sure that the ioctl() on buffer 0 would have return
> > > -EBUSY; but that was on an older version.
> > >  
> > > >  
> > > > >       filp->private_data = ib;
> > > > >
> > > > >       return 0;
> > > > > @@ -1739,6 +1742,11 @@ 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);
> > > > > +     struct iio_buffer *buffer = ib->buffer;
> > > > > +
> > > > > +     if (buffer)
> > > > > +             clear_bit(IIO_BUSY_BIT_POS, &buffer->flags);
> > > > > +
> > > > >       clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags);
> > > > >       iio_device_put(indio_dev);
> > > > >       kfree(ib);
> > > > > 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 c909835b6247..2c3374d465da 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..de571c83c9f2
> > > > > --- /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_ */  
> > > >  
> >  


^ permalink raw reply	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2021-02-06 14:49 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-01 14:50 [PATCH v3 00/11] iio: core,buffer: add support for multiple IIO buffers per IIO device Alexandru Ardelean
2021-02-01 14:50 ` [PATCH v3 01/11] docs: ioctl-number.rst: reserve IIO subsystem ioctl() space Alexandru Ardelean
2021-02-04 17:06   ` Jonathan Cameron
2021-02-05  7:08     ` Alexandru Ardelean
2021-02-01 14:50 ` [PATCH v3 02/11] iio: core: register chardev only if needed Alexandru Ardelean
2021-02-04 17:17   ` Jonathan Cameron
2021-02-01 14:50 ` [PATCH v3 03/11] iio: core-trigger: make iio_device_register_trigger_consumer() an int return Alexandru Ardelean
2021-02-01 14:50 ` [PATCH v3 04/11] iio: core: rework iio device group creation Alexandru Ardelean
2021-02-04 17:32   ` Jonathan Cameron
2021-02-05  7:32     ` Alexandru Ardelean
2021-02-01 14:50 ` [PATCH v3 05/11] iio: buffer: group attr count and attr alloc Alexandru Ardelean
2021-02-04 17:49   ` Jonathan Cameron
2021-02-05  8:12     ` Alexandru Ardelean
2021-02-05 12:33       ` Jonathan Cameron
2021-02-01 14:51 ` [PATCH v3 06/11] iio: core: merge buffer/ & scan_elements/ attributes Alexandru Ardelean
2021-02-01 20:02   ` kernel test robot
2021-02-02  6:07   ` Dan Carpenter
2021-02-03 10:02   ` Andy Shevchenko
2021-02-04 13:41     ` Alexandru Ardelean
2021-02-05 11:07       ` Andy Shevchenko
2021-02-01 14:51 ` [PATCH v3 07/11] iio: add reference to iio buffer on iio_dev_attr Alexandru Ardelean
2021-02-04 18:09   ` Jonathan Cameron
2021-02-05  8:26     ` Alexandru Ardelean
2021-02-01 14:51 ` [PATCH v3 08/11] iio: buffer: wrap all buffer attributes into iio_dev_attr Alexandru Ardelean
2021-02-04 18:23   ` Jonathan Cameron
2021-02-05  9:17     ` Alexandru Ardelean
2021-02-05 12:39       ` Jonathan Cameron
2021-02-05 12:57         ` Alexandru Ardelean
2021-02-06 14:46           ` Jonathan Cameron
2021-02-01 14:51 ` [PATCH v3 09/11] iio: core: wrap iio device & buffer into struct for character devices Alexandru Ardelean
2021-02-01 14:51 ` [PATCH v3 10/11] iio: buffer: introduce support for attaching more IIO buffers Alexandru Ardelean
2021-02-04 18:34   ` Jonathan Cameron
2021-02-05  9:32     ` Alexandru Ardelean
2021-02-01 14:51 ` [PATCH v3 11/11] iio: buffer: add ioctl() to support opening extra buffers for IIO device Alexandru Ardelean
2021-02-04 19:00   ` Jonathan Cameron
2021-02-05  9:51     ` Alexandru Ardelean
2021-02-05 12:44       ` Jonathan Cameron
2021-02-05 12:48         ` Alexandru Ardelean
2021-02-06 14:48           ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).