mirror of
https://github.com/torvalds/linux.git
synced 2024-11-26 06:02:05 +00:00
ASoC: SOF: Add memory_info
file to debugfs
This file content describes memory allocation status at run-time, typically to detect memory leaks. Signed-off-by: Karol Trzcinski <karolx.trzcinski@linux.intel.com> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Kai Vehmanen <kai.vehmanen@linux.intel.com> Link: https://lore.kernel.org/r/20201124180017.2232128-5-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
6dd958955d
commit
5b10b62989
41
include/sound/sof/debug.h
Normal file
41
include/sound/sof/debug.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */
|
||||
/*
|
||||
* This file is provided under a dual BSD/GPLv2 license. When using or
|
||||
* redistributing this file, you may do so under either license.
|
||||
*
|
||||
* Copyright(c) 2020 Intel Corporation. All rights reserved.
|
||||
*
|
||||
* Author: Karol Trzcinski <karolx.trzcinski@linux.intel.com>
|
||||
*/
|
||||
|
||||
#ifndef __INCLUDE_SOUND_SOF_DEBUG_H__
|
||||
#define __INCLUDE_SOUND_SOF_DEBUG_H__
|
||||
|
||||
#include <sound/sof/header.h>
|
||||
|
||||
/** ABI3.18 */
|
||||
enum sof_ipc_dbg_mem_zone {
|
||||
SOF_IPC_MEM_ZONE_SYS = 0, /**< System zone */
|
||||
SOF_IPC_MEM_ZONE_SYS_RUNTIME = 1, /**< System-runtime zone */
|
||||
SOF_IPC_MEM_ZONE_RUNTIME = 2, /**< Runtime zone */
|
||||
SOF_IPC_MEM_ZONE_BUFFER = 3, /**< Buffer zone */
|
||||
};
|
||||
|
||||
/** ABI3.18 */
|
||||
struct sof_ipc_dbg_mem_usage_elem {
|
||||
uint32_t zone; /**< see sof_ipc_dbg_mem_zone */
|
||||
uint32_t id; /**< heap index within zone */
|
||||
uint32_t used; /**< number of bytes used in zone */
|
||||
uint32_t free; /**< number of bytes free to use within zone */
|
||||
uint32_t reserved; /**< for future use */
|
||||
} __packed;
|
||||
|
||||
/** ABI3.18 */
|
||||
struct sof_ipc_dbg_mem_usage {
|
||||
struct sof_ipc_reply rhdr; /**< generic IPC reply header */
|
||||
uint32_t reserved[4]; /**< reserved for future use */
|
||||
uint32_t num_elems; /**< elems[] counter */
|
||||
struct sof_ipc_dbg_mem_usage_elem elems[]; /**< memory usage information */
|
||||
} __packed;
|
||||
|
||||
#endif
|
@ -104,6 +104,7 @@ struct ext_man_dbg_abi {
|
||||
enum config_elem_type {
|
||||
SOF_EXT_MAN_CONFIG_EMPTY = 0,
|
||||
SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE = 1,
|
||||
SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN = 2, /**< ABI 3.18 */
|
||||
};
|
||||
|
||||
struct sof_config_elem {
|
||||
|
@ -52,6 +52,7 @@
|
||||
#define SOF_IPC_GLB_GDB_DEBUG SOF_GLB_TYPE(0xAU)
|
||||
#define SOF_IPC_GLB_TEST_MSG SOF_GLB_TYPE(0xBU)
|
||||
#define SOF_IPC_GLB_PROBE SOF_GLB_TYPE(0xCU)
|
||||
#define SOF_IPC_GLB_DEBUG SOF_GLB_TYPE(0xDU)
|
||||
|
||||
/*
|
||||
* DSP Command Message Types
|
||||
@ -118,6 +119,9 @@
|
||||
#define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002)
|
||||
#define SOF_IPC_TRACE_DMA_PARAMS_EXT SOF_CMD_TYPE(0x003)
|
||||
|
||||
/* debug */
|
||||
#define SOF_IPC_DEBUG_MEM_USAGE SOF_CMD_TYPE(0x001)
|
||||
|
||||
/* test */
|
||||
#define SOF_IPC_TEST_IPC_FLOOD SOF_CMD_TYPE(0x001)
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
/* SOF ABI version major, minor and patch numbers */
|
||||
#define SOF_ABI_MAJOR 3
|
||||
#define SOF_ABI_MINOR 17
|
||||
#define SOF_ABI_MINOR 18
|
||||
#define SOF_ABI_PATCH 0
|
||||
|
||||
/* SOF ABI version number. Format within 32bit word is MMmmmppp */
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/sof/ext_manifest.h>
|
||||
#include <sound/sof/debug.h>
|
||||
#include "sof-priv.h"
|
||||
#include "ops.h"
|
||||
|
||||
@ -626,6 +628,121 @@ int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_sof_debugfs_buf_item);
|
||||
|
||||
static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_size)
|
||||
{
|
||||
struct sof_ipc_cmd_hdr msg = {
|
||||
.size = sizeof(struct sof_ipc_cmd_hdr),
|
||||
.cmd = SOF_IPC_GLB_DEBUG | SOF_IPC_DEBUG_MEM_USAGE,
|
||||
};
|
||||
struct sof_ipc_dbg_mem_usage *reply;
|
||||
int len;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
reply = kmalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL);
|
||||
if (!reply)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = pm_runtime_get_sync(sdev->dev);
|
||||
if (ret < 0 && ret != -EACCES) {
|
||||
pm_runtime_put_noidle(sdev->dev);
|
||||
dev_err(sdev->dev, "error: enabling device failed: %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE);
|
||||
pm_runtime_mark_last_busy(sdev->dev);
|
||||
pm_runtime_put_autosuspend(sdev->dev);
|
||||
if (ret < 0 || reply->rhdr.error < 0) {
|
||||
ret = min(ret, reply->rhdr.error);
|
||||
dev_err(sdev->dev, "error: reading memory info failed, %d\n", ret);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (struct_size(reply, elems, reply->num_elems) != reply->rhdr.hdr.size) {
|
||||
dev_err(sdev->dev, "error: invalid memory info ipc struct size, %d\n",
|
||||
reply->rhdr.hdr.size);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0, len = 0; i < reply->num_elems; i++) {
|
||||
ret = snprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n",
|
||||
reply->elems[i].zone, reply->elems[i].id,
|
||||
reply->elems[i].used, reply->elems[i].free);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
len += ret;
|
||||
}
|
||||
|
||||
ret = len;
|
||||
error:
|
||||
kfree(reply);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t memory_info_read(struct file *file, char __user *to, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct snd_sof_dfsentry *dfse = file->private_data;
|
||||
struct snd_sof_dev *sdev = dfse->sdev;
|
||||
int data_length;
|
||||
|
||||
/* read memory info from FW only once for each file read */
|
||||
if (!*ppos) {
|
||||
dfse->buf_data_size = 0;
|
||||
data_length = memory_info_update(sdev, dfse->buf, dfse->size);
|
||||
if (data_length < 0)
|
||||
return data_length;
|
||||
dfse->buf_data_size = data_length;
|
||||
}
|
||||
|
||||
return simple_read_from_buffer(to, count, ppos, dfse->buf, dfse->buf_data_size);
|
||||
}
|
||||
|
||||
static int memory_info_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct snd_sof_dfsentry *dfse = inode->i_private;
|
||||
struct snd_sof_dev *sdev = dfse->sdev;
|
||||
|
||||
file->private_data = dfse;
|
||||
|
||||
/* allocate buffer memory only in first open run, to save memory when unused */
|
||||
if (!dfse->buf) {
|
||||
dfse->buf = devm_kmalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL);
|
||||
if (!dfse->buf)
|
||||
return -ENOMEM;
|
||||
dfse->size = PAGE_SIZE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations memory_info_fops = {
|
||||
.open = memory_info_open,
|
||||
.read = memory_info_read,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev)
|
||||
{
|
||||
struct snd_sof_dfsentry *dfse;
|
||||
|
||||
dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
|
||||
if (!dfse)
|
||||
return -ENOMEM;
|
||||
|
||||
/* don't allocate buffer before first usage, to save memory when unused */
|
||||
dfse->type = SOF_DFSENTRY_TYPE_BUF;
|
||||
dfse->sdev = sdev;
|
||||
|
||||
debugfs_create_file("memory_info", 0444, sdev->debugfs_root, dfse, &memory_info_fops);
|
||||
|
||||
/* add to dfsentry list */
|
||||
list_add(&dfse->list, &sdev->dfsentry_list);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init);
|
||||
|
||||
int snd_sof_dbg_init(struct snd_sof_dev *sdev)
|
||||
{
|
||||
const struct snd_sof_dsp_ops *ops = sof_ops(sdev);
|
||||
|
@ -181,6 +181,15 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd)
|
||||
str2 = "unknown type"; break;
|
||||
}
|
||||
break;
|
||||
case SOF_IPC_GLB_DEBUG:
|
||||
str = "GLB_DEBUG";
|
||||
switch (type) {
|
||||
case SOF_IPC_DEBUG_MEM_USAGE:
|
||||
str2 = "MEM_USAGE"; break;
|
||||
default:
|
||||
str2 = "unknown type"; break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
str = "unknown GLB command"; break;
|
||||
}
|
||||
|
@ -205,6 +205,7 @@ static int ext_man_get_config_data(struct snd_sof_dev *sdev,
|
||||
const struct sof_config_elem *elem;
|
||||
int elems_counter;
|
||||
int elems_size;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
/* calculate elements counter */
|
||||
@ -225,11 +226,20 @@ static int ext_man_get_config_data(struct snd_sof_dev *sdev,
|
||||
case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE:
|
||||
/* TODO: use ipc msg size from config data */
|
||||
break;
|
||||
case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN:
|
||||
if (sdev->first_boot && elem->value)
|
||||
ret = snd_sof_dbg_memory_info_init(sdev);
|
||||
break;
|
||||
default:
|
||||
dev_info(sdev->dev, "Unknown firmware configuration token %d value %d",
|
||||
elem->token, elem->value);
|
||||
break;
|
||||
}
|
||||
if (ret < 0) {
|
||||
dev_err(sdev->dev, "error: processing sof_ext_man_config_data failed for token %d value 0x%x, %d\n",
|
||||
elem->token, elem->value, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -290,6 +290,7 @@ enum sof_debugfs_access_type {
|
||||
/* FS entry for debug files that can expose DSP memories, registers */
|
||||
struct snd_sof_dfsentry {
|
||||
size_t size;
|
||||
size_t buf_data_size; /* length of buffered data for file read operation */
|
||||
enum sof_dfsentry_type type;
|
||||
/*
|
||||
* access_type specifies if the
|
||||
@ -523,6 +524,7 @@ void snd_sof_get_status(struct snd_sof_dev *sdev, u32 panic_code,
|
||||
void *stack, size_t stack_words);
|
||||
int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev);
|
||||
void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev);
|
||||
int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev);
|
||||
|
||||
/*
|
||||
* Platform specific ops.
|
||||
|
Loading…
Reference in New Issue
Block a user