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:
parent
52eab858c0
commit
e9497f9bbd
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user