summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/os/exec.c24
-rw-r--r--usr/src/uts/common/os/exit.c18
-rw-r--r--usr/src/uts/common/os/fork.c5
-rw-r--r--usr/src/uts/common/os/lwp.c200
-rw-r--r--usr/src/uts/common/os/main.c5
-rw-r--r--usr/src/uts/common/sys/proc.h27
-rw-r--r--usr/src/uts/common/syscall/lwpsys.c48
7 files changed, 259 insertions, 68 deletions
diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c
index 9b3dce476a..2ca011f17b 100644
--- a/usr/src/uts/common/os/exec.c
+++ b/usr/src/uts/common/os/exec.c
@@ -130,11 +130,12 @@ exec_common(const char *fname, const char **argp, const char **envp,
struct execa ua;
k_sigset_t savedmask;
lwpdir_t *lwpdir = NULL;
- lwpdir_t **tidhash;
+ tidhash_t *tidhash;
lwpdir_t *old_lwpdir = NULL;
uint_t old_lwpdir_sz;
- lwpdir_t **old_tidhash;
+ tidhash_t *old_tidhash;
uint_t old_tidhash_sz;
+ ret_tidhash_t *ret_tidhash;
lwpent_t *lep;
boolean_t brandme = B_FALSE;
@@ -416,7 +417,7 @@ exec_common(const char *fname, const char **argp, const char **envp,
if (curthread->t_tid != 1 || p->p_lwpdir_sz != 2) {
lwpdir = kmem_zalloc(2 * sizeof (lwpdir_t), KM_SLEEP);
lwpdir->ld_next = lwpdir + 1;
- tidhash = kmem_zalloc(2 * sizeof (lwpdir_t *), KM_SLEEP);
+ tidhash = kmem_zalloc(2 * sizeof (tidhash_t), KM_SLEEP);
if (p->p_lwpdir != NULL)
lep = p->p_lwpdir[curthread->t_dslot].ld_entry;
else
@@ -461,13 +462,15 @@ exec_common(const char *fname, const char **argp, const char **envp,
old_tidhash_sz = p->p_tidhash_sz;
p->p_lwpdir = p->p_lwpfree = lwpdir;
p->p_lwpdir_sz = 2;
- p->p_tidhash = tidhash;
- p->p_tidhash_sz = 2;
lep->le_thread = curthread;
lep->le_lwpid = curthread->t_tid;
lep->le_start = curthread->t_start;
- lwp_hash_in(p, lep);
+ lwp_hash_in(p, lep, tidhash, 2, 0);
+ p->p_tidhash = tidhash;
+ p->p_tidhash_sz = 2;
}
+ ret_tidhash = p->p_ret_tidhash;
+ p->p_ret_tidhash = NULL;
/*
* Restore the saved signal mask and
@@ -478,7 +481,14 @@ exec_common(const char *fname, const char **argp, const char **envp,
mutex_exit(&p->p_lock);
if (old_lwpdir) {
kmem_free(old_lwpdir, old_lwpdir_sz * sizeof (lwpdir_t));
- kmem_free(old_tidhash, old_tidhash_sz * sizeof (lwpdir_t *));
+ kmem_free(old_tidhash, old_tidhash_sz * sizeof (tidhash_t));
+ }
+ while (ret_tidhash != NULL) {
+ ret_tidhash_t *next = ret_tidhash->rth_next;
+ kmem_free(ret_tidhash->rth_tidhash,
+ ret_tidhash->rth_tidhash_sz * sizeof (tidhash_t));
+ kmem_free(ret_tidhash, sizeof (*ret_tidhash));
+ ret_tidhash = next;
}
ASSERT(error == 0);
diff --git a/usr/src/uts/common/os/exit.c b/usr/src/uts/common/os/exit.c
index 02f7c5931f..bd2991c393 100644
--- a/usr/src/uts/common/os/exit.c
+++ b/usr/src/uts/common/os/exit.c
@@ -20,14 +20,12 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-#pragma ident "%Z%%M% %I% %E% SMI" /* from SVr4.0 1.74 */
-
#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
@@ -341,8 +339,9 @@ proc_exit(int why, int what)
sigqueue_t *sqp;
lwpdir_t *lwpdir;
uint_t lwpdir_sz;
- lwpdir_t **tidhash;
+ tidhash_t *tidhash;
uint_t tidhash_sz;
+ ret_tidhash_t *ret_tidhash;
refstr_t *cwd;
hrtime_t hrutime, hrstime;
int evaporate;
@@ -779,11 +778,13 @@ proc_exit(int why, int what)
lwpdir_sz = p->p_lwpdir_sz;
tidhash = p->p_tidhash;
tidhash_sz = p->p_tidhash_sz;
+ ret_tidhash = p->p_ret_tidhash;
p->p_lwpdir = NULL;
p->p_lwpfree = NULL;
p->p_lwpdir_sz = 0;
p->p_tidhash = NULL;
p->p_tidhash_sz = 0;
+ p->p_ret_tidhash = NULL;
/*
* If the process has context ops installed, call the exit routine
@@ -855,7 +856,14 @@ proc_exit(int why, int what)
task_rele(tk);
kmem_free(lwpdir, lwpdir_sz * sizeof (lwpdir_t));
- kmem_free(tidhash, tidhash_sz * sizeof (lwpdir_t *));
+ kmem_free(tidhash, tidhash_sz * sizeof (tidhash_t));
+ while (ret_tidhash != NULL) {
+ ret_tidhash_t *next = ret_tidhash->rth_next;
+ kmem_free(ret_tidhash->rth_tidhash,
+ ret_tidhash->rth_tidhash_sz * sizeof (tidhash_t));
+ kmem_free(ret_tidhash, sizeof (*ret_tidhash));
+ ret_tidhash = next;
+ }
lwp_pcb_exit();
diff --git a/usr/src/uts/common/os/fork.c b/usr/src/uts/common/os/fork.c
index fd4972f6d7..524f0f0aaf 100644
--- a/usr/src/uts/common/os/fork.c
+++ b/usr/src/uts/common/os/fork.c
@@ -374,7 +374,7 @@ cfork(int isvfork, int isfork1, int flags)
ldp->ld_next = ldp + 1;
cp->p_tidhash_sz = (cp->p_lwpdir_sz + 2) / 2;
cp->p_tidhash =
- kmem_zalloc(cp->p_tidhash_sz * sizeof (lwpdir_t *), KM_SLEEP);
+ kmem_zalloc(cp->p_tidhash_sz * sizeof (tidhash_t), KM_SLEEP);
/*
* Duplicate parent's lwps.
@@ -430,7 +430,8 @@ cfork(int isvfork, int isfork1, int flags)
clep = kmem_zalloc(sizeof (*clep), KM_SLEEP);
clep->le_lwpid = lep->le_lwpid;
clep->le_start = lep->le_start;
- lwp_hash_in(cp, clep);
+ lwp_hash_in(cp, clep,
+ cp->p_tidhash, cp->p_tidhash_sz, 0);
}
}
}
diff --git a/usr/src/uts/common/os/lwp.c b/usr/src/uts/common/os/lwp.c
index d80f32c658..a4bc1bffd2 100644
--- a/usr/src/uts/common/os/lwp.c
+++ b/usr/src/uts/common/os/lwp.c
@@ -20,12 +20,10 @@
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
@@ -60,6 +58,9 @@
#include <sys/cmn_err.h>
#include <sys/brand.h>
+/* hash function for the lwpid hash table, p->p_tidhash[] */
+#define TIDHASH(tid, hash_sz) ((tid) & ((hash_sz) - 1))
+
void *segkp_lwp; /* cookie for pool of segkp resources */
extern void reapq_move_lq_to_tq(kthread_t *);
extern void freectx_ctx(struct ctxop *);
@@ -85,8 +86,9 @@ lwp_create(void (*proc)(), caddr_t arg, size_t len, proc_t *p,
lwpent_t *lep;
lwpdir_t *old_dir = NULL;
uint_t old_dirsz = 0;
- lwpdir_t **old_hash = NULL;
+ tidhash_t *old_hash = NULL;
uint_t old_hashsz = 0;
+ ret_tidhash_t *ret_tidhash = NULL;
int i;
int rctlfail = 0;
boolean_t branded = 0;
@@ -232,31 +234,58 @@ grow:
* which leads to these hash table sizes corresponding to
* the above directory sizes:
* 2, 4, 8, 16, 32, 64, 128, 256, 512, ...
+ * A note on growing the hash table:
+ * For performance reasons, code in lwp_unpark() does not
+ * acquire curproc->p_lock when searching the hash table.
+ * Rather, it calls lwp_hash_lookup_and_lock() which
+ * acquires only the individual hash bucket lock, taking
+ * care to deal with reallocation of the hash table
+ * during the time it takes to acquire the lock.
+ *
+ * This is sufficient to protect the integrity of the
+ * hash table, but it requires us to acquire all of the
+ * old hash bucket locks before growing the hash table
+ * and to release them afterwards. It also requires us
+ * not to free the old hash table because some thread
+ * in lwp_hash_lookup_and_lock() might still be trying
+ * to acquire the old bucket lock.
+ *
+ * So we adopt the tactic of keeping all of the retired
+ * hash tables on a linked list, so they can be safely
+ * freed when the process exits or execs.
+ *
+ * Because the hash table grows in powers of two, the
+ * total size of all of the hash tables will be slightly
+ * less than twice the size of the largest hash table.
*/
while (p->p_lwpfree == NULL) {
uint_t dirsz = p->p_lwpdir_sz;
- uint_t new_dirsz;
- uint_t new_hashsz;
lwpdir_t *new_dir;
+ uint_t new_dirsz;
lwpdir_t *ldp;
- lwpdir_t **new_hash;
+ tidhash_t *new_hash;
+ uint_t new_hashsz;
mutex_exit(&p->p_lock);
- if (old_dir != NULL) {
+ /*
+ * Prepare to remember the old p_tidhash for later
+ * kmem_free()ing when the process exits or execs.
+ */
+ if (ret_tidhash == NULL)
+ ret_tidhash = kmem_zalloc(sizeof (ret_tidhash_t),
+ KM_SLEEP);
+ if (old_dir != NULL)
kmem_free(old_dir, old_dirsz * sizeof (*old_dir));
+ if (old_hash != NULL)
kmem_free(old_hash, old_hashsz * sizeof (*old_hash));
- old_dir = NULL;
- old_dirsz = 0;
- old_hash = NULL;
- old_hashsz = 0;
- }
+
new_dirsz = 2 * dirsz + 2;
new_dir = kmem_zalloc(new_dirsz * sizeof (lwpdir_t), KM_SLEEP);
for (ldp = new_dir, i = 1; i < new_dirsz; i++, ldp++)
ldp->ld_next = ldp + 1;
new_hashsz = (new_dirsz + 2) / 2;
- new_hash = kmem_zalloc(new_hashsz * sizeof (lwpdir_t *),
+ new_hash = kmem_zalloc(new_hashsz * sizeof (tidhash_t),
KM_SLEEP);
mutex_enter(&p->p_lock);
@@ -273,15 +302,20 @@ grow:
old_hash = new_hash;
old_hashsz = new_hashsz;
} else {
- old_dir = p->p_lwpdir;
- old_dirsz = p->p_lwpdir_sz;
+ /*
+ * For the benefit of lwp_hash_lookup_and_lock(),
+ * called from lwp_unpark(), which searches the
+ * tid hash table without acquiring p->p_lock,
+ * we must acquire all of the tid hash table
+ * locks before replacing p->p_tidhash.
+ */
old_hash = p->p_tidhash;
old_hashsz = p->p_tidhash_sz;
- p->p_lwpdir = new_dir;
- p->p_lwpfree = new_dir;
- p->p_lwpdir_sz = new_dirsz;
- p->p_tidhash = new_hash;
- p->p_tidhash_sz = new_hashsz;
+ for (i = 0; i < old_hashsz; i++) {
+ mutex_enter(&old_hash[i].th_lock);
+ mutex_enter(&new_hash[i].th_lock);
+ }
+
/*
* We simply hash in all of the old directory entries.
* This works because the old directory has no empty
@@ -289,11 +323,57 @@ grow:
* This reproduces the original directory ordering
* (required for /proc directory semantics).
*/
- for (ldp = old_dir, i = 0; i < dirsz; i++, ldp++)
- lwp_hash_in(p, ldp->ld_entry);
+ old_dir = p->p_lwpdir;
+ old_dirsz = p->p_lwpdir_sz;
+ p->p_lwpdir = new_dir;
+ p->p_lwpfree = new_dir;
+ p->p_lwpdir_sz = new_dirsz;
+ for (ldp = old_dir, i = 0; i < old_dirsz; i++, ldp++)
+ lwp_hash_in(p, ldp->ld_entry,
+ new_hash, new_hashsz, 0);
+
+ /*
+ * Remember the old hash table along with all
+ * of the previously-remembered hash tables.
+ * We will free them at process exit or exec.
+ */
+ ret_tidhash->rth_tidhash = old_hash;
+ ret_tidhash->rth_tidhash_sz = old_hashsz;
+ ret_tidhash->rth_next = p->p_ret_tidhash;
+ p->p_ret_tidhash = ret_tidhash;
+
/*
- * Defer freeing memory until we drop p->p_lock,
+ * Now establish the new tid hash table.
+ * As soon as we assign p->p_tidhash,
+ * code in lwp_unpark() can start using it.
*/
+ membar_producer();
+ p->p_tidhash = new_hash;
+
+ /*
+ * It is necessary that p_tidhash reach global
+ * visibility before p_tidhash_sz. Otherwise,
+ * code in lwp_hash_lookup_and_lock() could
+ * index into the old p_tidhash using the new
+ * p_tidhash_sz and thereby access invalid data.
+ */
+ membar_producer();
+ p->p_tidhash_sz = new_hashsz;
+
+ /*
+ * Release the locks; allow lwp_unpark() to carry on.
+ */
+ for (i = 0; i < old_hashsz; i++) {
+ mutex_exit(&old_hash[i].th_lock);
+ mutex_exit(&new_hash[i].th_lock);
+ }
+
+ /*
+ * Avoid freeing these objects below.
+ */
+ ret_tidhash = NULL;
+ old_hash = NULL;
+ old_hashsz = 0;
}
}
@@ -550,7 +630,7 @@ grow:
lep->le_thread = t;
lep->le_lwpid = t->t_tid;
lep->le_start = t->t_start;
- lwp_hash_in(p, lep);
+ lwp_hash_in(p, lep, p->p_tidhash, p->p_tidhash_sz, 1);
if (state == TS_RUN) {
/*
@@ -604,10 +684,12 @@ error:
mutex_exit(&p->p_lock);
}
- if (old_dir != NULL) {
+ if (old_dir != NULL)
kmem_free(old_dir, old_dirsz * sizeof (*old_dir));
+ if (old_hash != NULL)
kmem_free(old_hash, old_hashsz * sizeof (*old_hash));
- }
+ if (ret_tidhash != NULL)
+ kmem_free(ret_tidhash, sizeof (ret_tidhash_t));
DTRACE_PROC1(lwp__create, kthread_t *, t);
return (lwp);
@@ -1742,8 +1824,10 @@ retry:
* Add a new lwp entry to the lwp directory and to the lwpid hash table.
*/
void
-lwp_hash_in(proc_t *p, lwpent_t *lep)
+lwp_hash_in(proc_t *p, lwpent_t *lep, tidhash_t *tidhash, uint_t tidhash_sz,
+ int do_lock)
{
+ tidhash_t *thp = &tidhash[TIDHASH(lep->le_lwpid, tidhash_sz)];
lwpdir_t **ldpp;
lwpdir_t *ldp;
kthread_t *t;
@@ -1757,10 +1841,13 @@ lwp_hash_in(proc_t *p, lwpent_t *lep)
ASSERT(ldp->ld_entry == NULL);
ldp->ld_entry = lep;
+ if (do_lock)
+ mutex_enter(&thp->th_lock);
+
/*
* Insert it into the lwpid hash table.
*/
- ldpp = &p->p_tidhash[TIDHASH(p, lep->le_lwpid)];
+ ldpp = &thp->th_list;
ldp->ld_next = *ldpp;
*ldpp = ldp;
@@ -1771,6 +1858,9 @@ lwp_hash_in(proc_t *p, lwpent_t *lep)
ASSERT(lep->le_lwpid == t->t_tid);
t->t_dslot = (int)(ldp - p->p_lwpdir);
}
+
+ if (do_lock)
+ mutex_exit(&thp->th_lock);
}
/*
@@ -1782,11 +1872,13 @@ lwp_hash_in(proc_t *p, lwpent_t *lep)
void
lwp_hash_out(proc_t *p, id_t lwpid)
{
+ tidhash_t *thp = &p->p_tidhash[TIDHASH(lwpid, p->p_tidhash_sz)];
lwpdir_t **ldpp;
lwpdir_t *ldp;
lwpent_t *lep;
- for (ldpp = &p->p_tidhash[TIDHASH(p, lwpid)];
+ mutex_enter(&thp->th_lock);
+ for (ldpp = &thp->th_list;
(ldp = *ldpp) != NULL; ldpp = &ldp->ld_next) {
lep = ldp->ld_entry;
if (lep->le_lwpid == lwpid) {
@@ -1799,6 +1891,7 @@ lwp_hash_out(proc_t *p, id_t lwpid)
break;
}
}
+ mutex_exit(&thp->th_lock);
}
/*
@@ -1807,6 +1900,7 @@ lwp_hash_out(proc_t *p, id_t lwpid)
lwpdir_t *
lwp_hash_lookup(proc_t *p, id_t lwpid)
{
+ tidhash_t *thp;
lwpdir_t *ldp;
/*
@@ -1817,8 +1911,8 @@ lwp_hash_lookup(proc_t *p, id_t lwpid)
if (p->p_tidhash == NULL)
return (NULL);
- for (ldp = p->p_tidhash[TIDHASH(p, lwpid)];
- ldp != NULL; ldp = ldp->ld_next) {
+ thp = &p->p_tidhash[TIDHASH(lwpid, p->p_tidhash_sz)];
+ for (ldp = thp->th_list; ldp != NULL; ldp = ldp->ld_next) {
if (ldp->ld_entry->le_lwpid == lwpid)
return (ldp);
}
@@ -1827,6 +1921,48 @@ lwp_hash_lookup(proc_t *p, id_t lwpid)
}
/*
+ * Same as lwp_hash_lookup(), but acquire and return
+ * the tid hash table entry lock on success.
+ */
+lwpdir_t *
+lwp_hash_lookup_and_lock(proc_t *p, id_t lwpid, kmutex_t **mpp)
+{
+ tidhash_t *tidhash;
+ uint_t tidhash_sz;
+ tidhash_t *thp;
+ lwpdir_t *ldp;
+
+top:
+ tidhash_sz = p->p_tidhash_sz;
+ membar_consumer();
+ if ((tidhash = p->p_tidhash) == NULL)
+ return (NULL);
+
+ thp = &tidhash[TIDHASH(lwpid, tidhash_sz)];
+ mutex_enter(&thp->th_lock);
+
+ /*
+ * Since we are not holding p->p_lock, the tid hash table
+ * may have changed. If so, start over. If not, then
+ * it cannot change until after we drop &thp->th_lock;
+ */
+ if (tidhash != p->p_tidhash || tidhash_sz != p->p_tidhash_sz) {
+ mutex_exit(&thp->th_lock);
+ goto top;
+ }
+
+ for (ldp = thp->th_list; ldp != NULL; ldp = ldp->ld_next) {
+ if (ldp->ld_entry->le_lwpid == lwpid) {
+ *mpp = &thp->th_lock;
+ return (ldp);
+ }
+ }
+
+ mutex_exit(&thp->th_lock);
+ return (NULL);
+}
+
+/*
* Update the indicated LWP usage statistic for the current LWP.
*/
void
diff --git a/usr/src/uts/common/os/main.c b/usr/src/uts/common/os/main.c
index 7c5cd30915..03ada0c1e4 100644
--- a/usr/src/uts/common/os/main.c
+++ b/usr/src/uts/common/os/main.c
@@ -18,6 +18,7 @@
*
* CDDL HEADER END
*/
+
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
@@ -95,7 +96,7 @@ kmem_cache_t *process_cache; /* kmem cache for proc structures */
* Process 0's lwp directory and lwpid hash table.
*/
lwpdir_t p0_lwpdir[2];
-lwpdir_t *p0_tidhash[2];
+tidhash_t p0_tidhash[2];
lwpent_t p0_lep;
/*
@@ -526,7 +527,7 @@ main(void)
p0_lep.le_thread = curthread;
p0_lep.le_lwpid = curthread->t_tid;
p0_lep.le_start = curthread->t_start;
- lwp_hash_in(p, &p0_lep);
+ lwp_hash_in(p, &p0_lep, p0_tidhash, 2, 0);
/*
* Initialize extended accounting.
diff --git a/usr/src/uts/common/sys/proc.h b/usr/src/uts/common/sys/proc.h
index a8a8606245..43407bab38 100644
--- a/usr/src/uts/common/sys/proc.h
+++ b/usr/src/uts/common/sys/proc.h
@@ -31,7 +31,6 @@
#define _SYS_PROC_H
#include <sys/time.h>
-
#include <sys/thread.h>
#include <sys/cred.h>
#include <sys/user.h>
@@ -65,9 +64,6 @@ struct prof {
long pr_samples; /* sample count */
};
-/* hash function for the lwpid hash table, p->p_tidhash[] */
-#define TIDHASH(p, tid) ((tid) & ((p)->p_tidhash_sz - 1))
-
/*
* An lwp directory entry.
* If le_thread != NULL, this is an active lwp.
@@ -109,6 +105,23 @@ typedef struct lwpdir {
struct lwpent *ld_entry; /* lwp directory entry */
} lwpdir_t;
+/*
+ * Element of the p_tidhash thread-id (lwpid) hash table.
+ */
+typedef struct tidhash {
+ kmutex_t th_lock;
+ lwpdir_t *th_list;
+} tidhash_t;
+
+/*
+ * Retired tidhash hash tables.
+ */
+typedef struct ret_tidhash {
+ struct ret_tidhash *rth_next;
+ tidhash_t *rth_tidhash;
+ uint_t rth_tidhash_sz;
+} ret_tidhash_t;
+
struct pool;
struct task;
struct zone;
@@ -209,9 +222,10 @@ typedef struct proc {
kthread_t *p_tlist; /* circular list of threads */
lwpdir_t *p_lwpdir; /* thread (lwp) directory */
lwpdir_t *p_lwpfree; /* p_lwpdir free list */
- lwpdir_t **p_tidhash; /* tid (lwpid) lookup hash table */
+ tidhash_t *p_tidhash; /* tid (lwpid) lookup hash table */
uint_t p_lwpdir_sz; /* number of p_lwpdir[] entries */
uint_t p_tidhash_sz; /* number of p_tidhash[] entries */
+ ret_tidhash_t *p_ret_tidhash; /* retired tidhash hash tables */
uint64_t p_lgrpset; /* unprotected hint of set of lgrps */
/* on which process has threads */
volatile lgrp_id_t p_t1_lgrpid; /* main's thread lgroup id */
@@ -714,9 +728,10 @@ extern klwp_t *lwp_create(
int cid,
id_t lwpid);
extern kthread_t *idtot(proc_t *, id_t);
-extern void lwp_hash_in(proc_t *, lwpent_t *);
+extern void lwp_hash_in(proc_t *, lwpent_t *, tidhash_t *, uint_t, int);
extern void lwp_hash_out(proc_t *, id_t);
extern lwpdir_t *lwp_hash_lookup(proc_t *, id_t);
+extern lwpdir_t *lwp_hash_lookup_and_lock(proc_t *, id_t, kmutex_t **);
extern void lwp_create_done(kthread_t *);
extern void lwp_exit(void);
extern void lwp_pcb_exit(void);
diff --git a/usr/src/uts/common/syscall/lwpsys.c b/usr/src/uts/common/syscall/lwpsys.c
index 5e1f378125..40f242c7b4 100644
--- a/usr/src/uts/common/syscall/lwpsys.c
+++ b/usr/src/uts/common/syscall/lwpsys.c
@@ -18,15 +18,14 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/param.h>
#include <sys/types.h>
#include <sys/sysmacros.h>
@@ -54,6 +53,27 @@ idtot(proc_t *p, id_t lwpid)
}
/*
+ * Same as idtot(), but acquire and return
+ * the tid hash table entry lock on success.
+ * This allows lwp_unpark() to do its job without acquiring
+ * p->p_lock (and thereby causing congestion problems when
+ * the application calls lwp_unpark() too often).
+ */
+static kthread_t *
+idtot_and_lock(proc_t *p, id_t lwpid, kmutex_t **mpp)
+{
+ lwpdir_t *ldp;
+ kthread_t *t;
+
+ if ((ldp = lwp_hash_lookup_and_lock(p, lwpid, mpp)) != NULL) {
+ if ((t = ldp->ld_entry->le_thread) == NULL)
+ mutex_exit(*mpp);
+ return (t);
+ }
+ return (NULL);
+}
+
+/*
* Stop an lwp of the current process
*/
int
@@ -373,18 +393,18 @@ lwp_unpark(id_t lwpid)
{
proc_t *p = ttoproc(curthread);
kthread_t *t;
+ kmutex_t *mp;
int error = 0;
- mutex_enter(&p->p_lock);
- if ((t = idtot(p, lwpid)) == NULL)
+ if ((t = idtot_and_lock(p, lwpid, &mp)) == NULL) {
error = ESRCH;
- else {
+ } else {
mutex_enter(&t->t_delay_lock);
t->t_unpark = 1;
cv_signal(&t->t_delay_cv);
mutex_exit(&t->t_delay_lock);
+ mutex_exit(mp);
}
- mutex_exit(&p->p_lock);
return (error);
}
@@ -405,17 +425,17 @@ lwp_unpark_cancel(id_t lwpid)
{
proc_t *p = ttoproc(curthread);
kthread_t *t;
+ kmutex_t *mp;
int error = 0;
- mutex_enter(&p->p_lock);
- if ((t = idtot(p, lwpid)) == NULL) {
+ if ((t = idtot_and_lock(p, lwpid, &mp)) == NULL) {
error = ESRCH;
} else {
mutex_enter(&t->t_delay_lock);
t->t_unpark = 0;
mutex_exit(&t->t_delay_lock);
+ mutex_exit(mp);
}
- mutex_exit(&p->p_lock);
return (error);
}
@@ -528,6 +548,7 @@ lwp_unpark_all(id_t *lwpidp, int nids)
{
proc_t *p = ttoproc(curthread);
kthread_t *t;
+ kmutex_t *mp;
int error = 0;
id_t *lwpid;
size_t lwpidsz;
@@ -545,18 +566,17 @@ lwp_unpark_all(id_t *lwpidp, int nids)
error = EFAULT;
break;
}
- mutex_enter(&p->p_lock);
for (i = 0; i < n; i++) {
- if ((t = idtot(p, lwpid[i])) == NULL)
+ if ((t = idtot_and_lock(p, lwpid[i], &mp)) == NULL) {
error = ESRCH;
- else {
+ } else {
mutex_enter(&t->t_delay_lock);
t->t_unpark = 1;
cv_signal(&t->t_delay_cv);
mutex_exit(&t->t_delay_lock);
+ mutex_exit(mp);
}
}
- mutex_exit(&p->p_lock);
lwpidp += n;
nids -= n;
}