update sbchecklowmem() to better detect actual mbuf memory usage.
authordlg <dlg@openbsd.org>
Mon, 14 Feb 2022 04:33:18 +0000 (04:33 +0000)
committerdlg <dlg@openbsd.org>
Mon, 14 Feb 2022 04:33:18 +0000 (04:33 +0000)
previously sbchecklowmem() (and sonewconn()) would look at the mbuf
and mbuf cluster pools to see if they were approaching their hard
limits. based on how many mbufs/clusters were allocated against the
limits, socket operations would start to fail with ENOBUFS until
utilisation went down.

mbufs and clusters have changed a lot since then though. there are
now many mbuf cluster pools, not just one for 2k clusters. because
of this the mbuf layer now limits the amount of memory all the mbuf
pools can allocate backend pages from rather than limit the individual
pools. this means sbchecklowmem() ends up looking at the default
pool hard limit, which is UINT_MAX, which in turn means means
sbchecklowmem() probably never applies backpressure. this is made
worse on multiprocessor systems where per cpu caches of mbuf and
cluster pool items are enabled because the number of in use pool
items is distorted by the cpu caches.

this switches sbchecklowmem to looking at the page allocations made
by all the pools instead. the big benefit of this is that the page
allocations are much more representative of the overall mbuf memory
usage in the system. the downside is is that the backend page
allocation accounting does not see idle memory held by pools. pools
cannot release partially free pages to the page backend (obviously),
and pools cache idle items to avoid thrashing on the backend page
allocator. this means the page allocation level is higher than the
memory used by actual in-flight mbufs.

however, this can also be a benefit. the backend page allocation is a
kind of smoothed out "trend" line. mbuf utilisation over short periods
can be extremely bursty because of things like rx ring dequeue and fill
cycles, or large socket sends. if you're trying to grow socket
buffers while these things are happening, luck becomes an important
factor in whether it will work or not. because pools cache idle items,
the backend page utilisation better represents the overall trend
of activity in the system and will give more consistent behaviour here.

this diff is deliberately simple. we're basically going from "no
limits" to "some sort of limit" for sockets again, so keeping the
code simple means it should be easy to understand and tweak in the
future.

ok djm@ visa@ claudio@

sys/kern/uipc_mbuf.c
sys/kern/uipc_socket2.c
sys/sys/mbuf.h

index acac2c0..2f11e8c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uipc_mbuf.c,v 1.281 2022/02/08 11:28:19 dlg Exp $     */
+/*     $OpenBSD: uipc_mbuf.c,v 1.282 2022/02/14 04:33:18 dlg Exp $     */
 /*     $NetBSD: uipc_mbuf.c,v 1.15.4.1 1996/06/13 17:11:44 cgd Exp $   */
 
 /*
@@ -1502,6 +1502,12 @@ m_pool_init(struct pool *pp, u_int size, u_int align, const char *wmesg)
        pool_set_constraints(pp, &kp_dma_contig);
 }
 
+u_int
+m_pool_used(void)
+{
+       return ((mbuf_mem_alloc * 100) / mbuf_mem_limit);
+}
+
 #ifdef DDB
 void
 m_print(void *v,
index 42a61e6..6da3c74 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uipc_socket2.c,v 1.116 2021/11/06 05:26:33 visa Exp $ */
+/*     $OpenBSD: uipc_socket2.c,v 1.117 2022/02/14 04:33:18 dlg Exp $  */
 /*     $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $       */
 
 /*
@@ -155,7 +155,7 @@ sonewconn(struct socket *head, int connstatus)
         */
        soassertlocked(head);
 
-       if (mclpools[0].pr_nout > mclpools[0].pr_hardlimit * 95 / 100)
+       if (m_pool_used() > 95)
                return (NULL);
        if (head->so_qlen + head->so_q0len > head->so_qlimit * 3)
                return (NULL);
@@ -517,13 +517,13 @@ int
 sbchecklowmem(void)
 {
        static int sblowmem;
+       unsigned int used = m_pool_used();
 
-       if (mclpools[0].pr_nout < mclpools[0].pr_hardlimit * 60 / 100 ||
-           mbpool.pr_nout < mbpool.pr_hardlimit * 60 / 100)
+       if (used < 60)
                sblowmem = 0;
-       if (mclpools[0].pr_nout > mclpools[0].pr_hardlimit * 80 / 100 ||
-           mbpool.pr_nout > mbpool.pr_hardlimit * 80 / 100)
+       else if (used > 80)
                sblowmem = 1;
+
        return (sblowmem);
 }
 
index 488b75b..7265562 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mbuf.h,v 1.253 2021/05/15 08:07:20 yasuoka Exp $      */
+/*     $OpenBSD: mbuf.h,v 1.254 2022/02/14 04:33:18 dlg Exp $  */
 /*     $NetBSD: mbuf.h,v 1.19 1996/02/09 18:25:14 christos Exp $       */
 
 /*
@@ -429,6 +429,7 @@ void        m_align(struct mbuf *, int);
 struct mbuf *m_clget(struct mbuf *, int, u_int);
 void   m_extref(struct mbuf *, struct mbuf *);
 void   m_pool_init(struct pool *, u_int, u_int, const char *);
+u_int  m_pool_used(void);
 void   m_extfree_pool(caddr_t, u_int, void *);
 void   m_adj(struct mbuf *, int);
 int    m_copyback(struct mbuf *, int, int, const void *, int);