Commit 0e6463d9 authored by Changpeng Liu's avatar Changpeng Liu
Browse files

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: default avatarChangpeng Liu <changpeng.liu@intel.com>
parent fceb072b
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@
SPDK_ROOT_DIR := $(CURDIR)/../..
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk

DIRS-y += perf verify
DIRS-y += perf verify kperf

.PHONY: all clean $(DIRS-y)

+1 −0
Original line number Diff line number Diff line
ioat_kperf
+53 −0
Original line number Diff line number Diff line
#
#  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.
#

SPDK_ROOT_DIR := $(CURDIR)/../../..
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk

APP = ioat_kperf

C_SRCS := ioat_kperf.c

CFLAGS += -I.

LIBS += -lrt

all: $(APP)

$(APP): $(OBJS)
	$(LINK_C)

clean:
	$(CLEAN_C) $(APP)

include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk
+42 −0
Original line number Diff line number Diff line
IOAT Kernel Driver Test Tool
============================

For the purpose to make performance comparison with user space IOAT
driver, we developed the test tool based on IOAT kernel driver, the
test tool contains 2 components: kernel test module and user space
application. The kernel test module allocated one kernel thread for
each DMA channel, and the kernel thread did not pin to specified
CPU cores, but ensure all the thread run in the same NUMA socket
with DMA channel, and the user space application communicated
with kernel test module via sysfs interface.

Building & Usage
================

1. Compile and load the kernel test module first.

    modprobe -v ioatdma
    cd kmod && make && insmod dmaperf.ko

2. Run the test application.

    Parameters:
    [-h usage]
    [-n number of DMA channels]
    [-q queue depth, per DMA channel]
    [-s [n^2] transfer size, per descriptor]
    [-t total [n^2] data to tranfer, per DMA channel]

    For example: ./ioat_kperf -n 4 -q 128 -s 12 -t 32

    Total 4 Channels, Queue_Depth 128, Transfer Size 4096 Bytes, Total Transfer Size 4 GB
    Running I/O . . . .
    Channel 0 Performance Data 1414 MB/s
    Channel 1 Performance Data 1413 MB/s
    Channel 2 Performance Data 1413 MB/s
    Channel 3 Performance Data 1415 MB/s

OS Support
==========
We have tested several Linux distributions, currently Fedora 21/22 with kernel
version >= 3.17 are supported.
+320 −0
Original line number Diff line number Diff line
/*-
 *   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;
}
Loading