Change the UMA mutex into a rwlock

Acquire the lock in read mode when just needed to ensure the stability
of the keg list. The UMA lock may be held for a long time (relatively
speaking) in uma_reclaim() on machines with lots of zones/kegs. If the
uma_timeout() would fire during that period, subsequent callouts on that
CPU may be significantly delayed.

Reviewed by:	jhb
This commit is contained in:
Bryan Venteicher 2014-10-05 21:34:56 +00:00
parent 3b4b7de506
commit 111fbcd5ed

View File

@ -135,8 +135,8 @@ static LIST_HEAD(,uma_keg) uma_kegs = LIST_HEAD_INITIALIZER(uma_kegs);
static LIST_HEAD(,uma_zone) uma_cachezones =
LIST_HEAD_INITIALIZER(uma_cachezones);
/* This mutex protects the keg list */
static struct mtx_padalign uma_mtx;
/* This RW lock protects the keg list */
static struct rwlock_padalign uma_rwlock;
/* Linked list of boot time pages */
static LIST_HEAD(,uma_slab) uma_boot_pages =
@ -904,7 +904,7 @@ zone_drain_wait(uma_zone_t zone, int waitok)
ZONE_UNLOCK(zone);
/*
* The DRAINING flag protects us from being freed while
* we're running. Normally the uma_mtx would protect us but we
* we're running. Normally the uma_rwlock would protect us but we
* must be able to release and acquire the right lock for each keg.
*/
zone_foreach_keg(zone, &keg_drain);
@ -1540,9 +1540,9 @@ keg_ctor(void *mem, int size, void *udata, int flags)
LIST_INSERT_HEAD(&keg->uk_zones, zone, uz_link);
mtx_lock(&uma_mtx);
rw_wlock(&uma_rwlock);
LIST_INSERT_HEAD(&uma_kegs, keg, uk_link);
mtx_unlock(&uma_mtx);
rw_wunlock(&uma_rwlock);
return (0);
}
@ -1592,9 +1592,9 @@ zone_ctor(void *mem, int size, void *udata, int flags)
zone->uz_release = arg->release;
zone->uz_arg = arg->arg;
zone->uz_lockptr = &zone->uz_lock;
mtx_lock(&uma_mtx);
rw_wlock(&uma_rwlock);
LIST_INSERT_HEAD(&uma_cachezones, zone, uz_link);
mtx_unlock(&uma_mtx);
rw_wunlock(&uma_rwlock);
goto out;
}
@ -1611,7 +1611,7 @@ zone_ctor(void *mem, int size, void *udata, int flags)
zone->uz_fini = arg->fini;
zone->uz_lockptr = &keg->uk_lock;
zone->uz_flags |= UMA_ZONE_SECONDARY;
mtx_lock(&uma_mtx);
rw_wlock(&uma_rwlock);
ZONE_LOCK(zone);
LIST_FOREACH(z, &keg->uk_zones, uz_link) {
if (LIST_NEXT(z, uz_link) == NULL) {
@ -1620,7 +1620,7 @@ zone_ctor(void *mem, int size, void *udata, int flags)
}
}
ZONE_UNLOCK(zone);
mtx_unlock(&uma_mtx);
rw_wunlock(&uma_rwlock);
} else if (keg == NULL) {
if ((keg = uma_kcreate(zone, arg->size, arg->uminit, arg->fini,
arg->align, arg->flags)) == NULL)
@ -1718,9 +1718,9 @@ zone_dtor(void *arg, int size, void *udata)
if (!(zone->uz_flags & UMA_ZFLAG_INTERNAL))
cache_drain(zone);
mtx_lock(&uma_mtx);
rw_wlock(&uma_rwlock);
LIST_REMOVE(zone, uz_link);
mtx_unlock(&uma_mtx);
rw_wunlock(&uma_rwlock);
/*
* XXX there are some races here where
* the zone can be drained but zone lock
@ -1742,9 +1742,9 @@ zone_dtor(void *arg, int size, void *udata)
* We only destroy kegs from non secondary zones.
*/
if (keg != NULL && (zone->uz_flags & UMA_ZONE_SECONDARY) == 0) {
mtx_lock(&uma_mtx);
rw_wlock(&uma_rwlock);
LIST_REMOVE(keg, uk_link);
mtx_unlock(&uma_mtx);
rw_wunlock(&uma_rwlock);
zone_free_item(kegs, keg, NULL, SKIP_NONE);
}
ZONE_LOCK_FINI(zone);
@ -1766,12 +1766,12 @@ zone_foreach(void (*zfunc)(uma_zone_t))
uma_keg_t keg;
uma_zone_t zone;
mtx_lock(&uma_mtx);
rw_rlock(&uma_rwlock);
LIST_FOREACH(keg, &uma_kegs, uk_link) {
LIST_FOREACH(zone, &keg->uk_zones, uz_link)
zfunc(zone);
}
mtx_unlock(&uma_mtx);
rw_runlock(&uma_rwlock);
}
/* Public functions */
@ -1787,7 +1787,7 @@ uma_startup(void *bootmem, int boot_pages)
#ifdef UMA_DEBUG
printf("Creating uma keg headers zone and keg.\n");
#endif
mtx_init(&uma_mtx, "UMA lock", NULL, MTX_DEF);
rw_init(&uma_rwlock, "UMA lock");
/* "manually" create the initial zone */
memset(&args, 0, sizeof(args));
@ -3362,12 +3362,12 @@ sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS)
int count;
count = 0;
mtx_lock(&uma_mtx);
rw_rlock(&uma_rwlock);
LIST_FOREACH(kz, &uma_kegs, uk_link) {
LIST_FOREACH(z, &kz->uk_zones, uz_link)
count++;
}
mtx_unlock(&uma_mtx);
rw_runlock(&uma_rwlock);
return (sysctl_handle_int(oidp, &count, 0, req));
}
@ -3392,7 +3392,7 @@ sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS)
sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
count = 0;
mtx_lock(&uma_mtx);
rw_rlock(&uma_rwlock);
LIST_FOREACH(kz, &uma_kegs, uk_link) {
LIST_FOREACH(z, &kz->uk_zones, uz_link)
count++;
@ -3468,7 +3468,7 @@ sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS)
ZONE_UNLOCK(z);
}
}
mtx_unlock(&uma_mtx);
rw_runlock(&uma_rwlock);
error = sbuf_finish(&sbuf);
sbuf_delete(&sbuf);
return (error);