4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/types.h>
27 #include <sys/sysmacros.h>
28 #include <sys/systm.h>
32 #include <sys/cmn_err.h>
33 #include <sys/debug.h>
34 #include <sys/machparam.h>
36 #include <vm/seg_kmem.h>
37 #include <vm/seg_kpm.h>
40 #include <sys/cpu_module.h>
41 #define BP_FLUSH(addr, size) flush_instr_mem((void *)addr, size);
43 #define BP_FLUSH(addr, size)
46 int bp_force_copy
= 0;
51 static int bp_copy_common(bp_copydir_t dir
, struct buf
*bp
, void *driverbuf
,
52 offset_t offset
, size_t size
);
54 static vmem_t
*bp_map_arena
;
55 static size_t bp_align
;
56 static uint_t bp_devload_flags
= PROT_READ
| PROT_WRITE
| HAT_NOSYNC
;
57 int bp_max_cache
= 1 << 17; /* 128K default; tunable */
58 int bp_mapin_kpm_enable
= 1; /* enable default; tunable */
61 bp_vmem_alloc(vmem_t
*vmp
, size_t size
, int vmflag
)
63 return (vmem_xalloc(vmp
, size
, bp_align
, 0, 0, NULL
, NULL
, vmflag
));
67 bp_init(size_t align
, uint_t devload_flags
)
69 bp_align
= MAX(align
, PAGESIZE
);
70 bp_devload_flags
|= devload_flags
;
72 if (bp_align
<= bp_max_cache
)
73 bp_map_arena
= vmem_create("bp_map", NULL
, 0, bp_align
,
74 bp_vmem_alloc
, vmem_free
, heap_arena
,
75 MIN(8 * bp_align
, bp_max_cache
), VM_SLEEP
);
79 * common routine so can be called with/without VM_SLEEP
82 bp_mapin_common(struct buf
*bp
, int flag
)
96 /* return if already mapped in, no pageio/physio, or physio to kas */
97 if ((bp
->b_flags
& B_REMAPPED
) ||
98 !(bp
->b_flags
& (B_PAGEIO
| B_PHYS
)) ||
99 (((bp
->b_flags
& (B_PAGEIO
| B_PHYS
)) == B_PHYS
) &&
100 ((bp
->b_proc
== NULL
) || (bp
->b_proc
->p_as
== &kas
))))
101 return (bp
->b_un
.b_addr
);
103 ASSERT((bp
->b_flags
& (B_PAGEIO
| B_PHYS
)) != (B_PAGEIO
| B_PHYS
));
105 addr
= (caddr_t
)bp
->b_un
.b_addr
;
106 off
= (uintptr_t)addr
& PAGEOFFSET
;
107 size
= P2ROUNDUP(bp
->b_bcount
+ off
, PAGESIZE
);
110 /* Fastpath single page IO to locked memory by using kpm. */
111 if ((bp
->b_flags
& (B_SHADOW
| B_PAGEIO
)) && (npages
== 1) &&
112 kpm_enable
&& bp_mapin_kpm_enable
) {
113 if (bp
->b_flags
& B_SHADOW
)
117 kaddr
= hat_kpm_mapin(pp
, NULL
);
118 bp
->b_un
.b_addr
= kaddr
+ off
;
119 bp
->b_flags
|= B_REMAPPED
;
120 return (bp
->b_un
.b_addr
);
124 * Allocate kernel virtual space for remapping.
126 color
= bp_color(bp
);
127 ASSERT(color
< bp_align
);
129 if (bp_map_arena
!= NULL
) {
130 kaddr
= (caddr_t
)vmem_alloc(bp_map_arena
,
131 P2ROUNDUP(color
+ size
, bp_align
), flag
);
136 kaddr
= vmem_xalloc(heap_arena
, size
, bp_align
, color
,
137 0, NULL
, NULL
, flag
);
142 ASSERT(P2PHASE((uintptr_t)kaddr
, bp_align
) == color
);
145 * Map bp into the virtual space we just allocated.
147 if (bp
->b_flags
& B_PAGEIO
) {
150 } else if (bp
->b_flags
& B_SHADOW
) {
152 pplist
= bp
->b_shadow
;
156 if (bp
->b_proc
== NULL
|| (as
= bp
->b_proc
->p_as
) == NULL
)
160 bp
->b_flags
|= B_REMAPPED
;
161 bp
->b_un
.b_addr
= kaddr
+ off
;
163 while (npages
-- != 0) {
165 pfnum
= pp
->p_pagenum
;
167 } else if (pplist
== NULL
) {
168 pfnum
= hat_getpfnum(as
->a_hat
,
169 (caddr_t
)((uintptr_t)addr
& MMU_PAGEMASK
));
170 if (pfnum
== PFN_INVALID
)
171 panic("bp_mapin_common: hat_getpfnum for"
172 " addr %p failed\n", (void *)addr
);
175 pfnum
= (*pplist
)->p_pagenum
;
179 hat_devload(kas
.a_hat
, kaddr
, PAGESIZE
, pfnum
,
180 bp_devload_flags
, HAT_LOAD_LOCK
);
184 return (bp
->b_un
.b_addr
);
188 * Convert bp for pageio/physio to a kernel addressable location.
191 bp_mapin(struct buf
*bp
)
193 (void) bp_mapin_common(bp
, VM_SLEEP
);
197 * Release all the resources associated with a previous bp_mapin() call.
200 bp_mapout(struct buf
*bp
)
210 if ((bp
->b_flags
& B_REMAPPED
) == 0)
213 addr
= bp
->b_un
.b_addr
;
214 off
= (uintptr_t)addr
& PAGEOFFSET
;
215 size
= P2ROUNDUP(bp
->b_bcount
+ off
, PAGESIZE
);
218 bp
->b_un
.b_addr
= (caddr_t
)off
; /* debugging aid */
220 if ((bp
->b_flags
& (B_SHADOW
| B_PAGEIO
)) && (npages
== 1) &&
221 kpm_enable
&& bp_mapin_kpm_enable
) {
222 if (bp
->b_flags
& B_SHADOW
)
226 addr
= (caddr_t
)((uintptr_t)addr
& MMU_PAGEMASK
);
227 hat_kpm_mapout(pp
, NULL
, addr
);
228 bp
->b_flags
&= ~B_REMAPPED
;
232 base
= (uintptr_t)addr
& MMU_PAGEMASK
;
233 BP_FLUSH(base
, size
);
234 hat_unload(kas
.a_hat
, (void *)base
, size
,
235 HAT_UNLOAD_NOSYNC
| HAT_UNLOAD_UNLOCK
);
236 if (bp_map_arena
!= NULL
) {
237 color
= P2PHASE(base
, bp_align
);
238 vmem_free(bp_map_arena
, (void *)(base
- color
),
239 P2ROUNDUP(color
+ size
, bp_align
));
241 vmem_free(heap_arena
, (void *)base
, size
);
242 bp
->b_flags
&= ~B_REMAPPED
;
246 * copy data from a KVA into a buf_t which may not be mapped in. offset
247 * is relative to the buf_t only.
250 bp_copyout(void *driverbuf
, struct buf
*bp
, offset_t offset
, size_t size
)
252 return (bp_copy_common(BP_COPYOUT
, bp
, driverbuf
, offset
, size
));
256 * copy data from a buf_t which may not be mapped in, into a KVA.. offset
257 * is relative to the buf_t only.
260 bp_copyin(struct buf
*bp
, void *driverbuf
, offset_t offset
, size_t size
)
262 return (bp_copy_common(BP_COPYIN
, bp
, driverbuf
, offset
, size
));
266 #define BP_COPY(dir, driverbuf, baddr, sz) \
267 (dir == BP_COPYIN) ? \
268 bcopy(baddr, driverbuf, sz) : bcopy(driverbuf, baddr, sz)
271 bp_copy_common(bp_copydir_t dir
, struct buf
*bp
, void *driverbuf
,
272 offset_t offset
, size_t size
)
285 ASSERT((offset
+ size
) <= bp
->b_bcount
);
288 /* if the buf_t already has a KVA, just do a bcopy */
289 if (!(bp
->b_flags
& (B_PHYS
| B_PAGEIO
))) {
290 BP_COPY(dir
, driverbuf
, bp
->b_un
.b_addr
+ offset
, size
);
294 /* if we don't have kpm enabled, we need to do the slow path */
295 if (!kpm_enable
|| bp_force_copy
) {
297 BP_COPY(dir
, driverbuf
, bp
->b_un
.b_addr
+ offset
, size
);
303 * kpm is enabled, and we need to map in the buf_t for the copy
306 /* setup pp, plist, and make sure 'as' is right */
307 if (bp
->b_flags
& B_PAGEIO
) {
310 } else if (bp
->b_flags
& B_SHADOW
) {
312 pplist
= bp
->b_shadow
;
316 if (bp
->b_proc
== NULL
|| (as
= bp
->b_proc
->p_as
) == NULL
) {
322 * locals for the address, the offset into the first page, and the
323 * size of the first page we are going to copy.
325 addr
= (caddr_t
)bp
->b_un
.b_addr
;
326 poff
= (uintptr_t)addr
& PAGEOFFSET
;
327 psize
= MIN(PAGESIZE
- poff
, size
);
330 * we always start with a 0 offset into the driverbuf provided. The
331 * offset passed in only applies to the buf_t.
335 /* Loop until we've copied al the data */
339 * for a pp or pplist, get the pfn, then go to the next page_t
340 * for the next time around the loop.
345 } else if (pplist
!= NULL
) {
350 * We have a user VA. If we are going to copy this page, (e.g.
351 * the offset into the buf_t where we start to copy is
352 * within this page), get the pfn. Don't waste the cycles
353 * getting the pfn if we're not copying this page.
355 } else if (offset
< psize
) {
356 pfn
= hat_getpfnum(as
->a_hat
,
357 (caddr_t
)((uintptr_t)addr
& PAGEMASK
));
358 if (pfn
== PFN_INVALID
) {
361 page
= page_numtopp_nolock(pfn
);
362 addr
+= psize
- offset
;
368 * if we have an initial offset into the buf_t passed in,
369 * and it falls within the current page, account for it in
370 * the page size (how much we will copy) and the offset into the
371 * page (where we'll start copying from).
373 if ((offset
> 0) && (offset
< psize
)) {
379 * if we have an initial offset into the buf_t passed in,
380 * and it's not within the current page, skip this page.
381 * We don't have to worry about the first page offset and size
382 * anymore. psize will normally be PAGESIZE now unless we are
385 } else if (offset
>= psize
) {
387 psize
= MIN(PAGESIZE
, size
);
393 * get a kpm mapping to the page, them copy in/out of the
394 * page. update size left and offset into the driverbuf passed
395 * in for the next time around the loop.
397 kaddr
= hat_kpm_mapin(page
, NULL
) + poff
;
398 BP_COPY(dir
, (void *)((uintptr_t)driverbuf
+ voff
), kaddr
,
400 hat_kpm_mapout(page
, NULL
, kaddr
- poff
);
406 psize
= MIN(PAGESIZE
, size
);