diff --git a/userspace/units/mm/gmmu/pd_cache/pd_cache.c b/userspace/units/mm/gmmu/pd_cache/pd_cache.c index 096ac9c20..3082cdbba 100644 --- a/userspace/units/mm/gmmu/pd_cache/pd_cache.c +++ b/userspace/units/mm/gmmu/pd_cache/pd_cache.c @@ -20,6 +20,7 @@ * DEALINGS IN THE SOFTWARE. */ +#include "pd_cache.h" #include #include #include @@ -166,22 +167,8 @@ static int init_pd_cache(struct unit_module *m, return UNIT_SUCCESS; } -/* - * Generate a test based on the args in @args. The test is very simple. It - * allocates nr allocs of the passed size either all at once or in an - * interleaved pattern. - * - * If nr_allocs_before_free is set then this value will determine how many - * allocs to do before trying frees. If unset it will be simply be nr. - * - * If nr_free_before_alloc is set this will determine the number of frees to - * do before swapping back to allocs. This way you can control the interleaving - * pattern to some degree. If not set it defaults to nr_allocs_before_free. - * - * Anything left over after the last free loop will be freed in one big loop. - */ -static int test_pd_cache_alloc_gen(struct unit_module *m, - struct gk20a *g, void *args) +int test_pd_cache_alloc_gen(struct unit_module *m, struct gk20a *g, + void *args) { u32 i, j; int err; @@ -297,12 +284,8 @@ cleanup_err: return UNIT_FAIL; } -/* - * Test free on empty PD cache. But make it interesting by doing a valid alloc - * and freeing that alloc twice. Also verify NULL doesn't cause issues. - */ -static int test_pd_free_empty_pd(struct unit_module *m, - struct gk20a *g, void *args) +int test_pd_free_empty_pd(struct unit_module *m, struct gk20a *g, + void *args) { int err; struct vm_gk20a vm; @@ -359,11 +342,8 @@ static int test_pd_free_empty_pd(struct unit_module *m, return UNIT_SUCCESS; } -/* - * Test invalid nvgpu_pd_alloc() calls. Invalid bytes, invalid pd_cache, etc. - */ -static int test_pd_alloc_invalid_input(struct unit_module *m, - struct gk20a *g, void *args) +int test_pd_alloc_invalid_input(struct unit_module *m, struct gk20a *g, + void *args) { int err; struct vm_gk20a vm; @@ -398,8 +378,7 @@ static int test_pd_alloc_invalid_input(struct unit_module *m, return UNIT_SUCCESS; } -static int test_pd_alloc_direct_fi(struct unit_module *m, - struct gk20a *g, void *args) +int test_pd_alloc_direct_fi(struct unit_module *m, struct gk20a *g, void *args) { int err; struct vm_gk20a vm; @@ -439,8 +418,7 @@ static int test_pd_alloc_direct_fi(struct unit_module *m, return UNIT_SUCCESS; } -static int test_pd_alloc_fi(struct unit_module *m, - struct gk20a *g, void *args) +int test_pd_alloc_fi(struct unit_module *m, struct gk20a *g, void *args) { int err; struct vm_gk20a vm; @@ -478,16 +456,7 @@ static int test_pd_alloc_fi(struct unit_module *m, return UNIT_SUCCESS; } -/* - * Test nvgpu_pd_cache_init() - make sure that: - * - * 1. Check that init with a memory failure returns -ENOMEM and that the - * pd_cache is not initialized. - * 2. Initial init works. - * 3. That re-init doesn't re-allocate any resources. - */ -static int test_pd_cache_init(struct unit_module *m, - struct gk20a *g, void *args) +int test_pd_cache_init(struct unit_module *m, struct gk20a *g, void *args) { int err, i; struct nvgpu_pd_cache *cache; @@ -543,16 +512,7 @@ static int test_pd_cache_init(struct unit_module *m, return UNIT_SUCCESS; } -/* - * Test nvgpu_pd_cache_fini() - make sure that: - * - * 1. An actually allocated cache is cleaned up. - * 2. If there is no cache this code doesn't crash. - * - * Note: this inherits the already inited pd_cache from test_pd_cache_init(). - */ -static int test_pd_cache_fini(struct unit_module *m, - struct gk20a *g, void *args) +int test_pd_cache_fini(struct unit_module *m, struct gk20a *g, void *args) { if (g->mm.pd_cache == NULL) { unit_return_fail(m, "Missing an init'ed pd_cache\n"); @@ -576,24 +536,8 @@ static int test_pd_cache_fini(struct unit_module *m, return UNIT_SUCCESS; } -/** - * Requirement NVGPU-RQCD-68.C1 - * - * Valid/Invalid: The pd_cache does/does not allocate a suitable DMA'able - * buffer of memory. - * - * Requirement NVGPU-RQCD-68.C2 - * - * Valid/Invalid: The allocated PD is/is not sufficiently aligned for use by - * the GMMU. - * - * Requirement NVGPU-RQCD-124.C1 - * - * Valid/Invalid: After initialization of the pd_cache the pd_cache can/cannot - * allocate valid PDs. - */ -static int test_pd_cache_valid_alloc(struct unit_module *m, - struct gk20a *g, void *args) +int test_pd_cache_valid_alloc(struct unit_module *m, struct gk20a *g, + void *args) { u32 bytes; int err; @@ -761,8 +705,7 @@ cleanup: return err; } -static int test_per_pd_size(struct unit_module *m, - struct gk20a *g, void *args) +int test_per_pd_size(struct unit_module *m, struct gk20a *g, void *args) { int err; u32 pd_size; @@ -805,23 +748,7 @@ static bool readback_pd_write(struct gk20a *g, struct nvgpu_gmmu_pd *pd, return nvgpu_mem_rd32(g, pd->mem, offset) == pattern; } -/** - * Requirement NVGPU-RQCD-122.C1 - * - * Valid/Invalid: The pd_cache writes/does not write a word of memory in a - * passed PD. - * - * Requirement NVGPU-RQCD-126.C1,2 - * - * C1: Valid/Invalid: The pd_cache unit does/does not return a valid word - * offset for a 2 word PDE/PTE. - * C2: Valid/Invalid: The pd_cache unit does/does not return a valid word - * offset for a 4 word PDE/PTE. - * - * This test hits both the pd_write() and the pd_nvgpu_pd_offset_from_index() - * functions since these are used to validate each other. - */ -static int test_pd_write(struct unit_module *m, struct gk20a *g, void *args) +int test_pd_write(struct unit_module *m, struct gk20a *g, void *args) { int err = UNIT_SUCCESS; struct vm_gk20a vm; @@ -885,13 +812,7 @@ cleanup: return err; } -/** - * Requirement NVGPU-RQCD-123.C1 - * - * C1: Valid/Invalid: The pd_cache does/does not provide a valid GPU physical - * address for a given PD. - */ -static int test_gpu_address(struct unit_module *m, struct gk20a *g, void *args) +int test_gpu_address(struct unit_module *m, struct gk20a *g, void *args) { int err; struct vm_gk20a vm; @@ -920,15 +841,7 @@ static int test_gpu_address(struct unit_module *m, struct gk20a *g, void *args) return UNIT_SUCCESS; } -/** - * Requirement NVGPU-RQCD-126.C1,2 - * - * C1: Valid/Invalid: The pd_cache unit does/does not return a valid word - * offset for a 2 word PDE/PTE. - * C2: Valid/Invalid: The pd_cache unit does/does not return a valid word - * offset for a 4 word PDE/PTE. - */ -static int test_offset_computation(struct unit_module *m, struct gk20a *g, +int test_offset_computation(struct unit_module *m, struct gk20a *g, void *args) { const struct gk20a_mmu_level *mm_levels = @@ -960,24 +873,7 @@ static int test_offset_computation(struct unit_module *m, struct gk20a *g, return fail ? UNIT_FAIL : UNIT_SUCCESS; } -/** - * Call this to cover a range of requirement tests: - * - * NVGPU-RQCD-124.C1 - * C1: Valid/Invalid: After initialization of pd_cache pd_cache can/cannot - * allocate valid PDs - * - * NVGPU-RQCD-155.C1 - * C1: Valid/Invalid: Re-initialization does not/does cause subsequent kernel - * or DMA allocations. - * - * NVGPU-RQCD-125.C1 - * C1: Valid/Invalid: The pd_cache unit does/does not release all it's - allocated memory (kerrnel and DMA). - * - * It's redundant, certainly, but that's fine as this test runs fast. - */ -static int test_init_deinit(struct unit_module *m, struct gk20a *g, void *args) +int test_init_deinit(struct unit_module *m, struct gk20a *g, void *args) { int err, status = UNIT_SUCCESS; struct vm_gk20a vm; diff --git a/userspace/units/mm/gmmu/pd_cache/pd_cache.h b/userspace/units/mm/gmmu/pd_cache/pd_cache.h new file mode 100644 index 000000000..51e9d94aa --- /dev/null +++ b/userspace/units/mm/gmmu/pd_cache/pd_cache.h @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2019, 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_PD_CACHE_H +#define UNIT_PD_CACHE_H + +struct gk20a; +struct unit_module; + +/** @addtogroup SWUTS-mm-gmmu-pd_cache + * @{ + * + * Software Unit Test Specification for mm.gmmu.pd_cache + */ + +/** + * Test specification for: test_pd_cache_init + * + * Description: Test to cover the initialization routines of pd_cache. + * + * Test Type: Feature based, Error Injection + * + * Targets: nvgpu_pd_cache_init + * + * Input: None + * + * Steps: + * - Check that init with a memory failure returns -ENOMEM and that the pd_cache + * is not initialized. + * - Perform a normal initialization and ensure that all the expected data + * structures were initialized. + * - Perform the initialization again and make sure that any re-init call + * doesn't blow away a previously inited pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_cache_init(struct unit_module *m, struct gk20a *g, void *__args); + +/** + * Test specification for: test_pd_cache_fini + * + * Description: Test to cover the de-initialization routines of pd_cache. + * + * Test Type: Feature based + * + * Targets: nvgpu_pd_cache_fini + * + * Input: test_pd_cache_init + * + * Steps: + * - Check that de-initializing the pd_cache results in a NULL pointer. + * - Call the de-initialization again and ensure it doesn't cause a crash. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_cache_fini(struct unit_module *m, struct gk20a *g, void *__args); + +/** + * Test specification for: test_pd_cache_valid_alloc + * + * Description: Checks that pd_cache allocates suitable DMA'able buffer of + * memory, that it is sufficiently aligned for use by the GMMU and it can + * allocate valid PDs. + * + * Test Type: Feature based + * + * Targets: nvgpu_pd_alloc, nvgpu_pd_write, nvgpu_pd_free, nvgpu_pd_cache_fini + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - Allocate a PD of each valid PD size and ensure they are properly + * populated with nvgpu_mem data. This tests read/write and alignment. + * - Do a write to the zeroth word and then verify this made it to + * the nvgpu_mem. Using the zeroth word makes it easy to read back. + * - Check alignment is at least as much as the size. + * - Free the PD. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_cache_valid_alloc(struct unit_module *m, struct gk20a *g, + void *__args); + +/** + * Test specification for: test_per_pd_size + * + * Description: Checks that pd_cache allocations are successful in a number of + * supported sizes. + * + * Test Type: Feature based + * + * Targets: nvgpu_pd_cache_init, nvgpu_pd_alloc, nvgpu_pd_free, + * nvgpu_pd_cache_fini + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - Set PD size to 256 bytes (i.e. minimum PD size) + * - While the PD size is smaller than the page size: + * - Call one of 2 scenario: + * - Ensure that 16 256B, 8 512B, etc, PDs can fit into a single page sized + * DMA allocation. + * - Ensure that previously allocated PD entries are re-usable. + * - Double the PD size. + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_per_pd_size(struct unit_module *m, struct gk20a *g, void *__args); + +/** + * Test specification for: test_pd_write + * + * Description: Ensure that the pd_cache writes a word of memory in a + * passed PD with 2 word or 4 word PDE/PTE. + * + * Test Type: Feature based + * + * Targets: gp10b_mm_get_mmu_levels, nvgpu_pd_cache_init, nvgpu_pd_alloc, + * nvgpu_pd_offset_from_index, nvgpu_pd_write, nvgpu_pd_free, + * nvgpu_pd_cache_fini + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - Allocate 2 test PD with page size 4KB. + * - Iterate over the 3 supported index sizes: 0, 16, 255: + * - Get the PD offset from the current index at the 3rd level and 4th level + * (respectively for 2 word and 4 word PDE/PTE.) + * - Write a known 32-bit pattern as a PD. + * - Read back the pattern and ensure it matches the written value. + * - De-allocate the 2 test PD. + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_write(struct unit_module *m, struct gk20a *g, void *__args); + +/** + * Test specification for: test_gpu_address + * + * Description: Ensure the pd_cache does provide a valid GPU physical address + * for a given PD. + * + * Test Type: Feature based + * + * Targets: nvgpu_pd_cache_init, nvgpu_pd_alloc, nvgpu_pd_gpu_addr, + * nvgpu_pd_free, nvgpu_pd_cache_fini + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - Allocate a test PD with page size 4KB. + * - Get the GPU address of the allocated PD and ensure it is not NULL. + * - De-allocate the test PD. + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_gpu_address(struct unit_module *m, struct gk20a *g, void *__args); + +/** + * Test specification for: test_offset_computation + * + * Description: Ensure that the pd_cache unit returns a valid word offset for + * 2 and 4 word PDE/PTE. + * + * Test Type: Feature based + * + * Targets: gp10b_mm_get_mmu_levels, nvgpu_pd_offset_from_index + * + * Input: None + * + * Steps: + * - Get all supported MMU levels. + * - Iterate over 4 index sizes: 0, 4, 16, 255. + * - Get the offset for a 2 word PDE/PTE and ensure it matches the expected + * value. + * - Get the offset for a 4 word PDE/PTE and ensure it matches the expected + * value. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_offset_computation(struct unit_module *m, struct gk20a *g, + void *__args); + +/** + * Test specification for: test_init_deinit + * + * Description: Ensure that the initialization routines of pd_cache handle all + * corner cases appropriately. + * + * Test Type: Feature based, Error injection + * + * Targets: nvgpu_pd_cache_init, nvgpu_pd_alloc, nvgpu_pd_cache_fini, + * nvgpu_pd_free + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - Allocate a test PD with page size 4KB. + * - Enable memory and DMA fault injection. + * - Call the pd_cache initialization again. + * - Since the pd_cache was already initialized, ensure the previous call + * still reported success, confirming that no further allocations were made. + * - Disable fault injection. + * - De-allocate the test PD. + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_init_deinit(struct unit_module *m, struct gk20a *g, void *__args); + +/** + * Test specification for: test_pd_cache_alloc_gen + * + * Description: Simple test that will perform allocations. It allocates + * nr allocs of the passed size either all at once or in an interleaved + * pattern. + * If nr_allocs_before_free is set then this value will determine how many + * allocs to do before trying frees. If unset it will be simply be nr. + * If nr_free_before_alloc is set this will determine the number of frees to + * do before swapping back to allocs. This way you can control the interleaving + * pattern to some degree. If not set it defaults to nr_allocs_before_free. + * Anything left over after the last free loop will be freed in one big loop. + * + * Test Type: Feature based + * + * Targets: nvgpu_pd_cache_init, nvgpu_pd_alloc, nvgpu_pd_cache_fini, + * nvgpu_pd_free + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - If there is no requested "allocs before free" value, set it to the + * requested total number of allocations. Also set the number of "frees before + * alloc" to 0. + * - Loop over the requested number of allocations with index 'i': + * - Loop from 0 to the requested number of "allocs before free": + * - Perform a PD allocation of the requested size. + * - Loop from 0 to the requested number of "frees before alloc": + * - Perform a PD free of allocation at index 'i'. + * - Loop backwards to free all the allocations. + * - Loop over all the PD allocation handles and ensure they have been zero'ed + * out as expected. + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_cache_alloc_gen(struct unit_module *m, struct gk20a *g, + void *__args); + +/** + * Test specification for: test_pd_free_empty_pd + * + * Description: Test free on empty PD cache and extra corner cases. + * + * Test Type: Feature based, Error injection + * + * Targets: nvgpu_pd_cache_init, nvgpu_pd_alloc, nvgpu_pd_cache_fini, + * nvgpu_pd_free + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - Allocate a test PD with a 2KB page size (cached). + * - Free the test PD. + * - Attempt to free the test PD again and ensure it causes a call to BUG(). + * - Attempt another free with pd.mem set to NULL and ensure it causes a call to + * BUG(). + * - Allocate a test PD with a 4KB page size (direct). + * - Free the test PD. + * - Call the free again which should not cause a BUG(). + * - Call the free again with pd.mem set to NULL which should not cause a BUG(). + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_free_empty_pd(struct unit_module *m, struct gk20a *g, + void *__args); + +/** + * Test specification for: test_pd_alloc_invalid_input + * + * Description: Test invalid nvgpu_pd_alloc() calls. Invalid bytes, + * invalid pd_cache, etc. + * + * Test Type: Error injection + * + * Targets: nvgpu_pd_cache_init, nvgpu_pd_alloc, nvgpu_pd_cache_fini + * + * Input: None + * + * Steps: + * - Ensure that no pd_cache is initialized in the system. + * - Attempt to perform an allocation and ensure it causes a call to BUG(). + * - Initialize a pd_cache. + * - Perform several allocation attempts with invalid sizes and ensure all + * calls report a failure. + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_alloc_invalid_input(struct unit_module *m, struct gk20a *g, + void *__args); + +/** + * Test specification for: test_pd_alloc_direct_fi + * + * Description: Test invalid nvgpu_pd_alloc() when out of memory conditions + * occur for direct allocations. + * + * Test Type: Error injection + * + * Targets: nvgpu_pd_cache_init, nvgpu_pd_alloc, nvgpu_pd_cache_fini + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - Enable kernel memory error injection. + * - Try to perform a PD allocation and ensure it failed. + * - Disable kernel memory error injection. + * - Enable DMA memory error injection. + * - Try to perform a PD allocation and ensure it failed. + * - Disable DMA memory error injection. + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_alloc_direct_fi(struct unit_module *m, struct gk20a *g, void *args); + +/** + * Test specification for: test_pd_alloc_fi + * + * Description: Test invalid nvgpu_pd_alloc() when out of memory conditions + * occur for nvgpu_pd_alloc_new allocations. + * + * Test Type: Error injection + * + * Targets: nvgpu_pd_cache_init, nvgpu_pd_alloc, nvgpu_pd_cache_fini + * + * Input: None + * + * Steps: + * - Initialize a pd_cache. + * - Enable kernel memory error injection. + * - Try to perform a PD allocation and ensure it failed. + * - Disable kernel memory error injection. + * - Enable DMA memory error injection. + * - Try to perform a PD allocation and ensure it failed. + * - Disable DMA memory error injection. + * - De-allocate the pd_cache. + * + * Output: Returns PASS if the steps above were executed successfully. FAIL + * otherwise. + */ +int test_pd_alloc_fi(struct unit_module *m, struct gk20a *g, void *args); + +/** }@ */ +#endif /* UNIT_PD_CACHE_H */