git: 0190c38b9dfa - main - swapoff_one(): only check free pages count manually turning swap off

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Mon, 29 Nov 2021 16:39:17 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=0190c38b9dfaa16de1bc61e829b9a1221fed7896

commit 0190c38b9dfaa16de1bc61e829b9a1221fed7896
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2021-11-26 23:22:27 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2021-11-29 16:38:02 +0000

    swapoff_one(): only check free pages count manually turning swap off
    
    When swap is turned off due to system shutdown or reboot, ignore the
    check.  Problem is that the check is not accurate by any means, free
    page count can legitimately be low while system still able to page in
    everything from the swap.  Then, we turn swap off if swapping on
    real file or some non-standard geom provider, and typically panic
    when system appears to actually need to unavailable page.
    
    For syscall, it is better to be safe than sorry.
    
    Reported and tested by: peterj
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D33147
---
 sys/vm/swap_pager.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/sys/vm/swap_pager.c b/sys/vm/swap_pager.c
index 4cfdb3fd2cc8..f313d8b2d014 100644
--- a/sys/vm/swap_pager.c
+++ b/sys/vm/swap_pager.c
@@ -469,7 +469,8 @@ static bool	swp_pager_swblk_empty(struct swblk *sb, int start, int limit);
 static void	swp_pager_free_empty_swblk(vm_object_t, struct swblk *sb);
 static int	swapongeom(struct vnode *);
 static int	swaponvp(struct thread *, struct vnode *, u_long);
-static int	swapoff_one(struct swdevt *sp, struct ucred *cred);
+static int	swapoff_one(struct swdevt *sp, struct ucred *cred,
+		    bool ignore_check);
 
 /*
  * Swap bitmap functions
@@ -2523,14 +2524,14 @@ sys_swapoff(struct thread *td, struct swapoff_args *uap)
 		error = EINVAL;
 		goto done;
 	}
-	error = swapoff_one(sp, td->td_ucred);
+	error = swapoff_one(sp, td->td_ucred, false);
 done:
 	sx_xunlock(&swdev_syscall_lock);
 	return (error);
 }
 
 static int
-swapoff_one(struct swdevt *sp, struct ucred *cred)
+swapoff_one(struct swdevt *sp, struct ucred *cred, bool ignore_check)
 {
 	u_long nblks;
 #ifdef MAC
@@ -2552,8 +2553,16 @@ swapoff_one(struct swdevt *sp, struct ucred *cred)
 	 * available virtual memory in the system will fit the amount
 	 * of data we will have to page back in, plus an epsilon so
 	 * the system doesn't become critically low on swap space.
+	 * The vm_free_count() part does not account e.g. for clean
+	 * pages that can be immediately reclaimed without paging, so
+	 * this is a very rough estimation.
+	 *
+	 * On the other hand, not turning swap off on swapoff_all()
+	 * means that we can lose swap data when filesystems go away,
+	 * which is arguably worse.
 	 */
-	if (vm_free_count() + swap_pager_avail < nblks + nswap_lowat)
+	if (!ignore_check &&
+	    vm_free_count() + swap_pager_avail < nblks + nswap_lowat)
 		return (ENOMEM);
 
 	/*
@@ -2603,7 +2612,7 @@ swapoff_all(void)
 			devname = devtoname(sp->sw_vp->v_rdev);
 		else
 			devname = "[file]";
-		error = swapoff_one(sp, thread0.td_ucred);
+		error = swapoff_one(sp, thread0.td_ucred, true);
 		if (error != 0) {
 			printf("Cannot remove swap device %s (error=%d), "
 			    "skipping.\n", devname, error);