Plug a race and a leak in UMA.

1) The race has to do with zone destruction.  From the zone destructor we
   would lock the zone, set the working set size to 0, then unlock the zone,
   drain it, and then free the structure.  Within the window following the
   working-set-size set to 0 and unlocking of the zone and the point where
   in zone_drain we re-acquire the zone lock, the uma timer routine could
   have fired off and changed the working set size to something non-zero,
   thereby potentially preventing us from completely freeing slabs before
   destroying the zone (and thus leaking them).

2) The leak has to do with zone destruction as well.  When destroying a
   zone we would take care to free all the buckets cached in the zone, but
   although we would drain the pcpu cache buckets, we would not free them.
   This resulted in leaking a couple of bucket structures (512 bytes each)
   per cpu on SMP during zone destruction.

While I'm here, also silence GCC warnings by turning uma_slab_alloc()
from inline to real function.  It's too big to be an inline.

Reviewed by: JeffR
This commit is contained in:
Bosko Milekic 2003-07-30 18:55:15 +00:00
parent 93b4c5b707
commit d56368d779

View File

@ -156,9 +156,9 @@ static void *obj_alloc(uma_zone_t, int, u_int8_t *, int);
static void *page_alloc(uma_zone_t, int, u_int8_t *, int);
static void page_free(void *, int, u_int8_t);
static uma_slab_t slab_zalloc(uma_zone_t, int);
static void cache_drain(uma_zone_t);
static void cache_drain(uma_zone_t, int);
static void bucket_drain(uma_zone_t, uma_bucket_t);
static void zone_drain(uma_zone_t);
static void zone_drain_common(uma_zone_t, int);
static void zone_ctor(void *, int, void *);
static void zone_dtor(void *, int, void *);
static void zero_init(void *, int);
@ -177,6 +177,7 @@ static void bucket_enable(void);
static int uma_zalloc_bucket(uma_zone_t zone, int flags);
static uma_slab_t uma_zone_slab(uma_zone_t zone, int flags);
static void *uma_slab_alloc(uma_zone_t zone, uma_slab_t slab);
static __inline void zone_drain(uma_zone_t);
void uma_print_zone(uma_zone_t);
void uma_print_stats(void);
@ -475,7 +476,8 @@ bucket_drain(uma_zone_t zone, uma_bucket_t bucket)
* Drains the per cpu caches for a zone.
*
* Arguments:
* zone The zone to drain, must be unlocked.
* zone The zone to drain, must be unlocked.
* destroy Whether or not to destroy the pcpu buckets (from zone_dtor)
*
* Returns:
* Nothing
@ -485,7 +487,7 @@ bucket_drain(uma_zone_t zone, uma_bucket_t bucket)
*
*/
static void
cache_drain(uma_zone_t zone)
cache_drain(uma_zone_t zone, int destroy)
{
uma_bucket_t bucket;
uma_cache_t cache;
@ -510,6 +512,13 @@ cache_drain(uma_zone_t zone)
cache = &zone->uz_cpu[cpu];
bucket_drain(zone, cache->uc_allocbucket);
bucket_drain(zone, cache->uc_freebucket);
if (destroy) {
uma_zfree_internal(bucketzone, cache->uc_allocbucket,
NULL, 0);
uma_zfree_internal(bucketzone, cache->uc_freebucket,
NULL, 0);
cache->uc_allocbucket = cache->uc_freebucket = NULL;
}
}
/*
@ -545,13 +554,14 @@ cache_drain(uma_zone_t zone)
*
* Arguments:
* zone The zone to free pages from
* all Should we drain all items?
* all Should we drain all items?
* destroy Whether to destroy the zone and pcpu buckets (from zone_dtor)
*
* Returns:
* Nothing.
*/
static void
zone_drain(uma_zone_t zone)
zone_drain_common(uma_zone_t zone, int destroy)
{
struct slabhead freeslabs = {};
uma_slab_t slab;
@ -571,7 +581,10 @@ zone_drain(uma_zone_t zone)
ZONE_LOCK(zone);
if (!(zone->uz_flags & UMA_ZFLAG_INTERNAL))
cache_drain(zone);
cache_drain(zone, destroy);
if (destroy)
zone->uz_wssize = 0;
if (zone->uz_free < zone->uz_wssize)
goto finished;
@ -644,6 +657,13 @@ zone_drain(uma_zone_t zone)
}
static __inline
void
zone_drain(uma_zone_t zone)
{
zone_drain_common(zone, 0);
}
/*
* Allocate a new slab for a zone. This does not insert the slab onto a list.
*
@ -1098,13 +1118,9 @@ zone_dtor(void *arg, int size, void *udata)
uma_zone_t zone;
zone = (uma_zone_t)arg;
ZONE_LOCK(zone);
zone->uz_wssize = 0;
ZONE_UNLOCK(zone);
mtx_lock(&uma_mtx);
LIST_REMOVE(zone, uz_link);
zone_drain(zone);
zone_drain_common(zone, 1);
mtx_unlock(&uma_mtx);
ZONE_LOCK(zone);
@ -1501,7 +1517,7 @@ uma_zone_slab(uma_zone_t zone, int flags)
return (slab);
}
static __inline void *
static void *
uma_slab_alloc(uma_zone_t zone, uma_slab_t slab)
{
void *item;