xref: /onnv-gate/usr/src/uts/common/vm/vm_pvn.c (revision 2999)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*2999Sstans  * Common Development and Distribution License (the "License").
6*2999Sstans  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*2999Sstans  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
310Sstevel@tonic-gate  * The Regents of the University of California
320Sstevel@tonic-gate  * All Rights Reserved
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
350Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
360Sstevel@tonic-gate  * contributors.
370Sstevel@tonic-gate  */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
400Sstevel@tonic-gate 
410Sstevel@tonic-gate /*
420Sstevel@tonic-gate  * VM - paged vnode.
430Sstevel@tonic-gate  *
440Sstevel@tonic-gate  * This file supplies vm support for the vnode operations that deal with pages.
450Sstevel@tonic-gate  */
460Sstevel@tonic-gate #include <sys/types.h>
470Sstevel@tonic-gate #include <sys/t_lock.h>
480Sstevel@tonic-gate #include <sys/param.h>
490Sstevel@tonic-gate #include <sys/sysmacros.h>
500Sstevel@tonic-gate #include <sys/systm.h>
510Sstevel@tonic-gate #include <sys/time.h>
520Sstevel@tonic-gate #include <sys/buf.h>
530Sstevel@tonic-gate #include <sys/vnode.h>
540Sstevel@tonic-gate #include <sys/uio.h>
550Sstevel@tonic-gate #include <sys/vmmeter.h>
560Sstevel@tonic-gate #include <sys/vmsystm.h>
570Sstevel@tonic-gate #include <sys/mman.h>
580Sstevel@tonic-gate #include <sys/vfs.h>
590Sstevel@tonic-gate #include <sys/cred.h>
600Sstevel@tonic-gate #include <sys/user.h>
610Sstevel@tonic-gate #include <sys/kmem.h>
620Sstevel@tonic-gate #include <sys/cmn_err.h>
630Sstevel@tonic-gate #include <sys/debug.h>
640Sstevel@tonic-gate #include <sys/cpuvar.h>
650Sstevel@tonic-gate #include <sys/vtrace.h>
660Sstevel@tonic-gate #include <sys/tnf_probe.h>
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #include <vm/hat.h>
690Sstevel@tonic-gate #include <vm/as.h>
700Sstevel@tonic-gate #include <vm/seg.h>
710Sstevel@tonic-gate #include <vm/rm.h>
720Sstevel@tonic-gate #include <vm/pvn.h>
730Sstevel@tonic-gate #include <vm/page.h>
740Sstevel@tonic-gate #include <vm/seg_map.h>
750Sstevel@tonic-gate #include <vm/seg_kmem.h>
760Sstevel@tonic-gate #include <sys/fs/swapnode.h>
770Sstevel@tonic-gate 
780Sstevel@tonic-gate int pvn_nofodklust = 0;
790Sstevel@tonic-gate int pvn_write_noklust = 0;
800Sstevel@tonic-gate 
810Sstevel@tonic-gate uint_t pvn_vmodsort_supported = 0;	/* set if HAT supports VMODSORT */
820Sstevel@tonic-gate uint_t pvn_vmodsort_disable = 0;	/* set in /etc/system to disable HAT */
830Sstevel@tonic-gate 					/* support for vmodsort for testing */
840Sstevel@tonic-gate 
850Sstevel@tonic-gate static struct kmem_cache *marker_cache = NULL;
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /*
880Sstevel@tonic-gate  * Find the largest contiguous block which contains `addr' for file offset
890Sstevel@tonic-gate  * `offset' in it while living within the file system block sizes (`vp_off'
900Sstevel@tonic-gate  * and `vp_len') and the address space limits for which no pages currently
910Sstevel@tonic-gate  * exist and which map to consecutive file offsets.
920Sstevel@tonic-gate  */
930Sstevel@tonic-gate page_t *
940Sstevel@tonic-gate pvn_read_kluster(
950Sstevel@tonic-gate 	struct vnode *vp,
960Sstevel@tonic-gate 	u_offset_t off,
970Sstevel@tonic-gate 	struct seg *seg,
980Sstevel@tonic-gate 	caddr_t addr,
990Sstevel@tonic-gate 	u_offset_t *offp,			/* return values */
1000Sstevel@tonic-gate 	size_t *lenp,				/* return values */
1010Sstevel@tonic-gate 	u_offset_t vp_off,
1020Sstevel@tonic-gate 	size_t vp_len,
1030Sstevel@tonic-gate 	int isra)
1040Sstevel@tonic-gate {
1050Sstevel@tonic-gate 	ssize_t deltaf, deltab;
1060Sstevel@tonic-gate 	page_t *pp;
1070Sstevel@tonic-gate 	page_t *plist = NULL;
1080Sstevel@tonic-gate 	spgcnt_t pagesavail;
1090Sstevel@tonic-gate 	u_offset_t vp_end;
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate 	ASSERT(off >= vp_off && off < vp_off + vp_len);
1120Sstevel@tonic-gate 
1130Sstevel@tonic-gate 	/*
1140Sstevel@tonic-gate 	 * We only want to do klustering/read ahead if there
1150Sstevel@tonic-gate 	 * is more than minfree pages currently available.
1160Sstevel@tonic-gate 	 */
1170Sstevel@tonic-gate 	pagesavail = freemem - minfree;
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 	if (pagesavail <= 0)
1200Sstevel@tonic-gate 		if (isra)
1210Sstevel@tonic-gate 			return ((page_t *)NULL);    /* ra case - give up */
1220Sstevel@tonic-gate 		else
1230Sstevel@tonic-gate 			pagesavail = 1;		    /* must return a page */
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate 	/* We calculate in pages instead of bytes due to 32-bit overflows */
1260Sstevel@tonic-gate 	if (pagesavail < (spgcnt_t)btopr(vp_len)) {
1270Sstevel@tonic-gate 		/*
1280Sstevel@tonic-gate 		 * Don't have enough free memory for the
1290Sstevel@tonic-gate 		 * max request, try sizing down vp request.
1300Sstevel@tonic-gate 		 */
1310Sstevel@tonic-gate 		deltab = (ssize_t)(off - vp_off);
1320Sstevel@tonic-gate 		vp_len -= deltab;
1330Sstevel@tonic-gate 		vp_off += deltab;
1340Sstevel@tonic-gate 		if (pagesavail < btopr(vp_len)) {
1350Sstevel@tonic-gate 			/*
1360Sstevel@tonic-gate 			 * Still not enough memory, just settle for
1370Sstevel@tonic-gate 			 * pagesavail which is at least 1.
1380Sstevel@tonic-gate 			 */
1390Sstevel@tonic-gate 			vp_len = ptob(pagesavail);
1400Sstevel@tonic-gate 		}
1410Sstevel@tonic-gate 	}
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	vp_end = vp_off + vp_len;
1440Sstevel@tonic-gate 	ASSERT(off >= vp_off && off < vp_end);
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	if (isra && SEGOP_KLUSTER(seg, addr, 0))
1470Sstevel@tonic-gate 		return ((page_t *)NULL);	/* segment driver says no */
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 	if ((plist = page_create_va(vp, off,
1500Sstevel@tonic-gate 	    PAGESIZE, PG_EXCL | PG_WAIT, seg, addr)) == NULL)
1510Sstevel@tonic-gate 		return ((page_t *)NULL);
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate 	if (vp_len <= PAGESIZE || pvn_nofodklust) {
1540Sstevel@tonic-gate 		*offp = off;
1550Sstevel@tonic-gate 		*lenp = MIN(vp_len, PAGESIZE);
1560Sstevel@tonic-gate 	} else {
1570Sstevel@tonic-gate 		/*
1580Sstevel@tonic-gate 		 * Scan back from front by incrementing "deltab" and
1590Sstevel@tonic-gate 		 * comparing "off" with "vp_off + deltab" to avoid
1600Sstevel@tonic-gate 		 * "signed" versus "unsigned" conversion problems.
1610Sstevel@tonic-gate 		 */
1620Sstevel@tonic-gate 		for (deltab = PAGESIZE; off >= vp_off + deltab;
1630Sstevel@tonic-gate 		    deltab += PAGESIZE) {
1640Sstevel@tonic-gate 			/*
1650Sstevel@tonic-gate 			 * Call back to the segment driver to verify that
1660Sstevel@tonic-gate 			 * the klustering/read ahead operation makes sense.
1670Sstevel@tonic-gate 			 */
1680Sstevel@tonic-gate 			if (SEGOP_KLUSTER(seg, addr, -deltab))
1690Sstevel@tonic-gate 				break;		/* page not eligible */
1700Sstevel@tonic-gate 			if ((pp = page_create_va(vp, off - deltab,
1710Sstevel@tonic-gate 			    PAGESIZE, PG_EXCL, seg, addr - deltab))
1720Sstevel@tonic-gate 			    == NULL)
1730Sstevel@tonic-gate 				break;		/* already have the page */
1740Sstevel@tonic-gate 			/*
1750Sstevel@tonic-gate 			 * Add page to front of page list.
1760Sstevel@tonic-gate 			 */
1770Sstevel@tonic-gate 			page_add(&plist, pp);
1780Sstevel@tonic-gate 		}
1790Sstevel@tonic-gate 		deltab -= PAGESIZE;
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate 		/* scan forward from front */
1820Sstevel@tonic-gate 		for (deltaf = PAGESIZE; off + deltaf < vp_end;
1830Sstevel@tonic-gate 		    deltaf += PAGESIZE) {
1840Sstevel@tonic-gate 			/*
1850Sstevel@tonic-gate 			 * Call back to the segment driver to verify that
1860Sstevel@tonic-gate 			 * the klustering/read ahead operation makes sense.
1870Sstevel@tonic-gate 			 */
1880Sstevel@tonic-gate 			if (SEGOP_KLUSTER(seg, addr, deltaf))
1890Sstevel@tonic-gate 				break;		/* page not file extension */
1900Sstevel@tonic-gate 			if ((pp = page_create_va(vp, off + deltaf,
1910Sstevel@tonic-gate 			    PAGESIZE, PG_EXCL, seg, addr + deltaf))
1920Sstevel@tonic-gate 			    == NULL)
1930Sstevel@tonic-gate 				break;		/* already have page */
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 			/*
1960Sstevel@tonic-gate 			 * Add page to end of page list.
1970Sstevel@tonic-gate 			 */
1980Sstevel@tonic-gate 			page_add(&plist, pp);
1990Sstevel@tonic-gate 			plist = plist->p_next;
2000Sstevel@tonic-gate 		}
2010Sstevel@tonic-gate 		*offp = off = off - deltab;
2020Sstevel@tonic-gate 		*lenp = deltab + deltaf;
2030Sstevel@tonic-gate 		ASSERT(off >= vp_off);
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 		/*
2060Sstevel@tonic-gate 		 * If we ended up getting more than was actually
2070Sstevel@tonic-gate 		 * requested, retract the returned length to only
2080Sstevel@tonic-gate 		 * reflect what was requested.  This might happen
2090Sstevel@tonic-gate 		 * if we were allowed to kluster pages across a
2100Sstevel@tonic-gate 		 * span of (say) 5 frags, and frag size is less
2110Sstevel@tonic-gate 		 * than PAGESIZE.  We need a whole number of
2120Sstevel@tonic-gate 		 * pages to contain those frags, but the returned
2130Sstevel@tonic-gate 		 * size should only allow the returned range to
2140Sstevel@tonic-gate 		 * extend as far as the end of the frags.
2150Sstevel@tonic-gate 		 */
2160Sstevel@tonic-gate 		if ((vp_off + vp_len) < (off + *lenp)) {
2170Sstevel@tonic-gate 			ASSERT(vp_end > off);
2180Sstevel@tonic-gate 			*lenp = vp_end - off;
2190Sstevel@tonic-gate 		}
2200Sstevel@tonic-gate 	}
2210Sstevel@tonic-gate 	TRACE_3(TR_FAC_VM, TR_PVN_READ_KLUSTER,
2220Sstevel@tonic-gate 		"pvn_read_kluster:seg %p addr %x isra %x",
2230Sstevel@tonic-gate 		seg, addr, isra);
2240Sstevel@tonic-gate 	return (plist);
2250Sstevel@tonic-gate }
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate /*
2280Sstevel@tonic-gate  * Handle pages for this vnode on either side of the page "pp"
2290Sstevel@tonic-gate  * which has been locked by the caller.  This routine will also
2300Sstevel@tonic-gate  * do klustering in the range [vp_off, vp_off + vp_len] up
2310Sstevel@tonic-gate  * until a page which is not found.  The offset and length
2320Sstevel@tonic-gate  * of pages included is returned in "*offp" and "*lenp".
2330Sstevel@tonic-gate  *
2340Sstevel@tonic-gate  * Returns a list of dirty locked pages all ready to be
2350Sstevel@tonic-gate  * written back.
2360Sstevel@tonic-gate  */
2370Sstevel@tonic-gate page_t *
2380Sstevel@tonic-gate pvn_write_kluster(
2390Sstevel@tonic-gate 	struct vnode *vp,
2400Sstevel@tonic-gate 	page_t *pp,
2410Sstevel@tonic-gate 	u_offset_t *offp,		/* return values */
2420Sstevel@tonic-gate 	size_t *lenp,			/* return values */
2430Sstevel@tonic-gate 	u_offset_t vp_off,
2440Sstevel@tonic-gate 	size_t vp_len,
2450Sstevel@tonic-gate 	int flags)
2460Sstevel@tonic-gate {
2470Sstevel@tonic-gate 	u_offset_t off;
2480Sstevel@tonic-gate 	page_t *dirty;
2490Sstevel@tonic-gate 	size_t deltab, deltaf;
2500Sstevel@tonic-gate 	se_t se;
2510Sstevel@tonic-gate 	u_offset_t vp_end;
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	off = pp->p_offset;
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	/*
2560Sstevel@tonic-gate 	 * Kustering should not be done if we are invalidating
2570Sstevel@tonic-gate 	 * pages since we could destroy pages that belong to
2580Sstevel@tonic-gate 	 * some other process if this is a swap vnode.
2590Sstevel@tonic-gate 	 */
2600Sstevel@tonic-gate 	if (pvn_write_noklust || ((flags & B_INVAL) && IS_SWAPVP(vp))) {
2610Sstevel@tonic-gate 		*offp = off;
2620Sstevel@tonic-gate 		*lenp = PAGESIZE;
2630Sstevel@tonic-gate 		return (pp);
2640Sstevel@tonic-gate 	}
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	if (flags & (B_FREE | B_INVAL))
2670Sstevel@tonic-gate 		se = SE_EXCL;
2680Sstevel@tonic-gate 	else
2690Sstevel@tonic-gate 		se = SE_SHARED;
2700Sstevel@tonic-gate 
2710Sstevel@tonic-gate 	dirty = pp;
2720Sstevel@tonic-gate 	/*
2730Sstevel@tonic-gate 	 * Scan backwards looking for pages to kluster by incrementing
2740Sstevel@tonic-gate 	 * "deltab" and comparing "off" with "vp_off + deltab" to
2750Sstevel@tonic-gate 	 * avoid "signed" versus "unsigned" conversion problems.
2760Sstevel@tonic-gate 	 */
2770Sstevel@tonic-gate 	for (deltab = PAGESIZE; off >= vp_off + deltab; deltab += PAGESIZE) {
2780Sstevel@tonic-gate 		pp = page_lookup_nowait(vp, off - deltab, se);
2790Sstevel@tonic-gate 		if (pp == NULL)
2800Sstevel@tonic-gate 			break;		/* page not found */
2810Sstevel@tonic-gate 		if (pvn_getdirty(pp, flags | B_DELWRI) == 0)
2820Sstevel@tonic-gate 			break;
2830Sstevel@tonic-gate 		page_add(&dirty, pp);
2840Sstevel@tonic-gate 	}
2850Sstevel@tonic-gate 	deltab -= PAGESIZE;
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	vp_end = vp_off + vp_len;
2880Sstevel@tonic-gate 	/* now scan forwards looking for pages to kluster */
2890Sstevel@tonic-gate 	for (deltaf = PAGESIZE; off + deltaf < vp_end; deltaf += PAGESIZE) {
2900Sstevel@tonic-gate 		pp = page_lookup_nowait(vp, off + deltaf, se);
2910Sstevel@tonic-gate 		if (pp == NULL)
2920Sstevel@tonic-gate 			break;		/* page not found */
2930Sstevel@tonic-gate 		if (pvn_getdirty(pp, flags | B_DELWRI) == 0)
2940Sstevel@tonic-gate 			break;
2950Sstevel@tonic-gate 		page_add(&dirty, pp);
2960Sstevel@tonic-gate 		dirty = dirty->p_next;
2970Sstevel@tonic-gate 	}
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	*offp = off - deltab;
3000Sstevel@tonic-gate 	*lenp = deltab + deltaf;
3010Sstevel@tonic-gate 	return (dirty);
3020Sstevel@tonic-gate }
3030Sstevel@tonic-gate 
3040Sstevel@tonic-gate /*
3050Sstevel@tonic-gate  * Generic entry point used to release the "shared/exclusive" lock
3060Sstevel@tonic-gate  * and the "p_iolock" on pages after i/o is complete.
3070Sstevel@tonic-gate  */
3080Sstevel@tonic-gate void
3090Sstevel@tonic-gate pvn_io_done(page_t *plist)
3100Sstevel@tonic-gate {
3110Sstevel@tonic-gate 	page_t *pp;
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate 	while (plist != NULL) {
3140Sstevel@tonic-gate 		pp = plist;
3150Sstevel@tonic-gate 		page_sub(&plist, pp);
3160Sstevel@tonic-gate 		page_io_unlock(pp);
3170Sstevel@tonic-gate 		page_unlock(pp);
3180Sstevel@tonic-gate 	}
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate /*
3220Sstevel@tonic-gate  * Entry point to be used by file system getpage subr's and
3230Sstevel@tonic-gate  * other such routines which either want to unlock pages (B_ASYNC
3240Sstevel@tonic-gate  * request) or destroy a list of pages if an error occurred.
3250Sstevel@tonic-gate  */
3260Sstevel@tonic-gate void
3270Sstevel@tonic-gate pvn_read_done(page_t *plist, int flags)
3280Sstevel@tonic-gate {
3290Sstevel@tonic-gate 	page_t *pp;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 	while (plist != NULL) {
3320Sstevel@tonic-gate 		pp = plist;
3330Sstevel@tonic-gate 		page_sub(&plist, pp);
3340Sstevel@tonic-gate 		page_io_unlock(pp);
3350Sstevel@tonic-gate 		if (flags & B_ERROR) {
3360Sstevel@tonic-gate 			/*LINTED: constant in conditional context*/
3370Sstevel@tonic-gate 			VN_DISPOSE(pp, B_INVAL, 0, kcred);
3380Sstevel@tonic-gate 		} else {
3390Sstevel@tonic-gate 			(void) page_release(pp, 0);
3400Sstevel@tonic-gate 		}
3410Sstevel@tonic-gate 	}
3420Sstevel@tonic-gate }
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate /*
3450Sstevel@tonic-gate  * Automagic pageout.
3460Sstevel@tonic-gate  * When memory gets tight, start freeing pages popping out of the
3470Sstevel@tonic-gate  * write queue.
3480Sstevel@tonic-gate  */
3490Sstevel@tonic-gate int	write_free = 1;
3500Sstevel@tonic-gate pgcnt_t	pages_before_pager = 200;	/* LMXXX */
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate /*
3530Sstevel@tonic-gate  * Routine to be called when page-out's complete.
3540Sstevel@tonic-gate  * The caller, typically VOP_PUTPAGE, has to explicity call this routine
3550Sstevel@tonic-gate  * after waiting for i/o to complete (biowait) to free the list of
3560Sstevel@tonic-gate  * pages associated with the buffer.  These pages must be locked
3570Sstevel@tonic-gate  * before i/o is initiated.
3580Sstevel@tonic-gate  *
3590Sstevel@tonic-gate  * If a write error occurs, the pages are marked as modified
3600Sstevel@tonic-gate  * so the write will be re-tried later.
3610Sstevel@tonic-gate  */
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate void
3640Sstevel@tonic-gate pvn_write_done(page_t *plist, int flags)
3650Sstevel@tonic-gate {
3660Sstevel@tonic-gate 	int dfree = 0;
3670Sstevel@tonic-gate 	int pgrec = 0;
3680Sstevel@tonic-gate 	int pgout = 0;
3690Sstevel@tonic-gate 	int pgpgout = 0;
3700Sstevel@tonic-gate 	int anonpgout = 0;
3710Sstevel@tonic-gate 	int anonfree = 0;
3720Sstevel@tonic-gate 	int fspgout = 0;
3730Sstevel@tonic-gate 	int fsfree = 0;
3740Sstevel@tonic-gate 	int execpgout = 0;
3750Sstevel@tonic-gate 	int execfree = 0;
3760Sstevel@tonic-gate 	page_t *pp;
3770Sstevel@tonic-gate 	struct cpu *cpup;
3780Sstevel@tonic-gate 	struct vnode *vp = NULL;	/* for probe */
3790Sstevel@tonic-gate 	uint_t ppattr;
380*2999Sstans 	kmutex_t *vphm = NULL;
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate 	ASSERT((flags & B_READ) == 0);
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	/*
3850Sstevel@tonic-gate 	 * If we are about to start paging anyway, start freeing pages.
3860Sstevel@tonic-gate 	 */
3870Sstevel@tonic-gate 	if (write_free && freemem < lotsfree + pages_before_pager &&
3880Sstevel@tonic-gate 	    (flags & B_ERROR) == 0) {
3890Sstevel@tonic-gate 		flags |= B_FREE;
3900Sstevel@tonic-gate 	}
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	/*
3930Sstevel@tonic-gate 	 * Handle each page involved in the i/o operation.
3940Sstevel@tonic-gate 	 */
3950Sstevel@tonic-gate 	while (plist != NULL) {
3960Sstevel@tonic-gate 		pp = plist;
3970Sstevel@tonic-gate 		ASSERT(PAGE_LOCKED(pp) && page_iolock_assert(pp));
3980Sstevel@tonic-gate 		page_sub(&plist, pp);
3990Sstevel@tonic-gate 
4000Sstevel@tonic-gate 		/* Kernel probe support */
4010Sstevel@tonic-gate 		if (vp == NULL)
4020Sstevel@tonic-gate 			vp = pp->p_vnode;
4030Sstevel@tonic-gate 
404*2999Sstans 		if (IS_VMODSORT(vp)) {
405*2999Sstans 			/*
406*2999Sstans 			 * Move page to the top of the v_page list.
407*2999Sstans 			 * Skip pages modified during IO.
408*2999Sstans 			 */
409*2999Sstans 			vphm = page_vnode_mutex(vp);
410*2999Sstans 			mutex_enter(vphm);
411*2999Sstans 			if ((pp->p_vpnext != pp) && !hat_ismod(pp)) {
412*2999Sstans 				page_vpsub(&vp->v_pages, pp);
413*2999Sstans 				page_vpadd(&vp->v_pages, pp);
414*2999Sstans 			}
415*2999Sstans 			mutex_exit(vphm);
416*2999Sstans 		}
417*2999Sstans 
4180Sstevel@tonic-gate 		if (flags & B_ERROR) {
4190Sstevel@tonic-gate 			/*
4200Sstevel@tonic-gate 			 * Write operation failed.  We don't want
4210Sstevel@tonic-gate 			 * to destroy (or free) the page unless B_FORCE
4220Sstevel@tonic-gate 			 * is set. We set the mod bit again and release
4230Sstevel@tonic-gate 			 * all locks on the page so that it will get written
4240Sstevel@tonic-gate 			 * back again later when things are hopefully
4250Sstevel@tonic-gate 			 * better again.
4260Sstevel@tonic-gate 			 * If B_INVAL and B_FORCE is set we really have
4270Sstevel@tonic-gate 			 * to destroy the page.
4280Sstevel@tonic-gate 			 */
4290Sstevel@tonic-gate 			if ((flags & (B_INVAL|B_FORCE)) == (B_INVAL|B_FORCE)) {
4300Sstevel@tonic-gate 				page_io_unlock(pp);
4310Sstevel@tonic-gate 				/*LINTED: constant in conditional context*/
4320Sstevel@tonic-gate 				VN_DISPOSE(pp, B_INVAL, 0, kcred);
4330Sstevel@tonic-gate 			} else {
4340Sstevel@tonic-gate 				hat_setmod(pp);
4350Sstevel@tonic-gate 				page_io_unlock(pp);
4360Sstevel@tonic-gate 				page_unlock(pp);
4370Sstevel@tonic-gate 			}
4380Sstevel@tonic-gate 		} else if (flags & B_INVAL) {
4390Sstevel@tonic-gate 			/*
4400Sstevel@tonic-gate 			 * XXX - Failed writes with B_INVAL set are
4410Sstevel@tonic-gate 			 * not handled appropriately.
4420Sstevel@tonic-gate 			 */
4430Sstevel@tonic-gate 			page_io_unlock(pp);
4440Sstevel@tonic-gate 			/*LINTED: constant in conditional context*/
4450Sstevel@tonic-gate 			VN_DISPOSE(pp, B_INVAL, 0, kcred);
4460Sstevel@tonic-gate 		} else if (flags & B_FREE ||!hat_page_is_mapped(pp)) {
4470Sstevel@tonic-gate 			/*
4480Sstevel@tonic-gate 			 * Update statistics for pages being paged out
4490Sstevel@tonic-gate 			 */
4500Sstevel@tonic-gate 			if (pp->p_vnode) {
4510Sstevel@tonic-gate 				if (IS_SWAPFSVP(pp->p_vnode)) {
4520Sstevel@tonic-gate 					anonpgout++;
4530Sstevel@tonic-gate 				} else {
4540Sstevel@tonic-gate 					if (pp->p_vnode->v_flag & VVMEXEC) {
4550Sstevel@tonic-gate 						execpgout++;
4560Sstevel@tonic-gate 					} else {
4570Sstevel@tonic-gate 						fspgout++;
4580Sstevel@tonic-gate 					}
4590Sstevel@tonic-gate 				}
4600Sstevel@tonic-gate 			}
4610Sstevel@tonic-gate 			page_io_unlock(pp);
4620Sstevel@tonic-gate 			pgout = 1;
4630Sstevel@tonic-gate 			pgpgout++;
4640Sstevel@tonic-gate 			TRACE_1(TR_FAC_VM, TR_PAGE_WS_OUT,
4650Sstevel@tonic-gate 				"page_ws_out:pp %p", pp);
4660Sstevel@tonic-gate 
4670Sstevel@tonic-gate 			/*
4680Sstevel@tonic-gate 			 * The page_struct_lock need not be acquired to
4690Sstevel@tonic-gate 			 * examine "p_lckcnt" and "p_cowcnt" since we'll
4700Sstevel@tonic-gate 			 * have an "exclusive" lock if the upgrade succeeds.
4710Sstevel@tonic-gate 			 */
4720Sstevel@tonic-gate 			if (page_tryupgrade(pp) &&
4730Sstevel@tonic-gate 			    pp->p_lckcnt == 0 && pp->p_cowcnt == 0) {
4740Sstevel@tonic-gate 				/*
4750Sstevel@tonic-gate 				 * Check if someone has reclaimed the
4760Sstevel@tonic-gate 				 * page.  If ref and mod are not set, no
4770Sstevel@tonic-gate 				 * one is using it so we can free it.
4780Sstevel@tonic-gate 				 * The rest of the system is careful
4790Sstevel@tonic-gate 				 * to use the NOSYNC flag to unload
4800Sstevel@tonic-gate 				 * translations set up for i/o w/o
4810Sstevel@tonic-gate 				 * affecting ref and mod bits.
4820Sstevel@tonic-gate 				 *
4830Sstevel@tonic-gate 				 * Obtain a copy of the real hardware
4840Sstevel@tonic-gate 				 * mod bit using hat_pagesync(pp, HAT_DONTZERO)
4850Sstevel@tonic-gate 				 * to avoid having to flush the cache.
4860Sstevel@tonic-gate 				 */
4870Sstevel@tonic-gate 				ppattr = hat_pagesync(pp, HAT_SYNC_DONTZERO |
4880Sstevel@tonic-gate 					HAT_SYNC_STOPON_MOD);
4890Sstevel@tonic-gate 			ck_refmod:
4900Sstevel@tonic-gate 				if (!(ppattr & (P_REF | P_MOD))) {
4910Sstevel@tonic-gate 					if (hat_page_is_mapped(pp)) {
4920Sstevel@tonic-gate 						/*
4930Sstevel@tonic-gate 						 * Doesn't look like the page
4940Sstevel@tonic-gate 						 * was modified so now we
4950Sstevel@tonic-gate 						 * really have to unload the
4960Sstevel@tonic-gate 						 * translations.  Meanwhile
4970Sstevel@tonic-gate 						 * another CPU could've
4980Sstevel@tonic-gate 						 * modified it so we have to
4990Sstevel@tonic-gate 						 * check again.  We don't loop
5000Sstevel@tonic-gate 						 * forever here because now
5010Sstevel@tonic-gate 						 * the translations are gone
5020Sstevel@tonic-gate 						 * and no one can get a new one
5030Sstevel@tonic-gate 						 * since we have the "exclusive"
5040Sstevel@tonic-gate 						 * lock on the page.
5050Sstevel@tonic-gate 						 */
5060Sstevel@tonic-gate 						(void) hat_pageunload(pp,
5070Sstevel@tonic-gate 							HAT_FORCE_PGUNLOAD);
5080Sstevel@tonic-gate 						ppattr = hat_page_getattr(pp,
5090Sstevel@tonic-gate 							P_REF | P_MOD);
5100Sstevel@tonic-gate 						goto ck_refmod;
5110Sstevel@tonic-gate 					}
5120Sstevel@tonic-gate 					/*
5130Sstevel@tonic-gate 					 * Update statistics for pages being
5140Sstevel@tonic-gate 					 * freed
5150Sstevel@tonic-gate 					 */
5160Sstevel@tonic-gate 					if (pp->p_vnode) {
5170Sstevel@tonic-gate 						if (IS_SWAPFSVP(pp->p_vnode)) {
5180Sstevel@tonic-gate 							anonfree++;
5190Sstevel@tonic-gate 						} else {
5200Sstevel@tonic-gate 							if (pp->p_vnode->v_flag
5210Sstevel@tonic-gate 							    & VVMEXEC) {
5220Sstevel@tonic-gate 								execfree++;
5230Sstevel@tonic-gate 							} else {
5240Sstevel@tonic-gate 								fsfree++;
5250Sstevel@tonic-gate 							}
5260Sstevel@tonic-gate 						}
5270Sstevel@tonic-gate 					}
5280Sstevel@tonic-gate 					/*LINTED: constant in conditional ctx*/
5290Sstevel@tonic-gate 					VN_DISPOSE(pp, B_FREE,
5300Sstevel@tonic-gate 						(flags & B_DONTNEED), kcred);
5310Sstevel@tonic-gate 					dfree++;
5320Sstevel@tonic-gate 				} else {
5330Sstevel@tonic-gate 					page_unlock(pp);
5340Sstevel@tonic-gate 					pgrec++;
5350Sstevel@tonic-gate 					TRACE_1(TR_FAC_VM, TR_PAGE_WS_FREE,
5360Sstevel@tonic-gate 					    "page_ws_free:pp %p", pp);
5370Sstevel@tonic-gate 				}
5380Sstevel@tonic-gate 			} else {
5390Sstevel@tonic-gate 				/*
5400Sstevel@tonic-gate 				 * Page is either `locked' in memory
5410Sstevel@tonic-gate 				 * or was reclaimed and now has a
5420Sstevel@tonic-gate 				 * "shared" lock, so release it.
5430Sstevel@tonic-gate 				 */
5440Sstevel@tonic-gate 				page_unlock(pp);
5450Sstevel@tonic-gate 			}
5460Sstevel@tonic-gate 		} else {
5470Sstevel@tonic-gate 			/*
5480Sstevel@tonic-gate 			 * Neither B_FREE nor B_INVAL nor B_ERROR.
5490Sstevel@tonic-gate 			 * Just release locks.
5500Sstevel@tonic-gate 			 */
5510Sstevel@tonic-gate 			page_io_unlock(pp);
5520Sstevel@tonic-gate 			page_unlock(pp);
5530Sstevel@tonic-gate 		}
5540Sstevel@tonic-gate 	}
5550Sstevel@tonic-gate 
5560Sstevel@tonic-gate 	CPU_STATS_ENTER_K();
5570Sstevel@tonic-gate 	cpup = CPU;		/* get cpup now that CPU cannot change */
5580Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, dfree, dfree);
5590Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, pgrec, pgrec);
5600Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, pgout, pgout);
5610Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, pgpgout, pgpgout);
5620Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, anonpgout, anonpgout);
5630Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, anonfree, anonfree);
5640Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, fspgout, fspgout);
5650Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, fsfree, fsfree);
5660Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, execpgout, execpgout);
5670Sstevel@tonic-gate 	CPU_STATS_ADDQ(cpup, vm, execfree, execfree);
5680Sstevel@tonic-gate 	CPU_STATS_EXIT_K();
5690Sstevel@tonic-gate 
5700Sstevel@tonic-gate 	/* Kernel probe */
5710Sstevel@tonic-gate 	TNF_PROBE_4(pageout, "vm pageio io", /* CSTYLED */,
5720Sstevel@tonic-gate 		tnf_opaque,	vnode,			vp,
5730Sstevel@tonic-gate 		tnf_ulong,	pages_pageout,		pgpgout,
5740Sstevel@tonic-gate 		tnf_ulong,	pages_freed,		dfree,
5750Sstevel@tonic-gate 		tnf_ulong,	pages_reclaimed,	pgrec);
5760Sstevel@tonic-gate }
5770Sstevel@tonic-gate 
5780Sstevel@tonic-gate /*
5790Sstevel@tonic-gate  * Flags are composed of {B_ASYNC, B_INVAL, B_FREE, B_DONTNEED, B_DELWRI,
5800Sstevel@tonic-gate  * B_TRUNC, B_FORCE}.  B_DELWRI indicates that this page is part of a kluster
5810Sstevel@tonic-gate  * operation and is only to be considered if it doesn't involve any
5820Sstevel@tonic-gate  * waiting here.  B_TRUNC indicates that the file is being truncated
5830Sstevel@tonic-gate  * and so no i/o needs to be done. B_FORCE indicates that the page
5840Sstevel@tonic-gate  * must be destroyed so don't try wrting it out.
5850Sstevel@tonic-gate  *
5860Sstevel@tonic-gate  * The caller must ensure that the page is locked.  Returns 1, if
5870Sstevel@tonic-gate  * the page should be written back (the "iolock" is held in this
5880Sstevel@tonic-gate  * case), or 0 if the page has been dealt with or has been
5890Sstevel@tonic-gate  * unlocked.
5900Sstevel@tonic-gate  */
5910Sstevel@tonic-gate int
5920Sstevel@tonic-gate pvn_getdirty(page_t *pp, int flags)
5930Sstevel@tonic-gate {
5940Sstevel@tonic-gate 	ASSERT((flags & (B_INVAL | B_FREE)) ?
5950Sstevel@tonic-gate 	    PAGE_EXCL(pp) : PAGE_SHARED(pp));
5960Sstevel@tonic-gate 	ASSERT(PP_ISFREE(pp) == 0);
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	/*
5990Sstevel@tonic-gate 	 * If trying to invalidate or free a logically `locked' page,
6000Sstevel@tonic-gate 	 * forget it.  Don't need page_struct_lock to check p_lckcnt and
6010Sstevel@tonic-gate 	 * p_cowcnt as the page is exclusively locked.
6020Sstevel@tonic-gate 	 */
6030Sstevel@tonic-gate 	if ((flags & (B_INVAL | B_FREE)) && !(flags & (B_TRUNC|B_FORCE)) &&
6040Sstevel@tonic-gate 	    (pp->p_lckcnt != 0 || pp->p_cowcnt != 0)) {
6050Sstevel@tonic-gate 		page_unlock(pp);
6060Sstevel@tonic-gate 		return (0);
6070Sstevel@tonic-gate 	}
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	/*
6100Sstevel@tonic-gate 	 * Now acquire the i/o lock so we can add it to the dirty
6110Sstevel@tonic-gate 	 * list (if necessary).  We avoid blocking on the i/o lock
6120Sstevel@tonic-gate 	 * in the following cases:
6130Sstevel@tonic-gate 	 *
6140Sstevel@tonic-gate 	 *	If B_DELWRI is set, which implies that this request is
6150Sstevel@tonic-gate 	 *	due to a klustering operartion.
6160Sstevel@tonic-gate 	 *
6170Sstevel@tonic-gate 	 *	If this is an async (B_ASYNC) operation and we are not doing
6180Sstevel@tonic-gate 	 *	invalidation (B_INVAL) [The current i/o or fsflush will ensure
6190Sstevel@tonic-gate 	 *	that the the page is written out].
6200Sstevel@tonic-gate 	 */
6210Sstevel@tonic-gate 	if ((flags & B_DELWRI) || ((flags & (B_INVAL | B_ASYNC)) == B_ASYNC)) {
6220Sstevel@tonic-gate 		if (!page_io_trylock(pp)) {
6230Sstevel@tonic-gate 			page_unlock(pp);
6240Sstevel@tonic-gate 			return (0);
6250Sstevel@tonic-gate 		}
6260Sstevel@tonic-gate 	} else {
6270Sstevel@tonic-gate 		page_io_lock(pp);
6280Sstevel@tonic-gate 	}
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	/*
6310Sstevel@tonic-gate 	 * If we want to free or invalidate the page then
6320Sstevel@tonic-gate 	 * we need to unload it so that anyone who wants
6330Sstevel@tonic-gate 	 * it will have to take a minor fault to get it.
6340Sstevel@tonic-gate 	 * Otherwise, we're just writing the page back so we
6350Sstevel@tonic-gate 	 * need to sync up the hardwre and software mod bit to
6360Sstevel@tonic-gate 	 * detect any future modifications.  We clear the
6370Sstevel@tonic-gate 	 * software mod bit when we put the page on the dirty
6380Sstevel@tonic-gate 	 * list.
6390Sstevel@tonic-gate 	 */
6400Sstevel@tonic-gate 	if (flags & (B_INVAL | B_FREE)) {
6410Sstevel@tonic-gate 		(void) hat_pageunload(pp, HAT_FORCE_PGUNLOAD);
6420Sstevel@tonic-gate 	} else {
6430Sstevel@tonic-gate 		(void) hat_pagesync(pp, HAT_SYNC_ZERORM);
6440Sstevel@tonic-gate 	}
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 	if (!hat_ismod(pp) || (flags & B_TRUNC)) {
6470Sstevel@tonic-gate 		/*
6480Sstevel@tonic-gate 		 * Don't need to add it to the
6490Sstevel@tonic-gate 		 * list after all.
6500Sstevel@tonic-gate 		 */
6510Sstevel@tonic-gate 		page_io_unlock(pp);
6520Sstevel@tonic-gate 		if (flags & B_INVAL) {
6530Sstevel@tonic-gate 			/*LINTED: constant in conditional context*/
6540Sstevel@tonic-gate 			VN_DISPOSE(pp, B_INVAL, 0, kcred);
6550Sstevel@tonic-gate 		} else if (flags & B_FREE) {
6560Sstevel@tonic-gate 			/*LINTED: constant in conditional context*/
6570Sstevel@tonic-gate 			VN_DISPOSE(pp, B_FREE, (flags & B_DONTNEED), kcred);
6580Sstevel@tonic-gate 		} else {
6590Sstevel@tonic-gate 			/*
6600Sstevel@tonic-gate 			 * This is advisory path for the callers
6610Sstevel@tonic-gate 			 * of VOP_PUTPAGE() who prefer freeing the
6620Sstevel@tonic-gate 			 * page _only_ if no one else is accessing it.
6630Sstevel@tonic-gate 			 * E.g. segmap_release()
6640Sstevel@tonic-gate 			 *
6650Sstevel@tonic-gate 			 * The above hat_ismod() check is useless because:
6660Sstevel@tonic-gate 			 * (1) we may not be holding SE_EXCL lock;
6670Sstevel@tonic-gate 			 * (2) we've not unloaded _all_ translations
6680Sstevel@tonic-gate 			 *
6690Sstevel@tonic-gate 			 * Let page_release() do the heavy-lifting.
6700Sstevel@tonic-gate 			 */
6710Sstevel@tonic-gate 			(void) page_release(pp, 1);
6720Sstevel@tonic-gate 		}
6730Sstevel@tonic-gate 		return (0);
6740Sstevel@tonic-gate 	}
6750Sstevel@tonic-gate 
6760Sstevel@tonic-gate 	/*
6770Sstevel@tonic-gate 	 * Page is dirty, get it ready for the write back
6780Sstevel@tonic-gate 	 * and add page to the dirty list.
6790Sstevel@tonic-gate 	 */
6800Sstevel@tonic-gate 	hat_clrrefmod(pp);
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate 	/*
6830Sstevel@tonic-gate 	 * If we're going to free the page when we're done
6840Sstevel@tonic-gate 	 * then we can let others try to use it starting now.
6850Sstevel@tonic-gate 	 * We'll detect the fact that they used it when the
6860Sstevel@tonic-gate 	 * i/o is done and avoid freeing the page.
6870Sstevel@tonic-gate 	 */
6880Sstevel@tonic-gate 	if (flags & B_FREE)
6890Sstevel@tonic-gate 		page_downgrade(pp);
6900Sstevel@tonic-gate 
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate 	TRACE_1(TR_FAC_VM, TR_PVN_GETDIRTY, "pvn_getdirty:pp %p", pp);
6930Sstevel@tonic-gate 
6940Sstevel@tonic-gate 	return (1);
6950Sstevel@tonic-gate }
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate /*ARGSUSED*/
6990Sstevel@tonic-gate static int
7000Sstevel@tonic-gate marker_constructor(void *buf, void *cdrarg, int kmflags)
7010Sstevel@tonic-gate {
7020Sstevel@tonic-gate 	page_t *mark = buf;
7030Sstevel@tonic-gate 	bzero(mark, sizeof (page_t));
7040Sstevel@tonic-gate 	return (0);
7050Sstevel@tonic-gate }
7060Sstevel@tonic-gate 
7070Sstevel@tonic-gate void
7080Sstevel@tonic-gate pvn_init()
7090Sstevel@tonic-gate {
7100Sstevel@tonic-gate 	if (pvn_vmodsort_disable == 0)
7110Sstevel@tonic-gate 		pvn_vmodsort_supported = hat_supported(HAT_VMODSORT, NULL);
7120Sstevel@tonic-gate 	marker_cache = kmem_cache_create("marker_cache",
7130Sstevel@tonic-gate 	    sizeof (page_t), 0, marker_constructor,
7140Sstevel@tonic-gate 	    NULL, NULL, NULL, NULL, 0);
7150Sstevel@tonic-gate }
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 
7180Sstevel@tonic-gate /*
7190Sstevel@tonic-gate  * Process a vnode's page list for all pages whose offset is >= off.
7200Sstevel@tonic-gate  * Pages are to either be free'd, invalidated, or written back to disk.
7210Sstevel@tonic-gate  *
7220Sstevel@tonic-gate  * An "exclusive" lock is acquired for each page if B_INVAL or B_FREE
7230Sstevel@tonic-gate  * is specified, otherwise they are "shared" locked.
7240Sstevel@tonic-gate  *
7250Sstevel@tonic-gate  * Flags are {B_ASYNC, B_INVAL, B_FREE, B_DONTNEED, B_TRUNC}
7260Sstevel@tonic-gate  *
7270Sstevel@tonic-gate  * Special marker page_t's are inserted in the list in order
7280Sstevel@tonic-gate  * to keep track of where we are in the list when locks are dropped.
7290Sstevel@tonic-gate  *
7300Sstevel@tonic-gate  * Note the list is circular and insertions can happen only at the
7310Sstevel@tonic-gate  * head and tail of the list. The algorithm ensures visiting all pages
7320Sstevel@tonic-gate  * on the list in the following way:
7330Sstevel@tonic-gate  *
7340Sstevel@tonic-gate  *    Drop two marker pages at the end of the list.
7350Sstevel@tonic-gate  *
7360Sstevel@tonic-gate  *    Move one marker page backwards towards the start of the list until
7370Sstevel@tonic-gate  *    it is at the list head, processing the pages passed along the way.
7380Sstevel@tonic-gate  *
7390Sstevel@tonic-gate  *    Due to race conditions when the vphm mutex is dropped, additional pages
7400Sstevel@tonic-gate  *    can be added to either end of the list, so we'll continue to move
7410Sstevel@tonic-gate  *    the marker and process pages until it is up against the end marker.
7420Sstevel@tonic-gate  *
7430Sstevel@tonic-gate  * There is one special exit condition. If we are processing a VMODSORT
7440Sstevel@tonic-gate  * vnode and only writing back modified pages, we can stop as soon as
7450Sstevel@tonic-gate  * we run into an unmodified page.  This makes fsync(3) operations fast.
7460Sstevel@tonic-gate  */
7470Sstevel@tonic-gate int
7480Sstevel@tonic-gate pvn_vplist_dirty(
7490Sstevel@tonic-gate 	vnode_t		*vp,
7500Sstevel@tonic-gate 	u_offset_t	off,
7510Sstevel@tonic-gate 	int		(*putapage)(vnode_t *, page_t *, u_offset_t *,
7520Sstevel@tonic-gate 			size_t *, int, cred_t *),
7530Sstevel@tonic-gate 	int		flags,
7540Sstevel@tonic-gate 	cred_t		*cred)
7550Sstevel@tonic-gate {
7560Sstevel@tonic-gate 	page_t		*pp;
7570Sstevel@tonic-gate 	page_t		*mark;		/* marker page that moves toward head */
7580Sstevel@tonic-gate 	page_t		*end;		/* marker page at end of list */
7590Sstevel@tonic-gate 	int		err = 0;
7600Sstevel@tonic-gate 	int		error;
7610Sstevel@tonic-gate 	kmutex_t	*vphm;
7620Sstevel@tonic-gate 	se_t		se;
7630Sstevel@tonic-gate 	page_t		**where_to_move;
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate 	ASSERT(vp->v_type != VCHR);
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	if (vp->v_pages == NULL)
7680Sstevel@tonic-gate 		return (0);
7690Sstevel@tonic-gate 
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate 	/*
7720Sstevel@tonic-gate 	 * Serialize vplist_dirty operations on this vnode by setting VVMLOCK.
7730Sstevel@tonic-gate 	 *
7740Sstevel@tonic-gate 	 * Don't block on VVMLOCK if B_ASYNC is set. This prevents sync()
7750Sstevel@tonic-gate 	 * from getting blocked while flushing pages to a dead NFS server.
7760Sstevel@tonic-gate 	 */
7770Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
7780Sstevel@tonic-gate 	if ((vp->v_flag & VVMLOCK) && (flags & B_ASYNC)) {
7790Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
7800Sstevel@tonic-gate 		return (EAGAIN);
7810Sstevel@tonic-gate 	}
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 	while (vp->v_flag & VVMLOCK)
7840Sstevel@tonic-gate 		cv_wait(&vp->v_cv, &vp->v_lock);
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate 	if (vp->v_pages == NULL) {
7870Sstevel@tonic-gate 		mutex_exit(&vp->v_lock);
7880Sstevel@tonic-gate 		return (0);
7890Sstevel@tonic-gate 	}
7900Sstevel@tonic-gate 
7910Sstevel@tonic-gate 	vp->v_flag |= VVMLOCK;
7920Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	/*
7960Sstevel@tonic-gate 	 * Set up the marker pages used to walk the list
7970Sstevel@tonic-gate 	 */
7980Sstevel@tonic-gate 	end = kmem_cache_alloc(marker_cache, KM_SLEEP);
7990Sstevel@tonic-gate 	end->p_vnode = vp;
8000Sstevel@tonic-gate 	end->p_offset = (u_offset_t)-2;
8010Sstevel@tonic-gate 	mark = kmem_cache_alloc(marker_cache, KM_SLEEP);
8020Sstevel@tonic-gate 	mark->p_vnode = vp;
8030Sstevel@tonic-gate 	mark->p_offset = (u_offset_t)-1;
8040Sstevel@tonic-gate 
8050Sstevel@tonic-gate 	/*
8060Sstevel@tonic-gate 	 * Grab the lock protecting the vnode's page list
8070Sstevel@tonic-gate 	 * note that this lock is dropped at times in the loop.
8080Sstevel@tonic-gate 	 */
8090Sstevel@tonic-gate 	vphm = page_vnode_mutex(vp);
8100Sstevel@tonic-gate 	mutex_enter(vphm);
8110Sstevel@tonic-gate 	if (vp->v_pages == NULL)
8120Sstevel@tonic-gate 		goto leave;
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	/*
8150Sstevel@tonic-gate 	 * insert the markers and loop through the list of pages
8160Sstevel@tonic-gate 	 */
8170Sstevel@tonic-gate 	page_vpadd(&vp->v_pages->p_vpprev->p_vpnext, mark);
8180Sstevel@tonic-gate 	page_vpadd(&mark->p_vpnext, end);
8190Sstevel@tonic-gate 	for (;;) {
8200Sstevel@tonic-gate 
8210Sstevel@tonic-gate 		/*
8220Sstevel@tonic-gate 		 * If only doing an async write back, then we can
8230Sstevel@tonic-gate 		 * stop as soon as we get to start of the list.
8240Sstevel@tonic-gate 		 */
8250Sstevel@tonic-gate 		if (flags == B_ASYNC && vp->v_pages == mark)
8260Sstevel@tonic-gate 			break;
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 		/*
8290Sstevel@tonic-gate 		 * otherwise stop when we've gone through all the pages
8300Sstevel@tonic-gate 		 */
8310Sstevel@tonic-gate 		if (mark->p_vpprev == end)
8320Sstevel@tonic-gate 			break;
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 		pp = mark->p_vpprev;
8350Sstevel@tonic-gate 		if (vp->v_pages == pp)
8360Sstevel@tonic-gate 			where_to_move = &vp->v_pages;
8370Sstevel@tonic-gate 		else
8380Sstevel@tonic-gate 			where_to_move = &pp->p_vpprev->p_vpnext;
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 		ASSERT(pp->p_vnode == vp);
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 		/*
8430Sstevel@tonic-gate 		 * Skip this page if the offset is out of the desired range.
8440Sstevel@tonic-gate 		 * Just move the marker and continue.
8450Sstevel@tonic-gate 		 */
8460Sstevel@tonic-gate 		if (pp->p_offset < off) {
8470Sstevel@tonic-gate 			page_vpsub(&vp->v_pages, mark);
8480Sstevel@tonic-gate 			page_vpadd(where_to_move, mark);
8490Sstevel@tonic-gate 			continue;
8500Sstevel@tonic-gate 		}
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 		/*
8530Sstevel@tonic-gate 		 * If just flushing dirty pages to disk and this vnode
8540Sstevel@tonic-gate 		 * is using a sorted list of pages, we can stop processing
8550Sstevel@tonic-gate 		 * as soon as we find an unmodified page. Since all the
8560Sstevel@tonic-gate 		 * modified pages are visited first.
8570Sstevel@tonic-gate 		 */
8580Sstevel@tonic-gate 		if (IS_VMODSORT(vp) &&
859*2999Sstans 		    !(flags & (B_INVAL | B_FREE | B_TRUNC))) {
860*2999Sstans 			if (!hat_ismod(pp) && !page_io_locked(pp)) {
8610Sstevel@tonic-gate #ifdef  DEBUG
862*2999Sstans 				/*
863*2999Sstans 				 * For debug kernels examine what should be
864*2999Sstans 				 * all the remaining clean pages, asserting
865*2999Sstans 				 * that they are not modified.
866*2999Sstans 				 */
867*2999Sstans 				page_t	*chk = pp;
868*2999Sstans 				int	attr;
8690Sstevel@tonic-gate 
870*2999Sstans 				page_vpsub(&vp->v_pages, mark);
871*2999Sstans 				page_vpadd(where_to_move, mark);
872*2999Sstans 				do {
873*2999Sstans 					chk = chk->p_vpprev;
874*2999Sstans 					ASSERT(chk != end);
875*2999Sstans 					if (chk == mark)
876*2999Sstans 						continue;
877*2999Sstans 					attr = hat_page_getattr(chk, P_MOD |
878*2999Sstans 					    P_REF);
879*2999Sstans 					if ((attr & P_MOD) == 0)
880*2999Sstans 						continue;
881*2999Sstans 					panic("v_pages list not all clean: "
882*2999Sstans 					    "page_t*=%p vnode=%p off=%lx "
883*2999Sstans 					    "attr=0x%x last clean page_t*=%p\n",
884*2999Sstans 					    (void *)chk, (void *)chk->p_vnode,
885*2999Sstans 					    (long)chk->p_offset, attr,
886*2999Sstans 					    (void *)pp);
887*2999Sstans 				} while (chk != vp->v_pages);
8880Sstevel@tonic-gate #endif
889*2999Sstans 				break;
890*2999Sstans 			} else if (!(flags & B_ASYNC) && !hat_ismod(pp)) {
891*2999Sstans 				/*
892*2999Sstans 				 * Couldn't get io lock, wait until IO is done.
893*2999Sstans 				 * Block only for sync IO since we don't want
894*2999Sstans 				 * to block async IO.
895*2999Sstans 				 */
896*2999Sstans 				mutex_exit(vphm);
897*2999Sstans 				page_io_wait(pp);
898*2999Sstans 				mutex_enter(vphm);
899*2999Sstans 				continue;
900*2999Sstans 			}
9010Sstevel@tonic-gate 		}
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 		/*
9040Sstevel@tonic-gate 		 * If we are supposed to invalidate or free this
9050Sstevel@tonic-gate 		 * page, then we need an exclusive lock.
9060Sstevel@tonic-gate 		 */
9070Sstevel@tonic-gate 		se = (flags & (B_INVAL | B_FREE)) ? SE_EXCL : SE_SHARED;
9080Sstevel@tonic-gate 
9090Sstevel@tonic-gate 		/*
9100Sstevel@tonic-gate 		 * We must acquire the page lock for all synchronous
9110Sstevel@tonic-gate 		 * operations (invalidate, free and write).
9120Sstevel@tonic-gate 		 */
9130Sstevel@tonic-gate 		if ((flags & B_INVAL) != 0 || (flags & B_ASYNC) == 0) {
9140Sstevel@tonic-gate 			/*
9150Sstevel@tonic-gate 			 * If the page_lock() drops the mutex
9160Sstevel@tonic-gate 			 * we must retry the loop.
9170Sstevel@tonic-gate 			 */
9180Sstevel@tonic-gate 			if (!page_lock(pp, se, vphm, P_NO_RECLAIM))
9190Sstevel@tonic-gate 				continue;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 			/*
9220Sstevel@tonic-gate 			 * It's ok to move the marker page now.
9230Sstevel@tonic-gate 			 */
9240Sstevel@tonic-gate 			page_vpsub(&vp->v_pages, mark);
9250Sstevel@tonic-gate 			page_vpadd(where_to_move, mark);
9260Sstevel@tonic-gate 		} else {
9270Sstevel@tonic-gate 
9280Sstevel@tonic-gate 			/*
9290Sstevel@tonic-gate 			 * update the marker page for all remaining cases
9300Sstevel@tonic-gate 			 */
9310Sstevel@tonic-gate 			page_vpsub(&vp->v_pages, mark);
9320Sstevel@tonic-gate 			page_vpadd(where_to_move, mark);
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 			/*
9350Sstevel@tonic-gate 			 * For write backs, If we can't lock the page, it's
9360Sstevel@tonic-gate 			 * invalid or in the process of being destroyed.  Skip
9370Sstevel@tonic-gate 			 * it, assuming someone else is writing it.
9380Sstevel@tonic-gate 			 */
9390Sstevel@tonic-gate 			if (!page_trylock(pp, se))
9400Sstevel@tonic-gate 				continue;
9410Sstevel@tonic-gate 		}
9420Sstevel@tonic-gate 
9430Sstevel@tonic-gate 		ASSERT(pp->p_vnode == vp);
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate 		/*
9460Sstevel@tonic-gate 		 * Successfully locked the page, now figure out what to
9470Sstevel@tonic-gate 		 * do with it. Free pages are easily dealt with, invalidate
9480Sstevel@tonic-gate 		 * if desired or just go on to the next page.
9490Sstevel@tonic-gate 		 */
9500Sstevel@tonic-gate 		if (PP_ISFREE(pp)) {
9510Sstevel@tonic-gate 			if ((flags & B_INVAL) == 0) {
9520Sstevel@tonic-gate 				page_unlock(pp);
9530Sstevel@tonic-gate 				continue;
9540Sstevel@tonic-gate 			}
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate 			/*
9570Sstevel@tonic-gate 			 * Invalidate (destroy) the page.
9580Sstevel@tonic-gate 			 */
9590Sstevel@tonic-gate 			mutex_exit(vphm);
9600Sstevel@tonic-gate 			page_destroy_free(pp);
9610Sstevel@tonic-gate 			mutex_enter(vphm);
9620Sstevel@tonic-gate 			continue;
9630Sstevel@tonic-gate 		}
9640Sstevel@tonic-gate 
9650Sstevel@tonic-gate 		/*
9660Sstevel@tonic-gate 		 * pvn_getdirty() figures out what do do with a dirty page.
9670Sstevel@tonic-gate 		 * If the page is dirty, the putapage() routine will write it
9680Sstevel@tonic-gate 		 * and will kluster any other adjacent dirty pages it can.
9690Sstevel@tonic-gate 		 *
9700Sstevel@tonic-gate 		 * pvn_getdirty() and `(*putapage)' unlock the page.
9710Sstevel@tonic-gate 		 */
9720Sstevel@tonic-gate 		mutex_exit(vphm);
9730Sstevel@tonic-gate 		if (pvn_getdirty(pp, flags)) {
9740Sstevel@tonic-gate 			error = (*putapage)(vp, pp, NULL, NULL, flags, cred);
9750Sstevel@tonic-gate 			if (!err)
9760Sstevel@tonic-gate 				err = error;
9770Sstevel@tonic-gate 		}
9780Sstevel@tonic-gate 		mutex_enter(vphm);
9790Sstevel@tonic-gate 	}
9800Sstevel@tonic-gate 	page_vpsub(&vp->v_pages, mark);
9810Sstevel@tonic-gate 	page_vpsub(&vp->v_pages, end);
9820Sstevel@tonic-gate 
9830Sstevel@tonic-gate leave:
9840Sstevel@tonic-gate 	/*
9850Sstevel@tonic-gate 	 * Release v_pages mutex, also VVMLOCK and wakeup blocked thrds
9860Sstevel@tonic-gate 	 */
9870Sstevel@tonic-gate 	mutex_exit(vphm);
9880Sstevel@tonic-gate 	kmem_cache_free(marker_cache, mark);
9890Sstevel@tonic-gate 	kmem_cache_free(marker_cache, end);
9900Sstevel@tonic-gate 	mutex_enter(&vp->v_lock);
9910Sstevel@tonic-gate 	vp->v_flag &= ~VVMLOCK;
9920Sstevel@tonic-gate 	cv_broadcast(&vp->v_cv);
9930Sstevel@tonic-gate 	mutex_exit(&vp->v_lock);
9940Sstevel@tonic-gate 	return (err);
9950Sstevel@tonic-gate }
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate /*
9980Sstevel@tonic-gate  * Zero out zbytes worth of data. Caller should be aware that this
9990Sstevel@tonic-gate  * routine may enter back into the fs layer (xxx_getpage). Locks
10000Sstevel@tonic-gate  * that the xxx_getpage routine may need should not be held while
10010Sstevel@tonic-gate  * calling this.
10020Sstevel@tonic-gate  */
10030Sstevel@tonic-gate void
10040Sstevel@tonic-gate pvn_vpzero(struct vnode *vp, u_offset_t vplen, size_t zbytes)
10050Sstevel@tonic-gate {
10060Sstevel@tonic-gate 	caddr_t addr;
10070Sstevel@tonic-gate 
10080Sstevel@tonic-gate 	ASSERT(vp->v_type != VCHR);
10090Sstevel@tonic-gate 
10100Sstevel@tonic-gate 	if (vp->v_pages == NULL)
10110Sstevel@tonic-gate 		return;
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate 	/*
10140Sstevel@tonic-gate 	 * zbytes may be zero but there still may be some portion of
10150Sstevel@tonic-gate 	 * a page which needs clearing (since zbytes is a function
10160Sstevel@tonic-gate 	 * of filesystem block size, not pagesize.)
10170Sstevel@tonic-gate 	 */
10180Sstevel@tonic-gate 	if (zbytes == 0 && (PAGESIZE - (vplen & PAGEOFFSET)) == 0)
10190Sstevel@tonic-gate 		return;
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 	/*
10220Sstevel@tonic-gate 	 * We get the last page and handle the partial
10230Sstevel@tonic-gate 	 * zeroing via kernel mappings.  This will make the page
10240Sstevel@tonic-gate 	 * dirty so that we know that when this page is written
10250Sstevel@tonic-gate 	 * back, the zeroed information will go out with it.  If
10260Sstevel@tonic-gate 	 * the page is not currently in memory, then the kzero
10270Sstevel@tonic-gate 	 * operation will cause it to be brought it.  We use kzero
10280Sstevel@tonic-gate 	 * instead of bzero so that if the page cannot be read in
10290Sstevel@tonic-gate 	 * for any reason, the system will not panic.  We need
10300Sstevel@tonic-gate 	 * to zero out a minimum of the fs given zbytes, but we
10310Sstevel@tonic-gate 	 * might also have to do more to get the entire last page.
10320Sstevel@tonic-gate 	 */
10330Sstevel@tonic-gate 
10340Sstevel@tonic-gate 	if ((zbytes + (vplen & MAXBOFFSET)) > MAXBSIZE)
10350Sstevel@tonic-gate 		panic("pvn_vptrunc zbytes");
10360Sstevel@tonic-gate 	addr = segmap_getmapflt(segkmap, vp, vplen,
10370Sstevel@tonic-gate 	    MAX(zbytes, PAGESIZE - (vplen & PAGEOFFSET)), 1, S_WRITE);
10380Sstevel@tonic-gate 	(void) kzero(addr + (vplen & MAXBOFFSET),
10390Sstevel@tonic-gate 	    MAX(zbytes, PAGESIZE - (vplen & PAGEOFFSET)));
10400Sstevel@tonic-gate 	(void) segmap_release(segkmap, addr, SM_WRITE | SM_ASYNC);
10410Sstevel@tonic-gate }
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate /*
10440Sstevel@tonic-gate  * Handles common work of the VOP_GETPAGE routines when more than
10450Sstevel@tonic-gate  * one page must be returned by calling a file system specific operation
10460Sstevel@tonic-gate  * to do most of the work.  Must be called with the vp already locked
10470Sstevel@tonic-gate  * by the VOP_GETPAGE routine.
10480Sstevel@tonic-gate  */
10490Sstevel@tonic-gate int
10500Sstevel@tonic-gate pvn_getpages(
10510Sstevel@tonic-gate 	int (*getpage)(vnode_t *, u_offset_t, size_t, uint_t *, page_t *[],
10520Sstevel@tonic-gate 		size_t, struct seg *, caddr_t, enum seg_rw, cred_t *),
10530Sstevel@tonic-gate 	struct vnode *vp,
10540Sstevel@tonic-gate 	u_offset_t off,
10550Sstevel@tonic-gate 	size_t len,
10560Sstevel@tonic-gate 	uint_t *protp,
10570Sstevel@tonic-gate 	page_t *pl[],
10580Sstevel@tonic-gate 	size_t plsz,
10590Sstevel@tonic-gate 	struct seg *seg,
10600Sstevel@tonic-gate 	caddr_t addr,
10610Sstevel@tonic-gate 	enum seg_rw rw,
10620Sstevel@tonic-gate 	struct cred *cred)
10630Sstevel@tonic-gate {
10640Sstevel@tonic-gate 	page_t **ppp;
10650Sstevel@tonic-gate 	u_offset_t o, eoff;
10660Sstevel@tonic-gate 	size_t sz, xlen;
10670Sstevel@tonic-gate 	int err;
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	ASSERT(plsz >= len);		/* insure that we have enough space */
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate 	/*
10720Sstevel@tonic-gate 	 * Loop one page at a time and let getapage function fill
10730Sstevel@tonic-gate 	 * in the next page in array.  We only allow one page to be
10740Sstevel@tonic-gate 	 * returned at a time (except for the last page) so that we
10750Sstevel@tonic-gate 	 * don't have any problems with duplicates and other such
10760Sstevel@tonic-gate 	 * painful problems.  This is a very simple minded algorithm,
10770Sstevel@tonic-gate 	 * but it does the job correctly.  We hope that the cost of a
10780Sstevel@tonic-gate 	 * getapage call for a resident page that we might have been
10790Sstevel@tonic-gate 	 * able to get from an earlier call doesn't cost too much.
10800Sstevel@tonic-gate 	 */
10810Sstevel@tonic-gate 	ppp = pl;
10820Sstevel@tonic-gate 	sz = PAGESIZE;
10830Sstevel@tonic-gate 	eoff = off + len;
10840Sstevel@tonic-gate 	xlen = len;
10850Sstevel@tonic-gate 	for (o = off; o < eoff; o += PAGESIZE, addr += PAGESIZE,
10860Sstevel@tonic-gate 	    xlen -= PAGESIZE) {
10870Sstevel@tonic-gate 		if (o + PAGESIZE >= eoff) {
10880Sstevel@tonic-gate 			/*
10890Sstevel@tonic-gate 			 * Last time through - allow the all of
10900Sstevel@tonic-gate 			 * what's left of the pl[] array to be used.
10910Sstevel@tonic-gate 			 */
10920Sstevel@tonic-gate 			sz = plsz - (o - off);
10930Sstevel@tonic-gate 		}
10940Sstevel@tonic-gate 		err = (*getpage)(vp, o, xlen, protp, ppp, sz, seg, addr,
10950Sstevel@tonic-gate 		    rw, cred);
10960Sstevel@tonic-gate 		if (err) {
10970Sstevel@tonic-gate 			/*
10980Sstevel@tonic-gate 			 * Release any pages we already got.
10990Sstevel@tonic-gate 			 */
11000Sstevel@tonic-gate 			if (o > off && pl != NULL) {
11010Sstevel@tonic-gate 				for (ppp = pl; *ppp != NULL; *ppp++ = NULL)
11020Sstevel@tonic-gate 					(void) page_release(*ppp, 1);
11030Sstevel@tonic-gate 			}
11040Sstevel@tonic-gate 			break;
11050Sstevel@tonic-gate 		}
11060Sstevel@tonic-gate 		if (pl != NULL)
11070Sstevel@tonic-gate 			ppp++;
11080Sstevel@tonic-gate 	}
11090Sstevel@tonic-gate 	return (err);
11100Sstevel@tonic-gate }
11110Sstevel@tonic-gate 
11120Sstevel@tonic-gate /*
11130Sstevel@tonic-gate  * Initialize the page list array.
11140Sstevel@tonic-gate  */
11150Sstevel@tonic-gate void
11160Sstevel@tonic-gate pvn_plist_init(page_t *pp, page_t *pl[], size_t plsz,
11170Sstevel@tonic-gate     u_offset_t off, size_t io_len, enum seg_rw rw)
11180Sstevel@tonic-gate {
11190Sstevel@tonic-gate 	ssize_t sz;
11200Sstevel@tonic-gate 	page_t *ppcur, **ppp;
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	if (plsz >= io_len) {
11230Sstevel@tonic-gate 		/*
11240Sstevel@tonic-gate 		 * Everything fits, set up to load
11250Sstevel@tonic-gate 		 * all the pages.
11260Sstevel@tonic-gate 		 */
11270Sstevel@tonic-gate 		sz = io_len;
11280Sstevel@tonic-gate 	} else {
11290Sstevel@tonic-gate 		/*
11300Sstevel@tonic-gate 		 * Set up to load plsz worth
11310Sstevel@tonic-gate 		 * starting at the needed page.
11320Sstevel@tonic-gate 		 */
11330Sstevel@tonic-gate 		while (pp->p_offset != off) {
11340Sstevel@tonic-gate 			/* XXX - Do we need this assert? */
11350Sstevel@tonic-gate 			ASSERT(pp->p_next->p_offset !=
11360Sstevel@tonic-gate 			    pp->p_offset);
11370Sstevel@tonic-gate 			/*
11380Sstevel@tonic-gate 			 * Remove page from the i/o list,
11390Sstevel@tonic-gate 			 * release the i/o and the page lock.
11400Sstevel@tonic-gate 			 */
11410Sstevel@tonic-gate 			ppcur = pp;
11420Sstevel@tonic-gate 			page_sub(&pp, ppcur);
11430Sstevel@tonic-gate 			page_io_unlock(ppcur);
11440Sstevel@tonic-gate 			(void) page_release(ppcur, 1);
11450Sstevel@tonic-gate 		}
11460Sstevel@tonic-gate 		sz = plsz;
11470Sstevel@tonic-gate 	}
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate 	/*
11500Sstevel@tonic-gate 	 * Initialize the page list array.
11510Sstevel@tonic-gate 	 */
11520Sstevel@tonic-gate 	ppp = pl;
11530Sstevel@tonic-gate 	do {
11540Sstevel@tonic-gate 		ppcur = pp;
11550Sstevel@tonic-gate 		*ppp++ = ppcur;
11560Sstevel@tonic-gate 		page_sub(&pp, ppcur);
11570Sstevel@tonic-gate 		page_io_unlock(ppcur);
11580Sstevel@tonic-gate 		if (rw != S_CREATE)
11590Sstevel@tonic-gate 			page_downgrade(ppcur);
11600Sstevel@tonic-gate 		sz -= PAGESIZE;
11610Sstevel@tonic-gate 	} while (sz > 0 && pp != NULL);
11620Sstevel@tonic-gate 	*ppp = NULL;		/* terminate list */
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 	/*
11650Sstevel@tonic-gate 	 * Now free the remaining pages that weren't
11660Sstevel@tonic-gate 	 * loaded in the page list.
11670Sstevel@tonic-gate 	 */
11680Sstevel@tonic-gate 	while (pp != NULL) {
11690Sstevel@tonic-gate 		ppcur = pp;
11700Sstevel@tonic-gate 		page_sub(&pp, ppcur);
11710Sstevel@tonic-gate 		page_io_unlock(ppcur);
11720Sstevel@tonic-gate 		(void) page_release(ppcur, 1);
11730Sstevel@tonic-gate 	}
11740Sstevel@tonic-gate }
1175