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
#include <machine/limits.h> /* for LLONG_MAX */
#include <machine/stdarg.h>
+#include <sys/spinlock2.h>
+
#ifdef INVARIANTS
int lf_global_counter = 0;
#endif
KKASSERT(p != NULL);
uip = p->p_ucred->cr_uidinfo;
+ spin_lock_wr(&uip->ui_lock);
if (increase)
uip->ui_posixlocks += p->p_numposixlocks;
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)
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;
}
/*
#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 */
void
uihashinit(void)
{
+ spin_init(&uihash_lock);
uihashtbl = hashinit(maxproc / 16, M_UIDINFO, &uihash);
}
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);
}
{
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",
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
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;
}
/*
{
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
}
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);
}
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);
}
++i;
}
+ lockmgr(&vss->vx_lock, LK_RELEASE);
/*
* Save the marker back. If no error occured and earlyterm is clear
{
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 &&
}
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
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);
error = ENOENT;
}
}
+ lockmgr(&vss->vx_lock, LK_RELEASE);
return(error);
}
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)
{
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);
}
struct varsyment *ve;
TAILQ_INIT(&vss->vx_queue);
+ lockinit(&vss->vx_lock, "vx", 0, 0);
if (copy) {
TAILQ_FOREACH(ve, ©->vx_queue, ve_entry) {
varsymdup(vss, ve);
{
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);
}
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); }
* 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 */
#include <sys/queue.h> /* TAILQ_* macros */
#endif
+#include <sys/lock.h>
+
struct varsym {
int vs_refs; /* a lot of sharing occurs */
int vs_namelen;
struct varsymset {
TAILQ_HEAD(, varsyment) vx_queue;
int vx_setsize;
+ struct lock vx_lock;
};
#endif /* _KERNEL || _KERNEL_STRUCTURES */