mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-24 02:22:34 +03:00
Refactor user managed syncpoints out of the channel sync infrastructure that deals with jobs submitted via the kernel api. The user syncpt only needs to expose the id and gpu address of the reserved syncpoint. None of the rest (fences, priv cmdbufs) is needed for that, so it hasn't been ideal to couple with the user-allocated syncpts. With user syncpts now provided by channel_user_syncpt, remove the user_managed flag from the kernel sync api. This allows moving all the kernel submit sync code to be conditionally compiled in only when needed, and separates the user sync functionality in a more clear way from the rest with a minimal API. [this is squashed with commit 5111caea601a (gpu: nvgpu: guard user syncpt with nvhost config) from https://git-master.nvidia.com/r/c/linux-nvgpu/+/2325009] Jira NVGPU-4548 Change-Id: I99259fc9cbd30bbd478ed86acffcce12768502d3 Signed-off-by: Konsta Hölttä <kholtta@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2321768 (cherry picked from commit 1095ad353f5f1cf7ca180d0701bc02a607404f5e) Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2319629 Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
1988 lines
52 KiB
C
1988 lines
52 KiB
C
/*
|
|
* Copyright (c) 2019-2020, 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 <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;
|
|
}
|
|
|
|
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_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_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, PAGE_SIZE, &ch->usermode_userd);
|
|
if (err != 0) {
|
|
return err;
|
|
}
|
|
|
|
err = nvgpu_dma_alloc(g, 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_for_channel(struct gk20a *g, u32 runlist_id,
|
|
struct nvgpu_channel *ch, bool add, bool wait_for_finish)
|
|
{
|
|
stub[1].chid = ch->chid;
|
|
return 0;
|
|
}
|
|
|
|
static int stub_runlist_update_for_channel_ETIMEDOUT(struct gk20a *g,
|
|
u32 runlist_id, 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, 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_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_for_channel = branches &
|
|
F_CHANNEL_SETUP_BIND_USERMODE_UPDATE_RL_FAIL ?
|
|
stub_runlist_update_for_channel_ETIMEDOUT :
|
|
stub_runlist_update_for_channel;
|
|
|
|
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_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);
|
|
|
|
#ifndef CONFIG_NVGPU_RECOVERY
|
|
nvgpu_channel_sw_quiesce(g);
|
|
unit_assert(ch->unserviceable == true, goto done);
|
|
#endif
|
|
|
|
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, 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_for_channel = stub_runlist_update_for_channel;
|
|
|
|
(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_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_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, 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_for_channel = stub_runlist_update_for_channel;
|
|
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_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_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, 0),
|
|
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);
|