Files
linux-nvgpu/userspace/units/fifo/channel/nvgpu-channel.c
Debarshi Dutta 200777b854 gpu: nvgpu: bvec for channel and tsg
Below changes are added.

1) Added checks in
    nvgpu_channel_from_id__func, nvgpu_tsg_check_and_get_from_id
2) Added BVEC tests for
    nvgpu_channel_open_new, nvgpu_channel_from_id,
    nvgpu_tsg_check_and_get_from_id, nvgpu_tsg_set_error_notifier
3) Added common function get_random_u32.

Jira NVGPU-6905

Change-Id: I374d6f5503dc05e3224213d772a1752d82cbdc91
Signed-off-by: Debarshi Dutta <ddutta@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2548304
(cherry picked from commit 39b2529b3e96cfd3cbd3bb020f32ee2cca0ea363)
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2554021
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Reviewed-by: svc_kernel_abi <svc_kernel_abi@nvidia.com>
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
Reviewed-by: Vaibhav Kachore <vkachore@nvidia.com>
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
GVS: Gerrit_Virtual_Submit
2021-07-07 12:25:50 -07:00

2155 lines
57 KiB
C

/*
* 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 <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <unit/io.h>
#include <unit/unit.h>
#include <unit/utils.h>
#include <nvgpu/channel.h>
#include <nvgpu/channel_sync.h>
#include <nvgpu/dma.h>
#include <nvgpu/engines.h>
#include <nvgpu/tsg.h>
#include <nvgpu/gk20a.h>
#include <nvgpu/runlist.h>
#include <nvgpu/debug.h>
#include <nvgpu/thread.h>
#include <nvgpu/channel_user_syncpt.h>
#include <nvgpu/posix/posix-fault-injection.h>
#include <nvgpu/posix/posix-nvhost.h>
#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;
}
static int test_channel_open_bvec(struct unit_module *m,
struct gk20a *g, void *vargs, bool priviledged)
{
struct nvgpu_channel *ch = NULL;
int ret = UNIT_FAIL;
u32 gr_runlist_id = nvgpu_engine_get_gr_runlist_id(g);
u32 valid_runlist_ids[][2] = {{0, 1}};
u32 invalid_runlist_ids[][2] = {{2, U32_MAX}};
u32 runlist_id, runlist_id_range;
u32 (*working_list)[2];
/*
* i is to loop through valid and invalid cases
* j is to loop through different ranges within ith case
* states is for min, max and median
*/
u32 i, j, states;
const char *string_cases[] = {"Valid", "Invalid"};
const char *string_states[] = {"Min", "Max", "Mid"};
u32 runlist_range_difference;
/* loop through valid and invalid cases */
for (i = 0; i < 2; i++) {
/* select appropriate iteration size */
runlist_id_range = (i == 0) ? ARRAY_SIZE(valid_runlist_ids) : ARRAY_SIZE(invalid_runlist_ids);
/* select correct working list */
working_list = (i == 0) ? valid_runlist_ids : invalid_runlist_ids;
for (j = 0; j < runlist_id_range; j++) {
for (states = 0; states < 3; states++) {
/* check for min runlist id */
if (states == 0)
runlist_id = working_list[j][0];
else if (states == 1) {
/* check for max valid runlist id */
runlist_id = working_list[j][1];
} else {
runlist_range_difference = working_list[j][1] - working_list[j][0];
/* Check for random runlist id in range */
if (runlist_range_difference > 1)
runlist_id = get_random_u32(working_list[j][0] + 1, working_list[j][1] - 1);
else
continue;
}
unit_info(m, "BVEC testing for nvgpu_channel_open_new with runlist id = 0x%08x(%s range [0x%08x - 0x%08x] %s)\n", runlist_id, string_cases[i], working_list[j][0], working_list[j][1], string_states[states]);
ch = nvgpu_channel_open_new(g, runlist_id, priviledged, getpid(), getpid());
if (i == 0)
unit_assert(ch != NULL && ch->runlist->id == runlist_id, goto done);
else
unit_assert(ch != NULL && ch->runlist->id == gr_runlist_id, goto done);
/* Clearing for success cases */
if (ch != NULL) {
nvgpu_channel_close(ch);
ch = NULL;
}
}
}
}
ret = UNIT_SUCCESS;
done:
if (ret != UNIT_SUCCESS) {
unit_err(m, "%s failed\n", __func__);
if (ch != NULL) {
nvgpu_channel_close(ch);
}
}
return ret;
}
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);
/* add to head to increase visibility of timing-related bugs */
nvgpu_list_add(&next_ch->free_chs, &f->free_chs);
f->used_channels -= 1U;
} 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;
err = test_channel_open_bvec(m, g, vargs, privileged);
unit_assert(err == 0, goto done);
}
}
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_nvgpu_channel_from_id_bvec(struct unit_module *m,
struct gk20a *g, void *args)
{
struct nvgpu_channel *ch = NULL;
int ret = UNIT_FAIL;
/* One channel is already opened by default */
int num_channels_to_open = g->fifo.num_channels;
u32 valid_chids[][2] = {{0, g->fifo.num_channels - 1}};
u32 invalid_chids[][2] = {{g->fifo.num_channels, U32_MAX}};
u32 (*working_list)[2];
u32 chid, chid_ranges;
/*
* i is to loop through valid and invalid cases
* j is to loop through different ranges within ith case
* states is for min, max and median
*/
u32 i, j, states;
int c;
const char *string_cases[] = {"Valid", "Invalid"};
const char *string_states[] = {"Min", "Max", "Mid"};
u32 chid_range_difference;
struct nvgpu_channel **ch_list = (struct nvgpu_channel **)calloc(sizeof(struct nvgpu_channel *), num_channels_to_open);
for (c = 0; c < num_channels_to_open; c++) {
ch_list[c] = nvgpu_channel_open_new(g, -1, false, getpid(), getpid());
if (ch_list[c] == NULL) {
unit_err(m, "Unable to create channels\n");
goto done;
}
}
/* loop through valid and invalid cases */
for (i = 0; i < 2; i++) {
/* select appropriate iteration size */
chid_ranges = (i == 0) ? ARRAY_SIZE(valid_chids) : ARRAY_SIZE(invalid_chids);
/* select correct working list */
working_list = (i == 0) ? valid_chids : invalid_chids;
for (j = 0; j < chid_ranges; j++) {
for (states = 0; states < 3; states++) {
/* check for min chid */
if (states == 0)
chid = working_list[j][0];
else if (states == 1) {
/* check for max valid chid */
chid = working_list[j][1];
} else {
chid_range_difference = working_list[j][1] - working_list[j][0];
/* Check for random chid in range */
if (chid_range_difference > 1)
chid = get_random_u32(working_list[j][0] + 1, working_list[j][1] - 1);
else
continue;
}
unit_info(m, "BVEC testing for nvgpu_channel_from_id with chid = 0x%08x(%s range [0x%08x - 0x%08x] %s)\n", chid, string_cases[i], working_list[j][0], working_list[j][1], string_states[states]);
ch = nvgpu_channel_from_id(g, chid);
if (i == 0)
unit_assert(ch != NULL, goto done);
else
unit_assert(ch == NULL, goto done);
/* Clearing for success cases */
if (ch != NULL) {
nvgpu_channel_put(ch);
ch = NULL;
}
}
}
}
ret = UNIT_SUCCESS;
done:
if (ret != UNIT_SUCCESS) {
unit_err(m, "%s failed\n", __func__);
}
while (--c >= 0 ) {
nvgpu_channel_close(ch_list[c]);
}
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(nvgpu_channel_from_chid_bvec, test_nvgpu_channel_from_id_bvec, &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);