Re: [PATCH 1/2] iio: Add reference counting for buffers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 09/21/13 16:21, Lars-Peter Clausen wrote:
> Since the buffer is accessed by userspace we can not just free the buffers
> memory once we are done with it in kernel space. There might still be open file
> descriptors and userspace still might be accessing the buffer. This patch adds
> support for reference counting to the IIO buffers. When a buffer is created and
> initialized its initial reference count is set to 1. Instead of freeing the
> memory of the buffer the buffer's _free() function will drop that reference
> again. But only after the last reference to the buffer has been dropped the
> buffer the buffer's memory will be freed. The IIO device will take a reference
> to its primary buffer. The patch adds a small helper function for this called
> iio_device_attach_buffer() which will get a reference to the buffer and assign
> the buffer to the IIO device. This function must be used instead of assigning
> the buffer to the device by hand. The reference is only dropped once the IIO
> device is freed and we can be sure that there are no more open file handles. A
> reference to a buffer will also be taken whenever the buffer is active to avoid
> the buffer being freed while data is still being send to it.
> 
> Signed-off-by: Lars-Peter Clausen <lars@xxxxxxxxxx>

Applied to fixes-togreg.

Thanks for sorting this out.

Jonathan
> ---
>  drivers/iio/buffer_cb.c                         | 21 +++++++----
>  drivers/iio/industrialio-buffer.c               | 41 +++++++++++++++++++---
>  drivers/iio/industrialio-core.c                 |  4 +++
>  drivers/iio/industrialio-triggered-buffer.c     |  7 ++--
>  drivers/iio/kfifo_buf.c                         |  8 ++++-
>  drivers/staging/iio/accel/lis3l02dq_ring.c      |  2 +-
>  drivers/staging/iio/accel/sca3000_ring.c        | 13 ++++---
>  drivers/staging/iio/iio_simple_dummy_buffer.c   |  2 +-
>  drivers/staging/iio/impedance-analyzer/ad5933.c |  8 +++--
>  drivers/staging/iio/meter/ade7758_ring.c        |  7 ++--
>  include/linux/iio/buffer.h                      | 46 +++++++++++++++++++++++++
>  11 files changed, 135 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c
> index 415f3c6..659c97f 100644
> --- a/drivers/iio/buffer_cb.c
> +++ b/drivers/iio/buffer_cb.c
> @@ -12,17 +12,27 @@ struct iio_cb_buffer {
>  	struct iio_channel *channels;
>  };
>  
> -static int iio_buffer_cb_store_to(struct iio_buffer *buffer, u8 *data)
> +static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
>  {
> -	struct iio_cb_buffer *cb_buff = container_of(buffer,
> -						     struct iio_cb_buffer,
> -						     buffer);
> +	return container_of(buffer, struct iio_cb_buffer, buffer);
> +}
>  
> +static int iio_buffer_cb_store_to(struct iio_buffer *buffer, u8 *data)
> +{
> +	struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
>  	return cb_buff->cb(data, cb_buff->private);
>  }
>  
> +static void iio_buffer_cb_release(struct iio_buffer *buffer)
> +{
> +	struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
> +	kfree(cb_buff->buffer.scan_mask);
> +	kfree(cb_buff);
> +}
> +
>  static struct iio_buffer_access_funcs iio_cb_access = {
>  	.store_to = &iio_buffer_cb_store_to,
> +	.release = &iio_buffer_cb_release,
>  };
>  
>  struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
> @@ -104,9 +114,8 @@ EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
>  
>  void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff)
>  {
> -	kfree(cb_buff->buffer.scan_mask);
>  	iio_channel_release_all(cb_buff->channels);
> -	kfree(cb_buff);
> +	iio_buffer_put(&cb_buff->buffer);
>  }
>  EXPORT_SYMBOL_GPL(iio_channel_release_all_cb);
>  
> diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
> index 2710f72..9f84b22 100644
> --- a/drivers/iio/industrialio-buffer.c
> +++ b/drivers/iio/industrialio-buffer.c
> @@ -80,6 +80,7 @@ void iio_buffer_init(struct iio_buffer *buffer)
>  {
>  	INIT_LIST_HEAD(&buffer->demux_list);
>  	init_waitqueue_head(&buffer->pollq);
> +	kref_init(&buffer->ref);
>  }
>  EXPORT_SYMBOL(iio_buffer_init);
>  
> @@ -460,6 +461,19 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask,
>  	return bytes;
>  }
>  
> +static void iio_buffer_activate(struct iio_dev *indio_dev,
> +	struct iio_buffer *buffer)
> +{
> +	iio_buffer_get(buffer);
> +	list_add(&buffer->buffer_list, &indio_dev->buffer_list);
> +}
> +
> +static void iio_buffer_deactivate(struct iio_buffer *buffer)
> +{
> +	list_del_init(&buffer->buffer_list);
> +	iio_buffer_put(buffer);
> +}
> +
>  void iio_disable_all_buffers(struct iio_dev *indio_dev)
>  {
>  	struct iio_buffer *buffer, *_buffer;
> @@ -472,7 +486,7 @@ void iio_disable_all_buffers(struct iio_dev *indio_dev)
>  
>  	list_for_each_entry_safe(buffer, _buffer,
>  			&indio_dev->buffer_list, buffer_list)
> -		list_del_init(&buffer->buffer_list);
> +		iio_buffer_deactivate(buffer);
>  
>  	indio_dev->currentmode = INDIO_DIRECT_MODE;
>  	if (indio_dev->setup_ops->postdisable)
> @@ -509,9 +523,9 @@ int iio_update_buffers(struct iio_dev *indio_dev,
>  		indio_dev->active_scan_mask = NULL;
>  
>  	if (remove_buffer)
> -		list_del(&remove_buffer->buffer_list);
> +		iio_buffer_deactivate(remove_buffer);
>  	if (insert_buffer)
> -		list_add(&insert_buffer->buffer_list, &indio_dev->buffer_list);
> +		iio_buffer_activate(indio_dev, insert_buffer);
>  
>  	/* If no buffers in list, we are done */
>  	if (list_empty(&indio_dev->buffer_list)) {
> @@ -546,7 +560,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,
>  			 * Roll back.
>  			 * Note can only occur when adding a buffer.
>  			 */
> -			list_del(&insert_buffer->buffer_list);
> +			iio_buffer_deactivate(insert_buffer);
>  			if (old_mask) {
>  				indio_dev->active_scan_mask = old_mask;
>  				success = -EINVAL;
> @@ -637,7 +651,7 @@ error_run_postdisable:
>  error_remove_inserted:
>  
>  	if (insert_buffer)
> -		list_del(&insert_buffer->buffer_list);
> +		iio_buffer_deactivate(insert_buffer);
>  	indio_dev->active_scan_mask = old_mask;
>  	kfree(compound_mask);
>  error_ret:
> @@ -958,3 +972,20 @@ error_clear_mux_table:
>  	return ret;
>  }
>  EXPORT_SYMBOL_GPL(iio_update_demux);
> +
> +/**
> + * __iio_buffer_release() - Free a buffer's resources
> + * @ref: Pointer to the kref embedded in the iio_buffer struct
> + *
> + * This function is called when the last reference to the buffer has been
> + * dropped. It will typically free all resources allocated by the buffer. Do not
> + * call this function manually, always use iio_buffer_put() when done using a
> + * buffer.
> + */
> +void __iio_buffer_release(struct kref *ref)
> +{
> +	struct iio_buffer *buffer = container_of(ref, struct iio_buffer, ref);
> +
> +	buffer->access->release(buffer);
> +}
> +EXPORT_SYMBOL_GPL(__iio_buffer_release);
> diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
> index 8e84cd5..d189f6c 100644
> --- a/drivers/iio/industrialio-core.c
> +++ b/drivers/iio/industrialio-core.c
> @@ -28,6 +28,7 @@
>  #include "iio_core_trigger.h"
>  #include <linux/iio/sysfs.h>
>  #include <linux/iio/events.h>
> +#include <linux/iio/buffer.h>
>  
>  /* IDA to assign each registered device a unique id */
>  static DEFINE_IDA(iio_ida);
> @@ -854,6 +855,9 @@ static void iio_dev_release(struct device *device)
>  	iio_device_unregister_sysfs(indio_dev);
>  	iio_device_unregister_debugfs(indio_dev);
>  
> +	if (indio_dev->buffer)
> +		iio_buffer_put(indio_dev->buffer);
> +
>  	ida_simple_remove(&iio_ida, indio_dev->id);
>  	kfree(indio_dev);
>  }
> diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c
> index 46c619b..c1cb1f9 100644
> --- a/drivers/iio/industrialio-triggered-buffer.c
> +++ b/drivers/iio/industrialio-triggered-buffer.c
> @@ -47,14 +47,17 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
>  	irqreturn_t (*pollfunc_th)(int irq, void *p),
>  	const struct iio_buffer_setup_ops *setup_ops)
>  {
> +	struct iio_buffer *buffer;
>  	int ret;
>  
> -	indio_dev->buffer = iio_kfifo_allocate(indio_dev);
> -	if (!indio_dev->buffer) {
> +	buffer = iio_kfifo_allocate(indio_dev);
> +	if (!buffer) {
>  		ret = -ENOMEM;
>  		goto error_ret;
>  	}
>  
> +	iio_device_attach_buffer(indio_dev, buffer);
> +
>  	indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,
>  						 pollfunc_th,
>  						 IRQF_ONESHOT,
> diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
> index a923c78..7ac90fb 100644
> --- a/drivers/iio/kfifo_buf.c
> +++ b/drivers/iio/kfifo_buf.c
> @@ -129,6 +129,11 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
>  	return copied;
>  }
>  
> +static void iio_kfifo_buffer_release(struct iio_buffer *buffer)
> +{
> +	kfree(iio_to_kfifo(buffer));
> +}
> +
>  static const struct iio_buffer_access_funcs kfifo_access_funcs = {
>  	.store_to = &iio_store_to_kfifo,
>  	.read_first_n = &iio_read_first_n_kfifo,
> @@ -137,6 +142,7 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = {
>  	.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
>  	.get_length = &iio_get_length_kfifo,
>  	.set_length = &iio_set_length_kfifo,
> +	.release = &iio_kfifo_buffer_release,
>  };
>  
>  struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
> @@ -157,7 +163,7 @@ EXPORT_SYMBOL(iio_kfifo_allocate);
>  
>  void iio_kfifo_free(struct iio_buffer *r)
>  {
> -	kfree(iio_to_kfifo(r));
> +	iio_buffer_put(r);
>  }
>  EXPORT_SYMBOL(iio_kfifo_free);
>  
> diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
> index 5b8f0f6..2a0d012 100644
> --- a/drivers/staging/iio/accel/lis3l02dq_ring.c
> +++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
> @@ -401,7 +401,7 @@ int lis3l02dq_configure_buffer(struct iio_dev *indio_dev)
>  	if (!buffer)
>  		return -ENOMEM;
>  
> -	indio_dev->buffer = buffer;
> +	iio_device_attach_buffer(indio_dev, buffer);
>  
>  	buffer->scan_timestamp = true;
>  	indio_dev->setup_ops = &lis3l02dq_buffer_setup_ops;
> diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c
> index 3e5e860..5ca80b3 100644
> --- a/drivers/staging/iio/accel/sca3000_ring.c
> +++ b/drivers/staging/iio/accel/sca3000_ring.c
> @@ -265,7 +265,7 @@ static struct iio_buffer *sca3000_rb_allocate(struct iio_dev *indio_dev)
>  	return buf;
>  }
>  
> -static inline void sca3000_rb_free(struct iio_buffer *r)
> +static void sca3000_ring_release(struct iio_buffer *r)
>  {
>  	kfree(iio_to_hw_buf(r));
>  }
> @@ -274,23 +274,28 @@ static const struct iio_buffer_access_funcs sca3000_ring_access_funcs = {
>  	.read_first_n = &sca3000_read_first_n_hw_rb,
>  	.get_length = &sca3000_ring_get_length,
>  	.get_bytes_per_datum = &sca3000_ring_get_bytes_per_datum,
> +	.release = sca3000_ring_release,
>  };
>  
>  int sca3000_configure_ring(struct iio_dev *indio_dev)
>  {
> -	indio_dev->buffer = sca3000_rb_allocate(indio_dev);
> -	if (indio_dev->buffer == NULL)
> +	struct iio_buffer *buffer;
> +
> +	buffer = sca3000_rb_allocate(indio_dev);
> +	if (buffer == NULL)
>  		return -ENOMEM;
>  	indio_dev->modes |= INDIO_BUFFER_HARDWARE;
>  
>  	indio_dev->buffer->access = &sca3000_ring_access_funcs;
>  
> +	iio_device_attach_buffer(indio_dev, buffer);
> +
>  	return 0;
>  }
>  
>  void sca3000_unconfigure_ring(struct iio_dev *indio_dev)
>  {
> -	sca3000_rb_free(indio_dev->buffer);
> +	iio_buffer_put(indio_dev->buffer);
>  }
>  
>  static inline
> diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c
> index 72f400c..272eaac 100644
> --- a/drivers/staging/iio/iio_simple_dummy_buffer.c
> +++ b/drivers/staging/iio/iio_simple_dummy_buffer.c
> @@ -138,7 +138,7 @@ int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev,
>  		goto error_ret;
>  	}
>  
> -	indio_dev->buffer = buffer;
> +	iio_device_attach_buffer(indio_dev, buffer);
>  
>  	/* Enable timestamps by default */
>  	buffer->scan_timestamp = true;
> diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
> index 6330af6..f282b34 100644
> --- a/drivers/staging/iio/impedance-analyzer/ad5933.c
> +++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
> @@ -630,10 +630,14 @@ static const struct iio_buffer_setup_ops ad5933_ring_setup_ops = {
>  
>  static int ad5933_register_ring_funcs_and_init(struct iio_dev *indio_dev)
>  {
> -	indio_dev->buffer = iio_kfifo_allocate(indio_dev);
> -	if (!indio_dev->buffer)
> +	struct iio_buffer *buffer;
> +
> +	buffer = iio_kfifo_allocate(indio_dev);
> +	if (buffer)
>  		return -ENOMEM;
>  
> +	iio_device_attach_buffer(indio_dev, buffer);
> +
>  	/* Ring buffer functions - here trigger setup related */
>  	indio_dev->setup_ops = &ad5933_ring_setup_ops;
>  
> diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c
> index 7d5db71..28d4a02 100644
> --- a/drivers/staging/iio/meter/ade7758_ring.c
> +++ b/drivers/staging/iio/meter/ade7758_ring.c
> @@ -125,14 +125,17 @@ void ade7758_unconfigure_ring(struct iio_dev *indio_dev)
>  int ade7758_configure_ring(struct iio_dev *indio_dev)
>  {
>  	struct ade7758_state *st = iio_priv(indio_dev);
> +	struct iio_buffer *buffer;
>  	int ret = 0;
>  
> -	indio_dev->buffer = iio_kfifo_allocate(indio_dev);
> -	if (!indio_dev->buffer) {
> +	buffer = iio_kfifo_allocate(indio_dev);
> +	if (!buffer) {
>  		ret = -ENOMEM;
>  		return ret;
>  	}
>  
> +	iio_device_attach_buffer(indio_dev, buffer);
> +
>  	indio_dev->setup_ops = &ade7758_ring_setup_ops;
>  
>  	indio_dev->pollfunc = iio_alloc_pollfunc(&iio_pollfunc_store_time,
> diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
> index 2bac0eb..3fd8256 100644
> --- a/include/linux/iio/buffer.h
> +++ b/include/linux/iio/buffer.h
> @@ -11,6 +11,7 @@
>  #define _IIO_BUFFER_GENERIC_H_
>  #include <linux/sysfs.h>
>  #include <linux/iio/iio.h>
> +#include <linux/kref.h>
>  
>  #ifdef CONFIG_IIO_BUFFER
>  
> @@ -26,6 +27,8 @@ struct iio_buffer;
>   * @set_bytes_per_datum:set number of bytes per datum
>   * @get_length:		get number of datums in buffer
>   * @set_length:		set number of datums in buffer
> + * @release:		called when the last reference to the buffer is dropped,
> + *			should free all resources allocated by the buffer.
>   *
>   * The purpose of this structure is to make the buffer element
>   * modular as event for a given driver, different usecases may require
> @@ -47,6 +50,8 @@ struct iio_buffer_access_funcs {
>  	int (*set_bytes_per_datum)(struct iio_buffer *buffer, size_t bpd);
>  	int (*get_length)(struct iio_buffer *buffer);
>  	int (*set_length)(struct iio_buffer *buffer, int length);
> +
> +	void (*release)(struct iio_buffer *buffer);
>  };
>  
>  /**
> @@ -67,6 +72,7 @@ struct iio_buffer_access_funcs {
>   * @demux_list:		[INTERN] list of operations required to demux the scan.
>   * @demux_bounce:	[INTERN] buffer for doing gather from incoming scan.
>   * @buffer_list:	[INTERN] entry in the devices list of current buffers.
> + * @ref:		[INTERN] reference count of the buffer.
>   */
>  struct iio_buffer {
>  	int					length;
> @@ -83,6 +89,7 @@ struct iio_buffer {
>  	struct list_head			demux_list;
>  	unsigned char				*demux_bounce;
>  	struct list_head			buffer_list;
> +	struct kref				ref;
>  };
>  
>  /**
> @@ -179,6 +186,42 @@ int iio_sw_buffer_preenable(struct iio_dev *indio_dev);
>  bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
>  	const unsigned long *mask);
>  
> +void __iio_buffer_release(struct kref *kref);
> +
> +/**
> + * iio_buffer_get() - Grab a reference to the buffer
> + * @buffer: The buffer to grab a reference for
> + */
> +static inline void iio_buffer_get(struct iio_buffer *buffer)
> +{
> +	kref_get(&buffer->ref);
> +}
> +
> +/**
> + * iio_buffer_put() - Release the reference to the buffer
> + * @buffer: The buffer to release the reference for
> + */
> +static inline void iio_buffer_put(struct iio_buffer *buffer)
> +{
> +	kref_put(&buffer->ref, __iio_buffer_release);
> +}
> +
> +/**
> + * iio_device_attach_buffer - Attach a buffer to a IIO device
> + * @indio_dev: The device the buffer should be attached to
> + * @buffer: The buffer to attach to the device
> + *
> + * This function attaches a buffer to a IIO device. The buffer stays attached to
> + * the device until the device is freed. The function should only be called at
> + * most once per device.
> + */
> +static inline void iio_device_attach_buffer(struct iio_dev *indio_dev,
> +	struct iio_buffer *buffer)
> +{
> +	iio_buffer_get(buffer);
> +	indio_dev->buffer = buffer;
> +}
> +
>  #else /* CONFIG_IIO_BUFFER */
>  
>  static inline int iio_buffer_register(struct iio_dev *indio_dev,
> @@ -191,6 +234,9 @@ static inline int iio_buffer_register(struct iio_dev *indio_dev,
>  static inline void iio_buffer_unregister(struct iio_dev *indio_dev)
>  {}
>  
> +static inline void iio_buffer_get(struct iio_buffer *buffer) {}
> +static inline void iio_buffer_put(struct iio_buffer *buffer) {}
> +
>  #endif /* CONFIG_IIO_BUFFER */
>  
>  #endif /* _IIO_BUFFER_GENERIC_H_ */
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux