|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /*
26: * Mach Operating System
27: * Copyright (c) 1987 Carnegie-Mellon University
28: * All rights reserved. The CMU software License Agreement specifies
29: * the terms and conditions for use and redistribution.
30: */
31:
32: /*
33: * File: vnode_pager.c
34: *
35: * "Swap" pager that pages to/from vnodes. Also
36: * handles demand paging from files.
37: *
38: * 12-Mar-86 David Golub (dbg) at Carnegie-Mellon University
39: * Created.
40: */
41:
42: #import <mach_nbc.h>
43:
44: #import <mach/boolean.h>
45: #import <sys/param.h>
46: #import <sys/systm.h>
47: #import <kern/lock.h>
48: #import <sys/proc.h>
49: #import <sys/buf.h>
50: #import <sys/uio.h>
51: #import <sys/vnode.h>
52: #import <ufs/ufs/quota.h>
53: #import <ufs/ufs/inode.h>
54: #import <sys/namei.h>
55: #import <sys/mach_swapon.h>
56: #import <ufs/ffs/fs.h>
57: #import <sys/mount.h>
58: #import <net/if.h>
59: #import <netinet/in.h>
60: #import <nfs/rpcv2.h>
61: #import <nfs/nfsproto.h>
62: #import <nfs/nfs.h>
63: #undef fs_fsok
64: #undef fs_tsize
65: #undef fs_bsize
66: #undef fs_blocks
67: #undef fs_bfree
68: #undef fs_bavail
69:
70: #import <mach/mach_types.h>
71: #import <vm/vm_page.h>
72: #if defined(ppc)
73: #import <vm/pmap.h>
74: #endif /* ppc */
75: #import <vm/vm_map.h>
76: #import <vm/vm_kern.h>
77: #import <kern/parallel.h>
78: #import <kern/zalloc.h>
79: #import <kern/kalloc.h>
80:
81: #import <vm/vnode_pager.h>
82: #import <kern/mapfs.h>
83:
84: #import <kern/assert.h>
85:
86: extern struct vnodeops nfs_vnodeops;
87: extern struct vnodeops spec_vnodeops;
88:
89: #if NBBY == 8
90: #define BYTEMASK 0xff
91: #else NBBY
92: Define a byte mask for this machine.
93: #endif NBBY
94:
95:
96: #define PAGEMAP_THRESHOLD 64 /* Integral of sizeof(vm_offset_t) */
97: #define PAGEMAP_ENTRIES (PAGEMAP_THRESHOLD/sizeof(vm_offset_t))
98: #define PAGEMAP_SIZE(npgs) (npgs*sizeof(long))
99:
100: #define INDIR_PAGEMAP_ENTRIES(npgs) \
101: (((npgs-1)/PAGEMAP_ENTRIES) + 1)
102: #define INDIR_PAGEMAP_SIZE(npgs) \
103: (INDIR_PAGEMAP_ENTRIES(npgs) * sizeof(caddr_t))
104: #define INDIR_PAGEMAP(size) \
105: (PAGEMAP_SIZE(size) > PAGEMAP_THRESHOLD)
106:
107: #define RMAPSIZE(blocks) \
108: (howmany(blocks,NBBY))
109:
110: /*
111: * Sigh... with NFS/vnodes it is highly likely that we will need
112: * to allocate memory at page-out time, so use the XP hack to reserve
113: * pages and always use kalloc/zalloc instead of kget/zget.
114: * This must be fixed!!! FIXME - XXX.
115: */
116:
117: #define kget(size) kalloc_noblock(size)
118: #define zget(zone) zalloc_noblock(zone)
119:
120: /*
121: * Basic vnode pager data structures
122: */
123: zone_t vstruct_zone;
124: simple_lock_data_t vstruct_lock;
125:
126: static queue_head_t pager_files;
127: static int pager_file_count;
128: static pager_file_t pager_file_list[MAXPAGERFILES];
129:
130: static pf_entry seen_files[MAXPAGERFILES];
131: static int seen_files_max = 0;
132:
133:
134: /*
135: * Routine: vnode_pager_vput
136: * Function:
137: * Release one use of this vnode_pager_t
138: */
139: void
140: vnode_pager_vput(vs)
141: register vnode_pager_t vs;
142: {
143:
144: simple_lock(&vstruct_lock);
145: vs->vs_count--;
146: simple_unlock(&vstruct_lock);
147: }
148:
149: /*
150: * vnode_pager_vget:
151: *
152: * Return a vnode corresponding to the specified paging space
153: * and guarantee that it will remain in memory (until furthur action
154: * is taken).
155: *
156: * The vnode is returned unlocked.
157: */
158: struct vnode *
159: vnode_pager_vget(vs)
160: vnode_pager_t vs;
161: {
162: register struct vnode *vp;
163:
164: simple_lock(&vstruct_lock);
165: vs->vs_count++;
166: simple_unlock(&vstruct_lock);
167: vp = vs->vs_vp;
168: return(vp);
169: }
170:
171:
172: /*
173: * vnode_pager_allocpage - allocate a page in a paging file
174: */
175: daddr_t
176: vnode_pager_allocpage(pf)
177: register struct pager_file *pf;
178: {
179: int bp; /* byte counter */
180: int i; /* bit counter */
181: daddr_t page; /* page number */
182:
183: lock_write(&pf->pf_lock);
184:
185: if (pf->pf_pfree == 0) {
186: lock_done(&pf->pf_lock);
187: return(-1);
188: }
189:
190: /*
191: * Start at hint page and work up.
192: */
193: i = 0;
194: for (bp = pf->pf_hint / NBBY; bp < howmany(pf->pf_npgs, NBBY); bp++) {
195: if (*(pf->pf_bmap + bp) != BYTEMASK) {
196: for (i = 0; i < NBBY; i++) {
197: if (isclr((pf->pf_bmap + bp), i))
198: break;
199: }
200: break;
201: }
202: }
203: page = bp*NBBY+i;
204: if (page >= pf->pf_npgs) {
205: panic("vnode_pager_allocpage");
206: }
207: if (page > pf->pf_hipage) {
208: pf->pf_hipage = page;
209: }
210: setbit(pf->pf_bmap,page);
211: --pf->pf_pfree;
212: pf->pf_hint = page;
213:
214: lock_done(&pf->pf_lock);
215: return(page);
216: }
217:
218:
219: /*
220: * vnode_pager_findpage - find an available page in some paging file, using the
221: * argument as a preference. If the pager_file argument is NULL, any file will
222: * do. Return the designated page and file in entry.
223: */
224: kern_return_t
225: vnode_pager_findpage(struct pager_file *preferPf, pf_entry *entry)
226: {
227: daddr_t result;
228: pager_file_t pf;
229:
230: if (preferPf == PAGER_FILE_NULL) {
231: if (!queue_empty(&pager_files))
232: preferPf = (pager_file_t) queue_first(&pager_files);
233: else
234: return KERN_FAILURE;
235: }
236:
237: pf = preferPf;
238: do {
239: result = vnode_pager_allocpage(pf);
240: if (result != -1) {
241: entry->index = pf->pf_index;
242: entry->offset = result;
243: return KERN_SUCCESS;
244: }
245:
246: if (queue_end(&pager_files, &pf->pf_chain))
247: pf = (pager_file_t) queue_first(&pager_files);
248: else
249: pf = (pager_file_t) queue_next(&pf->pf_chain);
250:
251: } while (preferPf != pf);
252:
253: return KERN_FAILURE;
254: }
255:
256: static void
257: vnode_pager_deallocpage(pf_entry entry)
258: {
259: register struct pager_file *pf;
260: daddr_t page;
261:
262: if (entry.index == INDEX_NULL)
263: return;
264:
265: assert(entry.index <= pager_file_count);
266:
267: pf = pager_file_list[entry.index];
268: page = entry.offset;
269:
270: lock_write(&pf->pf_lock);
271:
272: if (page >= (daddr_t) pf->pf_npgs)
273: panic("vnode_pager_deallocpage");
274: if (page < pf->pf_hint)
275: pf->pf_hint = page;
276: clrbit(pf->pf_bmap, page);
277: ++pf->pf_pfree;
278:
279: lock_done(&pf->pf_lock);
280: }
281:
282:
283: /*
284: * pagerfile_pager_create
285: *
286: * Create an vstruct corresponding to the given pagerfile.
287: *
288: */
289: vnode_pager_t
290: pagerfile_pager_create(pf, size)
291: register pager_file_t pf;
292: vm_size_t size;
293: {
294: register vnode_pager_t vs;
295: register int i;
296:
297: /*
298: * XXX This can still livelock -- if the
299: * pageout daemon needs an vnode_pager record
300: * it won't get one until someone else
301: * refills the zone.
302: */
303:
304: vs = (struct vstruct *) zget(vstruct_zone);
305:
306: if (vs == VNODE_PAGER_NULL)
307: return(vs);
308:
309: vs->vs_size = atop(round_page(size));
310:
311: if (vs->vs_size == 0)
312: vs->vs_pmap = (pf_entry **) 0;
313: else {
314: if (INDIR_PAGEMAP(vs->vs_size)) {
315: vs->vs_pmap = (pf_entry **)
316: kget(INDIR_PAGEMAP_SIZE(vs->vs_size));
317: } else {
318: vs->vs_pmap = (pf_entry **)
319: kget(PAGEMAP_SIZE(vs->vs_size));
320: }
321: if (vs->vs_pmap == (pf_entry **) 0) {
322: /*
323: * We can't sleep here, so if there are no free pages, then
324: * just return nothing.
325: */
326: zfree(vstruct_zone, (vm_offset_t) vs);
327: return(VNODE_PAGER_NULL);
328: }
329:
330: if (INDIR_PAGEMAP(vs->vs_size)) {
331: bzero((caddr_t)vs->vs_pmap,
332: INDIR_PAGEMAP_SIZE(vs->vs_size));
333: } else {
334: for (i = 0; i < vs->vs_size; i++)
335: ((pf_entry *) &vs->vs_pmap[i])->index = INDEX_NULL;
336: }
337: }
338:
339: vs->is_device = FALSE;
340: vs->vs_count = 1;
341: vs->vs_vp = pf->pf_vp;
342: vs->vs_swapfile = TRUE;
343: vs->vs_pf = pf;
344: pf->pf_count++;
345:
346: vnode_pager_vput(vs);
347:
348: return(vs);
349: }
350:
351:
352: /*
353: * pagerfile_lookup
354: *
355: * Look for an entry at the specified offset in vstruct. If it's there,
356: * fill in the entry and return TRUE, otherwise return FALSE.
357: */
358: static boolean_t
359: pagerfile_lookup(struct vstruct *vs, vm_offset_t f_offset, pf_entry *entry)
360: {
361: vm_offset_t f_page = atop(f_offset);
362: int indirBlock;
363: int blockOffset;
364:
365: if (f_page >= vs->vs_size)
366: return FALSE;
367:
368: /*
369: * Now look for the entry in the map.
370: */
371: if (INDIR_PAGEMAP(vs->vs_size)) {
372: indirBlock = f_page / PAGEMAP_ENTRIES;
373: blockOffset = f_page % PAGEMAP_ENTRIES;
374:
375: if (vs->vs_pmap[indirBlock] == NULL ||
376: vs->vs_pmap[indirBlock][blockOffset].index == INDEX_NULL)
377: return FALSE;
378: else
379: *entry = vs->vs_pmap[indirBlock][blockOffset];
380:
381: } else { /* direct map */
382:
383: if (((pf_entry *) &vs->vs_pmap[f_page])->index == INDEX_NULL)
384: return FALSE;
385: else
386: *entry = *((pf_entry *) &vs->vs_pmap[f_page]);
387: }
388: return TRUE;
389: }
390:
391:
392: /*
393: * pagerfile_bmap
394: *
395: * Fill in the map entry (pager file, offset) for a given f_offset into an
396: * object backed this pager map.
397: *
398: * Returns: KERN_FAILURE if page not in map or no room left
399: */
400: static kern_return_t
401: pagerfile_bmap(vs, f_offset, flag, entry)
402: struct vstruct *vs;
403: vm_offset_t f_offset;
404: int flag;
405: pf_entry *entry;
406: {
407: vm_offset_t f_page = atop(f_offset);
408: boolean_t found;
409: int i;
410:
411: /*
412: * Check the map to see if we can find it. If we can't, then we'll
413: * make room below.
414: */
415: found = pagerfile_lookup(vs, f_offset, entry);
416:
417: if ((found) && (entry->index > pager_file_count))
418: panic("pagerfile_bmap: bad index %d", entry->index);
419:
420: if (flag == B_READ)
421: return found ? KERN_SUCCESS : KERN_FAILURE;
422: else if (found) {
423: /*
424: * Deallocate the page here, if the hint says that there's free
425: * space earlier in the file. We do this to keep the swapfile
426: * as small as possible, and to enable the swapfile compactor.
427: */
428: struct pager_file *pf;
429:
430: if (entry->index == 0)
431: panic("pagerfile_bmap: 0 index");
432:
433: pf = pager_file_list[entry->index];
434: if (pf->pf_hint < entry->offset)
435: vnode_pager_deallocpage(*entry);
436: else
437: return KERN_SUCCESS;
438: }
439:
440: /*
441: * If the object has grown, expand the page map.
442: */
443: if (f_page + 1 > vs->vs_size) {
444: pf_entry **new_pmap;
445: int new_size;
446:
447: new_size = f_page + 1;
448: assert(new_size > 0);
449:
450: if (INDIR_PAGEMAP(new_size)) { /* new map is indirect */
451:
452: if (vs->vs_size == 0) {
453: /*
454: * Nothing to copy, just get a new
455: * map and zero it.
456: */
457: new_pmap = (pf_entry **) kget(INDIR_PAGEMAP_SIZE(new_size));
458: if (new_pmap == NULL)
459: return (KERN_FAILURE);
460: bzero((caddr_t)new_pmap, INDIR_PAGEMAP_SIZE(new_size));
461: }
462: else if (INDIR_PAGEMAP(vs->vs_size)) {
463:
464: if (INDIR_PAGEMAP_SIZE(new_size) ==
465: INDIR_PAGEMAP_SIZE(vs->vs_size)) {
466: goto leavemapalone;
467: }
468:
469: /* Get a new indirect map */
470: new_pmap = (pf_entry **) kget(INDIR_PAGEMAP_SIZE(new_size));
471: if (new_pmap == NULL)
472: return KERN_FAILURE;
473:
474: bzero((caddr_t)new_pmap, INDIR_PAGEMAP_SIZE(new_size));
475:
476: /* Old map is indirect, copy the entries */
477: for (i = 0; i < INDIR_PAGEMAP_ENTRIES(vs->vs_size); i++)
478: new_pmap[i] = vs->vs_pmap[i];
479:
480: /* And free the old map */
481: kfree(vs->vs_pmap, INDIR_PAGEMAP_SIZE(vs->vs_size));
482:
483: } else { /* old map was direct, new map is indirect */
484:
485: /* Get a new indirect map */
486: new_pmap = (pf_entry **) kget(INDIR_PAGEMAP_SIZE(new_size));
487: if (new_pmap == NULL)
488: return KERN_FAILURE;
489:
490: bzero((caddr_t)new_pmap, INDIR_PAGEMAP_SIZE(new_size));
491:
492: /*
493: * Old map is direct, move it to the first indirect block.
494: */
495: new_pmap[0] = (pf_entry *) kget(PAGEMAP_THRESHOLD);
496: if (new_pmap[0] == NULL) {
497: kfree(new_pmap, INDIR_PAGEMAP_SIZE(new_size));
498: return KERN_FAILURE;
499: }
500: for (i = 0; i < vs->vs_size; i++)
501: new_pmap[0][i] = *((pf_entry *) &vs->vs_pmap[i]);
502:
503: /* Initialize the remainder of the block */
504: for (i = vs->vs_size; i < PAGEMAP_ENTRIES; i++)
505: new_pmap[0][i].index = INDEX_NULL;
506:
507: /* And free the old map */
508: kfree(vs->vs_pmap, PAGEMAP_SIZE(vs->vs_size));
509: }
510:
511: } else { /* The new map is a direct one */
512:
513: new_pmap = (pf_entry **) kget(PAGEMAP_SIZE(new_size));
514: if (new_pmap == NULL)
515: return KERN_FAILURE;
516:
517: /* Copy info from the old map */
518: for (i = 0; i < vs->vs_size; i++)
519: new_pmap[i] = vs->vs_pmap[i];
520:
521: /* Initialize the rest of the new map */
522: for (i = vs->vs_size; i < new_size; i++)
523: ((pf_entry *) &new_pmap[i])->index = INDEX_NULL;
524:
525: if (vs->vs_size > 0)
526: kfree(vs->vs_pmap, PAGEMAP_SIZE(vs->vs_size));
527: }
528:
529: vs->vs_pmap = new_pmap;
530: leavemapalone:
531: vs->vs_size = new_size;
532: }
533:
534: /*
535: * Now allocate the spot for the new page.
536: */
537: if (INDIR_PAGEMAP(vs->vs_size)) {
538:
539: int indirBlock = f_page / PAGEMAP_ENTRIES; /* the indirect block */
540: int blockOffset = f_page % PAGEMAP_ENTRIES; /* offset into block */
541:
542: /*
543: * In an indirect map, we may need to allocate space for the
544: * indirect block itself.
545: */
546: if (vs->vs_pmap[indirBlock] == NULL) {
547: vs->vs_pmap[indirBlock]=(pf_entry *) kget(PAGEMAP_THRESHOLD);
548:
549: if (vs->vs_pmap[indirBlock] == NULL)
550: return KERN_FAILURE;
551:
552: for (i = 0; i < PAGEMAP_ENTRIES; i++)
553: vs->vs_pmap[indirBlock][i].index = INDEX_NULL;
554: }
555:
556: if (vnode_pager_findpage(vs->vs_pf, entry) == KERN_FAILURE)
557: return KERN_FAILURE;
558: else
559: vs->vs_pmap[indirBlock][blockOffset] = *entry;
560:
561: } else { /* direct map */
562:
563: if (vnode_pager_findpage(vs->vs_pf, entry) == KERN_FAILURE)
564: return KERN_FAILURE;
565:
566: *(pf_entry *) &vs->vs_pmap[f_page] = *entry;
567: }
568:
569: return KERN_SUCCESS;
570: }
571:
572: /*
573: * vnode_pager_create
574: *
575: * Create an vstruct corresponding to the given vp.
576: *
577: */
578: vnode_pager_t
579: vnode_pager_create(vp)
580: register struct vnode *vp;
581: {
582: vnode_pager_t vs;
583: struct vattr vattr;
584: vm_size_t size;
585: struct proc *p = current_proc();
586: int error;
587:
588: /*
589: * XXX This can still livelock -- if the
590: * pageout daemon needs a vnode_pager record
591: * it won't get one until someone else
592: * refills the zone.
593: */
594:
595: vs = (struct vstruct *) zalloc(vstruct_zone);
596:
597: if (vs == VNODE_PAGER_NULL)
598: return(vs);
599:
600: bzero((caddr_t)vs, sizeof(struct vstruct));
601:
602: vs->is_device = FALSE;
603: vs->vs_count = 1;
604: vp->v_vm_info->pager = (vm_pager_t) vs;
605: vs->vs_vp = vp;
606: vs->vs_swapfile = FALSE;
607:
608: error = VOP_GETATTR(vp, &vattr, p->p_ucred, p);
609: if (!error) {
610: size = vattr.va_size;
611: vp->v_vm_info->vnode_size = size;
612: }
613: else
614: vp->v_vm_info->vnode_size = 0;
615:
616: VREF(vp);
617:
618: vnode_pager_vput(vs);
619:
620: return(vs);
621: }
622:
623: /*
624: * vnode_pager_setup
625: *
626: * Set up a vstruct for a given vnode. This is an exported routine.
627: */
628: vm_pager_t
629: vnode_pager_setup(vp, is_text, can_cache)
630: struct vnode *vp;
631: boolean_t is_text;
632: boolean_t can_cache;
633: {
634: register pager_file_t pf;
635:
636: unix_master();
637:
638: if (is_text)
639: vp->v_flag |= VTEXT;
640: #if !MACH_NBC
641: if(!vp->v_vm_info)
642: vm_info_init(vp);
643: #endif /* MACH_NBC */
644:
645: if (vp->v_vm_info->pager == vm_pager_null) {
646: /*
647: * Check to make sure this isn't in use as a pager file.
648: */
649: for (pf = (pager_file_t) queue_first(&pager_files);
650: !queue_end(&pager_files, &pf->pf_chain);
651: pf = (pager_file_t) queue_next(&pf->pf_chain)) {
652: if (pf->pf_vp == vp) {
653: return(vm_pager_null);
654: }
655: }
656: (void) vnode_pager_create(vp);
657: if (can_cache)
658: vm_object_cache_object(
659: vm_object_lookup(vp->v_vm_info->pager), TRUE);
660: }
661:
662: unix_release();
663: /*
664: * Try to keep something in the vstruct zone since we can sleep
665: * here if necessary.
666: */
667: zfree(vstruct_zone, zalloc(vstruct_zone));
668: return(vp->v_vm_info->pager);
669: }
670:
671: #ifdef i386
672:
673: static __inline__ vm_offset_t
674: vnode_pageio_setup(
675: vm_page_t m
676: )
677: {
678: return (VM_PAGE_TO_PHYS(m));
679: }
680:
681: static __inline__ void
682: vnode_pageio_complete(
683: vm_page_t m,
684: vm_offset_t addr
685: )
686: {
687: /* do nothing */
688: }
689:
690: #else /* notdef i386 */
691:
692: static vm_offset_t
693: vnode_pageio_setup(
694: vm_page_t m
695: )
696: {
697: kern_return_t result;
698: vm_offset_t addr = vm_map_min(kernel_map);
699:
700: result = vm_map_find(kernel_map, VM_OBJECT_NULL, 0,
701: &addr, PAGE_SIZE, TRUE);
702: if (result != KERN_SUCCESS)
703: return (0);
704:
705: pmap_enter_phys_page(VM_PAGE_TO_PHYS(m), addr);
706:
707: return (addr);
708: }
709:
710: static void
711: vnode_pageio_complete(
712: vm_page_t m,
713: vm_offset_t addr
714: )
715: {
716: vm_offset_t taddr = trunc_page(addr);
717:
718: (void) vm_map_remove(kernel_map, taddr, taddr + PAGE_SIZE);
719: }
720:
721: #endif /* notdef i386 */
722:
723: pager_return_t
724: vnode_pagein(
725: vm_page_t m,
726: int *errorp
727: )
728: {
729: struct vnode *vp;
730: vnode_pager_t vs;
731: pager_return_t result = PAGER_SUCCESS;
732: vm_offset_t f_offset;
733: pf_entry entry;
734: struct proc *p = current_proc();
735: int error = 0;
736:
737: unix_master();
738:
739: vs = (vnode_pager_t) m->object->pager;
740: vp = vnode_pager_vget(vs);
741: f_offset = m->offset + m->object->paging_offset;
742:
743: if (vs->vs_swapfile) {
744: if (pagerfile_bmap(vs, f_offset, B_READ, &entry) == KERN_FAILURE)
745: result = PAGER_ABSENT;
746: else {
747: f_offset = ptoa(entry.offset);
748: vp = pager_file_list[entry.index]->pf_vp;
749: }
750: }
751:
752: if (result != PAGER_ABSENT) {
753: vm_offset_t ioaddr = vnode_pageio_setup(m);
754: struct uio auio;
755: struct iovec aiov;
756:
757: if (ioaddr) {
758: auio.uio_iov = &aiov;
759: auio.uio_iovcnt = 1;
760: auio.uio_offset = f_offset;
761: auio.uio_segflg = UIO_SYSSPACE;
762: auio.uio_rw = UIO_READ;
763: auio.uio_resid = PAGE_SIZE;
764: auio.uio_procp = NULL;
765:
766: aiov.iov_len = PAGE_SIZE;
767: aiov.iov_base = (caddr_t)ioaddr;
768:
769: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE, p);
770:
771: m->nfspagereq=TRUE;
772: error = VOP_PAGEIN(vp, &auio, 0, p->p_ucred);
773: m->nfspagereq=FALSE;
774: if (error)
775: result = PAGER_ERROR;
776: vp->v_vm_info->error = error;
777:
778: VOP_UNLOCK(vp, 0, p);
779:
780: if (!error && auio.uio_resid > 0)
781: (void) memset((void *)(ioaddr + PAGE_SIZE -
782: auio.uio_resid), 0, auio.uio_resid);
783:
784: vnode_pageio_complete(m, ioaddr);
785: #ifdef ppc
786: /*
787: * After a pagein, we must synchronize the processor caches.
788: * On PPC, the i-cache is not coherent in all models, thus
789: * it needs to be invalidated.
790: */
791: flush_cache(VM_PAGE_TO_PHYS(m), PAGE_SIZE);
792: #endif /* ppc */
793: }
794: else
795: result = PAGER_ERROR;
796: }
797:
798: vnode_pager_vput(vs);
799:
800: if (errorp)
801: *errorp = error;
802:
803: unix_release();
804:
805: return (result);
806: }
807:
808: pager_return_t
809: vnode_pageout(
810: vm_page_t m
811: )
812: {
813: struct vnode *vp;
814: vnode_pager_t vs;
815: pager_return_t result = PAGER_SUCCESS;
816: vm_offset_t f_offset;
817: vm_size_t size = PAGE_SIZE;
818: pf_entry entry;
819: struct proc *p = current_proc();
820: int error = 0;
821:
822: unix_master();
823:
824: vs = (vnode_pager_t) m->object->pager;
825: vp = vnode_pager_vget(vs);
826: f_offset = m->offset + m->object->paging_offset;
827:
828: #if MACH_NBC
829: if (!vs->vs_swapfile) {
830: /*
831: * Be sure that a paging operation doesn't
832: * accidently extend the size of "mapped" file.
833: *
834: * However, we do extend the size up to the current
835: * size kept in the vm_info structure.
836: */
837: if (f_offset + size > vp->v_vm_info->vnode_size) {
838: if (f_offset > vp->v_vm_info->vnode_size)
839: size = 0;
840: else
841: size = vp->v_vm_info->vnode_size - f_offset;
842: }
843: }
844: #endif MACH_NBC
845:
846: if (vs->vs_swapfile) {
847: if (pagerfile_bmap(vs, f_offset, B_WRITE, &entry) == KERN_FAILURE)
848: result = PAGER_ERROR;
849: else {
850: /*
851: * If the paging operation extends the size of the
852: * pagerfile, update the information in the vm_info
853: * structure
854: */
855: f_offset = ptoa(entry.offset);
856: vp = pager_file_list[entry.index]->pf_vp;
857: if (f_offset + size > vp->v_vm_info->vnode_size)
858: vp->v_vm_info->vnode_size = f_offset + size;
859: }
860: }
861:
862: if (result != PAGER_ERROR && size > 0) {
863: vm_offset_t ioaddr = vnode_pageio_setup(m);
864: struct uio auio;
865: struct iovec aiov;
866:
867: if (ioaddr) {
868: auio.uio_iov = &aiov;
869: auio.uio_iovcnt = 1;
870: auio.uio_offset = f_offset;
871: auio.uio_segflg = UIO_SYSSPACE;
872: auio.uio_rw = UIO_WRITE;
873: auio.uio_procp = NULL;
874: aiov.iov_base = (caddr_t)ioaddr;
875: #if MACH_NBC
876: auio.uio_resid = size;
877: aiov.iov_len = size;
878: #else
879: auio.uio_resid = PAGE_SIZE;
880: aiov.iov_len = PAGE_SIZE;
881: #endif /* MACH_NBC */
882:
883: #if MACH_NBC
884: {
885: #define VNODE_LOCK_RETRY_COUNT 1
886: #define VNODE_LOCK_RETRY_TICKS 2
887:
888: int retry = VNODE_LOCK_RETRY_COUNT;
889: vnode_lock_retry:
890: error = vn_lock(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_CANRECURSE, p);
891: if (error) {
892: /*
893: * Retry the lock after yielding to other threads.
894: * Not doing this makes the pageout thread compute bound.
895: * Yielding lets I/O threads to run
896: * and make forward progress.
897: */
898: if (retry-- > 0) {
899: thread_will_wait(current_thread());
900: thread_set_timeout(VNODE_LOCK_RETRY_TICKS);
901: thread_block();
902: goto vnode_lock_retry;
903: }
904:
905: result = PAGER_ERROR;
906: vnode_pageio_complete(m, ioaddr);
907: goto out;
908: }
909: }
910: #else /* MACH_NBC */
911: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
912: #endif /* MACH_NBC */
913:
914: error = VOP_PAGEOUT(vp, &auio, 0, p->p_ucred);
915: if (error)
916: result = PAGER_ERROR;
917: vp->v_vm_info->error = error;
918:
919: VOP_UNLOCK(vp, 0, p);
920:
921: vnode_pageio_complete(m, ioaddr);
922: }
923: else
924: result = PAGER_ERROR;
925: }
926:
927: if (result == PAGER_SUCCESS) {
928: m->clean = TRUE; /* XXX - wrong place */
929: pmap_clear_modify(VM_PAGE_TO_PHYS(m)); /* XXX - wrong place */
930: }
931:
932: out:
933: vnode_pager_vput(vs);
934:
935: unix_release();
936:
937: return (result);
938: }
939:
940: /*
941: * vnode_has_page:
942: *
943: * Parameters:
944: * pager
945: * id paging object
946: * offset Offset in paging object to test
947: *
948: * Assumptions:
949: * This is only used on shadowing (copy) objects.
950: * Since copy objects are always backed by a swapfile, we just
951: * test the map for that swapfile to see if the page is present.
952: */
953: boolean_t
954: vnode_has_page(pager, offset)
955: vm_pager_t pager;
956: vm_offset_t offset;
957: {
958: vnode_pager_t vs = (vnode_pager_t) pager;
959: pf_entry entry;
960:
961: /*
962: * For now, we do all inode hacking on the master cpu.
963: */
964: unix_master();
965:
966: if (vs == VNODE_PAGER_NULL)
967: panic("vnode_has_page: failed lookup");
968:
969: if (vs->vs_swapfile) {
970: unix_release();
971: if (pagerfile_bmap(vs, offset, B_READ, &entry) == KERN_FAILURE)
972: return FALSE;
973: else
974: return TRUE;
975: }
976: else {
977: panic("vnode_has_page called on non-default pager");
978: }
979: /*NOTREACHED*/
980:
981: return FALSE;
982: }
983:
984:
985: /*
986: * Routine: vnode_pager_file_init
987: * Function:
988: * Create a pager_file structure for a new pager file.
989: * Arguments:
990: * The file in question is specified by vnode pointer.
991: * lowat and hiwat are the low water and high water marks
992: * that the size of pager file will float between. If
993: * the low water mark is zero, then the file will not
994: * shrink after paging space is freed. If the high water
995: * mark is zero, the file will grow without bounds.
996: *
997: * The vp is locked on entry to and exit from this function.
998: */
999: int
1000: vnode_pager_file_init(pfp, vp, lowat, hiwat)
1001: pager_file_t *pfp;
1002: struct vnode *vp;
1003: long lowat;
1004: long hiwat;
1005: {
1006: struct vattr vattr;
1007: register pager_file_t pf;
1008: int error;
1009: long i;
1010: struct proc *p = current_proc();
1011: struct ucred *cred;
1012: vm_size_t size;
1013:
1014: *pfp = PAGER_FILE_NULL;
1015:
1016: /*
1017: * Make sure no other object paging to this file?
1018: */
1019: #if MACH_NBC
1020: mapfs_uncache(vp);
1021: #endif /* MACH_NBC */
1022:
1023: if(!vp->v_vm_info) {
1024: vm_info_init(vp);
1025: }
1026:
1027: if (vp->v_vm_info->mapped) {
1028: return(EBUSY);
1029: }
1030:
1031: /*
1032: * Clean up the file blocks on a pager file by
1033: * truncating to length "lowat".
1034: */
1035: error = VOP_GETATTR(vp, &vattr, p->p_ucred, p);
1036: size = vattr.va_size;
1037: if (size > lowat) {
1038: vattr_null(&vattr);
1039: vattr.va_size = size = lowat;
1040: error = VOP_SETATTR(vp, &vattr, p->p_ucred, p);
1041: if (error) {
1042: return(error);
1043: }
1044: }
1045:
1046: /*
1047: * Initialize the vnode_size field
1048: */
1049: vp->v_vm_info->vnode_size = size;
1050:
1051: pf = (pager_file_t) kalloc(sizeof(struct pager_file));
1052: VREF(vp);
1053: pf->pf_vp = vp;
1054: cred = p->p_ucred;
1055: crhold(cred);
1056: vp->v_vm_info->cred = cred;
1057: pf->pf_count = 0;
1058: pf->pf_hint = 0;
1059: pf->pf_lowat = atop(round_page(lowat));
1060: /*
1061: * If no maximum space is specified, then we should make a map that
1062: * can cover the entire disk, otherwise the block map need only
1063: * cover the maximum space allowed.
1064: */
1065: if (!hiwat)
1066: hiwat = vp->v_mount->mnt_stat.f_blocks *
1067: vp->v_mount->mnt_stat.f_bsize;
1068: pf->pf_pfree = pf->pf_npgs = atop(hiwat);
1069: pf->pf_bmap = (u_char *) kalloc(RMAPSIZE(pf->pf_npgs));
1070: for (i = 0; i < pf->pf_npgs; i++) {
1071: clrbit(pf->pf_bmap, i);
1072: }
1073: pf->pf_hipage = -1;
1074: pf->pf_prefer = FALSE;
1075: lock_init(&pf->pf_lock, TRUE);
1076:
1077: /*
1078: * Put the new pager file in the list.
1079: */
1080: queue_enter(&pager_files, pf, pager_file_t, pf_chain);
1081: pager_file_count++;
1082: pf->pf_index = pager_file_count;
1083: pager_file_list[pager_file_count] = pf;
1084: *pfp = pf;
1085: return (0);
1086: }
1087:
1088: void
1089: vnode_pager_shutdown()
1090: {
1091: pager_file_t pf;
1092:
1093: while (!queue_empty(&pager_files)) {
1094: pf = (pager_file_t) queue_first(&pager_files);
1095: vrele(pf->pf_vp);
1096: queue_remove(&pager_files, pf, pager_file_t, pf_chain);
1097: pager_file_count--;
1098: }
1099: }
1100:
1101:
1102: /*
1103: * Routine: mach_swapon
1104: * Function:
1105: * Syscall interface to mach_swapon.
1106: */
1107: int
1108: mach_swapon(filename, flags, lowat, hiwat)
1109: char *filename;
1110: int flags;
1111: long lowat;
1112: long hiwat;
1113: {
1114: struct vnode *vp;
1115: struct nameidata nd, *ndp;
1116: struct proc *p = current_proc();
1117: pager_file_t pf;
1118: register int error;
1119:
1120: ndp = &nd;
1121:
1122: if ((error = suser(p->p_ucred, &p->p_acflag)))
1123: return (error);
1124:
1125: unix_master();
1126:
1127: /*
1128: * Get a vnode for the paging area.
1129: */
1130: NDINIT(ndp, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE,
1131: filename, p);
1132: if ((error = namei(ndp)))
1133: return (error);
1134: vp = ndp->ni_vp;
1135:
1136: if (vp->v_type != VREG) {
1137: error = EINVAL;
1138: goto bailout;
1139: }
1140:
1141: /*
1142: * Look to see if we are already paging to this file.
1143: */
1144: for (pf = (pager_file_t) queue_first(&pager_files);
1145: !queue_end(&pager_files, &pf->pf_chain);
1146: pf = (pager_file_t) queue_next(&pf->pf_chain)) {
1147: if (pf->pf_vp == vp)
1148: break;
1149: }
1150: if (!queue_end(&pager_files, &pf->pf_chain)) {
1151: error = EBUSY;
1152: goto bailout;
1153: }
1154:
1155: error = vnode_pager_file_init(&pf, vp, lowat, hiwat);
1156: if (error) {
1157: goto bailout;
1158: }
1159: pf->pf_prefer = ((flags & MS_PREFER) != 0);
1160:
1161: /*
1162: * Create dummy symbol file for current mach_kernel executable.
1163: * See bsd/kern/kern_symfile.c
1164: */
1165: output_kernel_symbols(p);
1166:
1167: error = 0;
1168: bailout:
1169: if (vp) {
1170: VOP_UNLOCK(vp, 0, p);
1171: vrele(vp);
1172: }
1173: unix_release();
1174: return(error);
1175: }
1176:
1177: /*
1178: * Routine: vswap_allocate
1179: * Function:
1180: * Allocate a place for paging out a kernel-created
1181: * memory object.
1182: *
1183: * Implementation:
1184: * Looks through the paging files for the one with the
1185: * most free space. First, only "preferred" paging files
1186: * are considered, then local paging files, and then
1187: * remote paging files. In each case, the pager file
1188: * the most free blocks will be chosen.
1189: *
1190: * In/out conditions:
1191: * If the paging area is on a local disk, the inode is
1192: * returned locked.
1193: */
1194: pager_file_t
1195: vswap_allocate()
1196: {
1197: int pass;
1198: int mostspace;
1199: pager_file_t pf, mostpf;
1200:
1201: extern int (**ffs_vnodeop_p)();
1202:
1203: mostpf = PAGER_FILE_NULL;
1204: mostspace = 0;
1205:
1206: if (pager_file_count > 1) {
1207: for (pass = 0; pass < 4; pass++) {
1208: for (pf = (pager_file_t)queue_first(&pager_files);
1209: !queue_end(&pager_files, &pf->pf_chain);
1210: pf = (pager_file_t)queue_next(&pf->pf_chain)) {
1211:
1212: if ((pass < 2) && !pf->pf_prefer)
1213: continue;
1214: if ((!(pass &1) &&
1215: (pf->pf_vp->v_op != ffs_vnodeop_p)))
1216: continue;
1217:
1218: if (pf->pf_pfree > mostspace) {
1219: mostspace = pf->pf_pfree;
1220: mostpf = pf;
1221: }
1222: }
1223: /*
1224: * If we found space, then break out of loop.
1225: */
1226: if (mostpf != PAGER_FILE_NULL)
1227: break;
1228: }
1229: } else if (pager_file_count == 1) {
1230: mostpf = (pager_file_t) queue_first(&pager_files);
1231: }
1232:
1233: return(mostpf);
1234: }
1235:
1236: vm_pager_t
1237: vnode_alloc(size)
1238: vm_size_t size;
1239: {
1240: pager_file_t pf;
1241: vnode_pager_t vs = (vnode_pager_t) vm_pager_null;
1242:
1243: #ifdef lint
1244: size++;
1245: #endif lint
1246:
1247: unix_master();
1248:
1249: /*
1250: * Get a pager_file, then turn it into a paging space.
1251: */
1252:
1253: if ((pf = vswap_allocate()) == PAGER_FILE_NULL) {
1254: goto out;
1255: }
1256: if ((vs = pagerfile_pager_create(pf, size)) ==
1257: VNODE_PAGER_NULL) {
1258: vs = (vnode_pager_t) vm_pager_null;
1259: goto out;
1260: }
1261: out:
1262: unix_release();
1263: return((vm_pager_t) vs);
1264: }
1265:
1266:
1267: /*
1268: * Try to truncate the paging files.
1269: */
1270: void
1271: vnode_pager_truncate(pf_entry entry)
1272: {
1273: struct pager_file *pf = pager_file_list[entry.index];
1274: struct vnode *vp = pf->pf_vp;
1275: struct vattr vattr;
1276: int error;
1277: struct proc *p = current_proc();
1278: long truncpage;
1279: int i;
1280:
1281: /*
1282: * If this is not the last page in the file, return now.
1283: * If the swaptimizer is enabled we cannot free blocks out from
1284: * underneath it.
1285: */
1286: assert(entry.offset <= pf->pf_hipage);
1287: if (entry.offset < pf->pf_hipage)
1288: return;
1289:
1290: lock_write(&pf->pf_lock);
1291:
1292: /*
1293: * Find a new high page
1294: */
1295: for (i = entry.offset - 1; i >= 0; i--) {
1296: if (isset(pf->pf_bmap, i)) {
1297: pf->pf_hipage = i;
1298: break;
1299: }
1300: }
1301:
1302: /*
1303: * If we are higher than the low water mark, truncate
1304: * the file.
1305: */
1306: truncpage = pf->pf_hipage + 1;
1307: if (pf->pf_lowat == 0 || truncpage <= pf->pf_lowat ||
1308: vp->v_vm_info->vnode_size < ptoa(truncpage)) {
1309: lock_done(&pf->pf_lock);
1310: return;
1311: }
1312:
1313: vattr_null(&vattr);
1314: vattr.va_size = ptoa(truncpage);
1315: ASSERT( (int) vattr.va_size >= 0 );
1316:
1317: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1318: if ((error = VOP_SETATTR(vp, &vattr, vp->v_vm_info->cred, p))) {
1319: printf("vnode_deallocpage: error truncating %s,"
1320: " error = %d\n", pf->pf_name, error);
1321: }
1322: VOP_UNLOCK(vp, 0, p);
1323:
1324:
1325: lock_done(&pf->pf_lock);
1326: }
1327:
1328:
1329: static void
1330: visit_file(pf_entry entry)
1331: {
1332: int i;
1333:
1334: if (entry.index == INDEX_NULL)
1335: return;
1336:
1337: for (i = 0; i < seen_files_max; i++)
1338: if (seen_files[i].index == entry.index) {
1339: seen_files[i].offset = max(seen_files[i].offset,entry.offset);
1340: return;
1341: }
1342:
1343: seen_files[seen_files_max++] = entry;
1344: }
1345:
1346:
1347: void
1348: vnode_dealloc(pager)
1349: vm_pager_t pager;
1350: {
1351: struct vnode *vp;
1352: vnode_pager_t vs = (vnode_pager_t) pager;
1353: int i;
1354:
1355: unix_master();
1356:
1357: vp = vnode_pager_vget(vs);
1358:
1359: ASSERT(vs->vs_count == 1);
1360:
1361: seen_files_max = 0;
1362: if (vs->vs_swapfile) {
1363: pager_file_t pf;
1364: int i,j;
1365:
1366: ASSERT(vs->vs_pf);
1367:
1368: pf = vs->vs_pf;
1369: if (INDIR_PAGEMAP(vs->vs_size)) {
1370: for (i = 0; i < INDIR_PAGEMAP_ENTRIES(vs->vs_size); i++) {
1371: if (vs->vs_pmap[i] != NULL) {
1372: for(j = 0; j < PAGEMAP_ENTRIES; j++) {
1373: vnode_pager_deallocpage(vs->vs_pmap[i][j]);
1374: visit_file(vs->vs_pmap[i][j]);
1375: }
1376: kfree(vs->vs_pmap[i], PAGEMAP_THRESHOLD);
1377: }
1378: }
1379: kfree(vs->vs_pmap, INDIR_PAGEMAP_SIZE(vs->vs_size));
1380: } else {
1381: for (i = 0; i < vs->vs_size; i++) {
1382: vnode_pager_deallocpage(*(pf_entry *)&vs->vs_pmap[i]);
1383: visit_file(*(pf_entry *)&vs->vs_pmap[i]);
1384: }
1385: if (vs->vs_size > 0)
1386: kfree(vs->vs_pmap, PAGEMAP_SIZE(vs->vs_size));
1387: }
1388: pf->pf_count--;
1389: } else {
1390: vp->v_flag &= ~VTEXT;
1391: vp->v_vm_info->pager = vm_pager_null; /* so vrele will free */
1392:
1393: vp->v_flag |= VAGE; /* put this vnode at the head of freelist */
1394: vrele(vp);
1395: }
1396:
1397: for (i=0; i < seen_files_max; i++)
1398: vnode_pager_truncate(seen_files[i]);
1399: zfree(vstruct_zone, (vm_offset_t) vs);
1400: unix_release();
1401: }
1402:
1403: /*
1404: * Remove vnode associated object from the object cache.
1405: *
1406: * XXX unlock the vnode if it is currently locked.
1407: * We must do this since uncaching the object may result in its
1408: * destruction which may initiate paging activity which may necessitate
1409: * re-locking the vnode.
1410: */
1411: int
1412: vnode_uncache(vp)
1413: register struct vnode *vp;
1414: {
1415: struct proc *p = current_proc();
1416:
1417: if (vp->v_type != VREG)
1418: return (1);
1419:
1420: if (vp->v_vm_info == 0 || vp->v_vm_info->pager == vm_pager_null)
1421: return (1);
1422:
1423: #ifdef DEBUG
1424: if (!VOP_ISLOCKED(vp)) {
1425: extern int (**nfsv2_vnodeop_p)();
1426:
1427: if (vp->v_op != nfsv2_vnodeop_p)
1428: panic("vnode_uncache: vnode not locked!");
1429: }
1430: #endif
1431:
1432: /*
1433: * The act of uncaching may cause an object to be deallocated
1434: * which may need to wait for the pageout daemon which in turn
1435: * may be waiting for this inode's lock, so be sure to unlock
1436: * and relock later if necessary. (This of course means that
1437: * code calling this routine must be able to handle the fact
1438: * that the inode has been unlocked temporarily). This code, of
1439: * course depends on the Unix master restriction for proper
1440: * synchronization.
1441: */
1442: #if MACH_NBC
1443: mapfs_uncache(vp);
1444: #endif /* MACH_NBC */
1445:
1446: VOP_UNLOCK(vp, 0, p);
1447: vm_object_uncache(vp->v_vm_info->pager);
1448: #if MACH_NBC
1449: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY | LK_CANRECURSE, p);
1450: #else
1451: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1452: #endif
1453: return (1);
1454: }
1455:
1456: void
1457: vnode_pager_init()
1458: {
1459: register vm_size_t size;
1460:
1461: /*
1462: * Initialize zone of paging structures.
1463: */
1464:
1465: size = (vm_size_t) sizeof(struct vstruct);
1466: vstruct_zone = zinit(size,
1467: (vm_size_t) 10000*size, /* XXX */
1468: PAGE_SIZE,
1469: FALSE, "vnode pager structures");
1470: simple_lock_init(&vstruct_lock);
1471: queue_init(&pager_files);
1472: }
1473:
1474: void
1475: vnode_pager_setsize(vp, nsize)
1476: struct vnode *vp;
1477: u_long nsize;
1478: {
1479: if (vp->v_vm_info) {
1480: if (vp->v_type != VREG)
1481: panic("vnode_pager_setsize not VREG");
1482: vp->v_vm_info->vnode_size = nsize;
1483: }
1484: }
1485:
1486: void
1487: vnode_pager_umount(mp)
1488: register struct mount *mp;
1489: {
1490: struct proc *p = current_proc();
1491: struct vnode *vp, *nvp;
1492:
1493: loop:
1494: for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) {
1495: if (vp->v_mount != mp)
1496: goto loop;
1497: nvp = vp->v_mntvnodes.le_next;
1498: vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, p);
1499: (void) vnode_uncache(vp);
1500: VOP_UNLOCK(vp, 0, p);
1501: }
1502: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.