ioat_test.c revision 290021
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: head/sys/dev/ioat/ioat_test.c 290021 2015-10-26 19:34:12Z cem $");
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>
48287117Scem#include <vm/pmap.h>
49287117Scem
50287117Scem#include "ioat.h"
51287117Scem#include "ioat_hw.h"
52287117Scem#include "ioat_internal.h"
53287117Scem#include "ioat_test.h"
54287117Scem
55289733Scem#ifndef time_after
56289733Scem#define	time_after(a,b)		((long)(b) - (long)(a) < 0)
57289733Scem#endif
58289733Scem
59287117ScemMALLOC_DEFINE(M_IOAT_TEST, "ioat_test", "ioat test allocations");
60287117Scem
61289733Scem#define	IOAT_MAX_BUFS	256
62287117Scem
63287117Scemstruct test_transaction {
64287117Scem	void			*buf[IOAT_MAX_BUFS];
65287117Scem	uint32_t		length;
66289733Scem	uint32_t		depth;
67287117Scem	struct ioat_test	*test;
68289733Scem	TAILQ_ENTRY(test_transaction)	entry;
69287117Scem};
70287117Scem
71289733Scem#define	IT_LOCK()	mtx_lock(&ioat_test_lk)
72289733Scem#define	IT_UNLOCK()	mtx_unlock(&ioat_test_lk)
73289733Scem#define	IT_ASSERT()	mtx_assert(&ioat_test_lk, MA_OWNED)
74289733Scemstatic struct mtx ioat_test_lk;
75289733ScemMTX_SYSINIT(ioat_test_lk, &ioat_test_lk, "test coordination mtx", MTX_DEF);
76289733Scem
77287117Scemstatic int g_thread_index = 1;
78287117Scemstatic struct cdev *g_ioat_cdev = NULL;
79287117Scem
80289777Scem#define	ioat_test_log(v, ...)	_ioat_test_log((v), "ioat_test: " __VA_ARGS__)
81289776Scemstatic inline void _ioat_test_log(int verbosity, const char *fmt, ...);
82289776Scem
83287117Scemstatic void
84287117Scemioat_test_transaction_destroy(struct test_transaction *tx)
85287117Scem{
86287117Scem	int i;
87287117Scem
88287117Scem	for (i = 0; i < IOAT_MAX_BUFS; i++) {
89287117Scem		if (tx->buf[i] != NULL) {
90289733Scem			contigfree(tx->buf[i], tx->length, M_IOAT_TEST);
91287117Scem			tx->buf[i] = NULL;
92287117Scem		}
93287117Scem	}
94287117Scem
95287117Scem	free(tx, M_IOAT_TEST);
96287117Scem}
97287117Scem
98287117Scemstatic struct
99289733Scemtest_transaction *ioat_test_transaction_create(unsigned num_buffers,
100287117Scem    uint32_t buffer_size)
101287117Scem{
102287117Scem	struct test_transaction *tx;
103289733Scem	unsigned i;
104287117Scem
105289733Scem	tx = malloc(sizeof(*tx), M_IOAT_TEST, M_NOWAIT | M_ZERO);
106287117Scem	if (tx == NULL)
107287117Scem		return (NULL);
108287117Scem
109287117Scem	tx->length = buffer_size;
110287117Scem
111287117Scem	for (i = 0; i < num_buffers; i++) {
112287117Scem		tx->buf[i] = contigmalloc(buffer_size, M_IOAT_TEST, M_NOWAIT,
113287117Scem		    0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
114287117Scem
115287117Scem		if (tx->buf[i] == NULL) {
116287117Scem			ioat_test_transaction_destroy(tx);
117287117Scem			return (NULL);
118287117Scem		}
119287117Scem	}
120287117Scem	return (tx);
121287117Scem}
122287117Scem
123289733Scemstatic bool
124289733Scemioat_compare_ok(struct test_transaction *tx)
125289733Scem{
126290021Scem	struct ioat_test *test;
127290021Scem	char *dst, *src;
128290021Scem	uint32_t i, j;
129289733Scem
130290021Scem	test = tx->test;
131290021Scem
132289733Scem	for (i = 0; i < tx->depth; i++) {
133290021Scem		dst = tx->buf[2 * i + 1];
134290021Scem		src = tx->buf[2 * i];
135290021Scem
136290021Scem		if (test->testkind == IOAT_TEST_FILL) {
137290021Scem			for (j = 0; j < tx->length; j += sizeof(uint64_t)) {
138290021Scem				if (memcmp(src, &dst[j],
139290021Scem					MIN(sizeof(uint64_t), tx->length - j))
140290021Scem				    != 0)
141290021Scem					return (false);
142290021Scem			}
143290021Scem		} else if (test->testkind == IOAT_TEST_DMA)
144290021Scem			if (memcmp(src, dst, tx->length) != 0)
145290021Scem				return (false);
146289733Scem	}
147289733Scem	return (true);
148289733Scem}
149289733Scem
150287117Scemstatic void
151287117Scemioat_dma_test_callback(void *arg)
152287117Scem{
153287117Scem	struct test_transaction *tx;
154287117Scem	struct ioat_test *test;
155287117Scem
156287117Scem	tx = arg;
157287117Scem	test = tx->test;
158287117Scem
159289733Scem	if (test->verify && !ioat_compare_ok(tx)) {
160289776Scem		ioat_test_log(0, "miscompare found\n");
161289733Scem		atomic_add_32(&test->status[IOAT_TEST_MISCOMPARE], tx->depth);
162289733Scem	} else if (!test->too_late)
163289733Scem		atomic_add_32(&test->status[IOAT_TEST_OK], tx->depth);
164289733Scem
165289733Scem	IT_LOCK();
166289733Scem	TAILQ_REMOVE(&test->pend_q, tx, entry);
167289733Scem	TAILQ_INSERT_TAIL(&test->free_q, tx, entry);
168289733Scem	wakeup(&test->free_q);
169289733Scem	IT_UNLOCK();
170289733Scem}
171289733Scem
172289733Scemstatic int
173289733Scemioat_test_prealloc_memory(struct ioat_test *test, int index)
174289733Scem{
175289733Scem	uint32_t i, j, k;
176289733Scem	struct test_transaction *tx;
177289733Scem
178289733Scem	for (i = 0; i < test->transactions; i++) {
179289733Scem		tx = ioat_test_transaction_create(test->chain_depth * 2,
180289733Scem		    test->buffer_size);
181289733Scem		if (tx == NULL) {
182289776Scem			ioat_test_log(0, "tx == NULL - memory exhausted\n");
183289733Scem			test->status[IOAT_TEST_NO_MEMORY]++;
184289733Scem			return (ENOMEM);
185289733Scem		}
186289733Scem
187289733Scem		TAILQ_INSERT_HEAD(&test->free_q, tx, entry);
188289733Scem
189289733Scem		tx->test = test;
190289733Scem		tx->depth = test->chain_depth;
191289733Scem
192289733Scem		/* fill in source buffers */
193289733Scem		for (j = 0; j < (tx->length / sizeof(uint32_t)); j++) {
194289733Scem			uint32_t val = j + (index << 28);
195289733Scem
196289733Scem			for (k = 0; k < test->chain_depth; k++) {
197289733Scem				((uint32_t *)tx->buf[2*k])[j] = ~val;
198289733Scem				((uint32_t *)tx->buf[2*k+1])[j] = val;
199289733Scem			}
200289733Scem		}
201287117Scem	}
202289733Scem	return (0);
203287117Scem}
204287117Scem
205287117Scemstatic void
206289733Scemioat_test_release_memory(struct ioat_test *test)
207289733Scem{
208289733Scem	struct test_transaction *tx, *s;
209289733Scem
210289733Scem	TAILQ_FOREACH_SAFE(tx, &test->free_q, entry, s)
211289733Scem		ioat_test_transaction_destroy(tx);
212289733Scem	TAILQ_INIT(&test->free_q);
213289733Scem
214289733Scem	TAILQ_FOREACH_SAFE(tx, &test->pend_q, entry, s)
215289733Scem		ioat_test_transaction_destroy(tx);
216289733Scem	TAILQ_INIT(&test->pend_q);
217289733Scem}
218289733Scem
219289733Scemstatic void
220289733Scemioat_test_submit_1_tx(struct ioat_test *test, bus_dmaengine_t dma)
221289733Scem{
222289733Scem	struct test_transaction *tx;
223289733Scem	struct bus_dmadesc *desc;
224289733Scem	bus_dmaengine_callback_t cb;
225289733Scem	bus_addr_t src, dest;
226290021Scem	uint64_t fillpattern;
227289733Scem	uint32_t i, flags;
228289733Scem
229290021Scem	desc = NULL;
230290021Scem
231289733Scem	IT_LOCK();
232289733Scem	while (TAILQ_EMPTY(&test->free_q))
233289733Scem		msleep(&test->free_q, &ioat_test_lk, 0, "test_submit", 0);
234289733Scem
235289733Scem	tx = TAILQ_FIRST(&test->free_q);
236289733Scem	TAILQ_REMOVE(&test->free_q, tx, entry);
237289733Scem	TAILQ_INSERT_HEAD(&test->pend_q, tx, entry);
238289733Scem	IT_UNLOCK();
239289733Scem
240289733Scem	ioat_acquire(dma);
241289733Scem	for (i = 0; i < tx->depth; i++) {
242289733Scem		src = vtophys((vm_offset_t)tx->buf[2*i]);
243289733Scem		dest = vtophys((vm_offset_t)tx->buf[2*i+1]);
244289733Scem
245289733Scem		if (i == tx->depth - 1) {
246289733Scem			cb = ioat_dma_test_callback;
247289733Scem			flags = DMA_INT_EN;
248289733Scem		} else {
249289733Scem			cb = NULL;
250289733Scem			flags = 0;
251289733Scem		}
252289733Scem
253290021Scem		if (test->testkind == IOAT_TEST_DMA)
254290021Scem			desc = ioat_copy(dma, dest, src, tx->length, cb, tx,
255290021Scem			    flags);
256290021Scem		else if (test->testkind == IOAT_TEST_FILL) {
257290021Scem			fillpattern = *(uint64_t *)tx->buf[2*i];
258290021Scem			desc = ioat_blockfill(dma, dest, fillpattern,
259290021Scem			    tx->length, cb, tx, flags);
260290021Scem		}
261290021Scem
262289733Scem		if (desc == NULL)
263289733Scem			panic("Failed to allocate a ring slot "
264289733Scem			    "-- this shouldn't happen!");
265289733Scem	}
266289733Scem	ioat_release(dma);
267289733Scem}
268289733Scem
269289733Scemstatic void
270287117Scemioat_dma_test(void *arg)
271287117Scem{
272287117Scem	struct ioat_test *test;
273287117Scem	bus_dmaengine_t dmaengine;
274287117Scem	uint32_t loops;
275289733Scem	int index, rc, start, end;
276287117Scem
277287117Scem	test = arg;
278289733Scem	memset(__DEVOLATILE(void *, test->status), 0, sizeof(test->status));
279287117Scem
280289733Scem	if (test->buffer_size > 1024 * 1024) {
281289776Scem		ioat_test_log(0, "Buffer size too large >1MB\n");
282289733Scem		test->status[IOAT_TEST_NO_MEMORY]++;
283289733Scem		return;
284289733Scem	}
285287117Scem
286289733Scem	if (test->chain_depth * 2 > IOAT_MAX_BUFS) {
287289776Scem		ioat_test_log(0, "Depth too large (> %u)\n",
288289733Scem		    (unsigned)IOAT_MAX_BUFS / 2);
289289733Scem		test->status[IOAT_TEST_NO_MEMORY]++;
290289733Scem		return;
291289733Scem	}
292289733Scem
293289733Scem	if (btoc((uint64_t)test->buffer_size * test->chain_depth *
294289733Scem	    test->transactions) > (physmem / 4)) {
295289776Scem		ioat_test_log(0, "Sanity check failed -- test would "
296289733Scem		    "use more than 1/4 of phys mem.\n");
297289733Scem		test->status[IOAT_TEST_NO_MEMORY]++;
298289733Scem		return;
299289733Scem	}
300289733Scem
301289733Scem	if ((uint64_t)test->transactions * test->chain_depth > (1<<16)) {
302289776Scem		ioat_test_log(0, "Sanity check failed -- test would "
303289733Scem		    "use more than available IOAT ring space.\n");
304289733Scem		test->status[IOAT_TEST_NO_MEMORY]++;
305289733Scem		return;
306289733Scem	}
307289733Scem
308290021Scem	if (test->testkind >= IOAT_NUM_TESTKINDS) {
309290021Scem		ioat_test_log(0, "Invalid kind %u\n",
310290021Scem		    (unsigned)test->testkind);
311290021Scem		test->status[IOAT_TEST_INVALID_INPUT]++;
312290021Scem		return;
313290021Scem	}
314290021Scem
315287117Scem	dmaengine = ioat_get_dmaengine(test->channel_index);
316287117Scem	if (dmaengine == NULL) {
317289776Scem		ioat_test_log(0, "Couldn't acquire dmaengine\n");
318289733Scem		test->status[IOAT_TEST_NO_DMA_ENGINE]++;
319287117Scem		return;
320287117Scem	}
321287117Scem
322289733Scem	index = g_thread_index++;
323289733Scem	TAILQ_INIT(&test->free_q);
324289733Scem	TAILQ_INIT(&test->pend_q);
325287117Scem
326289733Scem	if (test->duration == 0)
327289776Scem		ioat_test_log(1, "Thread %d: num_loops remaining: 0x%08x\n",
328289733Scem		    index, test->transactions);
329289733Scem	else
330289776Scem		ioat_test_log(1, "Thread %d: starting\n", index);
331287117Scem
332289733Scem	rc = ioat_test_prealloc_memory(test, index);
333289733Scem	if (rc != 0) {
334289776Scem		ioat_test_log(0, "prealloc_memory: %d\n", rc);
335289907Scem		goto out;
336289733Scem	}
337289733Scem	wmb();
338287117Scem
339289733Scem	test->too_late = false;
340289733Scem	start = ticks;
341289733Scem	end = start + (((sbintime_t)test->duration * hz) / 1000);
342289733Scem
343289733Scem	for (loops = 0;; loops++) {
344289733Scem		if (test->duration == 0 && loops >= test->transactions)
345289733Scem			break;
346289733Scem		else if (test->duration != 0 && time_after(ticks, end)) {
347289733Scem			test->too_late = true;
348289733Scem			break;
349287117Scem		}
350287117Scem
351289733Scem		ioat_test_submit_1_tx(test, dmaengine);
352289733Scem	}
353287117Scem
354289776Scem	ioat_test_log(1, "Test Elapsed: %d ticks (overrun %d), %d sec.\n",
355289733Scem	    ticks - start, ticks - end, (ticks - start) / hz);
356287117Scem
357289733Scem	IT_LOCK();
358289733Scem	while (!TAILQ_EMPTY(&test->pend_q))
359289733Scem		msleep(&test->free_q, &ioat_test_lk, 0, "ioattestcompl", hz);
360289733Scem	IT_UNLOCK();
361287117Scem
362289776Scem	ioat_test_log(1, "Test Elapsed2: %d ticks (overrun %d), %d sec.\n",
363289733Scem	    ticks - start, ticks - end, (ticks - start) / hz);
364287117Scem
365289733Scem	ioat_test_release_memory(test);
366289907Scemout:
367289907Scem	ioat_put_dmaengine(dmaengine);
368287117Scem}
369287117Scem
370287117Scemstatic int
371287117Scemioat_test_open(struct cdev *dev, int flags, int fmt, struct thread *td)
372287117Scem{
373287117Scem
374287117Scem	return (0);
375287117Scem}
376287117Scem
377287117Scemstatic int
378287117Scemioat_test_close(struct cdev *dev, int flags, int fmt, struct thread *td)
379287117Scem{
380287117Scem
381287117Scem	return (0);
382287117Scem}
383287117Scem
384287117Scemstatic int
385287117Scemioat_test_ioctl(struct cdev *dev, unsigned long cmd, caddr_t arg, int flag,
386287117Scem    struct thread *td)
387287117Scem{
388287117Scem
389287117Scem	switch (cmd) {
390287117Scem	case IOAT_DMATEST:
391287117Scem		ioat_dma_test(arg);
392287117Scem		break;
393287117Scem	default:
394287117Scem		return (EINVAL);
395287117Scem	}
396287117Scem	return (0);
397287117Scem}
398287117Scem
399287117Scemstatic struct cdevsw ioat_cdevsw = {
400287117Scem	.d_version =	D_VERSION,
401287117Scem	.d_flags =	0,
402287117Scem	.d_open =	ioat_test_open,
403287117Scem	.d_close =	ioat_test_close,
404287117Scem	.d_ioctl =	ioat_test_ioctl,
405287117Scem	.d_name =	"ioat_test",
406287117Scem};
407287117Scem
408287117Scemstatic int
409289760Scemenable_ioat_test(bool enable)
410289760Scem{
411289760Scem
412289760Scem	mtx_assert(&Giant, MA_OWNED);
413289760Scem
414289760Scem	if (enable && g_ioat_cdev == NULL) {
415289760Scem		g_ioat_cdev = make_dev(&ioat_cdevsw, 0, UID_ROOT, GID_WHEEL,
416289760Scem		    0600, "ioat_test");
417289760Scem	} else if (!enable && g_ioat_cdev != NULL) {
418289760Scem		destroy_dev(g_ioat_cdev);
419289760Scem		g_ioat_cdev = NULL;
420289760Scem	}
421289760Scem	return (0);
422289760Scem}
423289760Scem
424289760Scemstatic int
425287117Scemsysctl_enable_ioat_test(SYSCTL_HANDLER_ARGS)
426287117Scem{
427287117Scem	int error, enabled;
428287117Scem
429287117Scem	enabled = (g_ioat_cdev != NULL);
430287117Scem	error = sysctl_handle_int(oidp, &enabled, 0, req);
431287117Scem	if (error != 0 || req->newptr == NULL)
432287117Scem		return (error);
433287117Scem
434289760Scem	enable_ioat_test(enabled);
435287117Scem	return (0);
436287117Scem}
437287117ScemSYSCTL_PROC(_hw_ioat, OID_AUTO, enable_ioat_test, CTLTYPE_INT | CTLFLAG_RW,
438287117Scem    0, 0, sysctl_enable_ioat_test, "I",
439287117Scem    "Non-zero: Enable the /dev/ioat_test device");
440289760Scem
441289760Scemvoid
442289760Scemioat_test_attach(void)
443289760Scem{
444289760Scem	char *val;
445289760Scem
446289760Scem	val = kern_getenv("hw.ioat.enable_ioat_test");
447289760Scem	if (val != NULL && strcmp(val, "0") != 0) {
448289760Scem		mtx_lock(&Giant);
449289760Scem		enable_ioat_test(true);
450289760Scem		mtx_unlock(&Giant);
451289760Scem	}
452289760Scem	freeenv(val);
453289760Scem}
454289760Scem
455289760Scemvoid
456289760Scemioat_test_detach(void)
457289760Scem{
458289760Scem
459289760Scem	mtx_lock(&Giant);
460289760Scem	enable_ioat_test(false);
461289760Scem	mtx_unlock(&Giant);
462289760Scem}
463289776Scem
464289776Scemstatic inline void
465289776Scem_ioat_test_log(int verbosity, const char *fmt, ...)
466289776Scem{
467289776Scem	va_list argp;
468289776Scem
469289776Scem	if (verbosity > g_ioat_debug_level)
470289776Scem		return;
471289776Scem
472289776Scem	va_start(argp, fmt);
473289776Scem	vprintf(fmt, argp);
474289776Scem	va_end(argp);
475289776Scem}
476