Re: [PATCH v2 1/4] dmaengine: add per-channel sysfs attribute groups via chan_groups

Frank Li <[email protected]>
Newsgroups org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel
Message-ID <an91kOBKJuPtllP0@lizhi-Precision-Tower-5810>
On Mon, Jul 27, 2026 at 12:48:41PM -0600, Logan Gunthorpe wrote:
> Each channel already gets its own struct device (dma_chan_dev),
> registered with device_register()/device_unregister() and torn down
> correctly by chan_dev_release(). Let drivers add their own sysfs
> attributes to that device directly, via a new dma_device.chan_groups
> field.
>
> This avoids drivers needing a second, separately-managed kobject to
> expose driver-specific attributes. That's a use-after-free issue:
> kobject_put() can invoke the ktype's release() and free the structure
> embedding dma_chan while the core (or the driver) still expects to
> reference it afterward, so avoiding it requires careful ordering and
> bookkeeping that's easy to get wrong. The channel's struct device
> already has the correct lifetime, and sysfs_create_group() supports
> the same named-subdirectory layout (attribute_group.name) that a bare
> kobject would provide.
>
> To let show()/store() callbacks safely recover the struct dma_chan from
> the struct device they're attached to without exposing dma_list_mutex
> itself to drivers, add a small dma_chan_from_dev_lock() /
> dma_chan_from_dev_unlock() pair that take / release the lock, and
> wrap them in a dma_chan_from_dev CLASS so callers can write:
>
> 	CLASS(dma_chan_from_dev, c)(dev);
> 	if (!c)
> 		return -ENODEV;
>
> Signed-off-by: Logan Gunthorpe <[email protected]>
> ---
>  drivers/dma/dmaengine.c   | 27 +++++++++++++++++++++++++++
>  drivers/dma/dmaengine.h   | 19 +++++++++++++++++++
>  include/linux/dmaengine.h |  4 ++++
>  3 files changed, 50 insertions(+)
>
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index 9049171df857..060a0a482eb3 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -161,6 +161,32 @@ static struct dma_chan *dev_to_dma_chan(struct device *dev)
>  	return chan_dev->chan;
>  }
>
> +/**
> + * dma_chan_from_dev_lock - take dma_list_mutex and convert a channel's
> + *	struct device to its dma_chan
> + * @dev: the channel's struct device, embedded in struct dma_chan_dev
> + *
> + * Returns NULL if the channel has already been unregistered. Pairs with
> + * dma_chan_from_dev_unlock(); see the dma_chan_from_dev CLASS in
> + * drivers/dma/dmaengine.h.
> + */
> +struct dma_chan *dma_chan_from_dev_lock(struct device *dev)
> +{
> +	mutex_lock(&dma_list_mutex);
> +	return dev_to_dma_chan(dev);
> +}
> +EXPORT_SYMBOL_GPL(dma_chan_from_dev_lock);
> +

New dma API should start from dmaengine

Any one outside dmaengine need access dma_list_mutex? I'd like use
dma_list_mutex directly.


> +/**
> + * dma_chan_from_dev_unlock - release the lock taken by dma_chan_from_dev_lock()
> + * @chan: unused; matches the value produced by dma_chan_from_dev_lock()
> + */
> +void dma_chan_from_dev_unlock(struct dma_chan *chan)
> +{
> +	mutex_unlock(&dma_list_mutex);
> +}
> +EXPORT_SYMBOL_GPL(dma_chan_from_dev_unlock);
> +
>  static ssize_t memcpy_count_show(struct device *dev,
>  				 struct device_attribute *attr, char *buf)
>  {
> @@ -1098,6 +1124,7 @@ static int __dma_async_device_channel_register(struct dma_device *device,
>
>  	chan->dev->device.class = &dma_devclass;
>  	chan->dev->device.parent = device->dev;
> +	chan->dev->device.groups = device->chan_groups;
>  	chan->dev->chan = chan;
>  	chan->dev->dev_id = device->dev_id;
>  	if (!name)
> diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
> index 53f16d3f0029..224126e64d54 100644
> --- a/drivers/dma/dmaengine.h
> +++ b/drivers/dma/dmaengine.h
> @@ -182,6 +182,25 @@ dmaengine_desc_callback_valid(struct dmaengine_desc_callback *cb)
>  struct dma_chan *dma_get_slave_channel(struct dma_chan *chan);
>  struct dma_chan *dma_get_any_slave_channel(struct dma_device *device);
>
> +/*
> + * dma_chan_from_dev_lock() / dma_chan_from_dev_unlock() bracket a critical
> + * section across which a channel's struct device can be safely converted
> + * back to its struct dma_chan: dma_chan_from_dev_lock() returns NULL if the
> + * channel has already been unregistered, and the lock it takes must be held
> + * for as long as the returned channel (or anything derived from it) is
> + * accessed.
> + *
> + * Use these through the dma_chan_from_dev CLASS below rather than calling
> + * them directly.
> + */
> +struct dma_chan *dma_chan_from_dev_lock(struct device *dev);
> +void dma_chan_from_dev_unlock(struct dma_chan *chan);
> +
> +DEFINE_CLASS(dma_chan_from_dev, struct dma_chan *,
> +	     dma_chan_from_dev_unlock(_T),
> +	     dma_chan_from_dev_lock(dev),
> +	     struct device *dev)
> +
>  #ifdef CONFIG_DEBUG_FS
>  #include <linux/debugfs.h>
>
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index b3d251c9734e..f4e7861b2507 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -803,6 +803,9 @@ struct dma_filter {
>   * @dev: struct device reference for dma mapping api
>   * @owner: owner module (automatically set based on the provided dev)
>   * @chan_ida: unique channel ID
> + * @chan_groups: optional NULL-terminated array of sysfs attribute groups
> + *	added to each channel's struct device, for driver-specific per-channel
> + *	attributes
>   * @src_addr_widths: bit mask of src addr widths the device supports
>   *	Width is specified in bytes, e.g. for a device supporting
>   *	a width of 4 the mask should have BIT(4) set.
> @@ -886,6 +889,7 @@ struct dma_device {
>  	struct device *dev;
>  	struct module *owner;
>  	struct ida chan_ida;
> +	const struct attribute_group **chan_groups;

It is per channel group, why not put into dma_chan struct.

Frank

>
>  	u32 src_addr_widths;
>  	u32 dst_addr_widths;
> --
> 2.47.3
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.