Index: head/sys/dev/ioat/ioat_test.c =================================================================== --- head/sys/dev/ioat/ioat_test.c (revision 292043) +++ head/sys/dev/ioat/ioat_test.c (revision 292044) @@ -1,537 +1,572 @@ /*- * Copyright (C) 2012 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ioat.h" #include "ioat_hw.h" #include "ioat_internal.h" #include "ioat_test.h" #ifndef time_after #define time_after(a,b) ((long)(b) - (long)(a) < 0) #endif MALLOC_DEFINE(M_IOAT_TEST, "ioat_test", "ioat test allocations"); #define IOAT_MAX_BUFS 256 struct test_transaction { void *buf[IOAT_MAX_BUFS]; uint32_t length; uint32_t depth; struct ioat_test *test; TAILQ_ENTRY(test_transaction) entry; }; #define IT_LOCK() mtx_lock(&ioat_test_lk) #define IT_UNLOCK() mtx_unlock(&ioat_test_lk) #define IT_ASSERT() mtx_assert(&ioat_test_lk, MA_OWNED) static struct mtx ioat_test_lk; MTX_SYSINIT(ioat_test_lk, &ioat_test_lk, "test coordination mtx", MTX_DEF); static int g_thread_index = 1; static struct cdev *g_ioat_cdev = NULL; #define ioat_test_log(v, ...) _ioat_test_log((v), "ioat_test: " __VA_ARGS__) static inline void _ioat_test_log(int verbosity, const char *fmt, ...); static void ioat_test_transaction_destroy(struct test_transaction *tx) { + struct ioat_test *test; int i; + test = tx->test; + for (i = 0; i < IOAT_MAX_BUFS; i++) { if (tx->buf[i] != NULL) { - contigfree(tx->buf[i], tx->length, M_IOAT_TEST); + if (test->testkind == IOAT_TEST_DMA_8K) + free(tx->buf[i], M_IOAT_TEST); + else + contigfree(tx->buf[i], tx->length, M_IOAT_TEST); tx->buf[i] = NULL; } } free(tx, M_IOAT_TEST); } static struct -test_transaction *ioat_test_transaction_create(unsigned num_buffers, - uint32_t buffer_size) +test_transaction *ioat_test_transaction_create(struct ioat_test *test, + unsigned num_buffers) { struct test_transaction *tx; unsigned i; tx = malloc(sizeof(*tx), M_IOAT_TEST, M_NOWAIT | M_ZERO); if (tx == NULL) return (NULL); - tx->length = buffer_size; + tx->length = test->buffer_size; for (i = 0; i < num_buffers; i++) { - tx->buf[i] = contigmalloc(buffer_size, M_IOAT_TEST, M_NOWAIT, - 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); + if (test->testkind == IOAT_TEST_DMA_8K) + tx->buf[i] = malloc(test->buffer_size, M_IOAT_TEST, + M_NOWAIT); + else + tx->buf[i] = contigmalloc(test->buffer_size, + M_IOAT_TEST, M_NOWAIT, 0, BUS_SPACE_MAXADDR, + PAGE_SIZE, 0); if (tx->buf[i] == NULL) { ioat_test_transaction_destroy(tx); return (NULL); } } 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) { struct ioat_test *test; char *dst, *src; uint32_t i, j; test = tx->test; for (i = 0; i < tx->depth; i++) { dst = tx->buf[2 * i + 1]; src = tx->buf[2 * i]; if (test->testkind == IOAT_TEST_FILL) { for (j = 0; j < tx->length; j += sizeof(uint64_t)) { if (memcmp(src, &dst[j], MIN(sizeof(uint64_t), tx->length - j)) != 0) return (false); } } 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); } static void ioat_dma_test_callback(void *arg, int error) { struct test_transaction *tx; struct ioat_test *test; if (error != 0) ioat_test_log(0, "%s: Got error: %d\n", __func__, error); tx = arg; test = tx->test; if (test->verify && !ioat_compare_ok(tx)) { ioat_test_log(0, "miscompare found\n"); atomic_add_32(&test->status[IOAT_TEST_MISCOMPARE], tx->depth); } else if (!test->too_late) atomic_add_32(&test->status[IOAT_TEST_OK], tx->depth); IT_LOCK(); TAILQ_REMOVE(&test->pend_q, tx, entry); TAILQ_INSERT_TAIL(&test->free_q, tx, entry); wakeup(&test->free_q); IT_UNLOCK(); } static int ioat_test_prealloc_memory(struct ioat_test *test, int index) { uint32_t i, j, k; struct test_transaction *tx; for (i = 0; i < test->transactions; i++) { - tx = ioat_test_transaction_create(test->chain_depth * 2, - test->buffer_size); + tx = ioat_test_transaction_create(test, test->chain_depth * 2); if (tx == NULL) { ioat_test_log(0, "tx == NULL - memory exhausted\n"); test->status[IOAT_TEST_NO_MEMORY]++; return (ENOMEM); } TAILQ_INSERT_HEAD(&test->free_q, tx, entry); tx->test = test; tx->depth = test->chain_depth; /* fill in source buffers */ for (j = 0; j < (tx->length / sizeof(uint32_t)); j++) { uint32_t val = j + (index << 28); for (k = 0; k < test->chain_depth; k++) { ((uint32_t *)tx->buf[2*k])[j] = ~val; ((uint32_t *)tx->buf[2*k+1])[j] = val; } } } return (0); } static void ioat_test_release_memory(struct ioat_test *test) { struct test_transaction *tx, *s; TAILQ_FOREACH_SAFE(tx, &test->free_q, entry, s) ioat_test_transaction_destroy(tx); TAILQ_INIT(&test->free_q); TAILQ_FOREACH_SAFE(tx, &test->pend_q, entry, s) ioat_test_transaction_destroy(tx); TAILQ_INIT(&test->pend_q); } static void ioat_test_submit_1_tx(struct ioat_test *test, bus_dmaengine_t dma) { struct test_transaction *tx; struct bus_dmadesc *desc; bus_dmaengine_callback_t cb; bus_addr_t src, dest; uint64_t fillpattern; uint32_t i, flags; desc = NULL; IT_LOCK(); while (TAILQ_EMPTY(&test->free_q)) msleep(&test->free_q, &ioat_test_lk, 0, "test_submit", 0); tx = TAILQ_FIRST(&test->free_q); TAILQ_REMOVE(&test->free_q, tx, entry); TAILQ_INSERT_HEAD(&test->pend_q, tx, entry); IT_UNLOCK(); - ioat_acquire(dma); + if (test->testkind != IOAT_TEST_MEMCPY) + ioat_acquire(dma); for (i = 0; i < tx->depth; i++) { + if (test->testkind == IOAT_TEST_MEMCPY) { + memcpy(tx->buf[2 * i + 1], tx->buf[2 * i], tx->length); + if (i == tx->depth - 1) + ioat_dma_test_callback(tx, 0); + continue; + } + 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; } else { cb = NULL; flags = 0; } 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) { fillpattern = *(uint64_t *)tx->buf[2*i]; desc = ioat_blockfill(dma, dest, fillpattern, tx->length, cb, tx, flags); + } else if (test->testkind == IOAT_TEST_DMA_8K) { + bus_addr_t src2, dst2; + + src2 = vtophys((vm_offset_t)tx->buf[2*i] + PAGE_SIZE); + dst2 = vtophys((vm_offset_t)tx->buf[2*i+1] + PAGE_SIZE); + + desc = ioat_copy_8k_aligned(dma, dest, dst2, src, src2, + cb, tx, flags); } if (desc == NULL) break; } + if (test->testkind == IOAT_TEST_MEMCPY) + return; ioat_release(dma); /* * We couldn't issue an IO -- either the device is being detached or * the HW reset. Essentially spin until the device comes back up or * our timer expires. */ if (desc == NULL && tx->depth > 0) { atomic_add_32(&test->status[IOAT_TEST_NO_DMA_ENGINE], tx->depth); IT_LOCK(); TAILQ_REMOVE(&test->pend_q, tx, entry); TAILQ_INSERT_HEAD(&test->free_q, tx, entry); IT_UNLOCK(); } } static void ioat_dma_test(void *arg) { struct ioat_test *test; bus_dmaengine_t dmaengine; uint32_t loops; int index, rc, start, end; test = arg; memset(__DEVOLATILE(void *, test->status), 0, sizeof(test->status)); + + if (test->testkind == IOAT_TEST_DMA_8K && + test->buffer_size != 2 * PAGE_SIZE) { + ioat_test_log(0, "Asked for 8k test and buffer size isn't 8k\n"); + test->status[IOAT_TEST_INVALID_INPUT]++; + return; + } if (test->buffer_size > 1024 * 1024) { ioat_test_log(0, "Buffer size too large >1MB\n"); test->status[IOAT_TEST_NO_MEMORY]++; return; } if (test->chain_depth * 2 > IOAT_MAX_BUFS) { ioat_test_log(0, "Depth too large (> %u)\n", (unsigned)IOAT_MAX_BUFS / 2); test->status[IOAT_TEST_NO_MEMORY]++; return; } if (btoc((uint64_t)test->buffer_size * test->chain_depth * test->transactions) > (physmem / 4)) { ioat_test_log(0, "Sanity check failed -- test would " "use more than 1/4 of phys mem.\n"); test->status[IOAT_TEST_NO_MEMORY]++; return; } if ((uint64_t)test->transactions * test->chain_depth > (1<<16)) { ioat_test_log(0, "Sanity check failed -- test would " "use more than available IOAT ring space.\n"); test->status[IOAT_TEST_NO_MEMORY]++; return; } if (test->testkind >= IOAT_NUM_TESTKINDS) { ioat_test_log(0, "Invalid kind %u\n", (unsigned)test->testkind); test->status[IOAT_TEST_INVALID_INPUT]++; return; } dmaengine = ioat_get_dmaengine(test->channel_index); if (dmaengine == NULL) { ioat_test_log(0, "Couldn't acquire dmaengine\n"); test->status[IOAT_TEST_NO_DMA_ENGINE]++; return; } if (test->testkind == IOAT_TEST_FILL && (to_ioat_softc(dmaengine)->capabilities & IOAT_DMACAP_BFILL) == 0) { ioat_test_log(0, "Hardware doesn't support block fill, aborting test\n"); test->status[IOAT_TEST_INVALID_INPUT]++; 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); if (test->duration == 0) ioat_test_log(1, "Thread %d: num_loops remaining: 0x%08x\n", index, test->transactions); else ioat_test_log(1, "Thread %d: starting\n", index); rc = ioat_test_prealloc_memory(test, index); if (rc != 0) { ioat_test_log(0, "prealloc_memory: %d\n", rc); goto out; } wmb(); test->too_late = false; start = ticks; end = start + (((sbintime_t)test->duration * hz) / 1000); for (loops = 0;; loops++) { if (test->duration == 0 && loops >= test->transactions) break; else if (test->duration != 0 && time_after(ticks, end)) { test->too_late = true; break; } ioat_test_submit_1_tx(test, dmaengine); } ioat_test_log(1, "Test Elapsed: %d ticks (overrun %d), %d sec.\n", ticks - start, ticks - end, (ticks - start) / hz); IT_LOCK(); while (!TAILQ_EMPTY(&test->pend_q)) msleep(&test->free_q, &ioat_test_lk, 0, "ioattestcompl", hz); IT_UNLOCK(); ioat_test_log(1, "Test Elapsed2: %d ticks (overrun %d), %d sec.\n", ticks - start, ticks - end, (ticks - start) / hz); 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); } static int ioat_test_open(struct cdev *dev, int flags, int fmt, struct thread *td) { return (0); } static int ioat_test_close(struct cdev *dev, int flags, int fmt, struct thread *td) { return (0); } static int ioat_test_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg, int flag, struct thread *td) { switch (cmd) { case IOAT_DMATEST: ioat_dma_test(arg); break; default: return (EINVAL); } return (0); } static struct cdevsw ioat_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = ioat_test_open, .d_close = ioat_test_close, .d_ioctl = ioat_test_ioctl, .d_name = "ioat_test", }; static int enable_ioat_test(bool enable) { mtx_assert(&Giant, MA_OWNED); if (enable && g_ioat_cdev == NULL) { g_ioat_cdev = make_dev(&ioat_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "ioat_test"); } else if (!enable && g_ioat_cdev != NULL) { destroy_dev(g_ioat_cdev); g_ioat_cdev = NULL; } return (0); } static int sysctl_enable_ioat_test(SYSCTL_HANDLER_ARGS) { int error, enabled; enabled = (g_ioat_cdev != NULL); error = sysctl_handle_int(oidp, &enabled, 0, req); if (error != 0 || req->newptr == NULL) return (error); enable_ioat_test(enabled); return (0); } SYSCTL_PROC(_hw_ioat, OID_AUTO, enable_ioat_test, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_enable_ioat_test, "I", "Non-zero: Enable the /dev/ioat_test device"); void ioat_test_attach(void) { char *val; val = kern_getenv("hw.ioat.enable_ioat_test"); if (val != NULL && strcmp(val, "0") != 0) { mtx_lock(&Giant); enable_ioat_test(true); mtx_unlock(&Giant); } freeenv(val); } void ioat_test_detach(void) { mtx_lock(&Giant); enable_ioat_test(false); mtx_unlock(&Giant); } static inline void _ioat_test_log(int verbosity, const char *fmt, ...) { va_list argp; if (verbosity > g_ioat_debug_level) return; va_start(argp, fmt); vprintf(fmt, argp); va_end(argp); } Index: head/sys/dev/ioat/ioat_test.h =================================================================== --- head/sys/dev/ioat/ioat_test.h (revision 292043) +++ head/sys/dev/ioat/ioat_test.h (revision 292044) @@ -1,84 +1,86 @@ /*- * Copyright (C) 2012 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. */ __FBSDID("$FreeBSD$"); #ifndef __IOAT_TEST_H__ #define __IOAT_TEST_H__ enum ioat_res { IOAT_TEST_OK = 0, IOAT_TEST_NO_DMA_ENGINE, IOAT_TEST_NO_MEMORY, IOAT_TEST_MISCOMPARE, IOAT_TEST_INVALID_INPUT, IOAT_NUM_RES }; enum ioat_test_kind { IOAT_TEST_FILL = 0, IOAT_TEST_DMA, IOAT_TEST_RAW_DMA, + IOAT_TEST_DMA_8K, + IOAT_TEST_MEMCPY, IOAT_NUM_TESTKINDS }; struct test_transaction; struct ioat_test { volatile uint32_t status[IOAT_NUM_RES]; uint32_t channel_index; enum ioat_test_kind testkind; /* HW max of 1MB */ uint32_t buffer_size; uint32_t chain_depth; uint32_t transactions; /* * If non-zero, duration is time in ms; * If zero, bounded by 'transactions' above. */ uint32_t duration; /* 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; volatile bool too_late; }; #define IOAT_DMATEST _IOWR('i', 0, struct ioat_test) #endif /* __IOAT_TEST_H__ */ Index: head/tools/tools/ioat/ioatcontrol.8 =================================================================== --- head/tools/tools/ioat/ioatcontrol.8 (revision 292043) +++ head/tools/tools/ioat/ioatcontrol.8 (revision 292044) @@ -1,164 +1,170 @@ .\" Copyright (c) 2015 EMC / Isilon Storage Division .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. 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. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. .\" .\" $FreeBSD$ .\" -.Dd October 28, 2015 +.Dd December 9, 2015 .Dt IOATCONTROL 8 .Os .Sh NAME .Nm ioatcontrol .Nd Userspace tool to test .Xr ioat 4 .Sh SYNOPSIS .Nm +.Op Fl E .Op Fl f +.Op Fl m .Op Fl V .Ar channel_number .Ar num_txns .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 .Xr ioat 4 driver on a specific hardware channel. The arguments are as follows: .Bl -tag -width Ds +.It Fl E +Test non-contiguous 8k copy. .It Fl f Test block fill (by default, .Nm tests copy) +.It Fl m +Test memcpy instead of DMA. .It Fl V 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 argument is passed, .Nm tries to estimate the copy rate in bytes per second by running .Ar num_txns repeatedly in loop. If .Ar duration is not passed, .Nm only runs through .Ar num_txns once and prints the total bytes copied, as well as error information. .Pp The .Ar bufsize 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 argument determines the number of copies to chain together in a single DMA transaction. The default is 1, and the maximum is currently 4. .Pp The .Ar duration argument specifies an approximate time limit for the test, in milliseconds. .Pp The test will allocate two chunks of memory for each component of each transaction's chain. It will initialize them with specific data patterns. During the test, it submits DMA requests to copy between pairs of buffers. If the .Fl V flag was specified, it will compare the contents in the callback for a copy error. .Sh FILES .Pa /dev/ioat_test .Pp The interface between .Nm and .Xr ioat 4 . .Xr ioat 4 exposes it with .Cd hw.ioat.enable_ioat_test=1 . .Sh DIAGNOSTICS The wait channel .Va test_submit indicates that the test code has enqueued all requested transactions and is waiting on the IOAT hardware to complete one before issuing another operation. .Sh SEE ALSO .Xr ioat 4 .Sh HISTORY The .Xr ioat 4 driver first appeared in .Fx 11.0 . .Sh AUTHORS The .Xr ioat 4 driver and .Nm tool were developed by .An \&Jim Harris Aq Mt jimharris@FreeBSD.org , .An \&Carl Delsey Aq Mt carl.r.delsey@intel.com , and .An \&Conrad Meyer Aq Mt cem@FreeBSD.org . This manual page was written by .An \&Conrad Meyer Aq Mt cem@FreeBSD.org . Index: head/tools/tools/ioat/ioatcontrol.c =================================================================== --- head/tools/tools/ioat/ioatcontrol.c (revision 292043) +++ head/tools/tools/ioat/ioatcontrol.c (revision 292044) @@ -1,229 +1,248 @@ /*- * Copyright (C) 2012 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include "ioat_test.h" static int prettyprint(struct ioat_test *); static void usage(void) { - printf("Usage: %s [-fV] [ " + printf("Usage: %s [-E|-f|-m] [-V] [ " "[ [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, rflag; + bool fflag, rflag, Eflag, mflag; + unsigned modeflags; - while ((ch = getopt(argc, argv, "rfvVw")) != -1) { + fflag = rflag = Eflag = mflag = false; + modeflags = 0; + + while ((ch = getopt(argc, argv, "EfmrvVw")) != -1) { switch (ch) { + case 'E': + Eflag = true; + modeflags++; + break; case 'f': fflag = true; + modeflags++; break; + case 'm': + mflag = true; + modeflags++; + break; case 'r': rflag = true; + modeflags++; break; case 'v': t.raw_is_virtual = true; break; case 'V': t.verify = true; break; case 'w': t.raw_write = true; break; default: usage(); } } argc -= optind; argv += optind; if (argc < 2) usage(); - if (rflag && fflag) { - printf("Invalid: -r and -f\n"); + if (modeflags > 1) { + printf("Invalid: Cannot use >1 mode flag (-E, -f, -m, or -r)\n"); usage(); } /* Defaults for optional args */ t.buffer_size = 256 * 1024; t.chain_depth = 2; t.duration = 0; t.testkind = IOAT_TEST_DMA; if (fflag) t.testkind = IOAT_TEST_FILL; + else if (Eflag) { + t.testkind = IOAT_TEST_DMA_8K; + t.buffer_size = 8 * 1024; + } else if (mflag) + t.testkind = IOAT_TEST_MEMCPY; t.channel_index = atoi(argv[0]); if (t.channel_index > 8) { printf("Channel number must be between 0 and 7.\n"); return (EX_USAGE); } if (rflag) { main_raw(&t, argc, argv); return (EX_OK); } t.transactions = atoi(argv[1]); if (argc >= 3) { t.buffer_size = atoi(argv[2]); if (t.buffer_size == 0) { printf("Buffer size must be greater than zero\n"); return (EX_USAGE); } } if (argc >= 4) { t.chain_depth = atoi(argv[3]); if (t.chain_depth < 1) { printf("Chain length must be greater than zero\n"); return (EX_USAGE); } } if (argc >= 5) { t.duration = atoi(argv[4]); if (t.duration < 1) { printf("Duration must be greater than zero\n"); return (EX_USAGE); } } fd = open("/dev/ioat_test", O_RDWR); if (fd < 0) { printf("Cannot open /dev/ioat_test\n"); return (EX_UNAVAILABLE); } (void)ioctl(fd, IOAT_DMATEST, &t); close(fd); return (prettyprint(&t)); } static int prettyprint(struct ioat_test *t) { char bps[10], bytesh[10]; uintmax_t bytes; if (t->status[IOAT_TEST_NO_DMA_ENGINE] != 0 || t->status[IOAT_TEST_NO_MEMORY] != 0 || t->status[IOAT_TEST_MISCOMPARE] != 0) { printf("Errors:\n"); if (t->status[IOAT_TEST_NO_DMA_ENGINE] != 0) printf("\tNo DMA engine present: %u\n", (unsigned)t->status[IOAT_TEST_NO_DMA_ENGINE]); if (t->status[IOAT_TEST_NO_MEMORY] != 0) printf("\tOut of memory: %u\n", (unsigned)t->status[IOAT_TEST_NO_MEMORY]); if (t->status[IOAT_TEST_MISCOMPARE] != 0) printf("\tMiscompares: %u\n", (unsigned)t->status[IOAT_TEST_MISCOMPARE]); } printf("Processed %u txns\n", (unsigned)t->status[IOAT_TEST_OK] / t->chain_depth); bytes = (uintmax_t)t->buffer_size * t->status[IOAT_TEST_OK]; humanize_number(bytesh, sizeof(bytesh), (int64_t)bytes, "B", HN_AUTOSCALE, HN_DECIMAL); if (t->duration) { humanize_number(bps, sizeof(bps), (int64_t)1000 * bytes / t->duration, "B/s", HN_AUTOSCALE, HN_DECIMAL); printf("%ju (%s) copied in %u ms (%s)\n", bytes, bytesh, (unsigned)t->duration, bps); } else printf("%ju (%s) copied\n", bytes, bytesh); return (EX_OK); }