svn commit: r289141 - in user/alc/PQ_LAUNDRY/sys: sys vm

Alan Cox alc at FreeBSD.org
Sun Oct 11 16:31:29 UTC 2015


Author: alc
Date: Sun Oct 11 16:31:27 2015
New Revision: 289141
URL: https://svnweb.freebsd.org/changeset/base/289141

Log:
  Create the basic mechanisms, i.e., a separate queue for laundering dirty
  pages and a thread for servicing that queue.  However, the laundering
  policy is still very much a work in progress.
  
  Discussed with:	jeff, kib
  Sponsored by:	EMC / Isilon Storage Division

Modified:
  user/alc/PQ_LAUNDRY/sys/sys/vmmeter.h
  user/alc/PQ_LAUNDRY/sys/vm/swap_pager.c
  user/alc/PQ_LAUNDRY/sys/vm/vm_meter.c
  user/alc/PQ_LAUNDRY/sys/vm/vm_page.c
  user/alc/PQ_LAUNDRY/sys/vm/vm_page.h
  user/alc/PQ_LAUNDRY/sys/vm/vm_pageout.c

Modified: user/alc/PQ_LAUNDRY/sys/sys/vmmeter.h
==============================================================================
--- user/alc/PQ_LAUNDRY/sys/sys/vmmeter.h	Sun Oct 11 15:39:37 2015	(r289140)
+++ user/alc/PQ_LAUNDRY/sys/sys/vmmeter.h	Sun Oct 11 16:31:27 2015	(r289141)
@@ -96,6 +96,7 @@ struct vmmeter {
 	u_int v_active_count;	/* (q) pages active */
 	u_int v_inactive_target; /* (c) pages desired inactive */
 	u_int v_inactive_count;	/* (q) pages inactive */
+	u_int v_laundry_count;	/* (q) pages dirty */
 	u_int v_cache_count;	/* (f) pages on cache queue */
 	u_int v_pageout_free_min;   /* (c) min pages reserved for kernel */
 	u_int v_interrupt_free_min; /* (c) reserved pages for int code */
@@ -111,7 +112,7 @@ struct vmmeter {
 	u_int v_vforkpages;	/* (p) VM pages affected by vfork() */
 	u_int v_rforkpages;	/* (p) VM pages affected by rfork() */
 	u_int v_kthreadpages;	/* (p) VM pages affected by fork() by kernel */
-	u_int v_spare[2];
+	u_int v_spare[1];
 };
 #ifdef _KERNEL
 

Modified: user/alc/PQ_LAUNDRY/sys/vm/swap_pager.c
==============================================================================
--- user/alc/PQ_LAUNDRY/sys/vm/swap_pager.c	Sun Oct 11 15:39:37 2015	(r289140)
+++ user/alc/PQ_LAUNDRY/sys/vm/swap_pager.c	Sun Oct 11 16:31:27 2015	(r289141)
@@ -1617,12 +1617,10 @@ swp_pager_async_iodone(struct buf *bp)
 			    ("swp_pager_async_iodone: page %p is not write"
 			    " protected", m));
 			vm_page_undirty(m);
+			vm_page_lock(m);
+			vm_page_deactivate(m);
+			vm_page_unlock(m);
 			vm_page_sunbusy(m);
-			if (vm_page_count_severe()) {
-				vm_page_lock(m);
-				vm_page_try_to_cache(m);
-				vm_page_unlock(m);
-			}
 		}
 	}
 

Modified: user/alc/PQ_LAUNDRY/sys/vm/vm_meter.c
==============================================================================
--- user/alc/PQ_LAUNDRY/sys/vm/vm_meter.c	Sun Oct 11 15:39:37 2015	(r289140)
+++ user/alc/PQ_LAUNDRY/sys/vm/vm_meter.c	Sun Oct 11 16:31:27 2015	(r289141)
@@ -303,6 +303,7 @@ VM_STATS_VM(v_wire_count, "Wired pages")
 VM_STATS_VM(v_active_count, "Active pages");
 VM_STATS_VM(v_inactive_target, "Desired inactive pages");
 VM_STATS_VM(v_inactive_count, "Inactive pages");
+VM_STATS_VM(v_laundry_count, "Dirty pages");
 VM_STATS_VM(v_cache_count, "Pages on cache queue");
 VM_STATS_VM(v_pageout_free_min, "Min pages reserved for kernel");
 VM_STATS_VM(v_interrupt_free_min, "Reserved pages for interrupt code");

Modified: user/alc/PQ_LAUNDRY/sys/vm/vm_page.c
==============================================================================
--- user/alc/PQ_LAUNDRY/sys/vm/vm_page.c	Sun Oct 11 15:39:37 2015	(r289140)
+++ user/alc/PQ_LAUNDRY/sys/vm/vm_page.c	Sun Oct 11 16:31:27 2015	(r289141)
@@ -387,6 +387,10 @@ vm_page_domain_init(struct vm_domain *vm
 	    "vm active pagequeue";
 	*__DECONST(int **, &vmd->vmd_pagequeues[PQ_ACTIVE].pq_vcnt) =
 	    &vm_cnt.v_active_count;
+	*__DECONST(char **, &vmd->vmd_pagequeues[PQ_LAUNDRY].pq_name) =
+	    "vm laundry pagequeue";
+	*__DECONST(int **, &vmd->vmd_pagequeues[PQ_LAUNDRY].pq_vcnt) =
+	    &vm_cnt.v_laundry_count;
 	vmd->vmd_page_count = 0;
 	vmd->vmd_free_count = 0;
 	vmd->vmd_segs = 0;
@@ -2510,11 +2514,8 @@ vm_page_unwire(vm_page_t m, uint8_t queu
 		if (m->wire_count == 0) {
 			atomic_subtract_int(&vm_cnt.v_wire_count, 1);
 			if ((m->oflags & VPO_UNMANAGED) == 0 &&
-			    m->object != NULL && queue != PQ_NONE) {
-				if (queue == PQ_INACTIVE)
-					m->flags &= ~PG_WINATCFLS;
+			    m->object != NULL && queue != PQ_NONE)
 				vm_page_enqueue(queue, m);
-			}
 			return (TRUE);
 		} else
 			return (FALSE);
@@ -2565,7 +2566,6 @@ _vm_page_deactivate(vm_page_t m, int ath
 		} else {
 			if (queue != PQ_NONE)
 				vm_page_dequeue(m);
-			m->flags &= ~PG_WINATCFLS;
 			vm_pagequeue_lock(pq);
 		}
 		m->queue = PQ_INACTIVE;
@@ -2604,6 +2604,26 @@ vm_page_deactivate_noreuse(vm_page_t m)
 }
 
 /*
+ * XXX
+ */
+void
+vm_page_launder(vm_page_t m)
+{
+	int queue;
+
+	vm_page_assert_locked(m);
+	if ((queue = m->queue) != PQ_LAUNDRY) {
+		if (m->wire_count == 0 && (m->oflags & VPO_UNMANAGED) == 0) {
+			if (queue != PQ_NONE)
+				vm_page_dequeue(m);
+			vm_page_enqueue(PQ_LAUNDRY, m);
+		} else
+			KASSERT(queue == PQ_NONE,
+			    ("wired page %p is queued", m));
+	}
+}
+
+/*
  * vm_page_try_to_cache:
  *
  * Returns 0 on failure, 1 on success
@@ -2797,7 +2817,10 @@ vm_page_advise(vm_page_t m, int advice)
 	 * tail, thus defeating the queue's LRU operation and ensuring that the
 	 * page will be reused quickly.
 	 */
-	_vm_page_deactivate(m, m->dirty == 0);
+	if (m->dirty == 0)
+		_vm_page_deactivate(m, TRUE);
+	else
+		vm_page_launder(m);
 }
 
 /*

Modified: user/alc/PQ_LAUNDRY/sys/vm/vm_page.h
==============================================================================
--- user/alc/PQ_LAUNDRY/sys/vm/vm_page.h	Sun Oct 11 15:39:37 2015	(r289140)
+++ user/alc/PQ_LAUNDRY/sys/vm/vm_page.h	Sun Oct 11 16:31:27 2015	(r289141)
@@ -206,7 +206,8 @@ struct vm_page {
 #define	PQ_NONE		255
 #define	PQ_INACTIVE	0
 #define	PQ_ACTIVE	1
-#define	PQ_COUNT	2
+#define	PQ_LAUNDRY	2
+#define	PQ_COUNT	3
 
 TAILQ_HEAD(pglist, vm_page);
 SLIST_HEAD(spglist, vm_page);
@@ -326,7 +327,6 @@ extern struct mtx_padalign pa_lock[];
 #define	PG_FICTITIOUS	0x0004		/* physical page doesn't exist */
 #define	PG_ZERO		0x0008		/* page is zeroed */
 #define	PG_MARKER	0x0010		/* special queue marker page */
-#define	PG_WINATCFLS	0x0040		/* flush dirty page on inactive q */
 #define	PG_NODUMP	0x0080		/* don't include this page in a dump */
 #define	PG_UNHOLDFREE	0x0100		/* delayed free of a held page */
 
@@ -459,6 +459,7 @@ vm_page_t vm_page_getfake(vm_paddr_t pad
 void vm_page_initfake(vm_page_t m, vm_paddr_t paddr, vm_memattr_t memattr);
 int vm_page_insert (vm_page_t, vm_object_t, vm_pindex_t);
 boolean_t vm_page_is_cached(vm_object_t object, vm_pindex_t pindex);
+void vm_page_launder(vm_page_t m);
 vm_page_t vm_page_lookup (vm_object_t, vm_pindex_t);
 vm_page_t vm_page_next(vm_page_t m);
 int vm_page_pa_tryrelock(pmap_t, vm_paddr_t, vm_paddr_t *);

Modified: user/alc/PQ_LAUNDRY/sys/vm/vm_pageout.c
==============================================================================
--- user/alc/PQ_LAUNDRY/sys/vm/vm_pageout.c	Sun Oct 11 15:39:37 2015	(r289140)
+++ user/alc/PQ_LAUNDRY/sys/vm/vm_pageout.c	Sun Oct 11 16:31:27 2015	(r289141)
@@ -168,7 +168,6 @@ MTX_SYSINIT(vm_daemon, &vm_daemon_mtx, "
 #endif
 static int vm_max_launder = 32;
 static int vm_pageout_update_period;
-static int defer_swap_pageouts;
 static int disable_swap_pageouts;
 static int lowmem_period = 10;
 static time_t lowmem_uptime;
@@ -213,9 +212,6 @@ SYSCTL_INT(_vm, OID_AUTO, swap_idle_enab
 	CTLFLAG_RW, &vm_swap_idle_enabled, 0, "Allow swapout on idle criteria");
 #endif
 
-SYSCTL_INT(_vm, OID_AUTO, defer_swapspace_pageouts,
-	CTLFLAG_RW, &defer_swap_pageouts, 0, "Give preference to dirty pages in mem");
-
 SYSCTL_INT(_vm, OID_AUTO, disable_swapspace_pageouts,
 	CTLFLAG_RW, &disable_swap_pageouts, 0, "Disallow swapout of dirty pages");
 
@@ -233,6 +229,8 @@ SYSCTL_INT(_vm, OID_AUTO, max_wired,
 static boolean_t vm_pageout_fallback_object_lock(vm_page_t, vm_page_t *);
 static boolean_t vm_pageout_launder(struct vm_pagequeue *pq, int, vm_paddr_t,
     vm_paddr_t);
+static void vm_pageout_launder1(struct vm_domain *vmd, int pass);
+static void vm_pageout_laundry_worker(void *arg);
 #if !defined(NO_SWAPPING)
 static void vm_pageout_map_deactivate_pages(vm_map_t, long);
 static void vm_pageout_object_deactivate_pages(pmap_t, vm_object_t, long);
@@ -388,8 +386,7 @@ vm_pageout_cluster(vm_page_t m)
 	 * We can cluster ONLY if: ->> the page is NOT
 	 * clean, wired, busy, held, or mapped into a
 	 * buffer, and one of the following:
-	 * 1) The page is inactive, or a seldom used
-	 *    active page.
+	 * 1) The page is in the laundry.
 	 * -or-
 	 * 2) we force the issue.
 	 *
@@ -420,7 +417,7 @@ more:
 			break;
 		}
 		vm_page_lock(p);
-		if (p->queue != PQ_INACTIVE ||
+		if (p->queue != PQ_LAUNDRY ||
 		    p->hold_count != 0) {	/* may be undergoing I/O */
 			vm_page_unlock(p);
 			ib = 0;
@@ -448,7 +445,7 @@ more:
 		if (p->dirty == 0)
 			break;
 		vm_page_lock(p);
-		if (p->queue != PQ_INACTIVE ||
+		if (p->queue != PQ_LAUNDRY ||
 		    p->hold_count != 0) {	/* may be undergoing I/O */
 			vm_page_unlock(p);
 			break;
@@ -546,11 +543,11 @@ vm_pageout_flush(vm_page_t *mc, int coun
 		case VM_PAGER_FAIL:
 			/*
 			 * If page couldn't be paged out, then reactivate the
-			 * page so it doesn't clog the inactive list.  (We
+			 * page so it doesn't clog the XXX list.  (We
 			 * will try paging out it again later).
 			 */
 			vm_page_lock(mt);
-			vm_page_activate(mt);
+			vm_page_activate(mt);	// XXX
 			vm_page_unlock(mt);
 			if (eio != NULL && i >= mreq && i - mreq < runlen)
 				*eio = TRUE;
@@ -972,7 +969,7 @@ vm_pageout_clean(vm_page_t m)
 		 * (3) reallocated to a different offset, or
 		 * (4) cleaned.
 		 */
-		if (m->queue != PQ_INACTIVE || m->object != object ||
+		if (m->queue != PQ_LAUNDRY || m->object != object ||
 		    m->pindex != pindex || m->dirty == 0) {
 			vm_page_unlock(m);
 			error = ENXIO;
@@ -1015,11 +1012,218 @@ unlock_mp:
 }
 
 /*
+ * XXX
+ */
+static void
+vm_pageout_launder1(struct vm_domain *vmd, int pass)
+{
+	vm_page_t m, next;
+	struct vm_page laundry_marker;
+	struct vm_pagequeue *pq;
+	vm_object_t object;
+	int act_delta, error, maxlaunder, maxscan, vnodes_skipped;
+	boolean_t pageout_ok, queues_locked;
+
+	vm_pageout_init_marker(&laundry_marker, PQ_LAUNDRY);
+
+	/*
+	 * maxlaunder limits the number of dirty pages we flush per scan.
+	 * For most systems a smaller value (16 or 32) is more robust under
+	 * extreme memory and disk pressure because any unnecessary writes
+	 * to disk can result in extreme performance degredation.  However,
+	 * systems with excessive dirty pages (especially when MAP_NOSYNC is
+	 * used) will die horribly with limited laundering.  If the pageout
+	 * daemon cannot clean enough pages in the first pass, we let it go
+	 * all out in succeeding passes.
+	 */
+	if ((maxlaunder = vm_max_launder) <= 1)
+		maxlaunder = 1;
+	if (pass > 1)
+		maxlaunder = 10000;
+
+	vnodes_skipped = 0;
+
+	/*
+	 * XXX
+	 */
+	pq = &vmd->vmd_pagequeues[PQ_LAUNDRY];
+	maxscan = pq->pq_cnt;
+	vm_pagequeue_lock(pq);
+	queues_locked = TRUE;
+	for (m = TAILQ_FIRST(&pq->pq_pl);
+	    m != NULL && maxscan-- > 0 && maxlaunder > 0;
+	    m = next) {
+		vm_pagequeue_assert_locked(pq);
+		KASSERT(queues_locked, ("unlocked laundry queue"));
+		KASSERT(m->queue == PQ_LAUNDRY,
+		    ("page %p has an inconsistent queue", m));
+		next = TAILQ_NEXT(m, plinks.q);
+		if ((m->flags & PG_MARKER) != 0)
+			continue;
+		KASSERT((m->flags & PG_FICTITIOUS) == 0,
+		    ("PG_FICTITIOUS page %p cannot be in laundry queue", m));
+		KASSERT((m->oflags & VPO_UNMANAGED) == 0,
+		    ("VPO_UNMANAGED page %p cannot be in laundry queue", m));
+		if (!vm_pageout_page_lock(m, &next) || m->hold_count != 0) {
+			vm_page_unlock(m);
+			continue;
+		}
+		object = m->object;
+		if ((!VM_OBJECT_TRYWLOCK(object) &&
+		    (!vm_pageout_fallback_object_lock(m, &next) ||
+		    m->hold_count != 0)) || vm_page_busied(m)) {
+			VM_OBJECT_WUNLOCK(object);
+			vm_page_unlock(m);
+			continue;
+		}
+
+		/*
+		 * We unlock the laundry queue, invalidating the
+		 * 'next' pointer.  Use our marker to remember our
+		 * place.
+		 */
+		TAILQ_INSERT_AFTER(&pq->pq_pl, m, &laundry_marker, plinks.q);
+		vm_pagequeue_unlock(pq);
+		queues_locked = FALSE;
+
+		/*
+		 * Invalid pages can be easily freed.  They cannot be
+		 * mapped; vm_page_free() asserts this.
+		 */
+		if (m->valid == 0)
+			goto free_page;
+
+		/*
+		 * If the page has been referenced and the object is not dead,
+		 * reactivate or requeue the page depending on whether the
+		 * object is mapped.
+		 */
+		if ((m->aflags & PGA_REFERENCED) != 0) {
+			vm_page_aflag_clear(m, PGA_REFERENCED);
+			act_delta = 1;
+		} else
+			act_delta = 0;
+		if (object->ref_count != 0)
+			act_delta += pmap_ts_referenced(m);
+		else {
+			KASSERT(!pmap_page_is_mapped(m),
+			    ("page %p is mapped", m));
+		}
+		if (act_delta != 0) {
+			if (object->ref_count != 0) {
+				vm_page_activate(m);
+
+				/*
+				 * Increase the activation count if the page
+				 * was referenced while in the laundry queue.
+				 * This makes it less likely that the page will
+				 * be returned prematurely to the inactive
+				 * queue.
+ 				 */
+				m->act_count += act_delta + ACT_ADVANCE;
+				goto drop_page;
+			} else if ((object->flags & OBJ_DEAD) == 0) {
+				vm_page_deactivate(m);	// XXX
+				goto drop_page;
+			}
+		}
+
+		/*
+		 * If the page appears to be clean at the machine-independent
+		 * layer, then remove all of its mappings from the pmap in
+		 * anticipation of placing it onto the cache queue.  If,
+		 * however, any of the page's mappings allow write access,
+		 * then the page may still be modified until the last of those
+		 * mappings are removed.
+		 */
+		if (object->ref_count != 0) {
+			vm_page_test_dirty(m);
+			if (m->dirty == 0)
+				pmap_remove_all(m);
+		}
+
+		/*
+		 * Clean pages can be freed, but dirty pages must be sent back
+		 * to the laundry, unless they belong to a dead object.
+		 * However, requeueing dirty pages from dead objects is
+		 * pointless, as they are being paged out and freed by the
+		 * thread that destroyed the object.
+		 */
+		if (m->dirty == 0) {
+free_page:
+			vm_page_free(m);
+			PCPU_INC(cnt.v_dfree);
+		} else if ((object->flags & OBJ_DEAD) == 0) {
+			if (object->type != OBJT_SWAP &&
+			    object->type != OBJT_DEFAULT)
+				pageout_ok = TRUE;
+			else if (disable_swap_pageouts)
+				pageout_ok = FALSE;
+			else
+				pageout_ok = TRUE;
+			if (!pageout_ok) {
+				vm_pagequeue_lock(pq);
+				queues_locked = TRUE;
+				vm_page_requeue_locked(m);
+				goto drop_page;
+			}
+			error = vm_pageout_clean(m);
+			if (error == 0)
+				maxlaunder--;
+			else if (error == EDEADLK) {
+				pageout_lock_miss++;
+				vnodes_skipped++;
+			}
+			goto relock_queues;
+		}
+drop_page:
+		vm_page_unlock(m);
+		VM_OBJECT_WUNLOCK(object);
+relock_queues:
+		if (!queues_locked) {
+			vm_pagequeue_lock(pq);
+			queues_locked = TRUE;
+		}
+		next = TAILQ_NEXT(&laundry_marker, plinks.q);
+		TAILQ_REMOVE(&pq->pq_pl, &laundry_marker, plinks.q);
+	}
+	vm_pagequeue_unlock(pq);
+
+	/*
+	 * Wakeup the sync daemon if we skipped a vnode in a writeable object
+	 * and we didn't launder enough pages.
+	 */
+	if (vnodes_skipped > 0 && maxlaunder > 0)
+		(void)speedup_syncer();
+}
+
+/*
+ * XXX
+ */
+static void
+vm_pageout_laundry_worker(void *arg)
+{
+	struct vm_domain *domain;
+	int domidx;
+
+	domidx = (uintptr_t)arg;
+	domain = &vm_dom[domidx];
+	KASSERT(domain->vmd_segs != 0, ("domain without segments"));
+
+	/*
+	 * The pageout laundry worker is never done, so loop forever.
+	 */
+	for (;;) {
+		tsleep(&vm_cnt.v_laundry_count, PVM, "laundr", hz / 10);
+		vm_pageout_launder1(domain, 1);
+	}
+}
+
+/*
  *	vm_pageout_scan does the dirty work for the pageout daemon.
  *
  *	pass 0 - Update active LRU/deactivate pages
  *	pass 1 - Move inactive to cache or free
- *	pass 2 - Launder dirty pages
  */
 static void
 vm_pageout_scan(struct vm_domain *vmd, int pass)
@@ -1028,9 +1232,9 @@ vm_pageout_scan(struct vm_domain *vmd, i
 	struct vm_pagequeue *pq;
 	vm_object_t object;
 	long min_scan;
-	int act_delta, addl_page_shortage, deficit, error, maxlaunder, maxscan;
-	int page_shortage, scan_tick, scanned, vnodes_skipped;
-	boolean_t pageout_ok, queues_locked;
+	int act_delta, addl_page_shortage, deficit, maxscan;
+	int page_shortage, scan_tick, scanned;
+	boolean_t queues_locked;
 
 	/*
 	 * If we need to reclaim memory ask kernel caches to return
@@ -1070,23 +1274,6 @@ vm_pageout_scan(struct vm_domain *vmd, i
 		page_shortage = deficit = 0;
 
 	/*
-	 * maxlaunder limits the number of dirty pages we flush per scan.
-	 * For most systems a smaller value (16 or 32) is more robust under
-	 * extreme memory and disk pressure because any unnecessary writes
-	 * to disk can result in extreme performance degredation.  However,
-	 * systems with excessive dirty pages (especially when MAP_NOSYNC is
-	 * used) will die horribly with limited laundering.  If the pageout
-	 * daemon cannot clean enough pages in the first pass, we let it go
-	 * all out in succeeding passes.
-	 */
-	if ((maxlaunder = vm_max_launder) <= 1)
-		maxlaunder = 1;
-	if (pass > 1)
-		maxlaunder = 10000;
-
-	vnodes_skipped = 0;
-
-	/*
 	 * Start scanning the inactive queue for pages we can move to the
 	 * cache or free.  The scan will stop when the target is reached or
 	 * we have scanned the entire inactive queue.  Note that m->act_count
@@ -1170,6 +1357,7 @@ unlock_page:
 		 * place.
 		 */
 		TAILQ_INSERT_AFTER(&pq->pq_pl, m, &vmd->vmd_marker, plinks.q);
+		vm_page_dequeue_locked(m);
 		vm_pagequeue_unlock(pq);
 		queues_locked = FALSE;
 
@@ -1209,8 +1397,14 @@ unlock_page:
  				 */
 				m->act_count += act_delta + ACT_ADVANCE;
 				goto drop_page;
-			} else if ((object->flags & OBJ_DEAD) == 0)
-				goto requeue_page;
+			} else if ((object->flags & OBJ_DEAD) == 0) {
+				vm_pagequeue_lock(pq);
+				queues_locked = TRUE;
+				m->queue = PQ_INACTIVE;
+				TAILQ_INSERT_TAIL(&pq->pq_pl, m, plinks.q);
+				vm_pagequeue_cnt_inc(pq);
+				goto drop_page;
+			}
 		}
 
 		/*
@@ -1227,83 +1421,23 @@ unlock_page:
 				pmap_remove_all(m);
 		}
 
+		/*
+		 * Clean pages can be freed, but dirty pages must be sent back
+		 * to the laundry, unless they belong to a dead object.
+		 * However, requeueing dirty pages from dead objects is
+		 * pointless, as they are being paged out and freed by the
+		 * thread that destroyed the object.
+		 */
 		if (m->dirty == 0) {
-			/*
-			 * Clean pages can be freed.
-			 */
 free_page:
 			vm_page_free(m);
 			PCPU_INC(cnt.v_dfree);
 			--page_shortage;
-		} else if ((object->flags & OBJ_DEAD) != 0) {
-			/*
-			 * Leave dirty pages from dead objects at the front of
-			 * the queue.  They are being paged out and freed by
-			 * the thread that destroyed the object.  They will
-			 * leave the queue shortly after the scan finishes, so 
-			 * they should be discounted from the inactive count.
-			 */
-			addl_page_shortage++;
-		} else if ((m->flags & PG_WINATCFLS) == 0 && pass < 2) {
-			/*
-			 * Dirty pages need to be paged out, but flushing
-			 * a page is extremely expensive versus freeing
-			 * a clean page.  Rather then artificially limiting
-			 * the number of pages we can flush, we instead give
-			 * dirty pages extra priority on the inactive queue
-			 * by forcing them to be cycled through the queue
-			 * twice before being flushed, after which the
-			 * (now clean) page will cycle through once more
-			 * before being freed.  This significantly extends
-			 * the thrash point for a heavily loaded machine.
-			 */
-			m->flags |= PG_WINATCFLS;
-requeue_page:
-			vm_pagequeue_lock(pq);
-			queues_locked = TRUE;
-			vm_page_requeue_locked(m);
-		} else if (maxlaunder > 0) {
-			/*
-			 * We always want to try to flush some dirty pages if
-			 * we encounter them, to keep the system stable.
-			 * Normally this number is small, but under extreme
-			 * pressure where there are insufficient clean pages
-			 * on the inactive queue, we may have to go all out.
-			 */
-
-			if (object->type != OBJT_SWAP &&
-			    object->type != OBJT_DEFAULT)
-				pageout_ok = TRUE;
-			else if (disable_swap_pageouts)
-				pageout_ok = FALSE;
-			else if (defer_swap_pageouts)
-				pageout_ok = vm_page_count_min();
-			else
-				pageout_ok = TRUE;
-			if (!pageout_ok)
-				goto requeue_page;
-			error = vm_pageout_clean(m);
-			/*
-			 * Decrement page_shortage on success to account for
-			 * the (future) cleaned page.  Otherwise we could wind
-			 * up laundering or cleaning too many pages.
-			 */
-			if (error == 0) {
-				page_shortage--;
-				maxlaunder--;
-			} else if (error == EDEADLK) {
-				pageout_lock_miss++;
-				vnodes_skipped++;
-			} else if (error == EBUSY) {
-				addl_page_shortage++;
-			}
-			vm_page_lock_assert(m, MA_NOTOWNED);
-			goto relock_queues;
-		}
+		} else if ((object->flags & OBJ_DEAD) == 0)
+			vm_page_launder(m);
 drop_page:
 		vm_page_unlock(m);
 		VM_OBJECT_WUNLOCK(object);
-relock_queues:
 		if (!queues_locked) {
 			vm_pagequeue_lock(pq);
 			queues_locked = TRUE;
@@ -1313,6 +1447,13 @@ relock_queues:
 	}
 	vm_pagequeue_unlock(pq);
 
+	/*
+	 * Wakeup the laundry thread(s) if we didn't free the targeted number
+	 * of pages.
+	 */
+	if (page_shortage > 0)
+		wakeup(&vm_cnt.v_laundry_count);
+
 #if !defined(NO_SWAPPING)
 	/*
 	 * Wakeup the swapout daemon if we didn't cache or free the targeted
@@ -1323,14 +1464,6 @@ relock_queues:
 #endif
 
 	/*
-	 * Wakeup the sync daemon if we skipped a vnode in a writeable object
-	 * and we didn't cache or free enough pages.
-	 */
-	if (vnodes_skipped > 0 && page_shortage > vm_cnt.v_free_target -
-	    vm_cnt.v_free_min)
-		(void)speedup_syncer();
-
-	/*
 	 * Compute the number of pages we want to try to move from the
 	 * active queue to the inactive queue.
 	 */
@@ -1422,7 +1555,14 @@ relock_queues:
 		if (m->act_count == 0) {
 			/* Dequeue to avoid later lock recursion. */
 			vm_page_dequeue_locked(m);
-			vm_page_deactivate(m);
+#if 0
+			if (m->object->ref_count != 0)
+				vm_page_test_dirty(m);
+#endif
+			if (m->dirty == 0)
+				vm_page_deactivate(m);
+			else
+				vm_page_launder(m);
 			page_shortage--;
 		} else
 			vm_page_requeue_locked(m);
@@ -1734,6 +1874,10 @@ vm_pageout(void)
 #endif
 
 	swap_pager_swap_init();
+	error = kthread_add(vm_pageout_laundry_worker, NULL, curproc, NULL,
+	    0, 0, "laundry: dom0");
+	if (error != 0)
+		panic("starting laundry for domain 0, error %d", error);
 #if MAXMEMDOM > 1
 	for (i = 1; i < vm_ndomains; i++) {
 		error = kthread_add(vm_pageout_worker, (void *)(uintptr_t)i,


More information about the svn-src-user mailing list