From 01e6fac4d61fdd7fff5433942ec93fc2ea1e4df1 Mon Sep 17 00:00:00 2001 From: Joshua Bakita Date: Wed, 28 Jun 2023 18:24:25 -0400 Subject: Include nvgpu headers These are needed to build on NVIDIA's Jetson boards for the time being. Only a couple structs are required, so it should be fairly easy to remove this dependency at some point in the future. --- include/gk20a/fecs_trace_gk20a.c | 744 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 744 insertions(+) create mode 100644 include/gk20a/fecs_trace_gk20a.c (limited to 'include/gk20a/fecs_trace_gk20a.c') diff --git a/include/gk20a/fecs_trace_gk20a.c b/include/gk20a/fecs_trace_gk20a.c new file mode 100644 index 0000000..5c1c5e0 --- /dev/null +++ b/include/gk20a/fecs_trace_gk20a.c @@ -0,0 +1,744 @@ +/* + * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fecs_trace_gk20a.h" +#include "gk20a.h" +#include "gr_gk20a.h" + +#include +#include + +#include +#include + +struct gk20a_fecs_trace_hash_ent { + u32 context_ptr; + pid_t pid; + struct hlist_node node; +}; + +struct gk20a_fecs_trace { + + DECLARE_HASHTABLE(pid_hash_table, GK20A_FECS_TRACE_HASH_BITS); + struct nvgpu_mutex hash_lock; + struct nvgpu_mutex poll_lock; + struct nvgpu_thread poll_task; + bool init; + struct nvgpu_mutex enable_lock; + u32 enable_count; +}; + +#ifdef CONFIG_GK20A_CTXSW_TRACE +u32 gk20a_fecs_trace_record_ts_tag_invalid_ts_v(void) +{ + return ctxsw_prog_record_timestamp_timestamp_hi_tag_invalid_timestamp_v(); +} + +u32 gk20a_fecs_trace_record_ts_tag_v(u64 ts) +{ + return ctxsw_prog_record_timestamp_timestamp_hi_tag_v((u32) (ts >> 32)); +} + +u64 gk20a_fecs_trace_record_ts_timestamp_v(u64 ts) +{ + return ts & ~(((u64)ctxsw_prog_record_timestamp_timestamp_hi_tag_m()) << 32); +} + +static u32 gk20a_fecs_trace_fecs_context_ptr(struct gk20a *g, struct channel_gk20a *ch) +{ + return (u32) (nvgpu_inst_block_addr(g, &ch->inst_block) >> 12LL); +} + +int gk20a_fecs_trace_num_ts(void) +{ + return (ctxsw_prog_record_timestamp_record_size_in_bytes_v() + - sizeof(struct gk20a_fecs_trace_record)) / sizeof(u64); +} + +struct gk20a_fecs_trace_record *gk20a_fecs_trace_get_record( + struct gk20a *g, int idx) +{ + struct nvgpu_mem *mem = &g->gr.global_ctx_buffer[FECS_TRACE_BUFFER].mem; + + return (struct gk20a_fecs_trace_record *) + ((u8 *) mem->cpu_va + + (idx * ctxsw_prog_record_timestamp_record_size_in_bytes_v())); +} + +bool gk20a_fecs_trace_is_valid_record(struct gk20a_fecs_trace_record *r) +{ + /* + * testing magic_hi should suffice. magic_lo is sometimes used + * as a sequence number in experimental ucode. + */ + return (r->magic_hi + == ctxsw_prog_record_timestamp_magic_value_hi_v_value_v()); +} + +int gk20a_fecs_trace_get_read_index(struct gk20a *g) +{ + return gr_gk20a_elpg_protected_call(g, + gk20a_readl(g, gr_fecs_mailbox1_r())); +} + +int gk20a_fecs_trace_get_write_index(struct gk20a *g) +{ + return gr_gk20a_elpg_protected_call(g, + gk20a_readl(g, gr_fecs_mailbox0_r())); +} + +static int gk20a_fecs_trace_set_read_index(struct gk20a *g, int index) +{ + nvgpu_log(g, gpu_dbg_ctxsw, "set read=%d", index); + return gr_gk20a_elpg_protected_call(g, + (gk20a_writel(g, gr_fecs_mailbox1_r(), index), 0)); +} + +void gk20a_fecs_trace_hash_dump(struct gk20a *g) +{ + u32 bkt; + struct gk20a_fecs_trace_hash_ent *ent; + struct gk20a_fecs_trace *trace = g->fecs_trace; + + nvgpu_log(g, gpu_dbg_ctxsw, "dumping hash table"); + + nvgpu_mutex_acquire(&trace->hash_lock); + hash_for_each(trace->pid_hash_table, bkt, ent, node) + { + nvgpu_log(g, gpu_dbg_ctxsw, " ent=%p bkt=%x context_ptr=%x pid=%d", + ent, bkt, ent->context_ptr, ent->pid); + + } + nvgpu_mutex_release(&trace->hash_lock); +} + +static int gk20a_fecs_trace_hash_add(struct gk20a *g, u32 context_ptr, pid_t pid) +{ + struct gk20a_fecs_trace_hash_ent *he; + struct gk20a_fecs_trace *trace = g->fecs_trace; + + nvgpu_log(g, gpu_dbg_fn | gpu_dbg_ctxsw, + "adding hash entry context_ptr=%x -> pid=%d", context_ptr, pid); + + he = nvgpu_kzalloc(g, sizeof(*he)); + if (unlikely(!he)) { + nvgpu_warn(g, + "can't alloc new hash entry for context_ptr=%x pid=%d", + context_ptr, pid); + return -ENOMEM; + } + + he->context_ptr = context_ptr; + he->pid = pid; + nvgpu_mutex_acquire(&trace->hash_lock); + hash_add(trace->pid_hash_table, &he->node, context_ptr); + nvgpu_mutex_release(&trace->hash_lock); + return 0; +} + +static void gk20a_fecs_trace_hash_del(struct gk20a *g, u32 context_ptr) +{ + struct hlist_node *tmp; + struct gk20a_fecs_trace_hash_ent *ent; + struct gk20a_fecs_trace *trace = g->fecs_trace; + + nvgpu_log(g, gpu_dbg_fn | gpu_dbg_ctxsw, + "freeing hash entry context_ptr=%x", context_ptr); + + nvgpu_mutex_acquire(&trace->hash_lock); + hash_for_each_possible_safe(trace->pid_hash_table, ent, tmp, node, + context_ptr) { + if (ent->context_ptr == context_ptr) { + hash_del(&ent->node); + nvgpu_log(g, gpu_dbg_ctxsw, + "freed hash entry=%p context_ptr=%x", ent, + ent->context_ptr); + nvgpu_kfree(g, ent); + break; + } + } + nvgpu_mutex_release(&trace->hash_lock); +} + +static void gk20a_fecs_trace_free_hash_table(struct gk20a *g) +{ + u32 bkt; + struct hlist_node *tmp; + struct gk20a_fecs_trace_hash_ent *ent; + struct gk20a_fecs_trace *trace = g->fecs_trace; + + nvgpu_log(g, gpu_dbg_fn | gpu_dbg_ctxsw, "trace=%p", trace); + + nvgpu_mutex_acquire(&trace->hash_lock); + hash_for_each_safe(trace->pid_hash_table, bkt, tmp, ent, node) { + hash_del(&ent->node); + nvgpu_kfree(g, ent); + } + nvgpu_mutex_release(&trace->hash_lock); + +} + +static pid_t gk20a_fecs_trace_find_pid(struct gk20a *g, u32 context_ptr) +{ + struct gk20a_fecs_trace_hash_ent *ent; + struct gk20a_fecs_trace *trace = g->fecs_trace; + pid_t pid = 0; + + nvgpu_mutex_acquire(&trace->hash_lock); + hash_for_each_possible(trace->pid_hash_table, ent, node, context_ptr) { + if (ent->context_ptr == context_ptr) { + nvgpu_log(g, gpu_dbg_ctxsw, + "found context_ptr=%x -> pid=%d", + ent->context_ptr, ent->pid); + pid = ent->pid; + break; + } + } + nvgpu_mutex_release(&trace->hash_lock); + + return pid; +} + +/* + * Converts HW entry format to userspace-facing format and pushes it to the + * queue. + */ +static int gk20a_fecs_trace_ring_read(struct gk20a *g, int index) +{ + int i; + struct nvgpu_gpu_ctxsw_trace_entry entry = { }; + struct gk20a_fecs_trace *trace = g->fecs_trace; + pid_t cur_pid; + pid_t new_pid; + int count = 0; + + /* for now, only one VM */ + const int vmid = 0; + + struct gk20a_fecs_trace_record *r = + gk20a_fecs_trace_get_record(g, index); + + nvgpu_log(g, gpu_dbg_fn | gpu_dbg_ctxsw, + "consuming record trace=%p read=%d record=%p", trace, index, r); + + if (unlikely(!gk20a_fecs_trace_is_valid_record(r))) { + nvgpu_warn(g, + "trace=%p read=%d record=%p magic_lo=%08x magic_hi=%08x (invalid)", + trace, index, r, r->magic_lo, r->magic_hi); + return -EINVAL; + } + + /* Clear magic_hi to detect cases where CPU could read write index + * before FECS record is actually written to DRAM. This should not + * as we force FECS writes to SYSMEM by reading through PRAMIN. + */ + r->magic_hi = 0; + + cur_pid = gk20a_fecs_trace_find_pid(g, r->context_ptr); + new_pid = gk20a_fecs_trace_find_pid(g, r->new_context_ptr); + + nvgpu_log(g, gpu_dbg_fn | gpu_dbg_ctxsw, + "context_ptr=%x (pid=%d) new_context_ptr=%x (pid=%d)", + r->context_ptr, cur_pid, r->new_context_ptr, new_pid); + + entry.context_id = r->context_id; + entry.vmid = vmid; + + /* break out FECS record into trace events */ + for (i = 0; i < gk20a_fecs_trace_num_ts(); i++) { + + entry.tag = gk20a_fecs_trace_record_ts_tag_v(r->ts[i]); + entry.timestamp = gk20a_fecs_trace_record_ts_timestamp_v(r->ts[i]); + entry.timestamp <<= GK20A_FECS_TRACE_PTIMER_SHIFT; + + nvgpu_log(g, gpu_dbg_ctxsw, + "tag=%x timestamp=%llx context_id=%08x new_context_id=%08x", + entry.tag, entry.timestamp, r->context_id, + r->new_context_id); + + switch (nvgpu_gpu_ctxsw_tags_to_common_tags(entry.tag)) { + case NVGPU_GPU_CTXSW_TAG_RESTORE_START: + case NVGPU_GPU_CTXSW_TAG_CONTEXT_START: + entry.context_id = r->new_context_id; + entry.pid = new_pid; + break; + + case NVGPU_GPU_CTXSW_TAG_CTXSW_REQ_BY_HOST: + case NVGPU_GPU_CTXSW_TAG_FE_ACK: + case NVGPU_GPU_CTXSW_TAG_FE_ACK_WFI: + case NVGPU_GPU_CTXSW_TAG_FE_ACK_GFXP: + case NVGPU_GPU_CTXSW_TAG_FE_ACK_CTAP: + case NVGPU_GPU_CTXSW_TAG_FE_ACK_CILP: + case NVGPU_GPU_CTXSW_TAG_SAVE_END: + entry.context_id = r->context_id; + entry.pid = cur_pid; + break; + + default: + /* tags are not guaranteed to start at the beginning */ + WARN_ON(entry.tag && (entry.tag != NVGPU_GPU_CTXSW_TAG_INVALID_TIMESTAMP)); + continue; + } + + nvgpu_log(g, gpu_dbg_ctxsw, "tag=%x context_id=%x pid=%lld", + entry.tag, entry.context_id, entry.pid); + + if (!entry.context_id) + continue; + + gk20a_ctxsw_trace_write(g, &entry); + count++; + } + + gk20a_ctxsw_trace_wake_up(g, vmid); + return count; +} + +int gk20a_fecs_trace_poll(struct gk20a *g) +{ + struct gk20a_fecs_trace *trace = g->fecs_trace; + + int read = 0; + int write = 0; + int cnt; + int err; + + err = gk20a_busy(g); + if (unlikely(err)) + return err; + + nvgpu_mutex_acquire(&trace->poll_lock); + write = gk20a_fecs_trace_get_write_index(g); + if (unlikely((write < 0) || (write >= GK20A_FECS_TRACE_NUM_RECORDS))) { + nvgpu_err(g, + "failed to acquire write index, write=%d", write); + err = write; + goto done; + } + + read = gk20a_fecs_trace_get_read_index(g); + + cnt = CIRC_CNT(write, read, GK20A_FECS_TRACE_NUM_RECORDS); + if (!cnt) + goto done; + + nvgpu_log(g, gpu_dbg_ctxsw, + "circular buffer: read=%d (mailbox=%d) write=%d cnt=%d", + read, gk20a_fecs_trace_get_read_index(g), write, cnt); + + /* Ensure all FECS writes have made it to SYSMEM */ + g->ops.mm.fb_flush(g); + + if (nvgpu_is_enabled(g, NVGPU_FECS_TRACE_FEATURE_CONTROL)) { + /* Bits 30:0 of MAILBOX1 represents actual read pointer value */ + read = read & (~(BIT32(NVGPU_FECS_TRACE_FEATURE_CONTROL_BIT))); + } + + while (read != write) { + cnt = gk20a_fecs_trace_ring_read(g, read); + if (cnt > 0) { + nvgpu_log(g, gpu_dbg_ctxsw, + "number of trace entries added: %d", cnt); + } + + /* Get to next record. */ + read = (read + 1) & (GK20A_FECS_TRACE_NUM_RECORDS - 1); + } + + if (nvgpu_is_enabled(g, NVGPU_FECS_TRACE_FEATURE_CONTROL)) { + /* + * In the next step, read pointer is going to be updated. + * So, MSB of read pointer should be set back to 1. This will + * keep FECS trace enabled. + */ + read = read | (BIT32(NVGPU_FECS_TRACE_FEATURE_CONTROL_BIT)); + } + + /* ensure FECS records has been updated before incrementing read index */ + nvgpu_wmb(); + gk20a_fecs_trace_set_read_index(g, read); + +done: + nvgpu_mutex_release(&trace->poll_lock); + gk20a_idle(g); + return err; +} + +static int gk20a_fecs_trace_periodic_polling(void *arg) +{ + struct gk20a *g = (struct gk20a *)arg; + struct gk20a_fecs_trace *trace = g->fecs_trace; + + pr_info("%s: running\n", __func__); + + while (!nvgpu_thread_should_stop(&trace->poll_task)) { + + nvgpu_usleep_range(GK20A_FECS_TRACE_FRAME_PERIOD_US, + GK20A_FECS_TRACE_FRAME_PERIOD_US * 2); + + gk20a_fecs_trace_poll(g); + } + + return 0; +} + +size_t gk20a_fecs_trace_buffer_size(struct gk20a *g) +{ + return GK20A_FECS_TRACE_NUM_RECORDS + * ctxsw_prog_record_timestamp_record_size_in_bytes_v(); +} + +int gk20a_fecs_trace_init(struct gk20a *g) +{ + struct gk20a_fecs_trace *trace; + int err; + + trace = nvgpu_kzalloc(g, sizeof(struct gk20a_fecs_trace)); + if (!trace) { + nvgpu_warn(g, "failed to allocate fecs_trace"); + return -ENOMEM; + } + g->fecs_trace = trace; + + err = nvgpu_mutex_init(&trace->poll_lock); + if (err) + goto clean; + err = nvgpu_mutex_init(&trace->hash_lock); + if (err) + goto clean_poll_lock; + + err = nvgpu_mutex_init(&trace->enable_lock); + if (err) + goto clean_hash_lock; + + BUG_ON(!is_power_of_2(GK20A_FECS_TRACE_NUM_RECORDS)); + hash_init(trace->pid_hash_table); + + __nvgpu_set_enabled(g, NVGPU_SUPPORT_FECS_CTXSW_TRACE, true); + + trace->enable_count = 0; + trace->init = true; + + return 0; + +clean_hash_lock: + nvgpu_mutex_destroy(&trace->hash_lock); + +clean_poll_lock: + nvgpu_mutex_destroy(&trace->poll_lock); +clean: + nvgpu_kfree(g, trace); + g->fecs_trace = NULL; + return err; +} + +int gk20a_fecs_trace_bind_channel(struct gk20a *g, + struct channel_gk20a *ch) +{ + /* + * map our circ_buf to the context space and store the GPU VA + * in the context header. + */ + + u32 lo; + u32 hi; + u64 addr; + struct tsg_gk20a *tsg; + struct nvgpu_gr_ctx *ch_ctx; + struct gk20a_fecs_trace *trace = g->fecs_trace; + struct nvgpu_mem *mem; + u32 context_ptr = gk20a_fecs_trace_fecs_context_ptr(g, ch); + u32 aperture_mask; + + tsg = tsg_gk20a_from_ch(ch); + if (tsg == NULL) { + nvgpu_err(g, "chid: %d is not bound to tsg", ch->chid); + return -EINVAL; + } + + nvgpu_log(g, gpu_dbg_fn|gpu_dbg_ctxsw, + "chid=%d context_ptr=%x inst_block=%llx", + ch->chid, context_ptr, + nvgpu_inst_block_addr(g, &ch->inst_block)); + + tsg = tsg_gk20a_from_ch(ch); + if (!tsg) + return -EINVAL; + + ch_ctx = &tsg->gr_ctx; + mem = &ch_ctx->mem; + + if (!trace) + return -ENOMEM; + + mem = &g->gr.global_ctx_buffer[FECS_TRACE_BUFFER].mem; + + if (nvgpu_is_enabled(g, NVGPU_FECS_TRACE_VA)) { + addr = ch_ctx->global_ctx_buffer_va[FECS_TRACE_BUFFER_VA]; + nvgpu_log(g, gpu_dbg_ctxsw, "gpu_va=%llx", addr); + aperture_mask = 0; + } else { + addr = nvgpu_inst_block_addr(g, mem); + nvgpu_log(g, gpu_dbg_ctxsw, "pa=%llx", addr); + aperture_mask = nvgpu_aperture_mask(g, mem, + ctxsw_prog_main_image_context_timestamp_buffer_ptr_hi_target_sys_mem_noncoherent_f(), + ctxsw_prog_main_image_context_timestamp_buffer_ptr_hi_target_sys_mem_coherent_f(), + ctxsw_prog_main_image_context_timestamp_buffer_ptr_hi_target_vid_mem_f()); + } + if (!addr) + return -ENOMEM; + + lo = u64_lo32(addr); + hi = u64_hi32(addr); + + mem = &ch_ctx->mem; + + nvgpu_log(g, gpu_dbg_ctxsw, "addr_hi=%x addr_lo=%x count=%d", hi, + lo, GK20A_FECS_TRACE_NUM_RECORDS); + + nvgpu_mem_wr(g, mem, + ctxsw_prog_main_image_context_timestamp_buffer_control_o(), + ctxsw_prog_main_image_context_timestamp_buffer_control_num_records_f( + GK20A_FECS_TRACE_NUM_RECORDS)); + + if (nvgpu_is_enabled(g, NVGPU_FECS_TRACE_VA)) + mem = &ch->ctx_header; + + nvgpu_mem_wr(g, mem, + ctxsw_prog_main_image_context_timestamp_buffer_ptr_o(), + lo); + nvgpu_mem_wr(g, mem, + ctxsw_prog_main_image_context_timestamp_buffer_ptr_hi_o(), + ctxsw_prog_main_image_context_timestamp_buffer_ptr_v_f(hi) | + aperture_mask); + + /* pid (process identifier) in user space, corresponds to tgid (thread + * group id) in kernel space. + */ + gk20a_fecs_trace_hash_add(g, context_ptr, tsg->tgid); + + return 0; +} + +int gk20a_fecs_trace_unbind_channel(struct gk20a *g, struct channel_gk20a *ch) +{ + u32 context_ptr = gk20a_fecs_trace_fecs_context_ptr(g, ch); + + if (g->fecs_trace) { + nvgpu_log(g, gpu_dbg_fn|gpu_dbg_ctxsw, + "ch=%p context_ptr=%x", ch, context_ptr); + + if (g->ops.fecs_trace.is_enabled(g)) { + if (g->ops.fecs_trace.flush) + g->ops.fecs_trace.flush(g); + gk20a_fecs_trace_poll(g); + } + gk20a_fecs_trace_hash_del(g, context_ptr); + } + return 0; +} + +int gk20a_fecs_trace_reset(struct gk20a *g) +{ + nvgpu_log(g, gpu_dbg_fn|gpu_dbg_ctxsw, " "); + + if (!g->ops.fecs_trace.is_enabled(g)) + return 0; + + gk20a_fecs_trace_poll(g); + return gk20a_fecs_trace_set_read_index(g, 0); +} + +int gk20a_fecs_trace_deinit(struct gk20a *g) +{ + struct gk20a_fecs_trace *trace = g->fecs_trace; + + if (!trace->init) + return 0; + + /* + * Check if tracer was enabled before attempting to stop the + * tracer thread. + */ + if (trace->enable_count > 0) { + nvgpu_thread_stop(&trace->poll_task); + } + gk20a_fecs_trace_free_hash_table(g); + + nvgpu_mutex_destroy(&g->fecs_trace->hash_lock); + nvgpu_mutex_destroy(&g->fecs_trace->poll_lock); + nvgpu_mutex_destroy(&g->fecs_trace->enable_lock); + + nvgpu_kfree(g, g->fecs_trace); + g->fecs_trace = NULL; + return 0; +} + +int gk20a_gr_max_entries(struct gk20a *g, + struct nvgpu_gpu_ctxsw_trace_filter *filter) +{ + int n; + int tag; + + /* Compute number of entries per record, with given filter */ + for (n = 0, tag = 0; tag < gk20a_fecs_trace_num_ts(); tag++) + n += (NVGPU_GPU_CTXSW_FILTER_ISSET(tag, filter) != 0); + + /* Return max number of entries generated for the whole ring */ + return n * GK20A_FECS_TRACE_NUM_RECORDS; +} + +int gk20a_fecs_trace_enable(struct gk20a *g) +{ + struct gk20a_fecs_trace *trace = g->fecs_trace; + int write; + int err = 0; + + if (!trace) + return -EINVAL; + + nvgpu_mutex_acquire(&trace->enable_lock); + trace->enable_count++; + + if (trace->enable_count == 1U) { + /* drop data in hw buffer */ + if (g->ops.fecs_trace.flush) + g->ops.fecs_trace.flush(g); + + write = gk20a_fecs_trace_get_write_index(g); + + if (nvgpu_is_enabled(g, NVGPU_FECS_TRACE_FEATURE_CONTROL)) { + /* + * For enabling FECS trace support, MAILBOX1's MSB + * (Bit 31:31) should be set to 1. Bits 30:0 represents + * actual pointer value. + */ + write = write | + (BIT32(NVGPU_FECS_TRACE_FEATURE_CONTROL_BIT)); + } + gk20a_fecs_trace_set_read_index(g, write); + + /* + * FECS ucode does a priv holdoff around the assertion of + * context reset. So, pri transactions (e.g. mailbox1 register + * write) might fail due to this. Hence, do write with ack + * i.e. write and read it back to make sure write happened for + * mailbox1. + */ + while (gk20a_fecs_trace_get_read_index(g) != write) { + nvgpu_log(g, gpu_dbg_ctxsw, "mailbox1 update failed"); + gk20a_fecs_trace_set_read_index(g, write); + } + + err = nvgpu_thread_create(&trace->poll_task, g, + gk20a_fecs_trace_periodic_polling, __func__); + if (err) { + nvgpu_warn(g, + "failed to create FECS polling task"); + goto done; + } + } + +done: + nvgpu_mutex_release(&trace->enable_lock); + return err; +} + +int gk20a_fecs_trace_disable(struct gk20a *g) +{ + struct gk20a_fecs_trace *trace = g->fecs_trace; + int read = 0; + + if (trace == NULL) { + return -EINVAL; + } + + nvgpu_mutex_acquire(&trace->enable_lock); + if (trace->enable_count <= 0U) { + nvgpu_mutex_release(&trace->enable_lock); + return 0; + } + trace->enable_count--; + if (trace->enable_count == 0U) { + if (nvgpu_is_enabled(g, NVGPU_FECS_TRACE_FEATURE_CONTROL)) { + /* + * For disabling FECS trace support, MAILBOX1's MSB + * (Bit 31:31) should be set to 0. + */ + read = gk20a_fecs_trace_get_read_index(g) & + (~(BIT32(NVGPU_FECS_TRACE_FEATURE_CONTROL_BIT))); + + gk20a_fecs_trace_set_read_index(g, read); + + /* + * FECS ucode does a priv holdoff around the assertion + * of context reset. So, pri transactions (e.g. + * mailbox1 register write) might fail due to this. + * Hence, do write with ack i.e. write and read it back + * to make sure write happened for mailbox1. + */ + while (gk20a_fecs_trace_get_read_index(g) != read) { + nvgpu_log(g, gpu_dbg_ctxsw, + "mailbox1 update failed"); + gk20a_fecs_trace_set_read_index(g, read); + } + } + + nvgpu_thread_stop(&trace->poll_task); + + } + nvgpu_mutex_release(&trace->enable_lock); + + return -EPERM; +} + +bool gk20a_fecs_trace_is_enabled(struct gk20a *g) +{ + struct gk20a_fecs_trace *trace = g->fecs_trace; + + return (trace && nvgpu_thread_is_running(&trace->poll_task)); +} + +void gk20a_fecs_trace_reset_buffer(struct gk20a *g) +{ + nvgpu_log(g, gpu_dbg_fn|gpu_dbg_ctxsw, " "); + + gk20a_fecs_trace_set_read_index(g, + gk20a_fecs_trace_get_write_index(g)); +} +#endif /* CONFIG_GK20A_CTXSW_TRACE */ -- cgit v1.2.2