From c1aa01c96e590714d99c5f17cfae1b14dec8bdee Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 15 Sep 2016 13:54:11 +0200 Subject: greybus patches --- greybus_tools.patch | 1435 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1435 insertions(+) create mode 100644 greybus_tools.patch (limited to 'greybus_tools.patch') 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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; ++} -- cgit 1.2.3-korg