Module Name: src Committed By: jdolecek Date: Sun Apr 12 08:51:41 UTC 2020
Modified Files: src/sys/kern: vfs_wapbl.c Log Message: fix race between wapbl_discard() and wapbl_biodone() on forced unmount on shutdown with slow I/O device wapbl_discard() needs to hold both wl_mtx and bufcache_lock while manipulating wl_entries - the rw lock is not enough, because wapbl_biodone() only takes wl_mtx while removing the finished entry from list wapbl_biodone() must take bufcache_lock before reading we->we_wapbl, so it's blocked until wapbl_discard() finishes, and takes !wl path appropriately this is supposed to fix panic on shutdown: [ 67549.6304123] forcefully unmounting / (/dev/wd0a)... ... [ 67549.7272030] panic: mutex_vector_enter,510: uninitialized lock (lock=0xffffa722a4f4f5b0, from=ffffffff80a884fa) ... [ 67549.7272030] wapbl_biodone() at netbsd:wapbl_biodone+0x4d [ 67549.7272030] biointr() at netbsd:biointr+0x7d [ 67549.7272030] softint_dispatch() at netbsd:softint_dispatch+0x12c [ 67549.7272030] Xsoftintr() at netbsd:Xsoftintr+0x4f To generate a diff of this commit: cvs rdiff -u -r1.106 -r1.107 src/sys/kern/vfs_wapbl.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/kern/vfs_wapbl.c diff -u src/sys/kern/vfs_wapbl.c:1.106 src/sys/kern/vfs_wapbl.c:1.107 --- src/sys/kern/vfs_wapbl.c:1.106 Mon Mar 16 21:20:10 2020 +++ src/sys/kern/vfs_wapbl.c Sun Apr 12 08:51:41 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: vfs_wapbl.c,v 1.106 2020/03/16 21:20:10 pgoyette Exp $ */ +/* $NetBSD: vfs_wapbl.c,v 1.107 2020/04/12 08:51:41 jdolecek Exp $ */ /*- * Copyright (c) 2003, 2008, 2009 The NetBSD Foundation, Inc. @@ -36,7 +36,7 @@ #define WAPBL_INTERNAL #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: vfs_wapbl.c,v 1.106 2020/03/16 21:20:10 pgoyette Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vfs_wapbl.c,v 1.107 2020/04/12 08:51:41 jdolecek Exp $"); #include <sys/param.h> #include <sys/bitops.h> @@ -226,7 +226,7 @@ struct wapbl { u_long wl_inohashmask; int wl_inohashcnt; - SIMPLEQ_HEAD(, wapbl_entry) wl_entries; /* On disk transaction + SIMPLEQ_HEAD(, wapbl_entry) wl_entries; /* m: On disk transaction accounting */ /* buffers for wapbl_buffered_write() */ @@ -786,12 +786,10 @@ wapbl_discard(struct wapbl *wl) mutex_enter(&wl->wl_mtx); } } - mutex_exit(&wl->wl_mtx); - mutex_exit(&bufcache_lock); /* * Remove references to this wl from wl_entries, free any which - * no longer have buffers, others will be freed in wapbl_biodone + * no longer have buffers, others will be freed in wapbl_biodone() * when they no longer have any buffers. */ while ((we = SIMPLEQ_FIRST(&wl->wl_entries)) != NULL) { @@ -807,6 +805,9 @@ wapbl_discard(struct wapbl *wl) } } + mutex_exit(&wl->wl_mtx); + mutex_exit(&bufcache_lock); + /* Discard list of deallocs */ while ((wd = TAILQ_FIRST(&wl->wl_dealloclist)) != NULL) wapbl_deallocation_free(wl, wd, true); @@ -1604,11 +1605,15 @@ void wapbl_biodone(struct buf *bp) { struct wapbl_entry *we = bp->b_private; - struct wapbl *wl = we->we_wapbl; + struct wapbl *wl; #ifdef WAPBL_DEBUG_BUFBYTES const int bufsize = bp->b_bufsize; #endif + mutex_enter(&bufcache_lock); + wl = we->we_wapbl; + mutex_exit(&bufcache_lock); + /* * Handle possible flushing of buffers after log has been * decomissioned.