diff options
author | jtl <jtl@FreeBSD.org> | 2015-12-20 02:05:33 +0000 |
---|---|---|
committer | jtl <jtl@FreeBSD.org> | 2015-12-20 02:05:33 +0000 |
commit | 94d8d1452b2e808b715bab0a09be680cecdcb2a3 (patch) | |
tree | 35edbedbe3247ad76258a68a02cbebe4e0c54cb1 /sys/vm | |
parent | 08c9c0b08fad2803df027433603e31f54cfcd916 (diff) | |
download | FreeBSD-src-94d8d1452b2e808b715bab0a09be680cecdcb2a3.zip FreeBSD-src-94d8d1452b2e808b715bab0a09be680cecdcb2a3.tar.gz |
Add a safety net to reclaim mbufs when one of the mbuf zones become
exhausted.
It is possible for a bug in the code (or, theoretically, even unusual
network conditions) to exhaust all possible mbufs or mbuf clusters.
When this occurs, things can grind to a halt fairly quickly. However,
we currently do not call mb_reclaim() unless the entire system is
experiencing a low-memory condition.
While it is best to try to prevent exhaustion of one of the mbuf zones,
it would also be useful to have a mechanism to attempt to recover from
these situations by freeing "expendable" mbufs.
This patch makes two changes:
a) The patch adds a generic API to the UMA zone allocator to set a
function that should be called when an allocation fails because the
zone limit has been reached. Because of the way this function can be
called, it really should do minimal work.
b) The patch uses this API to try to free mbufs when an allocation
fails from one of the mbuf zones because the zone limit has been
reached. The function schedules a callout to run mb_reclaim().
Differential Revision: https://reviews.freebsd.org/D3864
Reviewed by: gnn
Comments by: rrs, glebius
MFC after: 2 weeks
Sponsored by: Juniper Networks
Diffstat (limited to 'sys/vm')
-rw-r--r-- | sys/vm/uma.h | 13 | ||||
-rw-r--r-- | sys/vm/uma_core.c | 20 | ||||
-rw-r--r-- | sys/vm/uma_int.h | 4 |
3 files changed, 36 insertions, 1 deletions
diff --git a/sys/vm/uma.h b/sys/vm/uma.h index d3e0658..d218e60 100644 --- a/sys/vm/uma.h +++ b/sys/vm/uma.h @@ -521,6 +521,19 @@ int uma_zone_get_max(uma_zone_t zone); void uma_zone_set_warning(uma_zone_t zone, const char *warning); /* + * Sets a function to run when limit is reached + * + * Arguments: + * zone The zone to which this applies + * fx The function ro run + * + * Returns: + * Nothing + */ +typedef void (*uma_maxaction_t)(uma_zone_t); +void uma_zone_set_maxaction(uma_zone_t zone, uma_maxaction_t); + +/* * Obtains the approximate current number of items allocated from a zone * * Arguments: diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c index 3a0a799..4600589 100644 --- a/sys/vm/uma_core.c +++ b/sys/vm/uma_core.c @@ -431,6 +431,13 @@ zone_log_warning(uma_zone_t zone) printf("[zone: %s] %s\n", zone->uz_name, zone->uz_warning); } +static inline void +zone_maxaction(uma_zone_t zone) +{ + if (zone->uz_maxaction) + (*zone->uz_maxaction)(zone); +} + static void zone_foreach_keg(uma_zone_t zone, void (*kegfn)(uma_keg_t)) { @@ -1578,6 +1585,7 @@ zone_ctor(void *mem, int size, void *udata, int flags) zone->uz_flags = 0; zone->uz_warning = NULL; timevalclear(&zone->uz_ratecheck); + zone->uz_maxaction = NULL; keg = arg->keg; ZONE_LOCK_INIT(zone, (arg->flags & UMA_ZONE_MTXCLASS)); @@ -2382,6 +2390,7 @@ keg_fetch_slab(uma_keg_t keg, uma_zone_t zone, int flags) if ((zone->uz_flags & UMA_ZFLAG_MULTI) == 0) { zone->uz_flags |= UMA_ZFLAG_FULL; zone_log_warning(zone); + zone_maxaction(zone); } if (flags & M_NOWAIT) break; @@ -2501,6 +2510,7 @@ zone_fetch_slab_multi(uma_zone_t zone, uma_keg_t last, int rflags) zone->uz_flags |= UMA_ZFLAG_FULL; zone->uz_sleeps++; zone_log_warning(zone); + zone_maxaction(zone); msleep(zone, zone->uz_lockptr, PVM, "zonelimit", hz/100); zone->uz_flags &= ~UMA_ZFLAG_FULL; @@ -3007,6 +3017,16 @@ uma_zone_set_warning(uma_zone_t zone, const char *warning) } /* See uma.h */ +void +uma_zone_set_maxaction(uma_zone_t zone, uma_maxaction_t maxaction) +{ + + ZONE_LOCK(zone); + zone->uz_maxaction = maxaction; + ZONE_UNLOCK(zone); +} + +/* See uma.h */ int uma_zone_get_cur(uma_zone_t zone) { diff --git a/sys/vm/uma_int.h b/sys/vm/uma_int.h index ad2a405..5d7ecd3 100644 --- a/sys/vm/uma_int.h +++ b/sys/vm/uma_int.h @@ -303,10 +303,12 @@ struct uma_zone { uint16_t uz_count; /* Amount of items in full bucket */ uint16_t uz_count_min; /* Minimal amount of items there */ - /* The next three fields are used to print a rate-limited warnings. */ + /* The next two fields are used to print a rate-limited warnings. */ const char *uz_warning; /* Warning to print on failure */ struct timeval uz_ratecheck; /* Warnings rate-limiting */ + uma_maxaction_t uz_maxaction; /* Function to run when at limit */ + /* * This HAS to be the last item because we adjust the zone size * based on NCPU and then allocate the space for the zones. |