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