xref: /titanic_51/usr/src/uts/common/syscall/lwpsys.c (revision 47eb4d1eb88936d972e042f5583dcbec45d121a9)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*47eb4d1eSsl108498  * Common Development and Distribution License (the "License").
6*47eb4d1eSsl108498  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*47eb4d1eSsl108498  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <sys/param.h>
317c478bd9Sstevel@tonic-gate #include <sys/types.h>
327c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
337c478bd9Sstevel@tonic-gate #include <sys/systm.h>
347c478bd9Sstevel@tonic-gate #include <sys/prsystm.h>
357c478bd9Sstevel@tonic-gate #include <sys/cred.h>
367c478bd9Sstevel@tonic-gate #include <sys/errno.h>
377c478bd9Sstevel@tonic-gate #include <sys/proc.h>
387c478bd9Sstevel@tonic-gate #include <sys/signal.h>
397c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
407c478bd9Sstevel@tonic-gate #include <sys/unistd.h>
417c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
427c478bd9Sstevel@tonic-gate #include <sys/schedctl.h>
437c478bd9Sstevel@tonic-gate #include <sys/debug.h>
447c478bd9Sstevel@tonic-gate #include <sys/contract/process_impl.h>
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate kthread_t *
477c478bd9Sstevel@tonic-gate idtot(proc_t *p, id_t lwpid)
487c478bd9Sstevel@tonic-gate {
497c478bd9Sstevel@tonic-gate 	lwpdir_t *ldp;
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate 	if ((ldp = lwp_hash_lookup(p, lwpid)) != NULL)
527c478bd9Sstevel@tonic-gate 		return (ldp->ld_entry->le_thread);
537c478bd9Sstevel@tonic-gate 	return (NULL);
547c478bd9Sstevel@tonic-gate }
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /*
577c478bd9Sstevel@tonic-gate  * Stop an lwp of the current process
587c478bd9Sstevel@tonic-gate  */
597c478bd9Sstevel@tonic-gate int
607c478bd9Sstevel@tonic-gate syslwp_suspend(id_t lwpid)
617c478bd9Sstevel@tonic-gate {
627c478bd9Sstevel@tonic-gate 	kthread_t *t;
637c478bd9Sstevel@tonic-gate 	int error;
647c478bd9Sstevel@tonic-gate 	proc_t *p = ttoproc(curthread);
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
677c478bd9Sstevel@tonic-gate 	if ((t = idtot(p, lwpid)) == NULL)
687c478bd9Sstevel@tonic-gate 		error = ESRCH;
697c478bd9Sstevel@tonic-gate 	else
707c478bd9Sstevel@tonic-gate 		error = lwp_suspend(t);
717c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
727c478bd9Sstevel@tonic-gate 	if (error)
737c478bd9Sstevel@tonic-gate 		return (set_errno(error));
747c478bd9Sstevel@tonic-gate 	return (0);
757c478bd9Sstevel@tonic-gate }
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate int
787c478bd9Sstevel@tonic-gate syslwp_continue(id_t lwpid)
797c478bd9Sstevel@tonic-gate {
807c478bd9Sstevel@tonic-gate 	kthread_t *t;
817c478bd9Sstevel@tonic-gate 	proc_t *p = ttoproc(curthread);
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
847c478bd9Sstevel@tonic-gate 	if ((t = idtot(p, lwpid)) == NULL) {
857c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
867c478bd9Sstevel@tonic-gate 		return (set_errno(ESRCH));
877c478bd9Sstevel@tonic-gate 	}
887c478bd9Sstevel@tonic-gate 	lwp_continue(t);
897c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
907c478bd9Sstevel@tonic-gate 	return (0);
917c478bd9Sstevel@tonic-gate }
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate int
947c478bd9Sstevel@tonic-gate lwp_kill(id_t lwpid, int sig)
957c478bd9Sstevel@tonic-gate {
967c478bd9Sstevel@tonic-gate 	sigqueue_t *sqp;
977c478bd9Sstevel@tonic-gate 	kthread_t *t;
987c478bd9Sstevel@tonic-gate 	proc_t *p = ttoproc(curthread);
997c478bd9Sstevel@tonic-gate 
1007c478bd9Sstevel@tonic-gate 	if (sig < 0 || sig >= NSIG)
1017c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
1027c478bd9Sstevel@tonic-gate 	if (sig != 0)
1037c478bd9Sstevel@tonic-gate 		sqp = kmem_zalloc(sizeof (sigqueue_t), KM_SLEEP);
1047c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
1057c478bd9Sstevel@tonic-gate 	if ((t = idtot(p, lwpid)) == NULL) {
1067c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
1077c478bd9Sstevel@tonic-gate 		if (sig != 0)
1087c478bd9Sstevel@tonic-gate 			kmem_free(sqp, sizeof (sigqueue_t));
1097c478bd9Sstevel@tonic-gate 		return (set_errno(ESRCH));
1107c478bd9Sstevel@tonic-gate 	}
1117c478bd9Sstevel@tonic-gate 	if (sig == 0) {
1127c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
1137c478bd9Sstevel@tonic-gate 		return (0);
1147c478bd9Sstevel@tonic-gate 	}
1157c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_signo = sig;
1167c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_code = SI_LWP;
1177c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_pid = p->p_pid;
1187c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_ctid = PRCTID(p);
1197c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_zoneid = getzoneid();
1207c478bd9Sstevel@tonic-gate 	sqp->sq_info.si_uid = crgetruid(CRED());
1217c478bd9Sstevel@tonic-gate 	sigaddqa(p, t, sqp);
1227c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
1237c478bd9Sstevel@tonic-gate 	return (0);
1247c478bd9Sstevel@tonic-gate }
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate /*
1277c478bd9Sstevel@tonic-gate  * This is the specification of lwp_wait() from the _lwp_wait(2) manual page:
1287c478bd9Sstevel@tonic-gate  *
1297c478bd9Sstevel@tonic-gate  * The lwp_wait() function blocks the current lwp until the lwp specified
1307c478bd9Sstevel@tonic-gate  * by 'lwpid' terminates.  If the specified lwp terminated prior to the call
1317c478bd9Sstevel@tonic-gate  * to lwp_wait(), then lwp_wait() returns immediately.  If 'lwpid' is zero,
1327c478bd9Sstevel@tonic-gate  * then lwp_wait() waits for any undetached lwp in the current process.
1337c478bd9Sstevel@tonic-gate  * If 'lwpid' is not zero, then it must specify an undetached lwp in the
1347c478bd9Sstevel@tonic-gate  * current process.  If 'departed' is not NULL, then it points to a location
1357c478bd9Sstevel@tonic-gate  * where the id of the exited lwp is stored.
1367c478bd9Sstevel@tonic-gate  *
1377c478bd9Sstevel@tonic-gate  * When an lwp exits and there are one or more lwps in the process waiting
1387c478bd9Sstevel@tonic-gate  * for this specific lwp to exit, then one of the waiting lwps is unblocked
1397c478bd9Sstevel@tonic-gate  * and it returns from lwp_wait() successfully.  Any other lwps waiting for
1407c478bd9Sstevel@tonic-gate  * this same lwp to exit are also unblocked, however, they return from
1417c478bd9Sstevel@tonic-gate  * lwp_wait() with the error ESRCH.  If there are no lwps in the process
1427c478bd9Sstevel@tonic-gate  * waiting for this specific lwp to exit but there are one or more lwps
1437c478bd9Sstevel@tonic-gate  * waiting for any lwp to exit, then one of the waiting lwps is unblocked
1447c478bd9Sstevel@tonic-gate  * and it returns from lwp_wait() successfully.
1457c478bd9Sstevel@tonic-gate  *
1467c478bd9Sstevel@tonic-gate  * If an lwp is waiting for any lwp to exit, it blocks until an undetached
1477c478bd9Sstevel@tonic-gate  * lwp for which no other lwp is waiting terminates, at which time it returns
1487c478bd9Sstevel@tonic-gate  * successfully, or until all other lwps in the process are either daemon
1497c478bd9Sstevel@tonic-gate  * lwps or lwps waiting in lwp_wait(), in which case it returns EDEADLK.
1507c478bd9Sstevel@tonic-gate  */
1517c478bd9Sstevel@tonic-gate int
1527c478bd9Sstevel@tonic-gate lwp_wait(id_t lwpid, id_t *departed)
1537c478bd9Sstevel@tonic-gate {
1547c478bd9Sstevel@tonic-gate 	proc_t *p = ttoproc(curthread);
1557c478bd9Sstevel@tonic-gate 	int error = 0;
1567c478bd9Sstevel@tonic-gate 	int daemon = (curthread->t_proc_flag & TP_DAEMON)? 1 : 0;
1577c478bd9Sstevel@tonic-gate 	lwpent_t *target_lep;
1587c478bd9Sstevel@tonic-gate 	lwpdir_t *ldp;
1597c478bd9Sstevel@tonic-gate 	lwpent_t *lep;
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	/*
1627c478bd9Sstevel@tonic-gate 	 * lwp_wait() is not supported for the /proc agent lwp.
1637c478bd9Sstevel@tonic-gate 	 */
1647c478bd9Sstevel@tonic-gate 	if (curthread == p->p_agenttp)
1657c478bd9Sstevel@tonic-gate 		return (set_errno(ENOTSUP));
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
1687c478bd9Sstevel@tonic-gate 	prbarrier(p);
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	curthread->t_waitfor = lwpid;
1717c478bd9Sstevel@tonic-gate 	p->p_lwpwait++;
1727c478bd9Sstevel@tonic-gate 	p->p_lwpdwait += daemon;
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	if (lwpid != 0) {
1757c478bd9Sstevel@tonic-gate 		if ((ldp = lwp_hash_lookup(p, lwpid)) == NULL)
1767c478bd9Sstevel@tonic-gate 			target_lep = NULL;
1777c478bd9Sstevel@tonic-gate 		else {
1787c478bd9Sstevel@tonic-gate 			target_lep = ldp->ld_entry;
1797c478bd9Sstevel@tonic-gate 			target_lep->le_waiters++;
1807c478bd9Sstevel@tonic-gate 			target_lep->le_dwaiters += daemon;
1817c478bd9Sstevel@tonic-gate 		}
1827c478bd9Sstevel@tonic-gate 	}
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate 	while (error == 0) {
1857c478bd9Sstevel@tonic-gate 		kthread_t *t;
1867c478bd9Sstevel@tonic-gate 		id_t tid;
1877c478bd9Sstevel@tonic-gate 		int i;
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 		if (lwpid != 0) {
1907c478bd9Sstevel@tonic-gate 			/*
1917c478bd9Sstevel@tonic-gate 			 * Look for a specific zombie lwp.
1927c478bd9Sstevel@tonic-gate 			 */
1937c478bd9Sstevel@tonic-gate 			if (target_lep == NULL)
1947c478bd9Sstevel@tonic-gate 				error = ESRCH;
1957c478bd9Sstevel@tonic-gate 			else if ((t = target_lep->le_thread) != NULL) {
1967c478bd9Sstevel@tonic-gate 				if (!(t->t_proc_flag & TP_TWAIT))
1977c478bd9Sstevel@tonic-gate 					error = EINVAL;
1987c478bd9Sstevel@tonic-gate 			} else {
1997c478bd9Sstevel@tonic-gate 				/*
2007c478bd9Sstevel@tonic-gate 				 * We found the zombie we are waiting for.
2017c478bd9Sstevel@tonic-gate 				 */
2027c478bd9Sstevel@tonic-gate 				ASSERT(p->p_zombcnt > 0);
2037c478bd9Sstevel@tonic-gate 				p->p_zombcnt--;
2047c478bd9Sstevel@tonic-gate 				p->p_lwpwait--;
2057c478bd9Sstevel@tonic-gate 				p->p_lwpdwait -= daemon;
2067c478bd9Sstevel@tonic-gate 				curthread->t_waitfor = -1;
2077c478bd9Sstevel@tonic-gate 				lwp_hash_out(p, lwpid);
2087c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_lock);
2097c478bd9Sstevel@tonic-gate 				if (departed != NULL &&
2107c478bd9Sstevel@tonic-gate 				    copyout(&lwpid, departed, sizeof (id_t)))
2117c478bd9Sstevel@tonic-gate 					return (set_errno(EFAULT));
2127c478bd9Sstevel@tonic-gate 				return (0);
2137c478bd9Sstevel@tonic-gate 			}
2147c478bd9Sstevel@tonic-gate 		} else {
2157c478bd9Sstevel@tonic-gate 			/*
2167c478bd9Sstevel@tonic-gate 			 * Look for any zombie lwp.
2177c478bd9Sstevel@tonic-gate 			 */
2187c478bd9Sstevel@tonic-gate 			int some_non_daemon_will_return = 0;
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 			/* for each entry in the lwp directory... */
2217c478bd9Sstevel@tonic-gate 			ldp = p->p_lwpdir;
2227c478bd9Sstevel@tonic-gate 			for (i = 0; i < p->p_lwpdir_sz; i++, ldp++) {
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 				if ((lep = ldp->ld_entry) == NULL ||
2257c478bd9Sstevel@tonic-gate 				    lep->le_thread != NULL)
2267c478bd9Sstevel@tonic-gate 					continue;
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 				/*
2297c478bd9Sstevel@tonic-gate 				 * We found a zombie lwp.  If there is some
2307c478bd9Sstevel@tonic-gate 				 * other thread waiting specifically for the
2317c478bd9Sstevel@tonic-gate 				 * zombie we just found, then defer to the other
2327c478bd9Sstevel@tonic-gate 				 * waiting thread and continue searching for
2337c478bd9Sstevel@tonic-gate 				 * another zombie.  Also check to see if there
2347c478bd9Sstevel@tonic-gate 				 * is some non-daemon thread sleeping here in
2357c478bd9Sstevel@tonic-gate 				 * lwp_wait() that will succeed and return when
2367c478bd9Sstevel@tonic-gate 				 * we drop p->p_lock.  This is tested below.
2377c478bd9Sstevel@tonic-gate 				 */
2387c478bd9Sstevel@tonic-gate 				tid = lep->le_lwpid;
2397c478bd9Sstevel@tonic-gate 				if (lep->le_waiters != 0) {
2407c478bd9Sstevel@tonic-gate 					if (lep->le_waiters - lep->le_dwaiters)
2417c478bd9Sstevel@tonic-gate 						some_non_daemon_will_return = 1;
2427c478bd9Sstevel@tonic-gate 					continue;
2437c478bd9Sstevel@tonic-gate 				}
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 				/*
2467c478bd9Sstevel@tonic-gate 				 * We found a zombie that no one else
2477c478bd9Sstevel@tonic-gate 				 * is specifically waiting for.
2487c478bd9Sstevel@tonic-gate 				 */
2497c478bd9Sstevel@tonic-gate 				ASSERT(p->p_zombcnt > 0);
2507c478bd9Sstevel@tonic-gate 				p->p_zombcnt--;
2517c478bd9Sstevel@tonic-gate 				p->p_lwpwait--;
2527c478bd9Sstevel@tonic-gate 				p->p_lwpdwait -= daemon;
2537c478bd9Sstevel@tonic-gate 				curthread->t_waitfor = -1;
2547c478bd9Sstevel@tonic-gate 				lwp_hash_out(p, tid);
2557c478bd9Sstevel@tonic-gate 				mutex_exit(&p->p_lock);
2567c478bd9Sstevel@tonic-gate 				if (departed != NULL &&
2577c478bd9Sstevel@tonic-gate 				    copyout(&tid, departed, sizeof (id_t)))
2587c478bd9Sstevel@tonic-gate 					return (set_errno(EFAULT));
2597c478bd9Sstevel@tonic-gate 				return (0);
2607c478bd9Sstevel@tonic-gate 			}
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 			/*
2637c478bd9Sstevel@tonic-gate 			 * We are waiting for anyone.  If all non-daemon lwps
2647c478bd9Sstevel@tonic-gate 			 * are waiting here, and if we determined above that
2657c478bd9Sstevel@tonic-gate 			 * no non-daemon lwp will return, we have deadlock.
2667c478bd9Sstevel@tonic-gate 			 */
2677c478bd9Sstevel@tonic-gate 			if (!some_non_daemon_will_return &&
2687c478bd9Sstevel@tonic-gate 			    p->p_lwpcnt == p->p_lwpdaemon +
2697c478bd9Sstevel@tonic-gate 			    (p->p_lwpwait - p->p_lwpdwait))
2707c478bd9Sstevel@tonic-gate 				error = EDEADLK;
2717c478bd9Sstevel@tonic-gate 		}
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 		if (error == 0 && lwpid != 0) {
2747c478bd9Sstevel@tonic-gate 			/*
2757c478bd9Sstevel@tonic-gate 			 * We are waiting for a specific non-zombie lwp.
2767c478bd9Sstevel@tonic-gate 			 * Fail if there is a deadlock loop.
2777c478bd9Sstevel@tonic-gate 			 */
2787c478bd9Sstevel@tonic-gate 			for (;;) {
2797c478bd9Sstevel@tonic-gate 				if (t == curthread) {
2807c478bd9Sstevel@tonic-gate 					error = EDEADLK;
2817c478bd9Sstevel@tonic-gate 					break;
2827c478bd9Sstevel@tonic-gate 				}
2837c478bd9Sstevel@tonic-gate 				/* who is he waiting for? */
2847c478bd9Sstevel@tonic-gate 				if ((tid = t->t_waitfor) == -1)
2857c478bd9Sstevel@tonic-gate 					break;
2867c478bd9Sstevel@tonic-gate 				if (tid == 0) {
2877c478bd9Sstevel@tonic-gate 					/*
2887c478bd9Sstevel@tonic-gate 					 * The lwp we are waiting for is
2897c478bd9Sstevel@tonic-gate 					 * waiting for anyone (transitively).
2907c478bd9Sstevel@tonic-gate 					 * If there are no zombies right now
2917c478bd9Sstevel@tonic-gate 					 * and if we would have deadlock due
2927c478bd9Sstevel@tonic-gate 					 * to all non-daemon lwps waiting here,
2937c478bd9Sstevel@tonic-gate 					 * wake up the lwp that is waiting for
2947c478bd9Sstevel@tonic-gate 					 * anyone so it can return EDEADLK.
2957c478bd9Sstevel@tonic-gate 					 */
2967c478bd9Sstevel@tonic-gate 					if (p->p_zombcnt == 0 &&
2977c478bd9Sstevel@tonic-gate 					    p->p_lwpcnt == p->p_lwpdaemon +
2987c478bd9Sstevel@tonic-gate 					    p->p_lwpwait - p->p_lwpdwait)
2997c478bd9Sstevel@tonic-gate 						cv_broadcast(&p->p_lwpexit);
3007c478bd9Sstevel@tonic-gate 					break;
3017c478bd9Sstevel@tonic-gate 				}
3027c478bd9Sstevel@tonic-gate 				if ((ldp = lwp_hash_lookup(p, tid)) == NULL ||
3037c478bd9Sstevel@tonic-gate 				    (t = ldp->ld_entry->le_thread) == NULL)
3047c478bd9Sstevel@tonic-gate 					break;
3057c478bd9Sstevel@tonic-gate 			}
3067c478bd9Sstevel@tonic-gate 		}
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate 		if (error)
3097c478bd9Sstevel@tonic-gate 			break;
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 		/*
3127c478bd9Sstevel@tonic-gate 		 * Wait for some lwp to terminate.
3137c478bd9Sstevel@tonic-gate 		 */
3147c478bd9Sstevel@tonic-gate 		if (!cv_wait_sig(&p->p_lwpexit, &p->p_lock))
3157c478bd9Sstevel@tonic-gate 			error = EINTR;
3167c478bd9Sstevel@tonic-gate 		prbarrier(p);
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 		if (lwpid != 0) {
3197c478bd9Sstevel@tonic-gate 			if ((ldp = lwp_hash_lookup(p, lwpid)) == NULL)
3207c478bd9Sstevel@tonic-gate 				target_lep = NULL;
3217c478bd9Sstevel@tonic-gate 			else
3227c478bd9Sstevel@tonic-gate 				target_lep = ldp->ld_entry;
3237c478bd9Sstevel@tonic-gate 		}
3247c478bd9Sstevel@tonic-gate 	}
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	if (lwpid != 0 && target_lep != NULL) {
3277c478bd9Sstevel@tonic-gate 		target_lep->le_waiters--;
3287c478bd9Sstevel@tonic-gate 		target_lep->le_dwaiters -= daemon;
3297c478bd9Sstevel@tonic-gate 	}
3307c478bd9Sstevel@tonic-gate 	p->p_lwpwait--;
3317c478bd9Sstevel@tonic-gate 	p->p_lwpdwait -= daemon;
3327c478bd9Sstevel@tonic-gate 	curthread->t_waitfor = -1;
3337c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
3347c478bd9Sstevel@tonic-gate 	return (set_errno(error));
3357c478bd9Sstevel@tonic-gate }
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate int
3387c478bd9Sstevel@tonic-gate lwp_detach(id_t lwpid)
3397c478bd9Sstevel@tonic-gate {
3407c478bd9Sstevel@tonic-gate 	kthread_t *t;
3417c478bd9Sstevel@tonic-gate 	proc_t *p = ttoproc(curthread);
3427c478bd9Sstevel@tonic-gate 	lwpdir_t *ldp;
3437c478bd9Sstevel@tonic-gate 	int error = 0;
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
3467c478bd9Sstevel@tonic-gate 	prbarrier(p);
3477c478bd9Sstevel@tonic-gate 	if ((ldp = lwp_hash_lookup(p, lwpid)) == NULL)
3487c478bd9Sstevel@tonic-gate 		error = ESRCH;
3497c478bd9Sstevel@tonic-gate 	else if ((t = ldp->ld_entry->le_thread) != NULL) {
3507c478bd9Sstevel@tonic-gate 		if (!(t->t_proc_flag & TP_TWAIT))
3517c478bd9Sstevel@tonic-gate 			error = EINVAL;
3527c478bd9Sstevel@tonic-gate 		else {
3537c478bd9Sstevel@tonic-gate 			t->t_proc_flag &= ~TP_TWAIT;
3547c478bd9Sstevel@tonic-gate 			cv_broadcast(&p->p_lwpexit);
3557c478bd9Sstevel@tonic-gate 		}
3567c478bd9Sstevel@tonic-gate 	} else {
3577c478bd9Sstevel@tonic-gate 		ASSERT(p->p_zombcnt > 0);
3587c478bd9Sstevel@tonic-gate 		p->p_zombcnt--;
3597c478bd9Sstevel@tonic-gate 		lwp_hash_out(p, lwpid);
3607c478bd9Sstevel@tonic-gate 	}
3617c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	if (error)
3647c478bd9Sstevel@tonic-gate 		return (set_errno(error));
3657c478bd9Sstevel@tonic-gate 	return (0);
3667c478bd9Sstevel@tonic-gate }
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate /*
3697c478bd9Sstevel@tonic-gate  * Unpark the specified lwp.
3707c478bd9Sstevel@tonic-gate  */
3717c478bd9Sstevel@tonic-gate static int
3727c478bd9Sstevel@tonic-gate lwp_unpark(id_t lwpid)
3737c478bd9Sstevel@tonic-gate {
3747c478bd9Sstevel@tonic-gate 	proc_t *p = ttoproc(curthread);
3757c478bd9Sstevel@tonic-gate 	kthread_t *t;
3767c478bd9Sstevel@tonic-gate 	int error = 0;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	mutex_enter(&p->p_lock);
3797c478bd9Sstevel@tonic-gate 	if ((t = idtot(p, lwpid)) == NULL)
3807c478bd9Sstevel@tonic-gate 		error = ESRCH;
3817c478bd9Sstevel@tonic-gate 	else {
3827c478bd9Sstevel@tonic-gate 		mutex_enter(&t->t_delay_lock);
3837c478bd9Sstevel@tonic-gate 		t->t_unpark = 1;
3847c478bd9Sstevel@tonic-gate 		cv_signal(&t->t_delay_cv);
3857c478bd9Sstevel@tonic-gate 		mutex_exit(&t->t_delay_lock);
3867c478bd9Sstevel@tonic-gate 	}
3877c478bd9Sstevel@tonic-gate 	mutex_exit(&p->p_lock);
3887c478bd9Sstevel@tonic-gate 	return (error);
3897c478bd9Sstevel@tonic-gate }
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate /*
392*47eb4d1eSsl108498  * Cancel a previous unpark for the specified lwp.
393*47eb4d1eSsl108498  *
394*47eb4d1eSsl108498  * This interface exists ONLY to support older versions of libthread, which
395*47eb4d1eSsl108498  * called lwp_unpark(self) to force calls to lwp_park(self) to return
396*47eb4d1eSsl108498  * immediately.  These older libthreads required a mechanism to cancel the
397*47eb4d1eSsl108498  * lwp_unpark(self).
398*47eb4d1eSsl108498  *
399*47eb4d1eSsl108498  * libc does not call this interface.  Instead, the sc_park flag in the
400*47eb4d1eSsl108498  * schedctl page is cleared to force calls to lwp_park() to return
401*47eb4d1eSsl108498  * immediately.
402*47eb4d1eSsl108498  */
403*47eb4d1eSsl108498 static int
404*47eb4d1eSsl108498 lwp_unpark_cancel(id_t lwpid)
405*47eb4d1eSsl108498 {
406*47eb4d1eSsl108498 	proc_t *p = ttoproc(curthread);
407*47eb4d1eSsl108498 	kthread_t *t;
408*47eb4d1eSsl108498 	int error = 0;
409*47eb4d1eSsl108498 
410*47eb4d1eSsl108498 	mutex_enter(&p->p_lock);
411*47eb4d1eSsl108498 	if ((t = idtot(p, lwpid)) == NULL) {
412*47eb4d1eSsl108498 		error = ESRCH;
413*47eb4d1eSsl108498 	} else {
414*47eb4d1eSsl108498 		mutex_enter(&t->t_delay_lock);
415*47eb4d1eSsl108498 		t->t_unpark = 0;
416*47eb4d1eSsl108498 		mutex_exit(&t->t_delay_lock);
417*47eb4d1eSsl108498 	}
418*47eb4d1eSsl108498 	mutex_exit(&p->p_lock);
419*47eb4d1eSsl108498 	return (error);
420*47eb4d1eSsl108498 }
421*47eb4d1eSsl108498 
422*47eb4d1eSsl108498 /*
4237c478bd9Sstevel@tonic-gate  * Sleep until we are set running by lwp_unpark() or until we are
4247c478bd9Sstevel@tonic-gate  * interrupted by a signal or until we exhaust our timeout.
4257c478bd9Sstevel@tonic-gate  * timeoutp is an in/out parameter.  On entry, it contains the relative
4267c478bd9Sstevel@tonic-gate  * time until timeout.  On exit, we copyout the residual time left to it.
4277c478bd9Sstevel@tonic-gate  */
4287c478bd9Sstevel@tonic-gate static int
4297c478bd9Sstevel@tonic-gate lwp_park(timespec_t *timeoutp, id_t lwpid)
4307c478bd9Sstevel@tonic-gate {
4317c478bd9Sstevel@tonic-gate 	timespec_t rqtime;
4327c478bd9Sstevel@tonic-gate 	timespec_t rmtime;
4337c478bd9Sstevel@tonic-gate 	timespec_t now;
4347c478bd9Sstevel@tonic-gate 	timespec_t *rqtp = NULL;
4357c478bd9Sstevel@tonic-gate 	kthread_t *t = curthread;
4363348528fSdm120769 	int timecheck = 0;
4377c478bd9Sstevel@tonic-gate 	int error = 0;
4387c478bd9Sstevel@tonic-gate 	model_t datamodel = ttoproc(t)->p_model;
4397c478bd9Sstevel@tonic-gate 
4407c478bd9Sstevel@tonic-gate 	if (lwpid != 0)		/* unpark the other lwp, if any */
4417c478bd9Sstevel@tonic-gate 		(void) lwp_unpark(lwpid);
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	if (timeoutp) {
4443348528fSdm120769 		timecheck = timechanged;
4457c478bd9Sstevel@tonic-gate 		gethrestime(&now);
4467c478bd9Sstevel@tonic-gate 		if (datamodel == DATAMODEL_NATIVE) {
4477c478bd9Sstevel@tonic-gate 			if (copyin(timeoutp, &rqtime, sizeof (timespec_t))) {
4487c478bd9Sstevel@tonic-gate 				error = EFAULT;
4497c478bd9Sstevel@tonic-gate 				goto out;
4507c478bd9Sstevel@tonic-gate 			}
4517c478bd9Sstevel@tonic-gate 		} else {
4527c478bd9Sstevel@tonic-gate 			timespec32_t timeout32;
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 			if (copyin(timeoutp, &timeout32, sizeof (timeout32))) {
4557c478bd9Sstevel@tonic-gate 				error = EFAULT;
4567c478bd9Sstevel@tonic-gate 				goto out;
4577c478bd9Sstevel@tonic-gate 			}
4587c478bd9Sstevel@tonic-gate 			TIMESPEC32_TO_TIMESPEC(&rqtime, &timeout32)
4597c478bd9Sstevel@tonic-gate 		}
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 		if (itimerspecfix(&rqtime)) {
4627c478bd9Sstevel@tonic-gate 			error = EINVAL;
4637c478bd9Sstevel@tonic-gate 			goto out;
4647c478bd9Sstevel@tonic-gate 		}
4657c478bd9Sstevel@tonic-gate 		/*
4667c478bd9Sstevel@tonic-gate 		 * Convert the timespec value into absolute time.
4677c478bd9Sstevel@tonic-gate 		 */
4687c478bd9Sstevel@tonic-gate 		timespecadd(&rqtime, &now);
4697c478bd9Sstevel@tonic-gate 		rqtp = &rqtime;
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	(void) new_mstate(t, LMS_USER_LOCK);
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate 	mutex_enter(&t->t_delay_lock);
4757c478bd9Sstevel@tonic-gate 	if (!schedctl_is_park())
4767c478bd9Sstevel@tonic-gate 		error = EINTR;
4777c478bd9Sstevel@tonic-gate 	while (error == 0 && t->t_unpark == 0) {
4787c478bd9Sstevel@tonic-gate 		switch (cv_waituntil_sig(&t->t_delay_cv,
4793348528fSdm120769 		    &t->t_delay_lock, rqtp, timecheck)) {
4807c478bd9Sstevel@tonic-gate 		case 0:
4817c478bd9Sstevel@tonic-gate 			error = EINTR;
4827c478bd9Sstevel@tonic-gate 			break;
4837c478bd9Sstevel@tonic-gate 		case -1:
4847c478bd9Sstevel@tonic-gate 			error = ETIME;
4857c478bd9Sstevel@tonic-gate 			break;
4867c478bd9Sstevel@tonic-gate 		}
4877c478bd9Sstevel@tonic-gate 	}
4887c478bd9Sstevel@tonic-gate 	t->t_unpark = 0;
4897c478bd9Sstevel@tonic-gate 	mutex_exit(&t->t_delay_lock);
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	if (timeoutp != NULL) {
4927c478bd9Sstevel@tonic-gate 		rmtime.tv_sec = rmtime.tv_nsec = 0;
4937c478bd9Sstevel@tonic-gate 		if (error != ETIME) {
4947c478bd9Sstevel@tonic-gate 			gethrestime(&now);
4957c478bd9Sstevel@tonic-gate 			if ((now.tv_sec < rqtime.tv_sec) ||
4967c478bd9Sstevel@tonic-gate 			    ((now.tv_sec == rqtime.tv_sec) &&
4977c478bd9Sstevel@tonic-gate 			    (now.tv_nsec < rqtime.tv_nsec))) {
4987c478bd9Sstevel@tonic-gate 				rmtime = rqtime;
4997c478bd9Sstevel@tonic-gate 				timespecsub(&rmtime, &now);
5007c478bd9Sstevel@tonic-gate 			}
5017c478bd9Sstevel@tonic-gate 		}
5027c478bd9Sstevel@tonic-gate 		if (datamodel == DATAMODEL_NATIVE) {
5037c478bd9Sstevel@tonic-gate 			if (copyout(&rmtime, timeoutp, sizeof (rmtime)))
5047c478bd9Sstevel@tonic-gate 				error = EFAULT;
5057c478bd9Sstevel@tonic-gate 		} else {
5067c478bd9Sstevel@tonic-gate 			timespec32_t rmtime32;
5077c478bd9Sstevel@tonic-gate 
5087c478bd9Sstevel@tonic-gate 			TIMESPEC_TO_TIMESPEC32(&rmtime32, &rmtime);
5097c478bd9Sstevel@tonic-gate 			if (copyout(&rmtime32, timeoutp, sizeof (rmtime32)))
5107c478bd9Sstevel@tonic-gate 				error = EFAULT;
5117c478bd9Sstevel@tonic-gate 		}
5127c478bd9Sstevel@tonic-gate 	}
5137c478bd9Sstevel@tonic-gate out:
5147c478bd9Sstevel@tonic-gate 	schedctl_unpark();
5157c478bd9Sstevel@tonic-gate 	if (t->t_mstate == LMS_USER_LOCK)
5167c478bd9Sstevel@tonic-gate 		(void) new_mstate(t, LMS_SYSTEM);
5177c478bd9Sstevel@tonic-gate 	return (error);
5187c478bd9Sstevel@tonic-gate }
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate #define	MAXLWPIDS	1024
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate /*
5237c478bd9Sstevel@tonic-gate  * Unpark all of the specified lwps.
5247c478bd9Sstevel@tonic-gate  * Do it in chunks of MAXLWPIDS to avoid allocating too much memory.
5257c478bd9Sstevel@tonic-gate  */
5267c478bd9Sstevel@tonic-gate static int
5277c478bd9Sstevel@tonic-gate lwp_unpark_all(id_t *lwpidp, int nids)
5287c478bd9Sstevel@tonic-gate {
5297c478bd9Sstevel@tonic-gate 	proc_t *p = ttoproc(curthread);
5307c478bd9Sstevel@tonic-gate 	kthread_t *t;
5317c478bd9Sstevel@tonic-gate 	int error = 0;
5327c478bd9Sstevel@tonic-gate 	id_t *lwpid;
5337c478bd9Sstevel@tonic-gate 	size_t lwpidsz;
5347c478bd9Sstevel@tonic-gate 	int n;
5357c478bd9Sstevel@tonic-gate 	int i;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	if (nids <= 0)
5387c478bd9Sstevel@tonic-gate 		return (EINVAL);
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate 	lwpidsz = MIN(nids, MAXLWPIDS) * sizeof (id_t);
5417c478bd9Sstevel@tonic-gate 	lwpid = kmem_alloc(lwpidsz, KM_SLEEP);
5427c478bd9Sstevel@tonic-gate 	while (nids > 0) {
5437c478bd9Sstevel@tonic-gate 		n = MIN(nids, MAXLWPIDS);
5447c478bd9Sstevel@tonic-gate 		if (copyin(lwpidp, lwpid, n * sizeof (id_t))) {
5457c478bd9Sstevel@tonic-gate 			error = EFAULT;
5467c478bd9Sstevel@tonic-gate 			break;
5477c478bd9Sstevel@tonic-gate 		}
5487c478bd9Sstevel@tonic-gate 		mutex_enter(&p->p_lock);
5497c478bd9Sstevel@tonic-gate 		for (i = 0; i < n; i++) {
5507c478bd9Sstevel@tonic-gate 			if ((t = idtot(p, lwpid[i])) == NULL)
5517c478bd9Sstevel@tonic-gate 				error = ESRCH;
5527c478bd9Sstevel@tonic-gate 			else {
5537c478bd9Sstevel@tonic-gate 				mutex_enter(&t->t_delay_lock);
5547c478bd9Sstevel@tonic-gate 				t->t_unpark = 1;
5557c478bd9Sstevel@tonic-gate 				cv_signal(&t->t_delay_cv);
5567c478bd9Sstevel@tonic-gate 				mutex_exit(&t->t_delay_lock);
5577c478bd9Sstevel@tonic-gate 			}
5587c478bd9Sstevel@tonic-gate 		}
5597c478bd9Sstevel@tonic-gate 		mutex_exit(&p->p_lock);
5607c478bd9Sstevel@tonic-gate 		lwpidp += n;
5617c478bd9Sstevel@tonic-gate 		nids -= n;
5627c478bd9Sstevel@tonic-gate 	}
5637c478bd9Sstevel@tonic-gate 	kmem_free(lwpid, lwpidsz);
5647c478bd9Sstevel@tonic-gate 	return (error);
5657c478bd9Sstevel@tonic-gate }
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate /*
5687c478bd9Sstevel@tonic-gate  * SYS_lwp_park() system call.
5697c478bd9Sstevel@tonic-gate  */
5707c478bd9Sstevel@tonic-gate int
5717c478bd9Sstevel@tonic-gate syslwp_park(int which, uintptr_t arg1, uintptr_t arg2)
5727c478bd9Sstevel@tonic-gate {
5737c478bd9Sstevel@tonic-gate 	int error;
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate 	switch (which) {
5767c478bd9Sstevel@tonic-gate 	case 0:
5777c478bd9Sstevel@tonic-gate 		error = lwp_park((timespec_t *)arg1, (id_t)arg2);
5787c478bd9Sstevel@tonic-gate 		break;
5797c478bd9Sstevel@tonic-gate 	case 1:
5807c478bd9Sstevel@tonic-gate 		error = lwp_unpark((id_t)arg1);
5817c478bd9Sstevel@tonic-gate 		break;
5827c478bd9Sstevel@tonic-gate 	case 2:
5837c478bd9Sstevel@tonic-gate 		error = lwp_unpark_all((id_t *)arg1, (int)arg2);
5847c478bd9Sstevel@tonic-gate 		break;
585*47eb4d1eSsl108498 	case 3:
586*47eb4d1eSsl108498 		/*
587*47eb4d1eSsl108498 		 * This subcode is not used by libc.  It exists ONLY to
588*47eb4d1eSsl108498 		 * support older versions of libthread which do not use
589*47eb4d1eSsl108498 		 * the sc_park flag in the schedctl page.
590*47eb4d1eSsl108498 		 *
591*47eb4d1eSsl108498 		 * These versions of libthread need to be modifed or emulated
592*47eb4d1eSsl108498 		 * to change calls to syslwp_park(1, tid, 0) to
593*47eb4d1eSsl108498 		 * syslwp_park(3, tid).
594*47eb4d1eSsl108498 		 */
595*47eb4d1eSsl108498 		error = lwp_unpark_cancel((id_t)arg1);
596*47eb4d1eSsl108498 		break;
597*47eb4d1eSsl108498 	case 4:
598*47eb4d1eSsl108498 		/*
599*47eb4d1eSsl108498 		 * This subcode is not used by libc.  It exists ONLY to
600*47eb4d1eSsl108498 		 * support older versions of libthread which do not use
601*47eb4d1eSsl108498 		 * the sc_park flag in the schedctl page.
602*47eb4d1eSsl108498 		 *
603*47eb4d1eSsl108498 		 * These versions of libthread need to be modified or emulated
604*47eb4d1eSsl108498 		 * to change calls to syslwp_park(0, ts, tid) to
605*47eb4d1eSsl108498 		 * syslwp_park(4, ts, tid).
606*47eb4d1eSsl108498 		 */
607*47eb4d1eSsl108498 		schedctl_set_park();
608*47eb4d1eSsl108498 		error = lwp_park((timespec_t *)arg1, (id_t)arg2);
609*47eb4d1eSsl108498 		break;
6107c478bd9Sstevel@tonic-gate 	default:
6117c478bd9Sstevel@tonic-gate 		error = EINVAL;
6127c478bd9Sstevel@tonic-gate 		break;
6137c478bd9Sstevel@tonic-gate 	}
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	if (error)
6167c478bd9Sstevel@tonic-gate 		return (set_errno(error));
6177c478bd9Sstevel@tonic-gate 	return (0);
6187c478bd9Sstevel@tonic-gate }
619