1287117Scem/*-
2287117Scem * Copyright (C) 2012 Intel Corporation
3287117Scem * All rights reserved.
4287117Scem *
5287117Scem * Redistribution and use in source and binary forms, with or without
6287117Scem * modification, are permitted provided that the following conditions
7287117Scem * are met:
8287117Scem * 1. Redistributions of source code must retain the above copyright
9287117Scem *    notice, this list of conditions and the following disclaimer.
10287117Scem * 2. Redistributions in binary form must reproduce the above copyright
11287117Scem *    notice, this list of conditions and the following disclaimer in the
12287117Scem *    documentation and/or other materials provided with the distribution.
13287117Scem *
14287117Scem * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15287117Scem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16287117Scem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17287117Scem * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18287117Scem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19287117Scem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20287117Scem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21287117Scem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22287117Scem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23287117Scem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24287117Scem * SUCH DAMAGE.
25287117Scem */
26287117Scem
27287117Scem#include <sys/cdefs.h>
28287117Scem__FBSDID("$FreeBSD: stable/11/sys/dev/ioat/ioat_test.c 355198 2019-11-29 00:38:34Z mav $");
29287117Scem
30287117Scem#include <sys/param.h>
31287117Scem#include <sys/systm.h>
32287117Scem#include <sys/bus.h>
33287117Scem#include <sys/conf.h>
34287117Scem#include <sys/ioccom.h>
35287117Scem#include <sys/kernel.h>
36287117Scem#include <sys/lock.h>
37287117Scem#include <sys/malloc.h>
38287117Scem#include <sys/module.h>
39287117Scem#include <sys/mutex.h>
40287117Scem#include <sys/rman.h>
41287117Scem#include <sys/sysctl.h>
42287117Scem#include <dev/pci/pcireg.h>
43287117Scem#include <dev/pci/pcivar.h>
44287117Scem#include <machine/bus.h>
45287117Scem#include <machine/resource.h>
46289776Scem#include <machine/stdarg.h>
47287117Scem#include <vm/vm.h>
48290129Scem#include <vm/vm_param.h>
49287117Scem#include <vm/pmap.h>
50287117Scem
51287117Scem#include "ioat.h"
52287117Scem#include "ioat_hw.h"
53287117Scem#include "ioat_internal.h"
54287117Scem#include "ioat_test.h"
55287117Scem
56289733Scem#ifndef time_after
57289733Scem#define	time_after(a,b)		((long)(b) - (long)(a) < 0)
58289733Scem#endif
59289733Scem
60287117ScemMALLOC_DEFINE(M_IOAT_TEST, "ioat_test", "ioat test allocations");
61287117Scem
62289733Scem#define	IOAT_MAX_BUFS	256
63287117Scem
64287117Scemstruct test_transaction {
65287117Scem	void			*buf[IOAT_MAX_BUFS];
66287117Scem	uint32_t		length;
67289733Scem	uint32_t		depth;
68355198Smav	uint32_t		crc[IOAT_MAX_BUFS];
69287117Scem	struct ioat_test	*test;
70289733Scem	TAILQ_ENTRY(test_transaction)	entry;
71287117Scem};
72287117Scem
73289733Scem#define	IT_LOCK()	mtx_lock(&ioat_test_lk)
74289733Scem#define	IT_UNLOCK()	mtx_unlock(&ioat_test_lk)
75289733Scem#define	IT_ASSERT()	mtx_assert(&ioat_test_lk, MA_OWNED)
76289733Scemstatic struct mtx ioat_test_lk;
77289733ScemMTX_SYSINIT(ioat_test_lk, &ioat_test_lk, "test coordination mtx", MTX_DEF);
78289733Scem
79287117Scemstatic int g_thread_index = 1;
80287117Scemstatic struct cdev *g_ioat_cdev = NULL;
81287117Scem
82289777Scem#define	ioat_test_log(v, ...)	_ioat_test_log((v), "ioat_test: " __VA_ARGS__)
83315070Savgstatic void _ioat_test_log(int verbosity, const char *fmt, ...);
84289776Scem
85287117Scemstatic void
86287117Scemioat_test_transaction_destroy(struct test_transaction *tx)
87287117Scem{
88292044Scem	struct ioat_test *test;
89287117Scem	int i;
90287117Scem
91292044Scem	test = tx->test;
92292044Scem
93287117Scem	for (i = 0; i < IOAT_MAX_BUFS; i++) {
94287117Scem		if (tx->buf[i] != NULL) {
95292044Scem			if (test->testkind == IOAT_TEST_DMA_8K)
96292044Scem				free(tx->buf[i], M_IOAT_TEST);
97292044Scem			else
98292044Scem				contigfree(tx->buf[i], tx->length, M_IOAT_TEST);
99287117Scem			tx->buf[i] = NULL;
100287117Scem		}
101287117Scem	}
102287117Scem
103287117Scem	free(tx, M_IOAT_TEST);
104287117Scem}
105287117Scem
106287117Scemstatic struct
107292044Scemtest_transaction *ioat_test_transaction_create(struct ioat_test *test,
108292044Scem    unsigned num_buffers)
109287117Scem{
110287117Scem	struct test_transaction *tx;
111289733Scem	unsigned i;
112287117Scem
113289733Scem	tx = malloc(sizeof(*tx), M_IOAT_TEST, M_NOWAIT | M_ZERO);
114287117Scem	if (tx == NULL)
115287117Scem		return (NULL);
116287117Scem
117292044Scem	tx->length = test->buffer_size;
118287117Scem
119287117Scem	for (i = 0; i < num_buffers; i++) {
120292044Scem		if (test->testkind == IOAT_TEST_DMA_8K)
121292044Scem			tx->buf[i] = malloc(test->buffer_size, M_IOAT_TEST,
122292044Scem			    M_NOWAIT);
123292044Scem		else
124292044Scem			tx->buf[i] = contigmalloc(test->buffer_size,
125292044Scem			    M_IOAT_TEST, M_NOWAIT, 0, BUS_SPACE_MAXADDR,
126292044Scem			    PAGE_SIZE, 0);
127287117Scem
128287117Scem		if (tx->buf[i] == NULL) {
129287117Scem			ioat_test_transaction_destroy(tx);
130287117Scem			return (NULL);
131287117Scem		}
132287117Scem	}
133287117Scem	return (tx);
134287117Scem}
135287117Scem
136290129Scemstatic void
137290129Scemdump_hex(void *p, size_t chunks)
138290129Scem{
139290129Scem	size_t i, j;
140290129Scem
141290129Scem	for (i = 0; i < chunks; i++) {
142290129Scem		for (j = 0; j < 8; j++)
143290129Scem			printf("%08x ", ((uint32_t *)p)[i * 8 + j]);
144290129Scem		printf("\n");
145290129Scem	}
146290129Scem}
147290129Scem
148289733Scemstatic bool
149289733Scemioat_compare_ok(struct test_transaction *tx)
150289733Scem{
151290021Scem	struct ioat_test *test;
152290021Scem	char *dst, *src;
153290021Scem	uint32_t i, j;
154289733Scem
155290021Scem	test = tx->test;
156290021Scem
157289733Scem	for (i = 0; i < tx->depth; i++) {
158290021Scem		dst = tx->buf[2 * i + 1];
159290021Scem		src = tx->buf[2 * i];
160290021Scem
161290021Scem		if (test->testkind == IOAT_TEST_FILL) {
162290021Scem			for (j = 0; j < tx->length; j += sizeof(uint64_t)) {
163290021Scem				if (memcmp(src, &dst[j],
164290021Scem					MIN(sizeof(uint64_t), tx->length - j))
165290021Scem				    != 0)
166290021Scem					return (false);
167290021Scem			}
168290129Scem		} else if (test->testkind == IOAT_TEST_DMA) {
169290021Scem			if (memcmp(src, dst, tx->length) != 0)
170290021Scem				return (false);
171290129Scem		} else if (test->testkind == IOAT_TEST_RAW_DMA) {
172290129Scem			if (test->raw_write)
173290129Scem				dst = test->raw_vtarget;
174290129Scem			dump_hex(dst, tx->length / 32);
175290129Scem		}
176289733Scem	}
177289733Scem	return (true);
178289733Scem}
179289733Scem
180287117Scemstatic void
181290229Scemioat_dma_test_callback(void *arg, int error)
182287117Scem{
183287117Scem	struct test_transaction *tx;
184287117Scem	struct ioat_test *test;
185287117Scem
186290229Scem	if (error != 0)
187290229Scem		ioat_test_log(0, "%s: Got error: %d\n", __func__, error);
188290229Scem
189287117Scem	tx = arg;
190287117Scem	test = tx->test;
191287117Scem
192289733Scem	if (test->verify && !ioat_compare_ok(tx)) {
193289776Scem		ioat_test_log(0, "miscompare found\n");
194289733Scem		atomic_add_32(&test->status[IOAT_TEST_MISCOMPARE], tx->depth);
195289733Scem	} else if (!test->too_late)
196289733Scem		atomic_add_32(&test->status[IOAT_TEST_OK], tx->depth);
197289733Scem
198289733Scem	IT_LOCK();
199289733Scem	TAILQ_REMOVE(&test->pend_q, tx, entry);
200289733Scem	TAILQ_INSERT_TAIL(&test->free_q, tx, entry);
201289733Scem	wakeup(&test->free_q);
202289733Scem	IT_UNLOCK();
203289733Scem}
204289733Scem
205289733Scemstatic int
206289733Scemioat_test_prealloc_memory(struct ioat_test *test, int index)
207289733Scem{
208289733Scem	uint32_t i, j, k;
209289733Scem	struct test_transaction *tx;
210289733Scem
211289733Scem	for (i = 0; i < test->transactions; i++) {
212292044Scem		tx = ioat_test_transaction_create(test, test->chain_depth * 2);
213289733Scem		if (tx == NULL) {
214289776Scem			ioat_test_log(0, "tx == NULL - memory exhausted\n");
215289733Scem			test->status[IOAT_TEST_NO_MEMORY]++;
216289733Scem			return (ENOMEM);
217289733Scem		}
218289733Scem
219289733Scem		TAILQ_INSERT_HEAD(&test->free_q, tx, entry);
220289733Scem
221289733Scem		tx->test = test;
222289733Scem		tx->depth = test->chain_depth;
223289733Scem
224289733Scem		/* fill in source buffers */
225289733Scem		for (j = 0; j < (tx->length / sizeof(uint32_t)); j++) {
226289733Scem			uint32_t val = j + (index << 28);
227289733Scem
228289733Scem			for (k = 0; k < test->chain_depth; k++) {
229289733Scem				((uint32_t *)tx->buf[2*k])[j] = ~val;
230289733Scem				((uint32_t *)tx->buf[2*k+1])[j] = val;
231289733Scem			}
232289733Scem		}
233287117Scem	}
234289733Scem	return (0);
235287117Scem}
236287117Scem
237287117Scemstatic void
238289733Scemioat_test_release_memory(struct ioat_test *test)
239289733Scem{
240289733Scem	struct test_transaction *tx, *s;
241289733Scem
242289733Scem	TAILQ_FOREACH_SAFE(tx, &test->free_q, entry, s)
243289733Scem		ioat_test_transaction_destroy(tx);
244289733Scem	TAILQ_INIT(&test->free_q);
245289733Scem
246289733Scem	TAILQ_FOREACH_SAFE(tx, &test->pend_q, entry, s)
247289733Scem		ioat_test_transaction_destroy(tx);
248289733Scem	TAILQ_INIT(&test->pend_q);
249289733Scem}
250289733Scem
251289733Scemstatic void
252289733Scemioat_test_submit_1_tx(struct ioat_test *test, bus_dmaengine_t dma)
253289733Scem{
254289733Scem	struct test_transaction *tx;
255289733Scem	struct bus_dmadesc *desc;
256289733Scem	bus_dmaengine_callback_t cb;
257289733Scem	bus_addr_t src, dest;
258290021Scem	uint64_t fillpattern;
259289733Scem	uint32_t i, flags;
260289733Scem
261290021Scem	desc = NULL;
262290021Scem
263289733Scem	IT_LOCK();
264289733Scem	while (TAILQ_EMPTY(&test->free_q))
265289733Scem		msleep(&test->free_q, &ioat_test_lk, 0, "test_submit", 0);
266289733Scem
267289733Scem	tx = TAILQ_FIRST(&test->free_q);
268289733Scem	TAILQ_REMOVE(&test->free_q, tx, entry);
269289733Scem	TAILQ_INSERT_HEAD(&test->pend_q, tx, entry);
270289733Scem	IT_UNLOCK();
271289733Scem
272292044Scem	if (test->testkind != IOAT_TEST_MEMCPY)
273292044Scem		ioat_acquire(dma);
274289733Scem	for (i = 0; i < tx->depth; i++) {
275292044Scem		if (test->testkind == IOAT_TEST_MEMCPY) {
276292044Scem			memcpy(tx->buf[2 * i + 1], tx->buf[2 * i], tx->length);
277292044Scem			if (i == tx->depth - 1)
278292044Scem				ioat_dma_test_callback(tx, 0);
279292044Scem			continue;
280292044Scem		}
281292044Scem
282289733Scem		src = vtophys((vm_offset_t)tx->buf[2*i]);
283289733Scem		dest = vtophys((vm_offset_t)tx->buf[2*i+1]);
284289733Scem
285290129Scem		if (test->testkind == IOAT_TEST_RAW_DMA) {
286290129Scem			if (test->raw_write)
287290129Scem				dest = test->raw_target;
288290129Scem			else
289290129Scem				src = test->raw_target;
290290129Scem		}
291290129Scem
292289733Scem		if (i == tx->depth - 1) {
293289733Scem			cb = ioat_dma_test_callback;
294289733Scem			flags = DMA_INT_EN;
295289733Scem		} else {
296289733Scem			cb = NULL;
297289733Scem			flags = 0;
298289733Scem		}
299289733Scem
300290129Scem		if (test->testkind == IOAT_TEST_DMA ||
301290129Scem		    test->testkind == IOAT_TEST_RAW_DMA)
302290021Scem			desc = ioat_copy(dma, dest, src, tx->length, cb, tx,
303290021Scem			    flags);
304290021Scem		else if (test->testkind == IOAT_TEST_FILL) {
305290021Scem			fillpattern = *(uint64_t *)tx->buf[2*i];
306290021Scem			desc = ioat_blockfill(dma, dest, fillpattern,
307290021Scem			    tx->length, cb, tx, flags);
308292044Scem		} else if (test->testkind == IOAT_TEST_DMA_8K) {
309292044Scem			bus_addr_t src2, dst2;
310292044Scem
311292044Scem			src2 = vtophys((vm_offset_t)tx->buf[2*i] + PAGE_SIZE);
312292044Scem			dst2 = vtophys((vm_offset_t)tx->buf[2*i+1] + PAGE_SIZE);
313292044Scem
314292044Scem			desc = ioat_copy_8k_aligned(dma, dest, dst2, src, src2,
315292044Scem			    cb, tx, flags);
316355198Smav		} else if (test->testkind == IOAT_TEST_DMA_8K_PB) {
317355198Smav			bus_addr_t src2, dst2;
318355198Smav
319355198Smav			src2 = vtophys((vm_offset_t)tx->buf[2*i+1] + PAGE_SIZE);
320355198Smav			dst2 = vtophys((vm_offset_t)tx->buf[2*i] + PAGE_SIZE);
321355198Smav
322355198Smav			desc = ioat_copy_8k_aligned(dma, dest, dst2, src, src2,
323355198Smav			    cb, tx, flags);
324355198Smav		} else if (test->testkind == IOAT_TEST_DMA_CRC) {
325355198Smav			bus_addr_t crc;
326355198Smav
327355198Smav			tx->crc[i] = 0;
328355198Smav			crc = vtophys((vm_offset_t)&tx->crc[i]);
329355198Smav			desc = ioat_crc(dma, src, tx->length,
330355198Smav			    NULL, crc, cb, tx, flags | DMA_CRC_STORE);
331355198Smav		} else if (test->testkind == IOAT_TEST_DMA_CRC_COPY) {
332355198Smav			bus_addr_t crc;
333355198Smav
334355198Smav			tx->crc[i] = 0;
335355198Smav			crc = vtophys((vm_offset_t)&tx->crc[i]);
336355198Smav			desc = ioat_copy_crc(dma, dest, src, tx->length,
337355198Smav			    NULL, crc, cb, tx, flags | DMA_CRC_STORE);
338290021Scem		}
339289733Scem		if (desc == NULL)
340290132Scem			break;
341289733Scem	}
342292044Scem	if (test->testkind == IOAT_TEST_MEMCPY)
343292044Scem		return;
344289733Scem	ioat_release(dma);
345290132Scem
346290132Scem	/*
347290132Scem	 * We couldn't issue an IO -- either the device is being detached or
348290132Scem	 * the HW reset.  Essentially spin until the device comes back up or
349290132Scem	 * our timer expires.
350290132Scem	 */
351290132Scem	if (desc == NULL && tx->depth > 0) {
352290132Scem		atomic_add_32(&test->status[IOAT_TEST_NO_DMA_ENGINE], tx->depth);
353290132Scem		IT_LOCK();
354290132Scem		TAILQ_REMOVE(&test->pend_q, tx, entry);
355290132Scem		TAILQ_INSERT_HEAD(&test->free_q, tx, entry);
356290132Scem		IT_UNLOCK();
357290132Scem	}
358289733Scem}
359289733Scem
360289733Scemstatic void
361287117Scemioat_dma_test(void *arg)
362287117Scem{
363292229Scem	struct ioat_softc *ioat;
364287117Scem	struct ioat_test *test;
365287117Scem	bus_dmaengine_t dmaengine;
366287117Scem	uint32_t loops;
367292229Scem	int index, rc, start, end, error;
368287117Scem
369287117Scem	test = arg;
370289733Scem	memset(__DEVOLATILE(void *, test->status), 0, sizeof(test->status));
371287117Scem
372355198Smav	if ((test->testkind == IOAT_TEST_DMA_8K ||
373355198Smav	    test->testkind == IOAT_TEST_DMA_8K_PB) &&
374292044Scem	    test->buffer_size != 2 * PAGE_SIZE) {
375292044Scem		ioat_test_log(0, "Asked for 8k test and buffer size isn't 8k\n");
376292044Scem		test->status[IOAT_TEST_INVALID_INPUT]++;
377292044Scem		return;
378292044Scem	}
379292044Scem
380289733Scem	if (test->buffer_size > 1024 * 1024) {
381289776Scem		ioat_test_log(0, "Buffer size too large >1MB\n");
382289733Scem		test->status[IOAT_TEST_NO_MEMORY]++;
383289733Scem		return;
384289733Scem	}
385287117Scem
386289733Scem	if (test->chain_depth * 2 > IOAT_MAX_BUFS) {
387289776Scem		ioat_test_log(0, "Depth too large (> %u)\n",
388289733Scem		    (unsigned)IOAT_MAX_BUFS / 2);
389289733Scem		test->status[IOAT_TEST_NO_MEMORY]++;
390289733Scem		return;
391289733Scem	}
392289733Scem
393289733Scem	if (btoc((uint64_t)test->buffer_size * test->chain_depth *
394289733Scem	    test->transactions) > (physmem / 4)) {
395289776Scem		ioat_test_log(0, "Sanity check failed -- test would "
396289733Scem		    "use more than 1/4 of phys mem.\n");
397289733Scem		test->status[IOAT_TEST_NO_MEMORY]++;
398289733Scem		return;
399289733Scem	}
400289733Scem
401289733Scem	if ((uint64_t)test->transactions * test->chain_depth > (1<<16)) {
402289776Scem		ioat_test_log(0, "Sanity check failed -- test would "
403289733Scem		    "use more than available IOAT ring space.\n");
404289733Scem		test->status[IOAT_TEST_NO_MEMORY]++;
405289733Scem		return;
406289733Scem	}
407289733Scem
408290021Scem	if (test->testkind >= IOAT_NUM_TESTKINDS) {
409290021Scem		ioat_test_log(0, "Invalid kind %u\n",
410290021Scem		    (unsigned)test->testkind);
411290021Scem		test->status[IOAT_TEST_INVALID_INPUT]++;
412290021Scem		return;
413290021Scem	}
414290021Scem
415297746Scem	dmaengine = ioat_get_dmaengine(test->channel_index, M_NOWAIT);
416287117Scem	if (dmaengine == NULL) {
417289776Scem		ioat_test_log(0, "Couldn't acquire dmaengine\n");
418289733Scem		test->status[IOAT_TEST_NO_DMA_ENGINE]++;
419287117Scem		return;
420287117Scem	}
421292229Scem	ioat = to_ioat_softc(dmaengine);
422287117Scem
423290087Scem	if (test->testkind == IOAT_TEST_FILL &&
424292229Scem	    (ioat->capabilities & IOAT_DMACAP_BFILL) == 0)
425290087Scem	{
426290087Scem		ioat_test_log(0,
427290087Scem		    "Hardware doesn't support block fill, aborting test\n");
428290087Scem		test->status[IOAT_TEST_INVALID_INPUT]++;
429290087Scem		goto out;
430290087Scem	}
431290087Scem
432292229Scem	if (test->coalesce_period > ioat->intrdelay_max) {
433292229Scem		ioat_test_log(0,
434292229Scem		    "Hardware doesn't support intrdelay of %u us.\n",
435292229Scem		    (unsigned)test->coalesce_period);
436292229Scem		test->status[IOAT_TEST_INVALID_INPUT]++;
437292229Scem		goto out;
438292229Scem	}
439292229Scem	error = ioat_set_interrupt_coalesce(dmaengine, test->coalesce_period);
440292229Scem	if (error == ENODEV && test->coalesce_period == 0)
441292229Scem		error = 0;
442292229Scem	if (error != 0) {
443292229Scem		ioat_test_log(0, "ioat_set_interrupt_coalesce: %d\n", error);
444292229Scem		test->status[IOAT_TEST_INVALID_INPUT]++;
445292229Scem		goto out;
446292229Scem	}
447292229Scem
448292229Scem	if (test->zero_stats)
449292229Scem		memset(&ioat->stats, 0, sizeof(ioat->stats));
450292229Scem
451290129Scem	if (test->testkind == IOAT_TEST_RAW_DMA) {
452290129Scem		if (test->raw_is_virtual) {
453290129Scem			test->raw_vtarget = (void *)test->raw_target;
454290129Scem			test->raw_target = vtophys(test->raw_vtarget);
455290129Scem		} else {
456290129Scem			test->raw_vtarget = pmap_mapdev(test->raw_target,
457290129Scem			    test->buffer_size);
458290129Scem		}
459290129Scem	}
460290129Scem
461289733Scem	index = g_thread_index++;
462289733Scem	TAILQ_INIT(&test->free_q);
463289733Scem	TAILQ_INIT(&test->pend_q);
464287117Scem
465289733Scem	if (test->duration == 0)
466289776Scem		ioat_test_log(1, "Thread %d: num_loops remaining: 0x%08x\n",
467289733Scem		    index, test->transactions);
468289733Scem	else
469289776Scem		ioat_test_log(1, "Thread %d: starting\n", index);
470287117Scem
471289733Scem	rc = ioat_test_prealloc_memory(test, index);
472289733Scem	if (rc != 0) {
473289776Scem		ioat_test_log(0, "prealloc_memory: %d\n", rc);
474289907Scem		goto out;
475289733Scem	}
476289733Scem	wmb();
477287117Scem
478289733Scem	test->too_late = false;
479289733Scem	start = ticks;
480289733Scem	end = start + (((sbintime_t)test->duration * hz) / 1000);
481289733Scem
482289733Scem	for (loops = 0;; loops++) {
483289733Scem		if (test->duration == 0 && loops >= test->transactions)
484289733Scem			break;
485289733Scem		else if (test->duration != 0 && time_after(ticks, end)) {
486289733Scem			test->too_late = true;
487289733Scem			break;
488287117Scem		}
489287117Scem
490289733Scem		ioat_test_submit_1_tx(test, dmaengine);
491289733Scem	}
492287117Scem
493289776Scem	ioat_test_log(1, "Test Elapsed: %d ticks (overrun %d), %d sec.\n",
494289733Scem	    ticks - start, ticks - end, (ticks - start) / hz);
495287117Scem
496289733Scem	IT_LOCK();
497289733Scem	while (!TAILQ_EMPTY(&test->pend_q))
498289733Scem		msleep(&test->free_q, &ioat_test_lk, 0, "ioattestcompl", hz);
499289733Scem	IT_UNLOCK();
500287117Scem
501289776Scem	ioat_test_log(1, "Test Elapsed2: %d ticks (overrun %d), %d sec.\n",
502289733Scem	    ticks - start, ticks - end, (ticks - start) / hz);
503287117Scem
504289733Scem	ioat_test_release_memory(test);
505289907Scemout:
506290129Scem	if (test->testkind == IOAT_TEST_RAW_DMA && !test->raw_is_virtual)
507290129Scem		pmap_unmapdev((vm_offset_t)test->raw_vtarget,
508290129Scem		    test->buffer_size);
509289907Scem	ioat_put_dmaengine(dmaengine);
510287117Scem}
511287117Scem
512287117Scemstatic int
513287117Scemioat_test_open(struct cdev *dev, int flags, int fmt, struct thread *td)
514287117Scem{
515287117Scem
516287117Scem	return (0);
517287117Scem}
518287117Scem
519287117Scemstatic int
520287117Scemioat_test_close(struct cdev *dev, int flags, int fmt, struct thread *td)
521287117Scem{
522287117Scem
523287117Scem	return (0);
524287117Scem}
525287117Scem
526287117Scemstatic int
527287117Scemioat_test_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg, int flag,
528287117Scem    struct thread *td)
529287117Scem{
530287117Scem
531287117Scem	switch (cmd) {
532287117Scem	case IOAT_DMATEST:
533287117Scem		ioat_dma_test(arg);
534287117Scem		break;
535287117Scem	default:
536287117Scem		return (EINVAL);
537287117Scem	}
538287117Scem	return (0);
539287117Scem}
540287117Scem
541287117Scemstatic struct cdevsw ioat_cdevsw = {
542287117Scem	.d_version =	D_VERSION,
543287117Scem	.d_flags =	0,
544287117Scem	.d_open =	ioat_test_open,
545287117Scem	.d_close =	ioat_test_close,
546287117Scem	.d_ioctl =	ioat_test_ioctl,
547287117Scem	.d_name =	"ioat_test",
548287117Scem};
549287117Scem
550287117Scemstatic int
551289760Scemenable_ioat_test(bool enable)
552289760Scem{
553289760Scem
554289760Scem	mtx_assert(&Giant, MA_OWNED);
555289760Scem
556289760Scem	if (enable && g_ioat_cdev == NULL) {
557289760Scem		g_ioat_cdev = make_dev(&ioat_cdevsw, 0, UID_ROOT, GID_WHEEL,
558289760Scem		    0600, "ioat_test");
559289760Scem	} else if (!enable && g_ioat_cdev != NULL) {
560289760Scem		destroy_dev(g_ioat_cdev);
561289760Scem		g_ioat_cdev = NULL;
562289760Scem	}
563289760Scem	return (0);
564289760Scem}
565289760Scem
566289760Scemstatic int
567287117Scemsysctl_enable_ioat_test(SYSCTL_HANDLER_ARGS)
568287117Scem{
569287117Scem	int error, enabled;
570287117Scem
571287117Scem	enabled = (g_ioat_cdev != NULL);
572287117Scem	error = sysctl_handle_int(oidp, &enabled, 0, req);
573287117Scem	if (error != 0 || req->newptr == NULL)
574287117Scem		return (error);
575287117Scem
576289760Scem	enable_ioat_test(enabled);
577287117Scem	return (0);
578287117Scem}
579287117ScemSYSCTL_PROC(_hw_ioat, OID_AUTO, enable_ioat_test, CTLTYPE_INT | CTLFLAG_RW,
580287117Scem    0, 0, sysctl_enable_ioat_test, "I",
581287117Scem    "Non-zero: Enable the /dev/ioat_test device");
582289760Scem
583289760Scemvoid
584289760Scemioat_test_attach(void)
585289760Scem{
586289760Scem	char *val;
587289760Scem
588289760Scem	val = kern_getenv("hw.ioat.enable_ioat_test");
589289760Scem	if (val != NULL && strcmp(val, "0") != 0) {
590289760Scem		mtx_lock(&Giant);
591289760Scem		enable_ioat_test(true);
592289760Scem		mtx_unlock(&Giant);
593289760Scem	}
594289760Scem	freeenv(val);
595289760Scem}
596289760Scem
597289760Scemvoid
598289760Scemioat_test_detach(void)
599289760Scem{
600289760Scem
601289760Scem	mtx_lock(&Giant);
602289760Scem	enable_ioat_test(false);
603289760Scem	mtx_unlock(&Giant);
604289760Scem}
605289776Scem
606315070Savgstatic void
607289776Scem_ioat_test_log(int verbosity, const char *fmt, ...)
608289776Scem{
609289776Scem	va_list argp;
610289776Scem
611289776Scem	if (verbosity > g_ioat_debug_level)
612289776Scem		return;
613289776Scem
614289776Scem	va_start(argp, fmt);
615289776Scem	vprintf(fmt, argp);
616289776Scem	va_end(argp);
617289776Scem}
618