freebsd-nq/sys/kern/subr_trap.c
John Baldwin 7e1f6dfe9d Modify the critical section API as follows:
- The MD functions critical_enter/exit are renamed to start with a cpu_
  prefix.
- MI wrapper functions critical_enter/exit maintain a per-thread nesting
  count and a per-thread critical section saved state set when entering
  a critical section while at nesting level 0 and restored when exiting
  to nesting level 0.  This moves the saved state out of spin mutexes so
  that interlocking spin mutexes works properly.
- Most low-level MD code that used critical_enter/exit now use
  cpu_critical_enter/exit.  MI code such as device drivers and spin
  mutexes use the MI wrappers.  Note that since the MI wrappers store
  the state in the current thread, they do not have any return values or
  arguments.
- mtx_intr_enable() is replaced with a constant CRITICAL_FORK which is
  assigned to curthread->td_savecrit during fork_exit().

Tested on:	i386, alpha
2001-12-18 00:27:18 +00:00

208 lines
6.0 KiB
C

/*-
* Copyright (C) 1994, David Greenman
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the University of Utah, and William Jolitz.
*
* 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, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
*
* from: @(#)trap.c 7.4 (Berkeley) 5/13/91
* $FreeBSD$
*/
#ifdef __i386__
#include "opt_npx.h"
#endif
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/systm.h>
#include <sys/vmmeter.h>
#include <machine/cpu.h>
#include <machine/pcb.h>
/*
* Define the code needed before returning to user mode, for
* trap and syscall.
*
* MPSAFE
*/
void
userret(td, frame, oticks)
struct thread *td;
struct trapframe *frame;
u_int oticks;
{
struct proc *p = td->td_proc;
struct kse *ke = td->td_kse;
struct ksegrp *kg = td->td_ksegrp;
int sig;
mtx_lock(&Giant);
PROC_LOCK(p);
while ((sig = CURSIG(p)) != 0)
postsig(sig);
PROC_UNLOCK(p);
mtx_unlock(&Giant);
mtx_lock_spin(&sched_lock);
kg->kg_pri.pri_level = kg->kg_pri.pri_user;
if (ke->ke_flags & KEF_NEEDRESCHED) {
/*
* Since we are curproc, a clock interrupt could
* change our priority without changing run queues
* (the running process is not kept on a run queue).
* If this happened after we setrunqueue ourselves but
* before we switch()'ed, we might not be on the queue
* indicated by our priority.
*/
DROP_GIANT_NOSWITCH();
setrunqueue(td);
p->p_stats->p_ru.ru_nivcsw++;
mi_switch();
mtx_unlock_spin(&sched_lock);
PICKUP_GIANT();
mtx_lock(&Giant);
PROC_LOCK(p);
while ((sig = CURSIG(p)) != 0)
postsig(sig);
mtx_unlock(&Giant);
PROC_UNLOCK(p);
} else
mtx_unlock_spin(&sched_lock);
/*
* Charge system time if profiling.
*/
if (p->p_sflag & PS_PROFIL) {
addupc_task(ke, TRAPF_PC(frame),
(u_int)(ke->ke_sticks - oticks) * psratio);
}
}
/*
* Process an asynchronous software trap.
* This is relatively easy.
* This function will return with preemption disabled.
*/
void
ast(framep)
struct trapframe *framep;
{
struct thread *td = curthread;
struct proc *p = td->td_proc;
struct kse *ke = td->td_kse;
u_int prticks, sticks;
critical_t s;
int sflag;
int flags;
#if defined(DEV_NPX) && !defined(SMP)
int ucode;
#endif
KASSERT(TRAPF_USERMODE(framep), ("ast in kernel mode"));
KASSERT(td->td_ucred == NULL, ("leaked ucred"));
#ifdef WITNESS
if (witness_list(td))
panic("Returning to user mode with mutex(s) held");
#endif
mtx_assert(&Giant, MA_NOTOWNED);
s = cpu_critical_enter();
while ((ke->ke_flags & (KEF_ASTPENDING | KEF_NEEDRESCHED)) != 0) {
cpu_critical_exit(s);
td->td_frame = framep;
/*
* This updates the p_sflag's for the checks below in one
* "atomic" operation with turning off the astpending flag.
* If another AST is triggered while we are handling the
* AST's saved in sflag, the astpending flag will be set and
* we will loop again.
*/
mtx_lock_spin(&sched_lock);
sticks = ke->ke_sticks;
sflag = p->p_sflag;
flags = ke->ke_flags;
p->p_sflag &= ~(PS_PROFPEND | PS_ALRMPEND);
ke->ke_flags &= ~(KEF_OWEUPC | KEF_ASTPENDING);
cnt.v_soft++;
if (flags & KEF_OWEUPC) {
prticks = p->p_stats->p_prof.pr_ticks;
p->p_stats->p_prof.pr_ticks = 0;
}
mtx_unlock_spin(&sched_lock);
PROC_LOCK(p);
td->td_ucred = crhold(p->p_ucred);
PROC_UNLOCK(p);
if (flags & KEF_OWEUPC)
addupc_task(ke, p->p_stats->p_prof.pr_addr, prticks);
if (sflag & PS_ALRMPEND) {
PROC_LOCK(p);
psignal(p, SIGVTALRM);
PROC_UNLOCK(p);
}
#if defined(DEV_NPX) && !defined(SMP)
if (PCPU_GET(curpcb)->pcb_flags & PCB_NPXTRAP) {
atomic_clear_char(&PCPU_GET(curpcb)->pcb_flags,
PCB_NPXTRAP);
ucode = npxtrap();
if (ucode != -1) {
trapsignal(p, SIGFPE, ucode);
}
}
#endif
if (sflag & PS_PROFPEND) {
PROC_LOCK(p);
psignal(p, SIGPROF);
PROC_UNLOCK(p);
}
userret(td, framep, sticks);
mtx_lock(&Giant);
crfree(td->td_ucred);
mtx_unlock(&Giant);
td->td_ucred = NULL;
s = cpu_critical_enter();
}
mtx_assert(&Giant, MA_NOTOWNED);
/*
* We need to keep interrupts disabled so that if any further AST's
* come in, the interrupt they come in on will be delayed until we
* finish returning to userland. We assume that the return to userland
* will perform the equivalent of cpu_critical_exit().
*/
}