summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/tools/loopback_test.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-10-05 14:50:51 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-05 14:50:51 -0700
commit41844e36206be90cd4d962ea49b0abc3612a99d0 (patch)
treece0b3a3403bc6abdb28f52779d0d7b57a51a5c86 /drivers/staging/greybus/tools/loopback_test.c
parent5691f0e9a3e7855832d5fd094801bf600347c2d0 (diff)
parentfc1e2c8ea85e109acf09e74789e9b852f6eed251 (diff)
downloadop-kernel-dev-41844e36206be90cd4d962ea49b0abc3612a99d0.zip
op-kernel-dev-41844e36206be90cd4d962ea49b0abc3612a99d0.tar.gz
Merge tag 'staging-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging and IIO updates from Greg KH: "Here is the big staging and IIO driver pull request for 4.9-rc1. There are a lot of patches in here, the majority due to the drivers/staging/greybus/ subsystem being merged in with full development history that went back a few years, in order to preserve the work that those developers did over time. Lots and lots of tiny cleanups happened in the tree as well, due to the Outreachy application process and lots of other developers showing up for the first time to clean code up. Along with those changes, we deleted a wireless driver, and added a raspberrypi driver (currently marked broken), and lots of new iio drivers. Overall the tree still shrunk with more lines removed than added, about 10 thousand lines removed in total. Full details are in the very long shortlog below. All of this has been in the linux-next tree with no issues. There will be some merge problems with other subsystem trees, but those are all minor problems and shouldn't be hard to work out when they happen (MAINTAINERS and some lustre build problems with the IB tree)" And furter from me asking for clarification about greybus: "Right now there is a phone from Motorola shipping with this code (a slightly older version, but the same tree), so even though Ara is not alive in the same form, the functionality is happening. We are working with the developers of that phone to merge the newer stuff in with their fork so they can use the upstream version in future versions of their phone product line. Toshiba has at least one chip shipping in their catalog that needs/uses this protocol over a Unipro link, and rumor has it that there might be more in the future. There are also other users of the greybus protocols, there is a talk next week at ELC that shows how it is being used across a network connection to control a device, and previous ELC talks have showed the protocol stack being used over USB to drive embedded Linux boards. I've also talked to some people who are starting to work to add a host controller driver to control arduinos as the greybus PHY protocols are very useful to control a serial/i2c/spio/whatever device across a random physical link, as it is a way to have a self-describing device be attached to a host without needing manual configuration. So yes, people are using it, and there is still the chance that it will show up in a phone/laptop/tablet/whatever from Google in the future as well, the tech isn't dead, even if the original large phone project happens to be" * tag 'staging-4.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (3703 commits) Staging: fbtft: Fix bug in fbtft-core staging: rtl8188eu: fix double unlock error in rtw_resume_process() staging:r8188eu: remove GEN_MLME_EXT_HANDLER macro staging:r8188eu: remove GEN_DRV_CMD_HANDLER macro staging:r8188eu: remove GEN_EVT_CODE macro staging:r8188eu: remove GEN_CMD_CODE macro staging:r8188eu: remove pkt_newalloc member of the recv_buf structure staging:r8188eu: remove rtw_handle_dualmac declaration staging:r8188eu: remove (RGTRY|BSSID)_(OFT|SZ) macros staging:r8188eu: change rtl8188e_process_phy_info function argument type Staging: fsl-mc: Remove blank lines Staging: fsl-mc: Fix unaligned * in block comments Staging: comedi: Align the * in block comments Staging : ks7010 : Fix block comments warninig Staging: vt6655: Remove explicit NULL comparison using Coccinelle staging: rtl8188eu: core: rtw_xmit: Use macros instead of constants staging: rtl8188eu: core: rtw_xmit: Move constant of the right side staging: dgnc: Fix lines longer than 80 characters Staging: dgnc: constify attribute_group structures Staging: most: hdm-dim2: constify attribute_group structures ...
Diffstat (limited to 'drivers/staging/greybus/tools/loopback_test.c')
-rw-r--r--drivers/staging/greybus/tools/loopback_test.c1000
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;
+}
OpenPOWER on IntegriCloud