0001
0002
0003
0004
0005
0006
0007 #include <linux/debugfs.h>
0008 #include <linux/sched/signal.h>
0009 #include "sof-priv.h"
0010 #include "sof-audio.h"
0011 #include "ops.h"
0012 #include "sof-utils.h"
0013 #include "ipc3-priv.h"
0014
0015 #define TRACE_FILTER_ELEMENTS_PER_ENTRY 4
0016 #define TRACE_FILTER_MAX_CONFIG_STRING_LENGTH 1024
0017
0018 enum sof_dtrace_state {
0019 SOF_DTRACE_DISABLED,
0020 SOF_DTRACE_STOPPED,
0021 SOF_DTRACE_INITIALIZING,
0022 SOF_DTRACE_ENABLED,
0023 };
0024
0025 struct sof_dtrace_priv {
0026 struct snd_dma_buffer dmatb;
0027 struct snd_dma_buffer dmatp;
0028 int dma_trace_pages;
0029 wait_queue_head_t trace_sleep;
0030 u32 host_offset;
0031 bool dtrace_error;
0032 bool dtrace_draining;
0033 enum sof_dtrace_state dtrace_state;
0034 };
0035
0036 static bool trace_pos_update_expected(struct sof_dtrace_priv *priv)
0037 {
0038 if (priv->dtrace_state == SOF_DTRACE_ENABLED ||
0039 priv->dtrace_state == SOF_DTRACE_INITIALIZING)
0040 return true;
0041
0042 return false;
0043 }
0044
0045 static int trace_filter_append_elem(struct snd_sof_dev *sdev, u32 key, u32 value,
0046 struct sof_ipc_trace_filter_elem *elem_list,
0047 int capacity, int *counter)
0048 {
0049 if (*counter >= capacity)
0050 return -ENOMEM;
0051
0052 elem_list[*counter].key = key;
0053 elem_list[*counter].value = value;
0054 ++*counter;
0055
0056 return 0;
0057 }
0058
0059 static int trace_filter_parse_entry(struct snd_sof_dev *sdev, const char *line,
0060 struct sof_ipc_trace_filter_elem *elem,
0061 int capacity, int *counter)
0062 {
0063 int log_level, pipe_id, comp_id, read, ret;
0064 int len = strlen(line);
0065 int cnt = *counter;
0066 u32 uuid_id;
0067
0068
0069 ret = sscanf(line, " %n", &read);
0070 if (!ret && read == len)
0071 return len;
0072
0073 ret = sscanf(line, " %d %x %d %d %n", &log_level, &uuid_id, &pipe_id, &comp_id, &read);
0074 if (ret != TRACE_FILTER_ELEMENTS_PER_ENTRY || read != len) {
0075 dev_err(sdev->dev, "Invalid trace filter entry '%s'\n", line);
0076 return -EINVAL;
0077 }
0078
0079 if (uuid_id > 0) {
0080 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_UUID,
0081 uuid_id, elem, capacity, &cnt);
0082 if (ret)
0083 return ret;
0084 }
0085 if (pipe_id >= 0) {
0086 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_PIPE,
0087 pipe_id, elem, capacity, &cnt);
0088 if (ret)
0089 return ret;
0090 }
0091 if (comp_id >= 0) {
0092 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_BY_COMP,
0093 comp_id, elem, capacity, &cnt);
0094 if (ret)
0095 return ret;
0096 }
0097
0098 ret = trace_filter_append_elem(sdev, SOF_IPC_TRACE_FILTER_ELEM_SET_LEVEL |
0099 SOF_IPC_TRACE_FILTER_ELEM_FIN,
0100 log_level, elem, capacity, &cnt);
0101 if (ret)
0102 return ret;
0103
0104
0105 *counter = cnt;
0106
0107 return len;
0108 }
0109
0110 static int trace_filter_parse(struct snd_sof_dev *sdev, char *string,
0111 int *out_elem_cnt,
0112 struct sof_ipc_trace_filter_elem **out)
0113 {
0114 static const char entry_delimiter[] = ";";
0115 char *entry = string;
0116 int capacity = 0;
0117 int entry_len;
0118 int cnt = 0;
0119
0120
0121
0122
0123
0124
0125 while (entry) {
0126 capacity += TRACE_FILTER_ELEMENTS_PER_ENTRY;
0127 entry = strchr(entry + 1, entry_delimiter[0]);
0128 }
0129 *out = kmalloc(capacity * sizeof(**out), GFP_KERNEL);
0130 if (!*out)
0131 return -ENOMEM;
0132
0133
0134 while ((entry = strsep(&string, entry_delimiter))) {
0135 entry_len = trace_filter_parse_entry(sdev, entry, *out, capacity, &cnt);
0136 if (entry_len < 0) {
0137 dev_err(sdev->dev,
0138 "Parsing filter entry '%s' failed with %d\n",
0139 entry, entry_len);
0140 return -EINVAL;
0141 }
0142 }
0143
0144 *out_elem_cnt = cnt;
0145
0146 return 0;
0147 }
0148
0149 static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
0150 struct sof_ipc_trace_filter_elem *elems)
0151 {
0152 struct sof_ipc_trace_filter *msg;
0153 struct sof_ipc_reply reply;
0154 size_t size;
0155 int ret;
0156
0157 size = struct_size(msg, elems, num_elems);
0158 if (size > SOF_IPC_MSG_MAX_SIZE)
0159 return -EINVAL;
0160
0161 msg = kmalloc(size, GFP_KERNEL);
0162 if (!msg)
0163 return -ENOMEM;
0164
0165 msg->hdr.size = size;
0166 msg->hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_FILTER_UPDATE;
0167 msg->elem_cnt = num_elems;
0168 memcpy(&msg->elems[0], elems, num_elems * sizeof(*elems));
0169
0170 ret = pm_runtime_resume_and_get(sdev->dev);
0171 if (ret < 0 && ret != -EACCES) {
0172 dev_err(sdev->dev, "enabling device failed: %d\n", ret);
0173 goto error;
0174 }
0175 ret = sof_ipc_tx_message(sdev->ipc, msg, msg->hdr.size, &reply, sizeof(reply));
0176 pm_runtime_mark_last_busy(sdev->dev);
0177 pm_runtime_put_autosuspend(sdev->dev);
0178
0179 error:
0180 kfree(msg);
0181 return ret ? ret : reply.error;
0182 }
0183
0184 static ssize_t dfsentry_trace_filter_write(struct file *file, const char __user *from,
0185 size_t count, loff_t *ppos)
0186 {
0187 struct snd_sof_dfsentry *dfse = file->private_data;
0188 struct sof_ipc_trace_filter_elem *elems = NULL;
0189 struct snd_sof_dev *sdev = dfse->sdev;
0190 loff_t pos = 0;
0191 int num_elems;
0192 char *string;
0193 int ret;
0194
0195 if (count > TRACE_FILTER_MAX_CONFIG_STRING_LENGTH) {
0196 dev_err(sdev->dev, "%s too long input, %zu > %d\n", __func__, count,
0197 TRACE_FILTER_MAX_CONFIG_STRING_LENGTH);
0198 return -EINVAL;
0199 }
0200
0201 string = kmalloc(count + 1, GFP_KERNEL);
0202 if (!string)
0203 return -ENOMEM;
0204
0205
0206 string[count] = 0;
0207 ret = simple_write_to_buffer(string, count, &pos, from, count);
0208 if (ret < 0)
0209 goto error;
0210
0211 ret = trace_filter_parse(sdev, string, &num_elems, &elems);
0212 if (ret < 0)
0213 goto error;
0214
0215 if (num_elems) {
0216 ret = ipc3_trace_update_filter(sdev, num_elems, elems);
0217 if (ret < 0) {
0218 dev_err(sdev->dev, "Filter update failed: %d\n", ret);
0219 goto error;
0220 }
0221 }
0222 ret = count;
0223 error:
0224 kfree(string);
0225 kfree(elems);
0226 return ret;
0227 }
0228
0229 static const struct file_operations sof_dfs_trace_filter_fops = {
0230 .open = simple_open,
0231 .write = dfsentry_trace_filter_write,
0232 .llseek = default_llseek,
0233 };
0234
0235 static int debugfs_create_trace_filter(struct snd_sof_dev *sdev)
0236 {
0237 struct snd_sof_dfsentry *dfse;
0238
0239 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
0240 if (!dfse)
0241 return -ENOMEM;
0242
0243 dfse->sdev = sdev;
0244 dfse->type = SOF_DFSENTRY_TYPE_BUF;
0245
0246 debugfs_create_file("filter", 0200, sdev->debugfs_root, dfse,
0247 &sof_dfs_trace_filter_fops);
0248
0249 list_add(&dfse->list, &sdev->dfsentry_list);
0250
0251 return 0;
0252 }
0253
0254 static bool sof_dtrace_set_host_offset(struct sof_dtrace_priv *priv, u32 new_offset)
0255 {
0256 u32 host_offset = READ_ONCE(priv->host_offset);
0257
0258 if (host_offset != new_offset) {
0259
0260 u32 ret = cmpxchg(&priv->host_offset, host_offset, new_offset);
0261
0262 if (ret == host_offset)
0263 return true;
0264 }
0265
0266 return false;
0267 }
0268
0269 static size_t sof_dtrace_avail(struct snd_sof_dev *sdev,
0270 loff_t pos, size_t buffer_size)
0271 {
0272 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0273 loff_t host_offset = READ_ONCE(priv->host_offset);
0274
0275
0276
0277
0278
0279
0280 if (host_offset < pos)
0281 return buffer_size - pos;
0282
0283
0284 if (host_offset > pos)
0285 return host_offset - pos;
0286
0287 return 0;
0288 }
0289
0290 static size_t sof_wait_dtrace_avail(struct snd_sof_dev *sdev, loff_t pos,
0291 size_t buffer_size)
0292 {
0293 size_t ret = sof_dtrace_avail(sdev, pos, buffer_size);
0294 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0295 wait_queue_entry_t wait;
0296
0297
0298 if (ret)
0299 return ret;
0300
0301 if (priv->dtrace_draining && !trace_pos_update_expected(priv)) {
0302
0303
0304
0305
0306 priv->dtrace_draining = false;
0307 return 0;
0308 }
0309
0310
0311 init_waitqueue_entry(&wait, current);
0312 set_current_state(TASK_INTERRUPTIBLE);
0313 add_wait_queue(&priv->trace_sleep, &wait);
0314
0315 if (!signal_pending(current)) {
0316
0317 schedule_timeout(MAX_SCHEDULE_TIMEOUT);
0318 }
0319 remove_wait_queue(&priv->trace_sleep, &wait);
0320
0321 return sof_dtrace_avail(sdev, pos, buffer_size);
0322 }
0323
0324 static ssize_t dfsentry_dtrace_read(struct file *file, char __user *buffer,
0325 size_t count, loff_t *ppos)
0326 {
0327 struct snd_sof_dfsentry *dfse = file->private_data;
0328 struct snd_sof_dev *sdev = dfse->sdev;
0329 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0330 unsigned long rem;
0331 loff_t lpos = *ppos;
0332 size_t avail, buffer_size = dfse->size;
0333 u64 lpos_64;
0334
0335
0336 priv->dtrace_error = false;
0337
0338
0339 if (lpos < 0)
0340 return -EINVAL;
0341 if (!count)
0342 return 0;
0343
0344
0345 lpos_64 = lpos;
0346 lpos = do_div(lpos_64, buffer_size);
0347
0348
0349 avail = sof_wait_dtrace_avail(sdev, lpos, buffer_size);
0350 if (priv->dtrace_error) {
0351 dev_err(sdev->dev, "trace IO error\n");
0352 return -EIO;
0353 }
0354
0355
0356 if (!avail)
0357 return 0;
0358
0359
0360 if (count > avail)
0361 count = avail;
0362
0363
0364
0365
0366
0367
0368
0369 snd_dma_buffer_sync(&priv->dmatb, SNDRV_DMA_SYNC_CPU);
0370
0371 rem = copy_to_user(buffer, ((u8 *)(dfse->buf) + lpos), count);
0372 if (rem)
0373 return -EFAULT;
0374
0375 *ppos += count;
0376
0377
0378 return count;
0379 }
0380
0381 static int dfsentry_dtrace_release(struct inode *inode, struct file *file)
0382 {
0383 struct snd_sof_dfsentry *dfse = inode->i_private;
0384 struct snd_sof_dev *sdev = dfse->sdev;
0385 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0386
0387
0388 if (priv->dtrace_state != SOF_DTRACE_ENABLED)
0389 sof_dtrace_set_host_offset(priv, 0);
0390
0391 return 0;
0392 }
0393
0394 static const struct file_operations sof_dfs_dtrace_fops = {
0395 .open = simple_open,
0396 .read = dfsentry_dtrace_read,
0397 .llseek = default_llseek,
0398 .release = dfsentry_dtrace_release,
0399 };
0400
0401 static int debugfs_create_dtrace(struct snd_sof_dev *sdev)
0402 {
0403 struct sof_dtrace_priv *priv;
0404 struct snd_sof_dfsentry *dfse;
0405 int ret;
0406
0407 if (!sdev)
0408 return -EINVAL;
0409
0410 priv = sdev->fw_trace_data;
0411
0412 ret = debugfs_create_trace_filter(sdev);
0413 if (ret < 0)
0414 dev_warn(sdev->dev, "failed to create filter debugfs file: %d", ret);
0415
0416 dfse = devm_kzalloc(sdev->dev, sizeof(*dfse), GFP_KERNEL);
0417 if (!dfse)
0418 return -ENOMEM;
0419
0420 dfse->type = SOF_DFSENTRY_TYPE_BUF;
0421 dfse->buf = priv->dmatb.area;
0422 dfse->size = priv->dmatb.bytes;
0423 dfse->sdev = sdev;
0424
0425 debugfs_create_file("trace", 0444, sdev->debugfs_root, dfse,
0426 &sof_dfs_dtrace_fops);
0427
0428 return 0;
0429 }
0430
0431 static int ipc3_dtrace_enable(struct snd_sof_dev *sdev)
0432 {
0433 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0434 struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
0435 struct sof_ipc_fw_version *v = &ready->version;
0436 struct sof_ipc_dma_trace_params_ext params;
0437 struct sof_ipc_reply ipc_reply;
0438 int ret;
0439
0440 if (!sdev->fw_trace_is_supported)
0441 return 0;
0442
0443 if (priv->dtrace_state == SOF_DTRACE_ENABLED || !priv->dma_trace_pages)
0444 return -EINVAL;
0445
0446 if (priv->dtrace_state == SOF_DTRACE_STOPPED)
0447 goto start;
0448
0449
0450 params.hdr.cmd = SOF_IPC_GLB_TRACE_MSG;
0451
0452 if (v->abi_version >= SOF_ABI_VER(3, 7, 0)) {
0453 params.hdr.size = sizeof(struct sof_ipc_dma_trace_params_ext);
0454 params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS_EXT;
0455 params.timestamp_ns = ktime_get();
0456 } else {
0457 params.hdr.size = sizeof(struct sof_ipc_dma_trace_params);
0458 params.hdr.cmd |= SOF_IPC_TRACE_DMA_PARAMS;
0459 }
0460 params.buffer.phy_addr = priv->dmatp.addr;
0461 params.buffer.size = priv->dmatb.bytes;
0462 params.buffer.pages = priv->dma_trace_pages;
0463 params.stream_tag = 0;
0464
0465 sof_dtrace_set_host_offset(priv, 0);
0466 priv->dtrace_draining = false;
0467
0468 ret = sof_dtrace_host_init(sdev, &priv->dmatb, ¶ms);
0469 if (ret < 0) {
0470 dev_err(sdev->dev, "Host dtrace init failed: %d\n", ret);
0471 return ret;
0472 }
0473 dev_dbg(sdev->dev, "stream_tag: %d\n", params.stream_tag);
0474
0475
0476 priv->dtrace_state = SOF_DTRACE_INITIALIZING;
0477 ret = sof_ipc_tx_message(sdev->ipc, ¶ms, sizeof(params), &ipc_reply, sizeof(ipc_reply));
0478 if (ret < 0) {
0479 dev_err(sdev->dev, "can't set params for DMA for trace %d\n", ret);
0480 goto trace_release;
0481 }
0482
0483 start:
0484 priv->dtrace_state = SOF_DTRACE_ENABLED;
0485
0486 ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_START);
0487 if (ret < 0) {
0488 dev_err(sdev->dev, "Host dtrace trigger start failed: %d\n", ret);
0489 goto trace_release;
0490 }
0491
0492 return 0;
0493
0494 trace_release:
0495 priv->dtrace_state = SOF_DTRACE_DISABLED;
0496 sof_dtrace_host_release(sdev);
0497 return ret;
0498 }
0499
0500 static int ipc3_dtrace_init(struct snd_sof_dev *sdev)
0501 {
0502 struct sof_dtrace_priv *priv;
0503 int ret;
0504
0505
0506 if (sdev->pdata->ipc_type != SOF_IPC)
0507 return -EOPNOTSUPP;
0508
0509 if (sdev->fw_trace_data) {
0510 dev_err(sdev->dev, "fw_trace_data has been already allocated\n");
0511 return -EBUSY;
0512 }
0513
0514 priv = devm_kzalloc(sdev->dev, sizeof(*priv), GFP_KERNEL);
0515 if (!priv)
0516 return -ENOMEM;
0517
0518 sdev->fw_trace_data = priv;
0519
0520
0521 priv->dtrace_state = SOF_DTRACE_DISABLED;
0522
0523
0524 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, sdev->dev,
0525 PAGE_SIZE, &priv->dmatp);
0526 if (ret < 0) {
0527 dev_err(sdev->dev, "can't alloc page table for trace %d\n", ret);
0528 return ret;
0529 }
0530
0531
0532 ret = snd_dma_alloc_dir_pages(SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
0533 DMA_FROM_DEVICE, DMA_BUF_SIZE_FOR_TRACE,
0534 &priv->dmatb);
0535 if (ret < 0) {
0536 dev_err(sdev->dev, "can't alloc buffer for trace %d\n", ret);
0537 goto page_err;
0538 }
0539
0540
0541 ret = snd_sof_create_page_table(sdev->dev, &priv->dmatb,
0542 priv->dmatp.area, priv->dmatb.bytes);
0543 if (ret < 0)
0544 goto table_err;
0545
0546 priv->dma_trace_pages = ret;
0547 dev_dbg(sdev->dev, "dma_trace_pages: %d\n", priv->dma_trace_pages);
0548
0549 if (sdev->first_boot) {
0550 ret = debugfs_create_dtrace(sdev);
0551 if (ret < 0)
0552 goto table_err;
0553 }
0554
0555 init_waitqueue_head(&priv->trace_sleep);
0556
0557 ret = ipc3_dtrace_enable(sdev);
0558 if (ret < 0)
0559 goto table_err;
0560
0561 return 0;
0562 table_err:
0563 priv->dma_trace_pages = 0;
0564 snd_dma_free_pages(&priv->dmatb);
0565 page_err:
0566 snd_dma_free_pages(&priv->dmatp);
0567 return ret;
0568 }
0569
0570 int ipc3_dtrace_posn_update(struct snd_sof_dev *sdev,
0571 struct sof_ipc_dma_trace_posn *posn)
0572 {
0573 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0574
0575 if (!sdev->fw_trace_is_supported)
0576 return 0;
0577
0578 if (trace_pos_update_expected(priv) &&
0579 sof_dtrace_set_host_offset(priv, posn->host_offset))
0580 wake_up(&priv->trace_sleep);
0581
0582 if (posn->overflow != 0)
0583 dev_err(sdev->dev,
0584 "DSP trace buffer overflow %u bytes. Total messages %d\n",
0585 posn->overflow, posn->messages);
0586
0587 return 0;
0588 }
0589
0590
0591 static void ipc3_dtrace_fw_crashed(struct snd_sof_dev *sdev)
0592 {
0593 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0594
0595 if (priv->dtrace_state == SOF_DTRACE_ENABLED) {
0596 priv->dtrace_error = true;
0597 wake_up(&priv->trace_sleep);
0598 }
0599 }
0600
0601 static void ipc3_dtrace_release(struct snd_sof_dev *sdev, bool only_stop)
0602 {
0603 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0604 struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
0605 struct sof_ipc_fw_version *v = &ready->version;
0606 struct sof_ipc_cmd_hdr hdr;
0607 struct sof_ipc_reply ipc_reply;
0608 int ret;
0609
0610 if (!sdev->fw_trace_is_supported || priv->dtrace_state == SOF_DTRACE_DISABLED)
0611 return;
0612
0613 ret = sof_dtrace_host_trigger(sdev, SNDRV_PCM_TRIGGER_STOP);
0614 if (ret < 0)
0615 dev_err(sdev->dev, "Host dtrace trigger stop failed: %d\n", ret);
0616 priv->dtrace_state = SOF_DTRACE_STOPPED;
0617
0618
0619
0620
0621
0622 if (v->abi_version >= SOF_ABI_VER(3, 20, 0)) {
0623 hdr.size = sizeof(hdr);
0624 hdr.cmd = SOF_IPC_GLB_TRACE_MSG | SOF_IPC_TRACE_DMA_FREE;
0625
0626 ret = sof_ipc_tx_message(sdev->ipc, &hdr, hdr.size,
0627 &ipc_reply, sizeof(ipc_reply));
0628 if (ret < 0)
0629 dev_err(sdev->dev, "DMA_TRACE_FREE failed with error: %d\n", ret);
0630 }
0631
0632 if (only_stop)
0633 goto out;
0634
0635 ret = sof_dtrace_host_release(sdev);
0636 if (ret < 0)
0637 dev_err(sdev->dev, "Host dtrace release failed %d\n", ret);
0638
0639 priv->dtrace_state = SOF_DTRACE_DISABLED;
0640
0641 out:
0642 priv->dtrace_draining = true;
0643 wake_up(&priv->trace_sleep);
0644 }
0645
0646 static void ipc3_dtrace_suspend(struct snd_sof_dev *sdev, pm_message_t pm_state)
0647 {
0648 ipc3_dtrace_release(sdev, pm_state.event == SOF_DSP_PM_D0);
0649 }
0650
0651 static int ipc3_dtrace_resume(struct snd_sof_dev *sdev)
0652 {
0653 return ipc3_dtrace_enable(sdev);
0654 }
0655
0656 static void ipc3_dtrace_free(struct snd_sof_dev *sdev)
0657 {
0658 struct sof_dtrace_priv *priv = sdev->fw_trace_data;
0659
0660
0661 ipc3_dtrace_release(sdev, false);
0662
0663 if (priv->dma_trace_pages) {
0664 snd_dma_free_pages(&priv->dmatb);
0665 snd_dma_free_pages(&priv->dmatp);
0666 priv->dma_trace_pages = 0;
0667 }
0668 }
0669
0670 const struct sof_ipc_fw_tracing_ops ipc3_dtrace_ops = {
0671 .init = ipc3_dtrace_init,
0672 .free = ipc3_dtrace_free,
0673 .fw_crashed = ipc3_dtrace_fw_crashed,
0674 .suspend = ipc3_dtrace_suspend,
0675 .resume = ipc3_dtrace_resume,
0676 };