Merge branch 'master' of /home/aggelos/devel/dfly/dfly.git/
authorAggelos Economopoulos <aoiko@cc.ece.ntua.gr>
Mon, 8 Jun 2009 14:54:38 +0000 (17:54 +0300)
committerAggelos Economopoulos <aoiko@cc.ece.ntua.gr>
Mon, 8 Jun 2009 14:54:38 +0000 (17:54 +0300)
sys/kern/kern_exit.c
sys/kern/kern_lockf.c
sys/kern/kern_resource.c
sys/kern/kern_varsym.c
sys/kern/syscalls.master
sys/sys/resourcevar.h
sys/sys/varsym.h

index 5a96ffb..0975726 100644 (file)
@@ -276,6 +276,8 @@ exit1(int rv)
                panic("Going nowhere without my init!");
        }
 
+       varsymset_clean(&p->p_varsymset);
+       lockuninit(&p->p_varsymset.vx_lock);
        /*
         * Kill all lwps associated with the current process, return an
         * error if we race another thread trying to do the same thing
index 3b4965a..3c41888 100644 (file)
@@ -56,6 +56,8 @@
 #include <machine/limits.h>    /* for LLONG_MAX */
 #include <machine/stdarg.h>
 
+#include <sys/spinlock2.h>
+
 #ifdef INVARIANTS
 int lf_global_counter = 0;
 #endif
@@ -132,6 +134,7 @@ lf_count_adjust(struct proc *p, int increase)
        KKASSERT(p != NULL);
 
        uip = p->p_ucred->cr_uidinfo;
+       spin_lock_wr(&uip->ui_lock);
 
        if (increase)
                uip->ui_posixlocks += p->p_numposixlocks;
@@ -141,13 +144,14 @@ lf_count_adjust(struct proc *p, int increase)
        KASSERT(uip->ui_posixlocks >= 0,
                ("Negative number of POSIX locks held by %s user: %d.",
                 increase ? "new" : "old", uip->ui_posixlocks));
+       spin_unlock_wr(&uip->ui_lock);
 }
 
 static int
 lf_count_change(struct proc *owner, int diff)
 {
        struct uidinfo *uip;
-       int max;
+       int max, ret;
 
        /* we might actually not have a process context */
        if (owner == NULL)
@@ -157,22 +161,24 @@ lf_count_change(struct proc *owner, int diff)
 
        max = MIN(owner->p_rlimit[RLIMIT_POSIXLOCKS].rlim_cur,
                  maxposixlocksperuid);
+
+       spin_lock_wr(&uip->ui_lock);
        if (diff > 0 && owner->p_ucred->cr_uid != 0 && max != -1 &&
            uip->ui_posixlocks >= max ) {
-               return(1);
+               ret = 1;
+       } else {
+               uip->ui_posixlocks += diff;
+               owner->p_numposixlocks += diff;
+               KASSERT(uip->ui_posixlocks >= 0,
+                       ("Negative number of POSIX locks held by user: %d.",
+                        uip->ui_posixlocks));
+               KASSERT(owner->p_numposixlocks >= 0,
+                       ("Negative number of POSIX locks held by proc: %d.",
+                        uip->ui_posixlocks));
+               ret = 0;
        }
-
-       uip->ui_posixlocks += diff;
-       owner->p_numposixlocks += diff;
-
-       KASSERT(uip->ui_posixlocks >= 0,
-               ("Negative number of POSIX locks held by user: %d.",
-                uip->ui_posixlocks));
-       KASSERT(owner->p_numposixlocks >= 0,
-               ("Negative number of POSIX locks held by proc: %d.",
-                uip->ui_posixlocks));
-
-       return(0);
+       spin_unlock_wr(&uip->ui_lock);
+       return ret;
 }
 
 /*
index 92a7e5f..1f69bbb 100644 (file)
 #include <vm/vm_map.h>
 
 #include <sys/thread2.h>
+#include <sys/spinlock2.h>
 
 static int donice (struct proc *chgp, int n);
 
 static MALLOC_DEFINE(M_UIDINFO, "uidinfo", "uidinfo structures");
 #define        UIHASH(uid)     (&uihashtbl[(uid) & uihash])
+static struct spinlock uihash_lock;
 static LIST_HEAD(uihashhead, uidinfo) *uihashtbl;
 static u_long uihash;          /* size of hash table - 1 */
 
@@ -563,6 +565,7 @@ ruadd(struct rusage *ru, struct rusage *ru2)
 void
 uihashinit(void)
 {
+       spin_init(&uihash_lock);
        uihashtbl = hashinit(maxproc / 16, M_UIDINFO, &uihash);
 }
 
@@ -583,28 +586,38 @@ uilookup(uid_t uid)
 static struct uidinfo *
 uicreate(uid_t uid)
 {
-       struct  uidinfo *uip, *norace;
-
+       struct  uidinfo *uip, *tmp;
        /*
         * Allocate space and check for a race
         */
        MALLOC(uip, struct uidinfo *, sizeof(*uip), M_UIDINFO, M_WAITOK);
-       norace = uilookup(uid);
-       if (norace != NULL) {
-               FREE(uip, M_UIDINFO);
-               return (norace);
-       }
-
        /*
         * Initialize structure and enter it into the hash table
         */
-       LIST_INSERT_HEAD(UIHASH(uid), uip, ui_hash);
+       spin_init(&uip->ui_lock);
        uip->ui_uid = uid;
        uip->ui_proccnt = 0;
        uip->ui_sbsize = 0;
-       uip->ui_ref = 0;
+       uip->ui_ref = 1;        /* we're returning a ref */
        uip->ui_posixlocks = 0;
        varsymset_init(&uip->ui_varsymset, NULL);
+
+       /*
+        * Somebody may have already created the uidinfo for this
+        * uid. If so, return that instead.
+        */
+       spin_lock_wr(&uihash_lock);
+       tmp = uilookup(uid);
+       if (tmp != NULL) {
+               varsymset_clean(&uip->ui_varsymset);
+               spin_uninit(&uip->ui_lock);
+               FREE(uip, M_UIDINFO);
+               uip = tmp;
+       } else {
+               LIST_INSERT_HEAD(UIHASH(uid), uip, ui_hash);
+       }
+       spin_unlock_wr(&uihash_lock);
+
        return (uip);
 }
 
@@ -613,16 +626,40 @@ uifind(uid_t uid)
 {
        struct  uidinfo *uip;
 
+       spin_lock_rd(&uihash_lock);
        uip = uilookup(uid);
-       if (uip == NULL)
+       if (uip == NULL) {
+               spin_unlock_rd(&uihash_lock);
                uip = uicreate(uid);
-       uip->ui_ref++;
+       } else {
+               uihold(uip);
+               spin_unlock_rd(&uihash_lock);
+       }
        return (uip);
 }
 
 static __inline void
 uifree(struct uidinfo *uip)
 {
+       spin_lock_wr(&uihash_lock);
+
+       /*
+        * Note that we're taking a read lock even though we
+        * modify the structure because we know nobody can find
+        * it now that we've locked uihash_lock. If somebody
+        * can get to it through a stored pointer, the reference
+        * count will not be 0 and in that case we don't modify
+        * the struct.
+        */
+       spin_lock_rd(&uip->ui_lock);
+       if (uip->ui_ref != 0) {
+               /*
+                * Someone found the uid and got a ref when we
+                * unlocked. No need to free any more.
+                */
+               spin_unlock_rd(&uip->ui_lock);
+               return;
+       }
        if (uip->ui_sbsize != 0)
                /* XXX no %qd in kernel.  Truncate. */
                kprintf("freeing uidinfo: uid = %d, sbsize = %ld\n",
@@ -630,24 +667,31 @@ uifree(struct uidinfo *uip)
        if (uip->ui_proccnt != 0)
                kprintf("freeing uidinfo: uid = %d, proccnt = %ld\n",
                    uip->ui_uid, uip->ui_proccnt);
+       
        LIST_REMOVE(uip, ui_hash);
+       spin_unlock_wr(&uihash_lock);
        varsymset_clean(&uip->ui_varsymset);
+       lockuninit(&uip->ui_varsymset.vx_lock);
+       spin_unlock_rd(&uip->ui_lock);
+       spin_uninit(&uip->ui_lock);
        FREE(uip, M_UIDINFO);
 }
 
 void
 uihold(struct uidinfo *uip)
 {
-       ++uip->ui_ref;
+       atomic_add_int(&uip->ui_ref, 1);
        KKASSERT(uip->ui_ref > 0);
 }
 
 void
 uidrop(struct uidinfo *uip)
 {
-       KKASSERT(uip->ui_ref > 0);
-       if (--uip->ui_ref == 0)
+       if (atomic_fetchadd_int(&uip->ui_ref, -1) == 1) {
                uifree(uip);
+       } else {
+               KKASSERT(uip->ui_ref > 0);
+       }
 }
 
 void
@@ -664,13 +708,19 @@ uireplace(struct uidinfo **puip, struct uidinfo *nuip)
 int
 chgproccnt(struct uidinfo *uip, int diff, int max)
 {
+       int ret;
+       spin_lock_wr(&uip->ui_lock);
        /* don't allow them to exceed max, but allow subtraction */
-       if (diff > 0 && uip->ui_proccnt + diff > max && max != 0)
-               return (0);
-       uip->ui_proccnt += diff;
-       if (uip->ui_proccnt < 0)
-               kprintf("negative proccnt for uid = %d\n", uip->ui_uid);
-       return (1);
+       if (diff > 0 && uip->ui_proccnt + diff > max && max != 0) {
+               ret = 0;
+       } else {
+               uip->ui_proccnt += diff;
+               if (uip->ui_proccnt < 0)
+                       kprintf("negative proccnt for uid = %d\n", uip->ui_uid);
+               ret = 1;
+       }
+       spin_unlock_wr(&uip->ui_lock);
+       return ret;
 }
 
 /*
@@ -681,8 +731,9 @@ chgsbsize(struct uidinfo *uip, u_long *hiwat, u_long to, rlim_t max)
 {
        rlim_t new;
 
-       crit_enter();
+       spin_lock_wr(&uip->ui_lock);
        new = uip->ui_sbsize + to - *hiwat;
+       KKASSERT(new >= 0);
 
        /*
         * If we are trying to increase the socket buffer size
@@ -699,9 +750,7 @@ chgsbsize(struct uidinfo *uip, u_long *hiwat, u_long to, rlim_t max)
        }
        uip->ui_sbsize = new;
        *hiwat = to;
-       if (uip->ui_sbsize < 0)
-               kprintf("negative sbsize for uid = %d\n", uip->ui_uid);
-       crit_exit();
+       spin_unlock_wr(&uip->ui_lock);
        return (1);
 }
 
index 955ccba..53d64f1 100644 (file)
@@ -261,6 +261,7 @@ sys_varsym_list(struct varsym_list_args *uap)
        bytes = 0;
        earlyterm = 0;
 
+       lockmgr(&vss->vx_lock, LK_SHARED);
        TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
                varsym_t sym = ve->ve_sym;
                int namelen = strlen(sym->vs_name);
@@ -302,6 +303,7 @@ sys_varsym_list(struct varsym_list_args *uap)
                }
                ++i;
        }
+       lockmgr(&vss->vx_lock, LK_RELEASE);
 
        /*
         * Save the marker back.  If no error occured and earlyterm is clear
@@ -329,6 +331,7 @@ varsymlookup(struct varsymset *vss, const char *name, int namelen)
 {
     struct varsyment *ve;
 
+    KKASSERT(lockstatus(&vss->vx_lock, curthread) != 0);
     TAILQ_FOREACH(ve, &vss->vx_queue, ve_entry) {
        varsym_t var = ve->ve_sym;
        if (var->vs_namelen == namelen && 
@@ -339,33 +342,53 @@ varsymlookup(struct varsymset *vss, const char *name, int namelen)
     }
     return(NULL);
 }
+static
+void
+vsslock(struct varsymset **vss, struct varsymset *n)
+{
+       if (*vss) {
+               lockmgr(&(*vss)->vx_lock, LK_RELEASE);
+       }
+       lockmgr(&n->vx_lock, LK_SHARED);
+       *vss = n;
+}
 
 varsym_t
 varsymfind(int mask, const char *name, int namelen)
 {
     struct proc *p = curproc;
     struct varsyment *ve = NULL;
+    struct varsymset *vss = NULL;
     varsym_t sym;
 
     if ((mask & (VARSYM_PROC_MASK|VARSYM_USER_MASK)) && p != NULL) {
-       if (mask & VARSYM_PROC_MASK)
-           ve = varsymlookup(&p->p_varsymset, name, namelen);
-       if (ve == NULL && (mask & VARSYM_USER_MASK))
-           ve = varsymlookup(&p->p_ucred->cr_uidinfo->ui_varsymset, name, namelen);
+       if (mask & VARSYM_PROC_MASK) {
+           vsslock(&vss, &p->p_varsymset);
+           ve = varsymlookup(vss, name, namelen);
+       }
+       if (ve == NULL && (mask & VARSYM_USER_MASK)) {
+           vsslock(&vss, &p->p_ucred->cr_uidinfo->ui_varsymset);
+           ve = varsymlookup(vss, name, namelen);
+       }
     }
     if (ve == NULL && (mask & VARSYM_SYS_MASK)) {
-       if (p != NULL && p->p_ucred->cr_prison) 
-           ve = varsymlookup(&p->p_ucred->cr_prison->pr_varsymset, name, namelen);
-       else
-           ve = varsymlookup(&varsymset_sys, name, namelen);
+       if (p != NULL && p->p_ucred->cr_prison) {
+           vsslock(&vss, &p->p_ucred->cr_prison->pr_varsymset);
+           ve = varsymlookup(vss, name, namelen);
+       } else {
+           vsslock(&vss, &varsymset_sys);
+           ve = varsymlookup(vss, name, namelen);
+       }
     }
     if (ve) {
        sym = ve->ve_sym;
-       ++sym->vs_refs;
-       return(sym);
+       atomic_add_int(&sym->vs_refs, 1);
     } else {
-       return(NULL);
+       sym = NULL;
     }
+    lockmgr(&vss->vx_lock, LK_RELEASE);
+    return sym;
 }
 
 int
@@ -397,8 +420,10 @@ varsymmake(int level, const char *name, const char *data)
        break;
     }
     if (vss == NULL) {
-       error = EINVAL;
-    } else if (data && vss->vx_setsize >= MAXVARSYM_SET) {
+       return EINVAL;
+    }
+    lockmgr(&vss->vx_lock, LK_EXCLUSIVE);
+    if (data && vss->vx_setsize >= MAXVARSYM_SET) {
        error = E2BIG;
     } else if (data) {
        datalen = strlen(data);
@@ -425,6 +450,7 @@ varsymmake(int level, const char *name, const char *data)
            error = ENOENT;
        }
     }
+    lockmgr(&vss->vx_lock, LK_RELEASE);
     return(error);
 }
 
@@ -432,11 +458,16 @@ void
 varsymdrop(varsym_t sym)
 {
     KKASSERT(sym->vs_refs > 0);
-    if (--sym->vs_refs == 0) {
+    if (atomic_fetchadd_int(&sym->vs_refs, -1) == 1) {
        kfree(sym, M_VARSYM);
     }
 }
 
+/*
+ * Insert a duplicate of ve in vss. Does not do any locking,
+ * so it is the callers responsibility to make sure nobody
+ * else can mess with the TAILQ in vss at the same time.
+ */
 static void
 varsymdup(struct varsymset *vss, struct varsyment *ve)
 {
@@ -444,7 +475,11 @@ varsymdup(struct varsymset *vss, struct varsyment *ve)
 
     nve = kmalloc(sizeof(struct varsyment), M_VARSYM, M_WAITOK|M_ZERO);
     nve->ve_sym = ve->ve_sym;
-    ++nve->ve_sym->vs_refs;
+    ++nve->ve_sym->vs_refs;    /* can't be reached, no need for atomic add */
+    /*
+     * We're only called through varsymset_init() so vss is not yet reachable,
+     * no need to lock.
+     */
     TAILQ_INSERT_TAIL(&vss->vx_queue, nve, ve_entry);
 }
 
@@ -454,6 +489,7 @@ varsymset_init(struct varsymset *vss, struct varsymset *copy)
     struct varsyment *ve;
 
     TAILQ_INIT(&vss->vx_queue);
+    lockinit(&vss->vx_lock, "vx", 0, 0);
     if (copy) {
        TAILQ_FOREACH(ve, &copy->vx_queue, ve_entry) {
            varsymdup(vss, ve);
@@ -467,11 +503,13 @@ varsymset_clean(struct varsymset *vss)
 {
     struct varsyment *ve;
 
+    lockmgr(&vss->vx_lock, LK_EXCLUSIVE);
     while ((ve = TAILQ_FIRST(&vss->vx_queue)) != NULL) {
        TAILQ_REMOVE(&vss->vx_queue, ve, ve_entry);
        varsymdrop(ve->ve_sym);
        kfree(ve, M_VARSYM);
     }
     vss->vx_setsize = 0;
+    lockmgr(&vss->vx_lock, LK_RELEASE);
 }
 
index 0a970d5..77ee667 100644 (file)
 448    UNIMPL  NOHIDE  nosys
 449    UNIMPL  NOHIDE  nosys
 ; 450 DragonFly system calls
-450    STD     BSD     { int varsym_set(int level, const char *name, const char *data); }
-451    STD     BSD     { int varsym_get(int mask, const char *wild, char *buf, int bufsize); }
-452    STD     BSD     { int varsym_list(int level, char *buf, int maxsize, int *marker); }
+450    MPSAFE  BSD     { int varsym_set(int level, const char *name, const char *data); }
+451    MPSAFE  BSD     { int varsym_get(int mask, const char *wild, char *buf, int bufsize); }
+452    MPSAFE  BSD     { int varsym_list(int level, char *buf, int maxsize, int *marker); }
 453    STD     BSD     { int upc_register(struct upcall *upc, void *ctxfunc, void *func, void *data); }
 454    STD     BSD     { int upc_control(int cmd, int upcid, void *data); }
 455    STD     BSD     { int caps_sys_service(const char *name, uid_t uid, gid_t gid, int upcid, int flags); }
index d411a13..66fd0ca 100644 (file)
@@ -90,6 +90,10 @@ struct plimit {
  * Per uid resource consumption
  */
 struct uidinfo {
+       /*
+        * Protects access to ui_sbsize, ui_proccnt, ui_posixlocks
+        */
+       struct spinlock ui_lock;
        LIST_ENTRY(uidinfo) ui_hash;
        rlim_t  ui_sbsize;              /* socket buffer space consumed */
        long    ui_proccnt;             /* number of processes */
index 3fdd1d4..f482fdb 100644 (file)
@@ -16,6 +16,8 @@
 #include <sys/queue.h>         /* TAILQ_* macros */
 #endif
 
+#include <sys/lock.h>
+
 struct varsym {
     int                vs_refs;        /* a lot of sharing occurs */
     int                vs_namelen;
@@ -33,6 +35,7 @@ struct varsyment {
 struct varsymset {
     TAILQ_HEAD(, varsyment) vx_queue;
     int                vx_setsize;
+    struct lock vx_lock;
 };
 
 #endif /* _KERNEL || _KERNEL_STRUCTURES */