Unverified Commit 5b10b629 authored by Karol Trzcinski's avatar Karol Trzcinski Committed by Mark Brown
Browse files

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: default avatarKarol Trzcinski <karolx.trzcinski@linux.intel.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: default avatarKai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20201124180017.2232128-5-kai.vehmanen@linux.intel.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 6dd95895
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
/* 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
+1 −0
Original line number Diff line number Diff line
@@ -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 {
+4 −0
Original line number Diff line number Diff line
@@ -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)

+1 −1
Original line number Diff line number Diff line
@@ -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 */
+117 −0
Original line number Diff line number Diff line
@@ -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);
Loading