Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c8c689bb authored by Kyle Yan's avatar Kyle Yan Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: ipa: add dma test suite" into msm-4.9

parents ee36c86e 6926de28
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
obj-$(CONFIG_IPA_UT) += ipa_ut_mod.o
ipa_ut_mod-y := ipa_ut_framework.o ipa_test_example.o ipa_test_mhi.o
ipa_ut_mod-y := ipa_ut_framework.o ipa_test_example.o ipa_test_mhi.o ipa_test_dma.o
+931 −0
Original line number Diff line number Diff line
/* 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		500
#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: 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: 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;

static void ipa_test_dma_wrapper_test_one_sync(struct work_struct *work)
{
	int rc;

	rc = ipa_test_dma_memcpy_sync(IPA_TEST_DMA_MEMCPY_BUFF_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;

	rc = ipa_test_dma_memcpy_async(IPA_TEST_DMA_MEMCPY_BUFF_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 work_struct work_async[IPA_TEST_DMA_MT_TEST_NUM_WQ];
	static struct work_struct 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(work_async, 0, sizeof(work_async));
	memset(work_sync, 0, sizeof(work_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;
		}

		INIT_WORK(&work_sync[i], ipa_test_dma_wrapper_test_one_sync);
		queue_work(wq_sync[i], &work_sync[i]);
		INIT_WORK(&work_async[i], ipa_test_dma_wrapper_test_one_async);
		queue_work(wq_async[i], &work_async[i]);
	}

	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(async_memcpy,
		"Async memory copy",
		ipa_test_dma_async_memcpy,
		true, IPA_HW_v3_0, 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);
+3 −1
Original line number Diff line number Diff line
/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
/* 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
@@ -21,6 +21,7 @@
 * No importance for order.
 */
IPA_UT_DECLARE_SUITE(mhi);
IPA_UT_DECLARE_SUITE(dma);
IPA_UT_DECLARE_SUITE(example);


@@ -31,6 +32,7 @@ IPA_UT_DECLARE_SUITE(example);
IPA_UT_DEFINE_ALL_SUITES_START
{
	IPA_UT_REGISTER_SUITE(mhi),
	IPA_UT_REGISTER_SUITE(dma),
	IPA_UT_REGISTER_SUITE(example),
} IPA_UT_DEFINE_ALL_SUITES_END;