Source to osfmk/i386/hardclock.c
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
*/
/*
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or [email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
*/
/*
* Clock interrupt.
*/
#include <cpus.h>
#include <time_stamp.h>
#include <mach_kdb.h>
#include <kern/cpu_number.h>
#include <kern/cpu_data.h>
#include <kern/kern_types.h>
#include <platforms.h>
#include <mp_v1_1.h>
#include <mach_kprof.h>
#include <mach_mp_debug.h>
#include <mach/std_types.h>
#include <mach/clock_types.h>
#include <mach/boolean.h>
#include <i386/thread.h>
#include <i386/eflags.h>
#include <kern/assert.h>
#include <kern/misc_protos.h>
#include <i386/misc_protos.h>
#include <kern/time_out.h>
#include <i386/ipl.h>
#include <i386/hardclock_entries.h>
#include <i386/rtclock_entries.h>
#if MACH_MP_DEBUG
#include <i386/mach_param.h> /* for HZ */
#endif /* MACH_MP_DEBUG */
extern char return_to_iret[];
#if TIME_STAMP && NCPUS > 1
extern unsigned time_stamp;
unsigned old_time_stamp, time_stamp_cum, nstamps;
/*
* If H/W provides a counter, record number of ticks and cumulated
* time stamps to know timestamps rate.
* This should go away when ALARMCLOCKS installed
*/
#define time_stamp_stat() \
if (my_cpu == 0) \
if (!old_time_stamp) { \
old_time_stamp = time_stamp; \
nstamps = 0; \
} else { \
nstamps++; \
time_stamp_cum = (time_stamp - old_time_stamp); \
}
#else /* TIME_STAMP && AT386 && NCPUS > 1 */
#define time_stamp_stat()
#endif /* TIME_STAMP && AT386 && NCPUS > 1 */
#if MACH_KPROF
int masked_pc[NCPUS];
int missed_clock[NCPUS];
int detect_lost_tick = 0;
#endif /* MACH_KPROF */
#if MACH_MP_DEBUG
int masked_state_cnt[NCPUS];
int masked_state_max = 10*HZ;
#endif /* MACH_MP_DEBUG */
/*
* In the interest of a fast clock interrupt service path,
* this routine should be folded into assembly language with
* a direct interrupt vector on the i386. The "pit" interrupt
* should always call the rtclock_intr() routine on the master
* processor. The return value of the rtclock_intr() routine
* indicates whether HZ rate clock processing should be
* performed. (On the Sequent, all slave processors will
* run at HZ rate). For now, we'll leave this routine in C
* (with TIME_STAMP, MACH_MP_DEBUG and MACH_KPROF code this
* routine is way too large for assembler anyway).
*/
#ifdef PARANOID_KDB
int paranoid_debugger = TRUE;
int paranoid_count = 1000;
int paranoid_current = 0;
int paranoid_cpu = 0;
#endif /* PARANOID_KDB */
void
hardclock(struct i386_interrupt_state *regs) /* saved registers */
{
int mycpu;
register unsigned pc;
register boolean_t usermode;
mp_disable_preemption();
mycpu = cpu_number();
#ifdef PARANOID_KDB
if (paranoid_cpu == mycpu &&
paranoid_current++ >= paranoid_count) {
paranoid_current = 0;
if (paranoid_debugger)
Debugger("hardclock");
}
#endif /* PARANOID_KDB */
#if 0
#if MACH_MP_DEBUG
/*
* Increments counter of clock ticks handled under a masked state.
* Debugger() is called if masked state is kept during 1 sec.
* The counter is reset by splx() when ipl mask is set back to SPL0,
* and by spl0().
*/
if (SPL_CMP_GT((old_ipl & 0xFF), SPL0)) {
if (masked_state_cnt[mycpu]++ >= masked_state_max) {
int max_save = masked_state_max;
masked_state_cnt[mycpu] = 0;
masked_state_max = 0x7fffffff;
if (ret_addr == return_to_iret) {
usermode = (regs->efl & EFL_VM) ||
((regs->cs & 0x03) != 0);
pc = (unsigned)regs->eip;
} else {
usermode = FALSE;
pc = (unsigned)
((struct i386_interrupt_state *)&old_ipl)->eip;
}
printf("looping at high IPL, usermode=%d pc=0x%x\n",
usermode, pc);
Debugger("");
masked_state_cnt[mycpu] = 0;
masked_state_max = max_save;
}
} else
masked_state_cnt[mycpu] = 0;
#endif /* MACH_MP_DEBUG */
#endif
#if MACH_KPROF
/*
* If we were masked against the clock skip call
* to rtclock_intr(). When MACH_KPROF is set, the
* clock frequency of the master-cpu is confined
* to the HZ rate.
*/
if (SPL_CMP_LT(old_ipl & 0xFF, SPL7))
#endif /* MACH_KPROF */
/*
* The master processor executes the rtclock_intr() routine
* on every clock tick. The rtclock_intr() routine returns
* a zero value on a HZ tick boundary.
*/
if (mycpu == master_cpu) {
if (rtclock_intr() != 0) {
mp_enable_preemption();
return;
}
}
/*
* The following code is executed at HZ rate by all processors
* in the system. This implies that the clock rate on slave
* processors must be HZ rate.
*/
time_stamp_stat();
#if 0
if (ret_addr == return_to_iret) {
/*
* A kernel-loaded task executing within itself will look like
* "kernel mode", here. This is correct with syscalls
* implemented using migrating threads, because it means that
* the time spent in the server by a client thread will be
* treated as "system" time for the client thread (and nothing
* for the server). This conforms to the CPU reporting for an
* integrated kernel.
*/
#endif
usermode = (regs->efl & EFL_VM) || ((regs->cs & 0x03) != 0);
pc = (unsigned)regs->eip;
#if 0
} else {
usermode = FALSE;
pc = (unsigned)((struct i386_interrupt_state *)&old_ipl)->eip;
}
#endif
#if MACH_KPROF
/*
* If we were masked against the clock, just memorize pc
* and the fact that the clock interrupt is delayed
*/
if (SPL_CMP_GE((old_ipl & 0xFF), SPL7)) {
assert(!usermode);
if (missed_clock[mycpu]++ && detect_lost_tick > 1)
Debugger("Mach_KPROF");
masked_pc[mycpu] = pc;
} else
#endif /* MACH_KPROF */
hertz_tick(usermode, pc);
#if NCPUS >1
/*
* Instead of having the master processor interrupt
* all active processors, each processor in turn interrupts
* the next active one. This avoids all slave processors
* accessing the same R/W data simultaneously.
*/
slave_clock();
#endif /* NCPUS >1 && AT386 */
mp_enable_preemption();
}
#if MACH_KPROF
void
delayed_clock(void)
{
int i;
int my_cpu;
mp_disable_preemption();
my_cpu = cpu_number();
if (missed_clock[my_cpu] > 1 && detect_lost_tick)
printf("hardclock: missed %d clock interrupt(s) at %x\n",
missed_clock[my_cpu]-1, masked_pc[my_cpu]);
if (my_cpu == master_cpu) {
i = rtclock_intr();
assert(i == 0);
}
hertz_tick(0, masked_pc[my_cpu]);
missed_clock[my_cpu] = 0;
mp_enable_preemption();
}
#endif /* MACH_KPROF */