linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 0/6] iio: core,buffer: re-organize chardev creation
@ 2020-04-26  7:38 Alexandru Ardelean
  2020-04-26  7:38 ` [PATCH v5 1/6] iio: buffer: add back-ref from iio_buffer to iio_dev Alexandru Ardelean
                   ` (5 more replies)
  0 siblings, 6 replies; 14+ messages in thread
From: Alexandru Ardelean @ 2020-04-26  7:38 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: lars, jic23, pmeerw, Alexandru Ardelean

The main intent is to be able to add more chardevs per IIO device, one
for each buffer. To get there, some rework is needed.
This tries to re-organize the init of the chardev.

Changelog v4 -> v5:
- dropped patch 'iio: Use an early return in iio_device_alloc to simplify code.'
  is applied upstream

Changelog v3 -> v4:
- added patch [1] 'iio: Use an early return in iio_device_alloc to simplify code.'
  it's main purpose is so that this patch applies:
     [2]'iio: core: add simple centralized mechanism for ioctl() handlers'
  depending on the final version of patch [1], patch [2] needs some
  minor fixup
- added patch 'iio: core,buffer: wrap iio_buffer_put() call into iio_buffers_put()'
- patch 'iio: core: register buffer fileops only if buffer present'
  is now: 'iio: core: register chardev only if needed'
- dropped 'iio: buffer: move sysfs alloc/free in industrialio-buffer.c'
  it's likely we won't be doing this patch anymore
- patches:
    'iio: buffer: move iio buffer chrdev in industrialio-buffer.c'
    'iio: event: move event-only chardev in industrialio-event.c'
  have been merged into 'iio: buffer,event: duplicate chardev creation for buffers & events'
  since now, the logic is a bit different, and 'indio_dev->chrdev' is
  now a reference to either the buffer's chrdev & or the events-only
  chrdev
- added simple mechanism to register ioctl() handlers for IIO device
  which is currently used only by events mechanism

Changelog v2 -> v3:
* removed double init in
  'iio: event: move event-only chardev in industrialio-event.c'

Changelog v1 -> v2:
* re-reviewed some exit-paths and cleanup some potential leaks on those
  exit paths:
  - for 'iio: buffer: move iio buffer chrdev in industrialio-buffer.c'
    add iio_device_buffers_put() helper and calling iio_buffers_uninit()
    on device un-regsiter
  - for 'move sysfs alloc/free in industrialio-buffer.c'
    call 'iio_buffer_free_sysfs_and_mask()' on exit path if
    cdev_device_add() fails
  - for 'move event-only chardev in industrialio-event.c'
    check if event_interface is NULL in
    iio_device_unregister_event_chrdev()

Alexandru Ardelean (6):
  iio: buffer: add back-ref from iio_buffer to iio_dev
  iio: core,buffer: wrap iio_buffer_put() call into iio_buffers_put()
  iio: core: register chardev only if needed
  iio: buffer,event: duplicate chardev creation for buffers & events
  iio: core: add simple centralized mechanism for ioctl() handlers
  iio: core: use new common ioctl() mechanism

 drivers/iio/iio_core.h            |  29 ++++++---
 drivers/iio/industrialio-buffer.c | 102 +++++++++++++++++++++++++++--
 drivers/iio/industrialio-core.c   | 105 +++++++++++-------------------
 drivers/iio/industrialio-event.c  | 100 +++++++++++++++++++++++++++-
 include/linux/iio/buffer_impl.h   |  10 +++
 include/linux/iio/iio.h           |   8 +--
 6 files changed, 267 insertions(+), 87 deletions(-)

-- 
2.17.1


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

* [PATCH v5 1/6] iio: buffer: add back-ref from iio_buffer to iio_dev
  2020-04-26  7:38 [PATCH v5 0/6] iio: core,buffer: re-organize chardev creation Alexandru Ardelean
@ 2020-04-26  7:38 ` Alexandru Ardelean
  2020-04-26  7:38 ` [PATCH v5 2/6] iio: core,buffer: wrap iio_buffer_put() call into iio_buffers_put() Alexandru Ardelean
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Alexandru Ardelean @ 2020-04-26  7:38 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: lars, jic23, pmeerw, Alexandru Ardelean

An IIO device will have multiple buffers, but it shouldn't be allowed that
an IIO buffer should belong to more than 1 IIO device.

Once things get moved more from IIO device to the IIO buffer, and an IIO
device will be able to have more than 1 buffer attached, there will be a
need for a back-ref to the IIO device [from the IIO buffer].

This change adds that.

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

diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index eae39eaf49af..bc5050191f51 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1477,5 +1477,7 @@ void iio_device_attach_buffer(struct iio_dev *indio_dev,
 			      struct iio_buffer *buffer)
 {
 	indio_dev->buffer = iio_buffer_get(buffer);
+
+	indio_dev->buffer->indio_dev = indio_dev;
 }
 EXPORT_SYMBOL_GPL(iio_device_attach_buffer);
diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index a63dc07b7350..67d73d465e02 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -69,6 +69,9 @@ struct iio_buffer_access_funcs {
  * those writing new buffer implementations.
  */
 struct iio_buffer {
+	/** @indio_dev: IIO device to which this buffer belongs to. */
+	struct iio_dev *indio_dev;
+
 	/** @length: Number of datums in buffer. */
 	unsigned int length;
 
-- 
2.17.1


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

* [PATCH v5 2/6] iio: core,buffer: wrap iio_buffer_put() call into iio_buffers_put()
  2020-04-26  7:38 [PATCH v5 0/6] iio: core,buffer: re-organize chardev creation Alexandru Ardelean
  2020-04-26  7:38 ` [PATCH v5 1/6] iio: buffer: add back-ref from iio_buffer to iio_dev Alexandru Ardelean
@ 2020-04-26  7:38 ` Alexandru Ardelean
  2020-04-26  7:38 ` [PATCH v5 3/6] iio: core: register chardev only if needed Alexandru Ardelean
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Alexandru Ardelean @ 2020-04-26  7:38 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: lars, jic23, pmeerw, Alexandru Ardelean

The name (and the wrapper) seems superfluous now, but when more buffers
will be attached to the IIO device this will be a bit more useful.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/iio/iio_core.h            |  4 ++++
 drivers/iio/industrialio-buffer.c | 10 ++++++++++
 drivers/iio/industrialio-core.c   |  2 +-
 3 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index fd9a5f1d5e51..39ec0344fb68 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -51,6 +51,8 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
 #define iio_buffer_poll_addr (&iio_buffer_poll)
 #define iio_buffer_read_outer_addr (&iio_buffer_read_outer)
 
+void iio_device_buffers_put(struct iio_dev *indio_dev);
+
 void iio_disable_all_buffers(struct iio_dev *indio_dev);
 void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
 
@@ -66,6 +68,8 @@ static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
 
 static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {}
 
+static inline void iio_device_buffers_put(struct iio_dev *indio_dev) {}
+
 static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {}
 static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}
 
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index bc5050191f51..a66d3fbc2905 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1355,6 +1355,16 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
 	iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
 }
 
+void iio_device_buffers_put(struct iio_dev *indio_dev)
+{
+	struct iio_buffer *buffer = indio_dev->buffer;
+
+	if (!buffer)
+		return;
+
+	iio_buffer_put(buffer);
+}
+
 /**
  * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected
  * @indio_dev: the iio device
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 462d3e810013..32c489139cd2 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1481,7 +1481,7 @@ static void iio_dev_release(struct device *device)
 	iio_device_unregister_eventset(indio_dev);
 	iio_device_unregister_sysfs(indio_dev);
 
-	iio_buffer_put(indio_dev->buffer);
+	iio_device_buffers_put(indio_dev);
 
 	ida_simple_remove(&iio_ida, indio_dev->id);
 	kfree(indio_dev);
-- 
2.17.1


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

* [PATCH v5 3/6] iio: core: register chardev only if needed
  2020-04-26  7:38 [PATCH v5 0/6] iio: core,buffer: re-organize chardev creation Alexandru Ardelean
  2020-04-26  7:38 ` [PATCH v5 1/6] iio: buffer: add back-ref from iio_buffer to iio_dev Alexandru Ardelean
  2020-04-26  7:38 ` [PATCH v5 2/6] iio: core,buffer: wrap iio_buffer_put() call into iio_buffers_put() Alexandru Ardelean
@ 2020-04-26  7:38 ` Alexandru Ardelean
  2020-04-26  7:38 ` [PATCH v5 4/6] iio: buffer,event: duplicate chardev creation for buffers & events Alexandru Ardelean
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 14+ messages in thread
From: Alexandru Ardelean @ 2020-04-26  7:38 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: lars, jic23, pmeerw, Alexandru Ardelean

The final intent is to localize all buffer ops into the
industrialio-buffer.c file, to be able to add support for multiple buffers
per IIO device.

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.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/iio/industrialio-core.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 32c489139cd2..a65022396329 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1682,6 +1682,15 @@ static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
 
 static const struct iio_buffer_setup_ops noop_ring_setup_ops;
 
+static const struct file_operations iio_event_fileops = {
+	.release = iio_chrdev_release,
+	.open = iio_chrdev_open,
+	.owner = THIS_MODULE,
+	.llseek = noop_llseek,
+	.unlocked_ioctl = iio_ioctl,
+	.compat_ioctl = compat_ptr_ioctl,
+};
+
 int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
 {
 	int ret;
@@ -1732,11 +1741,18 @@ 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 (indio_dev->event_interface)
+		cdev_init(&indio_dev->chrdev, &iio_event_fileops);
 
 	indio_dev->chrdev.owner = this_mod;
 
-	ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
+	if (indio_dev->buffer || indio_dev->event_interface)
+		ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
+	else
+		ret = device_add(&indio_dev->dev);
+
 	if (ret < 0)
 		goto error_unreg_eventset;
 
@@ -1760,7 +1776,10 @@ EXPORT_SYMBOL(__iio_device_register);
  **/
 void iio_device_unregister(struct iio_dev *indio_dev)
 {
-	cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
+	if (indio_dev->buffer || indio_dev->event_interface)
+		cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
+	else
+		device_del(&indio_dev->dev);
 
 	mutex_lock(&indio_dev->info_exist_lock);
 
-- 
2.17.1


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

* [PATCH v5 4/6] iio: buffer,event: duplicate chardev creation for buffers & events
  2020-04-26  7:38 [PATCH v5 0/6] iio: core,buffer: re-organize chardev creation Alexandru Ardelean
                   ` (2 preceding siblings ...)
  2020-04-26  7:38 ` [PATCH v5 3/6] iio: core: register chardev only if needed Alexandru Ardelean
@ 2020-04-26  7:38 ` Alexandru Ardelean
  2020-04-26 10:00   ` Jonathan Cameron
  2020-04-26  7:38 ` [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers Alexandru Ardelean
  2020-04-26  7:38 ` [PATCH v5 6/6] iio: core: use new common ioctl() mechanism Alexandru Ardelean
  5 siblings, 1 reply; 14+ messages in thread
From: Alexandru Ardelean @ 2020-04-26  7:38 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: lars, jic23, pmeerw, Alexandru Ardelean

This patch moves the chardev creation into industrialio-buffer.c &
industrialio-event.c. The move has to be in a single step (which makes the
patch a bit big), in order to pass the reference of the chardev to
'indio_dev->chrdev'.

For structure purposes, industrialio-core.c should be the place where
cdev_device_add()/device_add() gets called, and the deletion function as
well.

What happens after this patch is:
- 'indio_dev->chrdev' is converted to a pointer
- if there is an IIO buffer, iio_device_buffer_attach_chrdev() will attach
  it's chardev to 'indio_chrdev'
- if it doesn't, the event interface will attach a reference to it's
  chardev (if it is instantiated) via iio_device_event_attach_chrdev()

That way, the control of the 'legacy' chardev is still visible in
'industrialio-core.c'. So, each logic file (for buffers & events) shouldn't
hide things too much away from the core file.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/iio/iio_core.h            |  16 ++---
 drivers/iio/industrialio-buffer.c |  90 +++++++++++++++++++++++--
 drivers/iio/industrialio-core.c   | 105 ++++--------------------------
 drivers/iio/industrialio-event.c  |  98 +++++++++++++++++++++++++++-
 include/linux/iio/buffer_impl.h   |   7 ++
 include/linux/iio/iio.h           |   6 +-
 6 files changed, 208 insertions(+), 114 deletions(-)

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index 39ec0344fb68..a527a66be9e5 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -40,17 +40,14 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);
 #ifdef CONFIG_IIO_BUFFER
 struct poll_table_struct;
 
-__poll_t iio_buffer_poll(struct file *filp,
-			     struct poll_table_struct *wait);
-ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
-			      size_t n, loff_t *f_ps);
+long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
+			    unsigned int cmd, unsigned long arg);
+
+void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev);
 
 int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
 void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
 
-#define iio_buffer_poll_addr (&iio_buffer_poll)
-#define iio_buffer_read_outer_addr (&iio_buffer_read_outer)
-
 void iio_device_buffers_put(struct iio_dev *indio_dev);
 
 void iio_disable_all_buffers(struct iio_dev *indio_dev);
@@ -58,8 +55,7 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
 
 #else
 
-#define iio_buffer_poll_addr NULL
-#define iio_buffer_read_outer_addr NULL
+static inline void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev) {}
 
 static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
 {
@@ -77,8 +73,8 @@ static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}
 
 int iio_device_register_eventset(struct iio_dev *indio_dev);
 void iio_device_unregister_eventset(struct iio_dev *indio_dev);
+void iio_device_event_attach_chrdev(struct iio_dev *indio_dev);
 void iio_device_wakeup_eventset(struct iio_dev *indio_dev);
-int iio_event_getfd(struct iio_dev *indio_dev);
 
 struct iio_event_interface;
 bool iio_event_enabled(const struct iio_event_interface *ev_int);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index a66d3fbc2905..f5a975079bf4 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -99,11 +99,11 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
  * Return: negative values corresponding to error codes or ret != 0
  *	   for ending the reading activity
  **/
-ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
-			      size_t n, loff_t *f_ps)
+static ssize_t iio_buffer_read_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_buffer *rb = filp->private_data;
+	struct iio_dev *indio_dev = rb->indio_dev;
 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
 	size_t datum_size;
 	size_t to_wait;
@@ -165,11 +165,11 @@ ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
  * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading
  *	   or 0 for other cases
  */
-__poll_t iio_buffer_poll(struct file *filp,
+static __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_buffer *rb = filp->private_data;
+	struct iio_dev *indio_dev = rb->indio_dev;
 
 	if (!indio_dev->info || rb == NULL)
 		return 0;
@@ -180,6 +180,48 @@ __poll_t iio_buffer_poll(struct file *filp,
 	return 0;
 }
 
+/**
+ * iio_buffer_chrdev_open() - chrdev file open for buffer access
+ * @inode:	Inode structure for identifying the device in the file system
+ * @filp:	File structure for iio device used to keep and later access
+ *		private data
+ *
+ * Return: 0 on success or -EBUSY if the device is already opened
+ **/
+static int iio_buffer_chrdev_open(struct inode *inode, struct file *filp)
+{
+	struct iio_buffer *buffer = container_of(inode->i_cdev,
+						 struct iio_buffer, chrdev);
+
+	if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->file_ops_flags))
+		return -EBUSY;
+
+	iio_buffer_get(buffer);
+
+	filp->private_data = buffer;
+
+	return 0;
+}
+
+/**
+ * iio_buffer_chrdev_release() - chrdev file close for buffer access
+ * @inode:	Inode structure pointer for the char device
+ * @filp:	File structure pointer for the char device
+ *
+ * Return: 0 for successful release
+ */
+static int iio_buffer_chrdev_release(struct inode *inode, struct file *filp)
+{
+	struct iio_buffer *buffer = container_of(inode->i_cdev,
+						 struct iio_buffer, chrdev);
+
+	clear_bit(IIO_BUSY_BIT_POS, &buffer->file_ops_flags);
+
+	iio_buffer_put(buffer);
+
+	return 0;
+}
+
 /**
  * iio_buffer_wakeup_poll - Wakes up the buffer waitqueue
  * @indio_dev: The IIO device
@@ -1129,6 +1171,17 @@ void iio_disable_all_buffers(struct iio_dev *indio_dev)
 	iio_buffer_deactivate_all(indio_dev);
 }
 
+static long iio_buffer_ioctl(struct file *filep, unsigned int cmd,
+			     unsigned long arg)
+{
+	struct iio_buffer *buffer = filep->private_data;
+
+	if (!buffer || !buffer->access)
+		return -ENODEV;
+
+	return iio_device_event_ioctl(buffer->indio_dev, filep, cmd, arg);
+}
+
 static ssize_t iio_buffer_store_enable(struct device *dev,
 				       struct device_attribute *attr,
 				       const char *buf,
@@ -1355,6 +1408,29 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
 	iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
 }
 
+static const struct file_operations iio_buffer_fileops = {
+	.read = iio_buffer_read_outer,
+	.release = iio_buffer_chrdev_release,
+	.open = iio_buffer_chrdev_open,
+	.poll = iio_buffer_poll,
+	.owner = THIS_MODULE,
+	.llseek = noop_llseek,
+	.unlocked_ioctl = iio_buffer_ioctl,
+	.compat_ioctl = compat_ptr_ioctl,
+};
+
+void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev)
+{
+	struct iio_buffer *buffer = indio_dev->buffer;
+
+	if (!buffer)
+		return;
+
+	cdev_init(&buffer->chrdev, &iio_buffer_fileops);
+
+	indio_dev->chrdev = &buffer->chrdev;
+}
+
 void iio_device_buffers_put(struct iio_dev *indio_dev)
 {
 	struct iio_buffer *buffer = indio_dev->buffer;
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index a65022396329..aec585cc8453 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1584,79 +1584,6 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
 }
 EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
 
-/**
- * iio_chrdev_open() - chrdev file open for buffer access and ioctls
- * @inode:	Inode structure for identifying the device in the file system
- * @filp:	File structure for iio device used to keep and later access
- *		private data
- *
- * Return: 0 on success or -EBUSY if the device is already opened
- **/
-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);
-
-	if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags))
-		return -EBUSY;
-
-	iio_device_get(indio_dev);
-
-	filp->private_data = indio_dev;
-
-	return 0;
-}
-
-/**
- * iio_chrdev_release() - chrdev file close buffer access and ioctls
- * @inode:	Inode structure pointer for the char device
- * @filp:	File structure pointer for the char device
- *
- * Return: 0 for successful release
- */
-static int iio_chrdev_release(struct inode *inode, struct file *filp)
-{
-	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);
-
-	return 0;
-}
-
-/* Somewhat of a cross file organization violation - ioctls here are actually
- * event related */
-static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
-	struct iio_dev *indio_dev = filp->private_data;
-	int __user *ip = (int __user *)arg;
-	int fd;
-
-	if (!indio_dev->info)
-		return -ENODEV;
-
-	if (cmd == IIO_GET_EVENT_FD_IOCTL) {
-		fd = iio_event_getfd(indio_dev);
-		if (fd < 0)
-			return fd;
-		if (copy_to_user(ip, &fd, sizeof(fd)))
-			return -EFAULT;
-		return 0;
-	}
-	return -EINVAL;
-}
-
-static const struct file_operations iio_buffer_fileops = {
-	.read = iio_buffer_read_outer_addr,
-	.release = iio_chrdev_release,
-	.open = iio_chrdev_open,
-	.poll = iio_buffer_poll_addr,
-	.owner = THIS_MODULE,
-	.llseek = noop_llseek,
-	.unlocked_ioctl = iio_ioctl,
-	.compat_ioctl = compat_ptr_ioctl,
-};
-
 static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
 {
 	int i, j;
@@ -1682,15 +1609,6 @@ static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
 
 static const struct iio_buffer_setup_ops noop_ring_setup_ops;
 
-static const struct file_operations iio_event_fileops = {
-	.release = iio_chrdev_release,
-	.open = iio_chrdev_open,
-	.owner = THIS_MODULE,
-	.llseek = noop_llseek,
-	.unlocked_ioctl = iio_ioctl,
-	.compat_ioctl = compat_ptr_ioctl,
-};
-
 int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
 {
 	int ret;
@@ -1741,16 +1659,17 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
 		indio_dev->setup_ops == NULL)
 		indio_dev->setup_ops = &noop_ring_setup_ops;
 
-	if (indio_dev->buffer)
-		cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
-	else if (indio_dev->event_interface)
-		cdev_init(&indio_dev->chrdev, &iio_event_fileops);
+	iio_device_buffer_attach_chrdev(indio_dev);
 
-	indio_dev->chrdev.owner = this_mod;
+	/* no chrdev attached from buffer, we go with event-only chrdev */
+	if (!indio_dev->chrdev)
+		iio_device_event_attach_chrdev(indio_dev);
 
-	if (indio_dev->buffer || indio_dev->event_interface)
-		ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
-	else
+	if (indio_dev->chrdev) {
+		indio_dev->chrdev->owner = this_mod;
+
+		ret = cdev_device_add(indio_dev->chrdev, &indio_dev->dev);
+	} else
 		ret = device_add(&indio_dev->dev);
 
 	if (ret < 0)
@@ -1776,13 +1695,15 @@ EXPORT_SYMBOL(__iio_device_register);
  **/
 void iio_device_unregister(struct iio_dev *indio_dev)
 {
-	if (indio_dev->buffer || indio_dev->event_interface)
-		cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
+	if (indio_dev->chrdev)
+		cdev_device_del(indio_dev->chrdev, &indio_dev->dev);
 	else
 		device_del(&indio_dev->dev);
 
 	mutex_lock(&indio_dev->info_exist_lock);
 
+	indio_dev->chrdev = NULL;
+
 	iio_device_unregister_debugfs(indio_dev);
 
 	iio_disable_all_buffers(indio_dev);
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 5b17c92d3b50..0674b2117c98 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -30,6 +30,8 @@
  * @flags:		file operations related flags including busy flag.
  * @group:		event interface sysfs attribute group
  * @read_lock:		lock to protect kfifo read operations
+ * @chrdev:		associated chardev for this event
+ * @indio_dev:		IIO device to which this event interface belongs to
  */
 struct iio_event_interface {
 	wait_queue_head_t	wait;
@@ -39,6 +41,9 @@ struct iio_event_interface {
 	unsigned long		flags;
 	struct attribute_group	group;
 	struct mutex		read_lock;
+
+	struct cdev		chrdev;
+	struct iio_dev		*indio_dev;
 };
 
 bool iio_event_enabled(const struct iio_event_interface *ev_int)
@@ -182,7 +187,7 @@ static const struct file_operations iio_event_chrdev_fileops = {
 	.llseek = noop_llseek,
 };
 
-int iio_event_getfd(struct iio_dev *indio_dev)
+static int iio_event_getfd(struct iio_dev *indio_dev)
 {
 	struct iio_event_interface *ev_int = indio_dev->event_interface;
 	int fd;
@@ -215,6 +220,97 @@ int iio_event_getfd(struct iio_dev *indio_dev)
 	return fd;
 }
 
+/**
+ * iio_chrdev_open() - chrdev file open for event ioctls
+ * @inode:	Inode structure for identifying the device in the file system
+ * @filp:	File structure for iio device used to keep and later access
+ *		private data
+ *
+ * Return: 0 on success or -EBUSY if the device is already opened
+ **/
+static int iio_chrdev_open(struct inode *inode, struct file *filp)
+{
+	struct iio_event_interface *ev =
+		container_of(inode->i_cdev, struct iio_event_interface, chrdev);
+
+	if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev->flags))
+		return -EBUSY;
+
+	iio_device_get(ev->indio_dev);
+
+	filp->private_data = ev;
+
+	return 0;
+}
+
+/**
+ * iio_chrdev_release() - chrdev file close for event ioctls
+ * @inode:	Inode structure pointer for the char device
+ * @filp:	File structure pointer for the char device
+ *
+ * Return: 0 for successful release
+ */
+static int iio_chrdev_release(struct inode *inode, struct file *filp)
+{
+	struct iio_event_interface *ev =
+		container_of(inode->i_cdev, struct iio_event_interface, chrdev);
+
+	clear_bit(IIO_BUSY_BIT_POS, &ev->flags);
+	iio_device_put(ev->indio_dev);
+
+	return 0;
+}
+
+long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
+			    unsigned int cmd, unsigned long arg)
+{
+	int __user *ip = (int __user *)arg;
+	int fd;
+
+	if (!indio_dev->info)
+		return -ENODEV;
+
+	if (cmd == IIO_GET_EVENT_FD_IOCTL) {
+		fd = iio_event_getfd(indio_dev);
+		if (fd < 0)
+			return fd;
+		if (copy_to_user(ip, &fd, sizeof(fd)))
+			return -EFAULT;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static long iio_event_ioctl_wrapper(struct file *filp, unsigned int cmd,
+				    unsigned long arg)
+{
+	struct iio_event_interface *ev = filp->private_data;
+
+	return iio_device_event_ioctl(ev->indio_dev, filp, cmd, arg);
+}
+
+static const struct file_operations iio_event_fileops = {
+	.release = iio_chrdev_release,
+	.open = iio_chrdev_open,
+	.owner = THIS_MODULE,
+	.llseek = noop_llseek,
+	.unlocked_ioctl = iio_event_ioctl_wrapper,
+	.compat_ioctl = compat_ptr_ioctl,
+};
+
+void iio_device_event_attach_chrdev(struct iio_dev *indio_dev)
+{
+	struct iio_event_interface *ev = indio_dev->event_interface;
+
+	if (!ev)
+		return;
+
+	cdev_init(&ev->chrdev, &iio_event_fileops);
+
+	ev->indio_dev = indio_dev;
+	indio_dev->chrdev = &ev->chrdev;
+}
+
 static const char * const iio_ev_type_text[] = {
 	[IIO_EV_TYPE_THRESH] = "thresh",
 	[IIO_EV_TYPE_MAG] = "mag",
diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
index 67d73d465e02..46fc977deae3 100644
--- a/include/linux/iio/buffer_impl.h
+++ b/include/linux/iio/buffer_impl.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #ifndef _IIO_BUFFER_GENERIC_IMPL_H_
 #define _IIO_BUFFER_GENERIC_IMPL_H_
+#include <linux/cdev.h>
 #include <linux/sysfs.h>
 #include <linux/kref.h>
 
@@ -72,6 +73,12 @@ struct iio_buffer {
 	/** @indio_dev: IIO device to which this buffer belongs to. */
 	struct iio_dev *indio_dev;
 
+	/** @chrdev: associated character device. */
+	struct cdev chrdev;
+
+	/** @file_ops_flags: file ops related flags including busy flag. */
+	unsigned long file_ops_flags;
+
 	/** @length: Number of datums in buffer. */
 	unsigned int length;
 
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 5f9f439a4f01..52992be44e9e 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -516,10 +516,9 @@ struct iio_buffer_setup_ops {
  * @info_exist_lock:	[INTERN] lock to prevent use during removal
  * @setup_ops:		[DRIVER] callbacks to call before and after buffer
  *			enable/disable
- * @chrdev:		[INTERN] associated character device
+ * @chrdev:		[INTERN] reference to associated character device
  * @groups:		[INTERN] attribute groups
  * @groupcounter:	[INTERN] index of next attribute group
- * @flags:		[INTERN] file ops related flags including busy flag.
  * @debugfs_dentry:	[INTERN] device specific debugfs dentry.
  * @cached_reg_addr:	[INTERN] cached register address for debugfs reads.
  */
@@ -559,12 +558,11 @@ struct iio_dev {
 	clockid_t			clock_id;
 	struct mutex			info_exist_lock;
 	const struct iio_buffer_setup_ops	*setup_ops;
-	struct cdev			chrdev;
+	struct cdev			*chrdev;
 #define IIO_MAX_GROUPS 6
 	const struct attribute_group	*groups[IIO_MAX_GROUPS + 1];
 	int				groupcounter;
 
-	unsigned long			flags;
 #if defined(CONFIG_DEBUG_FS)
 	struct dentry			*debugfs_dentry;
 	unsigned			cached_reg_addr;
-- 
2.17.1


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

* [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers
  2020-04-26  7:38 [PATCH v5 0/6] iio: core,buffer: re-organize chardev creation Alexandru Ardelean
                   ` (3 preceding siblings ...)
  2020-04-26  7:38 ` [PATCH v5 4/6] iio: buffer,event: duplicate chardev creation for buffers & events Alexandru Ardelean
@ 2020-04-26  7:38 ` Alexandru Ardelean
  2020-04-26 10:10   ` Jonathan Cameron
  2020-04-26  7:38 ` [PATCH v5 6/6] iio: core: use new common ioctl() mechanism Alexandru Ardelean
  5 siblings, 1 reply; 14+ messages in thread
From: Alexandru Ardelean @ 2020-04-26  7:38 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: lars, jic23, pmeerw, Alexandru Ardelean

The aim of this is to reduce the organization violation of ioctl() calls in
IIO core. Currently, since the chardev is split across files, event ioctl()
calls need to be called in buffer ioctl() calls.

The 'industrialio-core.c' file will provide a 'iio_device_ioctl()' which
will iterate over a list of ioctls registered with the IIO device. These
can be event ioctl() or buffer ioctl() calls, or something else.
This is needed, since there is currently one chardev per IIO device and
that is used for both event handling and reading from the buffer.

Each ioctl() will have to return a IIO_IOCTL_UNHANDLED code (which is
positive 1), if the ioctl() did not handle the call in any. This eliminates
any potential ambiguities; if we were to have used error codes it would
have been uncertain whether they were actual errors, or whether
the registered ioctl() doesn't service the command.

If any ioctl() returns 0, it was considered that it was serviced
successfully and the loop will exit.

One assumption for all registered ioctl() handlers is that they are
statically allocated, so the iio_device_unregister() which just remove all
of them from the device's ioctl() handler list.

Also, something that is a bit hard to do [at this point] and may not be
worth the effort of doing, is to check whether registered ioctl()
calls/commands overlap. This should be unlikely to happen, and should get
caught at review time. Though, new ioctl() calls would likely not be added
too often.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/iio/iio_core.h          | 14 ++++++++++++++
 drivers/iio/industrialio-core.c | 33 +++++++++++++++++++++++++++++++++
 include/linux/iio/iio.h         |  2 ++
 3 files changed, 49 insertions(+)

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index a527a66be9e5..34c3e19229d8 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -17,6 +17,20 @@ struct iio_dev;
 
 extern struct device_type iio_device_type;
 
+#define IIO_IOCTL_UNHANDLED	1
+struct iio_ioctl_handler {
+	struct list_head entry;
+	long (*ioctl)(struct iio_dev *indio_dev, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+};
+
+long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
+		      unsigned int cmd, unsigned long arg);
+
+void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
+				       struct iio_ioctl_handler *h);
+void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h);
+
 int __iio_add_chan_devattr(const char *postfix,
 			   struct iio_chan_spec const *chan,
 			   ssize_t (*func)(struct device *dev,
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index aec585cc8453..79e8fa8ff70b 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1531,6 +1531,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)
 	}
 	dev_set_name(&dev->dev, "iio:device%d", dev->id);
 	INIT_LIST_HEAD(&dev->buffer_list);
+	INIT_LIST_HEAD(&dev->ioctl_handlers);
 
 	return dev;
 }
@@ -1584,6 +1585,33 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
 }
 EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
 
+void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
+				       struct iio_ioctl_handler *h)
+{
+	/* this assumes that all ioctl() handlers are statically allocated */
+	list_add_tail(&h->entry, &indio_dev->ioctl_handlers);
+}
+
+long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
+		      unsigned int cmd, unsigned long arg)
+{
+	struct iio_ioctl_handler *h;
+	int ret;
+
+	if (!indio_dev->info)
+		return -ENODEV;
+
+	list_for_each_entry(h, &indio_dev->ioctl_handlers, entry) {
+		ret = h->ioctl(indio_dev, filp, cmd, arg);
+		if (ret == 0)
+			return 0;
+		if (ret != IIO_IOCTL_UNHANDLED)
+			return ret;
+	}
+
+	return -EINVAL;
+}
+
 static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
 {
 	int i, j;
@@ -1695,6 +1723,8 @@ EXPORT_SYMBOL(__iio_device_register);
  **/
 void iio_device_unregister(struct iio_dev *indio_dev)
 {
+	struct iio_ioctl_handler *h, *t;
+
 	if (indio_dev->chrdev)
 		cdev_device_del(indio_dev->chrdev, &indio_dev->dev);
 	else
@@ -1708,6 +1738,9 @@ void iio_device_unregister(struct iio_dev *indio_dev)
 
 	iio_disable_all_buffers(indio_dev);
 
+	list_for_each_entry_safe(h, t, &indio_dev->ioctl_handlers, entry)
+		list_del(&h->entry);
+
 	indio_dev->info = NULL;
 
 	iio_device_wakeup_eventset(indio_dev);
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 52992be44e9e..b6ca8d85629e 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -488,6 +488,7 @@ struct iio_buffer_setup_ops {
  * @currentmode:	[DRIVER] current operating mode
  * @dev:		[DRIVER] device structure, should be assigned a parent
  *			and owner
+ * @ioctl_handlers:	[INTERN] list of registered ioctl handlers
  * @event_interface:	[INTERN] event chrdevs associated with interrupt lines
  * @buffer:		[DRIVER] any buffer present
  * @buffer_list:	[INTERN] list of all buffers currently attached
@@ -529,6 +530,7 @@ struct iio_dev {
 	int				modes;
 	int				currentmode;
 	struct device			dev;
+	struct list_head		ioctl_handlers;
 
 	struct iio_event_interface	*event_interface;
 
-- 
2.17.1


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

* [PATCH v5 6/6] iio: core: use new common ioctl() mechanism
  2020-04-26  7:38 [PATCH v5 0/6] iio: core,buffer: re-organize chardev creation Alexandru Ardelean
                   ` (4 preceding siblings ...)
  2020-04-26  7:38 ` [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers Alexandru Ardelean
@ 2020-04-26  7:38 ` Alexandru Ardelean
  2020-04-26 10:13   ` Jonathan Cameron
  5 siblings, 1 reply; 14+ messages in thread
From: Alexandru Ardelean @ 2020-04-26  7:38 UTC (permalink / raw)
  To: linux-iio, linux-kernel; +Cc: lars, jic23, pmeerw, Alexandru Ardelean

This change makes use of the new centralized ioctl() mechanism. The event
interface registers it's ioctl() handler to IIO device.
Both the buffer & event interface call 'iio_device_ioctl()', which should
take care of all of indio_dev's ioctl() calls.

Later, we may add per-buffer ioctl() calls, and since each buffer will get
it's own chardev, the buffer ioctl() handler will need a bit of tweaking
for the first/legacy buffer (i.e. indio_dev->buffer).
Also, those per-buffer ioctl() calls will not be registered with this
mechanism.

Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
---
 drivers/iio/iio_core.h            |  3 ---
 drivers/iio/industrialio-buffer.c |  2 +-
 drivers/iio/industrialio-event.c  | 14 ++++++++------
 3 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index 34c3e19229d8..f68de4af2738 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -54,9 +54,6 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);
 #ifdef CONFIG_IIO_BUFFER
 struct poll_table_struct;
 
-long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
-			    unsigned int cmd, unsigned long arg);
-
 void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev);
 
 int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index f5a975079bf4..43281c77ca22 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1179,7 +1179,7 @@ static long iio_buffer_ioctl(struct file *filep, unsigned int cmd,
 	if (!buffer || !buffer->access)
 		return -ENODEV;
 
-	return iio_device_event_ioctl(buffer->indio_dev, filep, cmd, arg);
+	return iio_device_ioctl(buffer->indio_dev, filep, cmd, arg);
 }
 
 static ssize_t iio_buffer_store_enable(struct device *dev,
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 0674b2117c98..1961c1d19370 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -32,6 +32,7 @@
  * @read_lock:		lock to protect kfifo read operations
  * @chrdev:		associated chardev for this event
  * @indio_dev:		IIO device to which this event interface belongs to
+ * @ioctl_handler:	handler for event ioctl() calls
  */
 struct iio_event_interface {
 	wait_queue_head_t	wait;
@@ -44,6 +45,7 @@ struct iio_event_interface {
 
 	struct cdev		chrdev;
 	struct iio_dev		*indio_dev;
+	struct iio_ioctl_handler	ioctl_handler;
 };
 
 bool iio_event_enabled(const struct iio_event_interface *ev_int)
@@ -261,15 +263,12 @@ static int iio_chrdev_release(struct inode *inode, struct file *filp)
 	return 0;
 }
 
-long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
+static long iio_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
 			    unsigned int cmd, unsigned long arg)
 {
 	int __user *ip = (int __user *)arg;
 	int fd;
 
-	if (!indio_dev->info)
-		return -ENODEV;
-
 	if (cmd == IIO_GET_EVENT_FD_IOCTL) {
 		fd = iio_event_getfd(indio_dev);
 		if (fd < 0)
@@ -278,7 +277,7 @@ long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
 			return -EFAULT;
 		return 0;
 	}
-	return -EINVAL;
+	return IIO_IOCTL_UNHANDLED;
 }
 
 static long iio_event_ioctl_wrapper(struct file *filp, unsigned int cmd,
@@ -286,7 +285,7 @@ static long iio_event_ioctl_wrapper(struct file *filp, unsigned int cmd,
 {
 	struct iio_event_interface *ev = filp->private_data;
 
-	return iio_device_event_ioctl(ev->indio_dev, filp, cmd, arg);
+	return iio_device_ioctl(ev->indio_dev, filp, cmd, arg);
 }
 
 static const struct file_operations iio_event_fileops = {
@@ -308,7 +307,10 @@ void iio_device_event_attach_chrdev(struct iio_dev *indio_dev)
 	cdev_init(&ev->chrdev, &iio_event_fileops);
 
 	ev->indio_dev = indio_dev;
+	ev->ioctl_handler.ioctl = iio_event_ioctl;
 	indio_dev->chrdev = &ev->chrdev;
+
+	iio_device_ioctl_handler_register(indio_dev, &ev->ioctl_handler);
 }
 
 static const char * const iio_ev_type_text[] = {
-- 
2.17.1


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

* Re: [PATCH v5 4/6] iio: buffer,event: duplicate chardev creation for buffers & events
  2020-04-26  7:38 ` [PATCH v5 4/6] iio: buffer,event: duplicate chardev creation for buffers & events Alexandru Ardelean
@ 2020-04-26 10:00   ` Jonathan Cameron
  2020-04-27  6:38     ` Ardelean, Alexandru
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Cameron @ 2020-04-26 10:00 UTC (permalink / raw)
  To: Alexandru Ardelean; +Cc: linux-iio, linux-kernel, lars, pmeerw

On Sun, 26 Apr 2020 10:38:15 +0300
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

> This patch moves the chardev creation into industrialio-buffer.c &
> industrialio-event.c. The move has to be in a single step (which makes the
> patch a bit big), in order to pass the reference of the chardev to
> 'indio_dev->chrdev'.
> 
> For structure purposes, industrialio-core.c should be the place where
> cdev_device_add()/device_add() gets called, and the deletion function as
> well.
> 
> What happens after this patch is:
> - 'indio_dev->chrdev' is converted to a pointer
> - if there is an IIO buffer, iio_device_buffer_attach_chrdev() will attach
>   it's chardev to 'indio_chrdev'
> - if it doesn't, the event interface will attach a reference to it's
>   chardev (if it is instantiated) via iio_device_event_attach_chrdev()
> 
> That way, the control of the 'legacy' chardev is still visible in
> 'industrialio-core.c'. So, each logic file (for buffers & events) shouldn't
> hide things too much away from the core file.
> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>

A few really minor suggestions inline. At least half of them are tidying up
things whilst we are here that were ugly in the code we are duplicating.

Looks good to me. However, I'd like more eyes on this set before I apply it
so will give it at least a week or two on the list!

Jonathan

> ---
>  drivers/iio/iio_core.h            |  16 ++---
>  drivers/iio/industrialio-buffer.c |  90 +++++++++++++++++++++++--
>  drivers/iio/industrialio-core.c   | 105 ++++--------------------------
>  drivers/iio/industrialio-event.c  |  98 +++++++++++++++++++++++++++-
>  include/linux/iio/buffer_impl.h   |   7 ++
>  include/linux/iio/iio.h           |   6 +-
>  6 files changed, 208 insertions(+), 114 deletions(-)
> 
> diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
> index 39ec0344fb68..a527a66be9e5 100644
> --- a/drivers/iio/iio_core.h
> +++ b/drivers/iio/iio_core.h
> @@ -40,17 +40,14 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);
>  #ifdef CONFIG_IIO_BUFFER
>  struct poll_table_struct;
>  
> -__poll_t iio_buffer_poll(struct file *filp,
> -			     struct poll_table_struct *wait);
> -ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
> -			      size_t n, loff_t *f_ps);
> +long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
> +			    unsigned int cmd, unsigned long arg);
> +
> +void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev);
>  
>  int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
>  void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
>  
> -#define iio_buffer_poll_addr (&iio_buffer_poll)
> -#define iio_buffer_read_outer_addr (&iio_buffer_read_outer)
> -
>  void iio_device_buffers_put(struct iio_dev *indio_dev);
>  
>  void iio_disable_all_buffers(struct iio_dev *indio_dev);
> @@ -58,8 +55,7 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
>  
>  #else
>  
> -#define iio_buffer_poll_addr NULL
> -#define iio_buffer_read_outer_addr NULL
> +static inline void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev) {}
>  
>  static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
>  {
> @@ -77,8 +73,8 @@ static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}
>  
>  int iio_device_register_eventset(struct iio_dev *indio_dev);
>  void iio_device_unregister_eventset(struct iio_dev *indio_dev);
> +void iio_device_event_attach_chrdev(struct iio_dev *indio_dev);
>  void iio_device_wakeup_eventset(struct iio_dev *indio_dev);
> -int iio_event_getfd(struct iio_dev *indio_dev);
>  
>  struct iio_event_interface;
>  bool iio_event_enabled(const struct iio_event_interface *ev_int);
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index a66d3fbc2905..f5a975079bf4 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -99,11 +99,11 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
>   * Return: negative values corresponding to error codes or ret != 0
>   *	   for ending the reading activity
>   **/
> -ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
> -			      size_t n, loff_t *f_ps)
> +static ssize_t iio_buffer_read_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_buffer *rb = filp->private_data;
> +	struct iio_dev *indio_dev = rb->indio_dev;
>  	DEFINE_WAIT_FUNC(wait, woken_wake_function);
>  	size_t datum_size;
>  	size_t to_wait;
> @@ -165,11 +165,11 @@ ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
>   * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading
>   *	   or 0 for other cases
>   */
> -__poll_t iio_buffer_poll(struct file *filp,
> +static __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_buffer *rb = filp->private_data;
> +	struct iio_dev *indio_dev = rb->indio_dev;
>  
>  	if (!indio_dev->info || rb == NULL)
>  		return 0;
> @@ -180,6 +180,48 @@ __poll_t iio_buffer_poll(struct file *filp,
>  	return 0;
>  }
>  
> +/**
> + * iio_buffer_chrdev_open() - chrdev file open for buffer access
> + * @inode:	Inode structure for identifying the device in the file system
> + * @filp:	File structure for iio device used to keep and later access
> + *		private data
> + *
> + * Return: 0 on success or -EBUSY if the device is already opened
> + **/

*/

> +static int iio_buffer_chrdev_open(struct inode *inode, struct file *filp)
> +{
> +	struct iio_buffer *buffer = container_of(inode->i_cdev,
> +						 struct iio_buffer, chrdev);
> +
> +	if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->file_ops_flags))
> +		return -EBUSY;
> +
> +	iio_buffer_get(buffer);
> +
> +	filp->private_data = buffer;
> +
> +	return 0;
> +}
> +
> +/**
> + * iio_buffer_chrdev_release() - chrdev file close for buffer access
> + * @inode:	Inode structure pointer for the char device
> + * @filp:	File structure pointer for the char device
> + *
> + * Return: 0 for successful release
> + */
> +static int iio_buffer_chrdev_release(struct inode *inode, struct file *filp)
> +{
> +	struct iio_buffer *buffer = container_of(inode->i_cdev,
> +						 struct iio_buffer, chrdev);
> +
> +	clear_bit(IIO_BUSY_BIT_POS, &buffer->file_ops_flags);
> +
> +	iio_buffer_put(buffer);
> +
> +	return 0;
> +}
> +
>  /**
>   * iio_buffer_wakeup_poll - Wakes up the buffer waitqueue
>   * @indio_dev: The IIO device
> @@ -1129,6 +1171,17 @@ void iio_disable_all_buffers(struct iio_dev *indio_dev)
>  	iio_buffer_deactivate_all(indio_dev);
>  }
>  
> +static long iio_buffer_ioctl(struct file *filep, unsigned int cmd,
> +			     unsigned long arg)
> +{
> +	struct iio_buffer *buffer = filep->private_data;
> +
> +	if (!buffer || !buffer->access)
> +		return -ENODEV;
> +
> +	return iio_device_event_ioctl(buffer->indio_dev, filep, cmd, arg);
> +}
> +
>  static ssize_t iio_buffer_store_enable(struct device *dev,
>  				       struct device_attribute *attr,
>  				       const char *buf,
> @@ -1355,6 +1408,29 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
>  	iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
>  }
>  
> +static const struct file_operations iio_buffer_fileops = {
> +	.read = iio_buffer_read_outer,
> +	.release = iio_buffer_chrdev_release,
> +	.open = iio_buffer_chrdev_open,
> +	.poll = iio_buffer_poll,
> +	.owner = THIS_MODULE,
> +	.llseek = noop_llseek,
> +	.unlocked_ioctl = iio_buffer_ioctl,
> +	.compat_ioctl = compat_ptr_ioctl,
> +};
> +
> +void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev)
> +{
> +	struct iio_buffer *buffer = indio_dev->buffer;
> +
> +	if (!buffer)
> +		return;
> +
> +	cdev_init(&buffer->chrdev, &iio_buffer_fileops);
> +
> +	indio_dev->chrdev = &buffer->chrdev;
> +}
> +
>  void iio_device_buffers_put(struct iio_dev *indio_dev)
>  {
>  	struct iio_buffer *buffer = indio_dev->buffer;
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index a65022396329..aec585cc8453 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -1584,79 +1584,6 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
>  }
>  EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
>  
> -/**
> - * iio_chrdev_open() - chrdev file open for buffer access and ioctls
> - * @inode:	Inode structure for identifying the device in the file system
> - * @filp:	File structure for iio device used to keep and later access
> - *		private data
> - *
> - * Return: 0 on success or -EBUSY if the device is already opened
> - **/
> -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);
> -
> -	if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags))
> -		return -EBUSY;
> -
> -	iio_device_get(indio_dev);
> -
> -	filp->private_data = indio_dev;
> -
> -	return 0;
> -}
> -
> -/**
> - * iio_chrdev_release() - chrdev file close buffer access and ioctls
> - * @inode:	Inode structure pointer for the char device
> - * @filp:	File structure pointer for the char device
> - *
> - * Return: 0 for successful release
> - */
> -static int iio_chrdev_release(struct inode *inode, struct file *filp)
> -{
> -	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);
> -
> -	return 0;
> -}
> -
> -/* Somewhat of a cross file organization violation - ioctls here are actually
> - * event related */
> -static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> -{
> -	struct iio_dev *indio_dev = filp->private_data;
> -	int __user *ip = (int __user *)arg;
> -	int fd;
> -
> -	if (!indio_dev->info)
> -		return -ENODEV;
> -
> -	if (cmd == IIO_GET_EVENT_FD_IOCTL) {
> -		fd = iio_event_getfd(indio_dev);
> -		if (fd < 0)
> -			return fd;
> -		if (copy_to_user(ip, &fd, sizeof(fd)))
> -			return -EFAULT;
> -		return 0;
> -	}
> -	return -EINVAL;
> -}
> -
> -static const struct file_operations iio_buffer_fileops = {
> -	.read = iio_buffer_read_outer_addr,
> -	.release = iio_chrdev_release,
> -	.open = iio_chrdev_open,
> -	.poll = iio_buffer_poll_addr,
> -	.owner = THIS_MODULE,
> -	.llseek = noop_llseek,
> -	.unlocked_ioctl = iio_ioctl,
> -	.compat_ioctl = compat_ptr_ioctl,
> -};
> -
>  static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
>  {
>  	int i, j;
> @@ -1682,15 +1609,6 @@ static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
>  
>  static const struct iio_buffer_setup_ops noop_ring_setup_ops;
>  
> -static const struct file_operations iio_event_fileops = {
> -	.release = iio_chrdev_release,
> -	.open = iio_chrdev_open,
> -	.owner = THIS_MODULE,
> -	.llseek = noop_llseek,
> -	.unlocked_ioctl = iio_ioctl,
> -	.compat_ioctl = compat_ptr_ioctl,
> -};
> -
>  int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
>  {
>  	int ret;
> @@ -1741,16 +1659,17 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
>  		indio_dev->setup_ops == NULL)
>  		indio_dev->setup_ops = &noop_ring_setup_ops;
>  
> -	if (indio_dev->buffer)
> -		cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
> -	else if (indio_dev->event_interface)
> -		cdev_init(&indio_dev->chrdev, &iio_event_fileops);
> +	iio_device_buffer_attach_chrdev(indio_dev);
>  
> -	indio_dev->chrdev.owner = this_mod;
> +	/* no chrdev attached from buffer, we go with event-only chrdev */
> +	if (!indio_dev->chrdev)
> +		iio_device_event_attach_chrdev(indio_dev);
>  
> -	if (indio_dev->buffer || indio_dev->event_interface)
> -		ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
> -	else
> +	if (indio_dev->chrdev) {
> +		indio_dev->chrdev->owner = this_mod;
> +
> +		ret = cdev_device_add(indio_dev->chrdev, &indio_dev->dev);
> +	} else
>  		ret = device_add(&indio_dev->dev);
>  
>  	if (ret < 0)
> @@ -1776,13 +1695,15 @@ EXPORT_SYMBOL(__iio_device_register);
>   **/
>  void iio_device_unregister(struct iio_dev *indio_dev)
>  {
> -	if (indio_dev->buffer || indio_dev->event_interface)
> -		cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
> +	if (indio_dev->chrdev)
> +		cdev_device_del(indio_dev->chrdev, &indio_dev->dev);
>  	else
>  		device_del(&indio_dev->dev);
>  
>  	mutex_lock(&indio_dev->info_exist_lock);
>  
> +	indio_dev->chrdev = NULL;
> +

Why? I can't immediately see the reason and I'm too lazy to figure it out
on a Sunday morning ;)

>  	iio_device_unregister_debugfs(indio_dev);
>  
>  	iio_disable_all_buffers(indio_dev);
> diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
> index 5b17c92d3b50..0674b2117c98 100644
> --- a/drivers/iio/industrialio-event.c
> +++ b/drivers/iio/industrialio-event.c
> @@ -30,6 +30,8 @@
>   * @flags:		file operations related flags including busy flag.
>   * @group:		event interface sysfs attribute group
>   * @read_lock:		lock to protect kfifo read operations
> + * @chrdev:		associated chardev for this event
> + * @indio_dev:		IIO device to which this event interface belongs to
>   */
>  struct iio_event_interface {
>  	wait_queue_head_t	wait;
> @@ -39,6 +41,9 @@ struct iio_event_interface {
>  	unsigned long		flags;
>  	struct attribute_group	group;
>  	struct mutex		read_lock;
> +
> +	struct cdev		chrdev;
> +	struct iio_dev		*indio_dev;
>  };
>  
>  bool iio_event_enabled(const struct iio_event_interface *ev_int)
> @@ -182,7 +187,7 @@ static const struct file_operations iio_event_chrdev_fileops = {
>  	.llseek = noop_llseek,
>  };
>  
> -int iio_event_getfd(struct iio_dev *indio_dev)
> +static int iio_event_getfd(struct iio_dev *indio_dev)
>  {
>  	struct iio_event_interface *ev_int = indio_dev->event_interface;
>  	int fd;
> @@ -215,6 +220,97 @@ int iio_event_getfd(struct iio_dev *indio_dev)
>  	return fd;
>  }
>  
> +/**
> + * iio_chrdev_open() - chrdev file open for event ioctls
> + * @inode:	Inode structure for identifying the device in the file system
> + * @filp:	File structure for iio device used to keep and later access
> + *		private data
> + *
> + * Return: 0 on success or -EBUSY if the device is already opened
> + **/

Don't we use the */ termination or kernel-doc through out IIO. Nice to be
consistent.  Ah. I see we don't.  Tidy that up whilst you are here :)


> +static int iio_chrdev_open(struct inode *inode, struct file *filp)
> +{
> +	struct iio_event_interface *ev =
> +		container_of(inode->i_cdev, struct iio_event_interface, chrdev);
> +
> +	if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev->flags))
> +		return -EBUSY;
> +
> +	iio_device_get(ev->indio_dev);
> +
> +	filp->private_data = ev;
> +
> +	return 0;
> +}
> +
> +/**
> + * iio_chrdev_release() - chrdev file close for event ioctls
> + * @inode:	Inode structure pointer for the char device
> + * @filp:	File structure pointer for the char device
> + *
> + * Return: 0 for successful release
> + */
> +static int iio_chrdev_release(struct inode *inode, struct file *filp)
> +{
> +	struct iio_event_interface *ev =
> +		container_of(inode->i_cdev, struct iio_event_interface, chrdev);
> +
> +	clear_bit(IIO_BUSY_BIT_POS, &ev->flags);
> +	iio_device_put(ev->indio_dev);
> +
> +	return 0;
> +}
> +
> +long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
> +			    unsigned int cmd, unsigned long arg)
> +{
> +	int __user *ip = (int __user *)arg;
> +	int fd;
> +

Given we've has some confusion on this recently perhaps a note that
this is protection on driver 'going away'.  We could even
add a little wrapper around it to make it explicit in the code.
What do you think?  Maybe a comment is enough.

> +	if (!indio_dev->info)
> +		return -ENODEV;
> +
> +	if (cmd == IIO_GET_EVENT_FD_IOCTL) {
> +		fd = iio_event_getfd(indio_dev);
> +		if (fd < 0)
> +			return fd;
> +		if (copy_to_user(ip, &fd, sizeof(fd)))
> +			return -EFAULT;
> +		return 0;
> +	}
> +	return -EINVAL;
> +}
> +
> +static long iio_event_ioctl_wrapper(struct file *filp, unsigned int cmd,
> +				    unsigned long arg)
> +{
> +	struct iio_event_interface *ev = filp->private_data;
> +
> +	return iio_device_event_ioctl(ev->indio_dev, filp, cmd, arg);
> +}
> +
> +static const struct file_operations iio_event_fileops = {
> +	.release = iio_chrdev_release,
> +	.open = iio_chrdev_open,
> +	.owner = THIS_MODULE,

As we are here. Might be nice to order this to match the file_operations structure
ordering.  Certainly seems odd to have .owner in the middle.. 
lalala for where you might have copied this from ;)

> +	.llseek = noop_llseek,
> +	.unlocked_ioctl = iio_event_ioctl_wrapper,
> +	.compat_ioctl = compat_ptr_ioctl,
> +};
> +
> +void iio_device_event_attach_chrdev(struct iio_dev *indio_dev)
> +{
> +	struct iio_event_interface *ev = indio_dev->event_interface;
> +
> +	if (!ev)
> +		return;
> +
> +	cdev_init(&ev->chrdev, &iio_event_fileops);
> +
> +	ev->indio_dev = indio_dev;
> +	indio_dev->chrdev = &ev->chrdev;
> +}
> +
>  static const char * const iio_ev_type_text[] = {
>  	[IIO_EV_TYPE_THRESH] = "thresh",
>  	[IIO_EV_TYPE_MAG] = "mag",
> diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h
> index 67d73d465e02..46fc977deae3 100644
> --- a/include/linux/iio/buffer_impl.h
> +++ b/include/linux/iio/buffer_impl.h
> @@ -1,6 +1,7 @@
>  /* SPDX-License-Identifier: GPL-2.0 */
>  #ifndef _IIO_BUFFER_GENERIC_IMPL_H_
>  #define _IIO_BUFFER_GENERIC_IMPL_H_
> +#include <linux/cdev.h>
>  #include <linux/sysfs.h>
>  #include <linux/kref.h>
>  
> @@ -72,6 +73,12 @@ struct iio_buffer {
>  	/** @indio_dev: IIO device to which this buffer belongs to. */
>  	struct iio_dev *indio_dev;
>  
> +	/** @chrdev: associated character device. */
> +	struct cdev chrdev;
> +
> +	/** @file_ops_flags: file ops related flags including busy flag. */
> +	unsigned long file_ops_flags;
> +
>  	/** @length: Number of datums in buffer. */
>  	unsigned int length;
>  
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index 5f9f439a4f01..52992be44e9e 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -516,10 +516,9 @@ struct iio_buffer_setup_ops {
>   * @info_exist_lock:	[INTERN] lock to prevent use during removal
>   * @setup_ops:		[DRIVER] callbacks to call before and after buffer
>   *			enable/disable
> - * @chrdev:		[INTERN] associated character device
> + * @chrdev:		[INTERN] reference to associated character device
>   * @groups:		[INTERN] attribute groups
>   * @groupcounter:	[INTERN] index of next attribute group
> - * @flags:		[INTERN] file ops related flags including busy flag.
>   * @debugfs_dentry:	[INTERN] device specific debugfs dentry.
>   * @cached_reg_addr:	[INTERN] cached register address for debugfs reads.
>   */
> @@ -559,12 +558,11 @@ struct iio_dev {
>  	clockid_t			clock_id;
>  	struct mutex			info_exist_lock;
>  	const struct iio_buffer_setup_ops	*setup_ops;
> -	struct cdev			chrdev;
> +	struct cdev			*chrdev;
>  #define IIO_MAX_GROUPS 6
>  	const struct attribute_group	*groups[IIO_MAX_GROUPS + 1];
>  	int				groupcounter;
>  
> -	unsigned long			flags;
>  #if defined(CONFIG_DEBUG_FS)
>  	struct dentry			*debugfs_dentry;
>  	unsigned			cached_reg_addr;


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

* Re: [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers
  2020-04-26  7:38 ` [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers Alexandru Ardelean
@ 2020-04-26 10:10   ` Jonathan Cameron
  2020-04-26 10:21     ` Jonathan Cameron
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Cameron @ 2020-04-26 10:10 UTC (permalink / raw)
  To: Alexandru Ardelean; +Cc: linux-iio, linux-kernel, lars, pmeerw

On Sun, 26 Apr 2020 10:38:16 +0300
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

> The aim of this is to reduce the organization violation of ioctl() calls in
> IIO core. Currently, since the chardev is split across files, event ioctl()
> calls need to be called in buffer ioctl() calls.
> 
> The 'industrialio-core.c' file will provide a 'iio_device_ioctl()' which
> will iterate over a list of ioctls registered with the IIO device. These
> can be event ioctl() or buffer ioctl() calls, or something else.
> This is needed, since there is currently one chardev per IIO device and
> that is used for both event handling and reading from the buffer.
> 
> Each ioctl() will have to return a IIO_IOCTL_UNHANDLED code (which is
> positive 1), if the ioctl() did not handle the call in any. This eliminates
> any potential ambiguities; if we were to have used error codes it would
> have been uncertain whether they were actual errors, or whether
> the registered ioctl() doesn't service the command.
> 
> If any ioctl() returns 0, it was considered that it was serviced
> successfully and the loop will exit.
> 
> One assumption for all registered ioctl() handlers is that they are
> statically allocated, so the iio_device_unregister() which just remove all
> of them from the device's ioctl() handler list.
> 
> Also, something that is a bit hard to do [at this point] and may not be
> worth the effort of doing, is to check whether registered ioctl()
> calls/commands overlap. This should be unlikely to happen, and should get
> caught at review time. Though, new ioctl() calls would likely not be added
> too often.
> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>

A question on locking inline. Otherwise this looks fairly clean and simple
to me.

Jonathan


> ---
>  drivers/iio/iio_core.h          | 14 ++++++++++++++
>  drivers/iio/industrialio-core.c | 33 +++++++++++++++++++++++++++++++++
>  include/linux/iio/iio.h         |  2 ++
>  3 files changed, 49 insertions(+)
> 
> diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
> index a527a66be9e5..34c3e19229d8 100644
> --- a/drivers/iio/iio_core.h
> +++ b/drivers/iio/iio_core.h
> @@ -17,6 +17,20 @@ struct iio_dev;
>  
>  extern struct device_type iio_device_type;
>  
> +#define IIO_IOCTL_UNHANDLED	1
> +struct iio_ioctl_handler {
> +	struct list_head entry;
> +	long (*ioctl)(struct iio_dev *indio_dev, struct file *filp,
> +		      unsigned int cmd, unsigned long arg);
> +};
> +
> +long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
> +		      unsigned int cmd, unsigned long arg);
> +
> +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
> +				       struct iio_ioctl_handler *h);
> +void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h);
> +
>  int __iio_add_chan_devattr(const char *postfix,
>  			   struct iio_chan_spec const *chan,
>  			   ssize_t (*func)(struct device *dev,
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index aec585cc8453..79e8fa8ff70b 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -1531,6 +1531,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)
>  	}
>  	dev_set_name(&dev->dev, "iio:device%d", dev->id);
>  	INIT_LIST_HEAD(&dev->buffer_list);
> +	INIT_LIST_HEAD(&dev->ioctl_handlers);
>  
>  	return dev;
>  }
> @@ -1584,6 +1585,33 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
>  }
>  EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
>  
> +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
> +				       struct iio_ioctl_handler *h)
> +{
> +	/* this assumes that all ioctl() handlers are statically allocated */
> +	list_add_tail(&h->entry, &indio_dev->ioctl_handlers);
> +}
> +
> +long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
> +		      unsigned int cmd, unsigned long arg)
> +{
> +	struct iio_ioctl_handler *h;
> +	int ret;
> +
> +	if (!indio_dev->info)
> +		return -ENODEV;
> +
> +	list_for_each_entry(h, &indio_dev->ioctl_handlers, entry) {
> +		ret = h->ioctl(indio_dev, filp, cmd, arg);
> +		if (ret == 0)
> +			return 0;
> +		if (ret != IIO_IOCTL_UNHANDLED)
> +			return ret;
> +	}
> +
> +	return -EINVAL;
> +}
> +
>  static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
>  {
>  	int i, j;
> @@ -1695,6 +1723,8 @@ EXPORT_SYMBOL(__iio_device_register);
>   **/
>  void iio_device_unregister(struct iio_dev *indio_dev)
>  {
> +	struct iio_ioctl_handler *h, *t;
> +
>  	if (indio_dev->chrdev)
>  		cdev_device_del(indio_dev->chrdev, &indio_dev->dev);
>  	else
> @@ -1708,6 +1738,9 @@ void iio_device_unregister(struct iio_dev *indio_dev)
>  
>  	iio_disable_all_buffers(indio_dev);
>  
> +	list_for_each_entry_safe(h, t, &indio_dev->ioctl_handlers, entry)
> +		list_del(&h->entry);
> +

Is there any chance anything is walking that list whilst we are deleting it?
I think this needs to happen under a lock as does the walk.

>  	indio_dev->info = NULL;
>  
>  	iio_device_wakeup_eventset(indio_dev);
> diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> index 52992be44e9e..b6ca8d85629e 100644
> --- a/include/linux/iio/iio.h
> +++ b/include/linux/iio/iio.h
> @@ -488,6 +488,7 @@ struct iio_buffer_setup_ops {
>   * @currentmode:	[DRIVER] current operating mode
>   * @dev:		[DRIVER] device structure, should be assigned a parent
>   *			and owner
> + * @ioctl_handlers:	[INTERN] list of registered ioctl handlers
>   * @event_interface:	[INTERN] event chrdevs associated with interrupt lines
>   * @buffer:		[DRIVER] any buffer present
>   * @buffer_list:	[INTERN] list of all buffers currently attached
> @@ -529,6 +530,7 @@ struct iio_dev {
>  	int				modes;
>  	int				currentmode;
>  	struct device			dev;
> +	struct list_head		ioctl_handlers;
>  
>  	struct iio_event_interface	*event_interface;
>  


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

* Re: [PATCH v5 6/6] iio: core: use new common ioctl() mechanism
  2020-04-26  7:38 ` [PATCH v5 6/6] iio: core: use new common ioctl() mechanism Alexandru Ardelean
@ 2020-04-26 10:13   ` Jonathan Cameron
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2020-04-26 10:13 UTC (permalink / raw)
  To: Alexandru Ardelean; +Cc: linux-iio, linux-kernel, lars, pmeerw

On Sun, 26 Apr 2020 10:38:17 +0300
Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:

> This change makes use of the new centralized ioctl() mechanism. The event
> interface registers it's ioctl() handler to IIO device.
> Both the buffer & event interface call 'iio_device_ioctl()', which should
> take care of all of indio_dev's ioctl() calls.
> 
> Later, we may add per-buffer ioctl() calls, and since each buffer will get
> it's own chardev, the buffer ioctl() handler will need a bit of tweaking
> for the first/legacy buffer (i.e. indio_dev->buffer).
> Also, those per-buffer ioctl() calls will not be registered with this
> mechanism.

Seems sensible to me.

J
> 
> Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> ---
>  drivers/iio/iio_core.h            |  3 ---
>  drivers/iio/industrialio-buffer.c |  2 +-
>  drivers/iio/industrialio-event.c  | 14 ++++++++------
>  3 files changed, 9 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
> index 34c3e19229d8..f68de4af2738 100644
> --- a/drivers/iio/iio_core.h
> +++ b/drivers/iio/iio_core.h
> @@ -54,9 +54,6 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals);
>  #ifdef CONFIG_IIO_BUFFER
>  struct poll_table_struct;
>  
> -long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
> -			    unsigned int cmd, unsigned long arg);
> -
>  void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev);
>  
>  int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index f5a975079bf4..43281c77ca22 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -1179,7 +1179,7 @@ static long iio_buffer_ioctl(struct file *filep, unsigned int cmd,
>  	if (!buffer || !buffer->access)
>  		return -ENODEV;
>  
> -	return iio_device_event_ioctl(buffer->indio_dev, filep, cmd, arg);
> +	return iio_device_ioctl(buffer->indio_dev, filep, cmd, arg);
>  }
>  
>  static ssize_t iio_buffer_store_enable(struct device *dev,
> diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
> index 0674b2117c98..1961c1d19370 100644
> --- a/drivers/iio/industrialio-event.c
> +++ b/drivers/iio/industrialio-event.c
> @@ -32,6 +32,7 @@
>   * @read_lock:		lock to protect kfifo read operations
>   * @chrdev:		associated chardev for this event
>   * @indio_dev:		IIO device to which this event interface belongs to
> + * @ioctl_handler:	handler for event ioctl() calls
>   */
>  struct iio_event_interface {
>  	wait_queue_head_t	wait;
> @@ -44,6 +45,7 @@ struct iio_event_interface {
>  
>  	struct cdev		chrdev;
>  	struct iio_dev		*indio_dev;
> +	struct iio_ioctl_handler	ioctl_handler;
>  };
>  
>  bool iio_event_enabled(const struct iio_event_interface *ev_int)
> @@ -261,15 +263,12 @@ static int iio_chrdev_release(struct inode *inode, struct file *filp)
>  	return 0;
>  }
>  
> -long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
> +static long iio_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
>  			    unsigned int cmd, unsigned long arg)
>  {
>  	int __user *ip = (int __user *)arg;
>  	int fd;
>  
> -	if (!indio_dev->info)
> -		return -ENODEV;
> -
>  	if (cmd == IIO_GET_EVENT_FD_IOCTL) {
>  		fd = iio_event_getfd(indio_dev);
>  		if (fd < 0)
> @@ -278,7 +277,7 @@ long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
>  			return -EFAULT;
>  		return 0;
>  	}
> -	return -EINVAL;
> +	return IIO_IOCTL_UNHANDLED;
>  }
>  
>  static long iio_event_ioctl_wrapper(struct file *filp, unsigned int cmd,
> @@ -286,7 +285,7 @@ static long iio_event_ioctl_wrapper(struct file *filp, unsigned int cmd,
>  {
>  	struct iio_event_interface *ev = filp->private_data;
>  
> -	return iio_device_event_ioctl(ev->indio_dev, filp, cmd, arg);
> +	return iio_device_ioctl(ev->indio_dev, filp, cmd, arg);
>  }
>  
>  static const struct file_operations iio_event_fileops = {
> @@ -308,7 +307,10 @@ void iio_device_event_attach_chrdev(struct iio_dev *indio_dev)
>  	cdev_init(&ev->chrdev, &iio_event_fileops);
>  
>  	ev->indio_dev = indio_dev;
> +	ev->ioctl_handler.ioctl = iio_event_ioctl;
>  	indio_dev->chrdev = &ev->chrdev;
> +
> +	iio_device_ioctl_handler_register(indio_dev, &ev->ioctl_handler);
>  }
>  
>  static const char * const iio_ev_type_text[] = {


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

* Re: [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers
  2020-04-26 10:10   ` Jonathan Cameron
@ 2020-04-26 10:21     ` Jonathan Cameron
  2020-04-27  6:43       ` Ardelean, Alexandru
  0 siblings, 1 reply; 14+ messages in thread
From: Jonathan Cameron @ 2020-04-26 10:21 UTC (permalink / raw)
  To: Alexandru Ardelean; +Cc: linux-iio, linux-kernel, lars, pmeerw

On Sun, 26 Apr 2020 11:10:37 +0100
Jonathan Cameron <jic23@kernel.org> wrote:

> On Sun, 26 Apr 2020 10:38:16 +0300
> Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
> 
> > The aim of this is to reduce the organization violation of ioctl() calls in
> > IIO core. Currently, since the chardev is split across files, event ioctl()
> > calls need to be called in buffer ioctl() calls.
> > 
> > The 'industrialio-core.c' file will provide a 'iio_device_ioctl()' which
> > will iterate over a list of ioctls registered with the IIO device. These
> > can be event ioctl() or buffer ioctl() calls, or something else.
> > This is needed, since there is currently one chardev per IIO device and
> > that is used for both event handling and reading from the buffer.
> > 
> > Each ioctl() will have to return a IIO_IOCTL_UNHANDLED code (which is
> > positive 1), if the ioctl() did not handle the call in any. This eliminates
> > any potential ambiguities; if we were to have used error codes it would
> > have been uncertain whether they were actual errors, or whether
> > the registered ioctl() doesn't service the command.
> > 
> > If any ioctl() returns 0, it was considered that it was serviced
> > successfully and the loop will exit.
> > 
> > One assumption for all registered ioctl() handlers is that they are
> > statically allocated, so the iio_device_unregister() which just remove all
> > of them from the device's ioctl() handler list.
> > 
> > Also, something that is a bit hard to do [at this point] and may not be
> > worth the effort of doing, is to check whether registered ioctl()
> > calls/commands overlap. This should be unlikely to happen, and should get
> > caught at review time. Though, new ioctl() calls would likely not be added
> > too often.
> > 
> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>  
> 
> A question on locking inline. Otherwise this looks fairly clean and simple
> to me.
> 
> Jonathan
> 
> 
> > ---
> >  drivers/iio/iio_core.h          | 14 ++++++++++++++
> >  drivers/iio/industrialio-core.c | 33 +++++++++++++++++++++++++++++++++
> >  include/linux/iio/iio.h         |  2 ++
> >  3 files changed, 49 insertions(+)
> > 
> > diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
> > index a527a66be9e5..34c3e19229d8 100644
> > --- a/drivers/iio/iio_core.h
> > +++ b/drivers/iio/iio_core.h
> > @@ -17,6 +17,20 @@ struct iio_dev;
> >  
> >  extern struct device_type iio_device_type;
> >  
> > +#define IIO_IOCTL_UNHANDLED	1
> > +struct iio_ioctl_handler {
> > +	struct list_head entry;
> > +	long (*ioctl)(struct iio_dev *indio_dev, struct file *filp,
> > +		      unsigned int cmd, unsigned long arg);
> > +};
> > +
> > +long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
> > +		      unsigned int cmd, unsigned long arg);
> > +
> > +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
> > +				       struct iio_ioctl_handler *h);
> > +void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h);
> > +
> >  int __iio_add_chan_devattr(const char *postfix,
> >  			   struct iio_chan_spec const *chan,
> >  			   ssize_t (*func)(struct device *dev,
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> > index aec585cc8453..79e8fa8ff70b 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -1531,6 +1531,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)
> >  	}
> >  	dev_set_name(&dev->dev, "iio:device%d", dev->id);
> >  	INIT_LIST_HEAD(&dev->buffer_list);
> > +	INIT_LIST_HEAD(&dev->ioctl_handlers);
> >  
> >  	return dev;
> >  }
> > @@ -1584,6 +1585,33 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
> >  }
> >  EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
> >  
> > +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
> > +				       struct iio_ioctl_handler *h)
> > +{
> > +	/* this assumes that all ioctl() handlers are statically allocated */
> > +	list_add_tail(&h->entry, &indio_dev->ioctl_handlers);
> > +}
> > +
> > +long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
> > +		      unsigned int cmd, unsigned long arg)
> > +{
> > +	struct iio_ioctl_handler *h;
> > +	int ret;
> > +
> > +	if (!indio_dev->info)
> > +		return -ENODEV;
> > +
> > +	list_for_each_entry(h, &indio_dev->ioctl_handlers, entry) {
> > +		ret = h->ioctl(indio_dev, filp, cmd, arg);
> > +		if (ret == 0)
> > +			return 0;
> > +		if (ret != IIO_IOCTL_UNHANDLED)
> > +			return ret;
> > +	}
> > +
> > +	return -EINVAL;
> > +}
> > +
> >  static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
> >  {
> >  	int i, j;
> > @@ -1695,6 +1723,8 @@ EXPORT_SYMBOL(__iio_device_register);
> >   **/
> >  void iio_device_unregister(struct iio_dev *indio_dev)
> >  {
> > +	struct iio_ioctl_handler *h, *t;
> > +
> >  	if (indio_dev->chrdev)
> >  		cdev_device_del(indio_dev->chrdev, &indio_dev->dev);
> >  	else
> > @@ -1708,6 +1738,9 @@ void iio_device_unregister(struct iio_dev *indio_dev)
> >  
> >  	iio_disable_all_buffers(indio_dev);
> >  
> > +	list_for_each_entry_safe(h, t, &indio_dev->ioctl_handlers, entry)
> > +		list_del(&h->entry);
> > +  
> 
> Is there any chance anything is walking that list whilst we are deleting it?
> I think this needs to happen under a lock as does the walk.

We may want to use the rcu methods to make the walk and delete safe whilst
avoiding heavy weight locking on the read path

linux/rculist.h

I've not thought about this in much depth though so take that advice as
a vague suggestion and nothing more!


> 
> >  	indio_dev->info = NULL;
> >  
> >  	iio_device_wakeup_eventset(indio_dev);
> > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> > index 52992be44e9e..b6ca8d85629e 100644
> > --- a/include/linux/iio/iio.h
> > +++ b/include/linux/iio/iio.h
> > @@ -488,6 +488,7 @@ struct iio_buffer_setup_ops {
> >   * @currentmode:	[DRIVER] current operating mode
> >   * @dev:		[DRIVER] device structure, should be assigned a parent
> >   *			and owner
> > + * @ioctl_handlers:	[INTERN] list of registered ioctl handlers
> >   * @event_interface:	[INTERN] event chrdevs associated with interrupt lines
> >   * @buffer:		[DRIVER] any buffer present
> >   * @buffer_list:	[INTERN] list of all buffers currently attached
> > @@ -529,6 +530,7 @@ struct iio_dev {
> >  	int				modes;
> >  	int				currentmode;
> >  	struct device			dev;
> > +	struct list_head		ioctl_handlers;
> >  
> >  	struct iio_event_interface	*event_interface;
> >    
> 


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

* Re: [PATCH v5 4/6] iio: buffer,event: duplicate chardev creation for buffers & events
  2020-04-26 10:00   ` Jonathan Cameron
@ 2020-04-27  6:38     ` Ardelean, Alexandru
  0 siblings, 0 replies; 14+ messages in thread
From: Ardelean, Alexandru @ 2020-04-27  6:38 UTC (permalink / raw)
  To: jic23; +Cc: linux-kernel, linux-iio, pmeerw, lars

On Sun, 2020-04-26 at 11:00 +0100, Jonathan Cameron wrote:
> [External]
> 
> On Sun, 26 Apr 2020 10:38:15 +0300
> Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
> 
> > This patch moves the chardev creation into industrialio-buffer.c &
> > industrialio-event.c. The move has to be in a single step (which makes the
> > patch a bit big), in order to pass the reference of the chardev to
> > 'indio_dev->chrdev'.
> > 
> > For structure purposes, industrialio-core.c should be the place where
> > cdev_device_add()/device_add() gets called, and the deletion function as
> > well.
> > 
> > What happens after this patch is:
> > - 'indio_dev->chrdev' is converted to a pointer
> > - if there is an IIO buffer, iio_device_buffer_attach_chrdev() will attach
> >   it's chardev to 'indio_chrdev'
> > - if it doesn't, the event interface will attach a reference to it's
> >   chardev (if it is instantiated) via iio_device_event_attach_chrdev()
> > 
> > That way, the control of the 'legacy' chardev is still visible in
> > 'industrialio-core.c'. So, each logic file (for buffers & events) shouldn't
> > hide things too much away from the core file.
> > 
> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>
> 
> A few really minor suggestions inline. At least half of them are tidying up
> things whilst we are here that were ugly in the code we are duplicating.
> 
> Looks good to me. However, I'd like more eyes on this set before I apply it
> so will give it at least a week or two on the list!
> 
> Jonathan
> 
> > ---
> >  drivers/iio/iio_core.h            |  16 ++---
> >  drivers/iio/industrialio-buffer.c |  90 +++++++++++++++++++++++--
> >  drivers/iio/industrialio-core.c   | 105 ++++--------------------------
> >  drivers/iio/industrialio-event.c  |  98 +++++++++++++++++++++++++++-
> >  include/linux/iio/buffer_impl.h   |   7 ++
> >  include/linux/iio/iio.h           |   6 +-
> >  6 files changed, 208 insertions(+), 114 deletions(-)
> > 
> > diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
> > index 39ec0344fb68..a527a66be9e5 100644
> > --- a/drivers/iio/iio_core.h
> > +++ b/drivers/iio/iio_core.h
> > @@ -40,17 +40,14 @@ ssize_t iio_format_value(char *buf, unsigned int type,
> > int size, int *vals);
> >  #ifdef CONFIG_IIO_BUFFER
> >  struct poll_table_struct;
> >  
> > -__poll_t iio_buffer_poll(struct file *filp,
> > -			     struct poll_table_struct *wait);
> > -ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
> > -			      size_t n, loff_t *f_ps);
> > +long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
> > +			    unsigned int cmd, unsigned long arg);
> > +
> > +void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev);
> >  
> >  int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
> >  void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
> >  
> > -#define iio_buffer_poll_addr (&iio_buffer_poll)
> > -#define iio_buffer_read_outer_addr (&iio_buffer_read_outer)
> > -
> >  void iio_device_buffers_put(struct iio_dev *indio_dev);
> >  
> >  void iio_disable_all_buffers(struct iio_dev *indio_dev);
> > @@ -58,8 +55,7 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
> >  
> >  #else
> >  
> > -#define iio_buffer_poll_addr NULL
> > -#define iio_buffer_read_outer_addr NULL
> > +static inline void iio_device_buffer_attach_chrdev(struct iio_dev
> > *indio_dev) {}
> >  
> >  static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev
> > *indio_dev)
> >  {
> > @@ -77,8 +73,8 @@ static inline void iio_buffer_wakeup_poll(struct iio_dev
> > *indio_dev) {}
> >  
> >  int iio_device_register_eventset(struct iio_dev *indio_dev);
> >  void iio_device_unregister_eventset(struct iio_dev *indio_dev);
> > +void iio_device_event_attach_chrdev(struct iio_dev *indio_dev);
> >  void iio_device_wakeup_eventset(struct iio_dev *indio_dev);
> > -int iio_event_getfd(struct iio_dev *indio_dev);
> >  
> >  struct iio_event_interface;
> >  bool iio_event_enabled(const struct iio_event_interface *ev_int);
> > diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-
> > buffer.c
> > index a66d3fbc2905..f5a975079bf4 100644
> > --- a/drivers/iio/industrialio-buffer.c
> > +++ b/drivers/iio/industrialio-buffer.c
> > @@ -99,11 +99,11 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev,
> > struct iio_buffer *buf,
> >   * Return: negative values corresponding to error codes or ret != 0
> >   *	   for ending the reading activity
> >   **/
> > -ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf,
> > -			      size_t n, loff_t *f_ps)
> > +static ssize_t iio_buffer_read_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_buffer *rb = filp->private_data;
> > +	struct iio_dev *indio_dev = rb->indio_dev;
> >  	DEFINE_WAIT_FUNC(wait, woken_wake_function);
> >  	size_t datum_size;
> >  	size_t to_wait;
> > @@ -165,11 +165,11 @@ ssize_t iio_buffer_read_outer(struct file *filp, char
> > __user *buf,
> >   * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading
> >   *	   or 0 for other cases
> >   */
> > -__poll_t iio_buffer_poll(struct file *filp,
> > +static __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_buffer *rb = filp->private_data;
> > +	struct iio_dev *indio_dev = rb->indio_dev;
> >  
> >  	if (!indio_dev->info || rb == NULL)
> >  		return 0;
> > @@ -180,6 +180,48 @@ __poll_t iio_buffer_poll(struct file *filp,
> >  	return 0;
> >  }
> >  
> > +/**
> > + * iio_buffer_chrdev_open() - chrdev file open for buffer access
> > + * @inode:	Inode structure for identifying the device in the file system
> > + * @filp:	File structure for iio device used to keep and later access
> > + *		private data
> > + *
> > + * Return: 0 on success or -EBUSY if the device is already opened
> > + **/
> 
> */
> 
> > +static int iio_buffer_chrdev_open(struct inode *inode, struct file *filp)
> > +{
> > +	struct iio_buffer *buffer = container_of(inode->i_cdev,
> > +						 struct iio_buffer, chrdev);
> > +
> > +	if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->file_ops_flags))
> > +		return -EBUSY;
> > +
> > +	iio_buffer_get(buffer);
> > +
> > +	filp->private_data = buffer;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iio_buffer_chrdev_release() - chrdev file close for buffer access
> > + * @inode:	Inode structure pointer for the char device
> > + * @filp:	File structure pointer for the char device
> > + *
> > + * Return: 0 for successful release
> > + */
> > +static int iio_buffer_chrdev_release(struct inode *inode, struct file
> > *filp)
> > +{
> > +	struct iio_buffer *buffer = container_of(inode->i_cdev,
> > +						 struct iio_buffer, chrdev);
> > +
> > +	clear_bit(IIO_BUSY_BIT_POS, &buffer->file_ops_flags);
> > +
> > +	iio_buffer_put(buffer);
> > +
> > +	return 0;
> > +}
> > +
> >  /**
> >   * iio_buffer_wakeup_poll - Wakes up the buffer waitqueue
> >   * @indio_dev: The IIO device
> > @@ -1129,6 +1171,17 @@ void iio_disable_all_buffers(struct iio_dev
> > *indio_dev)
> >  	iio_buffer_deactivate_all(indio_dev);
> >  }
> >  
> > +static long iio_buffer_ioctl(struct file *filep, unsigned int cmd,
> > +			     unsigned long arg)
> > +{
> > +	struct iio_buffer *buffer = filep->private_data;
> > +
> > +	if (!buffer || !buffer->access)
> > +		return -ENODEV;
> > +
> > +	return iio_device_event_ioctl(buffer->indio_dev, filep, cmd, arg);
> > +}
> > +
> >  static ssize_t iio_buffer_store_enable(struct device *dev,
> >  				       struct device_attribute *attr,
> >  				       const char *buf,
> > @@ -1355,6 +1408,29 @@ void iio_buffer_free_sysfs_and_mask(struct iio_dev
> > *indio_dev)
> >  	iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
> >  }
> >  
> > +static const struct file_operations iio_buffer_fileops = {
> > +	.read = iio_buffer_read_outer,
> > +	.release = iio_buffer_chrdev_release,
> > +	.open = iio_buffer_chrdev_open,
> > +	.poll = iio_buffer_poll,
> > +	.owner = THIS_MODULE,
> > +	.llseek = noop_llseek,
> > +	.unlocked_ioctl = iio_buffer_ioctl,
> > +	.compat_ioctl = compat_ptr_ioctl,
> > +};
> > +
> > +void iio_device_buffer_attach_chrdev(struct iio_dev *indio_dev)
> > +{
> > +	struct iio_buffer *buffer = indio_dev->buffer;
> > +
> > +	if (!buffer)
> > +		return;
> > +
> > +	cdev_init(&buffer->chrdev, &iio_buffer_fileops);
> > +
> > +	indio_dev->chrdev = &buffer->chrdev;
> > +}
> > +
> >  void iio_device_buffers_put(struct iio_dev *indio_dev)
> >  {
> >  	struct iio_buffer *buffer = indio_dev->buffer;
> > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-
> > core.c
> > index a65022396329..aec585cc8453 100644
> > --- a/drivers/iio/industrialio-core.c
> > +++ b/drivers/iio/industrialio-core.c
> > @@ -1584,79 +1584,6 @@ struct iio_dev *devm_iio_device_alloc(struct device
> > *dev, int sizeof_priv)
> >  }
> >  EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
> >  
> > -/**
> > - * iio_chrdev_open() - chrdev file open for buffer access and ioctls
> > - * @inode:	Inode structure for identifying the device in the file system
> > - * @filp:	File structure for iio device used to keep and later access
> > - *		private data
> > - *
> > - * Return: 0 on success or -EBUSY if the device is already opened
> > - **/
> > -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);
> > -
> > -	if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags))
> > -		return -EBUSY;
> > -
> > -	iio_device_get(indio_dev);
> > -
> > -	filp->private_data = indio_dev;
> > -
> > -	return 0;
> > -}
> > -
> > -/**
> > - * iio_chrdev_release() - chrdev file close buffer access and ioctls
> > - * @inode:	Inode structure pointer for the char device
> > - * @filp:	File structure pointer for the char device
> > - *
> > - * Return: 0 for successful release
> > - */
> > -static int iio_chrdev_release(struct inode *inode, struct file *filp)
> > -{
> > -	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);
> > -
> > -	return 0;
> > -}
> > -
> > -/* Somewhat of a cross file organization violation - ioctls here are
> > actually
> > - * event related */
> > -static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long
> > arg)
> > -{
> > -	struct iio_dev *indio_dev = filp->private_data;
> > -	int __user *ip = (int __user *)arg;
> > -	int fd;
> > -
> > -	if (!indio_dev->info)
> > -		return -ENODEV;
> > -
> > -	if (cmd == IIO_GET_EVENT_FD_IOCTL) {
> > -		fd = iio_event_getfd(indio_dev);
> > -		if (fd < 0)
> > -			return fd;
> > -		if (copy_to_user(ip, &fd, sizeof(fd)))
> > -			return -EFAULT;
> > -		return 0;
> > -	}
> > -	return -EINVAL;
> > -}
> > -
> > -static const struct file_operations iio_buffer_fileops = {
> > -	.read = iio_buffer_read_outer_addr,
> > -	.release = iio_chrdev_release,
> > -	.open = iio_chrdev_open,
> > -	.poll = iio_buffer_poll_addr,
> > -	.owner = THIS_MODULE,
> > -	.llseek = noop_llseek,
> > -	.unlocked_ioctl = iio_ioctl,
> > -	.compat_ioctl = compat_ptr_ioctl,
> > -};
> > -
> >  static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
> >  {
> >  	int i, j;
> > @@ -1682,15 +1609,6 @@ static int iio_check_unique_scan_index(struct iio_dev
> > *indio_dev)
> >  
> >  static const struct iio_buffer_setup_ops noop_ring_setup_ops;
> >  
> > -static const struct file_operations iio_event_fileops = {
> > -	.release = iio_chrdev_release,
> > -	.open = iio_chrdev_open,
> > -	.owner = THIS_MODULE,
> > -	.llseek = noop_llseek,
> > -	.unlocked_ioctl = iio_ioctl,
> > -	.compat_ioctl = compat_ptr_ioctl,
> > -};
> > -
> >  int __iio_device_register(struct iio_dev *indio_dev, struct module
> > *this_mod)
> >  {
> >  	int ret;
> > @@ -1741,16 +1659,17 @@ int __iio_device_register(struct iio_dev *indio_dev,
> > struct module *this_mod)
> >  		indio_dev->setup_ops == NULL)
> >  		indio_dev->setup_ops = &noop_ring_setup_ops;
> >  
> > -	if (indio_dev->buffer)
> > -		cdev_init(&indio_dev->chrdev, &iio_buffer_fileops);
> > -	else if (indio_dev->event_interface)
> > -		cdev_init(&indio_dev->chrdev, &iio_event_fileops);
> > +	iio_device_buffer_attach_chrdev(indio_dev);
> >  
> > -	indio_dev->chrdev.owner = this_mod;
> > +	/* no chrdev attached from buffer, we go with event-only chrdev */
> > +	if (!indio_dev->chrdev)
> > +		iio_device_event_attach_chrdev(indio_dev);
> >  
> > -	if (indio_dev->buffer || indio_dev->event_interface)
> > -		ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev);
> > -	else
> > +	if (indio_dev->chrdev) {
> > +		indio_dev->chrdev->owner = this_mod;
> > +
> > +		ret = cdev_device_add(indio_dev->chrdev, &indio_dev->dev);
> > +	} else
> >  		ret = device_add(&indio_dev->dev);
> >  
> >  	if (ret < 0)
> > @@ -1776,13 +1695,15 @@ EXPORT_SYMBOL(__iio_device_register);
> >   **/
> >  void iio_device_unregister(struct iio_dev *indio_dev)
> >  {
> > -	if (indio_dev->buffer || indio_dev->event_interface)
> > -		cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
> > +	if (indio_dev->chrdev)
> > +		cdev_device_del(indio_dev->chrdev, &indio_dev->dev);
> >  	else
> >  		device_del(&indio_dev->dev);
> >  
> >  	mutex_lock(&indio_dev->info_exist_lock);
> >  
> > +	indio_dev->chrdev = NULL;
> > +
> 
> Why? I can't immediately see the reason and I'm too lazy to figure it out
> on a Sunday morning ;)

Paranoid code.
Initially I just thought I'd add it as a similar mechanism for 'indio_dev->info' 
being made NULL later.
But I can't think of a good reason.
I guess I added it in the middle of coding, without much thought.
Especially, since there isn't any code to check this being NULL to do anything.

Will remove it.
'indio_dev->info = NULL' should be enough.


> 
> >  	iio_device_unregister_debugfs(indio_dev);
> >  
> >  	iio_disable_all_buffers(indio_dev);
> > diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-
> > event.c
> > index 5b17c92d3b50..0674b2117c98 100644
> > --- a/drivers/iio/industrialio-event.c
> > +++ b/drivers/iio/industrialio-event.c
> > @@ -30,6 +30,8 @@
> >   * @flags:		file operations related flags including busy flag.
> >   * @group:		event interface sysfs attribute group
> >   * @read_lock:		lock to protect kfifo read operations
> > + * @chrdev:		associated chardev for this event
> > + * @indio_dev:		IIO device to which this event interface belongs
> > to
> >   */
> >  struct iio_event_interface {
> >  	wait_queue_head_t	wait;
> > @@ -39,6 +41,9 @@ struct iio_event_interface {
> >  	unsigned long		flags;
> >  	struct attribute_group	group;
> >  	struct mutex		read_lock;
> > +
> > +	struct cdev		chrdev;
> > +	struct iio_dev		*indio_dev;
> >  };
> >  
> >  bool iio_event_enabled(const struct iio_event_interface *ev_int)
> > @@ -182,7 +187,7 @@ static const struct file_operations
> > iio_event_chrdev_fileops = {
> >  	.llseek = noop_llseek,
> >  };
> >  
> > -int iio_event_getfd(struct iio_dev *indio_dev)
> > +static int iio_event_getfd(struct iio_dev *indio_dev)
> >  {
> >  	struct iio_event_interface *ev_int = indio_dev->event_interface;
> >  	int fd;
> > @@ -215,6 +220,97 @@ int iio_event_getfd(struct iio_dev *indio_dev)
> >  	return fd;
> >  }
> >  
> > +/**
> > + * iio_chrdev_open() - chrdev file open for event ioctls
> > + * @inode:	Inode structure for identifying the device in the file system
> > + * @filp:	File structure for iio device used to keep and later access
> > + *		private data
> > + *
> > + * Return: 0 on success or -EBUSY if the device is already opened
> > + **/
> 
> Don't we use the */ termination or kernel-doc through out IIO. Nice to be
> consistent.  Ah. I see we don't.  Tidy that up whilst you are here :)

ack

> 
> 
> > +static int iio_chrdev_open(struct inode *inode, struct file *filp)
> > +{
> > +	struct iio_event_interface *ev =
> > +		container_of(inode->i_cdev, struct iio_event_interface, chrdev);
> > +
> > +	if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev->flags))
> > +		return -EBUSY;
> > +
> > +	iio_device_get(ev->indio_dev);
> > +
> > +	filp->private_data = ev;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iio_chrdev_release() - chrdev file close for event ioctls
> > + * @inode:	Inode structure pointer for the char device
> > + * @filp:	File structure pointer for the char device
> > + *
> > + * Return: 0 for successful release
> > + */
> > +static int iio_chrdev_release(struct inode *inode, struct file *filp)
> > +{
> > +	struct iio_event_interface *ev =
> > +		container_of(inode->i_cdev, struct iio_event_interface, chrdev);
> > +
> > +	clear_bit(IIO_BUSY_BIT_POS, &ev->flags);
> > +	iio_device_put(ev->indio_dev);
> > +
> > +	return 0;
> > +}
> > +
> > +long iio_device_event_ioctl(struct iio_dev *indio_dev, struct file *filp,
> > +			    unsigned int cmd, unsigned long arg)
> > +{
> > +	int __user *ip = (int __user *)arg;
> > +	int fd;
> > +
> 
> Given we've has some confusion on this recently perhaps a note that
> this is protection on driver 'going away'.  We could even
> add a little wrapper around it to make it explicit in the code.
> What do you think?  Maybe a comment is enough.

Comment sounds good enough.
This code should get called via 2 potential code-paths: 1 via events chardev & 1
via buffer chardev.

> 
> > +	if (!indio_dev->info)
> > +		return -ENODEV;
> > +
> > +	if (cmd == IIO_GET_EVENT_FD_IOCTL) {
> > +		fd = iio_event_getfd(indio_dev);
> > +		if (fd < 0)
> > +			return fd;
> > +		if (copy_to_user(ip, &fd, sizeof(fd)))
> > +			return -EFAULT;
> > +		return 0;
> > +	}
> > +	return -EINVAL;
> > +}
> > +
> > +static long iio_event_ioctl_wrapper(struct file *filp, unsigned int cmd,
> > +				    unsigned long arg)
> > +{
> > +	struct iio_event_interface *ev = filp->private_data;
> > +
> > +	return iio_device_event_ioctl(ev->indio_dev, filp, cmd, arg);
> > +}
> > +
> > +static const struct file_operations iio_event_fileops = {
> > +	.release = iio_chrdev_release,
> > +	.open = iio_chrdev_open,
> > +	.owner = THIS_MODULE,
> 
> As we are here. Might be nice to order this to match the file_operations
> structure
> ordering.  Certainly seems odd to have .owner in the middle.. 
> lalala for where you might have copied this from ;)

ack

> 
> > +	.llseek = noop_llseek,
> > +	.unlocked_ioctl = iio_event_ioctl_wrapper,
> > +	.compat_ioctl = compat_ptr_ioctl,
> > +};
> > +
> > +void iio_device_event_attach_chrdev(struct iio_dev *indio_dev)
> > +{
> > +	struct iio_event_interface *ev = indio_dev->event_interface;
> > +
> > +	if (!ev)
> > +		return;
> > +
> > +	cdev_init(&ev->chrdev, &iio_event_fileops);
> > +
> > +	ev->indio_dev = indio_dev;
> > +	indio_dev->chrdev = &ev->chrdev;
> > +}
> > +
> >  static const char * const iio_ev_type_text[] = {
> >  	[IIO_EV_TYPE_THRESH] = "thresh",
> >  	[IIO_EV_TYPE_MAG] = "mag",
> > diff --git a/include/linux/iio/buffer_impl.h
> > b/include/linux/iio/buffer_impl.h
> > index 67d73d465e02..46fc977deae3 100644
> > --- a/include/linux/iio/buffer_impl.h
> > +++ b/include/linux/iio/buffer_impl.h
> > @@ -1,6 +1,7 @@
> >  /* SPDX-License-Identifier: GPL-2.0 */
> >  #ifndef _IIO_BUFFER_GENERIC_IMPL_H_
> >  #define _IIO_BUFFER_GENERIC_IMPL_H_
> > +#include <linux/cdev.h>
> >  #include <linux/sysfs.h>
> >  #include <linux/kref.h>
> >  
> > @@ -72,6 +73,12 @@ struct iio_buffer {
> >  	/** @indio_dev: IIO device to which this buffer belongs to. */
> >  	struct iio_dev *indio_dev;
> >  
> > +	/** @chrdev: associated character device. */
> > +	struct cdev chrdev;
> > +
> > +	/** @file_ops_flags: file ops related flags including busy flag. */
> > +	unsigned long file_ops_flags;
> > +
> >  	/** @length: Number of datums in buffer. */
> >  	unsigned int length;
> >  
> > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> > index 5f9f439a4f01..52992be44e9e 100644
> > --- a/include/linux/iio/iio.h
> > +++ b/include/linux/iio/iio.h
> > @@ -516,10 +516,9 @@ struct iio_buffer_setup_ops {
> >   * @info_exist_lock:	[INTERN] lock to prevent use during removal
> >   * @setup_ops:		[DRIVER] callbacks to call before and after
> > buffer
> >   *			enable/disable
> > - * @chrdev:		[INTERN] associated character device
> > + * @chrdev:		[INTERN] reference to associated character
> > device
> >   * @groups:		[INTERN] attribute groups
> >   * @groupcounter:	[INTERN] index of next attribute group
> > - * @flags:		[INTERN] file ops related flags including busy flag.
> >   * @debugfs_dentry:	[INTERN] device specific debugfs dentry.
> >   * @cached_reg_addr:	[INTERN] cached register address for debugfs
> > reads.
> >   */
> > @@ -559,12 +558,11 @@ struct iio_dev {
> >  	clockid_t			clock_id;
> >  	struct mutex			info_exist_lock;
> >  	const struct iio_buffer_setup_ops	*setup_ops;
> > -	struct cdev			chrdev;
> > +	struct cdev			*chrdev;
> >  #define IIO_MAX_GROUPS 6
> >  	const struct attribute_group	*groups[IIO_MAX_GROUPS + 1];
> >  	int				groupcounter;
> >  
> > -	unsigned long			flags;
> >  #if defined(CONFIG_DEBUG_FS)
> >  	struct dentry			*debugfs_dentry;
> >  	unsigned			cached_reg_addr;

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

* Re: [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers
  2020-04-26 10:21     ` Jonathan Cameron
@ 2020-04-27  6:43       ` Ardelean, Alexandru
  2020-04-27 10:04         ` Ardelean, Alexandru
  0 siblings, 1 reply; 14+ messages in thread
From: Ardelean, Alexandru @ 2020-04-27  6:43 UTC (permalink / raw)
  To: jic23; +Cc: linux-kernel, linux-iio, pmeerw, lars

On Sun, 2020-04-26 at 11:21 +0100, Jonathan Cameron wrote:
> [External]
> 
> On Sun, 26 Apr 2020 11:10:37 +0100
> Jonathan Cameron <jic23@kernel.org> wrote:
> 
> > On Sun, 26 Apr 2020 10:38:16 +0300
> > Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
> > 
> > > The aim of this is to reduce the organization violation of ioctl() calls
> > > in
> > > IIO core. Currently, since the chardev is split across files, event
> > > ioctl()
> > > calls need to be called in buffer ioctl() calls.
> > > 
> > > The 'industrialio-core.c' file will provide a 'iio_device_ioctl()' which
> > > will iterate over a list of ioctls registered with the IIO device. These
> > > can be event ioctl() or buffer ioctl() calls, or something else.
> > > This is needed, since there is currently one chardev per IIO device and
> > > that is used for both event handling and reading from the buffer.
> > > 
> > > Each ioctl() will have to return a IIO_IOCTL_UNHANDLED code (which is
> > > positive 1), if the ioctl() did not handle the call in any. This
> > > eliminates
> > > any potential ambiguities; if we were to have used error codes it would
> > > have been uncertain whether they were actual errors, or whether
> > > the registered ioctl() doesn't service the command.
> > > 
> > > If any ioctl() returns 0, it was considered that it was serviced
> > > successfully and the loop will exit.
> > > 
> > > One assumption for all registered ioctl() handlers is that they are
> > > statically allocated, so the iio_device_unregister() which just remove all
> > > of them from the device's ioctl() handler list.
> > > 
> > > Also, something that is a bit hard to do [at this point] and may not be
> > > worth the effort of doing, is to check whether registered ioctl()
> > > calls/commands overlap. This should be unlikely to happen, and should get
> > > caught at review time. Though, new ioctl() calls would likely not be added
> > > too often.
> > > 
> > > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>  
> > 
> > A question on locking inline. Otherwise this looks fairly clean and simple
> > to me.
> > 
> > Jonathan
> > 
> > 
> > > ---
> > >  drivers/iio/iio_core.h          | 14 ++++++++++++++
> > >  drivers/iio/industrialio-core.c | 33 +++++++++++++++++++++++++++++++++
> > >  include/linux/iio/iio.h         |  2 ++
> > >  3 files changed, 49 insertions(+)
> > > 
> > > diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
> > > index a527a66be9e5..34c3e19229d8 100644
> > > --- a/drivers/iio/iio_core.h
> > > +++ b/drivers/iio/iio_core.h
> > > @@ -17,6 +17,20 @@ struct iio_dev;
> > >  
> > >  extern struct device_type iio_device_type;
> > >  
> > > +#define IIO_IOCTL_UNHANDLED	1
> > > +struct iio_ioctl_handler {
> > > +	struct list_head entry;
> > > +	long (*ioctl)(struct iio_dev *indio_dev, struct file *filp,
> > > +		      unsigned int cmd, unsigned long arg);
> > > +};
> > > +
> > > +long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
> > > +		      unsigned int cmd, unsigned long arg);
> > > +
> > > +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
> > > +				       struct iio_ioctl_handler *h);
> > > +void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h);
> > > +
> > >  int __iio_add_chan_devattr(const char *postfix,
> > >  			   struct iio_chan_spec const *chan,
> > >  			   ssize_t (*func)(struct device *dev,
> > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-
> > > core.c
> > > index aec585cc8453..79e8fa8ff70b 100644
> > > --- a/drivers/iio/industrialio-core.c
> > > +++ b/drivers/iio/industrialio-core.c
> > > @@ -1531,6 +1531,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)
> > >  	}
> > >  	dev_set_name(&dev->dev, "iio:device%d", dev->id);
> > >  	INIT_LIST_HEAD(&dev->buffer_list);
> > > +	INIT_LIST_HEAD(&dev->ioctl_handlers);
> > >  
> > >  	return dev;
> > >  }
> > > @@ -1584,6 +1585,33 @@ struct iio_dev *devm_iio_device_alloc(struct device
> > > *dev, int sizeof_priv)
> > >  }
> > >  EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
> > >  
> > > +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
> > > +				       struct iio_ioctl_handler *h)
> > > +{
> > > +	/* this assumes that all ioctl() handlers are statically allocated */
> > > +	list_add_tail(&h->entry, &indio_dev->ioctl_handlers);
> > > +}
> > > +
> > > +long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
> > > +		      unsigned int cmd, unsigned long arg)
> > > +{
> > > +	struct iio_ioctl_handler *h;
> > > +	int ret;
> > > +
> > > +	if (!indio_dev->info)
> > > +		return -ENODEV;
> > > +
> > > +	list_for_each_entry(h, &indio_dev->ioctl_handlers, entry) {
> > > +		ret = h->ioctl(indio_dev, filp, cmd, arg);
> > > +		if (ret == 0)
> > > +			return 0;
> > > +		if (ret != IIO_IOCTL_UNHANDLED)
> > > +			return ret;
> > > +	}
> > > +
> > > +	return -EINVAL;
> > > +}
> > > +
> > >  static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
> > >  {
> > >  	int i, j;
> > > @@ -1695,6 +1723,8 @@ EXPORT_SYMBOL(__iio_device_register);
> > >   **/
> > >  void iio_device_unregister(struct iio_dev *indio_dev)
> > >  {
> > > +	struct iio_ioctl_handler *h, *t;
> > > +
> > >  	if (indio_dev->chrdev)
> > >  		cdev_device_del(indio_dev->chrdev, &indio_dev->dev);
> > >  	else
> > > @@ -1708,6 +1738,9 @@ void iio_device_unregister(struct iio_dev
> > > *indio_dev)
> > >  
> > >  	iio_disable_all_buffers(indio_dev);
> > >  
> > > +	list_for_each_entry_safe(h, t, &indio_dev->ioctl_handlers, entry)
> > > +		list_del(&h->entry);
> > > +  
> > 
> > Is there any chance anything is walking that list whilst we are deleting it?
> > I think this needs to happen under a lock as does the walk.
> 
> We may want to use the rcu methods to make the walk and delete safe whilst
> avoiding heavy weight locking on the read path
> 
> linux/rculist.h
> 
> I've not thought about this in much depth though so take that advice as
> a vague suggestion and nothing more!

I'm a bit tempted to do the simple mutex approach, but I'll take a look at the
rculist.h thingi.

The point is good about the list being walked while being deleted.

> 
> 
> > >  	indio_dev->info = NULL;
> > >  
> > >  	iio_device_wakeup_eventset(indio_dev);
> > > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> > > index 52992be44e9e..b6ca8d85629e 100644
> > > --- a/include/linux/iio/iio.h
> > > +++ b/include/linux/iio/iio.h
> > > @@ -488,6 +488,7 @@ struct iio_buffer_setup_ops {
> > >   * @currentmode:	[DRIVER] current operating mode
> > >   * @dev:		[DRIVER] device structure, should be assigned a parent
> > >   *			and owner
> > > + * @ioctl_handlers:	[INTERN] list of registered ioctl handlers
> > >   * @event_interface:	[INTERN] event chrdevs associated with interrupt
> > > lines
> > >   * @buffer:		[DRIVER] any buffer present
> > >   * @buffer_list:	[INTERN] list of all buffers currently attached
> > > @@ -529,6 +530,7 @@ struct iio_dev {
> > >  	int				modes;
> > >  	int				currentmode;
> > >  	struct device			dev;
> > > +	struct list_head		ioctl_handlers;
> > >  
> > >  	struct iio_event_interface	*event_interface;
> > >    

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

* Re: [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers
  2020-04-27  6:43       ` Ardelean, Alexandru
@ 2020-04-27 10:04         ` Ardelean, Alexandru
  0 siblings, 0 replies; 14+ messages in thread
From: Ardelean, Alexandru @ 2020-04-27 10:04 UTC (permalink / raw)
  To: jic23; +Cc: linux-kernel, linux-iio, pmeerw, lars

On Mon, 2020-04-27 at 06:43 +0000, Ardelean, Alexandru wrote:
> [External]
> 
> On Sun, 2020-04-26 at 11:21 +0100, Jonathan Cameron wrote:
> > [External]
> > 
> > On Sun, 26 Apr 2020 11:10:37 +0100
> > Jonathan Cameron <jic23@kernel.org> wrote:
> > 
> > > On Sun, 26 Apr 2020 10:38:16 +0300
> > > Alexandru Ardelean <alexandru.ardelean@analog.com> wrote:
> > > 
> > > > The aim of this is to reduce the organization violation of ioctl() calls
> > > > in
> > > > IIO core. Currently, since the chardev is split across files, event
> > > > ioctl()
> > > > calls need to be called in buffer ioctl() calls.
> > > > 
> > > > The 'industrialio-core.c' file will provide a 'iio_device_ioctl()' which
> > > > will iterate over a list of ioctls registered with the IIO device. These
> > > > can be event ioctl() or buffer ioctl() calls, or something else.
> > > > This is needed, since there is currently one chardev per IIO device and
> > > > that is used for both event handling and reading from the buffer.
> > > > 
> > > > Each ioctl() will have to return a IIO_IOCTL_UNHANDLED code (which is
> > > > positive 1), if the ioctl() did not handle the call in any. This
> > > > eliminates
> > > > any potential ambiguities; if we were to have used error codes it would
> > > > have been uncertain whether they were actual errors, or whether
> > > > the registered ioctl() doesn't service the command.
> > > > 
> > > > If any ioctl() returns 0, it was considered that it was serviced
> > > > successfully and the loop will exit.
> > > > 
> > > > One assumption for all registered ioctl() handlers is that they are
> > > > statically allocated, so the iio_device_unregister() which just remove
> > > > all
> > > > of them from the device's ioctl() handler list.
> > > > 
> > > > Also, something that is a bit hard to do [at this point] and may not be
> > > > worth the effort of doing, is to check whether registered ioctl()
> > > > calls/commands overlap. This should be unlikely to happen, and should
> > > > get
> > > > caught at review time. Though, new ioctl() calls would likely not be
> > > > added
> > > > too often.
> > > > 
> > > > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@analog.com>  
> > > 
> > > A question on locking inline. Otherwise this looks fairly clean and simple
> > > to me.
> > > 
> > > Jonathan
> > > 
> > > 
> > > > ---
> > > >  drivers/iio/iio_core.h          | 14 ++++++++++++++
> > > >  drivers/iio/industrialio-core.c | 33 +++++++++++++++++++++++++++++++++
> > > >  include/linux/iio/iio.h         |  2 ++
> > > >  3 files changed, 49 insertions(+)
> > > > 
> > > > diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
> > > > index a527a66be9e5..34c3e19229d8 100644
> > > > --- a/drivers/iio/iio_core.h
> > > > +++ b/drivers/iio/iio_core.h
> > > > @@ -17,6 +17,20 @@ struct iio_dev;
> > > >  
> > > >  extern struct device_type iio_device_type;
> > > >  
> > > > +#define IIO_IOCTL_UNHANDLED	1
> > > > +struct iio_ioctl_handler {
> > > > +	struct list_head entry;
> > > > +	long (*ioctl)(struct iio_dev *indio_dev, struct file *filp,
> > > > +		      unsigned int cmd, unsigned long arg);
> > > > +};
> > > > +
> > > > +long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
> > > > +		      unsigned int cmd, unsigned long arg);
> > > > +
> > > > +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
> > > > +				       struct iio_ioctl_handler *h);
> > > > +void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h);
> > > > +
> > > >  int __iio_add_chan_devattr(const char *postfix,
> > > >  			   struct iio_chan_spec const *chan,
> > > >  			   ssize_t (*func)(struct device *dev,
> > > > diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-
> > > > core.c
> > > > index aec585cc8453..79e8fa8ff70b 100644
> > > > --- a/drivers/iio/industrialio-core.c
> > > > +++ b/drivers/iio/industrialio-core.c
> > > > @@ -1531,6 +1531,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)
> > > >  	}
> > > >  	dev_set_name(&dev->dev, "iio:device%d", dev->id);
> > > >  	INIT_LIST_HEAD(&dev->buffer_list);
> > > > +	INIT_LIST_HEAD(&dev->ioctl_handlers);
> > > >  
> > > >  	return dev;
> > > >  }
> > > > @@ -1584,6 +1585,33 @@ struct iio_dev *devm_iio_device_alloc(struct
> > > > device
> > > > *dev, int sizeof_priv)
> > > >  }
> > > >  EXPORT_SYMBOL_GPL(devm_iio_device_alloc);
> > > >  
> > > > +void iio_device_ioctl_handler_register(struct iio_dev *indio_dev,
> > > > +				       struct iio_ioctl_handler *h)
> > > > +{
> > > > +	/* this assumes that all ioctl() handlers are statically
> > > > allocated */
> > > > +	list_add_tail(&h->entry, &indio_dev->ioctl_handlers);
> > > > +}
> > > > +
> > > > +long iio_device_ioctl(struct iio_dev *indio_dev, struct file *filp,
> > > > +		      unsigned int cmd, unsigned long arg)
> > > > +{
> > > > +	struct iio_ioctl_handler *h;
> > > > +	int ret;
> > > > +
> > > > +	if (!indio_dev->info)
> > > > +		return -ENODEV;
> > > > +
> > > > +	list_for_each_entry(h, &indio_dev->ioctl_handlers, entry) {
> > > > +		ret = h->ioctl(indio_dev, filp, cmd, arg);
> > > > +		if (ret == 0)
> > > > +			return 0;
> > > > +		if (ret != IIO_IOCTL_UNHANDLED)
> > > > +			return ret;
> > > > +	}
> > > > +
> > > > +	return -EINVAL;
> > > > +}
> > > > +
> > > >  static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
> > > >  {
> > > >  	int i, j;
> > > > @@ -1695,6 +1723,8 @@ EXPORT_SYMBOL(__iio_device_register);
> > > >   **/
> > > >  void iio_device_unregister(struct iio_dev *indio_dev)
> > > >  {
> > > > +	struct iio_ioctl_handler *h, *t;
> > > > +
> > > >  	if (indio_dev->chrdev)
> > > >  		cdev_device_del(indio_dev->chrdev, &indio_dev->dev);
> > > >  	else
> > > > @@ -1708,6 +1738,9 @@ void iio_device_unregister(struct iio_dev
> > > > *indio_dev)
> > > >  
> > > >  	iio_disable_all_buffers(indio_dev);
> > > >  
> > > > +	list_for_each_entry_safe(h, t, &indio_dev->ioctl_handlers,
> > > > entry)
> > > > +		list_del(&h->entry);
> > > > +  
> > > 
> > > Is there any chance anything is walking that list whilst we are deleting
> > > it?
> > > I think this needs to happen under a lock as does the walk.
> > 
> > We may want to use the rcu methods to make the walk and delete safe whilst
> > avoiding heavy weight locking on the read path
> > 
> > linux/rculist.h
> > 
> > I've not thought about this in much depth though so take that advice as
> > a vague suggestion and nothing more!
> 
> I'm a bit tempted to do the simple mutex approach, but I'll take a look at the
> rculist.h thingi.
> 
> The point is good about the list being walked while being deleted.

I'm thinking this could re-use the 'indio_dev->info_exist_lock' [i.e add the
lock in iio_device_ioctl()].
Since, the use of this lock is to guard against the 'indio_dev->info = NULL', we
can couple this with the list deletion hardening.
iio_device_ioctl() does have a check for 'indio_dev->info == NULL'

> 
> > 
> > > >  	indio_dev->info = NULL;
> > > >  
> > > >  	iio_device_wakeup_eventset(indio_dev);
> > > > diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
> > > > index 52992be44e9e..b6ca8d85629e 100644
> > > > --- a/include/linux/iio/iio.h
> > > > +++ b/include/linux/iio/iio.h
> > > > @@ -488,6 +488,7 @@ struct iio_buffer_setup_ops {
> > > >   * @currentmode:	[DRIVER] current operating mode
> > > >   * @dev:		[DRIVER] device structure, should be assigned a
> > > > parent
> > > >   *			and owner
> > > > + * @ioctl_handlers:	[INTERN] list of registered ioctl handlers
> > > >   * @event_interface:	[INTERN] event chrdevs associated with interrupt
> > > > lines
> > > >   * @buffer:		[DRIVER] any buffer present
> > > >   * @buffer_list:	[INTERN] list of all buffers currently attached
> > > > @@ -529,6 +530,7 @@ struct iio_dev {
> > > >  	int				modes;
> > > >  	int				currentmode;
> > > >  	struct device			dev;
> > > > +	struct list_head		ioctl_handlers;
> > > >  
> > > >  	struct iio_event_interface	*event_interface;
> > > >    

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

end of thread, other threads:[~2020-04-27 10:05 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-26  7:38 [PATCH v5 0/6] iio: core,buffer: re-organize chardev creation Alexandru Ardelean
2020-04-26  7:38 ` [PATCH v5 1/6] iio: buffer: add back-ref from iio_buffer to iio_dev Alexandru Ardelean
2020-04-26  7:38 ` [PATCH v5 2/6] iio: core,buffer: wrap iio_buffer_put() call into iio_buffers_put() Alexandru Ardelean
2020-04-26  7:38 ` [PATCH v5 3/6] iio: core: register chardev only if needed Alexandru Ardelean
2020-04-26  7:38 ` [PATCH v5 4/6] iio: buffer,event: duplicate chardev creation for buffers & events Alexandru Ardelean
2020-04-26 10:00   ` Jonathan Cameron
2020-04-27  6:38     ` Ardelean, Alexandru
2020-04-26  7:38 ` [PATCH v5 5/6] iio: core: add simple centralized mechanism for ioctl() handlers Alexandru Ardelean
2020-04-26 10:10   ` Jonathan Cameron
2020-04-26 10:21     ` Jonathan Cameron
2020-04-27  6:43       ` Ardelean, Alexandru
2020-04-27 10:04         ` Ardelean, Alexandru
2020-04-26  7:38 ` [PATCH v5 6/6] iio: core: use new common ioctl() mechanism Alexandru Ardelean
2020-04-26 10:13   ` 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).