diff options
Diffstat (limited to 'drivers/staging/greybus/tools/loopback_test.c')
-rw-r--r-- | drivers/staging/greybus/tools/loopback_test.c | 1000 |
1 files changed, 1000 insertions, 0 deletions
diff --git a/drivers/staging/greybus/tools/loopback_test.c b/drivers/staging/greybus/tools/loopback_test.c new file mode 100644 index 0000000..f7f4cd6 --- /dev/null +++ b/drivers/staging/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; +} |