xref: /netbsd-src/sys/uvm/uvm_pglist.c (revision 89c5a767f8fc7a4633b2d409966e2becbb98ff92)
1 /*	$NetBSD: uvm_pglist.c,v 1.8 1999/07/22 22:58:39 thorpej Exp $	*/
2 
3 #define VM_PAGE_ALLOC_MEMORY_STATS
4 
5 /*-
6  * Copyright (c) 1997 The NetBSD Foundation, Inc.
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to The NetBSD Foundation
10  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
11  * NASA Ames Research Center.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. All advertising materials mentioning features or use of this software
22  *    must display the following acknowledgement:
23  *      This product includes software developed by the NetBSD
24  *      Foundation, Inc. and its contributors.
25  * 4. Neither the name of The NetBSD Foundation nor the names of its
26  *    contributors may be used to endorse or promote products derived
27  *    from this software without specific prior written permission.
28  *
29  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
30  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
31  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
32  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
33  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
34  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
35  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
37  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39  * POSSIBILITY OF SUCH DAMAGE.
40  */
41 
42 /*
43  * uvm_pglist.c: pglist functions
44  *
45  * XXX: was part of uvm_page but has an incompatable copyright so it
46  * gets its own file now.
47  */
48 
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/malloc.h>
52 #include <sys/proc.h>
53 
54 #include <vm/vm.h>
55 #include <vm/vm_page.h>
56 #include <vm/vm_kern.h>
57 
58 #include <uvm/uvm.h>
59 
60 #ifdef VM_PAGE_ALLOC_MEMORY_STATS
61 #define	STAT_INCR(v)	(v)++
62 #define	STAT_DECR(v)	do { \
63 		if ((v) == 0) \
64 			printf("%s:%d -- Already 0!\n", __FILE__, __LINE__); \
65 		else \
66 			(v)--; \
67 	} while (0)
68 u_long	uvm_pglistalloc_npages;
69 #else
70 #define	STAT_INCR(v)
71 #define	STAT_DECR(v)
72 #endif
73 
74 /*
75  * uvm_pglistalloc: allocate a list of pages
76  *
77  * => allocated pages are placed at the tail of rlist.  rlist is
78  *    assumed to be properly initialized by caller.
79  * => returns 0 on success or errno on failure
80  * => XXX: implementation allocates only a single segment, also
81  *	might be able to better advantage of vm_physeg[].
82  * => doesn't take into account clean non-busy pages on inactive list
83  *	that could be used(?)
84  * => params:
85  *	size		the size of the allocation, rounded to page size.
86  *	low		the low address of the allowed allocation range.
87  *	high		the high address of the allowed allocation range.
88  *	alignment	memory must be aligned to this power-of-two boundary.
89  *	boundary	no segment in the allocation may cross this
90  *			power-of-two boundary (relative to zero).
91  */
92 
93 int
94 uvm_pglistalloc(size, low, high, alignment, boundary, rlist, nsegs, waitok)
95 	psize_t size;
96 	paddr_t low, high, alignment, boundary;
97 	struct pglist *rlist;
98 	int nsegs, waitok;
99 {
100 	paddr_t try, idxpa, lastidxpa;
101 	int psi;
102 	struct vm_page *pgs;
103 	int s, tryidx, idx, end, error, free_list;
104 	vm_page_t m;
105 	u_long pagemask;
106 #ifdef DEBUG
107 	vm_page_t tp;
108 #endif
109 
110 #ifdef DIAGNOSTIC
111 	if ((alignment & (alignment - 1)) != 0)
112 		panic("vm_page_alloc_memory: alignment must be power of 2");
113 
114 	if ((boundary & (boundary - 1)) != 0)
115 		panic("vm_page_alloc_memory: boundary must be power of 2");
116 #endif
117 
118 	/*
119 	 * Our allocations are always page granularity, so our alignment
120 	 * must be, too.
121 	 */
122 	if (alignment < PAGE_SIZE)
123 		alignment = PAGE_SIZE;
124 
125 	size = round_page(size);
126 	try = roundup(low, alignment);
127 
128 	if (boundary != 0 && boundary < size)
129 		return (EINVAL);
130 
131 	pagemask = ~(boundary - 1);
132 
133 	/* Default to "lose". */
134 	error = ENOMEM;
135 
136 	/*
137 	 * Block all memory allocation and lock the free list.
138 	 */
139 	s = uvm_lock_fpageq();		/* lock free page queue */
140 
141 	/* Are there even any free pages? */
142 	for (idx = 0; idx < VM_NFREELIST; idx++)
143 		if (uvm.page_free[idx].tqh_first != NULL)
144 			break;
145 	if (idx == VM_NFREELIST)
146 		goto out;
147 
148 	for (;; try += alignment) {
149 		if (try + size > high) {
150 			/*
151 			 * We've run past the allowable range.
152 			 */
153 			goto out;
154 		}
155 
156 		/*
157 		 * Make sure this is a managed physical page.
158 		 */
159 
160 		if ((psi = vm_physseg_find(atop(try), &idx)) == -1)
161 			continue; /* managed? */
162 		if (vm_physseg_find(atop(try + size), NULL) != psi)
163 			continue; /* end must be in this segment */
164 
165 		tryidx = idx;
166 		end = idx + (size / PAGE_SIZE);
167 		pgs = vm_physmem[psi].pgs;
168 
169 		/*
170 		 * Found a suitable starting page.  See of the range is free.
171 		 */
172 		for (; idx < end; idx++) {
173 			if (VM_PAGE_IS_FREE(&pgs[idx]) == 0) {
174 				/*
175 				 * Page not available.
176 				 */
177 				break;
178 			}
179 
180 			idxpa = VM_PAGE_TO_PHYS(&pgs[idx]);
181 
182 			if (idx > tryidx) {
183 				lastidxpa = VM_PAGE_TO_PHYS(&pgs[idx - 1]);
184 
185 				if ((lastidxpa + PAGE_SIZE) != idxpa) {
186 					/*
187 					 * Region not contiguous.
188 					 */
189 					break;
190 				}
191 				if (boundary != 0 &&
192 				    ((lastidxpa ^ idxpa) & pagemask) != 0) {
193 					/*
194 					 * Region crosses boundary.
195 					 */
196 					break;
197 				}
198 			}
199 		}
200 
201 		if (idx == end) {
202 			/*
203 			 * Woo hoo!  Found one.
204 			 */
205 			break;
206 		}
207 	}
208 
209 	/*
210 	 * we have a chunk of memory that conforms to the requested constraints.
211 	 */
212 	idx = tryidx;
213 	while (idx < end) {
214 		m = &pgs[idx];
215 		free_list = uvm_page_lookup_freelist(m);
216 #ifdef DEBUG
217 		for (tp = uvm.page_free[free_list].tqh_first;
218 		     tp != NULL; tp = tp->pageq.tqe_next) {
219 			if (tp == m)
220 				break;
221 		}
222 		if (tp == NULL)
223 			panic("uvm_pglistalloc: page not on freelist");
224 #endif
225 		TAILQ_REMOVE(&uvm.page_free[free_list], m, pageq);
226 		uvmexp.free--;
227 		m->flags = PG_CLEAN;
228 		m->pqflags = 0;
229 		m->uobject = NULL;
230 		m->uanon = NULL;
231 		m->wire_count = 0;
232 		m->loan_count = 0;
233 		TAILQ_INSERT_TAIL(rlist, m, pageq);
234 		idx++;
235 		STAT_INCR(uvm_pglistalloc_npages);
236 	}
237 	error = 0;
238 
239 out:
240 	uvm_unlock_fpageq(s);
241 
242 	/*
243 	 * check to see if we need to generate some free pages waking
244 	 * the pagedaemon.
245 	 * XXX: we read uvm.free without locking
246 	 */
247 
248 	if (uvmexp.free < uvmexp.freemin ||
249 	    (uvmexp.free < uvmexp.freetarg &&
250 	    uvmexp.inactive < uvmexp.inactarg))
251 		wakeup(&uvm.pagedaemon);
252 
253 	return (error);
254 }
255 
256 /*
257  * uvm_pglistfree: free a list of pages
258  *
259  * => pages should already be unmapped
260  */
261 
262 void
263 uvm_pglistfree(list)
264 	struct pglist *list;
265 {
266 	vm_page_t m;
267 	int s;
268 
269 	/*
270 	 * Block all memory allocation and lock the free list.
271 	 */
272 	s = uvm_lock_fpageq();
273 
274 	while ((m = list->tqh_first) != NULL) {
275 #ifdef DIAGNOSTIC
276 		if (m->pqflags & (PQ_ACTIVE|PQ_INACTIVE))
277 			panic("uvm_pglistfree: active/inactive page!");
278 #endif
279 		TAILQ_REMOVE(list, m, pageq);
280 		m->pqflags = PQ_FREE;
281 		TAILQ_INSERT_TAIL(&uvm.page_free[uvm_page_lookup_freelist(m)],
282 		    m, pageq);
283 		uvmexp.free++;
284 		STAT_DECR(uvm_pglistalloc_npages);
285 	}
286 
287 	uvm_unlock_fpageq(s);
288 }
289