Author: mjg
Date: Tue Oct 28 04:33:57 2014
New Revision: 273763
URL: https://svnweb.freebsd.org/changeset/base/273763

Log:
  Change loginclass mutex to an rwlock.
  
  While here reduce nesting in loginclass_free.
  
  Submitted by: Tiwei Bie <btw mail.ustc.edu.cn>
  X-Additional: JuniorJobs project
  MFC after:    2 weeks

Modified:
  head/sys/kern/kern_loginclass.c

Modified: head/sys/kern/kern_loginclass.c
==============================================================================
--- head/sys/kern/kern_loginclass.c     Tue Oct 28 04:18:09 2014        
(r273762)
+++ head/sys/kern/kern_loginclass.c     Tue Oct 28 04:33:57 2014        
(r273763)
@@ -51,13 +51,13 @@ __FBSDID("$FreeBSD$");
 #include <sys/lock.h>
 #include <sys/loginclass.h>
 #include <sys/malloc.h>
-#include <sys/mutex.h>
 #include <sys/types.h>
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/queue.h>
 #include <sys/racct.h>
 #include <sys/refcount.h>
+#include <sys/rwlock.h>
 #include <sys/sysproto.h>
 #include <sys/systm.h>
 
@@ -68,8 +68,8 @@ LIST_HEAD(, loginclass)       loginclasses;
 /*
  * Lock protecting loginclasses list.
  */
-static struct mtx loginclasses_lock;
-MTX_SYSINIT(loginclasses_init, &loginclasses_lock, "loginclasses lock", 
MTX_DEF);
+static struct rwlock loginclasses_lock;
+RW_SYSINIT(loginclasses_init, &loginclasses_lock, "loginclasses lock");
 
 void
 loginclass_hold(struct loginclass *lc)
@@ -87,16 +87,37 @@ loginclass_free(struct loginclass *lc)
        if (old > 1 && atomic_cmpset_int(&lc->lc_refcount, old, old - 1))
                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);
-
+       rw_wlock(&loginclasses_lock);
+       if (!refcount_release(&lc->lc_refcount)) {
+               rw_wunlock(&loginclasses_lock);
                return;
        }
-       mtx_unlock(&loginclasses_lock);
+
+       racct_destroy(&lc->lc_racct);
+       LIST_REMOVE(lc, lc_next);
+       rw_wunlock(&loginclasses_lock);
+
+       free(lc, M_LOGINCLASS);
+}
+
+/*
+ * Look up a loginclass struct for the parameter name.
+ * loginclasses_lock must be locked.
+ * Increase refcount on loginclass struct returned.
+ */
+static struct loginclass *
+loginclass_lookup(const char *name)
+{
+       struct loginclass *lc;
+
+       rw_assert(&loginclasses_lock, RA_LOCKED);
+       LIST_FOREACH(lc, &loginclasses, lc_next)
+               if (strcmp(name, lc->lc_name) == 0) {
+                       loginclass_hold(lc);
+                       break;
+               }
+
+       return (lc);
 }
 
 /*
@@ -109,34 +130,39 @@ loginclass_free(struct loginclass *lc)
 struct loginclass *
 loginclass_find(const char *name)
 {
-       struct loginclass *lc, *newlc;
+       struct loginclass *lc, *new_lc;
 
        if (name[0] == '\0' || strlen(name) >= MAXLOGNAME)
                return (NULL);
 
-       newlc = malloc(sizeof(*newlc), M_LOGINCLASS, M_ZERO | M_WAITOK);
-       racct_create(&newlc->lc_racct);
-
-       mtx_lock(&loginclasses_lock);
-       LIST_FOREACH(lc, &loginclasses, lc_next) {
-               if (strcmp(name, lc->lc_name) != 0)
-                       continue;
-
-               /* Found loginclass with a matching name? */
-               loginclass_hold(lc);
-               mtx_unlock(&loginclasses_lock);
-               racct_destroy(&newlc->lc_racct);
-               free(newlc, M_LOGINCLASS);
+       rw_rlock(&loginclasses_lock);
+       lc = loginclass_lookup(name);
+       rw_runlock(&loginclasses_lock);
+       if (lc != NULL)
                return (lc);
-       }
 
-       /* Add new loginclass. */
-       strcpy(newlc->lc_name, name);
-       refcount_init(&newlc->lc_refcount, 1);
-       LIST_INSERT_HEAD(&loginclasses, newlc, lc_next);
-       mtx_unlock(&loginclasses_lock);
+       new_lc = malloc(sizeof(*new_lc), M_LOGINCLASS, M_ZERO | M_WAITOK);
+       racct_create(&new_lc->lc_racct);
+       refcount_init(&new_lc->lc_refcount, 1);
+       strcpy(new_lc->lc_name, name);
+
+       rw_wlock(&loginclasses_lock);
+       /*
+        * There's a chance someone created our loginclass while we
+        * were in malloc and not holding the lock, so we have to
+        * make sure we don't insert a duplicate loginclass.
+        */
+       if ((lc = loginclass_lookup(name)) == NULL) {
+               LIST_INSERT_HEAD(&loginclasses, new_lc, lc_next);
+               rw_wunlock(&loginclasses_lock);
+               lc = new_lc;
+       } else {
+               rw_wunlock(&loginclasses_lock);
+               racct_destroy(&new_lc->lc_racct);
+               free(new_lc, M_LOGINCLASS);
+       }
 
-       return (newlc);
+       return (lc);
 }
 
 /*
@@ -222,8 +248,8 @@ loginclass_racct_foreach(void (*callback
 {
        struct loginclass *lc;
 
-       mtx_lock(&loginclasses_lock);
+       rw_rlock(&loginclasses_lock);
        LIST_FOREACH(lc, &loginclasses, lc_next)
                (callback)(lc->lc_racct, arg2, arg3);
-       mtx_unlock(&loginclasses_lock);
+       rw_runlock(&loginclasses_lock);
 }
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to