Make out-of-swap checks more robust.
authormpi <mpi@openbsd.org>
Fri, 27 Oct 2023 19:18:53 +0000 (19:18 +0000)
committermpi <mpi@openbsd.org>
Fri, 27 Oct 2023 19:18:53 +0000 (19:18 +0000)
Consider that the swap space is full when 99% of it is filled with pages that
are no longer present in memory.  This prevents deadlocks when out-of-swap if
some swap ranges had I/O errors and have been marked as 'bad', or if some
pages are unreachable by the pagedaemon and still holding some slots.

Also introduce uvm_swapisfilled() to check if there are some free slots in
the swap.  Note that we consider the swap space completly filled if it is not
possible to write a full cluster.  This prevents deadlocks if a few slots are
never allocated.

ok miod@

sys/uvm/uvm_pdaemon.c
sys/uvm/uvm_swap.c
sys/uvm/uvm_swap.h
sys/uvm/uvmexp.h

index 445102e..1b99b19 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvm_pdaemon.c,v 1.108 2023/10/24 10:00:22 mpi Exp $   */
+/*     $OpenBSD: uvm_pdaemon.c,v 1.109 2023/10/27 19:18:53 mpi Exp $   */
 /*     $NetBSD: uvm_pdaemon.c,v 1.23 2000/08/20 10:24:14 bjh21 Exp $   */
 
 /*
@@ -595,11 +595,8 @@ uvmpd_scan_inactive(struct uvm_pmalloc *pma,
                         * is full, free any swap allocated to the page
                         * so that other pages can be paged out.
                         */
-                       KASSERT(uvmexp.swpginuse <= uvmexp.swpages);
-                       if ((p->pg_flags & PQ_SWAPBACKED) &&
-                           uvmexp.swpginuse == uvmexp.swpages) {
+                       if ((p->pg_flags & PQ_SWAPBACKED) && uvm_swapisfilled())
                                uvmpd_dropswap(p);
-                       }
 
                        /*
                         * the page we are looking at is dirty.   we must
@@ -917,9 +914,7 @@ uvmpd_scan(struct uvm_pmalloc *pma, struct uvm_constraint_range *constraint)
         */
        free = uvmexp.free - BUFPAGES_DEFICIT;
        swap_shortage = 0;
-       if (free < uvmexp.freetarg &&
-           uvmexp.swpginuse == uvmexp.swpages &&
-           !uvm_swapisfull() &&
+       if (free < uvmexp.freetarg && uvm_swapisfilled() && !uvm_swapisfull() &&
            pages_freed == 0) {
                swap_shortage = uvmexp.freetarg - free;
        }
index 2796325..1818cc0 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvm_swap.c,v 1.166 2023/01/10 11:18:47 kettenis Exp $ */
+/*     $OpenBSD: uvm_swap.c,v 1.167 2023/10/27 19:18:53 mpi Exp $      */
 /*     $NetBSD: uvm_swap.c,v 1.40 2000/11/17 11:39:39 mrg Exp $        */
 
 /*
@@ -1516,8 +1516,30 @@ ReTry:   /* XXXMRG */
 }
 
 /*
- * uvm_swapisfull: return true if all of available swap is allocated
- * and in use.
+ * uvm_swapisfilled: return true if the amount of free space in swap is
+ * smaller than the size of a cluster.
+ *
+ * As long as some swap slots are being used by pages currently in memory,
+ * it is possible to reuse them.  Even if the swap space has been completly
+ * filled we do not consider it full.
+ */
+int
+uvm_swapisfilled(void)
+{
+       int result;
+
+       mtx_enter(&uvm_swap_data_lock);
+       KASSERT(uvmexp.swpginuse <= uvmexp.swpages);
+       result = (uvmexp.swpginuse + SWCLUSTPAGES) >= uvmexp.swpages;
+       mtx_leave(&uvm_swap_data_lock);
+
+       return result;
+}
+
+/*
+ * uvm_swapisfull: return true if the amount of pages only in swap
+ * accounts for more than 99% of the total swap space.
+ *
  */
 int
 uvm_swapisfull(void)
@@ -1526,7 +1548,7 @@ uvm_swapisfull(void)
 
        mtx_enter(&uvm_swap_data_lock);
        KASSERT(uvmexp.swpgonly <= uvmexp.swpages);
-       result = (uvmexp.swpgonly == uvmexp.swpages);
+       result = (uvmexp.swpgonly >= (uvmexp.swpages * 99 / 100));
        mtx_leave(&uvm_swap_data_lock);
 
        return result;
index 9904fe5..58b0bfc 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvm_swap.h,v 1.19 2022/06/28 19:19:34 mpi Exp $       */
+/*     $OpenBSD: uvm_swap.h,v 1.20 2023/10/27 19:18:53 mpi Exp $       */
 /*     $NetBSD: uvm_swap.h,v 1.5 2000/01/11 06:57:51 chs Exp $ */
 
 /*
@@ -42,6 +42,7 @@ int                   uvm_swap_put(int, struct vm_page **, int, int);
 int                    uvm_swap_alloc(int *, boolean_t);
 void                   uvm_swap_free(int, int);
 void                   uvm_swap_markbad(int, int);
+int                    uvm_swapisfilled(void);
 int                    uvm_swapisfull(void);
 void                   uvm_swap_freepages(struct vm_page **, int);
 #ifdef HIBERNATE
index de5f5fa..7d02257 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uvmexp.h,v 1.10 2022/08/29 11:09:31 mpi Exp $ */
+/*     $OpenBSD: uvmexp.h,v 1.11 2023/10/27 19:18:53 mpi Exp $ */
 
 #ifndef        _UVM_UVMEXP_
 #define        _UVM_UVMEXP_
@@ -83,7 +83,7 @@ struct uvmexp {
        /* swap */
        int nswapdev;   /* [S] number of configured swap devices in system */
        int swpages;    /* [S] number of PAGE_SIZE'ed swap pages */
-       int swpginuse;  /* [K] number of swap pages in use */
+       int swpginuse;  /* [S] number of swap pages in use */
        int swpgonly;   /* [a] number of swap pages in use, not also in RAM */
        int nswget;     /* [a] number of swap pages moved from disk to RAM */
        int nanon;      /* XXX number total of anon's in system */