Module Name: src Committed By: chs Date: Sun Apr 21 15:32:18 UTC 2019
Modified Files: src/sys/uvm: uvm_pdaemon.c Log Message: Draining pools from the pagedaemon thread can deadlock, because draining a pool can involve taking a lock which can be held by a thread which is blocked waiting for memory. Avoid this by moving the pool-draining work to a separate worker thread. To generate a diff of this commit: cvs rdiff -u -r1.109 -r1.110 src/sys/uvm/uvm_pdaemon.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/uvm/uvm_pdaemon.c diff -u src/sys/uvm/uvm_pdaemon.c:1.109 src/sys/uvm/uvm_pdaemon.c:1.110 --- src/sys/uvm/uvm_pdaemon.c:1.109 Sat Oct 28 00:37:13 2017 +++ src/sys/uvm/uvm_pdaemon.c Sun Apr 21 15:32:18 2019 @@ -1,4 +1,4 @@ -/* $NetBSD: uvm_pdaemon.c,v 1.109 2017/10/28 00:37:13 pgoyette Exp $ */ +/* $NetBSD: uvm_pdaemon.c,v 1.110 2019/04/21 15:32:18 chs Exp $ */ /* * Copyright (c) 1997 Charles D. Cranor and Washington University. @@ -66,7 +66,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: uvm_pdaemon.c,v 1.109 2017/10/28 00:37:13 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: uvm_pdaemon.c,v 1.110 2019/04/21 15:32:18 chs Exp $"); #include "opt_uvmhist.h" #include "opt_readahead.h" @@ -79,6 +79,7 @@ __KERNEL_RCSID(0, "$NetBSD: uvm_pdaemon. #include <sys/buf.h> #include <sys/module.h> #include <sys/atomic.h> +#include <sys/kthread.h> #include <uvm/uvm.h> #include <uvm/uvm_pdpolicy.h> @@ -105,9 +106,16 @@ UVMHIST_DEFINE(pdhist); static void uvmpd_scan(void); static void uvmpd_scan_queue(void); static void uvmpd_tune(void); +static void uvmpd_pool_drain_thread(void *); +static void uvmpd_pool_drain_wakeup(void); static unsigned int uvm_pagedaemon_waiters; +/* State for the pool drainer thread */ +static kmutex_t uvmpd_pool_drain_lock; +static kcondvar_t uvmpd_pool_drain_cv; +static bool uvmpd_pool_drain_run = false; + /* * XXX hack to avoid hangs when large processes fork. */ @@ -229,14 +237,21 @@ uvmpd_tune(void) void uvm_pageout(void *arg) { - int bufcnt, npages = 0; + int npages = 0; int extrapages = 0; - struct pool *pp; UVMHIST_FUNC("uvm_pageout"); UVMHIST_CALLED(pdhist); UVMHIST_LOG(pdhist,"<starting uvm pagedaemon>", 0, 0, 0, 0); + mutex_init(&uvmpd_pool_drain_lock, MUTEX_DEFAULT, IPL_VM); + cv_init(&uvmpd_pool_drain_cv, "pooldrain"); + + /* Create the pool drainer kernel thread. */ + if (kthread_create(PRI_VM, KTHREAD_MPSAFE, NULL, + uvmpd_pool_drain_thread, NULL, NULL, "pooldrain")) + panic("fork pooldrain"); + /* * ensure correct priority and set paging parameters... */ @@ -288,9 +303,6 @@ uvm_pageout(void *arg) * system only when entire pool page is empty. */ mutex_spin_enter(&uvm_fpageqlock); - bufcnt = uvmexp.freetarg - uvmexp.free; - if (bufcnt < 0) - bufcnt = 0; UVMHIST_LOG(pdhist," free/ftarg=%jd/%jd", uvmexp.free, uvmexp.freetarg, 0,0); @@ -331,16 +343,10 @@ uvm_pageout(void *arg) continue; /* - * kill unused metadata buffers. + * kick the pool drainer thread. */ - mutex_enter(&bufcache_lock); - buf_drain(bufcnt << PAGE_SHIFT); - mutex_exit(&bufcache_lock); - /* - * drain the pools. - */ - pool_drain(&pp); + uvmpd_pool_drain_wakeup(); } /*NOTREACHED*/ } @@ -1022,3 +1028,53 @@ uvm_estimatepageable(int *active, int *i uvmpdpol_estimatepageable(active, inactive); } + +/* + * Use a separate thread for draining pools. + * This work can't done from the main pagedaemon thread because + * some pool allocators need to take vm_map locks. + */ + +static void +uvmpd_pool_drain_thread(void *arg) +{ + int bufcnt; + + for (;;) { + mutex_enter(&uvmpd_pool_drain_lock); + if (!uvmpd_pool_drain_run) { + cv_wait(&uvmpd_pool_drain_cv, &uvmpd_pool_drain_lock); + } + uvmpd_pool_drain_run = false; + mutex_exit(&uvmpd_pool_drain_lock); + + /* + * kill unused metadata buffers. + */ + mutex_spin_enter(&uvm_fpageqlock); + bufcnt = uvmexp.freetarg - uvmexp.free; + mutex_spin_exit(&uvm_fpageqlock); + if (bufcnt < 0) + bufcnt = 0; + + mutex_enter(&bufcache_lock); + buf_drain(bufcnt << PAGE_SHIFT); + mutex_exit(&bufcache_lock); + + /* + * drain a pool. + */ + pool_drain(NULL); + } + /*NOTREACHED*/ +} + +static void +uvmpd_pool_drain_wakeup(void) +{ + + mutex_enter(&uvmpd_pool_drain_lock); + uvmpd_pool_drain_run = true; + cv_signal(&uvmpd_pool_drain_cv); + mutex_exit(&uvmpd_pool_drain_lock); +}