2004-01-10 18:34:01 +00:00
|
|
|
/*
|
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.
|
|
|
|
*/
|
|
|
|
|
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/malloc.h>
|
|
|
|
#include <sys/mutex.h>
|
|
|
|
#include <sys/proc.h>
|
2002-11-17 23:26:42 +00:00
|
|
|
#include <sys/smp.h>
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <sys/sysctl.h>
|
2002-10-24 08:46:34 +00:00
|
|
|
#include <sys/sysproto.h>
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <sys/filedesc.h>
|
2002-11-21 01:22:38 +00:00
|
|
|
#include <sys/sched.h>
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <sys/signalvar.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>
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <sys/sx.h>
|
2002-11-21 01:22:38 +00:00
|
|
|
#include <sys/tty.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>
|
2002-06-29 07:04:59 +00:00
|
|
|
#include <sys/user.h>
|
|
|
|
#include <sys/kse.h>
|
|
|
|
#include <sys/ktr.h>
|
2002-09-16 19:26:48 +00:00
|
|
|
#include <sys/ucontext.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/vm_object.h>
|
|
|
|
#include <vm/pmap.h>
|
|
|
|
#include <vm/uma.h>
|
|
|
|
#include <vm/vm_map.h>
|
|
|
|
|
2002-07-17 23:43:55 +00:00
|
|
|
#include <machine/frame.h>
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
2002-09-15 23:52:25 +00:00
|
|
|
* KSEGRP related storage.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2002-09-15 23:52:25 +00:00
|
|
|
static uma_zone_t ksegrp_zone;
|
|
|
|
static uma_zone_t kse_zone;
|
2002-06-29 07:04:59 +00:00
|
|
|
static uma_zone_t thread_zone;
|
2003-02-17 05:14:26 +00:00
|
|
|
static uma_zone_t upcall_zone;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2002-09-15 23:52:25 +00:00
|
|
|
/* DEBUG ONLY */
|
2002-06-29 07:04:59 +00:00
|
|
|
SYSCTL_NODE(_kern, OID_AUTO, threads, CTLFLAG_RW, 0, "thread allocation");
|
2002-12-10 02:33:45 +00:00
|
|
|
static int thread_debug = 0;
|
|
|
|
SYSCTL_INT(_kern_threads, OID_AUTO, debug, CTLFLAG_RW,
|
|
|
|
&thread_debug, 0, "thread debug");
|
2002-11-17 11:47:03 +00:00
|
|
|
|
2004-03-21 09:22:38 +00:00
|
|
|
static int max_threads_per_proc = 1500;
|
2002-11-17 11:47:03 +00:00
|
|
|
SYSCTL_INT(_kern_threads, OID_AUTO, max_threads_per_proc, CTLFLAG_RW,
|
2002-09-15 23:52:25 +00:00
|
|
|
&max_threads_per_proc, 0, "Limit on threads per proc");
|
|
|
|
|
2004-03-21 09:22:38 +00:00
|
|
|
static int max_groups_per_proc = 500;
|
2002-11-17 11:47:03 +00:00
|
|
|
SYSCTL_INT(_kern_threads, OID_AUTO, max_groups_per_proc, CTLFLAG_RW,
|
|
|
|
&max_groups_per_proc, 0, "Limit on thread groups per proc");
|
|
|
|
|
2003-02-19 04:01:55 +00:00
|
|
|
static int max_threads_hits;
|
|
|
|
SYSCTL_INT(_kern_threads, OID_AUTO, max_threads_hits, CTLFLAG_RD,
|
|
|
|
&max_threads_hits, 0, "");
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
static int virtual_cpu;
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
#define RANGEOF(type, start, end) (offsetof(type, end) - offsetof(type, start))
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
TAILQ_HEAD(, thread) zombie_threads = TAILQ_HEAD_INITIALIZER(zombie_threads);
|
2002-10-24 08:46:34 +00:00
|
|
|
TAILQ_HEAD(, kse) zombie_kses = TAILQ_HEAD_INITIALIZER(zombie_kses);
|
|
|
|
TAILQ_HEAD(, ksegrp) zombie_ksegrps = TAILQ_HEAD_INITIALIZER(zombie_ksegrps);
|
2004-01-10 18:34:01 +00:00
|
|
|
TAILQ_HEAD(, kse_upcall) zombie_upcalls =
|
2003-02-17 05:14:26 +00:00
|
|
|
TAILQ_HEAD_INITIALIZER(zombie_upcalls);
|
|
|
|
struct mtx kse_zombie_lock;
|
|
|
|
MTX_SYSINIT(kse_zombie_lock, &kse_zombie_lock, "kse zombie lock", MTX_SPIN);
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2002-12-10 02:33:45 +00:00
|
|
|
static void kse_purge(struct proc *p, struct thread *td);
|
2003-02-17 05:14:26 +00:00
|
|
|
static void kse_purge_group(struct thread *td);
|
2003-02-26 00:58:23 +00:00
|
|
|
static int thread_update_usr_ticks(struct thread *td, int user);
|
2003-02-17 05:14:26 +00:00
|
|
|
static void thread_alloc_spare(struct thread *td, struct thread *spare);
|
|
|
|
|
|
|
|
static int
|
|
|
|
sysctl_kse_virtual_cpu(SYSCTL_HANDLER_ARGS)
|
|
|
|
{
|
|
|
|
int error, new_val;
|
|
|
|
int def_val;
|
|
|
|
|
|
|
|
#ifdef SMP
|
|
|
|
def_val = mp_ncpus;
|
|
|
|
#else
|
|
|
|
def_val = 1;
|
|
|
|
#endif
|
|
|
|
if (virtual_cpu == 0)
|
|
|
|
new_val = def_val;
|
|
|
|
else
|
|
|
|
new_val = virtual_cpu;
|
|
|
|
error = sysctl_handle_int(oidp, &new_val, 0, req);
|
|
|
|
if (error != 0 || req->newptr == NULL)
|
|
|
|
return (error);
|
|
|
|
if (new_val < 0)
|
|
|
|
return (EINVAL);
|
|
|
|
virtual_cpu = new_val;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DEBUG ONLY */
|
|
|
|
SYSCTL_PROC(_kern_threads, OID_AUTO, virtual_cpu, CTLTYPE_INT|CTLFLAG_RW,
|
|
|
|
0, sizeof(virtual_cpu), sysctl_kse_virtual_cpu, "I",
|
|
|
|
"debug virtual cpus");
|
2002-10-24 08:46:34 +00:00
|
|
|
|
2004-04-03 15:59:13 +00:00
|
|
|
/*
|
|
|
|
* Thread ID allocator. The allocator keeps track of assigned IDs by
|
|
|
|
* using a bitmap. The bitmap is created in parts. The parts are linked
|
|
|
|
* together.
|
|
|
|
*/
|
|
|
|
typedef u_long tid_bitmap_word;
|
|
|
|
|
|
|
|
#define TID_IDS_PER_PART 1024
|
|
|
|
#define TID_IDS_PER_IDX (sizeof(tid_bitmap_word) << 3)
|
|
|
|
#define TID_BITMAP_SIZE (TID_IDS_PER_PART / TID_IDS_PER_IDX)
|
|
|
|
#define TID_MIN (PID_MAX + 1)
|
|
|
|
|
|
|
|
struct tid_bitmap_part {
|
|
|
|
STAILQ_ENTRY(tid_bitmap_part) bmp_next;
|
|
|
|
tid_bitmap_word bmp_bitmap[TID_BITMAP_SIZE];
|
|
|
|
int bmp_base;
|
|
|
|
int bmp_free;
|
|
|
|
};
|
|
|
|
|
|
|
|
static STAILQ_HEAD(, tid_bitmap_part) tid_bitmap =
|
|
|
|
STAILQ_HEAD_INITIALIZER(tid_bitmap);
|
|
|
|
static uma_zone_t tid_zone;
|
|
|
|
|
|
|
|
struct mtx tid_lock;
|
|
|
|
MTX_SYSINIT(tid_lock, &tid_lock, "TID lock", MTX_DEF);
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
static void
|
|
|
|
thread_ctor(void *mem, int size, void *arg)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
|
|
|
|
td = (struct thread *)mem;
|
2004-04-03 15:59:13 +00:00
|
|
|
td->td_tid = 0;
|
2002-09-11 08:13:56 +00:00
|
|
|
td->td_state = TDS_INACTIVE;
|
2003-04-10 17:35:44 +00:00
|
|
|
td->td_oncpu = NOCPU;
|
2003-08-04 20:28:20 +00:00
|
|
|
td->td_critnest = 1;
|
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;
|
|
|
|
struct tid_bitmap_part *bmp;
|
|
|
|
int bit, idx, tid;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
td = (struct thread *)mem;
|
|
|
|
|
2004-04-03 15:59:13 +00:00
|
|
|
if (td->td_tid > PID_MAX) {
|
|
|
|
STAILQ_FOREACH(bmp, &tid_bitmap, bmp_next) {
|
|
|
|
if (td->td_tid >= bmp->bmp_base &&
|
|
|
|
td->td_tid < bmp->bmp_base + TID_IDS_PER_PART)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
KASSERT(bmp != NULL, ("No TID bitmap?"));
|
|
|
|
mtx_lock(&tid_lock);
|
|
|
|
tid = td->td_tid - bmp->bmp_base;
|
|
|
|
idx = tid / TID_IDS_PER_IDX;
|
|
|
|
bit = 1UL << (tid % TID_IDS_PER_IDX);
|
|
|
|
bmp->bmp_bitmap[idx] |= bit;
|
|
|
|
bmp->bmp_free++;
|
|
|
|
mtx_unlock(&tid_lock);
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
#ifdef INVARIANTS
|
|
|
|
/* Verify that this thread is in a safe state to free. */
|
|
|
|
switch (td->td_state) {
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize type-stable parts of a thread (when newly created).
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
thread_init(void *mem, int size)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
|
|
|
|
td = (struct thread *)mem;
|
2003-06-14 23:23:55 +00:00
|
|
|
vm_thread_new(td, 0);
|
2002-06-29 07:04:59 +00:00
|
|
|
cpu_thread_setup(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();
|
2002-11-21 01:22:38 +00:00
|
|
|
td->td_sched = (struct td_sched *)&td[1];
|
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)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
|
|
|
|
td = (struct thread *)mem;
|
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);
|
2003-06-14 23:23:55 +00:00
|
|
|
vm_thread_dispose(td);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
|
2002-11-21 01:22:38 +00:00
|
|
|
/*
|
|
|
|
* Initialize type-stable parts of a kse (when newly created).
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
kse_init(void *mem, int size)
|
|
|
|
{
|
|
|
|
struct kse *ke;
|
|
|
|
|
|
|
|
ke = (struct kse *)mem;
|
|
|
|
ke->ke_sched = (struct ke_sched *)&ke[1];
|
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
|
2002-11-21 01:22:38 +00:00
|
|
|
/*
|
|
|
|
* Initialize type-stable parts of a ksegrp (when newly created).
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
ksegrp_init(void *mem, int size)
|
|
|
|
{
|
|
|
|
struct ksegrp *kg;
|
|
|
|
|
|
|
|
kg = (struct ksegrp *)mem;
|
|
|
|
kg->kg_sched = (struct kg_sched *)&kg[1];
|
|
|
|
}
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* KSE is linked into kse group.
|
2002-10-24 08:46:34 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
kse_link(struct kse *ke, struct ksegrp *kg)
|
|
|
|
{
|
|
|
|
struct proc *p = kg->kg_proc;
|
|
|
|
|
|
|
|
TAILQ_INSERT_HEAD(&kg->kg_kseq, ke, ke_kglist);
|
|
|
|
kg->kg_kses++;
|
2003-02-17 05:14:26 +00:00
|
|
|
ke->ke_state = KES_UNQUEUED;
|
2002-10-24 08:46:34 +00:00
|
|
|
ke->ke_proc = p;
|
|
|
|
ke->ke_ksegrp = kg;
|
|
|
|
ke->ke_thread = NULL;
|
2003-02-17 05:14:26 +00:00
|
|
|
ke->ke_oncpu = NOCPU;
|
|
|
|
ke->ke_flags = 0;
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
kse_unlink(struct kse *ke)
|
|
|
|
{
|
|
|
|
struct ksegrp *kg;
|
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
|
|
|
kg = ke->ke_ksegrp;
|
|
|
|
TAILQ_REMOVE(&kg->kg_kseq, ke, ke_kglist);
|
2003-02-17 05:14:26 +00:00
|
|
|
if (ke->ke_state == KES_IDLE) {
|
|
|
|
TAILQ_REMOVE(&kg->kg_iq, ke, ke_kgrlist);
|
|
|
|
kg->kg_idle_kses--;
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
2003-08-26 11:33:15 +00:00
|
|
|
--kg->kg_kses;
|
2002-10-24 08:46:34 +00:00
|
|
|
/*
|
|
|
|
* Aggregate stats from the KSE
|
|
|
|
*/
|
|
|
|
kse_stash(ke);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ksegrp_link(struct ksegrp *kg, struct proc *p)
|
|
|
|
{
|
|
|
|
|
|
|
|
TAILQ_INIT(&kg->kg_threads);
|
|
|
|
TAILQ_INIT(&kg->kg_runq); /* links with td_runq */
|
|
|
|
TAILQ_INIT(&kg->kg_slpq); /* links with td_runq */
|
|
|
|
TAILQ_INIT(&kg->kg_kseq); /* all kses in ksegrp */
|
2003-02-17 05:14:26 +00:00
|
|
|
TAILQ_INIT(&kg->kg_iq); /* all idle kses in ksegrp */
|
|
|
|
TAILQ_INIT(&kg->kg_upcalls); /* all upcall structure in ksegrp */
|
|
|
|
kg->kg_proc = p;
|
|
|
|
/*
|
|
|
|
* the following counters are in the -zero- section
|
|
|
|
* and may not need clearing
|
|
|
|
*/
|
2002-10-24 08:46:34 +00:00
|
|
|
kg->kg_numthreads = 0;
|
2003-02-17 05:14:26 +00:00
|
|
|
kg->kg_runnable = 0;
|
|
|
|
kg->kg_kses = 0;
|
|
|
|
kg->kg_runq_kses = 0; /* XXXKSE change name */
|
|
|
|
kg->kg_idle_kses = 0;
|
|
|
|
kg->kg_numupcalls = 0;
|
|
|
|
/* link it in now that it's consistent */
|
2002-10-24 08:46:34 +00:00
|
|
|
p->p_numksegrps++;
|
|
|
|
TAILQ_INSERT_HEAD(&p->p_ksegrps, kg, kg_ksegrp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ksegrp_unlink(struct ksegrp *kg)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
2003-02-17 05:14:26 +00:00
|
|
|
KASSERT((kg->kg_numthreads == 0), ("ksegrp_unlink: residual threads"));
|
|
|
|
KASSERT((kg->kg_kses == 0), ("ksegrp_unlink: residual kses"));
|
|
|
|
KASSERT((kg->kg_numupcalls == 0), ("ksegrp_unlink: residual upcalls"));
|
|
|
|
|
2002-10-24 08:46:34 +00:00
|
|
|
p = kg->kg_proc;
|
|
|
|
TAILQ_REMOVE(&p->p_ksegrps, kg, kg_ksegrp);
|
|
|
|
p->p_numksegrps--;
|
|
|
|
/*
|
|
|
|
* Aggregate stats from the KSE
|
|
|
|
*/
|
|
|
|
ksegrp_stash(kg);
|
|
|
|
}
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
struct kse_upcall *
|
|
|
|
upcall_alloc(void)
|
|
|
|
{
|
|
|
|
struct kse_upcall *ku;
|
|
|
|
|
2003-02-19 09:59:12 +00:00
|
|
|
ku = uma_zalloc(upcall_zone, M_WAITOK);
|
2003-02-17 05:14:26 +00:00
|
|
|
bzero(ku, sizeof(*ku));
|
|
|
|
return (ku);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
upcall_free(struct kse_upcall *ku)
|
|
|
|
{
|
|
|
|
|
|
|
|
uma_zfree(upcall_zone, ku);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
upcall_link(struct kse_upcall *ku, struct ksegrp *kg)
|
|
|
|
{
|
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
|
|
|
TAILQ_INSERT_TAIL(&kg->kg_upcalls, ku, ku_link);
|
|
|
|
ku->ku_ksegrp = kg;
|
|
|
|
kg->kg_numupcalls++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
upcall_unlink(struct kse_upcall *ku)
|
|
|
|
{
|
|
|
|
struct ksegrp *kg = ku->ku_ksegrp;
|
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
|
|
|
KASSERT(ku->ku_owner == NULL, ("%s: have owner", __func__));
|
2004-01-10 18:34:01 +00:00
|
|
|
TAILQ_REMOVE(&kg->kg_upcalls, ku, ku_link);
|
2003-02-17 05:14:26 +00:00
|
|
|
kg->kg_numupcalls--;
|
|
|
|
upcall_stash(ku);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
upcall_remove(struct thread *td)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (td->td_upcall) {
|
|
|
|
td->td_upcall->ku_owner = NULL;
|
|
|
|
upcall_unlink(td->td_upcall);
|
|
|
|
td->td_upcall = 0;
|
2004-01-10 18:34:01 +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.
|
2002-10-24 08:46:34 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
proc_linkup(struct proc *p, struct ksegrp *kg,
|
2003-02-17 05:14:26 +00:00
|
|
|
struct kse *ke, struct thread *td)
|
2002-10-24 08:46:34 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
TAILQ_INIT(&p->p_ksegrps); /* all ksegrps in proc */
|
|
|
|
TAILQ_INIT(&p->p_threads); /* all threads in proc */
|
|
|
|
TAILQ_INIT(&p->p_suspended); /* Threads suspended */
|
|
|
|
p->p_numksegrps = 0;
|
|
|
|
p->p_numthreads = 0;
|
|
|
|
|
|
|
|
ksegrp_link(kg, p);
|
|
|
|
kse_link(ke, kg);
|
|
|
|
thread_link(td, kg);
|
|
|
|
}
|
|
|
|
|
2003-12-07 19:34:29 +00:00
|
|
|
#ifndef _SYS_SYSPROTO_H_
|
|
|
|
struct kse_switchin_args {
|
|
|
|
const struct __mcontext *mcp;
|
|
|
|
long val;
|
|
|
|
long *loc;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int
|
|
|
|
kse_switchin(struct thread *td, struct kse_switchin_args *uap)
|
|
|
|
{
|
|
|
|
mcontext_t mc;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = (uap->mcp == NULL) ? EINVAL : 0;
|
|
|
|
if (!error)
|
|
|
|
error = copyin(uap->mcp, &mc, sizeof(mc));
|
2003-12-10 01:59:23 +00:00
|
|
|
if (!error && uap->loc != NULL)
|
|
|
|
error = (suword(uap->loc, uap->val) != 0) ? EINVAL : 0;
|
2003-12-07 19:34:29 +00:00
|
|
|
if (!error)
|
|
|
|
error = set_mcontext(td, &mc);
|
|
|
|
return ((error == 0) ? EJUSTRETURN : error);
|
|
|
|
}
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
struct kse_thr_interrupt_args {
|
|
|
|
struct kse_thr_mailbox * tmbx;
|
2003-07-17 22:45:33 +00:00
|
|
|
int cmd;
|
|
|
|
long data;
|
2003-02-17 05:14:26 +00:00
|
|
|
};
|
|
|
|
*/
|
2002-10-24 08:46:34 +00:00
|
|
|
int
|
|
|
|
kse_thr_interrupt(struct thread *td, struct kse_thr_interrupt_args *uap)
|
|
|
|
{
|
2002-10-30 02:28:41 +00:00
|
|
|
struct proc *p;
|
|
|
|
struct thread *td2;
|
2002-10-24 08:46:34 +00:00
|
|
|
|
2002-10-31 08:00:51 +00:00
|
|
|
p = td->td_proc;
|
2003-08-26 11:33:15 +00:00
|
|
|
|
2003-07-17 22:45:33 +00:00
|
|
|
if (!(p->p_flag & P_SA))
|
2002-10-30 05:09:29 +00:00
|
|
|
return (EINVAL);
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
|
2003-07-17 22:45:33 +00:00
|
|
|
switch (uap->cmd) {
|
|
|
|
case KSE_INTR_SENDSIG:
|
|
|
|
if (uap->data < 0 || uap->data > _SIG_MAXSIG)
|
|
|
|
return (EINVAL);
|
|
|
|
case KSE_INTR_INTERRUPT:
|
|
|
|
case KSE_INTR_RESTART:
|
|
|
|
PROC_LOCK(p);
|
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
FOREACH_THREAD_IN_PROC(p, td2) {
|
|
|
|
if (td2->td_mailbox == uap->tmbx)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (td2 == NULL) {
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
return (ESRCH);
|
|
|
|
}
|
|
|
|
if (uap->cmd == KSE_INTR_SENDSIG) {
|
|
|
|
if (uap->data > 0) {
|
|
|
|
td2->td_flags &= ~TDF_INTERRUPT;
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
tdsignal(td2, (int)uap->data, SIGTARGET_TD);
|
|
|
|
} else {
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
td2->td_flags |= TDF_INTERRUPT | TDF_ASTPENDING;
|
|
|
|
if (TD_CAN_UNBIND(td2))
|
|
|
|
td2->td_upcall->ku_flags |= KUF_DOUPCALL;
|
|
|
|
if (uap->cmd == KSE_INTR_INTERRUPT)
|
|
|
|
td2->td_intrval = EINTR;
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
else
|
2003-07-17 22:45:33 +00:00
|
|
|
td2->td_intrval = ERESTART;
|
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
|
|
|
if (TD_ON_SLEEPQ(td2) && (td2->td_flags & TDF_SINTR))
|
|
|
|
sleepq_abort(td2);
|
2003-07-17 22:45:33 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2002-10-30 02:28:41 +00:00
|
|
|
}
|
2003-07-17 22:45:33 +00:00
|
|
|
PROC_UNLOCK(p);
|
|
|
|
break;
|
|
|
|
case KSE_INTR_SIGEXIT:
|
|
|
|
if (uap->data < 1 || uap->data > _SIG_MAXSIG)
|
|
|
|
return (EINVAL);
|
|
|
|
PROC_LOCK(p);
|
|
|
|
sigexit(td, (int)uap->data);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return (EINVAL);
|
2002-10-30 02:28:41 +00:00
|
|
|
}
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
return (0);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
struct kse_exit_args {
|
|
|
|
register_t dummy;
|
|
|
|
};
|
|
|
|
*/
|
2002-10-24 08:46:34 +00:00
|
|
|
int
|
|
|
|
kse_exit(struct thread *td, struct kse_exit_args *uap)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
struct ksegrp *kg;
|
2003-01-04 05:59:25 +00:00
|
|
|
struct kse *ke;
|
2003-06-04 00:12:57 +00:00
|
|
|
struct kse_upcall *ku, *ku2;
|
|
|
|
int error, count;
|
2002-10-24 08:46:34 +00:00
|
|
|
|
|
|
|
p = td->td_proc;
|
2003-06-04 00:12:57 +00:00
|
|
|
if ((ku = td->td_upcall) == NULL || TD_CAN_UNBIND(td))
|
2002-10-30 03:01:28 +00:00
|
|
|
return (EINVAL);
|
2002-10-24 08:46:34 +00:00
|
|
|
kg = td->td_ksegrp;
|
2003-06-04 00:12:57 +00:00
|
|
|
count = 0;
|
2002-10-24 08:46:34 +00:00
|
|
|
PROC_LOCK(p);
|
|
|
|
mtx_lock_spin(&sched_lock);
|
2003-06-04 00:12:57 +00:00
|
|
|
FOREACH_UPCALL_IN_GROUP(kg, ku2) {
|
|
|
|
if (ku2->ku_flags & KUF_EXITING)
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
if ((kg->kg_numupcalls - count) == 1 &&
|
|
|
|
(kg->kg_numthreads > 1)) {
|
2002-10-24 08:46:34 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
return (EDEADLK);
|
|
|
|
}
|
2003-06-04 00:12:57 +00:00
|
|
|
ku->ku_flags |= KUF_EXITING;
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
error = suword(&ku->ku_mailbox->km_flags, ku->ku_mflags|KMF_DONE);
|
|
|
|
PROC_LOCK(p);
|
|
|
|
if (error)
|
|
|
|
psignal(p, SIGSEGV);
|
|
|
|
mtx_lock_spin(&sched_lock);
|
2003-02-17 05:14:26 +00:00
|
|
|
upcall_remove(td);
|
2003-06-04 00:12:57 +00:00
|
|
|
ke = td->td_kse;
|
2003-01-04 05:59:25 +00:00
|
|
|
if (p->p_numthreads == 1) {
|
2003-02-17 05:14:26 +00:00
|
|
|
kse_purge(p, td);
|
2003-06-15 00:31:24 +00:00
|
|
|
p->p_flag &= ~P_SA;
|
2002-10-24 08:46:34 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
} else {
|
2003-02-17 05:14:26 +00:00
|
|
|
if (kg->kg_numthreads == 1) { /* Shutdown a group */
|
|
|
|
kse_purge_group(td);
|
|
|
|
ke->ke_flags |= KEF_EXIT;
|
|
|
|
}
|
2003-03-11 00:07:53 +00:00
|
|
|
thread_stopped(p);
|
2002-10-24 08:46:34 +00:00
|
|
|
thread_exit();
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
2002-10-30 03:01:28 +00:00
|
|
|
return (0);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
2002-12-10 02:33:45 +00:00
|
|
|
/*
|
2002-12-28 01:23:07 +00:00
|
|
|
* Either becomes an upcall or waits for an awakening event and
|
2003-02-17 05:14:26 +00:00
|
|
|
* then becomes an upcall. Only error cases return.
|
2002-12-10 02:33:45 +00:00
|
|
|
*/
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
struct kse_release_args {
|
2003-02-20 08:18:15 +00:00
|
|
|
struct timespec *timeout;
|
2003-02-17 05:14:26 +00:00
|
|
|
};
|
|
|
|
*/
|
2002-10-24 08:46:34 +00:00
|
|
|
int
|
2003-02-17 05:14:26 +00:00
|
|
|
kse_release(struct thread *td, struct kse_release_args *uap)
|
2002-10-24 08:46:34 +00:00
|
|
|
{
|
|
|
|
struct proc *p;
|
2002-12-10 02:33:45 +00:00
|
|
|
struct ksegrp *kg;
|
2003-06-15 12:51:26 +00:00
|
|
|
struct kse_upcall *ku;
|
|
|
|
struct timespec timeout;
|
2003-02-20 08:18:15 +00:00
|
|
|
struct timeval tv;
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
sigset_t sigset;
|
2003-02-20 08:18:15 +00:00
|
|
|
int error;
|
2002-10-24 08:46:34 +00:00
|
|
|
|
|
|
|
p = td->td_proc;
|
2002-12-10 02:33:45 +00:00
|
|
|
kg = td->td_ksegrp;
|
2003-06-15 12:51:26 +00:00
|
|
|
if ((ku = td->td_upcall) == NULL || TD_CAN_UNBIND(td))
|
2002-12-10 02:33:45 +00:00
|
|
|
return (EINVAL);
|
2003-02-20 08:18:15 +00:00
|
|
|
if (uap->timeout != NULL) {
|
|
|
|
if ((error = copyin(uap->timeout, &timeout, sizeof(timeout))))
|
|
|
|
return (error);
|
|
|
|
TIMESPEC_TO_TIMEVAL(&tv, &timeout);
|
|
|
|
}
|
2003-06-15 12:51:26 +00:00
|
|
|
if (td->td_flags & TDF_SA)
|
|
|
|
td->td_pflags |= TDP_UPCALLING;
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
else {
|
|
|
|
ku->ku_mflags = fuword(&ku->ku_mailbox->km_flags);
|
|
|
|
if (ku->ku_mflags == -1) {
|
|
|
|
PROC_LOCK(p);
|
|
|
|
sigexit(td, SIGSEGV);
|
|
|
|
}
|
|
|
|
}
|
2003-02-20 08:18:15 +00:00
|
|
|
PROC_LOCK(p);
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
if (ku->ku_mflags & KMF_WAITSIGEVENT) {
|
|
|
|
/* UTS wants to wait for signal event */
|
|
|
|
if (!(p->p_flag & P_SIGEVENT) && !(ku->ku_flags & KUF_DOUPCALL))
|
|
|
|
error = msleep(&p->p_siglist, &p->p_mtx, PPAUSE|PCATCH,
|
|
|
|
"ksesigwait", (uap->timeout ? tvtohz(&tv) : 0));
|
|
|
|
p->p_flag &= ~P_SIGEVENT;
|
|
|
|
sigset = p->p_siglist;
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
error = copyout(&sigset, &ku->ku_mailbox->km_sigscaught,
|
|
|
|
sizeof(sigset));
|
|
|
|
} else {
|
|
|
|
if (! kg->kg_completed && !(ku->ku_flags & KUF_DOUPCALL)) {
|
|
|
|
kg->kg_upsleeps++;
|
|
|
|
error = msleep(&kg->kg_completed, &p->p_mtx,
|
|
|
|
PPAUSE|PCATCH, "kserel",
|
|
|
|
(uap->timeout ? tvtohz(&tv) : 0));
|
|
|
|
kg->kg_upsleeps--;
|
|
|
|
}
|
|
|
|
PROC_UNLOCK(p);
|
2003-06-15 12:51:26 +00:00
|
|
|
}
|
|
|
|
if (ku->ku_flags & KUF_DOUPCALL) {
|
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
ku->ku_flags &= ~KUF_DOUPCALL;
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
2002-12-10 02:33:45 +00:00
|
|
|
return (0);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* struct kse_wakeup_args {
|
|
|
|
struct kse_mailbox *mbx;
|
|
|
|
}; */
|
|
|
|
int
|
|
|
|
kse_wakeup(struct thread *td, struct kse_wakeup_args *uap)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
struct ksegrp *kg;
|
2003-02-17 05:14:26 +00:00
|
|
|
struct kse_upcall *ku;
|
2002-12-28 01:23:07 +00:00
|
|
|
struct thread *td2;
|
2002-10-24 08:46:34 +00:00
|
|
|
|
|
|
|
p = td->td_proc;
|
2002-12-28 01:23:07 +00:00
|
|
|
td2 = NULL;
|
2003-02-17 05:14:26 +00:00
|
|
|
ku = NULL;
|
2002-10-24 08:46:34 +00:00
|
|
|
/* KSE-enabled processes only, please. */
|
2003-06-15 00:31:24 +00:00
|
|
|
if (!(p->p_flag & P_SA))
|
2003-02-17 05:14:26 +00:00
|
|
|
return (EINVAL);
|
|
|
|
PROC_LOCK(p);
|
2003-01-03 20:41:49 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
if (uap->mbx) {
|
|
|
|
FOREACH_KSEGRP_IN_PROC(p, kg) {
|
2003-02-17 05:14:26 +00:00
|
|
|
FOREACH_UPCALL_IN_GROUP(kg, ku) {
|
2003-02-21 07:11:38 +00:00
|
|
|
if (ku->ku_mailbox == uap->mbx)
|
2003-02-17 05:14:26 +00:00
|
|
|
break;
|
2003-01-03 20:41:49 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
if (ku)
|
2002-12-28 01:23:07 +00:00
|
|
|
break;
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
kg = td->td_ksegrp;
|
2003-02-17 05:14:26 +00:00
|
|
|
if (kg->kg_upsleeps) {
|
|
|
|
wakeup_one(&kg->kg_completed);
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
return (0);
|
2002-12-28 01:23:07 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
ku = TAILQ_FIRST(&kg->kg_upcalls);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
if (ku) {
|
|
|
|
if ((td2 = ku->ku_owner) == NULL) {
|
|
|
|
panic("%s: no owner", __func__);
|
2004-03-01 23:07:58 +00:00
|
|
|
} else if (TD_ON_SLEEPQ(td2) && (td2->td_flags & TDF_SINTR) &&
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
((td2->td_wchan == &kg->kg_completed) ||
|
|
|
|
(td2->td_wchan == &p->p_siglist &&
|
|
|
|
(ku->ku_mflags & KMF_WAITSIGEVENT)))) {
|
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_abort(td2);
|
2003-02-17 05:14:26 +00:00
|
|
|
} else {
|
|
|
|
ku->ku_flags |= KUF_DOUPCALL;
|
2003-01-03 20:41:49 +00:00
|
|
|
}
|
2002-10-24 08:46:34 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-02-17 05:14:26 +00:00
|
|
|
PROC_UNLOCK(p);
|
2002-12-28 01:23:07 +00:00
|
|
|
return (0);
|
2003-01-03 20:41:49 +00:00
|
|
|
}
|
2002-10-24 08:46:34 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-02-17 05:14:26 +00:00
|
|
|
PROC_UNLOCK(p);
|
2002-12-28 01:23:07 +00:00
|
|
|
return (ESRCH);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2002-10-24 08:46:34 +00:00
|
|
|
* No new KSEG: first call: use current KSE, don't schedule an upcall
|
2003-02-17 05:14:26 +00:00
|
|
|
* All other situations, do allocate max new KSEs and schedule an upcall.
|
2002-10-24 08:46:34 +00:00
|
|
|
*/
|
|
|
|
/* struct kse_create_args {
|
|
|
|
struct kse_mailbox *mbx;
|
|
|
|
int newgroup;
|
|
|
|
}; */
|
|
|
|
int
|
|
|
|
kse_create(struct thread *td, struct kse_create_args *uap)
|
|
|
|
{
|
|
|
|
struct kse *newke;
|
|
|
|
struct ksegrp *newkg;
|
|
|
|
struct ksegrp *kg;
|
|
|
|
struct proc *p;
|
|
|
|
struct kse_mailbox mbx;
|
2003-02-17 05:14:26 +00:00
|
|
|
struct kse_upcall *newku;
|
2003-06-15 12:51:26 +00:00
|
|
|
int err, ncpus, sa = 0, first = 0;
|
|
|
|
struct thread *newtd;
|
2002-10-24 08:46:34 +00:00
|
|
|
|
|
|
|
p = td->td_proc;
|
|
|
|
if ((err = copyin(uap->mbx, &mbx, sizeof(mbx))))
|
|
|
|
return (err);
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/* Too bad, why hasn't kernel always a cpu counter !? */
|
|
|
|
#ifdef SMP
|
|
|
|
ncpus = mp_ncpus;
|
|
|
|
#else
|
|
|
|
ncpus = 1;
|
|
|
|
#endif
|
2003-06-15 12:51:26 +00:00
|
|
|
if (virtual_cpu != 0)
|
2003-02-17 05:14:26 +00:00
|
|
|
ncpus = virtual_cpu;
|
2003-06-15 12:51:26 +00:00
|
|
|
if (!(mbx.km_flags & KMF_BOUND))
|
|
|
|
sa = TDF_SA;
|
2003-06-16 13:14:52 +00:00
|
|
|
else
|
|
|
|
ncpus = 1;
|
2003-03-11 03:16:02 +00:00
|
|
|
PROC_LOCK(p);
|
2003-06-15 12:51:26 +00:00
|
|
|
if (!(p->p_flag & P_SA)) {
|
|
|
|
first = 1;
|
|
|
|
p->p_flag |= P_SA;
|
|
|
|
}
|
2003-03-11 03:16:02 +00:00
|
|
|
PROC_UNLOCK(p);
|
2003-06-15 12:51:26 +00:00
|
|
|
if (!sa && !uap->newgroup && !first)
|
|
|
|
return (EINVAL);
|
2002-10-24 08:46:34 +00:00
|
|
|
kg = td->td_ksegrp;
|
|
|
|
if (uap->newgroup) {
|
2004-01-10 18:34:01 +00:00
|
|
|
/* Have race condition but it is cheap */
|
2003-06-15 12:51:26 +00:00
|
|
|
if (p->p_numksegrps >= max_groups_per_proc)
|
2002-11-17 11:47:03 +00:00
|
|
|
return (EPROCLIM);
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2002-10-24 08:46:34 +00:00
|
|
|
* If we want a new KSEGRP it doesn't matter whether
|
|
|
|
* we have already fired up KSE mode before or not.
|
2003-02-17 05:14:26 +00:00
|
|
|
* We put the process in KSE mode and create a new KSEGRP.
|
2002-10-24 08:46:34 +00:00
|
|
|
*/
|
|
|
|
newkg = ksegrp_alloc();
|
|
|
|
bzero(&newkg->kg_startzero, RANGEOF(struct ksegrp,
|
2003-02-17 05:14:26 +00:00
|
|
|
kg_startzero, kg_endzero));
|
2002-10-24 08:46:34 +00:00
|
|
|
bcopy(&kg->kg_startcopy, &newkg->kg_startcopy,
|
|
|
|
RANGEOF(struct ksegrp, kg_startcopy, kg_endcopy));
|
2004-01-10 18:34:01 +00:00
|
|
|
PROC_LOCK(p);
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
if (p->p_numksegrps >= max_groups_per_proc) {
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-08-26 11:33:15 +00:00
|
|
|
PROC_UNLOCK(p);
|
2003-02-28 15:57:33 +00:00
|
|
|
ksegrp_free(newkg);
|
2003-02-17 05:14:26 +00:00
|
|
|
return (EPROCLIM);
|
|
|
|
}
|
2003-02-28 15:57:33 +00:00
|
|
|
ksegrp_link(newkg, p);
|
2003-08-26 11:33:15 +00:00
|
|
|
sched_fork_ksegrp(kg, newkg);
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-08-26 11:33:15 +00:00
|
|
|
PROC_UNLOCK(p);
|
2002-10-24 08:46:34 +00:00
|
|
|
} else {
|
2003-06-16 23:46:41 +00:00
|
|
|
if (!first && ((td->td_flags & TDF_SA) ^ sa) != 0)
|
|
|
|
return (EINVAL);
|
2003-02-17 05:14:26 +00:00
|
|
|
newkg = kg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Creating upcalls more than number of physical cpu does
|
2004-01-10 18:34:01 +00:00
|
|
|
* not help performance.
|
2003-02-17 05:14:26 +00:00
|
|
|
*/
|
|
|
|
if (newkg->kg_numupcalls >= ncpus)
|
|
|
|
return (EPROCLIM);
|
|
|
|
|
|
|
|
if (newkg->kg_numupcalls == 0) {
|
|
|
|
/*
|
2003-06-15 12:51:26 +00:00
|
|
|
* Initialize KSE group
|
|
|
|
*
|
|
|
|
* For multiplxed group, create KSEs as many as physical
|
|
|
|
* cpus. This increases concurrent even if userland
|
|
|
|
* is not MP safe and can only run on single CPU.
|
2003-02-17 05:14:26 +00:00
|
|
|
* In ideal world, every physical cpu should execute a thread.
|
|
|
|
* If there is enough KSEs, threads in kernel can be
|
2004-01-10 18:34:01 +00:00
|
|
|
* executed parallel on different cpus with full speed,
|
|
|
|
* Concurrent in kernel shouldn't be restricted by number of
|
2003-06-15 12:51:26 +00:00
|
|
|
* upcalls userland provides. Adding more upcall structures
|
|
|
|
* only increases concurrent in userland.
|
|
|
|
*
|
|
|
|
* For bound thread group, because there is only thread in the
|
|
|
|
* group, we only create one KSE for the group. Thread in this
|
|
|
|
* kind of group will never schedule an upcall when blocked,
|
|
|
|
* this intends to simulate pthread system scope thread.
|
2002-10-24 08:46:34 +00:00
|
|
|
*/
|
2003-02-17 05:14:26 +00:00
|
|
|
while (newkg->kg_kses < ncpus) {
|
2002-10-24 08:46:34 +00:00
|
|
|
newke = kse_alloc();
|
2003-02-17 05:14:26 +00:00
|
|
|
bzero(&newke->ke_startzero, RANGEOF(struct kse,
|
|
|
|
ke_startzero, ke_endzero));
|
2002-10-24 08:46:34 +00:00
|
|
|
#if 0
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
bcopy(&ke->ke_startcopy, &newke->ke_startcopy,
|
|
|
|
RANGEOF(struct kse, ke_startcopy, ke_endcopy));
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
#endif
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
kse_link(newke, newkg);
|
2003-08-26 11:33:15 +00:00
|
|
|
sched_fork_kse(td->td_kse, newke);
|
2003-02-17 05:14:26 +00:00
|
|
|
/* Add engine */
|
|
|
|
kse_reassign(newke);
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-02-01 12:17:09 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
}
|
|
|
|
newku = upcall_alloc();
|
|
|
|
newku->ku_mailbox = uap->mbx;
|
|
|
|
newku->ku_func = mbx.km_func;
|
|
|
|
bcopy(&mbx.km_stack, &newku->ku_stack, sizeof(stack_t));
|
|
|
|
|
|
|
|
/* For the first call this may not have been set */
|
|
|
|
if (td->td_standin == NULL)
|
|
|
|
thread_alloc_spare(td, NULL);
|
|
|
|
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
PROC_LOCK(p);
|
2003-02-17 05:14:26 +00:00
|
|
|
if (newkg->kg_numupcalls >= ncpus) {
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
PROC_UNLOCK(p);
|
2003-02-27 05:42:01 +00:00
|
|
|
upcall_free(newku);
|
2003-02-17 05:14:26 +00:00
|
|
|
return (EPROCLIM);
|
|
|
|
}
|
2003-07-15 14:04:38 +00:00
|
|
|
if (first && sa) {
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
SIGSETOR(p->p_siglist, td->td_siglist);
|
|
|
|
SIGEMPTYSET(td->td_siglist);
|
|
|
|
SIGFILLSET(td->td_sigmask);
|
|
|
|
SIG_CANTMASK(td->td_sigmask);
|
|
|
|
}
|
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
PROC_UNLOCK(p);
|
2003-02-17 05:14:26 +00:00
|
|
|
upcall_link(newku, newkg);
|
2003-03-19 05:49:38 +00:00
|
|
|
if (mbx.km_quantum)
|
|
|
|
newkg->kg_upquantum = max(1, mbx.km_quantum/tick);
|
2003-02-17 05:14:26 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Each upcall structure has an owner thread, find which
|
|
|
|
* one owns it.
|
|
|
|
*/
|
|
|
|
if (uap->newgroup) {
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* Because new ksegrp hasn't thread,
|
|
|
|
* create an initial upcall thread to own it.
|
|
|
|
*/
|
2003-06-15 12:51:26 +00:00
|
|
|
newtd = thread_schedule_upcall(td, newku);
|
2002-10-24 08:46:34 +00:00
|
|
|
} else {
|
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* If current thread hasn't an upcall structure,
|
|
|
|
* just assign the upcall to it.
|
2002-10-24 08:46:34 +00:00
|
|
|
*/
|
2003-02-17 05:14:26 +00:00
|
|
|
if (td->td_upcall == NULL) {
|
|
|
|
newku->ku_owner = td;
|
|
|
|
td->td_upcall = newku;
|
2003-06-15 12:51:26 +00:00
|
|
|
newtd = td;
|
2003-02-17 05:14:26 +00:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Create a new upcall thread to own it.
|
|
|
|
*/
|
2003-06-15 12:51:26 +00:00
|
|
|
newtd = thread_schedule_upcall(td, newku);
|
2003-02-17 05:14:26 +00:00
|
|
|
}
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
2003-06-15 12:51:26 +00:00
|
|
|
if (!sa) {
|
|
|
|
newtd->td_mailbox = mbx.km_curthread;
|
|
|
|
newtd->td_flags &= ~TDF_SA;
|
2003-06-20 09:12:12 +00:00
|
|
|
if (newtd != td) {
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
cpu_set_upcall_kse(newtd, newku);
|
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
}
|
2003-06-15 12:51:26 +00:00
|
|
|
} else {
|
|
|
|
newtd->td_flags |= TDF_SA;
|
|
|
|
}
|
2003-06-20 09:12:12 +00:00
|
|
|
if (newtd != td)
|
|
|
|
setrunqueue(newtd);
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Initialize global thread allocation resources.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
threadinit(void)
|
|
|
|
{
|
|
|
|
|
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,
|
|
|
|
UMA_ALIGN_CACHE, 0);
|
2004-04-03 15:59:13 +00:00
|
|
|
tid_zone = uma_zcreate("TID", sizeof(struct tid_bitmap_part),
|
|
|
|
NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
|
2002-11-21 01:22:38 +00:00
|
|
|
ksegrp_zone = uma_zcreate("KSEGRP", sched_sizeof_ksegrp(),
|
|
|
|
NULL, NULL, ksegrp_init, NULL,
|
2002-09-15 23:52:25 +00:00
|
|
|
UMA_ALIGN_CACHE, 0);
|
2002-11-21 01:22:38 +00:00
|
|
|
kse_zone = uma_zcreate("KSE", sched_sizeof_kse(),
|
|
|
|
NULL, NULL, kse_init, NULL,
|
2002-09-15 23:52:25 +00:00
|
|
|
UMA_ALIGN_CACHE, 0);
|
2003-02-17 05:14:26 +00:00
|
|
|
upcall_zone = uma_zcreate("UPCALL", sizeof(struct kse_upcall),
|
|
|
|
NULL, NULL, NULL, NULL, UMA_ALIGN_CACHE, 0);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2002-09-06 07:00:37 +00:00
|
|
|
* Stash an embarasingly extra thread into the zombie thread queue.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_stash(struct thread *td)
|
|
|
|
{
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_lock_spin(&kse_zombie_lock);
|
2002-06-29 07:04:59 +00:00
|
|
|
TAILQ_INSERT_HEAD(&zombie_threads, td, td_runq);
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_unlock_spin(&kse_zombie_lock);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
2002-10-24 08:46:34 +00:00
|
|
|
/*
|
|
|
|
* Stash an embarasingly extra kse into the zombie kse queue.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
kse_stash(struct kse *ke)
|
|
|
|
{
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_lock_spin(&kse_zombie_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
TAILQ_INSERT_HEAD(&zombie_kses, ke, ke_procq);
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_unlock_spin(&kse_zombie_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stash an embarasingly extra upcall into the zombie upcall queue.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
|
|
|
upcall_stash(struct kse_upcall *ku)
|
|
|
|
{
|
|
|
|
mtx_lock_spin(&kse_zombie_lock);
|
|
|
|
TAILQ_INSERT_HEAD(&zombie_upcalls, ku, ku_link);
|
|
|
|
mtx_unlock_spin(&kse_zombie_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stash an embarasingly extra ksegrp into the zombie ksegrp queue.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ksegrp_stash(struct ksegrp *kg)
|
|
|
|
{
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_lock_spin(&kse_zombie_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
TAILQ_INSERT_HEAD(&zombie_ksegrps, kg, kg_ksegrp);
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_unlock_spin(&kse_zombie_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
}
|
|
|
|
|
2002-09-16 19:26:48 +00:00
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* Reap zombie kse resource.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_reap(void)
|
|
|
|
{
|
2002-10-24 08:46:34 +00:00
|
|
|
struct thread *td_first, *td_next;
|
|
|
|
struct kse *ke_first, *ke_next;
|
|
|
|
struct ksegrp *kg_first, * kg_next;
|
2003-02-17 05:14:26 +00:00
|
|
|
struct kse_upcall *ku_first, *ku_next;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* Don't even bother to lock if none at this instant,
|
|
|
|
* we really don't care about the next instant..
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
2002-10-24 08:46:34 +00:00
|
|
|
if ((!TAILQ_EMPTY(&zombie_threads))
|
|
|
|
|| (!TAILQ_EMPTY(&zombie_kses))
|
2003-02-17 05:14:26 +00:00
|
|
|
|| (!TAILQ_EMPTY(&zombie_ksegrps))
|
|
|
|
|| (!TAILQ_EMPTY(&zombie_upcalls))) {
|
|
|
|
mtx_lock_spin(&kse_zombie_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
td_first = TAILQ_FIRST(&zombie_threads);
|
|
|
|
ke_first = TAILQ_FIRST(&zombie_kses);
|
|
|
|
kg_first = TAILQ_FIRST(&zombie_ksegrps);
|
2003-02-17 05:14:26 +00:00
|
|
|
ku_first = TAILQ_FIRST(&zombie_upcalls);
|
2002-10-24 08:46:34 +00:00
|
|
|
if (td_first)
|
|
|
|
TAILQ_INIT(&zombie_threads);
|
|
|
|
if (ke_first)
|
|
|
|
TAILQ_INIT(&zombie_kses);
|
|
|
|
if (kg_first)
|
|
|
|
TAILQ_INIT(&zombie_ksegrps);
|
2003-02-17 05:14:26 +00:00
|
|
|
if (ku_first)
|
|
|
|
TAILQ_INIT(&zombie_upcalls);
|
|
|
|
mtx_unlock_spin(&kse_zombie_lock);
|
2002-10-24 08:46:34 +00:00
|
|
|
while (td_first) {
|
|
|
|
td_next = TAILQ_NEXT(td_first, td_runq);
|
2003-02-17 05:14:26 +00:00
|
|
|
if (td_first->td_ucred)
|
|
|
|
crfree(td_first->td_ucred);
|
2002-10-24 08:46:34 +00:00
|
|
|
thread_free(td_first);
|
|
|
|
td_first = td_next;
|
|
|
|
}
|
|
|
|
while (ke_first) {
|
|
|
|
ke_next = TAILQ_NEXT(ke_first, ke_procq);
|
|
|
|
kse_free(ke_first);
|
|
|
|
ke_first = ke_next;
|
|
|
|
}
|
|
|
|
while (kg_first) {
|
|
|
|
kg_next = TAILQ_NEXT(kg_first, kg_ksegrp);
|
|
|
|
ksegrp_free(kg_first);
|
|
|
|
kg_first = kg_next;
|
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
while (ku_first) {
|
|
|
|
ku_next = TAILQ_NEXT(ku_first, ku_link);
|
|
|
|
upcall_free(ku_first);
|
|
|
|
ku_first = ku_next;
|
|
|
|
}
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-09-15 23:52:25 +00:00
|
|
|
/*
|
|
|
|
* Allocate a ksegrp.
|
|
|
|
*/
|
|
|
|
struct ksegrp *
|
|
|
|
ksegrp_alloc(void)
|
|
|
|
{
|
2003-02-19 05:47:46 +00:00
|
|
|
return (uma_zalloc(ksegrp_zone, M_WAITOK));
|
2002-09-15 23:52:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate a kse.
|
|
|
|
*/
|
|
|
|
struct kse *
|
|
|
|
kse_alloc(void)
|
|
|
|
{
|
2003-02-19 05:47:46 +00:00
|
|
|
return (uma_zalloc(kse_zone, M_WAITOK));
|
2002-09-15 23:52:25 +00:00
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Allocate a thread.
|
|
|
|
*/
|
|
|
|
struct thread *
|
|
|
|
thread_alloc(void)
|
|
|
|
{
|
|
|
|
thread_reap(); /* check if any zombies to get */
|
2003-02-19 05:47:46 +00:00
|
|
|
return (uma_zalloc(thread_zone, M_WAITOK));
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
2002-09-15 23:52:25 +00:00
|
|
|
/*
|
|
|
|
* Deallocate a ksegrp.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ksegrp_free(struct ksegrp *td)
|
|
|
|
{
|
|
|
|
uma_zfree(ksegrp_zone, td);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Deallocate a kse.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
kse_free(struct kse *td)
|
|
|
|
{
|
|
|
|
uma_zfree(kse_zone, td);
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Deallocate a thread.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_free(struct thread *td)
|
|
|
|
{
|
2002-12-10 02:33:45 +00:00
|
|
|
|
|
|
|
cpu_thread_clean(td);
|
2002-06-29 07:04:59 +00:00
|
|
|
uma_zfree(thread_zone, td);
|
|
|
|
}
|
|
|
|
|
2004-04-03 15:59:13 +00:00
|
|
|
/*
|
|
|
|
* Assign a thread ID.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
thread_new_tid(void)
|
|
|
|
{
|
|
|
|
struct tid_bitmap_part *bmp, *new;
|
|
|
|
int bit, idx, tid;
|
|
|
|
|
|
|
|
mtx_lock(&tid_lock);
|
|
|
|
STAILQ_FOREACH(bmp, &tid_bitmap, bmp_next) {
|
|
|
|
if (bmp->bmp_free)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Create a new bitmap if we run out of free bits. */
|
|
|
|
if (bmp == NULL) {
|
|
|
|
mtx_unlock(&tid_lock);
|
|
|
|
new = uma_zalloc(tid_zone, M_WAITOK);
|
|
|
|
mtx_lock(&tid_lock);
|
|
|
|
bmp = STAILQ_LAST(&tid_bitmap, tid_bitmap_part, bmp_next);
|
|
|
|
if (bmp == NULL || bmp->bmp_free < TID_IDS_PER_PART/2) {
|
|
|
|
/* 1=free, 0=assigned. This way we can use ffsl(). */
|
|
|
|
memset(new->bmp_bitmap, ~0U, sizeof(new->bmp_bitmap));
|
|
|
|
new->bmp_base = (bmp == NULL) ? TID_MIN :
|
|
|
|
bmp->bmp_base + TID_IDS_PER_PART;
|
|
|
|
new->bmp_free = TID_IDS_PER_PART;
|
|
|
|
STAILQ_INSERT_TAIL(&tid_bitmap, new, bmp_next);
|
|
|
|
bmp = new;
|
|
|
|
new = NULL;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
new = NULL;
|
|
|
|
/* We have a bitmap with available IDs. */
|
|
|
|
idx = 0;
|
|
|
|
while (idx < TID_BITMAP_SIZE && bmp->bmp_bitmap[idx] == 0UL)
|
|
|
|
idx++;
|
|
|
|
bit = ffsl(bmp->bmp_bitmap[idx]) - 1;
|
|
|
|
tid = bmp->bmp_base + idx * TID_IDS_PER_IDX + bit;
|
|
|
|
bmp->bmp_bitmap[idx] &= ~(1UL << bit);
|
|
|
|
bmp->bmp_free--;
|
|
|
|
mtx_unlock(&tid_lock);
|
|
|
|
|
|
|
|
if (new != NULL)
|
|
|
|
uma_zfree(tid_zone, new);
|
|
|
|
return (tid);
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Store the thread context in the UTS's mailbox.
|
2002-09-27 07:11:11 +00:00
|
|
|
* then add the mailbox at the head of a list we are building in user space.
|
|
|
|
* The list is anchored in the ksegrp structure.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
int
|
2003-07-17 22:45:33 +00:00
|
|
|
thread_export_context(struct thread *td, int willexit)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
2002-10-05 04:49:46 +00:00
|
|
|
struct proc *p;
|
2002-09-27 07:11:11 +00:00
|
|
|
struct ksegrp *kg;
|
|
|
|
uintptr_t mbx;
|
|
|
|
void *addr;
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
int error = 0, temp, sig;
|
2003-06-04 00:12:57 +00:00
|
|
|
mcontext_t mc;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2002-10-05 04:49:46 +00:00
|
|
|
p = td->td_proc;
|
|
|
|
kg = td->td_ksegrp;
|
|
|
|
|
2002-09-16 19:26:48 +00:00
|
|
|
/* Export the user/machine context. */
|
2003-06-04 00:12:57 +00:00
|
|
|
get_mcontext(td, &mc, 0);
|
|
|
|
addr = (void *)(&td->td_mailbox->tm_context.uc_mcontext);
|
|
|
|
error = copyout(&mc, addr, sizeof(mcontext_t));
|
|
|
|
if (error)
|
2002-12-28 01:23:07 +00:00
|
|
|
goto bad;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/* Exports clock ticks in kernel mode */
|
|
|
|
addr = (caddr_t)(&td->td_mailbox->tm_sticks);
|
2003-06-28 19:45:15 +00:00
|
|
|
temp = fuword32(addr) + td->td_usticks;
|
|
|
|
if (suword32(addr, temp)) {
|
2003-06-04 00:12:57 +00:00
|
|
|
error = EFAULT;
|
2003-02-17 05:14:26 +00:00
|
|
|
goto bad;
|
2003-06-04 00:12:57 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
/*
|
|
|
|
* Post sync signal, or process SIGKILL and SIGSTOP.
|
|
|
|
* For sync signal, it is only possible when the signal is not
|
|
|
|
* caught by userland or process is being debugged.
|
|
|
|
*/
|
2003-07-17 22:45:33 +00:00
|
|
|
PROC_LOCK(p);
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
if (td->td_flags & TDF_NEEDSIGCHK) {
|
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
td->td_flags &= ~TDF_NEEDSIGCHK;
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
mtx_lock(&p->p_sigacts->ps_mtx);
|
|
|
|
while ((sig = cursig(td)) != 0)
|
|
|
|
postsig(sig);
|
|
|
|
mtx_unlock(&p->p_sigacts->ps_mtx);
|
|
|
|
}
|
2003-07-17 22:45:33 +00:00
|
|
|
if (willexit)
|
|
|
|
SIGFILLSET(td->td_sigmask);
|
|
|
|
PROC_UNLOCK(p);
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/* Get address in latest mbox of list pointer */
|
2002-09-27 07:11:11 +00:00
|
|
|
addr = (void *)(&td->td_mailbox->tm_next);
|
|
|
|
/*
|
|
|
|
* Put the saved address of the previous first
|
|
|
|
* entry into this one
|
|
|
|
*/
|
|
|
|
for (;;) {
|
|
|
|
mbx = (uintptr_t)kg->kg_completed;
|
|
|
|
if (suword(addr, mbx)) {
|
2002-12-28 01:23:07 +00:00
|
|
|
error = EFAULT;
|
2002-11-18 01:59:31 +00:00
|
|
|
goto bad;
|
2002-09-27 07:11:11 +00:00
|
|
|
}
|
2002-09-29 02:48:37 +00:00
|
|
|
PROC_LOCK(p);
|
2002-09-27 07:11:11 +00:00
|
|
|
if (mbx == (uintptr_t)kg->kg_completed) {
|
|
|
|
kg->kg_completed = td->td_mailbox;
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
* The thread context may be taken away by
|
|
|
|
* other upcall threads when we unlock
|
|
|
|
* process lock. it's no longer valid to
|
|
|
|
* use it again in any other places.
|
|
|
|
*/
|
|
|
|
td->td_mailbox = NULL;
|
2002-09-29 02:48:37 +00:00
|
|
|
PROC_UNLOCK(p);
|
2002-09-27 07:11:11 +00:00
|
|
|
break;
|
|
|
|
}
|
2002-09-29 02:48:37 +00:00
|
|
|
PROC_UNLOCK(p);
|
2002-09-27 07:11:11 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_usticks = 0;
|
2002-09-27 07:11:11 +00:00
|
|
|
return (0);
|
2002-11-18 01:59:31 +00:00
|
|
|
|
|
|
|
bad:
|
|
|
|
PROC_LOCK(p);
|
2003-07-17 22:45:33 +00:00
|
|
|
sigexit(td, SIGILL);
|
2002-12-28 01:23:07 +00:00
|
|
|
return (error);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
2002-09-27 07:11:11 +00:00
|
|
|
/*
|
|
|
|
* Take the list of completed mailboxes for this KSEGRP and put them on this
|
2003-02-17 05:14:26 +00:00
|
|
|
* upcall's mailbox as it's the next one going up.
|
2002-09-27 07:11:11 +00:00
|
|
|
*/
|
|
|
|
static int
|
2003-02-17 05:14:26 +00:00
|
|
|
thread_link_mboxes(struct ksegrp *kg, struct kse_upcall *ku)
|
2002-09-27 07:11:11 +00:00
|
|
|
{
|
2002-09-29 02:48:37 +00:00
|
|
|
struct proc *p = kg->kg_proc;
|
2002-09-27 07:11:11 +00:00
|
|
|
void *addr;
|
|
|
|
uintptr_t mbx;
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
addr = (void *)(&ku->ku_mailbox->km_completed);
|
2002-09-27 07:11:11 +00:00
|
|
|
for (;;) {
|
|
|
|
mbx = (uintptr_t)kg->kg_completed;
|
|
|
|
if (suword(addr, mbx)) {
|
2002-09-29 02:48:37 +00:00
|
|
|
PROC_LOCK(p);
|
|
|
|
psignal(p, SIGSEGV);
|
|
|
|
PROC_UNLOCK(p);
|
2002-09-27 07:11:11 +00:00
|
|
|
return (EFAULT);
|
|
|
|
}
|
2002-09-29 02:48:37 +00:00
|
|
|
PROC_LOCK(p);
|
2002-09-27 07:11:11 +00:00
|
|
|
if (mbx == (uintptr_t)kg->kg_completed) {
|
|
|
|
kg->kg_completed = NULL;
|
2002-09-29 02:48:37 +00:00
|
|
|
PROC_UNLOCK(p);
|
2002-09-27 07:11:11 +00:00
|
|
|
break;
|
|
|
|
}
|
2002-09-29 02:48:37 +00:00
|
|
|
PROC_UNLOCK(p);
|
2002-09-27 07:11:11 +00:00
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2002-11-18 01:59:31 +00:00
|
|
|
/*
|
|
|
|
* This function should be called at statclock interrupt time
|
|
|
|
*/
|
|
|
|
int
|
2003-02-17 05:14:26 +00:00
|
|
|
thread_statclock(int user)
|
2002-11-18 01:59:31 +00:00
|
|
|
{
|
|
|
|
struct thread *td = curthread;
|
2003-06-15 12:51:26 +00:00
|
|
|
struct ksegrp *kg = td->td_ksegrp;
|
2004-01-10 18:34:01 +00:00
|
|
|
|
2003-06-15 12:51:26 +00:00
|
|
|
if (kg->kg_numupcalls == 0 || !(td->td_flags & TDF_SA))
|
|
|
|
return (0);
|
2002-11-18 01:59:31 +00:00
|
|
|
if (user) {
|
|
|
|
/* Current always do via ast() */
|
2003-03-08 04:09:04 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
2003-02-17 09:55:10 +00:00
|
|
|
td->td_flags |= (TDF_USTATCLOCK|TDF_ASTPENDING);
|
2003-03-08 04:09:04 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_uuticks++;
|
2002-11-18 01:59:31 +00:00
|
|
|
} else {
|
|
|
|
if (td->td_mailbox != NULL)
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_usticks++;
|
|
|
|
else {
|
|
|
|
/* XXXKSE
|
|
|
|
* We will call thread_user_enter() for every
|
|
|
|
* kernel entry in future, so if the thread mailbox
|
|
|
|
* is NULL, it must be a UTS kernel, don't account
|
|
|
|
* clock ticks for it.
|
|
|
|
*/
|
|
|
|
}
|
2002-11-18 01:59:31 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
return (0);
|
2002-11-18 01:59:31 +00:00
|
|
|
}
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
2003-02-26 00:58:23 +00:00
|
|
|
* Export state clock ticks for userland
|
2003-02-17 05:14:26 +00:00
|
|
|
*/
|
2002-11-18 01:59:31 +00:00
|
|
|
static int
|
2003-02-26 00:58:23 +00:00
|
|
|
thread_update_usr_ticks(struct thread *td, int user)
|
2002-11-18 01:59:31 +00:00
|
|
|
{
|
|
|
|
struct proc *p = td->td_proc;
|
|
|
|
struct kse_thr_mailbox *tmbx;
|
2003-02-17 05:14:26 +00:00
|
|
|
struct kse_upcall *ku;
|
2003-03-19 05:49:38 +00:00
|
|
|
struct ksegrp *kg;
|
2002-11-18 01:59:31 +00:00
|
|
|
caddr_t addr;
|
2003-08-07 15:04:27 +00:00
|
|
|
u_int uticks;
|
2003-02-01 12:17:09 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
if ((ku = td->td_upcall) == NULL)
|
|
|
|
return (-1);
|
2004-01-10 18:34:01 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
tmbx = (void *)fuword((void *)&ku->ku_mailbox->km_curthread);
|
2002-11-18 01:59:31 +00:00
|
|
|
if ((tmbx == NULL) || (tmbx == (void *)-1))
|
2003-02-17 05:14:26 +00:00
|
|
|
return (-1);
|
2003-02-26 00:58:23 +00:00
|
|
|
if (user) {
|
|
|
|
uticks = td->td_uuticks;
|
|
|
|
td->td_uuticks = 0;
|
2003-02-17 05:14:26 +00:00
|
|
|
addr = (caddr_t)&tmbx->tm_uticks;
|
2003-02-26 00:58:23 +00:00
|
|
|
} else {
|
|
|
|
uticks = td->td_usticks;
|
|
|
|
td->td_usticks = 0;
|
|
|
|
addr = (caddr_t)&tmbx->tm_sticks;
|
|
|
|
}
|
|
|
|
if (uticks) {
|
2003-06-28 19:45:15 +00:00
|
|
|
if (suword32(addr, uticks+fuword32(addr))) {
|
2003-02-17 05:14:26 +00:00
|
|
|
PROC_LOCK(p);
|
|
|
|
psignal(p, SIGSEGV);
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
return (-2);
|
|
|
|
}
|
2003-03-19 05:49:38 +00:00
|
|
|
}
|
|
|
|
kg = td->td_ksegrp;
|
|
|
|
if (kg->kg_upquantum && ticks >= kg->kg_nextupcall) {
|
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
td->td_upcall->ku_flags |= KUF_DOUPCALL;
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
2002-11-18 01:59:31 +00:00
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
return (0);
|
2002-11-18 01:59:31 +00:00
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
|
|
|
* Discard the current thread and exit from its context.
|
|
|
|
*
|
|
|
|
* 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
|
|
|
|
* do a cpu_throw().
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_exit(void)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
struct kse *ke;
|
|
|
|
struct proc *p;
|
|
|
|
struct ksegrp *kg;
|
|
|
|
|
|
|
|
td = curthread;
|
|
|
|
kg = td->td_ksegrp;
|
|
|
|
p = td->td_proc;
|
|
|
|
ke = td->td_kse;
|
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
2002-08-29 19:49:53 +00:00
|
|
|
KASSERT(p != NULL, ("thread exiting without a process"));
|
|
|
|
KASSERT(ke != NULL, ("thread exiting without a kse"));
|
|
|
|
KASSERT(kg != NULL, ("thread exiting without a kse group"));
|
2002-06-29 07:04:59 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
|
|
|
CTR1(KTR_PROC, "thread_exit: thread %p", td);
|
2004-01-28 20:26:39 +00:00
|
|
|
mtx_assert(&Giant, MA_NOTOWNED);
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2002-10-09 02:33:36 +00:00
|
|
|
if (td->td_standin != NULL) {
|
|
|
|
thread_stash(td->td_standin);
|
|
|
|
td->td_standin = NULL;
|
|
|
|
}
|
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
cpu_thread_exit(td); /* XXXSMP */
|
|
|
|
|
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
|
|
|
|
* all this stuff.
|
2002-08-29 19:49:53 +00:00
|
|
|
*/
|
2002-09-06 07:00:37 +00:00
|
|
|
if (p->p_numthreads > 1) {
|
2003-04-18 00:16:13 +00:00
|
|
|
thread_unlink(td);
|
2003-02-19 04:01:55 +00:00
|
|
|
if (p->p_maxthrwaits)
|
|
|
|
wakeup(&p->p_numthreads);
|
2002-09-06 07:00:37 +00:00
|
|
|
/*
|
|
|
|
* The test below is NOT true if we are the
|
|
|
|
* sole exiting thread. P_STOPPED_SNGL is unset
|
|
|
|
* in exit1() after it is the only survivor.
|
|
|
|
*/
|
|
|
|
if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) {
|
|
|
|
if (p->p_numthreads == p->p_suspcount) {
|
2002-09-11 08:13:56 +00:00
|
|
|
thread_unsuspend_one(p->p_singlethread);
|
2002-09-06 07:00:37 +00:00
|
|
|
}
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2002-10-09 02:33:36 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
* Because each upcall structure has an owner thread,
|
|
|
|
* owner thread exits only when process is in exiting
|
|
|
|
* state, so upcall to userland is no longer needed,
|
|
|
|
* deleting upcall structure is safe here.
|
|
|
|
* So when all threads in a group is exited, all upcalls
|
|
|
|
* in the group should be automatically freed.
|
|
|
|
*/
|
|
|
|
if (td->td_upcall)
|
|
|
|
upcall_remove(td);
|
2004-01-10 18:34:01 +00:00
|
|
|
|
2003-08-26 11:33:15 +00:00
|
|
|
sched_exit_thread(FIRST_THREAD_IN_PROC(p), td);
|
|
|
|
sched_exit_kse(FIRST_KSE_IN_PROC(p), ke);
|
2002-10-09 02:33:36 +00:00
|
|
|
ke->ke_state = KES_UNQUEUED;
|
2003-02-17 05:14:26 +00:00
|
|
|
ke->ke_thread = NULL;
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2002-12-28 01:23:07 +00:00
|
|
|
* Decide what to do with the KSE attached to this thread.
|
2002-10-09 02:33:36 +00:00
|
|
|
*/
|
2003-08-26 11:33:15 +00:00
|
|
|
if (ke->ke_flags & KEF_EXIT) {
|
2002-10-24 08:46:34 +00:00
|
|
|
kse_unlink(ke);
|
2003-08-26 11:33:15 +00:00
|
|
|
if (kg->kg_kses == 0) {
|
|
|
|
sched_exit_ksegrp(FIRST_KSEGRP_IN_PROC(p), kg);
|
|
|
|
ksegrp_unlink(kg);
|
|
|
|
}
|
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
else
|
2002-10-24 08:46:34 +00:00
|
|
|
kse_reassign(ke);
|
|
|
|
PROC_UNLOCK(p);
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_kse = NULL;
|
2002-10-24 08:46:34 +00:00
|
|
|
td->td_state = TDS_INACTIVE;
|
2003-04-08 07:45:56 +00:00
|
|
|
#if 0
|
2002-10-24 08:46:34 +00:00
|
|
|
td->td_proc = NULL;
|
2003-04-08 07:45:56 +00:00
|
|
|
#endif
|
2002-10-24 08:46:34 +00:00
|
|
|
td->td_ksegrp = NULL;
|
|
|
|
td->td_last_kse = NULL;
|
2002-12-10 02:33:45 +00:00
|
|
|
PCPU_SET(deadthread, td);
|
2002-09-06 07:00:37 +00:00
|
|
|
} else {
|
|
|
|
PROC_UNLOCK(p);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2003-03-31 22:49:17 +00:00
|
|
|
/* XXX Shouldn't cpu_throw() here. */
|
2003-04-02 23:53:30 +00:00
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
|
|
|
cpu_throw(td, choosethread());
|
|
|
|
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);
|
2004-01-10 18:34:01 +00:00
|
|
|
KASSERT((p->p_numthreads == 1), ("Multiple threads in wait1()"));
|
|
|
|
KASSERT((p->p_numksegrps == 1), ("Multiple ksegrps in wait1()"));
|
2002-12-10 02:33:45 +00:00
|
|
|
FOREACH_THREAD_IN_PROC(p, td) {
|
|
|
|
if (td->td_standin != NULL) {
|
|
|
|
thread_free(td->td_standin);
|
|
|
|
td->td_standin = NULL;
|
|
|
|
}
|
|
|
|
cpu_thread_clean(td);
|
|
|
|
}
|
|
|
|
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
|
|
|
*
|
|
|
|
* Note that we do not link to the proc's ucred here.
|
|
|
|
* The thread is linked as if running but no KSE assigned.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_link(struct thread *td, struct ksegrp *kg)
|
|
|
|
{
|
|
|
|
struct proc *p;
|
|
|
|
|
|
|
|
p = kg->kg_proc;
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_state = TDS_INACTIVE;
|
|
|
|
td->td_proc = p;
|
|
|
|
td->td_ksegrp = kg;
|
|
|
|
td->td_last_kse = NULL;
|
|
|
|
td->td_flags = 0;
|
|
|
|
td->td_kse = NULL;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2002-09-06 07:00:37 +00:00
|
|
|
LIST_INIT(&td->td_contested);
|
2003-08-19 17:51:11 +00:00
|
|
|
callout_init(&td->td_slpcallout, CALLOUT_MPSAFE);
|
2002-06-29 07:04:59 +00:00
|
|
|
TAILQ_INSERT_HEAD(&p->p_threads, td, td_plist);
|
|
|
|
TAILQ_INSERT_HEAD(&kg->kg_threads, td, td_kglist);
|
|
|
|
p->p_numthreads++;
|
|
|
|
kg->kg_numthreads++;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
struct ksegrp *kg = td->td_ksegrp;
|
2003-04-23 18:46:51 +00:00
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
2003-04-18 00:16:13 +00:00
|
|
|
TAILQ_REMOVE(&p->p_threads, td, td_plist);
|
|
|
|
p->p_numthreads--;
|
|
|
|
TAILQ_REMOVE(&kg->kg_threads, td, td_kglist);
|
|
|
|
kg->kg_numthreads--;
|
|
|
|
/* could clear a few other things here */
|
2004-01-10 18:34:01 +00:00
|
|
|
}
|
2003-04-18 00:16:13 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
* Purge a ksegrp resource. When a ksegrp is preparing to
|
2004-01-10 18:34:01 +00:00
|
|
|
* exit, it calls this function.
|
2003-02-17 05:14:26 +00:00
|
|
|
*/
|
2003-04-22 19:47:55 +00:00
|
|
|
static void
|
2003-02-17 05:14:26 +00:00
|
|
|
kse_purge_group(struct thread *td)
|
|
|
|
{
|
|
|
|
struct ksegrp *kg;
|
|
|
|
struct kse *ke;
|
|
|
|
|
|
|
|
kg = td->td_ksegrp;
|
|
|
|
KASSERT(kg->kg_numthreads == 1, ("%s: bad thread number", __func__));
|
|
|
|
while ((ke = TAILQ_FIRST(&kg->kg_iq)) != NULL) {
|
|
|
|
KASSERT(ke->ke_state == KES_IDLE,
|
|
|
|
("%s: wrong idle KSE state", __func__));
|
|
|
|
kse_unlink(ke);
|
|
|
|
}
|
|
|
|
KASSERT((kg->kg_kses == 1),
|
|
|
|
("%s: ksegrp still has %d KSEs", __func__, kg->kg_kses));
|
|
|
|
KASSERT((kg->kg_numupcalls == 0),
|
|
|
|
("%s: ksegrp still has %d upcall datas",
|
|
|
|
__func__, kg->kg_numupcalls));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2004-01-10 18:34:01 +00:00
|
|
|
* Purge a process's KSE resource. When a process is preparing to
|
|
|
|
* exit, it calls kse_purge to release any extra KSE resources in
|
2003-02-17 05:14:26 +00:00
|
|
|
* the process.
|
|
|
|
*/
|
2003-04-22 19:47:55 +00:00
|
|
|
static void
|
2002-10-24 08:46:34 +00:00
|
|
|
kse_purge(struct proc *p, struct thread *td)
|
|
|
|
{
|
|
|
|
struct ksegrp *kg;
|
2003-02-17 05:14:26 +00:00
|
|
|
struct kse *ke;
|
2002-10-24 08:46:34 +00:00
|
|
|
|
|
|
|
KASSERT(p->p_numthreads == 1, ("bad thread number"));
|
|
|
|
while ((kg = TAILQ_FIRST(&p->p_ksegrps)) != NULL) {
|
|
|
|
TAILQ_REMOVE(&p->p_ksegrps, kg, kg_ksegrp);
|
|
|
|
p->p_numksegrps--;
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
* There is no ownership for KSE, after all threads
|
2004-01-10 18:34:01 +00:00
|
|
|
* in the group exited, it is possible that some KSEs
|
2003-02-17 05:14:26 +00:00
|
|
|
* were left in idle queue, gc them now.
|
|
|
|
*/
|
|
|
|
while ((ke = TAILQ_FIRST(&kg->kg_iq)) != NULL) {
|
|
|
|
KASSERT(ke->ke_state == KES_IDLE,
|
|
|
|
("%s: wrong idle KSE state", __func__));
|
|
|
|
TAILQ_REMOVE(&kg->kg_iq, ke, ke_kgrlist);
|
|
|
|
kg->kg_idle_kses--;
|
|
|
|
TAILQ_REMOVE(&kg->kg_kseq, ke, ke_kglist);
|
|
|
|
kg->kg_kses--;
|
|
|
|
kse_stash(ke);
|
|
|
|
}
|
2002-10-24 08:46:34 +00:00
|
|
|
KASSERT(((kg->kg_kses == 0) && (kg != td->td_ksegrp)) ||
|
2003-02-17 05:14:26 +00:00
|
|
|
((kg->kg_kses == 1) && (kg == td->td_ksegrp)),
|
|
|
|
("ksegrp has wrong kg_kses: %d", kg->kg_kses));
|
|
|
|
KASSERT((kg->kg_numupcalls == 0),
|
|
|
|
("%s: ksegrp still has %d upcall datas",
|
|
|
|
__func__, kg->kg_numupcalls));
|
2004-01-10 18:34:01 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
if (kg != td->td_ksegrp)
|
2002-10-24 08:46:34 +00:00
|
|
|
ksegrp_stash(kg);
|
|
|
|
}
|
|
|
|
TAILQ_INSERT_HEAD(&p->p_ksegrps, td->td_ksegrp, kg_ksegrp);
|
|
|
|
p->p_numksegrps++;
|
|
|
|
}
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
* This function is intended to be used to initialize a spare thread
|
|
|
|
* for upcall. Initialize thread's large data area outside sched_lock
|
|
|
|
* for thread_schedule_upcall().
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_alloc_spare(struct thread *td, struct thread *spare)
|
|
|
|
{
|
2004-03-13 22:31:39 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
if (td->td_standin)
|
|
|
|
return;
|
2004-04-03 15:59:13 +00:00
|
|
|
if (spare == NULL) {
|
2003-02-17 05:14:26 +00:00
|
|
|
spare = thread_alloc();
|
2004-04-03 15:59:13 +00:00
|
|
|
spare->td_tid = thread_new_tid();
|
|
|
|
}
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_standin = spare;
|
|
|
|
bzero(&spare->td_startzero,
|
|
|
|
(unsigned)RANGEOF(struct thread, td_startzero, td_endzero));
|
|
|
|
spare->td_proc = td->td_proc;
|
|
|
|
spare->td_ucred = crhold(td->td_ucred);
|
|
|
|
}
|
2002-10-24 08:46:34 +00:00
|
|
|
|
2002-06-29 07:04:59 +00:00
|
|
|
/*
|
2002-09-16 19:26:48 +00:00
|
|
|
* Create a thread and schedule it for upcall on the KSE given.
|
2002-12-28 01:23:07 +00:00
|
|
|
* Use our thread's standin so that we don't have to allocate one.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
struct thread *
|
2003-02-17 05:14:26 +00:00
|
|
|
thread_schedule_upcall(struct thread *td, struct kse_upcall *ku)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
|
|
|
struct thread *td2;
|
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
2002-10-09 02:33:36 +00:00
|
|
|
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* Schedule an upcall thread on specified kse_upcall,
|
|
|
|
* the kse_upcall must be free.
|
|
|
|
* td must have a spare thread.
|
2002-10-09 02:33:36 +00:00
|
|
|
*/
|
2003-02-17 05:14:26 +00:00
|
|
|
KASSERT(ku->ku_owner == NULL, ("%s: upcall has owner", __func__));
|
2002-10-09 02:33:36 +00:00
|
|
|
if ((td2 = td->td_standin) != NULL) {
|
|
|
|
td->td_standin = NULL;
|
2002-06-29 07:04:59 +00:00
|
|
|
} else {
|
2003-02-17 05:14:26 +00:00
|
|
|
panic("no reserve thread when scheduling an upcall");
|
2002-10-30 03:01:28 +00:00
|
|
|
return (NULL);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
CTR3(KTR_PROC, "thread_schedule_upcall: thread %p (pid %d, %s)",
|
2002-10-09 02:33:36 +00:00
|
|
|
td2, td->td_proc->p_pid, td->td_proc->p_comm);
|
2002-09-06 07:00:37 +00:00
|
|
|
bcopy(&td->td_startcopy, &td2->td_startcopy,
|
|
|
|
(unsigned) RANGEOF(struct thread, td_startcopy, td_endcopy));
|
2003-02-17 05:14:26 +00:00
|
|
|
thread_link(td2, ku->ku_ksegrp);
|
2003-04-08 07:45:56 +00:00
|
|
|
/* inherit blocked thread's context */
|
2003-06-04 21:13:21 +00:00
|
|
|
cpu_set_upcall(td2, td);
|
2003-02-17 05:14:26 +00:00
|
|
|
/* Let the new thread become owner of the upcall */
|
|
|
|
ku->ku_owner = td2;
|
|
|
|
td2->td_upcall = ku;
|
2003-06-15 12:51:26 +00:00
|
|
|
td2->td_flags = TDF_SA;
|
2003-06-15 03:18:58 +00:00
|
|
|
td2->td_pflags = TDP_UPCALLING;
|
2003-02-17 05:14:26 +00:00
|
|
|
td2->td_kse = NULL;
|
|
|
|
td2->td_state = TDS_CAN_RUN;
|
2002-10-09 02:33:36 +00:00
|
|
|
td2->td_inhibitors = 0;
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
SIGFILLSET(td2->td_sigmask);
|
|
|
|
SIG_CANTMASK(td2->td_sigmask);
|
2003-08-26 11:33:15 +00:00
|
|
|
sched_fork_thread(td, td2);
|
2002-10-09 02:33:36 +00:00
|
|
|
return (td2); /* bogus.. should be a void function */
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
/*
|
|
|
|
* It is only used when thread generated a trap and process is being
|
|
|
|
* debugged.
|
|
|
|
*/
|
2003-02-17 09:58:11 +00:00
|
|
|
void
|
|
|
|
thread_signal_add(struct thread *td, int sig)
|
2002-09-16 19:26:48 +00:00
|
|
|
{
|
2003-02-17 09:58:11 +00:00
|
|
|
struct proc *p;
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
siginfo_t siginfo;
|
|
|
|
struct sigacts *ps;
|
2002-09-16 19:26:48 +00:00
|
|
|
int error;
|
|
|
|
|
2003-06-06 02:17:38 +00:00
|
|
|
p = td->td_proc;
|
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
ps = p->p_sigacts;
|
|
|
|
mtx_assert(&ps->ps_mtx, MA_OWNED);
|
2003-02-17 09:58:11 +00:00
|
|
|
|
2003-07-15 04:26:26 +00:00
|
|
|
cpu_thread_siginfo(sig, 0, &siginfo);
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
mtx_unlock(&ps->ps_mtx);
|
|
|
|
PROC_UNLOCK(p);
|
|
|
|
error = copyout(&siginfo, &td->td_mailbox->tm_syncsig, sizeof(siginfo));
|
|
|
|
if (error) {
|
|
|
|
PROC_LOCK(p);
|
|
|
|
sigexit(td, SIGILL);
|
|
|
|
}
|
2003-02-17 09:58:11 +00:00
|
|
|
PROC_LOCK(p);
|
o Change kse_thr_interrupt to allow send a signal to a specified thread,
or unblock a thread in kernel, and allow UTS to specify whether syscall
should be restarted.
o Add ability for UTS to monitor signal comes in and removed from process,
the flag PS_SIGEVENT is used to indicate the events.
o Add a KMF_WAITSIGEVENT for KSE mailbox flag, UTS call kse_release with
this flag set to wait for above signal event.
o For SA based thread, kernel masks all signal in its signal mask, let
UTS to use kse_thr_interrupt interrupt a thread, and install a signal
frame in userland for the thread.
o Add a tm_syncsig in thread mailbox, when a hardware trap occurs,
it is used to deliver synchronous signal to userland, and upcall
is schedule, so UTS can process the synchronous signal for the thread.
Reviewed by: julian (mentor)
2003-06-28 08:29:05 +00:00
|
|
|
SIGADDSET(td->td_sigmask, sig);
|
|
|
|
mtx_lock(&ps->ps_mtx);
|
2002-09-16 19:26:48 +00:00
|
|
|
}
|
|
|
|
|
2003-03-19 05:49:38 +00:00
|
|
|
void
|
|
|
|
thread_switchout(struct thread *td)
|
|
|
|
{
|
|
|
|
struct kse_upcall *ku;
|
2003-06-20 09:12:12 +00:00
|
|
|
struct thread *td2;
|
2003-03-19 05:49:38 +00:00
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the outgoing thread is in threaded group and has never
|
|
|
|
* scheduled an upcall, decide whether this is a short
|
|
|
|
* or long term event and thus whether or not to schedule
|
|
|
|
* an upcall.
|
|
|
|
* If it is a short term event, just suspend it in
|
|
|
|
* a way that takes its KSE with it.
|
|
|
|
* Select the events for which we want to schedule upcalls.
|
|
|
|
* For now it's just sleep.
|
|
|
|
* XXXKSE eventually almost any inhibition could do.
|
|
|
|
*/
|
|
|
|
if (TD_CAN_UNBIND(td) && (td->td_standin) && TD_ON_SLEEPQ(td)) {
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2003-03-19 05:49:38 +00:00
|
|
|
* Release ownership of upcall, and schedule an upcall
|
|
|
|
* thread, this new upcall thread becomes the owner of
|
|
|
|
* the upcall structure.
|
|
|
|
*/
|
|
|
|
ku = td->td_upcall;
|
|
|
|
ku->ku_owner = NULL;
|
2004-01-10 18:34:01 +00:00
|
|
|
td->td_upcall = NULL;
|
2003-03-19 05:49:38 +00:00
|
|
|
td->td_flags &= ~TDF_CAN_UNBIND;
|
2003-06-20 09:12:12 +00:00
|
|
|
td2 = thread_schedule_upcall(td, ku);
|
|
|
|
setrunqueue(td2);
|
2003-03-19 05:49:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-10-24 23:09:48 +00:00
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* Setup done on the thread when it enters the kernel.
|
2002-10-24 23:09:48 +00:00
|
|
|
* XXXKSE Presently only for syscalls but eventually all kernel entries.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
thread_user_enter(struct proc *p, struct thread *td)
|
|
|
|
{
|
2003-02-17 05:14:26 +00:00
|
|
|
struct ksegrp *kg;
|
|
|
|
struct kse_upcall *ku;
|
2003-04-21 07:27:59 +00:00
|
|
|
struct kse_thr_mailbox *tmbx;
|
2003-08-05 12:00:55 +00:00
|
|
|
uint32_t tflags;
|
2002-10-24 23:09:48 +00:00
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
kg = td->td_ksegrp;
|
2003-04-21 07:27:59 +00:00
|
|
|
|
2002-10-24 23:09:48 +00:00
|
|
|
/*
|
|
|
|
* First check that we shouldn't just abort.
|
|
|
|
* But check if we are the single thread first!
|
|
|
|
*/
|
2003-06-15 12:51:26 +00:00
|
|
|
if (p->p_flag & P_SINGLE_EXIT) {
|
|
|
|
PROC_LOCK(p);
|
2002-10-24 23:09:48 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
2003-03-11 00:07:53 +00:00
|
|
|
thread_stopped(p);
|
2002-10-24 23:09:48 +00:00
|
|
|
thread_exit();
|
|
|
|
/* NOTREACHED */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are doing a syscall in a KSE environment,
|
|
|
|
* note where our mailbox is. There is always the
|
2002-12-28 01:23:07 +00:00
|
|
|
* possibility that we could do this lazily (in kse_reassign()),
|
2002-10-24 23:09:48 +00:00
|
|
|
* but for now do it every time.
|
|
|
|
*/
|
2003-02-17 05:14:26 +00:00
|
|
|
kg = td->td_ksegrp;
|
2003-06-15 12:51:26 +00:00
|
|
|
if (td->td_flags & TDF_SA) {
|
2003-02-17 05:14:26 +00:00
|
|
|
ku = td->td_upcall;
|
|
|
|
KASSERT(ku, ("%s: no upcall owned", __func__));
|
|
|
|
KASSERT((ku->ku_owner == td), ("%s: wrong owner", __func__));
|
2003-04-21 07:27:59 +00:00
|
|
|
KASSERT(!TD_CAN_UNBIND(td), ("%s: can unbind", __func__));
|
2003-06-28 19:45:15 +00:00
|
|
|
ku->ku_mflags = fuword32((void *)&ku->ku_mailbox->km_flags);
|
2003-04-21 07:27:59 +00:00
|
|
|
tmbx = (void *)fuword((void *)&ku->ku_mailbox->km_curthread);
|
2003-08-05 12:00:55 +00:00
|
|
|
if ((tmbx == NULL) || (tmbx == (void *)-1L) ||
|
|
|
|
(ku->ku_mflags & KMF_NOUPCALL)) {
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_mailbox = NULL;
|
2002-10-24 23:09:48 +00:00
|
|
|
} else {
|
2003-02-19 04:01:55 +00:00
|
|
|
if (td->td_standin == NULL)
|
|
|
|
thread_alloc_spare(td, NULL);
|
2003-08-08 20:23:10 +00:00
|
|
|
tflags = fuword32(&tmbx->tm_flags);
|
2003-08-05 12:00:55 +00:00
|
|
|
/*
|
|
|
|
* On some architectures, TP register points to thread
|
2004-01-10 18:34:01 +00:00
|
|
|
* mailbox but not points to kse mailbox, and userland
|
|
|
|
* can not atomically clear km_curthread, but can
|
2003-08-05 12:00:55 +00:00
|
|
|
* use TP register, and set TMF_NOUPCALL in thread
|
|
|
|
* flag to indicate a critical region.
|
|
|
|
*/
|
|
|
|
if (tflags & TMF_NOUPCALL) {
|
|
|
|
td->td_mailbox = NULL;
|
|
|
|
} else {
|
|
|
|
td->td_mailbox = tmbx;
|
|
|
|
mtx_lock_spin(&sched_lock);
|
2003-04-21 07:27:59 +00:00
|
|
|
td->td_flags |= TDF_CAN_UNBIND;
|
2003-08-05 12:00:55 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
}
|
2002-10-24 23:09:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-09-16 19:26:48 +00:00
|
|
|
/*
|
|
|
|
* The extra work we go through if we are a threaded process when we
|
|
|
|
* return to userland.
|
2002-06-29 07:04:59 +00:00
|
|
|
*
|
|
|
|
* If we are a KSE process and returning to user mode, check for
|
|
|
|
* extra work to do before we return (e.g. for more syscalls
|
|
|
|
* to complete first). If we were in a critical section, we should
|
|
|
|
* just return to let it finish. Same if we were in the UTS (in
|
2002-09-16 19:26:48 +00:00
|
|
|
* which case the mailbox's context's busy indicator will be set).
|
|
|
|
* The only traps we suport will have set the mailbox.
|
|
|
|
* We will clear it here.
|
2002-06-29 07:04:59 +00:00
|
|
|
*/
|
|
|
|
int
|
2002-09-23 06:14:30 +00:00
|
|
|
thread_userret(struct thread *td, struct trapframe *frame)
|
2002-06-29 07:04:59 +00:00
|
|
|
{
|
2003-04-21 07:27:59 +00:00
|
|
|
int error = 0, upcalls, uts_crit;
|
2003-02-17 05:14:26 +00:00
|
|
|
struct kse_upcall *ku;
|
2003-02-19 04:01:55 +00:00
|
|
|
struct ksegrp *kg, *kg2;
|
2002-10-09 02:33:36 +00:00
|
|
|
struct proc *p;
|
2002-11-18 12:28:15 +00:00
|
|
|
struct timespec ts;
|
2002-06-29 07:04:59 +00:00
|
|
|
|
2003-02-01 12:17:09 +00:00
|
|
|
p = td->td_proc;
|
2003-02-17 05:14:26 +00:00
|
|
|
kg = td->td_ksegrp;
|
2003-06-15 12:51:26 +00:00
|
|
|
ku = td->td_upcall;
|
2002-12-28 01:23:07 +00:00
|
|
|
|
2003-06-15 12:51:26 +00:00
|
|
|
/* Nothing to do with bound thread */
|
|
|
|
if (!(td->td_flags & TDF_SA))
|
2003-02-17 05:14:26 +00:00
|
|
|
return (0);
|
2002-10-09 02:33:36 +00:00
|
|
|
|
2002-09-16 19:26:48 +00:00
|
|
|
/*
|
2004-01-10 18:34:01 +00:00
|
|
|
* Stat clock interrupt hit in userland, it
|
2003-02-17 05:14:26 +00:00
|
|
|
* is returning from interrupt, charge thread's
|
|
|
|
* userland time for UTS.
|
2002-09-16 19:26:48 +00:00
|
|
|
*/
|
2003-02-17 05:14:26 +00:00
|
|
|
if (td->td_flags & TDF_USTATCLOCK) {
|
2003-02-26 00:58:23 +00:00
|
|
|
thread_update_usr_ticks(td, 1);
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
td->td_flags &= ~TDF_USTATCLOCK;
|
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-06-15 12:51:26 +00:00
|
|
|
if (kg->kg_completed ||
|
2003-02-26 00:58:23 +00:00
|
|
|
(td->td_upcall->ku_flags & KUF_DOUPCALL))
|
|
|
|
thread_user_enter(p, td);
|
2003-02-17 05:14:26 +00:00
|
|
|
}
|
2002-12-28 01:23:07 +00:00
|
|
|
|
2003-04-21 07:27:59 +00:00
|
|
|
uts_crit = (td->td_mailbox == NULL);
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* Optimisation:
|
|
|
|
* This thread has not started any upcall.
|
|
|
|
* If there is no work to report other than ourself,
|
|
|
|
* then it can return direct to userland.
|
|
|
|
*/
|
2002-12-28 01:23:07 +00:00
|
|
|
if (TD_CAN_UNBIND(td)) {
|
2003-02-17 05:14:26 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
td->td_flags &= ~TDF_CAN_UNBIND;
|
2003-03-31 22:49:17 +00:00
|
|
|
if ((td->td_flags & TDF_NEEDSIGCHK) == 0 &&
|
2003-03-11 02:59:50 +00:00
|
|
|
(kg->kg_completed == NULL) &&
|
2003-03-19 05:49:38 +00:00
|
|
|
(ku->ku_flags & KUF_DOUPCALL) == 0 &&
|
2003-04-19 06:16:04 +00:00
|
|
|
(kg->kg_upquantum && ticks < kg->kg_nextupcall)) {
|
2003-03-31 22:49:17 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-02-26 00:58:23 +00:00
|
|
|
thread_update_usr_ticks(td, 0);
|
2003-03-14 03:52:16 +00:00
|
|
|
nanotime(&ts);
|
2003-03-19 05:49:38 +00:00
|
|
|
error = copyout(&ts,
|
2003-03-14 03:52:16 +00:00
|
|
|
(caddr_t)&ku->ku_mailbox->km_timeofday,
|
|
|
|
sizeof(ts));
|
2003-03-11 02:59:50 +00:00
|
|
|
td->td_mailbox = 0;
|
2003-04-21 07:27:59 +00:00
|
|
|
ku->ku_mflags = 0;
|
2003-03-14 03:52:16 +00:00
|
|
|
if (error)
|
|
|
|
goto out;
|
2003-03-11 02:59:50 +00:00
|
|
|
return (0);
|
2002-12-28 01:23:07 +00:00
|
|
|
}
|
2003-03-31 22:49:17 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-07-17 22:45:33 +00:00
|
|
|
thread_export_context(td, 0);
|
2002-09-27 07:11:11 +00:00
|
|
|
/*
|
2003-02-17 05:14:26 +00:00
|
|
|
* There is something to report, and we own an upcall
|
|
|
|
* strucuture, we can go to userland.
|
|
|
|
* Turn ourself into an upcall thread.
|
2002-09-27 07:11:11 +00:00
|
|
|
*/
|
2003-06-15 03:18:58 +00:00
|
|
|
td->td_pflags |= TDP_UPCALLING;
|
2003-04-21 07:27:59 +00:00
|
|
|
} else if (td->td_mailbox && (ku == NULL)) {
|
2003-07-17 22:45:33 +00:00
|
|
|
thread_export_context(td, 1);
|
2003-03-11 00:07:53 +00:00
|
|
|
PROC_LOCK(p);
|
|
|
|
/*
|
|
|
|
* There are upcall threads waiting for
|
|
|
|
* work to do, wake one of them up.
|
2004-01-10 18:34:01 +00:00
|
|
|
* XXXKSE Maybe wake all of them up.
|
2003-03-11 00:07:53 +00:00
|
|
|
*/
|
2003-07-17 22:45:33 +00:00
|
|
|
if (kg->kg_upsleeps)
|
2003-03-11 00:07:53 +00:00
|
|
|
wakeup_one(&kg->kg_completed);
|
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
thread_stopped(p);
|
2002-12-28 01:23:07 +00:00
|
|
|
thread_exit();
|
2003-02-17 05:14:26 +00:00
|
|
|
/* NOTREACHED */
|
2002-10-09 02:33:36 +00:00
|
|
|
}
|
|
|
|
|
2003-06-15 12:51:26 +00:00
|
|
|
KASSERT(ku != NULL, ("upcall is NULL\n"));
|
2003-02-20 01:11:17 +00:00
|
|
|
KASSERT(TD_CAN_UNBIND(td) == 0, ("can unbind"));
|
|
|
|
|
|
|
|
if (p->p_numthreads > max_threads_per_proc) {
|
|
|
|
max_threads_hits++;
|
|
|
|
PROC_LOCK(p);
|
2003-04-23 18:46:51 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
2003-06-11 01:08:33 +00:00
|
|
|
p->p_maxthrwaits++;
|
2003-02-20 01:11:17 +00:00
|
|
|
while (p->p_numthreads > max_threads_per_proc) {
|
|
|
|
upcalls = 0;
|
|
|
|
FOREACH_KSEGRP_IN_PROC(p, kg2) {
|
|
|
|
if (kg2->kg_numupcalls == 0)
|
|
|
|
upcalls++;
|
|
|
|
else
|
|
|
|
upcalls += kg2->kg_numupcalls;
|
|
|
|
}
|
|
|
|
if (upcalls >= max_threads_per_proc)
|
|
|
|
break;
|
2003-04-27 04:32:40 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-06-10 02:21:32 +00:00
|
|
|
if (msleep(&p->p_numthreads, &p->p_mtx, PPAUSE|PCATCH,
|
2003-12-23 02:28:42 +00:00
|
|
|
"maxthreads", 0)) {
|
2003-06-11 01:08:33 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
}
|
2003-02-20 01:11:17 +00:00
|
|
|
}
|
2003-06-11 01:08:33 +00:00
|
|
|
p->p_maxthrwaits--;
|
2003-04-23 18:46:51 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2003-02-20 01:11:17 +00:00
|
|
|
PROC_UNLOCK(p);
|
|
|
|
}
|
|
|
|
|
2003-06-15 03:18:58 +00:00
|
|
|
if (td->td_pflags & TDP_UPCALLING) {
|
2003-04-21 07:27:59 +00:00
|
|
|
uts_crit = 0;
|
2003-03-19 05:49:38 +00:00
|
|
|
kg->kg_nextupcall = ticks+kg->kg_upquantum;
|
2004-01-10 18:34:01 +00:00
|
|
|
/*
|
2002-12-28 01:23:07 +00:00
|
|
|
* There is no more work to do and we are going to ride
|
2003-02-17 05:14:26 +00:00
|
|
|
* this thread up to userland as an upcall.
|
2002-12-28 01:23:07 +00:00
|
|
|
* Do the last parts of the setup needed for the upcall.
|
|
|
|
*/
|
|
|
|
CTR3(KTR_PROC, "userret: upcall thread %p (pid %d, %s)",
|
|
|
|
td, td->td_proc->p_pid, td->td_proc->p_comm);
|
2002-10-09 02:33:36 +00:00
|
|
|
|
2003-06-15 03:18:58 +00:00
|
|
|
td->td_pflags &= ~TDP_UPCALLING;
|
2003-06-15 12:51:26 +00:00
|
|
|
if (ku->ku_flags & KUF_DOUPCALL) {
|
|
|
|
mtx_lock_spin(&sched_lock);
|
2003-02-17 05:14:26 +00:00
|
|
|
ku->ku_flags &= ~KUF_DOUPCALL;
|
2003-06-15 12:51:26 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
}
|
2003-04-21 07:27:59 +00:00
|
|
|
/*
|
|
|
|
* Set user context to the UTS
|
|
|
|
*/
|
|
|
|
if (!(ku->ku_mflags & KMF_NOUPCALL)) {
|
|
|
|
cpu_set_upcall_kse(td, ku);
|
|
|
|
error = suword(&ku->ku_mailbox->km_curthread, 0);
|
|
|
|
if (error)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
2002-12-28 01:23:07 +00:00
|
|
|
* Unhook the list of completed threads.
|
2004-01-10 18:34:01 +00:00
|
|
|
* anything that completes after this gets to
|
2002-12-28 01:23:07 +00:00
|
|
|
* come in next time.
|
|
|
|
* Put the list of completed thread mailboxes on
|
|
|
|
* this KSE's mailbox.
|
|
|
|
*/
|
2003-04-21 07:27:59 +00:00
|
|
|
if (!(ku->ku_mflags & KMF_NOCOMPLETED) &&
|
|
|
|
(error = thread_link_mboxes(kg, ku)) != 0)
|
2003-02-19 04:01:55 +00:00
|
|
|
goto out;
|
2003-04-21 07:27:59 +00:00
|
|
|
}
|
|
|
|
if (!uts_crit) {
|
2002-11-18 12:28:15 +00:00
|
|
|
nanotime(&ts);
|
2003-04-21 07:27:59 +00:00
|
|
|
error = copyout(&ts, &ku->ku_mailbox->km_timeofday, sizeof(ts));
|
2003-02-19 04:01:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
if (error) {
|
|
|
|
/*
|
2003-02-19 13:40:24 +00:00
|
|
|
* Things are going to be so screwed we should just kill
|
|
|
|
* the process.
|
2003-02-19 04:01:55 +00:00
|
|
|
* how do we do that?
|
|
|
|
*/
|
|
|
|
PROC_LOCK(td->td_proc);
|
|
|
|
psignal(td->td_proc, SIGSEGV);
|
|
|
|
PROC_UNLOCK(td->td_proc);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Optimisation:
|
|
|
|
* Ensure that we have a spare thread available,
|
|
|
|
* for when we re-enter the kernel.
|
|
|
|
*/
|
|
|
|
if (td->td_standin == NULL)
|
|
|
|
thread_alloc_spare(td, NULL);
|
2002-11-18 12:28:15 +00:00
|
|
|
}
|
2002-12-28 01:23:07 +00:00
|
|
|
|
2003-04-21 07:27:59 +00:00
|
|
|
ku->ku_mflags = 0;
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
* Clear thread mailbox first, then clear system tick count.
|
2004-01-10 18:34:01 +00:00
|
|
|
* The order is important because thread_statclock() use
|
2003-02-17 05:14:26 +00:00
|
|
|
* mailbox pointer to see if it is an userland thread or
|
|
|
|
* an UTS kernel thread.
|
|
|
|
*/
|
2002-12-28 01:23:07 +00:00
|
|
|
td->td_mailbox = NULL;
|
2003-02-17 05:14:26 +00:00
|
|
|
td->td_usticks = 0;
|
2002-10-09 02:33:36 +00:00
|
|
|
return (error); /* go sync */
|
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
|
|
|
|
* accellerated in reaching the user boundary as we will wake up
|
|
|
|
* any sleeping threads that are interruptable. (PCATCH).
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
thread_single(int force_exit)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
struct thread *td2;
|
|
|
|
struct proc *p;
|
|
|
|
|
|
|
|
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);
|
|
|
|
KASSERT((td != NULL), ("curthread is NULL"));
|
|
|
|
|
2003-06-15 00:31:24 +00:00
|
|
|
if ((p->p_flag & P_SA) == 0 && p->p_numthreads == 1)
|
2002-06-29 07:04:59 +00:00
|
|
|
return (0);
|
|
|
|
|
2002-07-25 00:27:39 +00:00
|
|
|
/* Is someone already single threading? */
|
2004-01-10 18:34:01 +00:00
|
|
|
if (p->p_singlethread)
|
2002-06-29 07:04:59 +00:00
|
|
|
return (1);
|
|
|
|
|
2002-12-28 01:23:07 +00:00
|
|
|
if (force_exit == SINGLE_EXIT) {
|
2002-06-29 07:04:59 +00:00
|
|
|
p->p_flag |= P_SINGLE_EXIT;
|
2002-12-28 01:23:07 +00:00
|
|
|
} else
|
2002-06-29 07:04:59 +00:00
|
|
|
p->p_flag &= ~P_SINGLE_EXIT;
|
2002-09-05 07:30:18 +00:00
|
|
|
p->p_flag |= P_STOPPED_SINGLE;
|
2003-04-23 18:46:51 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
2002-06-29 07:04:59 +00:00
|
|
|
p->p_singlethread = td;
|
|
|
|
while ((p->p_numthreads - p->p_suspcount) != 1) {
|
|
|
|
FOREACH_THREAD_IN_PROC(p, td2) {
|
|
|
|
if (td2 == td)
|
|
|
|
continue;
|
2003-04-19 04:39:10 +00:00
|
|
|
td2->td_flags |= TDF_ASTPENDING;
|
2002-09-11 08:13:56 +00:00
|
|
|
if (TD_IS_INHIBITED(td2)) {
|
2002-10-25 07:11:12 +00:00
|
|
|
if (force_exit == SINGLE_EXIT) {
|
|
|
|
if (TD_IS_SUSPENDED(td2)) {
|
2002-09-11 08:13:56 +00:00
|
|
|
thread_unsuspend_one(td2);
|
|
|
|
}
|
2002-10-25 07:11:12 +00:00
|
|
|
if (TD_ON_SLEEPQ(td2) &&
|
|
|
|
(td2->td_flags & TDF_SINTR)) {
|
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_abort(td2);
|
2002-10-25 07:11:12 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (TD_IS_SUSPENDED(td2))
|
|
|
|
continue;
|
2003-02-17 05:14:26 +00:00
|
|
|
/*
|
|
|
|
* maybe other inhibitted states too?
|
|
|
|
* XXXKSE Is it totally safe to
|
|
|
|
* suspend a non-interruptable thread?
|
|
|
|
*/
|
2002-12-28 01:23:07 +00:00
|
|
|
if (td2->td_inhibitors &
|
2003-02-17 05:14:26 +00:00
|
|
|
(TDI_SLEEPING | TDI_SWAPPED))
|
2002-10-25 07:11:12 +00:00
|
|
|
thread_suspend_one(td2);
|
2002-09-11 08:13:56 +00:00
|
|
|
}
|
2002-06-29 07:04:59 +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
|
|
|
*/
|
2003-04-23 18:46:51 +00:00
|
|
|
if ((p->p_numthreads - p->p_suspcount) == 1)
|
2002-10-25 07:11:12 +00:00
|
|
|
break;
|
|
|
|
|
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
|
|
|
*/
|
2002-09-11 08:13:56 +00:00
|
|
|
thread_suspend_one(td);
|
2002-06-29 07:04:59 +00:00
|
|
|
PROC_UNLOCK(p);
|
2004-01-25 03:54:52 +00:00
|
|
|
mi_switch(SW_VOL);
|
2002-06-29 07:04:59 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
PROC_LOCK(p);
|
2003-04-23 18:46:51 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2004-01-10 18:34:01 +00:00
|
|
|
if (force_exit == SINGLE_EXIT) {
|
2003-04-23 18:46:51 +00:00
|
|
|
if (td->td_upcall)
|
2003-02-17 05:14:26 +00:00
|
|
|
upcall_remove(td);
|
2002-10-24 08:46:34 +00:00
|
|
|
kse_purge(p, td);
|
2003-02-17 05:14:26 +00:00
|
|
|
}
|
2003-04-23 18:46:51 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2002-06-29 07:04:59 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* | when ST ends | immediatly
|
|
|
|
*---------------+--------------------+---------------------
|
|
|
|
* 1 | thread exits | returns 1
|
|
|
|
* | | immediatly
|
|
|
|
* 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;
|
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);
|
|
|
|
while (P_SHOULDSTOP(p)) {
|
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.
|
2004-01-10 18:34:01 +00:00
|
|
|
* XXX Should be safe to access unlocked
|
2002-07-24 23:21:05 +00:00
|
|
|
* as it can only be set to be true 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
|
|
|
}
|
2002-07-25 00:27:39 +00:00
|
|
|
if (return_instead)
|
2002-06-29 07:04:59 +00:00
|
|
|
return (1);
|
|
|
|
|
2003-03-11 00:07:53 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
|
|
|
thread_stopped(p);
|
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
|
|
|
*/
|
|
|
|
if ((p->p_flag & P_SINGLE_EXIT) && (p->p_singlethread != td)) {
|
2003-06-15 00:31:24 +00:00
|
|
|
if (p->p_flag & P_SA)
|
2003-04-01 01:26:20 +00:00
|
|
|
thread_exit();
|
|
|
|
else
|
|
|
|
thr_exit1();
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When a thread suspends, it just
|
|
|
|
* moves to the processes's suspend queue
|
|
|
|
* and stays there.
|
|
|
|
*/
|
2002-09-11 08:13:56 +00:00
|
|
|
thread_suspend_one(td);
|
2002-09-05 07:30:18 +00:00
|
|
|
if (P_SHOULDSTOP(p) == P_STOPPED_SINGLE) {
|
2002-07-24 19:50:08 +00:00
|
|
|
if (p->p_numthreads == p->p_suspcount) {
|
2002-09-11 08:13:56 +00:00
|
|
|
thread_unsuspend_one(p->p_singlethread);
|
2002-07-24 19:50:08 +00:00
|
|
|
}
|
|
|
|
}
|
2003-04-22 19:47:55 +00:00
|
|
|
PROC_UNLOCK(p);
|
2004-01-25 03:54:52 +00:00
|
|
|
mi_switch(SW_INVOL);
|
2002-06-29 07:04:59 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
|
|
|
PROC_LOCK(p);
|
|
|
|
}
|
|
|
|
return (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
|
|
|
void
|
|
|
|
thread_suspend_one(struct thread *td)
|
|
|
|
{
|
|
|
|
struct proc *p = td->td_proc;
|
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
2003-04-23 18:46:51 +00:00
|
|
|
PROC_LOCK_ASSERT(p, 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++;
|
2002-09-11 08:13:56 +00:00
|
|
|
TD_SET_SUSPENDED(td);
|
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
|
|
|
TAILQ_INSERT_TAIL(&p->p_suspended, td, td_runq);
|
2002-09-11 08:13:56 +00:00
|
|
|
/*
|
|
|
|
* Hack: If we are suspending but are on the sleep queue
|
|
|
|
* then we are in msleep or the cv equivalent. We
|
|
|
|
* want to look like we have two Inhibitors.
|
2002-10-25 07:11:12 +00:00
|
|
|
* May already be set.. doesn't matter.
|
2002-09-11 08:13:56 +00:00
|
|
|
*/
|
|
|
|
if (TD_ON_SLEEPQ(td))
|
|
|
|
TD_SET_SLEEPING(td);
|
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_unsuspend_one(struct thread *td)
|
|
|
|
{
|
|
|
|
struct proc *p = td->td_proc;
|
|
|
|
|
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
2003-04-23 18:46:51 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
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
|
|
|
TAILQ_REMOVE(&p->p_suspended, td, td_runq);
|
2002-09-11 08:13:56 +00:00
|
|
|
TD_CLR_SUSPENDED(td);
|
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--;
|
2002-09-11 08:13:56 +00:00
|
|
|
setrunnable(td);
|
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
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2002-07-24 23:21:05 +00:00
|
|
|
mtx_assert(&sched_lock, MA_OWNED);
|
2002-06-29 07:04:59 +00:00
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
|
|
|
if (!P_SHOULDSTOP(p)) {
|
|
|
|
while (( td = TAILQ_FIRST(&p->p_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
|
|
|
thread_unsuspend_one(td);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
2002-09-05 07:30:18 +00:00
|
|
|
} else if ((P_SHOULDSTOP(p) == P_STOPPED_SINGLE) &&
|
2002-06-29 07:04:59 +00:00
|
|
|
(p->p_numthreads == p->p_suspcount)) {
|
|
|
|
/*
|
|
|
|
* Stopping everything also did the job for the single
|
|
|
|
* threading request. Now we've downgraded to single-threaded,
|
|
|
|
* let it continue.
|
|
|
|
*/
|
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
|
|
|
thread_unsuspend_one(p->p_singlethread);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
thread_single_end(void)
|
|
|
|
{
|
|
|
|
struct thread *td;
|
|
|
|
struct proc *p;
|
|
|
|
|
|
|
|
td = curthread;
|
|
|
|
p = td->td_proc;
|
|
|
|
PROC_LOCK_ASSERT(p, MA_OWNED);
|
2002-09-05 07:30:18 +00:00
|
|
|
p->p_flag &= ~P_STOPPED_SINGLE;
|
2003-04-23 18:46:51 +00:00
|
|
|
mtx_lock_spin(&sched_lock);
|
2002-06-29 07:04:59 +00:00
|
|
|
p->p_singlethread = NULL;
|
2002-08-22 21:45:58 +00:00
|
|
|
/*
|
|
|
|
* If there are other threads they mey now run,
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
if ((p->p_numthreads != 1) && (!P_SHOULDSTOP(p))) {
|
|
|
|
while (( td = TAILQ_FIRST(&p->p_suspended))) {
|
2002-09-11 08:13:56 +00:00
|
|
|
thread_unsuspend_one(td);
|
2002-08-22 21:45:58 +00:00
|
|
|
}
|
|
|
|
}
|
2003-04-23 18:46:51 +00:00
|
|
|
mtx_unlock_spin(&sched_lock);
|
2002-06-29 07:04:59 +00:00
|
|
|
}
|