xref: /386bsd/usr/src/kernel/kern/vm/page.c (revision a2142627)
1 /*
2  * Copyright (c) 1991 Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * The Mach Operating System project at Carnegie-Mellon University.
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  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *	$Id: page.c,v 1.1 94/10/19 17:37:20 bill Exp $
37  *
38  *
39  * Copyright (c) 1987, 1990 Carnegie-Mellon University.
40  * All rights reserved.
41  *
42  * Authors: Avadis Tevanian, Jr., Michael Wayne Young
43  *
44  * Permission to use, copy, modify and distribute this software and
45  * its documentation is hereby granted, provided that both the copyright
46  * notice and this permission notice appear in all copies of the
47  * software, derivative works or modified versions, and any portions
48  * thereof, and that both notices appear in supporting documentation.
49  *
50  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
51  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
52  * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
53  *
54  * Carnegie Mellon requests users of this software to return to
55  *
56  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
57  *  School of Computer Science
58  *  Carnegie Mellon University
59  *  Pittsburgh PA 15213-3890
60  *
61  * any improvements or extensions that they make and grant Carnegie the
62  * rights to redistribute these changes.
63  */
64 
65 /* Physical memory page allocation via logical page abstraction.  */
66 
67 #include "sys/param.h"
68 #include "sys/errno.h" 	/* for inlines */
69 #include "proc.h"
70 #ifdef	KTRACE_notyet
71 #include "sys/ktrace.h"
72 #endif
73 #include "vm.h"
74 #include "vm_pageout.h"
75 #include "prototypes.h"
76 
77 /*
78  * Each unit of physical memory has a logical page (vm_page) entry.
79  */
80 
81 /* allocate pages are located by logical "name" (obj/offset) via hash table */
82 queue_head_t	*vm_page_buckets;		/* hash bucket array */
83 int		vm_page_bucket_count = 0;	/* bucket array length */
84 int		vm_page_hash_mask;	/* address mask for hash function */
85 
86 /* storage is allocated in units of logical pages, which are integral HW pages */
87 vm_size_t	page_size, page_mask;		/* logical page size */
88 int		page_shift;
89 
90 /* pages are queued for use (active), reclaimation (inactive), or reuse (free) */
91 queue_head_t	vm_page_queue_free;
92 queue_head_t	vm_page_queue_active, vm_page_queue_inactive;
93 
94 /* managed pages have logical page entries in an array, indexed by phys addr */
95 vm_page_t	vm_page_array;
96 long		first_page;
97 long		last_page;
98 vm_offset_t	first_phys_addr;
99 vm_offset_t	last_phys_addr;
100 
101 /* each page queue has a count of pages contained within */
102 int	vm_page_free_count;
103 int	vm_page_free_io_count;	/* XXX */
104 int	vm_page_active_count;
105 int	vm_page_inactive_count;
106 int	vm_page_free_target = 0;
107 int	vm_page_free_min = 0;
108 int	vm_page_inactive_target = 0;
109 
110 /* pages may be wired to keep them from being reclaimed */
111 int	vm_page_wire_count;
112 
113 /*static void vm_page_insert(vm_page_t mem, vm_object_t object,
114 	vm_offset_t offset);*/
115 /* "internal" functions used externally by device_pager:
116 static void vm_page_remove(vm_page_t mem);
117 static void vm_page_init(vm_page_t mem, vm_object_t object, vm_offset_t offset);
118 */
119 
120 /* set logical page size, which is an integral number of hardware pages */
121 void
vm_set_page_size(int sz)122 vm_set_page_size(int sz)
123 {
124 	page_size = 4096; /*sz;*/
125 	page_mask = page_size - 1;
126 
127 	if ((page_mask & page_size) != 0)
128 		panic("vm_set_page_size: size not a power of two");
129 
130 	if (page_size % NBPG != 0)
131 		panic("vm_set_page_size: size not an integral number of cpu pages ");
132 
133 	for (page_shift = 0; ; page_shift++)
134 		if ((1 << page_shift) == page_size)
135 			break;
136 }
137 
138 /* Initialize the array of logical pages. */
139 void
vm_page_startup(void)140 vm_page_startup(void /*vm_offset_t spa, vm_offset_t epa, vm_offset_t sva*/)
141 {
142 extern vm_offset_t avail_start, avail_end, virtual_avail;
143 	vm_offset_t spa = avail_start, epa = avail_end, sva = virtual_avail,
144 		mapped, new_start, pa;
145 	vm_page_t m;
146 	queue_t	bucket;
147 	vm_size_t npages;
148 	int i;
149 
150 	/* Initialize the queue headers as being empty. */
151 	queue_init(&vm_page_queue_free);
152 	queue_init(&vm_page_queue_active);
153 	queue_init(&vm_page_queue_inactive);
154 
155 	/*
156 	 * find the next power of 2 number of hash buckets to
157 	 * cover the range of managed pages and set hash table
158 	 * address mask.
159 	 */
160 	vm_page_buckets = (queue_t) sva;
161 	bucket = vm_page_buckets;
162 	if (vm_page_bucket_count == 0) {
163 		vm_page_bucket_count = 1;
164 		while (vm_page_bucket_count < atop(epa - spa))
165 			vm_page_bucket_count <<= 1;
166 	}
167 	vm_page_hash_mask = vm_page_bucket_count - 1;
168 
169 	/* allocate and clear hash buckets from sva/spa */
170 	new_start = round_page(((queue_t)spa) + vm_page_bucket_count);
171 	mapped = sva;
172 	sva = pmap_map(mapped, spa, new_start,
173 			VM_PROT_READ|VM_PROT_WRITE);
174 	spa = new_start;
175 	memset((caddr_t) mapped, 0, sva - mapped);
176 	mapped = sva;
177 
178 	/* initialize hash buckets each as empty queues */
179 	for (i = vm_page_bucket_count; i--;)
180 { queue_init(bucket); bucket++; }	/* fix goddamn macros */
181 
182 	/* find the number of logical pages to be managed */
183 	epa = trunc_page(epa);
184 	vm_page_free_count = npages =
185 	    howmany(epa - spa, PAGE_SIZE + sizeof(struct vm_page));
186 
187 	/* find boundaries of logical page array and managed pages */
188 	m = vm_page_array = (vm_page_t) sva;
189 	first_page = spa;
190 	first_page += npages * sizeof(struct vm_page);
191 	first_page = atop(round_page(first_page));
192 	last_page  = first_page + npages - 1;
193 	first_phys_addr = ptoa(first_page);
194 	last_phys_addr  = ptoa(last_page) + page_mask;
195 
196 	/* allocate & clear the logical page array from sva/spa */
197 	new_start = spa + (round_page(m + npages) - mapped);
198 	mapped = pmap_map(mapped, spa, new_start,
199 			VM_PROT_READ|VM_PROT_WRITE);
200 	spa = new_start;
201 	memset((caddr_t)m, 0, npages * sizeof(*m));
202 
203 	/* initialize each logical page in the array */
204 	pa = first_phys_addr;
205 	while (npages--) {
206 		m->copy_on_write = FALSE;
207 		m->wanted = FALSE;
208 		m->inactive = FALSE;
209 		m->active = FALSE;
210 		m->busy = FALSE;
211 		m->object = NULL;
212 		m->phys_addr = pa;
213 #ifdef	i386
214 		m->io = (pa < 16*1024*1024);	/* XXX */
215 #else
216 		m->io = FALSE;			/* XXX */
217 #endif
218 		if (m->io)
219 			vm_page_free_io_count++;
220 		queue_enter(&vm_page_queue_free, m, vm_page_t, pageq);
221 		m++;
222 		pa += PAGE_SIZE;
223 	}
224 
225 	virtual_avail = mapped;
226 }
227 
228 /* construct a hash of the low-order bits of the page offset/obj pair */
229 #define vm_page_hash(object, offset) \
230     (((unsigned)object + (unsigned)atop(offset)) & vm_page_hash_mask)
231 
232 /* insert a page into an object at an offset */
233 void
vm_page_insert(register vm_page_t mem,vm_object_t object,vm_offset_t offset)234 vm_page_insert(register vm_page_t mem, vm_object_t object, vm_offset_t offset)
235 {
236 	queue_t	bucket;
237 	int spl;
238 
239 #ifdef	DIAGNOSTIC
240 	if (mem->tabled)
241 		panic("vm_page_insert: already inserted");
242 #endif
243 
244 	/* assign page to object at an offset */
245 	mem->object = object;
246 	mem->offset = offset;
247 
248 	/* enter the page into the hash table queue */
249 	bucket = &vm_page_buckets[vm_page_hash(object, offset)];
250 	spl = splimp();
251 	queue_enter(bucket, mem, vm_page_t, hashq);
252 
253 	/* enter the page into the object's queue of pages */
254 	queue_enter(&object->memq, mem, vm_page_t, listq);
255 	(void) splx(spl);
256 	mem->tabled = TRUE;
257 	object->resident_page_count++;
258 }
259 
260 /* remove page from an object */
261 /* static */ void
vm_page_remove(vm_page_t mem)262 vm_page_remove(vm_page_t mem)
263 {
264 	queue_t bucket;
265 	int spl;
266 
267 	/* if not in an object, don't need to remove from an object */
268 	if (!mem->tabled)
269 		return;
270 
271 	/* remove page from lookup hash table */
272 	bucket = &vm_page_buckets[vm_page_hash(mem->object, mem->offset)];
273 	spl = splimp();
274 	queue_remove(bucket, mem, vm_page_t, hashq);
275 
276 	/* remove page from object's queue of pages */
277 	queue_remove(&mem->object->memq, mem, vm_page_t, listq);
278 	(void) splx(spl);
279 	mem->object->resident_page_count--;
280 	mem->tabled = FALSE;
281 }
282 
283 /* locate a page by object/offset using hash table queues */
284 vm_page_t
vm_page_lookup(vm_object_t object,vm_offset_t offset)285 vm_page_lookup(vm_object_t object, vm_offset_t offset)
286 {
287 	vm_page_t mem;
288 	queue_t	bucket;
289 	int spl;
290 
291 	/* find the bucket queue header for this object/offset */
292 	bucket = &vm_page_buckets[vm_page_hash(object, offset)];
293 
294 	/* linear search of the queue for the desired page */
295 	spl = splimp();
296 	mem = (vm_page_t) queue_first(bucket);
297 	while (!queue_end(bucket, (queue_entry_t) mem)) {
298 		if ((mem->object == object) && (mem->offset == offset)) {
299 			splx(spl);
300 			return(mem);
301 		}
302 		mem = (vm_page_t) queue_next(&mem->hashq);
303 	}
304 	splx(spl);
305 	return(NULL);
306 }
307 
308 
309 /* associate page with new object/offset pair */
310 void
vm_page_rename(vm_page_t mem,vm_object_t new_object,vm_offset_t new_offset)311 vm_page_rename(vm_page_t mem, vm_object_t new_object, vm_offset_t new_offset)
312 {
313 	if (mem->object == new_object)
314 		return;
315 
316     	vm_page_remove(mem);
317 	vm_page_insert(mem, new_object, new_offset);
318 }
319 
320 /* allocate a page (if available) to the object at the offset */
321 vm_page_t
vm_page_alloc(vm_object_t object,vm_offset_t offset,boolean_t io)322 vm_page_alloc(vm_object_t object, vm_offset_t offset, boolean_t io)
323 {
324 	vm_page_t mem;
325 	int spl;
326 
327 #ifdef KTRACE_notyet
328 	if (curproc && KTRPOINT(curproc, KTR_VM))
329 		ktrvm(curproc->p_tracep, KTVM_PAGE, object, offset, 0, 0);
330 #endif
331 	spl = splimp();				/* XXX */
332 	if (queue_empty(&vm_page_queue_free)) {
333 	none:
334 mem = NULL;
335 goto jab;
336 		/*splx(spl);
337 		return(NULL);*/
338 	}
339 
340 	mem = (vm_page_t) queue_first(&vm_page_queue_free);
341 	while (!queue_end(&vm_page_queue_free, (queue_entry_t) mem)) {
342 if (mem->tabled)
343 panic("vm_page_alloc");
344 		if (!io || mem->io)
345 			goto found;
346 		mem = (vm_page_t) queue_next(&mem->pageq);
347 	}
348 	goto none;
349 
350 found:
351 	queue_remove(&vm_page_queue_free, mem, vm_page_t, pageq);
352 	vm_page_free_count--;
353 	if (mem->io)
354 		vm_page_free_io_count--;
355 	vm_page_init(mem, object, offset);
356 jab:
357 
358 	/* schedule pageout if too few free (or soon to be free) pages */
359 	if (vm_pages_needed || (vm_page_free_count < vm_page_free_min) ||
360 	    (vm_page_free_io_count < vm_page_free_min) ||
361 	    ((vm_page_free_count < vm_page_free_target) &&
362 	    (vm_page_inactive_count < vm_page_inactive_target)))
363 		wakeup((caddr_t)&proc0);
364 
365 	splx(spl);
366 	return(mem);
367 }
368 
369 /* release page to free memory queue */
370 void
vm_page_free(register vm_page_t mem)371 vm_page_free(register vm_page_t mem)
372 {
373 	/* remove from object/offset */
374 	vm_page_remove(mem);
375 
376 	/* remove from active queue */
377 	if (mem->active) {
378 		queue_remove(&vm_page_queue_active, mem, vm_page_t, pageq);
379 		mem->active = FALSE;
380 		vm_page_active_count--;
381 	}
382 
383 	/* remove from inactive queue */
384 	if (mem->inactive) {
385 		queue_remove(&vm_page_queue_inactive, mem, vm_page_t, pageq);
386 		mem->inactive = FALSE;
387 		vm_page_inactive_count--;
388 	}
389 
390 	/* if a "real" memory page, add to free queue */
391 	if (!mem->fictitious) {
392 		int spl = splimp();
393 
394 		queue_enter(&vm_page_queue_free, mem, vm_page_t, pageq);
395 		vm_page_free_count++;
396 		if (vm_pages_needed && vm_pages_needed > vm_page_free_count)
397 			wakeup((caddr_t) &vm_page_free_count);
398 		if (mem->io)
399 			vm_page_free_io_count++;
400 		splx(spl);
401 	}
402 }
403 
404 /* increase map entry wiring reference count on page. */
405 void
vm_page_wire(register vm_page_t mem)406 vm_page_wire(register vm_page_t mem)
407 {
408 	/* if this is the first wiring, isolate from pageout daemon */
409 	if (mem->wire_count++ == 0) {
410 
411 		/* remove from active queue */
412 		if (mem->active) {
413 			queue_remove(&vm_page_queue_active, mem, vm_page_t,
414 						pageq);
415 			vm_page_active_count--;
416 			mem->active = FALSE;
417 		}
418 
419 		/* remove from inactive queue */
420 		if (mem->inactive) {
421 			queue_remove(&vm_page_queue_inactive, mem, vm_page_t,
422 						pageq);
423 			vm_page_inactive_count--;
424 			mem->inactive = FALSE;
425 		}
426 
427 		vm_page_wire_count++;
428 	}
429 }
430 
431 /* decrease map entry wiring reference count on page. */
432 void
vm_page_unwire(register vm_page_t mem)433 vm_page_unwire(register vm_page_t mem)
434 {
435 	/* if page becomes unwired, activate the page */
436 	if (--mem->wire_count == 0) {
437 		queue_enter(&vm_page_queue_active, mem, vm_page_t, pageq);
438 		vm_page_active_count++;
439 		mem->active = TRUE;
440 		vm_page_wire_count--;
441 	}
442 }
443 
444 /* deactivate a page by removing it from reuse. */
445 void
vm_page_deactivate(register vm_page_t m)446 vm_page_deactivate(register vm_page_t m)
447 {
448 	/* ignore already inactive or wired pages */
449 	if (!m->inactive && m->wire_count == 0) {
450 
451 		/* loose reference bit and any adr. trans. to detect reuse */
452                 pmap_clear_reference(VM_PAGE_TO_PHYS(m));
453 		pmap_page_protect(VM_PAGE_TO_PHYS(m), VM_PROT_NONE);
454 
455 		/* remove from active queue */
456 		if (m->active) {
457 			queue_remove(&vm_page_queue_active, m, vm_page_t, pageq);
458 			m->active = FALSE;
459 			vm_page_active_count--;
460 		}
461 
462 		/* insert in inactive queue */
463 		queue_enter(&vm_page_queue_inactive, m, vm_page_t, pageq);
464 		m->active = FALSE;
465 		m->inactive = TRUE;
466 		vm_page_inactive_count++;
467 
468 		/* has page been modified? */
469 		if (pmap_is_modified(VM_PAGE_TO_PHYS(m)))
470 			m->clean = FALSE;
471 		m->laundry = !m->clean;
472 	}
473 }
474 
475 /* activate an inactive or off-queue page */
476 void
vm_page_activate(register vm_page_t m)477 vm_page_activate(register vm_page_t m)
478 {
479 	/* remove from inactive queue */
480 	if (m->inactive) {
481 		queue_remove(&vm_page_queue_inactive, m, vm_page_t, pageq);
482 		vm_page_inactive_count--;
483 		m->inactive = FALSE;
484 	}
485 
486 	/* ignore wired pages */
487 	if (m->wire_count)
488 		return;
489 
490 #ifdef	DIAGNOSTIC
491 	if (m->active)
492 		panic("vm_page_activate: already active");
493 #endif
494 
495 	/* enter active queue */
496 	queue_enter(&vm_page_queue_active, m, vm_page_t, pageq);
497 	m->active = TRUE;
498 	vm_page_active_count++;
499 }
500 
501 #if	defined(DEBUG) || defined(DDB)
502 /*vm_page_array_check() {
503 	vm_page_t p;
504 
505 	for(p = vm_page_array ; p < vm_page_array + (last_page - first_page); p++) {
506 */
vm_Page_check(vm_page_t p)507 vm_Page_check(vm_page_t p) {
508 	if ( (((unsigned int) p) < ((unsigned int) &vm_page_array[0])) ||
509 	     (((unsigned int) p) > ((unsigned int) &vm_page_array[last_page-first_page])))
510 {
511 vm_page_print(p);
512 	return(0);
513 }
514 return(1);
515 }
516 #undef vm_page_check
vm_page_check(vm_page_t p)517 vm_page_check(vm_page_t p) {
518 	if ( (((unsigned int) p) < ((unsigned int) &vm_page_array[0])) ||
519 	     (((unsigned int) p) > ((unsigned int) &vm_page_array[last_page-first_page])))
520 {
521 vm_page_print(p);
522 		/*panic("not vm_page");*/
523 Debugger();
524 }
525 }
526 
vm_page_print(vm_page_t p)527 vm_page_print(vm_page_t p) {
528 	if ( (((unsigned int) p) < ((unsigned int) &vm_page_array[0])) ||
529 	     (((unsigned int) p) > ((unsigned int) &vm_page_array[last_page-first_page]))){
530 		printf("Page %x: not in page array\n ");
531 return;
532 }
533 	printf("Page %x: obj/off %x/%x list %x/%x phys %x ",
534 	    p, p->object, p->offset, p->listq.next, p->listq.prev,
535 	    p->phys_addr);
536 /*pageq
537 hashq
538 listq*/
539 	/* booleans first */
540 	if (p->busy) printf("busy ");
541 	if (p->tabled) printf("tabled ");
542 	if (p->fictitious) printf("ficitious ");
543 	if (p->laundry) printf("laundry ");
544 	if (p->active) printf("active ");
545 	if (p->inactive) printf("inactive ");
546 	if (p->clean) printf("clean ");
547 	if (p->fake) printf("fake ");
548 	if (p->copy_on_write) printf("copy_on_write ");
549 
550 	/* decimal fields */
551 	if (p->page_lock) printf("page_lock %d ", p->page_lock);
552 	if (p->unlock_request) printf("unlock_request %d ", p->unlock_request);
553 	if (p->wire_count) printf("wire_count %d ", p->wire_count);
554 
555 	printf("\n");
556 }
557 
vm_page_queues_print()558 vm_page_queues_print() {
559 
560 	printf("free (%d):\n", vm_page_free_count);
561 	vm_page_queue_print(&vm_page_queue_free);
562 	printf("inactive(%d):\n", vm_page_inactive_count);
563 	vm_page_queue_print(&vm_page_queue_inactive);
564 	printf("active(%d):\n", vm_page_active_count);
565 	vm_page_queue_print(&vm_page_queue_active);
566 }
567 
vm_page_queue_print(queue_t q)568 vm_page_queue_print(queue_t q) {
569 	int cnt=0;
570 	vm_page_t mem;
571 
572 	mem = (vm_page_t) queue_first(q);
573 	while (!queue_end(q, (queue_entry_t) mem)) {
574 		vm_page_print(mem);
575 		mem = (vm_page_t) queue_next(&mem->pageq);
576 		if (++cnt > 10) {
577 			if(pg("hit return for more") != '\n')
578 				return;
579 			cnt = 0;
580 		}
581 	}
582 }
583 #endif
584