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 #pragma ident "%Z%%M% %I% %E% SMI"
28 #include <sys/types.h>
29 #include <sys/sysmacros.h>
30 #include <sys/systm.h>
34 #include <sys/cmn_err.h>
35 #include <sys/debug.h>
36 #include <sys/machparam.h>
38 #include <vm/seg_kmem.h>
39 #include <vm/seg_kpm.h>
41 #define BP_FLUSH(addr, size)
43 int bp_force_copy
= 0;
48 static int bp_copy_common(bp_copydir_t dir
, struct buf
*bp
, void *driverbuf
,
49 offset_t offset
, size_t size
);
51 static vmem_t
*bp_map_arena
;
52 static size_t bp_align
;
53 static uint_t bp_devload_flags
= PROT_READ
| PROT_WRITE
| HAT_NOSYNC
;
54 int bp_max_cache
= 1 << 17; /* 128K default; tunable */
55 int bp_mapin_kpm_enable
= 1; /* enable default; tunable */
58 bp_vmem_alloc(vmem_t
*vmp
, size_t size
, int vmflag
)
60 return (vmem_xalloc(vmp
, size
, bp_align
, 0, 0, NULL
, NULL
, vmflag
));
64 bp_init(size_t align
, uint_t devload_flags
)
66 bp_align
= MAX(align
, PAGESIZE
);
67 bp_devload_flags
|= devload_flags
;
69 if (bp_align
<= bp_max_cache
)
70 bp_map_arena
= vmem_create("bp_map", NULL
, 0, bp_align
,
71 bp_vmem_alloc
, vmem_free
, heap_arena
,
72 MIN(8 * bp_align
, bp_max_cache
), VM_SLEEP
);
76 * common routine so can be called with/without VM_SLEEP
79 bp_mapin_common(struct buf
*bp
, int flag
)
92 /* return if already mapped in, no pageio/physio, or physio to kas */
93 if ((bp
->b_flags
& B_REMAPPED
) ||
94 !(bp
->b_flags
& (B_PAGEIO
| B_PHYS
)) ||
95 (((bp
->b_flags
& (B_PAGEIO
| B_PHYS
)) == B_PHYS
) &&
96 ((bp
->b_proc
== NULL
) || (bp
->b_proc
->p_as
== &kas
))))
97 return (bp
->b_un
.b_addr
);
99 ASSERT((bp
->b_flags
& (B_PAGEIO
| B_PHYS
)) != (B_PAGEIO
| B_PHYS
));
101 addr
= (caddr_t
)bp
->b_un
.b_addr
;
102 off
= (uintptr_t)addr
& PAGEOFFSET
;
103 size
= P2ROUNDUP(bp
->b_bcount
+ off
, PAGESIZE
);
106 /* Fastpath single page IO to locked memory by using kpm. */
107 if ((bp
->b_flags
& (B_SHADOW
| B_PAGEIO
)) && (npages
== 1) &&
108 kpm_enable
&& bp_mapin_kpm_enable
) {
109 if (bp
->b_flags
& B_SHADOW
)
113 kaddr
= hat_kpm_mapin(pp
, NULL
);
114 bp
->b_un
.b_addr
= kaddr
+ off
;
115 bp
->b_flags
|= B_REMAPPED
;
116 return (bp
->b_un
.b_addr
);
120 * Allocate kernel virtual space for remapping.
122 color
= bp_color(bp
);
123 ASSERT(color
< bp_align
);
125 if (bp_map_arena
!= NULL
) {
126 kaddr
= (caddr_t
)vmem_alloc(bp_map_arena
,
127 P2ROUNDUP(color
+ size
, bp_align
), flag
);
132 kaddr
= vmem_xalloc(heap_arena
, size
, bp_align
, color
,
133 0, NULL
, NULL
, flag
);
138 ASSERT(P2PHASE((uintptr_t)kaddr
, bp_align
) == color
);
141 * Map bp into the virtual space we just allocated.
143 if (bp
->b_flags
& B_PAGEIO
) {
146 } else if (bp
->b_flags
& B_SHADOW
) {
148 pplist
= bp
->b_shadow
;
152 if (bp
->b_proc
== NULL
|| (as
= bp
->b_proc
->p_as
) == NULL
)
156 bp
->b_flags
|= B_REMAPPED
;
157 bp
->b_un
.b_addr
= kaddr
+ off
;
159 while (npages
-- != 0) {
161 pfnum
= pp
->p_pagenum
;
163 } else if (pplist
== NULL
) {
164 pfnum
= hat_getpfnum(as
->a_hat
,
165 (caddr_t
)((uintptr_t)addr
& MMU_PAGEMASK
));
166 if (pfnum
== PFN_INVALID
)
167 panic("bp_mapin_common: hat_getpfnum for"
168 " addr %p failed\n", (void *)addr
);
171 pfnum
= (*pplist
)->p_pagenum
;
175 hat_devload(kas
.a_hat
, kaddr
, PAGESIZE
, pfnum
,
176 bp_devload_flags
, HAT_LOAD_LOCK
);
180 return (bp
->b_un
.b_addr
);
184 * Convert bp for pageio/physio to a kernel addressable location.
187 bp_mapin(struct buf
*bp
)
189 (void) bp_mapin_common(bp
, VM_SLEEP
);
193 * Release all the resources associated with a previous bp_mapin() call.
196 bp_mapout(struct buf
*bp
)
206 if ((bp
->b_flags
& B_REMAPPED
) == 0)
209 addr
= bp
->b_un
.b_addr
;
210 off
= (uintptr_t)addr
& PAGEOFFSET
;
211 size
= P2ROUNDUP(bp
->b_bcount
+ off
, PAGESIZE
);
214 bp
->b_un
.b_addr
= (caddr_t
)off
; /* debugging aid */
216 if ((bp
->b_flags
& (B_SHADOW
| B_PAGEIO
)) && (npages
== 1) &&
217 kpm_enable
&& bp_mapin_kpm_enable
) {
218 if (bp
->b_flags
& B_SHADOW
)
222 addr
= (caddr_t
)((uintptr_t)addr
& MMU_PAGEMASK
);
223 hat_kpm_mapout(pp
, NULL
, addr
);
224 bp
->b_flags
&= ~B_REMAPPED
;
228 base
= (uintptr_t)addr
& MMU_PAGEMASK
;
229 BP_FLUSH(base
, size
);
230 hat_unload(kas
.a_hat
, (void *)base
, size
,
231 HAT_UNLOAD_NOSYNC
| HAT_UNLOAD_UNLOCK
);
232 if (bp_map_arena
!= NULL
) {
233 color
= P2PHASE(base
, bp_align
);
234 vmem_free(bp_map_arena
, (void *)(base
- color
),
235 P2ROUNDUP(color
+ size
, bp_align
));
237 vmem_free(heap_arena
, (void *)base
, size
);
238 bp
->b_flags
&= ~B_REMAPPED
;
242 * copy data from a KVA into a buf_t which may not be mapped in. offset
243 * is relative to the buf_t only.
246 bp_copyout(void *driverbuf
, struct buf
*bp
, offset_t offset
, size_t size
)
248 return (bp_copy_common(BP_COPYOUT
, bp
, driverbuf
, offset
, size
));
252 * copy data from a buf_t which may not be mapped in, into a KVA.. offset
253 * is relative to the buf_t only.
256 bp_copyin(struct buf
*bp
, void *driverbuf
, offset_t offset
, size_t size
)
258 return (bp_copy_common(BP_COPYIN
, bp
, driverbuf
, offset
, size
));
262 #define BP_COPY(dir, driverbuf, baddr, sz) \
263 (dir == BP_COPYIN) ? \
264 bcopy(baddr, driverbuf, sz) : bcopy(driverbuf, baddr, sz)
267 bp_copy_common(bp_copydir_t dir
, struct buf
*bp
, void *driverbuf
,
268 offset_t offset
, size_t size
)
282 ASSERT((offset
+ size
) <= bp
->b_bcount
);
284 /* if the buf_t already has a KVA, just do a bcopy */
285 if (!(bp
->b_flags
& (B_PHYS
| B_PAGEIO
))) {
286 BP_COPY(dir
, driverbuf
, bp
->b_un
.b_addr
+ offset
, size
);
290 /* if we don't have kpm enabled, we need to do the slow path */
291 if (!kpm_enable
|| bp_force_copy
) {
293 BP_COPY(dir
, driverbuf
, bp
->b_un
.b_addr
+ offset
, size
);
299 * kpm is enabled, and we need to map in the buf_t for the copy
302 /* setup pp, plist, and make sure 'as' is right */
303 if (bp
->b_flags
& B_PAGEIO
) {
306 } else if (bp
->b_flags
& B_SHADOW
) {
308 pplist
= bp
->b_shadow
;
312 if (bp
->b_proc
== NULL
|| (as
= bp
->b_proc
->p_as
) == NULL
) {
318 * locals for the address, the offset into the first page, and the
319 * size of the first page we are going to copy.
321 addr
= (caddr_t
)bp
->b_un
.b_addr
;
322 poff
= (uintptr_t)addr
& PAGEOFFSET
;
323 psize
= MIN(PAGESIZE
- poff
, size
);
326 * we always start with a 0 offset into the driverbuf provided. The
327 * offset passed in only applies to the buf_t.
331 /* Loop until we've copied al the data */
335 * for a pp or pplist, get the pfn, then go to the next page_t
336 * for the next time around the loop.
341 } else if (pplist
!= NULL
) {
346 * We have a user VA. If we are going to copy this page, (e.g.
347 * the offset into the buf_t where we start to copy is
348 * within this page), get the pfn. Don't waste the cycles
349 * getting the pfn if we're not copying this page.
351 } else if (offset
< psize
) {
352 pfn
= hat_getpfnum(as
->a_hat
,
353 (caddr_t
)((uintptr_t)addr
& PAGEMASK
));
354 if (pfn
== PFN_INVALID
) {
357 page
= page_numtopp_nolock(pfn
);
358 addr
+= psize
- offset
;
364 * if we have an initial offset into the buf_t passed in,
365 * and it falls within the current page, account for it in
366 * the page size (how much we will copy) and the offset into the
367 * page (where we'll start copying from).
369 if ((offset
> 0) && (offset
< psize
)) {
375 * if we have an initial offset into the buf_t passed in,
376 * and it's not within the current page, skip this page.
377 * We don't have to worry about the first page offset and size
378 * anymore. psize will normally be PAGESIZE now unless we are
381 } else if (offset
>= psize
) {
383 psize
= MIN(PAGESIZE
, size
);
389 * get a kpm mapping to the page, them copy in/out of the
390 * page. update size left and offset into the driverbuf passed
391 * in for the next time around the loop.
393 kaddr
= hat_kpm_mapin(page
, NULL
) + poff
;
394 BP_COPY(dir
, (void *)((uintptr_t)driverbuf
+ voff
), kaddr
,
396 hat_kpm_mapout(page
, NULL
, kaddr
- poff
);
402 psize
= MIN(PAGESIZE
, size
);