Date: Sat, 25 Oct 2014 20:44:48 +0200 From: Mateusz Guzik <mjguzik@gmail.com> To: freebsd-arch@freebsd.org Subject: refcount_release_take_##lock Message-ID: <20141025184448.GA19066@dft-labs.eu>
next in thread | raw e-mail | index | archive | help
The following idiom is used here and there: int old; old = obj->ref; if (old > 1 && atomic_cmpset_int(&obj->ref, old, old -1)) return; lock(&something); if (refcount_release(&obj->ref) == 0) { unlock(&something); return; } free up unlock(&something); ========== I decided to implement it as a common function. We have only refcount.h and I didn't want to bloat all including code with additional definitions and as such I came up with a macro that has to be used in .c file and that will define appropriate inline func. I'm definitely looking for better names for REFCOUNT_RELEASE_TAKE_USE_ macro, assuming it has to stay. Comments? diff --git a/sys/kern/kern_jail.c b/sys/kern/kern_jail.c index f8ae0e6..cf16344 100644 --- a/sys/kern/kern_jail.c +++ b/sys/kern/kern_jail.c @@ -81,6 +81,8 @@ __FBSDID("$FreeBSD$"); MALLOC_DEFINE(M_PRISON, "prison", "Prison structures"); static MALLOC_DEFINE(M_PRISON_RACCT, "prison_racct", "Prison racct structures"); +REFCOUNT_RELEASE_TAKE_USE_SX; + /* Keep struct prison prison0 and some code in kern_jail_set() readable. */ #ifdef INET #ifdef INET6 @@ -4466,15 +4468,12 @@ prison_racct_free_locked(struct prison_racct *prr) void prison_racct_free(struct prison_racct *prr) { - int old; sx_assert(&allprison_lock, SA_UNLOCKED); - old = prr->prr_refcount; - if (old > 1 && atomic_cmpset_int(&prr->prr_refcount, old, old - 1)) + if (refcount_release_take_sx(&prr->prr_refcount, &allprison_lock) == 0) return; - sx_xlock(&allprison_lock); prison_racct_free_locked(prr); sx_xunlock(&allprison_lock); } diff --git a/sys/kern/kern_loginclass.c b/sys/kern/kern_loginclass.c index b20f60b..8cc4b40 100644 --- a/sys/kern/kern_loginclass.c +++ b/sys/kern/kern_loginclass.c @@ -70,6 +70,7 @@ LIST_HEAD(, loginclass) loginclasses; */ static struct mtx loginclasses_lock; MTX_SYSINIT(loginclasses_init, &loginclasses_lock, "loginclasses lock", MTX_DEF); +REFCOUNT_RELEASE_TAKE_USE_MTX; void loginclass_hold(struct loginclass *lc) @@ -81,22 +82,14 @@ loginclass_hold(struct loginclass *lc) void loginclass_free(struct loginclass *lc) { - int old; - old = lc->lc_refcount; - if (old > 1 && atomic_cmpset_int(&lc->lc_refcount, old, old - 1)) + if (refcount_release_take_mtx(&lc->lc_refcount, &loginclasses_lock) == 0) return; - mtx_lock(&loginclasses_lock); - if (refcount_release(&lc->lc_refcount)) { - racct_destroy(&lc->lc_racct); - LIST_REMOVE(lc, lc_next); - mtx_unlock(&loginclasses_lock); - free(lc, M_LOGINCLASS); - - return; - } + racct_destroy(&lc->lc_racct); + LIST_REMOVE(lc, lc_next); mtx_unlock(&loginclasses_lock); + free(lc, M_LOGINCLASS); } /* diff --git a/sys/kern/kern_resource.c b/sys/kern/kern_resource.c index f4914fa..efbaa87 100644 --- a/sys/kern/kern_resource.c +++ b/sys/kern/kern_resource.c @@ -71,6 +71,7 @@ static MALLOC_DEFINE(M_PLIMIT, "plimit", "plimit structures"); static MALLOC_DEFINE(M_UIDINFO, "uidinfo", "uidinfo structures"); #define UIHASH(uid) (&uihashtbl[(uid) & uihash]) static struct rwlock uihashtbl_lock; +REFCOUNT_RELEASE_TAKE_USE_RWLOCK; static LIST_HEAD(uihashhead, uidinfo) *uihashtbl; static u_long uihash; /* size of hash table - 1 */ @@ -1327,37 +1328,24 @@ void uifree(uip) struct uidinfo *uip; { - int old; - /* Prepare for optimal case. */ - old = uip->ui_ref; - if (old > 1 && atomic_cmpset_int(&uip->ui_ref, old, old - 1)) + if (refcount_release_take_rwlock(&uip->ui_ref, &uihashtbl_lock) == 0) return; - /* Prepare for suboptimal case. */ - rw_wlock(&uihashtbl_lock); - if (refcount_release(&uip->ui_ref)) { - racct_destroy(&uip->ui_racct); - LIST_REMOVE(uip, ui_hash); - rw_wunlock(&uihashtbl_lock); - if (uip->ui_sbsize != 0) - printf("freeing uidinfo: uid = %d, sbsize = %ld\n", - uip->ui_uid, uip->ui_sbsize); - if (uip->ui_proccnt != 0) - printf("freeing uidinfo: uid = %d, proccnt = %ld\n", - uip->ui_uid, uip->ui_proccnt); - if (uip->ui_vmsize != 0) - printf("freeing uidinfo: uid = %d, swapuse = %lld\n", - uip->ui_uid, (unsigned long long)uip->ui_vmsize); - mtx_destroy(&uip->ui_vmsize_mtx); - free(uip, M_UIDINFO); - return; - } - /* - * Someone added a reference between atomic_cmpset_int() and - * rw_wlock(&uihashtbl_lock). - */ + racct_destroy(&uip->ui_racct); + LIST_REMOVE(uip, ui_hash); rw_wunlock(&uihashtbl_lock); + if (uip->ui_sbsize != 0) + printf("freeing uidinfo: uid = %d, sbsize = %ld\n", + uip->ui_uid, uip->ui_sbsize); + if (uip->ui_proccnt != 0) + printf("freeing uidinfo: uid = %d, proccnt = %ld\n", + uip->ui_uid, uip->ui_proccnt); + if (uip->ui_vmsize != 0) + printf("freeing uidinfo: uid = %d, swapuse = %lld\n", + uip->ui_uid, (unsigned long long)uip->ui_vmsize); + mtx_destroy(&uip->ui_vmsize_mtx); + free(uip, M_UIDINFO); } void diff --git a/sys/sys/refcount.h b/sys/sys/refcount.h index 4611664..9dff576 100644 --- a/sys/sys/refcount.h +++ b/sys/sys/refcount.h @@ -64,4 +64,29 @@ refcount_release(volatile u_int *count) return (old == 1); } +#define REFCOUNT_RELEASE_DEFINE(NAME, TYPE, LOCK, UNLOCK) \ +static __inline int \ +refcount_release_take_##NAME(volatile u_int *count, TYPE *v) \ +{ \ + u_int old; \ + \ + old = *count; \ + if (old > 1 && atomic_cmpset_int(count, old, old - 1)) \ + return (0); \ + LOCK(v); \ + if (refcount_release(count)) \ + return (1); \ + UNLOCK(v); \ + return (0); \ +} + +#define REFCOUNT_RELEASE_TAKE_USE_MTX \ + REFCOUNT_RELEASE_DEFINE(mtx, struct mtx, mtx_lock, mtx_unlock); +#define REFCOUNT_RELEASE_TAKE_USE_RWLOCK \ + REFCOUNT_RELEASE_DEFINE(rwlock, struct rwlock, rw_wlock, rw_wunlock); +#define REFCOUNT_RELEASE_TAKE_USE_RMLOCK \ + REFCOUNT_RELEASE_DEFINE(rmlock, struct rmlock, rm_wlock, rm_wunlock); +#define REFCOUNT_RELEASE_TAKE_USE_SX \ + REFCOUNT_RELEASE_DEFINE(sx, struct sx, sx_xlock, sx_xunlock); + #endif /* ! __SYS_REFCOUNT_H__ */
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20141025184448.GA19066>