From c0ae4c6e6e3bfa614189892af20f63f2c982283e Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Mon, 9 Jul 2018 13:48:44 +0800 Subject: [PATCH] trace: fix dma trace with repeated data bug on APL&CNL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is caused by dma trace buffer overflow in FW. There are too many trace events that the dma trace buffer can’t hold them, And this makes buffer in chaos. It is assumed that the trace msg is transferred to host when it is full, and the occupied memory can be reused. But it is not always true. The dma trace buffer is used as a ring buffer. In warp case, both tail and head of the buffer are used, only the tail of buffer is transferred to host and released. It is because GPDMA on BYT only support successive memory mode. So it is need to be done with two dma calls. Because trace is for debugging and to occupy the cpu usage as little as possible, only the first dma transfer is done and the second one is scheduled next time. So only part of dma trace buffer is released. On APL & CNL, dma gateway is used for dma trace and it supports wrap mode transfer. So the algorithm can be refined for dma gateway Now add a function to do the job for different HW feature. For GPDMA, no change is made currently, but later it would be refined to support link list mode. For platform with dma gateway, transferring trace msg with one dma call. Signed-off-by: Rander Wang --- src/lib/dma-trace.c | 116 +++++++++++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 40 deletions(-) diff --git a/src/lib/dma-trace.c b/src/lib/dma-trace.c index cff9171e04a0..3563e10a8544 100644 --- a/src/lib/dma-trace.c +++ b/src/lib/dma-trace.c @@ -42,6 +42,10 @@ static struct dma_trace_data *trace_data = NULL; +static int dma_trace_get_avali_data(struct dma_trace_data *d, + struct dma_trace_buf *buffer, + int avail); + static uint64_t trace_work(void *data, uint64_t delay) { struct dma_trace_data *d = (struct dma_trace_data *)data; @@ -50,25 +54,7 @@ static uint64_t trace_work(void *data, uint64_t delay) unsigned long flags; uint32_t avail = buffer->avail; int32_t size; - uint32_t hsize; - uint32_t lsize; - - if (d->host_offset == d->host_size) - d->host_offset = 0; - -#if defined CONFIG_DMA_GW - /* - * there isn't DMA completion callback in GW DMA copying. - * so we send previous position always before the next copying - * for guaranteeing previous DMA copying is finished. - * This function will be called once every 500ms at least even - * if no new trace is filled. - */ - if (d->old_host_offset != d->host_offset) { - ipc_dma_trace_send_position(); - d->old_host_offset = d->host_offset; - } -#endif + uint32_t overflow; /* any data to copy ? */ if (avail == 0) @@ -77,33 +63,19 @@ static uint64_t trace_work(void *data, uint64_t delay) /* DMA trace copying is working */ d->copy_in_progress = 1; - /* make sure we dont write more than buffer */ + /* make sure we don't write more than buffer */ if (avail > DMA_TRACE_LOCAL_SIZE) { - d->overflow = avail - DMA_TRACE_LOCAL_SIZE; + overflow = avail - DMA_TRACE_LOCAL_SIZE; avail = DMA_TRACE_LOCAL_SIZE; } else { - d->overflow = 0; + overflow = 0; } - /* copy to host in sections if we wrap */ - lsize = hsize = avail; - - /* host buffer wrap ? */ - if (d->host_offset + avail > d->host_size) - hsize = d->host_size - d->host_offset; - - /* local buffer wrap ? */ - if (buffer->r_ptr + avail > buffer->end_addr) - lsize = buffer->end_addr - buffer->r_ptr; - - /* get smallest size */ - if (hsize < lsize) - size = hsize; - else - size = lsize; + /* dma gateway supports wrap mode copy, but GPDMA doesn't*/ + /* support, so do it differently based on HW features */ + size = dma_trace_get_avali_data(d, buffer, avail); - /* writeback trace data */ - dcache_writeback_region((void*)buffer->r_ptr, size); + d->overflow = overflow; /* copy this section to host */ size = dma_copy_to_host_nowait(&d->dc, config, d->host_offset, @@ -115,6 +87,8 @@ static uint64_t trace_work(void *data, uint64_t delay) /* update host pointer and check for wrap */ d->host_offset += size; + if (d->host_offset == d->host_size) + d->host_offset = 0; /* update local pointer and check for wrap */ buffer->r_ptr += size; @@ -267,6 +241,68 @@ static int dma_trace_start(struct dma_trace_data *d) return err; } +static int dma_trace_get_avali_data(struct dma_trace_data *d, + struct dma_trace_buf *buffer, + int avail) +{ + int size; + + /* there isn't DMA completion callback in GW DMA copying. + * so we send previous position always before the next copying + * for guaranteeing previous DMA copying is finished. + * This function will be called once every 500ms at least even + * if no new trace is filled. + */ + if (d->old_host_offset != d->host_offset) { + ipc_dma_trace_send_position(); + d->old_host_offset = d->host_offset; + } + + /* writeback trace data */ + if (buffer->r_ptr + avail <= buffer->end_addr) { + dcache_writeback_region((void *)buffer->r_ptr, avail); + } else { + size = buffer->end_addr - buffer->r_ptr + 1; + + /* warp case, flush tail and head of trace buffer */ + dcache_writeback_region((void *)buffer->r_ptr, size); + dcache_writeback_region((void *)buffer->addr, avail - size); + } + + return avail; +} +#else +static int dma_trace_get_avali_data(struct dma_trace_data *d, + struct dma_trace_buf *buffer, + int avail) +{ + uint32_t hsize; + uint32_t lsize; + int32_t size; + + /* copy to host in sections if we wrap */ + lsize = avail; + hsize = avail; + + /* host buffer wrap ? */ + if (d->host_offset + avail > d->host_size) + hsize = d->host_size - d->host_offset; + + /* local buffer wrap ? */ + if (buffer->r_ptr + avail > buffer->end_addr) + lsize = buffer->end_addr - buffer->r_ptr; + + /* get smallest size */ + if (hsize < lsize) + size = hsize; + else + size = lsize; + + /* writeback trace data */ + dcache_writeback_region((void *)buffer->r_ptr, size); + + return size; +} #endif int dma_trace_enable(struct dma_trace_data *d)