busdma_dmar.c revision 263470
1/*-
2 * Copyright (c) 2013 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
6 * under sponsorship from the FreeBSD Foundation.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: stable/10/sys/x86/iommu/busdma_dmar.c 263470 2014-03-21 13:31:56Z kib $");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/malloc.h>
36#include <sys/bus.h>
37#include <sys/conf.h>
38#include <sys/interrupt.h>
39#include <sys/kernel.h>
40#include <sys/ktr.h>
41#include <sys/lock.h>
42#include <sys/proc.h>
43#include <sys/memdesc.h>
44#include <sys/mutex.h>
45#include <sys/sysctl.h>
46#include <sys/rman.h>
47#include <sys/taskqueue.h>
48#include <sys/tree.h>
49#include <sys/uio.h>
50#include <dev/pci/pcivar.h>
51#include <vm/vm.h>
52#include <vm/vm_extern.h>
53#include <vm/vm_kern.h>
54#include <vm/vm_object.h>
55#include <vm/vm_page.h>
56#include <vm/vm_map.h>
57#include <machine/atomic.h>
58#include <machine/bus.h>
59#include <machine/md_var.h>
60#include <machine/specialreg.h>
61#include <x86/include/busdma_impl.h>
62#include <x86/iommu/intel_reg.h>
63#include <x86/iommu/busdma_dmar.h>
64#include <x86/iommu/intel_dmar.h>
65
66/*
67 * busdma_dmar.c, the implementation of the busdma(9) interface using
68 * DMAR units from Intel VT-d.
69 */
70
71static bool
72dmar_bus_dma_is_dev_disabled(device_t dev)
73{
74	char str[128], *env;
75	int domain, bus, slot, func;
76
77	domain = pci_get_domain(dev);
78	bus = pci_get_bus(dev);
79	slot = pci_get_slot(dev);
80	func = pci_get_function(dev);
81	snprintf(str, sizeof(str), "hw.busdma.pci%d.%d.%d.%d.bounce",
82	    domain, bus, slot, func);
83	env = getenv(str);
84	if (env == NULL)
85		return (false);
86	freeenv(env);
87	return (true);
88}
89
90struct dmar_ctx *
91dmar_instantiate_ctx(struct dmar_unit *dmar, device_t dev, bool rmrr)
92{
93	struct dmar_ctx *ctx;
94	bool disabled;
95
96	/*
97	 * If the user requested the IOMMU disabled for the device, we
98	 * cannot disable the DMAR, due to possibility of other
99	 * devices on the same DMAR still requiring translation.
100	 * Instead provide the identity mapping for the device
101	 * context.
102	 */
103	disabled = dmar_bus_dma_is_dev_disabled(dev);
104	ctx = dmar_get_ctx(dmar, dev, disabled, rmrr);
105	if (ctx == NULL)
106		return (NULL);
107	ctx->ctx_tag.owner = dev;
108	if (disabled) {
109		/*
110		 * Keep the first reference on context, release the
111		 * later refs.
112		 */
113		DMAR_LOCK(dmar);
114		if ((ctx->flags & DMAR_CTX_DISABLED) == 0) {
115			ctx->flags |= DMAR_CTX_DISABLED;
116			DMAR_UNLOCK(dmar);
117		} else {
118			dmar_free_ctx_locked(dmar, ctx);
119		}
120		ctx = NULL;
121	}
122	return (ctx);
123}
124
125bus_dma_tag_t
126dmar_get_dma_tag(device_t dev, device_t child)
127{
128	struct dmar_unit *dmar;
129	struct dmar_ctx *ctx;
130	bus_dma_tag_t res;
131
132	dmar = dmar_find(child);
133	/* Not in scope of any DMAR ? */
134	if (dmar == NULL)
135		return (NULL);
136	dmar_quirks_pre_use(dmar);
137	dmar_instantiate_rmrr_ctxs(dmar);
138
139	ctx = dmar_instantiate_ctx(dmar, child, false);
140	res = ctx == NULL ? NULL : (bus_dma_tag_t)&ctx->ctx_tag;
141	return (res);
142}
143
144static MALLOC_DEFINE(M_DMAR_DMAMAP, "dmar_dmamap", "Intel DMAR DMA Map");
145
146static void dmar_bus_schedule_dmamap(struct dmar_unit *unit,
147    struct bus_dmamap_dmar *map);
148
149static int
150dmar_bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
151    bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
152    bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
153    int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
154    void *lockfuncarg, bus_dma_tag_t *dmat)
155{
156	struct bus_dma_tag_dmar *newtag, *oldtag;
157	int error;
158
159	*dmat = NULL;
160	error = common_bus_dma_tag_create(parent != NULL ?
161	    &((struct bus_dma_tag_dmar *)parent)->common : NULL, alignment,
162	    boundary, lowaddr, highaddr, filter, filterarg, maxsize,
163	    nsegments, maxsegsz, flags, lockfunc, lockfuncarg,
164	    sizeof(struct bus_dma_tag_dmar), (void **)&newtag);
165	if (error != 0)
166		goto out;
167
168	oldtag = (struct bus_dma_tag_dmar *)parent;
169	newtag->common.impl = &bus_dma_dmar_impl;
170	newtag->ctx = oldtag->ctx;
171	newtag->owner = oldtag->owner;
172
173	*dmat = (bus_dma_tag_t)newtag;
174out:
175	CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
176	    __func__, newtag, (newtag != NULL ? newtag->common.flags : 0),
177	    error);
178	return (error);
179}
180
181static int
182dmar_bus_dma_tag_destroy(bus_dma_tag_t dmat1)
183{
184	struct bus_dma_tag_dmar *dmat, *dmat_copy, *parent;
185	int error;
186
187	error = 0;
188	dmat_copy = dmat = (struct bus_dma_tag_dmar *)dmat1;
189
190	if (dmat != NULL) {
191		if (dmat->map_count != 0) {
192			error = EBUSY;
193			goto out;
194		}
195		while (dmat != NULL) {
196			parent = (struct bus_dma_tag_dmar *)dmat->common.parent;
197			if (atomic_fetchadd_int(&dmat->common.ref_count, -1) ==
198			    1) {
199				if (dmat == &dmat->ctx->ctx_tag)
200					dmar_free_ctx(dmat->ctx);
201				free(dmat->segments, M_DMAR_DMAMAP);
202				free(dmat, M_DEVBUF);
203				dmat = parent;
204			} else
205				dmat = NULL;
206		}
207	}
208out:
209	CTR3(KTR_BUSDMA, "%s tag %p error %d", __func__, dmat_copy, error);
210	return (error);
211}
212
213static int
214dmar_bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
215{
216	struct bus_dma_tag_dmar *tag;
217	struct bus_dmamap_dmar *map;
218
219	tag = (struct bus_dma_tag_dmar *)dmat;
220	map = malloc(sizeof(*map), M_DMAR_DMAMAP, M_NOWAIT | M_ZERO);
221	if (map == NULL) {
222		*mapp = NULL;
223		return (ENOMEM);
224	}
225	if (tag->segments == NULL) {
226		tag->segments = malloc(sizeof(bus_dma_segment_t) *
227		    tag->common.nsegments, M_DMAR_DMAMAP, M_NOWAIT);
228		if (tag->segments == NULL) {
229			free(map, M_DMAR_DMAMAP);
230			*mapp = NULL;
231			return (ENOMEM);
232		}
233	}
234	TAILQ_INIT(&map->map_entries);
235	map->tag = tag;
236	map->locked = true;
237	map->cansleep = false;
238	tag->map_count++;
239	*mapp = (bus_dmamap_t)map;
240
241	return (0);
242}
243
244static int
245dmar_bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map1)
246{
247	struct bus_dma_tag_dmar *tag;
248	struct bus_dmamap_dmar *map;
249
250	tag = (struct bus_dma_tag_dmar *)dmat;
251	map = (struct bus_dmamap_dmar *)map1;
252	if (map != NULL) {
253		DMAR_CTX_LOCK(tag->ctx);
254		if (!TAILQ_EMPTY(&map->map_entries)) {
255			DMAR_CTX_UNLOCK(tag->ctx);
256			return (EBUSY);
257		}
258		DMAR_CTX_UNLOCK(tag->ctx);
259		free(map, M_DMAR_DMAMAP);
260	}
261	tag->map_count--;
262	return (0);
263}
264
265
266static int
267dmar_bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
268    bus_dmamap_t *mapp)
269{
270	struct bus_dma_tag_dmar *tag;
271	struct bus_dmamap_dmar *map;
272	int error, mflags;
273	vm_memattr_t attr;
274
275	error = dmar_bus_dmamap_create(dmat, flags, mapp);
276	if (error != 0)
277		return (error);
278
279	mflags = (flags & BUS_DMA_NOWAIT) != 0 ? M_NOWAIT : M_WAITOK;
280	mflags |= (flags & BUS_DMA_ZERO) != 0 ? M_ZERO : 0;
281	attr = (flags & BUS_DMA_NOCACHE) != 0 ? VM_MEMATTR_UNCACHEABLE :
282	    VM_MEMATTR_DEFAULT;
283
284	tag = (struct bus_dma_tag_dmar *)dmat;
285	map = (struct bus_dmamap_dmar *)*mapp;
286
287	if (tag->common.maxsize < PAGE_SIZE &&
288	    tag->common.alignment <= tag->common.maxsize &&
289	    attr == VM_MEMATTR_DEFAULT) {
290		*vaddr = malloc(tag->common.maxsize, M_DEVBUF, mflags);
291		map->flags |= BUS_DMAMAP_DMAR_MALLOC;
292	} else {
293		*vaddr = (void *)kmem_alloc_attr(kernel_arena,
294		    tag->common.maxsize, mflags, 0ul, BUS_SPACE_MAXADDR,
295		    attr);
296		map->flags |= BUS_DMAMAP_DMAR_KMEM_ALLOC;
297	}
298	if (*vaddr == NULL) {
299		dmar_bus_dmamap_destroy(dmat, *mapp);
300		*mapp = NULL;
301		return (ENOMEM);
302	}
303	return (0);
304}
305
306static void
307dmar_bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map1)
308{
309	struct bus_dma_tag_dmar *tag;
310	struct bus_dmamap_dmar *map;
311
312	tag = (struct bus_dma_tag_dmar *)dmat;
313	map = (struct bus_dmamap_dmar *)map1;
314
315	if ((map->flags & BUS_DMAMAP_DMAR_MALLOC) != 0) {
316		free(vaddr, M_DEVBUF);
317		map->flags &= ~BUS_DMAMAP_DMAR_MALLOC;
318	} else {
319		KASSERT((map->flags & BUS_DMAMAP_DMAR_KMEM_ALLOC) != 0,
320		    ("dmar_bus_dmamem_free for non alloced map %p", map));
321		kmem_free(kernel_arena, (vm_offset_t)vaddr, tag->common.maxsize);
322		map->flags &= ~BUS_DMAMAP_DMAR_KMEM_ALLOC;
323	}
324
325	dmar_bus_dmamap_destroy(dmat, map1);
326}
327
328static int
329dmar_bus_dmamap_load_something1(struct bus_dma_tag_dmar *tag,
330    struct bus_dmamap_dmar *map, vm_page_t *ma, int offset, bus_size_t buflen,
331    int flags, bus_dma_segment_t *segs, int *segp,
332    struct dmar_map_entries_tailq *unroll_list)
333{
334	struct dmar_ctx *ctx;
335	struct dmar_map_entry *entry;
336	dmar_gaddr_t size;
337	bus_size_t buflen1;
338	int error, idx, gas_flags, seg;
339
340	if (segs == NULL)
341		segs = tag->segments;
342	ctx = tag->ctx;
343	seg = *segp;
344	error = 0;
345	idx = 0;
346	while (buflen > 0) {
347		seg++;
348		if (seg >= tag->common.nsegments) {
349			error = EFBIG;
350			break;
351		}
352		buflen1 = buflen > tag->common.maxsegsz ?
353		    tag->common.maxsegsz : buflen;
354		buflen -= buflen1;
355		size = round_page(offset + buflen1);
356
357		/*
358		 * (Too) optimistically allow split if there are more
359		 * then one segments left.
360		 */
361		gas_flags = map->cansleep ? DMAR_GM_CANWAIT : 0;
362		if (seg + 1 < tag->common.nsegments)
363			gas_flags |= DMAR_GM_CANSPLIT;
364
365		error = dmar_gas_map(ctx, &tag->common, size,
366		    DMAR_MAP_ENTRY_READ | DMAR_MAP_ENTRY_WRITE,
367		    gas_flags, ma + idx, &entry);
368		if (error != 0)
369			break;
370		if ((gas_flags & DMAR_GM_CANSPLIT) != 0) {
371			KASSERT(size >= entry->end - entry->start,
372			    ("split increased entry size %jx %jx %jx",
373			    (uintmax_t)size, (uintmax_t)entry->start,
374			    (uintmax_t)entry->end));
375			size = entry->end - entry->start;
376			if (buflen1 > size)
377				buflen1 = size;
378		} else {
379			KASSERT(entry->end - entry->start == size,
380			    ("no split allowed %jx %jx %jx",
381			    (uintmax_t)size, (uintmax_t)entry->start,
382			    (uintmax_t)entry->end));
383		}
384
385		KASSERT(((entry->start + offset) & (tag->common.alignment - 1))
386		    == 0,
387		    ("alignment failed: ctx %p start 0x%jx offset %x "
388		    "align 0x%jx", ctx, (uintmax_t)entry->start, offset,
389		    (uintmax_t)tag->common.alignment));
390		KASSERT(entry->end <= tag->common.lowaddr ||
391		    entry->start >= tag->common.highaddr,
392		    ("entry placement failed: ctx %p start 0x%jx end 0x%jx "
393		    "lowaddr 0x%jx highaddr 0x%jx", ctx,
394		    (uintmax_t)entry->start, (uintmax_t)entry->end,
395		    (uintmax_t)tag->common.lowaddr,
396		    (uintmax_t)tag->common.highaddr));
397		KASSERT(dmar_test_boundary(entry->start, entry->end -
398		    entry->start, tag->common.boundary),
399		    ("boundary failed: ctx %p start 0x%jx end 0x%jx "
400		    "boundary 0x%jx", ctx, (uintmax_t)entry->start,
401		    (uintmax_t)entry->end, (uintmax_t)tag->common.boundary));
402		KASSERT(buflen1 <= tag->common.maxsegsz,
403		    ("segment too large: ctx %p start 0x%jx end 0x%jx "
404		    "maxsegsz 0x%jx", ctx, (uintmax_t)entry->start,
405		    (uintmax_t)entry->end, (uintmax_t)tag->common.maxsegsz));
406
407		DMAR_CTX_LOCK(ctx);
408		TAILQ_INSERT_TAIL(&map->map_entries, entry, dmamap_link);
409		entry->flags |= DMAR_MAP_ENTRY_MAP;
410		DMAR_CTX_UNLOCK(ctx);
411		TAILQ_INSERT_TAIL(unroll_list, entry, unroll_link);
412
413		segs[seg].ds_addr = entry->start + offset;
414		segs[seg].ds_len = buflen1;
415
416		idx += OFF_TO_IDX(trunc_page(offset + buflen1));
417		offset += buflen1;
418		offset &= DMAR_PAGE_MASK;
419	}
420	if (error == 0)
421		*segp = seg;
422	return (error);
423}
424
425static int
426dmar_bus_dmamap_load_something(struct bus_dma_tag_dmar *tag,
427    struct bus_dmamap_dmar *map, vm_page_t *ma, int offset, bus_size_t buflen,
428    int flags, bus_dma_segment_t *segs, int *segp)
429{
430	struct dmar_ctx *ctx;
431	struct dmar_map_entry *entry, *entry1;
432	struct dmar_map_entries_tailq unroll_list;
433	int error;
434
435	ctx = tag->ctx;
436	atomic_add_long(&ctx->loads, 1);
437
438	TAILQ_INIT(&unroll_list);
439	error = dmar_bus_dmamap_load_something1(tag, map, ma, offset,
440	    buflen, flags, segs, segp, &unroll_list);
441	if (error != 0) {
442		/*
443		 * The busdma interface does not allow us to report
444		 * partial buffer load, so unfortunately we have to
445		 * revert all work done.
446		 */
447		DMAR_CTX_LOCK(ctx);
448		TAILQ_FOREACH_SAFE(entry, &unroll_list, unroll_link,
449		    entry1) {
450			/*
451			 * No entries other than what we have created
452			 * during the failed run might have been
453			 * inserted there in between, since we own ctx
454			 * pglock.
455			 */
456			TAILQ_REMOVE(&map->map_entries, entry, dmamap_link);
457			TAILQ_REMOVE(&unroll_list, entry, unroll_link);
458			TAILQ_INSERT_TAIL(&ctx->unload_entries, entry,
459			    dmamap_link);
460		}
461		DMAR_CTX_UNLOCK(ctx);
462		taskqueue_enqueue(ctx->dmar->delayed_taskqueue,
463		    &ctx->unload_task);
464	}
465
466	if (error == ENOMEM && (flags & BUS_DMA_NOWAIT) == 0 &&
467	    !map->cansleep)
468		error = EINPROGRESS;
469	if (error == EINPROGRESS)
470		dmar_bus_schedule_dmamap(ctx->dmar, map);
471	return (error);
472}
473
474static int
475dmar_bus_dmamap_load_ma(bus_dma_tag_t dmat, bus_dmamap_t map1,
476    struct vm_page **ma, bus_size_t tlen, int ma_offs, int flags,
477    bus_dma_segment_t *segs, int *segp)
478{
479	struct bus_dma_tag_dmar *tag;
480	struct bus_dmamap_dmar *map;
481
482	tag = (struct bus_dma_tag_dmar *)dmat;
483	map = (struct bus_dmamap_dmar *)map1;
484	return (dmar_bus_dmamap_load_something(tag, map, ma, ma_offs, tlen,
485	    flags, segs, segp));
486}
487
488static int
489dmar_bus_dmamap_load_phys(bus_dma_tag_t dmat, bus_dmamap_t map1,
490    vm_paddr_t buf, bus_size_t buflen, int flags, bus_dma_segment_t *segs,
491    int *segp)
492{
493	struct bus_dma_tag_dmar *tag;
494	struct bus_dmamap_dmar *map;
495	vm_page_t *ma;
496	vm_paddr_t pstart, pend;
497	int error, i, ma_cnt, offset;
498
499	tag = (struct bus_dma_tag_dmar *)dmat;
500	map = (struct bus_dmamap_dmar *)map1;
501	pstart = trunc_page(buf);
502	pend = round_page(buf + buflen);
503	offset = buf & PAGE_MASK;
504	ma_cnt = OFF_TO_IDX(pend - pstart);
505	ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, map->cansleep ?
506	    M_WAITOK : M_NOWAIT);
507	if (ma == NULL)
508		return (ENOMEM);
509	for (i = 0; i < ma_cnt; i++)
510		ma[i] = PHYS_TO_VM_PAGE(pstart + i * PAGE_SIZE);
511	error = dmar_bus_dmamap_load_something(tag, map, ma, offset, buflen,
512	    flags, segs, segp);
513	free(ma, M_DEVBUF);
514	return (error);
515}
516
517static int
518dmar_bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dmamap_t map1, void *buf,
519    bus_size_t buflen, pmap_t pmap, int flags, bus_dma_segment_t *segs,
520    int *segp)
521{
522	struct bus_dma_tag_dmar *tag;
523	struct bus_dmamap_dmar *map;
524	vm_page_t *ma, fma;
525	vm_paddr_t pstart, pend, paddr;
526	int error, i, ma_cnt, offset;
527
528	tag = (struct bus_dma_tag_dmar *)dmat;
529	map = (struct bus_dmamap_dmar *)map1;
530	pstart = trunc_page((vm_offset_t)buf);
531	pend = round_page((vm_offset_t)buf + buflen);
532	offset = (vm_offset_t)buf & PAGE_MASK;
533	ma_cnt = OFF_TO_IDX(pend - pstart);
534	ma = malloc(sizeof(vm_page_t) * ma_cnt, M_DEVBUF, map->cansleep ?
535	    M_WAITOK : M_NOWAIT);
536	if (ma == NULL)
537		return (ENOMEM);
538	if (dumping) {
539		/*
540		 * If dumping, do not attempt to call
541		 * PHYS_TO_VM_PAGE() at all.  It may return non-NULL
542		 * but the vm_page returned might be not initialized,
543		 * e.g. for the kernel itself.
544		 */
545		KASSERT(pmap == kernel_pmap, ("non-kernel address write"));
546		fma = malloc(sizeof(struct vm_page) * ma_cnt, M_DEVBUF,
547		    M_ZERO | (map->cansleep ? M_WAITOK : M_NOWAIT));
548		if (fma == NULL) {
549			free(ma, M_DEVBUF);
550			return (ENOMEM);
551		}
552		for (i = 0; i < ma_cnt; i++, pstart += PAGE_SIZE) {
553			paddr = pmap_kextract(pstart);
554			vm_page_initfake(&fma[i], paddr, VM_MEMATTR_DEFAULT);
555			ma[i] = &fma[i];
556		}
557	} else {
558		fma = NULL;
559		for (i = 0; i < ma_cnt; i++, pstart += PAGE_SIZE) {
560			if (pmap == kernel_pmap)
561				paddr = pmap_kextract(pstart);
562			else
563				paddr = pmap_extract(pmap, pstart);
564			ma[i] = PHYS_TO_VM_PAGE(paddr);
565			KASSERT(VM_PAGE_TO_PHYS(ma[i]) == paddr,
566			    ("PHYS_TO_VM_PAGE failed %jx %jx m %p",
567			    (uintmax_t)paddr, (uintmax_t)VM_PAGE_TO_PHYS(ma[i]),
568			    ma[i]));
569		}
570	}
571	error = dmar_bus_dmamap_load_something(tag, map, ma, offset, buflen,
572	    flags, segs, segp);
573	free(ma, M_DEVBUF);
574	free(fma, M_DEVBUF);
575	return (error);
576}
577
578static void
579dmar_bus_dmamap_waitok(bus_dma_tag_t dmat, bus_dmamap_t map1,
580    struct memdesc *mem, bus_dmamap_callback_t *callback, void *callback_arg)
581{
582	struct bus_dmamap_dmar *map;
583
584	if (map1 == NULL)
585		return;
586	map = (struct bus_dmamap_dmar *)map1;
587	map->mem = *mem;
588	map->tag = (struct bus_dma_tag_dmar *)dmat;
589	map->callback = callback;
590	map->callback_arg = callback_arg;
591}
592
593static bus_dma_segment_t *
594dmar_bus_dmamap_complete(bus_dma_tag_t dmat, bus_dmamap_t map1,
595    bus_dma_segment_t *segs, int nsegs, int error)
596{
597	struct bus_dma_tag_dmar *tag;
598	struct bus_dmamap_dmar *map;
599
600	tag = (struct bus_dma_tag_dmar *)dmat;
601	map = (struct bus_dmamap_dmar *)map1;
602
603	if (!map->locked) {
604		KASSERT(map->cansleep,
605		    ("map not locked and not sleepable context %p", map));
606
607		/*
608		 * We are called from the delayed context.  Relock the
609		 * driver.
610		 */
611		(tag->common.lockfunc)(tag->common.lockfuncarg, BUS_DMA_LOCK);
612		map->locked = true;
613	}
614
615	if (segs == NULL)
616		segs = tag->segments;
617	return (segs);
618}
619
620/*
621 * The limitations of busdma KPI forces the dmar to perform the actual
622 * unload, consisting of the unmapping of the map entries page tables,
623 * from the delayed context on i386, since page table page mapping
624 * might require a sleep to be successfull.  The unfortunate
625 * consequence is that the DMA requests can be served some time after
626 * the bus_dmamap_unload() call returned.
627 *
628 * On amd64, we assume that sf allocation cannot fail.
629 */
630static void
631dmar_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map1)
632{
633	struct bus_dma_tag_dmar *tag;
634	struct bus_dmamap_dmar *map;
635	struct dmar_ctx *ctx;
636#if defined(__amd64__)
637	struct dmar_map_entries_tailq entries;
638#endif
639
640	tag = (struct bus_dma_tag_dmar *)dmat;
641	map = (struct bus_dmamap_dmar *)map1;
642	ctx = tag->ctx;
643	atomic_add_long(&ctx->unloads, 1);
644
645#if defined(__i386__)
646	DMAR_CTX_LOCK(ctx);
647	TAILQ_CONCAT(&ctx->unload_entries, &map->map_entries, dmamap_link);
648	DMAR_CTX_UNLOCK(ctx);
649	taskqueue_enqueue(ctx->dmar->delayed_taskqueue, &ctx->unload_task);
650#else /* defined(__amd64__) */
651	TAILQ_INIT(&entries);
652	DMAR_CTX_LOCK(ctx);
653	TAILQ_CONCAT(&entries, &map->map_entries, dmamap_link);
654	DMAR_CTX_UNLOCK(ctx);
655	THREAD_NO_SLEEPING();
656	dmar_ctx_unload(ctx, &entries, false);
657	THREAD_SLEEPING_OK();
658	KASSERT(TAILQ_EMPTY(&entries), ("lazy dmar_ctx_unload %p", ctx));
659#endif
660}
661
662static void
663dmar_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map,
664    bus_dmasync_op_t op)
665{
666}
667
668struct bus_dma_impl bus_dma_dmar_impl = {
669	.tag_create = dmar_bus_dma_tag_create,
670	.tag_destroy = dmar_bus_dma_tag_destroy,
671	.map_create = dmar_bus_dmamap_create,
672	.map_destroy = dmar_bus_dmamap_destroy,
673	.mem_alloc = dmar_bus_dmamem_alloc,
674	.mem_free = dmar_bus_dmamem_free,
675	.load_phys = dmar_bus_dmamap_load_phys,
676	.load_buffer = dmar_bus_dmamap_load_buffer,
677	.load_ma = dmar_bus_dmamap_load_ma,
678	.map_waitok = dmar_bus_dmamap_waitok,
679	.map_complete = dmar_bus_dmamap_complete,
680	.map_unload = dmar_bus_dmamap_unload,
681	.map_sync = dmar_bus_dmamap_sync
682};
683
684static void
685dmar_bus_task_dmamap(void *arg, int pending)
686{
687	struct bus_dma_tag_dmar *tag;
688	struct bus_dmamap_dmar *map;
689	struct dmar_unit *unit;
690	struct dmar_ctx *ctx;
691
692	unit = arg;
693	DMAR_LOCK(unit);
694	while ((map = TAILQ_FIRST(&unit->delayed_maps)) != NULL) {
695		TAILQ_REMOVE(&unit->delayed_maps, map, delay_link);
696		DMAR_UNLOCK(unit);
697		tag = map->tag;
698		ctx = map->tag->ctx;
699		map->cansleep = true;
700		map->locked = false;
701		bus_dmamap_load_mem((bus_dma_tag_t)tag, (bus_dmamap_t)map,
702		    &map->mem, map->callback, map->callback_arg,
703		    BUS_DMA_WAITOK);
704		map->cansleep = false;
705		if (map->locked) {
706			(tag->common.lockfunc)(tag->common.lockfuncarg,
707			    BUS_DMA_UNLOCK);
708		} else
709			map->locked = true;
710		map->cansleep = false;
711		DMAR_LOCK(unit);
712	}
713	DMAR_UNLOCK(unit);
714}
715
716static void
717dmar_bus_schedule_dmamap(struct dmar_unit *unit, struct bus_dmamap_dmar *map)
718{
719	struct dmar_ctx *ctx;
720
721	ctx = map->tag->ctx;
722	map->locked = false;
723	DMAR_LOCK(unit);
724	TAILQ_INSERT_TAIL(&unit->delayed_maps, map, delay_link);
725	DMAR_UNLOCK(unit);
726	taskqueue_enqueue(unit->delayed_taskqueue, &unit->dmamap_load_task);
727}
728
729int
730dmar_init_busdma(struct dmar_unit *unit)
731{
732
733	TAILQ_INIT(&unit->delayed_maps);
734	TASK_INIT(&unit->dmamap_load_task, 0, dmar_bus_task_dmamap, unit);
735	unit->delayed_taskqueue = taskqueue_create("dmar", M_WAITOK,
736	    taskqueue_thread_enqueue, &unit->delayed_taskqueue);
737	taskqueue_start_threads(&unit->delayed_taskqueue, 1, PI_DISK,
738	    "dmar%d busdma taskq", unit->unit);
739	return (0);
740}
741
742void
743dmar_fini_busdma(struct dmar_unit *unit)
744{
745
746	if (unit->delayed_taskqueue == NULL)
747		return;
748
749	taskqueue_drain(unit->delayed_taskqueue, &unit->dmamap_load_task);
750	taskqueue_free(unit->delayed_taskqueue);
751	unit->delayed_taskqueue = NULL;
752}
753