[PATCH 0/8] ASoC: SOF: Introduce generic (in)firmware tracing infrastructure
Hi,
SOF is using dma-trace (or dtrace) as a firmware tracing method, which is only supported with IPC3 and it is not applicable for IPC4.
Currently the dtrace is 'open managed' regardless of IPC version (we do force disable it for IPC4, but the dtrace calls remain in place).
From the kernel point of view there are only few ops that needs to be exposed
by the firmware tracing support and everything else is IPC private, should not be known by the core.
This series converts the current dma-trace as ipc3 specific firmware tracing sub-component and moves all private data out from generic code.
Regards, Peter --- Peter Ujfalusi (8): ASoC: SOF: Introduce IPC independent ops for firmware tracing support ASoC: SOF: Rename dtrace_is_supported flag to fw_trace_is_supported ASoC: SOF: Clone the trace code to ipc3-dtrace as fw_tracing implementation ASoC: SOF: Switch to IPC generic firmware tracing ASoC: SOF: ipc3-dtrace: Move host ops wrappers from generic header to private ASoC: SOF: Modify the host trace_init parameter list to include dmab ASoC: SOF: Introduce opaque storage of private data for firmware tracing ASoC: SOF: ipc3-dtrace: Move dtrace related variables local from sof_dev
sound/soc/sof/Makefile | 1 + sound/soc/sof/amd/acp-trace.c | 4 +- sound/soc/sof/amd/acp.h | 2 +- sound/soc/sof/core.c | 13 +- sound/soc/sof/debug.c | 2 +- sound/soc/sof/intel/hda-dsp.c | 2 +- sound/soc/sof/intel/hda-trace.c | 4 +- sound/soc/sof/intel/hda.h | 2 +- sound/soc/sof/ipc.c | 6 + sound/soc/sof/ipc3-dtrace.c | 649 ++++++++++++++++++++++++++++++++ sound/soc/sof/ipc3-priv.h | 38 ++ sound/soc/sof/ipc3.c | 3 +- sound/soc/sof/ops.c | 2 +- sound/soc/sof/ops.h | 26 -- sound/soc/sof/pm.c | 8 +- sound/soc/sof/sof-priv.h | 53 +-- sound/soc/sof/trace.c | 621 ++---------------------------- 17 files changed, 767 insertions(+), 669 deletions(-) create mode 100644 sound/soc/sof/ipc3-dtrace.c
The current (dma-)trace is only supported with IPC3, it is not available when IPC4 is used.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/sof-priv.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 8ea196305e4b..a7ffb6ecf332 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -357,6 +357,22 @@ struct snd_sof_ipc_msg { bool ipc_complete; };
+/** + * struct sof_ipc_fw_tracing_ops - IPC-specific firmware tracing ops + * @init: Function pointer for initialization of the tracing + * @free: Optional function pointer for freeing of the tracing + * @fw_crashed: Optional function pointer to notify the tracing of a firmware crash + * @suspend: Function pointer for system/runtime suspend + * @resume: Function pointer for system/runtime resume + */ +struct sof_ipc_fw_tracing_ops { + int (*init)(struct snd_sof_dev *sdev); + void (*free)(struct snd_sof_dev *sdev); + void (*fw_crashed)(struct snd_sof_dev *sdev); + void (*suspend)(struct snd_sof_dev *sdev, pm_message_t pm_state); + int (*resume)(struct snd_sof_dev *sdev); +}; + /** * struct sof_ipc_pm_ops - IPC-specific PM ops * @ctx_save: Function pointer for context save @@ -395,6 +411,7 @@ struct sof_ipc_pcm_ops; * @pm: Pointer to PM ops * @pcm: Pointer to PCM ops * @fw_loader: Pointer to Firmware Loader ops + * @fw_tracing: Pointer to Firmware tracing ops * * @tx_msg: Function pointer for sending a 'short' IPC message * @set_get_data: Function pointer for set/get data ('large' IPC message). This @@ -415,6 +432,7 @@ struct sof_ipc_ops { const struct sof_ipc_pm_ops *pm; const struct sof_ipc_pcm_ops *pcm; const struct sof_ipc_fw_loader_ops *fw_loader; + const struct sof_ipc_fw_tracing_ops *fw_tracing;
int (*tx_msg)(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, void *reply_data, size_t reply_bytes, bool no_pm);
Rename the internal flag to not limit it's use for dma-trace, but to be used for generic firmware tracing functionality.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/core.c | 2 +- sound/soc/sof/intel/hda-dsp.c | 2 +- sound/soc/sof/sof-priv.h | 4 +++- sound/soc/sof/trace.c | 14 +++++++------- 4 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 2d12e8bab769..ff636f0e2435 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -250,7 +250,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) }
if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) { - sdev->dtrace_is_supported = true; + sdev->fw_trace_is_supported = true;
/* init DMA trace */ ret = snd_sof_init_trace(sdev); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index c068a3f2f6df..000ea906670c 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -432,7 +432,7 @@ static int hda_dsp_set_D0_state(struct snd_sof_dev *sdev, * when the DSP enters D0I3 while the system is in S0 * for debug purpose. */ - if (!sdev->dtrace_is_supported || + if (!sdev->fw_trace_is_supported || !hda_enable_trace_D0I3_S0 || sdev->system_suspend_target != SOF_SUSPEND_NONE) flags = HDA_PM_NO_DMA_TRACE; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index a7ffb6ecf332..f1cbd2b0d1c9 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -546,13 +546,15 @@ struct snd_sof_dev { int ipc_timeout; int boot_timeout;
+ /* firmwre tracing */ + bool fw_trace_is_supported; /* set with Kconfig or module parameter */ + /* DMA for Trace */ struct snd_dma_buffer dmatb; struct snd_dma_buffer dmatp; int dma_trace_pages; wait_queue_head_t trace_sleep; u32 host_offset; - bool dtrace_is_supported; /* set with Kconfig or module parameter */ bool dtrace_error; bool dtrace_draining; enum sof_dtrace_state dtrace_state; diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index 5d171bf8a5ea..c5cc78c1958f 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -392,7 +392,7 @@ static int snd_sof_enable_trace(struct snd_sof_dev *sdev) struct sof_ipc_reply ipc_reply; int ret;
- if (!sdev->dtrace_is_supported) + if (!sdev->fw_trace_is_supported) return 0;
if (sdev->dtrace_state == SOF_DTRACE_ENABLED || !sdev->dma_trace_pages) @@ -459,9 +459,9 @@ int snd_sof_init_trace(struct snd_sof_dev *sdev)
/* dtrace is only supported with SOF_IPC */ if (sdev->pdata->ipc_type != SOF_IPC) - sdev->dtrace_is_supported = false; + sdev->fw_trace_is_supported = false;
- if (!sdev->dtrace_is_supported) + if (!sdev->fw_trace_is_supported) return 0;
/* set false before start initialization */ @@ -521,7 +521,7 @@ EXPORT_SYMBOL(snd_sof_init_trace); int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn) { - if (!sdev->dtrace_is_supported) + if (!sdev->fw_trace_is_supported) return 0;
if (sdev->dtrace_state == SOF_DTRACE_ENABLED && @@ -541,7 +541,7 @@ int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, /* an error has occurred within the DSP that prevents further trace */ void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) { - if (!sdev->dtrace_is_supported) + if (!sdev->fw_trace_is_supported) return;
if (sdev->dtrace_state == SOF_DTRACE_ENABLED) { @@ -559,7 +559,7 @@ static void snd_sof_release_trace(struct snd_sof_dev *sdev, bool only_stop) struct sof_ipc_reply ipc_reply; int ret;
- if (!sdev->dtrace_is_supported || sdev->dtrace_state == SOF_DTRACE_DISABLED) + if (!sdev->fw_trace_is_supported || sdev->dtrace_state == SOF_DTRACE_DISABLED) return;
ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); @@ -611,7 +611,7 @@ EXPORT_SYMBOL(snd_sof_trace_resume);
void snd_sof_free_trace(struct snd_sof_dev *sdev) { - if (!sdev->dtrace_is_supported) + if (!sdev->fw_trace_is_supported) return;
/* release trace */
The existing trace.c file is implementing the IPC3 dma-trace support.
Clone the existing code with prefix fixes as ipc3 fw_tracing implementation to be used when the core is converted to use generic ops for firmware tracing.
Drop the dual licensing of the content as the implementation is based on debugfs.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/Makefile | 1 + sound/soc/sof/ipc3-dtrace.c | 605 ++++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc3-priv.h | 5 + sound/soc/sof/ipc3.c | 1 + 4 files changed, 612 insertions(+) create mode 100644 sound/soc/sof/ipc3-dtrace.c
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 8a79f03207fe..92b5e83601be 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -3,6 +3,7 @@ snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ ipc3-topology.o ipc3-control.o ipc3.o ipc3-pcm.o ipc3-loader.o\ + ipc3-dtrace.o\ ipc4.o ipc4-loader.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c new file mode 100644 index 000000000000..0bc3ad586d23 --- /dev/null +++ b/sound/soc/sof/ipc3-dtrace.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2022 Intel Corporation. All rights reserved. +// +// Author: Liam Girdwood liam.r.girdwood@linux.intel.com + +#include <linux/debugfs.h> +#include <linux/sched/signal.h> +#include "sof-priv.h" +#include "sof-audio.h" +#include "ops.h" +#include "sof-utils.h" +#include "ipc3-priv.h" + +#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 +#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 + +static int trace_filter_append_elem(struct snd_sof_dev *sdev, u32 key, u32 value, + struct sof_ipc_trace_filter_elem *elem_list, + int capacity, int *counter) +{ + if (*counter >= capacity) + return -ENOMEM; + + elem_list[*counter].key = key; + elem_list[*counter].value = value; + ++*counter; + + return 0; +} + +static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line, + struct sof_ipc_trace_filter_elem *elem, + int capacity, int *counter) +{ + int log_level, pipe_id, comp_id, read, ret; + int len = strlen(line); + int cnt = *counter; + u32 uuid_id; + + /* ignore empty content */ + ret = sscanf(line, " %n", &read); + if (!ret && read == len) + return len; + + ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); + if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { + dev_err(sdev->dev, "Invalid trace filter entry '%s'\n", line); + return -EINVAL; + } + + if (uuid_id > 0) { + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID, + uuid_id, elem, capacity, &cnt); + if (ret) + return ret; + } + if (pipe_id >= 0) { + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE, + pipe_id, elem, capacity, &cnt); + if (ret) + return ret; + } + if (comp_id >= 0) { + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP, + comp_id, elem, capacity, &cnt); + if (ret) + return ret; + } + + ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL | + SOF_IPC_TRACE_FILTER_ELEM_FIN, + log_level, elem, capacity, &cnt); + if (ret) + return ret; + + /* update counter only when parsing whole entry passed */ + *counter = cnt; + + return len; +} + +static int trace_filter_parse(struct snd_sof_dev *sdev, char *string, + int *out_elem_cnt, + struct sof_ipc_trace_filter_elem **out) +{ + static const char entry_delimiter[] = ";"; + char *entry = string; + int capacity = 0; + int entry_len; + int cnt = 0; + + /* + * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY + * IPC elements, depending on content. Calculate IPC elements capacity + * for the input string where each element is set. + */ + while (entry) { + capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY; + entry = strchr(entry + 1, entry_delimiter[0]); + } + *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL); + if (!*out) + return -ENOMEM; + + /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ + while ((entry = strsep(&string, entry_delimiter))) { + entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt); + if (entry_len < 0) { + dev_err(sdev->dev, + "Parsing filter entry '%s' failed with %d\n", + entry, entry_len); + return -EINVAL; + } + } + + *out_elem_cnt = cnt; + + return 0; +} + +static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, + struct sof_ipc_trace_filter_elem *elems) +{ + struct sof_ipc_trace_filter *msg; + struct sof_ipc_reply reply; + size_t size; + int ret; + + size = struct_size(msg, elems, num_elems); + if (size > SOF_IPC_MSG_MAX_SIZE) + return -EINVAL; + + msg = kmalloc(size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->hdr.size = size; + msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE; + msg->elem_cnt = num_elems; + memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems)); + + ret = pm_runtime_get_sync(sdev->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(sdev->dev); + dev_err(sdev->dev, "enabling device failed: %d\n", ret); + goto error; + } + ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply)); + pm_runtime_mark_last_busy(sdev->dev); + pm_runtime_put_autosuspend(sdev->dev); + +error: + kfree(msg); + return ret ? ret : reply.error; +} + +static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct sof_ipc_trace_filter_elem *elems = NULL; + struct snd_sof_dev *sdev = dfse->sdev; + loff_t pos = 0; + int num_elems; + char *string; + int ret; + + if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { + dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count, + TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); + return -EINVAL; + } + + string = kmalloc(count + 1, GFP_KERNEL); + if (!string) + return -ENOMEM; + + /* assert null termination */ + string[count] = 0; + ret = simple_write_to_buffer(string, count, &pos, from, count); + if (ret < 0) + goto error; + + ret = trace_filter_parse(sdev, string, &num_elems, &elems); + if (ret < 0) + goto error; + + if (num_elems) { + ret = ipc3_trace_update_filter(sdev, num_elems, elems); + if (ret < 0) { + dev_err(sdev->dev, "Filter update failed: %d\n", ret); + goto error; + } + } + ret = count; +error: + kfree(string); + kfree(elems); + return ret; +} + +static const struct file_operations sof_dfs_trace_filter_fops = { + .open = simple_open, + .write = dfsentry_trace_filter_write, + .llseek = default_llseek, +}; + +static int debugfs_create_trace_filter(struct snd_sof_dev *sdev) +{ + struct snd_sof_dfsentry *dfse; + + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->sdev = sdev; + dfse->type = SOF_DFSENTRY_TYPE_BUF; + + debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse, + &sof_dfs_trace_filter_fops); + /* add to dfsentry list */ + list_add(&dfse->list, &sdev->dfsentry_list); + + return 0; +} + +static size_t sof_dtrace_avail(struct snd_sof_dev *sdev, + loff_t pos, size_t buffer_size) +{ + loff_t host_offset = READ_ONCE(sdev->host_offset); + + /* + * If host offset is less than local pos, it means write pointer of + * host DMA buffer has been wrapped. We should output the trace data + * at the end of host DMA buffer at first. + */ + if (host_offset < pos) + return buffer_size - pos; + + /* If there is available trace data now, it is unnecessary to wait. */ + if (host_offset > pos) + return host_offset - pos; + + return 0; +} + +static size_t sof_wait_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos, + size_t buffer_size) +{ + wait_queue_entry_t wait; + size_t ret = sof_dtrace_avail(sdev, pos, buffer_size); + + /* data immediately available */ + if (ret) + return ret; + + if (sdev->dtrace_state != SOF_DTRACE_ENABLED && sdev->dtrace_draining) { + /* + * tracing has ended and all traces have been + * read by client, return EOF + */ + sdev->dtrace_draining = false; + return 0; + } + + /* wait for available trace data from FW */ + init_waitqueue_entry(&wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&sdev->trace_sleep, &wait); + + if (!signal_pending(current)) { + /* set timeout to max value, no error code */ + schedule_timeout(MAX_SCHEDULE_TIMEOUT); + } + remove_wait_queue(&sdev->trace_sleep, &wait); + + return sof_dtrace_avail(sdev, pos, buffer_size); +} + +static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct snd_sof_dfsentry *dfse = file->private_data; + struct snd_sof_dev *sdev = dfse->sdev; + unsigned long rem; + loff_t lpos = *ppos; + size_t avail, buffer_size = dfse->size; + u64 lpos_64; + + /* make sure we know about any failures on the DSP side */ + sdev->dtrace_error = false; + + /* check pos and count */ + if (lpos < 0) + return -EINVAL; + if (!count) + return 0; + + /* check for buffer wrap and count overflow */ + lpos_64 = lpos; + lpos = do_div(lpos_64, buffer_size); + + /* get available count based on current host offset */ + avail = sof_wait_dtrace_avail(sdev, lpos, buffer_size); + if (sdev->dtrace_error) { + dev_err(sdev->dev, "trace IO error\n"); + return -EIO; + } + + /* make sure count is <= avail */ + if (count > avail) + count = avail; + + /* + * make sure that all trace data is available for the CPU as the trace + * data buffer might be allocated from non consistent memory. + * Note: snd_dma_buffer_sync() is called for normal audio playback and + * capture streams also. + */ + snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU); + /* copy available trace data to debugfs */ + rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); + if (rem) + return -EFAULT; + + *ppos += count; + + /* move debugfs reading position */ + return count; +} + +static int dfsentry_dtrace_release(struct inode *inode, struct file *file) +{ + struct snd_sof_dfsentry *dfse = inode->i_private; + struct snd_sof_dev *sdev = dfse->sdev; + + /* avoid duplicate traces at next open */ + if (sdev->dtrace_state != SOF_DTRACE_ENABLED) + sdev->host_offset = 0; + + return 0; +} + +static const struct file_operations sof_dfs_dtrace_fops = { + .open = simple_open, + .read = dfsentry_dtrace_read, + .llseek = default_llseek, + .release = dfsentry_dtrace_release, +}; + +static int debugfs_create_dtrace(struct snd_sof_dev *sdev) +{ + struct snd_sof_dfsentry *dfse; + int ret; + + if (!sdev) + return -EINVAL; + + ret = debugfs_create_trace_filter(sdev); + if (ret < 0) + dev_warn(sdev->dev, "failed to create filter debugfs file: %d", ret); + + dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); + if (!dfse) + return -ENOMEM; + + dfse->type = SOF_DFSENTRY_TYPE_BUF; + dfse->buf = sdev->dmatb.area; + dfse->size = sdev->dmatb.bytes; + dfse->sdev = sdev; + + debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse, + &sof_dfs_dtrace_fops); + + return 0; +} + +static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) +{ + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + struct sof_ipc_dma_trace_params_ext params; + struct sof_ipc_reply ipc_reply; + int ret; + + if (!sdev->fw_trace_is_supported) + return 0; + + if (sdev->dtrace_state == SOF_DTRACE_ENABLED || !sdev->dma_trace_pages) + return -EINVAL; + + if (sdev->dtrace_state == SOF_DTRACE_STOPPED) + goto start; + + /* set IPC parameters */ + params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG; + /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */ + if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) { + params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext); + params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT; + params.timestamp_ns = ktime_get(); /* in nanosecond */ + } else { + params.hdr.size = sizeof(struct sof_ipc_dma_trace_params); + params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS; + } + params.buffer.phy_addr = sdev->dmatp.addr; + params.buffer.size = sdev->dmatb.bytes; + params.buffer.pages = sdev->dma_trace_pages; + params.stream_tag = 0; + + sdev->host_offset = 0; + sdev->dtrace_draining = false; + + ret = snd_sof_dma_trace_init(sdev, ¶ms); + if (ret < 0) { + dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret); + return ret; + } + dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag); + + /* send IPC to the DSP */ + ret = sof_ipc_tx_message(sdev->ipc, ¶ms, sizeof(params), &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) { + dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret); + goto trace_release; + } + +start: + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); + if (ret < 0) { + dev_err(sdev->dev, "Host dtrace trigger start failed: %d\n", ret); + goto trace_release; + } + + sdev->dtrace_state = SOF_DTRACE_ENABLED; + + return 0; + +trace_release: + snd_sof_dma_trace_release(sdev); + return ret; +} + +static int ipc3_dtrace_init(struct snd_sof_dev *sdev) +{ + int ret; + + /* dtrace is only supported with SOF_IPC */ + if (sdev->pdata->ipc_type != SOF_IPC) + return -EOPNOTSUPP; + + /* set false before start initialization */ + sdev->dtrace_state = SOF_DTRACE_DISABLED; + + /* allocate trace page table buffer */ + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, + PAGE_SIZE, &sdev->dmatp); + if (ret < 0) { + dev_err(sdev->dev, "can't alloc page table for trace %d\n", ret); + return ret; + } + + /* allocate trace data buffer */ + ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, + DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE, + &sdev->dmatb); + if (ret < 0) { + dev_err(sdev->dev, "can't alloc buffer for trace %d\n", ret); + goto page_err; + } + + /* create compressed page table for audio firmware */ + ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb, + sdev->dmatp.area, sdev->dmatb.bytes); + if (ret < 0) + goto table_err; + + sdev->dma_trace_pages = ret; + dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", __func__, + sdev->dma_trace_pages); + + if (sdev->first_boot) { + ret = debugfs_create_dtrace(sdev); + if (ret < 0) + goto table_err; + } + + init_waitqueue_head(&sdev->trace_sleep); + + ret = ipc3_dtrace_enable(sdev); + if (ret < 0) + goto table_err; + + return 0; +table_err: + sdev->dma_trace_pages = 0; + snd_dma_free_pages(&sdev->dmatb); +page_err: + snd_dma_free_pages(&sdev->dmatp); + return ret; +} + +int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn) +{ + if (!sdev->fw_trace_is_supported) + return 0; + + if (sdev->dtrace_state == SOF_DTRACE_ENABLED && + sdev->host_offset != posn->host_offset) { + sdev->host_offset = posn->host_offset; + wake_up(&sdev->trace_sleep); + } + + if (posn->overflow != 0) + dev_err(sdev->dev, + "DSP trace buffer overflow %u bytes. Total messages %d\n", + posn->overflow, posn->messages); + + return 0; +} + +/* an error has occurred within the DSP that prevents further trace */ +static void ipc3_dtrace_fw_crashed(struct snd_sof_dev *sdev) +{ + if (sdev->dtrace_state == SOF_DTRACE_ENABLED) { + sdev->dtrace_error = true; + wake_up(&sdev->trace_sleep); + } +} + +static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop) +{ + struct sof_ipc_fw_ready *ready = &sdev->fw_ready; + struct sof_ipc_fw_version *v = &ready->version; + struct sof_ipc_cmd_hdr hdr; + struct sof_ipc_reply ipc_reply; + int ret; + + if (!sdev->fw_trace_is_supported || sdev->dtrace_state == SOF_DTRACE_DISABLED) + return; + + ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) + dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret); + sdev->dtrace_state = SOF_DTRACE_STOPPED; + + /* + * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from + * ABI 3.20.0 onwards + */ + if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) { + hdr.size = sizeof(hdr); + hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE; + + ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size, + &ipc_reply, sizeof(ipc_reply)); + if (ret < 0) + dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret); + } + + if (only_stop) + goto out; + + ret = snd_sof_dma_trace_release(sdev); + if (ret < 0) + dev_err(sdev->dev, "Host dtrace release failed %d\n", ret); + + sdev->dtrace_state = SOF_DTRACE_DISABLED; + +out: + sdev->dtrace_draining = true; + wake_up(&sdev->trace_sleep); +} + +static void ipc3_dtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state) +{ + ipc3_dtrace_release(sdev, pm_state.event == SOF_DSP_PM_D0); +} + +static int ipc3_dtrace_resume(struct snd_sof_dev *sdev) +{ + return ipc3_dtrace_enable(sdev); +} + +static void ipc3_dtrace_free(struct snd_sof_dev *sdev) +{ + /* release trace */ + ipc3_dtrace_release(sdev, false); + + if (sdev->dma_trace_pages) { + snd_dma_free_pages(&sdev->dmatb); + snd_dma_free_pages(&sdev->dmatp); + sdev->dma_trace_pages = 0; + } +} + +const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops = { + .init = ipc3_dtrace_init, + .free = ipc3_dtrace_free, + .fw_crashed = ipc3_dtrace_fw_crashed, + .suspend = ipc3_dtrace_suspend, + .resume = ipc3_dtrace_resume, +}; diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h index 82f9d0cbfb93..bb9cb0678686 100644 --- a/sound/soc/sof/ipc3-priv.h +++ b/sound/soc/sof/ipc3-priv.h @@ -16,6 +16,7 @@ extern const struct sof_ipc_pcm_ops ipc3_pcm_ops; extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops; extern const struct sof_ipc_fw_loader_ops ipc3_loader_ops; +extern const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops;
/* helpers for fw_ready and ext_manifest parsing */ int sof_ipc3_get_ext_windows(struct snd_sof_dev *sdev, @@ -24,4 +25,8 @@ int sof_ipc3_get_cc_info(struct snd_sof_dev *sdev, const struct sof_ipc_ext_data_hdr *ext_hdr); int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev);
+/* dtrace position update */ +int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_posn *posn); + #endif diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index a8ffc4f99565..124d4442c6c3 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -1070,6 +1070,7 @@ const struct sof_ipc_ops ipc3_ops = { .pm = &ipc3_pm_ops, .pcm = &ipc3_pcm_ops, .fw_loader = &ipc3_loader_ops, + .fw_tracing = &ipc3_dtrace_ops,
.tx_msg = sof_ipc3_tx_msg, .rx_msg = sof_ipc3_rx_msg,
Introduce new, generic API for firmware tracing with sof_fw_trace_ prefix and switch to use it. At the same time the old IPC3 code can be dropped from trace.c, which is now a generic wrapper for the firmware tracing ops.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/core.c | 11 +- sound/soc/sof/debug.c | 2 +- sound/soc/sof/ipc.c | 6 + sound/soc/sof/ipc3.c | 2 +- sound/soc/sof/ops.c | 2 +- sound/soc/sof/pm.c | 8 +- sound/soc/sof/sof-priv.h | 13 +- sound/soc/sof/trace.c | 615 ++------------------------------------- 8 files changed, 44 insertions(+), 615 deletions(-)
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index ff636f0e2435..53719c04658f 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -252,12 +252,11 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) if (sof_debug_check_flag(SOF_DBG_ENABLE_TRACE)) { sdev->fw_trace_is_supported = true;
- /* init DMA trace */ - ret = snd_sof_init_trace(sdev); + /* init firmware tracing */ + ret = sof_fw_trace_init(sdev); if (ret < 0) { /* non fatal */ - dev_warn(sdev->dev, - "warning: failed to initialize trace %d\n", + dev_warn(sdev->dev, "failed to initialize firmware tracing %d\n", ret); } } else { @@ -308,7 +307,7 @@ static int sof_probe_continue(struct snd_sof_dev *sdev) sof_machine_err: snd_sof_machine_unregister(sdev, plat_data); fw_trace_err: - snd_sof_free_trace(sdev); + sof_fw_trace_free(sdev); fw_run_err: snd_sof_fw_unload(sdev); fw_load_err: @@ -447,7 +446,7 @@ int snd_sof_device_remove(struct device *dev) snd_sof_machine_unregister(sdev, pdata);
if (sdev->fw_state > SOF_FW_BOOT_NOT_STARTED) { - snd_sof_free_trace(sdev); + sof_fw_trace_free(sdev); ret = snd_sof_dsp_power_down_notify(sdev); if (ret < 0) dev_warn(dev, "error: %d failed to prepare DSP for device removal", diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 54d3643b46ad..cf1271eb29b2 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -443,6 +443,6 @@ void snd_sof_handle_fw_exception(struct snd_sof_dev *sdev) snd_sof_ipc_dump(sdev); snd_sof_dsp_dbg_dump(sdev, "Firmware exception", SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); - snd_sof_trace_notify_for_error(sdev); + sof_fw_trace_fw_crashed(sdev); } EXPORT_SYMBOL(snd_sof_handle_fw_exception); diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 41f3a217be5d..c5aef5fc056b 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -184,6 +184,12 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) return NULL; }
+ if (ops->fw_tracing && (!ops->fw_tracing->init || !ops->fw_tracing->suspend || + !ops->fw_tracing->resume)) { + dev_err(sdev->dev, "Missing firmware tracing ops\n"); + return NULL; + } + return ipc; } EXPORT_SYMBOL(snd_sof_ipc_init); diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index 124d4442c6c3..dff5feaad370 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -946,7 +946,7 @@ static void ipc3_trace_message(struct snd_sof_dev *sdev, void *msg_buf)
switch (msg_type) { case SOF_IPC_TRACE_DMA_POSITION: - snd_sof_trace_update_pos(sdev, msg_buf); + ipc3_dtrace_posn_update(sdev, msg_buf); break; default: dev_err(sdev->dev, "unhandled trace message %#x\n", msg_type); diff --git a/sound/soc/sof/ops.c b/sound/soc/sof/ops.c index 235e2ef72178..ff066de4ceb9 100644 --- a/sound/soc/sof/ops.c +++ b/sound/soc/sof/ops.c @@ -177,7 +177,7 @@ void snd_sof_dsp_panic(struct snd_sof_dev *sdev, u32 offset, bool non_recoverabl snd_sof_dsp_dbg_dump(sdev, "DSP panic!", SOF_DBG_DUMP_REGS | SOF_DBG_DUMP_MBOX); sof_set_fw_state(sdev, SOF_FW_CRASHED); - snd_sof_trace_notify_for_error(sdev); + sof_fw_trace_fw_crashed(sdev); } else { snd_sof_dsp_dbg_dump(sdev, "DSP panic (recovery will be attempted)", diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index fa3f5514c00f..18eb327a57f0 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -107,7 +107,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) */ if (!runtime_resume && sof_ops(sdev)->set_power_state && old_state == SOF_DSP_PM_D0) { - ret = snd_sof_trace_resume(sdev); + ret = sof_fw_trace_resume(sdev); if (ret < 0) /* non fatal */ dev_warn(sdev->dev, @@ -143,7 +143,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) }
/* resume DMA trace */ - ret = snd_sof_trace_resume(sdev); + ret = sof_fw_trace_resume(sdev); if (ret < 0) { /* non fatal */ dev_warn(sdev->dev, @@ -208,7 +208,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
/* Skip to platform-specific suspend if DSP is entering D0 */ if (target_state == SOF_DSP_PM_D0) { - snd_sof_trace_suspend(sdev, pm_state); + sof_fw_trace_suspend(sdev, pm_state); /* Notify clients not managed by pm framework about core suspend */ sof_suspend_clients(sdev, pm_state); goto suspend; @@ -218,7 +218,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) tplg_ops->tear_down_all_pipelines(sdev, false);
/* suspend DMA trace */ - snd_sof_trace_suspend(sdev, pm_state); + sof_fw_trace_suspend(sdev, pm_state);
/* Notify clients not managed by pm framework about core suspend */ sof_suspend_clients(sdev, pm_state); diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index f1cbd2b0d1c9..61ef739461f0 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -660,27 +660,26 @@ static inline void snd_sof_ipc_process_reply(struct snd_sof_dev *sdev, u32 msg_i /* * Trace/debug */ -int snd_sof_init_trace(struct snd_sof_dev *sdev); -void snd_sof_free_trace(struct snd_sof_dev *sdev); int snd_sof_dbg_init(struct snd_sof_dev *sdev); void snd_sof_free_debug(struct snd_sof_dev *sdev); int snd_sof_debugfs_buf_item(struct snd_sof_dev *sdev, void *base, size_t size, const char *name, mode_t mode); -int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_posn *posn); -void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev); void sof_print_oops_and_stack(struct snd_sof_dev *sdev, const char *level, u32 panic_code, u32 tracep_code, void *oops, struct sof_ipc_panic_info *panic_info, void *stack, size_t stack_words); -void snd_sof_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state); -int snd_sof_trace_resume(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); int snd_sof_debugfs_add_region_item_iomem(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, u32 offset, size_t size, const char *name, enum sof_debugfs_access_type access_type); +/* Firmware tracing */ +int sof_fw_trace_init(struct snd_sof_dev *sdev); +void sof_fw_trace_free(struct snd_sof_dev *sdev); +void sof_fw_trace_fw_crashed(struct snd_sof_dev *sdev); +void sof_fw_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state); +int sof_fw_trace_resume(struct snd_sof_dev *sdev);
/* * DSP Architectures. diff --git a/sound/soc/sof/trace.c b/sound/soc/sof/trace.c index c5cc78c1958f..6f662642d611 100644 --- a/sound/soc/sof/trace.c +++ b/sound/soc/sof/trace.c @@ -1,626 +1,51 @@ -// 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) 2018 Intel Corporation. All rights reserved. -// -// Author: Liam Girdwood liam.r.girdwood@linux.intel.com +// SPDX-License-Identifier: GPL-2.0-only // +// Copyright(c) 2022 Intel Corporation. All rights reserved.
-#include <linux/debugfs.h> -#include <linux/sched/signal.h> #include "sof-priv.h" -#include "sof-audio.h" -#include "ops.h" -#include "sof-utils.h" - -#define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 -#define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024 - -static int trace_filter_append_elem(struct snd_sof_dev *sdev, uint32_t key, uint32_t value, - struct sof_ipc_trace_filter_elem *elem_list, - int capacity, int *counter) -{ - if (*counter >= capacity) - return -ENOMEM; - - elem_list[*counter].key = key; - elem_list[*counter].value = value; - ++*counter; - - return 0; -} - -static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line, - struct sof_ipc_trace_filter_elem *elem, - int capacity, int *counter) -{ - int len = strlen(line); - int cnt = *counter; - uint32_t uuid_id; - int log_level; - int pipe_id; - int comp_id; - int read; - int ret; - - /* ignore empty content */ - ret = sscanf(line, " %n", &read); - if (!ret && read == len) - return len; - - ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read); - if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) { - dev_err(sdev->dev, "error: invalid trace filter entry '%s'\n", line); - return -EINVAL; - } - - if (uuid_id > 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID, - uuid_id, elem, capacity, &cnt); - if (ret) - return ret; - } - if (pipe_id >= 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE, - pipe_id, elem, capacity, &cnt); - if (ret) - return ret; - } - if (comp_id >= 0) { - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP, - comp_id, elem, capacity, &cnt); - if (ret) - return ret; - } - - ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL | - SOF_IPC_TRACE_FILTER_ELEM_FIN, - log_level, elem, capacity, &cnt); - if (ret) - return ret; - - /* update counter only when parsing whole entry passed */ - *counter = cnt; - - return len; -} - -static int trace_filter_parse(struct snd_sof_dev *sdev, char *string, - int *out_elem_cnt, - struct sof_ipc_trace_filter_elem **out) -{ - static const char entry_delimiter[] = ";"; - char *entry = string; - int capacity = 0; - int entry_len; - int cnt = 0; - - /* - * Each entry contains at least 1, up to TRACE_FILTER_ELEMENTS_PER_ENTRY - * IPC elements, depending on content. Calculate IPC elements capacity - * for the input string where each element is set. - */ - while (entry) { - capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY; - entry = strchr(entry + 1, entry_delimiter[0]); - } - *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL); - if (!*out) - return -ENOMEM; - - /* split input string by ';', and parse each entry separately in trace_filter_parse_entry */ - while ((entry = strsep(&string, entry_delimiter))) { - entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt); - if (entry_len < 0) { - dev_err(sdev->dev, "error: %s failed for '%s', %d\n", __func__, entry, - entry_len); - return -EINVAL; - } - } - - *out_elem_cnt = cnt; - - return 0; -} - -static int sof_ipc_trace_update_filter(struct snd_sof_dev *sdev, int num_elems, - struct sof_ipc_trace_filter_elem *elems) -{ - struct sof_ipc_trace_filter *msg; - struct sof_ipc_reply reply; - size_t size; - int ret; - - size = struct_size(msg, elems, num_elems); - if (size > SOF_IPC_MSG_MAX_SIZE) - return -EINVAL; - - msg = kmalloc(size, GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->hdr.size = size; - msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE; - msg->elem_cnt = num_elems; - memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems)); - - 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, msg->hdr.size, &reply, sizeof(reply)); - pm_runtime_mark_last_busy(sdev->dev); - pm_runtime_put_autosuspend(sdev->dev); - -error: - kfree(msg); - return ret ? ret : reply.error; -} - -static ssize_t sof_dfsentry_trace_filter_write(struct file *file, const char __user *from, - size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct sof_ipc_trace_filter_elem *elems = NULL; - struct snd_sof_dev *sdev = dfse->sdev; - loff_t pos = 0; - int num_elems; - char *string; - int ret; - - if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) { - dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count, - TRACE_FILTER_MAX_CONFIG_STRING_LENGTH); - return -EINVAL; - } - - string = kmalloc(count + 1, GFP_KERNEL); - if (!string) - return -ENOMEM; - - /* assert null termination */ - string[count] = 0; - ret = simple_write_to_buffer(string, count, &pos, from, count); - if (ret < 0) - goto error; - - ret = trace_filter_parse(sdev, string, &num_elems, &elems); - if (ret < 0) { - dev_err(sdev->dev, "error: fail in trace_filter_parse, %d\n", ret); - goto error; - } - - if (num_elems) { - ret = sof_ipc_trace_update_filter(sdev, num_elems, elems); - if (ret < 0) { - dev_err(sdev->dev, "error: fail in sof_ipc_trace_update_filter %d\n", ret); - goto error; - } - } - ret = count; -error: - kfree(string); - kfree(elems); - return ret; -} - -static const struct file_operations sof_dfs_trace_filter_fops = { - .open = simple_open, - .write = sof_dfsentry_trace_filter_write, - .llseek = default_llseek, -}; - -static int trace_debugfs_filter_create(struct snd_sof_dev *sdev) -{ - struct snd_sof_dfsentry *dfse; - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - dfse->sdev = sdev; - dfse->type = SOF_DFSENTRY_TYPE_BUF; - - debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse, - &sof_dfs_trace_filter_fops); - /* add to dfsentry list */ - list_add(&dfse->list, &sdev->dfsentry_list); - - return 0; -} - -static size_t sof_trace_avail(struct snd_sof_dev *sdev, - loff_t pos, size_t buffer_size) -{ - loff_t host_offset = READ_ONCE(sdev->host_offset); - - /* - * If host offset is less than local pos, it means write pointer of - * host DMA buffer has been wrapped. We should output the trace data - * at the end of host DMA buffer at first. - */ - if (host_offset < pos) - return buffer_size - pos; - - /* If there is available trace data now, it is unnecessary to wait. */ - if (host_offset > pos) - return host_offset - pos; - - return 0; -} - -static size_t sof_wait_trace_avail(struct snd_sof_dev *sdev, - loff_t pos, size_t buffer_size) -{ - wait_queue_entry_t wait; - size_t ret = sof_trace_avail(sdev, pos, buffer_size); - - /* data immediately available */ - if (ret) - return ret; - - if (sdev->dtrace_state != SOF_DTRACE_ENABLED && sdev->dtrace_draining) { - /* - * tracing has ended and all traces have been - * read by client, return EOF - */ - sdev->dtrace_draining = false; - return 0; - } - - /* wait for available trace data from FW */ - init_waitqueue_entry(&wait, current); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&sdev->trace_sleep, &wait); - - if (!signal_pending(current)) { - /* set timeout to max value, no error code */ - schedule_timeout(MAX_SCHEDULE_TIMEOUT); - } - remove_wait_queue(&sdev->trace_sleep, &wait); - - return sof_trace_avail(sdev, pos, buffer_size); -} - -static ssize_t sof_dfsentry_trace_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct snd_sof_dfsentry *dfse = file->private_data; - struct snd_sof_dev *sdev = dfse->sdev; - unsigned long rem; - loff_t lpos = *ppos; - size_t avail, buffer_size = dfse->size; - u64 lpos_64; - - /* make sure we know about any failures on the DSP side */ - sdev->dtrace_error = false; - - /* check pos and count */ - if (lpos < 0) - return -EINVAL; - if (!count) - return 0;
- /* check for buffer wrap and count overflow */ - lpos_64 = lpos; - lpos = do_div(lpos_64, buffer_size); - - /* get available count based on current host offset */ - avail = sof_wait_trace_avail(sdev, lpos, buffer_size); - if (sdev->dtrace_error) { - dev_err(sdev->dev, "error: trace IO error\n"); - return -EIO; - } - - /* make sure count is <= avail */ - if (count > avail) - count = avail; - - /* - * make sure that all trace data is available for the CPU as the trace - * data buffer might be allocated from non consistent memory. - * Note: snd_dma_buffer_sync() is called for normal audio playback and - * capture streams also. - */ - snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU); - /* copy available trace data to debugfs */ - rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); - if (rem) - return -EFAULT; - - *ppos += count; - - /* move debugfs reading position */ - return count; -} - -static int sof_dfsentry_trace_release(struct inode *inode, struct file *file) +int sof_fw_trace_init(struct snd_sof_dev *sdev) { - struct snd_sof_dfsentry *dfse = inode->i_private; - struct snd_sof_dev *sdev = dfse->sdev; - - /* avoid duplicate traces at next open */ - if (sdev->dtrace_state != SOF_DTRACE_ENABLED) - sdev->host_offset = 0; - - return 0; -} - -static const struct file_operations sof_dfs_trace_fops = { - .open = simple_open, - .read = sof_dfsentry_trace_read, - .llseek = default_llseek, - .release = sof_dfsentry_trace_release, -}; - -static int trace_debugfs_create(struct snd_sof_dev *sdev) -{ - struct snd_sof_dfsentry *dfse; - int ret; - - if (!sdev) - return -EINVAL; - - ret = trace_debugfs_filter_create(sdev); - if (ret < 0) - dev_err(sdev->dev, "error: fail in %s, %d", __func__, ret); - - dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL); - if (!dfse) - return -ENOMEM; - - dfse->type = SOF_DFSENTRY_TYPE_BUF; - dfse->buf = sdev->dmatb.area; - dfse->size = sdev->dmatb.bytes; - dfse->sdev = sdev; - - debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse, - &sof_dfs_trace_fops); - - return 0; -} - -static int snd_sof_enable_trace(struct snd_sof_dev *sdev) -{ - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - struct sof_ipc_dma_trace_params_ext params; - struct sof_ipc_reply ipc_reply; - int ret; - - if (!sdev->fw_trace_is_supported) - return 0; - - if (sdev->dtrace_state == SOF_DTRACE_ENABLED || !sdev->dma_trace_pages) - return -EINVAL; - - if (sdev->dtrace_state == SOF_DTRACE_STOPPED) - goto start; - - /* set IPC parameters */ - params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG; - /* PARAMS_EXT is only supported from ABI 3.7.0 onwards */ - if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) { - params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext); - params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT; - params.timestamp_ns = ktime_get(); /* in nanosecond */ - } else { - params.hdr.size = sizeof(struct sof_ipc_dma_trace_params); - params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS; - } - params.buffer.phy_addr = sdev->dmatp.addr; - params.buffer.size = sdev->dmatb.bytes; - params.buffer.pages = sdev->dma_trace_pages; - params.stream_tag = 0; - - sdev->host_offset = 0; - sdev->dtrace_draining = false; - - ret = snd_sof_dma_trace_init(sdev, ¶ms); - if (ret < 0) { - dev_err(sdev->dev, - "error: fail in snd_sof_dma_trace_init %d\n", ret); - return ret; - } - dev_dbg(sdev->dev, "%s: stream_tag: %d\n", __func__, params.stream_tag); - - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, ¶ms, sizeof(params), &ipc_reply, sizeof(ipc_reply)); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't set params for DMA for trace %d\n", ret); - goto trace_release; - } - -start: - ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); - if (ret < 0) { - dev_err(sdev->dev, - "error: snd_sof_dma_trace_trigger: start: %d\n", ret); - goto trace_release; - } - - sdev->dtrace_state = SOF_DTRACE_ENABLED; - - return 0; - -trace_release: - snd_sof_dma_trace_release(sdev); - return ret; -} - -int snd_sof_init_trace(struct snd_sof_dev *sdev) -{ - int ret; - - /* dtrace is only supported with SOF_IPC */ - if (sdev->pdata->ipc_type != SOF_IPC) + if (!sdev->ipc->ops->fw_tracing) { + dev_info(sdev->dev, "Firmware tracing is not available\n"); sdev->fw_trace_is_supported = false;
- if (!sdev->fw_trace_is_supported) return 0; - - /* set false before start initialization */ - sdev->dtrace_state = SOF_DTRACE_DISABLED; - - /* allocate trace page table buffer */ - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, - PAGE_SIZE, &sdev->dmatp); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't alloc page table for trace %d\n", ret); - return ret; - } - - /* allocate trace data buffer */ - ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, - DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE, - &sdev->dmatb); - if (ret < 0) { - dev_err(sdev->dev, - "error: can't alloc buffer for trace %d\n", ret); - goto page_err; - } - - /* create compressed page table for audio firmware */ - ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb, - sdev->dmatp.area, sdev->dmatb.bytes); - if (ret < 0) - goto table_err; - - sdev->dma_trace_pages = ret; - dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", - __func__, sdev->dma_trace_pages); - - if (sdev->first_boot) { - ret = trace_debugfs_create(sdev); - if (ret < 0) - goto table_err; }
- init_waitqueue_head(&sdev->trace_sleep); - - ret = snd_sof_enable_trace(sdev); - if (ret < 0) - goto table_err; - - return 0; -table_err: - sdev->dma_trace_pages = 0; - snd_dma_free_pages(&sdev->dmatb); -page_err: - snd_dma_free_pages(&sdev->dmatp); - return ret; + return sdev->ipc->ops->fw_tracing->init(sdev); } -EXPORT_SYMBOL(snd_sof_init_trace);
-int snd_sof_trace_update_pos(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_posn *posn) +void sof_fw_trace_free(struct snd_sof_dev *sdev) { - if (!sdev->fw_trace_is_supported) - return 0; - - if (sdev->dtrace_state == SOF_DTRACE_ENABLED && - sdev->host_offset != posn->host_offset) { - sdev->host_offset = posn->host_offset; - wake_up(&sdev->trace_sleep); - } - - if (posn->overflow != 0) - dev_err(sdev->dev, - "error: DSP trace buffer overflow %u bytes. Total messages %d\n", - posn->overflow, posn->messages); + if (!sdev->fw_trace_is_supported || !sdev->ipc->ops->fw_tracing) + return;
- return 0; + if (sdev->ipc->ops->fw_tracing->free) + sdev->ipc->ops->fw_tracing->free(sdev); }
-/* an error has occurred within the DSP that prevents further trace */ -void snd_sof_trace_notify_for_error(struct snd_sof_dev *sdev) +void sof_fw_trace_fw_crashed(struct snd_sof_dev *sdev) { if (!sdev->fw_trace_is_supported) return;
- if (sdev->dtrace_state == SOF_DTRACE_ENABLED) { - sdev->dtrace_error = true; - wake_up(&sdev->trace_sleep); - } + if (sdev->ipc->ops->fw_tracing->fw_crashed) + sdev->ipc->ops->fw_tracing->fw_crashed(sdev); } -EXPORT_SYMBOL(snd_sof_trace_notify_for_error);
-static void snd_sof_release_trace(struct snd_sof_dev *sdev, bool only_stop) +void sof_fw_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state) { - struct sof_ipc_fw_ready *ready = &sdev->fw_ready; - struct sof_ipc_fw_version *v = &ready->version; - struct sof_ipc_cmd_hdr hdr; - struct sof_ipc_reply ipc_reply; - int ret; - - if (!sdev->fw_trace_is_supported || sdev->dtrace_state == SOF_DTRACE_DISABLED) + if (!sdev->fw_trace_is_supported) return;
- ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); - if (ret < 0) - dev_err(sdev->dev, - "error: snd_sof_dma_trace_trigger: stop: %d\n", ret); - sdev->dtrace_state = SOF_DTRACE_STOPPED; - - /* - * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from - * ABI 3.20.0 onwards - */ - if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) { - hdr.size = sizeof(hdr); - hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE; - - ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size, - &ipc_reply, sizeof(ipc_reply)); - if (ret < 0) - dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret); - } - - if (only_stop) - goto out; - - ret = snd_sof_dma_trace_release(sdev); - if (ret < 0) - dev_err(sdev->dev, - "error: fail in snd_sof_dma_trace_release %d\n", ret); - - sdev->dtrace_state = SOF_DTRACE_DISABLED; - -out: - sdev->dtrace_draining = true; - wake_up(&sdev->trace_sleep); + sdev->ipc->ops->fw_tracing->suspend(sdev, pm_state); }
-void snd_sof_trace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state) -{ - snd_sof_release_trace(sdev, pm_state.event == SOF_DSP_PM_D0); -} -EXPORT_SYMBOL(snd_sof_trace_suspend); - -int snd_sof_trace_resume(struct snd_sof_dev *sdev) -{ - return snd_sof_enable_trace(sdev); -} -EXPORT_SYMBOL(snd_sof_trace_resume); - -void snd_sof_free_trace(struct snd_sof_dev *sdev) +int sof_fw_trace_resume(struct snd_sof_dev *sdev) { if (!sdev->fw_trace_is_supported) - return; - - /* release trace */ - snd_sof_release_trace(sdev, false); + return 0;
- if (sdev->dma_trace_pages) { - snd_dma_free_pages(&sdev->dmatb); - snd_dma_free_pages(&sdev->dmatp); - sdev->dma_trace_pages = 0; - } + return sdev->ipc->ops->fw_tracing->resume(sdev); } -EXPORT_SYMBOL(snd_sof_free_trace);
Move the snd_sof_dma_trace_* ops wrappers from ops.h to ipc3-priv.h since they are not used outside of IPC3 code. While moving, rename them to sof_dtrace_host_*
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Paul Olaru paul.olaru@oss.nxp.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- sound/soc/sof/ipc3-dtrace.c | 10 +++++----- sound/soc/sof/ipc3-priv.h | 32 ++++++++++++++++++++++++++++++++ sound/soc/sof/ops.h | 26 -------------------------- 3 files changed, 37 insertions(+), 31 deletions(-)
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c index 0bc3ad586d23..63132baaaa5a 100644 --- a/sound/soc/sof/ipc3-dtrace.c +++ b/sound/soc/sof/ipc3-dtrace.c @@ -412,7 +412,7 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) sdev->host_offset = 0; sdev->dtrace_draining = false;
- ret = snd_sof_dma_trace_init(sdev, ¶ms); + ret = sof_dtrace_host_init(sdev, ¶ms); if (ret < 0) { dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret); return ret; @@ -427,7 +427,7 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) }
start: - ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_START); + ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_START); if (ret < 0) { dev_err(sdev->dev, "Host dtrace trigger start failed: %d\n", ret); goto trace_release; @@ -438,7 +438,7 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) return 0;
trace_release: - snd_sof_dma_trace_release(sdev); + sof_dtrace_host_release(sdev); return ret; }
@@ -541,7 +541,7 @@ static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop) if (!sdev->fw_trace_is_supported || sdev->dtrace_state == SOF_DTRACE_DISABLED) return;
- ret = snd_sof_dma_trace_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); + ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); if (ret < 0) dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret); sdev->dtrace_state = SOF_DTRACE_STOPPED; @@ -563,7 +563,7 @@ static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop) if (only_stop) goto out;
- ret = snd_sof_dma_trace_release(sdev); + ret = sof_dtrace_host_release(sdev); if (ret < 0) dev_err(sdev->dev, "Host dtrace release failed %d\n", ret);
diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h index bb9cb0678686..21f0bd20323f 100644 --- a/sound/soc/sof/ipc3-priv.h +++ b/sound/soc/sof/ipc3-priv.h @@ -29,4 +29,36 @@ int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev); int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn);
+/* dtrace platform callback wrappers */ +static inline int sof_dtrace_host_init(struct snd_sof_dev *sdev, + struct sof_ipc_dma_trace_params_ext *dtrace_params) +{ + struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops; + + if (dsp_ops->trace_init) + return dsp_ops->trace_init(sdev, dtrace_params); + + return 0; +} + +static inline int sof_dtrace_host_release(struct snd_sof_dev *sdev) +{ + struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops; + + if (dsp_ops->trace_release) + return dsp_ops->trace_release(sdev); + + return 0; +} + +static inline int sof_dtrace_host_trigger(struct snd_sof_dev *sdev, int cmd) +{ + struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops; + + if (dsp_ops->trace_trigger) + return dsp_ops->trace_trigger(sdev, cmd); + + return 0; +} + #endif diff --git a/sound/soc/sof/ops.h b/sound/soc/sof/ops.h index aa64e3bd645f..b79ae4f66eba 100644 --- a/sound/soc/sof/ops.h +++ b/sound/soc/sof/ops.h @@ -375,32 +375,6 @@ static inline int snd_sof_dsp_send_msg(struct snd_sof_dev *sdev, return sof_ops(sdev)->send_msg(sdev, msg); }
-/* host DMA trace */ -static inline int snd_sof_dma_trace_init(struct snd_sof_dev *sdev, - struct sof_ipc_dma_trace_params_ext *dtrace_params) -{ - if (sof_ops(sdev)->trace_init) - return sof_ops(sdev)->trace_init(sdev, dtrace_params); - - return 0; -} - -static inline int snd_sof_dma_trace_release(struct snd_sof_dev *sdev) -{ - if (sof_ops(sdev)->trace_release) - return sof_ops(sdev)->trace_release(sdev); - - return 0; -} - -static inline int snd_sof_dma_trace_trigger(struct snd_sof_dev *sdev, int cmd) -{ - if (sof_ops(sdev)->trace_trigger) - return sof_ops(sdev)->trace_trigger(sdev, cmd); - - return 0; -} - /* host PCM ops */ static inline int snd_sof_pcm_platform_open(struct snd_sof_dev *sdev,
Stop host code (AMD, Intel) to access sdev->dmatb directly. Modify the trace_init prototype to include the pointer to a struct snd_dma_buffer. The ipc3-dtrace passes for now the pointer to sdev->dmatb, but the aim is to move all tracing related runtime information local to a trace implementation.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Paul Olaru paul.olaru@oss.nxp.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- sound/soc/sof/amd/acp-trace.c | 4 ++-- sound/soc/sof/amd/acp.h | 2 +- sound/soc/sof/intel/hda-trace.c | 4 ++-- sound/soc/sof/intel/hda.h | 2 +- sound/soc/sof/ipc3-dtrace.c | 2 +- sound/soc/sof/ipc3-priv.h | 3 ++- sound/soc/sof/sof-priv.h | 3 ++- 7 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/sound/soc/sof/amd/acp-trace.c b/sound/soc/sof/amd/acp-trace.c index 903b6cc3dda3..c9482b27cbe3 100644 --- a/sound/soc/sof/amd/acp-trace.c +++ b/sound/soc/sof/amd/acp-trace.c @@ -34,7 +34,7 @@ int acp_sof_trace_release(struct snd_sof_dev *sdev) } EXPORT_SYMBOL_NS(acp_sof_trace_release, SND_SOC_SOF_AMD_COMMON);
-int acp_sof_trace_init(struct snd_sof_dev *sdev, +int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct sof_ipc_dma_trace_params_ext *dtrace_params) { struct acp_dsp_stream *stream; @@ -46,7 +46,7 @@ int acp_sof_trace_init(struct snd_sof_dev *sdev, if (!stream) return -ENODEV;
- stream->dmab = &sdev->dmatb; + stream->dmab = dmab; stream->num_pages = NUM_PAGES;
ret = acp_dsp_stream_config(sdev, stream); diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h index de526a1bce13..291b44c54bcc 100644 --- a/sound/soc/sof/amd/acp.h +++ b/sound/soc/sof/amd/acp.h @@ -212,7 +212,7 @@ extern struct snd_sof_dsp_ops sof_renoir_ops; int snd_amd_acp_find_config(struct pci_dev *pci);
/* Trace */ -int acp_sof_trace_init(struct snd_sof_dev *sdev, +int acp_sof_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct sof_ipc_dma_trace_params_ext *dtrace_params); int acp_sof_trace_release(struct snd_sof_dev *sdev);
diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 755ef1d835e0..cbb9bd7770e6 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -36,7 +36,7 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev, struct snd_dma_buffer return ret; }
-int hda_dsp_trace_init(struct snd_sof_dev *sdev, +int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct sof_ipc_dma_trace_params_ext *dtrace_params) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; @@ -57,7 +57,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, * initialize capture stream, set BDL address and return corresponding * stream tag which will be sent to the firmware by IPC message. */ - ret = hda_dsp_trace_prepare(sdev, &sdev->dmatb); + ret = hda_dsp_trace_prepare(sdev, dmab); if (ret < 0) { dev_err(sdev->dev, "error: hdac trace init failed: %d\n", ret); hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_CAPTURE, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 535791c7d187..3e0f7b0c586a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -658,7 +658,7 @@ static inline int hda_codec_i915_exit(struct snd_sof_dev *sdev) { return 0; } /* * Trace Control. */ -int hda_dsp_trace_init(struct snd_sof_dev *sdev, +int hda_dsp_trace_init(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct sof_ipc_dma_trace_params_ext *dtrace_params); int hda_dsp_trace_release(struct snd_sof_dev *sdev); int hda_dsp_trace_trigger(struct snd_sof_dev *sdev, int cmd); diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c index 63132baaaa5a..91a2792b9beb 100644 --- a/sound/soc/sof/ipc3-dtrace.c +++ b/sound/soc/sof/ipc3-dtrace.c @@ -412,7 +412,7 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) sdev->host_offset = 0; sdev->dtrace_draining = false;
- ret = sof_dtrace_host_init(sdev, ¶ms); + ret = sof_dtrace_host_init(sdev, &sdev->dmatb, ¶ms); if (ret < 0) { dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret); return ret; diff --git a/sound/soc/sof/ipc3-priv.h b/sound/soc/sof/ipc3-priv.h index 21f0bd20323f..f5044202f3c5 100644 --- a/sound/soc/sof/ipc3-priv.h +++ b/sound/soc/sof/ipc3-priv.h @@ -31,12 +31,13 @@ int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev,
/* dtrace platform callback wrappers */ static inline int sof_dtrace_host_init(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmatb, struct sof_ipc_dma_trace_params_ext *dtrace_params) { struct snd_sof_dsp_ops *dsp_ops = sdev->pdata->desc->ops;
if (dsp_ops->trace_init) - return dsp_ops->trace_init(sdev, dtrace_params); + return dsp_ops->trace_init(sdev, dmatb, dtrace_params);
return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 61ef739461f0..b176fc7e346c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -254,8 +254,9 @@ struct snd_sof_dsp_ops { size_t size, const char *name, enum sof_debugfs_access_type access_type); /* optional */
- /* host DMA trace initialization */ + /* host DMA trace (IPC3) */ int (*trace_init)(struct snd_sof_dev *sdev, + struct snd_dma_buffer *dmatb, struct sof_ipc_dma_trace_params_ext *dtrace_params); /* optional */ int (*trace_release)(struct snd_sof_dev *sdev); /* optional */ int (*trace_trigger)(struct snd_sof_dev *sdev,
Firmware tracing implementations can allocate and store their privately used data behind the fw_trace_pdata pointer instead of adding more members to struct snd_sof_dev.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Paul Olaru paul.olaru@oss.nxp.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- sound/soc/sof/sof-priv.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index b176fc7e346c..d8d81e1ec259 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -549,6 +549,7 @@ struct snd_sof_dev {
/* firmwre tracing */ bool fw_trace_is_supported; /* set with Kconfig or module parameter */ + void *fw_trace_data; /* private data used by firmware tracing implementation */
/* DMA for Trace */ struct snd_dma_buffer dmatb;
The variables and structs for DMA trace can be moved local to ipc3-dtrace.c and the storage can be allocated dynamically, stored behind the fw_trace_data pointer.
Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Paul Olaru paul.olaru@oss.nxp.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com Reviewed-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com --- sound/soc/sof/ipc3-dtrace.c | 142 +++++++++++++++++++++++------------- sound/soc/sof/sof-priv.h | 16 ---- 2 files changed, 93 insertions(+), 65 deletions(-)
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c index 91a2792b9beb..b4e1343f9138 100644 --- a/sound/soc/sof/ipc3-dtrace.c +++ b/sound/soc/sof/ipc3-dtrace.c @@ -15,6 +15,23 @@ #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
+enum sof_dtrace_state { + SOF_DTRACE_DISABLED, + SOF_DTRACE_STOPPED, + SOF_DTRACE_ENABLED, +}; + +struct sof_dtrace_priv { + struct snd_dma_buffer dmatb; + struct snd_dma_buffer dmatp; + int dma_trace_pages; + wait_queue_head_t trace_sleep; + u32 host_offset; + bool dtrace_error; + bool dtrace_draining; + enum sof_dtrace_state dtrace_state; +}; + static int trace_filter_append_elem(struct snd_sof_dev *sdev, u32 key, u32 value, struct sof_ipc_trace_filter_elem *elem_list, int capacity, int *counter) @@ -228,7 +245,8 @@ static int debugfs_create_trace_filter(struct snd_sof_dev *sdev) static size_t sof_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos, size_t buffer_size) { - loff_t host_offset = READ_ONCE(sdev->host_offset); + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + loff_t host_offset = READ_ONCE(priv->host_offset);
/* * If host offset is less than local pos, it means write pointer of @@ -248,32 +266,33 @@ static size_t sof_dtrace_avail(struct snd_sof_dev *sdev, static size_t sof_wait_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos, size_t buffer_size) { - wait_queue_entry_t wait; size_t ret = sof_dtrace_avail(sdev, pos, buffer_size); + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + wait_queue_entry_t wait;
/* data immediately available */ if (ret) return ret;
- if (sdev->dtrace_state != SOF_DTRACE_ENABLED && sdev->dtrace_draining) { + if (priv->dtrace_state != SOF_DTRACE_ENABLED && priv->dtrace_draining) { /* * tracing has ended and all traces have been * read by client, return EOF */ - sdev->dtrace_draining = false; + priv->dtrace_draining = false; return 0; }
/* wait for available trace data from FW */ init_waitqueue_entry(&wait, current); set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&sdev->trace_sleep, &wait); + add_wait_queue(&priv->trace_sleep, &wait);
if (!signal_pending(current)) { /* set timeout to max value, no error code */ schedule_timeout(MAX_SCHEDULE_TIMEOUT); } - remove_wait_queue(&sdev->trace_sleep, &wait); + remove_wait_queue(&priv->trace_sleep, &wait);
return sof_dtrace_avail(sdev, pos, buffer_size); } @@ -283,13 +302,14 @@ static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer, { struct snd_sof_dfsentry *dfse = file->private_data; struct snd_sof_dev *sdev = dfse->sdev; + struct sof_dtrace_priv *priv = sdev->fw_trace_data; unsigned long rem; loff_t lpos = *ppos; size_t avail, buffer_size = dfse->size; u64 lpos_64;
/* make sure we know about any failures on the DSP side */ - sdev->dtrace_error = false; + priv->dtrace_error = false;
/* check pos and count */ if (lpos < 0) @@ -303,7 +323,7 @@ static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer,
/* get available count based on current host offset */ avail = sof_wait_dtrace_avail(sdev, lpos, buffer_size); - if (sdev->dtrace_error) { + if (priv->dtrace_error) { dev_err(sdev->dev, "trace IO error\n"); return -EIO; } @@ -318,7 +338,7 @@ static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer, * Note: snd_dma_buffer_sync() is called for normal audio playback and * capture streams also. */ - snd_dma_buffer_sync(&sdev->dmatb, SNDRV_DMA_SYNC_CPU); + snd_dma_buffer_sync(&priv->dmatb, SNDRV_DMA_SYNC_CPU); /* copy available trace data to debugfs */ rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count); if (rem) @@ -334,10 +354,11 @@ static int dfsentry_dtrace_release(struct inode *inode, struct file *file) { struct snd_sof_dfsentry *dfse = inode->i_private; struct snd_sof_dev *sdev = dfse->sdev; + struct sof_dtrace_priv *priv = sdev->fw_trace_data;
/* avoid duplicate traces at next open */ - if (sdev->dtrace_state != SOF_DTRACE_ENABLED) - sdev->host_offset = 0; + if (priv->dtrace_state != SOF_DTRACE_ENABLED) + priv->host_offset = 0;
return 0; } @@ -351,12 +372,15 @@ static const struct file_operations sof_dfs_dtrace_fops = {
static int debugfs_create_dtrace(struct snd_sof_dev *sdev) { + struct sof_dtrace_priv *priv; struct snd_sof_dfsentry *dfse; int ret;
if (!sdev) return -EINVAL;
+ priv = sdev->fw_trace_data; + ret = debugfs_create_trace_filter(sdev); if (ret < 0) dev_warn(sdev->dev, "failed to create filter debugfs file: %d", ret); @@ -366,8 +390,8 @@ static int debugfs_create_dtrace(struct snd_sof_dev *sdev) return -ENOMEM;
dfse->type = SOF_DFSENTRY_TYPE_BUF; - dfse->buf = sdev->dmatb.area; - dfse->size = sdev->dmatb.bytes; + dfse->buf = priv->dmatb.area; + dfse->size = priv->dmatb.bytes; dfse->sdev = sdev;
debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse, @@ -378,6 +402,7 @@ static int debugfs_create_dtrace(struct snd_sof_dev *sdev)
static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) { + struct sof_dtrace_priv *priv = sdev->fw_trace_data; struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; struct sof_ipc_dma_trace_params_ext params; @@ -387,10 +412,10 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) if (!sdev->fw_trace_is_supported) return 0;
- if (sdev->dtrace_state == SOF_DTRACE_ENABLED || !sdev->dma_trace_pages) + if (priv->dtrace_state == SOF_DTRACE_ENABLED || !priv->dma_trace_pages) return -EINVAL;
- if (sdev->dtrace_state == SOF_DTRACE_STOPPED) + if (priv->dtrace_state == SOF_DTRACE_STOPPED) goto start;
/* set IPC parameters */ @@ -404,15 +429,15 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) params.hdr.size = sizeof(struct sof_ipc_dma_trace_params); params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS; } - params.buffer.phy_addr = sdev->dmatp.addr; - params.buffer.size = sdev->dmatb.bytes; - params.buffer.pages = sdev->dma_trace_pages; + params.buffer.phy_addr = priv->dmatp.addr; + params.buffer.size = priv->dmatb.bytes; + params.buffer.pages = priv->dma_trace_pages; params.stream_tag = 0;
- sdev->host_offset = 0; - sdev->dtrace_draining = false; + priv->host_offset = 0; + priv->dtrace_draining = false;
- ret = sof_dtrace_host_init(sdev, &sdev->dmatb, ¶ms); + ret = sof_dtrace_host_init(sdev, &priv->dmatb, ¶ms); if (ret < 0) { dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret); return ret; @@ -433,7 +458,7 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev) goto trace_release; }
- sdev->dtrace_state = SOF_DTRACE_ENABLED; + priv->dtrace_state = SOF_DTRACE_ENABLED;
return 0;
@@ -444,18 +469,30 @@ static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
static int ipc3_dtrace_init(struct snd_sof_dev *sdev) { + struct sof_dtrace_priv *priv; int ret;
/* dtrace is only supported with SOF_IPC */ if (sdev->pdata->ipc_type != SOF_IPC) return -EOPNOTSUPP;
+ if (sdev->fw_trace_data) { + dev_err(sdev->dev, "fw_trace_data has been already allocated\n"); + return -EBUSY; + } + + priv = devm_kzalloc(sdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + sdev->fw_trace_data = priv; + /* set false before start initialization */ - sdev->dtrace_state = SOF_DTRACE_DISABLED; + priv->dtrace_state = SOF_DTRACE_DISABLED;
/* allocate trace page table buffer */ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev, - PAGE_SIZE, &sdev->dmatp); + PAGE_SIZE, &priv->dmatp); if (ret < 0) { dev_err(sdev->dev, "can't alloc page table for trace %d\n", ret); return ret; @@ -464,21 +501,21 @@ static int ipc3_dtrace_init(struct snd_sof_dev *sdev) /* allocate trace data buffer */ ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev, DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE, - &sdev->dmatb); + &priv->dmatb); if (ret < 0) { dev_err(sdev->dev, "can't alloc buffer for trace %d\n", ret); goto page_err; }
/* create compressed page table for audio firmware */ - ret = snd_sof_create_page_table(sdev->dev, &sdev->dmatb, - sdev->dmatp.area, sdev->dmatb.bytes); + ret = snd_sof_create_page_table(sdev->dev, &priv->dmatb, + priv->dmatp.area, priv->dmatb.bytes); if (ret < 0) goto table_err;
- sdev->dma_trace_pages = ret; + priv->dma_trace_pages = ret; dev_dbg(sdev->dev, "%s: dma_trace_pages: %d\n", __func__, - sdev->dma_trace_pages); + priv->dma_trace_pages);
if (sdev->first_boot) { ret = debugfs_create_dtrace(sdev); @@ -486,7 +523,7 @@ static int ipc3_dtrace_init(struct snd_sof_dev *sdev) goto table_err; }
- init_waitqueue_head(&sdev->trace_sleep); + init_waitqueue_head(&priv->trace_sleep);
ret = ipc3_dtrace_enable(sdev); if (ret < 0) @@ -494,23 +531,25 @@ static int ipc3_dtrace_init(struct snd_sof_dev *sdev)
return 0; table_err: - sdev->dma_trace_pages = 0; - snd_dma_free_pages(&sdev->dmatb); + priv->dma_trace_pages = 0; + snd_dma_free_pages(&priv->dmatb); page_err: - snd_dma_free_pages(&sdev->dmatp); + snd_dma_free_pages(&priv->dmatp); return ret; }
int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, struct sof_ipc_dma_trace_posn *posn) { + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + if (!sdev->fw_trace_is_supported) return 0;
- if (sdev->dtrace_state == SOF_DTRACE_ENABLED && - sdev->host_offset != posn->host_offset) { - sdev->host_offset = posn->host_offset; - wake_up(&sdev->trace_sleep); + if (priv->dtrace_state == SOF_DTRACE_ENABLED && + priv->host_offset != posn->host_offset) { + priv->host_offset = posn->host_offset; + wake_up(&priv->trace_sleep); }
if (posn->overflow != 0) @@ -524,27 +563,30 @@ int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev, /* an error has occurred within the DSP that prevents further trace */ static void ipc3_dtrace_fw_crashed(struct snd_sof_dev *sdev) { - if (sdev->dtrace_state == SOF_DTRACE_ENABLED) { - sdev->dtrace_error = true; - wake_up(&sdev->trace_sleep); + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + + if (priv->dtrace_state == SOF_DTRACE_ENABLED) { + priv->dtrace_error = true; + wake_up(&priv->trace_sleep); } }
static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop) { + struct sof_dtrace_priv *priv = sdev->fw_trace_data; struct sof_ipc_fw_ready *ready = &sdev->fw_ready; struct sof_ipc_fw_version *v = &ready->version; struct sof_ipc_cmd_hdr hdr; struct sof_ipc_reply ipc_reply; int ret;
- if (!sdev->fw_trace_is_supported || sdev->dtrace_state == SOF_DTRACE_DISABLED) + if (!sdev->fw_trace_is_supported || priv->dtrace_state == SOF_DTRACE_DISABLED) return;
ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_STOP); if (ret < 0) dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret); - sdev->dtrace_state = SOF_DTRACE_STOPPED; + priv->dtrace_state = SOF_DTRACE_STOPPED;
/* * stop and free trace DMA in the DSP. TRACE_DMA_FREE is only supported from @@ -567,11 +609,11 @@ static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop) if (ret < 0) dev_err(sdev->dev, "Host dtrace release failed %d\n", ret);
- sdev->dtrace_state = SOF_DTRACE_DISABLED; + priv->dtrace_state = SOF_DTRACE_DISABLED;
out: - sdev->dtrace_draining = true; - wake_up(&sdev->trace_sleep); + priv->dtrace_draining = true; + wake_up(&priv->trace_sleep); }
static void ipc3_dtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state) @@ -586,13 +628,15 @@ static int ipc3_dtrace_resume(struct snd_sof_dev *sdev)
static void ipc3_dtrace_free(struct snd_sof_dev *sdev) { + struct sof_dtrace_priv *priv = sdev->fw_trace_data; + /* release trace */ ipc3_dtrace_release(sdev, false);
- if (sdev->dma_trace_pages) { - snd_dma_free_pages(&sdev->dmatb); - snd_dma_free_pages(&sdev->dmatp); - sdev->dma_trace_pages = 0; + if (priv->dma_trace_pages) { + snd_dma_free_pages(&priv->dmatb); + snd_dma_free_pages(&priv->dmatp); + priv->dma_trace_pages = 0; } }
diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index d8d81e1ec259..9d7f53ff9c70 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -461,12 +461,6 @@ struct snd_sof_ipc { const struct sof_ipc_ops *ops; };
-enum sof_dtrace_state { - SOF_DTRACE_DISABLED, - SOF_DTRACE_STOPPED, - SOF_DTRACE_ENABLED, -}; - /* * SOF Device Level. */ @@ -551,16 +545,6 @@ struct snd_sof_dev { bool fw_trace_is_supported; /* set with Kconfig or module parameter */ void *fw_trace_data; /* private data used by firmware tracing implementation */
- /* DMA for Trace */ - struct snd_dma_buffer dmatb; - struct snd_dma_buffer dmatp; - int dma_trace_pages; - wait_queue_head_t trace_sleep; - u32 host_offset; - bool dtrace_error; - bool dtrace_draining; - enum sof_dtrace_state dtrace_state; - bool msi_enabled;
/* DSP core context */
On Mon, 16 May 2022 13:47:03 +0300, Peter Ujfalusi wrote:
SOF is using dma-trace (or dtrace) as a firmware tracing method, which is only supported with IPC3 and it is not applicable for IPC4.
Currently the dtrace is 'open managed' regardless of IPC version (we do force disable it for IPC4, but the dtrace calls remain in place).
From the kernel point of view there are only few ops that needs to be exposed by the firmware tracing support and everything else is IPC private, should not be known by the core.
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[1/8] ASoC: SOF: Introduce IPC independent ops for firmware tracing support commit: 0cfbaee21fcbf131f02c475dbc15f8a18ee621bc [2/8] ASoC: SOF: Rename dtrace_is_supported flag to fw_trace_is_supported commit: 25b17da691f3c1a7cc433f864fd4845998a5a37a [3/8] ASoC: SOF: Clone the trace code to ipc3-dtrace as fw_tracing implementation commit: 671e0b90051ec19e83c12501905734fb808b944e [4/8] ASoC: SOF: Switch to IPC generic firmware tracing commit: 1dedbe4f223cac603e871d91133b9aa3136fbc21 [5/8] ASoC: SOF: ipc3-dtrace: Move host ops wrappers from generic header to private commit: b69979a1ec2d9347a43bf0ebdad2c1eb23447ca6 [6/8] ASoC: SOF: Modify the host trace_init parameter list to include dmab commit: 4b49cbd1e7ebe4b000a7eedc4f910488da62c055 [7/8] ASoC: SOF: Introduce opaque storage of private data for firmware tracing commit: 08341b27bd2ee3c79265ef7925b3bc68a1790ab9 [8/8] ASoC: SOF: ipc3-dtrace: Move dtrace related variables local from sof_dev commit: 0683532999ab3890f44f832cd47feee9e2374c22
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (2)
-
Mark Brown
-
Peter Ujfalusi