diff options
author | Mateusz Guzik <mjg@FreeBSD.org> | 2021-03-17 21:33:47 +0000 |
---|---|---|
committer | Mateusz Guzik <mjg@FreeBSD.org> | 2021-03-18 14:59:03 +0000 |
commit | e9272225e6bed840b00eef1c817b188c172338ee (patch) | |
tree | b1e3f314416e8626886eb32757ea3b0a400dcf99 /sys/contrib/openzfs/module | |
parent | 21864048f3929192bd20f34145ba62cda6e1d4f9 (diff) | |
download | src-e9272225e6bed840b00eef1c817b188c172338ee.tar.gz src-e9272225e6bed840b00eef1c817b188c172338ee.zip |
vfs: fix vnlru marker handling for filtered/unfiltered cases
The global list has a marker with an invariant that free vnodes are
placed somewhere past that. A caller which performs filtering (like ZFS)
can move said marker all the way to the end, across free vnodes which
don't match. Then a caller which does not perform filtering will fail to
find them. This makes vn_alloc_hard sleep for 1 second instead of
reclaiming, resulting in significant stalls.
Fix the problem by requiring an explicit marker by callers which do
filtering.
As a temporary measure extend vnlru_free to restart if it fails to
reclaim anything.
Big thanks go to the reporter for testing several iterations of the
patch.
Reported by: Yamagi <lists yamagi.org>
Tested by: Yamagi <lists yamagi.org>
Reviewed by: kib
Differential Revision: https://reviews.freebsd.org/D29324
Diffstat (limited to 'sys/contrib/openzfs/module')
-rw-r--r-- | sys/contrib/openzfs/module/os/freebsd/zfs/arc_os.c | 14 |
1 files changed, 12 insertions, 2 deletions
diff --git a/sys/contrib/openzfs/module/os/freebsd/zfs/arc_os.c b/sys/contrib/openzfs/module/os/freebsd/zfs/arc_os.c index 4fc7468bfa47..0d5cffbe8d1e 100644 --- a/sys/contrib/openzfs/module/os/freebsd/zfs/arc_os.c +++ b/sys/contrib/openzfs/module/os/freebsd/zfs/arc_os.c @@ -51,6 +51,9 @@ #include <sys/vm.h> #include <sys/vmmeter.h> +static struct sx arc_vnlru_lock; +static struct vnode *arc_vnlru_marker; + extern struct vfsops zfs_vfsops; uint_t zfs_arc_free_target = 0; @@ -157,7 +160,9 @@ arc_prune_task(void *arg) arc_reduce_target_size(ptob(nr_scan)); free(arg, M_TEMP); - vnlru_free(nr_scan, &zfs_vfsops); + sx_xlock(&arc_vnlru_lock); + vnlru_free_vfsops(nr_scan, &zfs_vfsops, arc_vnlru_marker); + sx_xunlock(&arc_vnlru_lock); } /* @@ -234,7 +239,8 @@ arc_lowmem_init(void) { arc_event_lowmem = EVENTHANDLER_REGISTER(vm_lowmem, arc_lowmem, NULL, EVENTHANDLER_PRI_FIRST); - + arc_vnlru_marker = vnlru_alloc_marker(); + sx_init(&arc_vnlru_lock, "arc vnlru lock"); } void @@ -242,6 +248,10 @@ arc_lowmem_fini(void) { if (arc_event_lowmem != NULL) EVENTHANDLER_DEREGISTER(vm_lowmem, arc_event_lowmem); + if (arc_vnlru_marker != NULL) { + vnlru_free_marker(arc_vnlru_marker); + sx_destroy(&arc_vnlru_lock); + } } void |