2005-01-06 23:35:40 +00:00
|
|
|
/*-
|
2017-11-27 15:20:12 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
|
|
*
|
2002-06-29 07:04:59 +00:00
|
|
|
* Copyright (C) 2001 Julian Elischer <julian@freebsd.org>.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice(s), this list of conditions and the following disclaimer as
|
2004-01-10 18:34:01 +00:00
|
|
|
* the first lines of this file unmodified other than the possible
|
2002-06-29 07:04:59 +00:00
|
|
|
* addition of one or more copyright notices.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice(s), this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
|
|
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
|
|
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
|
|
* DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2008-08-13 18:24:22 +00:00
|
|
|
#include "opt_witness.h"
|
2009-10-25 04:34:47 +00:00
|
|
|
#include "opt_hwpmc_hooks.h"
|
2008-08-13 18:24:22 +00:00
|
|
|
|
2003-06-11 00:56:59 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/lock.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <sys/proc.h>
|
2020-11-09 23:05:28 +00:00
|
|
|
#include <sys/bitstring.h>
|
2018-11-14 00:33:03 +00:00
|
|
|
#include <sys/epoch.h>
|
2012-05-30 16:06:38 +00:00
|
|
|
#include <sys/rangelock.h>
|
2006-03-14 04:00:21 +00:00
|
|
|
#include <sys/resourcevar.h>
|
2012-05-15 01:30:25 +00:00
|
|
|
#include <sys/sdt.h>
|
2004-06-11 17:48:20 +00:00
|
|
|
#include <sys/smp.h>
|
2002-11-21 01:22:38 +00:00
|
|
|
#include <sys/sched.h>
|
Switch the sleep/wakeup and condition variable implementations to use the
sleep queue interface:
- Sleep queues attempt to merge some of the benefits of both sleep queues
and condition variables. Having sleep qeueus in a hash table avoids
having to allocate a queue head for each wait channel. Thus, struct cv
has shrunk down to just a single char * pointer now. However, the
hash table does not hold threads directly, but queue heads. This means
that once you have located a queue in the hash bucket, you no longer have
to walk the rest of the hash chain looking for threads. Instead, you have
a list of all the threads sleeping on that wait channel.
- Outside of the sleepq code and the sleep/cv code the kernel no longer
differentiates between cv's and sleep/wakeup. For example, calls to
abortsleep() and cv_abort() are replaced with a call to sleepq_abort().
Thus, the TDF_CVWAITQ flag is removed. Also, calls to unsleep() and
cv_waitq_remove() have been replaced with calls to sleepq_remove().
- The sched_sleep() function no longer accepts a priority argument as
sleep's no longer inherently bump the priority. Instead, this is soley
a propery of msleep() which explicitly calls sched_prio() before
blocking.
- The TDF_ONSLEEPQ flag has been dropped as it was never used. The
associated TDF_SET_ONSLEEPQ and TDF_CLR_ON_SLEEPQ macros have also been
dropped and replaced with a single explicit clearing of td_wchan.
TD_SET_ONSLEEPQ() would really have only made sense if it had taken
the wait channel and message as arguments anyway. Now that that only
happens in one place, a macro would be overkill.
2004-02-27 18:52:44 +00:00
|
|
|
#include <sys/sleepqueue.h>
|
2007-12-16 06:21:20 +00:00
|
|
|
#include <sys/selinfo.h>
|
2015-12-29 23:16:20 +00:00
|
|
|
#include <sys/syscallsubr.h>
|
2020-11-23 18:27:21 +00:00
|
|
|
#include <sys/dtrace_bsd.h>
|
2015-05-24 14:51:29 +00:00
|
|
|
#include <sys/sysent.h>
|
Add an implementation of turnstiles and change the sleep mutex code to use
turnstiles to implement blocking isntead of implementing a thread queue
directly. These turnstiles are somewhat similar to those used in Solaris 7
as described in Solaris Internals but are also different.
Turnstiles do not come out of a fixed-sized pool. Rather, each thread is
assigned a turnstile when it is created that it frees when it is destroyed.
When a thread blocks on a lock, it donates its turnstile to that lock to
serve as queue of blocked threads. The queue associated with a given lock
is found by a lookup in a simple hash table. The turnstile itself is
protected by a lock associated with its entry in the hash table. This
means that sched_lock is no longer needed to contest on a mutex. Instead,
sched_lock is only used when manipulating run queues or thread priorities.
Turnstiles also implement priority propagation inherently.
Currently turnstiles only support mutexes. Eventually, however, turnstiles
may grow two queue's to support a non-sleepable reader/writer lock
implementation. For more details, see the comments in sys/turnstile.h and
kern/subr_turnstile.c.
The two primary advantages from the turnstile code include: 1) the size
of struct mutex shrinks by four pointers as it no longer stores the
thread queue linkages directly, and 2) less contention on sched_lock in
SMP systems including the ability for multiple CPUs to contend on different
locks simultaneously (not that this last detail is necessarily that much of
a big win). Note that 1) means that this commit is a kernel ABI breaker,
so don't mix old modules with a new kernel and vice versa.
Tested on: i386 SMP, sparc64 SMP, alpha SMP
2003-11-11 22:07:29 +00:00
|
|
|
#include <sys/turnstile.h>
|
2020-11-19 10:00:48 +00:00
|
|
|
#include <sys/taskqueue.h>
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <sys/ktr.h>
|
2010-10-09 02:50:23 +00:00
|
|
|
#include <sys/rwlock.h>
|
2005-03-05 09:15:03 +00:00
|
|
|
#include <sys/umtx.h>
|
2017-04-17 17:07:00 +00:00
|
|
|
#include <sys/vmmeter.h>
|
Add cpuset, an api for thread to cpu binding and cpu resource grouping
and assignment.
- Add a reference to a struct cpuset in each thread that is inherited from
the thread that created it.
- Release the reference when the thread is destroyed.
- Add prototypes for syscalls and macros for manipulating cpusets in
sys/cpuset.h
- Add syscalls to create, get, and set new numbered cpusets:
cpuset(), cpuset_{get,set}id()
- Add syscalls for getting and setting affinity masks for cpusets or
individual threads: cpuid_{get,set}affinity()
- Add types for the 'level' and 'which' parameters for the cpuset. This
will permit expansion of the api to cover cpu masks for other objects
identifiable with an id_t integer. For example, IRQs and Jails may be
coming soon.
- The root set 0 contains all valid cpus. All thread initially belong to
cpuset 1. This permits migrating all threads off of certain cpus to
reserve them for special applications.
Sponsored by: Nokia
Discussed with: arch, rwatson, brooks, davidxu, deischen
Reviewed by: antoine
2008-03-02 07:39:22 +00:00
|
|
|
#include <sys/cpuset.h>
|
2009-10-25 04:34:47 +00:00
|
|
|
#ifdef HWPMC_HOOKS
|
|
|
|
#include <sys/pmckern.h>
|
|
|
|
#endif
|
2020-11-09 23:04:30 +00:00
|
|
|
#include <sys/priv.h>
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2006-02-02 00:37:05 +00:00
|
|
|
#include <security/audit/audit.h>
|
|
|
|
|
2020-11-19 10:00:48 +00:00
|
|
|
#include <vm/pmap.h>
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <vm/vm.h>
|
2003-06-14 23:23:55 +00:00
|
|
|
#include <vm/vm_extern.h>
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <vm/uma.h>
|
2020-11-19 10:00:48 +00:00
|
|
|
#include <vm/vm_phys.h>
|
2007-11-15 14:20:07 +00:00
|
|
|
#include <sys/eventhandler.h>
|
2002-07-17 23:43:55 +00:00
|
|
|
|
2017-04-27 21:24:50 +00:00
|
|
|
/*
|
|
|
|
* Asserts below verify the stability of struct thread and struct proc
|
|
|
|
* layout, as exposed by KBI to modules. On head, the KBI is allowed
|
|
|
|
* to drift, change to the structures must be accompanied by the
|
|
|
|
* assert update.
|
|
|
|
*
|
|
|
|
* On the stable branches after KBI freeze, conditions must not be
|
|
|
|
* violated. Typically new fields are moved to the end of the
|
|
|
|
* structures.
|
|
|
|
*/
|
|
|
|
#ifdef __amd64__
|
2018-01-12 22:48:23 +00:00
|
|
|
_Static_assert(offsetof(struct thread, td_flags) == 0xfc,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct thread KBI td_flags");
|
2018-01-12 22:48:23 +00:00
|
|
|
_Static_assert(offsetof(struct thread, td_pflags) == 0x104,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct thread KBI td_pflags");
|
2020-09-27 18:47:06 +00:00
|
|
|
_Static_assert(offsetof(struct thread, td_frame) == 0x4a0,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct thread KBI td_frame");
|
2020-06-09 23:03:48 +00:00
|
|
|
_Static_assert(offsetof(struct thread, td_emuldata) == 0x6b0,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct thread KBI td_emuldata");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_flag) == 0xb8,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_flag");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_pid) == 0xc4,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_pid");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_filemon) == 0x3c0,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_filemon");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_comm) == 0x3d8,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_comm");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_emuldata) == 0x4b8,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_emuldata");
|
|
|
|
#endif
|
|
|
|
#ifdef __i386__
|
2018-01-12 22:48:23 +00:00
|
|
|
_Static_assert(offsetof(struct thread, td_flags) == 0x98,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct thread KBI td_flags");
|
2018-01-12 22:48:23 +00:00
|
|
|
_Static_assert(offsetof(struct thread, td_pflags) == 0xa0,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct thread KBI td_pflags");
|
2020-09-27 18:47:06 +00:00
|
|
|
_Static_assert(offsetof(struct thread, td_frame) == 0x300,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct thread KBI td_frame");
|
2020-09-27 18:47:06 +00:00
|
|
|
_Static_assert(offsetof(struct thread, td_emuldata) == 0x344,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct thread KBI td_emuldata");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_flag) == 0x6c,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_flag");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_pid) == 0x78,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_pid");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_filemon) == 0x26c,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_filemon");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_comm) == 0x280,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_comm");
|
2020-11-17 21:14:13 +00:00
|
|
|
_Static_assert(offsetof(struct proc, p_emuldata) == 0x30c,
|
2017-04-27 21:24:50 +00:00
|
|
|
"struct proc KBI p_emuldata");
|
|
|
|
#endif
|
|
|
|
|
2012-05-15 01:30:25 +00:00
|
|
|
SDT_PROVIDER_DECLARE(proc);
|
2013-11-26 08:46:27 +00:00
|
|
|
SDT_PROBE_DEFINE(proc, , , lwp__exit);
|
2012-05-15 01:30:25 +00:00
|
|
|
|
2006-10-26 21:42:22 +00:00
|
|
|
/*
|
|
|
|
* thread related storage.
|
|
|
|
*/
|
2002-06-29 07:04:59 +00:00
|
|
|
static uma_zone_t thread_zone;
|
|
|
|
|
2020-11-19 10:00:48 +00:00
|
|
|
struct thread_domain_data {
|
|
|
|
struct thread *tdd_zombies;
|
|
|
|
int tdd_reapticks;
|
|
|
|
} __aligned(CACHE_LINE_SIZE);
|
|
|
|
|
|
|
|
static struct thread_domain_data thread_domain_data[MAXMEMDOM];
|
|
|
|
|
|
|
|
static struct task thread_reap_task;
|
|
|
|
static struct callout thread_reap_callout;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2007-06-12 07:24:46 +00:00
|
|
|
static void thread_zombie(struct thread *);
|
2020-11-26 06:59:27 +00:00
|
|
|
static void thread_reap(void);
|
2020-11-19 10:00:48 +00:00
|
|
|
static void thread_reap_all(void);
|
|
|
|
static void thread_reap_task_cb(void *, int);
|
|
|
|
static void thread_reap_callout_cb(void *);
|
2015-05-15 07:54:31 +00:00
|
|
|
static int thread_unsuspend_one(struct thread *td, struct proc *p,
|
|
|
|
bool boundary);
|
2020-11-11 18:45:06 +00:00
|
|
|
static void thread_free_batched(struct thread *td);
|
2007-06-12 07:24:46 +00:00
|
|
|
|
2020-11-14 19:19:27 +00:00
|
|
|
static __exclusive_cache_line struct mtx tid_lock;
|
2020-11-10 01:13:58 +00:00
|
|
|
static bitstr_t *tid_bitmap;
|
2020-11-09 23:05:28 +00:00
|
|
|
|
2010-10-09 02:50:23 +00:00
|
|
|
static MALLOC_DEFINE(M_TIDHASH, "tidhash", "thread hash");
|
|
|
|
|
2020-11-09 23:04:30 +00:00
|
|
|
static int maxthread;
|
|
|
|
SYSCTL_INT(_kern, OID_AUTO, maxthread, CTLFLAG_RDTUN,
|
|
|
|
&maxthread, 0, "Maximum number of threads");
|
|
|
|
|
2020-11-12 00:29:23 +00:00
|
|
|
static __exclusive_cache_line int nthreads;
|
2020-11-09 23:04:30 +00:00
|
|
|
|
2020-11-11 08:50:04 +00:00
|
|
|
static LIST_HEAD(tidhashhead, thread) *tidhashtbl;
|
|
|
|
static u_long tidhash;
|
2020-11-11 08:51:04 +00:00
|
|
|
static u_long tidhashlock;
|
|
|
|
static struct rwlock *tidhashtbl_lock;
|
|
|
|
#define TIDHASH(tid) (&tidhashtbl[(tid) & tidhash])
|
|
|
|
#define TIDHASHLOCK(tid) (&tidhashtbl_lock[(tid) & tidhashlock])
|
2010-10-09 02:50:23 +00:00
|
|
|
|
2017-11-09 22:51:48 +00:00
|
|
|
EVENTHANDLER_LIST_DEFINE(thread_ctor);
|
|
|
|
EVENTHANDLER_LIST_DEFINE(thread_dtor);
|
|
|
|
EVENTHANDLER_LIST_DEFINE(thread_init);
|
|
|
|
EVENTHANDLER_LIST_DEFINE(thread_fini);
|
|
|
|
|
2020-11-12 00:29:23 +00:00
|
|
|
static bool
|
2020-11-19 10:00:48 +00:00
|
|
|
thread_count_inc_try(void)
|
2010-12-09 05:16:20 +00:00
|
|
|
{
|
2020-11-12 00:29:23 +00:00
|
|
|
int nthreads_new;
|
2020-11-09 23:04:30 +00:00
|
|
|
|
2020-11-12 00:29:23 +00:00
|
|
|
nthreads_new = atomic_fetchadd_int(&nthreads, 1) + 1;
|
|
|
|
if (nthreads_new >= maxthread - 100) {
|
2020-11-09 23:04:30 +00:00
|
|
|
if (priv_check_cred(curthread->td_ucred, PRIV_MAXPROC) != 0 ||
|
2020-11-12 00:29:23 +00:00
|
|
|
nthreads_new >= maxthread) {
|
|
|
|
atomic_subtract_int(&nthreads, 1);
|
|
|
|
return (false);
|
2020-11-09 23:04:30 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-12 00:29:23 +00:00
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
2020-11-19 10:00:48 +00:00
|
|
|
static bool
|
|
|
|
thread_count_inc(void)
|
|
|
|
{
|
|
|
|
static struct timeval lastfail;
|
|
|
|
static int curfail;
|
|
|
|
|
|
|
|
thread_reap();
|
|
|
|
if (thread_count_inc_try()) {
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
thread_reap_all();
|
|
|
|
if (thread_count_inc_try()) {
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ppsratecheck(&lastfail, &curfail, 1)) {
|
|
|
|
printf("maxthread limit exceeded by uid %u "
|
|
|
|
"(pid %d); consider increasing kern.maxthread\n",
|
|
|
|
curthread->td_ucred->cr_ruid, curproc->p_pid);
|
|
|
|
}
|
|
|
|
return (false);
|
|
|
|
}
|
|
|
|
|
2020-11-12 00:29:23 +00:00
|
|
|
static void
|
|
|
|
thread_count_sub(int n)
|
|
|
|
{
|
|
|
|
|
|
|
|
atomic_subtract_int(&nthreads, n);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
thread_count_dec(void)
|
|
|
|
{
|
2010-12-09 05:16:20 +00:00
|
|
|
|
2020-11-12 00:29:23 +00:00
|
|
|
thread_count_sub(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static lwpid_t
|
|
|
|
tid_alloc(void)
|
|
|
|
{
|
|
|
|
static lwpid_t trytid;
|
|
|
|
lwpid_t tid;
|
|
|
|
|
|
|
|
mtx_lock(&tid_lock);
|
2020-11-09 23:05:28 +00:00
|
|
|
/*
|
|
|
|
* It is an invariant that the bitmap is big enough to hold maxthread
|
|
|
|
* IDs. If we got to this point there has to be at least one free.
|
|
|
|
*/
|
|
|
|
if (trytid >= maxthread)
|
|
|
|
trytid = 0;
|
|
|
|
bit_ffc_at(tid_bitmap, trytid, maxthread, &tid);
|
|
|
|
if (tid == -1) {
|
|
|
|
KASSERT(trytid != 0, ("unexpectedly ran out of IDs"));
|
|
|
|
trytid = 0;
|
|
|
|
bit_ffc_at(tid_bitmap, trytid, maxthread, &tid);
|
|
|
|
KASSERT(tid != -1, ("unexpectedly ran out of IDs"));
|
2010-12-09 05:16:20 +00:00
|
|
|
}
|
2020-11-09 23:05:28 +00:00
|
|
|
bit_set(tid_bitmap, tid);
|
2020-11-10 01:13:58 +00:00
|
|
|
trytid = tid + 1;
|
2010-12-09 05:16:20 +00:00
|
|
|
mtx_unlock(&tid_lock);
|
2020-11-09 23:05:28 +00:00
|
|
|
return (tid + NO_PID);
|
2010-12-09 05:16:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-11-11 18:45:06 +00:00
|
|
|
tid_free_locked(lwpid_t rtid)
|
2010-12-09 05:16:20 +00:00
|
|
|
{
|
2020-11-09 23:05:28 +00:00
|
|
|
lwpid_t tid;
|
2010-12-09 05:16:20 +00:00
|
|
|
|
2020-11-11 18:45:06 +00:00
|
|
|
mtx_assert(&tid_lock, MA_OWNED);
|
2020-11-09 23:05:28 +00:00
|
|
|
KASSERT(rtid >= NO_PID,
|
|
|
|
("%s: invalid tid %d\n", __func__, rtid));
|
|
|
|
tid = rtid - NO_PID;
|
|
|
|
KASSERT(bit_test(tid_bitmap, tid) != 0,
|
|
|
|
("thread ID %d not allocated\n", rtid));
|
|
|
|
bit_clear(tid_bitmap, tid);
|
2020-11-11 18:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
tid_free(lwpid_t rtid)
|
|
|
|
{
|
|
|
|
|
|
|
|
mtx_lock(&tid_lock);
|
|
|
|
tid_free_locked(rtid);
|
|
|
|
mtx_unlock(&tid_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
tid_free_batch(lwpid_t *batch, int n)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
mtx_lock(&tid_lock);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
tid_free_locked(batch[i]);
|
|
|
|
}
|
2010-12-09 05:16:20 +00:00
|
|
|
mtx_unlock(&tid_lock);
|
|
|
|
}
|
|
|
|
|
2020-11-14 19:20:58 +00:00
|
|
|
/*
|
|
|
|
* Batching for thread reapping.
|
|
|
|
*/
|
|
|
|
struct tidbatch {
|
|
|
|
lwpid_t tab[16];
|
|
|
|
int n;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
tidbatch_prep(struct tidbatch *tb)
|
|
|
|
{
|
|
|
|
|
|
|
|
tb->n = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
tidbatch_add(struct tidbatch *tb, struct thread *td)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(tb->n < nitems(tb->tab),
|
|
|
|
("%s: count too high %d", __func__, tb->n));
|
|
|
|
tb->tab[tb->n] = td->td_tid;
|
|
|
|
tb->n++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
tidbatch_process(struct tidbatch *tb)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(tb->n <= nitems(tb->tab),
|
|
|
|
("%s: count too high %d", __func__, tb->n));
|
|
|
|
if (tb->n == nitems(tb->tab)) {
|
|
|
|
tid_free_batch(tb->tab, tb->n);
|
|
|
|
tb->n = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
tidbatch_final(struct tidbatch *tb)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(tb->n <= nitems(tb->tab),
|
|
|
|
("%s: count too high %d", __func__, tb->n));
|
|
|
|
if (tb->n != 0) {
|
|
|
|
tid_free_batch(tb->tab, tb->n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
2002-12-10 02:33:45 +00:00
|
|
|
* Prepare a thread for use.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2004-08-02 00:18:36 +00:00
|
|
|
static int
|
|
|
|
thread_ctor(void *mem, int size, void *arg, int flags)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
|
|
|
|
td = (struct thread *)mem;
|
2021-02-18 10:25:10 +00:00
|
|
|
TD_SET_STATE(td, TDS_INACTIVE);
|
2018-12-11 02:54:36 +00:00
|
|
|
td->td_lastcpu = td->td_oncpu = NOCPU;
|
2004-06-09 14:06:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Note that td_critnest begins life as 1 because the thread is not
|
|
|
|
* running and is thereby implicitly waiting to be on the receiving
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
* end of a context switch.
|
2004-06-09 14:06:44 +00:00
|
|
|
*/
|
2003-08-04 20:28:20 +00:00
|
|
|
td->td_critnest = 1;
|
2010-12-09 02:42:02 +00:00
|
|
|
td->td_lend_user_pri = PRI_MAX;
|
2006-02-02 00:37:05 +00:00
|
|
|
#ifdef AUDIT
|
|
|
|
audit_thread_alloc(td);
|
2020-11-23 18:27:21 +00:00
|
|
|
#endif
|
|
|
|
#ifdef KDTRACE_HOOKS
|
|
|
|
kdtrace_thread_ctor(td);
|
2006-02-02 00:37:05 +00:00
|
|
|
#endif
|
This is initial version of POSIX priority mutex support, a new userland
mutex structure is added as following:
struct umutex {
__lwpid_t m_owner;
uint32_t m_flags;
uint32_t m_ceilings[2];
uint32_t m_spare[4];
};
The m_owner represents owner thread, it is a thread id, in non-contested
case, userland can simply use atomic_cmpset_int to lock the mutex, if the
mutex is contested, high order bit will be set, and userland should do locking
and unlocking via kernel syscall. Flag UMUTEX_PRIO_INHERIT represents
pthread's PTHREAD_PRIO_INHERIT mutex, which when contention happens, kernel
should do priority propagating. Flag UMUTEX_PRIO_PROTECT indicates it is
pthread's PTHREAD_PRIO_PROTECT mutex, userland should initialize m_owner
to contested state UMUTEX_CONTESTED, then atomic_cmpset_int will be failure
and kernel syscall should be invoked to do locking, this becauses
for such a mutex, kernel should always boost the thread's priority before
it can lock the mutex, m_ceilings is used by PTHREAD_PRIO_PROTECT mutex,
the first element is used to boost thread's priority when it locked the mutex,
second element is used when the mutex is unlocked, the PTHREAD_PRIO_PROTECT
mutex's link list is kept in userland, the m_ceiling[1] is managed by thread
library so kernel needn't allocate memory to keep the link list, when such
a mutex is unlocked, kernel reset m_owner to UMUTEX_CONTESTED.
Flag USYNC_PROCESS_SHARED indicate if the synchronization object is process
shared, if the flag is not set, it saves a vm_map_lookup() call.
The umtx chain is still used as a sleep queue, when a thread is blocked on
PTHREAD_PRIO_INHERIT mutex, a umtx_pi is allocated to support priority
propagating, it is dynamically allocated and reference count is used,
it is not optimized but works well in my tests, while the umtx chain has
its own locking protocol, the priority propagating protocol are all protected
by sched_lock because priority propagating function is called with sched_lock
held from scheduler.
No visible performance degradation is found which these changes. Some parameter
names in _umtx_op syscall are renamed.
2006-08-28 04:24:51 +00:00
|
|
|
umtx_thread_alloc(td);
|
2020-11-16 03:12:21 +00:00
|
|
|
MPASS(td->td_sel == NULL);
|
2004-08-02 00:18:36 +00:00
|
|
|
return (0);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reclaim a thread after use.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
thread_dtor(void *mem, int size, void *arg)
|
|
|
|
{
|
2004-04-03 15:59:13 +00:00
|
|
|
struct thread *td;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
td = (struct thread *)mem;
|
|
|
|
|
|
|
|
#ifdef INVARIANTS
|
|
|
|
/* Verify that this thread is in a safe state to free. */
|
2021-02-18 10:25:10 +00:00
|
|
|
switch (TD_GET_STATE(td)) {
|
2002-09-11 08:13:56 +00:00
|
|
|
case TDS_INHIBITED:
|
|
|
|
case TDS_RUNNING:
|
|
|
|
case TDS_CAN_RUN:
|
2002-06-29 07:04:59 +00:00
|
|
|
case TDS_RUNQ:
|
|
|
|
/*
|
|
|
|
* We must never unlink a thread that is in one of
|
|
|
|
* these states, because it is currently active.
|
|
|
|
*/
|
|
|
|
panic("bad state for thread unlinking");
|
|
|
|
/* NOTREACHED */
|
2002-09-11 08:13:56 +00:00
|
|
|
case TDS_INACTIVE:
|
2002-06-29 07:04:59 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
panic("bad thread state");
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
#endif
|
2006-02-05 21:06:09 +00:00
|
|
|
#ifdef AUDIT
|
|
|
|
audit_thread_free(td);
|
2020-11-23 18:27:21 +00:00
|
|
|
#endif
|
|
|
|
#ifdef KDTRACE_HOOKS
|
|
|
|
kdtrace_thread_dtor(td);
|
2006-02-05 21:06:09 +00:00
|
|
|
#endif
|
Update ZFS from version 6 to 13 and bring some FreeBSD-specific changes.
This bring huge amount of changes, I'll enumerate only user-visible changes:
- Delegated Administration
Allows regular users to perform ZFS operations, like file system
creation, snapshot creation, etc.
- L2ARC
Level 2 cache for ZFS - allows to use additional disks for cache.
Huge performance improvements mostly for random read of mostly
static content.
- slog
Allow to use additional disks for ZFS Intent Log to speed up
operations like fsync(2).
- vfs.zfs.super_owner
Allows regular users to perform privileged operations on files stored
on ZFS file systems owned by him. Very careful with this one.
- chflags(2)
Not all the flags are supported. This still needs work.
- ZFSBoot
Support to boot off of ZFS pool. Not finished, AFAIK.
Submitted by: dfr
- Snapshot properties
- New failure modes
Before if write requested failed, system paniced. Now one
can select from one of three failure modes:
- panic - panic on write error
- wait - wait for disk to reappear
- continue - serve read requests if possible, block write requests
- Refquota, refreservation properties
Just quota and reservation properties, but don't count space consumed
by children file systems, clones and snapshots.
- Sparse volumes
ZVOLs that don't reserve space in the pool.
- External attributes
Compatible with extattr(2).
- NFSv4-ACLs
Not sure about the status, might not be complete yet.
Submitted by: trasz
- Creation-time properties
- Regression tests for zpool(8) command.
Obtained from: OpenSolaris
2008-11-17 20:49:29 +00:00
|
|
|
/* Free all OSD associated to this thread. */
|
|
|
|
osd_thread_exit(td);
|
2017-02-25 10:38:18 +00:00
|
|
|
td_softdep_cleanup(td);
|
|
|
|
MPASS(td->td_su == NULL);
|
2020-11-16 03:12:21 +00:00
|
|
|
seltdfini(td);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize type-stable parts of a thread (when newly created).
|
|
|
|
*/
|
2004-08-02 00:18:36 +00:00
|
|
|
static int
|
|
|
|
thread_init(void *mem, int size, int flags)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
2004-06-26 18:58:22 +00:00
|
|
|
struct thread *td;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
td = (struct thread *)mem;
|
2004-06-26 18:58:22 +00:00
|
|
|
|
2020-11-26 06:59:27 +00:00
|
|
|
td->td_allocdomain = vm_phys_domain(vtophys(td));
|
Switch the sleep/wakeup and condition variable implementations to use the
sleep queue interface:
- Sleep queues attempt to merge some of the benefits of both sleep queues
and condition variables. Having sleep qeueus in a hash table avoids
having to allocate a queue head for each wait channel. Thus, struct cv
has shrunk down to just a single char * pointer now. However, the
hash table does not hold threads directly, but queue heads. This means
that once you have located a queue in the hash bucket, you no longer have
to walk the rest of the hash chain looking for threads. Instead, you have
a list of all the threads sleeping on that wait channel.
- Outside of the sleepq code and the sleep/cv code the kernel no longer
differentiates between cv's and sleep/wakeup. For example, calls to
abortsleep() and cv_abort() are replaced with a call to sleepq_abort().
Thus, the TDF_CVWAITQ flag is removed. Also, calls to unsleep() and
cv_waitq_remove() have been replaced with calls to sleepq_remove().
- The sched_sleep() function no longer accepts a priority argument as
sleep's no longer inherently bump the priority. Instead, this is soley
a propery of msleep() which explicitly calls sched_prio() before
blocking.
- The TDF_ONSLEEPQ flag has been dropped as it was never used. The
associated TDF_SET_ONSLEEPQ and TDF_CLR_ON_SLEEPQ macros have also been
dropped and replaced with a single explicit clearing of td_wchan.
TD_SET_ONSLEEPQ() would really have only made sense if it had taken
the wait channel and message as arguments anyway. Now that that only
happens in one place, a macro would be overkill.
2004-02-27 18:52:44 +00:00
|
|
|
td->td_sleepqueue = sleepq_alloc();
|
Add an implementation of turnstiles and change the sleep mutex code to use
turnstiles to implement blocking isntead of implementing a thread queue
directly. These turnstiles are somewhat similar to those used in Solaris 7
as described in Solaris Internals but are also different.
Turnstiles do not come out of a fixed-sized pool. Rather, each thread is
assigned a turnstile when it is created that it frees when it is destroyed.
When a thread blocks on a lock, it donates its turnstile to that lock to
serve as queue of blocked threads. The queue associated with a given lock
is found by a lookup in a simple hash table. The turnstile itself is
protected by a lock associated with its entry in the hash table. This
means that sched_lock is no longer needed to contest on a mutex. Instead,
sched_lock is only used when manipulating run queues or thread priorities.
Turnstiles also implement priority propagation inherently.
Currently turnstiles only support mutexes. Eventually, however, turnstiles
may grow two queue's to support a non-sleepable reader/writer lock
implementation. For more details, see the comments in sys/turnstile.h and
kern/subr_turnstile.c.
The two primary advantages from the turnstile code include: 1) the size
of struct mutex shrinks by four pointers as it no longer stores the
thread queue linkages directly, and 2) less contention on sched_lock in
SMP systems including the ability for multiple CPUs to contend on different
locks simultaneously (not that this last detail is necessarily that much of
a big win). Note that 1) means that this commit is a kernel ABI breaker,
so don't mix old modules with a new kernel and vice versa.
Tested on: i386 SMP, sparc64 SMP, alpha SMP
2003-11-11 22:07:29 +00:00
|
|
|
td->td_turnstile = turnstile_alloc();
|
2012-05-30 16:06:38 +00:00
|
|
|
td->td_rlqe = NULL;
|
2017-11-09 22:51:48 +00:00
|
|
|
EVENTHANDLER_DIRECT_INVOKE(thread_init, td);
|
This is initial version of POSIX priority mutex support, a new userland
mutex structure is added as following:
struct umutex {
__lwpid_t m_owner;
uint32_t m_flags;
uint32_t m_ceilings[2];
uint32_t m_spare[4];
};
The m_owner represents owner thread, it is a thread id, in non-contested
case, userland can simply use atomic_cmpset_int to lock the mutex, if the
mutex is contested, high order bit will be set, and userland should do locking
and unlocking via kernel syscall. Flag UMUTEX_PRIO_INHERIT represents
pthread's PTHREAD_PRIO_INHERIT mutex, which when contention happens, kernel
should do priority propagating. Flag UMUTEX_PRIO_PROTECT indicates it is
pthread's PTHREAD_PRIO_PROTECT mutex, userland should initialize m_owner
to contested state UMUTEX_CONTESTED, then atomic_cmpset_int will be failure
and kernel syscall should be invoked to do locking, this becauses
for such a mutex, kernel should always boost the thread's priority before
it can lock the mutex, m_ceilings is used by PTHREAD_PRIO_PROTECT mutex,
the first element is used to boost thread's priority when it locked the mutex,
second element is used when the mutex is unlocked, the PTHREAD_PRIO_PROTECT
mutex's link list is kept in userland, the m_ceiling[1] is managed by thread
library so kernel needn't allocate memory to keep the link list, when such
a mutex is unlocked, kernel reset m_owner to UMUTEX_CONTESTED.
Flag USYNC_PROCESS_SHARED indicate if the synchronization object is process
shared, if the flag is not set, it saves a vm_map_lookup() call.
The umtx chain is still used as a sleep queue, when a thread is blocked on
PTHREAD_PRIO_INHERIT mutex, a umtx_pi is allocated to support priority
propagating, it is dynamically allocated and reference count is used,
it is not optimized but works well in my tests, while the umtx chain has
its own locking protocol, the priority propagating protocol are all protected
by sched_lock because priority propagating function is called with sched_lock
held from scheduler.
No visible performance degradation is found which these changes. Some parameter
names in _umtx_op syscall are renamed.
2006-08-28 04:24:51 +00:00
|
|
|
umtx_thread_init(td);
|
2007-11-05 11:36:16 +00:00
|
|
|
td->td_kstack = 0;
|
2015-04-18 17:21:12 +00:00
|
|
|
td->td_sel = NULL;
|
2004-08-02 00:18:36 +00:00
|
|
|
return (0);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tear down type-stable parts of a thread (just before being discarded).
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
thread_fini(void *mem, int size)
|
|
|
|
{
|
2004-06-26 18:58:22 +00:00
|
|
|
struct thread *td;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
td = (struct thread *)mem;
|
2017-11-09 22:51:48 +00:00
|
|
|
EVENTHANDLER_DIRECT_INVOKE(thread_fini, td);
|
2012-05-30 16:06:38 +00:00
|
|
|
rlqentry_free(td->td_rlqe);
|
Add an implementation of turnstiles and change the sleep mutex code to use
turnstiles to implement blocking isntead of implementing a thread queue
directly. These turnstiles are somewhat similar to those used in Solaris 7
as described in Solaris Internals but are also different.
Turnstiles do not come out of a fixed-sized pool. Rather, each thread is
assigned a turnstile when it is created that it frees when it is destroyed.
When a thread blocks on a lock, it donates its turnstile to that lock to
serve as queue of blocked threads. The queue associated with a given lock
is found by a lookup in a simple hash table. The turnstile itself is
protected by a lock associated with its entry in the hash table. This
means that sched_lock is no longer needed to contest on a mutex. Instead,
sched_lock is only used when manipulating run queues or thread priorities.
Turnstiles also implement priority propagation inherently.
Currently turnstiles only support mutexes. Eventually, however, turnstiles
may grow two queue's to support a non-sleepable reader/writer lock
implementation. For more details, see the comments in sys/turnstile.h and
kern/subr_turnstile.c.
The two primary advantages from the turnstile code include: 1) the size
of struct mutex shrinks by four pointers as it no longer stores the
thread queue linkages directly, and 2) less contention on sched_lock in
SMP systems including the ability for multiple CPUs to contend on different
locks simultaneously (not that this last detail is necessarily that much of
a big win). Note that 1) means that this commit is a kernel ABI breaker,
so don't mix old modules with a new kernel and vice versa.
Tested on: i386 SMP, sparc64 SMP, alpha SMP
2003-11-11 22:07:29 +00:00
|
|
|
turnstile_free(td->td_turnstile);
|
Switch the sleep/wakeup and condition variable implementations to use the
sleep queue interface:
- Sleep queues attempt to merge some of the benefits of both sleep queues
and condition variables. Having sleep qeueus in a hash table avoids
having to allocate a queue head for each wait channel. Thus, struct cv
has shrunk down to just a single char * pointer now. However, the
hash table does not hold threads directly, but queue heads. This means
that once you have located a queue in the hash bucket, you no longer have
to walk the rest of the hash chain looking for threads. Instead, you have
a list of all the threads sleeping on that wait channel.
- Outside of the sleepq code and the sleep/cv code the kernel no longer
differentiates between cv's and sleep/wakeup. For example, calls to
abortsleep() and cv_abort() are replaced with a call to sleepq_abort().
Thus, the TDF_CVWAITQ flag is removed. Also, calls to unsleep() and
cv_waitq_remove() have been replaced with calls to sleepq_remove().
- The sched_sleep() function no longer accepts a priority argument as
sleep's no longer inherently bump the priority. Instead, this is soley
a propery of msleep() which explicitly calls sched_prio() before
blocking.
- The TDF_ONSLEEPQ flag has been dropped as it was never used. The
associated TDF_SET_ONSLEEPQ and TDF_CLR_ON_SLEEPQ macros have also been
dropped and replaced with a single explicit clearing of td_wchan.
TD_SET_ONSLEEPQ() would really have only made sense if it had taken
the wait channel and message as arguments anyway. Now that that only
happens in one place, a macro would be overkill.
2004-02-27 18:52:44 +00:00
|
|
|
sleepq_free(td->td_sleepqueue);
|
This is initial version of POSIX priority mutex support, a new userland
mutex structure is added as following:
struct umutex {
__lwpid_t m_owner;
uint32_t m_flags;
uint32_t m_ceilings[2];
uint32_t m_spare[4];
};
The m_owner represents owner thread, it is a thread id, in non-contested
case, userland can simply use atomic_cmpset_int to lock the mutex, if the
mutex is contested, high order bit will be set, and userland should do locking
and unlocking via kernel syscall. Flag UMUTEX_PRIO_INHERIT represents
pthread's PTHREAD_PRIO_INHERIT mutex, which when contention happens, kernel
should do priority propagating. Flag UMUTEX_PRIO_PROTECT indicates it is
pthread's PTHREAD_PRIO_PROTECT mutex, userland should initialize m_owner
to contested state UMUTEX_CONTESTED, then atomic_cmpset_int will be failure
and kernel syscall should be invoked to do locking, this becauses
for such a mutex, kernel should always boost the thread's priority before
it can lock the mutex, m_ceilings is used by PTHREAD_PRIO_PROTECT mutex,
the first element is used to boost thread's priority when it locked the mutex,
second element is used when the mutex is unlocked, the PTHREAD_PRIO_PROTECT
mutex's link list is kept in userland, the m_ceiling[1] is managed by thread
library so kernel needn't allocate memory to keep the link list, when such
a mutex is unlocked, kernel reset m_owner to UMUTEX_CONTESTED.
Flag USYNC_PROCESS_SHARED indicate if the synchronization object is process
shared, if the flag is not set, it saves a vm_map_lookup() call.
The umtx chain is still used as a sleep queue, when a thread is blocked on
PTHREAD_PRIO_INHERIT mutex, a umtx_pi is allocated to support priority
propagating, it is dynamically allocated and reference count is used,
it is not optimized but works well in my tests, while the umtx chain has
its own locking protocol, the priority propagating protocol are all protected
by sched_lock because priority propagating function is called with sched_lock
held from scheduler.
No visible performance degradation is found which these changes. Some parameter
names in _umtx_op syscall are renamed.
2006-08-28 04:24:51 +00:00
|
|
|
umtx_thread_fini(td);
|
2020-11-16 03:12:21 +00:00
|
|
|
MPASS(td->td_sel == NULL);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
|
2002-10-24 08:46:34 +00:00
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* For a newly created process,
|
|
|
|
* link up all the structures and its initial threads etc.
|
2004-09-05 02:09:54 +00:00
|
|
|
* called from:
|
2014-07-07 00:27:09 +00:00
|
|
|
* {arch}/{arch}/machdep.c {arch}_init(), init386() etc.
|
2004-09-05 02:09:54 +00:00
|
|
|
* proc_dtor() (should go away)
|
|
|
|
* proc_init()
|
2002-10-24 08:46:34 +00:00
|
|
|
*/
|
2007-11-05 11:36:16 +00:00
|
|
|
void
|
|
|
|
proc_linkup0(struct proc *p, struct thread *td)
|
|
|
|
{
|
|
|
|
TAILQ_INIT(&p->p_threads); /* all threads in proc */
|
|
|
|
proc_linkup(p, td);
|
|
|
|
}
|
|
|
|
|
2002-10-24 08:46:34 +00:00
|
|
|
void
|
2006-10-26 21:42:22 +00:00
|
|
|
proc_linkup(struct proc *p, struct thread *td)
|
2002-10-24 08:46:34 +00:00
|
|
|
{
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
|
1. Change prototype of trapsignal and sendsig to use ksiginfo_t *, most
changes in MD code are trivial, before this change, trapsignal and
sendsig use discrete parameters, now they uses member fields of
ksiginfo_t structure. For sendsig, this change allows us to pass
POSIX realtime signal value to user code.
2. Remove cpu_thread_siginfo, it is no longer needed because we now always
generate ksiginfo_t data and feed it to libpthread.
3. Add p_sigqueue to proc structure to hold shared signals which were
blocked by all threads in the proc.
4. Add td_sigqueue to thread structure to hold all signals delivered to
thread.
5. i386 and amd64 now return POSIX standard si_code, other arches will
be fixed.
6. In this sigqueue implementation, pending signal set is kept as before,
an extra siginfo list holds additional siginfo_t data for signals.
kernel code uses psignal() still behavior as before, it won't be failed
even under memory pressure, only exception is when deleting a signal,
we should call sigqueue_delete to remove signal from sigqueue but
not SIGDELSET. Current there is no kernel code will deliver a signal
with additional data, so kernel should be as stable as before,
a ksiginfo can carry more information, for example, allow signal to
be delivered but throw away siginfo data if memory is not enough.
SIGKILL and SIGSTOP have fast path in sigqueue_add, because they can
not be caught or masked.
The sigqueue() syscall allows user code to queue a signal to target
process, if resource is unavailable, EAGAIN will be returned as
specification said.
Just before thread exits, signal queue memory will be freed by
sigqueue_flush.
Current, all signals are allowed to be queued, not only realtime signals.
Earlier patch reviewed by: jhb, deischen
Tested on: i386, amd64
2005-10-14 12:43:47 +00:00
|
|
|
sigqueue_init(&p->p_sigqueue, p);
|
2005-12-09 02:27:55 +00:00
|
|
|
p->p_ksi = ksiginfo_alloc(1);
|
|
|
|
if (p->p_ksi != NULL) {
|
|
|
|
/* XXX p_ksi may be null if ksiginfo zone is not ready */
|
|
|
|
p->p_ksi->ksi_flags = KSI_EXT | KSI_INS;
|
2005-11-08 09:09:26 +00:00
|
|
|
}
|
2005-11-30 05:12:03 +00:00
|
|
|
LIST_INIT(&p->p_mqnotifier);
|
2002-10-24 08:46:34 +00:00
|
|
|
p->p_numthreads = 0;
|
2006-10-26 21:42:22 +00:00
|
|
|
thread_link(td, p);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
2020-11-09 23:04:30 +00:00
|
|
|
extern int max_threads_per_proc;
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Initialize global thread allocation resources.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
threadinit(void)
|
|
|
|
{
|
2020-11-11 08:51:04 +00:00
|
|
|
u_long i;
|
2020-11-11 08:48:43 +00:00
|
|
|
lwpid_t tid0;
|
2020-02-29 18:41:48 +00:00
|
|
|
uint32_t flags;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2020-11-09 23:04:30 +00:00
|
|
|
/*
|
|
|
|
* Place an upper limit on threads which can be allocated.
|
|
|
|
*
|
|
|
|
* Note that other factors may make the de facto limit much lower.
|
|
|
|
*
|
|
|
|
* Platform limits are somewhat arbitrary but deemed "more than good
|
|
|
|
* enough" for the foreseable future.
|
|
|
|
*/
|
|
|
|
if (maxthread == 0) {
|
|
|
|
#ifdef _LP64
|
|
|
|
maxthread = MIN(maxproc * max_threads_per_proc, 1000000);
|
|
|
|
#else
|
|
|
|
maxthread = MIN(maxproc * max_threads_per_proc, 100000);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2005-03-18 12:34:14 +00:00
|
|
|
mtx_init(&tid_lock, "TID lock", NULL, MTX_DEF);
|
2020-11-09 23:05:28 +00:00
|
|
|
tid_bitmap = bit_alloc(maxthread, M_TIDHASH, M_WAITOK);
|
2020-11-12 00:29:23 +00:00
|
|
|
/*
|
|
|
|
* Handle thread0.
|
|
|
|
*/
|
|
|
|
thread_count_inc();
|
2020-11-11 08:48:43 +00:00
|
|
|
tid0 = tid_alloc();
|
|
|
|
if (tid0 != THREAD0_TID)
|
|
|
|
panic("tid0 %d != %d\n", tid0, THREAD0_TID);
|
2005-03-18 12:34:14 +00:00
|
|
|
|
2020-02-29 18:41:48 +00:00
|
|
|
flags = UMA_ZONE_NOFREE;
|
|
|
|
#ifdef __aarch64__
|
|
|
|
/*
|
|
|
|
* Force thread structures to be allocated from the direct map.
|
|
|
|
* Otherwise, superpage promotions and demotions may temporarily
|
|
|
|
* invalidate thread structure mappings. For most dynamically allocated
|
|
|
|
* structures this is not a problem, but translation faults cannot be
|
|
|
|
* handled without accessing curthread.
|
|
|
|
*/
|
|
|
|
flags |= UMA_ZONE_CONTIG;
|
|
|
|
#endif
|
2002-11-21 01:22:38 +00:00
|
|
|
thread_zone = uma_zcreate("THREAD", sched_sizeof_thread(),
|
2002-06-29 07:04:59 +00:00
|
|
|
thread_ctor, thread_dtor, thread_init, thread_fini,
|
2020-02-29 18:41:48 +00:00
|
|
|
32 - 1, flags);
|
2010-10-09 02:50:23 +00:00
|
|
|
tidhashtbl = hashinit(maxproc / 2, M_TIDHASH, &tidhash);
|
2020-11-11 08:51:04 +00:00
|
|
|
tidhashlock = (tidhash + 1) / 64;
|
|
|
|
if (tidhashlock > 0)
|
|
|
|
tidhashlock--;
|
|
|
|
tidhashtbl_lock = malloc(sizeof(*tidhashtbl_lock) * (tidhashlock + 1),
|
|
|
|
M_TIDHASH, M_WAITOK | M_ZERO);
|
|
|
|
for (i = 0; i < tidhashlock + 1; i++)
|
|
|
|
rw_init(&tidhashtbl_lock[i], "tidhash");
|
2020-11-19 10:00:48 +00:00
|
|
|
|
|
|
|
TASK_INIT(&thread_reap_task, 0, thread_reap_task_cb, NULL);
|
|
|
|
callout_init(&thread_reap_callout, 1);
|
|
|
|
callout_reset(&thread_reap_callout, 5 * hz, thread_reap_callout_cb, NULL);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-06-12 07:24:46 +00:00
|
|
|
* Place an unused thread on the zombie list.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
void
|
2007-06-12 07:24:46 +00:00
|
|
|
thread_zombie(struct thread *td)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
2020-11-19 10:00:48 +00:00
|
|
|
struct thread_domain_data *tdd;
|
2020-11-11 18:43:51 +00:00
|
|
|
struct thread *ztd;
|
|
|
|
|
2020-11-23 18:26:47 +00:00
|
|
|
tdd = &thread_domain_data[td->td_allocdomain];
|
2020-11-19 10:00:48 +00:00
|
|
|
ztd = atomic_load_ptr(&tdd->tdd_zombies);
|
2020-11-11 18:43:51 +00:00
|
|
|
for (;;) {
|
|
|
|
td->td_zombie = ztd;
|
2020-11-19 10:00:48 +00:00
|
|
|
if (atomic_fcmpset_rel_ptr((uintptr_t *)&tdd->tdd_zombies,
|
2020-11-11 18:43:51 +00:00
|
|
|
(uintptr_t *)&ztd, (uintptr_t)td))
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
2007-06-12 07:24:46 +00:00
|
|
|
/*
|
|
|
|
* Release a thread that has exited after cpu_throw().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_stash(struct thread *td)
|
|
|
|
{
|
|
|
|
atomic_subtract_rel_int(&td->td_proc->p_exitthreads, 1);
|
|
|
|
thread_zombie(td);
|
|
|
|
}
|
|
|
|
|
2002-09-16 19:26:48 +00:00
|
|
|
/*
|
2020-11-19 10:00:48 +00:00
|
|
|
* Reap zombies from passed domain.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2020-11-19 10:00:48 +00:00
|
|
|
static void
|
|
|
|
thread_reap_domain(struct thread_domain_data *tdd)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
2020-11-11 18:43:51 +00:00
|
|
|
struct thread *itd, *ntd;
|
2020-11-14 19:20:58 +00:00
|
|
|
struct tidbatch tidbatch;
|
2020-11-14 19:22:02 +00:00
|
|
|
struct credbatch credbatch;
|
2020-11-14 19:20:58 +00:00
|
|
|
int tdcount;
|
2020-11-14 19:21:46 +00:00
|
|
|
struct plimit *lim;
|
|
|
|
int limcount;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
/*
|
2020-11-11 18:43:51 +00:00
|
|
|
* Reading upfront is pessimal if followed by concurrent atomic_swap,
|
|
|
|
* but most of the time the list is empty.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2020-11-19 10:00:48 +00:00
|
|
|
if (tdd->tdd_zombies == NULL)
|
2020-11-11 18:43:51 +00:00
|
|
|
return;
|
|
|
|
|
2020-11-19 10:00:48 +00:00
|
|
|
itd = (struct thread *)atomic_swap_ptr((uintptr_t *)&tdd->tdd_zombies,
|
2020-11-11 18:43:51 +00:00
|
|
|
(uintptr_t)NULL);
|
2020-11-14 19:20:58 +00:00
|
|
|
if (itd == NULL)
|
|
|
|
return;
|
|
|
|
|
2020-11-19 10:00:48 +00:00
|
|
|
/*
|
|
|
|
* Multiple CPUs can get here, the race is fine as ticks is only
|
|
|
|
* advisory.
|
|
|
|
*/
|
|
|
|
tdd->tdd_reapticks = ticks;
|
|
|
|
|
2020-11-14 19:20:58 +00:00
|
|
|
tidbatch_prep(&tidbatch);
|
2020-11-14 19:22:02 +00:00
|
|
|
credbatch_prep(&credbatch);
|
2020-11-14 19:20:58 +00:00
|
|
|
tdcount = 0;
|
2020-11-14 19:21:46 +00:00
|
|
|
lim = NULL;
|
|
|
|
limcount = 0;
|
2020-11-19 10:00:48 +00:00
|
|
|
|
2020-11-11 18:43:51 +00:00
|
|
|
while (itd != NULL) {
|
|
|
|
ntd = itd->td_zombie;
|
2020-11-14 19:20:58 +00:00
|
|
|
EVENTHANDLER_DIRECT_INVOKE(thread_dtor, itd);
|
|
|
|
tidbatch_add(&tidbatch, itd);
|
2020-11-14 19:22:02 +00:00
|
|
|
credbatch_add(&credbatch, itd);
|
2020-11-14 19:21:46 +00:00
|
|
|
MPASS(itd->td_limit != NULL);
|
|
|
|
if (lim != itd->td_limit) {
|
|
|
|
if (limcount != 0) {
|
|
|
|
lim_freen(lim, limcount);
|
|
|
|
limcount = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lim = itd->td_limit;
|
|
|
|
limcount++;
|
2020-11-11 18:45:06 +00:00
|
|
|
thread_free_batched(itd);
|
2020-11-14 19:20:58 +00:00
|
|
|
tidbatch_process(&tidbatch);
|
2020-11-14 19:22:02 +00:00
|
|
|
credbatch_process(&credbatch);
|
2020-11-14 19:20:58 +00:00
|
|
|
tdcount++;
|
|
|
|
if (tdcount == 32) {
|
|
|
|
thread_count_sub(tdcount);
|
|
|
|
tdcount = 0;
|
2020-11-11 18:45:06 +00:00
|
|
|
}
|
2020-11-11 18:43:51 +00:00
|
|
|
itd = ntd;
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2020-11-11 18:45:06 +00:00
|
|
|
|
2020-11-14 19:20:58 +00:00
|
|
|
tidbatch_final(&tidbatch);
|
2020-11-14 19:22:02 +00:00
|
|
|
credbatch_final(&credbatch);
|
2020-11-14 19:20:58 +00:00
|
|
|
if (tdcount != 0) {
|
|
|
|
thread_count_sub(tdcount);
|
2020-11-11 18:45:06 +00:00
|
|
|
}
|
2020-11-14 19:21:46 +00:00
|
|
|
MPASS(limcount != 0);
|
|
|
|
lim_freen(lim, limcount);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
2020-11-19 10:00:48 +00:00
|
|
|
/*
|
|
|
|
* Reap zombies from all domains.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
thread_reap_all(void)
|
|
|
|
{
|
|
|
|
struct thread_domain_data *tdd;
|
|
|
|
int i, domain;
|
|
|
|
|
|
|
|
domain = PCPU_GET(domain);
|
|
|
|
for (i = 0; i < vm_ndomains; i++) {
|
|
|
|
tdd = &thread_domain_data[(i + domain) % vm_ndomains];
|
|
|
|
thread_reap_domain(tdd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reap zombies from local domain.
|
|
|
|
*/
|
2020-11-26 06:59:27 +00:00
|
|
|
static void
|
2020-11-19 10:00:48 +00:00
|
|
|
thread_reap(void)
|
|
|
|
{
|
|
|
|
struct thread_domain_data *tdd;
|
|
|
|
int domain;
|
|
|
|
|
|
|
|
domain = PCPU_GET(domain);
|
|
|
|
tdd = &thread_domain_data[domain];
|
|
|
|
|
|
|
|
thread_reap_domain(tdd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
thread_reap_task_cb(void *arg __unused, int pending __unused)
|
|
|
|
{
|
|
|
|
|
|
|
|
thread_reap_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
thread_reap_callout_cb(void *arg __unused)
|
|
|
|
{
|
|
|
|
struct thread_domain_data *tdd;
|
|
|
|
int i, cticks, lticks;
|
|
|
|
bool wantreap;
|
|
|
|
|
|
|
|
wantreap = false;
|
|
|
|
cticks = atomic_load_int(&ticks);
|
|
|
|
for (i = 0; i < vm_ndomains; i++) {
|
|
|
|
tdd = &thread_domain_data[i];
|
|
|
|
lticks = tdd->tdd_reapticks;
|
|
|
|
if (tdd->tdd_zombies != NULL &&
|
|
|
|
(u_int)(cticks - lticks) > 5 * hz) {
|
|
|
|
wantreap = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (wantreap)
|
|
|
|
taskqueue_enqueue(taskqueue_thread, &thread_reap_task);
|
|
|
|
callout_reset(&thread_reap_callout, 5 * hz, thread_reap_callout_cb, NULL);
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Allocate a thread.
|
|
|
|
*/
|
|
|
|
struct thread *
|
2009-09-01 11:41:51 +00:00
|
|
|
thread_alloc(int pages)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
2007-11-05 11:36:16 +00:00
|
|
|
struct thread *td;
|
2020-11-09 23:04:30 +00:00
|
|
|
lwpid_t tid;
|
2006-10-26 21:42:22 +00:00
|
|
|
|
2020-11-12 00:29:23 +00:00
|
|
|
if (!thread_count_inc()) {
|
2020-11-09 23:04:30 +00:00
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
2020-11-12 00:29:23 +00:00
|
|
|
tid = tid_alloc();
|
2020-11-09 23:04:30 +00:00
|
|
|
td = uma_zalloc(thread_zone, M_WAITOK);
|
2007-11-05 11:36:16 +00:00
|
|
|
KASSERT(td->td_kstack == 0, ("thread_alloc got thread with kstack"));
|
2009-09-01 11:41:51 +00:00
|
|
|
if (!vm_thread_new(td, pages)) {
|
2007-11-05 11:36:16 +00:00
|
|
|
uma_zfree(thread_zone, td);
|
2020-11-09 23:04:30 +00:00
|
|
|
tid_free(tid);
|
2020-11-12 00:29:23 +00:00
|
|
|
thread_count_dec();
|
2007-11-05 11:36:16 +00:00
|
|
|
return (NULL);
|
|
|
|
}
|
2020-11-09 23:04:30 +00:00
|
|
|
td->td_tid = tid;
|
2007-11-14 20:21:54 +00:00
|
|
|
cpu_thread_alloc(td);
|
2020-11-09 23:04:30 +00:00
|
|
|
EVENTHANDLER_DIRECT_INVOKE(thread_ctor, td);
|
2007-11-05 11:36:16 +00:00
|
|
|
return (td);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
2009-09-01 11:41:51 +00:00
|
|
|
int
|
|
|
|
thread_alloc_stack(struct thread *td, int pages)
|
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(td->td_kstack == 0,
|
|
|
|
("thread_alloc_stack called on a thread with kstack"));
|
|
|
|
if (!vm_thread_new(td, pages))
|
|
|
|
return (0);
|
|
|
|
cpu_thread_alloc(td);
|
|
|
|
return (1);
|
|
|
|
}
|
2002-09-15 23:52:25 +00:00
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Deallocate a thread.
|
|
|
|
*/
|
2020-11-11 18:45:06 +00:00
|
|
|
static void
|
|
|
|
thread_free_batched(struct thread *td)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
2009-03-15 06:41:47 +00:00
|
|
|
|
|
|
|
lock_profile_thread_exit(td);
|
2008-03-19 06:20:21 +00:00
|
|
|
if (td->td_cpuset)
|
|
|
|
cpuset_rel(td->td_cpuset);
|
Add cpuset, an api for thread to cpu binding and cpu resource grouping
and assignment.
- Add a reference to a struct cpuset in each thread that is inherited from
the thread that created it.
- Release the reference when the thread is destroyed.
- Add prototypes for syscalls and macros for manipulating cpusets in
sys/cpuset.h
- Add syscalls to create, get, and set new numbered cpusets:
cpuset(), cpuset_{get,set}id()
- Add syscalls for getting and setting affinity masks for cpusets or
individual threads: cpuid_{get,set}affinity()
- Add types for the 'level' and 'which' parameters for the cpuset. This
will permit expansion of the api to cover cpu masks for other objects
identifiable with an id_t integer. For example, IRQs and Jails may be
coming soon.
- The root set 0 contains all valid cpus. All thread initially belong to
cpuset 1. This permits migrating all threads off of certain cpus to
reserve them for special applications.
Sponsored by: Nokia
Discussed with: arch, rwatson, brooks, davidxu, deischen
Reviewed by: antoine
2008-03-02 07:39:22 +00:00
|
|
|
td->td_cpuset = NULL;
|
2007-11-14 20:21:54 +00:00
|
|
|
cpu_thread_free(td);
|
2007-11-05 11:36:16 +00:00
|
|
|
if (td->td_kstack != 0)
|
|
|
|
vm_thread_dispose(td);
|
2016-07-28 09:09:55 +00:00
|
|
|
callout_drain(&td->td_slpcallout);
|
2020-11-11 18:45:06 +00:00
|
|
|
/*
|
|
|
|
* Freeing handled by the caller.
|
|
|
|
*/
|
2020-11-09 23:04:30 +00:00
|
|
|
td->td_tid = -1;
|
2002-06-29 07:04:59 +00:00
|
|
|
uma_zfree(thread_zone, td);
|
|
|
|
}
|
|
|
|
|
2020-11-11 18:45:06 +00:00
|
|
|
void
|
|
|
|
thread_free(struct thread *td)
|
|
|
|
{
|
|
|
|
lwpid_t tid;
|
|
|
|
|
2020-11-14 19:20:58 +00:00
|
|
|
EVENTHANDLER_DIRECT_INVOKE(thread_dtor, td);
|
2020-11-11 18:45:06 +00:00
|
|
|
tid = td->td_tid;
|
|
|
|
thread_free_batched(td);
|
|
|
|
tid_free(tid);
|
2020-11-12 00:29:23 +00:00
|
|
|
thread_count_dec();
|
2020-11-11 18:45:06 +00:00
|
|
|
}
|
|
|
|
|
2015-06-10 10:43:59 +00:00
|
|
|
void
|
|
|
|
thread_cow_get_proc(struct thread *newtd, struct proc *p)
|
|
|
|
{
|
|
|
|
|
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2020-06-09 23:03:48 +00:00
|
|
|
newtd->td_realucred = crcowget(p->p_ucred);
|
|
|
|
newtd->td_ucred = newtd->td_realucred;
|
2015-06-10 10:48:12 +00:00
|
|
|
newtd->td_limit = lim_hold(p->p_limit);
|
2015-06-10 10:43:59 +00:00
|
|
|
newtd->td_cowgen = p->p_cowgen;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
thread_cow_get(struct thread *newtd, struct thread *td)
|
|
|
|
{
|
|
|
|
|
2020-06-09 23:03:48 +00:00
|
|
|
MPASS(td->td_realucred == td->td_ucred);
|
|
|
|
newtd->td_realucred = crcowget(td->td_realucred);
|
|
|
|
newtd->td_ucred = newtd->td_realucred;
|
2015-06-10 10:48:12 +00:00
|
|
|
newtd->td_limit = lim_hold(td->td_limit);
|
2015-06-10 10:43:59 +00:00
|
|
|
newtd->td_cowgen = td->td_cowgen;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
thread_cow_free(struct thread *td)
|
|
|
|
{
|
|
|
|
|
2020-06-09 23:03:48 +00:00
|
|
|
if (td->td_realucred != NULL)
|
|
|
|
crcowfree(td);
|
2015-07-16 14:30:11 +00:00
|
|
|
if (td->td_limit != NULL)
|
2015-06-10 10:48:12 +00:00
|
|
|
lim_free(td->td_limit);
|
2015-06-10 10:43:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
thread_cow_update(struct thread *td)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
2015-07-16 14:30:11 +00:00
|
|
|
struct ucred *oldcred;
|
|
|
|
struct plimit *oldlimit;
|
2015-06-10 10:43:59 +00:00
|
|
|
|
|
|
|
p = td->td_proc;
|
2015-07-16 14:30:11 +00:00
|
|
|
oldlimit = NULL;
|
2015-06-10 10:43:59 +00:00
|
|
|
PROC_LOCK(p);
|
2020-06-09 23:03:48 +00:00
|
|
|
oldcred = crcowsync();
|
2015-07-16 14:30:11 +00:00
|
|
|
if (td->td_limit != p->p_limit) {
|
|
|
|
oldlimit = td->td_limit;
|
|
|
|
td->td_limit = lim_hold(p->p_limit);
|
|
|
|
}
|
2015-06-10 10:43:59 +00:00
|
|
|
td->td_cowgen = p->p_cowgen;
|
|
|
|
PROC_UNLOCK(p);
|
2015-07-16 14:30:11 +00:00
|
|
|
if (oldcred != NULL)
|
|
|
|
crfree(oldcred);
|
|
|
|
if (oldlimit != NULL)
|
|
|
|
lim_free(oldlimit);
|
2015-06-10 10:43:59 +00:00
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Discard the current thread and exit from its context.
|
2004-06-11 17:48:20 +00:00
|
|
|
* Always called with scheduler locked.
|
2002-06-29 07:04:59 +00:00
|
|
|
*
|
|
|
|
* Because we can't free a thread while we're operating under its context,
|
2002-12-10 02:33:45 +00:00
|
|
|
* push the current thread into our CPU's deadthread holder. This means
|
|
|
|
* we needn't worry about someone else grabbing our context before we
|
2008-03-12 10:12:01 +00:00
|
|
|
* do a cpu_throw().
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_exit(void)
|
|
|
|
{
|
2012-01-03 21:03:28 +00:00
|
|
|
uint64_t runtime, new_switchtime;
|
2002-06-29 07:04:59 +00:00
|
|
|
struct thread *td;
|
2007-06-01 01:12:45 +00:00
|
|
|
struct thread *td2;
|
2002-06-29 07:04:59 +00:00
|
|
|
struct proc *p;
|
2008-08-22 16:15:58 +00:00
|
|
|
int wakeup_swapper;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
td = curthread;
|
|
|
|
p = td->td_proc;
|
|
|
|
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
PROC_SLOCK_ASSERT(p, MA_OWNED);
|
2004-09-05 02:09:54 +00:00
|
|
|
mtx_assert(&Giant, MA_NOTOWNED);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
|
2004-09-05 02:09:54 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2002-08-29 19:49:53 +00:00
|
|
|
KASSERT(p != NULL, ("thread exiting without a process"));
|
2004-08-06 22:06:14 +00:00
|
|
|
CTR3(KTR_PROC, "thread_exit: thread %p (pid %ld, %s)", td,
|
2007-11-14 06:51:33 +00:00
|
|
|
(long)p->p_pid, td->td_name);
|
2017-03-11 15:47:27 +00:00
|
|
|
SDT_PROBE0(proc, , , lwp__exit);
|
1. Change prototype of trapsignal and sendsig to use ksiginfo_t *, most
changes in MD code are trivial, before this change, trapsignal and
sendsig use discrete parameters, now they uses member fields of
ksiginfo_t structure. For sendsig, this change allows us to pass
POSIX realtime signal value to user code.
2. Remove cpu_thread_siginfo, it is no longer needed because we now always
generate ksiginfo_t data and feed it to libpthread.
3. Add p_sigqueue to proc structure to hold shared signals which were
blocked by all threads in the proc.
4. Add td_sigqueue to thread structure to hold all signals delivered to
thread.
5. i386 and amd64 now return POSIX standard si_code, other arches will
be fixed.
6. In this sigqueue implementation, pending signal set is kept as before,
an extra siginfo list holds additional siginfo_t data for signals.
kernel code uses psignal() still behavior as before, it won't be failed
even under memory pressure, only exception is when deleting a signal,
we should call sigqueue_delete to remove signal from sigqueue but
not SIGDELSET. Current there is no kernel code will deliver a signal
with additional data, so kernel should be as stable as before,
a ksiginfo can carry more information, for example, allow signal to
be delivered but throw away siginfo data if memory is not enough.
SIGKILL and SIGSTOP have fast path in sigqueue_add, because they can
not be caught or masked.
The sigqueue() syscall allows user code to queue a signal to target
process, if resource is unavailable, EAGAIN will be returned as
specification said.
Just before thread exits, signal queue memory will be freed by
sigqueue_flush.
Current, all signals are allowed to be queued, not only realtime signals.
Earlier patch reviewed by: jhb, deischen
Tested on: i386, amd64
2005-10-14 12:43:47 +00:00
|
|
|
KASSERT(TAILQ_EMPTY(&td->td_sigqueue.sq_list), ("signal pending"));
|
2020-08-01 16:02:32 +00:00
|
|
|
MPASS(td->td_realucred == td->td_ucred);
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2004-09-05 02:09:54 +00:00
|
|
|
/*
|
|
|
|
* drop FPU & debug register state storage, or any other
|
|
|
|
* architecture specific resources that
|
|
|
|
* would not be on a new untouched process.
|
|
|
|
*/
|
2016-06-16 12:01:11 +00:00
|
|
|
cpu_thread_exit(td);
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2002-08-29 19:49:53 +00:00
|
|
|
/*
|
2002-09-06 07:00:37 +00:00
|
|
|
* The last thread is left attached to the process
|
|
|
|
* So that the whole bundle gets recycled. Skip
|
2004-09-05 02:09:54 +00:00
|
|
|
* all this stuff if we never had threads.
|
|
|
|
* EXIT clears all sign of other threads when
|
|
|
|
* it goes to single threading, so the last thread always
|
|
|
|
* takes the short path.
|
2002-08-29 19:49:53 +00:00
|
|
|
*/
|
2004-09-05 02:09:54 +00:00
|
|
|
if (p->p_flag & P_HADTHREADS) {
|
|
|
|
if (p->p_numthreads > 1) {
|
2014-09-03 08:18:07 +00:00
|
|
|
atomic_add_int(&td->td_proc->p_exitthreads, 1);
|
2004-09-05 02:09:54 +00:00
|
|
|
thread_unlink(td);
|
2007-06-01 01:12:45 +00:00
|
|
|
td2 = FIRST_THREAD_IN_PROC(p);
|
|
|
|
sched_exit_thread(td2, td);
|
2004-09-05 02:09:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The test below is NOT true if we are the
|
2010-05-04 06:06:01 +00:00
|
|
|
* sole exiting thread. P_STOPPED_SINGLE is unset
|
2004-09-05 02:09:54 +00:00
|
|
|
* in exit1() after it is the only survivor.
|
|
|
|
*/
|
|
|
|
if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) {
|
|
|
|
if (p->p_numthreads == p->p_suspcount) {
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
thread_lock(p->p_singlethread);
|
2008-08-22 16:15:58 +00:00
|
|
|
wakeup_swapper = thread_unsuspend_one(
|
2015-05-15 07:54:31 +00:00
|
|
|
p->p_singlethread, p, false);
|
2008-08-22 16:15:58 +00:00
|
|
|
if (wakeup_swapper)
|
|
|
|
kick_proc0();
|
2004-09-05 02:09:54 +00:00
|
|
|
}
|
2002-09-06 07:00:37 +00:00
|
|
|
}
|
2002-10-09 02:33:36 +00:00
|
|
|
|
2004-09-05 02:09:54 +00:00
|
|
|
PCPU_SET(deadthread, td);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The last thread is exiting.. but not through exit()
|
|
|
|
*/
|
|
|
|
panic ("thread_exit: Last thread exiting on its own");
|
2003-08-26 11:33:15 +00:00
|
|
|
}
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
}
|
2009-10-25 04:34:47 +00:00
|
|
|
#ifdef HWPMC_HOOKS
|
|
|
|
/*
|
|
|
|
* If this thread is part of a process that is being tracked by hwpmc(4),
|
|
|
|
* inform the module of the thread's impending exit.
|
|
|
|
*/
|
2018-05-16 22:29:20 +00:00
|
|
|
if (PMC_PROC_IS_USING_PMCS(td->td_proc)) {
|
2009-10-25 04:34:47 +00:00
|
|
|
PMC_SWITCH_CONTEXT(td, PMC_FN_CSW_OUT);
|
2018-05-16 22:29:20 +00:00
|
|
|
PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_EXIT, NULL);
|
2018-06-05 04:26:40 +00:00
|
|
|
} else if (PMC_SYSTEM_SAMPLING_ACTIVE())
|
|
|
|
PMC_CALL_HOOK_UNLOCKED(td, PMC_FN_THR_EXIT_LOG, NULL);
|
2009-10-25 04:34:47 +00:00
|
|
|
#endif
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
PROC_UNLOCK(p);
|
2014-11-26 14:10:00 +00:00
|
|
|
PROC_STATLOCK(p);
|
|
|
|
thread_lock(td);
|
|
|
|
PROC_SUNLOCK(p);
|
2012-01-03 21:03:28 +00:00
|
|
|
|
|
|
|
/* Do the same timestamp bookkeeping that mi_switch() would do. */
|
|
|
|
new_switchtime = cpu_ticks();
|
|
|
|
runtime = new_switchtime - PCPU_GET(switchtime);
|
|
|
|
td->td_runtime += runtime;
|
|
|
|
td->td_incruntime += runtime;
|
|
|
|
PCPU_SET(switchtime, new_switchtime);
|
|
|
|
PCPU_SET(switchticks, ticks);
|
- Remove 'struct vmmeter' from 'struct pcpu', leaving only global vmmeter
in place. To do per-cpu stats, convert all fields that previously were
maintained in the vmmeters that sit in pcpus to counter(9).
- Since some vmmeter stats may be touched at very early stages of boot,
before we have set up UMA and we can do counter_u64_alloc(), provide an
early counter mechanism:
o Leave one spare uint64_t in struct pcpu, named pc_early_dummy_counter.
o Point counter(9) fields of vmmeter to pcpu[0].pc_early_dummy_counter,
so that at early stages of boot, before counters are allocated we already
point to a counter that can be safely written to.
o For sparc64 that required a whole dummy pcpu[MAXCPU] array.
Further related changes:
- Don't include vmmeter.h into pcpu.h.
- vm.stats.vm.v_swappgsout and vm.stats.vm.v_swappgsin changed to 64-bit,
to match kernel representation.
- struct vmmeter hidden under _KERNEL, and only vmstat(1) is an exclusion.
This is based on benno@'s 4-year old patch:
https://lists.freebsd.org/pipermail/freebsd-arch/2013-July/014471.html
Reviewed by: kib, gallatin, marius, lidl
Differential Revision: https://reviews.freebsd.org/D10156
2017-04-17 17:34:47 +00:00
|
|
|
VM_CNT_INC(v_swtch);
|
2012-01-03 21:03:28 +00:00
|
|
|
|
|
|
|
/* Save our resource usage in our process. */
|
|
|
|
td->td_ru.ru_nvcsw++;
|
2019-12-15 21:11:15 +00:00
|
|
|
ruxagg_locked(p, td);
|
2012-01-03 21:03:28 +00:00
|
|
|
rucollect(&p->p_ru, &td->td_ru);
|
2014-11-26 14:10:00 +00:00
|
|
|
PROC_STATUNLOCK(p);
|
2012-01-03 21:03:28 +00:00
|
|
|
|
2021-02-18 10:25:10 +00:00
|
|
|
TD_SET_STATE(td, TDS_INACTIVE);
|
2008-08-13 18:24:22 +00:00
|
|
|
#ifdef WITNESS
|
|
|
|
witness_thread_exit(td);
|
|
|
|
#endif
|
2004-08-09 18:21:12 +00:00
|
|
|
CTR1(KTR_PROC, "thread_exit: cpu_throw() thread %p", td);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
sched_throw(td);
|
2003-04-02 23:53:30 +00:00
|
|
|
panic("I'm a teapot!");
|
2002-06-29 07:04:59 +00:00
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2002-12-10 02:33:45 +00:00
|
|
|
* Do any thread specific cleanups that may be needed in wait()
|
2004-03-13 22:31:39 +00:00
|
|
|
* called with Giant, proc and schedlock not held.
|
2002-12-10 02:33:45 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_wait(struct proc *p)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
|
2004-03-13 22:31:39 +00:00
|
|
|
mtx_assert(&Giant, MA_NOTOWNED);
|
2014-09-03 08:40:16 +00:00
|
|
|
KASSERT(p->p_numthreads == 1, ("multiple threads in thread_wait()"));
|
|
|
|
KASSERT(p->p_exitthreads == 0, ("p_exitthreads leaking"));
|
2007-06-12 07:24:46 +00:00
|
|
|
td = FIRST_THREAD_IN_PROC(p);
|
|
|
|
/* Lock the last thread so we spin until it exits cpu_throw(). */
|
|
|
|
thread_lock(td);
|
|
|
|
thread_unlock(td);
|
2009-03-15 06:41:47 +00:00
|
|
|
lock_profile_thread_exit(td);
|
Add cpuset, an api for thread to cpu binding and cpu resource grouping
and assignment.
- Add a reference to a struct cpuset in each thread that is inherited from
the thread that created it.
- Release the reference when the thread is destroyed.
- Add prototypes for syscalls and macros for manipulating cpusets in
sys/cpuset.h
- Add syscalls to create, get, and set new numbered cpusets:
cpuset(), cpuset_{get,set}id()
- Add syscalls for getting and setting affinity masks for cpusets or
individual threads: cpuid_{get,set}affinity()
- Add types for the 'level' and 'which' parameters for the cpuset. This
will permit expansion of the api to cover cpu masks for other objects
identifiable with an id_t integer. For example, IRQs and Jails may be
coming soon.
- The root set 0 contains all valid cpus. All thread initially belong to
cpuset 1. This permits migrating all threads off of certain cpus to
reserve them for special applications.
Sponsored by: Nokia
Discussed with: arch, rwatson, brooks, davidxu, deischen
Reviewed by: antoine
2008-03-02 07:39:22 +00:00
|
|
|
cpuset_rel(td->td_cpuset);
|
|
|
|
td->td_cpuset = NULL;
|
2007-06-12 07:24:46 +00:00
|
|
|
cpu_thread_clean(td);
|
2015-06-10 10:43:59 +00:00
|
|
|
thread_cow_free(td);
|
2016-07-28 09:09:55 +00:00
|
|
|
callout_drain(&td->td_slpcallout);
|
2002-12-10 02:33:45 +00:00
|
|
|
thread_reap(); /* check for zombie threads etc. */
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Link a thread to a process.
|
2002-09-06 07:00:37 +00:00
|
|
|
* set up anything that needs to be initialized for it to
|
|
|
|
* be used by the process.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
void
|
2006-10-26 21:42:22 +00:00
|
|
|
thread_link(struct thread *td, struct proc *p)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
|
|
|
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
/*
|
|
|
|
* XXX This can't be enabled because it's called for proc0 before
|
2008-03-19 06:19:01 +00:00
|
|
|
* its lock has been created.
|
|
|
|
* PROC_LOCK_ASSERT(p, MA_OWNED);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
*/
|
2021-02-18 10:25:10 +00:00
|
|
|
TD_SET_STATE(td, TDS_INACTIVE);
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_proc = p;
|
2007-09-17 05:31:39 +00:00
|
|
|
td->td_flags = TDF_INMEM;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2002-09-06 07:00:37 +00:00
|
|
|
LIST_INIT(&td->td_contested);
|
2007-12-15 23:13:31 +00:00
|
|
|
LIST_INIT(&td->td_lprof[0]);
|
|
|
|
LIST_INIT(&td->td_lprof[1]);
|
2019-10-14 04:17:56 +00:00
|
|
|
#ifdef EPOCH_TRACE
|
2019-09-25 18:26:31 +00:00
|
|
|
SLIST_INIT(&td->td_epochs);
|
2019-10-14 04:17:56 +00:00
|
|
|
#endif
|
1. Change prototype of trapsignal and sendsig to use ksiginfo_t *, most
changes in MD code are trivial, before this change, trapsignal and
sendsig use discrete parameters, now they uses member fields of
ksiginfo_t structure. For sendsig, this change allows us to pass
POSIX realtime signal value to user code.
2. Remove cpu_thread_siginfo, it is no longer needed because we now always
generate ksiginfo_t data and feed it to libpthread.
3. Add p_sigqueue to proc structure to hold shared signals which were
blocked by all threads in the proc.
4. Add td_sigqueue to thread structure to hold all signals delivered to
thread.
5. i386 and amd64 now return POSIX standard si_code, other arches will
be fixed.
6. In this sigqueue implementation, pending signal set is kept as before,
an extra siginfo list holds additional siginfo_t data for signals.
kernel code uses psignal() still behavior as before, it won't be failed
even under memory pressure, only exception is when deleting a signal,
we should call sigqueue_delete to remove signal from sigqueue but
not SIGDELSET. Current there is no kernel code will deliver a signal
with additional data, so kernel should be as stable as before,
a ksiginfo can carry more information, for example, allow signal to
be delivered but throw away siginfo data if memory is not enough.
SIGKILL and SIGSTOP have fast path in sigqueue_add, because they can
not be caught or masked.
The sigqueue() syscall allows user code to queue a signal to target
process, if resource is unavailable, EAGAIN will be returned as
specification said.
Just before thread exits, signal queue memory will be freed by
sigqueue_flush.
Current, all signals are allowed to be queued, not only realtime signals.
Earlier patch reviewed by: jhb, deischen
Tested on: i386, amd64
2005-10-14 12:43:47 +00:00
|
|
|
sigqueue_init(&td->td_sigqueue, p);
|
2015-05-22 17:05:21 +00:00
|
|
|
callout_init(&td->td_slpcallout, 1);
|
2014-07-25 20:21:02 +00:00
|
|
|
TAILQ_INSERT_TAIL(&p->p_threads, td, td_plist);
|
2002-06-29 07:04:59 +00:00
|
|
|
p->p_numthreads++;
|
|
|
|
}
|
|
|
|
|
2004-09-05 02:09:54 +00:00
|
|
|
/*
|
|
|
|
* Called from:
|
|
|
|
* thread_exit()
|
|
|
|
*/
|
2003-04-18 00:16:13 +00:00
|
|
|
void
|
|
|
|
thread_unlink(struct thread *td)
|
2004-01-10 18:34:01 +00:00
|
|
|
{
|
2003-04-18 00:16:13 +00:00
|
|
|
struct proc *p = td->td_proc;
|
2003-04-23 18:46:51 +00:00
|
|
|
|
2008-03-19 06:19:01 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2019-10-14 04:17:56 +00:00
|
|
|
#ifdef EPOCH_TRACE
|
2019-09-25 18:26:31 +00:00
|
|
|
MPASS(SLIST_EMPTY(&td->td_epochs));
|
2019-10-14 04:17:56 +00:00
|
|
|
#endif
|
2019-09-25 18:26:31 +00:00
|
|
|
|
2003-04-18 00:16:13 +00:00
|
|
|
TAILQ_REMOVE(&p->p_threads, td, td_plist);
|
|
|
|
p->p_numthreads--;
|
|
|
|
/* could clear a few other things here */
|
2006-10-26 21:42:22 +00:00
|
|
|
/* Must NOT clear links to proc! */
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
2009-07-14 22:51:31 +00:00
|
|
|
static int
|
|
|
|
calc_remaining(struct proc *p, int mode)
|
|
|
|
{
|
|
|
|
int remaining;
|
|
|
|
|
2011-11-18 09:12:26 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
|
|
|
PROC_SLOCK_ASSERT(p, MA_OWNED);
|
2009-07-14 22:51:31 +00:00
|
|
|
if (mode == SINGLE_EXIT)
|
|
|
|
remaining = p->p_numthreads;
|
|
|
|
else if (mode == SINGLE_BOUNDARY)
|
|
|
|
remaining = p->p_numthreads - p->p_boundary_count;
|
2014-12-13 16:18:29 +00:00
|
|
|
else if (mode == SINGLE_NO_EXIT || mode == SINGLE_ALLPROC)
|
2009-07-14 22:51:31 +00:00
|
|
|
remaining = p->p_numthreads - p->p_suspcount;
|
|
|
|
else
|
|
|
|
panic("calc_remaining: wrong mode %d", mode);
|
|
|
|
return (remaining);
|
|
|
|
}
|
|
|
|
|
2014-12-08 16:27:43 +00:00
|
|
|
static int
|
|
|
|
remain_for_mode(int mode)
|
|
|
|
{
|
|
|
|
|
2014-12-13 16:18:29 +00:00
|
|
|
return (mode == SINGLE_ALLPROC ? 0 : 1);
|
2014-12-08 16:27:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
weed_inhib(int mode, struct thread *td2, struct proc *p)
|
|
|
|
{
|
|
|
|
int wakeup_swapper;
|
|
|
|
|
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
|
|
|
PROC_SLOCK_ASSERT(p, MA_OWNED);
|
|
|
|
THREAD_LOCK_ASSERT(td2, MA_OWNED);
|
|
|
|
|
|
|
|
wakeup_swapper = 0;
|
2019-12-15 21:11:15 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Since the thread lock is dropped by the scheduler we have
|
|
|
|
* to retry to check for races.
|
|
|
|
*/
|
|
|
|
restart:
|
2014-12-08 16:27:43 +00:00
|
|
|
switch (mode) {
|
|
|
|
case SINGLE_EXIT:
|
2019-12-15 21:11:15 +00:00
|
|
|
if (TD_IS_SUSPENDED(td2)) {
|
2015-05-15 07:54:31 +00:00
|
|
|
wakeup_swapper |= thread_unsuspend_one(td2, p, true);
|
2019-12-15 21:11:15 +00:00
|
|
|
thread_lock(td2);
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
if (TD_CAN_ABORT(td2)) {
|
2014-12-08 16:27:43 +00:00
|
|
|
wakeup_swapper |= sleepq_abort(td2, EINTR);
|
2019-12-15 21:11:15 +00:00
|
|
|
return (wakeup_swapper);
|
|
|
|
}
|
2014-12-08 16:27:43 +00:00
|
|
|
break;
|
|
|
|
case SINGLE_BOUNDARY:
|
|
|
|
case SINGLE_NO_EXIT:
|
2019-12-15 21:11:15 +00:00
|
|
|
if (TD_IS_SUSPENDED(td2) &&
|
|
|
|
(td2->td_flags & TDF_BOUNDARY) == 0) {
|
2015-05-15 07:54:31 +00:00
|
|
|
wakeup_swapper |= thread_unsuspend_one(td2, p, false);
|
2019-12-15 21:11:15 +00:00
|
|
|
thread_lock(td2);
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
if (TD_CAN_ABORT(td2)) {
|
2014-12-08 16:27:43 +00:00
|
|
|
wakeup_swapper |= sleepq_abort(td2, ERESTART);
|
2019-12-15 21:11:15 +00:00
|
|
|
return (wakeup_swapper);
|
|
|
|
}
|
2014-12-16 09:48:23 +00:00
|
|
|
break;
|
2014-12-13 16:18:29 +00:00
|
|
|
case SINGLE_ALLPROC:
|
|
|
|
/*
|
|
|
|
* ALLPROC suspend tries to avoid spurious EINTR for
|
|
|
|
* threads sleeping interruptable, by suspending the
|
|
|
|
* thread directly, similarly to sig_suspend_threads().
|
|
|
|
* Since such sleep is not performed at the user
|
|
|
|
* boundary, TDF_BOUNDARY flag is not set, and TDF_ALLPROCSUSP
|
|
|
|
* is used to avoid immediate un-suspend.
|
|
|
|
*/
|
|
|
|
if (TD_IS_SUSPENDED(td2) && (td2->td_flags & (TDF_BOUNDARY |
|
2019-12-15 21:11:15 +00:00
|
|
|
TDF_ALLPROCSUSP)) == 0) {
|
2015-05-15 07:54:31 +00:00
|
|
|
wakeup_swapper |= thread_unsuspend_one(td2, p, false);
|
2019-12-15 21:11:15 +00:00
|
|
|
thread_lock(td2);
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
if (TD_CAN_ABORT(td2)) {
|
2014-12-13 16:18:29 +00:00
|
|
|
if ((td2->td_flags & TDF_SBDRY) == 0) {
|
|
|
|
thread_suspend_one(td2);
|
|
|
|
td2->td_flags |= TDF_ALLPROCSUSP;
|
|
|
|
} else {
|
|
|
|
wakeup_swapper |= sleepq_abort(td2, ERESTART);
|
2019-12-15 21:11:15 +00:00
|
|
|
return (wakeup_swapper);
|
2014-12-13 16:18:29 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-08 16:27:43 +00:00
|
|
|
break;
|
2019-12-15 21:11:15 +00:00
|
|
|
default:
|
|
|
|
break;
|
2014-12-08 16:27:43 +00:00
|
|
|
}
|
2019-12-15 21:11:15 +00:00
|
|
|
thread_unlock(td2);
|
2014-12-08 16:27:43 +00:00
|
|
|
return (wakeup_swapper);
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Enforce single-threading.
|
|
|
|
*
|
|
|
|
* Returns 1 if the caller must abort (another thread is waiting to
|
|
|
|
* exit the process or similar). Process is locked!
|
|
|
|
* Returns 0 when you are successfully the only thread running.
|
|
|
|
* A process has successfully single threaded in the suspend mode when
|
|
|
|
* There are no threads in user mode. Threads in the kernel must be
|
|
|
|
* allowed to continue until they get to the user boundary. They may even
|
|
|
|
* copy out their return values and data before suspending. They may however be
|
2006-06-30 08:10:55 +00:00
|
|
|
* accelerated in reaching the user boundary as we will wake up
|
2002-06-29 07:04:59 +00:00
|
|
|
* any sleeping threads that are interruptable. (PCATCH).
|
|
|
|
*/
|
|
|
|
int
|
2014-12-13 16:18:29 +00:00
|
|
|
thread_single(struct proc *p, int mode)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
struct thread *td2;
|
If a thread that is swapped out is made runnable, then the setrunnable()
routine wakes up proc0 so that proc0 can swap the thread back in.
Historically, this has been done by waking up proc0 directly from
setrunnable() itself via a wakeup(). When waking up a sleeping thread
that was swapped out (the usual case when waking proc0 since only sleeping
threads are eligible to be swapped out), this resulted in a bit of
recursion (e.g. wakeup() -> setrunnable() -> wakeup()).
With sleep queues having separate locks in 6.x and later, this caused a
spin lock LOR (sleepq lock -> sched_lock/thread lock -> sleepq lock).
An attempt was made to fix this in 7.0 by making the proc0 wakeup use
the ithread mechanism for doing the wakeup. However, this required
grabbing proc0's thread lock to perform the wakeup. If proc0 was asleep
elsewhere in the kernel (e.g. waiting for disk I/O), then this degenerated
into the same LOR since the thread lock would be some other sleepq lock.
Fix this by deferring the wakeup of the swapper until after the sleepq
lock held by the upper layer has been locked. The setrunnable() routine
now returns a boolean value to indicate whether or not proc0 needs to be
woken up. The end result is that consumers of the sleepq API such as
*sleep/wakeup, condition variables, sx locks, and lockmgr, have to wakeup
proc0 if they get a non-zero return value from sleepq_abort(),
sleepq_broadcast(), or sleepq_signal().
Discussed with: jeff
Glanced at by: sam
Tested by: Jurgen Weber jurgen - ish com au
MFC after: 2 weeks
2008-08-05 20:02:31 +00:00
|
|
|
int remaining, wakeup_swapper;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
td = curthread;
|
2014-12-13 16:18:29 +00:00
|
|
|
KASSERT(mode == SINGLE_EXIT || mode == SINGLE_BOUNDARY ||
|
|
|
|
mode == SINGLE_ALLPROC || mode == SINGLE_NO_EXIT,
|
|
|
|
("invalid mode %d", mode));
|
|
|
|
/*
|
|
|
|
* If allowing non-ALLPROC singlethreading for non-curproc
|
|
|
|
* callers, calc_remaining() and remain_for_mode() should be
|
|
|
|
* adjusted to also account for td->td_proc != p. For now
|
|
|
|
* this is not implemented because it is not used.
|
|
|
|
*/
|
|
|
|
KASSERT((mode == SINGLE_ALLPROC && td->td_proc != p) ||
|
|
|
|
(mode != SINGLE_ALLPROC && td->td_proc == p),
|
|
|
|
("mode %d proc %p curproc %p", mode, p, td->td_proc));
|
2004-03-13 22:31:39 +00:00
|
|
|
mtx_assert(&Giant, MA_NOTOWNED);
|
2002-06-29 07:04:59 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
|
|
|
|
2014-12-13 16:18:29 +00:00
|
|
|
if ((p->p_flag & P_HADTHREADS) == 0 && mode != SINGLE_ALLPROC)
|
2002-06-29 07:04:59 +00:00
|
|
|
return (0);
|
|
|
|
|
2002-07-25 00:27:39 +00:00
|
|
|
/* Is someone already single threading? */
|
In original kern_execve() code, at the start of the function, it forces
all other threads to suicide, problem is execve() could be failed, and
a failed execve() would change threaded process to unthreaded, this side
effect is unexpected.
The new code introduces a new single threading mode SINGLE_BOUNDARY, in
the mode, all threads should suspend themself at user boundary except
the singler. we can not use SINGLE_NO_EXIT because we want to start from
a clean state if execve() is successful, suspending other threads at unknown
point and later resuming them from there and forcing them to exit at user
boundary may cause the process to start from a dirty state. If execve() is
successful, current thread upgrades to SINGLE_EXIT mode and forces other
threads to suicide at user boundary, otherwise, other threads will be resumed
and their interrupted syscall will be restarted.
Reviewed by: julian
2004-10-06 00:40:41 +00:00
|
|
|
if (p->p_singlethread != NULL && p->p_singlethread != td)
|
2002-06-29 07:04:59 +00:00
|
|
|
return (1);
|
|
|
|
|
In original kern_execve() code, at the start of the function, it forces
all other threads to suicide, problem is execve() could be failed, and
a failed execve() would change threaded process to unthreaded, this side
effect is unexpected.
The new code introduces a new single threading mode SINGLE_BOUNDARY, in
the mode, all threads should suspend themself at user boundary except
the singler. we can not use SINGLE_NO_EXIT because we want to start from
a clean state if execve() is successful, suspending other threads at unknown
point and later resuming them from there and forcing them to exit at user
boundary may cause the process to start from a dirty state. If execve() is
successful, current thread upgrades to SINGLE_EXIT mode and forces other
threads to suicide at user boundary, otherwise, other threads will be resumed
and their interrupted syscall will be restarted.
Reviewed by: julian
2004-10-06 00:40:41 +00:00
|
|
|
if (mode == SINGLE_EXIT) {
|
|
|
|
p->p_flag |= P_SINGLE_EXIT;
|
|
|
|
p->p_flag &= ~P_SINGLE_BOUNDARY;
|
|
|
|
} else {
|
|
|
|
p->p_flag &= ~P_SINGLE_EXIT;
|
|
|
|
if (mode == SINGLE_BOUNDARY)
|
|
|
|
p->p_flag |= P_SINGLE_BOUNDARY;
|
|
|
|
else
|
|
|
|
p->p_flag &= ~P_SINGLE_BOUNDARY;
|
|
|
|
}
|
2014-12-13 16:18:29 +00:00
|
|
|
if (mode == SINGLE_ALLPROC)
|
|
|
|
p->p_flag |= P_TOTAL_STOP;
|
2002-09-05 07:30:18 +00:00
|
|
|
p->p_flag |= P_STOPPED_SINGLE;
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SLOCK(p);
|
2002-06-29 07:04:59 +00:00
|
|
|
p->p_singlethread = td;
|
2009-07-14 22:51:31 +00:00
|
|
|
remaining = calc_remaining(p, mode);
|
2014-12-08 16:27:43 +00:00
|
|
|
while (remaining != remain_for_mode(mode)) {
|
2006-03-21 10:05:15 +00:00
|
|
|
if (P_SHOULDSTOP(p) != P_STOPPED_SINGLE)
|
|
|
|
goto stopme;
|
If a thread that is swapped out is made runnable, then the setrunnable()
routine wakes up proc0 so that proc0 can swap the thread back in.
Historically, this has been done by waking up proc0 directly from
setrunnable() itself via a wakeup(). When waking up a sleeping thread
that was swapped out (the usual case when waking proc0 since only sleeping
threads are eligible to be swapped out), this resulted in a bit of
recursion (e.g. wakeup() -> setrunnable() -> wakeup()).
With sleep queues having separate locks in 6.x and later, this caused a
spin lock LOR (sleepq lock -> sched_lock/thread lock -> sleepq lock).
An attempt was made to fix this in 7.0 by making the proc0 wakeup use
the ithread mechanism for doing the wakeup. However, this required
grabbing proc0's thread lock to perform the wakeup. If proc0 was asleep
elsewhere in the kernel (e.g. waiting for disk I/O), then this degenerated
into the same LOR since the thread lock would be some other sleepq lock.
Fix this by deferring the wakeup of the swapper until after the sleepq
lock held by the upper layer has been locked. The setrunnable() routine
now returns a boolean value to indicate whether or not proc0 needs to be
woken up. The end result is that consumers of the sleepq API such as
*sleep/wakeup, condition variables, sx locks, and lockmgr, have to wakeup
proc0 if they get a non-zero return value from sleepq_abort(),
sleepq_broadcast(), or sleepq_signal().
Discussed with: jeff
Glanced at by: sam
Tested by: Jurgen Weber jurgen - ish com au
MFC after: 2 weeks
2008-08-05 20:02:31 +00:00
|
|
|
wakeup_swapper = 0;
|
2002-06-29 07:04:59 +00:00
|
|
|
FOREACH_THREAD_IN_PROC(p, td2) {
|
|
|
|
if (td2 == td)
|
|
|
|
continue;
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
thread_lock(td2);
|
2008-03-21 08:23:25 +00:00
|
|
|
td2->td_flags |= TDF_ASTPENDING | TDF_NEEDSUSPCHK;
|
2014-12-13 16:18:29 +00:00
|
|
|
if (TD_IS_INHIBITED(td2)) {
|
2014-12-08 16:27:43 +00:00
|
|
|
wakeup_swapper |= weed_inhib(mode, td2, p);
|
2006-02-13 03:16:55 +00:00
|
|
|
#ifdef SMP
|
2014-12-13 16:18:29 +00:00
|
|
|
} else if (TD_IS_RUNNING(td2) && td != td2) {
|
2006-02-13 03:16:55 +00:00
|
|
|
forward_signal(td2);
|
2019-12-15 21:11:15 +00:00
|
|
|
thread_unlock(td2);
|
2006-02-13 03:16:55 +00:00
|
|
|
#endif
|
2019-12-15 21:11:15 +00:00
|
|
|
} else
|
|
|
|
thread_unlock(td2);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
If a thread that is swapped out is made runnable, then the setrunnable()
routine wakes up proc0 so that proc0 can swap the thread back in.
Historically, this has been done by waking up proc0 directly from
setrunnable() itself via a wakeup(). When waking up a sleeping thread
that was swapped out (the usual case when waking proc0 since only sleeping
threads are eligible to be swapped out), this resulted in a bit of
recursion (e.g. wakeup() -> setrunnable() -> wakeup()).
With sleep queues having separate locks in 6.x and later, this caused a
spin lock LOR (sleepq lock -> sched_lock/thread lock -> sleepq lock).
An attempt was made to fix this in 7.0 by making the proc0 wakeup use
the ithread mechanism for doing the wakeup. However, this required
grabbing proc0's thread lock to perform the wakeup. If proc0 was asleep
elsewhere in the kernel (e.g. waiting for disk I/O), then this degenerated
into the same LOR since the thread lock would be some other sleepq lock.
Fix this by deferring the wakeup of the swapper until after the sleepq
lock held by the upper layer has been locked. The setrunnable() routine
now returns a boolean value to indicate whether or not proc0 needs to be
woken up. The end result is that consumers of the sleepq API such as
*sleep/wakeup, condition variables, sx locks, and lockmgr, have to wakeup
proc0 if they get a non-zero return value from sleepq_abort(),
sleepq_broadcast(), or sleepq_signal().
Discussed with: jeff
Glanced at by: sam
Tested by: Jurgen Weber jurgen - ish com au
MFC after: 2 weeks
2008-08-05 20:02:31 +00:00
|
|
|
if (wakeup_swapper)
|
|
|
|
kick_proc0();
|
2009-07-14 22:51:31 +00:00
|
|
|
remaining = calc_remaining(p, mode);
|
2004-06-18 06:15:21 +00:00
|
|
|
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
|
|
|
* Maybe we suspended some threads.. was it enough?
|
2002-10-25 07:11:12 +00:00
|
|
|
*/
|
2014-12-08 16:27:43 +00:00
|
|
|
if (remaining == remain_for_mode(mode))
|
2002-10-25 07:11:12 +00:00
|
|
|
break;
|
|
|
|
|
2006-03-21 10:05:15 +00:00
|
|
|
stopme:
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Wake us up when everyone else has suspended.
|
2002-07-25 00:27:39 +00:00
|
|
|
* In the mean time we suspend as well.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2014-12-13 16:18:29 +00:00
|
|
|
thread_suspend_switch(td, p);
|
2009-07-14 22:51:31 +00:00
|
|
|
remaining = calc_remaining(p, mode);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
In original kern_execve() code, at the start of the function, it forces
all other threads to suicide, problem is execve() could be failed, and
a failed execve() would change threaded process to unthreaded, this side
effect is unexpected.
The new code introduces a new single threading mode SINGLE_BOUNDARY, in
the mode, all threads should suspend themself at user boundary except
the singler. we can not use SINGLE_NO_EXIT because we want to start from
a clean state if execve() is successful, suspending other threads at unknown
point and later resuming them from there and forcing them to exit at user
boundary may cause the process to start from a dirty state. If execve() is
successful, current thread upgrades to SINGLE_EXIT mode and forces other
threads to suicide at user boundary, otherwise, other threads will be resumed
and their interrupted syscall will be restarted.
Reviewed by: julian
2004-10-06 00:40:41 +00:00
|
|
|
if (mode == SINGLE_EXIT) {
|
2004-09-15 18:39:09 +00:00
|
|
|
/*
|
2014-09-03 08:35:42 +00:00
|
|
|
* Convert the process to an unthreaded process. The
|
|
|
|
* SINGLE_EXIT is called by exit1() or execve(), in
|
|
|
|
* both cases other threads must be retired.
|
2004-09-15 18:39:09 +00:00
|
|
|
*/
|
2014-09-03 08:35:42 +00:00
|
|
|
KASSERT(p->p_numthreads == 1, ("Unthreading with >1 threads"));
|
2004-09-05 02:09:54 +00:00
|
|
|
p->p_singlethread = NULL;
|
2014-09-03 08:35:42 +00:00
|
|
|
p->p_flag &= ~(P_STOPPED_SINGLE | P_SINGLE_EXIT | P_HADTHREADS);
|
2014-09-03 08:18:07 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for any remaining threads to exit cpu_throw().
|
|
|
|
*/
|
|
|
|
while (p->p_exitthreads != 0) {
|
|
|
|
PROC_SUNLOCK(p);
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
sched_relinquish(td);
|
|
|
|
PROC_LOCK(p);
|
|
|
|
PROC_SLOCK(p);
|
|
|
|
}
|
2015-05-09 18:32:13 +00:00
|
|
|
} else if (mode == SINGLE_BOUNDARY) {
|
|
|
|
/*
|
|
|
|
* Wait until all suspended threads are removed from
|
|
|
|
* the processors. The thread_suspend_check()
|
|
|
|
* increments p_boundary_count while it is still
|
|
|
|
* running, which makes it possible for the execve()
|
|
|
|
* to destroy vmspace while our other threads are
|
|
|
|
* still using the address space.
|
|
|
|
*
|
|
|
|
* We lock the thread, which is only allowed to
|
|
|
|
* succeed after context switch code finished using
|
|
|
|
* the address space.
|
|
|
|
*/
|
|
|
|
FOREACH_THREAD_IN_PROC(p, td2) {
|
|
|
|
if (td2 == td)
|
|
|
|
continue;
|
|
|
|
thread_lock(td2);
|
|
|
|
KASSERT((td2->td_flags & TDF_BOUNDARY) != 0,
|
|
|
|
("td %p not on boundary", td2));
|
|
|
|
KASSERT(TD_IS_SUSPENDED(td2),
|
|
|
|
("td %p is not suspended", td2));
|
|
|
|
thread_unlock(td2);
|
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
}
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SUNLOCK(p);
|
2002-06-29 07:04:59 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2014-12-08 16:18:05 +00:00
|
|
|
bool
|
|
|
|
thread_suspend_check_needed(void)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
struct thread *td;
|
|
|
|
|
|
|
|
td = curthread;
|
|
|
|
p = td->td_proc;
|
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
|
|
|
return (P_SHOULDSTOP(p) || ((p->p_flag & P_TRACED) != 0 &&
|
|
|
|
(td->td_dbgflags & TDB_SUSPEND) != 0));
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Called in from locations that can safely check to see
|
|
|
|
* whether we have to suspend or at least throttle for a
|
|
|
|
* single-thread event (e.g. fork).
|
|
|
|
*
|
|
|
|
* Such locations include userret().
|
|
|
|
* If the "return_instead" argument is non zero, the thread must be able to
|
|
|
|
* accept 0 (caller may continue), or 1 (caller must abort) as a result.
|
|
|
|
*
|
|
|
|
* The 'return_instead' argument tells the function if it may do a
|
|
|
|
* thread_exit() or suspend, or whether the caller must abort and back
|
|
|
|
* out instead.
|
|
|
|
*
|
|
|
|
* If the thread that set the single_threading request has set the
|
|
|
|
* P_SINGLE_EXIT bit in the process flags then this call will never return
|
|
|
|
* if 'return_instead' is false, but will exit.
|
|
|
|
*
|
|
|
|
* P_SINGLE_EXIT | return_instead == 0| return_instead != 0
|
|
|
|
*---------------+--------------------+---------------------
|
|
|
|
* 0 | returns 0 | returns 0 or 1
|
2013-02-19 16:35:27 +00:00
|
|
|
* | when ST ends | immediately
|
2002-06-29 07:04:59 +00:00
|
|
|
*---------------+--------------------+---------------------
|
|
|
|
* 1 | thread exits | returns 1
|
2013-02-19 16:35:27 +00:00
|
|
|
* | | immediately
|
2002-06-29 07:04:59 +00:00
|
|
|
* 0 = thread_exit() or suspension ok,
|
|
|
|
* other = return error instead of stopping the thread.
|
|
|
|
*
|
|
|
|
* While a full suspension is under effect, even a single threading
|
|
|
|
* thread would be suspended if it made this call (but it shouldn't).
|
|
|
|
* This call should only be made from places where
|
2004-01-10 18:34:01 +00:00
|
|
|
* thread_exit() would be safe as that may be the outcome unless
|
2002-06-29 07:04:59 +00:00
|
|
|
* return_instead is set.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
thread_suspend_check(int return_instead)
|
|
|
|
{
|
2002-10-05 04:35:59 +00:00
|
|
|
struct thread *td;
|
|
|
|
struct proc *p;
|
2016-07-03 18:19:48 +00:00
|
|
|
int wakeup_swapper;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
td = curthread;
|
|
|
|
p = td->td_proc;
|
2004-03-13 22:31:39 +00:00
|
|
|
mtx_assert(&Giant, MA_NOTOWNED);
|
2002-06-29 07:04:59 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2014-12-08 16:18:05 +00:00
|
|
|
while (thread_suspend_check_needed()) {
|
2002-09-05 07:30:18 +00:00
|
|
|
if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) {
|
2002-06-29 07:04:59 +00:00
|
|
|
KASSERT(p->p_singlethread != NULL,
|
|
|
|
("singlethread not set"));
|
|
|
|
/*
|
2002-07-25 00:27:39 +00:00
|
|
|
* The only suspension in action is a
|
|
|
|
* single-threading. Single threader need not stop.
|
2016-06-16 12:01:11 +00:00
|
|
|
* It is safe to access p->p_singlethread unlocked
|
|
|
|
* because it can only be set to our address by us.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2002-07-25 00:27:39 +00:00
|
|
|
if (p->p_singlethread == td)
|
2002-06-29 07:04:59 +00:00
|
|
|
return (0); /* Exempt from stopping. */
|
2004-01-10 18:34:01 +00:00
|
|
|
}
|
2004-08-29 23:10:02 +00:00
|
|
|
if ((p->p_flag & P_SINGLE_EXIT) && return_instead)
|
Fix a long standing race between sleep queue and thread
suspension code. When a thread A is going to sleep, it calls
sleepq_catch_signals() to detect any pending signals or thread
suspension request, if nothing happens, it returns without
holding process lock or scheduler lock, this opens a race
window which allows thread B to come in and do process
suspension work, however since A is still at running state,
thread B can do nothing to A, thread A continues, and puts
itself into actually sleeping state, but B has never seen it,
and it sits there forever until B is woken up by other threads
sometimes later(this can be very long delay or never
happen). Fix this bug by forcing sleepq_catch_signals to
return with scheduler lock held.
Fix sleepq_abort() by passing it an interrupted code, previously,
it worked as wakeup_one(), and the interruption can not be
identified correctly by sleep queue code when the sleeping
thread is resumed.
Let thread_suspend_check() returns EINTR or ERESTART, so sleep
queue no longer has to use SIGSTOP as a hack to build a return
value.
Reviewed by: jhb
MFC after: 1 week
2006-02-15 23:52:01 +00:00
|
|
|
return (EINTR);
|
2002-06-29 07:04:59 +00:00
|
|
|
|
In original kern_execve() code, at the start of the function, it forces
all other threads to suicide, problem is execve() could be failed, and
a failed execve() would change threaded process to unthreaded, this side
effect is unexpected.
The new code introduces a new single threading mode SINGLE_BOUNDARY, in
the mode, all threads should suspend themself at user boundary except
the singler. we can not use SINGLE_NO_EXIT because we want to start from
a clean state if execve() is successful, suspending other threads at unknown
point and later resuming them from there and forcing them to exit at user
boundary may cause the process to start from a dirty state. If execve() is
successful, current thread upgrades to SINGLE_EXIT mode and forces other
threads to suicide at user boundary, otherwise, other threads will be resumed
and their interrupted syscall will be restarted.
Reviewed by: julian
2004-10-06 00:40:41 +00:00
|
|
|
/* Should we goto user boundary if we didn't come from there? */
|
|
|
|
if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE &&
|
|
|
|
(p->p_flag & P_SINGLE_BOUNDARY) && return_instead)
|
Fix a long standing race between sleep queue and thread
suspension code. When a thread A is going to sleep, it calls
sleepq_catch_signals() to detect any pending signals or thread
suspension request, if nothing happens, it returns without
holding process lock or scheduler lock, this opens a race
window which allows thread B to come in and do process
suspension work, however since A is still at running state,
thread B can do nothing to A, thread A continues, and puts
itself into actually sleeping state, but B has never seen it,
and it sits there forever until B is woken up by other threads
sometimes later(this can be very long delay or never
happen). Fix this bug by forcing sleepq_catch_signals to
return with scheduler lock held.
Fix sleepq_abort() by passing it an interrupted code, previously,
it worked as wakeup_one(), and the interruption can not be
identified correctly by sleep queue code when the sleeping
thread is resumed.
Let thread_suspend_check() returns EINTR or ERESTART, so sleep
queue no longer has to use SIGSTOP as a hack to build a return
value.
Reviewed by: jhb
MFC after: 1 week
2006-02-15 23:52:01 +00:00
|
|
|
return (ERESTART);
|
In original kern_execve() code, at the start of the function, it forces
all other threads to suicide, problem is execve() could be failed, and
a failed execve() would change threaded process to unthreaded, this side
effect is unexpected.
The new code introduces a new single threading mode SINGLE_BOUNDARY, in
the mode, all threads should suspend themself at user boundary except
the singler. we can not use SINGLE_NO_EXIT because we want to start from
a clean state if execve() is successful, suspending other threads at unknown
point and later resuming them from there and forcing them to exit at user
boundary may cause the process to start from a dirty state. If execve() is
successful, current thread upgrades to SINGLE_EXIT mode and forces other
threads to suicide at user boundary, otherwise, other threads will be resumed
and their interrupted syscall will be restarted.
Reviewed by: julian
2004-10-06 00:40:41 +00:00
|
|
|
|
2013-03-21 14:06:27 +00:00
|
|
|
/*
|
2015-05-23 19:09:04 +00:00
|
|
|
* Ignore suspend requests if they are deferred.
|
2013-03-21 14:06:27 +00:00
|
|
|
*/
|
2015-05-23 19:09:04 +00:00
|
|
|
if ((td->td_flags & TDF_SBDRY) != 0) {
|
2013-03-21 14:06:27 +00:00
|
|
|
KASSERT(return_instead,
|
|
|
|
("TDF_SBDRY set for unsafe thread_suspend_check"));
|
2016-07-03 18:19:48 +00:00
|
|
|
KASSERT((td->td_flags & (TDF_SEINTR | TDF_SERESTART)) !=
|
|
|
|
(TDF_SEINTR | TDF_SERESTART),
|
|
|
|
("both TDF_SEINTR and TDF_SERESTART"));
|
|
|
|
return (TD_SBDRY_INTR(td) ? TD_SBDRY_ERRNO(td) : 0);
|
2013-03-21 14:06:27 +00:00
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* If the process is waiting for us to exit,
|
|
|
|
* this thread should just suicide.
|
2002-09-05 07:30:18 +00:00
|
|
|
* Assumes that P_SINGLE_EXIT implies P_STOPPED_SINGLE.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2010-10-09 02:50:23 +00:00
|
|
|
if ((p->p_flag & P_SINGLE_EXIT) && (p->p_singlethread != td)) {
|
|
|
|
PROC_UNLOCK(p);
|
2015-05-24 14:51:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Allow Linux emulation layer to do some work
|
|
|
|
* before thread suicide.
|
|
|
|
*/
|
|
|
|
if (__predict_false(p->p_sysent->sv_thread_detach != NULL))
|
|
|
|
(p->p_sysent->sv_thread_detach)(td);
|
Add implementation of robust mutexes, hopefully close enough to the
intention of the POSIX IEEE Std 1003.1TM-2008/Cor 1-2013.
A robust mutex is guaranteed to be cleared by the system upon either
thread or process owner termination while the mutex is held. The next
mutex locker is then notified about inconsistent mutex state and can
execute (or abandon) corrective actions.
The patch mostly consists of small changes here and there, adding
neccessary checks for the inconsistent and abandoned conditions into
existing paths. Additionally, the thread exit handler was extended to
iterate over the userspace-maintained list of owned robust mutexes,
unlocking and marking as terminated each of them.
The list of owned robust mutexes cannot be maintained atomically
synchronous with the mutex lock state (it is possible in kernel, but
is too expensive). Instead, for the duration of lock or unlock
operation, the current mutex is remembered in a special slot that is
also checked by the kernel at thread termination.
Kernel must be aware about the per-thread location of the heads of
robust mutex lists and the current active mutex slot. When a thread
touches a robust mutex for the first time, a new umtx op syscall is
issued which informs about location of lists heads.
The umtx sleep queues for PP and PI mutexes are split between
non-robust and robust.
Somewhat unrelated changes in the patch:
1. Style.
2. The fix for proper tdfind() call use in umtxq_sleep_pi() for shared
pi mutexes.
3. Removal of the userspace struct pthread_mutex m_owner field.
4. The sysctl kern.ipc.umtx_vnode_persistent is added, which controls
the lifetime of the shared mutex associated with a vnode' page.
Reviewed by: jilles (previous version, supposedly the objection was fixed)
Discussed with: brooks, Martin Simmons <martin@lispworks.com> (some aspects)
Tested by: pho
Sponsored by: The FreeBSD Foundation
2016-05-17 09:56:22 +00:00
|
|
|
umtx_thread_exit(td);
|
2015-12-29 23:16:20 +00:00
|
|
|
kern_thr_exit(td);
|
|
|
|
panic("stopped thread did not exit");
|
2010-10-09 02:50:23 +00:00
|
|
|
}
|
2010-10-17 11:01:52 +00:00
|
|
|
|
|
|
|
PROC_SLOCK(p);
|
|
|
|
thread_stopped(p);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) {
|
|
|
|
if (p->p_numthreads == p->p_suspcount + 1) {
|
|
|
|
thread_lock(p->p_singlethread);
|
2015-05-15 07:54:31 +00:00
|
|
|
wakeup_swapper = thread_unsuspend_one(
|
|
|
|
p->p_singlethread, p, false);
|
2008-08-22 16:15:58 +00:00
|
|
|
if (wakeup_swapper)
|
|
|
|
kick_proc0();
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
}
|
|
|
|
}
|
2008-10-23 07:55:38 +00:00
|
|
|
PROC_UNLOCK(p);
|
2008-11-05 03:01:23 +00:00
|
|
|
thread_lock(td);
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* When a thread suspends, it just
|
2006-12-06 06:34:57 +00:00
|
|
|
* gets taken off all queues.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2002-09-11 08:13:56 +00:00
|
|
|
thread_suspend_one(td);
|
In original kern_execve() code, at the start of the function, it forces
all other threads to suicide, problem is execve() could be failed, and
a failed execve() would change threaded process to unthreaded, this side
effect is unexpected.
The new code introduces a new single threading mode SINGLE_BOUNDARY, in
the mode, all threads should suspend themself at user boundary except
the singler. we can not use SINGLE_NO_EXIT because we want to start from
a clean state if execve() is successful, suspending other threads at unknown
point and later resuming them from there and forcing them to exit at user
boundary may cause the process to start from a dirty state. If execve() is
successful, current thread upgrades to SINGLE_EXIT mode and forces other
threads to suicide at user boundary, otherwise, other threads will be resumed
and their interrupted syscall will be restarted.
Reviewed by: julian
2004-10-06 00:40:41 +00:00
|
|
|
if (return_instead == 0) {
|
|
|
|
p->p_boundary_count++;
|
|
|
|
td->td_flags |= TDF_BOUNDARY;
|
|
|
|
}
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SUNLOCK(p);
|
2019-12-15 21:26:50 +00:00
|
|
|
mi_switch(SW_INVOL | SWT_SUSPEND);
|
2002-06-29 07:04:59 +00:00
|
|
|
PROC_LOCK(p);
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2020-01-02 22:13:59 +00:00
|
|
|
/*
|
|
|
|
* Check for possible stops and suspensions while executing a
|
|
|
|
* casueword or similar transiently failing operation.
|
|
|
|
*
|
|
|
|
* The sleep argument controls whether the function can handle a stop
|
|
|
|
* request itself or it should return ERESTART and the request is
|
|
|
|
* proceed at the kernel/user boundary in ast.
|
|
|
|
*
|
|
|
|
* Typically, when retrying due to casueword(9) failure (rv == 1), we
|
|
|
|
* should handle the stop requests there, with exception of cases when
|
|
|
|
* the thread owns a kernel resource, for instance busied the umtx
|
2020-02-08 15:06:06 +00:00
|
|
|
* key, or when functions return immediately if thread_check_susp()
|
2020-01-02 22:13:59 +00:00
|
|
|
* returned non-zero. On the other hand, retrying the whole lock
|
|
|
|
* operation, we better not stop there but delegate the handling to
|
|
|
|
* ast.
|
|
|
|
*
|
|
|
|
* If the request is for thread termination P_SINGLE_EXIT, we cannot
|
|
|
|
* handle it at all, and simply return EINTR.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
thread_check_susp(struct thread *td, bool sleep)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The check for TDF_NEEDSUSPCHK is racy, but it is enough to
|
|
|
|
* eventually break the lockstep loop.
|
|
|
|
*/
|
|
|
|
if ((td->td_flags & TDF_NEEDSUSPCHK) == 0)
|
|
|
|
return (0);
|
|
|
|
error = 0;
|
|
|
|
p = td->td_proc;
|
|
|
|
PROC_LOCK(p);
|
|
|
|
if (p->p_flag & P_SINGLE_EXIT)
|
|
|
|
error = EINTR;
|
|
|
|
else if (P_SHOULDSTOP(p) ||
|
|
|
|
((p->p_flag & P_TRACED) && (td->td_dbgflags & TDB_SUSPEND)))
|
|
|
|
error = sleep ? thread_suspend_check(0) : ERESTART;
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
void
|
2014-12-13 16:18:29 +00:00
|
|
|
thread_suspend_switch(struct thread *td, struct proc *p)
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
KASSERT(!TD_IS_SUSPENDED(td), ("already suspended"));
|
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SLOCK_ASSERT(p, MA_OWNED);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
/*
|
|
|
|
* We implement thread_suspend_one in stages here to avoid
|
|
|
|
* dropping the proc lock while the thread lock is owned.
|
|
|
|
*/
|
2014-12-13 16:18:29 +00:00
|
|
|
if (p == td->td_proc) {
|
|
|
|
thread_stopped(p);
|
|
|
|
p->p_suspcount++;
|
|
|
|
}
|
2008-10-23 07:55:38 +00:00
|
|
|
PROC_UNLOCK(p);
|
2008-11-05 03:01:23 +00:00
|
|
|
thread_lock(td);
|
2008-03-21 08:23:25 +00:00
|
|
|
td->td_flags &= ~TDF_NEEDSUSPCHK;
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
TD_SET_SUSPENDED(td);
|
2008-03-12 06:31:06 +00:00
|
|
|
sched_sleep(td, 0);
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SUNLOCK(p);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
DROP_GIANT();
|
2019-12-15 21:26:50 +00:00
|
|
|
mi_switch(SW_VOL | SWT_SUSPEND);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
PICKUP_GIANT();
|
|
|
|
PROC_LOCK(p);
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SLOCK(p);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
}
|
|
|
|
|
In the kernel code, we have the tsleep() call with the PCATCH argument.
PCATCH means 'if we get a signal, interrupt me!" and tsleep returns
either EINTR or ERESTART depending on the circumstances. ERESTART is
"special" because it causes the system call to fail, but right as it
returns back to userland it tells the trap handler to move %eip back a
bit so that userland will immediately re-run the syscall.
This is a syscall restart. It only works for things like read() etc where
nothing has changed yet. Note that *userland* is tricked into restarting
the syscall by the kernel. The kernel doesn't actually do the restart. It
is deadly for things like select, poll, nanosleep etc where it might cause
the elapsed time to be reset and start again from scratch. So those
syscalls do this to prevent userland rerunning the syscall:
if (error == ERESTART) error = EINTR;
Fake "signals" like SIGTSTP from ^Z etc do not normally invoke userland
signal handlers. But, in -current, the PCATCH *is* being triggered and
tsleep is returning ERESTART, and the syscall is aborted even though no
userland signal handler was run.
That is the fault here. We're triggering the PCATCH in cases that we
shouldn't. ie: it is being triggered on *any* signal processing, rather
than the case where the signal is posted to userland.
--- Peter
The work of psignal() is a patchwork of special case required by the process
debugging and job-control facilities...
--- Kirk McKusick
"The design and impelementation of the 4.4BSD Operating system"
Page 105
in STABLE source, when psignal is posting a STOP signal to sleeping
process and the signal action of the process is SIG_DFL, system will
directly change the process state from SSLEEP to SSTOP, and when
SIGCONT is posted to the stopped process, if it finds that the process
is still on sleep queue, the process state will be restored to SSLEEP,
and won't wakeup the process.
this commit mimics the behaviour in STABLE source tree.
Reviewed by: Jon Mini, Tim Robbins, Peter Wemm
Approved by: julian@freebsd.org (mentor)
2002-09-03 12:56:01 +00:00
|
|
|
void
|
|
|
|
thread_suspend_one(struct thread *td)
|
|
|
|
{
|
2014-12-13 16:18:29 +00:00
|
|
|
struct proc *p;
|
In the kernel code, we have the tsleep() call with the PCATCH argument.
PCATCH means 'if we get a signal, interrupt me!" and tsleep returns
either EINTR or ERESTART depending on the circumstances. ERESTART is
"special" because it causes the system call to fail, but right as it
returns back to userland it tells the trap handler to move %eip back a
bit so that userland will immediately re-run the syscall.
This is a syscall restart. It only works for things like read() etc where
nothing has changed yet. Note that *userland* is tricked into restarting
the syscall by the kernel. The kernel doesn't actually do the restart. It
is deadly for things like select, poll, nanosleep etc where it might cause
the elapsed time to be reset and start again from scratch. So those
syscalls do this to prevent userland rerunning the syscall:
if (error == ERESTART) error = EINTR;
Fake "signals" like SIGTSTP from ^Z etc do not normally invoke userland
signal handlers. But, in -current, the PCATCH *is* being triggered and
tsleep is returning ERESTART, and the syscall is aborted even though no
userland signal handler was run.
That is the fault here. We're triggering the PCATCH in cases that we
shouldn't. ie: it is being triggered on *any* signal processing, rather
than the case where the signal is posted to userland.
--- Peter
The work of psignal() is a patchwork of special case required by the process
debugging and job-control facilities...
--- Kirk McKusick
"The design and impelementation of the 4.4BSD Operating system"
Page 105
in STABLE source, when psignal is posting a STOP signal to sleeping
process and the signal action of the process is SIG_DFL, system will
directly change the process state from SSLEEP to SSTOP, and when
SIGCONT is posted to the stopped process, if it finds that the process
is still on sleep queue, the process state will be restored to SSLEEP,
and won't wakeup the process.
this commit mimics the behaviour in STABLE source tree.
Reviewed by: Jon Mini, Tim Robbins, Peter Wemm
Approved by: julian@freebsd.org (mentor)
2002-09-03 12:56:01 +00:00
|
|
|
|
2014-12-13 16:18:29 +00:00
|
|
|
p = td->td_proc;
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SLOCK_ASSERT(p, MA_OWNED);
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2003-03-11 00:07:53 +00:00
|
|
|
KASSERT(!TD_IS_SUSPENDED(td), ("already suspended"));
|
In the kernel code, we have the tsleep() call with the PCATCH argument.
PCATCH means 'if we get a signal, interrupt me!" and tsleep returns
either EINTR or ERESTART depending on the circumstances. ERESTART is
"special" because it causes the system call to fail, but right as it
returns back to userland it tells the trap handler to move %eip back a
bit so that userland will immediately re-run the syscall.
This is a syscall restart. It only works for things like read() etc where
nothing has changed yet. Note that *userland* is tricked into restarting
the syscall by the kernel. The kernel doesn't actually do the restart. It
is deadly for things like select, poll, nanosleep etc where it might cause
the elapsed time to be reset and start again from scratch. So those
syscalls do this to prevent userland rerunning the syscall:
if (error == ERESTART) error = EINTR;
Fake "signals" like SIGTSTP from ^Z etc do not normally invoke userland
signal handlers. But, in -current, the PCATCH *is* being triggered and
tsleep is returning ERESTART, and the syscall is aborted even though no
userland signal handler was run.
That is the fault here. We're triggering the PCATCH in cases that we
shouldn't. ie: it is being triggered on *any* signal processing, rather
than the case where the signal is posted to userland.
--- Peter
The work of psignal() is a patchwork of special case required by the process
debugging and job-control facilities...
--- Kirk McKusick
"The design and impelementation of the 4.4BSD Operating system"
Page 105
in STABLE source, when psignal is posting a STOP signal to sleeping
process and the signal action of the process is SIG_DFL, system will
directly change the process state from SSLEEP to SSTOP, and when
SIGCONT is posted to the stopped process, if it finds that the process
is still on sleep queue, the process state will be restored to SSLEEP,
and won't wakeup the process.
this commit mimics the behaviour in STABLE source tree.
Reviewed by: Jon Mini, Tim Robbins, Peter Wemm
Approved by: julian@freebsd.org (mentor)
2002-09-03 12:56:01 +00:00
|
|
|
p->p_suspcount++;
|
2008-03-21 08:23:25 +00:00
|
|
|
td->td_flags &= ~TDF_NEEDSUSPCHK;
|
2002-09-11 08:13:56 +00:00
|
|
|
TD_SET_SUSPENDED(td);
|
2008-03-12 06:31:06 +00:00
|
|
|
sched_sleep(td, 0);
|
In the kernel code, we have the tsleep() call with the PCATCH argument.
PCATCH means 'if we get a signal, interrupt me!" and tsleep returns
either EINTR or ERESTART depending on the circumstances. ERESTART is
"special" because it causes the system call to fail, but right as it
returns back to userland it tells the trap handler to move %eip back a
bit so that userland will immediately re-run the syscall.
This is a syscall restart. It only works for things like read() etc where
nothing has changed yet. Note that *userland* is tricked into restarting
the syscall by the kernel. The kernel doesn't actually do the restart. It
is deadly for things like select, poll, nanosleep etc where it might cause
the elapsed time to be reset and start again from scratch. So those
syscalls do this to prevent userland rerunning the syscall:
if (error == ERESTART) error = EINTR;
Fake "signals" like SIGTSTP from ^Z etc do not normally invoke userland
signal handlers. But, in -current, the PCATCH *is* being triggered and
tsleep is returning ERESTART, and the syscall is aborted even though no
userland signal handler was run.
That is the fault here. We're triggering the PCATCH in cases that we
shouldn't. ie: it is being triggered on *any* signal processing, rather
than the case where the signal is posted to userland.
--- Peter
The work of psignal() is a patchwork of special case required by the process
debugging and job-control facilities...
--- Kirk McKusick
"The design and impelementation of the 4.4BSD Operating system"
Page 105
in STABLE source, when psignal is posting a STOP signal to sleeping
process and the signal action of the process is SIG_DFL, system will
directly change the process state from SSLEEP to SSTOP, and when
SIGCONT is posted to the stopped process, if it finds that the process
is still on sleep queue, the process state will be restored to SSLEEP,
and won't wakeup the process.
this commit mimics the behaviour in STABLE source tree.
Reviewed by: Jon Mini, Tim Robbins, Peter Wemm
Approved by: julian@freebsd.org (mentor)
2002-09-03 12:56:01 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 07:54:31 +00:00
|
|
|
static int
|
|
|
|
thread_unsuspend_one(struct thread *td, struct proc *p, bool boundary)
|
In the kernel code, we have the tsleep() call with the PCATCH argument.
PCATCH means 'if we get a signal, interrupt me!" and tsleep returns
either EINTR or ERESTART depending on the circumstances. ERESTART is
"special" because it causes the system call to fail, but right as it
returns back to userland it tells the trap handler to move %eip back a
bit so that userland will immediately re-run the syscall.
This is a syscall restart. It only works for things like read() etc where
nothing has changed yet. Note that *userland* is tricked into restarting
the syscall by the kernel. The kernel doesn't actually do the restart. It
is deadly for things like select, poll, nanosleep etc where it might cause
the elapsed time to be reset and start again from scratch. So those
syscalls do this to prevent userland rerunning the syscall:
if (error == ERESTART) error = EINTR;
Fake "signals" like SIGTSTP from ^Z etc do not normally invoke userland
signal handlers. But, in -current, the PCATCH *is* being triggered and
tsleep is returning ERESTART, and the syscall is aborted even though no
userland signal handler was run.
That is the fault here. We're triggering the PCATCH in cases that we
shouldn't. ie: it is being triggered on *any* signal processing, rather
than the case where the signal is posted to userland.
--- Peter
The work of psignal() is a patchwork of special case required by the process
debugging and job-control facilities...
--- Kirk McKusick
"The design and impelementation of the 4.4BSD Operating system"
Page 105
in STABLE source, when psignal is posting a STOP signal to sleeping
process and the signal action of the process is SIG_DFL, system will
directly change the process state from SSLEEP to SSTOP, and when
SIGCONT is posted to the stopped process, if it finds that the process
is still on sleep queue, the process state will be restored to SSLEEP,
and won't wakeup the process.
this commit mimics the behaviour in STABLE source tree.
Reviewed by: Jon Mini, Tim Robbins, Peter Wemm
Approved by: julian@freebsd.org (mentor)
2002-09-03 12:56:01 +00:00
|
|
|
{
|
|
|
|
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
2006-12-06 06:34:57 +00:00
|
|
|
KASSERT(TD_IS_SUSPENDED(td), ("Thread not suspended"));
|
2002-09-11 08:13:56 +00:00
|
|
|
TD_CLR_SUSPENDED(td);
|
2014-12-13 16:18:29 +00:00
|
|
|
td->td_flags &= ~TDF_ALLPROCSUSP;
|
|
|
|
if (td->td_proc == p) {
|
|
|
|
PROC_SLOCK_ASSERT(p, MA_OWNED);
|
|
|
|
p->p_suspcount--;
|
2015-05-15 07:54:31 +00:00
|
|
|
if (boundary && (td->td_flags & TDF_BOUNDARY) != 0) {
|
|
|
|
td->td_flags &= ~TDF_BOUNDARY;
|
|
|
|
p->p_boundary_count--;
|
|
|
|
}
|
2014-12-13 16:18:29 +00:00
|
|
|
}
|
2019-12-15 21:11:15 +00:00
|
|
|
return (setrunnable(td, 0));
|
In the kernel code, we have the tsleep() call with the PCATCH argument.
PCATCH means 'if we get a signal, interrupt me!" and tsleep returns
either EINTR or ERESTART depending on the circumstances. ERESTART is
"special" because it causes the system call to fail, but right as it
returns back to userland it tells the trap handler to move %eip back a
bit so that userland will immediately re-run the syscall.
This is a syscall restart. It only works for things like read() etc where
nothing has changed yet. Note that *userland* is tricked into restarting
the syscall by the kernel. The kernel doesn't actually do the restart. It
is deadly for things like select, poll, nanosleep etc where it might cause
the elapsed time to be reset and start again from scratch. So those
syscalls do this to prevent userland rerunning the syscall:
if (error == ERESTART) error = EINTR;
Fake "signals" like SIGTSTP from ^Z etc do not normally invoke userland
signal handlers. But, in -current, the PCATCH *is* being triggered and
tsleep is returning ERESTART, and the syscall is aborted even though no
userland signal handler was run.
That is the fault here. We're triggering the PCATCH in cases that we
shouldn't. ie: it is being triggered on *any* signal processing, rather
than the case where the signal is posted to userland.
--- Peter
The work of psignal() is a patchwork of special case required by the process
debugging and job-control facilities...
--- Kirk McKusick
"The design and impelementation of the 4.4BSD Operating system"
Page 105
in STABLE source, when psignal is posting a STOP signal to sleeping
process and the signal action of the process is SIG_DFL, system will
directly change the process state from SSLEEP to SSTOP, and when
SIGCONT is posted to the stopped process, if it finds that the process
is still on sleep queue, the process state will be restored to SSLEEP,
and won't wakeup the process.
this commit mimics the behaviour in STABLE source tree.
Reviewed by: Jon Mini, Tim Robbins, Peter Wemm
Approved by: julian@freebsd.org (mentor)
2002-09-03 12:56:01 +00:00
|
|
|
}
|
|
|
|
|
2021-04-24 11:47:53 +00:00
|
|
|
void
|
|
|
|
thread_run_flash(struct thread *td)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
|
|
|
|
p = td->td_proc;
|
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
|
|
|
|
|
|
|
if (TD_ON_SLEEPQ(td))
|
|
|
|
sleepq_remove_nested(td);
|
|
|
|
else
|
|
|
|
thread_lock(td);
|
|
|
|
|
|
|
|
THREAD_LOCK_ASSERT(td, MA_OWNED);
|
|
|
|
KASSERT(TD_IS_SUSPENDED(td), ("Thread not suspended"));
|
|
|
|
|
|
|
|
TD_CLR_SUSPENDED(td);
|
|
|
|
PROC_SLOCK(p);
|
|
|
|
MPASS(p->p_suspcount > 0);
|
|
|
|
p->p_suspcount--;
|
|
|
|
PROC_SUNLOCK(p);
|
|
|
|
if (setrunnable(td, 0))
|
|
|
|
kick_proc0();
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Allow all threads blocked by single threading to continue running.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_unsuspend(struct proc *p)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
2008-08-22 16:15:58 +00:00
|
|
|
int wakeup_swapper;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SLOCK_ASSERT(p, MA_OWNED);
|
2008-08-22 16:15:58 +00:00
|
|
|
wakeup_swapper = 0;
|
2002-06-29 07:04:59 +00:00
|
|
|
if (!P_SHOULDSTOP(p)) {
|
2006-12-06 06:34:57 +00:00
|
|
|
FOREACH_THREAD_IN_PROC(p, td) {
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
thread_lock(td);
|
2006-12-06 06:34:57 +00:00
|
|
|
if (TD_IS_SUSPENDED(td)) {
|
2015-05-15 07:54:31 +00:00
|
|
|
wakeup_swapper |= thread_unsuspend_one(td, p,
|
|
|
|
true);
|
2019-12-15 21:11:15 +00:00
|
|
|
} else
|
|
|
|
thread_unlock(td);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2015-05-15 07:54:31 +00:00
|
|
|
} else if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE &&
|
|
|
|
p->p_numthreads == p->p_suspcount) {
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Stopping everything also did the job for the single
|
|
|
|
* threading request. Now we've downgraded to single-threaded,
|
|
|
|
* let it continue.
|
|
|
|
*/
|
2014-12-13 16:18:29 +00:00
|
|
|
if (p->p_singlethread->td_proc == p) {
|
|
|
|
thread_lock(p->p_singlethread);
|
|
|
|
wakeup_swapper = thread_unsuspend_one(
|
2015-05-15 07:54:31 +00:00
|
|
|
p->p_singlethread, p, false);
|
2014-12-13 16:18:29 +00:00
|
|
|
}
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2008-08-22 16:15:58 +00:00
|
|
|
if (wakeup_swapper)
|
|
|
|
kick_proc0();
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
2004-09-05 02:09:54 +00:00
|
|
|
/*
|
|
|
|
* End the single threading mode..
|
|
|
|
*/
|
2002-06-29 07:04:59 +00:00
|
|
|
void
|
2014-12-13 16:18:29 +00:00
|
|
|
thread_single_end(struct proc *p, int mode)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
|
|
|
struct thread *td;
|
2008-08-22 16:15:58 +00:00
|
|
|
int wakeup_swapper;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2014-12-13 16:18:29 +00:00
|
|
|
KASSERT(mode == SINGLE_EXIT || mode == SINGLE_BOUNDARY ||
|
|
|
|
mode == SINGLE_ALLPROC || mode == SINGLE_NO_EXIT,
|
|
|
|
("invalid mode %d", mode));
|
2002-06-29 07:04:59 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2014-12-13 16:18:29 +00:00
|
|
|
KASSERT((mode == SINGLE_ALLPROC && (p->p_flag & P_TOTAL_STOP) != 0) ||
|
|
|
|
(mode != SINGLE_ALLPROC && (p->p_flag & P_TOTAL_STOP) == 0),
|
|
|
|
("mode %d does not match P_TOTAL_STOP", mode));
|
2015-05-15 07:54:31 +00:00
|
|
|
KASSERT(mode == SINGLE_ALLPROC || p->p_singlethread == curthread,
|
|
|
|
("thread_single_end from other thread %p %p",
|
|
|
|
curthread, p->p_singlethread));
|
|
|
|
KASSERT(mode != SINGLE_BOUNDARY ||
|
|
|
|
(p->p_flag & P_SINGLE_BOUNDARY) != 0,
|
|
|
|
("mis-matched SINGLE_BOUNDARY flags %x", p->p_flag));
|
2014-12-13 16:18:29 +00:00
|
|
|
p->p_flag &= ~(P_STOPPED_SINGLE | P_SINGLE_EXIT | P_SINGLE_BOUNDARY |
|
|
|
|
P_TOTAL_STOP);
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SLOCK(p);
|
2002-06-29 07:04:59 +00:00
|
|
|
p->p_singlethread = NULL;
|
2008-08-22 16:15:58 +00:00
|
|
|
wakeup_swapper = 0;
|
2002-08-22 21:45:58 +00:00
|
|
|
/*
|
2008-08-22 16:15:58 +00:00
|
|
|
* If there are other threads they may now run,
|
2002-08-22 21:45:58 +00:00
|
|
|
* unless of course there is a blanket 'stop order'
|
|
|
|
* on the process. The single threader must be allowed
|
|
|
|
* to continue however as this is a bad place to stop.
|
|
|
|
*/
|
2014-12-13 16:18:29 +00:00
|
|
|
if (p->p_numthreads != remain_for_mode(mode) && !P_SHOULDSTOP(p)) {
|
2006-12-06 06:34:57 +00:00
|
|
|
FOREACH_THREAD_IN_PROC(p, td) {
|
Commit 4/14 of sched_lock decomposition.
- Use thread_lock() rather than sched_lock for per-thread scheduling
sychronization.
- Use the per-process spinlock rather than the sched_lock for per-process
scheduling synchronization.
- Move some common code into thread_suspend_switch() to handle the
mechanics of suspending a thread. The locking here is incredibly
convoluted and should be simplified.
Tested by: kris, current@
Tested on: i386, amd64, ULE, 4BSD, libthr, libkse, PREEMPTION, etc.
Discussed with: kris, attilio, kmacy, jhb, julian, bde (small parts each)
2007-06-04 23:52:24 +00:00
|
|
|
thread_lock(td);
|
2006-12-06 06:34:57 +00:00
|
|
|
if (TD_IS_SUSPENDED(td)) {
|
2015-05-15 07:54:31 +00:00
|
|
|
wakeup_swapper |= thread_unsuspend_one(td, p,
|
|
|
|
mode == SINGLE_BOUNDARY);
|
2019-12-15 21:11:15 +00:00
|
|
|
} else
|
|
|
|
thread_unlock(td);
|
2002-08-22 21:45:58 +00:00
|
|
|
}
|
|
|
|
}
|
2015-05-15 07:54:31 +00:00
|
|
|
KASSERT(mode != SINGLE_BOUNDARY || p->p_boundary_count == 0,
|
|
|
|
("inconsistent boundary count %d", p->p_boundary_count));
|
2008-11-05 03:01:23 +00:00
|
|
|
PROC_SUNLOCK(p);
|
2008-08-22 16:15:58 +00:00
|
|
|
if (wakeup_swapper)
|
|
|
|
kick_proc0();
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2004-04-28 20:36:53 +00:00
|
|
|
|
2020-11-11 08:50:04 +00:00
|
|
|
/*
|
|
|
|
* Locate a thread by number and return with proc lock held.
|
|
|
|
*
|
|
|
|
* thread exit establishes proc -> tidhash lock ordering, but lookup
|
|
|
|
* takes tidhash first and needs to return locked proc.
|
|
|
|
*
|
|
|
|
* The problem is worked around by relying on type-safety of both
|
|
|
|
* structures and doing the work in 2 steps:
|
|
|
|
* - tidhash-locked lookup which saves both thread and proc pointers
|
|
|
|
* - proc-locked verification that the found thread still matches
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
tdfind_hash(lwpid_t tid, pid_t pid, struct proc **pp, struct thread **tdp)
|
|
|
|
{
|
|
|
|
#define RUN_THRESH 16
|
|
|
|
struct proc *p;
|
|
|
|
struct thread *td;
|
|
|
|
int run;
|
|
|
|
bool locked;
|
|
|
|
|
|
|
|
run = 0;
|
2020-11-11 08:51:04 +00:00
|
|
|
rw_rlock(TIDHASHLOCK(tid));
|
2020-11-11 08:50:04 +00:00
|
|
|
locked = true;
|
|
|
|
LIST_FOREACH(td, TIDHASH(tid), td_hash) {
|
|
|
|
if (td->td_tid != tid) {
|
|
|
|
run++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p = td->td_proc;
|
|
|
|
if (pid != -1 && p->p_pid != pid) {
|
|
|
|
td = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (run > RUN_THRESH) {
|
2020-11-11 08:51:04 +00:00
|
|
|
if (rw_try_upgrade(TIDHASHLOCK(tid))) {
|
2020-11-11 08:50:04 +00:00
|
|
|
LIST_REMOVE(td, td_hash);
|
|
|
|
LIST_INSERT_HEAD(TIDHASH(td->td_tid),
|
|
|
|
td, td_hash);
|
2020-11-11 08:51:04 +00:00
|
|
|
rw_wunlock(TIDHASHLOCK(tid));
|
2020-11-11 08:50:04 +00:00
|
|
|
locked = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (locked)
|
2020-11-11 08:51:04 +00:00
|
|
|
rw_runlock(TIDHASHLOCK(tid));
|
2020-11-11 08:50:04 +00:00
|
|
|
if (td == NULL)
|
|
|
|
return (false);
|
|
|
|
*pp = p;
|
|
|
|
*tdp = td;
|
|
|
|
return (true);
|
|
|
|
}
|
|
|
|
|
2010-10-09 02:50:23 +00:00
|
|
|
struct thread *
|
|
|
|
tdfind(lwpid_t tid, pid_t pid)
|
|
|
|
{
|
2020-11-11 08:50:04 +00:00
|
|
|
struct proc *p;
|
2010-10-09 02:50:23 +00:00
|
|
|
struct thread *td;
|
|
|
|
|
2020-07-18 00:14:43 +00:00
|
|
|
td = curthread;
|
|
|
|
if (td->td_tid == tid) {
|
|
|
|
if (pid != -1 && td->td_proc->p_pid != pid)
|
|
|
|
return (NULL);
|
|
|
|
PROC_LOCK(td->td_proc);
|
|
|
|
return (td);
|
|
|
|
}
|
|
|
|
|
2020-11-11 08:50:04 +00:00
|
|
|
for (;;) {
|
|
|
|
if (!tdfind_hash(tid, pid, &p, &td))
|
|
|
|
return (NULL);
|
|
|
|
PROC_LOCK(p);
|
|
|
|
if (td->td_tid != tid) {
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (td->td_proc != p) {
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
continue;
|
2010-10-09 02:50:23 +00:00
|
|
|
}
|
2020-11-11 08:50:04 +00:00
|
|
|
if (p->p_state == PRS_NEW) {
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
return (td);
|
2010-10-09 02:50:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tidhash_add(struct thread *td)
|
|
|
|
{
|
2020-11-11 08:51:04 +00:00
|
|
|
rw_wlock(TIDHASHLOCK(td->td_tid));
|
2010-10-17 11:01:52 +00:00
|
|
|
LIST_INSERT_HEAD(TIDHASH(td->td_tid), td, td_hash);
|
2020-11-11 08:51:04 +00:00
|
|
|
rw_wunlock(TIDHASHLOCK(td->td_tid));
|
2010-10-09 02:50:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
tidhash_remove(struct thread *td)
|
|
|
|
{
|
2020-11-11 08:51:04 +00:00
|
|
|
|
|
|
|
rw_wlock(TIDHASHLOCK(td->td_tid));
|
2010-10-17 11:01:52 +00:00
|
|
|
LIST_REMOVE(td, td_hash);
|
2020-11-11 08:51:04 +00:00
|
|
|
rw_wunlock(TIDHASHLOCK(td->td_tid));
|
2010-10-09 02:50:23 +00:00
|
|
|
}
|