lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 20 Nov 2020 08:26:06 -0700
From:   Shuah Khan <skhan@...uxfoundation.org>
To:     Hemant Kumar <hemantk@...eaurora.org>,
        manivannan.sadhasivam@...aro.org, gregkh@...uxfoundation.org
Cc:     linux-arm-msm@...r.kernel.org, linux-kernel@...r.kernel.org,
        jhugo@...eaurora.org, bbhatt@...eaurora.org,
        loic.poulain@...aro.org, netdev@...r.kernel.org,
        Shuah Khan <skhan@...uxfoundation.org>
Subject: Re: [PATCH v12 5/5] selftest: mhi: Add support to test MHI LOOPBACK
 channel

On 11/16/20 3:46 PM, Hemant Kumar wrote:
> Loopback test opens the MHI device file node and writes
> a data buffer to it. MHI UCI kernel space driver copies
> the data and sends it to MHI uplink (Tx) LOOPBACK channel.
> MHI device loops back the same data to MHI downlink (Rx)
> LOOPBACK channel. This data is read by test application
> and compared against the data sent. Test passes if data
> buffer matches between Tx and Rx. Test application performs
> open(), poll(), write(), read() and close() file operations.
> 
> Signed-off-by: Hemant Kumar <hemantk@...eaurora.org>
> ---
>   Documentation/mhi/uci.rst                          |  32 +
>   tools/testing/selftests/Makefile                   |   1 +
>   tools/testing/selftests/drivers/.gitignore         |   1 +
>   tools/testing/selftests/drivers/mhi/Makefile       |   8 +
>   tools/testing/selftests/drivers/mhi/config         |   2 +
>   .../testing/selftests/drivers/mhi/loopback_test.c  | 802 +++++++++++++++++++++
>   6 files changed, 846 insertions(+)
>   create mode 100644 tools/testing/selftests/drivers/mhi/Makefile
>   create mode 100644 tools/testing/selftests/drivers/mhi/config
>   create mode 100644 tools/testing/selftests/drivers/mhi/loopback_test.c
> 
> diff --git a/Documentation/mhi/uci.rst b/Documentation/mhi/uci.rst
> index ce8740e..0a04afe 100644
> --- a/Documentation/mhi/uci.rst
> +++ b/Documentation/mhi/uci.rst
> @@ -79,6 +79,38 @@ MHI client driver performs read operation, same data gets looped back to MHI
>   host using LOOPBACK channel 1. LOOPBACK channel is used to verify data path
>   and data integrity between MHI Host and MHI device.
>   

Nice.

> +Loopback Test
> +~~~~~~~~~~~~~
> +
> +Loopback test application is used to verify data integrity between MHI host and
> +MHI device over LOOPBACK channel. This also confirms that basic MHI data path
> +is working properly. Test performs write() to send tx buffer to MHI device file
> +node for LOOPBACK uplink channel. MHI LOOPBACK downlink channel loops back
> +transmit data to MHI Host. Test application receives data in receive buffer as
> +part of read(). It verifies if tx buffer matches rx buffer. Test application
> +performs poll() before making write() and read() system calls. Test passes if
> +match is found.
> +
> +Test is present under tools/testing/selftests/drivers/mhi. It is compiled using
> +following command in same dir:-
> +
> +make loopback_test
> +
> +Test is run using following command arguments:-
> +
> +loopback_test -c <device_node> -b <transmit buffer size> -l <log level> -i
> +<number of iterations>
> +
> +Required argument:
> +-c : loopback chardev node
> +
> +Optional argument:
> +-b : transmit buffer size. If not present 1024 bytes size transmit buffer
> +     is sent.
> +-i : Number of iterations to perform, If not present only one transmit buffer
> +     is sent.
> +-l : Log level. If not present defaults to DBG_LVL_INFO.
> +
>   Other Use Cases
>   ---------------
>   
> diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
> index d9c2835..084bc1e 100644
> --- a/tools/testing/selftests/Makefile
> +++ b/tools/testing/selftests/Makefile
> @@ -10,6 +10,7 @@ TARGETS += core
>   TARGETS += cpufreq
>   TARGETS += cpu-hotplug
>   TARGETS += drivers/dma-buf
> +TARGETS += drivers/mhi
>   TARGETS += efivarfs
>   TARGETS += exec
>   TARGETS += filesystems
> diff --git a/tools/testing/selftests/drivers/.gitignore b/tools/testing/selftests/drivers/.gitignore
> index ca74f2e..e4806d5 100644
> --- a/tools/testing/selftests/drivers/.gitignore
> +++ b/tools/testing/selftests/drivers/.gitignore
> @@ -1,2 +1,3 @@
>   # SPDX-License-Identifier: GPL-2.0-only
>   /dma-buf/udmabuf
> +/mhi/loopback_test
> diff --git a/tools/testing/selftests/drivers/mhi/Makefile b/tools/testing/selftests/drivers/mhi/Makefile
> new file mode 100644
> index 0000000..c06c925
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/mhi/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +CFLAGS += -I../../../../../usr/include/ -g -Wall
> +
> +LDLIBS = -lpthread
> +TEST_GEN_PROGS := loopback_test
> +
> +include ../../lib.mk
> +
> diff --git a/tools/testing/selftests/drivers/mhi/config b/tools/testing/selftests/drivers/mhi/config
> new file mode 100644
> index 0000000..471dc92
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/mhi/config
> @@ -0,0 +1,2 @@
> +CONFIG_MHI_BUS=y
> +CONFIG_MHi_UCI=y
> diff --git a/tools/testing/selftests/drivers/mhi/loopback_test.c b/tools/testing/selftests/drivers/mhi/loopback_test.c
> new file mode 100644
> index 0000000..99b7712
> --- /dev/null
> +++ b/tools/testing/selftests/drivers/mhi/loopback_test.c
> @@ -0,0 +1,802 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Loopback test application performs write() to send tx buffer to MHI device
> + * file node for LOOPBACK uplink channel. MHI LOOPBACK downlink channel loops
> + * back transmit data to MHI Host. Test application receives data in receive
> + * buffer as part of read(). It verifies if tx buffer matches rx buffer. Test
> + * application performs poll() before making write() and read() system
> + * calls. Test passes if match is found.
> + *
> + * Test is compiled using following command:-
> + *
> + * make loopback_test
> + *
> + * Test is run using following command arguments:-
> + *
> + * loopback_test -c <device_node> -b <transmit buffer size> -l <log level> -i
> + * <number of iterations>
> + *
> + * Required argument:
> + * -c : loopback chardev node
> + *
> + * Optional argument:
> + * -b : transmit buffer size. If not present 1024 bytes size transmit buffer
> + *      is sent.
> + * -i : Number of iterations to perform, If not present only one transmit buffer
> + *      is sent.
> + * -l : Log level. If not present defaults to DBG_LVL_INFO.
> + */
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <getopt.h>
> +#include <pthread.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/poll.h>
> +#include <unistd.h>
> +
> +enum debug_level {
> +	DBG_LVL_VERBOSE,
> +	DBG_LVL_INFO,
> +	DBG_LVL_ERROR,
> +};
> +
> +enum test_status {
> +	TEST_STATUS_SUCCESS,
> +	TEST_STATUS_ERROR,
> +	TEST_STATUS_NO_DEV,
> +};
> +

Since you are running this test as part of kselftest, please use
ksft errors nd returns.

> +struct lb_test_ctx {
> +	char dev_node[256];
> +	unsigned char *tx_buff;
> +	unsigned char *rx_buff;
> +	unsigned int rx_pkt_count;
> +	unsigned int tx_pkt_count;
> +	int iterations;
> +	bool iter_complete;
> +	bool comp_complete;
> +	bool test_complete;
> +	bool all_complete;
> +	unsigned long buff_size;
> +	long byte_recvd;
> +	long byte_sent;
> +};
> +
> +bool force_exit;
> +char write_data = 'a';
> +int completed_iterations;
> +
> +struct lb_test_ctx test_ctxt;
> +enum debug_level msg_lvl;
> +struct pollfd read_fd;
> +struct pollfd write_fd;
> +enum test_status mhi_test_return_value;
> +enum test_status tx_status;
> +enum test_status rx_status;
> +enum test_status cmp_rxtx_status;
> +
> +#define test_log(test_msg_lvl, format, ...) do { \
> +		if (test_msg_lvl >= msg_lvl) \
> +			fprintf(stderr, format, ##__VA_ARGS__); \
> +} while (0)
> +
> +static void loopback_test_sleep_ms(int ms)
> +{
> +	usleep(1000 * ms);
> +}
> +

Have you run this as part of "make kselftest" run. How does this
sleep work in that env.?

> +static enum test_status loopback_test_open(struct pollfd *write_fd,
> +					struct pollfd *read_fd, char *dev_name)
> +{
> +	enum test_status ret_val = TEST_STATUS_SUCCESS;
> +
> +	write_fd->fd = open(dev_name, O_SYNC | O_WRONLY);
> +	if (write_fd->fd < 0) {
> +		test_log(DBG_LVL_ERROR, "Error opening file for w: errno: %d\n", errno);
> +		ret_val = TEST_STATUS_ERROR;
> +	} else {
> +		test_log(DBG_LVL_INFO, "Opened %s, return code: %i.\n",
> +			 dev_name, write_fd->fd);
> +		write_fd->events = POLLOUT;
> +	}
> +
> +	read_fd->fd = open(dev_name, O_SYNC | O_RDONLY);
> +	if (read_fd->fd < 0) {
> +		test_log(DBG_LVL_ERROR, "Error opening file for r: errno: %d\n", errno);
> +		ret_val = TEST_STATUS_ERROR;
> +	} else {
> +		test_log(DBG_LVL_INFO, "Opened %s, return code: %i.\n",
> +			 dev_name, read_fd->fd);
> +		read_fd->events = POLLIN;
> +	}
> +
> +	return ret_val;
> +}
> +
> +static enum test_status loopback_test_close(struct pollfd *write_fd,
> +						 struct pollfd *read_fd)
> +{
> +	enum test_status ret_val = TEST_STATUS_SUCCESS;
> +
> +	if (close(read_fd->fd) == 0) {
> +		test_log(DBG_LVL_INFO, "Closed device handle\n");
> +	} else {
> +		test_log(DBG_LVL_ERROR, "Unable to close handle to device\n");
> +		ret_val = TEST_STATUS_ERROR;
> +	}
> +
> +	if (close(write_fd->fd) == 0) {
> +		test_log(DBG_LVL_INFO, "Closed device handle\n");
> +	} else {
> +		test_log(DBG_LVL_ERROR, "Unable to close handle to device\n");
> +		ret_val = TEST_STATUS_ERROR;
> +	}
> +

This should match the KSFT error codes

> +	return ret_val;
> +}
> +
> +static enum test_status loopback_test_alloc_rx_tx_buff(void)
> +{
> +	test_ctxt.rx_buff = (unsigned char *)malloc(test_ctxt.buff_size);
> +	if (!test_ctxt.rx_buff) {
> +		test_log(DBG_LVL_ERROR, "Failed to allocate rx buff\n");
> +		return TEST_STATUS_ERROR;
> +	}
> +
> +	memset(test_ctxt.rx_buff, 0x00, sizeof(test_ctxt.buff_size));
> +	test_ctxt.tx_buff = (unsigned char *)malloc(test_ctxt.buff_size);
> +	if (!test_ctxt.tx_buff) {
> +		test_log(DBG_LVL_ERROR, "Failed to allocate tx buff\n");
> +		return TEST_STATUS_ERROR;
> +	}
> +
> +	return TEST_STATUS_SUCCESS;
> +}
> +
> +static enum test_status loopback_test_write(struct pollfd *write_fd)
> +{
> +	enum test_status ret_value = TEST_STATUS_SUCCESS;
> +	signed long temp_bytes_sent = 0;
> +	signed long total_bytes_sent = 0;
> +
> +	test_log(DBG_LVL_VERBOSE, "About to write %lu\n", test_ctxt.buff_size);
> +
> +	temp_bytes_sent = write(write_fd->fd,
> +		test_ctxt.tx_buff + test_ctxt.byte_sent, test_ctxt.buff_size);
> +
> +	test_log(DBG_LVL_VERBOSE, "Returned %li\n", temp_bytes_sent);
> +	test_log(DBG_LVL_INFO, "Wrote packet with %li bytes of %02x\n",
> +		 temp_bytes_sent, *(test_ctxt.tx_buff + test_ctxt.byte_sent));
> +
> +	if (temp_bytes_sent < 0) {
> +		/* write returned a negative value i.e. write failed */
> +		test_log(DBG_LVL_ERROR, "write() failed: err code %d\n", errno);
> +		return TEST_STATUS_ERROR;
> +	}
> +
> +	if ((unsigned long)temp_bytes_sent > test_ctxt.buff_size) {
> +		test_log(DBG_LVL_ERROR, "Wrote more bytes than intended\n");
> +		return TEST_STATUS_ERROR;
> +	}
> +
> +	if (temp_bytes_sent == 0) {
> +		loopback_test_sleep_ms(10);
> +	} else if (temp_bytes_sent > 0) {
> +		test_ctxt.tx_pkt_count++;
> +		test_ctxt.byte_sent += temp_bytes_sent;
> +		total_bytes_sent += temp_bytes_sent;
> +		test_log(DBG_LVL_INFO, "Total written packets %d\n",
> +			 test_ctxt.tx_pkt_count);
> +	}
> +
> +	return ret_value;
> +}
> +
> +static void loopback_test_reset_ctxt(void)
> +{
> +	test_ctxt.byte_sent = 0;
> +	__sync_synchronize();
> +	test_ctxt.byte_recvd = 0;
> +	test_ctxt.rx_pkt_count = 0;
> +	test_ctxt.tx_pkt_count = 0;
> +	test_ctxt.comp_complete = false;
> +	memset((void *)test_ctxt.rx_buff, 0x00, test_ctxt.buff_size);
> +}
> +
> +static enum test_status loopback_test_time_out(int *time_count)
> +{
> +	enum test_status ret_val = TEST_STATUS_SUCCESS;
> +
> +	loopback_test_sleep_ms(3);
> +	if ((++*time_count) > 100000) {
> +		test_log(DBG_LVL_ERROR, "Error: all bytes not received for this iteration\n");
> +		ret_val = TEST_STATUS_ERROR;
> +		while (1)
> +			loopback_test_sleep_ms(1000);
> +	}
> +
> +	return ret_val;
> +}
> +
> +static enum test_status loopback_test_poll(struct pollfd *fd,
> +						bool poll_timeout)
> +{
> +	enum test_status ret_val = TEST_STATUS_SUCCESS;
> +	int poll_return = 0;
> +	int i = 0;
> +	int timeout = 1000; /* poll-wait 1 second */
> +	int file_desc_type = -1;
> +
> +	if (fd->events == POLLIN) {
> +		file_desc_type = 1; /* read */
> +		test_log(DBG_LVL_VERBOSE,
> +			 "Poll: opened read handle to device\n");
> +	} else if (fd->events == POLLOUT) {
> +		file_desc_type = 0; /* write */
> +		test_log(DBG_LVL_VERBOSE,
> +			 "Poll: opened write handle to device\n");
> +	}
> +
> +	if (poll_timeout == false)
> +		timeout = -1; /* poll-wait forever */
> +
> +	while (ret_val == TEST_STATUS_SUCCESS) {
> +		if (force_exit == true) {
> +			ret_val = TEST_STATUS_ERROR;
> +		} else {
> +			test_log(DBG_LVL_VERBOSE, "%s Polling %i\n",
> +				 file_desc_type ? "Read" : "Write", i);
> +
> +			poll_return = poll(fd, 1, timeout);
> +			if (poll_return == -1) {
> +				test_log(DBG_LVL_ERROR, "%s Poll error - %d\n",
> +					 file_desc_type ? "Read" : "Write",
> +					 errno);
> +				return TEST_STATUS_ERROR;
> +			} else if (((file_desc_type == 1) &&
> +				   (fd->revents & POLLIN)) ||
> +				   ((file_desc_type == 0) &&
> +				   (fd->revents & POLLOUT))) {
> +				test_log(DBG_LVL_VERBOSE,
> +					 "%s Poll Returned\n",
> +					 file_desc_type ? "Read" : "Write");
> +				return TEST_STATUS_SUCCESS;
> +			} else if (poll_timeout == true) {
> +				i++;
> +				poll_return = 0;
> +				if (i >= 250) {
> +					test_log(DBG_LVL_ERROR,
> +						 "Poll: %s Timed out: 10s\n",
> +					file_desc_type ? "Read" : "Write");
> +					return TEST_STATUS_ERROR;
> +				}
> +			}
> +
> +			loopback_test_sleep_ms(10);
> +		}
> +	}
> +
> +	return ret_val;
> +}
> +
> +static void *loopback_test_rx(void *data)
> +{
> +	bool poll_timeout = true;
> +	unsigned long read_size = test_ctxt.buff_size;
> +	long temp_bytes_rec;
> +	enum test_status status;
> +
> +	rx_status = TEST_STATUS_SUCCESS;
> +
> +	while (force_exit == false) {
> +		if (test_ctxt.test_complete) {
> +			test_log(DBG_LVL_INFO, "Rx: exiting thread\n");
> +			pthread_exit(&rx_status);
> +			return NULL;
> +		}
> +
> +		while ((test_ctxt.byte_sent > test_ctxt.byte_recvd) &&
> +		       (force_exit == false)) {
> +			test_log(DBG_LVL_VERBOSE,
> +				 "Rx: Bytes Sent: %li, Received %li\n",
> +				test_ctxt.byte_sent, test_ctxt.byte_recvd);
> +
> +			temp_bytes_rec = 0;
> +
> +			if (test_ctxt.comp_complete == false) {
> +				status = loopback_test_poll(&read_fd,
> +							    poll_timeout);
> +				if (status == TEST_STATUS_ERROR) {
> +					if (test_ctxt.test_complete == true)
> +						rx_status = TEST_STATUS_SUCCESS;
> +					else
> +						rx_status = TEST_STATUS_ERROR;
> +
> +					test_log(DBG_LVL_INFO,
> +						 "Rx: exiting thread\n");
> +					pthread_exit(&rx_status);
> +					return NULL;
> +				}
> +
> +				test_log(DBG_LVL_VERBOSE, "Rx: before read\n");
> +				temp_bytes_rec = read(read_fd.fd,
> +					test_ctxt.rx_buff + test_ctxt.byte_recvd,
> +					read_size);
> +				if (temp_bytes_rec < 0) {
> +					test_log(DBG_LVL_ERROR,
> +					"Rx: read() failed - err code %d\n",
> +					errno);
> +					rx_status = TEST_STATUS_ERROR;
> +					pthread_exit(&rx_status);
> +					return NULL;
> +				}
> +
> +				if (temp_bytes_rec == 0) {
> +					loopback_test_sleep_ms(10);
> +					rx_status = TEST_STATUS_ERROR;
> +					pthread_exit(&rx_status);
> +					return NULL;
> +				}
> +
> +				test_ctxt.rx_pkt_count++;
> +				test_log(DBG_LVL_INFO,
> +				"Rx: Read packet with %li bytes of %02x\n",
> +				temp_bytes_rec,
> +				*(test_ctxt.rx_buff + test_ctxt.byte_recvd));
> +				test_ctxt.byte_recvd += temp_bytes_rec;
> +				test_log(DBG_LVL_INFO,
> +					 "Rx: Total byte_recvd: %li\n",
> +					test_ctxt.byte_recvd);
> +				test_log(DBG_LVL_INFO,
> +					 "Rx: Total read packets %d\n",
> +					test_ctxt.rx_pkt_count);
> +				test_log(DBG_LVL_INFO,
> +					 "Rx: %lu byte buff_size\n",
> +					test_ctxt.buff_size);
> +
> +				read_fd.revents = 0;
> +			}
> +		}
> +	}
> +	rx_status = TEST_STATUS_ERROR;
> +	pthread_exit(&rx_status);
> +	return NULL;
> +}
> +
> +/* This single compare thread, will compare each byte of tx and rx Buff */
> +static void *loopback_test_compare_rxtx(void *data)
> +{
> +	int k;
> +	bool completion = true;
> +	long curr_ptr = 0;
> +
> +	test_log(DBG_LVL_INFO, "CompareRxTxThread: Inside thread\n");
> +	cmp_rxtx_status = TEST_STATUS_SUCCESS;
> +
> +	for (;;) {
> +		/* if the test is completed exit thread */
> +		if (test_ctxt.all_complete == true) {
> +			curr_ptr = 0;
> +			test_log(DBG_LVL_INFO,
> +				 "CompareRxTx: exiting thread\n");
> +			pthread_exit(&cmp_rxtx_status);
> +			return NULL;
> +		}
> +
> +		/* Simply compares each byte in each Tx and Rx buffer */
> +		while (curr_ptr < test_ctxt.byte_recvd) {
> +			if (*(test_ctxt.rx_buff + curr_ptr) !=
> +					*(test_ctxt.tx_buff + curr_ptr)) {
> +				test_log(DBG_LVL_ERROR,
> +					 "Mismatch Byte num %li\n", curr_ptr);
> +				test_log(DBG_LVL_ERROR,
> +					 "Bytes: Sent 0x%x, Received 0x%x\n",
> +					 *(test_ctxt.tx_buff + curr_ptr),
> +					 *(test_ctxt.rx_buff + curr_ptr));
> +				test_log(DBG_LVL_VERBOSE, "Rx buffer dump:\n");
> +				for (k = 0; k < 1024; k++)
> +					test_log(DBG_LVL_VERBOSE, "%d:0x%x ", k,
> +						 *(test_ctxt.rx_buff + k));
> +
> +				test_log(DBG_LVL_VERBOSE, "\n");
> +
> +				test_log(DBG_LVL_INFO,
> +					 "CompareRxTx: finished iterations %i\n",
> +					completed_iterations);
> +				test_log(DBG_LVL_INFO,
> +					 "CompareRxTx: Total pkts sent %u\n",
> +					test_ctxt.tx_pkt_count);
> +
> +				curr_ptr = 0;
> +				force_exit = true;
> +				cmp_rxtx_status = TEST_STATUS_ERROR;
> +				pthread_exit(&cmp_rxtx_status);
> +				return NULL;
> +			}
> +
> +			test_log(DBG_LVL_VERBOSE,
> +			"CompareRxTx: Bytes Sent 0x%x, Received 0x%x\n",
> +			*(test_ctxt.tx_buff + curr_ptr),
> +			*(test_ctxt.rx_buff + curr_ptr));
> +			curr_ptr++;
> +		}
> +
> +		/*
> +		 * completion will start as true and change depending if the
> +		 * iterations has been completed for all client threads
> +		 */
> +		if (curr_ptr >= test_ctxt.byte_sent)
> +			completion = (completion && test_ctxt.iter_complete);
> +		else
> +			completion = false;
> +
> +		/*
> +		 * Once all client threads have finished their iteration, reset
> +		 * everything and allow the client thread to start their next
> +		 * iteration
> +		 */
> +		if (completion) {
> +			test_ctxt.byte_sent = 0;
> +			__sync_synchronize();
> +			test_ctxt.byte_recvd = 0;
> +			test_ctxt.iter_complete = false;
> +			test_ctxt.comp_complete = true;
> +			curr_ptr = 0;
> +			test_log(DBG_LVL_INFO, "CompareRxTx: TX and RX match\n");
> +		} else {
> +			completion = true;
> +		}
> +	}
> +}
> +
> +static void loopback_test_display_err_data(void)
> +{
> +	test_log(DBG_LVL_ERROR, "Wrote %lu bytes out of a %lu buff size\n",
> +		 test_ctxt.byte_sent, test_ctxt.buff_size);
> +	test_log(DBG_LVL_ERROR, "Remaining bytes to write %lu\n",
> +		 (test_ctxt.buff_size - test_ctxt.byte_sent));
> +}
> +
> +static enum test_status loopback_test_setup_write_packet(void)
> +{
> +	bool poll_timeout = true;
> +
> +	if (loopback_test_poll(&write_fd, poll_timeout) == TEST_STATUS_ERROR)
> +		return TEST_STATUS_ERROR;
> +
> +	test_log(DBG_LVL_VERBOSE, "revents: %d\n", write_fd.revents);
> +
> +	if (write_fd.revents & POLLOUT) {
> +		memset(test_ctxt.tx_buff + test_ctxt.byte_sent, write_data,
> +		       test_ctxt.buff_size);
> +		test_log(DBG_LVL_VERBOSE, "Write %02x of size %lu\n", write_data,
> +			 test_ctxt.buff_size);
> +
> +		if (loopback_test_write(&write_fd) != TEST_STATUS_SUCCESS) {
> +			loopback_test_sleep_ms(1000);
> +			return TEST_STATUS_ERROR;
> +		}
> +
> +		test_log(DBG_LVL_INFO, "Total byte_sent %li\n",
> +			 test_ctxt.byte_sent);
> +		write_fd.revents = 0;
> +	}
> +
> +	return TEST_STATUS_SUCCESS;
> +}
> +
> +static enum test_status loopback_test_wait_to_get_bytes_back(void)
> +{
> +	int time_count = 0;
> +
> +	test_log(DBG_LVL_INFO, "Sent %li bytes, received %li bytes\n",
> +		 test_ctxt.byte_sent, test_ctxt.byte_recvd);
> +
> +	/* read thread will fill bytesRec and wait till it equals byte_sent */
> +	while (test_ctxt.byte_recvd != test_ctxt.byte_sent) {
> +		if (loopback_test_time_out(&time_count) == TEST_STATUS_ERROR)
> +			return TEST_STATUS_ERROR;
> +	}

Same here.

> +
> +	return TEST_STATUS_SUCCESS;
> +}
Here too

> +
> +static void *loopback_test_tx(void *data)
> +{
> +	int i;
> +	int time_count = 0;
> +	int failed_iterations = 0;
> +	enum test_status status = TEST_STATUS_SUCCESS;
> +
> +	tx_status = TEST_STATUS_SUCCESS;
> +
> +	for (i = 0; i < test_ctxt.iterations; i++) {
> +		loopback_test_reset_ctxt();
> +
> +		test_ctxt.iter_complete = false;
> +
> +		/* While the bytes sent is less than the buffsize */
> +		while (((unsigned int)test_ctxt.byte_sent < test_ctxt.buff_size)
> +			&& (status == TEST_STATUS_SUCCESS)) {
> +			if (force_exit == true)
> +				goto exit;
> +
> +			if (status != TEST_STATUS_SUCCESS) {
> +				loopback_test_display_err_data();
> +				tx_status = TEST_STATUS_ERROR;
> +				goto exit;
> +			}
> +
> +			status = loopback_test_setup_write_packet();
> +			if (status == TEST_STATUS_ERROR) {
> +				test_log(DBG_LVL_INFO,
> +					 "Failed after %d iterations\n", i);
> +				failed_iterations++;
> +				goto exit;
> +			}
> +		}
> +
> +		if (loopback_test_wait_to_get_bytes_back() ==
> +			TEST_STATUS_ERROR) {
> +			tx_status = TEST_STATUS_ERROR;
> +			goto exit;
> +		}
> +
> +		__sync_synchronize();
> +
> +		test_ctxt.iter_complete = true;
> +
> +		while (!test_ctxt.comp_complete) {
> +			if (loopback_test_time_out(&time_count) ==
> +				TEST_STATUS_ERROR) {
> +				tx_status = TEST_STATUS_ERROR;
> +				goto exit;
> +			}
> +		}
> +		time_count = 0;
> +	}
> +
> +	if (failed_iterations > 0)
> +		test_log(DBG_LVL_INFO, "Amount of Failed Iterations: %d\n",
> +			 failed_iterations);
> +
> +exit:
> +	loopback_test_reset_ctxt();
> +
> +	/* all of the iterations for this client thread have been completed */
> +	test_ctxt.test_complete = true;
> +
> +	/* close the opened file descriptors */
> +	loopback_test_close(&write_fd, &read_fd);
> +
> +	test_log(DBG_LVL_INFO, "mhi_test_loopback: exiting thread\n");
> +
> +	pthread_exit(&tx_status);
> +
> +	return NULL;
> +}
> +
> +/*
> + * This function will be called when the user wishes to perform a loopback test.
> + * In this function, a compare thread will be created to compare the tx buffer
> + * and the rx Buffer. Device file node is opened for write and read. Tx thread
> + * is created to send data and Rx thread to receive data.
> + */
> +enum test_status loopback_test_thread_create(void)
> +{
> +	int i = 0;
> +	enum test_status status = TEST_STATUS_SUCCESS;
> +	enum test_status *write_thread_retval;
> +	enum test_status *read_thread_retval;
> +	enum test_status *compare_thread_retval;
> +
> +	pthread_t compare_rx_tx_thread;
> +	pthread_t loopback_thread;
> +	pthread_t receive_thread;
> +
> +	/* set all tests (including open/close) to not completed */
> +	test_ctxt.all_complete = false;
> +
> +	/* set current test to not completed */
> +	test_ctxt.test_complete = false;
> +
> +	if (loopback_test_alloc_rx_tx_buff() == TEST_STATUS_ERROR) {
> +		status = TEST_STATUS_ERROR;
> +		goto exit;
> +	}
> +
> +	/* Create thread for comparing the rx and tx buffers */
> +	if (pthread_create(&compare_rx_tx_thread, NULL,
> +			   loopback_test_compare_rxtx, NULL) != 0) {
> +		test_log(DBG_LVL_ERROR, "Error creating compare thread\n");
> +		status = TEST_STATUS_ERROR;
> +		goto exit;
> +	}
> +
> +	test_log(DBG_LVL_INFO, "dev_node: %s\n", test_ctxt.dev_node);
> +
> +	/* closing the device node will happen in the loopback thread */
> +	status = loopback_test_open(&write_fd, &read_fd, test_ctxt.dev_node);
> +	if (status == TEST_STATUS_ERROR) {
> +		test_log(DBG_LVL_ERROR, "Failed to open device node\n");
> +		goto exit;
> +	}
> +
> +	/* Spawn the loopback tx thread */
> +	if (pthread_create(&loopback_thread, NULL, loopback_test_tx, NULL)
> +			   != 0) {
> +		test_log(DBG_LVL_ERROR, "Error creating tx thread");
> +		status = TEST_STATUS_ERROR;
> +		goto exit;
> +	}
> +
> +	/* Create thread for receiving packets */
> +	if (pthread_create(&receive_thread, NULL, loopback_test_rx, NULL)
> +			   != 0) {
> +		test_log(DBG_LVL_ERROR, "Error creating rx thread\n");
> +		status = TEST_STATUS_ERROR;
> +		goto exit;
> +	}
> +
> +	test_log(DBG_LVL_INFO, "Waiting for Tx and Rx threads to complete\n");
> +
> +	/* wait till all client and receive threads have finished */
> +	pthread_join(loopback_thread, (void **)(&write_thread_retval));
> +	pthread_join(receive_thread, (void **)(&read_thread_retval));
> +
> +	test_log(DBG_LVL_VERBOSE, "TX and Rx threads completed\n");
> +
> +	test_log(DBG_LVL_INFO, "Write thread status: %s\n",
> +		 *write_thread_retval ? "Fail" : "Pass");
> +	test_log(DBG_LVL_INFO, "Read thread status: %s\n",
> +		 *read_thread_retval ? "Fail" : "Pass");
> +
> +	if ((*write_thread_retval == TEST_STATUS_ERROR) ||
> +	    (*read_thread_retval == TEST_STATUS_ERROR)) {
> +		test_log(DBG_LVL_ERROR, "Returned error, exiting\n");
> +		status = TEST_STATUS_ERROR;
> +		goto exit;
> +	}
> +
> +	test_log(DBG_LVL_INFO, "Thread %i of tests complete\n", i);
> +	test_ctxt.test_complete = false;
> +
> +	test_log(DBG_LVL_INFO, "All loopback threads completed\n");
> +
> +	test_ctxt.all_complete = true;
> +
> +	test_log(DBG_LVL_INFO, "Waiting for comp thread to exit\n");
> +
> +	pthread_join(compare_rx_tx_thread, (void **)(&compare_thread_retval));
> +	if (*compare_thread_retval == TEST_STATUS_ERROR) {
> +		test_log(DBG_LVL_ERROR, "Compare thread returned error\n");
> +		status = TEST_STATUS_ERROR;
> +	}
> +
> +exit:
> +	/* All threads have finished using the Tx and Rx Buffers, free them */
> +	free(test_ctxt.tx_buff);
> +	test_ctxt.tx_buff = NULL;
> +	free(test_ctxt.rx_buff);
> +	test_ctxt.rx_buff = NULL;
> +
> +	return status;
> +}
> +
> +static void *loopback_test_run(void *data)
> +{
> +	enum test_status status = TEST_STATUS_SUCCESS;
> +
> +	force_exit = false;
> +	srand(time(NULL));
> +
> +	status = loopback_test_thread_create();
> +
> +	pthread_exit(&status);
> +
> +	return NULL;
> +}
> +
> +static void loopback_test_set_defaults(void)
> +{
> +	msg_lvl = DBG_LVL_INFO;
> +
> +	memset(&test_ctxt, 0, sizeof(test_ctxt));
> +	test_ctxt.iterations = 1;
> +	test_ctxt.buff_size = 1024;
> +}
> +
> +static void usage(void)
> +{
> +	test_log(DBG_LVL_INFO, "\nUsage:\n");
> +	test_log(DBG_LVL_INFO, "-c: Device node\n");
> +	test_log(DBG_LVL_INFO, "-b: Buffer size (default: 1024 bytes)\n");
> +	test_log(DBG_LVL_INFO, "-i: Number of iterations\n");
> +	test_log(DBG_LVL_INFO, "-l: log level, default - 1\n");
> +	test_log(DBG_LVL_INFO, "log level : 0 -verbose, 1 -info , 2 -err\n");
> +}
> +
> +static int loopback_test_parse(int argc, char *argv[])
> +{
> +	int i = 0;
> +	int command = 0;
> +	int indexptr = 0;
> +	char *optstr = "-:c:b:i:l:";
> +	int *return_value = NULL;
> +	int status = -EINVAL;
> +	pthread_t run_test;
> +
> +	optind = 1;
> +
> +	static const struct option optlongstr[] = {
> +			{ "device node", required_argument, NULL, 'c'},
> +			{0, 0, 0, 0}
> +	};
> +
> +	test_log(DBG_LVL_INFO, "\nStarting MHI loopback tests\n");
> +
> +	test_log(DBG_LVL_VERBOSE, "argc: %d\n", argc);
> +	for (i = 0; i < argc; i++)
> +		test_log(DBG_LVL_VERBOSE, "argv[%i] = %s; ", i, argv[i]);
> +
> +	while ((command = getopt_long(argc, (char **)argv, optstr, optlongstr,
> +				      &indexptr)) && (command != -1)) {
> +		switch (command) {
> +		case 'c':
> +			sprintf(test_ctxt.dev_node, "%s", optarg);
> +			test_log(DBG_LVL_INFO, "%s\n", test_ctxt.dev_node);
> +			break;
> +		case 'b':
> +			test_ctxt.buff_size = atoi(optarg);
> +			test_log(DBG_LVL_INFO, "%lu\n", test_ctxt.buff_size);
> +			break;
> +		case 'i':
> +			test_ctxt.iterations = atoi(optarg);
> +			test_log(DBG_LVL_INFO, "%u\n", test_ctxt.iterations);
> +			break;
> +		case 'l':
> +			msg_lvl = atoi(optarg);
> +			test_log(DBG_LVL_INFO, "log level: %d\n", atoi(optarg));
> +			break;
> +		default:
> +			test_log(DBG_LVL_ERROR, "Invalid Option: %d\n",
> +				 command);
> +			usage();
> +			return TEST_STATUS_ERROR;
> +		}
> +	}
> +
> +	if (pthread_create(&run_test, NULL, loopback_test_run, NULL) != 0) {
> +		test_log(DBG_LVL_ERROR, "Error creating run_mhi_test\n");
> +	} else {
> +		pthread_join(run_test, (void **)(&return_value));
> +
> +		if (*return_value != TEST_STATUS_SUCCESS) {
> +			test_log(DBG_LVL_ERROR, "Test Failed\n");
> +		} else {
> +			test_log(DBG_LVL_INFO, "Test Passed\n");
> +			status = 0;
> +		}
> +	}
> +
> +	return status;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> +	int ret = 0;
> +
> +	loopback_test_set_defaults();
> +	test_log(DBG_LVL_INFO, "MHI Loopback test App\n");
> +
> +	if (argc > 1)
> +		ret = loopback_test_parse(argc, argv);
> +	else
> +		usage();
> +
> +	return ret;
> +}
> 

Are there any cases where this test can't run and have to - those
cases need to be skips.

thanks,
-- Shuah

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ