Skip site navigation (1)Skip section navigation (2)
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>