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
This commit is contained in:
Conrad Meyer 2015-10-29 04:16:16 +00:00
parent 52eab858c0
commit e9497f9bbd
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=290129
4 changed files with 141 additions and 5 deletions

View File

@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$");
#include <machine/resource.h>
#include <machine/stdarg.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#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);
}

View File

@ -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;

View File

@ -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

View File

@ -50,24 +50,72 @@ usage(void)
printf("Usage: %s [-fV] <channel #> <txns> [<bufsize> "
"[<chain-len> [duration]]]\n", getprogname());
printf(" %s -r [-vV] <channel #> <addr> [<bufsize>]\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) {