aboutsummaryrefslogtreecommitdiffstats
path: root/greybus_tools.patch
diff options
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-09-15 13:54:11 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-09-15 13:54:11 +0200
commitc1aa01c96e590714d99c5f17cfae1b14dec8bdee (patch)
tree5811422f65efdff3fd89fdaf9a8c6d7d1cc4d06b /greybus_tools.patch
parent4758ee8bc89a86c2110b9e85878538ced8045ef5 (diff)
downloadpatches-c1aa01c96e590714d99c5f17cfae1b14dec8bdee.tar.gz
greybus patches
Diffstat (limited to 'greybus_tools.patch')
-rw-r--r--greybus_tools.patch1435
1 files changed, 1435 insertions, 0 deletions
diff --git a/greybus_tools.patch b/greybus_tools.patch
new file mode 100644
index 00000000000000..e24e3689a99373
--- /dev/null
+++ b/greybus_tools.patch
@@ -0,0 +1,1435 @@
+---
+ drivers/greybus/tools/.gitignore | 1
+ drivers/greybus/tools/Android.mk | 10
+ drivers/greybus/tools/Makefile | 31 +
+ drivers/greybus/tools/README.loopback | 198 ++++++
+ drivers/greybus/tools/lbtest | 168 +++++
+ drivers/greybus/tools/loopback_test.c | 1000 ++++++++++++++++++++++++++++++++++
+ 6 files changed, 1408 insertions(+)
+
+--- /dev/null
++++ b/drivers/greybus/tools/.gitignore
+@@ -0,0 +1 @@
++loopback_test
+--- /dev/null
++++ b/drivers/greybus/tools/Android.mk
+@@ -0,0 +1,10 @@
++LOCAL_PATH:= $(call my-dir)
++
++include $(CLEAR_VARS)
++
++LOCAL_SRC_FILES:= loopback_test.c
++LOCAL_MODULE_TAGS := optional
++LOCAL_MODULE := gb_loopback_test
++
++include $(BUILD_EXECUTABLE)
++
+--- /dev/null
++++ b/drivers/greybus/tools/Makefile
+@@ -0,0 +1,31 @@
++ifeq ($(strip $(V)), 1)
++ Q =
++else
++ Q = @
++endif
++
++CFLAGS += -std=gnu99 -Wall -Wextra -g \
++ -D_GNU_SOURCE \
++ -Wno-unused-parameter \
++ -Wmaybe-uninitialized \
++ -Wredundant-decls \
++ -Wcast-align \
++ -Wsign-compare \
++ -Wno-missing-field-initializers
++
++CC := $(CROSS_COMPILE)gcc
++
++TOOLS = loopback_test
++
++all: $(TOOLS)
++
++%.o: %.c ../greybus_protocols.h
++ @echo ' TARGET_CC $@'
++ $(Q)$(CC) $(CFLAGS) -c $< -o $@
++
++loopback_%: loopback_%.o
++ @echo ' TARGET_LD $@'
++ $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
++
++clean::
++ rm -f *.o $(TOOLS)
+--- /dev/null
++++ b/drivers/greybus/tools/README.loopback
+@@ -0,0 +1,198 @@
++
++
++ 1 - LOOPBACK DRIVER
++
++The driver implements the main logic of the loopback test and provides
++sysfs files to configure the test and retrieve the results.
++A user could run a test without the need of the test application given
++that he understands the sysfs interface of the loopback driver.
++
++The loopback kernel driver needs to be loaded and at least one module
++with the loopback feature enabled must be present for the sysfs files to be
++created and for the loopback test application to be able to run.
++
++To load the module:
++# modprobe gb-loopback
++
++
++When the module is probed, New files are available on the sysfs
++directory of the detected loopback device.
++(typically under "/sys/bus/graybus/devices").
++
++Here is a short summary of the sysfs interface files that should be visible:
++
++* Loopback Configuration Files:
++ async - Use asynchronous operations.
++ iteration_max - Number of tests iterations to perform.
++ size - payload size of the transfer.
++ timeout - The number of microseconds to give an individual
++ asynchronous request before timing out.
++ us_wait - Time to wait between 2 messages
++ type - By writing the test type to this file, the test starts.
++ Valid tests are:
++ 0 stop the test
++ 2 - ping
++ 3 - transfer
++ 4 - sink
++
++* Loopback feedback files:
++ error - number of errors that have occurred.
++ iteration_count - Number of iterations performed.
++ requests_completed - Number of requests successfully completed.
++ requests_timedout - Number of requests that have timed out.
++ timeout_max - Max allowed timeout
++ timeout_min - Min allowed timeout.
++
++* Loopback result files:
++ apbridge_unipro_latency_avg
++ apbridge_unipro_latency_max
++ apbridge_unipro_latency_min
++ gpbridge_firmware_latency_avg
++ gpbridge_firmware_latency_max
++ gpbridge_firmware_latency_min
++ requests_per_second_avg
++ requests_per_second_max
++ requests_per_second_min
++ latency_avg
++ latency_max
++ latency_min
++ throughput_avg
++ throughput_max
++ throughput_min
++
++
++
++ 2 - LOOPBACK TEST APPLICATION
++
++The loopback test application manages and formats the results provided by
++the loopback kernel module. The purpose of this application
++is to:
++ - Start and manage multiple loopback device tests concurrently.
++ - Calculate the aggregate results for multiple devices.
++ - Gather and format test results (csv or human readable).
++
++The best way to get up to date usage information for the application is
++usually to pass the "-h" parameter.
++Here is the summary of the available options:
++
++ Mandatory arguments
++ -t must be one of the test names - sink, transfer or ping
++ -i iteration count - the number of iterations to run the test over
++ Optional arguments
++ -S sysfs location - location for greybus 'endo' entires default /sys/bus/greybus/devices/
++ -D debugfs location - location for loopback debugfs entries default /sys/kernel/debug/gb_loopback/
++ -s size of data packet to send during test - defaults to zero
++ -m mask - a bit mask of connections to include example: -m 8 = 4th connection -m 9 = 1st and 4th connection etc
++ default is zero which means broadcast to all connections
++ -v verbose output
++ -d debug output
++ -r raw data output - when specified the full list of latency values are included in the output CSV
++ -p porcelain - when specified printout is in a user-friendly non-CSV format. This option suppresses writing to CSV file
++ -a aggregate - show aggregation of all enabled devies
++ -l list found loopback devices and exit.
++ -x Async - Enable async transfers.
++ -o Timeout - Timeout in microseconds for async operations.
++
++
++
++ 3 - REAL WORLD EXAMPLE USAGES
++
++ 3.1 - Using the driver sysfs files to run a test on a single device:
++
++* Run a 1000 transfers of a 100 byte packet. Each transfer is started only
++after the previous one finished successfully:
++ echo 0 > /sys/bus/greybus/devices/1-2.17/type
++ echo 0 > /sys/bus/greybus/devices/1-2.17/async
++ echo 2000 > /sys/bus/greybus/devices/1-2.17/us_wait
++ echo 100 > /sys/bus/greybus/devices/1-2.17/size
++ echo 1000 > /sys/bus/greybus/devices/1-2.17/iteration_max
++ echo 0 > /sys/bus/greybus/devices/1-2.17/mask
++ echo 200000 > /sys/bus/greybus/devices/1-2.17/timeout
++ echo 3 > /sys/bus/greybus/devices/1-2.17/type
++
++* Run a 1000 transfers of a 100 byte packet. Transfers are started without
++waiting for the previous one to finish:
++ echo 0 > /sys/bus/greybus/devices/1-2.17/type
++ echo 3 > /sys/bus/greybus/devices/1-2.17/async
++ echo 0 > /sys/bus/greybus/devices/1-2.17/us_wait
++ echo 100 > /sys/bus/greybus/devices/1-2.17/size
++ echo 1000 > /sys/bus/greybus/devices/1-2.17/iteration_max
++ echo 0 > /sys/bus/greybus/devices/1-2.17/mask
++ echo 200000 > /sys/bus/greybus/devices/1-2.17/timeout
++ echo 3 > /sys/bus/greybus/devices/1-2.17/type
++
++* Read the results from sysfs:
++ cat /sys/bus/greybus/devices/1-2.17/requests_per_second_min
++ cat /sys/bus/greybus/devices/1-2.17/requests_per_second_max
++ cat /sys/bus/greybus/devices/1-2.17/requests_per_second_avg
++
++ cat /sys/bus/greybus/devices/1-2.17/latency_min
++ cat /sys/bus/greybus/devices/1-2.17/latency_max
++ cat /sys/bus/greybus/devices/1-2.17/latency_avg
++
++ cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_min
++ cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_max
++ cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_avg
++
++ cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_min
++ cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_max
++ cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_avg
++
++ cat /sys/bus/greybus/devices/1-2.17/error
++ cat /sys/bus/greybus/devices/1-2.17/requests_completed
++ cat /sys/bus/greybus/devices/1-2.17/requests_timedout
++
++
++3.2 - using the test application:
++
++* Run a transfer test 10 iterations of size 100 bytes on all available devices
++ #/loopback_test -t transfer -i 10 -s 100
++ 1970-1-1 0:10:7,transfer,1-4.17,100,10,0,443,509,471.700012,66,1963,2256,2124.600098,293,102776,118088,109318.898438,15312,1620,1998,1894.099976,378,56,57,56.799999,1
++ 1970-1-1 0:10:7,transfer,1-5.17,100,10,0,399,542,463.399994,143,1845,2505,2175.800049,660,92568,125744,107393.296875,33176,1469,2305,1806.500000,836,56,57,56.799999,1
++
++
++* Show the aggregate results of both devices. ("-a")
++ #/loopback_test -t transfer -i 10 -s 100 -a
++ 1970-1-1 0:10:35,transfer,1-4.17,100,10,0,448,580,494.100006,132,1722,2230,2039.400024,508,103936,134560,114515.703125,30624,1513,1980,1806.900024,467,56,57,57.299999,1
++ 1970-1-1 0:10:35,transfer,1-5.17,100,10,0,383,558,478.600006,175,1791,2606,2115.199951,815,88856,129456,110919.703125,40600,1457,2246,1773.599976,789,56,57,57.099998,1
++ 1970-1-1 0:10:35,transfer,aggregate,100,10,0,383,580,486.000000,197,1722,2606,2077.000000,884,88856,134560,112717.000000,45704,1457,2246,1789.000000,789,56,57,57.000000,1
++
++* Example usage of the mask option to select which devices will
++ run the test (1st, 2nd, or both devices):
++ # /loopback_test -t transfer -i 10 -s 100 -m 1
++ 1970-1-1 0:11:56,transfer,1-4.17,100,10,0,514,558,544.900024,44,1791,1943,1836.599976,152,119248,129456,126301.296875,10208,1600,1001609,101613.601562,1000009,56,57,56.900002,1
++ # /loopback_test -t transfer -i 10 -s 100 -m 2
++ 1970-1-1 0:12:0,transfer,1-5.17,100,10,0,468,554,539.000000,86,1804,2134,1859.500000,330,108576,128528,124932.500000,19952,1606,1626,1619.300049,20,56,57,57.400002,1
++ # /loopback_test -t transfer -i 10 -s 100 -m 3
++ 1970-1-1 0:12:3,transfer,1-4.17,100,10,0,432,510,469.399994,78,1959,2313,2135.800049,354,100224,118320,108785.296875,18096,1610,2024,1893.500000,414,56,57,57.200001,1
++ 1970-1-1 0:12:3,transfer,1-5.17,100,10,0,404,542,468.799988,138,1843,2472,2152.500000,629,93728,125744,108646.101562,32016,1504,2247,1853.099976,743,56,57,57.099998,1
++
++* Show output in human readable format ("-p")
++ # /loopback_test -t transfer -i 10 -s 100 -m 3 -p
++
++ 1970-1-1 0:12:37
++ test: transfer
++ path: 1-4.17
++ size: 100
++ iterations: 10
++ errors: 0
++ async: Disabled
++ requests per-sec: min=390, max=547, average=469.299988, jitter=157
++ ap-throughput B/s: min=90480 max=126904 average=108762.101562 jitter=36424
++ ap-latency usec: min=1826 max=2560 average=2146.000000 jitter=734
++ apbridge-latency usec: min=1620 max=1982 average=1882.099976 jitter=362
++ gpbridge-latency usec: min=56 max=57 average=57.099998 jitter=1
++
++
++ 1970-1-1 0:12:37
++ test: transfer
++ path: 1-5.17
++ size: 100
++ iterations: 10
++ errors: 0
++ async: Disabled
++ requests per-sec: min=397, max=538, average=461.700012, jitter=141
++ ap-throughput B/s: min=92104 max=124816 average=106998.898438 jitter=32712
++ ap-latency usec: min=1856 max=2514 average=2185.699951 jitter=658
++ apbridge-latency usec: min=1460 max=2296 average=1828.599976 jitter=836
++ gpbridge-latency usec: min=56 max=57 average=57.099998 jitter=1
+--- /dev/null
++++ b/drivers/greybus/tools/lbtest
+@@ -0,0 +1,168 @@
++#!/usr/bin/env python
++
++# Copyright (c) 2015 Google, Inc.
++# Copyright (c) 2015 Linaro, Ltd.
++# All rights reserved.
++#
++# Redistribution and use in source and binary forms, with or without
++# modification, are permitted provided that the following conditions are met:
++# 1. Redistributions of source code must retain the above copyright notice,
++# this list of conditions and the following disclaimer.
++# 2. Redistributions in binary form must reproduce the above copyright notice,
++# this list of conditions and the following disclaimer in the documentation
++# and/or other materials provided with the distribution.
++# 3. Neither the name of the copyright holder nor the names of its
++# contributors may be used to endorse or promote products derived from this
++# software without specific prior written permission.
++#
++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
++# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
++# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
++# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
++# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
++# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
++# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
++# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
++# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
++# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++from __future__ import print_function
++import csv
++import datetime
++import sys
++import time
++
++dict = {'ping': '2', 'transfer': '3', 'sink': '4'}
++verbose = 1
++
++def abort():
++ sys.exit(1)
++
++def usage():
++ print('Usage: looptest TEST SIZE ITERATIONS PATH\n\n'
++ ' Run TEST for a number of ITERATIONS with operation data SIZE bytes\n'
++ ' TEST may be \'ping\' \'transfer\' or \'sink\'\n'
++ ' SIZE indicates the size of transfer <= greybus max payload bytes\n'
++ ' ITERATIONS indicates the number of times to execute TEST at SIZE bytes\n'
++ ' Note if ITERATIONS is set to zero then this utility will\n'
++ ' initiate an infinite (non terminating) test and exit\n'
++ ' without logging any metrics data\n'
++ ' PATH indicates the sysfs path for the loopback greybus entries e.g.\n'
++ ' /sys/bus/greybus/devices/endo0:1:1:1:1/\n'
++ 'Examples:\n'
++ ' looptest transfer 128 10000\n'
++ ' looptest ping 0 128\n'
++ ' looptest sink 2030 32768\n'
++ .format(sys.argv[0]), file=sys.stderr)
++
++ abort()
++
++def read_sysfs_int(path):
++ try:
++ f = open(path, "r");
++ val = f.read();
++ f.close()
++ return int(val)
++ except IOError as e:
++ print("I/O error({0}): {1}".format(e.errno, e.strerror))
++ print("Invalid path %s" % path)
++
++def write_sysfs_val(path, val):
++ try:
++ f = open(path, "r+")
++ f.write(val)
++ f.close()
++ except IOError as e:
++ print("I/O error({0}): {1}".format(e.errno, e.strerror))
++ print("Invalid path %s" % path)
++
++def log_csv(test_name, size, iteration_max, sys_pfx):
++ # file name will test_name_size_iteration_max.csv
++ # every time the same test with the same parameters is run we will then
++ # append to the same CSV with datestamp - representing each test dataset
++ fname = test_name + '_' + size + '_' + str(iteration_max) + '.csv'
++
++ try:
++ # gather data set
++ date = str(datetime.datetime.now())
++ error = read_sysfs_int(sys_pfx + 'error')
++ request_min = read_sysfs_int(sys_pfx + 'requests_per_second_min')
++ request_max = read_sysfs_int(sys_pfx + 'requests_per_second_max')
++ request_avg = read_sysfs_int(sys_pfx + 'requests_per_second_avg')
++ latency_min = read_sysfs_int(sys_pfx + 'latency_min')
++ latency_max = read_sysfs_int(sys_pfx + 'latency_max')
++ latency_avg = read_sysfs_int(sys_pfx + 'latency_avg')
++ throughput_min = read_sysfs_int(sys_pfx + 'throughput_min')
++ throughput_max = read_sysfs_int(sys_pfx + 'throughput_max')
++ throughput_avg = read_sysfs_int(sys_pfx + 'throughput_avg')
++
++ # derive jitter
++ request_jitter = request_max - request_min
++ latency_jitter = latency_max - latency_min
++ throughput_jitter = throughput_max - throughput_min
++
++ # append data set to file
++ with open(fname, 'a') as csvf:
++ row = csv.writer(csvf, delimiter=",", quotechar="'",
++ quoting=csv.QUOTE_MINIMAL)
++ row.writerow([date, test_name, size, iteration_max, error,
++ request_min, request_max, request_avg, request_jitter,
++ latency_min, latency_max, latency_avg, latency_jitter,
++ throughput_min, throughput_max, throughput_avg, throughput_jitter])
++ except IOError as e:
++ print("I/O error({0}): {1}".format(e.errno, e.strerror))
++
++def loopback_run(test_name, size, iteration_max, sys_pfx):
++ test_id = dict[test_name]
++ try:
++ # Terminate any currently running test
++ write_sysfs_val(sys_pfx + 'type', '0')
++ # Set parameter for no wait between messages
++ write_sysfs_val(sys_pfx + 'ms_wait', '0')
++ # Set operation size
++ write_sysfs_val(sys_pfx + 'size', size)
++ # Set iterations
++ write_sysfs_val(sys_pfx + 'iteration_max', str(iteration_max))
++ # Initiate by setting loopback operation type
++ write_sysfs_val(sys_pfx + 'type', test_id)
++ time.sleep(1)
++
++ if iteration_max == 0:
++ print ("Infinite test initiated CSV won't be logged\n")
++ return
++
++ previous = 0
++ err = 0
++ while True:
++ # get current count bail out if it hasn't changed
++ iteration_count = read_sysfs_int(sys_pfx + 'iteration_count')
++ if previous == iteration_count:
++ err = 1
++ break
++ elif iteration_count == iteration_max:
++ break
++ previous = iteration_count
++ if verbose:
++ print('%02d%% complete %d of %d ' %
++ (100 * iteration_count / iteration_max,
++ iteration_count, iteration_max))
++ time.sleep(1)
++ if err:
++ print ('\nError executing test\n')
++ else:
++ log_csv(test_name, size, iteration_max, sys_pfx)
++ except ValueError as ve:
++ print("Error: %s " % format(e.strerror), file=sys.stderr)
++ abort()
++
++def main():
++ if len(sys.argv) < 5:
++ usage()
++
++ if sys.argv[1] in dict.keys():
++ loopback_run(sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4])
++ else:
++ usage()
++if __name__ == '__main__':
++ main()
+--- /dev/null
++++ b/drivers/greybus/tools/loopback_test.c
+@@ -0,0 +1,1000 @@
++/*
++ * Loopback test application
++ *
++ * Copyright 2015 Google Inc.
++ * Copyright 2015 Linaro Ltd.
++ *
++ * Provided under the three clause BSD license found in the LICENSE file.
++ */
++#include <errno.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <stdint.h>
++#include <poll.h>
++#include <sys/types.h>
++#include <time.h>
++#include <unistd.h>
++#include <dirent.h>
++#include <signal.h>
++
++#define MAX_NUM_DEVICES 10
++#define MAX_SYSFS_PATH 0x200
++#define CSV_MAX_LINE 0x1000
++#define SYSFS_MAX_INT 0x20
++#define MAX_STR_LEN 255
++#define DEFAULT_ASYNC_TIMEOUT 200000
++
++struct dict {
++ char *name;
++ int type;
++};
++
++static struct dict dict[] = {
++ {"ping", 2},
++ {"transfer", 3},
++ {"sink", 4},
++ {NULL,} /* list termination */
++};
++
++struct loopback_results {
++ float latency_avg;
++ uint32_t latency_max;
++ uint32_t latency_min;
++ uint32_t latency_jitter;
++
++ float request_avg;
++ uint32_t request_max;
++ uint32_t request_min;
++ uint32_t request_jitter;
++
++ float throughput_avg;
++ uint32_t throughput_max;
++ uint32_t throughput_min;
++ uint32_t throughput_jitter;
++
++ float apbridge_unipro_latency_avg;
++ uint32_t apbridge_unipro_latency_max;
++ uint32_t apbridge_unipro_latency_min;
++ uint32_t apbridge_unipro_latency_jitter;
++
++ float gbphy_firmware_latency_avg;
++ uint32_t gbphy_firmware_latency_max;
++ uint32_t gbphy_firmware_latency_min;
++ uint32_t gbphy_firmware_latency_jitter;
++
++ uint32_t error;
++};
++
++struct loopback_device {
++ char name[MAX_SYSFS_PATH];
++ char sysfs_entry[MAX_SYSFS_PATH];
++ char debugfs_entry[MAX_SYSFS_PATH];
++ struct loopback_results results;
++};
++
++struct loopback_test {
++ int verbose;
++ int debug;
++ int raw_data_dump;
++ int porcelain;
++ int mask;
++ int size;
++ int iteration_max;
++ int aggregate_output;
++ int test_id;
++ int device_count;
++ int list_devices;
++ int use_async;
++ int async_timeout;
++ int async_outstanding_operations;
++ int us_wait;
++ int file_output;
++ int stop_all;
++ int poll_count;
++ char test_name[MAX_STR_LEN];
++ char sysfs_prefix[MAX_SYSFS_PATH];
++ char debugfs_prefix[MAX_SYSFS_PATH];
++ struct timespec poll_timeout;
++ struct loopback_device devices[MAX_NUM_DEVICES];
++ struct loopback_results aggregate_results;
++ struct pollfd fds[MAX_NUM_DEVICES];
++};
++
++struct loopback_test t;
++
++/* Helper macros to calculate the aggregate results for all devices */
++static inline int device_enabled(struct loopback_test *t, int dev_idx);
++
++#define GET_MAX(field) \
++static int get_##field##_aggregate(struct loopback_test *t) \
++{ \
++ uint32_t max = 0; \
++ int i; \
++ for (i = 0; i < t->device_count; i++) { \
++ if (!device_enabled(t, i)) \
++ continue; \
++ if (t->devices[i].results.field > max) \
++ max = t->devices[i].results.field; \
++ } \
++ return max; \
++} \
++
++#define GET_MIN(field) \
++static int get_##field##_aggregate(struct loopback_test *t) \
++{ \
++ uint32_t min = ~0; \
++ int i; \
++ for (i = 0; i < t->device_count; i++) { \
++ if (!device_enabled(t, i)) \
++ continue; \
++ if (t->devices[i].results.field < min) \
++ min = t->devices[i].results.field; \
++ } \
++ return min; \
++} \
++
++#define GET_AVG(field) \
++static int get_##field##_aggregate(struct loopback_test *t) \
++{ \
++ uint32_t val = 0; \
++ uint32_t count = 0; \
++ int i; \
++ for (i = 0; i < t->device_count; i++) { \
++ if (!device_enabled(t, i)) \
++ continue; \
++ count++; \
++ val += t->devices[i].results.field; \
++ } \
++ if (count) \
++ val /= count; \
++ return val; \
++} \
++
++GET_MAX(throughput_max);
++GET_MAX(request_max);
++GET_MAX(latency_max);
++GET_MAX(apbridge_unipro_latency_max);
++GET_MAX(gbphy_firmware_latency_max);
++GET_MIN(throughput_min);
++GET_MIN(request_min);
++GET_MIN(latency_min);
++GET_MIN(apbridge_unipro_latency_min);
++GET_MIN(gbphy_firmware_latency_min);
++GET_AVG(throughput_avg);
++GET_AVG(request_avg);
++GET_AVG(latency_avg);
++GET_AVG(apbridge_unipro_latency_avg);
++GET_AVG(gbphy_firmware_latency_avg);
++
++void abort()
++{
++ _exit(1);
++}
++
++void usage(void)
++{
++ fprintf(stderr, "Usage: loopback_test TEST [SIZE] ITERATIONS [SYSPATH] [DBGPATH]\n\n"
++ " Run TEST for a number of ITERATIONS with operation data SIZE bytes\n"
++ " TEST may be \'ping\' \'transfer\' or \'sink\'\n"
++ " SIZE indicates the size of transfer <= greybus max payload bytes\n"
++ " ITERATIONS indicates the number of times to execute TEST at SIZE bytes\n"
++ " Note if ITERATIONS is set to zero then this utility will\n"
++ " initiate an infinite (non terminating) test and exit\n"
++ " without logging any metrics data\n"
++ " SYSPATH indicates the sysfs path for the loopback greybus entries e.g.\n"
++ " /sys/bus/greybus/devices\n"
++ " DBGPATH indicates the debugfs path for the loopback greybus entries e.g.\n"
++ " /sys/kernel/debug/gb_loopback/\n"
++ " Mandatory arguments\n"
++ " -t must be one of the test names - sink, transfer or ping\n"
++ " -i iteration count - the number of iterations to run the test over\n"
++ " Optional arguments\n"
++ " -S sysfs location - location for greybus 'endo' entires default /sys/bus/greybus/devices/\n"
++ " -D debugfs location - location for loopback debugfs entries default /sys/kernel/debug/gb_loopback/\n"
++ " -s size of data packet to send during test - defaults to zero\n"
++ " -m mask - a bit mask of connections to include example: -m 8 = 4th connection -m 9 = 1st and 4th connection etc\n"
++ " default is zero which means broadcast to all connections\n"
++ " -v verbose output\n"
++ " -d debug output\n"
++ " -r raw data output - when specified the full list of latency values are included in the output CSV\n"
++ " -p porcelain - when specified printout is in a user-friendly non-CSV format. This option suppresses writing to CSV file\n"
++ " -a aggregate - show aggregation of all enabled devices\n"
++ " -l list found loopback devices and exit\n"
++ " -x Async - Enable async transfers\n"
++ " -o Async Timeout - Timeout in uSec for async operations\n"
++ " -O Poll loop time out in seconds(max time a test is expected to last, default: 30sec)\n"
++ " -c Max number of outstanding operations for async operations\n"
++ " -w Wait in uSec between operations\n"
++ " -z Enable output to a CSV file (incompatible with -p)\n"
++ " -f When starting new loopback test, stop currently running tests on all devices\n"
++ "Examples:\n"
++ " Send 10000 transfers with a packet size of 128 bytes to all active connections\n"
++ " loopback_test -t transfer -s 128 -i 10000 -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n"
++ " loopback_test -t transfer -s 128 -i 10000 -m 0\n"
++ " Send 10000 transfers with a packet size of 128 bytes to connection 1 and 4\n"
++ " loopback_test -t transfer -s 128 -i 10000 -m 9\n"
++ " loopback_test -t ping -s 0 128 -i -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n"
++ " loopback_test -t sink -s 2030 -i 32768 -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n");
++ abort();
++}
++
++static inline int device_enabled(struct loopback_test *t, int dev_idx)
++{
++ if (!t->mask || (t->mask & (1 << dev_idx)))
++ return 1;
++
++ return 0;
++}
++
++static void show_loopback_devices(struct loopback_test *t)
++{
++ int i;
++
++ if (t->device_count == 0) {
++ printf("No loopback devices.\n");
++ return;
++ }
++
++ for (i = 0; i < t->device_count; i++)
++ printf("device[%d] = %s\n", i, t->devices[i].name);
++
++}
++
++int open_sysfs(const char *sys_pfx, const char *node, int flags)
++{
++ int fd;
++ char path[MAX_SYSFS_PATH];
++
++ snprintf(path, sizeof(path), "%s%s", sys_pfx, node);
++ fd = open(path, flags);
++ if (fd < 0) {
++ fprintf(stderr, "unable to open %s\n", path);
++ abort();
++ }
++ return fd;
++}
++
++int read_sysfs_int_fd(int fd, const char *sys_pfx, const char *node)
++{
++ char buf[SYSFS_MAX_INT];
++
++ if (read(fd, buf, sizeof(buf)) < 0) {
++ fprintf(stderr, "unable to read from %s%s %s\n", sys_pfx, node,
++ strerror(errno));
++ close(fd);
++ abort();
++ }
++ return atoi(buf);
++}
++
++float read_sysfs_float_fd(int fd, const char *sys_pfx, const char *node)
++{
++ char buf[SYSFS_MAX_INT];
++
++ if (read(fd, buf, sizeof(buf)) < 0) {
++
++ fprintf(stderr, "unable to read from %s%s %s\n", sys_pfx, node,
++ strerror(errno));
++ close(fd);
++ abort();
++ }
++ return atof(buf);
++}
++
++int read_sysfs_int(const char *sys_pfx, const char *node)
++{
++ int fd, val;
++
++ fd = open_sysfs(sys_pfx, node, O_RDONLY);
++ val = read_sysfs_int_fd(fd, sys_pfx, node);
++ close(fd);
++ return val;
++}
++
++float read_sysfs_float(const char *sys_pfx, const char *node)
++{
++ int fd;
++ float val;
++
++ fd = open_sysfs(sys_pfx, node, O_RDONLY);
++ val = read_sysfs_float_fd(fd, sys_pfx, node);
++ close(fd);
++ return val;
++}
++
++void write_sysfs_val(const char *sys_pfx, const char *node, int val)
++{
++ int fd, len;
++ char buf[SYSFS_MAX_INT];
++
++ fd = open_sysfs(sys_pfx, node, O_RDWR);
++ len = snprintf(buf, sizeof(buf), "%d", val);
++ if (write(fd, buf, len) < 0) {
++ fprintf(stderr, "unable to write to %s%s %s\n", sys_pfx, node,
++ strerror(errno));
++ close(fd);
++ abort();
++ }
++ close(fd);
++}
++
++static int get_results(struct loopback_test *t)
++{
++ struct loopback_device *d;
++ struct loopback_results *r;
++ int i;
++
++ for (i = 0; i < t->device_count; i++) {
++ if (!device_enabled(t, i))
++ continue;
++
++ d = &t->devices[i];
++ r = &d->results;
++
++ r->error = read_sysfs_int(d->sysfs_entry, "error");
++ r->request_min = read_sysfs_int(d->sysfs_entry, "requests_per_second_min");
++ r->request_max = read_sysfs_int(d->sysfs_entry, "requests_per_second_max");
++ r->request_avg = read_sysfs_float(d->sysfs_entry, "requests_per_second_avg");
++
++ r->latency_min = read_sysfs_int(d->sysfs_entry, "latency_min");
++ r->latency_max = read_sysfs_int(d->sysfs_entry, "latency_max");
++ r->latency_avg = read_sysfs_float(d->sysfs_entry, "latency_avg");
++
++ r->throughput_min = read_sysfs_int(d->sysfs_entry, "throughput_min");
++ r->throughput_max = read_sysfs_int(d->sysfs_entry, "throughput_max");
++ r->throughput_avg = read_sysfs_float(d->sysfs_entry, "throughput_avg");
++
++ r->apbridge_unipro_latency_min =
++ read_sysfs_int(d->sysfs_entry, "apbridge_unipro_latency_min");
++ r->apbridge_unipro_latency_max =
++ read_sysfs_int(d->sysfs_entry, "apbridge_unipro_latency_max");
++ r->apbridge_unipro_latency_avg =
++ read_sysfs_float(d->sysfs_entry, "apbridge_unipro_latency_avg");
++
++ r->gbphy_firmware_latency_min =
++ read_sysfs_int(d->sysfs_entry, "gbphy_firmware_latency_min");
++ r->gbphy_firmware_latency_max =
++ read_sysfs_int(d->sysfs_entry, "gbphy_firmware_latency_max");
++ r->gbphy_firmware_latency_avg =
++ read_sysfs_float(d->sysfs_entry, "gbphy_firmware_latency_avg");
++
++ r->request_jitter = r->request_max - r->request_min;
++ r->latency_jitter = r->latency_max - r->latency_min;
++ r->throughput_jitter = r->throughput_max - r->throughput_min;
++ r->apbridge_unipro_latency_jitter =
++ r->apbridge_unipro_latency_max - r->apbridge_unipro_latency_min;
++ r->gbphy_firmware_latency_jitter =
++ r->gbphy_firmware_latency_max - r->gbphy_firmware_latency_min;
++
++ }
++
++ /*calculate the aggregate results of all enabled devices */
++ if (t->aggregate_output) {
++ r = &t->aggregate_results;
++
++ r->request_min = get_request_min_aggregate(t);
++ r->request_max = get_request_max_aggregate(t);
++ r->request_avg = get_request_avg_aggregate(t);
++
++ r->latency_min = get_latency_min_aggregate(t);
++ r->latency_max = get_latency_max_aggregate(t);
++ r->latency_avg = get_latency_avg_aggregate(t);
++
++ r->throughput_min = get_throughput_min_aggregate(t);
++ r->throughput_max = get_throughput_max_aggregate(t);
++ r->throughput_avg = get_throughput_avg_aggregate(t);
++
++ r->apbridge_unipro_latency_min =
++ get_apbridge_unipro_latency_min_aggregate(t);
++ r->apbridge_unipro_latency_max =
++ get_apbridge_unipro_latency_max_aggregate(t);
++ r->apbridge_unipro_latency_avg =
++ get_apbridge_unipro_latency_avg_aggregate(t);
++
++ r->gbphy_firmware_latency_min =
++ get_gbphy_firmware_latency_min_aggregate(t);
++ r->gbphy_firmware_latency_max =
++ get_gbphy_firmware_latency_max_aggregate(t);
++ r->gbphy_firmware_latency_avg =
++ get_gbphy_firmware_latency_avg_aggregate(t);
++
++ r->request_jitter = r->request_max - r->request_min;
++ r->latency_jitter = r->latency_max - r->latency_min;
++ r->throughput_jitter = r->throughput_max - r->throughput_min;
++ r->apbridge_unipro_latency_jitter =
++ r->apbridge_unipro_latency_max - r->apbridge_unipro_latency_min;
++ r->gbphy_firmware_latency_jitter =
++ r->gbphy_firmware_latency_max - r->gbphy_firmware_latency_min;
++
++ }
++
++ return 0;
++}
++
++void log_csv_error(int len, int err)
++{
++ fprintf(stderr, "unable to write %d bytes to csv %s\n", len,
++ strerror(err));
++}
++
++int format_output(struct loopback_test *t,
++ struct loopback_results *r,
++ const char *dev_name,
++ char *buf, int buf_len,
++ struct tm *tm)
++{
++ int len = 0;
++
++ memset(buf, 0x00, buf_len);
++ len = snprintf(buf, buf_len, "%u-%u-%u %u:%u:%u",
++ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
++ tm->tm_hour, tm->tm_min, tm->tm_sec);
++
++ if (t->porcelain) {
++ len += snprintf(&buf[len], buf_len - len,
++ "\n test:\t\t\t%s\n path:\t\t\t%s\n size:\t\t\t%u\n iterations:\t\t%u\n errors:\t\t%u\n async:\t\t\t%s\n",
++ t->test_name,
++ dev_name,
++ t->size,
++ t->iteration_max,
++ r->error,
++ t->use_async ? "Enabled" : "Disabled");
++
++ len += snprintf(&buf[len], buf_len - len,
++ " requests per-sec:\tmin=%u, max=%u, average=%f, jitter=%u\n",
++ r->request_min,
++ r->request_max,
++ r->request_avg,
++ r->request_jitter);
++
++ len += snprintf(&buf[len], buf_len - len,
++ " ap-throughput B/s:\tmin=%u max=%u average=%f jitter=%u\n",
++ r->throughput_min,
++ r->throughput_max,
++ r->throughput_avg,
++ r->throughput_jitter);
++ len += snprintf(&buf[len], buf_len - len,
++ " ap-latency usec:\tmin=%u max=%u average=%f jitter=%u\n",
++ r->latency_min,
++ r->latency_max,
++ r->latency_avg,
++ r->latency_jitter);
++ len += snprintf(&buf[len], buf_len - len,
++ " apbridge-latency usec:\tmin=%u max=%u average=%f jitter=%u\n",
++ r->apbridge_unipro_latency_min,
++ r->apbridge_unipro_latency_max,
++ r->apbridge_unipro_latency_avg,
++ r->apbridge_unipro_latency_jitter);
++
++ len += snprintf(&buf[len], buf_len - len,
++ " gbphy-latency usec:\tmin=%u max=%u average=%f jitter=%u\n",
++ r->gbphy_firmware_latency_min,
++ r->gbphy_firmware_latency_max,
++ r->gbphy_firmware_latency_avg,
++ r->gbphy_firmware_latency_jitter);
++
++ } else {
++ len += snprintf(&buf[len], buf_len- len, ",%s,%s,%u,%u,%u",
++ t->test_name, dev_name, t->size, t->iteration_max,
++ r->error);
++
++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u",
++ r->request_min,
++ r->request_max,
++ r->request_avg,
++ r->request_jitter);
++
++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u",
++ r->latency_min,
++ r->latency_max,
++ r->latency_avg,
++ r->latency_jitter);
++
++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u",
++ r->throughput_min,
++ r->throughput_max,
++ r->throughput_avg,
++ r->throughput_jitter);
++
++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u",
++ r->apbridge_unipro_latency_min,
++ r->apbridge_unipro_latency_max,
++ r->apbridge_unipro_latency_avg,
++ r->apbridge_unipro_latency_jitter);
++
++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u",
++ r->gbphy_firmware_latency_min,
++ r->gbphy_firmware_latency_max,
++ r->gbphy_firmware_latency_avg,
++ r->gbphy_firmware_latency_jitter);
++ }
++
++ printf("\n%s\n", buf);
++
++ return len;
++}
++
++static int log_results(struct loopback_test *t)
++{
++ int fd, i, len, ret;
++ struct tm tm;
++ time_t local_time;
++ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
++ char file_name[MAX_SYSFS_PATH];
++ char data[CSV_MAX_LINE];
++
++ local_time = time(NULL);
++ tm = *localtime(&local_time);
++
++ /*
++ * file name will test_name_size_iteration_max.csv
++ * every time the same test with the same parameters is run we will then
++ * append to the same CSV with datestamp - representing each test
++ * dataset.
++ */
++ if (t->file_output && !t->porcelain) {
++ snprintf(file_name, sizeof(file_name), "%s_%d_%d.csv",
++ t->test_name, t->size, t->iteration_max);
++
++ fd = open(file_name, O_WRONLY | O_CREAT | O_APPEND, mode);
++ if (fd < 0) {
++ fprintf(stderr, "unable to open %s for appendation\n", file_name);
++ abort();
++ }
++
++ }
++ for (i = 0; i < t->device_count; i++) {
++ if (!device_enabled(t, i))
++ continue;
++
++ len = format_output(t, &t->devices[i].results,
++ t->devices[i].name,
++ data, sizeof(data), &tm);
++ if (t->file_output && !t->porcelain) {
++ ret = write(fd, data, len);
++ if (ret == -1)
++ fprintf(stderr, "unable to write %d bytes to csv.\n", len);
++ }
++
++ }
++
++
++ if (t->aggregate_output) {
++ len = format_output(t, &t->aggregate_results, "aggregate",
++ data, sizeof(data), &tm);
++ if (t->file_output && !t->porcelain) {
++ ret = write(fd, data, len);
++ if (ret == -1)
++ fprintf(stderr, "unable to write %d bytes to csv.\n", len);
++ }
++ }
++
++ if (t->file_output && !t->porcelain)
++ close(fd);
++
++ return 0;
++}
++
++int is_loopback_device(const char *path, const char *node)
++{
++ char file[MAX_SYSFS_PATH];
++
++ snprintf(file, MAX_SYSFS_PATH, "%s%s/iteration_count", path, node);
++ if (access(file, F_OK) == 0)
++ return 1;
++ return 0;
++}
++
++int find_loopback_devices(struct loopback_test *t)
++{
++ struct dirent **namelist;
++ int i, n, ret;
++ unsigned int dev_id;
++ struct loopback_device *d;
++
++ n = scandir(t->sysfs_prefix, &namelist, NULL, alphasort);
++ if (n < 0) {
++ perror("scandir");
++ ret = -ENODEV;
++ goto baddir;
++ }
++
++ /* Don't include '.' and '..' */
++ if (n <= 2) {
++ ret = -ENOMEM;
++ goto done;
++ }
++
++ for (i = 0; i < n; i++) {
++ ret = sscanf(namelist[i]->d_name, "gb_loopback%u", &dev_id);
++ if (ret != 1)
++ continue;
++
++ if (!is_loopback_device(t->sysfs_prefix, namelist[i]->d_name))
++ continue;
++
++ if (t->device_count == MAX_NUM_DEVICES) {
++ fprintf(stderr, "max number of devices reached!\n");
++ break;
++ }
++
++ d = &t->devices[t->device_count++];
++ snprintf(d->name, MAX_STR_LEN, "gb_loopback%u", dev_id);
++
++ snprintf(d->sysfs_entry, MAX_SYSFS_PATH, "%s%s/",
++ t->sysfs_prefix, d->name);
++
++ snprintf(d->debugfs_entry, MAX_SYSFS_PATH, "%sraw_latency_%s",
++ t->debugfs_prefix, d->name);
++
++ if (t->debug)
++ printf("add %s %s\n", d->sysfs_entry,
++ d->debugfs_entry);
++ }
++
++ ret = 0;
++done:
++ for (i = 0; i < n; i++)
++ free(namelist[n]);
++ free(namelist);
++baddir:
++ return ret;
++}
++
++static int open_poll_files(struct loopback_test *t)
++{
++ struct loopback_device *dev;
++ char buf[MAX_STR_LEN];
++ char dummy;
++ int fds_idx = 0;
++ int i;
++
++ for (i = 0; i < t->device_count; i++) {
++ dev = &t->devices[i];
++
++ if (!device_enabled(t, i))
++ continue;
++
++ snprintf(buf, sizeof(buf), "%s%s", dev->sysfs_entry, "iteration_count");
++ t->fds[fds_idx].fd = open(buf, O_RDONLY);
++ if (t->fds[fds_idx].fd < 0) {
++ fprintf(stderr, "Error opening poll file!\n");
++ goto err;
++ }
++ read(t->fds[fds_idx].fd, &dummy, 1);
++ t->fds[fds_idx].events = POLLERR|POLLPRI;
++ t->fds[fds_idx].revents = 0;
++ fds_idx++;
++ }
++
++ t->poll_count = fds_idx;
++
++ return 0;
++
++err:
++ for (i = 0; i < fds_idx; i++)
++ close(t->fds[fds_idx].fd);
++
++ return -1;
++}
++
++static int close_poll_files(struct loopback_test *t)
++{
++ int i;
++ for (i = 0; i < t->poll_count; i++)
++ close(t->fds[i].fd);
++
++ return 0;
++}
++static int is_complete(struct loopback_test *t)
++{
++ int iteration_count;
++ int i;
++
++ for (i = 0; i < t->device_count; i++) {
++ if (!device_enabled(t, i))
++ continue;
++
++ iteration_count = read_sysfs_int(t->devices[i].sysfs_entry,
++ "iteration_count");
++
++ /* at least one device did not finish yet */
++ if (iteration_count != t->iteration_max)
++ return 0;
++ }
++
++ return 1;
++}
++
++static void stop_tests(struct loopback_test *t)
++{
++ int i;
++
++ for (i = 0; i < t->device_count; i++) {
++ if (!device_enabled(t, i))
++ continue;
++ write_sysfs_val(t->devices[i].sysfs_entry, "type", 0);
++ }
++}
++
++static void handler(int sig) { /* do nothing */ }
++
++static int wait_for_complete(struct loopback_test *t)
++{
++ int number_of_events = 0;
++ char dummy;
++ int ret;
++ int i;
++ struct timespec *ts = NULL;
++ struct sigaction sa;
++ sigset_t mask_old, mask;
++
++ sigemptyset(&mask);
++ sigemptyset(&mask_old);
++ sigaddset(&mask, SIGINT);
++ sigprocmask(SIG_BLOCK, &mask, &mask_old);
++
++ sa.sa_handler = handler;
++ sa.sa_flags = 0;
++ sigemptyset(&sa.sa_mask);
++ if (sigaction(SIGINT, &sa, NULL) == -1) {
++ fprintf(stderr, "sigaction error\n");
++ return -1;
++ }
++
++ if (t->poll_timeout.tv_sec != 0)
++ ts = &t->poll_timeout;
++
++ while (1) {
++
++ ret = ppoll(t->fds, t->poll_count, ts, &mask_old);
++ if (ret <= 0) {
++ stop_tests(t);
++ fprintf(stderr, "Poll exit with errno %d\n", errno);
++ return -1;
++ }
++
++ for (i = 0; i < t->poll_count; i++) {
++ if (t->fds[i].revents & POLLPRI) {
++ /* Dummy read to clear the event */
++ read(t->fds[i].fd, &dummy, 1);
++ number_of_events++;
++ }
++ }
++
++ if (number_of_events == t->poll_count)
++ break;
++ }
++
++ if (!is_complete(t)) {
++ fprintf(stderr, "Iteration count did not finish!\n");
++ return -1;
++ }
++
++ return 0;
++}
++
++static void prepare_devices(struct loopback_test *t)
++{
++ int i;
++
++ /* Cancel any running tests on enabled devices. If
++ * stop_all option is given, stop test on all devices.
++ */
++ for (i = 0; i < t->device_count; i++)
++ if (t->stop_all || device_enabled(t, i))
++ write_sysfs_val(t->devices[i].sysfs_entry, "type", 0);
++
++
++ for (i = 0; i < t->device_count; i++) {
++ if (!device_enabled(t, i))
++ continue;
++
++ write_sysfs_val(t->devices[i].sysfs_entry, "us_wait",
++ t->us_wait);
++
++ /* Set operation size */
++ write_sysfs_val(t->devices[i].sysfs_entry, "size", t->size);
++
++ /* Set iterations */
++ write_sysfs_val(t->devices[i].sysfs_entry, "iteration_max",
++ t->iteration_max);
++
++ if (t->use_async) {
++ write_sysfs_val(t->devices[i].sysfs_entry,
++ "async", 1);
++ write_sysfs_val(t->devices[i].sysfs_entry,
++ "timeout", t->async_timeout);
++ write_sysfs_val(t->devices[i].sysfs_entry,
++ "outstanding_operations_max",
++ t->async_outstanding_operations);
++ } else
++ write_sysfs_val(t->devices[i].sysfs_entry,
++ "async", 0);
++ }
++}
++
++static int start(struct loopback_test *t)
++{
++ int i;
++
++ /* the test starts by writing test_id to the type file. */
++ for (i = 0; i < t->device_count; i++) {
++ if (!device_enabled(t, i))
++ continue;
++
++ write_sysfs_val(t->devices[i].sysfs_entry, "type", t->test_id);
++ }
++
++ return 0;
++}
++
++
++void loopback_run(struct loopback_test *t)
++{
++ int i;
++ int ret;
++
++ for (i = 0; dict[i].name != NULL; i++) {
++ if (strstr(dict[i].name, t->test_name))
++ t->test_id = dict[i].type;
++ }
++ if (!t->test_id) {
++ fprintf(stderr, "invalid test %s\n", t->test_name);
++ usage();
++ return;
++ }
++
++ prepare_devices(t);
++
++ ret = open_poll_files(t);
++ if (ret)
++ goto err;
++
++ start(t);
++
++ ret = wait_for_complete(t);
++ close_poll_files(t);
++ if (ret)
++ goto err;
++
++
++ get_results(t);
++
++ log_results(t);
++
++ return;
++
++err:
++ printf("Error running test\n");
++ return;
++}
++
++static int sanity_check(struct loopback_test *t)
++{
++ int i;
++
++ if (t->device_count == 0) {
++ fprintf(stderr, "No loopback devices found\n");
++ return -1;
++ }
++
++ for (i = 0; i < MAX_NUM_DEVICES; i++) {
++ if (!device_enabled(t, i))
++ continue;
++
++ if (t->mask && !strcmp(t->devices[i].name, "")) {
++ fprintf(stderr, "Bad device mask %x\n", (1 << i));
++ return -1;
++ }
++
++ }
++
++
++ return 0;
++}
++
++int main(int argc, char *argv[])
++{
++ int o, ret;
++ char *sysfs_prefix = "/sys/class/gb_loopback/";
++ char *debugfs_prefix = "/sys/kernel/debug/gb_loopback/";
++
++ memset(&t, 0, sizeof(t));
++
++ while ((o = getopt(argc, argv,
++ "t:s:i:S:D:m:v::d::r::p::a::l::x::o:O:c:w:z::f::")) != -1) {
++ switch (o) {
++ case 't':
++ snprintf(t.test_name, MAX_STR_LEN, "%s", optarg);
++ break;
++ case 's':
++ t.size = atoi(optarg);
++ break;
++ case 'i':
++ t.iteration_max = atoi(optarg);
++ break;
++ case 'S':
++ snprintf(t.sysfs_prefix, MAX_SYSFS_PATH, "%s", optarg);
++ break;
++ case 'D':
++ snprintf(t.debugfs_prefix, MAX_SYSFS_PATH, "%s", optarg);
++ break;
++ case 'm':
++ t.mask = atol(optarg);
++ break;
++ case 'v':
++ t.verbose = 1;
++ break;
++ case 'd':
++ t.debug = 1;
++ break;
++ case 'r':
++ t.raw_data_dump = 1;
++ break;
++ case 'p':
++ t.porcelain = 1;
++ break;
++ case 'a':
++ t.aggregate_output = 1;
++ break;
++ case 'l':
++ t.list_devices = 1;
++ break;
++ case 'x':
++ t.use_async = 1;
++ break;
++ case 'o':
++ t.async_timeout = atoi(optarg);
++ break;
++ case 'O':
++ t.poll_timeout.tv_sec = atoi(optarg);
++ break;
++ case 'c':
++ t.async_outstanding_operations = atoi(optarg);
++ break;
++ case 'w':
++ t.us_wait = atoi(optarg);
++ break;
++ case 'z':
++ t.file_output = 1;
++ break;
++ case 'f':
++ t.stop_all = 1;
++ break;
++ default:
++ usage();
++ return -EINVAL;
++ }
++ }
++
++ if (!strcmp(t.sysfs_prefix, ""))
++ snprintf(t.sysfs_prefix, MAX_SYSFS_PATH, "%s", sysfs_prefix);
++
++ if (!strcmp(t.debugfs_prefix, ""))
++ snprintf(t.debugfs_prefix, MAX_SYSFS_PATH, "%s", debugfs_prefix);
++
++ ret = find_loopback_devices(&t);
++ if (ret)
++ return ret;
++ ret = sanity_check(&t);
++ if (ret)
++ return ret;
++
++ if (t.list_devices) {
++ show_loopback_devices(&t);
++ return 0;
++ }
++
++ if (t.test_name[0] == '\0' || t.iteration_max == 0)
++ usage();
++
++ if (t.async_timeout == 0)
++ t.async_timeout = DEFAULT_ASYNC_TIMEOUT;
++
++ loopback_run(&t);
++
++ return 0;
++}