diff mbox series

[v4,1/2] dmaengine: Add basic debugfs support

Message ID 20200228130747.22905-2-peter.ujfalusi@ti.com (mailing list archive)
State Changes Requested
Headers show
Series dmaengine: Initial debugfs support | expand

Commit Message

Peter Ujfalusi Feb. 28, 2020, 1:07 p.m. UTC
Via the /sys/kernel/debug/dmaengine/summary users can get information
about the DMA devices and the used channels.

Example output on am654-evm with audio using two channels and after running
dmatest on 4 channels:

dma0 (285c0000.dma-controller): number of channels: 96

dma1 (31150000.dma-controller): number of channels: 267
 dma1chan0    | 2b00000.mcasp:tx
 dma1chan1    | 2b00000.mcasp:rx
 dma1chan2    | in-use
 dma1chan3    | in-use
 dma1chan4    | in-use
 dma1chan5    | in-use

For slave channels we can show the device and the channel name a given
channel is requested.
For non slave devices the only information we know is that the channel is
in use.

DMA drivers can implement the optional dbg_summary_show callback to
provide controller specific information instead of the generic one.

It is easy to extend the generic dmaengine_summary_show() to print
additional information about the used channels.

In case DMA drivers want to create additional files under the dmaengine
directory to provide additional debug points, they can use the
dmaengine_get_debugfs_root() to get the dentry of the rootdir.

I have taken the idea from gpiolib and clk subsystems.

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
---
 drivers/dma/dmaengine.c   | 77 +++++++++++++++++++++++++++++++++++++++
 drivers/dma/dmaengine.h   |  6 +++
 include/linux/dmaengine.h | 12 +++++-
 3 files changed, 94 insertions(+), 1 deletion(-)

Comments

Vinod Koul March 2, 2020, 7:11 a.m. UTC | #1
On 28-02-20, 15:07, Peter Ujfalusi wrote:
> Via the /sys/kernel/debug/dmaengine/summary users can get information
> about the DMA devices and the used channels.
> 
> Example output on am654-evm with audio using two channels and after running
> dmatest on 4 channels:
> 
> dma0 (285c0000.dma-controller): number of channels: 96
> 
> dma1 (31150000.dma-controller): number of channels: 267
>  dma1chan0    | 2b00000.mcasp:tx
>  dma1chan1    | 2b00000.mcasp:rx
>  dma1chan2    | in-use
>  dma1chan3    | in-use
>  dma1chan4    | in-use
>  dma1chan5    | in-use
> 
> For slave channels we can show the device and the channel name a given
> channel is requested.
> For non slave devices the only information we know is that the channel is
> in use.
> 
> DMA drivers can implement the optional dbg_summary_show callback to
> provide controller specific information instead of the generic one.
> 
> It is easy to extend the generic dmaengine_summary_show() to print
> additional information about the used channels.
> 
> In case DMA drivers want to create additional files under the dmaengine
> directory to provide additional debug points, they can use the
> dmaengine_get_debugfs_root() to get the dentry of the rootdir.
> 
> I have taken the idea from gpiolib and clk subsystems.
> 
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
>  drivers/dma/dmaengine.c   | 77 +++++++++++++++++++++++++++++++++++++++
>  drivers/dma/dmaengine.h   |  6 +++
>  include/linux/dmaengine.h | 12 +++++-
>  3 files changed, 94 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index c3b1283b6d31..b5e8b737785c 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -760,6 +760,11 @@ struct dma_chan *dma_request_chan(struct device *dev, const char *name)
>  		return chan ? chan : ERR_PTR(-EPROBE_DEFER);
>  
>  found:
> +#ifdef CONFIG_DEBUG_FS
> +	chan->dbg_client_name = kasprintf(GFP_KERNEL, "%s:%s", dev_name(dev),
> +					  name);
> +#endif
> +
>  	chan->name = kasprintf(GFP_KERNEL, "dma:%s", name);
>  	if (!chan->name)
>  		return chan;
> @@ -837,6 +842,11 @@ void dma_release_channel(struct dma_chan *chan)
>  		chan->name = NULL;
>  		chan->slave = NULL;
>  	}
> +
> +#ifdef CONFIG_DEBUG_FS
> +	kfree(chan->dbg_client_name);
> +	chan->dbg_client_name = NULL;
> +#endif
>  	mutex_unlock(&dma_list_mutex);
>  }
>  EXPORT_SYMBOL_GPL(dma_release_channel);
> @@ -1562,3 +1572,70 @@ static int __init dma_bus_init(void)
>  	return class_register(&dma_devclass);
>  }
>  arch_initcall(dma_bus_init);
> +
> +#ifdef CONFIG_DEBUG_FS
> +#include <linux/debugfs.h>
> +
> +static struct dentry *rootdir;
> +
> +struct dentry *dmaengine_get_debugfs_root(void)
> +{
> +	return rootdir;
> +}
> +EXPORT_SYMBOL_GPL(dmaengine_get_debugfs_root);
> +
> +static void dmaengine_dbg_summary_show(struct seq_file *s,
> +				       struct dma_device *dma_dev)
> +{
> +	struct dma_chan *chan;
> +
> +	list_for_each_entry(chan, &dma_dev->channels, device_node) {
> +		if (chan->client_count) {
> +			seq_printf(s, " %-13s| %s", dma_chan_name(chan),
> +				   chan->dbg_client_name ?: "in-use");
> +
> +			if (chan->router)
> +				seq_printf(s, " (via router: %s)\n",
> +					dev_name(chan->router->dev));
> +			else
> +				seq_puts(s, "\n");
> +		}
> +	}
> +}
> +
> +static int dmaengine_summary_show(struct seq_file *s, void *data)
> +{
> +	struct dma_device *dma_dev = NULL;
> +
> +	mutex_lock(&dma_list_mutex);
> +	list_for_each_entry(dma_dev, &dma_device_list, global_node) {
> +		seq_printf(s, "dma%d (%s): number of channels: %u\n",
> +			   dma_dev->dev_id, dev_name(dma_dev->dev),
> +			   dma_dev->chancnt);
> +
> +		if (dma_dev->dbg_summary_show)
> +			dma_dev->dbg_summary_show(s, dma_dev);
> +		else
> +			dmaengine_dbg_summary_show(s, dma_dev);
> +
> +		if (!list_is_last(&dma_dev->global_node, &dma_device_list))
> +			seq_puts(s, "\n");
> +	}
> +	mutex_unlock(&dma_list_mutex);
> +
> +	return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(dmaengine_summary);
> +
> +static int __init dmaengine_debugfs_init(void)
> +{
> +	rootdir = debugfs_create_dir("dmaengine", NULL);
> +
> +	/* /sys/kernel/debug/dmaengine */
> +	debugfs_create_file("summary", 0444, rootdir, NULL,
> +			    &dmaengine_summary_fops);
> +	return 0;
> +}
> +late_initcall(dmaengine_debugfs_init);
> +
> +#endif	/* DEBUG_FS */
> diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
> index e8a320c9e57c..72cd7fe33638 100644
> --- a/drivers/dma/dmaengine.h
> +++ b/drivers/dma/dmaengine.h
> @@ -182,4 +182,10 @@ 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);
>  
> +#ifdef CONFIG_DEBUG_FS
> +#include <linux/debugfs.h>
> +
> +struct dentry *dmaengine_get_debugfs_root(void);

this needs to have an else defined with NULL return so that we dont
force users to wrap the code under CONFIG_DEBUG_FS..
Peter Ujfalusi March 2, 2020, 10:28 a.m. UTC | #2
Hi Vinod,

On 02/03/2020 9.11, Vinod Koul wrote:
>> diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
>> index e8a320c9e57c..72cd7fe33638 100644
>> --- a/drivers/dma/dmaengine.h
>> +++ b/drivers/dma/dmaengine.h
>> @@ -182,4 +182,10 @@ 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);
>>  
>> +#ifdef CONFIG_DEBUG_FS
>> +#include <linux/debugfs.h>
>> +
>> +struct dentry *dmaengine_get_debugfs_root(void);
> 
> this needs to have an else defined with NULL return so that we dont
> force users to wrap the code under CONFIG_DEBUG_FS..

Drivers would anyways should have their debugfs related code wrapped
within ifdef. There is no point of having the code complied when it can
not be used (no debugfs support).

But I can add the  else case if we really want to:

#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>

struct dentry *dmaengine_get_debugfs_root(void);

#else
struct dentry;
static inline struct dentry *dmaengine_get_debugfs_root(void)
{
	return NULL;
}
#endif /* CONFIG_DEBUG_FS */


- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
Peter Ujfalusi March 2, 2020, 11:53 a.m. UTC | #3
On 02/03/2020 12.28, Peter Ujfalusi wrote:
> Hi Vinod,
> 
> On 02/03/2020 9.11, Vinod Koul wrote:
>>> diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
>>> index e8a320c9e57c..72cd7fe33638 100644
>>> --- a/drivers/dma/dmaengine.h
>>> +++ b/drivers/dma/dmaengine.h
>>> @@ -182,4 +182,10 @@ 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);
>>>  
>>> +#ifdef CONFIG_DEBUG_FS
>>> +#include <linux/debugfs.h>
>>> +
>>> +struct dentry *dmaengine_get_debugfs_root(void);
>>
>> this needs to have an else defined with NULL return so that we dont
>> force users to wrap the code under CONFIG_DEBUG_FS..
> 
> Drivers would anyways should have their debugfs related code wrapped
> within ifdef. There is no point of having the code complied when it can
> not be used (no debugfs support).
> 
> But I can add the  else case if we really want to:
> 
> #ifdef CONFIG_DEBUG_FS
> #include <linux/debugfs.h>
> 
> struct dentry *dmaengine_get_debugfs_root(void);
> 
> #else
> struct dentry;
> static inline struct dentry *dmaengine_get_debugfs_root(void)
> {
> 	return NULL;
> }
> #endif /* CONFIG_DEBUG_FS */

It might be even better if the core creates directories for the dma
controllers in dma_async_device_register() and removes the whole
directory in dma_async_device_unregister()

Then drivers can get their per device root via:
#ifdef CONFIG_DEBUG_FS
static inline struct dentry *
dmaengine_get_debugfs_root(struct dma_device *dma_dev) {
	return dma_dev->dbg_dev_root;
}
#else
struct dentry;
static inline struct dentry *
dmaengine_get_debugfs_root(struct dma_device *dma_dev)
{
	return NULL;
}
#endif /* CONFIG_DEBUG_FS */

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
Vinod Koul March 3, 2020, 4:27 a.m. UTC | #4
On 02-03-20, 13:53, Peter Ujfalusi wrote:
> 
> 
> On 02/03/2020 12.28, Peter Ujfalusi wrote:
> > Hi Vinod,
> > 
> > On 02/03/2020 9.11, Vinod Koul wrote:
> >>> diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
> >>> index e8a320c9e57c..72cd7fe33638 100644
> >>> --- a/drivers/dma/dmaengine.h
> >>> +++ b/drivers/dma/dmaengine.h
> >>> @@ -182,4 +182,10 @@ 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);
> >>>  
> >>> +#ifdef CONFIG_DEBUG_FS
> >>> +#include <linux/debugfs.h>
> >>> +
> >>> +struct dentry *dmaengine_get_debugfs_root(void);
> >>
> >> this needs to have an else defined with NULL return so that we dont
> >> force users to wrap the code under CONFIG_DEBUG_FS..
> > 
> > Drivers would anyways should have their debugfs related code wrapped
> > within ifdef. There is no point of having the code complied when it can
> > not be used (no debugfs support).
> > 
> > But I can add the  else case if we really want to:
> > 
> > #ifdef CONFIG_DEBUG_FS
> > #include <linux/debugfs.h>
> > 
> > struct dentry *dmaengine_get_debugfs_root(void);
> > 
> > #else
> > struct dentry;
> > static inline struct dentry *dmaengine_get_debugfs_root(void)
> > {
> > 	return NULL;
> > }
> > #endif /* CONFIG_DEBUG_FS */
> 
> It might be even better if the core creates directories for the dma
> controllers in dma_async_device_register() and removes the whole
> directory in dma_async_device_unregister()

hmmm, i think makes sense and dentry can be part of dma_device

> 
> Then drivers can get their per device root via:
> #ifdef CONFIG_DEBUG_FS
> static inline struct dentry *
> dmaengine_get_debugfs_root(struct dma_device *dma_dev) {
> 	return dma_dev->dbg_dev_root;
> }

right!

> #else
> struct dentry;
> static inline struct dentry *
> dmaengine_get_debugfs_root(struct dma_device *dma_dev)
> {
> 	return NULL;
> }
> #endif /* CONFIG_DEBUG_FS */
diff mbox series

Patch

diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index c3b1283b6d31..b5e8b737785c 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -760,6 +760,11 @@  struct dma_chan *dma_request_chan(struct device *dev, const char *name)
 		return chan ? chan : ERR_PTR(-EPROBE_DEFER);
 
 found:
+#ifdef CONFIG_DEBUG_FS
+	chan->dbg_client_name = kasprintf(GFP_KERNEL, "%s:%s", dev_name(dev),
+					  name);
+#endif
+
 	chan->name = kasprintf(GFP_KERNEL, "dma:%s", name);
 	if (!chan->name)
 		return chan;
@@ -837,6 +842,11 @@  void dma_release_channel(struct dma_chan *chan)
 		chan->name = NULL;
 		chan->slave = NULL;
 	}
+
+#ifdef CONFIG_DEBUG_FS
+	kfree(chan->dbg_client_name);
+	chan->dbg_client_name = NULL;
+#endif
 	mutex_unlock(&dma_list_mutex);
 }
 EXPORT_SYMBOL_GPL(dma_release_channel);
@@ -1562,3 +1572,70 @@  static int __init dma_bus_init(void)
 	return class_register(&dma_devclass);
 }
 arch_initcall(dma_bus_init);
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+
+static struct dentry *rootdir;
+
+struct dentry *dmaengine_get_debugfs_root(void)
+{
+	return rootdir;
+}
+EXPORT_SYMBOL_GPL(dmaengine_get_debugfs_root);
+
+static void dmaengine_dbg_summary_show(struct seq_file *s,
+				       struct dma_device *dma_dev)
+{
+	struct dma_chan *chan;
+
+	list_for_each_entry(chan, &dma_dev->channels, device_node) {
+		if (chan->client_count) {
+			seq_printf(s, " %-13s| %s", dma_chan_name(chan),
+				   chan->dbg_client_name ?: "in-use");
+
+			if (chan->router)
+				seq_printf(s, " (via router: %s)\n",
+					dev_name(chan->router->dev));
+			else
+				seq_puts(s, "\n");
+		}
+	}
+}
+
+static int dmaengine_summary_show(struct seq_file *s, void *data)
+{
+	struct dma_device *dma_dev = NULL;
+
+	mutex_lock(&dma_list_mutex);
+	list_for_each_entry(dma_dev, &dma_device_list, global_node) {
+		seq_printf(s, "dma%d (%s): number of channels: %u\n",
+			   dma_dev->dev_id, dev_name(dma_dev->dev),
+			   dma_dev->chancnt);
+
+		if (dma_dev->dbg_summary_show)
+			dma_dev->dbg_summary_show(s, dma_dev);
+		else
+			dmaengine_dbg_summary_show(s, dma_dev);
+
+		if (!list_is_last(&dma_dev->global_node, &dma_device_list))
+			seq_puts(s, "\n");
+	}
+	mutex_unlock(&dma_list_mutex);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(dmaengine_summary);
+
+static int __init dmaengine_debugfs_init(void)
+{
+	rootdir = debugfs_create_dir("dmaengine", NULL);
+
+	/* /sys/kernel/debug/dmaengine */
+	debugfs_create_file("summary", 0444, rootdir, NULL,
+			    &dmaengine_summary_fops);
+	return 0;
+}
+late_initcall(dmaengine_debugfs_init);
+
+#endif	/* DEBUG_FS */
diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
index e8a320c9e57c..72cd7fe33638 100644
--- a/drivers/dma/dmaengine.h
+++ b/drivers/dma/dmaengine.h
@@ -182,4 +182,10 @@  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);
 
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+
+struct dentry *dmaengine_get_debugfs_root(void);
+#endif
+
 #endif
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index 64461fc64e1b..a0fff2f9f3b5 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -300,6 +300,8 @@  struct dma_router {
  * @chan_id: channel ID for sysfs
  * @dev: class device for sysfs
  * @name: backlink name for sysfs
+ * @dbg_client_name: slave name for debugfs in format:
+ *	dev_name(requester's dev):channel name, for example: "2b00000.mcasp:tx"
  * @device_node: used to add this to the device chan list
  * @local: per-cpu pointer to a struct dma_chan_percpu
  * @client_count: how many clients are using this channel
@@ -318,6 +320,9 @@  struct dma_chan {
 	int chan_id;
 	struct dma_chan_dev *dev;
 	const char *name;
+#ifdef CONFIG_DEBUG_FS
+	char *dbg_client_name;
+#endif
 
 	struct list_head device_node;
 	struct dma_chan_percpu __percpu *local;
@@ -805,7 +810,9 @@  struct dma_filter {
  *     called and there are no further references to this structure. This
  *     must be implemented to free resources however many existing drivers
  *     do not and are therefore not safe to unbind while in use.
- *
+ * @dbg_summary_show: optional routine to show contents in debugfs; default code
+ *     will be used when this is omitted, but custom code can show extra,
+ *     controller specific information.
  */
 struct dma_device {
 	struct kref ref;
@@ -891,6 +898,9 @@  struct dma_device {
 					    struct dma_tx_state *txstate);
 	void (*device_issue_pending)(struct dma_chan *chan);
 	void (*device_release)(struct dma_device *dev);
+#ifdef CONFIG_DEBUG_FS
+	void (*dbg_summary_show)(struct seq_file *s, struct dma_device *dev);
+#endif
 };
 
 static inline int dmaengine_slave_config(struct dma_chan *chan,