/* * Copyright (c) 2019-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 "../nvgpu-fifo-common.h" #include "nvgpu-channel.h" #define MAX_STUB 2 struct stub_ctx { u32 count; u32 chid; u32 tsgid; }; struct stub_ctx stub[MAX_STUB]; struct channel_unit_ctx { u32 branches; struct stub_ctx stub[MAX_STUB]; }; static struct channel_unit_ctx unit_ctx; static void subtest_setup(u32 branches) { u32 i; unit_ctx.branches = branches; memset(stub, 0, sizeof(stub)); for (i = 0; i < MAX_STUB; i++) { stub[i].chid = NVGPU_INVALID_CHANNEL_ID; } } #define subtest_pruned test_fifo_subtest_pruned #define branches_str test_fifo_flags_str #define F_CHANNEL_SETUP_SW_VZALLOC_FAIL BIT(0) #define F_CHANNEL_SETUP_SW_REF_COND_FAIL BIT(1) #define F_CHANNEL_SETUP_SW_LAST BIT(2) static const char *f_channel_setup_sw[] = { "vzalloc_fail", "cond_init failure" }; static u32 stub_channel_count(struct gk20a *g) { return 32; } int test_channel_setup_sw(struct unit_module *m, struct gk20a *g, void *vargs) { struct gpu_ops gops = g->ops; struct nvgpu_fifo *f = &g->fifo; struct nvgpu_posix_fault_inj *kmem_fi; struct nvgpu_posix_fault_inj *l_cond_fi; u32 branches; int ret = UNIT_FAIL; int err; u32 fail = F_CHANNEL_SETUP_SW_VZALLOC_FAIL | F_CHANNEL_SETUP_SW_REF_COND_FAIL; u32 prune = fail; kmem_fi = nvgpu_kmem_get_fault_injection(); l_cond_fi = nvgpu_cond_get_fault_injection(); g->ops.channel.count = stub_channel_count; for (branches = 0U; branches < F_CHANNEL_SETUP_SW_LAST; branches++) { if (subtest_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_setup_sw)); continue; } subtest_setup(branches); nvgpu_posix_enable_fault_injection(kmem_fi, branches & F_CHANNEL_SETUP_SW_VZALLOC_FAIL ? true : false, 0); /* Insert condition fault after some channels are initialized */ if ((branches & F_CHANNEL_SETUP_SW_REF_COND_FAIL) != 0U) { nvgpu_posix_enable_fault_injection(l_cond_fi, true, 5); } unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_setup_sw)); err = nvgpu_channel_setup_sw(g); if (branches & fail) { nvgpu_posix_enable_fault_injection(kmem_fi, false, 0); nvgpu_posix_enable_fault_injection(l_cond_fi, false, 0); unit_assert(err != 0, goto done); unit_assert(f->channel == NULL, goto done); } else { unit_assert(err == 0, goto done); nvgpu_channel_cleanup_sw(g); } } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_setup_sw)); } g->ops = gops; return ret; } #define F_CHANNEL_OPEN_ENGINE_NOT_VALID BIT(0) #define F_CHANNEL_OPEN_PRIVILEGED BIT(1) #define F_CHANNEL_OPEN_ALLOC_CH_FAIL BIT(2) #define F_CHANNEL_OPEN_ALLOC_CH_WARN0 BIT(3) #define F_CHANNEL_OPEN_ALLOC_CH_WARN1 BIT(4) #define F_CHANNEL_OPEN_ALLOC_CH_AGGRESSIVE BIT(5) #define F_CHANNEL_OPEN_BUG_ON BIT(6) #define F_CHANNEL_OPEN_ALLOC_INST_FAIL BIT(7) #define F_CHANNEL_OPEN_NOTIFIER_WQ_INIT_FAIL BIT(8) #define F_CHANNEL_OPEN_SEMAPHORE_WQ_INIT_FAIL BIT(9) #define F_CHANNEL_OPEN_LAST BIT(10) static const char *f_channel_open[] = { "engine_not_valid", "privileged", "alloc_ch_fail", "alloc_ch_warn0", "alloc_ch_warn1", "aggressive_destroy", "bug_on", "alloc_inst_fail", "notifier_wq_init_fail", "semaphore_wq_init_fail", }; static int stub_channel_alloc_inst_ENOMEM(struct gk20a *g, struct nvgpu_channel *ch) { return -ENOMEM; } int test_channel_open(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_fifo *f = &g->fifo; struct nvgpu_fifo fifo = g->fifo; struct gpu_ops gops = g->ops; struct nvgpu_channel *ch, *next_ch; struct nvgpu_posix_fault_inj *l_cond_fi; u32 branches; int ret = UNIT_FAIL; u32 fail = F_CHANNEL_OPEN_ALLOC_CH_FAIL | F_CHANNEL_OPEN_BUG_ON | F_CHANNEL_OPEN_ALLOC_INST_FAIL | F_CHANNEL_OPEN_NOTIFIER_WQ_INIT_FAIL | F_CHANNEL_OPEN_SEMAPHORE_WQ_INIT_FAIL; u32 prune = fail | F_CHANNEL_OPEN_ALLOC_CH_WARN0 | F_CHANNEL_OPEN_ALLOC_CH_WARN1; u32 runlist_id; bool privileged; int err; void (*os_channel_open)(struct nvgpu_channel *ch) = g->os_channel.open; l_cond_fi = nvgpu_cond_get_fault_injection(); for (branches = 0U; branches < F_CHANNEL_OPEN_LAST; branches++) { if (subtest_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_open)); continue; } subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_open)); next_ch = nvgpu_list_empty(&f->free_chs) ? NULL : nvgpu_list_first_entry(&f->free_chs, nvgpu_channel, free_chs); unit_assert(next_ch != NULL, goto done); runlist_id = branches & F_CHANNEL_OPEN_ENGINE_NOT_VALID ? NVGPU_INVALID_RUNLIST_ID : NVGPU_ENGINE_GR; privileged = branches & F_CHANNEL_OPEN_PRIVILEGED ? true : false; if (branches & F_CHANNEL_OPEN_ALLOC_CH_FAIL) { nvgpu_init_list_node(&f->free_chs); } if (branches & F_CHANNEL_OPEN_ALLOC_CH_WARN0) { nvgpu_atomic_inc(&next_ch->ref_count); } if (branches & F_CHANNEL_OPEN_ALLOC_CH_WARN1) { next_ch->referenceable = false; } if (branches & F_CHANNEL_OPEN_ALLOC_CH_AGGRESSIVE) { g->aggressive_sync_destroy_thresh += 1U; f->used_channels += 2U; } if (branches & F_CHANNEL_OPEN_NOTIFIER_WQ_INIT_FAIL) { nvgpu_posix_enable_fault_injection(l_cond_fi, true, 0); } if (branches & F_CHANNEL_OPEN_SEMAPHORE_WQ_INIT_FAIL) { nvgpu_posix_enable_fault_injection(l_cond_fi, true, 1); } g->ops.channel.alloc_inst = branches & F_CHANNEL_OPEN_ALLOC_INST_FAIL ? stub_channel_alloc_inst_ENOMEM : gops.channel.alloc_inst; if (branches & F_CHANNEL_OPEN_BUG_ON) { next_ch->g = (void *)1; } err = EXPECT_BUG( ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); ); if (branches & F_CHANNEL_OPEN_BUG_ON) { next_ch->g = NULL; unit_assert(err != 0, goto done); } else { unit_assert(err == 0, goto done); }; if (branches & F_CHANNEL_OPEN_ALLOC_CH_WARN1) { next_ch->referenceable = true; } if (branches & F_CHANNEL_OPEN_ALLOC_CH_AGGRESSIVE) { g->aggressive_sync_destroy_thresh -= 1U; f->used_channels -= 2U; unit_assert(g->aggressive_sync_destroy, goto done); g->aggressive_sync_destroy = false; } if (branches & fail) { nvgpu_posix_enable_fault_injection(l_cond_fi, false, 0); if (branches & F_CHANNEL_OPEN_ALLOC_CH_FAIL) { f->free_chs = fifo.free_chs; } if (branches & F_CHANNEL_OPEN_ALLOC_CH_WARN0) { nvgpu_atomic_dec(&ch->ref_count); } unit_assert(ch == NULL, goto done); } else { unit_assert(ch != NULL, goto done); unit_assert(ch->g == g, goto done); unit_assert(nvgpu_list_empty(&ch->free_chs), goto done); nvgpu_channel_close(ch); ch = NULL; } } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_open)); } if (ch != NULL) { nvgpu_channel_close(ch); } g->ops = gops; g->os_channel.open = os_channel_open; return ret; } #define F_CHANNEL_CLOSE_ALREADY_FREED BIT(0) #define F_CHANNEL_CLOSE_FORCE BIT(1) #define F_CHANNEL_CLOSE_DYING BIT(2) #define F_CHANNEL_CLOSE_TSG_BOUND BIT(3) #define F_CHANNEL_CLOSE_TSG_UNBIND_FAIL BIT(4) #define F_CHANNEL_CLOSE_OS_CLOSE BIT(5) #define F_CHANNEL_CLOSE_NON_REFERENCEABLE BIT(6) #define F_CHANNEL_CLOSE_FREE_SUBCTX BIT(7) #define F_CHANNEL_CLOSE_USER_SYNC BIT(8) #define F_CHANNEL_CLOSE_NONZERO_DESTROY_THRESH_64 BIT(9) #define F_CHANNEL_CLOSE_NONZERO_DESTROY_THRESH_1 BIT(10) #define F_CHANNEL_CLOSE_DETERMINISTIC BIT(11) #define F_CHANNEL_CLOSE_DETERMINISTIC_RAILGATE_ALLOWED BIT(12) #define F_CHANNEL_WAIT_UNTIL_COUNTER BIT(13) #define F_CHANNEL_CLOSE_AS_BOUND BIT(14) #define F_CHANNEL_CLOSE_LAST BIT(15) /* nvgpu_tsg_force_unbind_channel always return 0 */ static const char *f_channel_close[] = { "already_freed", "force", "dying", "tsg_bound", "tsg_unbind_fail", "os_close", "non_referenceable", "free_subctx", "user_sync", "destroy_thresh_64", "destroy_thresh_1", "deterministic", "deterministic_railgate_allowed", "as_bound", }; static int thread_reset_function(void *arg) { struct nvgpu_channel *ch = (struct nvgpu_channel *)arg; sleep(1); nvgpu_atomic_set(&ch->ref_count, 1); return 0; } static void stub_os_channel_close(struct nvgpu_channel *ch, bool force) { stub[0].chid = ch->chid; } static void stub_gr_intr_flush_channel_tlb(struct gk20a *g) { } static bool channel_close_pruned(u32 branches, u32 final) { u32 branches_init = branches; if (subtest_pruned(branches, final)) { return true; } /* TODO: nvgpu_tsg_force_unbind_channel always returns 0 */ branches &= ~F_CHANNEL_CLOSE_TSG_UNBIND_FAIL; if ((branches & F_CHANNEL_CLOSE_AS_BOUND) == 0) { branches &= ~F_CHANNEL_CLOSE_FREE_SUBCTX; } if (branches < branches_init) { return true; } return false; } int test_channel_close(struct unit_module *m, struct gk20a *g, void *vargs) { struct gpu_ops gops = g->ops; struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg; u32 branches = 0U; int ret = UNIT_FAIL; u32 fail = F_CHANNEL_CLOSE_ALREADY_FREED | F_CHANNEL_CLOSE_NON_REFERENCEABLE; u32 prune = (u32)(F_CHANNEL_CLOSE_USER_SYNC) | F_CHANNEL_CLOSE_DETERMINISTIC_RAILGATE_ALLOWED | F_CHANNEL_WAIT_UNTIL_COUNTER | fail; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; void (*os_channel_close)(struct nvgpu_channel *ch, bool force) = g->os_channel.close; bool privileged = false; bool force; int err = 0; struct mm_gk20a mm; struct vm_gk20a vm; struct nvgpu_thread thread_reset; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); g->ops.gr.intr.flush_channel_tlb = stub_gr_intr_flush_channel_tlb; for (branches = 0U; branches < F_CHANNEL_CLOSE_LAST; branches++) { if (channel_close_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_close)); continue; } subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_close)); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); ch->usermode_submit_enabled = true; force = branches & F_CHANNEL_CLOSE_FORCE ? true : false; nvgpu_set_enabled(g, NVGPU_DRIVER_IS_DYING, branches & F_CHANNEL_CLOSE_DYING ? true : false); g->os_channel.close = branches & F_CHANNEL_CLOSE_OS_CLOSE ? stub_os_channel_close : NULL; g->aggressive_sync_destroy_thresh = branches & F_CHANNEL_CLOSE_NONZERO_DESTROY_THRESH_64 ? 64U : (branches & F_CHANNEL_CLOSE_NONZERO_DESTROY_THRESH_1) ? 1U : 0U; if (branches & F_CHANNEL_CLOSE_TSG_BOUND) { err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); } ch->referenceable = branches & F_CHANNEL_CLOSE_NON_REFERENCEABLE ? false : true; if (branches & F_CHANNEL_CLOSE_AS_BOUND) { memset(&mm, 0, sizeof(mm)); memset(&vm, 0, sizeof(vm)); mm.g = g; vm.mm = &mm; ch->vm = &vm; nvgpu_ref_init(&vm.ref); nvgpu_ref_get(&vm.ref); } else { ch->vm = NULL; } #ifdef CONFIG_NVGPU_DETERMINISTIC_CHANNELS if (branches & F_CHANNEL_CLOSE_DETERMINISTIC) { /* Compensate for atomic dec in gk20a_idle() */ nvgpu_atomic_set(&g->usage_count, 1); ch->deterministic = true; } if (branches & F_CHANNEL_CLOSE_DETERMINISTIC_RAILGATE_ALLOWED) { ch->deterministic = true; ch->deterministic_railgate_allowed = true; } #endif g->ops.gr.setup.free_subctx = branches & F_CHANNEL_CLOSE_FREE_SUBCTX ? gops.gr.setup.free_subctx : NULL; if (branches & F_CHANNEL_CLOSE_USER_SYNC) { /* TODO: stub ch->user_sync */ } if (branches & F_CHANNEL_WAIT_UNTIL_COUNTER) { nvgpu_atomic_set(&ch->ref_count, 2); err = nvgpu_thread_create(&thread_reset, (void *)ch, &thread_reset_function, "reset_thread"); unit_assert(err == 0, goto done); } if (branches & F_CHANNEL_CLOSE_ALREADY_FREED) { nvgpu_channel_close(ch); } if (force) { err = EXPECT_BUG(nvgpu_channel_kill(ch)); } else { err = EXPECT_BUG(nvgpu_channel_close(ch)); } if (branches & F_CHANNEL_WAIT_UNTIL_COUNTER) { nvgpu_thread_join(&thread_reset); } if (branches & F_CHANNEL_CLOSE_ALREADY_FREED) { unit_assert(err != 0, goto done); unit_assert(ch->g == NULL, goto done); continue; } if ((branches & F_CHANNEL_CLOSE_USER_SYNC) != 0U) { unit_assert(ch->user_sync == NULL, goto done); } if (branches & fail) { unit_assert(ch->g != NULL, goto done); unit_assert(nvgpu_list_empty(&ch->free_chs), goto done); if (branches & F_CHANNEL_CLOSE_ALREADY_FREED) { continue; } ch->referenceable = true; nvgpu_channel_kill(ch); continue; } if (branches & F_CHANNEL_CLOSE_DYING) { /* when driver is dying, tsg unbind is skipped */ nvgpu_init_list_node(&tsg->ch_list); nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); } else { unit_assert(!nvgpu_list_empty(&ch->free_chs), goto done); unit_assert(nvgpu_list_empty(&tsg->ch_list), goto done); } if (branches & F_CHANNEL_CLOSE_OS_CLOSE) { unit_assert(stub[0].chid == ch->chid, goto done); } if (!(branches & F_CHANNEL_CLOSE_AS_BOUND)) { goto unbind; } if (branches & F_CHANNEL_CLOSE_FREE_SUBCTX) { unit_assert(ch->subctx == NULL, goto done); } if (ch->subctx != NULL) { if (g->ops.gr.setup.free_subctx != NULL) { g->ops.gr.setup.free_subctx(ch); } ch->subctx = NULL; } #ifdef CONFIG_NVGPU_DETERMINISTIC_CHANNELS ch->deterministic = false; ch->deterministic_railgate_allowed = false; #endif unit_assert(ch->usermode_submit_enabled == false, goto done); /* we took an extra reference to avoid nvgpu_vm_remove_ref */ unit_assert(nvgpu_ref_put_return(&vm.ref, NULL), goto done); unit_assert(ch->user_sync == NULL, goto done); unbind: /* * branches not taken in safety build: * - ch->sync != NULL * - allow railgate for deterministic channel * - unlink all debug sessions * - free pre-allocated resources * - channel refcount tracking */ unit_assert(ch->g == NULL, goto done); unit_assert(!ch->referenceable, goto done); unit_assert(!nvgpu_list_empty(&ch->free_chs), goto done); ch = NULL; } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_close)); } nvgpu_set_enabled(g, NVGPU_DRIVER_IS_DYING, false); if (ch != NULL) { nvgpu_channel_close(ch); } if (tsg != NULL) { nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); } g->ops = gops; g->os_channel.close = os_channel_close; return ret; } #define F_CHANNEL_SETUP_BIND_NO_AS BIT(0) #define F_CHANNEL_SETUP_BIND_USERMODE_ENABLED BIT(1) #define F_CHANNEL_SETUP_BIND_USERMODE_ALLOC_BUF_NULL BIT(2) #define F_CHANNEL_SETUP_BIND_USERMODE_ALLOC_BUF_FAIL BIT(3) #define F_CHANNEL_SETUP_BIND_USERMODE_SETUP_RAMFC_FAIL BIT(4) #define F_CHANNEL_SETUP_BIND_USERMODE_UPDATE_RL_FAIL BIT(5) #define F_CHANNEL_SETUP_BIND_USERMODE_TSGID_INVALID BIT(6) #define F_CHANNEL_SETUP_BIND_USERMODE_SUPPORT_DETERMINISTIC BIT(7) #define F_CHANNEL_SETUP_BIND_USERMODE_POWER_REF_COUNT_FAIL BIT(8) #define F_CHANNEL_SETUP_BIND_NON_USERMODE_DETERMINISTIC BIT(9) #define F_CHANNEL_SETUP_BIND_USERMODE_OS_CH_USERMODE_BUF BIT(10) #define F_CHANNEL_SETUP_GPFIFO_ENTRIES_OUT_OF_BOUND BIT(11) #define F_CHANNEL_SETUP_BIND_LAST BIT(12) static const char *f_channel_setup_bind[] = { "no_as", "usermode_enabled", "alloc_buf_null", "alloc_buf_fail", "setup_ramfc_fail", "update_rl_fail", "invalid tsgid", "support determininstic", "power ref count fail", "non usermode determinstic channel", "os_channel free usermode buffer", }; static int stub_os_channel_alloc_usermode_buffers(struct nvgpu_channel *ch, struct nvgpu_setup_bind_args *args) { int err; struct gk20a *g = ch->g; err = nvgpu_dma_alloc(g, NVGPU_CPU_PAGE_SIZE, &ch->usermode_userd); if (err != 0) { return err; } err = nvgpu_dma_alloc(g, NVGPU_CPU_PAGE_SIZE, &ch->usermode_gpfifo); if (err != 0) { return err; } stub[0].chid = ch->chid; return err; } static int stub_os_channel_alloc_usermode_buffers_ENOMEM( struct nvgpu_channel *ch, struct nvgpu_setup_bind_args *args) { return -ENOMEM; } static int stub_runlist_update(struct gk20a *g, struct nvgpu_runlist *rl, struct nvgpu_channel *ch, bool add, bool wait_for_finish) { stub[1].chid = ch->chid; return 0; } static int stub_runlist_update_ETIMEDOUT(struct gk20a *g, struct nvgpu_runlist *rl, struct nvgpu_channel *ch, bool add, bool wait_for_finish) { return -ETIMEDOUT; } static int stub_ramfc_setup_EINVAL(struct nvgpu_channel *ch, u64 gpfifo_base, u32 gpfifo_entries, u64 pbdma_acquire_timeout, u32 flags) { return -EINVAL; } static int stub_mm_l2_flush(struct gk20a *g, bool invalidate) { return 0; } static void stub_os_channel_free_usermode_buffers(struct nvgpu_channel *c) { } int test_channel_setup_bind(struct unit_module *m, struct gk20a *g, void *vargs) { struct gpu_ops gops = g->ops; struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg = NULL; struct nvgpu_posix_fault_inj *l_nvgpu_fi; u32 branches = 0U; int ret = UNIT_FAIL; u32 fail = F_CHANNEL_SETUP_BIND_NO_AS | F_CHANNEL_SETUP_BIND_USERMODE_ENABLED | F_CHANNEL_SETUP_BIND_USERMODE_ALLOC_BUF_NULL | F_CHANNEL_SETUP_BIND_USERMODE_ALLOC_BUF_FAIL | F_CHANNEL_SETUP_BIND_USERMODE_SETUP_RAMFC_FAIL | F_CHANNEL_SETUP_BIND_USERMODE_UPDATE_RL_FAIL | F_CHANNEL_SETUP_BIND_USERMODE_TSGID_INVALID | F_CHANNEL_SETUP_BIND_USERMODE_POWER_REF_COUNT_FAIL | F_CHANNEL_SETUP_BIND_NON_USERMODE_DETERMINISTIC | F_CHANNEL_SETUP_BIND_USERMODE_OS_CH_USERMODE_BUF | F_CHANNEL_SETUP_GPFIFO_ENTRIES_OUT_OF_BOUND; u32 prune = (u32)(F_CHANNEL_SETUP_BIND_USERMODE_SUPPORT_DETERMINISTIC) | fail; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; u32 tsgid_orig; bool privileged = false; int err; struct nvgpu_mem pdb_mem; struct mm_gk20a mm; struct vm_gk20a vm; int (*alloc_usermode_buffers)(struct nvgpu_channel *c, struct nvgpu_setup_bind_args *args) = g->os_channel.alloc_usermode_buffers; struct nvgpu_setup_bind_args bind_args; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); g->ops.gr.intr.flush_channel_tlb = stub_gr_intr_flush_channel_tlb; g->ops.mm.cache.l2_flush = stub_mm_l2_flush; /* bug 2621189 */ memset(&mm, 0, sizeof(mm)); memset(&vm, 0, sizeof(vm)); mm.g = g; vm.mm = &mm; ch->vm = &vm; err = nvgpu_dma_alloc(g, NVGPU_CPU_PAGE_SIZE, &pdb_mem); unit_assert(err == 0, goto done); vm.pdb.mem = &pdb_mem; memset(&bind_args, 0, sizeof(bind_args)); bind_args.num_gpfifo_entries = 32; tsgid_orig = ch->tsgid; l_nvgpu_fi = nvgpu_nvgpu_get_fault_injection(); for (branches = 0U; branches < F_CHANNEL_SETUP_BIND_LAST; branches++) { if (subtest_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_setup_bind)); continue; } subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_setup_bind)); ch->vm = branches & F_CHANNEL_SETUP_BIND_NO_AS ? NULL : &vm; if (branches & F_CHANNEL_SETUP_BIND_USERMODE_ENABLED) { ch->usermode_submit_enabled = true; } g->os_channel.alloc_usermode_buffers = branches & F_CHANNEL_SETUP_BIND_USERMODE_ALLOC_BUF_NULL ? NULL : stub_os_channel_alloc_usermode_buffers; if (branches & F_CHANNEL_SETUP_BIND_USERMODE_ALLOC_BUF_FAIL) { g->os_channel.alloc_usermode_buffers = stub_os_channel_alloc_usermode_buffers_ENOMEM; } #ifdef CONFIG_NVGPU_IOCTL_NON_FUSA if (branches & F_CHANNEL_SETUP_BIND_USERMODE_SUPPORT_DETERMINISTIC) { bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_SUPPORT_DETERMINISTIC; } if (branches & F_CHANNEL_SETUP_BIND_USERMODE_POWER_REF_COUNT_FAIL) { bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_SUPPORT_DETERMINISTIC; ch->usermode_submit_enabled = false; nvgpu_posix_enable_fault_injection(l_nvgpu_fi, true, 0); } #endif if (branches & F_CHANNEL_SETUP_BIND_NON_USERMODE_DETERMINISTIC) { bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_SUPPORT_DETERMINISTIC; bind_args.flags &= ~NVGPU_SETUP_BIND_FLAGS_USERMODE_SUPPORT; } else { bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_SUPPORT_DETERMINISTIC; bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_USERMODE_SUPPORT; } if (branches & F_CHANNEL_SETUP_GPFIFO_ENTRIES_OUT_OF_BOUND) { bind_args.num_gpfifo_entries = -1; } ch->tsgid = branches & F_CHANNEL_SETUP_BIND_USERMODE_TSGID_INVALID ? NVGPU_INVALID_TSG_ID : tsgid_orig; g->ops.runlist.update = branches & F_CHANNEL_SETUP_BIND_USERMODE_UPDATE_RL_FAIL ? stub_runlist_update_ETIMEDOUT : stub_runlist_update; g->ops.ramfc.setup = branches & F_CHANNEL_SETUP_BIND_USERMODE_SETUP_RAMFC_FAIL ? stub_ramfc_setup_EINVAL : gops.ramfc.setup; if (branches & F_CHANNEL_SETUP_BIND_USERMODE_OS_CH_USERMODE_BUF) { g->ops.ramfc.setup = stub_ramfc_setup_EINVAL; g->os_channel.free_usermode_buffers = stub_os_channel_free_usermode_buffers; } err = nvgpu_channel_setup_bind(ch, &bind_args); if (branches & fail) { nvgpu_posix_enable_fault_injection( l_nvgpu_fi, false, 0); unit_assert(err != 0, goto done); unit_assert(!nvgpu_mem_is_valid(&ch->usermode_userd), goto done); unit_assert(!nvgpu_mem_is_valid(&ch->usermode_gpfifo), goto done); ch->usermode_submit_enabled = false; unit_assert(nvgpu_atomic_read(&ch->bound) == false, goto done); g->os_channel.free_usermode_buffers = NULL; bind_args.num_gpfifo_entries = 32; } else { unit_assert(err == 0, goto done); unit_assert(stub[0].chid == ch->chid, goto done); unit_assert(ch->usermode_submit_enabled == true, goto done); unit_assert(ch->userd_iova != 0U, goto done); unit_assert(stub[1].chid == ch->chid, goto done); unit_assert(nvgpu_atomic_read(&ch->bound) == true, goto done); nvgpu_dma_free(g, &ch->usermode_userd); nvgpu_dma_free(g, &ch->usermode_gpfifo); ch->userd_iova = 0U; #ifdef CONFIG_NVGPU_DETERMINISTIC_CHANNELS ch->deterministic = false; #endif nvgpu_atomic_set(&ch->bound, false); } bind_args.flags &= ~NVGPU_SETUP_BIND_FLAGS_SUPPORT_DETERMINISTIC; } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_setup_bind)); } nvgpu_set_enabled(g, NVGPU_DRIVER_IS_DYING, false); if (ch != NULL) { nvgpu_channel_close(ch); } nvgpu_dma_free(g, &pdb_mem); g->os_channel.alloc_usermode_buffers = alloc_usermode_buffers; g->ops = gops; return ret; } #define F_CHANNEL_ALLOC_INST_ENOMEM BIT(0) #define F_CHANNEL_ALLOC_INST_LAST BIT(1) static const char *f_channel_alloc_inst[] = { "nomem", }; int test_channel_alloc_inst(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; u32 branches = 0U; u32 fail = F_CHANNEL_ALLOC_INST_ENOMEM; u32 prune = fail; int ret = UNIT_FAIL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; struct nvgpu_posix_fault_inj *dma_fi; int err; dma_fi = nvgpu_dma_alloc_get_fault_injection(); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); for (branches = 0U; branches < F_CHANNEL_ALLOC_INST_LAST; branches++) { if (subtest_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_alloc_inst)); continue; } subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_alloc_inst)); nvgpu_posix_enable_fault_injection(dma_fi, branches & F_CHANNEL_ALLOC_INST_ENOMEM ? true : false, 0); err = nvgpu_channel_alloc_inst(g, ch); if (branches & fail) { unit_assert(err != 0, goto done); unit_assert(ch->inst_block.aperture == APERTURE_INVALID, goto done); } else { unit_assert(err == 0, goto done); unit_assert(ch->inst_block.aperture != APERTURE_INVALID, goto done); } nvgpu_channel_free_inst(g, ch); unit_assert(ch->inst_block.aperture == APERTURE_INVALID, goto done); } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_alloc_inst)); } if (ch != NULL) { nvgpu_channel_close(ch); } nvgpu_posix_enable_fault_injection(dma_fi, false, 0); return ret; } /* * channel non-referenceable case is covered when no match is found * since we looked up all possible channels. */ #define F_CHANNEL_FROM_INST_NO_INIT BIT(0) #define F_CHANNEL_FROM_INST_NO_CHANNEL BIT(1) #define F_CHANNEL_FROM_INST_MATCH_A BIT(2) #define F_CHANNEL_FROM_INST_MATCH_B BIT(3) #define F_CHANNEL_FROM_INST_LAST BIT(4) static const char *f_channel_from_inst[] = { "no_init", "no_channel", "match_a", "match_b", }; int test_channel_from_inst(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_channel *chA = NULL; struct nvgpu_channel *chB = NULL; struct nvgpu_fifo *f = &g->fifo; struct nvgpu_fifo fifo = g->fifo; u32 branches = 0U; u32 found = F_CHANNEL_FROM_INST_MATCH_A | F_CHANNEL_FROM_INST_MATCH_B; u32 prune = found | F_CHANNEL_FROM_INST_NO_INIT | F_CHANNEL_FROM_INST_NO_CHANNEL; int ret = UNIT_FAIL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; u64 inst_ptr; bool privileged = false; chA = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(chA != NULL, goto done); chB = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(chB != NULL, goto done); unit_assert(f->num_channels > 0U, goto done); for (branches = 0U; branches < F_CHANNEL_FROM_INST_LAST; branches++) { if (subtest_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_from_inst)); continue; } subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_from_inst)); if (branches & F_CHANNEL_FROM_INST_NO_INIT) { f->channel = NULL; } if (branches & F_CHANNEL_FROM_INST_NO_CHANNEL) { f->num_channels = 0U; } inst_ptr = (u64)-1; if (branches & F_CHANNEL_FROM_INST_MATCH_A) { inst_ptr = nvgpu_inst_block_addr(g, &chA->inst_block); } if (branches & F_CHANNEL_FROM_INST_MATCH_B) { inst_ptr = nvgpu_inst_block_addr(g, &chB->inst_block); } ch = nvgpu_channel_refch_from_inst_ptr(g, inst_ptr); if (branches & found) { if (branches & F_CHANNEL_FROM_INST_MATCH_A) { unit_assert(ch == chA, goto done); } if (branches & F_CHANNEL_FROM_INST_MATCH_B) { unit_assert(ch == chB, goto done); } unit_assert(nvgpu_atomic_read(&ch->ref_count) == 2, goto done); nvgpu_channel_put(ch); } else { f->channel = fifo.channel; f->num_channels = fifo.num_channels; unit_assert(ch == NULL, goto done); } } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_from_inst)); } if (chA != NULL) { nvgpu_channel_close(chA); } if (chB != NULL) { nvgpu_channel_close(chB); } return ret; } static void stub_tsg_enable(struct nvgpu_tsg *tsg) { stub[0].tsgid = tsg->tsgid; } static void stub_tsg_disable(struct nvgpu_tsg *tsg) { stub[1].tsgid = tsg->tsgid; } int test_channel_enable_disable_tsg(struct unit_module *m, struct gk20a *g, void *vargs) { struct gpu_ops gops = g->ops; struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg = NULL; u32 branches = 0U; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; int err; int ret = UNIT_FAIL; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); g->ops.tsg.enable = stub_tsg_enable; g->ops.tsg.disable = stub_tsg_disable; subtest_setup(branches); err = nvgpu_channel_enable_tsg(g, ch); unit_assert(stub[0].tsgid = tsg->tsgid, goto done); err = nvgpu_channel_disable_tsg(g, ch); unit_assert(stub[1].tsgid = tsg->tsgid, goto done); subtest_setup(branches); err = nvgpu_tsg_force_unbind_channel(tsg, ch); unit_assert(err == 0, goto done); err = nvgpu_channel_enable_tsg(g, ch); unit_assert(err != 0, goto done); err = nvgpu_channel_disable_tsg(g, ch); unit_assert(err != 0, goto done); ret = UNIT_SUCCESS; done: if (ch != NULL) { nvgpu_channel_close(ch); } if (tsg != NULL) { nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); } g->ops = gops; return ret; } #define F_CHANNEL_ABORT_TSG BIT(0) #define F_CHANNEL_ABORT_LAST BIT(1) static const char *f_channel_abort[] = { "tsg not null", }; int test_channel_abort(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg = NULL; u32 branches = 0U; int ret = UNIT_FAIL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; int err; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); for (branches = 0U; branches < F_CHANNEL_ABORT_LAST; branches++) { subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_abort)); if ((branches & F_CHANNEL_ABORT_TSG) != 0U) { err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); } nvgpu_channel_abort(ch, false); unit_assert(ch->unserviceable == true, goto done); } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_abort)); } if (ch != NULL) { nvgpu_channel_close(ch); } if (tsg != NULL) { nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); } return ret; } #define F_CHANNEL_MARK_ERROR_COND_BROADCAST_FAIL BIT(0) #define F_CHANNEL_MARK_ERROR_LAST BIT(1) static const char *f_channel_mark_error[] = { "condition_broadcast_fail", }; int test_channel_mark_error(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; u32 branches = 0U; int ret = UNIT_FAIL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; bool err; ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); for (branches = 0U; branches < F_CHANNEL_MARK_ERROR_LAST; branches++) { subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_mark_error)); if ((branches & F_CHANNEL_MARK_ERROR_COND_BROADCAST_FAIL) != 0) { ch->semaphore_wq.initialized = false; ch->notifier_wq.initialized = false; } err = nvgpu_channel_mark_error(g, ch); unit_assert(err == false, goto done); unit_assert(ch->unserviceable == true, goto done); ch->semaphore_wq.initialized = true; ch->notifier_wq.initialized = true; } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_mark_error)); } if (ch != NULL) { nvgpu_channel_close(ch); } return ret; } int test_channel_sw_quiesce(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_fifo *f = &g->fifo; int ret = UNIT_FAIL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); unit_assert(f->num_channels > 0U, goto done); nvgpu_channel_sw_quiesce(g); unit_assert(ch->unserviceable == true, goto done); ret = UNIT_SUCCESS; done: if (ch != NULL) { nvgpu_channel_close(ch); } return ret; } #define F_CHANNEL_DETERMINISTIC_IDLE_UNIDLE BIT(0) #define F_CHANNEL_DETERMINISTIC_IDLE_RAILGATE_ALLOWED BIT(1) #define F_CHANNEL_DETERMINISTIC_UNIDLE_GK20ABUSY_FAIL BIT(2) #define F_CHANNEL_DETERMINISTIC_IDLE_LAST BIT(3) #ifdef CONFIG_NVGPU_DETERMINISTIC_CHANNELS static const char *f_channel_deterministic_idle_unidle[] = { "deterministic_channel", "determinstic_railgate_allowed", "gk20a_busy_fail", }; int test_channel_deterministic_idle_unidle(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_posix_fault_inj *l_nvgpu_fi; struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg = NULL; struct nvgpu_mem pdb_mem; struct mm_gk20a mm; struct vm_gk20a vm; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; int err; u32 branches = 0U; int ret = UNIT_FAIL; int gpu_usage_count_initial; struct nvgpu_setup_bind_args bind_args; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); memset(&mm, 0, sizeof(mm)); memset(&vm, 0, sizeof(vm)); mm.g = g; vm.mm = &mm; ch->vm = &vm; err = nvgpu_dma_alloc(g, NVGPU_CPU_PAGE_SIZE, &pdb_mem); unit_assert(err == 0, goto done); vm.pdb.mem = &pdb_mem; g->ops.gr.intr.flush_channel_tlb = stub_gr_intr_flush_channel_tlb; g->ops.mm.cache.l2_flush = stub_mm_l2_flush; /* bug 2621189 */ g->os_channel.alloc_usermode_buffers = stub_os_channel_alloc_usermode_buffers; g->ops.runlist.update = stub_runlist_update; (void)memset(&bind_args, 0, sizeof(bind_args)); bind_args.num_gpfifo_entries = 32; bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_USERMODE_SUPPORT; l_nvgpu_fi = nvgpu_nvgpu_get_fault_injection(); for (branches = 0U; branches < F_CHANNEL_DETERMINISTIC_IDLE_LAST; branches++) { subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_deterministic_idle_unidle)); if ((branches & F_CHANNEL_DETERMINISTIC_IDLE_UNIDLE) != 0) { bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_SUPPORT_DETERMINISTIC; } else { bind_args.flags &= ~NVGPU_SETUP_BIND_FLAGS_SUPPORT_DETERMINISTIC; } err = nvgpu_channel_setup_bind(ch, &bind_args); unit_assert(err == 0, goto done); unit_assert(nvgpu_atomic_read(&ch->bound) == true, goto done); ch->deterministic_railgate_allowed = (branches & F_CHANNEL_DETERMINISTIC_IDLE_RAILGATE_ALLOWED) ? true : false; nvgpu_posix_enable_fault_injection(l_nvgpu_fi, ((branches & F_CHANNEL_DETERMINISTIC_UNIDLE_GK20ABUSY_FAIL) != 0) ? true : false, 0); gpu_usage_count_initial = g->usage_count.v; nvgpu_channel_deterministic_idle(g); if ((u64)(branches & 0x3U) == (F_CHANNEL_DETERMINISTIC_IDLE_UNIDLE & ~F_CHANNEL_DETERMINISTIC_IDLE_RAILGATE_ALLOWED)) { unit_assert(g->usage_count.v == (gpu_usage_count_initial - 1), goto done); } else { unit_assert(g->usage_count.v == gpu_usage_count_initial, goto done); } nvgpu_channel_deterministic_unidle(g); if (branches == ((F_CHANNEL_DETERMINISTIC_IDLE_UNIDLE | F_CHANNEL_DETERMINISTIC_UNIDLE_GK20ABUSY_FAIL) & ~F_CHANNEL_DETERMINISTIC_IDLE_RAILGATE_ALLOWED)) { unit_assert(g->usage_count.v == (gpu_usage_count_initial - 1), goto done); } else { unit_assert(g->usage_count.v == gpu_usage_count_initial, goto done); } nvgpu_dma_free(g, &ch->usermode_userd); nvgpu_dma_free(g, &ch->usermode_gpfifo); ch->userd_iova = 0U; ch->deterministic = false; ch->usermode_submit_enabled = false; nvgpu_atomic_set(&ch->bound, false); nvgpu_posix_enable_fault_injection(l_nvgpu_fi, false, 0); } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_deterministic_idle_unidle)); } if (ch != NULL) { nvgpu_channel_close(ch); } if (tsg != NULL) { nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); } return ret; } #endif #define F_CHANNEL_SUSPEND_RESUME_UNSERVICEABLE_CH BIT(0) #define F_CHANNEL_SUSPEND_RESUME_INVALID_TSGID BIT(1) #define F_CHANNEL_SUSPEND_RESUME_CH_WRK_CMPL_CNCL_SYNC BIT(2) #define F_CHANNEL_SUSPEND_RESUME_CHS_LAST BIT(3) static const char *f_channel_suspend_resume[] = { "suspend_resume_unserviceable_channels", "invalid_tsgid", "work_completion_cancel_sync", }; static int stub_fifo_preempt_tsg(struct gk20a *g, struct nvgpu_tsg *tsg) { stub[0].tsgid = tsg->tsgid; return 0; } static int stub_fifo_preempt_channel(struct gk20a *g, struct nvgpu_channel *ch) { stub[0].chid = ch->chid; return -1; } static void stub_channel_work_completion_cancel_sync(struct nvgpu_channel *ch) { } int test_channel_suspend_resume_serviceable_chs(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg = NULL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; bool err; u32 orig_ch_tsgid; u32 branches = 0U; u32 prune = F_CHANNEL_SUSPEND_RESUME_UNSERVICEABLE_CH | F_CHANNEL_SUSPEND_RESUME_INVALID_TSGID | F_CHANNEL_SUSPEND_RESUME_CH_WRK_CMPL_CNCL_SYNC; int ret = UNIT_FAIL; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); g->ops.fifo.preempt_tsg = stub_fifo_preempt_tsg; g->ops.fifo.preempt_channel = stub_fifo_preempt_channel; orig_ch_tsgid = ch->tsgid; for (branches = 0U; branches < F_CHANNEL_SUSPEND_RESUME_CHS_LAST; branches++) { if (subtest_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_suspend_resume)); continue; } subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_suspend_resume)); if (branches & F_CHANNEL_SUSPEND_RESUME_UNSERVICEABLE_CH) { nvgpu_channel_set_unserviceable(ch); } else { ch->unserviceable = false; } g->os_channel.work_completion_cancel_sync = branches & F_CHANNEL_SUSPEND_RESUME_CH_WRK_CMPL_CNCL_SYNC ? stub_channel_work_completion_cancel_sync : NULL; ch->tsgid = branches & F_CHANNEL_SUSPEND_RESUME_INVALID_TSGID ? NVGPU_INVALID_TSG_ID : orig_ch_tsgid; err = nvgpu_channel_suspend_all_serviceable_ch(g); unit_assert(err == 0, goto done); err = nvgpu_channel_resume_all_serviceable_ch(g); if (branches & F_CHANNEL_SUSPEND_RESUME_INVALID_TSGID) { unit_assert(stub[0].chid == ch->chid, goto done); } else if (branches & F_CHANNEL_SUSPEND_RESUME_UNSERVICEABLE_CH) { unit_assert(err == 0, goto done); } else { unit_assert(stub[0].tsgid == ch->tsgid, goto done); } } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_suspend_resume)); } if (ch != NULL) { nvgpu_tsg_force_unbind_channel(tsg, ch); nvgpu_channel_close(ch); } if (tsg != NULL) { nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); } return ret; } #define F_CHANNEL_DEBUG_DUMP_INFOS_ALLOC_FAIL BIT(0) #define F_CHANNEL_DEBUG_DUMP_INFO_ALLOC_FAIL BIT(1) #define F_CHANNEL_DEBUG_DUMP_LAST BIT(2) static const char *f_channel_debug_dump[] = { "infos_alloc_fail", "info_alloc_fail", }; static void stub_channel_read_state(struct gk20a *g, struct nvgpu_channel *ch, struct nvgpu_channel_hw_state *state) { stub[0].chid = ch->chid; } static void stub_ramfc_capture_ram_dump(struct gk20a *g, struct nvgpu_channel *ch, struct nvgpu_channel_dump_info *info) { stub[1].chid = ch->chid; } int test_channel_debug_dump(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg = NULL; struct gpu_ops gops = g->ops; struct nvgpu_posix_fault_inj *kmem_fi; struct nvgpu_debug_context o = { .fn = NULL }; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; bool err; u32 branches = 0U; u32 fail = F_CHANNEL_DEBUG_DUMP_INFOS_ALLOC_FAIL | F_CHANNEL_DEBUG_DUMP_INFO_ALLOC_FAIL; u32 prune = fail; int ret = UNIT_FAIL; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); kmem_fi = nvgpu_kmem_get_fault_injection(); g->ops.channel.read_state = stub_channel_read_state; g->ops.ramfc.capture_ram_dump = stub_ramfc_capture_ram_dump; for (branches = 0U; branches < F_CHANNEL_DEBUG_DUMP_LAST; branches++) { if (subtest_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_debug_dump)); continue; } subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_debug_dump)); if (branches & F_CHANNEL_DEBUG_DUMP_INFOS_ALLOC_FAIL) { nvgpu_posix_enable_fault_injection(kmem_fi, true, 0); } if (branches & F_CHANNEL_DEBUG_DUMP_INFO_ALLOC_FAIL) { nvgpu_posix_enable_fault_injection(kmem_fi, true, 1); } nvgpu_channel_debug_dump_all(g, &o); if (branches & fail) { nvgpu_posix_enable_fault_injection(kmem_fi, false, 0); } else { unit_assert(stub[0].chid == ch->chid, goto done); unit_assert(stub[1].chid == ch->chid, goto done); } } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_debug_dump)); } if (ch != NULL) { nvgpu_tsg_force_unbind_channel(tsg, ch); nvgpu_channel_close(ch); } if (tsg != NULL) { nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); } g->ops = gops; return ret; } #define F_CHANNEL_SEMAPHORRE_WAKEUP_DETERMINISTIC_CH BIT(0) #define F_CHANNEL_SEMAPHORRE_WAKEUP_COND_BROADCAST_FAIL BIT(1) #define F_CHANNEL_SEMAPHORRE_WAKEUP_CH_NOT_BOUND BIT(2) #define F_CHANNEL_SEMAPHORRE_WAKEUP_LAST BIT(3) static const char *f_channel_semaphore_wakeup[] = { "deterministic_channel", "condition_broadcast_fail", "channel_not_bound", }; static u32 global_count; static int stub_mm_fb_flush(struct gk20a *g) { stub[0].count = global_count++; return 0; } int test_channel_semaphore_wakeup(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg = NULL; struct nvgpu_mem pdb_mem; struct mm_gk20a mm; struct vm_gk20a vm; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; bool err; u32 branches = 0U; u32 prune = F_CHANNEL_SEMAPHORRE_WAKEUP_CH_NOT_BOUND; int ret = UNIT_FAIL; struct nvgpu_setup_bind_args bind_args; global_count = 0; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); memset(&mm, 0, sizeof(mm)); memset(&vm, 0, sizeof(vm)); mm.g = g; vm.mm = &mm; ch->vm = &vm; err = nvgpu_dma_alloc(g, NVGPU_CPU_PAGE_SIZE, &pdb_mem); unit_assert(err == 0, goto done); vm.pdb.mem = &pdb_mem; g->ops.gr.intr.flush_channel_tlb = stub_gr_intr_flush_channel_tlb; g->ops.mm.cache.l2_flush = stub_mm_l2_flush; /* bug 2621189 */ g->os_channel.alloc_usermode_buffers = stub_os_channel_alloc_usermode_buffers; g->ops.runlist.update = stub_runlist_update; g->ops.mm.cache.fb_flush = stub_mm_fb_flush; memset(&bind_args, 0, sizeof(bind_args)); bind_args.num_gpfifo_entries = 32; bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_SUPPORT_DETERMINISTIC; bind_args.flags |= NVGPU_SETUP_BIND_FLAGS_USERMODE_SUPPORT; err = nvgpu_channel_setup_bind(ch, &bind_args); unit_assert(err == 0, goto done); unit_assert(nvgpu_atomic_read(&ch->bound) == true, goto done); for (branches = 0U; branches < F_CHANNEL_SEMAPHORRE_WAKEUP_LAST; branches++) { if (subtest_pruned(branches, prune)) { unit_verbose(m, "%s branches=%s (pruned)\n", __func__, branches_str(branches, f_channel_semaphore_wakeup)); continue; } subtest_setup(branches); unit_verbose(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_semaphore_wakeup)); #ifdef CONFIG_NVGPU_DETERMINISTIC_CHANNELS if (branches & F_CHANNEL_SEMAPHORRE_WAKEUP_DETERMINISTIC_CH) { ch->deterministic = true; } #endif ch->semaphore_wq.initialized = branches & F_CHANNEL_SEMAPHORRE_WAKEUP_COND_BROADCAST_FAIL ? false : true; if (branches & F_CHANNEL_SEMAPHORRE_WAKEUP_CH_NOT_BOUND) { nvgpu_atomic_set(&ch->bound, false); } else { nvgpu_atomic_set(&ch->bound, true); } nvgpu_channel_semaphore_wakeup(g, false); unit_assert(stub[0].count == (global_count - 1U), goto done); #ifdef CONFIG_NVGPU_DETERMINISTIC_CHANNELS ch->deterministic = false; #endif } ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s branches=%s\n", __func__, branches_str(branches, f_channel_semaphore_wakeup)); } if (ch != NULL) { nvgpu_channel_close(ch); } if (tsg != NULL) { nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release); } return ret; } int test_channel_from_invalid_id(struct unit_module *m, struct gk20a *g, void *args) { struct nvgpu_channel *ch = NULL; int ret = UNIT_FAIL; ch = nvgpu_channel_from_id(g, NVGPU_INVALID_CHANNEL_ID); unit_assert(ch == NULL, goto done); ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s failed\n", __func__); } return ret; } int test_channel_put_warn(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_fifo *f = &g->fifo; int ret = UNIT_FAIL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); unit_assert(f->num_channels > 0U, goto done); /* condition broadcast fail */ ch->ref_count_dec_wq.initialized = false; nvgpu_atomic_set(&ch->ref_count, 2); ch->referenceable = true; nvgpu_channel_put(ch); /* * Note: channel ref_count value is 1 now * This function call will reduce count to 0 */ nvgpu_channel_put(ch); ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s failed\n", __func__); } if (ch != NULL) { nvgpu_atomic_set(&ch->ref_count, 1); nvgpu_channel_close(ch); } return ret; } int test_ch_referenceable_cleanup(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_fifo *f = &g->fifo; int err = 0; int ret = UNIT_FAIL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); unit_assert(f->num_channels > 0U, goto done); nvgpu_channel_cleanup_sw(g); unit_assert(f->channel == NULL, goto done); /* Reset environment variables */ err = nvgpu_channel_setup_sw(g); unit_assert(err == 0, goto done); ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s failed\n", __func__); } return ret; } int test_channel_abort_cleanup(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel *ch = NULL; struct nvgpu_tsg *tsg; struct mm_gk20a mm; struct vm_gk20a vm; int err = 0; int ret = UNIT_FAIL; u32 runlist_id = NVGPU_INVALID_RUNLIST_ID; bool privileged = false; tsg = nvgpu_tsg_open(g, getpid()); unit_assert(tsg != NULL, goto done); g->ops.gr.intr.flush_channel_tlb = stub_gr_intr_flush_channel_tlb; ch = nvgpu_channel_open_new(g, runlist_id, privileged, getpid(), getpid()); unit_assert(ch != NULL, goto done); ch->usermode_submit_enabled = true; /* Channel requires to be as_bound */ memset(&mm, 0, sizeof(mm)); memset(&vm, 0, sizeof(vm)); mm.g = g; vm.mm = &mm; ch->vm = &vm; nvgpu_ref_init(&vm.ref); nvgpu_ref_get(&vm.ref); err = nvgpu_tsg_bind_channel(tsg, ch); unit_assert(err == 0, goto done); err = nvgpu_tsg_force_unbind_channel(tsg, ch); unit_assert(err == 0, goto done); nvgpu_channel_close(ch); ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s failed\n", __func__); } return ret; } static void stub_mm_init_inst_block(struct nvgpu_mem *inst_block, struct vm_gk20a *vm, u32 big_page_size) { stub[0].count = big_page_size; } int test_nvgpu_channel_commit_va(struct unit_module *m, struct gk20a *g, void *vargs) { struct gpu_ops gops = g->ops; struct nvgpu_channel ch; struct vm_gk20a vm; int ret = UNIT_FAIL; memset(&vm, 0, sizeof(vm)); ch.g = g; ch.vm = &vm; g->ops.mm.init_inst_block = stub_mm_init_inst_block; vm.gmmu_page_sizes[GMMU_PAGE_SIZE_BIG] = nvgpu_safe_cast_u64_to_u32(SZ_1K); nvgpu_channel_commit_va(&ch); unit_assert(stub[0].count == SZ_1K, goto done); vm.gmmu_page_sizes[GMMU_PAGE_SIZE_BIG] = 0; ret = UNIT_SUCCESS; done: if (ret != UNIT_SUCCESS) { unit_err(m, "%s failed\n", __func__); } g->ops = gops; return ret; } int test_nvgpu_get_gpfifo_entry_size(struct unit_module *m, struct gk20a *g, void *vargs) { if (nvgpu_get_gpfifo_entry_size() != 8U) { unit_return_fail(m, "posix gpfifo entry size is non-zero\n"); } return UNIT_SUCCESS; } int test_trace_write_pushbuffers(struct unit_module *m, struct gk20a *g, void *vargs) { struct nvgpu_channel ch; #ifndef CONFIG_DEBUG_FS trace_write_pushbuffers(&ch, 1U); #endif return UNIT_SUCCESS; } struct unit_module_test nvgpu_channel_tests[] = { UNIT_TEST(setup_sw, test_channel_setup_sw, &unit_ctx, 0), UNIT_TEST(init_support, test_fifo_init_support, &unit_ctx, 0), UNIT_TEST(open, test_channel_open, &unit_ctx, 0), UNIT_TEST(close, test_channel_close, &unit_ctx, 0), UNIT_TEST(setup_bind, test_channel_setup_bind, &unit_ctx, 0), UNIT_TEST(alloc_inst, test_channel_alloc_inst, &unit_ctx, 0), UNIT_TEST(from_inst, test_channel_from_inst, &unit_ctx, 0), UNIT_TEST(enable_disable_tsg, test_channel_enable_disable_tsg, &unit_ctx, 0), UNIT_TEST(ch_abort, test_channel_abort, &unit_ctx, 0), UNIT_TEST(mark_error, test_channel_mark_error, &unit_ctx, 0), UNIT_TEST(sw_quiesce, test_channel_sw_quiesce, &unit_ctx, 0), #ifdef CONFIG_NVGPU_DETERMINISTIC_CHANNELS UNIT_TEST(idle_unidle, test_channel_deterministic_idle_unidle, &unit_ctx, 0), #endif UNIT_TEST(suspend_resume, test_channel_suspend_resume_serviceable_chs, &unit_ctx, 0), UNIT_TEST(debug_dump, test_channel_debug_dump, &unit_ctx, 0), UNIT_TEST(semaphore_wakeup, test_channel_semaphore_wakeup, &unit_ctx, 0), UNIT_TEST(channel_from_invalid_id, test_channel_from_invalid_id, &unit_ctx, 0), UNIT_TEST(channel_put_warn, test_channel_put_warn, &unit_ctx, 0), UNIT_TEST(referenceable_cleanup, test_ch_referenceable_cleanup, &unit_ctx, 0), UNIT_TEST(abort_cleanup, test_channel_abort_cleanup, &unit_ctx, 0), UNIT_TEST(channel_commit_va, test_nvgpu_channel_commit_va, &unit_ctx, 2), UNIT_TEST(get_gpfifo_entry_size, test_nvgpu_get_gpfifo_entry_size, &unit_ctx, 0), UNIT_TEST(trace_write_pushbuffers, test_trace_write_pushbuffers, &unit_ctx, 0), UNIT_TEST(remove_support, test_fifo_remove_support, &unit_ctx, 0), }; UNIT_MODULE(nvgpu_channel, nvgpu_channel_tests, UNIT_PRIO_NVGPU_TEST);