diff --git a/userspace/units/posix/cond/posix-cond.c b/userspace/units/posix/cond/posix-cond.c index 8ec051084..d34e49343 100644 --- a/userspace/units/posix/cond/posix-cond.c +++ b/userspace/units/posix/cond/posix-cond.c @@ -1,5 +1,5 @@ /* - * 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"), @@ -29,6 +29,7 @@ #include #include +#include #include "posix-cond.h" @@ -338,6 +339,72 @@ int test_cond_init_destroy(struct unit_module *m, return UNIT_SUCCESS; } +int test_cond_bug(struct unit_module *m, + struct gk20a *g, void *args) +{ + int ret; + + memset(&test_cond, 0, sizeof(struct nvgpu_cond)); + + if(!EXPECT_BUG(nvgpu_cond_signal(NULL))) { + unit_return_fail(m, "BUG not called for NULL cond var\n"); + } + + if(!EXPECT_BUG(nvgpu_cond_signal(&test_cond))) { + unit_return_fail(m, + "BUG not called for uninitialized cond var\n"); + } + + if(!EXPECT_BUG(nvgpu_cond_signal_interruptible(NULL))) { + unit_return_fail(m, "BUG not called for NULL cond var\n"); + } + + if(!EXPECT_BUG(nvgpu_cond_signal_interruptible(&test_cond))) { + unit_return_fail(m, + "BUG not called for uninitialized cond var\n"); + } + + if(!EXPECT_BUG(nvgpu_cond_destroy(NULL))) { + unit_return_fail(m, "BUG not called for NULL cond var\n"); + } + + if(!EXPECT_BUG(nvgpu_cond_signal_locked(NULL))) { + unit_return_fail(m, "BUG not called for NULL cond var\n"); + } + + if(!EXPECT_BUG(nvgpu_cond_signal_locked(&test_cond))) { + unit_return_fail(m, + "BUG not called for uninitialized cond var\n"); + } + + ret = nvgpu_cond_broadcast(NULL); + if (ret != -EINVAL) { + unit_return_fail(m, "NULL cond var not handled\n"); + } + + ret = nvgpu_cond_broadcast(&test_cond); + if (ret != -EINVAL) { + unit_return_fail(m, "Uninitialized cond var not handled\n"); + } + + ret = nvgpu_cond_broadcast_interruptible(NULL); + if (ret != -EINVAL) { + unit_return_fail(m, "NULL cond var not handled\n"); + } + + ret = nvgpu_cond_broadcast_interruptible(&test_cond); + if (ret != -EINVAL) { + unit_return_fail(m, "Uninitialized cond var not handled\n"); + } + + ret = nvgpu_cond_broadcast_locked(&test_cond); + if (ret != -EINVAL) { + unit_return_fail(m, "Uninitialized cond var not handled\n"); + } + + return UNIT_SUCCESS; +} + int test_cond_signal(struct unit_module *m, struct gk20a *g, void *args) { @@ -423,8 +490,51 @@ int test_cond_signal(struct unit_module *m, return UNIT_SUCCESS; } +int test_cond_timeout(struct unit_module *m, + struct gk20a *g, void *args) +{ + int ret; + unsigned int timeout; + signed long ts_before, ts_after, delay; + + memset(&test_cond, 0, sizeof(struct nvgpu_cond)); + timeout = 10; + + ret = nvgpu_cond_init(&test_cond); + if (ret != 0) { + unit_return_fail(m, "Cond init failed\n"); + } + + ts_before = nvgpu_current_time_us(); + nvgpu_cond_lock(&test_cond); + + ret = nvgpu_cond_timedwait(&test_cond, &timeout); + if (ret != ETIMEDOUT) { + nvgpu_cond_unlock(&test_cond); + nvgpu_cond_destroy(&test_cond); + unit_return_fail(m, "Cond timed wait return error %d\n", ret); + } + + ts_after = nvgpu_current_time_us(); + delay = ts_after - ts_before; + delay /= 1000; + + if (delay < 10) { + nvgpu_cond_unlock(&test_cond); + nvgpu_cond_destroy(&test_cond); + unit_return_fail(m, + "Timed wait Duration incorrect\n"); + } + + nvgpu_cond_unlock(&test_cond); + nvgpu_cond_destroy(&test_cond); + + return UNIT_SUCCESS; +} + struct unit_module_test posix_cond_tests[] = { UNIT_TEST(init, test_cond_init_destroy, NULL, 0), + UNIT_TEST(bug_cond, test_cond_bug, NULL, 0), UNIT_TEST(wait_signal, test_cond_signal, &signal_normal, 0), UNIT_TEST(wait_signal_int, test_cond_signal, &signal_int, 0), UNIT_TEST(wait_signal_locked, test_cond_signal, &signal_locked, 0), @@ -435,6 +545,7 @@ struct unit_module_test posix_cond_tests[] = { UNIT_TEST(wait_condition, test_cond_signal, &condition_wait, 0), UNIT_TEST(wait_condition_int, test_cond_signal, &condition_wait_int, 0), UNIT_TEST(wait_condition_locked, test_cond_signal, &condition_wait_locked, 0), + UNIT_TEST(wait_timeout, test_cond_timeout, NULL, 0), }; UNIT_MODULE(posix_cond, posix_cond_tests, UNIT_PRIO_POSIX_TEST); diff --git a/userspace/units/posix/cond/posix-cond.h b/userspace/units/posix/cond/posix-cond.h index 477c3839f..40a2ee601 100644 --- a/userspace/units/posix/cond/posix-cond.h +++ b/userspace/units/posix/cond/posix-cond.h @@ -53,7 +53,7 @@ * struct nvgpu_cond * * Output: - * The test returns PASS if cond variable initiaisation and cleanup functions + * The test returns PASS if cond variable initialization and cleanup functions * returns expected success values and internal variables in cond variable * structure is initialised with proper values. * The test returns FAIL if either initialisation or cleanup routine fails. @@ -64,6 +64,37 @@ int test_cond_init_destroy(struct unit_module *m, struct gk20a *g, void *args); +/** + * Test specification for test_cond_bug + * + * Description: Test NULL and uninitialized cond vars. + * + * Test Type: Feature, Error injection + * + * Inputs: + * 1) Global instance of struct nvgpu_cond. + * + * Steps: + * 1) Call the condition variable functions with NULL as input parameter. + * 2) Make sure that all the called functions either invoke BUG or return + * an error value for NULL value as input parameter. + * 3) Call the condition variable function with uninitialized condition + * variable as input parameter. + * 4) Make sure that all the called functions either invokes BUG or returns + * an error value for uninitialized condition variable passed as input + * parameter. + * + * Output: + * The test returns PASS if all the NULL and uninitialized input parameters + * are handled by the condition variable functions by either calling BUG or by + * returning an error value. + * The test returns FAIL if any of the NULL or uninitialized condition variable + * passed as input parameter is not handled as expected. + * + */ +int test_cond_bug(struct unit_module *m, + struct gk20a *g, void *args); + /** * Test specification for test_cond_signal * @@ -106,16 +137,16 @@ int test_cond_init_destroy(struct unit_module *m, * - Main Thread: * 1) Main thread resets the global variables test_code, test_cond * and test_data. - * 2) Initialise the condition variabe by calling nvgpu_cond_init. + * 2) Initialise the condition variable by calling nvgpu_cond_init. * 3) Return failure if the init function returns error. - * 4) Copy the test args into global structure instance of unit_test_cond_data. + * 4) Copy the test args into global instance of unit_test_cond_data. * 5) Reset global variables read_status and bcst_read_status to 0. * 6) Create the read thread. - * 7) Cleanup the initialised cond variable and return failure if read thread - * creation fails. + * 7) Cleanup the initialised cond variable and return failure if read + * thread creation fails. * 8) Create the write thread. - * 9) Cleanup the initialised cond variable, cancel the read thread and return - * failure if write thread creation fails. + * 9) Cleanup the initialised cond variable, cancel the read thread and + * return failure if write thread creation fails. * 10) Wait for both read and write thread to exit using pthread_join. * 11) Check for global variable read_status and return FAIL if the value * indicates an error. @@ -131,7 +162,8 @@ int test_cond_init_destroy(struct unit_module *m, * 5) Return from the thread handler. * * - Write Thread: - * 1) Wait on global variable read_wait to be true before proceeding further. + * 1) Wait on global variable read_wait to be true before proceeding + * further. * 2) Update the global array test_code with a defined value. * 3) Reset read_wait to 0. * 4) Signal the condition variable. @@ -157,7 +189,7 @@ int test_cond_init_destroy(struct unit_module *m, * * e) Wait and Broadcast * In broadcast test cases an extra read thread is created by the main - * thread. Both the read threads will get blocked on the codition variable. + * thread. Both the read threads will get blocked on the condition variable. * The write thread has to broadcast the signal, which should bring both * the read threads out of blocked state. The main thread needs to wait for * the extra read thread also to exit in this case. @@ -185,10 +217,43 @@ int test_cond_init_destroy(struct unit_module *m, * Output: * All the tests return PASS if the condition variable is properly signalled * by the write thread and further verification of shared data shows a - * succesful update from write thread with a predefined value. + * successful update from write thread with a predefined value. * The tests return FAIL, if any of the above conditions are not met. * */ int test_cond_signal(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for test_cond_timeout + * + * Description: Test time out for a condition variable wait. + * + * Test Type: Feature, Error injection + * + * Inputs: + * 1) Global instance of struct nvgpu_cond. + * + * Steps: + * 1) Initialize the condition variable. + * 2) Call the function nvgpu_cond_timedwait with a timeout value. + * 3) Check the return value from the function nvgpu_cond_timedwait. If the + * return value is not ETIMEDOUT, unlock the mutex associated with the + * condition variable then destroy the condition variable and return fail. + * 4) If the return value is ETIMEDOUT, check the actual duration of timed + * wait. If it is less than the requested timeout value, unlock the mutex + * associated with the condition variable then destroy the condition + * variable and return FAIL. + * 5) Unlock the mutex associated with the condition variable then destroy the + * condition variable and return pass. + * + * Output: + * The test returns PASS if the nvgpu_cond_timedwait function returns + * ETIMEDOUT error. + * The test returns FAIL if the return value from nvgpu_cond_timedwait function + * is not ETIMEDOUT. + * + */ +int test_cond_timeout(struct unit_module *m, + struct gk20a *g, void *args); #endif /* __UNIT_POSIX_COND_H__ */