Index: uma_core.c =================================================================== --- uma_core.c (revision 258256) +++ uma_core.c (working copy) @@ -75,6 +75,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -207,7 +208,9 @@ struct uma_bucket_zone { struct uma_bucket_zone bucket_zones[] = { { NULL, "4 Bucket", BUCKET_SIZE(4), 4096 }, + { NULL, "6 Bucket", BUCKET_SIZE(6), 3072 }, { NULL, "8 Bucket", BUCKET_SIZE(8), 2048 }, + { NULL, "12 Bucket", BUCKET_SIZE(12), 1536 }, { NULL, "16 Bucket", BUCKET_SIZE(16), 1024 }, { NULL, "32 Bucket", BUCKET_SIZE(32), 512 }, { NULL, "64 Bucket", BUCKET_SIZE(64), 256 }, @@ -683,6 +686,49 @@ cache_drain(uma_zone_t zone) } /* + * Safely drain per-CPU caches of a zone to alloc bucket. + * This is an expensive call because it needs to bind to all CPUs + * one by one and enter a critical section on each of them in order + * to safely access their cache buckets. + * Zone lock must be held on call this function. + */ +static void +cache_drain_safe(uma_zone_t zone) +{ + uma_cache_t cache; + int cpu; + + mtx_assert(&zone->uz_lock, MA_OWNED); + + /* + * Polite bucket sizes shrinking was not enouth, shrink aggressively. + */ + zone->uz_count = (zone->uz_count_min + zone->uz_count) / 2; + + CPU_FOREACH(cpu) { + thread_lock(curthread); + sched_bind(curthread, cpu); + thread_unlock(curthread); + critical_enter(); + cache = &zone->uz_cpu[cpu]; + if (cache->uc_allocbucket) { + LIST_INSERT_HEAD(&zone->uz_buckets, + cache->uc_allocbucket, ub_link); + cache->uc_allocbucket = NULL; + } + if (cache->uc_freebucket) { + LIST_INSERT_HEAD(&zone->uz_buckets, + cache->uc_freebucket, ub_link); + cache->uc_freebucket = NULL; + } + critical_exit(); + } + thread_lock(curthread); + sched_unbind(curthread); + thread_unlock(curthread); +} + +/* * Drain the cached buckets from a zone. Expects a locked zone on entry. */ static void @@ -701,6 +747,13 @@ bucket_cache_drain(uma_zone_t zone) bucket_free(zone, bucket, NULL); ZONE_LOCK(zone); } + + /* + * Shrink further bucket sizes. Price of single zone lock collision + * is probably lower then price of global cache drain. + */ + if (zone->uz_count > zone->uz_count_min) + zone->uz_count--; } static void @@ -803,6 +856,8 @@ zone_drain_wait(uma_zone_t zone, int waitok) mtx_lock(&uma_mtx); } zone->uz_flags |= UMA_ZFLAG_DRAINING; + if (vm_page_count_min() && !(zone->uz_flags & UMA_ZFLAG_INTERNAL)) + cache_drain_safe(zone); bucket_cache_drain(zone); ZONE_UNLOCK(zone); /* @@ -1461,6 +1516,7 @@ zone_ctor(void *mem, int size, void *udata, int fl zone->uz_fails = 0; zone->uz_sleeps = 0; zone->uz_count = 0; + zone->uz_count_min = 0; zone->uz_flags = 0; zone->uz_warning = NULL; timevalclear(&zone->uz_ratecheck); @@ -1552,6 +1608,7 @@ out: zone->uz_count = bucket_select(zone->uz_size); else zone->uz_count = BUCKET_MAX; + zone->uz_count_min = zone->uz_count; return (0); } @@ -2518,6 +2575,7 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *u { uma_cache_t cache; uma_bucket_t bucket; + int lockfail; int cpu; #ifdef UMA_DEBUG_ALLOC_1 @@ -2602,7 +2660,12 @@ zfree_start: if (zone->uz_count == 0 || bucketdisable) goto zfree_item; - ZONE_LOCK(zone); + lockfail = 0; + if (ZONE_TRYLOCK(zone) == 0) { + /* Record contention to size the buckets. */ + ZONE_LOCK(zone); + lockfail = 1; + } critical_enter(); cpu = curcpu; cache = &zone->uz_cpu[cpu]; @@ -2636,7 +2699,12 @@ zfree_start: /* We are no longer associated with this CPU. */ critical_exit(); - /* And the zone.. */ + /* + * We bump the uz count when the cache size is insufficient to + * handle the working set. + */ + if (lockfail && zone->uz_count < BUCKET_MAX) + zone->uz_count++; ZONE_UNLOCK(zone); #ifdef UMA_DEBUG_ALLOC Index: uma_int.h =================================================================== --- uma_int.h (revision 258256) +++ uma_int.h (working copy) @@ -300,7 +300,8 @@ struct uma_zone { volatile u_long uz_fails; /* Total number of alloc failures */ volatile u_long uz_frees; /* Total number of frees */ uint64_t uz_sleeps; /* Total number of alloc sleeps */ - uint16_t uz_count; /* Highest amount of items in bucket */ + 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. */ const char *uz_warning; /* Warning to print on failure */