Date: Wed, 25 Dec 2019 20:57:24 +0000 (UTC) From: Jeff Roberson <jeff@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r356081 - head/sys/vm Message-ID: <201912252057.xBPKvON8046974@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jeff Date: Wed Dec 25 20:57:24 2019 New Revision: 356081 URL: https://svnweb.freebsd.org/changeset/base/356081 Log: Further reduce the cacheline footprint of fast allocations by duplicating the zone size and flags fields in the per-cpu caches. This allows fast alloctions to proceed only touching the single per-cpu cacheline and simplifies the common case when no ctor/dtor is specified. Reviewed by: markj, rlibby Differential Revision: https://reviews.freebsd.org/D22826 Modified: head/sys/vm/uma_core.c head/sys/vm/uma_int.h Modified: head/sys/vm/uma_core.c ============================================================================== --- head/sys/vm/uma_core.c Wed Dec 25 20:50:53 2019 (r356080) +++ head/sys/vm/uma_core.c Wed Dec 25 20:57:24 2019 (r356081) @@ -281,7 +281,6 @@ static uma_keg_t uma_kcreate(uma_zone_t zone, size_t s uma_fini fini, int align, uint32_t flags); static int zone_import(void *, void **, int, int, int); static void zone_release(void *, void **, int); -static void uma_zero_item(void *, uma_zone_t); static bool cache_alloc(uma_zone_t, uma_cache_t, void *, int); static bool cache_free(uma_zone_t, uma_cache_t, void *, void *, int); @@ -2183,6 +2182,17 @@ zone_count(uma_zone_t zone, void *arg) zone->uz_namecnt + 1); } +static void +zone_update_caches(uma_zone_t zone) +{ + int i; + + for (i = 0; i <= mp_maxid; i++) { + cache_set_uz_size(&zone->uz_cpu[i], zone->uz_size); + cache_set_uz_flags(&zone->uz_cpu[i], zone->uz_flags); + } +} + /* * Zone header ctor. This initializes all fields, locks, etc. * @@ -2228,7 +2238,7 @@ zone_ctor(void *mem, int size, void *udata, int flags) #ifdef INVARIANTS if (arg->uminit == trash_init && arg->fini == trash_fini) - zone->uz_flags |= UMA_ZFLAG_TRASH; + zone->uz_flags |= UMA_ZFLAG_TRASH | UMA_ZFLAG_CTORDTOR; #endif /* @@ -2327,6 +2337,9 @@ out: else zone->uz_bucket_size = bucket_select(zone->uz_size); zone->uz_bucket_size_min = zone->uz_bucket_size; + if (zone->uz_dtor != NULL || zone->uz_ctor != NULL) + zone->uz_flags |= UMA_ZFLAG_CTORDTOR; + zone_update_caches(zone); return (0); } @@ -2801,8 +2814,14 @@ uma_zfree_pcpu_arg(uma_zone_t zone, void *item, void * uma_zfree_arg(zone, item, udata); } +#ifdef INVARIANTS +#define UMA_ALWAYS_CTORDTOR 1 +#else +#define UMA_ALWAYS_CTORDTOR 0 +#endif + static void * -item_ctor(uma_zone_t zone, void *udata, int flags, void *item) +item_ctor(uma_zone_t zone, int size, void *udata, int flags, void *item) { #ifdef INVARIANTS bool skipdbg; @@ -2810,10 +2829,10 @@ item_ctor(uma_zone_t zone, void *udata, int flags, voi skipdbg = uma_dbg_zskip(zone, item); if (!skipdbg && (zone->uz_flags & UMA_ZFLAG_TRASH) != 0 && zone->uz_ctor != trash_ctor) - trash_ctor(item, zone->uz_size, udata, flags); + trash_ctor(item, size, udata, flags); #endif if (__predict_false(zone->uz_ctor != NULL) && - zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) { + zone->uz_ctor(item, size, udata, flags) != 0) { counter_u64_add(zone->uz_fails, 1); zone_free_item(zone, item, udata, SKIP_DTOR | SKIP_CNT); return (NULL); @@ -2823,13 +2842,14 @@ item_ctor(uma_zone_t zone, void *udata, int flags, voi uma_dbg_alloc(zone, NULL, item); #endif if (flags & M_ZERO) - uma_zero_item(item, zone); + bzero(item, size); return (item); } static inline void -item_dtor(uma_zone_t zone, void *item, void *udata, enum zfreeskip skip) +item_dtor(uma_zone_t zone, void *item, int size, void *udata, + enum zfreeskip skip) { #ifdef INVARIANTS bool skipdbg; @@ -2842,13 +2862,13 @@ item_dtor(uma_zone_t zone, void *item, void *udata, en uma_dbg_free(zone, NULL, item); } #endif - if (skip < SKIP_DTOR) { + if (__predict_true(skip < SKIP_DTOR)) { if (zone->uz_dtor != NULL) - zone->uz_dtor(item, zone->uz_size, udata); + zone->uz_dtor(item, size, udata); #ifdef INVARIANTS if (!skipdbg && (zone->uz_flags & UMA_ZFLAG_TRASH) != 0 && zone->uz_dtor != trash_dtor) - trash_dtor(item, zone->uz_size, udata); + trash_dtor(item, size, udata); #endif } } @@ -2860,7 +2880,7 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags uma_cache_bucket_t bucket; uma_cache_t cache; void *item; - int cpu, domain; + int domain, size, uz_flags; /* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */ random_harvest_fast_uma(&zone, sizeof(zone), RANDOM_UMA); @@ -2869,16 +2889,21 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags CTR4(KTR_UMA, "uma_zalloc_arg thread %x zone %s(%p) flags %d", curthread, zone->uz_name, zone, flags); +#ifdef WITNESS if (flags & M_WAITOK) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "uma_zalloc_arg: zone \"%s\"", zone->uz_name); } +#endif + +#ifdef INVARIANTS KASSERT((flags & M_EXEC) == 0, ("uma_zalloc_arg: called with M_EXEC")); KASSERT(curthread->td_critnest == 0 || SCHEDULER_STOPPED(), ("uma_zalloc_arg: called with spinlock or critical section held")); if (zone->uz_flags & UMA_ZONE_PCPU) KASSERT((flags & M_ZERO) == 0, ("allocating from a pcpu zone " "with M_ZERO passed")); +#endif #ifdef DEBUG_MEMGUARD if (memguard_cmp_zone(zone)) { @@ -2912,13 +2937,19 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags */ critical_enter(); do { - cpu = curcpu; - cache = &zone->uz_cpu[cpu]; + cache = &zone->uz_cpu[curcpu]; bucket = &cache->uc_allocbucket; + size = cache_uz_size(cache); + uz_flags = cache_uz_flags(cache); if (__predict_true(bucket->ucb_cnt != 0)) { item = cache_bucket_pop(cache, bucket); critical_exit(); - return (item_ctor(zone, udata, flags, item)); + if (__predict_false((uz_flags & UMA_ZFLAG_CTORDTOR) != 0 || + UMA_ALWAYS_CTORDTOR)) + return (item_ctor(zone, size, udata, flags, item)); + if (flags & M_ZERO) + bzero(item, size); + return (item); } } while (cache_alloc(zone, cache, udata, flags)); critical_exit(); @@ -2926,7 +2957,7 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags /* * We can not get a bucket so try to return a single item. */ - if (zone->uz_flags & UMA_ZONE_NUMA) + if (uz_flags & UMA_ZONE_NUMA) domain = PCPU_GET(domain); else domain = UMA_ANYDOMAIN; @@ -2945,7 +2976,7 @@ cache_alloc(uma_zone_t zone, uma_cache_t cache, void * { uma_zone_domain_t zdom; uma_bucket_t bucket; - int cpu, domain; + int domain; bool lockfail; CRITICAL_ASSERT(curthread); @@ -2988,8 +3019,7 @@ cache_alloc(uma_zone_t zone, uma_cache_t cache, void * if (zone->uz_bucket_size == 0 || bucketdisable) return (false); - cpu = curcpu; - cache = &zone->uz_cpu[cpu]; + cache = &zone->uz_cpu[curcpu]; /* See if we lost the race to fill the cache. */ if (cache->uc_allocbucket.ucb_bucket != NULL) { @@ -3040,8 +3070,7 @@ cache_alloc(uma_zone_t zone, uma_cache_t cache, void * * initialized bucket to make this less likely or claim * the memory directly. */ - cpu = curcpu; - cache = &zone->uz_cpu[cpu]; + cache = &zone->uz_cpu[curcpu]; if (cache->uc_allocbucket.ucb_bucket == NULL && ((zone->uz_flags & UMA_ZONE_NUMA) == 0 || domain == PCPU_GET(domain))) { @@ -3437,7 +3466,7 @@ zone_alloc_item_locked(uma_zone_t zone, void *udata, i goto fail_cnt; } } - item = item_ctor(zone, udata, flags, item); + item = item_ctor(zone, zone->uz_size, udata, flags, item); if (item == NULL) goto fail; @@ -3467,7 +3496,7 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata { uma_cache_t cache; uma_cache_bucket_t bucket; - int cpu, domain, itemdomain; + int domain, itemdomain, uz_flags; /* Enable entropy collection for RANDOM_ENABLE_UMA kernel option */ random_harvest_fast_uma(&zone, sizeof(zone), RANDOM_UMA); @@ -3491,14 +3520,26 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata return; } #endif - item_dtor(zone, item, udata, SKIP_NONE); /* + * We are accessing the per-cpu cache without a critical section to + * fetch size and flags. This is acceptable, if we are preempted we + * will simply read another cpu's line. + */ + cache = &zone->uz_cpu[curcpu]; + uz_flags = cache_uz_flags(cache); + if (__predict_false((uz_flags & UMA_ZFLAG_CTORDTOR) != 0 || + UMA_ALWAYS_CTORDTOR)) + item_dtor(zone, item, cache_uz_size(cache), udata, SKIP_NONE); + + /* * The race here is acceptable. If we miss it we'll just have to wait * a little longer for the limits to be reset. */ - if (zone->uz_sleepers > 0) - goto zfree_item; + if (__predict_false(uz_flags & UMA_ZFLAG_LIMIT)) { + if (zone->uz_sleepers > 0) + goto zfree_item; + } /* * If possible, free to the per-CPU cache. There are two @@ -3514,16 +3555,14 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata domain = itemdomain = 0; critical_enter(); do { - cpu = curcpu; - cache = &zone->uz_cpu[cpu]; + cache = &zone->uz_cpu[curcpu]; bucket = &cache->uc_allocbucket; #ifdef UMA_XDOMAIN - if ((zone->uz_flags & UMA_ZONE_NUMA) != 0) { + if ((uz_flags & UMA_ZONE_NUMA) != 0) { itemdomain = _vm_phys_domain(pmap_kextract((vm_offset_t)item)); domain = PCPU_GET(domain); } - if ((zone->uz_flags & UMA_ZONE_NUMA) != 0 && - domain != itemdomain) { + if ((uz_flags & UMA_ZONE_NUMA) != 0 && domain != itemdomain) { bucket = &cache->uc_crossbucket; } else #endif @@ -3615,15 +3654,14 @@ cache_free(uma_zone_t zone, uma_cache_t cache, void *u int itemdomain) { uma_bucket_t bucket; - int cpu, domain; + int domain; CRITICAL_ASSERT(curthread); if (zone->uz_bucket_size == 0 || bucketdisable) return false; - cpu = curcpu; - cache = &zone->uz_cpu[cpu]; + cache = &zone->uz_cpu[curcpu]; /* * NUMA domains need to free to the correct zdom. When XDOMAIN @@ -3660,8 +3698,7 @@ cache_free(uma_zone_t zone, uma_cache_t cache, void *u critical_enter(); if (bucket == NULL) return (false); - cpu = curcpu; - cache = &zone->uz_cpu[cpu]; + cache = &zone->uz_cpu[curcpu]; #ifdef UMA_XDOMAIN /* * Check to see if we should be populating the cross bucket. If it @@ -3783,7 +3820,7 @@ static void zone_free_item(uma_zone_t zone, void *item, void *udata, enum zfreeskip skip) { - item_dtor(zone, item, udata, skip); + item_dtor(zone, item, zone->uz_size, udata, skip); if (skip < SKIP_FINI && zone->uz_fini) zone->uz_fini(item, zone->uz_size); @@ -3819,6 +3856,8 @@ uma_zone_set_max(uma_zone_t zone, int nitems) if (zone->uz_bucket_size_min > zone->uz_bucket_size_max) zone->uz_bucket_size_min = zone->uz_bucket_size_max; zone->uz_max_items = nitems; + zone->uz_flags |= UMA_ZFLAG_LIMIT; + zone_update_caches(zone); ZONE_UNLOCK(zone); return (nitems); @@ -4086,7 +4125,9 @@ uma_zone_reserve_kva(uma_zone_t zone, int count) #else keg->uk_allocf = noobj_alloc; #endif - keg->uk_flags |= UMA_ZONE_NOFREE; + keg->uk_flags |= UMA_ZFLAG_LIMIT | UMA_ZONE_NOFREE; + zone->uz_flags |= UMA_ZFLAG_LIMIT | UMA_ZONE_NOFREE; + zone_update_caches(zone); ZONE_UNLOCK(zone); return (1); @@ -4231,13 +4272,6 @@ int uma_zone_exhausted_nolock(uma_zone_t zone) { return (zone->uz_sleepers > 0); -} - -static void -uma_zero_item(void *item, uma_zone_t zone) -{ - - bzero(item, zone->uz_size); } unsigned long Modified: head/sys/vm/uma_int.h ============================================================================== --- head/sys/vm/uma_int.h Wed Dec 25 20:50:53 2019 (r356080) +++ head/sys/vm/uma_int.h Wed Dec 25 20:57:24 2019 (r356081) @@ -218,6 +218,39 @@ typedef struct uma_cache * uma_cache_t; LIST_HEAD(slabhead, uma_slab); /* + * The cache structure pads perfectly into 64 bytes so we use spare + * bits from the embedded cache buckets to store information from the zone + * and keep all fast-path allocations accessing a single per-cpu line. + */ +static inline void +cache_set_uz_flags(uma_cache_t cache, uint32_t flags) +{ + + cache->uc_freebucket.ucb_spare = flags; +} + +static inline void +cache_set_uz_size(uma_cache_t cache, uint32_t size) +{ + + cache->uc_allocbucket.ucb_spare = size; +} + +static inline uint32_t +cache_uz_flags(uma_cache_t cache) +{ + + return (cache->uc_freebucket.ucb_spare); +} + +static inline uint32_t +cache_uz_size(uma_cache_t cache) +{ + + return (cache->uc_allocbucket.ucb_spare); +} + +/* * Per-domain slab lists. Embedded in the kegs. */ struct uma_domain { @@ -442,6 +475,8 @@ struct uma_zone { /* * These flags must not overlap with the UMA_ZONE flags specified in uma.h. */ +#define UMA_ZFLAG_CTORDTOR 0x01000000 /* Zone has ctor/dtor set. */ +#define UMA_ZFLAG_LIMIT 0x02000000 /* Zone has limit set. */ #define UMA_ZFLAG_CACHE 0x04000000 /* uma_zcache_create()d it */ #define UMA_ZFLAG_RECLAIMING 0x08000000 /* Running zone_reclaim(). */ #define UMA_ZFLAG_BUCKET 0x10000000 /* Bucket zone. */ @@ -459,6 +494,8 @@ struct uma_zone { "\35BUCKET" \ "\34RECLAIMING" \ "\33CACHE" \ + "\32LIMIT" \ + "\31CTORDTOR" \ "\22MINBUCKET" \ "\21NUMA" \ "\20PCPU" \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201912252057.xBPKvON8046974>