diff --git a/Makefile.umbrella.tmk b/Makefile.umbrella.tmk index 0fe0c40cf..a9b84f56d 100644 --- a/Makefile.umbrella.tmk +++ b/Makefile.umbrella.tmk @@ -1,6 +1,6 @@ ################################### tell Emacs this is a -*- makefile-gmake -*- # -# Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. +# 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"), @@ -51,6 +51,7 @@ NV_REPOSITORY_COMPONENTS += userspace/units/interface/bsearch NV_REPOSITORY_COMPONENTS += userspace/units/interface/lock NV_REPOSITORY_COMPONENTS += userspace/units/interface/atomic NV_REPOSITORY_COMPONENTS += userspace/units/interface/rbtree +NV_REPOSITORY_COMPONENTS += userspace/units/interface/worker NV_REPOSITORY_COMPONENTS += userspace/units/bus NV_REPOSITORY_COMPONENTS += userspace/units/pramin NV_REPOSITORY_COMPONENTS += userspace/units/priv_ring diff --git a/drivers/gpu/nvgpu/include/nvgpu/posix/posix-fault-injection.h b/drivers/gpu/nvgpu/include/nvgpu/posix/posix-fault-injection.h index 264b5b60b..035f1c3c0 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/posix/posix-fault-injection.h +++ b/drivers/gpu/nvgpu/include/nvgpu/posix/posix-fault-injection.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. + * 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"), @@ -37,6 +37,7 @@ struct nvgpu_posix_fault_inj { struct nvgpu_posix_fault_inj_container { /* nvgpu-core */ struct nvgpu_posix_fault_inj thread_fi; + struct nvgpu_posix_fault_inj thread_running_true_fi; struct nvgpu_posix_fault_inj cond_fi; struct nvgpu_posix_fault_inj fstat_op; struct nvgpu_posix_fault_inj fread_op; diff --git a/drivers/gpu/nvgpu/include/nvgpu/posix/thread.h b/drivers/gpu/nvgpu/include/nvgpu/posix/thread.h index bb2172089..ea36c4f0c 100644 --- a/drivers/gpu/nvgpu/include/nvgpu/posix/thread.h +++ b/drivers/gpu/nvgpu/include/nvgpu/posix/thread.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. + * 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"), @@ -106,6 +106,8 @@ struct nvgpu_thread { #ifdef NVGPU_UNITTEST_FAULT_INJECTION_ENABLEMENT struct nvgpu_posix_fault_inj *nvgpu_thread_get_fault_injection(void); +struct nvgpu_posix_fault_inj + *nvgpu_thread_running_true_get_fault_injection(void); #endif /** diff --git a/drivers/gpu/nvgpu/libnvgpu-drv_safe.export b/drivers/gpu/nvgpu/libnvgpu-drv_safe.export index 93aaab25b..57a060c85 100644 --- a/drivers/gpu/nvgpu/libnvgpu-drv_safe.export +++ b/drivers/gpu/nvgpu/libnvgpu-drv_safe.export @@ -686,6 +686,7 @@ nvgpu_thread_create_priority nvgpu_thread_get_fault_injection nvgpu_thread_is_running nvgpu_thread_join +nvgpu_thread_running_true_get_fault_injection nvgpu_thread_should_stop nvgpu_thread_stop nvgpu_thread_stop_graceful @@ -713,6 +714,11 @@ nvgpu_vm_unmap nvgpu_vmalloc_impl nvgpu_vzalloc_impl nvgpu_wait_for_deferred_interrupts +nvgpu_worker_deinit +nvgpu_worker_enqueue +nvgpu_worker_init +nvgpu_worker_init_name +nvgpu_worker_should_stop nvgpu_writel nvgpu_writel_check nvgpu_clear_bit diff --git a/drivers/gpu/nvgpu/os/posix/thread.c b/drivers/gpu/nvgpu/os/posix/thread.c index 74d5f7a5d..3c68fd237 100644 --- a/drivers/gpu/nvgpu/os/posix/thread.c +++ b/drivers/gpu/nvgpu/os/posix/thread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved. + * 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"), @@ -35,6 +35,14 @@ struct nvgpu_posix_fault_inj *nvgpu_thread_get_fault_injection(void) return &c->thread_fi; } +struct nvgpu_posix_fault_inj + *nvgpu_thread_running_true_get_fault_injection(void) +{ + struct nvgpu_posix_fault_inj_container *c = + nvgpu_posix_fault_injection_get_container(); + + return &c->thread_running_true_fi; +} #endif /** @@ -250,6 +258,12 @@ bool nvgpu_thread_should_stop(struct nvgpu_thread *thread) bool nvgpu_thread_is_running(struct nvgpu_thread *thread) { +#ifdef NVGPU_UNITTEST_FAULT_INJECTION_ENABLEMENT + if (nvgpu_posix_fault_injection_handle_call( + nvgpu_thread_running_true_get_fault_injection())) { + return true; + } +#endif return (nvgpu_atomic_read(&thread->running) == 1); } diff --git a/userspace/Makefile.sources b/userspace/Makefile.sources index 813c12531..6a6c24764 100644 --- a/userspace/Makefile.sources +++ b/userspace/Makefile.sources @@ -1,6 +1,6 @@ # -*- mode: makefile -*- # -# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# 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"), @@ -63,6 +63,7 @@ UNITS := \ $(UNIT_SRC)/interface/lock \ $(UNIT_SRC)/interface/atomic \ $(UNIT_SRC)/interface/rbtree \ + $(UNIT_SRC)/interface/worker \ $(UNIT_SRC)/mc \ $(UNIT_SRC)/mm/nvgpu_sgt \ $(UNIT_SRC)/mm/allocators/buddy_allocator \ diff --git a/userspace/required_tests.json b/userspace/required_tests.json index 31896c499..1fecb8a5d 100644 --- a/userspace/required_tests.json +++ b/userspace/required_tests.json @@ -4501,5 +4501,29 @@ "case": "ecc_free", "unit": "ecc", "test_level": 0 + }, + { + "test": "test_branches", + "case": "branches", + "unit": "worker", + "test_level": 0 + }, + { + "test": "test_deinit", + "case": "deinit", + "unit": "worker", + "test_level": 0 + }, + { + "test": "test_enqueue", + "case": "enqueue", + "unit": "worker", + "test_level": 0 + }, + { + "test": "test_init", + "case": "init", + "unit": "worker", + "test_level": 0 } ] diff --git a/userspace/units/interface/worker/Makefile b/userspace/units/interface/worker/Makefile new file mode 100644 index 000000000..90daadc71 --- /dev/null +++ b/userspace/units/interface/worker/Makefile @@ -0,0 +1,26 @@ +# 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. + +.SUFFIXES: + +OBJS = worker.o +MODULE = worker + +include ../../Makefile.units diff --git a/userspace/units/interface/worker/Makefile.interface.tmk b/userspace/units/interface/worker/Makefile.interface.tmk new file mode 100644 index 000000000..c2daba1cd --- /dev/null +++ b/userspace/units/interface/worker/Makefile.interface.tmk @@ -0,0 +1,23 @@ +################################### tell Emacs this is a -*- makefile-gmake -*- +# +# Copyright (c) 2019-2020, NVIDIA CORPORATION. All Rights Reserved. +# +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. +# +# tmake for SW Mobile component makefile +# +############################################################################### + +NVGPU_UNIT_NAME=worker + +include $(NV_COMPONENT_DIR)/../../Makefile.units.common.interface.tmk + +# Local Variables: +# indent-tabs-mode: t +# tab-width: 8 +# End: +# vi: set tabstop=8 noexpandtab: diff --git a/userspace/units/interface/worker/Makefile.tmk b/userspace/units/interface/worker/Makefile.tmk new file mode 100644 index 000000000..718997acd --- /dev/null +++ b/userspace/units/interface/worker/Makefile.tmk @@ -0,0 +1,24 @@ +################################### tell Emacs this is a -*- makefile-gmake -*- +# +# Copyright (c) 2019-2020, NVIDIA CORPORATION. All Rights Reserved. +# +# NVIDIA CORPORATION and its licensors retain all intellectual property +# and proprietary rights in and to this software, related documentation +# and any modifications thereto. Any use, reproduction, disclosure or +# distribution of this software and related documentation without an express +# license agreement from NVIDIA CORPORATION is strictly prohibited. +# +# tmake for SW Mobile component makefile +# +############################################################################### + +NVGPU_UNIT_NAME=worker +NVGPU_UNIT_SRCS=worker.c + +include $(NV_COMPONENT_DIR)/../../Makefile.units.common.tmk + +# Local Variables: +# indent-tabs-mode: t +# tab-width: 8 +# End: +# vi: set tabstop=8 noexpandtab: diff --git a/userspace/units/interface/worker/worker.c b/userspace/units/interface/worker/worker.c new file mode 100644 index 000000000..352077c7f --- /dev/null +++ b/userspace/units/interface/worker/worker.c @@ -0,0 +1,311 @@ +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "worker.h" + +/* + * nvgpu_worker_ops functions + */ +static nvgpu_atomic_t pre_process_count; +static void pre_process(struct nvgpu_worker *worker) +{ + nvgpu_atomic_inc(&pre_process_count); +} + +static bool force_early_exit = false; +static nvgpu_atomic_t wakeup_early_exit_count; +static bool wakeup_early_exit(struct nvgpu_worker *worker) +{ + nvgpu_atomic_inc(&wakeup_early_exit_count); + if (force_early_exit) { + return true; + } + + return nvgpu_worker_should_stop(worker); +} + +static nvgpu_atomic_t wakeup_post_process_val; +static void wakeup_post_process(struct nvgpu_worker *worker) +{ + nvgpu_atomic_inc(&wakeup_post_process_val); +} + +static bool stall_processing = false; +static nvgpu_atomic_t item_count; +static void wakeup_process_item(struct nvgpu_list_node *work_item) +{ + bool stall = stall_processing; + + nvgpu_atomic_inc(&item_count); + while (stall) { + nvgpu_udelay(5); + stall = stall_processing; + } +} + +static bool wakeup_condition_val = false; +static bool wakeup_condition(struct nvgpu_worker *worker) +{ + return wakeup_condition_val; +} + +static u32 wakeup_timeout_val = 0U; +static u32 wakeup_timeout(struct nvgpu_worker *worker) +{ + return wakeup_timeout_val; +} + +_Thread_local struct nvgpu_worker worker; +_Thread_local struct nvgpu_worker_ops worker_ops = { + /* pre_process is NULL for branch testing for NULL when thread starts. */ + .pre_process = NULL, + .wakeup_early_exit = wakeup_early_exit, + .wakeup_post_process = wakeup_post_process, + .wakeup_process_item = wakeup_process_item, + .wakeup_condition = wakeup_condition, + .wakeup_timeout = wakeup_timeout, +}; + +int test_init(struct unit_module *m, struct gk20a *g, void *args) +{ + int err; + struct nvgpu_posix_fault_inj *thread_fi = + nvgpu_thread_get_fault_injection(); + char tmp[sizeof(worker.thread_name)+10]; + + memset(tmp, 'g', sizeof(tmp) - 1); + tmp[sizeof(tmp) - 1] = '\0'; + + /* init with a long name to get branch coverage */ + nvgpu_worker_init_name(&worker, tmp, + "A long-named simulated unit test gpu"); + + /* init with a reasonable name */ + nvgpu_worker_init_name(&worker, "testworker", "gpu"); + + /* enable fault injection to create error starting thread for worker */ + nvgpu_posix_enable_fault_injection(thread_fi, true, 0); + err = nvgpu_worker_init(g, &worker, &worker_ops); + unit_assert(err != 0, return UNIT_FAIL); + nvgpu_posix_enable_fault_injection(thread_fi, false, 0); + + /* normal init */ + err = nvgpu_worker_init(g, &worker, &worker_ops); + unit_assert(err == 0, return UNIT_FAIL); + + /* init when already running */ + while (!nvgpu_thread_is_running(&worker.poll_task)) { + nvgpu_udelay(5); + } + err = nvgpu_worker_init(g, &worker, &worker_ops); + unit_assert(err == 0, return UNIT_FAIL); + + return UNIT_SUCCESS; +} + +int test_enqueue(struct unit_module *m, struct gk20a *g, void *args) +{ + int err; + const unsigned int num_work_items = 3U; + struct nvgpu_list_node work_items[num_work_items]; + unsigned int i; + + for (i = 0U; i < num_work_items; i++) { + nvgpu_init_list_node(&work_items[i]); + } + nvgpu_atomic_set(&item_count, 0); + + for (i = 0U; i < num_work_items; i++) { + err = nvgpu_worker_enqueue(&worker, &work_items[i]); + unit_assert(err == 0, return UNIT_FAIL); + } + /* wait until all items are processed */ + while ((u32)nvgpu_atomic_read(&item_count) < num_work_items) { + nvgpu_udelay(5); + } + + /* + * Test requeueing same item. To do this, we have to stall the worker + * in the processing loop so we can make sure the item isn't removed. + */ + stall_processing = true; + nvgpu_init_list_node(&work_items[0]); + err = nvgpu_worker_enqueue(&worker, &work_items[0]); + unit_assert(err == 0, return UNIT_FAIL); + while ((u32)nvgpu_atomic_read(&item_count) < (num_work_items + 1)) { + nvgpu_udelay(5); + } + err = nvgpu_worker_enqueue(&worker, &work_items[0]); + unit_assert(err == 0, return UNIT_FAIL); + err = nvgpu_worker_enqueue(&worker, &work_items[0]); + unit_assert(err != 0, return UNIT_FAIL); + stall_processing = false; + while ((u32)nvgpu_atomic_read(&item_count) < (num_work_items + 2)) { + nvgpu_udelay(5); + } + + return UNIT_SUCCESS; +} + +int test_branches(struct unit_module *m, struct gk20a *g, void *args) +{ + int err; + struct nvgpu_list_node work_item; + int last_item_count; + struct nvgpu_posix_fault_inj *thread_create_fi = + nvgpu_thread_get_fault_injection(); + struct nvgpu_posix_fault_inj *thread_running_fi = + nvgpu_thread_running_true_get_fault_injection(); + unsigned int i; + + /* + * make timeout value short to get those branches, but have to + * call enqueue to make it trigger + */ + wakeup_timeout_val = 1U; + nvgpu_atomic_set(&wakeup_post_process_val, 0); + nvgpu_init_list_node(&work_item); + err = nvgpu_worker_enqueue(&worker, &work_item); + unit_assert(err == 0, return UNIT_FAIL); + while (nvgpu_atomic_read(&wakeup_post_process_val) < 10) { + nvgpu_udelay(5); + } + wakeup_timeout_val = 0U; + + /* cover branches where these ops are NULL */ + worker_ops.wakeup_condition = NULL; + worker_ops.wakeup_timeout = NULL; + worker_ops.wakeup_early_exit = NULL; + worker_ops.wakeup_post_process = NULL; + /* do this twice to make sure each is given a chance */ + for (i = 0U; i < 2; i++) { + last_item_count = nvgpu_atomic_read(&item_count); + err = nvgpu_worker_enqueue(&worker, &work_item); + unit_assert(err == 0, return UNIT_FAIL); + while (last_item_count == nvgpu_atomic_read(&item_count)) { + nvgpu_udelay(5); + } + } + worker_ops.wakeup_condition = wakeup_condition; + worker_ops.wakeup_timeout = wakeup_timeout; + worker_ops.wakeup_early_exit = wakeup_early_exit; + worker_ops.wakeup_post_process = wakeup_post_process; + + /* cover branch for the wakeup_condition op */ + nvgpu_atomic_set(&wakeup_post_process_val, 0); + wakeup_condition_val = true; + last_item_count = nvgpu_atomic_read(&item_count); + err = nvgpu_worker_enqueue(&worker, &work_item); + unit_assert(err == 0, return UNIT_FAIL); + while (nvgpu_atomic_read(&wakeup_post_process_val) < 1) { + nvgpu_udelay(5); + } + wakeup_condition_val = false; + + /* + * Cover branches for failsafe checks for empty work. This shouldn't + * really happen, but there's logic to catch them just in case. So, we + * can't make it happen directly, so we send the cond directly. + */ + nvgpu_atomic_set(&wakeup_post_process_val, 0); + nvgpu_atomic_inc(&worker.put); + nvgpu_cond_signal_interruptible(&worker.wq); + while (nvgpu_atomic_read(&wakeup_post_process_val) < 1) { + nvgpu_udelay(5); + } + + /* Cover branch for early exit. This will exit the thread. */ + nvgpu_atomic_set(&wakeup_early_exit_count, 0); + force_early_exit = true; + nvgpu_init_list_node(&work_item); + err = nvgpu_worker_enqueue(&worker, &work_item); + unit_assert(err == 0, return UNIT_FAIL); + while (nvgpu_atomic_read(&wakeup_early_exit_count) < 1) { + nvgpu_udelay(5); + } + force_early_exit = false; + /* when the thread exists, we need sync some state */ + nvgpu_thread_stop(&worker.poll_task); + + /* + * While the thread is stopped, we can hit a branch in enqueue where + * starting the thread fails. + */ + nvgpu_init_list_node(&work_item); + nvgpu_posix_enable_fault_injection(thread_create_fi, true, 0); + if (!EXPECT_BUG(nvgpu_worker_enqueue(&worker, &work_item))) { + unit_return_fail(m, "should have failed to enqueue\n"); + } + nvgpu_posix_enable_fault_injection(thread_create_fi, false, 0); + + /* + * While the thread is stopped, we can hit a branch in the worker start + * function where the first check for thread running is false, then + * second check is true. + */ + nvgpu_init_list_node(&work_item); + nvgpu_posix_enable_fault_injection(thread_running_fi, true, 1); + err = nvgpu_worker_enqueue(&worker, &work_item); + unit_assert(err == 0, return UNIT_FAIL); + nvgpu_posix_enable_fault_injection(thread_running_fi, false, 0); + + /* Re-init the worker to start the thread for de-init testing. */ + worker_ops.pre_process = pre_process; + nvgpu_atomic_set(&pre_process_count, 0); + nvgpu_worker_init(g, &worker, &worker_ops); + unit_assert(err == 0, return UNIT_FAIL); + /* make sure thread has started */ + while (nvgpu_atomic_read(&pre_process_count) < 1) { + nvgpu_udelay(5); + } + + return UNIT_SUCCESS; +} + +int test_deinit(struct unit_module *m, struct gk20a *g, void *args) +{ + nvgpu_worker_deinit(&worker); + nvgpu_udelay(10); + + return UNIT_SUCCESS; +} + + +struct unit_module_test worker_tests[] = { + UNIT_TEST(init, test_init, NULL, 0), + UNIT_TEST(enqueue, test_enqueue, NULL, 0), + UNIT_TEST(branches, test_branches, NULL, 0), + UNIT_TEST(deinit, test_deinit, NULL, 0), +}; + +UNIT_MODULE(worker, worker_tests, UNIT_PRIO_NVGPU_TEST); \ No newline at end of file diff --git a/userspace/units/interface/worker/worker.h b/userspace/units/interface/worker/worker.h new file mode 100644 index 000000000..eeb9670f1 --- /dev/null +++ b/userspace/units/interface/worker/worker.h @@ -0,0 +1,169 @@ +/* + * 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. + */ + +#ifndef UNIT_WORKER_H +#define UNIT_WORKER_H + +struct gk20a; +struct unit_module; + +/** @addtogroup SWUTS-interface-worker + * @{ + * + * Software Unit Test Specification for worker unit + */ + +/** + * Test specification for: test_init + * + * Description: Verify functionality of worker init APIs. + * + * Test Type: Feature, Error guessing, Boundary values + * + * Targets: nvgpu_worker_init_name, nvgpu_worker_init + * + * Input: None + * + * Steps: + * - Case 1: + * - Call nvgpu_worker_init_name() with a long name to verify the API can + * handle strings longer than the worker struct supports. + * - Case 2: + * - Call nvgpu_worker_init_name() with a short name to get full line/branch + * coverage. + * - Case 3: + * - Enable fault injection for creating threads. + * - Call nvgpu_worker_init() and verify it returns an error. + * - Disable fault injection for creating threads. + * - Case 4: + * - Call nvgpu_worker_init() and verify it returns success. + * - Case 5: + * - Call nvgpu_worker_init() and verify it returns success to verify the API + * can handle being called after the worker is already initialized. + * + * Output: Returns PASS if expected result is met, FAIL otherwise. + */ +int test_init(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for: test_enqueue + * + * Description: Verify functionality of worker enqueue API. + * + * Test Type: Feature, Error guessing + * + * Targets: nvgpu_worker_enqueue + * + * Input: test_init shall have run. + * + * Steps: + * - Initialize work items. + * - Case 1: + * - Enqueue work items, verify success. + * - Wait until all work items have been processed. + * - Case 2: + * - Enqueue a work item. + * - Before the item is processed, enqueue it again and verify error is + * returned. + * + * Output: Returns PASS if expected result is met, FAIL otherwise. + */ +int test_enqueue(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for: test_branches + * + * Description: Test a variety of special cases and error checking in the + * worker enqueue API and worker thread. + * + * Test Type: Feature, Error injection, Error guessing + * + * Targets: nvgpu_worker_enqueue, nvgpu_worker_should_stop + * + * Input: test_init shall have run. + * + * Steps: + * - Case 1: Coverage for wait timeout. + * - Make timeout value for thread very short. + * - Enqueue a work item to trigger thread to break out of waiting state. + * - Wait until thread has executed processing at least 10 times. + * - Reset timeout value to maximum. + * - Case 2: Coverage for worker_ops being NULL. + * - Set worker_op function pointers to NULL to verify these conditions + * are correctly handled by the worker thread. + * - Enqueue 3 work items to ensure all the conditions are checked in the + * thread loop. + * - Restore original worker_ops. + * - Case 3: Coverage for wakeup_condition op returning true; + * - Setup wakeup_condition worker op to return true. + * - Enqueue a work item. + * - Wait until the the item has been processed. + * - Case 4: Coverage for unexpected empty work item list. + * - Increment the worker put value to appear there is work pending. + * - Wake the thread by signalling the condition. + * - Wait for the thread to iterate the loop. + * - Case 5: Coverage for the wakeup_early_exit op returning true. + * - Setup the wakeup_early_exit op to return true. + * - Enqueue a work item. + * - Wait for the thread to detect the early exit condition. + * - NOTE: This causes the worker thread to exit. + * - Case 6: Coverage for failure to start thread. + * - Enable fault injection for creating threads. + * - Enqueue a work item (which will try to restart the thread). + * - Verify error is returned. + * - Disable fault injection for creating threads. + * - Case 7: Coverage for starting a thread and state changes. + * - Enable fault injection for checking if thread is running to return true + * on second call. + * - Enqueue a work item (which will try to restart the thread). + * - Verify no error is returned. + * - Disable fault injection for checking if thread is running. + * - Re-init the worker to restart the thread properly. + * + * Output: Returns PASS if expected result is met, FAIL otherwise. + */ +int test_branches(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for: test_deinit + * + * Description: Test functionality of the deinit API. + * + * Test Type: Feature + * + * Targets: nvgpu_worker_deinit + * + * Input: test_init shall have run. + * + * Steps: + * - Call the nvgpu_worker_deinit() API. + * - Wait 10us to ensure it has time to stop the running thread. + * + * Output: Returns PASS if expected result is met, FAIL otherwise. + */ +int test_deinit(struct unit_module *m, struct gk20a *g, void *args); + +/** + * @} + */ + +#endif /* UNIT_WORKER_H */