/* * GK20A Graphics FIFO (gr host) * * Copyright (c) 2011-2021, 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 #include #include #include #include #include #include #include #include #include "gk20a.h" #include "mm_gk20a.h" #include #include #include #include #include #include #define FECS_METHOD_WFI_RESTORE 0x80000 #define FECS_MAILBOX_0_ACK_RESTORE 0x4 static u32 gk20a_fifo_engines_on_id(struct gk20a *g, u32 id, bool is_tsg); static const char *const pbdma_intr_fault_type_desc[] = { "MEMREQ timeout", "MEMACK_TIMEOUT", "MEMACK_EXTRA acks", "MEMDAT_TIMEOUT", "MEMDAT_EXTRA acks", "MEMFLUSH noack", "MEMOP noack", "LBCONNECT noack", "NONE - was LBREQ", "LBACK_TIMEOUT", "LBACK_EXTRA acks", "LBDAT_TIMEOUT", "LBDAT_EXTRA acks", "GPFIFO won't fit", "GPPTR invalid", "GPENTRY invalid", "GPCRC mismatch", "PBPTR get>put", "PBENTRY invld", "PBCRC mismatch", "NONE - was XBARC", "METHOD invld", "METHODCRC mismat", "DEVICE sw method", "[ENGINE]", "SEMAPHORE invlid", "ACQUIRE timeout", "PRI forbidden", "ILLEGAL SYNCPT", "[NO_CTXSW_SEG]", "PBSEG badsplit", "SIGNATURE bad" }; u32 gk20a_fifo_get_engine_ids(struct gk20a *g, u32 engine_id[], u32 engine_id_sz, u32 engine_enum) { struct fifo_gk20a *f = NULL; u32 instance_cnt = 0; u32 engine_id_idx; u32 active_engine_id = 0; struct fifo_engine_info_gk20a *info = NULL; if (g && engine_id_sz && (engine_enum < ENGINE_INVAL_GK20A)) { f = &g->fifo; for (engine_id_idx = 0; engine_id_idx < f->num_engines; ++engine_id_idx) { active_engine_id = f->active_engines_list[engine_id_idx]; info = &f->engine_info[active_engine_id]; if (info->engine_enum == engine_enum) { if (instance_cnt < engine_id_sz) { engine_id[instance_cnt] = active_engine_id; ++instance_cnt; } else { nvgpu_log_info(g, "warning engine_id table sz is small %d", engine_id_sz); } } } } return instance_cnt; } struct fifo_engine_info_gk20a *gk20a_fifo_get_engine_info(struct gk20a *g, u32 engine_id) { struct fifo_gk20a *f = NULL; u32 engine_id_idx; struct fifo_engine_info_gk20a *info = NULL; if (!g) { return info; } f = &g->fifo; if (engine_id < f->max_engines) { for (engine_id_idx = 0; engine_id_idx < f->num_engines; ++engine_id_idx) { if (engine_id == f->active_engines_list[engine_id_idx]) { info = &f->engine_info[engine_id]; break; } } } if (!info) { nvgpu_err(g, "engine_id is not in active list/invalid %d", engine_id); } return info; } bool gk20a_fifo_is_valid_engine_id(struct gk20a *g, u32 engine_id) { struct fifo_gk20a *f = NULL; u32 engine_id_idx; bool valid = false; if (!g) { return valid; } f = &g->fifo; if (engine_id < f->max_engines) { for (engine_id_idx = 0; engine_id_idx < f->num_engines; ++engine_id_idx) { if (engine_id == f->active_engines_list[engine_id_idx]) { valid = true; break; } } } if (!valid) { nvgpu_err(g, "engine_id is not in active list/invalid %d", engine_id); } return valid; } u32 gk20a_fifo_get_gr_engine_id(struct gk20a *g) { u32 gr_engine_cnt = 0; u32 gr_engine_id = FIFO_INVAL_ENGINE_ID; /* Consider 1st available GR engine */ gr_engine_cnt = gk20a_fifo_get_engine_ids(g, &gr_engine_id, 1, ENGINE_GR_GK20A); if (!gr_engine_cnt) { nvgpu_err(g, "No GR engine available on this device!"); } return gr_engine_id; } u32 gk20a_fifo_get_all_ce_engine_reset_mask(struct gk20a *g) { u32 reset_mask = 0; u32 engine_enum = ENGINE_INVAL_GK20A; struct fifo_gk20a *f = NULL; u32 engine_id_idx; struct fifo_engine_info_gk20a *engine_info; u32 active_engine_id = 0; if (!g) { return reset_mask; } f = &g->fifo; for (engine_id_idx = 0; engine_id_idx < f->num_engines; ++engine_id_idx) { active_engine_id = f->active_engines_list[engine_id_idx]; engine_info = &f->engine_info[active_engine_id]; engine_enum = engine_info->engine_enum; if ((engine_enum == ENGINE_GRCE_GK20A) || (engine_enum == ENGINE_ASYNC_CE_GK20A)) { reset_mask |= engine_info->reset_mask; } } return reset_mask; } u32 gk20a_fifo_get_fast_ce_runlist_id(struct gk20a *g) { u32 ce_runlist_id = gk20a_fifo_get_gr_runlist_id(g); u32 engine_enum = ENGINE_INVAL_GK20A; struct fifo_gk20a *f = NULL; u32 engine_id_idx; struct fifo_engine_info_gk20a *engine_info; u32 active_engine_id = 0; if (!g) { return ce_runlist_id; } f = &g->fifo; for (engine_id_idx = 0; engine_id_idx < f->num_engines; ++engine_id_idx) { active_engine_id = f->active_engines_list[engine_id_idx]; engine_info = &f->engine_info[active_engine_id]; engine_enum = engine_info->engine_enum; /* selecet last available ASYNC_CE if available */ if (engine_enum == ENGINE_ASYNC_CE_GK20A) { ce_runlist_id = engine_info->runlist_id; } } return ce_runlist_id; } u32 gk20a_fifo_get_gr_runlist_id(struct gk20a *g) { u32 gr_engine_cnt = 0; u32 gr_engine_id = FIFO_INVAL_ENGINE_ID; struct fifo_engine_info_gk20a *engine_info; u32 gr_runlist_id = ~0; /* Consider 1st available GR engine */ gr_engine_cnt = gk20a_fifo_get_engine_ids(g, &gr_engine_id, 1, ENGINE_GR_GK20A); if (!gr_engine_cnt) { nvgpu_err(g, "No GR engine available on this device!"); goto end; } engine_info = gk20a_fifo_get_engine_info(g, gr_engine_id); if (engine_info) { gr_runlist_id = engine_info->runlist_id; } else { nvgpu_err(g, "gr_engine_id is not in active list/invalid %d", gr_engine_id); } end: return gr_runlist_id; } bool gk20a_fifo_is_valid_runlist_id(struct gk20a *g, u32 runlist_id) { struct fifo_gk20a *f = NULL; u32 engine_id_idx; u32 active_engine_id; struct fifo_engine_info_gk20a *engine_info; if (!g) { return false; } f = &g->fifo; for (engine_id_idx = 0; engine_id_idx < f->num_engines; ++engine_id_idx) { active_engine_id = f->active_engines_list[engine_id_idx]; engine_info = gk20a_fifo_get_engine_info(g, active_engine_id); if (engine_info && (engine_info->runlist_id == runlist_id)) { return true; } } return false; } /* * Link engine IDs to MMU IDs and vice versa. */ static inline u32 gk20a_engine_id_to_mmu_id(struct gk20a *g, u32 engine_id) { u32 fault_id = FIFO_INVAL_ENGINE_ID; struct fifo_engine_info_gk20a *engine_info; engine_info = gk20a_fifo_get_engine_info(g, engine_id); if (engine_info) { fault_id = engine_info->fault_id; } else { nvgpu_err(g, "engine_id is not in active list/invalid %d", engine_id); } return fault_id; } static inline u32 gk20a_mmu_id_to_engine_id(struct gk20a *g, u32 fault_id) { u32 engine_id; u32 active_engine_id; struct fifo_engine_info_gk20a *engine_info; struct fifo_gk20a *f = &g->fifo; for (engine_id = 0; engine_id < f->num_engines; engine_id++) { active_engine_id = f->active_engines_list[engine_id]; engine_info = &g->fifo.engine_info[active_engine_id]; if (engine_info->fault_id == fault_id) { break; } active_engine_id = FIFO_INVAL_ENGINE_ID; } return active_engine_id; } int gk20a_fifo_engine_enum_from_type(struct gk20a *g, u32 engine_type, u32 *inst_id) { int ret = ENGINE_INVAL_GK20A; nvgpu_log_info(g, "engine type %d", engine_type); if (engine_type == top_device_info_type_enum_graphics_v()) { ret = ENGINE_GR_GK20A; } else if ((engine_type >= top_device_info_type_enum_copy0_v()) && (engine_type <= top_device_info_type_enum_copy2_v())) { /* Lets consider all the CE engine have separate runlist at this point * We can identify the ENGINE_GRCE_GK20A type CE using runlist_id * comparsion logic with GR runlist_id in init_engine_info() */ ret = ENGINE_ASYNC_CE_GK20A; /* inst_id starts from CE0 to CE2 */ if (inst_id) { *inst_id = (engine_type - top_device_info_type_enum_copy0_v()); } } return ret; } int gk20a_fifo_init_engine_info(struct fifo_gk20a *f) { struct gk20a *g = f->g; u32 i; u32 max_info_entries = top_device_info__size_1_v(); u32 engine_enum = ENGINE_INVAL_GK20A; u32 engine_id = FIFO_INVAL_ENGINE_ID; u32 runlist_id = ~0; u32 pbdma_id = ~0; u32 intr_id = ~0; u32 reset_id = ~0; u32 inst_id = 0; u32 pri_base = 0; u32 fault_id = 0; u32 gr_runlist_id = ~0; bool found_pbdma_for_runlist = false; nvgpu_log_fn(g, " "); f->num_engines = 0; for (i = 0; i < max_info_entries; i++) { u32 table_entry = gk20a_readl(f->g, top_device_info_r(i)); u32 entry = top_device_info_entry_v(table_entry); u32 runlist_bit; if (entry == top_device_info_entry_enum_v()) { if (top_device_info_engine_v(table_entry)) { engine_id = top_device_info_engine_enum_v(table_entry); nvgpu_log_info(g, "info: engine_id %d", top_device_info_engine_enum_v(table_entry)); } if (top_device_info_runlist_v(table_entry)) { runlist_id = top_device_info_runlist_enum_v(table_entry); nvgpu_log_info(g, "gr info: runlist_id %d", runlist_id); runlist_bit = BIT(runlist_id); found_pbdma_for_runlist = false; for (pbdma_id = 0; pbdma_id < f->num_pbdma; pbdma_id++) { if (f->pbdma_map[pbdma_id] & runlist_bit) { nvgpu_log_info(g, "gr info: pbdma_map[%d]=%d", pbdma_id, f->pbdma_map[pbdma_id]); found_pbdma_for_runlist = true; break; } } if (!found_pbdma_for_runlist) { nvgpu_err(g, "busted pbdma map"); return -EINVAL; } } if (top_device_info_intr_v(table_entry)) { intr_id = top_device_info_intr_enum_v(table_entry); nvgpu_log_info(g, "gr info: intr_id %d", intr_id); } if (top_device_info_reset_v(table_entry)) { reset_id = top_device_info_reset_enum_v(table_entry); nvgpu_log_info(g, "gr info: reset_id %d", reset_id); } } else if (entry == top_device_info_entry_engine_type_v()) { u32 engine_type = top_device_info_type_enum_v(table_entry); engine_enum = g->ops.fifo.engine_enum_from_type(g, engine_type, &inst_id); } else if (entry == top_device_info_entry_data_v()) { /* gk20a doesn't support device_info_data packet parsing */ if (g->ops.fifo.device_info_data_parse) { g->ops.fifo.device_info_data_parse(g, table_entry, &inst_id, &pri_base, &fault_id); } } if (!top_device_info_chain_v(table_entry)) { if (engine_enum < ENGINE_INVAL_GK20A) { struct fifo_engine_info_gk20a *info = &g->fifo.engine_info[engine_id]; info->intr_mask |= BIT(intr_id); info->reset_mask |= BIT(reset_id); info->runlist_id = runlist_id; info->pbdma_id = pbdma_id; info->inst_id = inst_id; info->pri_base = pri_base; if (engine_enum == ENGINE_GR_GK20A) { gr_runlist_id = runlist_id; } /* GR and GR_COPY shares same runlist_id */ if ((engine_enum == ENGINE_ASYNC_CE_GK20A) && (gr_runlist_id == runlist_id)) { engine_enum = ENGINE_GRCE_GK20A; } info->engine_enum = engine_enum; if (!fault_id && (engine_enum == ENGINE_GRCE_GK20A)) { fault_id = 0x1b; } info->fault_id = fault_id; /* engine_id starts from 0 to NV_HOST_NUM_ENGINES */ f->active_engines_list[f->num_engines] = engine_id; ++f->num_engines; engine_enum = ENGINE_INVAL_GK20A; } } } return 0; } u32 gk20a_fifo_act_eng_interrupt_mask(struct gk20a *g, u32 act_eng_id) { struct fifo_engine_info_gk20a *engine_info = NULL; engine_info = gk20a_fifo_get_engine_info(g, act_eng_id); if (engine_info) { return engine_info->intr_mask; } return 0; } u32 gk20a_fifo_engine_interrupt_mask(struct gk20a *g) { u32 eng_intr_mask = 0; unsigned int i; u32 active_engine_id = 0; u32 engine_enum = ENGINE_INVAL_GK20A; for (i = 0; i < g->fifo.num_engines; i++) { u32 intr_mask; active_engine_id = g->fifo.active_engines_list[i]; intr_mask = g->fifo.engine_info[active_engine_id].intr_mask; engine_enum = g->fifo.engine_info[active_engine_id].engine_enum; if (((engine_enum == ENGINE_GRCE_GK20A) || (engine_enum == ENGINE_ASYNC_CE_GK20A)) && (!g->ops.ce2.isr_stall || !g->ops.ce2.isr_nonstall)) { continue; } eng_intr_mask |= intr_mask; } return eng_intr_mask; } void gk20a_fifo_delete_runlist(struct fifo_gk20a *f) { u32 i; u32 runlist_id; struct fifo_runlist_info_gk20a *runlist; struct gk20a *g = NULL; if (!f || !f->runlist_info) { return; } g = f->g; for (runlist_id = 0; runlist_id < f->max_runlists; runlist_id++) { runlist = &f->runlist_info[runlist_id]; for (i = 0; i < MAX_RUNLIST_BUFFERS; i++) { nvgpu_dma_free(g, &runlist->mem[i]); } nvgpu_kfree(g, runlist->active_channels); runlist->active_channels = NULL; nvgpu_kfree(g, runlist->active_tsgs); runlist->active_tsgs = NULL; nvgpu_mutex_destroy(&runlist->runlist_lock); } memset(f->runlist_info, 0, (sizeof(struct fifo_runlist_info_gk20a) * f->max_runlists)); nvgpu_kfree(g, f->runlist_info); f->runlist_info = NULL; f->max_runlists = 0; } static void gk20a_remove_fifo_support(struct fifo_gk20a *f) { struct gk20a *g = f->g; unsigned int i = 0; nvgpu_log_fn(g, " "); nvgpu_channel_worker_deinit(g); /* * Make sure all channels are closed before deleting them. */ for (; i < f->num_channels; i++) { struct channel_gk20a *c = f->channel + i; struct tsg_gk20a *tsg = f->tsg + i; /* * Could race but worst that happens is we get an error message * from gk20a_free_channel() complaining about multiple closes. */ if (c->referenceable) { __gk20a_channel_kill(c); } nvgpu_mutex_destroy(&tsg->event_id_list_lock); nvgpu_mutex_destroy(&c->ioctl_lock); nvgpu_mutex_destroy(&c->joblist.cleanup_lock); nvgpu_mutex_destroy(&c->joblist.pre_alloc.read_lock); nvgpu_mutex_destroy(&c->sync_lock); #if defined(CONFIG_GK20A_CYCLE_STATS) nvgpu_mutex_destroy(&c->cyclestate.cyclestate_buffer_mutex); nvgpu_mutex_destroy(&c->cs_client_mutex); #endif nvgpu_mutex_destroy(&c->dbg_s_lock); } nvgpu_vfree(g, f->channel); nvgpu_vfree(g, f->tsg); if (g->ops.mm.is_bar1_supported(g)) { nvgpu_dma_unmap_free(g->mm.bar1.vm, &f->userd); } else { nvgpu_dma_free(g, &f->userd); } gk20a_fifo_delete_runlist(f); nvgpu_kfree(g, f->pbdma_map); f->pbdma_map = NULL; nvgpu_kfree(g, f->engine_info); f->engine_info = NULL; nvgpu_kfree(g, f->active_engines_list); f->active_engines_list = NULL; } /* reads info from hardware and fills in pbmda exception info record */ static inline void get_exception_pbdma_info( struct gk20a *g, struct fifo_engine_info_gk20a *eng_info) { struct fifo_pbdma_exception_info_gk20a *e = &eng_info->pbdma_exception_info; u32 pbdma_status_r = e->status_r = gk20a_readl(g, fifo_pbdma_status_r(eng_info->pbdma_id)); e->id = fifo_pbdma_status_id_v(pbdma_status_r); /* vs. id_hw_v()? */ e->id_is_chid = fifo_pbdma_status_id_type_v(pbdma_status_r) == fifo_pbdma_status_id_type_chid_v(); e->chan_status_v = fifo_pbdma_status_chan_status_v(pbdma_status_r); e->next_id_is_chid = fifo_pbdma_status_next_id_type_v(pbdma_status_r) == fifo_pbdma_status_next_id_type_chid_v(); e->next_id = fifo_pbdma_status_next_id_v(pbdma_status_r); e->chsw_in_progress = fifo_pbdma_status_chsw_v(pbdma_status_r) == fifo_pbdma_status_chsw_in_progress_v(); } static void fifo_pbdma_exception_status(struct gk20a *g, struct fifo_engine_info_gk20a *eng_info) { struct fifo_pbdma_exception_info_gk20a *e; get_exception_pbdma_info(g, eng_info); e = &eng_info->pbdma_exception_info; nvgpu_log_fn(g, "pbdma_id %d, " "id_type %s, id %d, chan_status %d, " "next_id_type %s, next_id %d, " "chsw_in_progress %d", eng_info->pbdma_id, e->id_is_chid ? "chid" : "tsgid", e->id, e->chan_status_v, e->next_id_is_chid ? "chid" : "tsgid", e->next_id, e->chsw_in_progress); } /* reads info from hardware and fills in pbmda exception info record */ static inline void get_exception_engine_info( struct gk20a *g, struct fifo_engine_info_gk20a *eng_info) { struct fifo_engine_exception_info_gk20a *e = &eng_info->engine_exception_info; u32 engine_status_r = e->status_r = gk20a_readl(g, fifo_engine_status_r(eng_info->engine_id)); e->id = fifo_engine_status_id_v(engine_status_r); /* vs. id_hw_v()? */ e->id_is_chid = fifo_engine_status_id_type_v(engine_status_r) == fifo_engine_status_id_type_chid_v(); e->ctx_status_v = fifo_engine_status_ctx_status_v(engine_status_r); e->faulted = fifo_engine_status_faulted_v(engine_status_r) == fifo_engine_status_faulted_true_v(); e->idle = fifo_engine_status_engine_v(engine_status_r) == fifo_engine_status_engine_idle_v(); e->ctxsw_in_progress = fifo_engine_status_ctxsw_v(engine_status_r) == fifo_engine_status_ctxsw_in_progress_v(); } static void fifo_engine_exception_status(struct gk20a *g, struct fifo_engine_info_gk20a *eng_info) { struct fifo_engine_exception_info_gk20a *e; get_exception_engine_info(g, eng_info); e = &eng_info->engine_exception_info; nvgpu_log_fn(g, "engine_id %d, id_type %s, id %d, ctx_status %d, " "faulted %d, idle %d, ctxsw_in_progress %d, ", eng_info->engine_id, e->id_is_chid ? "chid" : "tsgid", e->id, e->ctx_status_v, e->faulted, e->idle, e->ctxsw_in_progress); } static int init_runlist(struct gk20a *g, struct fifo_gk20a *f) { struct fifo_runlist_info_gk20a *runlist; struct fifo_engine_info_gk20a *engine_info; unsigned int runlist_id; u32 i; size_t runlist_size; u32 active_engine_id, pbdma_id, engine_id; int flags = nvgpu_is_enabled(g, NVGPU_MM_USE_PHYSICAL_SG) ? NVGPU_DMA_FORCE_CONTIGUOUS : 0; int err = 0; nvgpu_log_fn(g, " "); f->max_runlists = g->ops.fifo.eng_runlist_base_size(); f->runlist_info = nvgpu_kzalloc(g, sizeof(struct fifo_runlist_info_gk20a) * f->max_runlists); if (!f->runlist_info) { goto clean_up_runlist; } memset(f->runlist_info, 0, (sizeof(struct fifo_runlist_info_gk20a) * f->max_runlists)); for (runlist_id = 0; runlist_id < f->max_runlists; runlist_id++) { runlist = &f->runlist_info[runlist_id]; runlist->active_channels = nvgpu_kzalloc(g, DIV_ROUND_UP(f->num_channels, BITS_PER_BYTE)); if (!runlist->active_channels) { goto clean_up_runlist; } runlist->active_tsgs = nvgpu_kzalloc(g, DIV_ROUND_UP(f->num_channels, BITS_PER_BYTE)); if (!runlist->active_tsgs) { goto clean_up_runlist; } runlist_size = f->runlist_entry_size * f->num_runlist_entries; nvgpu_log(g, gpu_dbg_info, "runlist_entries %d runlist size %zu", f->num_runlist_entries, runlist_size); for (i = 0; i < MAX_RUNLIST_BUFFERS; i++) { err = nvgpu_dma_alloc_flags_sys(g, flags, runlist_size, &runlist->mem[i]); if (err) { nvgpu_err(g, "memory allocation failed"); goto clean_up_runlist; } } err = nvgpu_mutex_init(&runlist->runlist_lock); if (err != 0) { nvgpu_err(g, "Error in runlist_lock mutex initialization"); goto clean_up_runlist; } /* None of buffers is pinned if this value doesn't change. Otherwise, one of them (cur_buffer) must have been pinned. */ runlist->cur_buffer = MAX_RUNLIST_BUFFERS; for (pbdma_id = 0; pbdma_id < f->num_pbdma; pbdma_id++) { if (f->pbdma_map[pbdma_id] & BIT(runlist_id)) { runlist->pbdma_bitmask |= BIT(pbdma_id); } } nvgpu_log(g, gpu_dbg_info, "runlist %d : pbdma bitmask 0x%x", runlist_id, runlist->pbdma_bitmask); for (engine_id = 0; engine_id < f->num_engines; ++engine_id) { active_engine_id = f->active_engines_list[engine_id]; engine_info = &f->engine_info[active_engine_id]; if (engine_info && engine_info->runlist_id == runlist_id) { runlist->eng_bitmask |= BIT(active_engine_id); } } nvgpu_log(g, gpu_dbg_info, "runlist %d : act eng bitmask 0x%x", runlist_id, runlist->eng_bitmask); } nvgpu_log_fn(g, "done"); return 0; clean_up_runlist: gk20a_fifo_delete_runlist(f); nvgpu_log_fn(g, "fail"); return err; } u32 gk20a_fifo_intr_0_error_mask(struct gk20a *g) { u32 intr_0_error_mask = fifo_intr_0_bind_error_pending_f() | fifo_intr_0_sched_error_pending_f() | fifo_intr_0_chsw_error_pending_f() | fifo_intr_0_fb_flush_timeout_pending_f() | fifo_intr_0_dropped_mmu_fault_pending_f() | fifo_intr_0_mmu_fault_pending_f() | fifo_intr_0_lb_error_pending_f() | fifo_intr_0_pio_error_pending_f(); return intr_0_error_mask; } static u32 gk20a_fifo_intr_0_en_mask(struct gk20a *g) { u32 intr_0_en_mask; intr_0_en_mask = g->ops.fifo.intr_0_error_mask(g); intr_0_en_mask |= fifo_intr_0_runlist_event_pending_f() | fifo_intr_0_pbdma_intr_pending_f(); return intr_0_en_mask; } int gk20a_init_fifo_reset_enable_hw(struct gk20a *g) { u32 intr_stall; u32 mask; u32 timeout; unsigned int i; u32 host_num_pbdma = nvgpu_get_litter_value(g, GPU_LIT_HOST_NUM_PBDMA); nvgpu_log_fn(g, " "); /* enable pmc pfifo */ g->ops.mc.reset(g, g->ops.mc.reset_mask(g, NVGPU_UNIT_FIFO)); nvgpu_cg_slcg_fifo_load_enable(g); nvgpu_cg_blcg_fifo_load_enable(g); timeout = gk20a_readl(g, fifo_fb_timeout_r()); timeout = set_field(timeout, fifo_fb_timeout_period_m(), fifo_fb_timeout_period_max_f()); nvgpu_log_info(g, "fifo_fb_timeout reg val = 0x%08x", timeout); gk20a_writel(g, fifo_fb_timeout_r(), timeout); /* write pbdma timeout value */ for (i = 0; i < host_num_pbdma; i++) { timeout = gk20a_readl(g, pbdma_timeout_r(i)); timeout = set_field(timeout, pbdma_timeout_period_m(), pbdma_timeout_period_max_f()); nvgpu_log_info(g, "pbdma_timeout reg val = 0x%08x", timeout); gk20a_writel(g, pbdma_timeout_r(i), timeout); } if (g->ops.fifo.apply_pb_timeout) { g->ops.fifo.apply_pb_timeout(g); } if (g->ops.fifo.apply_ctxsw_timeout_intr) { g->ops.fifo.apply_ctxsw_timeout_intr(g); } else { timeout = g->fifo_eng_timeout_us; timeout = scale_ptimer(timeout, ptimer_scalingfactor10x(g->ptimer_src_freq)); timeout |= fifo_eng_timeout_detection_enabled_f(); gk20a_writel(g, fifo_eng_timeout_r(), timeout); } /* clear and enable pbdma interrupt */ for (i = 0; i < host_num_pbdma; i++) { gk20a_writel(g, pbdma_intr_0_r(i), 0xFFFFFFFF); gk20a_writel(g, pbdma_intr_1_r(i), 0xFFFFFFFF); intr_stall = gk20a_readl(g, pbdma_intr_stall_r(i)); intr_stall &= ~pbdma_intr_stall_lbreq_enabled_f(); gk20a_writel(g, pbdma_intr_stall_r(i), intr_stall); nvgpu_log_info(g, "pbdma id:%u, intr_en_0 0x%08x", i, intr_stall); gk20a_writel(g, pbdma_intr_en_0_r(i), intr_stall); intr_stall = gk20a_readl(g, pbdma_intr_stall_1_r(i)); /* * For bug 2082123 * Mask the unused HCE_RE_ILLEGAL_OP bit from the interrupt. */ intr_stall &= ~pbdma_intr_stall_1_hce_illegal_op_enabled_f(); nvgpu_log_info(g, "pbdma id:%u, intr_en_1 0x%08x", i, intr_stall); gk20a_writel(g, pbdma_intr_en_1_r(i), intr_stall); } /* reset runlist interrupts */ gk20a_writel(g, fifo_intr_runlist_r(), ~0); /* clear and enable pfifo interrupt */ gk20a_writel(g, fifo_intr_0_r(), 0xFFFFFFFF); mask = gk20a_fifo_intr_0_en_mask(g); nvgpu_log_info(g, "fifo_intr_en_0 0x%08x", mask); gk20a_writel(g, fifo_intr_en_0_r(), mask); nvgpu_log_info(g, "fifo_intr_en_1 = 0x80000000"); gk20a_writel(g, fifo_intr_en_1_r(), 0x80000000); nvgpu_log_fn(g, "done"); return 0; } int gk20a_init_fifo_setup_sw_common(struct gk20a *g) { struct fifo_gk20a *f = &g->fifo; unsigned int chid, i; int err = 0; nvgpu_log_fn(g, " "); f->g = g; err = nvgpu_mutex_init(&f->intr.isr.mutex); if (err) { nvgpu_err(g, "failed to init isr.mutex"); return err; } err = nvgpu_mutex_init(&f->engines_reset_mutex); if (err) { nvgpu_err(g, "failed to init engines_reset_mutex"); return err; } g->ops.fifo.init_pbdma_intr_descs(f); /* just filling in data/tables */ f->num_channels = g->ops.fifo.get_num_fifos(g); f->runlist_entry_size = g->ops.fifo.runlist_entry_size(); f->num_runlist_entries = fifo_eng_runlist_length_max_v(); f->num_pbdma = nvgpu_get_litter_value(g, GPU_LIT_HOST_NUM_PBDMA); f->max_engines = nvgpu_get_litter_value(g, GPU_LIT_HOST_NUM_ENGINES); f->userd_entry_size = 1 << ram_userd_base_shift_v(); f->channel = nvgpu_vzalloc(g, f->num_channels * sizeof(*f->channel)); f->tsg = nvgpu_vzalloc(g, f->num_channels * sizeof(*f->tsg)); f->pbdma_map = nvgpu_kzalloc(g, f->num_pbdma * sizeof(*f->pbdma_map)); f->engine_info = nvgpu_kzalloc(g, f->max_engines * sizeof(*f->engine_info)); f->active_engines_list = nvgpu_kzalloc(g, f->max_engines * sizeof(u32)); if (!(f->channel && f->tsg && f->pbdma_map && f->engine_info && f->active_engines_list)) { err = -ENOMEM; goto clean_up; } memset(f->active_engines_list, 0xff, (f->max_engines * sizeof(u32))); /* pbdma map needs to be in place before calling engine info init */ for (i = 0; i < f->num_pbdma; ++i) { f->pbdma_map[i] = gk20a_readl(g, fifo_pbdma_map_r(i)); } g->ops.fifo.init_engine_info(f); err = init_runlist(g, f); if (err) { nvgpu_err(g, "failed to init runlist"); goto clean_up; } nvgpu_init_list_node(&f->free_chs); err = nvgpu_mutex_init(&f->free_chs_mutex); if (err) { nvgpu_err(g, "failed to init free_chs_mutex"); goto clean_up; } for (chid = 0; chid < f->num_channels; chid++) { gk20a_init_channel_support(g, chid); gk20a_init_tsg_support(g, chid); } err = nvgpu_mutex_init(&f->tsg_inuse_mutex); if (err) { nvgpu_err(g, "failed to init tsg_inuse_mutex"); goto clean_up; } f->remove_support = gk20a_remove_fifo_support; f->deferred_reset_pending = false; err = nvgpu_mutex_init(&f->deferred_reset_mutex); if (err) { nvgpu_err(g, "failed to init deferred_reset_mutex"); goto clean_up; } nvgpu_log_fn(g, "done"); return 0; clean_up: nvgpu_err(g, "fail"); nvgpu_vfree(g, f->channel); f->channel = NULL; nvgpu_vfree(g, f->tsg); f->tsg = NULL; nvgpu_kfree(g, f->pbdma_map); f->pbdma_map = NULL; nvgpu_kfree(g, f->engine_info); f->engine_info = NULL; nvgpu_kfree(g, f->active_engines_list); f->active_engines_list = NULL; return err; } int gk20a_init_fifo_setup_sw(struct gk20a *g) { struct fifo_gk20a *f = &g->fifo; unsigned int chid; u64 userd_base; int err = 0; nvgpu_log_fn(g, " "); if (f->sw_ready) { nvgpu_log_fn(g, "skip init"); return 0; } err = gk20a_init_fifo_setup_sw_common(g); if (err) { nvgpu_err(g, "fail: err: %d", err); return err; } if (g->ops.mm.is_bar1_supported(g)) { err = nvgpu_dma_alloc_map_sys(g->mm.bar1.vm, f->userd_entry_size * f->num_channels, &f->userd); } else { err = nvgpu_dma_alloc_sys(g, f->userd_entry_size * f->num_channels, &f->userd); } if (err) { nvgpu_err(g, "userd memory allocation failed"); goto clean_up; } nvgpu_log(g, gpu_dbg_map, "userd gpu va = 0x%llx", f->userd.gpu_va); userd_base = nvgpu_mem_get_addr(g, &f->userd); for (chid = 0; chid < f->num_channels; chid++) { f->channel[chid].userd_iova = userd_base + chid * f->userd_entry_size; f->channel[chid].userd_gpu_va = f->userd.gpu_va + chid * f->userd_entry_size; } err = nvgpu_channel_worker_init(g); if (err) { goto clean_up; } f->sw_ready = true; nvgpu_log_fn(g, "done"); return 0; clean_up: nvgpu_log_fn(g, "fail"); if (nvgpu_mem_is_valid(&f->userd)) { if (g->ops.mm.is_bar1_supported(g)) { nvgpu_dma_unmap_free(g->mm.bar1.vm, &f->userd); } else { nvgpu_dma_free(g, &f->userd); } } return err; } void gk20a_fifo_handle_runlist_event(struct gk20a *g) { u32 runlist_event = gk20a_readl(g, fifo_intr_runlist_r()); nvgpu_log(g, gpu_dbg_intr, "runlist event %08x", runlist_event); gk20a_writel(g, fifo_intr_runlist_r(), runlist_event); } int gk20a_init_fifo_setup_hw(struct gk20a *g) { struct fifo_gk20a *f = &g->fifo; nvgpu_log_fn(g, " "); /* test write, read through bar1 @ userd region before * turning on the snooping */ { struct fifo_gk20a *f = &g->fifo; u32 v, v1 = 0x33, v2 = 0x55; u32 bar1_vaddr = f->userd.gpu_va; volatile u32 *cpu_vaddr = f->userd.cpu_va; nvgpu_log_info(g, "test bar1 @ vaddr 0x%x", bar1_vaddr); v = gk20a_bar1_readl(g, bar1_vaddr); *cpu_vaddr = v1; nvgpu_mb(); if (v1 != gk20a_bar1_readl(g, bar1_vaddr)) { nvgpu_err(g, "bar1 broken @ gk20a: CPU wrote 0x%x, \ GPU read 0x%x", *cpu_vaddr, gk20a_bar1_readl(g, bar1_vaddr)); return -EINVAL; } gk20a_bar1_writel(g, bar1_vaddr, v2); if (v2 != gk20a_bar1_readl(g, bar1_vaddr)) { nvgpu_err(g, "bar1 broken @ gk20a: GPU wrote 0x%x, \ CPU read 0x%x", gk20a_bar1_readl(g, bar1_vaddr), *cpu_vaddr); return -EINVAL; } /* is it visible to the cpu? */ if (*cpu_vaddr != v2) { nvgpu_err(g, "cpu didn't see bar1 write @ %p!", cpu_vaddr); } /* put it back */ gk20a_bar1_writel(g, bar1_vaddr, v); } /*XXX all manner of flushes and caching worries, etc */ /* set the base for the userd region now */ gk20a_writel(g, fifo_bar1_base_r(), fifo_bar1_base_ptr_f(f->userd.gpu_va >> 12) | fifo_bar1_base_valid_true_f()); nvgpu_log_fn(g, "done"); return 0; } int gk20a_init_fifo_support(struct gk20a *g) { u32 err; err = g->ops.fifo.setup_sw(g); if (err) { return err; } if (g->ops.fifo.init_fifo_setup_hw) { err = g->ops.fifo.init_fifo_setup_hw(g); } if (err) { return err; } return err; } /* return with a reference to the channel, caller must put it back */ struct channel_gk20a * gk20a_refch_from_inst_ptr(struct gk20a *g, u64 inst_ptr) { struct fifo_gk20a *f = &g->fifo; unsigned int ci; if (unlikely(!f->channel)) { return NULL; } for (ci = 0; ci < f->num_channels; ci++) { struct channel_gk20a *ch; u64 ch_inst_ptr; ch = gk20a_channel_from_id(g, ci); /* only alive channels are searched */ if (!ch) { continue; } ch_inst_ptr = nvgpu_inst_block_addr(g, &ch->inst_block); if (inst_ptr == ch_inst_ptr) { return ch; } gk20a_channel_put(ch); } return NULL; } /* fault info/descriptions. * tbd: move to setup * */ static const char * const gk20a_fault_type_descs[] = { "pde", /*fifo_intr_mmu_fault_info_type_pde_v() == 0 */ "pde size", "pte", "va limit viol", "unbound inst", "priv viol", "ro viol", "wo viol", "pitch mask", "work creation", "bad aperture", "compression failure", "bad kind", "region viol", "dual ptes", "poisoned", }; /* engine descriptions */ static const char * const engine_subid_descs[] = { "gpc", "hub", }; static const char * const gk20a_hub_client_descs[] = { "vip", "ce0", "ce1", "dniso", "fe", "fecs", "host", "host cpu", "host cpu nb", "iso", "mmu", "mspdec", "msppp", "msvld", "niso", "p2p", "pd", "perf", "pmu", "raster twod", "scc", "scc nb", "sec", "ssync", "gr copy", "xv", "mmu nb", "msenc", "d falcon", "sked", "a falcon", "n/a", }; static const char * const gk20a_gpc_client_descs[] = { "l1 0", "t1 0", "pe 0", "l1 1", "t1 1", "pe 1", "l1 2", "t1 2", "pe 2", "l1 3", "t1 3", "pe 3", "rast", "gcc", "gpccs", "prop 0", "prop 1", "prop 2", "prop 3", "l1 4", "t1 4", "pe 4", "l1 5", "t1 5", "pe 5", "l1 6", "t1 6", "pe 6", "l1 7", "t1 7", "pe 7", }; static const char * const does_not_exist[] = { "does not exist" }; /* fill in mmu fault desc */ void gk20a_fifo_get_mmu_fault_desc(struct mmu_fault_info *mmfault) { if (mmfault->fault_type >= ARRAY_SIZE(gk20a_fault_type_descs)) { WARN_ON(mmfault->fault_type >= ARRAY_SIZE(gk20a_fault_type_descs)); } else { mmfault->fault_type_desc = gk20a_fault_type_descs[mmfault->fault_type]; } } /* fill in mmu fault client description */ void gk20a_fifo_get_mmu_fault_client_desc(struct mmu_fault_info *mmfault) { if (mmfault->client_id >= ARRAY_SIZE(gk20a_hub_client_descs)) { WARN_ON(mmfault->client_id >= ARRAY_SIZE(gk20a_hub_client_descs)); } else { mmfault->client_id_desc = gk20a_hub_client_descs[mmfault->client_id]; } } /* fill in mmu fault gpc description */ void gk20a_fifo_get_mmu_fault_gpc_desc(struct mmu_fault_info *mmfault) { if (mmfault->client_id >= ARRAY_SIZE(gk20a_gpc_client_descs)) { WARN_ON(mmfault->client_id >= ARRAY_SIZE(gk20a_gpc_client_descs)); } else { mmfault->client_id_desc = gk20a_gpc_client_descs[mmfault->client_id]; } } static void get_exception_mmu_fault_info(struct gk20a *g, u32 mmu_fault_id, struct mmu_fault_info *mmfault) { g->ops.fifo.get_mmu_fault_info(g, mmu_fault_id, mmfault); /* parse info */ mmfault->fault_type_desc = does_not_exist[0]; if (g->ops.fifo.get_mmu_fault_desc) { g->ops.fifo.get_mmu_fault_desc(mmfault); } if (mmfault->client_type >= ARRAY_SIZE(engine_subid_descs)) { WARN_ON(mmfault->client_type >= ARRAY_SIZE(engine_subid_descs)); mmfault->client_type_desc = does_not_exist[0]; } else { mmfault->client_type_desc = engine_subid_descs[mmfault->client_type]; } mmfault->client_id_desc = does_not_exist[0]; if ((mmfault->client_type == fifo_intr_mmu_fault_info_engine_subid_hub_v()) && g->ops.fifo.get_mmu_fault_client_desc) { g->ops.fifo.get_mmu_fault_client_desc(mmfault); } else if ((mmfault->client_type == fifo_intr_mmu_fault_info_engine_subid_gpc_v()) && g->ops.fifo.get_mmu_fault_gpc_desc) { g->ops.fifo.get_mmu_fault_gpc_desc(mmfault); } } /* reads info from hardware and fills in mmu fault info record */ void gk20a_fifo_get_mmu_fault_info(struct gk20a *g, u32 mmu_fault_id, struct mmu_fault_info *mmfault) { u32 fault_info; u32 addr_lo, addr_hi; nvgpu_log_fn(g, "mmu_fault_id %d", mmu_fault_id); memset(mmfault, 0, sizeof(*mmfault)); fault_info = gk20a_readl(g, fifo_intr_mmu_fault_info_r(mmu_fault_id)); mmfault->fault_type = fifo_intr_mmu_fault_info_type_v(fault_info); mmfault->access_type = fifo_intr_mmu_fault_info_write_v(fault_info); mmfault->client_type = fifo_intr_mmu_fault_info_engine_subid_v(fault_info); mmfault->client_id = fifo_intr_mmu_fault_info_client_v(fault_info); addr_lo = gk20a_readl(g, fifo_intr_mmu_fault_lo_r(mmu_fault_id)); addr_hi = gk20a_readl(g, fifo_intr_mmu_fault_hi_r(mmu_fault_id)); mmfault->fault_addr = hi32_lo32_to_u64(addr_hi, addr_lo); /* note:ignoring aperture on gk20a... */ mmfault->inst_ptr = fifo_intr_mmu_fault_inst_ptr_v( gk20a_readl(g, fifo_intr_mmu_fault_inst_r(mmu_fault_id))); /* note: inst_ptr is a 40b phys addr. */ mmfault->inst_ptr <<= fifo_intr_mmu_fault_inst_ptr_align_shift_v(); } void gk20a_fifo_reset_engine(struct gk20a *g, u32 engine_id) { u32 engine_enum = ENGINE_INVAL_GK20A; struct fifo_engine_info_gk20a *engine_info; nvgpu_log_fn(g, " "); if (!g) { return; } engine_info = gk20a_fifo_get_engine_info(g, engine_id); if (engine_info) { engine_enum = engine_info->engine_enum; } if (engine_enum == ENGINE_INVAL_GK20A) { nvgpu_err(g, "unsupported engine_id %d", engine_id); } if (engine_enum == ENGINE_GR_GK20A) { if (g->support_pmu) { if (nvgpu_pg_elpg_disable(g) != 0 ) { nvgpu_err(g, "failed to set disable elpg"); } } #ifdef CONFIG_GK20A_CTXSW_TRACE /* * Resetting engine will alter read/write index. Need to flush * circular buffer before re-enabling FECS. */ if (g->ops.fecs_trace.reset) g->ops.fecs_trace.reset(g); #endif if (!nvgpu_platform_is_simulation(g)) { /*HALT_PIPELINE method, halt GR engine*/ if (gr_gk20a_halt_pipe(g)) { nvgpu_err(g, "failed to HALT gr pipe"); } /* * resetting engine using mc_enable_r() is not * enough, we do full init sequence */ nvgpu_log(g, gpu_dbg_info, "resetting gr engine"); gk20a_gr_reset(g); } else { nvgpu_log(g, gpu_dbg_info, "HALT gr pipe not supported and " "gr cannot be reset without halting gr pipe"); } if (g->support_pmu) { if (nvgpu_pg_elpg_enable(g) != 0 ) { nvgpu_err(g, "failed to set enable elpg"); } } } if ((engine_enum == ENGINE_GRCE_GK20A) || (engine_enum == ENGINE_ASYNC_CE_GK20A)) { g->ops.mc.reset(g, engine_info->reset_mask); } } static void gk20a_fifo_handle_chsw_fault(struct gk20a *g) { u32 intr; intr = gk20a_readl(g, fifo_intr_chsw_error_r()); nvgpu_err(g, "chsw: %08x", intr); gk20a_fecs_dump_falcon_stats(g); gk20a_gpccs_dump_falcon_stats(g); gk20a_writel(g, fifo_intr_chsw_error_r(), intr); } static void gk20a_fifo_handle_dropped_mmu_fault(struct gk20a *g) { u32 fault_id = gk20a_readl(g, fifo_intr_mmu_fault_id_r()); nvgpu_err(g, "dropped mmu fault (0x%08x)", fault_id); } bool gk20a_is_fault_engine_subid_gpc(struct gk20a *g, u32 engine_subid) { return (engine_subid == fifo_intr_mmu_fault_info_engine_subid_gpc_v()); } bool gk20a_fifo_should_defer_engine_reset(struct gk20a *g, u32 engine_id, u32 engine_subid, bool fake_fault) { u32 engine_enum = ENGINE_INVAL_GK20A; struct fifo_engine_info_gk20a *engine_info; if (!g) { return false; } engine_info = gk20a_fifo_get_engine_info(g, engine_id); if (engine_info) { engine_enum = engine_info->engine_enum; } if (engine_enum == ENGINE_INVAL_GK20A) { return false; } /* channel recovery is only deferred if an sm debugger is attached and has MMU debug mode is enabled */ if (!g->ops.gr.sm_debugger_attached(g) || !g->ops.fb.is_debug_mode_enabled(g)) { return false; } /* if this fault is fake (due to RC recovery), don't defer recovery */ if (fake_fault) { return false; } if (engine_enum != ENGINE_GR_GK20A) { return false; } return g->ops.fifo.is_fault_engine_subid_gpc(g, engine_subid); } /* caller must hold a channel reference */ static bool gk20a_fifo_ch_timeout_debug_dump_state(struct gk20a *g, struct channel_gk20a *refch) { bool verbose = false; if (!refch) { return verbose; } if (nvgpu_is_error_notifier_set(refch, NVGPU_ERR_NOTIFIER_FIFO_ERROR_IDLE_TIMEOUT)) { verbose = refch->timeout_debug_dump; } return verbose; } /* caller must hold a channel reference */ static void gk20a_fifo_set_has_timedout_and_wake_up_wqs(struct gk20a *g, struct channel_gk20a *refch) { if (refch) { /* mark channel as faulted */ gk20a_channel_set_timedout(refch); /* unblock pending waits */ nvgpu_cond_broadcast_interruptible(&refch->semaphore_wq); nvgpu_cond_broadcast_interruptible(&refch->notifier_wq); } } /* caller must hold a channel reference */ bool gk20a_fifo_error_ch(struct gk20a *g, struct channel_gk20a *refch) { bool verbose; verbose = gk20a_fifo_ch_timeout_debug_dump_state(g, refch); gk20a_fifo_set_has_timedout_and_wake_up_wqs(g, refch); return verbose; } bool gk20a_fifo_error_tsg(struct gk20a *g, struct tsg_gk20a *tsg) { struct channel_gk20a *ch = NULL; bool verbose = false; nvgpu_rwsem_down_read(&tsg->ch_list_lock); nvgpu_list_for_each_entry(ch, &tsg->ch_list, channel_gk20a, ch_entry) { if (gk20a_channel_get(ch)) { if (gk20a_fifo_error_ch(g, ch)) { verbose = true; } gk20a_channel_put(ch); } } nvgpu_rwsem_up_read(&tsg->ch_list_lock); return verbose; } /* caller must hold a channel reference */ void gk20a_fifo_set_ctx_mmu_error_ch(struct gk20a *g, struct channel_gk20a *refch) { nvgpu_err(g, "channel %d generated a mmu fault", refch->chid); g->ops.fifo.set_error_notifier(refch, NVGPU_ERR_NOTIFIER_FIFO_ERROR_MMU_ERR_FLT); } void gk20a_fifo_set_ctx_mmu_error_tsg(struct gk20a *g, struct tsg_gk20a *tsg) { struct channel_gk20a *ch = NULL; nvgpu_err(g, "TSG %d generated a mmu fault", tsg->tsgid); nvgpu_rwsem_down_read(&tsg->ch_list_lock); nvgpu_list_for_each_entry(ch, &tsg->ch_list, channel_gk20a, ch_entry) { if (gk20a_channel_get(ch)) { gk20a_fifo_set_ctx_mmu_error_ch(g, ch); gk20a_channel_put(ch); } } nvgpu_rwsem_up_read(&tsg->ch_list_lock); } void gk20a_fifo_abort_tsg(struct gk20a *g, struct tsg_gk20a *tsg, bool preempt) { struct channel_gk20a *ch = NULL; nvgpu_log_fn(g, " "); g->ops.fifo.disable_tsg(tsg); if (preempt) { g->ops.fifo.preempt_tsg(g, tsg); } nvgpu_rwsem_down_read(&tsg->ch_list_lock); nvgpu_list_for_each_entry(ch, &tsg->ch_list, channel_gk20a, ch_entry) { if (gk20a_channel_get(ch)) { gk20a_channel_set_timedout(ch); if (ch->g->ops.fifo.ch_abort_clean_up) { ch->g->ops.fifo.ch_abort_clean_up(ch); } gk20a_channel_put(ch); } } nvgpu_rwsem_up_read(&tsg->ch_list_lock); } int gk20a_fifo_deferred_reset(struct gk20a *g, struct channel_gk20a *ch) { unsigned long engine_id, engines = 0U; struct tsg_gk20a *tsg; bool deferred_reset_pending; struct fifo_gk20a *f = &g->fifo; nvgpu_mutex_acquire(&g->dbg_sessions_lock); nvgpu_mutex_acquire(&f->deferred_reset_mutex); deferred_reset_pending = g->fifo.deferred_reset_pending; nvgpu_mutex_release(&f->deferred_reset_mutex); if (!deferred_reset_pending) { nvgpu_mutex_release(&g->dbg_sessions_lock); return 0; } gr_gk20a_disable_ctxsw(g); tsg = tsg_gk20a_from_ch(ch); if (tsg != NULL) { engines = gk20a_fifo_engines_on_id(g, tsg->tsgid, true); } else { nvgpu_err(g, "chid: %d is not bound to tsg", ch->chid); engines = g->fifo.deferred_fault_engines; } if (engines == 0U) { goto clean_up; } /* * If deferred reset is set for an engine, and channel is running * on that engine, reset it */ for_each_set_bit(engine_id, &g->fifo.deferred_fault_engines, 32) { if (BIT(engine_id) & engines) { gk20a_fifo_reset_engine(g, engine_id); } } nvgpu_mutex_acquire(&f->deferred_reset_mutex); g->fifo.deferred_fault_engines = 0; g->fifo.deferred_reset_pending = false; nvgpu_mutex_release(&f->deferred_reset_mutex); clean_up: gr_gk20a_enable_ctxsw(g); nvgpu_mutex_release(&g->dbg_sessions_lock); return 0; } static bool gk20a_fifo_handle_mmu_fault_locked( struct gk20a *g, u32 mmu_fault_engines, /* queried from HW if 0 */ u32 hw_id, /* queried from HW if ~(u32)0 OR mmu_fault_engines == 0*/ bool id_is_tsg) { bool fake_fault; unsigned long fault_id; unsigned long engine_mmu_fault_id; bool verbose = true; u32 grfifo_ctl; bool deferred_reset_pending = false; struct fifo_gk20a *f = &g->fifo; nvgpu_log_fn(g, " "); /* Disable power management */ if (g->support_pmu) { if (nvgpu_cg_pg_disable(g) != 0) { nvgpu_warn(g, "fail to disable power mgmt"); } } /* Disable fifo access */ grfifo_ctl = gk20a_readl(g, gr_gpfifo_ctl_r()); grfifo_ctl &= ~gr_gpfifo_ctl_semaphore_access_f(1); grfifo_ctl &= ~gr_gpfifo_ctl_access_f(1); gk20a_writel(g, gr_gpfifo_ctl_r(), grfifo_ctl | gr_gpfifo_ctl_access_f(0) | gr_gpfifo_ctl_semaphore_access_f(0)); if (mmu_fault_engines) { fault_id = mmu_fault_engines; fake_fault = true; } else { fault_id = gk20a_readl(g, fifo_intr_mmu_fault_id_r()); fake_fault = false; gk20a_debug_dump(g); } nvgpu_mutex_acquire(&f->deferred_reset_mutex); g->fifo.deferred_reset_pending = false; nvgpu_mutex_release(&f->deferred_reset_mutex); /* go through all faulted engines */ for_each_set_bit(engine_mmu_fault_id, &fault_id, 32) { /* bits in fifo_intr_mmu_fault_id_r do not correspond 1:1 to * engines. Convert engine_mmu_id to engine_id */ u32 engine_id = gk20a_mmu_id_to_engine_id(g, engine_mmu_fault_id); struct mmu_fault_info mmfault_info; struct channel_gk20a *ch = NULL; struct tsg_gk20a *tsg = NULL; struct channel_gk20a *refch = NULL; /* read and parse engine status */ u32 status = gk20a_readl(g, fifo_engine_status_r(engine_id)); u32 ctx_status = fifo_engine_status_ctx_status_v(status); bool ctxsw = (ctx_status == fifo_engine_status_ctx_status_ctxsw_switch_v() || ctx_status == fifo_engine_status_ctx_status_ctxsw_save_v() || ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()); get_exception_mmu_fault_info(g, engine_mmu_fault_id, &mmfault_info); trace_gk20a_mmu_fault(mmfault_info.fault_addr, mmfault_info.fault_type, mmfault_info.access_type, mmfault_info.inst_ptr, engine_id, mmfault_info.client_type_desc, mmfault_info.client_id_desc, mmfault_info.fault_type_desc); nvgpu_err(g, "%s mmu fault on engine %d, " "engine subid %d (%s), client %d (%s), " "addr 0x%llx, type %d (%s), access_type 0x%08x," "inst_ptr 0x%llx", fake_fault ? "fake" : "", engine_id, mmfault_info.client_type, mmfault_info.client_type_desc, mmfault_info.client_id, mmfault_info.client_id_desc, mmfault_info.fault_addr, mmfault_info.fault_type, mmfault_info.fault_type_desc, mmfault_info.access_type, mmfault_info.inst_ptr); if (ctxsw) { gk20a_fecs_dump_falcon_stats(g); gk20a_gpccs_dump_falcon_stats(g); nvgpu_err(g, "gr_status_r : 0x%x", gk20a_readl(g, gr_status_r())); } /* get the channel/TSG */ if (fake_fault) { /* use next_id if context load is failing */ u32 id, type; if (hw_id == ~(u32)0) { id = (ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()) ? fifo_engine_status_next_id_v(status) : fifo_engine_status_id_v(status); type = (ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()) ? fifo_engine_status_next_id_type_v(status) : fifo_engine_status_id_type_v(status); } else { id = hw_id; type = id_is_tsg ? fifo_engine_status_id_type_tsgid_v() : fifo_engine_status_id_type_chid_v(); } if (type == fifo_engine_status_id_type_tsgid_v()) { tsg = &g->fifo.tsg[id]; } else if (type == fifo_engine_status_id_type_chid_v()) { ch = &g->fifo.channel[id]; refch = gk20a_channel_get(ch); if (refch != NULL) { tsg = tsg_gk20a_from_ch(refch); } } } else { /* read channel based on instruction pointer */ ch = gk20a_refch_from_inst_ptr(g, mmfault_info.inst_ptr); refch = ch; if (refch != NULL) { tsg = tsg_gk20a_from_ch(refch); } } /* check if engine reset should be deferred */ if (engine_id != FIFO_INVAL_ENGINE_ID) { bool defer = gk20a_fifo_should_defer_engine_reset(g, engine_id, mmfault_info.client_type, fake_fault); if ((ch || tsg) && defer) { g->fifo.deferred_fault_engines |= BIT(engine_id); /* handled during channel free */ nvgpu_mutex_acquire(&f->deferred_reset_mutex); g->fifo.deferred_reset_pending = true; nvgpu_mutex_release(&f->deferred_reset_mutex); deferred_reset_pending = true; nvgpu_log(g, gpu_dbg_intr | gpu_dbg_gpu_dbg, "sm debugger attached," " deferring channel recovery to channel free"); } else { gk20a_fifo_reset_engine(g, engine_id); } } #ifdef CONFIG_GK20A_CTXSW_TRACE if (tsg) { gk20a_ctxsw_trace_tsg_reset(g, tsg); } #endif /* * Disable the channel/TSG from hw and increment syncpoints. */ if (tsg) { if (deferred_reset_pending) { gk20a_disable_tsg(tsg); } else { if (!fake_fault) { gk20a_fifo_set_ctx_mmu_error_tsg(g, tsg); } verbose = gk20a_fifo_error_tsg(g, tsg); gk20a_fifo_abort_tsg(g, tsg, false); } /* put back the ref taken early above */ if (refch) { gk20a_channel_put(ch); } } else if (refch != NULL) { nvgpu_err(g, "mmu error in unbound channel %d", ch->chid); gk20a_channel_put(ch); } else if (mmfault_info.inst_ptr == nvgpu_inst_block_addr(g, &g->mm.bar1.inst_block)) { nvgpu_err(g, "mmu fault from bar1"); } else if (mmfault_info.inst_ptr == nvgpu_inst_block_addr(g, &g->mm.pmu.inst_block)) { nvgpu_err(g, "mmu fault from pmu"); } else { nvgpu_err(g, "couldn't locate channel for mmu fault"); } } /* clear interrupt */ gk20a_writel(g, fifo_intr_mmu_fault_id_r(), fault_id); /* resume scheduler */ gk20a_writel(g, fifo_error_sched_disable_r(), gk20a_readl(g, fifo_error_sched_disable_r())); /* Re-enable fifo access */ gk20a_writel(g, gr_gpfifo_ctl_r(), gr_gpfifo_ctl_access_enabled_f() | gr_gpfifo_ctl_semaphore_access_enabled_f()); /* It is safe to enable ELPG again. */ if (g->support_pmu) { if (nvgpu_cg_pg_enable(g) != 0) { nvgpu_warn(g, "fail to enable power mgmt"); } } return verbose; } static bool gk20a_fifo_handle_mmu_fault( struct gk20a *g, u32 mmu_fault_engines, /* queried from HW if 0 */ u32 hw_id, /* queried from HW if ~(u32)0 OR mmu_fault_engines == 0*/ bool id_is_tsg) { u32 rlid; bool verbose; nvgpu_log_fn(g, " "); nvgpu_log_info(g, "acquire engines_reset_mutex"); nvgpu_mutex_acquire(&g->fifo.engines_reset_mutex); nvgpu_log_info(g, "acquire runlist_lock for all runlists"); for (rlid = 0; rlid < g->fifo.max_runlists; rlid++) { nvgpu_mutex_acquire(&g->fifo.runlist_info[rlid].runlist_lock); } verbose = gk20a_fifo_handle_mmu_fault_locked(g, mmu_fault_engines, hw_id, id_is_tsg); nvgpu_log_info(g, "release runlist_lock for all runlists"); for (rlid = 0; rlid < g->fifo.max_runlists; rlid++) { nvgpu_mutex_release(&g->fifo.runlist_info[rlid].runlist_lock); } nvgpu_log_info(g, "release engines_reset_mutex"); nvgpu_mutex_release(&g->fifo.engines_reset_mutex); return verbose; } static void gk20a_fifo_get_faulty_id_type(struct gk20a *g, int engine_id, u32 *id, u32 *type) { u32 status = gk20a_readl(g, fifo_engine_status_r(engine_id)); u32 ctx_status = fifo_engine_status_ctx_status_v(status); /* use next_id if context load is failing */ *id = (ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()) ? fifo_engine_status_next_id_v(status) : fifo_engine_status_id_v(status); *type = (ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()) ? fifo_engine_status_next_id_type_v(status) : fifo_engine_status_id_type_v(status); } static u32 gk20a_fifo_engines_on_id(struct gk20a *g, u32 id, bool is_tsg) { unsigned int i; u32 engines = 0; for (i = 0; i < g->fifo.num_engines; i++) { u32 active_engine_id = g->fifo.active_engines_list[i]; u32 status = gk20a_readl(g, fifo_engine_status_r(active_engine_id)); u32 ctx_status = fifo_engine_status_ctx_status_v(status); u32 ctx_id = (ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()) ? fifo_engine_status_next_id_v(status) : fifo_engine_status_id_v(status); u32 type = (ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()) ? fifo_engine_status_next_id_type_v(status) : fifo_engine_status_id_type_v(status); bool busy = fifo_engine_status_engine_v(status) == fifo_engine_status_engine_busy_v(); if (busy && ctx_id == id) { if ((is_tsg && type == fifo_engine_status_id_type_tsgid_v()) || (!is_tsg && type == fifo_engine_status_id_type_chid_v())) { engines |= BIT(active_engine_id); } } } return engines; } void gk20a_fifo_recover_ch(struct gk20a *g, struct channel_gk20a *ch, bool verbose, u32 rc_type) { u32 engines; /* stop context switching to prevent engine assignments from changing until channel is recovered */ nvgpu_mutex_acquire(&g->dbg_sessions_lock); gr_gk20a_disable_ctxsw(g); engines = gk20a_fifo_engines_on_id(g, ch->chid, false); if (engines) { gk20a_fifo_recover(g, engines, ch->chid, false, true, verbose, rc_type); } else { gk20a_channel_abort(ch, false); if (gk20a_fifo_error_ch(g, ch)) { gk20a_debug_dump(g); } } gr_gk20a_enable_ctxsw(g); nvgpu_mutex_release(&g->dbg_sessions_lock); } void gk20a_fifo_recover_tsg(struct gk20a *g, struct tsg_gk20a *tsg, bool verbose, u32 rc_type) { u32 engines = 0U; int err; /* stop context switching to prevent engine assignments from changing until TSG is recovered */ nvgpu_mutex_acquire(&g->dbg_sessions_lock); /* disable tsg so that it does not get scheduled again */ g->ops.fifo.disable_tsg(tsg); /* * On hitting engine reset, h/w drops the ctxsw_status to INVALID in * fifo_engine_status register. Also while the engine is held in reset * h/w passes busy/idle straight through. fifo_engine_status registers * are correct in that there is no context switch outstanding * as the CTXSW is aborted when reset is asserted. */ nvgpu_log_info(g, "acquire engines_reset_mutex"); nvgpu_mutex_acquire(&g->fifo.engines_reset_mutex); /* * stop context switching to prevent engine assignments from * changing until engine status is checked to make sure tsg * being recovered is not loaded on the engines */ err = gr_gk20a_disable_ctxsw(g); if (err != 0) { /* if failed to disable ctxsw, just abort tsg */ nvgpu_err(g, "failed to disable ctxsw"); } else { /* recover engines if tsg is loaded on the engines */ engines = gk20a_fifo_engines_on_id(g, tsg->tsgid, true); /* * it is ok to enable ctxsw before tsg is recovered. If engines * is 0, no engine recovery is needed and if it is non zero, * gk20a_fifo_recover will call get_engines_mask_on_id again. * By that time if tsg is not on the engine, engine need not * be reset. */ err = gr_gk20a_enable_ctxsw(g); if (err != 0) { nvgpu_err(g, "failed to enable ctxsw"); } } nvgpu_log_info(g, "release engines_reset_mutex"); nvgpu_mutex_release(&g->fifo.engines_reset_mutex); if (engines) { gk20a_fifo_recover(g, engines, tsg->tsgid, true, true, verbose, rc_type); } else { if (gk20a_fifo_error_tsg(g, tsg) && verbose) { gk20a_debug_dump(g); } gk20a_fifo_abort_tsg(g, tsg, false); } nvgpu_mutex_release(&g->dbg_sessions_lock); } void gk20a_fifo_teardown_mask_intr(struct gk20a *g) { u32 val; val = gk20a_readl(g, fifo_intr_en_0_r()); val &= ~(fifo_intr_en_0_sched_error_m() | fifo_intr_en_0_mmu_fault_m()); gk20a_writel(g, fifo_intr_en_0_r(), val); gk20a_writel(g, fifo_intr_0_r(), fifo_intr_0_sched_error_reset_f()); } void gk20a_fifo_teardown_unmask_intr(struct gk20a *g) { u32 val; val = gk20a_readl(g, fifo_intr_en_0_r()); val |= fifo_intr_en_0_mmu_fault_f(1) | fifo_intr_en_0_sched_error_f(1); gk20a_writel(g, fifo_intr_en_0_r(), val); } void gk20a_fifo_teardown_ch_tsg(struct gk20a *g, u32 __engine_ids, u32 hw_id, unsigned int id_type, unsigned int rc_type, struct mmu_fault_info *mmfault) { unsigned long engine_id, i; unsigned long _engine_ids = __engine_ids; unsigned long engine_ids = 0; u32 mmu_fault_engines = 0; u32 ref_type; u32 ref_id; u32 ref_id_is_tsg = false; bool id_is_known = (id_type != ID_TYPE_UNKNOWN) ? true : false; bool id_is_tsg = (id_type == ID_TYPE_TSG) ? true : false; u32 rlid; nvgpu_log_info(g, "acquire engines_reset_mutex"); nvgpu_mutex_acquire(&g->fifo.engines_reset_mutex); nvgpu_log_info(g, "acquire runlist_lock for all runlists"); for (rlid = 0; rlid < g->fifo.max_runlists; rlid++) { nvgpu_mutex_acquire(&g->fifo.runlist_info[rlid].runlist_lock); } if (id_is_known) { engine_ids = gk20a_fifo_engines_on_id(g, hw_id, id_is_tsg); ref_id = hw_id; ref_type = id_is_tsg ? fifo_engine_status_id_type_tsgid_v() : fifo_engine_status_id_type_chid_v(); ref_id_is_tsg = id_is_tsg; /* atleast one engine will get passed during sched err*/ engine_ids |= __engine_ids; for_each_set_bit(engine_id, &engine_ids, 32) { u32 mmu_id = gk20a_engine_id_to_mmu_id(g, engine_id); if (mmu_id != FIFO_INVAL_ENGINE_ID) { mmu_fault_engines |= BIT(mmu_id); } } } else { /* store faulted engines in advance */ for_each_set_bit(engine_id, &_engine_ids, 32) { gk20a_fifo_get_faulty_id_type(g, engine_id, &ref_id, &ref_type); if (ref_type == fifo_engine_status_id_type_tsgid_v()) { ref_id_is_tsg = true; } else { ref_id_is_tsg = false; } /* Reset *all* engines that use the * same channel as faulty engine */ for (i = 0; i < g->fifo.num_engines; i++) { u32 active_engine_id = g->fifo.active_engines_list[i]; u32 type; u32 id; gk20a_fifo_get_faulty_id_type(g, active_engine_id, &id, &type); if (ref_type == type && ref_id == id) { u32 mmu_id = gk20a_engine_id_to_mmu_id(g, active_engine_id); engine_ids |= BIT(active_engine_id); if (mmu_id != FIFO_INVAL_ENGINE_ID) { mmu_fault_engines |= BIT(mmu_id); } } } } } if (mmu_fault_engines) { g->ops.fifo.teardown_mask_intr(g); g->ops.fifo.trigger_mmu_fault(g, engine_ids); gk20a_fifo_handle_mmu_fault_locked(g, mmu_fault_engines, ref_id, ref_id_is_tsg); g->ops.fifo.teardown_unmask_intr(g); } nvgpu_log_info(g, "release runlist_lock for all runlists"); for (rlid = 0; rlid < g->fifo.max_runlists; rlid++) { nvgpu_mutex_release(&g->fifo.runlist_info[rlid].runlist_lock); } nvgpu_log_info(g, "release engines_reset_mutex"); nvgpu_mutex_release(&g->fifo.engines_reset_mutex); } void gk20a_fifo_recover(struct gk20a *g, u32 __engine_ids, u32 hw_id, bool id_is_tsg, bool id_is_known, bool verbose, int rc_type) { unsigned int id_type; if (verbose) { gk20a_debug_dump(g); } if (g->ops.ltc.flush) { g->ops.ltc.flush(g); } if (id_is_known) { id_type = id_is_tsg ? ID_TYPE_TSG : ID_TYPE_CHANNEL; } else { id_type = ID_TYPE_UNKNOWN; } g->ops.fifo.teardown_ch_tsg(g, __engine_ids, hw_id, id_type, rc_type, NULL); } /* force reset channel and tsg */ int gk20a_fifo_force_reset_ch(struct channel_gk20a *ch, u32 err_code, bool verbose) { struct channel_gk20a *ch_tsg = NULL; struct gk20a *g = ch->g; struct tsg_gk20a *tsg = tsg_gk20a_from_ch(ch); if (tsg != NULL) { nvgpu_rwsem_down_read(&tsg->ch_list_lock); nvgpu_list_for_each_entry(ch_tsg, &tsg->ch_list, channel_gk20a, ch_entry) { if (gk20a_channel_get(ch_tsg)) { g->ops.fifo.set_error_notifier(ch_tsg, err_code); gk20a_channel_put(ch_tsg); } } nvgpu_rwsem_up_read(&tsg->ch_list_lock); gk20a_fifo_recover_tsg(g, tsg, verbose, RC_TYPE_FORCE_RESET); } else { nvgpu_err(g, "chid: %d is not bound to tsg", ch->chid); } return 0; } int gk20a_fifo_tsg_unbind_channel_verify_status(struct channel_gk20a *ch) { struct gk20a *g = ch->g; if (gk20a_fifo_channel_status_is_next(g, ch->chid)) { nvgpu_log_info(g, "Channel %d to be removed from TSG %d has NEXT set!", ch->chid, ch->tsgid); return -EAGAIN; } if (g->ops.fifo.tsg_verify_status_ctx_reload) { g->ops.fifo.tsg_verify_status_ctx_reload(ch); } if (g->ops.fifo.tsg_verify_status_faulted) { g->ops.fifo.tsg_verify_status_faulted(ch); } return 0; } static bool gk20a_fifo_tsg_is_multi_channel(struct tsg_gk20a *tsg) { bool ret = false; nvgpu_rwsem_down_read(&tsg->ch_list_lock); if (nvgpu_list_first_entry(&tsg->ch_list, channel_gk20a, ch_entry) != nvgpu_list_last_entry(&tsg->ch_list, channel_gk20a, ch_entry)) { ret = true; } nvgpu_rwsem_up_read(&tsg->ch_list_lock); return ret; } int gk20a_fifo_tsg_unbind_channel(struct channel_gk20a *ch) { struct gk20a *g = ch->g; struct tsg_gk20a *tsg = tsg_gk20a_from_ch(ch); int err; bool tsg_timedout = false; if (tsg == NULL) { nvgpu_err(g, "chid: %d is not bound to tsg", ch->chid); return 0; } /* If one channel in TSG times out, we disable all channels */ nvgpu_rwsem_down_write(&tsg->ch_list_lock); tsg_timedout = gk20a_channel_check_timedout(ch); nvgpu_rwsem_up_write(&tsg->ch_list_lock); /* Disable TSG and examine status before unbinding channel */ g->ops.fifo.disable_tsg(tsg); err = g->ops.fifo.preempt_tsg(g, tsg); if (err != 0) { goto fail_enable_tsg; } /* * State validation is only necessary if there are multiple channels in * the TSG. */ if (gk20a_fifo_tsg_is_multi_channel(tsg) && g->ops.fifo.tsg_verify_channel_status && !tsg_timedout) { err = g->ops.fifo.tsg_verify_channel_status(ch); if (err) { goto fail_enable_tsg; } } /* Channel should be seen as TSG channel while updating runlist */ err = channel_gk20a_update_runlist(ch, false); if (err) { goto fail_enable_tsg; } while (ch->mmu_debug_mode_refcnt > 0U) { err = nvgpu_tsg_set_mmu_debug_mode(ch, false); if (err != 0) { nvgpu_err(g, "disable mmu debug mode failed ch:%u", ch->chid); break; } } /* Remove channel from TSG and re-enable rest of the channels */ nvgpu_rwsem_down_write(&tsg->ch_list_lock); nvgpu_list_del(&ch->ch_entry); ch->tsgid = NVGPU_INVALID_TSG_ID; /* another thread could have re-enabled the channel because it was * still on the list at that time, so make sure it's truly disabled */ g->ops.fifo.disable_channel(ch); nvgpu_rwsem_up_write(&tsg->ch_list_lock); /* * Don't re-enable all channels if TSG has timed out already * * Note that we can skip disabling and preempting TSG too in case of * time out, but we keep that to ensure TSG is kicked out */ if (!tsg_timedout) { g->ops.fifo.enable_tsg(tsg); } if (ch->g->ops.fifo.ch_abort_clean_up) { ch->g->ops.fifo.ch_abort_clean_up(ch); } return 0; fail_enable_tsg: if (!tsg_timedout) { g->ops.fifo.enable_tsg(tsg); } return err; } u32 gk20a_fifo_get_failing_engine_data(struct gk20a *g, int *__id, bool *__is_tsg) { u32 engine_id; int id = -1; bool is_tsg = false; u32 mailbox2; u32 active_engine_id = FIFO_INVAL_ENGINE_ID; for (engine_id = 0; engine_id < g->fifo.num_engines; engine_id++) { u32 status; u32 ctx_status; bool failing_engine; active_engine_id = g->fifo.active_engines_list[engine_id]; status = gk20a_readl(g, fifo_engine_status_r(active_engine_id)); ctx_status = fifo_engine_status_ctx_status_v(status); /* we are interested in busy engines */ failing_engine = fifo_engine_status_engine_v(status) == fifo_engine_status_engine_busy_v(); /* ..that are doing context switch */ failing_engine = failing_engine && (ctx_status == fifo_engine_status_ctx_status_ctxsw_switch_v() || ctx_status == fifo_engine_status_ctx_status_ctxsw_save_v() || ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()); if (!failing_engine) { active_engine_id = FIFO_INVAL_ENGINE_ID; continue; } if (ctx_status == fifo_engine_status_ctx_status_ctxsw_load_v()) { id = fifo_engine_status_next_id_v(status); is_tsg = fifo_engine_status_next_id_type_v(status) != fifo_engine_status_next_id_type_chid_v(); } else if (ctx_status == fifo_engine_status_ctx_status_ctxsw_switch_v()) { mailbox2 = gk20a_readl(g, gr_fecs_ctxsw_mailbox_r(2)); if (mailbox2 & FECS_METHOD_WFI_RESTORE) { id = fifo_engine_status_next_id_v(status); is_tsg = fifo_engine_status_next_id_type_v(status) != fifo_engine_status_next_id_type_chid_v(); } else { id = fifo_engine_status_id_v(status); is_tsg = fifo_engine_status_id_type_v(status) != fifo_engine_status_id_type_chid_v(); } } else { id = fifo_engine_status_id_v(status); is_tsg = fifo_engine_status_id_type_v(status) != fifo_engine_status_id_type_chid_v(); } break; } *__id = id; *__is_tsg = is_tsg; return active_engine_id; } bool gk20a_fifo_check_ch_ctxsw_timeout(struct channel_gk20a *ch, bool *verbose, u32 *ms) { bool recover = false; bool progress = false; struct gk20a *g = ch->g; if (gk20a_channel_get(ch)) { recover = gk20a_channel_update_and_check_timeout(ch, g->fifo_eng_timeout_us / 1000, &progress); *verbose = ch->timeout_debug_dump; *ms = ch->timeout_accumulated_ms; if (recover) { g->ops.fifo.set_error_notifier(ch, NVGPU_ERR_NOTIFIER_FIFO_ERROR_IDLE_TIMEOUT); } gk20a_channel_put(ch); } return recover; } bool gk20a_fifo_check_tsg_ctxsw_timeout(struct tsg_gk20a *tsg, bool *verbose, u32 *ms) { struct channel_gk20a *ch; bool recover = false; bool progress = false; struct gk20a *g = tsg->g; *verbose = false; *ms = g->fifo_eng_timeout_us / 1000; nvgpu_rwsem_down_read(&tsg->ch_list_lock); /* check if there was some progress on any of the TSG channels. * fifo recovery is needed if at least one channel reached the * maximum timeout without progress (update in gpfifo pointers). */ nvgpu_list_for_each_entry(ch, &tsg->ch_list, channe