| /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <linux/ipa.h> |
| #include "../ipa_v3/ipa_i.h" |
| #include "ipa_ut_framework.h" |
| |
| #define IPA_TEST_DMA_WQ_NAME_BUFF_SZ 64 |
| #define IPA_TEST_DMA_MT_TEST_NUM_WQ 400 |
| #define IPA_TEST_DMA_MEMCPY_BUFF_SIZE 16384 |
| #define IPA_TEST_DMA_MAX_PKT_SIZE 0xFF00 |
| #define IPA_DMA_TEST_LOOP_NUM 1000 |
| #define IPA_DMA_TEST_INT_LOOP_NUM 50 |
| #define IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM 128 |
| #define IPA_DMA_RUN_TEST_UNIT_IN_LOOP(test_unit, iters, rc, args...) \ |
| do { \ |
| int __i; \ |
| for (__i = 0; __i < iters; __i++) { \ |
| IPA_UT_LOG(#test_unit " START iter %d\n", __i); \ |
| rc = test_unit(args); \ |
| if (!rc) \ |
| continue; \ |
| IPA_UT_LOG(#test_unit " failed %d\n", rc); \ |
| break; \ |
| } \ |
| } while (0) |
| |
| /** |
| * struct ipa_test_dma_async_user_data - user_data structure for async memcpy |
| * @src_mem: source memory buffer |
| * @dest_mem: destination memory buffer |
| * @call_serial_number: Id of the caller |
| * @copy_done: Completion object |
| */ |
| struct ipa_test_dma_async_user_data { |
| struct ipa_mem_buffer src_mem; |
| struct ipa_mem_buffer dest_mem; |
| int call_serial_number; |
| struct completion copy_done; |
| }; |
| |
| /** |
| * ipa_test_dma_setup() - Suite setup function |
| */ |
| static int ipa_test_dma_setup(void **ppriv) |
| { |
| int rc; |
| |
| IPA_UT_DBG("Start Setup\n"); |
| |
| if (!ipa3_ctx) { |
| IPA_UT_ERR("No IPA ctx\n"); |
| return -EINVAL; |
| } |
| |
| rc = ipa_dma_init(); |
| if (rc) |
| IPA_UT_ERR("Fail to init ipa_dma - return code %d\n", rc); |
| else |
| IPA_UT_DBG("ipa_dma_init() Completed successfully!\n"); |
| |
| *ppriv = NULL; |
| |
| return rc; |
| } |
| |
| /** |
| * ipa_test_dma_teardown() - Suite teardown function |
| */ |
| static int ipa_test_dma_teardown(void *priv) |
| { |
| IPA_UT_DBG("Start Teardown\n"); |
| ipa_dma_destroy(); |
| return 0; |
| } |
| |
| static int ipa_test_dma_alloc_buffs(struct ipa_mem_buffer *src, |
| struct ipa_mem_buffer *dest, |
| int size) |
| { |
| int i; |
| static int val = 1; |
| int rc; |
| |
| val++; |
| src->size = size; |
| src->base = dma_alloc_coherent(ipa3_ctx->pdev, src->size, |
| &src->phys_base, GFP_KERNEL); |
| if (!src->base) { |
| IPA_UT_LOG("fail to alloc dma mem %d bytes\n", size); |
| IPA_UT_TEST_FAIL_REPORT("fail to alloc dma mem"); |
| return -ENOMEM; |
| } |
| |
| dest->size = size; |
| dest->base = dma_alloc_coherent(ipa3_ctx->pdev, dest->size, |
| &dest->phys_base, GFP_KERNEL); |
| if (!dest->base) { |
| IPA_UT_LOG("fail to alloc dma mem %d bytes\n", size); |
| IPA_UT_TEST_FAIL_REPORT("fail to alloc dma mem"); |
| rc = -ENOMEM; |
| goto fail_alloc_dest; |
| } |
| |
| memset(dest->base, 0, dest->size); |
| for (i = 0; i < src->size; i++) |
| memset(src->base + i, (val + i) & 0xFF, 1); |
| rc = memcmp(dest->base, src->base, dest->size); |
| if (rc == 0) { |
| IPA_UT_LOG("dest & src buffers are equal\n"); |
| IPA_UT_TEST_FAIL_REPORT("dest & src buffers are equal"); |
| rc = -EFAULT; |
| goto fail_buf_cmp; |
| } |
| |
| return 0; |
| |
| fail_buf_cmp: |
| dma_free_coherent(ipa3_ctx->pdev, dest->size, dest->base, |
| dest->phys_base); |
| fail_alloc_dest: |
| dma_free_coherent(ipa3_ctx->pdev, src->size, src->base, |
| src->phys_base); |
| return rc; |
| } |
| |
| static void ipa_test_dma_destroy_buffs(struct ipa_mem_buffer *src, |
| struct ipa_mem_buffer *dest) |
| { |
| dma_free_coherent(ipa3_ctx->pdev, src->size, src->base, |
| src->phys_base); |
| dma_free_coherent(ipa3_ctx->pdev, dest->size, dest->base, |
| dest->phys_base); |
| } |
| |
| /** |
| * ipa_test_dma_memcpy_sync() - memcpy in sync mode |
| * |
| * @size: buffer size |
| * @expect_fail: test expects the memcpy to fail |
| * |
| * To be run during tests |
| * 1. Alloc src and dst buffers |
| * 2. sync memcpy src to dst via dma |
| * 3. compare src and dts if memcpy succeeded as expected |
| */ |
| static int ipa_test_dma_memcpy_sync(int size, bool expect_fail) |
| { |
| int rc = 0; |
| int i; |
| struct ipa_mem_buffer src_mem; |
| struct ipa_mem_buffer dest_mem; |
| u8 *src; |
| u8 *dest; |
| |
| rc = ipa_test_dma_alloc_buffs(&src_mem, &dest_mem, size); |
| if (rc) { |
| IPA_UT_LOG("fail to alloc buffers\n"); |
| IPA_UT_TEST_FAIL_REPORT("fail to alloc buffers"); |
| return rc; |
| } |
| |
| rc = ipa_dma_sync_memcpy(dest_mem.phys_base, src_mem.phys_base, size); |
| if (!expect_fail && rc) { |
| IPA_UT_LOG("fail to sync memcpy - rc = %d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("sync memcpy failed"); |
| goto free_buffs; |
| } |
| if (expect_fail && !rc) { |
| IPA_UT_LOG("sync memcpy succeeded while expected to fail\n"); |
| IPA_UT_TEST_FAIL_REPORT( |
| "sync memcpy succeeded while expected to fail"); |
| rc = -EFAULT; |
| goto free_buffs; |
| } |
| |
| if (!rc) { |
| /* if memcpy succeeded, compare the buffers */ |
| rc = memcmp(dest_mem.base, src_mem.base, size); |
| if (rc) { |
| IPA_UT_LOG("BAD memcpy - buffs are not equals\n"); |
| IPA_UT_TEST_FAIL_REPORT( |
| "BAD memcpy - buffs are not equals"); |
| src = src_mem.base; |
| dest = dest_mem.base; |
| for (i = 0; i < size; i++) { |
| if (*(src + i) != *(dest + i)) { |
| IPA_UT_LOG("byte: %d 0x%x != 0x%x\n", |
| i, *(src + i), *(dest + i)); |
| } |
| } |
| } |
| } else { |
| /* if memcpy failed as expected, update the rc */ |
| rc = 0; |
| } |
| |
| free_buffs: |
| ipa_test_dma_destroy_buffs(&src_mem, &dest_mem); |
| return rc; |
| } |
| |
| static void ipa_test_dma_async_memcpy_cb(void *comp_obj) |
| { |
| struct completion *xfer_done; |
| |
| if (!comp_obj) { |
| IPA_UT_ERR("Invalid Input\n"); |
| return; |
| } |
| xfer_done = (struct completion *)comp_obj; |
| complete(xfer_done); |
| } |
| |
| static void ipa_test_dma_async_memcpy_cb_user_data(void *user_param) |
| { |
| int rc; |
| int i; |
| u8 *src; |
| u8 *dest; |
| struct ipa_test_dma_async_user_data *udata = |
| (struct ipa_test_dma_async_user_data *)user_param; |
| |
| if (!udata) { |
| IPA_UT_ERR("Invalid user param\n"); |
| return; |
| } |
| |
| rc = memcmp(udata->dest_mem.base, udata->src_mem.base, |
| udata->src_mem.size); |
| if (rc) { |
| IPA_UT_LOG("BAD memcpy - buffs are not equal sn=%d\n", |
| udata->call_serial_number); |
| IPA_UT_TEST_FAIL_REPORT( |
| "BAD memcpy - buffs are not equal"); |
| src = udata->src_mem.base; |
| dest = udata->dest_mem.base; |
| for (i = 0; i < udata->src_mem.size; i++) { |
| if (*(src + i) != *(dest + i)) { |
| IPA_UT_ERR("byte: %d 0x%x != 0x%x\n", i, |
| *(src + i), *(dest + i)); |
| } |
| } |
| return; |
| } |
| |
| IPA_UT_LOG("Notify on async memcopy sn=%d\n", |
| udata->call_serial_number); |
| complete(&(udata->copy_done)); |
| } |
| |
| /** |
| * ipa_test_dma_memcpy_async() - memcpy in async mode |
| * |
| * @size: buffer size |
| * @expect_fail: test expected the memcpy to fail |
| * |
| * To be run during tests |
| * 1. Alloc src and dst buffers |
| * 2. async memcpy src to dst via dma and wait for completion |
| * 3. compare src and dts if memcpy succeeded as expected |
| */ |
| static int ipa_test_dma_memcpy_async(int size, bool expect_fail) |
| { |
| int rc = 0; |
| int i; |
| struct ipa_mem_buffer src_mem; |
| struct ipa_mem_buffer dest_mem; |
| u8 *src; |
| u8 *dest; |
| struct completion xfer_done; |
| |
| rc = ipa_test_dma_alloc_buffs(&src_mem, &dest_mem, size); |
| if (rc) { |
| IPA_UT_LOG("fail to alloc buffers\n"); |
| IPA_UT_TEST_FAIL_REPORT("fail to alloc buffers"); |
| return rc; |
| } |
| |
| init_completion(&xfer_done); |
| rc = ipa_dma_async_memcpy(dest_mem.phys_base, src_mem.phys_base, size, |
| ipa_test_dma_async_memcpy_cb, &xfer_done); |
| if (!expect_fail && rc) { |
| IPA_UT_LOG("fail to initiate async memcpy - rc=%d\n", |
| rc); |
| IPA_UT_TEST_FAIL_REPORT("async memcpy initiate failed"); |
| goto free_buffs; |
| } |
| if (expect_fail && !rc) { |
| IPA_UT_LOG("async memcpy succeeded while expected to fail\n"); |
| IPA_UT_TEST_FAIL_REPORT( |
| "async memcpy succeeded while expected to fail"); |
| rc = -EFAULT; |
| goto free_buffs; |
| } |
| |
| if (!rc) { |
| /* if memcpy succeeded, compare the buffers */ |
| wait_for_completion(&xfer_done); |
| rc = memcmp(dest_mem.base, src_mem.base, size); |
| if (rc) { |
| IPA_UT_LOG("BAD memcpy - buffs are not equals\n"); |
| IPA_UT_TEST_FAIL_REPORT( |
| "BAD memcpy - buffs are not equals"); |
| src = src_mem.base; |
| dest = dest_mem.base; |
| for (i = 0; i < size; i++) { |
| if (*(src + i) != *(dest + i)) { |
| IPA_UT_LOG("byte: %d 0x%x != 0x%x\n", |
| i, *(src + i), *(dest + i)); |
| } |
| } |
| } |
| } else { |
| /* if memcpy failed as expected, update the rc */ |
| rc = 0; |
| } |
| |
| free_buffs: |
| ipa_test_dma_destroy_buffs(&src_mem, &dest_mem); |
| return rc; |
| } |
| |
| /** |
| * ipa_test_dma_sync_async_memcpy() - memcpy in sync and then async mode |
| * |
| * @size: buffer size |
| * |
| * To be run during tests |
| * 1. several sync memcopy in row |
| * 2. several async memcopy - |
| * back-to-back (next async try initiated after prev is completed) |
| */ |
| static int ipa_test_dma_sync_async_memcpy(int size) |
| { |
| int rc; |
| |
| IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_memcpy_sync, |
| IPA_DMA_TEST_INT_LOOP_NUM, rc, size, false); |
| if (rc) { |
| IPA_UT_LOG("sync memcopy fail rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("sync memcopy fail"); |
| return rc; |
| } |
| |
| IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_memcpy_async, |
| IPA_DMA_TEST_INT_LOOP_NUM, rc, size, false); |
| if (rc) { |
| IPA_UT_LOG("async memcopy fail rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("async memcopy fail"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: test control API - enable/disable dma |
| * 1. enable dma |
| * 2. disable dma |
| */ |
| static int ipa_test_dma_control_api(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: memcpy before dma enable |
| * |
| * 1. sync memcpy - should fail |
| * 2. async memcpy - should fail |
| */ |
| static int ipa_test_dma_memcpy_before_enable(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_test_dma_memcpy_sync(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, true); |
| if (rc) { |
| IPA_UT_LOG("sync memcpy succeeded unexpectedly rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("sync memcpy succeeded unexpectedly"); |
| return rc; |
| } |
| |
| rc = ipa_test_dma_memcpy_async(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, true); |
| if (rc) { |
| IPA_UT_LOG("async memcpy succeeded unexpectedly rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("sync memcpy succeeded unexpectedly"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: Sync memory copy |
| * |
| * 1. dma enable |
| * 2. sync memcpy |
| * 3. dma disable |
| */ |
| static int ipa_test_dma_sync_memcpy(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| rc = ipa_test_dma_memcpy_sync(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false); |
| if (rc) { |
| IPA_UT_LOG("sync memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("sync memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: Small sync memory copy |
| * |
| * 1. dma enable |
| * 2. small sync memcpy |
| * 3. small sync memcpy |
| * 4. dma disable |
| */ |
| static int ipa_test_dma_sync_memcpy_small(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| rc = ipa_test_dma_memcpy_sync(4, false); |
| if (rc) { |
| IPA_UT_LOG("sync memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("sync memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_test_dma_memcpy_sync(7, false); |
| if (rc) { |
| IPA_UT_LOG("sync memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("sync memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: Async memory copy |
| * |
| * 1. dma enable |
| * 2. async memcpy |
| * 3. dma disable |
| */ |
| static int ipa_test_dma_async_memcpy(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| rc = ipa_test_dma_memcpy_async(IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false); |
| if (rc) { |
| IPA_UT_LOG("async memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("async memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: Small async memory copy |
| * |
| * 1. dma enable |
| * 2. async memcpy |
| * 3. async memcpy |
| * 4. dma disable |
| */ |
| static int ipa_test_dma_async_memcpy_small(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| rc = ipa_test_dma_memcpy_async(4, false); |
| if (rc) { |
| IPA_UT_LOG("async memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("async memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_test_dma_memcpy_async(7, false); |
| if (rc) { |
| IPA_UT_LOG("async memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("async memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: Iteration of sync memory copy |
| * |
| * 1. dma enable |
| * 2. sync memcpy in loop - in row |
| * 3. dma disable |
| */ |
| static int ipa_test_dma_sync_memcpy_in_loop(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_memcpy_sync, |
| IPA_DMA_TEST_LOOP_NUM, rc, |
| IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false); |
| if (rc) { |
| IPA_UT_LOG("Iterations of sync memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("Iterations of sync memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: Iteration of async memory copy |
| * |
| * 1. dma enable |
| * 2. async memcpy in loop - back-to-back |
| * next async copy is initiated once previous one completed |
| * 3. dma disable |
| */ |
| static int ipa_test_dma_async_memcpy_in_loop(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_memcpy_async, |
| IPA_DMA_TEST_LOOP_NUM, rc, |
| IPA_TEST_DMA_MEMCPY_BUFF_SIZE, false); |
| if (rc) { |
| IPA_UT_LOG("Iterations of async memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("Iterations of async memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: Iteration of interleaved sync and async memory copy |
| * |
| * 1. dma enable |
| * 2. sync and async memcpy in loop - interleaved |
| * 3. dma disable |
| */ |
| static int ipa_test_dma_interleaved_sync_async_memcpy_in_loop(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| IPA_DMA_RUN_TEST_UNIT_IN_LOOP(ipa_test_dma_sync_async_memcpy, |
| IPA_DMA_TEST_INT_LOOP_NUM, rc, |
| IPA_TEST_DMA_MEMCPY_BUFF_SIZE); |
| if (rc) { |
| IPA_UT_LOG( |
| "Iterations of interleaved sync async memcpy failed rc=%d\n" |
| , rc); |
| IPA_UT_TEST_FAIL_REPORT( |
| "Iterations of interleaved sync async memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static atomic_t ipa_test_dma_mt_test_pass; |
| |
| struct one_memcpy_work { |
| struct work_struct work_s; |
| int size; |
| }; |
| |
| static void ipa_test_dma_wrapper_test_one_sync(struct work_struct *work) |
| { |
| int rc; |
| struct one_memcpy_work *data = |
| container_of(work, struct one_memcpy_work, work_s); |
| |
| rc = ipa_test_dma_memcpy_sync(data->size, false); |
| if (rc) { |
| IPA_UT_LOG("fail sync memcpy from thread rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail sync memcpy from thread"); |
| return; |
| } |
| atomic_inc(&ipa_test_dma_mt_test_pass); |
| } |
| |
| static void ipa_test_dma_wrapper_test_one_async(struct work_struct *work) |
| { |
| int rc; |
| struct one_memcpy_work *data = |
| container_of(work, struct one_memcpy_work, work_s); |
| |
| rc = ipa_test_dma_memcpy_async(data->size, false); |
| if (rc) { |
| IPA_UT_LOG("fail async memcpy from thread rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail async memcpy from thread"); |
| return; |
| } |
| atomic_inc(&ipa_test_dma_mt_test_pass); |
| } |
| |
| /** |
| * TEST: Multiple threads running sync and sync mem copy |
| * |
| * 1. dma enable |
| * 2. In-loop |
| * 2.1 create wq for sync memcpy |
| * 2.2 create wq for async memcpy |
| * 2.3 queue sync memcpy work |
| * 2.4 queue async memcoy work |
| * 3. In-loop |
| * 3.1 flush and destroy wq sync |
| * 3.2 flush and destroy wq async |
| * 3. dma disable |
| */ |
| static int ipa_test_dma_mt_sync_async(void *priv) |
| { |
| int rc; |
| int i; |
| static struct workqueue_struct *wq_sync[IPA_TEST_DMA_MT_TEST_NUM_WQ]; |
| static struct workqueue_struct *wq_async[IPA_TEST_DMA_MT_TEST_NUM_WQ]; |
| static struct one_memcpy_work async[IPA_TEST_DMA_MT_TEST_NUM_WQ]; |
| static struct one_memcpy_work sync[IPA_TEST_DMA_MT_TEST_NUM_WQ]; |
| char buff[IPA_TEST_DMA_WQ_NAME_BUFF_SZ]; |
| |
| memset(wq_sync, 0, sizeof(wq_sync)); |
| memset(wq_sync, 0, sizeof(wq_async)); |
| memset(async, 0, sizeof(async)); |
| memset(sync, 0, sizeof(sync)); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| atomic_set(&ipa_test_dma_mt_test_pass, 0); |
| for (i = 0; i < IPA_TEST_DMA_MT_TEST_NUM_WQ; i++) { |
| snprintf(buff, sizeof(buff), "ipa_test_dmaSwq%d", i); |
| wq_sync[i] = create_singlethread_workqueue(buff); |
| if (!wq_sync[i]) { |
| IPA_UT_ERR("failed to create sync wq#%d\n", i); |
| rc = -EFAULT; |
| goto fail_create_wq; |
| } |
| snprintf(buff, IPA_RESOURCE_NAME_MAX, "ipa_test_dmaAwq%d", i); |
| wq_async[i] = create_singlethread_workqueue(buff); |
| if (!wq_async[i]) { |
| IPA_UT_ERR("failed to create async wq#%d\n", i); |
| rc = -EFAULT; |
| goto fail_create_wq; |
| } |
| |
| if (i % 2) { |
| sync[i].size = IPA_TEST_DMA_MEMCPY_BUFF_SIZE; |
| async[i].size = IPA_TEST_DMA_MEMCPY_BUFF_SIZE; |
| } else { |
| sync[i].size = 4; |
| async[i].size = 4; |
| } |
| INIT_WORK(&sync[i].work_s, ipa_test_dma_wrapper_test_one_sync); |
| queue_work(wq_sync[i], &sync[i].work_s); |
| INIT_WORK(&async[i].work_s, |
| ipa_test_dma_wrapper_test_one_async); |
| queue_work(wq_async[i], &async[i].work_s); |
| } |
| |
| for (i = 0; i < IPA_TEST_DMA_MT_TEST_NUM_WQ; i++) { |
| flush_workqueue(wq_sync[i]); |
| destroy_workqueue(wq_sync[i]); |
| flush_workqueue(wq_async[i]); |
| destroy_workqueue(wq_async[i]); |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| if ((2 * IPA_TEST_DMA_MT_TEST_NUM_WQ) != |
| atomic_read(&ipa_test_dma_mt_test_pass)) { |
| IPA_UT_LOG( |
| "Multi-threaded sync/async memcopy failed passed=%d\n" |
| , atomic_read(&ipa_test_dma_mt_test_pass)); |
| IPA_UT_TEST_FAIL_REPORT( |
| "Multi-threaded sync/async memcopy failed"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| |
| fail_create_wq: |
| (void)ipa_dma_disable(); |
| for (i = 0; i < IPA_TEST_DMA_MT_TEST_NUM_WQ; i++) { |
| if (wq_sync[i]) |
| destroy_workqueue(wq_sync[i]); |
| if (wq_async[i]) |
| destroy_workqueue(wq_async[i]); |
| } |
| |
| return rc; |
| } |
| |
| /** |
| * TEST: Several parallel async memory copy iterations |
| * |
| * 1. create several user_data structures - one per iteration |
| * 2. allocate buffs. Give slice for each iteration |
| * 3. iterations of async mem copy |
| * 4. wait for all to complete |
| * 5. dma disable |
| */ |
| static int ipa_test_dma_parallel_async_memcpy_in_loop(void *priv) |
| { |
| int rc; |
| struct ipa_test_dma_async_user_data *udata; |
| struct ipa_mem_buffer all_src_mem; |
| struct ipa_mem_buffer all_dest_mem; |
| int i; |
| bool is_fail = false; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| udata = kzalloc(IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM * |
| sizeof(struct ipa_test_dma_async_user_data), GFP_KERNEL); |
| if (!udata) { |
| IPA_UT_ERR("fail allocate user_data array\n"); |
| (void)ipa_dma_disable(); |
| return -ENOMEM; |
| } |
| |
| rc = ipa_test_dma_alloc_buffs(&all_src_mem, &all_dest_mem, |
| IPA_TEST_DMA_MEMCPY_BUFF_SIZE); |
| if (rc) { |
| IPA_UT_LOG("fail to alloc buffers\n"); |
| IPA_UT_TEST_FAIL_REPORT("fail to alloc buffers"); |
| kfree(udata); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| for (i = 0 ; i < IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM ; i++) { |
| udata[i].src_mem.size = |
| IPA_TEST_DMA_MEMCPY_BUFF_SIZE / |
| IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM; |
| udata[i].src_mem.base = all_src_mem.base + i * |
| (IPA_TEST_DMA_MEMCPY_BUFF_SIZE / |
| IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM); |
| udata[i].src_mem.phys_base = all_src_mem.phys_base + i * |
| (IPA_TEST_DMA_MEMCPY_BUFF_SIZE / |
| IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM); |
| |
| udata[i].dest_mem.size = |
| (IPA_TEST_DMA_MEMCPY_BUFF_SIZE / |
| IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM); |
| udata[i].dest_mem.base = all_dest_mem.base + i * |
| (IPA_TEST_DMA_MEMCPY_BUFF_SIZE / |
| IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM); |
| udata[i].dest_mem.phys_base = all_dest_mem.phys_base + i * |
| (IPA_TEST_DMA_MEMCPY_BUFF_SIZE / |
| IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM); |
| |
| udata[i].call_serial_number = i + 1; |
| init_completion(&(udata[i].copy_done)); |
| rc = ipa_dma_async_memcpy(udata[i].dest_mem.phys_base, |
| udata[i].src_mem.phys_base, |
| (IPA_TEST_DMA_MEMCPY_BUFF_SIZE / |
| IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM), |
| ipa_test_dma_async_memcpy_cb_user_data, &udata[i]); |
| if (rc) { |
| IPA_UT_LOG("async memcpy initiation fail i=%d rc=%d\n", |
| i, rc); |
| is_fail = true; |
| } |
| } |
| |
| for (i = 0; i < IPA_DMA_TEST_ASYNC_PARALLEL_LOOP_NUM ; i++) |
| wait_for_completion(&udata[i].copy_done); |
| |
| ipa_test_dma_destroy_buffs(&all_src_mem, &all_dest_mem); |
| kfree(udata); |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| if (is_fail) { |
| IPA_UT_LOG("async memcopy failed\n"); |
| IPA_UT_TEST_FAIL_REPORT("async memcopy failed"); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * TEST: Sync memory copy |
| * |
| * 1. dma enable |
| * 2. sync memcpy with max packet size |
| * 3. dma disable |
| */ |
| static int ipa_test_dma_sync_memcpy_max_pkt_size(void *priv) |
| { |
| int rc; |
| |
| IPA_UT_LOG("Test Start\n"); |
| |
| rc = ipa_dma_enable(); |
| if (rc) { |
| IPA_UT_LOG("DMA enable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail enable dma"); |
| return rc; |
| } |
| |
| rc = ipa_test_dma_memcpy_sync(IPA_TEST_DMA_MAX_PKT_SIZE, false); |
| if (rc) { |
| IPA_UT_LOG("sync memcpy failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("sync memcpy failed"); |
| (void)ipa_dma_disable(); |
| return rc; |
| } |
| |
| rc = ipa_dma_disable(); |
| if (rc) { |
| IPA_UT_LOG("DMA disable failed rc=%d\n", rc); |
| IPA_UT_TEST_FAIL_REPORT("fail disable dma"); |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| /* Suite definition block */ |
| IPA_UT_DEFINE_SUITE_START(dma, "DMA for GSI", |
| ipa_test_dma_setup, ipa_test_dma_teardown) |
| { |
| IPA_UT_ADD_TEST(control_api, |
| "Control API", |
| ipa_test_dma_control_api, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(memcpy_before_enable, |
| "Call memcpy before dma enable and expect it to fail", |
| ipa_test_dma_memcpy_before_enable, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(sync_memcpy, |
| "Sync memory copy", |
| ipa_test_dma_sync_memcpy, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(sync_memcpy_small, |
| "Small Sync memory copy", |
| ipa_test_dma_sync_memcpy_small, |
| true, IPA_HW_v3_5, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(async_memcpy, |
| "Async memory copy", |
| ipa_test_dma_async_memcpy, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(async_memcpy_small, |
| "Small async memory copy", |
| ipa_test_dma_async_memcpy_small, |
| true, IPA_HW_v3_5, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(sync_memcpy_in_loop, |
| "Several sync memory copy iterations", |
| ipa_test_dma_sync_memcpy_in_loop, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(async_memcpy_in_loop, |
| "Several async memory copy iterations", |
| ipa_test_dma_async_memcpy_in_loop, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(interleaved_sync_async_memcpy_in_loop, |
| "Several interleaved sync and async memory copy iterations", |
| ipa_test_dma_interleaved_sync_async_memcpy_in_loop, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(multi_threaded_multiple_sync_async_memcpy, |
| "Several multi-threaded sync and async memory copy iterations", |
| ipa_test_dma_mt_sync_async, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(parallel_async_memcpy_in_loop, |
| "Several parallel async memory copy iterations", |
| ipa_test_dma_parallel_async_memcpy_in_loop, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| IPA_UT_ADD_TEST(sync_memcpy_max_pkt_size, |
| "Sync memory copy with max packet size", |
| ipa_test_dma_sync_memcpy_max_pkt_size, |
| true, IPA_HW_v3_0, IPA_HW_MAX), |
| } IPA_UT_DEFINE_SUITE_END(dma); |