Skip to content

Commit 03c2f48

Browse files
committed
ASoC: SOF: Add memory_info file to debugfs
This file content describes firmware memory allocation retrieved at run-time, typically to detect memory leaks. Signed-off-by: Karol Trzcinski <karolx.trzcinski@linux.intel.com>
1 parent a987755 commit 03c2f48

File tree

5 files changed

+165
-1
lines changed

5 files changed

+165
-1
lines changed

include/sound/sof/debug.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
2+
/*
3+
* This file is provided under a dual BSD/GPLv2 license. When using or
4+
* redistributing this file, you may do so under either license.
5+
*
6+
* Copyright(c) 2020 Intel Corporation. All rights reserved.
7+
*/
8+
9+
#ifndef __INCLUDE_SOUND_SOF_DEBUG_H__
10+
#define __INCLUDE_SOUND_SOF_DEBUG_H__
11+
12+
#include <sound/sof/header.h>
13+
14+
/** ABI3.18 */
15+
enum sof_ipc_dbg_mem_zone {
16+
SOF_IPC_MEM_ZONE_SYS = 0, /**< System zone */
17+
SOF_IPC_MEM_ZONE_SYS_RUNTIME = 1, /**< System-runtime zone */
18+
SOF_IPC_MEM_ZONE_RUNTIME = 2, /**< Runtime zone */
19+
SOF_IPC_MEM_ZONE_BUFFER = 3, /**< Buffer zone */
20+
};
21+
22+
/** ABI3.18 */
23+
struct sof_ipc_dbg_mem_usage_elem {
24+
uint32_t zone; /**< see sof_ipc_dbg_mem_zone */
25+
uint32_t id; /**< heap index within zone */
26+
uint32_t used; /**< number of bytes used in zone */
27+
uint32_t free; /**< number of bytes free to use within zone */
28+
uint32_t reserved; /**< for future use */
29+
} __attribute__((packed));
30+
31+
/** ABI3.18 */
32+
struct sof_ipc_dbg_mem_usage {
33+
struct sof_ipc_reply rhdr; /**< generic IPC reply header */
34+
uint32_t reserved[8]; /**< reserved for future use */
35+
uint32_t num_elems; /**< elems[] counter */
36+
struct sof_ipc_dbg_mem_usage_elem elems[]; /**< memory usage information */
37+
} __attribute__((packed));
38+
39+
#endif

include/sound/sof/header.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#define SOF_IPC_GLB_GDB_DEBUG SOF_GLB_TYPE(0xAU)
5353
#define SOF_IPC_GLB_TEST_MSG SOF_GLB_TYPE(0xBU)
5454
#define SOF_IPC_GLB_PROBE SOF_GLB_TYPE(0xCU)
55+
#define SOF_IPC_GLB_DEBUG SOF_GLB_TYPE(0xDU)
5556

5657
/*
5758
* DSP Command Message Types
@@ -118,6 +119,9 @@
118119
#define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002)
119120
#define SOF_IPC_TRACE_DMA_PARAMS_EXT SOF_CMD_TYPE(0x003)
120121

122+
/* debug */
123+
#define SOF_IPC_DEBUG_MEM_USAGE SOF_CMD_TYPE(0x001)
124+
121125
/* test */
122126
#define SOF_IPC_TEST_IPC_FLOOD SOF_CMD_TYPE(0x001)
123127

include/uapi/sound/sof/abi.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
/* SOF ABI version major, minor and patch numbers */
2828
#define SOF_ABI_MAJOR 3
29-
#define SOF_ABI_MINOR 17
29+
#define SOF_ABI_MINOR 18
3030
#define SOF_ABI_PATCH 0
3131

3232
/* SOF ABI version number. Format within 32bit word is MMmmmppp */

sound/soc/sof/debug.c

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include <linux/debugfs.h>
1515
#include <linux/io.h>
1616
#include <linux/pm_runtime.h>
17+
#include <sound/sof/ext_manifest.h>
18+
#include <sound/sof/debug.h>
1719
#include "sof-priv.h"
1820
#include "ops.h"
1921

@@ -626,10 +628,120 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
626628
}
627629
EXPORT_SYMBOL_GPL(snd_sof_debugfs_buf_item);
628630

631+
static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_size)
632+
{
633+
struct sof_ipc_cmd_hdr msg = {
634+
.size = sizeof(struct sof_ipc_cmd_hdr),
635+
.cmd = SOF_IPC_GLB_DEBUG | SOF_IPC_DEBUG_MEM_USAGE,
636+
};
637+
struct sof_ipc_dbg_mem_usage *reply;
638+
int len;
639+
int ret;
640+
int i;
641+
642+
reply = kmalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
643+
if (!reply)
644+
return -ENOMEM;
645+
646+
ret = pm_runtime_get_sync(sdev->dev);
647+
if (ret < 0 && ret != -EACCES) {
648+
pm_runtime_put_noidle(sdev->dev);
649+
dev_err(sdev->dev, "error: enabling device failed: %d\n", ret);
650+
goto error;
651+
}
652+
653+
ret = sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
654+
pm_runtime_mark_last_busy(sdev->dev);
655+
pm_runtime_put_autosuspend(sdev->dev);
656+
if (ret < 0 || reply->rhdr.error < 0) {
657+
ret = min(ret, reply->rhdr.error);
658+
dev_err(sdev->dev, "error: reading memory info failed, %d\n", ret);
659+
goto error;
660+
}
661+
662+
if (struct_size(reply, elems, reply->num_elems) != reply->rhdr.hdr.size) {
663+
dev_err(sdev->dev, "error: invalid memory info ipc struct size, %d\n",
664+
reply->rhdr.hdr.size);
665+
ret = -EINVAL;
666+
goto error;
667+
}
668+
669+
for (i = 0, len = 0; i < reply->num_elems; i++) {
670+
ret = snprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n",
671+
reply->elems[i].zone, reply->elems[i].id,
672+
reply->elems[i].used, reply->elems[i].free);
673+
if (ret < 0)
674+
goto error;
675+
len += ret;
676+
}
677+
678+
return len;
679+
error:
680+
kfree(reply);
681+
return ret;
682+
}
683+
684+
static ssize_t memory_info_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
685+
{
686+
struct snd_sof_dfsentry *dfse = file->private_data;
687+
struct snd_sof_dev *sdev = dfse->sdev;
688+
int data_length;
689+
690+
/* allocate buffer memory only in first function run, to save memory when unused */
691+
if (!dfse->buf) {
692+
dfse->size = PAGE_SIZE;
693+
dfse->buf = devm_kmalloc(sdev->dev, dfse->size, GFP_KERNEL);
694+
if (!dfse->buf) {
695+
dfse->size = 0;
696+
return -ENOMEM;
697+
}
698+
}
699+
700+
/* read memory info from FW only once for each file read */
701+
if (!*ppos) {
702+
dfse->buf_data_size = 0;
703+
data_length = memory_info_update(sdev, dfse->buf, dfse->size);
704+
if (data_length < 0)
705+
return data_length;
706+
dfse->buf_data_size = data_length;
707+
}
708+
709+
return simple_read_from_buffer(to, count, ppos, dfse->buf, dfse->buf_data_size);
710+
}
711+
712+
static const struct file_operations memory_info_fops = {
713+
.open = simple_open,
714+
.read = memory_info_read,
715+
.llseek = default_llseek,
716+
};
717+
718+
static int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev)
719+
{
720+
struct snd_sof_dfsentry *dfse;
721+
722+
dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
723+
if (!dfse)
724+
goto error;
725+
726+
/* don't allocate buffer before first usage, to save memory when unused */
727+
dfse->type = SOF_DFSENTRY_TYPE_BUF;
728+
dfse->sdev = sdev;
729+
730+
debugfs_create_file("memory_info", 0444, sdev->debugfs_root, dfse, &memory_info_fops);
731+
732+
/* add to dfsentry list */
733+
list_add(&dfse->list, &sdev->dfsentry_list);
734+
return 0;
735+
error:
736+
kfree(dfse);
737+
return -ENOMEM;
738+
}
739+
629740
int snd_sof_dbg_init(struct snd_sof_dev *sdev)
630741
{
631742
const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
632743
const struct snd_sof_debugfs_map *map;
744+
uint32_t memory_scan_en;
633745
int i;
634746
int err;
635747

@@ -651,6 +763,14 @@ int snd_sof_dbg_init(struct snd_sof_dev *sdev)
651763
return err;
652764
}
653765

766+
/* create read-only memory_info debugfs entry */
767+
err = snd_sof_get_config_value(sdev, EXT_MAN_CONFIG_MEMORY_USAGE_SCAN, &memory_scan_en);
768+
if (!err && memory_scan_en) {
769+
err = snd_sof_dbg_memory_info_init(sdev);
770+
if (err < 0)
771+
return err;
772+
}
773+
654774
#if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
655775
err = snd_sof_debugfs_probe_item(sdev, "probe_points",
656776
0644, &probe_points_fops);

sound/soc/sof/sof-priv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ enum sof_debugfs_access_type {
290290
/* FS entry for debug files that can expose DSP memories, registers */
291291
struct snd_sof_dfsentry {
292292
size_t size;
293+
size_t buf_data_size; /* length of buffered data for file read operation */
293294
enum sof_dfsentry_type type;
294295
/*
295296
* access_type specifies if the

0 commit comments

Comments
 (0)