Author: markj Date: Tue Feb 11 20:06:33 2020 New Revision: 357776 URL: https://svnweb.freebsd.org/changeset/base/357776
Log: Reduce lock hold time in keg_drain(). Maintain a count of free slabs in the per-domain keg structure and use that to clear the free slab list in constant time for most cases. This helps minimize lock contention induced by reclamation, in preparation for proactive trimming of excesses of free memory. Reviewed by: jeff, rlibby Tested by: pho Differential Revision: https://reviews.freebsd.org/D23532 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 Tue Feb 11 20:02:20 2020 (r357775) +++ head/sys/vm/uma_core.c Tue Feb 11 20:06:33 2020 (r357776) @@ -1258,39 +1258,34 @@ keg_free_slab(uma_keg_t keg, uma_slab_t slab, int star static void keg_drain(uma_keg_t keg) { - struct slabhead freeslabs = { 0 }; + struct slabhead freeslabs; uma_domain_t dom; uma_slab_t slab, tmp; int i, n; - /* - * We don't want to take pages from statically allocated kegs at this - * time - */ if (keg->uk_flags & UMA_ZONE_NOFREE || keg->uk_freef == NULL) return; for (i = 0; i < vm_ndomains; i++) { CTR4(KTR_UMA, "keg_drain %s(%p) domain %d free items: %u", - keg->uk_name, keg, i, dom->ud_free); - n = 0; + keg->uk_name, keg, i, dom->ud_free_items); dom = &keg->uk_domain[i]; + LIST_INIT(&freeslabs); + KEG_LOCK(keg, i); - LIST_FOREACH_SAFE(slab, &dom->ud_free_slab, us_link, tmp) { - if (keg->uk_flags & UMA_ZFLAG_HASH) + if ((keg->uk_flags & UMA_ZFLAG_HASH) != 0) { + LIST_FOREACH(slab, &dom->ud_free_slab, us_link) UMA_HASH_REMOVE(&keg->uk_hash, slab); - n++; - LIST_REMOVE(slab, us_link); - LIST_INSERT_HEAD(&freeslabs, slab, us_link); } + n = dom->ud_free_slabs; + LIST_SWAP(&freeslabs, &dom->ud_free_slab, uma_slab, us_link); + dom->ud_free_slabs = 0; + dom->ud_free_items -= n * keg->uk_ipers; dom->ud_pages -= n * keg->uk_ppera; - dom->ud_free -= n * keg->uk_ipers; KEG_UNLOCK(keg, i); - } - while ((slab = LIST_FIRST(&freeslabs)) != NULL) { - LIST_REMOVE(slab, us_link); - keg_free_slab(keg, slab, keg->uk_ipers); + LIST_FOREACH_SAFE(slab, &freeslabs, us_link, tmp) + keg_free_slab(keg, slab, keg->uk_ipers); } } @@ -1458,7 +1453,7 @@ keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int dom dom = &keg->uk_domain[domain]; LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link); dom->ud_pages += keg->uk_ppera; - dom->ud_free += keg->uk_ipers; + dom->ud_free_items += keg->uk_ipers; return (slab); @@ -2286,7 +2281,7 @@ zone_alloc_sysctl(uma_zone_t zone, void *unused) "pages", CTLFLAG_RD, &dom->ud_pages, 0, "Total pages currently allocated from VM"); SYSCTL_ADD_U32(NULL, SYSCTL_CHILDREN(oid), OID_AUTO, - "free", CTLFLAG_RD, &dom->ud_free, 0, + "free_items", CTLFLAG_RD, &dom->ud_free_items, 0, "items free in the slab layer"); } } else @@ -2572,7 +2567,7 @@ keg_dtor(void *arg, int size, void *udata) keg = (uma_keg_t)arg; free = pages = 0; for (i = 0; i < vm_ndomains; i++) { - free += keg->uk_domain[i].ud_free; + free += keg->uk_domain[i].ud_free_items; pages += keg->uk_domain[i].ud_pages; KEG_LOCK_FINI(keg, i); } @@ -3386,11 +3381,11 @@ keg_first_slab(uma_keg_t keg, int domain, bool rr) start = domain; do { dom = &keg->uk_domain[domain]; - if (!LIST_EMPTY(&dom->ud_part_slab)) - return (LIST_FIRST(&dom->ud_part_slab)); - if (!LIST_EMPTY(&dom->ud_free_slab)) { - slab = LIST_FIRST(&dom->ud_free_slab); + if ((slab = LIST_FIRST(&dom->ud_part_slab)) != NULL) + return (slab); + if ((slab = LIST_FIRST(&dom->ud_free_slab)) != NULL) { LIST_REMOVE(slab, us_link); + dom->ud_free_slabs--; LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link); return (slab); } @@ -3417,7 +3412,7 @@ keg_fetch_free_slab(uma_keg_t keg, int domain, bool rr KEG_LOCK(keg, domain); reserve = (flags & M_USE_RESERVE) != 0 ? 0 : keg->uk_reserve; - if (keg->uk_domain[domain].ud_free <= reserve || + if (keg->uk_domain[domain].ud_free_items <= reserve || (slab = keg_first_slab(keg, domain, rr)) == NULL) { KEG_UNLOCK(keg, domain); return (NULL); @@ -3502,9 +3497,13 @@ slab_alloc_item(uma_keg_t keg, uma_slab_t slab) BIT_CLR(keg->uk_ipers, freei, &slab->us_free); item = slab_item(slab, keg, freei); slab->us_freecount--; - dom->ud_free--; + dom->ud_free_items--; - /* Move this slab to the full list */ + /* + * Move this slab to the full list. It must be on the partial list, so + * we do not need to update the free slab count. In particular, + * keg_fetch_slab() always returns slabs on the partial list. + */ if (slab->us_freecount == 0) { LIST_REMOVE(slab, us_link); LIST_INSERT_HEAD(&dom->ud_full_slab, slab, us_link); @@ -3538,7 +3537,7 @@ zone_import(void *arg, void **bucket, int max, int dom dom = &keg->uk_domain[slab->us_domain]; while (slab->us_freecount && i < max) { bucket[i++] = slab_alloc_item(keg, slab); - if (dom->ud_free <= keg->uk_reserve) + if (dom->ud_free_items <= keg->uk_reserve) break; #ifdef NUMA /* @@ -4240,9 +4239,10 @@ slab_free_item(uma_zone_t zone, uma_slab_t slab, void /* Do we need to remove from any lists? */ dom = &keg->uk_domain[slab->us_domain]; - if (slab->us_freecount+1 == keg->uk_ipers) { + if (slab->us_freecount + 1 == keg->uk_ipers) { LIST_REMOVE(slab, us_link); LIST_INSERT_HEAD(&dom->ud_free_slab, slab, us_link); + dom->ud_free_slabs++; } else if (slab->us_freecount == 0) { LIST_REMOVE(slab, us_link); LIST_INSERT_HEAD(&dom->ud_part_slab, slab, us_link); @@ -4254,7 +4254,7 @@ slab_free_item(uma_zone_t zone, uma_slab_t slab, void slab->us_freecount++; /* Keg statistics. */ - dom->ud_free++; + dom->ud_free_items++; } static void @@ -4635,9 +4635,14 @@ uma_prealloc(uma_zone_t zone, int items) aflags); if (slab != NULL) { dom = &keg->uk_domain[slab->us_domain]; + /* + * keg_alloc_slab() always returns a slab on the + * partial list. + */ LIST_REMOVE(slab, us_link); LIST_INSERT_HEAD(&dom->ud_free_slab, slab, us_link); + dom->ud_free_slabs++; KEG_UNLOCK(keg, slab->us_domain); break; } @@ -4915,7 +4920,7 @@ sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS) LIST_FOREACH(kz, &uma_kegs, uk_link) { kfree = pages = 0; for (i = 0; i < vm_ndomains; i++) { - kfree += kz->uk_domain[i].ud_free; + kfree += kz->uk_domain[i].ud_free_items; pages += kz->uk_domain[i].ud_pages; } LIST_FOREACH(z, &kz->uk_zones, uz_link) { @@ -5219,7 +5224,7 @@ get_uma_stats(uma_keg_t kz, uma_zone_t z, uint64_t *al *cachefree += z->uz_domain[i].uzd_nitems; if (!((z->uz_flags & UMA_ZONE_SECONDARY) && (LIST_FIRST(&kz->uk_zones) != z))) - *cachefree += kz->uk_domain[i].ud_free; + *cachefree += kz->uk_domain[i].ud_free_items; } *used = *allocs - frees; return (((int64_t)*used + *cachefree) * kz->uk_size); Modified: head/sys/vm/uma_int.h ============================================================================== --- head/sys/vm/uma_int.h Tue Feb 11 20:02:20 2020 (r357775) +++ head/sys/vm/uma_int.h Tue Feb 11 20:06:33 2020 (r357776) @@ -324,7 +324,8 @@ struct uma_domain { struct slabhead ud_free_slab; /* completely unallocated slabs */ struct slabhead ud_full_slab; /* fully allocated slabs */ uint32_t ud_pages; /* Total page count */ - uint32_t ud_free; /* Count of items free in slabs */ + uint32_t ud_free_items; /* Count of items free in all slabs */ + uint32_t ud_free_slabs; /* Count of free slabs */ } __aligned(CACHE_LINE_SIZE); typedef struct uma_domain * uma_domain_t; _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"