Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Karol Trzcinski and committed by
Mark Brown
5b10b629 6dd95895

+185 -1
+41
include/sound/sof/debug.h
··· 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 + * Author: Karol Trzcinski <karolx.trzcinski@linux.intel.com> 9 + */ 10 + 11 + #ifndef __INCLUDE_SOUND_SOF_DEBUG_H__ 12 + #define __INCLUDE_SOUND_SOF_DEBUG_H__ 13 + 14 + #include <sound/sof/header.h> 15 + 16 + /** ABI3.18 */ 17 + enum sof_ipc_dbg_mem_zone { 18 + SOF_IPC_MEM_ZONE_SYS = 0, /**< System zone */ 19 + SOF_IPC_MEM_ZONE_SYS_RUNTIME = 1, /**< System-runtime zone */ 20 + SOF_IPC_MEM_ZONE_RUNTIME = 2, /**< Runtime zone */ 21 + SOF_IPC_MEM_ZONE_BUFFER = 3, /**< Buffer zone */ 22 + }; 23 + 24 + /** ABI3.18 */ 25 + struct sof_ipc_dbg_mem_usage_elem { 26 + uint32_t zone; /**< see sof_ipc_dbg_mem_zone */ 27 + uint32_t id; /**< heap index within zone */ 28 + uint32_t used; /**< number of bytes used in zone */ 29 + uint32_t free; /**< number of bytes free to use within zone */ 30 + uint32_t reserved; /**< for future use */ 31 + } __packed; 32 + 33 + /** ABI3.18 */ 34 + struct sof_ipc_dbg_mem_usage { 35 + struct sof_ipc_reply rhdr; /**< generic IPC reply header */ 36 + uint32_t reserved[4]; /**< reserved for future use */ 37 + uint32_t num_elems; /**< elems[] counter */ 38 + struct sof_ipc_dbg_mem_usage_elem elems[]; /**< memory usage information */ 39 + } __packed; 40 + 41 + #endif
+1
include/sound/sof/ext_manifest.h
··· 104 104 enum config_elem_type { 105 105 SOF_EXT_MAN_CONFIG_EMPTY = 0, 106 106 SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE = 1, 107 + SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN = 2, /**< ABI 3.18 */ 107 108 }; 108 109 109 110 struct sof_config_elem {
+4
include/sound/sof/header.h
··· 52 52 #define SOF_IPC_GLB_GDB_DEBUG SOF_GLB_TYPE(0xAU) 53 53 #define SOF_IPC_GLB_TEST_MSG SOF_GLB_TYPE(0xBU) 54 54 #define SOF_IPC_GLB_PROBE SOF_GLB_TYPE(0xCU) 55 + #define SOF_IPC_GLB_DEBUG SOF_GLB_TYPE(0xDU) 55 56 56 57 /* 57 58 * DSP Command Message Types ··· 118 117 #define SOF_IPC_TRACE_DMA_PARAMS SOF_CMD_TYPE(0x001) 119 118 #define SOF_IPC_TRACE_DMA_POSITION SOF_CMD_TYPE(0x002) 120 119 #define SOF_IPC_TRACE_DMA_PARAMS_EXT SOF_CMD_TYPE(0x003) 120 + 121 + /* debug */ 122 + #define SOF_IPC_DEBUG_MEM_USAGE SOF_CMD_TYPE(0x001) 121 123 122 124 /* test */ 123 125 #define SOF_IPC_TEST_IPC_FLOOD SOF_CMD_TYPE(0x001)
+1 -1
include/uapi/sound/sof/abi.h
··· 26 26 27 27 /* SOF ABI version major, minor and patch numbers */ 28 28 #define SOF_ABI_MAJOR 3 29 - #define SOF_ABI_MINOR 17 29 + #define SOF_ABI_MINOR 18 30 30 #define SOF_ABI_PATCH 0 31 31 32 32 /* SOF ABI version number. Format within 32bit word is MMmmmppp */
+117
sound/soc/sof/debug.c
··· 14 14 #include <linux/debugfs.h> 15 15 #include <linux/io.h> 16 16 #include <linux/pm_runtime.h> 17 + #include <sound/sof/ext_manifest.h> 18 + #include <sound/sof/debug.h> 17 19 #include "sof-priv.h" 18 20 #include "ops.h" 19 21 ··· 627 625 return 0; 628 626 } 629 627 EXPORT_SYMBOL_GPL(snd_sof_debugfs_buf_item); 628 + 629 + static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_size) 630 + { 631 + struct sof_ipc_cmd_hdr msg = { 632 + .size = sizeof(struct sof_ipc_cmd_hdr), 633 + .cmd = SOF_IPC_GLB_DEBUG | SOF_IPC_DEBUG_MEM_USAGE, 634 + }; 635 + struct sof_ipc_dbg_mem_usage *reply; 636 + int len; 637 + int ret; 638 + int i; 639 + 640 + reply = kmalloc(SOF_IPC_MSG_MAX_SIZE, GFP_KERNEL); 641 + if (!reply) 642 + return -ENOMEM; 643 + 644 + ret = pm_runtime_get_sync(sdev->dev); 645 + if (ret < 0 && ret != -EACCES) { 646 + pm_runtime_put_noidle(sdev->dev); 647 + dev_err(sdev->dev, "error: enabling device failed: %d\n", ret); 648 + goto error; 649 + } 650 + 651 + ret = sof_ipc_tx_message(sdev->ipc, msg.cmd, &msg, msg.size, reply, SOF_IPC_MSG_MAX_SIZE); 652 + pm_runtime_mark_last_busy(sdev->dev); 653 + pm_runtime_put_autosuspend(sdev->dev); 654 + if (ret < 0 || reply->rhdr.error < 0) { 655 + ret = min(ret, reply->rhdr.error); 656 + dev_err(sdev->dev, "error: reading memory info failed, %d\n", ret); 657 + goto error; 658 + } 659 + 660 + if (struct_size(reply, elems, reply->num_elems) != reply->rhdr.hdr.size) { 661 + dev_err(sdev->dev, "error: invalid memory info ipc struct size, %d\n", 662 + reply->rhdr.hdr.size); 663 + ret = -EINVAL; 664 + goto error; 665 + } 666 + 667 + for (i = 0, len = 0; i < reply->num_elems; i++) { 668 + ret = snprintf(buf + len, buff_size - len, "zone %d.%d used %#8x free %#8x\n", 669 + reply->elems[i].zone, reply->elems[i].id, 670 + reply->elems[i].used, reply->elems[i].free); 671 + if (ret < 0) 672 + goto error; 673 + len += ret; 674 + } 675 + 676 + ret = len; 677 + error: 678 + kfree(reply); 679 + return ret; 680 + } 681 + 682 + static ssize_t memory_info_read(struct file *file, char __user *to, size_t count, loff_t *ppos) 683 + { 684 + struct snd_sof_dfsentry *dfse = file->private_data; 685 + struct snd_sof_dev *sdev = dfse->sdev; 686 + int data_length; 687 + 688 + /* read memory info from FW only once for each file read */ 689 + if (!*ppos) { 690 + dfse->buf_data_size = 0; 691 + data_length = memory_info_update(sdev, dfse->buf, dfse->size); 692 + if (data_length < 0) 693 + return data_length; 694 + dfse->buf_data_size = data_length; 695 + } 696 + 697 + return simple_read_from_buffer(to, count, ppos, dfse->buf, dfse->buf_data_size); 698 + } 699 + 700 + static int memory_info_open(struct inode *inode, struct file *file) 701 + { 702 + struct snd_sof_dfsentry *dfse = inode->i_private; 703 + struct snd_sof_dev *sdev = dfse->sdev; 704 + 705 + file->private_data = dfse; 706 + 707 + /* allocate buffer memory only in first open run, to save memory when unused */ 708 + if (!dfse->buf) { 709 + dfse->buf = devm_kmalloc(sdev->dev, PAGE_SIZE, GFP_KERNEL); 710 + if (!dfse->buf) 711 + return -ENOMEM; 712 + dfse->size = PAGE_SIZE; 713 + } 714 + 715 + return 0; 716 + } 717 + 718 + static const struct file_operations memory_info_fops = { 719 + .open = memory_info_open, 720 + .read = memory_info_read, 721 + .llseek = default_llseek, 722 + }; 723 + 724 + int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev) 725 + { 726 + struct snd_sof_dfsentry *dfse; 727 + 728 + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); 729 + if (!dfse) 730 + return -ENOMEM; 731 + 732 + /* don't allocate buffer before first usage, to save memory when unused */ 733 + dfse->type = SOF_DFSENTRY_TYPE_BUF; 734 + dfse->sdev = sdev; 735 + 736 + debugfs_create_file("memory_info", 0444, sdev->debugfs_root, dfse, &memory_info_fops); 737 + 738 + /* add to dfsentry list */ 739 + list_add(&dfse->list, &sdev->dfsentry_list); 740 + return 0; 741 + } 742 + EXPORT_SYMBOL_GPL(snd_sof_dbg_memory_info_init); 630 743 631 744 int snd_sof_dbg_init(struct snd_sof_dev *sdev) 632 745 {
+9
sound/soc/sof/ipc.c
··· 181 181 str2 = "unknown type"; break; 182 182 } 183 183 break; 184 + case SOF_IPC_GLB_DEBUG: 185 + str = "GLB_DEBUG"; 186 + switch (type) { 187 + case SOF_IPC_DEBUG_MEM_USAGE: 188 + str2 = "MEM_USAGE"; break; 189 + default: 190 + str2 = "unknown type"; break; 191 + } 192 + break; 184 193 default: 185 194 str = "unknown GLB command"; break; 186 195 }
+10
sound/soc/sof/loader.c
··· 205 205 const struct sof_config_elem *elem; 206 206 int elems_counter; 207 207 int elems_size; 208 + int ret = 0; 208 209 int i; 209 210 210 211 /* calculate elements counter */ ··· 226 225 case SOF_EXT_MAN_CONFIG_IPC_MSG_SIZE: 227 226 /* TODO: use ipc msg size from config data */ 228 227 break; 228 + case SOF_EXT_MAN_CONFIG_MEMORY_USAGE_SCAN: 229 + if (sdev->first_boot && elem->value) 230 + ret = snd_sof_dbg_memory_info_init(sdev); 231 + break; 229 232 default: 230 233 dev_info(sdev->dev, "Unknown firmware configuration token %d value %d", 231 234 elem->token, elem->value); 232 235 break; 236 + } 237 + if (ret < 0) { 238 + dev_err(sdev->dev, "error: processing sof_ext_man_config_data failed for token %d value 0x%x, %d\n", 239 + elem->token, elem->value, ret); 240 + return ret; 233 241 } 234 242 } 235 243
+2
sound/soc/sof/sof-priv.h
··· 290 290 /* FS entry for debug files that can expose DSP memories, registers */ 291 291 struct snd_sof_dfsentry { 292 292 size_t size; 293 + size_t buf_data_size; /* length of buffered data for file read operation */ 293 294 enum sof_dfsentry_type type; 294 295 /* 295 296 * access_type specifies if the ··· 524 523 void *stack, size_t stack_words); 525 524 int snd_sof_init_trace_ipc(struct snd_sof_dev *sdev); 526 525 void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev); 526 + int snd_sof_dbg_memory_info_init(struct snd_sof_dev *sdev); 527 527 528 528 /* 529 529 * Platform specific ops.