From owner-svn-src-stable-11@freebsd.org Sun Oct 30 18:11:36 2016 Return-Path: Delivered-To: svn-src-stable-11@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id A38B5C27DDB; Sun, 30 Oct 2016 18:11:36 +0000 (UTC) (envelope-from alc@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 7AB311D45; Sun, 30 Oct 2016 18:11:36 +0000 (UTC) (envelope-from alc@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u9UIBZUO062255; Sun, 30 Oct 2016 18:11:35 GMT (envelope-from alc@FreeBSD.org) Received: (from alc@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u9UIBZG3062254; Sun, 30 Oct 2016 18:11:35 GMT (envelope-from alc@FreeBSD.org) Message-Id: <201610301811.u9UIBZG3062254@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: alc set sender to alc@FreeBSD.org using -f From: Alan Cox Date: Sun, 30 Oct 2016 18:11:35 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r308110 - stable/11/sys/vm X-SVN-Group: stable-11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-11@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: SVN commit messages for only the 11-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 30 Oct 2016 18:11:36 -0000 Author: alc Date: Sun Oct 30 18:11:35 2016 New Revision: 308110 URL: https://svnweb.freebsd.org/changeset/base/308110 Log: MFC r306706 Change vm_pageout_scan() to return a value indicating whether the free page target was met. Previously, vm_pageout_worker() itself checked the length of the free page queues to determine whether vm_pageout_scan(pass >= 1)'s inactive queue scan freed enough pages to meet the free page target. Specifically, vm_pageout_worker() used vm_paging_needed(). The trouble with vm_paging_needed() is that it compares the length of the free page queues to the wakeup threshold for the page daemon, which is much lower than the free page target. Consequently, vm_pageout_worker() could conclude that the inactive queue scan succeeded in meeting its free page target when in fact it did not; and rather than immediately triggering an all-out laundering pass over the inactive queue, vm_pageout_worker() would go back to sleep waiting for the free page count to fall below the page daemon wakeup threshold again, at which point it will perform another limited (pass == 1) scan over the inactive queue. Changing vm_pageout_worker() to use vm_page_count_target() instead of vm_paging_needed() won't work because any page allocations that happen concurrently with the inactive queue scan will result in the free page count being below the target at the end of a successful scan. Instead, having vm_pageout_scan() return a value indicating success or failure is the most straightforward fix. Modified: stable/11/sys/vm/vm_pageout.c Directory Properties: stable/11/ (props changed) Modified: stable/11/sys/vm/vm_pageout.c ============================================================================== --- stable/11/sys/vm/vm_pageout.c Sun Oct 30 18:05:18 2016 (r308109) +++ stable/11/sys/vm/vm_pageout.c Sun Oct 30 18:11:35 2016 (r308110) @@ -121,7 +121,7 @@ static void vm_pageout(void); static void vm_pageout_init(void); static int vm_pageout_clean(vm_page_t m); static int vm_pageout_cluster(vm_page_t m); -static void vm_pageout_scan(struct vm_domain *vmd, int pass); +static bool vm_pageout_scan(struct vm_domain *vmd, int pass); static void vm_pageout_mightbe_oom(struct vm_domain *vmd, int page_shortage, int starting_page_shortage); @@ -845,17 +845,20 @@ unlock_mp: * pass 0 - Update active LRU/deactivate pages * pass 1 - Free inactive pages * pass 2 - Launder dirty pages + * + * Returns true if pass was zero or enough pages were freed by the inactive + * queue scan to meet the target. */ -static void +static bool vm_pageout_scan(struct vm_domain *vmd, int pass) { vm_page_t m, next; 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, starting_page_shortage; - int vnodes_skipped; + int act_delta, addl_page_shortage, deficit, error, inactq_shortage; + int maxlaunder, maxscan, page_shortage, scan_tick, scanned; + int starting_page_shortage, vnodes_skipped; boolean_t pageout_ok, queue_locked; /* @@ -886,7 +889,9 @@ vm_pageout_scan(struct vm_domain *vmd, i addl_page_shortage = 0; /* - * Calculate the number of pages that we want to free. + * Calculate the number of pages that we want to free. This number + * can be negative if many pages are freed between the wakeup call to + * the page daemon and this calculation. */ if (pass > 0) { deficit = atomic_readandclear_int(&vm_pageout_deficit); @@ -956,7 +961,7 @@ vm_pageout_scan(struct vm_domain *vmd, i * Held pages are essentially stuck in the * queue. So, they ought to be discounted * from the inactive count. See the - * calculation of the page_shortage for the + * calculation of inactq_shortage before the * loop over the active queue below. */ addl_page_shortage++; @@ -1164,7 +1169,7 @@ relock_queue: * Compute the number of pages we want to try to move from the * active queue to the inactive queue. */ - page_shortage = vm_cnt.v_inactive_target - vm_cnt.v_inactive_count + + inactq_shortage = vm_cnt.v_inactive_target - vm_cnt.v_inactive_count + vm_paging_target() + deficit + addl_page_shortage; pq = &vmd->vmd_pagequeues[PQ_ACTIVE]; @@ -1182,7 +1187,7 @@ relock_queue: min_scan /= hz * vm_pageout_update_period; } else min_scan = 0; - if (min_scan > 0 || (page_shortage > 0 && maxscan > 0)) + if (min_scan > 0 || (inactq_shortage > 0 && maxscan > 0)) vmd->vmd_last_active_scan = scan_tick; /* @@ -1191,7 +1196,7 @@ relock_queue: * candidates. Held pages may be deactivated. */ for (m = TAILQ_FIRST(&pq->pq_pl), scanned = 0; m != NULL && (scanned < - min_scan || (page_shortage > 0 && scanned < maxscan)); m = next, + min_scan || (inactq_shortage > 0 && scanned < maxscan)); m = next, scanned++) { KASSERT(m->queue == PQ_ACTIVE, ("vm_pageout_scan: page %p isn't active", m)); @@ -1256,7 +1261,7 @@ relock_queue: /* Dequeue to avoid later lock recursion. */ vm_page_dequeue_locked(m); vm_page_deactivate(m); - page_shortage--; + inactq_shortage--; } else vm_page_requeue_locked(m); vm_page_unlock(m); @@ -1274,6 +1279,7 @@ relock_queue: } } #endif + return (page_shortage <= 0); } static int vm_pageout_oom_vote; @@ -1503,9 +1509,11 @@ vm_pageout_worker(void *arg) { struct vm_domain *domain; int domidx; + bool target_met; domidx = (uintptr_t)arg; domain = &vm_dom[domidx]; + target_met = true; /* * XXXKIB It could be useful to bind pageout daemon threads to @@ -1544,11 +1552,11 @@ vm_pageout_worker(void *arg) } /* - * Do not clear vm_pageout_wanted until we reach our target. - * Otherwise, we may be awakened over and over again, wasting - * CPU time. + * Do not clear vm_pageout_wanted until we reach our free page + * target. Otherwise, we may be awakened over and over again, + * wasting CPU time. */ - if (vm_pageout_wanted && !vm_paging_needed()) + if (vm_pageout_wanted && target_met) vm_pageout_wanted = false; /* @@ -1583,7 +1591,7 @@ vm_pageout_worker(void *arg) domain->vmd_pass = 0; } - vm_pageout_scan(domain, domain->vmd_pass); + target_met = vm_pageout_scan(domain, domain->vmd_pass); } }