Module Name: src
Committed By: thorpej
Date: Wed Apr 29 01:52:26 UTC 2020
Modified Files:
src/sys/kern: kern_lwp.c kern_proc.c
src/sys/sys: lwp.h proc.h
Log Message:
- proc_find() retains traditional semantics of requiring the canonical
PID to look up a proc. Add a separate proc_find_lwpid() to look up a
proc by the ID of any of its LWPs.
- Add proc_find_lwp_acquire_proc(), which enables looking up the LWP
*and* a proc given the ID of any LWP. Returns with the proc::p_lock
held.
- Rewrite lwp_find2() in terms of proc_find_lwp_acquire_proc(), and add
allow the proc to be wildcarded, rather than just curproc or specific
proc.
- lwp_find2() now subsumes the original intent of lwp_getref_lwpid(), but
in a much nicer way, so garbage-collect the remnants of that recently
added mechanism.
To generate a diff of this commit:
cvs rdiff -u -r1.236 -r1.237 src/sys/kern/kern_lwp.c
cvs rdiff -u -r1.250 -r1.251 src/sys/kern/kern_proc.c
cvs rdiff -u -r1.208 -r1.209 src/sys/sys/lwp.h
cvs rdiff -u -r1.363 -r1.364 src/sys/sys/proc.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/kern/kern_lwp.c
diff -u src/sys/kern/kern_lwp.c:1.236 src/sys/kern/kern_lwp.c:1.237
--- src/sys/kern/kern_lwp.c:1.236 Sun Apr 26 18:53:33 2020
+++ src/sys/kern/kern_lwp.c Wed Apr 29 01:52:26 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_lwp.c,v 1.236 2020/04/26 18:53:33 thorpej Exp $ */
+/* $NetBSD: kern_lwp.c,v 1.237 2020/04/29 01:52:26 thorpej Exp $ */
/*-
* Copyright (c) 2001, 2006, 2007, 2008, 2009, 2019, 2020
@@ -223,7 +223,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.236 2020/04/26 18:53:33 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.237 2020/04/29 01:52:26 thorpej Exp $");
#include "opt_ddb.h"
#include "opt_lockdebug.h"
@@ -266,32 +266,6 @@ __KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v
static pool_cache_t lwp_cache __read_mostly;
struct lwplist alllwp __cacheline_aligned;
-/*
- * Lookups by global thread ID operate outside of the normal LWP
- * locking protocol.
- *
- * => When we look up an LWP in the table, we take lwp_threadid_lock as
- * a READER. While still holding the lock, we add a reference to
- * the LWP (using atomics). After adding the reference, we drop the
- * lwp_threadid_lock. We now take p_lock and check the state of the
- * LWP. If the LWP is draining its references, we drop the reference
- * we took and return NULL. Otherwise, the lookup has succeeded and
- * the LWP is returned with a reference count that the caller is
- * responsible for dropping.
- *
- * => When a LWP is exiting, it also drains off any references being
- * held by others. However, the reference in the lookup path is taken
- * outside the normal locking protocol. There needs to be additional
- * serialization so that EITHER lwp_drainrefs() sees the incremented
- * reference count so that it knows to wait, OR lwp_getref_lwpid() sees
- * that the LWP is waiting to drain and thus drops the reference
- * immediately. This is achieved by taking lwp_threadid_lock as a
- * WRITER when setting LPR_DRAINING. Note the locking order:
- *
- * p_lock -> lwp_threadid_lock
- */
-static krwlock_t lwp_threadid_lock __cacheline_aligned;
-
static void lwp_dtor(void *, void *);
/* DTrace proc provider probes */
@@ -325,7 +299,6 @@ struct lwp lwp0 __aligned(MIN_LWP_ALIGNM
.l_fd = &filedesc0,
};
-static void lwp_threadid_init(void);
static int sysctl_kern_maxlwp(SYSCTLFN_PROTO);
/*
@@ -378,7 +351,6 @@ lwpinit(void)
maxlwp = cpu_maxlwp();
sysctl_kern_lwp_setup();
- lwp_threadid_init();
}
void
@@ -1405,10 +1377,20 @@ lwp_migrate(lwp_t *l, struct cpu_info *t
lwp_unlock(l);
}
+#define lwp_find_exclude(l) \
+ ((l)->l_stat == LSIDL || (l)->l_stat == LSZOMB)
+
/*
* Find the LWP in the process. Arguments may be zero, in such case,
* the calling process and first LWP in the list will be used.
* On success - returns proc locked.
+ *
+ * => pid == 0 -> look in curproc.
+ * => pid == -1 -> match any proc.
+ * => otherwise look up the proc.
+ *
+ * => lid == 0 -> first LWP in the proc
+ * => otherwise specific LWP
*/
struct lwp *
lwp_find2(pid_t pid, lwpid_t lid)
@@ -1416,27 +1398,62 @@ lwp_find2(pid_t pid, lwpid_t lid)
proc_t *p;
lwp_t *l;
- /* Find the process. */
- if (pid != 0) {
- mutex_enter(proc_lock);
- p = proc_find(pid);
- if (p == NULL) {
- mutex_exit(proc_lock);
+ /* First LWP of specified proc. */
+ if (lid == 0) {
+ switch (pid) {
+ case -1:
+ /* No lookup keys. */
return NULL;
+ case 0:
+ p = curproc;
+ mutex_enter(p->p_lock);
+ break;
+ default:
+ mutex_enter(proc_lock);
+ p = proc_find(pid);
+ if (__predict_false(p == NULL)) {
+ mutex_exit(proc_lock);
+ return NULL;
+ }
+ mutex_enter(p->p_lock);
+ mutex_exit(proc_lock);
+ break;
}
- mutex_enter(p->p_lock);
- mutex_exit(proc_lock);
- } else {
- p = curlwp->l_proc;
- mutex_enter(p->p_lock);
+ LIST_FOREACH(l, &p->p_lwps, l_sibling) {
+ if (__predict_true(!lwp_find_exclude(l)))
+ break;
+ }
+ goto out;
}
- /* Find the thread. */
- if (lid != 0) {
- l = lwp_find(p, lid);
- } else {
- l = LIST_FIRST(&p->p_lwps);
+
+ l = proc_find_lwp_acquire_proc(lid, &p);
+ if (l == NULL)
+ return NULL;
+ KASSERT(p != NULL);
+ KASSERT(mutex_owned(p->p_lock));
+
+ if (__predict_false(lwp_find_exclude(l))) {
+ l = NULL;
+ goto out;
+ }
+
+ /* Apply proc filter, if applicable. */
+ switch (pid) {
+ case -1:
+ /* Match anything. */
+ break;
+ case 0:
+ if (p != curproc)
+ l = NULL;
+ break;
+ default:
+ if (p->p_pid != pid)
+ l = NULL;
+ break;
}
- if (l == NULL) {
+
+ out:
+ if (__predict_false(l == NULL)) {
mutex_exit(p->p_lock);
}
return l;
@@ -1462,7 +1479,7 @@ lwp_find(struct proc *p, lwpid_t id)
* No need to lock - all of these conditions will
* be visible with the process level mutex held.
*/
- if (l != NULL && (l->l_stat == LSIDL || l->l_stat == LSZOMB))
+ if (__predict_false(l != NULL && lwp_find_exclude(l)))
l = NULL;
return l;
@@ -1664,21 +1681,6 @@ lwp_need_userret(struct lwp *l)
}
/*
- * Add one reference to an LWP. Interlocked against lwp_drainrefs()
- * either by holding the proc's lock or by holding lwp_threadid_lock.
- * If callers don't hold the proc's lock, then they must check for a
- * larva after acquiring the reference. References can't be added to
- * zombies because references have already been drained off before the
- * state changes to LSZOMB.
- */
-static void
-lwp_addref2(struct lwp *l)
-{
- KASSERT(l->l_stat != LSZOMB);
- atomic_inc_uint(&l->l_refcnt);
-}
-
-/*
* Add one reference to an LWP. This will prevent the LWP from
* exiting, thus keep the lwp structure and PCB around to inspect.
*/
@@ -1686,7 +1688,8 @@ void
lwp_addref(struct lwp *l)
{
KASSERT(mutex_owned(l->l_proc->p_lock));
- lwp_addref2(l);
+ KASSERT(l->l_stat != LSZOMB);
+ l->l_refcnt++;
}
/*
@@ -1715,9 +1718,9 @@ lwp_delref2(struct lwp *l)
KASSERT(mutex_owned(p->p_lock));
KASSERT(l->l_stat != LSZOMB);
- KASSERT(atomic_load_relaxed(&l->l_refcnt) > 0);
+ KASSERT(l->l_refcnt > 0);
- if (atomic_dec_uint_nv(&l->l_refcnt) == 0)
+ if (--l->l_refcnt == 0)
cv_broadcast(&p->p_lwpcv);
}
@@ -1733,18 +1736,9 @@ lwp_drainrefs(struct lwp *l)
KASSERT(mutex_owned(p->p_lock));
- /*
- * Lookups by thread ID hold lwp_threadid_lock as a reader,
- * increase l_refcnt, release it, and then acquire p_lock to
- * check for LPR_DRAINING. By taking lwp_threadid_lock as a
- * writer here we ensure that either we see the increase in
- * l_refcnt or that they see LPR_DRAINING.
- */
- rw_enter(&lwp_threadid_lock, RW_WRITER);
l->l_prflag |= LPR_DRAINING;
- rw_exit(&lwp_threadid_lock);
- while (atomic_load_relaxed(&l->l_refcnt) > 0) {
+ while (l->l_refcnt > 0) {
rv = true;
cv_wait(&p->p_lwpcv, p->p_lock);
}
@@ -2031,58 +2025,6 @@ lwp_setprivate(struct lwp *l, void *ptr)
return error;
}
-static void
-lwp_threadid_init(void)
-{
- rw_init(&lwp_threadid_lock);
-}
-
-/*
- * Lookup an LWP by global thread ID. Care must be taken because
- * callers of this are operating outside the normal locking protocol.
- * We return the LWP with an additional reference that must be dropped
- * with lwp_delref().
- */
-struct lwp *
-lwp_getref_lwpid(lwpid_t tid)
-{
- struct lwp *l;
-
- /*
- * We rely on lwp_thread_cleanup() to hide LWP IDs from us
- * to ensure that we cannot add a reference do an exiting
- * LWP.
- */
- rw_enter(&lwp_threadid_lock, RW_READER);
- l = proc_seek_lwpid(tid);
- if (__predict_false(l == NULL)) {
- rw_exit(&lwp_threadid_lock);
- return NULL;
- }
-
- /*
- * Acquire a reference on the lwp. It is safe to do this unlocked
- * here because lwp_drainrefs() serializes with us by taking the
- * lwp_threadid_lock as a writer.
- */
- lwp_addref2(l);
- rw_exit(&lwp_threadid_lock);
-
- /*
- * Now verify that our reference is valid.
- */
- struct proc *p = l->l_proc;
- mutex_enter(p->p_lock);
- if (__predict_false(l->l_stat == LSLARVAL ||
- (l->l_prflag & LPR_DRAINING) != 0)) {
- lwp_delref2(l);
- l = NULL;
- }
- mutex_exit(p->p_lock);
-
- return l;
-}
-
/*
* Perform any thread-related cleanup on LWP exit.
* N.B. l->l_proc->p_lock must be HELD on entry but will
@@ -2097,13 +2039,6 @@ lwp_thread_cleanup(struct lwp *l)
KASSERT((tid & FUTEX_TID_MASK) == tid);
KASSERT(mutex_owned(l->l_proc->p_lock));
- /*
- * Hide this LWP from seekers (namely lwp_getref_lwpid())
- * to prevent them from attempting to acquire a reference
- * on a zombie.
- */
- proc_hide_lwpid(tid);
-
mutex_exit(l->l_proc->p_lock);
/*
Index: src/sys/kern/kern_proc.c
diff -u src/sys/kern/kern_proc.c:1.250 src/sys/kern/kern_proc.c:1.251
--- src/sys/kern/kern_proc.c:1.250 Sun Apr 26 18:53:33 2020
+++ src/sys/kern/kern_proc.c Wed Apr 29 01:52:26 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: kern_proc.c,v 1.250 2020/04/26 18:53:33 thorpej Exp $ */
+/* $NetBSD: kern_proc.c,v 1.251 2020/04/29 01:52:26 thorpej Exp $ */
/*-
* Copyright (c) 1999, 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc.
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_proc.c,v 1.250 2020/04/26 18:53:33 thorpej Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_proc.c,v 1.251 2020/04/29 01:52:26 thorpej Exp $");
#ifdef _KERNEL_OPT
#include "opt_kstack.h"
@@ -147,17 +147,14 @@ struct pid_table {
#define PT_F_FREE ((uintptr_t)__BIT(0))
#define PT_F_LWP 0 /* pseudo-flag */
#define PT_F_PROC ((uintptr_t)__BIT(1))
-#define PT_F_HIDDEN ((uintptr_t)__BIT(2))
#define PT_F_TYPEBITS (PT_F_FREE|PT_F_PROC)
-#define PT_F_ALLBITS (PT_F_FREE|PT_F_PROC|PT_F_HIDDEN)
+#define PT_F_ALLBITS (PT_F_FREE|PT_F_PROC)
#define PT_VALID(s) (((s) & PT_F_FREE) == 0)
#define PT_RESERVED(s) ((s) == 0)
-#define PT_HIDDEN(s) ((s) & PT_F_HIDDEN)
#define PT_NEXT(s) ((u_int)(s) >> 1)
#define PT_SET_FREE(pid) (((pid) << 1) | PT_F_FREE)
-#define PT_SET_HIDDEN(s) ((s) | PT_F_HIDDEN)
#define PT_SET_LWP(l) ((uintptr_t)(l))
#define PT_SET_PROC(p) (((uintptr_t)(p)) | PT_F_PROC)
#define PT_SET_RESERVED 0
@@ -708,63 +705,63 @@ proc_find_lwp(proc_t *p, pid_t pid)
}
/*
- * proc_seek_lwpid: locate an lwp by only the ID.
+ * proc_find_lwp_acquire_proc: locate an lwp and acquire a lock
+ * on its containing proc.
*
- * => This is a specialized interface used for looking up an LWP
- * without holding a lock on its owner process.
- * => Callers of this interface MUST provide a separate synchronization
- * mechanism to ensure the validity of the returned LWP. LARVAL LWPs
- * are found there, so callers must check for them!
- * => Only returns LWPs whose ID has not been hidden from us.
+ * => Similar to proc_find_lwp(), but does not require you to have
+ * the proc a priori.
+ * => Also returns proc * to caller, with p::p_lock held.
+ * => Same caveats apply.
*/
struct lwp *
-proc_seek_lwpid(pid_t pid)
+proc_find_lwp_acquire_proc(pid_t pid, struct proc **pp)
{
struct pid_table *pt;
+ struct proc *p = NULL;
struct lwp *l = NULL;
uintptr_t slot;
+ KASSERT(pp != NULL);
+ mutex_enter(proc_lock);
rw_enter(&pid_table_lock, RW_READER);
pt = &pid_table[pid & pid_tbl_mask];
slot = pt->pt_slot;
- if (__predict_true(PT_IS_LWP(slot) && pt->pt_pid == pid &&
- !PT_HIDDEN(slot))) {
+ if (__predict_true(PT_IS_LWP(slot) && pt->pt_pid == pid)) {
+ /*
+ * Locking order is p::p_lock -> pid_table_lock, but
+ * we're already holding pid_table_lock; we need to
+ * release it before acquiring p::p_lock. This is
+ * safe because p will be stable by virtue of holding
+ * proc_lock.
+ */
l = PT_GET_LWP(slot);
+ p = l->l_proc;
+ rw_exit(&pid_table_lock);
+ mutex_enter(p->p_lock);
+ if (__predict_false(l->l_stat == LSLARVAL)) {
+ mutex_exit(p->p_lock);
+ l = NULL;
+ p = NULL;
+ }
+ } else {
+ rw_exit(&pid_table_lock);
}
- rw_exit(&pid_table_lock);
+ mutex_exit(proc_lock);
+ KASSERT(p == NULL || mutex_owned(p->p_lock));
+ *pp = p;
return l;
}
/*
- * proc_hide_lwpid: hide an lwp ID from seekers.
- */
-void
-proc_hide_lwpid(pid_t pid)
-{
- struct pid_table *pt;
- uintptr_t slot;
-
- rw_enter(&pid_table_lock, RW_WRITER);
- pt = &pid_table[pid & pid_tbl_mask];
-
- slot = pt->pt_slot;
- KASSERT(PT_IS_LWP(slot));
- KASSERT(pt->pt_pid == pid);
- pt->pt_slot = PT_SET_HIDDEN(slot);
-
- rw_exit(&pid_table_lock);
-}
-
-/*
* proc_find_raw_pid_table_locked: locate a process by the ID.
*
* => Must be called with proc_lock held and the pid_table_lock
* at least held for reading.
*/
static proc_t *
-proc_find_raw_pid_table_locked(pid_t pid)
+proc_find_raw_pid_table_locked(pid_t pid, bool any_lwpid)
{
struct pid_table *pt;
proc_t *p = NULL;
@@ -784,7 +781,7 @@ proc_find_raw_pid_table_locked(pid_t pid
* valid here.
*/
p = PT_GET_LWP(slot)->l_proc;
- if (__predict_false(p->p_pid != pid))
+ if (__predict_false(p->p_pid != pid && !any_lwpid))
p = NULL;
} else if (PT_IS_PROC(slot) && pt->pt_pid == pid) {
p = PT_GET_PROC(slot);
@@ -797,19 +794,19 @@ proc_find_raw(pid_t pid)
{
KASSERT(mutex_owned(proc_lock));
rw_enter(&pid_table_lock, RW_READER);
- proc_t *p = proc_find_raw_pid_table_locked(pid);
+ proc_t *p = proc_find_raw_pid_table_locked(pid, false);
rw_exit(&pid_table_lock);
return p;
}
static proc_t *
-proc_find_pid_table_locked(pid_t pid)
+proc_find_pid_table_locked(pid_t pid, bool any_lwpid)
{
proc_t *p;
KASSERT(mutex_owned(proc_lock));
- p = proc_find_raw_pid_table_locked(pid);
+ p = proc_find_raw_pid_table_locked(pid, any_lwpid);
if (__predict_false(p == NULL)) {
return NULL;
}
@@ -824,16 +821,28 @@ proc_find_pid_table_locked(pid_t pid)
return NULL;
}
-proc_t *
-proc_find(pid_t pid)
+static proc_t *
+proc_find_internal(pid_t pid, bool any_lwpid)
{
KASSERT(mutex_owned(proc_lock));
rw_enter(&pid_table_lock, RW_READER);
- proc_t *p = proc_find_pid_table_locked(pid);
+ proc_t *p = proc_find_pid_table_locked(pid, any_lwpid);
rw_exit(&pid_table_lock);
return p;
}
+proc_t *
+proc_find(pid_t pid)
+{
+ return proc_find_internal(pid, false);
+}
+
+proc_t *
+proc_find_lwpid(pid_t pid)
+{
+ return proc_find_internal(pid, true);
+}
+
/*
* pgrp_find_pid_table_locked: locate a process group by the ID.
*
@@ -1227,7 +1236,7 @@ proc_enterpgrp(struct proc *curp, pid_t
/* Can only set another process under restricted circumstances. */
if (pid != curp->p_pid) {
/* Must exist and be one of our children... */
- p = proc_find_pid_table_locked(pid);
+ p = proc_find_pid_table_locked(pid, false);
if (p == NULL || !p_inferior(p, curp)) {
rval = ESRCH;
goto done;
Index: src/sys/sys/lwp.h
diff -u src/sys/sys/lwp.h:1.208 src/sys/sys/lwp.h:1.209
--- src/sys/sys/lwp.h:1.208 Sun Apr 26 18:53:33 2020
+++ src/sys/sys/lwp.h Wed Apr 29 01:52:26 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: lwp.h,v 1.208 2020/04/26 18:53:33 thorpej Exp $ */
+/* $NetBSD: lwp.h,v 1.209 2020/04/29 01:52:26 thorpej Exp $ */
/*
* Copyright (c) 2001, 2006, 2007, 2008, 2009, 2010, 2019, 2020
@@ -349,7 +349,6 @@ kmutex_t *lwp_setlock(lwp_t *, kmutex_t
void lwp_unlock_to(lwp_t *, kmutex_t *);
int lwp_trylock(lwp_t *);
void lwp_addref(lwp_t *);
-lwp_t * lwp_getref_lwpid(lwpid_t);
void lwp_delref(lwp_t *);
void lwp_delref2(lwp_t *);
bool lwp_drainrefs(lwp_t *);
Index: src/sys/sys/proc.h
diff -u src/sys/sys/proc.h:1.363 src/sys/sys/proc.h:1.364
--- src/sys/sys/proc.h:1.363 Fri Apr 24 03:22:06 2020
+++ src/sys/sys/proc.h Wed Apr 29 01:52:26 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: proc.h,v 1.363 2020/04/24 03:22:06 thorpej Exp $ */
+/* $NetBSD: proc.h,v 1.364 2020/04/29 01:52:26 thorpej Exp $ */
/*-
* Copyright (c) 2006, 2007, 2008, 2020 The NetBSD Foundation, Inc.
@@ -497,12 +497,12 @@ extern struct pool ptimer_pool; /* Memor
int proc_find_locked(struct lwp *, struct proc **, pid_t);
proc_t * proc_find_raw(pid_t);
proc_t * proc_find(pid_t); /* Find process by ID */
+proc_t * proc_find_lwpid(pid_t); /* Find process by LWP ID */
struct lwp * proc_find_lwp(proc_t *, pid_t); /* Find LWP in proc by ID */
+ /* Find LWP, acquire proc */
+struct lwp * proc_find_lwp_acquire_proc(pid_t, proc_t **);
struct pgrp * pgrp_find(pid_t); /* Find process group by ID */
-struct lwp * proc_seek_lwpid(pid_t); /* Find LWP by ID only */
-void proc_hide_lwpid(pid_t); /* Hide LWP ID from seekers */
-
void procinit(void);
void procinit_sysctl(void);
int proc_enterpgrp(struct proc *, pid_t, pid_t, bool);