Spdk/examples/ioat/kperf/ioat_kperf.c
Changpeng Liu 0e6463d9b3 ioat: add ioat kernel driver performance test harness
For the purpose to make performance comparison between the ioat
kernel driver and user space driver, we added the kernel driver
test harness here, all the workload executed in the kernel space
and controlled via sysfs.

Change-Id: I2c8d826283405a5e1c9ba6a033503bcb98541370
Signed-off-by: Changpeng Liu <changpeng.liu@intel.com>
2016-01-15 10:53:19 +08:00

321 lines
7.8 KiB
C

/*-
* BSD LICENSE
*
* Copyright(c) 2010-2015 Intel Corporation. All rights reserved.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Intel Corporation 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
* OWNER 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.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
static int
check_modules(char *driver_name)
{
FILE *fd;
const char *proc_modules = "/proc/modules";
char buffer[256];
fd = fopen(proc_modules, "r");
if (!fd)
return -1;
while (fgets(buffer, sizeof(buffer), fd)) {
if (strstr(buffer, driver_name) == NULL)
continue;
else {
fclose(fd);
return 0;
}
}
fclose(fd);
return -1;
}
static int
get_u32_from_file(const char *sysfs_file, uint32_t *value)
{
FILE *f;
char buf[BUFSIZ];
f = fopen(sysfs_file, "r");
if (f == NULL) {
return -1;
}
if (fgets(buf, sizeof(buf), f) != NULL) {
*value = strtoul(buf, NULL, 10);
}
fclose(f);
return 0;
}
static int
get_str_from_file(const char *sysfs_file, char *buf, int len)
{
FILE *f;
f = fopen(sysfs_file, "r");
if (f == NULL) {
return -1;
}
if (fgets(buf, len, f) != NULL) {
fclose(f);
return 0;
}
fclose(f);
return -1;
}
static int
put_u32_to_file(const char *sysfs_file, uint32_t value)
{
FILE *f;
int n;
char buf[BUFSIZ];
f = fopen(sysfs_file, "w");
if (f == NULL) {
return -1;
}
n = snprintf(buf, sizeof(buf), "%ul", value);
if ((n < 0) || (n >= (int)sizeof(buf))) {
fclose(f);
return -1;
}
if (fwrite(buf, n, 1, f) == 0) {
fclose(f);
return -1;
}
fclose(f);
return 0;
}
static int
get_u64_from_file(const char *sysfs_file, uint64_t *value)
{
FILE *f;
char buf[BUFSIZ];
f = fopen(sysfs_file, "r");
if (f == NULL) {
return -1;
}
if (fgets(buf, sizeof(buf), f) != NULL) {
*value = strtoull(buf, NULL, 10);
}
fclose(f);
return 0;
}
static void
usage(char *program_name)
{
printf("%s options\n", program_name);
printf("\t[-h usage]\n");
printf("\t[-n number of DMA channels]\n");
printf("\t[-q queue depth, per DMA channel]\n");
printf("\t[-s [n^2] transfer size, per descriptor]\n");
printf("\t[-t total [n^2] data to tranfer, per DMA channel]\n");
}
int main(int argc, char *argv[])
{
int op;
int rc;
char buf[BUFSIZ];
uint32_t i, threads = 0;
uint32_t ring_size, queue_depth = 0;
uint32_t transfer_size, order = 0;
uint64_t total_size, copied = 0;
uint64_t elapsed_time = 0;
char channel[1024];
if (check_modules("ioatdma")) {
fprintf(stderr, "Ioat driver not loaded,"
" run `modprove -v ioatdma` first\n");
return -1;
}
if (check_modules("dmaperf")) {
fprintf(stderr, "Kernel Ioat test driver not loaded,"
" run `insmod dmaperf.ko` in the kmod directory\n");
return -1;
}
rc = get_u32_from_file("/sys/module/ioatdma/parameters/ioat_ring_alloc_order",
&order);
if (rc < 0) {
fprintf(stderr, "Cannot get default ioat queue depth\n");
return -1;
}
ring_size = 1UL << order;
while ((op = getopt(argc, argv, "h:n:q:s:t:")) != -1) {
switch (op) {
case 'n':
threads = atoi(optarg);
rc = put_u32_to_file("/sys/kernel/debug/dmaperf/dmaperf/threads", threads);
if (rc < 0) {
fprintf(stderr, "Cannot set dma channels\n");
return -1;
}
break;
case 'q':
queue_depth = atoi(optarg);
if (queue_depth > ring_size) {
fprintf(stderr, "Max Ioat DMA ring size %d\n", ring_size);
return -1;
}
rc = put_u32_to_file("/sys/kernel/debug/dmaperf/dmaperf/queue_depth", queue_depth);
if (rc < 0) {
fprintf(stderr, "Cannot set queue depth\n");
return -1;
}
break;
case 's':
order = atoi(optarg);
rc = put_u32_to_file("/sys/kernel/debug/dmaperf/dmaperf/transfer_size_order", order);
if (rc < 0) {
fprintf(stderr, "Cannot set descriptor transfer size order\n");
return -1;
}
break;
case 't':
order = atoi(optarg);
rc = put_u32_to_file("/sys/kernel/debug/dmaperf/dmaperf/total_size_order", order);
if (rc < 0) {
fprintf(stderr, "Cannot set channel total transfer size order\n");
return -1;
}
break;
case 'h' :
usage(argv[0]);
exit(0);
default:
usage(argv[0]);
exit(1);
}
}
/* get driver configuration */
rc = get_u32_from_file("/sys/kernel/debug/dmaperf/dmaperf/transfer_size_order",
&order);
if (rc < 0) {
fprintf(stderr, "Cannot get channel descriptor transfer size\n");
return -1;
}
transfer_size = 1UL << order;
rc = get_u32_from_file("/sys/kernel/debug/dmaperf/dmaperf/total_size_order",
&order);
if (rc < 0) {
fprintf(stderr, "Cannot get channel total transfer size\n");
return -1;
}
total_size = 1ULL << order;
rc = get_u32_from_file("/sys/kernel/debug/dmaperf/dmaperf/threads",
&threads);
if (rc < 0) {
fprintf(stderr, "Cannot get dma channel threads\n");
return -1;
}
rc = get_u32_from_file("/sys/kernel/debug/dmaperf/dmaperf/queue_depth",
&queue_depth);
if (rc < 0) {
fprintf(stderr, "Cannot get queue depth\n");
return -1;
}
fprintf(stdout,
"Total %d Channels, Queue_Depth %d, Transfer Size %d Bytes, Total Transfer Size %"PRIu64" GB\n",
threads, queue_depth, transfer_size, total_size >> 30ULL);
/* run the channels */
rc = put_u32_to_file("/sys/kernel/debug/dmaperf/dmaperf/run", 1);
if (rc < 0) {
fprintf(stderr, "Cannot run the channels\n");
return -1;
}
fprintf(stdout, "Running I/O ");
fflush(stdout);
/* wait all the channels to be idle */
while (!get_str_from_file("/sys/kernel/debug/dmaperf/dmaperf/status", buf, BUFSIZ)) {
if (strstr(buf, "idle") != NULL) {
fprintf(stdout, "\n");
fflush(stdout);
sleep(1);
break;
}
fprintf(stdout, ". ");
fflush(stdout);
sleep(1);
}
/* collect each channel performance data */
for (i = 0; i < threads; i++) {
/* total data transfer length for the DMA channel in Bytes */
sprintf(channel, "/sys/kernel/debug/dmaperf/dmaperf/thread_%u/copied", i);
rc = get_u64_from_file(channel, &copied);
if (rc < 0) {
fprintf(stderr, "Cannot get channel copied bytes\n");
return -1;
}
/* time in microseconds for total data transfer length */
sprintf(channel, "/sys/kernel/debug/dmaperf/dmaperf/thread_%u/elapsed_time", i);
rc = get_u64_from_file(channel, &elapsed_time);
if (rc < 0) {
fprintf(stderr, "Cannot get channel elapsed time\n");
return -1;
}
assert(elapsed_time != 0);
fprintf(stdout, "Channel %d Performance Data %"PRIu64" MB/s\n",
i, copied / elapsed_time);
}
return 0;
}