From e9497f9bbd2ac5e16b9d913c89ff2d2a08f52cd7 Mon Sep 17 00:00:00 2001 From: Conrad Meyer Date: Thu, 29 Oct 2015 04:16:16 +0000 Subject: [PATCH] ioatcontrol(8): Add and document "raw" testing mode Allows DMA from/to arbitrary KVA or physical address. /dev/ioat_test must be enabled by root and is only R/W root, so this is approximately as dangerous as /dev/mem and /dev/kmem. Sponsored by: EMC / Isilon Storage Division --- sys/dev/ioat/ioat_test.c | 43 +++++++++++++++++++++-- sys/dev/ioat/ioat_test.h | 7 ++++ tools/tools/ioat/ioatcontrol.8 | 34 ++++++++++++++++++- tools/tools/ioat/ioatcontrol.c | 62 ++++++++++++++++++++++++++++++++-- 4 files changed, 141 insertions(+), 5 deletions(-) diff --git a/sys/dev/ioat/ioat_test.c b/sys/dev/ioat/ioat_test.c index 53d5e8a12b94..b4920d777ce7 100644 --- a/sys/dev/ioat/ioat_test.c +++ b/sys/dev/ioat/ioat_test.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include "ioat.h" @@ -120,6 +121,18 @@ test_transaction *ioat_test_transaction_create(unsigned num_buffers, return (tx); } +static void +dump_hex(void *p, size_t chunks) +{ + size_t i, j; + + for (i = 0; i < chunks; i++) { + for (j = 0; j < 8; j++) + printf("%08x ", ((uint32_t *)p)[i * 8 + j]); + printf("\n"); + } +} + static bool ioat_compare_ok(struct test_transaction *tx) { @@ -140,9 +153,14 @@ ioat_compare_ok(struct test_transaction *tx) != 0) return (false); } - } else if (test->testkind == IOAT_TEST_DMA) + } else if (test->testkind == IOAT_TEST_DMA) { if (memcmp(src, dst, tx->length) != 0) return (false); + } else if (test->testkind == IOAT_TEST_RAW_DMA) { + if (test->raw_write) + dst = test->raw_vtarget; + dump_hex(dst, tx->length / 32); + } } return (true); } @@ -242,6 +260,13 @@ ioat_test_submit_1_tx(struct ioat_test *test, bus_dmaengine_t dma) src = vtophys((vm_offset_t)tx->buf[2*i]); dest = vtophys((vm_offset_t)tx->buf[2*i+1]); + if (test->testkind == IOAT_TEST_RAW_DMA) { + if (test->raw_write) + dest = test->raw_target; + else + src = test->raw_target; + } + if (i == tx->depth - 1) { cb = ioat_dma_test_callback; flags = DMA_INT_EN; @@ -250,7 +275,8 @@ ioat_test_submit_1_tx(struct ioat_test *test, bus_dmaengine_t dma) flags = 0; } - if (test->testkind == IOAT_TEST_DMA) + if (test->testkind == IOAT_TEST_DMA || + test->testkind == IOAT_TEST_RAW_DMA) desc = ioat_copy(dma, dest, src, tx->length, cb, tx, flags); else if (test->testkind == IOAT_TEST_FILL) { @@ -328,6 +354,16 @@ ioat_dma_test(void *arg) goto out; } + if (test->testkind == IOAT_TEST_RAW_DMA) { + if (test->raw_is_virtual) { + test->raw_vtarget = (void *)test->raw_target; + test->raw_target = vtophys(test->raw_vtarget); + } else { + test->raw_vtarget = pmap_mapdev(test->raw_target, + test->buffer_size); + } + } + index = g_thread_index++; TAILQ_INIT(&test->free_q); TAILQ_INIT(&test->pend_q); @@ -373,6 +409,9 @@ ioat_dma_test(void *arg) ioat_test_release_memory(test); out: + if (test->testkind == IOAT_TEST_RAW_DMA && !test->raw_is_virtual) + pmap_unmapdev((vm_offset_t)test->raw_vtarget, + test->buffer_size); ioat_put_dmaengine(dmaengine); } diff --git a/sys/dev/ioat/ioat_test.h b/sys/dev/ioat/ioat_test.h index ecfef7d5cb19..b6e6a15acb9e 100644 --- a/sys/dev/ioat/ioat_test.h +++ b/sys/dev/ioat/ioat_test.h @@ -41,6 +41,7 @@ enum ioat_res { enum ioat_test_kind { IOAT_TEST_FILL = 0, IOAT_TEST_DMA, + IOAT_TEST_RAW_DMA, IOAT_NUM_TESTKINDS }; @@ -66,6 +67,12 @@ struct ioat_test { /* If true, check for miscompares after a copy. */ bool verify; + /* DMA directly to/from some memory address */ + uint64_t raw_target; + void *raw_vtarget; + bool raw_write; + bool raw_is_virtual; + /* Internal usage -- not test inputs */ TAILQ_HEAD(, test_transaction) free_q; TAILQ_HEAD(, test_transaction) pend_q; diff --git a/tools/tools/ioat/ioatcontrol.8 b/tools/tools/ioat/ioatcontrol.8 index 7e3234825eda..2306d3876a36 100644 --- a/tools/tools/ioat/ioatcontrol.8 +++ b/tools/tools/ioat/ioatcontrol.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 26, 2015 +.Dd October 28, 2015 .Dt IOATCONTROL 8 .Os .Sh NAME @@ -40,6 +40,14 @@ .Ar [ bufsize .Ar [ chain-len .Ar [ duration ] ] ] +.Nm +.Fl r +.Op Fl v +.Op Fl V +.Op Fl w +.Ar channel_number +.Ar address +.Ar [ bufsize ] .Sh DESCRIPTION .Nm allows one to issue some number of test operations to the @@ -55,6 +63,29 @@ tests copy) Verify copies/fills for accuracy .El .Pp +Alternatively one can use +.Nm +.Fl r +to issue DMA to or from a specific +.Ar address . +The arguments in "raw" mode are: +.Bl -tag -width Ds +.It Fl v +.Ar address +is a kernel virtual address (by default, +.Ar address +is assumed to be a physical address) +.It Fl V +Dump the resulting hex to syslog +.It Fl w +Write to the specified +.Ar address +(by default, +.Nm +.Fl r +reads) +.El +.Pp .Nm operates in one of two modes; if the .Ar duration @@ -77,6 +108,7 @@ argument determines the size of buffers to use for each .Fn ioat_copy invocation. The default is 256 KB. +In raw mode, the default is 4 KB. .Pp The .Ar chain-len diff --git a/tools/tools/ioat/ioatcontrol.c b/tools/tools/ioat/ioatcontrol.c index e55ce4f605d3..90255e7c7d25 100644 --- a/tools/tools/ioat/ioatcontrol.c +++ b/tools/tools/ioat/ioatcontrol.c @@ -50,24 +50,72 @@ usage(void) printf("Usage: %s [-fV] [ " "[ [duration]]]\n", getprogname()); + printf(" %s -r [-vV] []\n", + getprogname()); exit(EX_USAGE); } +static void +main_raw(struct ioat_test *t, int argc, char **argv) +{ + int fd; + + /* Raw DMA defaults */ + t->testkind = IOAT_TEST_RAW_DMA; + t->transactions = 1; + t->chain_depth = 1; + t->buffer_size = 4 * 1024; + + t->raw_target = strtoull(argv[1], NULL, 0); + if (t->raw_target == 0) { + printf("Target shoudln't be NULL\n"); + exit(EX_USAGE); + } + + if (argc >= 3) { + t->buffer_size = atoi(argv[2]); + if (t->buffer_size == 0) { + printf("Buffer size must be greater than zero\n"); + exit(EX_USAGE); + } + } + + fd = open("/dev/ioat_test", O_RDWR); + if (fd < 0) { + printf("Cannot open /dev/ioat_test\n"); + exit(EX_UNAVAILABLE); + } + + (void)ioctl(fd, IOAT_DMATEST, t); + close(fd); + + exit(prettyprint(t)); +} + int main(int argc, char **argv) { struct ioat_test t; int fd, ch; - bool fflag; + bool fflag, rflag; - while ((ch = getopt(argc, argv, "fV")) != -1) { + while ((ch = getopt(argc, argv, "rfvVw")) != -1) { switch (ch) { case 'f': fflag = true; break; + case 'r': + rflag = true; + break; + case 'v': + t.raw_is_virtual = true; + break; case 'V': t.verify = true; break; + case 'w': + t.raw_write = true; + break; default: usage(); } @@ -78,6 +126,11 @@ main(int argc, char **argv) if (argc < 2) usage(); + if (rflag && fflag) { + printf("Invalid: -r and -f\n"); + usage(); + } + /* Defaults for optional args */ t.buffer_size = 256 * 1024; t.chain_depth = 2; @@ -93,6 +146,11 @@ main(int argc, char **argv) return (EX_USAGE); } + if (rflag) { + main_raw(&t, argc, argv); + return (EX_OK); + } + t.transactions = atoi(argv[1]); if (argc >= 3) {