xref: /netbsd-src/sys/ufs/lfs/lfs_pages.c (revision df7bd27d7df0177599d89b1c03ba5ae066118d86)
1*df7bd27dSriastradh /*	$NetBSD: lfs_pages.c,v 1.27 2023/04/11 14:50:47 riastradh Exp $	*/
27de8c6f8Sdholland 
37de8c6f8Sdholland /*-
45978ddc6Sad  * Copyright (c) 1999, 2000, 2001, 2002, 2003, 2019 The NetBSD Foundation, Inc.
57de8c6f8Sdholland  * All rights reserved.
67de8c6f8Sdholland  *
77de8c6f8Sdholland  * This code is derived from software contributed to The NetBSD Foundation
87de8c6f8Sdholland  * by Konrad E. Schroder <perseant@hhhh.org>.
97de8c6f8Sdholland  *
107de8c6f8Sdholland  * Redistribution and use in source and binary forms, with or without
117de8c6f8Sdholland  * modification, are permitted provided that the following conditions
127de8c6f8Sdholland  * are met:
137de8c6f8Sdholland  * 1. Redistributions of source code must retain the above copyright
147de8c6f8Sdholland  *    notice, this list of conditions and the following disclaimer.
157de8c6f8Sdholland  * 2. Redistributions in binary form must reproduce the above copyright
167de8c6f8Sdholland  *    notice, this list of conditions and the following disclaimer in the
177de8c6f8Sdholland  *    documentation and/or other materials provided with the distribution.
187de8c6f8Sdholland  *
197de8c6f8Sdholland  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
207de8c6f8Sdholland  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
217de8c6f8Sdholland  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
227de8c6f8Sdholland  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
237de8c6f8Sdholland  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
247de8c6f8Sdholland  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
257de8c6f8Sdholland  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
267de8c6f8Sdholland  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
277de8c6f8Sdholland  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
287de8c6f8Sdholland  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
297de8c6f8Sdholland  * POSSIBILITY OF SUCH DAMAGE.
307de8c6f8Sdholland  */
317de8c6f8Sdholland /*
327de8c6f8Sdholland  * Copyright (c) 1986, 1989, 1991, 1993, 1995
337de8c6f8Sdholland  *	The Regents of the University of California.  All rights reserved.
347de8c6f8Sdholland  *
357de8c6f8Sdholland  * Redistribution and use in source and binary forms, with or without
367de8c6f8Sdholland  * modification, are permitted provided that the following conditions
377de8c6f8Sdholland  * are met:
387de8c6f8Sdholland  * 1. Redistributions of source code must retain the above copyright
397de8c6f8Sdholland  *    notice, this list of conditions and the following disclaimer.
407de8c6f8Sdholland  * 2. Redistributions in binary form must reproduce the above copyright
417de8c6f8Sdholland  *    notice, this list of conditions and the following disclaimer in the
427de8c6f8Sdholland  *    documentation and/or other materials provided with the distribution.
437de8c6f8Sdholland  * 3. Neither the name of the University nor the names of its contributors
447de8c6f8Sdholland  *    may be used to endorse or promote products derived from this software
457de8c6f8Sdholland  *    without specific prior written permission.
467de8c6f8Sdholland  *
477de8c6f8Sdholland  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
487de8c6f8Sdholland  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
497de8c6f8Sdholland  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
507de8c6f8Sdholland  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
517de8c6f8Sdholland  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
527de8c6f8Sdholland  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
537de8c6f8Sdholland  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
547de8c6f8Sdholland  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
557de8c6f8Sdholland  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
567de8c6f8Sdholland  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
577de8c6f8Sdholland  * SUCH DAMAGE.
587de8c6f8Sdholland  *
597de8c6f8Sdholland  *	@(#)lfs_vnops.c	8.13 (Berkeley) 6/10/95
607de8c6f8Sdholland  */
617de8c6f8Sdholland 
627de8c6f8Sdholland #include <sys/cdefs.h>
63*df7bd27dSriastradh __KERNEL_RCSID(0, "$NetBSD: lfs_pages.c,v 1.27 2023/04/11 14:50:47 riastradh Exp $");
647de8c6f8Sdholland 
657de8c6f8Sdholland #ifdef _KERNEL_OPT
667de8c6f8Sdholland #include "opt_compat_netbsd.h"
677de8c6f8Sdholland #include "opt_uvm_page_trkown.h"
687de8c6f8Sdholland #endif
697de8c6f8Sdholland 
707de8c6f8Sdholland #include <sys/param.h>
717de8c6f8Sdholland #include <sys/systm.h>
727de8c6f8Sdholland #include <sys/namei.h>
737de8c6f8Sdholland #include <sys/resourcevar.h>
747de8c6f8Sdholland #include <sys/kernel.h>
757de8c6f8Sdholland #include <sys/file.h>
767de8c6f8Sdholland #include <sys/stat.h>
777de8c6f8Sdholland #include <sys/buf.h>
787de8c6f8Sdholland #include <sys/proc.h>
797de8c6f8Sdholland #include <sys/mount.h>
807de8c6f8Sdholland #include <sys/vnode.h>
817de8c6f8Sdholland #include <sys/pool.h>
827de8c6f8Sdholland #include <sys/signalvar.h>
837de8c6f8Sdholland #include <sys/kauth.h>
847de8c6f8Sdholland #include <sys/syslog.h>
857de8c6f8Sdholland #include <sys/fstrans.h>
867de8c6f8Sdholland 
877de8c6f8Sdholland #include <miscfs/fifofs/fifo.h>
887de8c6f8Sdholland #include <miscfs/genfs/genfs.h>
897de8c6f8Sdholland #include <miscfs/specfs/specdev.h>
907de8c6f8Sdholland 
917de8c6f8Sdholland #include <ufs/lfs/ulfs_inode.h>
927de8c6f8Sdholland #include <ufs/lfs/ulfsmount.h>
937de8c6f8Sdholland #include <ufs/lfs/ulfs_bswap.h>
947de8c6f8Sdholland #include <ufs/lfs/ulfs_extern.h>
957de8c6f8Sdholland 
967de8c6f8Sdholland #include <uvm/uvm.h>
979fc45356Sriastradh #include <uvm/uvm_page.h>
989fc45356Sriastradh #include <uvm/uvm_pager.h>
997de8c6f8Sdholland #include <uvm/uvm_pmap.h>
1007de8c6f8Sdholland #include <uvm/uvm_stat.h>
1017de8c6f8Sdholland 
1027de8c6f8Sdholland #include <ufs/lfs/lfs.h>
10334f0d74cSdholland #include <ufs/lfs/lfs_accessors.h>
1047de8c6f8Sdholland #include <ufs/lfs/lfs_kernel.h>
1057de8c6f8Sdholland #include <ufs/lfs/lfs_extern.h>
1067de8c6f8Sdholland 
10763e604efSmaya extern kcondvar_t lfs_writerd_cv;
1087de8c6f8Sdholland 
1097de8c6f8Sdholland static int check_dirty(struct lfs *, struct vnode *, off_t, off_t, off_t, int, int, struct vm_page **);
1107de8c6f8Sdholland 
1117de8c6f8Sdholland int
lfs_getpages(void * v)1127de8c6f8Sdholland lfs_getpages(void *v)
1137de8c6f8Sdholland {
1147de8c6f8Sdholland 	struct vop_getpages_args /* {
1157de8c6f8Sdholland 		struct vnode *a_vp;
1167de8c6f8Sdholland 		voff_t a_offset;
1177de8c6f8Sdholland 		struct vm_page **a_m;
1187de8c6f8Sdholland 		int *a_count;
1197de8c6f8Sdholland 		int a_centeridx;
1207de8c6f8Sdholland 		vm_prot_t a_access_type;
1217de8c6f8Sdholland 		int a_advice;
1227de8c6f8Sdholland 		int a_flags;
1237de8c6f8Sdholland 	} */ *ap = v;
1247de8c6f8Sdholland 
1257de8c6f8Sdholland 	if (VTOI(ap->a_vp)->i_number == LFS_IFILE_INUM &&
1267de8c6f8Sdholland 	    (ap->a_access_type & VM_PROT_WRITE) != 0) {
1277de8c6f8Sdholland 		return EPERM;
1287de8c6f8Sdholland 	}
1297de8c6f8Sdholland 	if ((ap->a_access_type & VM_PROT_WRITE) != 0) {
1307de8c6f8Sdholland 		mutex_enter(&lfs_lock);
1317de8c6f8Sdholland 		LFS_SET_UINO(VTOI(ap->a_vp), IN_MODIFIED);
1327de8c6f8Sdholland 		mutex_exit(&lfs_lock);
1337de8c6f8Sdholland 	}
1347de8c6f8Sdholland 
1357de8c6f8Sdholland 	/*
1367de8c6f8Sdholland 	 * we're relying on the fact that genfs_getpages() always read in
1377de8c6f8Sdholland 	 * entire filesystem blocks.
1387de8c6f8Sdholland 	 */
1397de8c6f8Sdholland 	return genfs_getpages(v);
1407de8c6f8Sdholland }
1417de8c6f8Sdholland 
1427de8c6f8Sdholland /*
1437de8c6f8Sdholland  * Wait for a page to become unbusy, possibly printing diagnostic messages
1447de8c6f8Sdholland  * as well.
1457de8c6f8Sdholland  *
146d2a0ebb6Sad  * Called with vp->v_uobj.vmobjlock held; return with it held.
1477de8c6f8Sdholland  */
1487de8c6f8Sdholland static void
wait_for_page(struct vnode * vp,struct vm_page * pg,const char * label)1497de8c6f8Sdholland wait_for_page(struct vnode *vp, struct vm_page *pg, const char *label)
1507de8c6f8Sdholland {
151d2a0ebb6Sad 	KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
1527de8c6f8Sdholland 	if ((pg->flags & PG_BUSY) == 0)
1537de8c6f8Sdholland 		return;		/* Nothing to wait for! */
1547de8c6f8Sdholland 
1557de8c6f8Sdholland #if defined(DEBUG) && defined(UVM_PAGE_TRKOWN)
1567de8c6f8Sdholland 	static struct vm_page *lastpg;
1577de8c6f8Sdholland 
1587de8c6f8Sdholland 	if (label != NULL && pg != lastpg) {
1597de8c6f8Sdholland 		if (pg->owner_tag) {
1607de8c6f8Sdholland 			printf("lfs_putpages[%d.%d]: %s: page %p owner %d.%d [%s]\n",
1617de8c6f8Sdholland 			       curproc->p_pid, curlwp->l_lid, label,
1627de8c6f8Sdholland 			       pg, pg->owner, pg->lowner, pg->owner_tag);
1637de8c6f8Sdholland 		} else {
1647de8c6f8Sdholland 			printf("lfs_putpages[%d.%d]: %s: page %p unowned?!\n",
1657de8c6f8Sdholland 			       curproc->p_pid, curlwp->l_lid, label, pg);
1667de8c6f8Sdholland 		}
1677de8c6f8Sdholland 	}
1687de8c6f8Sdholland 	lastpg = pg;
1697de8c6f8Sdholland #endif
1707de8c6f8Sdholland 
1715972ba16Sad 	uvm_pagewait(pg, vp->v_uobj.vmobjlock, "lfsput");
172d2a0ebb6Sad 	rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
1737de8c6f8Sdholland }
1747de8c6f8Sdholland 
1757de8c6f8Sdholland /*
1767de8c6f8Sdholland  * This routine is called by lfs_putpages() when it can't complete the
1777de8c6f8Sdholland  * write because a page is busy.  This means that either (1) someone,
1787de8c6f8Sdholland  * possibly the pagedaemon, is looking at this page, and will give it up
1797de8c6f8Sdholland  * presently; or (2) we ourselves are holding the page busy in the
1807de8c6f8Sdholland  * process of being written (either gathered or actually on its way to
1817de8c6f8Sdholland  * disk).  We don't need to give up the segment lock, but we might need
1827de8c6f8Sdholland  * to call lfs_writeseg() to expedite the page's journey to disk.
1837de8c6f8Sdholland  *
184d2a0ebb6Sad  * Called with vp->v_uobj.vmobjlock held; return with it held.
1857de8c6f8Sdholland  */
1867de8c6f8Sdholland /* #define BUSYWAIT */
1877de8c6f8Sdholland static void
write_and_wait(struct lfs * fs,struct vnode * vp,struct vm_page * pg,int seglocked,const char * label)1887de8c6f8Sdholland write_and_wait(struct lfs *fs, struct vnode *vp, struct vm_page *pg,
1897de8c6f8Sdholland 	       int seglocked, const char *label)
1907de8c6f8Sdholland {
191d2a0ebb6Sad 	KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
1927de8c6f8Sdholland #ifndef BUSYWAIT
1937de8c6f8Sdholland 	struct inode *ip = VTOI(vp);
1947de8c6f8Sdholland 	struct segment *sp = fs->lfs_sp;
1957de8c6f8Sdholland 	int count = 0;
1967de8c6f8Sdholland 
1977de8c6f8Sdholland 	if (pg == NULL)
1987de8c6f8Sdholland 		return;
1997de8c6f8Sdholland 
2007de8c6f8Sdholland 	while (pg->flags & PG_BUSY &&
2017de8c6f8Sdholland 	    pg->uobject == &vp->v_uobj) {
202d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
2037de8c6f8Sdholland 		if (sp->cbpp - sp->bpp > 1) {
2047de8c6f8Sdholland 			/* Write gathered pages */
2057de8c6f8Sdholland 			lfs_updatemeta(sp);
2067de8c6f8Sdholland 			lfs_release_finfo(fs);
2077de8c6f8Sdholland 			(void) lfs_writeseg(fs, sp);
2087de8c6f8Sdholland 
2097de8c6f8Sdholland 			/*
2107de8c6f8Sdholland 			 * Reinitialize FIP
2117de8c6f8Sdholland 			 */
2127de8c6f8Sdholland 			KASSERT(sp->vp == vp);
2137de8c6f8Sdholland 			lfs_acquire_finfo(fs, ip->i_number,
2147de8c6f8Sdholland 					  ip->i_gen);
2157de8c6f8Sdholland 		}
2167de8c6f8Sdholland 		++count;
217d2a0ebb6Sad 		rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
2187de8c6f8Sdholland 		wait_for_page(vp, pg, label);
2197de8c6f8Sdholland 	}
2207de8c6f8Sdholland 	if (label != NULL && count > 1) {
2217de8c6f8Sdholland 		DLOG((DLOG_PAGE, "lfs_putpages[%d]: %s: %sn = %d\n",
2227de8c6f8Sdholland 		      curproc->p_pid, label, (count > 0 ? "looping, " : ""),
2237de8c6f8Sdholland 		      count));
2247de8c6f8Sdholland 	}
2257de8c6f8Sdholland #else
2267de8c6f8Sdholland 	preempt(1);
2277de8c6f8Sdholland #endif
228d2a0ebb6Sad 	KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
2297de8c6f8Sdholland }
2307de8c6f8Sdholland 
2317de8c6f8Sdholland /*
2327de8c6f8Sdholland  * Make sure that for all pages in every block in the given range,
2337de8c6f8Sdholland  * either all are dirty or all are clean.  If any of the pages
2347de8c6f8Sdholland  * we've seen so far are dirty, put the vnode on the paging chain,
2357de8c6f8Sdholland  * and mark it IN_PAGING.
2367de8c6f8Sdholland  *
2377de8c6f8Sdholland  * If checkfirst != 0, don't check all the pages but return at the
2387de8c6f8Sdholland  * first dirty page.
2397de8c6f8Sdholland  */
2407de8c6f8Sdholland static int
check_dirty(struct lfs * fs,struct vnode * vp,off_t startoffset,off_t endoffset,off_t blkeof,int flags,int checkfirst,struct vm_page ** pgp)2417de8c6f8Sdholland check_dirty(struct lfs *fs, struct vnode *vp,
2427de8c6f8Sdholland 	    off_t startoffset, off_t endoffset, off_t blkeof,
2437de8c6f8Sdholland 	    int flags, int checkfirst, struct vm_page **pgp)
2447de8c6f8Sdholland {
245e433d111Schristos 	struct vm_page *pgs[MAXBSIZE / MIN_PAGE_SIZE], *pg;
2467de8c6f8Sdholland 	off_t soff = 0; /* XXX: gcc */
2477de8c6f8Sdholland 	voff_t off;
2487de8c6f8Sdholland 	int i;
2497de8c6f8Sdholland 	int nonexistent;
2507de8c6f8Sdholland 	int any_dirty;	/* number of dirty pages */
2517de8c6f8Sdholland 	int dirty;	/* number of dirty pages in a block */
2527de8c6f8Sdholland 	int tdirty;
253f59b8f4bSdholland 	int pages_per_block = lfs_sb_getbsize(fs) >> PAGE_SHIFT;
2547de8c6f8Sdholland 	int pagedaemon = (curlwp == uvm.pagedaemon_lwp);
2557de8c6f8Sdholland 
256d2a0ebb6Sad 	KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
2577de8c6f8Sdholland 	ASSERT_MAYBE_SEGLOCK(fs);
2587de8c6f8Sdholland   top:
2597de8c6f8Sdholland 	any_dirty = 0;
2607de8c6f8Sdholland 
2617de8c6f8Sdholland 	soff = startoffset;
262881d12e6Sad 	KASSERT((soff & (lfs_sb_getbsize(fs) - 1)) == 0);
263881d12e6Sad 	while (soff < MIN(blkeof, endoffset)) {
2647de8c6f8Sdholland 
2657de8c6f8Sdholland 		/*
2667de8c6f8Sdholland 		 * Mark all pages in extended range busy; find out if any
2677de8c6f8Sdholland 		 * of them are dirty.
2687de8c6f8Sdholland 		 */
2697de8c6f8Sdholland 		nonexistent = dirty = 0;
2707de8c6f8Sdholland 		for (i = 0; i == 0 || i < pages_per_block; i++) {
271d2a0ebb6Sad 			KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
2727de8c6f8Sdholland 			off = soff + (i << PAGE_SHIFT);
2737de8c6f8Sdholland 			pgs[i] = pg = uvm_pagelookup(&vp->v_uobj, off);
2747de8c6f8Sdholland 			if (pg == NULL) {
2757de8c6f8Sdholland 				++nonexistent;
2767de8c6f8Sdholland 				continue;
2777de8c6f8Sdholland 			}
2787de8c6f8Sdholland 			KASSERT(pg != NULL);
2797de8c6f8Sdholland 
2807de8c6f8Sdholland 			/*
2817de8c6f8Sdholland 			 * If we're holding the segment lock, we can deadlock
2827de8c6f8Sdholland 			 * against a process that has our page and is waiting
2837de8c6f8Sdholland 			 * for the cleaner, while the cleaner waits for the
2847de8c6f8Sdholland 			 * segment lock.  Just bail in that case.
2857de8c6f8Sdholland 			 */
2867de8c6f8Sdholland 			if ((pg->flags & PG_BUSY) &&
2877de8c6f8Sdholland 			    (pagedaemon || LFS_SEGLOCK_HELD(fs))) {
2887de8c6f8Sdholland 				if (i > 0)
2897de8c6f8Sdholland 					uvm_page_unbusy(pgs, i);
2907de8c6f8Sdholland 				DLOG((DLOG_PAGE, "lfs_putpages: avoiding 3-way or pagedaemon deadlock\n"));
2917de8c6f8Sdholland 				if (pgp)
2927de8c6f8Sdholland 					*pgp = pg;
293d2a0ebb6Sad 				KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
2947de8c6f8Sdholland 				return -1;
2957de8c6f8Sdholland 			}
2967de8c6f8Sdholland 
2977de8c6f8Sdholland 			while (pg->flags & PG_BUSY) {
2987de8c6f8Sdholland 				wait_for_page(vp, pg, NULL);
299d2a0ebb6Sad 				KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
3007de8c6f8Sdholland 				if (i > 0)
3017de8c6f8Sdholland 					uvm_page_unbusy(pgs, i);
302d2a0ebb6Sad 				KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
3037de8c6f8Sdholland 				goto top;
3047de8c6f8Sdholland 			}
3057de8c6f8Sdholland 			pg->flags |= PG_BUSY;
3067de8c6f8Sdholland 			UVM_PAGE_OWN(pg, "lfs_putpages");
3077de8c6f8Sdholland 
3087de8c6f8Sdholland 			pmap_page_protect(pg, VM_PROT_NONE);
30905a3457eSad 			tdirty =
31005a3457eSad 			    uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_CLEAN &&
31105a3457eSad 			    (uvm_pagegetdirty(pg) == UVM_PAGE_STATUS_DIRTY ||
31205a3457eSad 			    pmap_clear_modify(pg));
3137de8c6f8Sdholland 			dirty += tdirty;
3147de8c6f8Sdholland 		}
3151f8943ccSad 		if ((pages_per_block > 0 && nonexistent >= pages_per_block) ||
3161f8943ccSad 		    (pages_per_block == 0 && nonexistent > 0)) {
317881d12e6Sad 			soff += MAX(PAGE_SIZE, lfs_sb_getbsize(fs));
3187de8c6f8Sdholland 			continue;
3197de8c6f8Sdholland 		}
3207de8c6f8Sdholland 
3217de8c6f8Sdholland 		any_dirty += dirty;
3227de8c6f8Sdholland 		KASSERT(nonexistent == 0);
323d2a0ebb6Sad 		KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
3247de8c6f8Sdholland 
3257de8c6f8Sdholland 		/*
3267de8c6f8Sdholland 		 * If any are dirty make all dirty; unbusy them,
3277de8c6f8Sdholland 		 * but if we were asked to clean, wire them so that
3287de8c6f8Sdholland 		 * the pagedaemon doesn't bother us about them while
3297de8c6f8Sdholland 		 * they're on their way to disk.
3307de8c6f8Sdholland 		 */
3317de8c6f8Sdholland 		for (i = 0; i == 0 || i < pages_per_block; i++) {
332d2a0ebb6Sad 			KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
3337de8c6f8Sdholland 			pg = pgs[i];
33405a3457eSad 			KASSERT(!(uvm_pagegetdirty(pg) != UVM_PAGE_STATUS_DIRTY
33505a3457eSad 			    && (pg->flags & PG_DELWRI)));
3367de8c6f8Sdholland 			KASSERT(pg->flags & PG_BUSY);
3377de8c6f8Sdholland 			if (dirty) {
33805a3457eSad 				uvm_pagemarkdirty(pg, UVM_PAGE_STATUS_DIRTY);
3397de8c6f8Sdholland 				if (flags & PGO_FREE) {
3407de8c6f8Sdholland 					/*
3417de8c6f8Sdholland 					 * Wire the page so that
3427de8c6f8Sdholland 					 * pdaemon doesn't see it again.
3437de8c6f8Sdholland 					 */
34494843b13Sad 					uvm_pagelock(pg);
3457de8c6f8Sdholland 					uvm_pagewire(pg);
34694843b13Sad 					uvm_pageunlock(pg);
3477de8c6f8Sdholland 
3487de8c6f8Sdholland 					/* Suspended write flag */
3497de8c6f8Sdholland 					pg->flags |= PG_DELWRI;
3507de8c6f8Sdholland 				}
3517de8c6f8Sdholland 			}
3521912643fSad 			pg->flags &= ~PG_BUSY;
3535972ba16Sad 			uvm_pagelock(pg);
3541912643fSad 			uvm_pagewakeup(pg);
3555972ba16Sad 			uvm_pageunlock(pg);
3567de8c6f8Sdholland 			UVM_PAGE_OWN(pg, NULL);
3577de8c6f8Sdholland 		}
3587de8c6f8Sdholland 
3597de8c6f8Sdholland 		if (checkfirst && any_dirty)
3607de8c6f8Sdholland 			break;
3617de8c6f8Sdholland 
362f59b8f4bSdholland 		soff += MAX(PAGE_SIZE, lfs_sb_getbsize(fs));
3637de8c6f8Sdholland 	}
3647de8c6f8Sdholland 
365d2a0ebb6Sad 	KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
3667de8c6f8Sdholland 	return any_dirty;
3677de8c6f8Sdholland }
3687de8c6f8Sdholland 
3697de8c6f8Sdholland /*
3707de8c6f8Sdholland  * lfs_putpages functions like genfs_putpages except that
3717de8c6f8Sdholland  *
3727de8c6f8Sdholland  * (1) It needs to bounds-check the incoming requests to ensure that
3737de8c6f8Sdholland  *     they are block-aligned; if they are not, expand the range and
3747de8c6f8Sdholland  *     do the right thing in case, e.g., the requested range is clean
3757de8c6f8Sdholland  *     but the expanded range is dirty.
3767de8c6f8Sdholland  *
3777de8c6f8Sdholland  * (2) It needs to explicitly send blocks to be written when it is done.
3787de8c6f8Sdholland  *     If VOP_PUTPAGES is called without the seglock held, we simply take
3797de8c6f8Sdholland  *     the seglock and let lfs_segunlock wait for us.
3807de8c6f8Sdholland  *     XXX There might be a bad situation if we have to flush a vnode while
3817de8c6f8Sdholland  *     XXX lfs_markv is in operation.  As of this writing we panic in this
3827de8c6f8Sdholland  *     XXX case.
3837de8c6f8Sdholland  *
3847de8c6f8Sdholland  * Assumptions:
3857de8c6f8Sdholland  *
3867de8c6f8Sdholland  * (1) The caller does not hold any pages in this vnode busy.  If it does,
3877de8c6f8Sdholland  *     there is a danger that when we expand the page range and busy the
3887de8c6f8Sdholland  *     pages we will deadlock.
3897de8c6f8Sdholland  *
390d2a0ebb6Sad  * (2) We are called with vp->v_uobj.vmobjlock held; we must return with it
3917de8c6f8Sdholland  *     released.
3927de8c6f8Sdholland  *
3937de8c6f8Sdholland  * (3) We don't absolutely have to free pages right away, provided that
3947de8c6f8Sdholland  *     the request does not have PGO_SYNCIO.  When the pagedaemon gives
3957de8c6f8Sdholland  *     us a request with PGO_FREE, we take the pages out of the paging
3967de8c6f8Sdholland  *     queue and wake up the writer, which will handle freeing them for us.
3977de8c6f8Sdholland  *
3987de8c6f8Sdholland  *     We ensure that for any filesystem block, all pages for that
3997de8c6f8Sdholland  *     block are either resident or not, even if those pages are higher
4007de8c6f8Sdholland  *     than EOF; that means that we will be getting requests to free
4017de8c6f8Sdholland  *     "unused" pages above EOF all the time, and should ignore them.
4027de8c6f8Sdholland  *
4037de8c6f8Sdholland  * (4) If we are called with PGO_LOCKED, the finfo array we are to write
4047de8c6f8Sdholland  *     into has been set up for us by lfs_writefile.  If not, we will
4057de8c6f8Sdholland  *     have to handle allocating and/or freeing an finfo entry.
4067de8c6f8Sdholland  *
4077de8c6f8Sdholland  * XXX note that we're (ab)using PGO_LOCKED as "seglock held".
4087de8c6f8Sdholland  */
4097de8c6f8Sdholland 
4107de8c6f8Sdholland /* How many times to loop before we should start to worry */
4117de8c6f8Sdholland #define TOOMANY 4
4127de8c6f8Sdholland 
4137de8c6f8Sdholland int
lfs_putpages(void * v)4147de8c6f8Sdholland lfs_putpages(void *v)
4157de8c6f8Sdholland {
4167de8c6f8Sdholland 	int error;
4177de8c6f8Sdholland 	struct vop_putpages_args /* {
4187de8c6f8Sdholland 		struct vnode *a_vp;
4197de8c6f8Sdholland 		voff_t a_offlo;
4207de8c6f8Sdholland 		voff_t a_offhi;
4217de8c6f8Sdholland 		int a_flags;
4227de8c6f8Sdholland 	} */ *ap = v;
4237de8c6f8Sdholland 	struct vnode *vp;
4247de8c6f8Sdholland 	struct inode *ip;
4257de8c6f8Sdholland 	struct lfs *fs;
4267de8c6f8Sdholland 	struct segment *sp;
4277de8c6f8Sdholland 	off_t origoffset, startoffset, endoffset, origendoffset, blkeof;
4287de8c6f8Sdholland 	off_t off, max_endoffset;
4297de8c6f8Sdholland 	bool seglocked, sync, pagedaemon, reclaim;
4307de8c6f8Sdholland 	struct vm_page *pg, *busypg;
4317de8c6f8Sdholland 	UVMHIST_FUNC("lfs_putpages"); UVMHIST_CALLED(ubchist);
43263ce83dcShannken 	struct mount *trans_mp;
4337de8c6f8Sdholland 	int oreclaim = 0;
4347de8c6f8Sdholland 	int donewriting = 0;
4357de8c6f8Sdholland #ifdef DEBUG
4367de8c6f8Sdholland 	int debug_n_again, debug_n_dirtyclean;
4377de8c6f8Sdholland #endif
4387de8c6f8Sdholland 
4397de8c6f8Sdholland 	vp = ap->a_vp;
4407de8c6f8Sdholland 	ip = VTOI(vp);
4417de8c6f8Sdholland 	fs = ip->i_lfs;
4427de8c6f8Sdholland 	sync = (ap->a_flags & PGO_SYNCIO) != 0;
4437de8c6f8Sdholland 	reclaim = (ap->a_flags & PGO_RECLAIM) != 0;
4447de8c6f8Sdholland 	pagedaemon = (curlwp == uvm.pagedaemon_lwp);
44563ce83dcShannken 	trans_mp = NULL;
4467de8c6f8Sdholland 
447d2a0ebb6Sad 	KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
4487de8c6f8Sdholland 
4497de8c6f8Sdholland 	/* Putpages does nothing for metadata. */
4507de8c6f8Sdholland 	if (vp == fs->lfs_ivnode || vp->v_type != VREG) {
451d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
4527de8c6f8Sdholland 		return 0;
4537de8c6f8Sdholland 	}
4547de8c6f8Sdholland 
45563ce83dcShannken retry:
4567de8c6f8Sdholland 	/*
4577de8c6f8Sdholland 	 * If there are no pages, don't do anything.
4587de8c6f8Sdholland 	 */
4597de8c6f8Sdholland 	if (vp->v_uobj.uo_npages == 0) {
460da3ef92bSad 		mutex_enter(vp->v_interlock);
461881d12e6Sad 		if ((vp->v_iflag & VI_ONWORKLST) &&
4627de8c6f8Sdholland 		    LIST_FIRST(&vp->v_dirtyblkhd) == NULL) {
4637de8c6f8Sdholland 			vn_syncer_remove_from_worklist(vp);
4647de8c6f8Sdholland 		}
465da3ef92bSad 		mutex_exit(vp->v_interlock);
46663ce83dcShannken 		if (trans_mp)
46763ce83dcShannken 			fstrans_done(trans_mp);
468d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
4697de8c6f8Sdholland 
4707de8c6f8Sdholland 		/* Remove us from paging queue, if we were on it */
4717de8c6f8Sdholland 		mutex_enter(&lfs_lock);
4728f063ba0Smaya 		if (ip->i_state & IN_PAGING) {
4738f063ba0Smaya 			ip->i_state &= ~IN_PAGING;
4747de8c6f8Sdholland 			TAILQ_REMOVE(&fs->lfs_pchainhd, ip, i_lfs_pchain);
4757de8c6f8Sdholland 		}
4767de8c6f8Sdholland 		mutex_exit(&lfs_lock);
4777de8c6f8Sdholland 
478d2a0ebb6Sad 		KASSERT(!rw_write_held(vp->v_uobj.vmobjlock));
4797de8c6f8Sdholland 		return 0;
4807de8c6f8Sdholland 	}
4817de8c6f8Sdholland 
4827de8c6f8Sdholland 	blkeof = lfs_blkroundup(fs, ip->i_size);
4837de8c6f8Sdholland 
4847de8c6f8Sdholland 	/*
4857de8c6f8Sdholland 	 * Ignore requests to free pages past EOF but in the same block
4867de8c6f8Sdholland 	 * as EOF, unless the vnode is being reclaimed or the request
4877de8c6f8Sdholland 	 * is synchronous.  (If the request is sync, it comes from
4887de8c6f8Sdholland 	 * lfs_truncate.)
4897de8c6f8Sdholland 	 *
4907de8c6f8Sdholland 	 * To avoid being flooded with this request, make these pages
4917de8c6f8Sdholland 	 * look "active".
4927de8c6f8Sdholland 	 */
4937de8c6f8Sdholland 	if (!sync && !reclaim &&
4947de8c6f8Sdholland 	    ap->a_offlo >= ip->i_size && ap->a_offlo < blkeof) {
4957de8c6f8Sdholland 		origoffset = ap->a_offlo;
496f59b8f4bSdholland 		for (off = origoffset; off < blkeof; off += lfs_sb_getbsize(fs)) {
4977de8c6f8Sdholland 			pg = uvm_pagelookup(&vp->v_uobj, off);
4987de8c6f8Sdholland 			KASSERT(pg != NULL);
4997de8c6f8Sdholland 			while (pg->flags & PG_BUSY) {
5005972ba16Sad 				uvm_pagewait(pg, vp->v_uobj.vmobjlock, "lfsput2");
501d2a0ebb6Sad 				rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
502*df7bd27dSriastradh 				/* XXX Page can't change identity here? */
503*df7bd27dSriastradh 				KDASSERT(pg ==
504*df7bd27dSriastradh 				    uvm_pagelookup(&vp->v_uobj, off));
5057de8c6f8Sdholland 			}
50694843b13Sad 			uvm_pagelock(pg);
5077de8c6f8Sdholland 			uvm_pageactivate(pg);
50894843b13Sad 			uvm_pageunlock(pg);
5097de8c6f8Sdholland 		}
5107de8c6f8Sdholland 		ap->a_offlo = blkeof;
5117de8c6f8Sdholland 		if (ap->a_offhi > 0 && ap->a_offhi <= ap->a_offlo) {
512d2a0ebb6Sad 			rw_exit(vp->v_uobj.vmobjlock);
5137de8c6f8Sdholland 			return 0;
5147de8c6f8Sdholland 		}
5157de8c6f8Sdholland 	}
5167de8c6f8Sdholland 
5177de8c6f8Sdholland 	/*
5187de8c6f8Sdholland 	 * Extend page range to start and end at block boundaries.
5197de8c6f8Sdholland 	 * (For the purposes of VOP_PUTPAGES, fragments don't exist.)
5207de8c6f8Sdholland 	 */
5217de8c6f8Sdholland 	origoffset = ap->a_offlo;
5227de8c6f8Sdholland 	origendoffset = ap->a_offhi;
523adca8af5Sdholland 	startoffset = origoffset & ~(lfs_sb_getbmask(fs));
524adca8af5Sdholland 	max_endoffset = (trunc_page(LLONG_MAX) >> lfs_sb_getbshift(fs))
525adca8af5Sdholland 					       << lfs_sb_getbshift(fs);
5267de8c6f8Sdholland 
5277de8c6f8Sdholland 	if (origendoffset == 0 || ap->a_flags & PGO_ALLPAGES) {
5287de8c6f8Sdholland 		endoffset = max_endoffset;
5297de8c6f8Sdholland 		origendoffset = endoffset;
5307de8c6f8Sdholland 	} else {
5317de8c6f8Sdholland 		origendoffset = round_page(ap->a_offhi);
5327de8c6f8Sdholland 		endoffset = round_page(lfs_blkroundup(fs, origendoffset));
5337de8c6f8Sdholland 	}
5347de8c6f8Sdholland 
5357de8c6f8Sdholland 	KASSERT(startoffset > 0 || endoffset >= startoffset);
5367de8c6f8Sdholland 	if (startoffset == endoffset) {
5377de8c6f8Sdholland 		/* Nothing to do, why were we called? */
538d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
5397de8c6f8Sdholland 		DLOG((DLOG_PAGE, "lfs_putpages: startoffset = endoffset = %"
5407de8c6f8Sdholland 		      PRId64 "\n", startoffset));
5417de8c6f8Sdholland 		return 0;
5427de8c6f8Sdholland 	}
5437de8c6f8Sdholland 
5447de8c6f8Sdholland 	ap->a_offlo = startoffset;
5457de8c6f8Sdholland 	ap->a_offhi = endoffset;
5467de8c6f8Sdholland 
5477de8c6f8Sdholland 	/*
5487de8c6f8Sdholland 	 * If not cleaning, just send the pages through genfs_putpages
5497de8c6f8Sdholland 	 * to be returned to the pool.
5507de8c6f8Sdholland 	 */
5517de8c6f8Sdholland 	if (!(ap->a_flags & PGO_CLEANIT)) {
5527de8c6f8Sdholland 		DLOG((DLOG_PAGE, "lfs_putpages: no cleanit vn %p ino %d (flags %x)\n",
5537de8c6f8Sdholland 		      vp, (int)ip->i_number, ap->a_flags));
5547de8c6f8Sdholland 		int r = genfs_putpages(v);
555d2a0ebb6Sad 		KASSERT(!rw_write_held(vp->v_uobj.vmobjlock));
5567de8c6f8Sdholland 		return r;
5577de8c6f8Sdholland 	}
5587de8c6f8Sdholland 
55963ce83dcShannken 	if (trans_mp /* && (ap->a_flags & PGO_CLEANIT) != 0 */) {
56063ce83dcShannken 		if (pagedaemon) {
56163ce83dcShannken 			/* Pagedaemon must not sleep here. */
56263ce83dcShannken 			trans_mp = vp->v_mount;
563287643b0Shannken 			error = fstrans_start_nowait(trans_mp);
56463ce83dcShannken 			if (error) {
565d2a0ebb6Sad 				rw_exit(vp->v_uobj.vmobjlock);
56663ce83dcShannken 				return error;
56763ce83dcShannken 			}
56863ce83dcShannken 		} else {
56963ce83dcShannken 			/*
57063ce83dcShannken 			 * Cannot use vdeadcheck() here as this operation
57163ce83dcShannken 			 * usually gets used from VOP_RECLAIM().  Test for
57263ce83dcShannken 			 * change of v_mount instead and retry on change.
57363ce83dcShannken 			 */
574d2a0ebb6Sad 			rw_exit(vp->v_uobj.vmobjlock);
57563ce83dcShannken 			trans_mp = vp->v_mount;
576287643b0Shannken 			fstrans_start(trans_mp);
57763ce83dcShannken 			if (vp->v_mount != trans_mp) {
57863ce83dcShannken 				fstrans_done(trans_mp);
57963ce83dcShannken 				trans_mp = NULL;
58063ce83dcShannken 			}
58163ce83dcShannken 		}
582d2a0ebb6Sad 		rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
58363ce83dcShannken 		goto retry;
58463ce83dcShannken 	}
58563ce83dcShannken 
5867de8c6f8Sdholland 	/* Set PGO_BUSYFAIL to avoid deadlocks */
5877de8c6f8Sdholland 	ap->a_flags |= PGO_BUSYFAIL;
5887de8c6f8Sdholland 
5897de8c6f8Sdholland 	/*
5907de8c6f8Sdholland 	 * Likewise, if we are asked to clean but the pages are not
5917de8c6f8Sdholland 	 * dirty, we can just free them using genfs_putpages.
5927de8c6f8Sdholland 	 */
5937de8c6f8Sdholland #ifdef DEBUG
5947de8c6f8Sdholland 	debug_n_dirtyclean = 0;
5957de8c6f8Sdholland #endif
5967de8c6f8Sdholland 	do {
5977de8c6f8Sdholland 		int r;
598d2a0ebb6Sad 		KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
5997de8c6f8Sdholland 
6007de8c6f8Sdholland 		/* Count the number of dirty pages */
6017de8c6f8Sdholland 		r = check_dirty(fs, vp, startoffset, endoffset, blkeof,
6027de8c6f8Sdholland 				ap->a_flags, 1, NULL);
6037de8c6f8Sdholland 		if (r < 0) {
6047de8c6f8Sdholland 			/* Pages are busy with another process */
605d2a0ebb6Sad 			rw_exit(vp->v_uobj.vmobjlock);
60663ce83dcShannken 			error = EDEADLK;
60763ce83dcShannken 			goto out;
6087de8c6f8Sdholland 		}
6097de8c6f8Sdholland 		if (r > 0) /* Some pages are dirty */
6107de8c6f8Sdholland 			break;
6117de8c6f8Sdholland 
6127de8c6f8Sdholland 		/*
6137de8c6f8Sdholland 		 * Sometimes pages are dirtied between the time that
6147de8c6f8Sdholland 		 * we check and the time we try to clean them.
6157de8c6f8Sdholland 		 * Instruct lfs_gop_write to return EDEADLK in this case
6167de8c6f8Sdholland 		 * so we can write them properly.
6177de8c6f8Sdholland 		 */
6187de8c6f8Sdholland 		ip->i_lfs_iflags |= LFSI_NO_GOP_WRITE;
6197de8c6f8Sdholland 		r = genfs_do_putpages(vp, startoffset, endoffset,
6207de8c6f8Sdholland 				       ap->a_flags & ~PGO_SYNCIO, &busypg);
6217de8c6f8Sdholland 		ip->i_lfs_iflags &= ~LFSI_NO_GOP_WRITE;
6227de8c6f8Sdholland 		if (r != EDEADLK) {
623d2a0ebb6Sad 			KASSERT(!rw_write_held(vp->v_uobj.vmobjlock));
62463ce83dcShannken  			error = r;
62563ce83dcShannken 			goto out;
6267de8c6f8Sdholland 		}
6277de8c6f8Sdholland 
6287de8c6f8Sdholland 		/* One of the pages was busy.  Start over. */
629d2a0ebb6Sad 		rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
6307de8c6f8Sdholland 		wait_for_page(vp, busypg, "dirtyclean");
6317de8c6f8Sdholland #ifdef DEBUG
6327de8c6f8Sdholland 		++debug_n_dirtyclean;
6337de8c6f8Sdholland #endif
6347de8c6f8Sdholland 	} while(1);
6357de8c6f8Sdholland 
6367de8c6f8Sdholland #ifdef DEBUG
6377de8c6f8Sdholland 	if (debug_n_dirtyclean > TOOMANY)
6387de8c6f8Sdholland 		DLOG((DLOG_PAGE, "lfs_putpages: dirtyclean: looping, n = %d\n",
6397de8c6f8Sdholland 		      debug_n_dirtyclean));
6407de8c6f8Sdholland #endif
6417de8c6f8Sdholland 
6427de8c6f8Sdholland 	/*
6437de8c6f8Sdholland 	 * Dirty and asked to clean.
6447de8c6f8Sdholland 	 *
6457de8c6f8Sdholland 	 * Pagedaemon can't actually write LFS pages; wake up
6467de8c6f8Sdholland 	 * the writer to take care of that.  The writer will
6477de8c6f8Sdholland 	 * notice the pager inode queue and act on that.
6487de8c6f8Sdholland 	 *
6497de8c6f8Sdholland 	 * XXX We must drop the vp->interlock before taking the lfs_lock or we
6507de8c6f8Sdholland 	 * get a nasty deadlock with lfs_flush_pchain().
6517de8c6f8Sdholland 	 */
6527de8c6f8Sdholland 	if (pagedaemon) {
653d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
6547de8c6f8Sdholland 		mutex_enter(&lfs_lock);
6558f063ba0Smaya 		if (!(ip->i_state & IN_PAGING)) {
6568f063ba0Smaya 			ip->i_state |= IN_PAGING;
6577de8c6f8Sdholland 			TAILQ_INSERT_TAIL(&fs->lfs_pchainhd, ip, i_lfs_pchain);
6587de8c6f8Sdholland 		}
65963e604efSmaya 		cv_broadcast(&lfs_writerd_cv);
6607de8c6f8Sdholland 		mutex_exit(&lfs_lock);
6617de8c6f8Sdholland 		preempt();
662d2a0ebb6Sad 		KASSERT(!rw_write_held(vp->v_uobj.vmobjlock));
66363ce83dcShannken 		error = EWOULDBLOCK;
66463ce83dcShannken 		goto out;
6657de8c6f8Sdholland 	}
6667de8c6f8Sdholland 
6677de8c6f8Sdholland 	/*
6687de8c6f8Sdholland 	 * If this is a file created in a recent dirop, we can't flush its
6697de8c6f8Sdholland 	 * inode until the dirop is complete.  Drain dirops, then flush the
6707de8c6f8Sdholland 	 * filesystem (taking care of any other pending dirops while we're
6717de8c6f8Sdholland 	 * at it).
6727de8c6f8Sdholland 	 */
6737de8c6f8Sdholland 	if ((ap->a_flags & (PGO_CLEANIT|PGO_LOCKED)) == PGO_CLEANIT &&
6747de8c6f8Sdholland 	    (vp->v_uflag & VU_DIROP)) {
6757de8c6f8Sdholland 		DLOG((DLOG_PAGE, "lfs_putpages: flushing VU_DIROP\n"));
6767de8c6f8Sdholland 
677a46b1bb7Sriastradh 		/*
678a46b1bb7Sriastradh 		 * NB: lfs_flush_fs can recursively call lfs_putpages,
679a46b1bb7Sriastradh 		 * but it won't reach this branch because it passes
680a46b1bb7Sriastradh 		 * PGO_LOCKED.
681a46b1bb7Sriastradh 		 */
6827de8c6f8Sdholland 
683d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
6847de8c6f8Sdholland 		mutex_enter(&lfs_lock);
6857de8c6f8Sdholland 		lfs_flush_fs(fs, sync ? SEGM_SYNC : 0);
6867de8c6f8Sdholland 		mutex_exit(&lfs_lock);
687d2a0ebb6Sad 		rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
6887de8c6f8Sdholland 
689650aa898Smaya 		/*
690650aa898Smaya 		 * The flush will have cleaned out this vnode as well,
691650aa898Smaya 		 *  no need to do more to it.
692650aa898Smaya 		 *  XXX then why are we falling through and continuing?
693650aa898Smaya 		 */
694a46b1bb7Sriastradh 
695a46b1bb7Sriastradh 		/*
696a46b1bb7Sriastradh 		 * XXX State may have changed while we dropped the
697a46b1bb7Sriastradh 		 * lock; start over just in case.  The above comment
698a46b1bb7Sriastradh 		 * suggests this should maybe instead be goto out.
699a46b1bb7Sriastradh 		 */
700a46b1bb7Sriastradh 		goto retry;
7017de8c6f8Sdholland 	}
7027de8c6f8Sdholland 
7037de8c6f8Sdholland 	/*
7047de8c6f8Sdholland 	 * This is it.	We are going to write some pages.  From here on
7057de8c6f8Sdholland 	 * down it's all just mechanics.
7067de8c6f8Sdholland 	 *
7077de8c6f8Sdholland 	 * Don't let genfs_putpages wait; lfs_segunlock will wait for us.
7087de8c6f8Sdholland 	 */
7097de8c6f8Sdholland 	ap->a_flags &= ~PGO_SYNCIO;
7107de8c6f8Sdholland 
7117de8c6f8Sdholland 	/*
7127de8c6f8Sdholland 	 * If we've already got the seglock, flush the node and return.
7137de8c6f8Sdholland 	 * The FIP has already been set up for us by lfs_writefile,
7147de8c6f8Sdholland 	 * and FIP cleanup and lfs_updatemeta will also be done there,
7157de8c6f8Sdholland 	 * unless genfs_putpages returns EDEADLK; then we must flush
7167de8c6f8Sdholland 	 * what we have, and correct FIP and segment header accounting.
7177de8c6f8Sdholland 	 */
7187de8c6f8Sdholland   get_seglock:
7197de8c6f8Sdholland 	/*
7207de8c6f8Sdholland 	 * If we are not called with the segment locked, lock it.
7217de8c6f8Sdholland 	 * Account for a new FIP in the segment header, and set sp->vp.
7227de8c6f8Sdholland 	 * (This should duplicate the setup at the top of lfs_writefile().)
7237de8c6f8Sdholland 	 */
7247de8c6f8Sdholland 	seglocked = (ap->a_flags & PGO_LOCKED) != 0;
7257de8c6f8Sdholland 	if (!seglocked) {
726d2a0ebb6Sad 		rw_exit(vp->v_uobj.vmobjlock);
7277de8c6f8Sdholland 		error = lfs_seglock(fs, SEGM_PROT | (sync ? SEGM_SYNC : 0));
7287de8c6f8Sdholland 		if (error != 0) {
729d2a0ebb6Sad 			KASSERT(!rw_write_held(vp->v_uobj.vmobjlock));
73063ce83dcShannken  			goto out;
7317de8c6f8Sdholland 		}
732d2a0ebb6Sad 		rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
7337de8c6f8Sdholland 		lfs_acquire_finfo(fs, ip->i_number, ip->i_gen);
7347de8c6f8Sdholland 	}
7357de8c6f8Sdholland 	sp = fs->lfs_sp;
7367de8c6f8Sdholland 	KASSERT(sp->vp == NULL);
7377de8c6f8Sdholland 	sp->vp = vp;
7387de8c6f8Sdholland 
7397de8c6f8Sdholland 	/* Note segments written by reclaim; only for debugging */
740d2a0ebb6Sad 	mutex_enter(vp->v_interlock);
7417de8c6f8Sdholland 	if (vdead_check(vp, VDEAD_NOWAIT) != 0) {
7427de8c6f8Sdholland 		sp->seg_flags |= SEGM_RECLAIM;
7437de8c6f8Sdholland 		fs->lfs_reclino = ip->i_number;
7447de8c6f8Sdholland 	}
745d2a0ebb6Sad 	mutex_exit(vp->v_interlock);
7467de8c6f8Sdholland 
7477de8c6f8Sdholland 	/*
7487de8c6f8Sdholland 	 * Ensure that the partial segment is marked SS_DIROP if this
7497de8c6f8Sdholland 	 * vnode is a DIROP.
7507de8c6f8Sdholland 	 */
7512e090556Sdholland 	if (!seglocked && vp->v_uflag & VU_DIROP) {
7522e090556Sdholland 		SEGSUM *ssp = sp->segsum;
7532e090556Sdholland 
7542e090556Sdholland 		lfs_ss_setflags(fs, ssp,
7552e090556Sdholland 				lfs_ss_getflags(fs, ssp) | (SS_DIROP|SS_CONT));
7562e090556Sdholland 	}
7577de8c6f8Sdholland 
7587de8c6f8Sdholland 	/*
7597de8c6f8Sdholland 	 * Loop over genfs_putpages until all pages are gathered.
7607de8c6f8Sdholland 	 * genfs_putpages() drops the interlock, so reacquire it if necessary.
7617de8c6f8Sdholland 	 * Whenever we lose the interlock we have to rerun check_dirty, as
7627de8c6f8Sdholland 	 * well, since more pages might have been dirtied in our absence.
7637de8c6f8Sdholland 	 */
7647de8c6f8Sdholland #ifdef DEBUG
7657de8c6f8Sdholland 	debug_n_again = 0;
7667de8c6f8Sdholland #endif
7677de8c6f8Sdholland 	do {
7687de8c6f8Sdholland 		busypg = NULL;
769d2a0ebb6Sad 		KASSERT(rw_write_held(vp->v_uobj.vmobjlock));
7707de8c6f8Sdholland 		if (check_dirty(fs, vp, startoffset, endoffset, blkeof,
7717de8c6f8Sdholland 				ap->a_flags, 0, &busypg) < 0) {
7727de8c6f8Sdholland 			write_and_wait(fs, vp, busypg, seglocked, NULL);
7737de8c6f8Sdholland 			if (!seglocked) {
774d2a0ebb6Sad 				rw_exit(vp->v_uobj.vmobjlock);
7757de8c6f8Sdholland 				lfs_release_finfo(fs);
7767de8c6f8Sdholland 				lfs_segunlock(fs);
777d2a0ebb6Sad 				rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
7787de8c6f8Sdholland 			}
7797de8c6f8Sdholland 			sp->vp = NULL;
7807de8c6f8Sdholland 			goto get_seglock;
7817de8c6f8Sdholland 		}
7827de8c6f8Sdholland 
7837de8c6f8Sdholland 		busypg = NULL;
7847de8c6f8Sdholland 		oreclaim = (ap->a_flags & PGO_RECLAIM);
7857de8c6f8Sdholland 		ap->a_flags &= ~PGO_RECLAIM;
7867de8c6f8Sdholland 		error = genfs_do_putpages(vp, startoffset, endoffset,
7877de8c6f8Sdholland 					   ap->a_flags, &busypg);
7887de8c6f8Sdholland 		ap->a_flags |= oreclaim;
7897de8c6f8Sdholland 
7907de8c6f8Sdholland 		if (error == EDEADLK || error == EAGAIN) {
7917de8c6f8Sdholland 			DLOG((DLOG_PAGE, "lfs_putpages: genfs_putpages returned"
792078ffcb8Sdholland 			      " %d ino %d off %jx (seg %d)\n", error,
793078ffcb8Sdholland 			      ip->i_number, (uintmax_t)lfs_sb_getoffset(fs),
7947f29db77Smartin 			      lfs_dtosn(fs, lfs_sb_getoffset(fs))));
7957de8c6f8Sdholland 
7967de8c6f8Sdholland 			if (oreclaim) {
797d2a0ebb6Sad 				rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
7987de8c6f8Sdholland 				write_and_wait(fs, vp, busypg, seglocked, "again");
799d2a0ebb6Sad 				rw_exit(vp->v_uobj.vmobjlock);
8007de8c6f8Sdholland 			} else {
8017de8c6f8Sdholland 				if ((sp->seg_flags & SEGM_SINGLE) &&
802f59b8f4bSdholland 				    lfs_sb_getcurseg(fs) != fs->lfs_startseg)
8037de8c6f8Sdholland 					donewriting = 1;
8047de8c6f8Sdholland 			}
8057de8c6f8Sdholland 		} else if (error) {
8067de8c6f8Sdholland 			DLOG((DLOG_PAGE, "lfs_putpages: genfs_putpages returned"
807078ffcb8Sdholland 			      " %d ino %d off %jx (seg %d)\n", error,
808078ffcb8Sdholland 			      (int)ip->i_number, (uintmax_t)lfs_sb_getoffset(fs),
8097f29db77Smartin 			      lfs_dtosn(fs, lfs_sb_getoffset(fs))));
8107de8c6f8Sdholland 		}
8117de8c6f8Sdholland 		/* genfs_do_putpages loses the interlock */
8127de8c6f8Sdholland #ifdef DEBUG
8137de8c6f8Sdholland 		++debug_n_again;
8147de8c6f8Sdholland #endif
8157de8c6f8Sdholland 		if (oreclaim && error == EAGAIN) {
8167de8c6f8Sdholland 			DLOG((DLOG_PAGE, "vp %p ino %d vi_flags %x a_flags %x avoiding vclean panic\n",
8177de8c6f8Sdholland 			      vp, (int)ip->i_number, vp->v_iflag, ap->a_flags));
818d2a0ebb6Sad 			rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
8197de8c6f8Sdholland 		}
8207de8c6f8Sdholland 		if (error == EDEADLK)
821d2a0ebb6Sad 			rw_enter(vp->v_uobj.vmobjlock, RW_WRITER);
8227de8c6f8Sdholland 	} while (error == EDEADLK || (oreclaim && error == EAGAIN));
8237de8c6f8Sdholland #ifdef DEBUG
8247de8c6f8Sdholland 	if (debug_n_again > TOOMANY)
8257de8c6f8Sdholland 		DLOG((DLOG_PAGE, "lfs_putpages: again: looping, n = %d\n", debug_n_again));
8267de8c6f8Sdholland #endif
8277de8c6f8Sdholland 
8287de8c6f8Sdholland 	KASSERT(sp != NULL && sp->vp == vp);
8297de8c6f8Sdholland 	if (!seglocked && !donewriting) {
8307de8c6f8Sdholland 		sp->vp = NULL;
8317de8c6f8Sdholland 
8327de8c6f8Sdholland 		/* Write indirect blocks as well */
8337de8c6f8Sdholland 		lfs_gather(fs, fs->lfs_sp, vp, lfs_match_indir);
8347de8c6f8Sdholland 		lfs_gather(fs, fs->lfs_sp, vp, lfs_match_dindir);
8357de8c6f8Sdholland 		lfs_gather(fs, fs->lfs_sp, vp, lfs_match_tindir);
8367de8c6f8Sdholland 
8377de8c6f8Sdholland 		KASSERT(sp->vp == NULL);
8387de8c6f8Sdholland 		sp->vp = vp;
8397de8c6f8Sdholland 	}
8407de8c6f8Sdholland 
8417de8c6f8Sdholland 	/*
8427de8c6f8Sdholland 	 * Blocks are now gathered into a segment waiting to be written.
8437de8c6f8Sdholland 	 * All that's left to do is update metadata, and write them.
8447de8c6f8Sdholland 	 */
8457de8c6f8Sdholland 	lfs_updatemeta(sp);
8467de8c6f8Sdholland 	KASSERT(sp->vp == vp);
8477de8c6f8Sdholland 	sp->vp = NULL;
8487de8c6f8Sdholland 
8497de8c6f8Sdholland 	/*
8507de8c6f8Sdholland 	 * If we were called from lfs_writefile, we don't need to clean up
8517de8c6f8Sdholland 	 * the FIP or unlock the segment lock.	We're done.
8527de8c6f8Sdholland 	 */
8537de8c6f8Sdholland 	if (seglocked) {
854d2a0ebb6Sad 		KASSERT(!rw_write_held(vp->v_uobj.vmobjlock));
85563ce83dcShannken 		goto out;
8567de8c6f8Sdholland 	}
8577de8c6f8Sdholland 
8587de8c6f8Sdholland 	/* Clean up FIP and send it to disk. */
8597de8c6f8Sdholland 	lfs_release_finfo(fs);
8607de8c6f8Sdholland 	lfs_writeseg(fs, fs->lfs_sp);
8617de8c6f8Sdholland 
8627de8c6f8Sdholland 	/*
8637de8c6f8Sdholland 	 * Remove us from paging queue if we wrote all our pages.
8647de8c6f8Sdholland 	 */
8657de8c6f8Sdholland 	if (origendoffset == 0 || ap->a_flags & PGO_ALLPAGES) {
8667de8c6f8Sdholland 		mutex_enter(&lfs_lock);
8678f063ba0Smaya 		if (ip->i_state & IN_PAGING) {
8688f063ba0Smaya 			ip->i_state &= ~IN_PAGING;
8697de8c6f8Sdholland 			TAILQ_REMOVE(&fs->lfs_pchainhd, ip, i_lfs_pchain);
8707de8c6f8Sdholland 		}
8717de8c6f8Sdholland 		mutex_exit(&lfs_lock);
8727de8c6f8Sdholland 	}
8737de8c6f8Sdholland 
8747de8c6f8Sdholland 	/*
8757de8c6f8Sdholland 	 * XXX - with the malloc/copy writeseg, the pages are freed by now
8767de8c6f8Sdholland 	 * even if we don't wait (e.g. if we hold a nested lock).  This
8777de8c6f8Sdholland 	 * will not be true if we stop using malloc/copy.
8787de8c6f8Sdholland 	 */
8797de8c6f8Sdholland 	KASSERT(fs->lfs_sp->seg_flags & SEGM_PROT);
8807de8c6f8Sdholland 	lfs_segunlock(fs);
8817de8c6f8Sdholland 
8827de8c6f8Sdholland 	/*
8837de8c6f8Sdholland 	 * Wait for v_numoutput to drop to zero.  The seglock should
8847de8c6f8Sdholland 	 * take care of this, but there is a slight possibility that
8857de8c6f8Sdholland 	 * aiodoned might not have got around to our buffers yet.
8867de8c6f8Sdholland 	 */
8877de8c6f8Sdholland 	if (sync) {
8887de8c6f8Sdholland 		mutex_enter(vp->v_interlock);
8897de8c6f8Sdholland 		while (vp->v_numoutput > 0) {
8907de8c6f8Sdholland 			DLOG((DLOG_PAGE, "lfs_putpages: ino %d sleeping on"
8917de8c6f8Sdholland 			      " num %d\n", ip->i_number, vp->v_numoutput));
8927de8c6f8Sdholland 			cv_wait(&vp->v_cv, vp->v_interlock);
8937de8c6f8Sdholland 		}
8947de8c6f8Sdholland 		mutex_exit(vp->v_interlock);
8957de8c6f8Sdholland 	}
89663ce83dcShannken 
89763ce83dcShannken out:;
89863ce83dcShannken 	if (trans_mp)
89963ce83dcShannken 		fstrans_done(trans_mp);
900d2a0ebb6Sad 	KASSERT(!rw_write_held(vp->v_uobj.vmobjlock));
9017de8c6f8Sdholland 	return error;
9027de8c6f8Sdholland }
9037de8c6f8Sdholland 
904