xref: /titanic_51/usr/src/uts/common/os/bp_map.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/sysmacros.h>
31 #include <sys/systm.h>
32 #include <sys/mman.h>
33 #include <sys/buf.h>
34 #include <sys/vmem.h>
35 #include <sys/cmn_err.h>
36 #include <sys/debug.h>
37 #include <sys/machparam.h>
38 #include <vm/page.h>
39 #include <vm/seg_kmem.h>
40 
41 #ifdef __sparc
42 #include <sys/cpu_module.h>
43 #define	BP_FLUSH(addr, size)	flush_instr_mem((void *)addr, size);
44 #else
45 #define	BP_FLUSH(addr, size)
46 #endif
47 
48 static vmem_t *bp_map_arena;
49 static size_t bp_align;
50 static uint_t bp_devload_flags = PROT_READ | PROT_WRITE | HAT_NOSYNC;
51 int bp_max_cache = 1 << 17;		/* 128K default; tunable */
52 
53 static void *
54 bp_vmem_alloc(vmem_t *vmp, size_t size, int vmflag)
55 {
56 	return (vmem_xalloc(vmp, size, bp_align, 0, 0, NULL, NULL, vmflag));
57 }
58 
59 void
60 bp_init(size_t align, uint_t devload_flags)
61 {
62 	bp_align = MAX(align, PAGESIZE);
63 	bp_devload_flags |= devload_flags;
64 
65 	if (bp_align <= bp_max_cache)
66 		bp_map_arena = vmem_create("bp_map", NULL, 0, bp_align,
67 		    bp_vmem_alloc, vmem_free, heap_arena,
68 		    MIN(8 * bp_align, bp_max_cache), VM_SLEEP);
69 }
70 
71 /*
72  * common routine so can be called with/without VM_SLEEP
73  */
74 void *
75 bp_mapin_common(struct buf *bp, int flag)
76 {
77 	struct as *as;
78 	pfn_t pfnum;
79 	page_t *pp = NULL;
80 	page_t **pplist = NULL;
81 	caddr_t kaddr;
82 	caddr_t addr = (caddr_t)bp->b_un.b_addr;
83 	uintptr_t off = (uintptr_t)addr & PAGEOFFSET;
84 	size_t size = P2ROUNDUP(bp->b_bcount + off, PAGESIZE);
85 	pgcnt_t npages = btop(size);
86 	int color;
87 
88 	/* return if already mapped in, no pageio/physio, or physio to kas */
89 	if ((bp->b_flags & B_REMAPPED) ||
90 	    !(bp->b_flags & (B_PAGEIO | B_PHYS)) ||
91 	    (((bp->b_flags & (B_PAGEIO | B_PHYS)) == B_PHYS) &&
92 	    ((bp->b_proc == NULL) || (bp->b_proc->p_as == &kas))))
93 		return (bp->b_un.b_addr);
94 
95 	ASSERT((bp->b_flags & (B_PAGEIO | B_PHYS)) != (B_PAGEIO | B_PHYS));
96 
97 	/*
98 	 * Allocate kernel virtual space for remapping.
99 	 */
100 	color = bp_color(bp);
101 	ASSERT(color < bp_align);
102 
103 	if (bp_map_arena != NULL) {
104 		kaddr = (caddr_t)vmem_alloc(bp_map_arena,
105 		    P2ROUNDUP(color + size, bp_align), flag);
106 		if (kaddr == NULL)
107 			return (NULL);
108 		kaddr += color;
109 	} else {
110 		kaddr = vmem_xalloc(heap_arena, size, bp_align, color,
111 		    0, NULL, NULL, flag);
112 		if (kaddr == NULL)
113 			return (NULL);
114 	}
115 
116 	ASSERT(P2PHASE((uintptr_t)kaddr, bp_align) == color);
117 
118 	/*
119 	 * Map bp into the virtual space we just allocated.
120 	 */
121 	if (bp->b_flags & B_PAGEIO) {
122 		pp = bp->b_pages;
123 	} else if (bp->b_flags & B_SHADOW) {
124 		pplist = bp->b_shadow;
125 	} else {
126 		if (bp->b_proc == NULL || (as = bp->b_proc->p_as) == NULL)
127 			as = &kas;
128 	}
129 
130 	bp->b_flags |= B_REMAPPED;
131 	bp->b_un.b_addr = kaddr + off;
132 
133 	while (npages-- != 0) {
134 		if (pp) {
135 			pfnum = pp->p_pagenum;
136 			pp = pp->p_next;
137 		} else if (pplist == NULL) {
138 			if ((pfnum = hat_getpfnum(as->a_hat, addr - off)) ==
139 			    PFN_INVALID)
140 				panic("bp_mapin_common: hat_getpfnum for"
141 				    " addr %p failed\n", (void *)addr);
142 			addr += PAGESIZE;
143 		} else {
144 			pfnum = (*pplist)->p_pagenum;
145 			pplist++;
146 		}
147 
148 		hat_devload(kas.a_hat, kaddr, PAGESIZE, pfnum,
149 		    bp_devload_flags, HAT_LOAD_LOCK);
150 
151 		kaddr += PAGESIZE;
152 	}
153 	return (bp->b_un.b_addr);
154 }
155 
156 /*
157  * Convert bp for pageio/physio to a kernel addressable location.
158  */
159 void
160 bp_mapin(struct buf *bp)
161 {
162 	(void) bp_mapin_common(bp, VM_SLEEP);
163 }
164 
165 /*
166  * Release all the resources associated with a previous bp_mapin() call.
167  */
168 void
169 bp_mapout(struct buf *bp)
170 {
171 	if (bp->b_flags & B_REMAPPED) {
172 		uintptr_t addr = (uintptr_t)bp->b_un.b_addr;
173 		uintptr_t off = addr & PAGEOFFSET;
174 		uintptr_t base = addr - off;
175 		uintptr_t color = P2PHASE(base, bp_align);
176 		size_t size = P2ROUNDUP(bp->b_bcount + off, PAGESIZE);
177 		bp->b_un.b_addr = (caddr_t)off;		/* debugging aid */
178 		BP_FLUSH(base, size);
179 		hat_unload(kas.a_hat, (void *)base, size,
180 		    HAT_UNLOAD_NOSYNC | HAT_UNLOAD_UNLOCK);
181 		if (bp_map_arena != NULL)
182 			vmem_free(bp_map_arena, (void *)(base - color),
183 			    P2ROUNDUP(color + size, bp_align));
184 		else
185 			vmem_free(heap_arena, (void *)base, size);
186 		bp->b_flags &= ~B_REMAPPED;
187 	}
188 }
189