mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-24 02:22:34 +03:00
test_tsg_format_gen() accessed test_args->timeslice when test_args has not been initialized. Change-Id: Ia72c5371c8634b7b1fe54c9aa20693edc47cb762 Signed-off-by: Terje Bergstrom <tbergstrom@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2318389 Reviewed-by: automaticguardword <automaticguardword@nvidia.com> Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com> Reviewed-by: svc-mobile-misra <svc-mobile-misra@nvidia.com> Reviewed-by: Thomas Fleury <tfleury@nvidia.com> Reviewed-by: Alex Waterman <alexw@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com> GVS: Gerrit_Virtual_Submit Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
1393 lines
37 KiB
C
1393 lines
37 KiB
C
/*
|
|
* Copyright (c) 2018-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/runlist.h>
|
|
#include <nvgpu/gk20a.h>
|
|
#include <nvgpu/posix/posix-fault-injection.h>
|
|
#include <nvgpu/posix/dma.h>
|
|
#include <nvgpu/io.h>
|
|
|
|
#include "hal/fifo/runlist_ram_gk20a.h"
|
|
#include "hal/fifo/tsg_gk20a.h"
|
|
#include "nvgpu/hw/gk20a/hw_ram_gk20a.h"
|
|
#include "nvgpu-runlist.h"
|
|
#include "nvgpu/hw/gk20a/hw_fifo_gk20a.h"
|
|
|
|
#define RL_MAX_TIMESLICE_TIMEOUT ram_rl_entry_timeslice_timeout_v(U32_MAX)
|
|
#define RL_MAX_TIMESLICE_SCALE ram_rl_entry_timeslice_scale_v(U32_MAX)
|
|
|
|
#include "../nvgpu-fifo-common.h"
|
|
|
|
#ifdef RUNLIST_UNIT_DEBUG
|
|
#define unit_verbose unit_info
|
|
#else
|
|
#define unit_verbose(unit, msg, ...) \
|
|
do { \
|
|
if (0) { \
|
|
unit_info(unit, msg, ##__VA_ARGS__); \
|
|
} \
|
|
} while (0)
|
|
#endif
|
|
|
|
struct runlist_unit_ctx {
|
|
u32 branches;
|
|
};
|
|
|
|
static struct runlist_unit_ctx unit_ctx;
|
|
|
|
#define MAX_STUB 4
|
|
|
|
struct stub_ctx {
|
|
const char *name;
|
|
u32 count;
|
|
u32 chid;
|
|
u32 tsgid;
|
|
};
|
|
|
|
struct stub_ctx stub[MAX_STUB];
|
|
|
|
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].name = "";
|
|
stub[i].count = 0;
|
|
stub[i].chid = NVGPU_INVALID_CHANNEL_ID;
|
|
stub[i].tsgid = NVGPU_INVALID_TSG_ID;
|
|
}
|
|
}
|
|
|
|
#define pruned test_fifo_subtest_pruned
|
|
#define branches_str test_fifo_flags_str
|
|
|
|
static u32 get_log2(u32 num)
|
|
{
|
|
u32 res = 0;
|
|
|
|
if (num == 0) {
|
|
return 0;
|
|
}
|
|
while (num > 0) {
|
|
res++;
|
|
num >>= 1;
|
|
}
|
|
return res - 1U;
|
|
}
|
|
|
|
#define RL_MAX_TIMESLICE_TIMEOUT ram_rl_entry_timeslice_timeout_v(U32_MAX)
|
|
#define RL_MAX_TIMESLICE_SCALE ram_rl_entry_timeslice_scale_v(U32_MAX)
|
|
|
|
/*
|
|
* This helper function mimics the non-FUSA gk20a_runlist_get_tsg_entry
|
|
* function that has a simpler logic than other chips but is sufficient for
|
|
* runlist test purposes.
|
|
*/
|
|
static void generic_runlist_get_tsg_entry(struct nvgpu_tsg *tsg,
|
|
u32 *runlist, u32 timeslice)
|
|
{
|
|
u32 timeout = timeslice;
|
|
u32 scale = 0U;
|
|
|
|
while (timeout > RL_MAX_TIMESLICE_TIMEOUT) {
|
|
timeout >>= 1U;
|
|
scale++;
|
|
}
|
|
|
|
if (scale > RL_MAX_TIMESLICE_SCALE) {
|
|
timeout = RL_MAX_TIMESLICE_TIMEOUT;
|
|
scale = RL_MAX_TIMESLICE_SCALE;
|
|
}
|
|
|
|
runlist[0] = ram_rl_entry_id_f(tsg->tsgid) |
|
|
ram_rl_entry_type_tsg_f() |
|
|
ram_rl_entry_tsg_length_f(tsg->num_active_channels) |
|
|
ram_rl_entry_timeslice_scale_f(scale) |
|
|
ram_rl_entry_timeslice_timeout_f(timeout);
|
|
runlist[1] = 0;
|
|
}
|
|
|
|
/*
|
|
* This helper function mimics the non-FUSA gk20a_runlist_get_ch_entry
|
|
* function that has a simpler logic than other chips but is sufficient for
|
|
* runlist test purposes.
|
|
*/
|
|
static void generic_runlist_get_ch_entry(struct nvgpu_channel *ch, u32 *runlist)
|
|
{
|
|
runlist[0] = ram_rl_entry_chid_f(ch->chid);
|
|
runlist[1] = 0;
|
|
}
|
|
|
|
static void setup_fifo(struct gk20a *g, unsigned long *tsg_map,
|
|
unsigned long *ch_map, struct nvgpu_tsg *tsgs,
|
|
struct nvgpu_channel *chs, unsigned int num_tsgs,
|
|
unsigned int num_channels,
|
|
struct nvgpu_runlist_info **runlists, u32 *rl_data,
|
|
bool interleave)
|
|
{
|
|
struct nvgpu_fifo *f = &g->fifo;
|
|
struct nvgpu_runlist_info *runlist = runlists[0];
|
|
|
|
/* we only use the runlist 0 here */
|
|
runlist->mem[0].aperture = APERTURE_SYSMEM;
|
|
runlist->mem[0].cpu_va = rl_data;
|
|
|
|
runlist->active_tsgs = tsg_map;
|
|
runlist->active_channels = ch_map;
|
|
|
|
g->fifo.g = g;
|
|
/* to debug, change this to (u64)-1 */
|
|
g->log_mask = 0;
|
|
|
|
/*
|
|
* set PTIMER src freq to its nominal frequency to avoid rounding
|
|
* errors when scaling timeslice.
|
|
*/
|
|
g->ptimer_src_freq = 31250000;
|
|
|
|
f->tsg = tsgs;
|
|
f->channel = chs;
|
|
f->num_channels = num_channels;
|
|
f->runlist_info = runlists;
|
|
|
|
/*
|
|
* For testing the runlist entry order format, these simpler dual-u32
|
|
* entries are enough. The logic is same across chips.
|
|
*/
|
|
f->runlist_entry_size = 2 * sizeof(u32);
|
|
g->ops.runlist.get_tsg_entry = generic_runlist_get_tsg_entry;
|
|
g->ops.runlist.get_ch_entry = generic_runlist_get_ch_entry;
|
|
g->ops.tsg.default_timeslice_us = nvgpu_tsg_default_timeslice_us;
|
|
|
|
g->runlist_interleave = interleave;
|
|
|
|
/* set bits in active_tsgs correspond to indices in f->tsg[...] */
|
|
nvgpu_bitmap_set(runlist->active_tsgs, 0, num_tsgs);
|
|
/* same; these are only used if a high enough tsg appears */
|
|
nvgpu_bitmap_set(runlist->active_channels, 0, num_channels);
|
|
}
|
|
|
|
static void setup_tsg(struct nvgpu_tsg *tsgs, struct nvgpu_channel *chs,
|
|
u32 i, u32 level)
|
|
{
|
|
struct nvgpu_tsg *tsg = &tsgs[i];
|
|
struct nvgpu_channel *ch = &chs[i];
|
|
|
|
tsg->tsgid = i;
|
|
nvgpu_init_list_node(&tsg->ch_list);
|
|
tsg->num_active_channels = 1;
|
|
tsg->interleave_level = level;
|
|
|
|
/* 1:1 mapping for simplicity */
|
|
ch->chid = i;
|
|
nvgpu_list_add_tail(&ch->ch_entry, &tsg->ch_list);
|
|
}
|
|
|
|
static void setup_tsg_multich(struct nvgpu_tsg *tsgs, struct nvgpu_channel *chs,
|
|
u32 i, u32 level, u32 ch_capacity, u32 ch_active)
|
|
{
|
|
struct nvgpu_tsg *tsg = &tsgs[i];
|
|
struct nvgpu_channel *ch = &chs[i + 1];
|
|
u32 c;
|
|
|
|
setup_tsg(tsgs, chs, i, level);
|
|
tsg->num_active_channels = ch_active;
|
|
|
|
/* bind the rest of the channels, onwards from the same id */
|
|
for (c = 1; c < ch_capacity; c++) {
|
|
ch->chid = i + c;
|
|
nvgpu_list_add_tail(&ch->ch_entry, &tsg->ch_list);
|
|
ch++;
|
|
}
|
|
}
|
|
|
|
static int run_format_test(struct unit_module *m, struct nvgpu_fifo *f,
|
|
struct nvgpu_tsg *tsg, struct nvgpu_channel *chs,
|
|
u32 prio, u32 n_ch, u32 *rl_data,
|
|
u32 *expect_header, u32 *expect_channel)
|
|
{
|
|
u32 n;
|
|
|
|
setup_tsg_multich(tsg, chs, 0, prio, 5, n_ch);
|
|
|
|
/* entry capacity: tsg header and some channels */
|
|
n = nvgpu_runlist_construct_locked(f, f->runlist_info[0], 0, 1 + n_ch);
|
|
|
|
if (n != 1 + n_ch) {
|
|
return -1;
|
|
}
|
|
if (memcmp(rl_data, expect_header, 2 * sizeof(u32)) != 0) {
|
|
unit_err(m, "rl_data[0]=%08x", rl_data[0]);
|
|
unit_err(m, "rl_data[1]=%08x", rl_data[1]);
|
|
unit_err(m, "expect_header[0]=%08x", expect_header[0]);
|
|
unit_err(m, "expect_header[1]=%08x", expect_header[1]);
|
|
|
|
unit_err(m, "tsg header mismatch\n");
|
|
return -1;
|
|
}
|
|
if (memcmp(rl_data + 2, expect_channel, 2 * n_ch * sizeof(u32)) != 0) {
|
|
unit_err(m, "channel data mismatch\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct tsg_fmt_test_args {
|
|
u32 channels;
|
|
u32 chs_bitmap;
|
|
u32 level;
|
|
u32 timeslice;
|
|
u32 expect_header[2];
|
|
u32 expect_channel[10];
|
|
} tsg_fmt_tests[] = {
|
|
/* priority 0, one channel */
|
|
{ 1, 0x01, 0, 0, { 0x0600e000, 0 }, { 0, 0 } },
|
|
/* priority 1, two channels */
|
|
{ 2, 0x03, 1, 0, { 0x0a00e000, 0 }, { 0, 0, 1, 0 } },
|
|
/* priority 2, five channels */
|
|
{ 5, 0x1f, 2, 0, { 0x1600e000, 0 }, { 0, 0, 1, 0, 2, 0, 3, 0, 4, 0 } },
|
|
/* priority 0, one channel, nondefault timeslice timeout */
|
|
{ 1, 0x01, 0, 0xaa<<3, { 0x06a8e000, 0 }, { 0, 0 } },
|
|
/* priority 0, three channels with two inactives in the middle */
|
|
{ 3, 0x01 | 0x04 | 0x10, 0, 0, { 0x0e00e000, 0 }, { 0, 0, 2, 0, 4, 0 } },
|
|
};
|
|
|
|
/*
|
|
* Check that inserting a single tsg of any level with a number of channels
|
|
* works as expected.
|
|
*/
|
|
#define F_RUNLIST_FORMAT_FAIL_ENTRIES0 BIT(0)
|
|
#define F_RUNLIST_FORMAT_CH2 BIT(1)
|
|
#define F_RUNLIST_FORMAT_CH5 BIT(2)
|
|
#define F_RUNLIST_FORMAT_CH1_TIMESLICE BIT(3)
|
|
#define F_RUNLIST_FORMAT_CH3_INACTIVE2 BIT(4)
|
|
#define F_RUNLIST_FORMAT_FAIL_ENTRY1 BIT(5)
|
|
#define F_RUNLIST_FORMAT_LAST BIT(6)
|
|
|
|
static const char *f_runlist_format[] = {
|
|
"priority_0_one_channel",
|
|
"fail_zero_entries",
|
|
"priority_1_two_channels",
|
|
"priority_2_five_channels",
|
|
"one_channel_nondefault_timeslice_timeout",
|
|
"three_channels_with_two_inactives_in_the_middle",
|
|
"fail_one_entry",
|
|
};
|
|
|
|
int test_tsg_format_gen(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
struct nvgpu_fifo *f = &g->fifo;
|
|
struct nvgpu_runlist_info runlist;
|
|
struct nvgpu_runlist_info *runlists = &runlist;
|
|
unsigned long active_tsgs_map = 0;
|
|
unsigned long active_chs_map = 0;
|
|
struct nvgpu_tsg tsgs[1] = {{0}};
|
|
struct nvgpu_channel chs[5] = {{0}};
|
|
/* header + at most five channels */
|
|
const u32 entries_in_list_max = 1 + 5;
|
|
u32 rl_data[2 * entries_in_list_max];
|
|
struct tsg_fmt_test_args *test_args;
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
int err = 0;
|
|
u32 fail = F_RUNLIST_FORMAT_FAIL_ENTRIES0 |
|
|
F_RUNLIST_FORMAT_FAIL_ENTRY1;
|
|
u32 prune = F_RUNLIST_FORMAT_CH2 | F_RUNLIST_FORMAT_CH5 |
|
|
F_RUNLIST_FORMAT_CH1_TIMESLICE |
|
|
F_RUNLIST_FORMAT_CH3_INACTIVE2 |
|
|
fail;
|
|
|
|
setup_fifo(g, &active_tsgs_map, &active_chs_map, tsgs, chs, 1, 5,
|
|
&runlists, rl_data, false);
|
|
|
|
for (branches = 0U; branches < F_RUNLIST_FORMAT_LAST;
|
|
branches++) {
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
if (branches & fail) {
|
|
test_args = &tsg_fmt_tests[0];
|
|
} else {
|
|
test_args = &tsg_fmt_tests[get_log2(branches)];
|
|
}
|
|
|
|
active_chs_map = test_args->chs_bitmap;
|
|
|
|
if (test_args->timeslice == 0U) {
|
|
tsgs[0].timeslice_us =
|
|
g->ops.tsg.default_timeslice_us(g);
|
|
} else {
|
|
tsgs[0].timeslice_us = test_args->timeslice;
|
|
}
|
|
|
|
if (branches & fail) {
|
|
err = run_format_test(m, f, &tsgs[0], chs,
|
|
test_args->level, get_log2(branches)-1, rl_data,
|
|
test_args->expect_header,
|
|
test_args->expect_channel);
|
|
unit_assert(err != 0, goto done);
|
|
} else {
|
|
err = run_format_test(m, f, &tsgs[0], chs,
|
|
test_args->level, test_args->channels, rl_data,
|
|
test_args->expect_header,
|
|
test_args->expect_channel);
|
|
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_runlist_format));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* compare 1:1 tsg-channel entries against expectations */
|
|
static int check_same_simple_tsgs(struct unit_module *m, u32 *expected,
|
|
u32 *actual, u32 n_entries)
|
|
{
|
|
u32 i;
|
|
|
|
for (i = 0; i < n_entries; i++) {
|
|
u32 want = expected[i];
|
|
/*
|
|
* 2 u32s per each entry, 2 entries per tsg, and the second
|
|
* entry of each tsg has the channel id at the first u32.
|
|
*/
|
|
u32 entry_off = 2 * i + 1;
|
|
u32 got = actual[2 * entry_off];
|
|
if (want != got) {
|
|
unit_return_fail(m,
|
|
"wrong entry at %u: expected %u, got %u\n",
|
|
i, want, got);
|
|
}
|
|
}
|
|
return UNIT_SUCCESS;
|
|
}
|
|
|
|
/* Common stuff for all tests below to reduce boilerplate */
|
|
static int test_common_gen(struct unit_module *m, struct gk20a *g,
|
|
bool interleave, u32 sizelimit,
|
|
u32 *levels, u32 levels_count,
|
|
u32 *expected, u32 expect_count)
|
|
{
|
|
struct nvgpu_fifo *f = &g->fifo;
|
|
struct nvgpu_runlist_info runlist;
|
|
struct nvgpu_runlist_info *runlists = &runlist;
|
|
unsigned long active_tsgs_map = 0;
|
|
unsigned long active_chs_map = 0;
|
|
struct nvgpu_tsg tsgs[6] = {{0}};
|
|
struct nvgpu_channel chs[6] = {{0}};
|
|
u32 tsgs_in_list = expect_count;
|
|
/* a tsg header and a channel entry for each */
|
|
const u32 entries_in_list = 2 * tsgs_in_list;
|
|
/* one entry is two u32s in these tests */
|
|
u32 rl_data[2 * entries_in_list];
|
|
u32 n;
|
|
u32 i = 0;
|
|
|
|
setup_fifo(g, &active_tsgs_map, &active_chs_map, tsgs, chs,
|
|
levels_count, 6, &runlists, rl_data, interleave);
|
|
|
|
for (i = 0; i < levels_count; i++) {
|
|
setup_tsg(tsgs, chs, i, levels[i]);
|
|
}
|
|
|
|
n = nvgpu_runlist_construct_locked(f, &runlist, 0,
|
|
sizelimit != 0U ? sizelimit : entries_in_list);
|
|
|
|
if (sizelimit != 0 && sizelimit != entries_in_list) {
|
|
/* Less than enough size is always a negative test here */
|
|
if (n != 0xffffffffU) {
|
|
unit_info(m,
|
|
"limit %d, expected failure, got %u\n",
|
|
sizelimit, n);
|
|
return UNIT_FAIL;
|
|
}
|
|
/*
|
|
* Compare what we got; should be good up until the limit. For
|
|
* odd limit we miss the last u32 but it's better than nothing.
|
|
*/
|
|
return check_same_simple_tsgs(m, expected, rl_data,
|
|
sizelimit / 2);
|
|
}
|
|
|
|
if (n != entries_in_list) {
|
|
unit_info(m, "expected %u entries, got %u\n",
|
|
entries_in_list, n);
|
|
return UNIT_FAIL;
|
|
}
|
|
|
|
return check_same_simple_tsgs(m, expected, rl_data, tsgs_in_list);
|
|
}
|
|
|
|
#define F_RUNLIST_FLAT_GEN_OVERSIZE_TINY BIT(0)
|
|
#define F_RUNLIST_FLAT_GEN_OVERSIZE_SINGLE BIT(1)
|
|
#define F_RUNLIST_FLAT_GEN_OVERSIZE_ONEHALF BIT(2)
|
|
#define F_RUNLIST_FLAT_GEN_OVERSIZE_TWO BIT(3)
|
|
#define F_RUNLIST_FLAT_GEN_OVERSIZE_END BIT(4)
|
|
#define F_RUNLIST_FLAT_GEN_LAST BIT(5)
|
|
|
|
static const char *f_runlist_flat[] = {
|
|
"runlist_flat_oversize_tiny",
|
|
"runlist_flat_oversize_single",
|
|
"runlist_flat_oversize_onehalf",
|
|
"runlist_flat_oversize_two",
|
|
"runlist_flat_oversize_end",
|
|
};
|
|
|
|
int test_flat_gen(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
u32 levels[] = {
|
|
/* Some random-ish order of priority levels */
|
|
0, 1, 2, 1, 0, 2,
|
|
};
|
|
u32 expected[] = {
|
|
/* High (2) indices first, then medium (1), then low (0). */
|
|
2, 5, 1, 3, 0, 4,
|
|
};
|
|
u32 sizelimits[] = {
|
|
0, 1, 2, 3, 4, 11,
|
|
};
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
u32 prune = F_RUNLIST_FLAT_GEN_OVERSIZE_TINY |
|
|
F_RUNLIST_FLAT_GEN_OVERSIZE_SINGLE |
|
|
F_RUNLIST_FLAT_GEN_OVERSIZE_ONEHALF |
|
|
F_RUNLIST_FLAT_GEN_OVERSIZE_TWO |
|
|
F_RUNLIST_FLAT_GEN_OVERSIZE_END;
|
|
|
|
for (branches = 0U; branches < F_RUNLIST_FLAT_GEN_LAST; branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
ret = test_common_gen(m, g, false,
|
|
sizelimits[get_log2(branches)],
|
|
levels, ARRAY_SIZE(levels),
|
|
expected, ARRAY_SIZE(expected));
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_flat));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define F_RUNLIST_INTERLEAVE_SINGLE_L0 BIT(0)
|
|
#define F_RUNLIST_INTERLEAVE_SINGLE_L1 BIT(1)
|
|
#define F_RUNLIST_INTERLEAVE_SINGLE_L2 BIT(2)
|
|
#define F_RUNLIST_INTERLEAVE_SINGLE_LAST BIT(3)
|
|
|
|
static const char *f_runlist_interleave_single[] = {
|
|
"only_L0_items",
|
|
"only_L1_items",
|
|
"only_L2_items",
|
|
};
|
|
|
|
static struct interleave_single_args {
|
|
u32 n_levels;
|
|
u32 levels[2];
|
|
u32 n_expected;
|
|
u32 expected[2];
|
|
} interleave_single_tests[] = {
|
|
/* Only l0 items */
|
|
{ 2, { 0, 0 }, 2, { 0, 1 } },
|
|
/* Only l1 items */
|
|
{ 2, { 1, 1 }, 2, { 0, 1 } },
|
|
/* Only l2 items */
|
|
{ 2, { 2, 2 }, 2, { 0, 1 } },
|
|
};
|
|
|
|
int test_interleave_single(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
struct interleave_single_args *single_args;
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
u32 prune = F_RUNLIST_INTERLEAVE_SINGLE_L0 |
|
|
F_RUNLIST_INTERLEAVE_SINGLE_L1 |
|
|
F_RUNLIST_INTERLEAVE_SINGLE_L2;
|
|
|
|
for (branches = 0U; branches < F_RUNLIST_INTERLEAVE_SINGLE_LAST;
|
|
branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
single_args = &interleave_single_tests[get_log2(branches)];
|
|
|
|
ret = test_common_gen(m, g, true, 2 * single_args->n_expected,
|
|
single_args->levels, single_args->n_levels,
|
|
single_args->expected, single_args->n_expected);
|
|
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_interleave_single));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define F_RUNLIST_INTERLEAVE_DUAL_L0_L1 BIT(0)
|
|
#define F_RUNLIST_INTERLEAVE_DUAL_L1_L2 BIT(1)
|
|
#define F_RUNLIST_INTERLEAVE_DUAL_L0_L2 BIT(2)
|
|
#define F_RUNLIST_INTERLEAVE_DUAL_L0_L2_FAIL BIT(3)
|
|
#define F_RUNLIST_INTERLEAVE_DUAL_LAST BIT(4)
|
|
|
|
static const char *f_runlist_interleave_dual[] = {
|
|
"only_L0_and_L1_items",
|
|
"only_L1_and_L2_items",
|
|
"only_L0_and_L2_items",
|
|
"L0_and_L2_items_2_entries",
|
|
};
|
|
|
|
static struct interleave_dual_args {
|
|
u32 n_levels;
|
|
u32 levels[4];
|
|
u32 n_expected;
|
|
u32 expected[6];
|
|
} interleave_dual_tests[] = {
|
|
/* Only low and medium priority items. */
|
|
{ 4, { 0, 0, 1, 1 }, 6, { 2, 3, 0, 2, 3, 1 } },
|
|
/* Only medium and high priority items. */
|
|
{ 4, { 1, 1, 2, 2 }, 6, { 2, 3, 0, 2, 3, 1 } },
|
|
/* Only low and high priority items. */
|
|
{ 4, { 0, 0, 2, 2 }, 6, { 2, 3, 0, 2, 3, 1 } },
|
|
/* Only low and high priority items. */
|
|
{ 4, { 0, 0, 2, 2 }, 2, { 2, 3, 0, 2, 3, 1 } },
|
|
};
|
|
|
|
int test_interleave_dual(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
struct interleave_dual_args *dual_args;
|
|
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
int err;
|
|
u32 fail = F_RUNLIST_INTERLEAVE_DUAL_L0_L2_FAIL;
|
|
u32 prune = F_RUNLIST_INTERLEAVE_DUAL_L0_L1 |
|
|
F_RUNLIST_INTERLEAVE_DUAL_L1_L2 |
|
|
F_RUNLIST_INTERLEAVE_DUAL_L0_L2 |
|
|
fail;
|
|
|
|
for (branches = 1U; branches < F_RUNLIST_INTERLEAVE_DUAL_LAST;
|
|
branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
dual_args = &interleave_dual_tests[get_log2(branches)];
|
|
|
|
err = test_common_gen(m, g, true, 2 * dual_args->n_expected,
|
|
dual_args->levels, dual_args->n_levels,
|
|
dual_args->expected, dual_args->n_expected);
|
|
|
|
if (branches & fail) {
|
|
unit_assert(err != UNIT_SUCCESS, goto done);
|
|
} else {
|
|
unit_assert(err == UNIT_SUCCESS, goto done);
|
|
}
|
|
}
|
|
|
|
ret = UNIT_SUCCESS;
|
|
|
|
done:
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_interleave_dual));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct interleave_level_test_args {
|
|
u32 sizelimit;
|
|
} interleave_level_tests[] = {
|
|
/* All priority items. */
|
|
{ 0 },
|
|
/* Fail at level 2 immediately: space for just a tsg header,
|
|
* no ch entries.
|
|
*/
|
|
{ 1 },
|
|
/* Insert both l2 entries, then fail at l1 level. */
|
|
{ 2 * 2 },
|
|
/* Insert both l2 entries, one l1, and just one l2: fail at last l2. */
|
|
{ (2 + 1 + 1) * 2 },
|
|
/* Stop at exactly the first l2 entry in the first l1-l0 transition. */
|
|
{ (2 + 1 + 2 + 1) * 2 },
|
|
/* Stop at exactly the first l0 entry that doesn't fit. */
|
|
{ (2 + 1 + 2 + 1 + 2) * 2 },
|
|
};
|
|
|
|
#define F_RUNLIST_INTERLEAVE_LEVELS_ALL_PRIO BIT(0)
|
|
#define F_RUNLIST_INTERLEAVE_LEVELS_FAIL_L2 BIT(1)
|
|
#define F_RUNLIST_INTERLEAVE_LEVELS_FAIL_L1 BIT(2)
|
|
#define F_RUNLIST_INTERLEAVE_LEVELS_FIT BIT(3)
|
|
#define F_RUNLIST_INTERLEAVE_LEVELS_FAIL_L0 BIT(4)
|
|
#define F_RUNLIST_INTERLEAVE_LEVELS_LAST BIT(5)
|
|
|
|
static const char *f_runlist_interleave_levels[] = {
|
|
"interleaving",
|
|
"interleaving_oversize_tiny",
|
|
"interleaving_oversize_l2",
|
|
"interleaving_oversize_l2_l1_l2",
|
|
"interleaving_oversize_l2_l1_l2_l1",
|
|
"interleaving_oversize_l2_l1_l2_l1_l2",
|
|
};
|
|
|
|
int test_interleaving_levels(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
u32 sizelimit;
|
|
u32 l1 = 0, l2 = 1;
|
|
u32 m1 = 2, m2 = 3;
|
|
u32 h1 = 4, h2 = 5;
|
|
u32 levels[] = { 0, 0, 1, 1, 2, 2 };
|
|
u32 expected[] = {
|
|
/* Order of channel ids; partly used also for oversize tests */
|
|
h1, h2, m1, h1, h2, m2, h1, h2, l1,
|
|
h1, h2, m1, h1, h2, m2, h1, h2, l2,
|
|
};
|
|
|
|
u32 branches = 1U;
|
|
int ret = UNIT_FAIL;
|
|
u32 prune = F_RUNLIST_INTERLEAVE_LEVELS_ALL_PRIO |
|
|
F_RUNLIST_INTERLEAVE_LEVELS_FAIL_L2 |
|
|
F_RUNLIST_INTERLEAVE_LEVELS_FAIL_L1 |
|
|
F_RUNLIST_INTERLEAVE_LEVELS_FIT |
|
|
F_RUNLIST_INTERLEAVE_LEVELS_FAIL_L0;
|
|
|
|
for (branches = 0U; branches < F_RUNLIST_INTERLEAVE_LEVELS_LAST;
|
|
branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
sizelimit = interleave_level_tests[
|
|
get_log2(branches)].sizelimit;
|
|
|
|
ret = test_common_gen(m, g, true, sizelimit, levels,
|
|
ARRAY_SIZE(levels), expected, ARRAY_SIZE(expected));
|
|
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_interleave_levels));
|
|
break;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#define F_RUNLIST_INTERLEAVE_LEVEL_LOW BIT(0)
|
|
#define F_RUNLIST_INTERLEAVE_LEVEL_MEDIUM BIT(1)
|
|
#define F_RUNLIST_INTERLEAVE_LEVEL_HIGH BIT(2)
|
|
#define F_RUNLIST_INTERLEAVE_LEVEL_DEFAULT BIT(3)
|
|
#define F_RUNLIST_INTERLEAVE_LEVEL_LAST BIT(4)
|
|
|
|
static const char *f_runlist_interleave_level_name[] = {
|
|
"LOW",
|
|
"MEDIUM",
|
|
"HIGH",
|
|
"?"
|
|
};
|
|
|
|
int test_runlist_interleave_level_name(struct unit_module *m,
|
|
struct gk20a *g, void *args)
|
|
{
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
u32 fail = F_RUNLIST_INTERLEAVE_LEVEL_MEDIUM |
|
|
F_RUNLIST_INTERLEAVE_LEVEL_HIGH |
|
|
F_RUNLIST_INTERLEAVE_LEVEL_DEFAULT;
|
|
u32 prune = fail;
|
|
const char *interleave_level_name = NULL;
|
|
|
|
for (branches = 0U; branches < F_RUNLIST_INTERLEAVE_LEVEL_LAST;
|
|
branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
interleave_level_name =
|
|
nvgpu_runlist_interleave_level_name(get_log2(branches));
|
|
unit_assert(strcmp(interleave_level_name,
|
|
f_runlist_interleave_level_name[
|
|
get_log2(branches)]) == 0, goto done);
|
|
}
|
|
|
|
ret = UNIT_SUCCESS;
|
|
|
|
done:
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s failed\n", __func__);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void stub_runlist_write_state(struct gk20a *g, u32 runlists_mask,
|
|
u32 runlist_state)
|
|
{
|
|
stub[0].count = runlists_mask;
|
|
}
|
|
|
|
#define F_RUNLIST_SET_STATE_DISABLED BIT(0)
|
|
#define F_RUNLIST_SET_STATE_ENABLED BIT(1)
|
|
#define F_RUNLIST_SET_STATE_LAST BIT(2)
|
|
|
|
static const char *f_runlist_set_state[] = {
|
|
"set_state_disabled",
|
|
"set_state_enabled",
|
|
};
|
|
|
|
int test_runlist_set_state(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
struct gpu_ops gops = g->ops;
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
u32 fail = F_RUNLIST_SET_STATE_ENABLED | F_RUNLIST_SET_STATE_DISABLED;
|
|
u32 prune = fail;
|
|
|
|
g->ops.runlist.write_state = stub_runlist_write_state;
|
|
|
|
for (branches = 1U; branches < F_RUNLIST_SET_STATE_LAST; branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
if (branches & F_RUNLIST_SET_STATE_DISABLED) {
|
|
nvgpu_runlist_set_state(g, 0U, RUNLIST_DISABLED);
|
|
unit_assert(stub[0].count == 0U, goto done);
|
|
} else {
|
|
nvgpu_runlist_set_state(g, 1U, RUNLIST_ENABLED);
|
|
unit_assert(stub[0].count == 1U, goto done);
|
|
}
|
|
}
|
|
|
|
ret = UNIT_SUCCESS;
|
|
|
|
done:
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_set_state));
|
|
}
|
|
|
|
g->ops = gops;
|
|
return ret;
|
|
}
|
|
|
|
#define F_RUNLIST_LOCK_UNLOCK_ACTIVE_RUNLISTS_LAST BIT(0)
|
|
|
|
int test_runlist_lock_unlock_active_runlists(struct unit_module *m,
|
|
struct gk20a *g, void *args)
|
|
{
|
|
int err = 0;
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
u32 fail = 0U;
|
|
u32 prune = fail;
|
|
|
|
err = nvgpu_runlist_setup_sw(g);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
for (branches = 0U;
|
|
branches < F_RUNLIST_LOCK_UNLOCK_ACTIVE_RUNLISTS_LAST;
|
|
branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
nvgpu_runlist_lock_active_runlists(g);
|
|
nvgpu_runlist_unlock_active_runlists(g);
|
|
|
|
nvgpu_runlist_lock_active_runlists(g);
|
|
nvgpu_runlist_unlock_runlists(g, 3U);
|
|
|
|
nvgpu_runlist_unlock_runlists(g, 0U);
|
|
}
|
|
|
|
ret = UNIT_SUCCESS;
|
|
|
|
done:
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s failed\n", __func__);
|
|
}
|
|
|
|
nvgpu_runlist_cleanup_sw(g);
|
|
return ret;
|
|
}
|
|
|
|
#define F_RUNLIST_GET_MASK_ID_TYPE_KNOWN BIT(0)
|
|
#define F_RUNLIST_GET_MASK_ID_TYPE_TSG BIT(1)
|
|
#define F_RUNLIST_GET_MASK_ACT_ENG_BITMASK_NONZERO BIT(2)
|
|
#define F_RUNLIST_GET_MASK_PBDMA_BITMASK_NONZERO BIT(3)
|
|
#define F_RUNLIST_GET_MASK_LAST BIT(4)
|
|
|
|
static const char *f_runlist_get_mask[] = {
|
|
"ID_type_known",
|
|
"act_eng_bitmask_nonzero",
|
|
"pbdma_bitmask_nonzero",
|
|
};
|
|
|
|
int test_runlist_get_mask(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
unsigned int id_type = ID_TYPE_UNKNOWN;
|
|
u32 act_eng_bitmask = 0U;
|
|
u32 pbdma_bitmask = 0U;
|
|
u32 ret_mask = 0U;
|
|
int err = 0;
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
u32 fail = 0U;
|
|
u32 prune = fail;
|
|
|
|
err = nvgpu_runlist_setup_sw(g);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
for (branches = 0U; branches < F_RUNLIST_GET_MASK_LAST; branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%s (pruned)\n", __func__,
|
|
branches_str(branches, f_runlist_get_mask));
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_get_mask));
|
|
subtest_setup(branches);
|
|
|
|
id_type = (branches & F_RUNLIST_GET_MASK_ID_TYPE_KNOWN) ?
|
|
((branches & F_RUNLIST_GET_MASK_ID_TYPE_TSG) ?
|
|
ID_TYPE_TSG : ID_TYPE_CHANNEL) :
|
|
ID_TYPE_UNKNOWN;
|
|
|
|
act_eng_bitmask = (branches &
|
|
F_RUNLIST_GET_MASK_ACT_ENG_BITMASK_NONZERO) ?
|
|
1U : 0U;
|
|
|
|
pbdma_bitmask = (branches &
|
|
F_RUNLIST_GET_MASK_PBDMA_BITMASK_NONZERO) ?
|
|
1U : 0U;
|
|
|
|
ret_mask = nvgpu_runlist_get_runlists_mask(g, 0U, id_type,
|
|
act_eng_bitmask, pbdma_bitmask);
|
|
|
|
}
|
|
|
|
if (branches == 0U) {
|
|
unit_assert(ret_mask == 3U, goto done);
|
|
} else {
|
|
unit_assert(ret_mask == 1U, goto done);
|
|
}
|
|
|
|
ret = UNIT_SUCCESS;
|
|
|
|
done:
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_get_mask));
|
|
}
|
|
|
|
nvgpu_runlist_cleanup_sw(g);
|
|
return ret;
|
|
}
|
|
|
|
#define F_RUNLIST_SETUP_ALLOC_RUNLIST_INFO_FAIL BIT(0)
|
|
#define F_RUNLIST_SETUP_ALLOC_ACTIVE_RUNLIST_INFO_FAIL BIT(1)
|
|
#define F_RUNLIST_SETUP_ALLOC_ACTIVE_CHANNELS_FAIL BIT(2)
|
|
#define F_RUNLIST_SETUP_ALLOC_ACTIVE_TSGS_FAIL BIT(3)
|
|
#define F_RUNLIST_SETUP_ALLOC_DMA_FLAGS_SYS_FAIL BIT(4)
|
|
#define F_RUNLIST_SETUP_GPU_IS_VIRTUAL BIT(5)
|
|
#define F_RUNLIST_SETUP_LAST BIT(6)
|
|
|
|
static const char *f_runlist_setup[] = {
|
|
"alloc_runlist_info_fail",
|
|
"alloc_active_runlist_info_fail",
|
|
"alloc_active_channels_fail",
|
|
"alloc_active_tsgs_fail",
|
|
"alloc_dma_flags_sys_fail",
|
|
"GPU_is_virtual"
|
|
};
|
|
|
|
int test_runlist_setup_sw(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
struct nvgpu_posix_fault_inj *kmem_fi;
|
|
struct nvgpu_posix_fault_inj *dma_fi;
|
|
u32 branches = 0U;
|
|
int err = 0;
|
|
int ret = UNIT_FAIL;
|
|
u32 fail = F_RUNLIST_SETUP_ALLOC_RUNLIST_INFO_FAIL |
|
|
F_RUNLIST_SETUP_ALLOC_ACTIVE_RUNLIST_INFO_FAIL |
|
|
F_RUNLIST_SETUP_ALLOC_ACTIVE_CHANNELS_FAIL |
|
|
F_RUNLIST_SETUP_ALLOC_ACTIVE_TSGS_FAIL |
|
|
F_RUNLIST_SETUP_ALLOC_DMA_FLAGS_SYS_FAIL;
|
|
u32 prune = fail;
|
|
|
|
kmem_fi = nvgpu_kmem_get_fault_injection();
|
|
dma_fi = nvgpu_dma_alloc_get_fault_injection();
|
|
|
|
for (branches = 0U; branches < F_RUNLIST_SETUP_LAST; branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%s (pruned)\n", __func__,
|
|
branches_str(branches, f_runlist_setup));
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_setup));
|
|
subtest_setup(branches);
|
|
|
|
if ((branches >= F_RUNLIST_SETUP_ALLOC_RUNLIST_INFO_FAIL) &&
|
|
(branches <= F_RUNLIST_SETUP_ALLOC_ACTIVE_TSGS_FAIL)) {
|
|
nvgpu_posix_enable_fault_injection(kmem_fi,
|
|
branches & fail ? true : false,
|
|
get_log2(branches));
|
|
}
|
|
|
|
nvgpu_posix_enable_fault_injection(dma_fi,
|
|
branches & F_RUNLIST_SETUP_ALLOC_DMA_FLAGS_SYS_FAIL ?
|
|
true : false, 0);
|
|
|
|
if (branches & F_RUNLIST_SETUP_GPU_IS_VIRTUAL) {
|
|
g->is_virtual = true;
|
|
}
|
|
|
|
err = nvgpu_runlist_setup_sw(g);
|
|
|
|
g->is_virtual = false;
|
|
nvgpu_posix_enable_fault_injection(kmem_fi, false, 0);
|
|
nvgpu_posix_enable_fault_injection(dma_fi, false, 0);
|
|
|
|
if (branches & fail) {
|
|
unit_assert(err != 0, goto done);
|
|
} else {
|
|
unit_assert(err == 0, goto done);
|
|
nvgpu_runlist_cleanup_sw(g);
|
|
}
|
|
}
|
|
|
|
ret = UNIT_SUCCESS;
|
|
|
|
done:
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s branches=%s\n", __func__,
|
|
branches_str(branches, f_runlist_setup));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int stub_runlist_wait_pending_timedout(struct gk20a *g, u32 runlist_id)
|
|
{
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
static int stub_runlist_wait_pending_interrupted(struct gk20a *g,
|
|
u32 runlist_id)
|
|
{
|
|
return -EINTR;
|
|
}
|
|
|
|
static int stub_runlist_wait_pending_success(struct gk20a *g, u32 runlist_id)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void stub_runlist_hw_submit(struct gk20a *g, u32 runlist_id,
|
|
u32 count, u32 buffer_index)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#define F_RUNLIST_RELOAD_IDS_GPU_NULL BIT(0)
|
|
#define F_RUNLIST_RELOAD_IDS_NO_RUNLIST BIT(1)
|
|
#define F_RUNLIST_RELOAD_IDS_WAIT_TIMEOUT BIT(2)
|
|
#define F_RUNLIST_RELOAD_IDS_WAIT_INTERRUPT BIT(3)
|
|
#define F_RUNLIST_RELOAD_IDS_REMOVE_CHANNELS BIT(4)
|
|
#define F_RUNLIST_RELOAD_IDS_RESTORE_CHANNELS BIT(5)
|
|
#define F_RUNLIST_RELOAD_IDS_LAST BIT(6)
|
|
|
|
static const char *f_runlist_reload_ids[] = {
|
|
"null_gpu_pointer",
|
|
"no_runlist_selected",
|
|
"runlist_wait_pending_timeout",
|
|
"runlist_wait_pending_interrupted",
|
|
"remove_active_channels_from_runlist",
|
|
"restore_active_channels_from_runlist",
|
|
};
|
|
|
|
int test_runlist_reload_ids(struct unit_module *m, struct gk20a *g, void *args)
|
|
{
|
|
struct gpu_ops gops = g->ops;
|
|
bool add = false;
|
|
u32 runlist_ids = 0U;
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
int err = 0;
|
|
u32 fail = F_RUNLIST_RELOAD_IDS_GPU_NULL |
|
|
F_RUNLIST_RELOAD_IDS_WAIT_TIMEOUT |
|
|
F_RUNLIST_RELOAD_IDS_WAIT_INTERRUPT;
|
|
u32 prune = F_RUNLIST_RELOAD_IDS_NO_RUNLIST |
|
|
F_RUNLIST_RELOAD_IDS_REMOVE_CHANNELS |
|
|
F_RUNLIST_RELOAD_IDS_RESTORE_CHANNELS |
|
|
fail;
|
|
|
|
g->ops.runlist.hw_submit = stub_runlist_hw_submit;
|
|
err = nvgpu_runlist_setup_sw(g);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
for (branches = 1U; branches < F_RUNLIST_RELOAD_IDS_LAST;
|
|
branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
runlist_ids = (branches & F_RUNLIST_RELOAD_IDS_NO_RUNLIST) ?
|
|
0U : 1U;
|
|
add = (branches & F_RUNLIST_RELOAD_IDS_RESTORE_CHANNELS) ?
|
|
true : false;
|
|
|
|
if (branches & F_RUNLIST_RELOAD_IDS_WAIT_TIMEOUT) {
|
|
g->ops.runlist.wait_pending =
|
|
stub_runlist_wait_pending_timedout;
|
|
} else if (branches & F_RUNLIST_RELOAD_IDS_WAIT_INTERRUPT) {
|
|
g->ops.runlist.wait_pending =
|
|
stub_runlist_wait_pending_interrupted;
|
|
} else {
|
|
g->ops.runlist.wait_pending =
|
|
stub_runlist_wait_pending_success;
|
|
}
|
|
|
|
if (branches & F_RUNLIST_RELOAD_IDS_GPU_NULL) {
|
|
err = nvgpu_runlist_reload_ids(NULL, runlist_ids, add);
|
|
} else {
|
|
err = nvgpu_runlist_reload_ids(g, runlist_ids, add);
|
|
}
|
|
|
|
if (branches & fail) {
|
|
unit_assert(err != 0, goto done);
|
|
} else {
|
|
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_runlist_reload_ids));
|
|
}
|
|
|
|
g->ops = gops;
|
|
return ret;
|
|
}
|
|
|
|
static void stub_runlist_get_ch_entry(struct nvgpu_channel *ch, u32 *runlist)
|
|
{
|
|
}
|
|
|
|
#define F_RUNLIST_UPDATE_ADD BIT(0)
|
|
#define F_RUNLIST_UPDATE_CH_NULL BIT(1)
|
|
#define F_RUNLIST_UPDATE_CH_TSGID_INVALID BIT(2)
|
|
#define F_RUNLIST_UPDATE_ADD_AGAIN BIT(3)
|
|
#define F_RUNLIST_UPDATE_RECONSTRUCT_FAIL BIT(4)
|
|
#define F_RUNLIST_UPDATE_REMOVE_ALL_CHANNELS BIT(5)
|
|
#define F_RUNLIST_UPDATE_LAST BIT(6)
|
|
|
|
static const char *f_runlist_update[] = {
|
|
"add_ch",
|
|
"add_ch_again",
|
|
"update_null_ch",
|
|
"update_ch_with_invalid_tsgid",
|
|
"wait_for_finish",
|
|
"update_reconstruct_fail",
|
|
"remove_all_channels",
|
|
};
|
|
|
|
int test_runlist_update_locked(struct unit_module *m, struct gk20a *g,
|
|
void *args)
|
|
{
|
|
struct gpu_ops gops = g->ops;
|
|
struct nvgpu_channel *ch = NULL;
|
|
struct nvgpu_tsg *tsg = NULL;
|
|
u32 num_runlist_entries_orig = 0U;
|
|
u32 ch_tsgid_orig = 0U;
|
|
bool add = false;
|
|
u32 branches = 0U;
|
|
int ret = UNIT_FAIL;
|
|
int err = 0;
|
|
u32 fail = F_RUNLIST_UPDATE_RECONSTRUCT_FAIL;
|
|
u32 prune = F_RUNLIST_UPDATE_CH_TSGID_INVALID |
|
|
F_RUNLIST_UPDATE_ADD_AGAIN |
|
|
F_RUNLIST_UPDATE_REMOVE_ALL_CHANNELS | fail;
|
|
|
|
g->ops.runlist.hw_submit = stub_runlist_hw_submit;
|
|
g->ops.runlist.get_ch_entry = stub_runlist_get_ch_entry;
|
|
num_runlist_entries_orig = g->fifo.num_runlist_entries;
|
|
g->ptimer_src_freq = 31250000;
|
|
|
|
tsg = nvgpu_tsg_open(g, getpid());
|
|
unit_assert(tsg != NULL, goto done);
|
|
|
|
ch = nvgpu_channel_open_new(g, NVGPU_INVALID_RUNLIST_ID, false,
|
|
getpid(), getpid());
|
|
unit_assert(ch != NULL, goto done);
|
|
|
|
err = nvgpu_tsg_bind_channel(tsg, ch);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
ch_tsgid_orig = ch->tsgid;
|
|
|
|
for (branches = 0U; branches < F_RUNLIST_UPDATE_LAST;
|
|
branches++) {
|
|
|
|
if (pruned(branches, prune)) {
|
|
unit_verbose(m, "%s branches=%u (pruned)\n", __func__,
|
|
branches);
|
|
continue;
|
|
}
|
|
unit_verbose(m, "%s branches=%u\n", __func__, branches);
|
|
subtest_setup(branches);
|
|
|
|
ch->tsgid = (branches & F_RUNLIST_UPDATE_CH_TSGID_INVALID) ?
|
|
NVGPU_INVALID_TSG_ID : ch_tsgid_orig;
|
|
|
|
add = branches & F_RUNLIST_UPDATE_ADD ? true : false;
|
|
|
|
if (branches & F_RUNLIST_UPDATE_ADD_AGAIN) {
|
|
err = nvgpu_runlist_update_locked(g,
|
|
0U, ch, true, false);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
add = true;
|
|
}
|
|
|
|
if (branches & F_RUNLIST_UPDATE_RECONSTRUCT_FAIL) {
|
|
g->fifo.num_runlist_entries = 0U;
|
|
/* force null ch and add = true to execute fail path */
|
|
branches |= F_RUNLIST_UPDATE_CH_NULL;
|
|
add = true;
|
|
} else {
|
|
g->fifo.num_runlist_entries = num_runlist_entries_orig;
|
|
}
|
|
|
|
if (branches & F_RUNLIST_UPDATE_REMOVE_ALL_CHANNELS) {
|
|
/* Add additional channel to cover more branches */
|
|
struct nvgpu_channel *chA = NULL;
|
|
|
|
chA = nvgpu_channel_open_new(g,
|
|
NVGPU_INVALID_RUNLIST_ID, false,
|
|
getpid(), getpid());
|
|
unit_assert(chA != NULL, goto done);
|
|
|
|
err = nvgpu_tsg_bind_channel(tsg, chA);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
err = nvgpu_runlist_update_locked(g,
|
|
0U, chA, true, false);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
err = nvgpu_runlist_update_locked(g,
|
|
0U, chA, false, false);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
err = nvgpu_tsg_unbind_channel(tsg, chA);
|
|
if (err != 0) {
|
|
unit_err(m, "Cannot unbind channel A\n");
|
|
}
|
|
if (chA != NULL) {
|
|
nvgpu_channel_close(chA);
|
|
}
|
|
}
|
|
|
|
if (branches & F_RUNLIST_UPDATE_CH_NULL) {
|
|
err = nvgpu_runlist_update_locked(g,
|
|
0U, NULL, add, false);
|
|
} else {
|
|
err = nvgpu_runlist_update_locked(g,
|
|
0U, ch, add, false);
|
|
}
|
|
|
|
if (branches & fail) {
|
|
unit_assert(err != 0, goto done);
|
|
} else {
|
|
unit_assert(err == 0, goto done);
|
|
}
|
|
ch->tsgid = ch_tsgid_orig;
|
|
}
|
|
|
|
ret = UNIT_SUCCESS;
|
|
|
|
done:
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s branches %u=%s\n", __func__, branches,
|
|
branches_str(branches, f_runlist_update));
|
|
}
|
|
|
|
err = nvgpu_tsg_unbind_channel(tsg, ch);
|
|
if (err != 0) {
|
|
unit_err(m, "Cannot unbind channel\n");
|
|
}
|
|
if (ch != NULL) {
|
|
nvgpu_channel_close(ch);
|
|
}
|
|
if (tsg != NULL) {
|
|
nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release);
|
|
}
|
|
g->ptimer_src_freq = 0;
|
|
g->ops = gops;
|
|
return ret;
|
|
}
|
|
|
|
int test_runlist_update_for_channel(struct unit_module *m, struct gk20a *g,
|
|
void *args)
|
|
{
|
|
struct gpu_ops gops = g->ops;
|
|
struct nvgpu_channel *ch = NULL;
|
|
struct nvgpu_tsg *tsg = NULL;
|
|
int ret = UNIT_FAIL;
|
|
int err = 0;
|
|
|
|
g->ops.runlist.hw_submit = stub_runlist_hw_submit;
|
|
g->ops.runlist.get_ch_entry = stub_runlist_get_ch_entry;
|
|
g->ptimer_src_freq = 31250000;
|
|
|
|
tsg = nvgpu_tsg_open(g, getpid());
|
|
unit_assert(tsg != NULL, goto done);
|
|
|
|
ch = nvgpu_channel_open_new(g, NVGPU_INVALID_RUNLIST_ID, false,
|
|
getpid(), getpid());
|
|
unit_assert(ch != NULL, goto done);
|
|
|
|
err = nvgpu_tsg_bind_channel(tsg, ch);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
err = nvgpu_runlist_update_for_channel(g, 0U, ch, false, false);
|
|
unit_assert(err == 0, goto done);
|
|
|
|
ret = UNIT_SUCCESS;
|
|
|
|
done:
|
|
if (ret != UNIT_SUCCESS) {
|
|
unit_err(m, "%s failed\n", __func__);
|
|
}
|
|
|
|
err = nvgpu_tsg_unbind_channel(tsg, ch);
|
|
if (err != 0) {
|
|
unit_err(m, "Cannot unbind channel\n");
|
|
}
|
|
if (ch != NULL) {
|
|
nvgpu_channel_close(ch);
|
|
}
|
|
if (tsg != NULL) {
|
|
nvgpu_ref_put(&tsg->refcount, nvgpu_tsg_release);
|
|
}
|
|
g->ptimer_src_freq = 0;
|
|
g->ops = gops;
|
|
return ret;
|
|
}
|
|
|
|
struct unit_module_test nvgpu_runlist_tests[] = {
|
|
|
|
UNIT_TEST(init_support, test_fifo_init_support, &unit_ctx, 0),
|
|
UNIT_TEST(setup_sw, test_runlist_setup_sw, NULL, 0),
|
|
UNIT_TEST(get_mask, test_runlist_get_mask, NULL, 0),
|
|
UNIT_TEST(lock_unlock_active_runlists, test_runlist_lock_unlock_active_runlists, NULL, 0),
|
|
UNIT_TEST(set_state, test_runlist_set_state, NULL, 0),
|
|
UNIT_TEST(interleave_level_name, test_runlist_interleave_level_name, NULL, 0),
|
|
UNIT_TEST(reload_ids, test_runlist_reload_ids, NULL, 0),
|
|
UNIT_TEST(runlist_update, test_runlist_update_locked, NULL, 0),
|
|
UNIT_TEST(update_for_channel, test_runlist_update_for_channel, NULL, 0),
|
|
UNIT_TEST(remove_support, test_fifo_remove_support, &unit_ctx, 0),
|
|
UNIT_TEST(tsg_format_flat, test_tsg_format_gen, NULL, 0),
|
|
UNIT_TEST(flat, test_flat_gen, NULL, 0),
|
|
UNIT_TEST(interleave_single, test_interleave_single, NULL, 0),
|
|
UNIT_TEST(interleave_dual, test_interleave_dual, NULL, 0),
|
|
UNIT_TEST(interleave_level, test_interleaving_levels, NULL, 0),
|
|
};
|
|
|
|
UNIT_MODULE(nvgpu_runlist, nvgpu_runlist_tests, UNIT_PRIO_NVGPU_TEST);
|