bitkeeper revision 1.14.1.1 (3e397e7cPGmZK7y5LLOGYa43nTQMUw)

added time and accurate timer support
partially there for better scheduler (most of the infrastructure should be there)
This commit is contained in:
rn@wyvis.camb.intel-research.net committed 2003-01-30 19:35:24 +00:00
1 parent 22a857bde9
commit 9062553a0d
22 files changed
+1754 -495

No files matched your search

+4
View File
@@ -35,6 +35,7 @@
3ddb79bc4nTpGQOe6_-MbyZzkhlhFQ xen-2.4.16/arch/i386/usercopy.c
3ddb79bcOMCu9-5mKpjIh5d0qqBDPg xen-2.4.16/arch/i386/xeno.lds
3ddb79bdff-gj-jFGKjOejeHLqL8Lg xen-2.4.16/common/Makefile
3e397e66AyyD5fYraAySWuwi9uqSXg xen-2.4.16/common/ac_timer.c
3ddb79bddEYJbcURvqqcx99Yl2iAhQ xen-2.4.16/common/block.c
3ddb79bdrqnW93GR9gZk1OJe1qK-iQ xen-2.4.16/common/brlock.c
3ddb79bdLX_P6iB7ILiblRLWvebapg xen-2.4.16/common/dom0_ops.c
@@ -46,6 +47,7 @@
3ddb79bdN51qpRC-6bOH-v5hl_AK6A xen-2.4.16/common/network.c
3ddb79bdD4SLmmdMD7yLW5HcUWucXw xen-2.4.16/common/page_alloc.c
3ddb79bdHqdQpATqC0rmUZNbsb6L6A xen-2.4.16/common/resource.c
3e397e6619PgAfBbw2XFbXkewvUWgw xen-2.4.16/common/schedule.c
3ddb79bdB9RNMnkQnUyZ5C9hhMSQQw xen-2.4.16/common/slab.c
3ddb79bd0gVQYmL2zvuJnldvD0AGxQ xen-2.4.16/common/softirq.c
3ddb79bdQqFHtHRGEO2dsxGgo6eAhw xen-2.4.16/common/timer.c
@@ -157,6 +159,7 @@
3ddb79c3e9DCEoR-WzNxcOQDzLu7BQ xen-2.4.16/include/asm-i386/softirq.h
3ddb79c3NiyQE2vQnyGiaBnNjBO1rA xen-2.4.16/include/asm-i386/spinlock.h
3ddb79c3ezddh34MdelJpa5tNR00Dw xen-2.4.16/include/asm-i386/system.h
3e397e66xPNc8eaSqC9pPbyAtRGzHA xen-2.4.16/include/asm-i386/time.h
3ddb79c4HugMq7IYGxcQKFBpKwKhzA xen-2.4.16/include/asm-i386/types.h
3ddb79c3M2n1ROZH6xk3HbyN4CPDqg xen-2.4.16/include/asm-i386/uaccess.h
3ddb79c3uPGcP_l_2xyGgBSWd5aC-Q xen-2.4.16/include/asm-i386/unaligned.h
@@ -167,6 +170,7 @@
3ddb79c4R4iVwqIIeychVQYmIH4FUg xen-2.4.16/include/scsi/scsi_ioctl.h
3ddb79c4yw_mfd4Uikn3v_IOPRpa1Q xen-2.4.16/include/scsi/scsicam.h
3ddb79c4HKPMLvDBP9LxzPi_szVxGA xen-2.4.16/include/scsi/sg.h
3e397e66m2tO3s-J8Jnr7Ws_tGoPTg xen-2.4.16/include/xeno/ac_timer.h
3ddb79c0nTsjSpVK4ZVTI9WwN24xtQ xen-2.4.16/include/xeno/blk.h
3ddb79c0dVhTHLsv6CPTf4baKix4mA xen-2.4.16/include/xeno/blkdev.h
3ddb79c18ePBgitnOs7GiOCFilODVw xen-2.4.16/include/xeno/blkpg.h
+1
View File
@@ -3,5 +3,6 @@ akw27@labyrinth.cl.cam.ac.uk
kaf24@labyrinth.cl.cam.ac.uk
kaf24@plym.cl.cam.ac.uk
kaf24@striker.cl.cam.ac.uk
rn@wyvis.camb.intel-research.net
smh22@boulderdash.cl.cam.ac.uk
smh22@uridium.cl.cam.ac.uk
+183 -140
View File
@@ -1,3 +1,23 @@
/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
****************************************************************************
* (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
****************************************************************************
*
* File: apic.c
* Author:
* Changes:
*
* Date: Nov 2002
*
* Environment: Xen Hypervisor
* Description: programmable APIC timer interface for accurate timers
* modified version of Linux' apic.c
*
****************************************************************************
* $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
****************************************************************************
*/
/*
* Local APIC handling, local APIC timers
*
@@ -10,6 +30,7 @@
* for testing these extensively.
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/sched.h>
@@ -25,6 +46,17 @@
#include <asm/pgalloc.h>
#include <asm/hardirq.h>
#include <xeno/ac_timer.h>
#undef APIC_TIME_TRACE
#ifdef APIC_TIME_TRACE
#define TRC(_x) _x
#else
#define TRC(_x)
#endif
/* Using APIC to generate smp_local_timer_interrupt? */
int using_apic_timer = 0;
@@ -39,7 +71,7 @@ int get_maxlvt(void)
return maxlvt;
}
void clear_local_APIC(void)
static void clear_local_APIC(void)
{
int maxlvt;
unsigned long v;
@@ -313,7 +345,6 @@ void __init setup_local_APIC (void)
*/
value = apic_read(APIC_LVT0) & APIC_LVT_MASKED;
if (!smp_processor_id()) {
/* && (pic_mode || !value)) { */
value = APIC_DM_EXTINT;
printk("enabled ExtINT on CPU#%d\n", smp_processor_id());
} else {
@@ -340,11 +371,9 @@ void __init setup_local_APIC (void)
value = apic_read(APIC_ESR);
printk("ESR value before enabling vector: %08lx\n", value);
value = ERROR_APIC_VECTOR; // enables sending errors
value = ERROR_APIC_VECTOR; /* enables sending errors */
apic_write_around(APIC_LVTERR, value);
/*
* spec says clear errors after enabling vector.
*/
/* spec says clear errors after enabling vector. */
if (maxlvt > 3)
apic_write(APIC_ESR, 0);
value = apic_read(APIC_ESR);
@@ -416,9 +445,7 @@ static int __init detect_init_APIC (void)
boot_cpu_physical_apicid = 0;
printk("Found and enabled local APIC!\n");
apic_pm_init1();
return 0;
no_apic:
@@ -467,14 +494,24 @@ void __init init_apic_mappings(void)
#endif
}
/*
* This part sets up the APIC 32 bit clock in LVTT1, with HZ interrupts
* per second. We assume that the caller has already set up the local
* APIC.
*
* The APIC timer is not exactly sync with the external timer chip, it
* closely follows bus clocks.
*/
/*****************************************************************************
* APIC calibration
*
* The APIC is programmed in bus cycles.
* Timeout values should specified in real time units.
* The "cheapest" time source is the cyclecounter.
*
* Thus, we need a mappings from: bus cycles <- cycle counter <- system time
*
* The calibration is currently a bit shoddy since it requires the external
* timer chip to generate periodic timer interupts.
*****************************************************************************/
/* used for system time scaling */
static unsigned int bus_freq;
static u32 bus_cycle; /* length of one bus cycle in pico-seconds */
static u32 bus_scale; /* scaling factor convert ns to bus cycles */
/*
* The timer chip is already set up at HZ interrupts per second here,
@@ -485,17 +522,12 @@ static unsigned int __init get_8254_timer_count(void)
{
/*extern spinlock_t i8253_lock;*/
/*unsigned long flags;*/
unsigned int count;
/*spin_lock_irqsave(&i8253_lock, flags);*/
outb_p(0x00, 0x43);
count = inb_p(0x40);
count |= inb_p(0x40) << 8;
/*spin_unlock_irqrestore(&i8253_lock, flags);*/
return count;
}
@@ -503,112 +535,67 @@ void __init wait_8254_wraparound(void)
{
unsigned int curr_count, prev_count=~0;
int delta;
curr_count = get_8254_timer_count();
do {
prev_count = curr_count;
curr_count = get_8254_timer_count();
delta = curr_count-prev_count;
/*
* This limit for delta seems arbitrary, but it isn't, it's
* slightly above the level of error a buggy Mercury/Neptune
* chipset timer can cause.
*/
} while (delta < 300);
}
/*
* This function sets up the local APIC timer, with a timeout of
* 'clocks' APIC bus clock. During calibration we actually call
* this function twice on the boot CPU, once with a bogus timeout
* value, second time for real. The other (noncalibrating) CPUs
* call this function only once, with the real, calibrated value.
* this function with a very large value and read the current time after
* a well defined period of time as expired.
*
* Calibration is only performed once, for CPU0!
*
* We do reads before writes even if unnecessary, to get around the
* P5 APIC double write bug.
*/
#define APIC_DIVISOR 16
void __setup_APIC_LVTT(unsigned int clocks)
#define APIC_DIVISOR 1
static void __setup_APIC_LVTT(unsigned int clocks)
{
unsigned int lvtt1_value, tmp_value;
lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) |
APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV)|LOCAL_TIMER_VECTOR;
apic_write_around(APIC_LVTT, lvtt1_value);
/*
* Divide PICLK by 16
*/
tmp_value = apic_read(APIC_TDCR);
apic_write_around(APIC_TDCR, (tmp_value
& ~(APIC_TDR_DIV_1 | APIC_TDR_DIV_TMBASE))
| APIC_TDR_DIV_16);
apic_write_around(APIC_TDCR, (tmp_value | APIC_TDR_DIV_1));
apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);
}
/*
* this is done for every CPU from setup_APIC_clocks() below.
* We setup each local APIC with a zero timeout value for now.
* Unlike Linux, we don't have to wait for slices etc.
*/
void setup_APIC_timer(void * data)
{
unsigned int clocks = (unsigned int) data, slice, t0, t1;
unsigned long flags;
int delta;
__save_flags(flags);
__sti();
/*
* ok, Intel has some smart code in their APIC that knows
* if a CPU was in 'hlt' lowpower mode, and this increases
* its APIC arbitration priority. To avoid the external timer
* IRQ APIC event being in synchron with the APIC clock we
* introduce an interrupt skew to spread out timer events.
*
* The number of slices within a 'big' timeslice is smp_num_cpus+1
*/
slice = clocks / (smp_num_cpus+1);
printk("cpu: %d, clocks: %d, slice: %d\n",
smp_processor_id(), clocks, slice);
/*
* Wait for IRQ0's slice:
*/
wait_8254_wraparound();
__setup_APIC_LVTT(clocks);
t0 = apic_read(APIC_TMICT)*APIC_DIVISOR;
/* Wait till TMCCT gets reloaded from TMICT... */
do {
t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR;
delta = (int)(t0 - t1 - slice*(smp_processor_id()+1));
} while (delta >= 0);
/* Now wait for our slice for real. */
do {
t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR;
delta = (int)(t0 - t1 - slice*(smp_processor_id()+1));
} while (delta < 0);
__setup_APIC_LVTT(clocks);
printk("CPU%d<T0:%d,T1:%d,D:%d,S:%d,C:%d>\n",
smp_processor_id(), t0, t1, delta, slice, clocks);
printk("cpu: %d: setup timer.", smp_processor_id());
__setup_APIC_LVTT(0);
printk("done\n");
__restore_flags(flags);
}
/*
* In this function we calibrate APIC bus clocks to the external timer.
*
* We want to do the calibration only once since we
* want to have local timer irqs syncron. CPUs connected
* by the same APIC bus have the very same bus frequency.
* And we want to have irqs off anyways, no accidental
* APIC irq that way.
* As a result we have the Bys Speed and CPU speed in Hz.
*
* We want to do the calibration only once (for CPU0). CPUs connected by the
* same APIC bus have the very same bus frequency.
*
* This bit is a bit shoddy since we use the very same periodic timer interrupt
* we try to eliminate to calibrate the APIC.
*/
int __init calibrate_APIC_clock(void)
@@ -619,95 +606,152 @@ int __init calibrate_APIC_clock(void)
int i;
const int LOOPS = HZ/10;
printk("calibrating APIC timer ...\n");
printk("calibrating APIC timer for CPU%d...\n", smp_processor_id());
/*
* Put whatever arbitrary (but long enough) timeout
/* Put whatever arbitrary (but long enough) timeout
* value into the APIC clock, we just want to get the
* counter running for calibration.
*/
* counter running for calibration. */
__setup_APIC_LVTT(1000000000);
/*
* The timer chip counts down to zero. Let's wait
/* The timer chip counts down to zero. Let's wait
* for a wraparound to start exact measurement:
* (the current tick might have been already half done)
*/
* (the current tick might have been already half done) */
wait_8254_wraparound();
/*
* We wrapped around just now. Let's start:
*/
/* We wrapped around just now. Let's start: */
rdtscll(t1);
tt1 = apic_read(APIC_TMCCT);
/*
* Let's wait LOOPS wraprounds:
*/
/* Let's wait LOOPS wraprounds: */
for (i = 0; i < LOOPS; i++)
wait_8254_wraparound();
tt2 = apic_read(APIC_TMCCT);
rdtscll(t2);
/*
* The APIC bus clock counter is 32 bits only, it
/* The APIC bus clock counter is 32 bits only, it
* might have overflown, but note that we use signed
* longs, thus no extra care needed.
*
* underflown to be exact, as the timer counts down ;)
*/
* underflown to be exact, as the timer counts down ;) */
result = (tt1-tt2)*APIC_DIVISOR/LOOPS;
printk("..... CPU clock speed is %ld.%04ld MHz.\n",
printk("..... CPU speed is %ld.%04ld MHz.\n",
((long)(t2-t1)/LOOPS)/(1000000/HZ),
((long)(t2-t1)/LOOPS)%(1000000/HZ));
printk("..... host bus clock speed is %ld.%04ld MHz.\n",
printk("..... Bus speed is %ld.%04ld MHz.\n",
result/(1000000/HZ),
result%(1000000/HZ));
/* set up multipliers for accurate timer code */
bus_freq = result*HZ;
bus_cycle = (u32) (1000000000000LL/bus_freq); /* in pico seconds */
bus_scale = (1000*262144)/bus_cycle;
/* print results */
printk("..... bus_freq = %u Hz\n", bus_freq);
printk("..... bus_cycle = %u ps\n", bus_cycle);
printk("..... bus_scale = %u \n", bus_scale);
/* reset APIC to zero timeout value */
__setup_APIC_LVTT(0);
return result;
}
static unsigned int calibration_result;
/*
* initialise the APIC timers for all CPUs
* we start with the first and find out processor frequency and bus speed
*/
void __init setup_APIC_clocks (void)
{
printk("Using local APIC timer interrupts.\n");
using_apic_timer = 1;
__cli();
calibration_result = calibrate_APIC_clock();
/*
* Now set up the timer for real.
*/
setup_APIC_timer((void *)calibration_result);
/* calibrate CPU0 for CPU speed and BUS speed */
bus_freq = calibrate_APIC_clock();
/* Now set up the timer for real. */
setup_APIC_timer((void *)bus_freq);
__sti();
/* and update all other cpus */
smp_call_function(setup_APIC_timer, (void *)calibration_result, 1, 1);
smp_call_function(setup_APIC_timer, (void *)bus_freq, 1, 1);
}
#undef APIC_DIVISOR
/*
* Local timer interrupt handler. It does both profiling and
* process statistics/rescheduling.
*
* We do profiling in every local tick, statistics/rescheduling
* happen only every 'profiling multiplier' ticks. The default
* multiplier is 1 and it can be changed by writing the new multiplier
* value into /proc/profile.
* reprogram the APIC timer. Timeoutvalue is in ns from start of boot
* returns 1 on success
* returns 0 if the timeout value is too small or in the past.
*/
int reprogram_ac_timer(s_time_t timeout)
{
int cpu = smp_processor_id();
s_time_t now;
s_time_t expire;
u64 apic_tmict;
now = NOW();
expire = timeout - now; /* value from now */
if (expire <= 0) {
printk("APICT[%02d] Timeout value in the past %lld > %lld\n",
cpu, now, timeout);
return 0; /* timeout value in the past */
}
/* conversion to bus units */
apic_tmict = (((u64)bus_scale) * expire)>>18;
if (apic_tmict >= 0xffffffff) {
printk("APICT[%02d] Timeout value too large\n", cpu);
apic_tmict = 0xffffffff;
}
if (apic_tmict == 0) {
printk("APICT[%02d] timeout value too small\n", cpu);
return 0;
}
/* programm timer */
apic_write(APIC_TMICT, (unsigned long)apic_tmict);
TRC(printk("APICT[%02d] reprog(): expire=%lld %u\n",
cpu, expire, apic_tmict));
return 1;
}
/*
* Local timer interrupt handler.
* here the programmable, accurate timers are executed.
* If we are on CPU0 and we should have updated jiffies, we do this
* as well and and deal with traditional linux timers. Note, that of
* the timer APIC on CPU does not go off every 10ms or so the linux
* timers loose accuracy, but that shouldn't be a problem.
*/
static s_time_t last_cpu0_tirq = 0;
inline void smp_local_timer_interrupt(struct pt_regs * regs)
{
update_process_times(user_mode(regs));
int cpu = smp_processor_id();
s_time_t diff, now;
/* if CPU 0 do old timer stuff */
if (cpu == 0) {
update_time();
now = NOW();
diff = now - last_cpu0_tirq;
/* this uses three 64bit divisions which should be avoided!! */
if (diff >= MILLISECS(10)) {
/* update jiffies */
(*(unsigned long *)&jiffies) += diff / MILLISECS(10);
/* do traditional linux timers */
do_timer(regs);
last_cpu0_tirq = now;
}
}
/* call timer function */
do_ac_timer();
}
/*
@@ -732,13 +776,11 @@ void smp_apic_timer_interrupt(struct pt_regs * regs)
/*
* NOTE! We'd better ACK the irq immediately,
* because timer handling can be slow.
* XXX is this save?
*/
ack_APIC_irq();
/*
* update_process_times() expects us to have done irq_enter().
* Besides, if we don't timer interrupts ignore the global
* interrupt lock, which is the WrongThing (tm) to do.
*/
/* call the local handler */
irq_enter(cpu, 0);
smp_local_timer_interrupt(regs);
irq_exit(cpu, 0);
@@ -809,7 +851,8 @@ int __init APIC_init_uniprocessor (void)
/*
* Complain if the BIOS pretends there is one.
*/
if (!cpu_has_apic && APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid])) {
if (!cpu_has_apic&&APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]))
{
printk("BIOS bug, local APIC #%d not detected!...\n",
boot_cpu_physical_apicid);
return -1;
+13 -13
View File
@@ -513,19 +513,19 @@ ENTRY(spurious_interrupt_bug)
.data
ENTRY(hypervisor_call_table)
.long SYMBOL_NAME(do_set_trap_table)
.long SYMBOL_NAME(do_process_page_updates)
.long SYMBOL_NAME(do_console_write)
.long SYMBOL_NAME(sys_ni_syscall)
.long SYMBOL_NAME(do_set_guest_stack)
.long SYMBOL_NAME(do_net_update)
.long SYMBOL_NAME(do_fpu_taskswitch)
.long SYMBOL_NAME(do_yield)
.long SYMBOL_NAME(kill_domain)
.long SYMBOL_NAME(do_dom0_op)
.long SYMBOL_NAME(do_network_op)
.long SYMBOL_NAME(do_set_debugreg)
.long SYMBOL_NAME(do_get_debugreg)
.long SYMBOL_NAME(do_set_trap_table) /* 0 */
.long SYMBOL_NAME(do_process_page_updates) /* 1 */
.long SYMBOL_NAME(do_console_write) /* 2 */
.long SYMBOL_NAME(sys_ni_syscall) /* 3 */
.long SYMBOL_NAME(do_set_guest_stack) /* 4 */
.long SYMBOL_NAME(do_net_update) /* 5 */
.long SYMBOL_NAME(do_fpu_taskswitch) /* 6 */
.long SYMBOL_NAME(do_sched_op) /* 7 */
.long SYMBOL_NAME(kill_domain) /* 10 */
.long SYMBOL_NAME(do_dom0_op) /* 11 */
.long SYMBOL_NAME(do_network_op) /* 12 */
.long SYMBOL_NAME(do_set_debugreg) /* 13 */
.long SYMBOL_NAME(do_get_debugreg) /* 14 */
.rept NR_syscalls-(.-hypervisor_call_table)/4
.long SYMBOL_NAME(sys_ni_syscall)
.endr
+5
View File
@@ -277,6 +277,7 @@ void __init start_of_day(void)
extern void tqueue_bh(void);
extern void immediate_bh(void);
extern void init_timervecs(void);
extern void ac_timer_init(void);
extern int setup_network_devices(void);
extern void net_init(void);
@@ -300,6 +301,7 @@ void __init start_of_day(void)
paging_init(); /* not much here now, but sets up fixmap */
if ( smp_found_config ) get_smp_config();
domain_init();
scheduler_init();
trap_init();
init_IRQ(); /* installs simple interrupt wrappers. Starts HZ clock. */
time_init(); /* installs software handler for HZ clock. */
@@ -320,6 +322,9 @@ void __init start_of_day(void)
* fall thru to 8259A if we have to (but slower).
*/
#endif
init_xeno_time(); /* initialise the time */
ac_timer_init(); /* init accurate timers */
schedulers_start(); /* start scheduler for each CPU */
sti();
+234 -2
View File
@@ -1,3 +1,22 @@
/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
****************************************************************************
* (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
****************************************************************************
*
* File: i386/time.c
* Author:
* Changes:
*
* Date: Jan 2003
*
* Environment: Xen Hypervisor
* Description: modified version of Linux' time.c
* implement system and wall clock time.
*
****************************************************************************
* $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
****************************************************************************
*/
/*
* linux/arch/i386/kernel/time.c
*
@@ -19,12 +38,22 @@
#include <asm/mpspec.h>
#include <asm/processor.h>
#include <asm/fixmap.h>
#include <asm/mc146818rtc.h>
#ifdef TIME_TRACE
#define TRC(_x) _x
#else
#define TRC(_x)
#endif
unsigned long cpu_khz; /* Detected as we calibrate the TSC */
unsigned long ticks_per_usec; /* TSC ticks per microsecond. */
spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
@@ -62,14 +91,15 @@ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
do_timer_interrupt(irq, NULL, regs);
}
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL};
static struct irqaction irq0 = { timer_interrupt, SA_INTERRUPT, 0,
"timer", NULL, NULL};
/* ------ Calibrate the TSC -------
* Return processor ticks per second / CALIBRATE_FRAC.
*/
#define CLOCK_TICK_RATE 1193180 /* system crystal frequency (Hz) */
#define CALIBRATE_FRAC 20 /* calibrate over 50ms */
#define CALIBRATE_FRAC 20 /* calibrate over 50ms */
#define CALIBRATE_LATCH ((CLOCK_TICK_RATE+(CALIBRATE_FRAC/2))/CALIBRATE_FRAC)
static unsigned long __init calibrate_tsc(void)
@@ -126,6 +156,207 @@ static unsigned long __init calibrate_tsc(void)
return 0;
}
/***************************************************************************
* CMOS Timer functions
***************************************************************************/
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
*
* [For the Julian calendar (which was used in Russia before 1917,
* Britain & colonies before 1752, anywhere else before 1582,
* and is still in use by some communities) leave out the
* -year/100+year/400 terms, and add 10.]
*
* This algorithm was first published by Gauss (I think).
*
* WARNING: this function will overflow on 2106-02-07 06:28:16 on
* machines were long is 32-bit! (However, as time_t is signed, we
* will already get problems at other places on 2038-01-19 03:14:08)
*/
static inline unsigned long
mktime (unsigned int year, unsigned int mon,
unsigned int day, unsigned int hour,
unsigned int min, unsigned int sec)
{
if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
mon += 12; /* Puts Feb last since it has leap day */
year -= 1;
}
return ((((unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day)+
year*365 - 719499
)*24 + hour /* now have hours */
)*60 + min /* now have minutes */
)*60 + sec; /* finally seconds */
}
static unsigned long get_cmos_time(void)
{
unsigned int year, mon, day, hour, min, sec;
int i;
spin_lock(&rtc_lock);
/* The Linux interpretation of the CMOS clock register contents:
* When the Update-In-Progress (UIP) flag goes from 1 to 0, the
* RTC registers show the second which has precisely just started.
* Let's hope other operating systems interpret the RTC the same way.
*/
/* read RTC exactly on falling edge of update flag */
for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
break;
for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms */
if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
break;
do { /* Isn't this overkill ? UIP above should guarantee consistency */
sec = CMOS_READ(RTC_SECONDS);
min = CMOS_READ(RTC_MINUTES);
hour = CMOS_READ(RTC_HOURS);
day = CMOS_READ(RTC_DAY_OF_MONTH);
mon = CMOS_READ(RTC_MONTH);
year = CMOS_READ(RTC_YEAR);
} while (sec != CMOS_READ(RTC_SECONDS));
if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
{
BCD_TO_BIN(sec);
BCD_TO_BIN(min);
BCD_TO_BIN(hour);
BCD_TO_BIN(day);
BCD_TO_BIN(mon);
BCD_TO_BIN(year);
}
spin_unlock(&rtc_lock);
if ((year += 1900) < 1970)
year += 100;
printk(".... CMOS Clock: %02d/%02d/%04d %02d:%02d:%02d\n",
day, mon, year, hour, min, sec);
return mktime(year, mon, day, hour, min, sec);
}
/***************************************************************************
* System time
***************************************************************************/
u32 stime_pcc; /* cycle counter value at last timer irq */
u32 stime_scale; /* scale factor for converting cc to ns */
s_time_t stime_now; /* time in ns at last timer IRQ */
/***************************************************************************
* Wall Clock time
***************************************************************************/
static rwlock_t wctime_lock = RW_LOCK_UNLOCKED;
struct timeval wall_clock_time; /* wall clock time at last update */
s_time_t wctime_st; /* system time at last update */
void do_gettimeofday(struct timeval *tv)
{
unsigned long flags;
unsigned long usec, sec;
read_lock_irqsave(&wctime_lock, flags);
usec = ((unsigned long)(NOW() - wctime_st))/1000;
sec = wall_clock_time.tv_sec;
usec += wall_clock_time.tv_usec;
read_unlock_irqrestore(&wctime_lock, flags);
while (usec >= 1000000) {
usec -= 1000000;
sec++;
}
tv->tv_sec = sec;
tv->tv_usec = usec;
}
void do_settimeofday(struct timeval *tv)
{
printk("XXX: do_settimeofday not implemented\n");
}
/***************************************************************************
* Update times
***************************************************************************/
/* update hypervisors notion of time */
void update_time(void) {
u32 new_pcc;
s_time_t new_st;
unsigned long usec;
/* update system time */
rdtscl(new_pcc);
stime_now = stime_now+((((s_time_t)stime_scale)*
(new_pcc-stime_pcc))>>10);
stime_pcc=new_pcc;
/* update wall clock time */
write_lock(&wctime_lock);
new_st = NOW();
usec = ((unsigned long)(new_st - wctime_st))/1000;
usec += wall_clock_time.tv_usec;
while (usec >= 1000000) {
usec -= 1000000;
wall_clock_time.tv_sec++;
}
wall_clock_time.tv_usec = usec;
wctime_st = new_st;
write_unlock(&wctime_lock);
TRC(printk("TIME[%02d] update time: stime_now=%lld now=%lld, wct=%ld:%ld\n"
cpu, stime_now, new_st, wall_clock_time.tv_sec,
wall_clock_time.tv_usec));
}
/* update a domains notion of time */
void update_dom_time(shared_info_t *si)
{
unsigned long flags;
read_lock_irqsave(&wctime_lock, flags);
si->system_time = stime_now;
si->st_timestamp = stime_pcc;
si->tv_sec = wall_clock_time.tv_sec;
si->tv_usec = wall_clock_time.tv_usec;
si->wc_timestamp = wctime_st;
read_unlock_irqrestore(&wctime_lock, flags);
}
/***************************************************************************
* Init Xeno Time
* This has to be done after all CPUs have been booted
***************************************************************************/
int __init init_xeno_time()
{
int cpu = smp_processor_id();
u32 cpu_cycle; /* time of one cpu cyle in pico-seconds */
/* System Time */
cpu_cycle = (u32) (1000000000LL/cpu_khz); /* in pico seconds */
stime_scale = (cpu_cycle * 1024) / 1000;
stime_now = (s_time_t)0;
rdtscl(stime_pcc);
printk("Init Time[%02d]:\n", cpu);
printk(".... System Time: %lldns\n", NOW());
printk(".... stime_scale: %u\n", stime_scale);
printk(".... stime_pcc: %u\n", stime_pcc);
/* Wall Clock time */
wall_clock_time.tv_sec = get_cmos_time();
wall_clock_time.tv_usec = 0;
wctime_st = NOW();
printk(".... Wall Clock: %lds %ldus\n", wall_clock_time.tv_sec,
wall_clock_time.tv_usec);
printk(".... wctime_st: %lld\n", wctime_st);
return 0;
}
/***************************************************************************
* Init
***************************************************************************/
void __init time_init(void)
{
unsigned long ticks_per_frac = calibrate_tsc();
@@ -136,6 +367,7 @@ void __init time_init(void)
ticks_per_usec = ticks_per_frac / (1000000/CALIBRATE_FRAC);
cpu_khz = ticks_per_frac / (1000/CALIBRATE_FRAC);
printk("Detected %lu.%03lu MHz processor.\n",
cpu_khz / 1000, cpu_khz % 1000);
+304
View File
@@ -0,0 +1,304 @@
/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
****************************************************************************
* (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
****************************************************************************
*
* File: ac_timer.c
* Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
* Changes:
*
* Date: Nov 2002
*
* Environment: Xen Hypervisor
* Description: Accurate timer for the Hypervisor
*
****************************************************************************
* $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
****************************************************************************
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/types.h>
#include <xeno/errno.h>
#include <xeno/sched.h>
#include <xeno/lib.h>
#include <xeno/config.h>
#include <xeno/smp.h>
#include <xeno/init.h>
#include <xeno/time.h>
#include <xeno/ac_timer.h>
#include <asm/system.h>
#include <asm/desc.h>
#undef AC_TIMER_TRACE
#undef AC_TIMER_STATS
#ifdef AC_TIMER_TRACE
#define TRC(_x) _x
#else
#define TRC(_x)
#endif
/* A timer list per CPU */
typedef struct ac_timers_st
{
spinlock_t lock;
struct list_head timers;
struct ac_timer *prev, *curr;
} __cacheline_aligned ac_timers_t;
static ac_timers_t ac_timers[NR_CPUS];
#ifdef AC_TIMER_STATS
#define BUCKETS 1000
#define MAX_STATS
typedef struct act_stats_st
{
u32 count;
u32 times[2*(BUCKETS)];
} __cacheline_aligned act_stats_t;
static act_stats_t act_stats[NR_CPUS];
#endif
/* local prototypes */
static int detach_ac_timer(struct ac_timer *timer);
static void ac_timer_debug(unsigned long);
/*
* add a timer.
* return value:
* 0: success
* 1: failure, timer in the past or timeout value to small
* -1: failure, timer uninitialised
* fail
*/
int add_ac_timer(struct ac_timer *timer)
{
int cpu = smp_processor_id();
unsigned long flags;
struct list_head *tmp, *prev;
struct ac_timer *t;
s_time_t now;
/* sanity checks */
/* make sure timeout value is in the future */
now = NOW();
TRC(printk("ACT [%02d] add(): now=%lld timo=%lld\n",
cpu, now, timer->expires));
if (timer->expires <= now) {
printk("ACT[%02d] add_ac_timer(): timeout value in the past\n", cpu);
return 1;
}
local_irq_save(flags);
/* check if timer would be inserted at start of list */
if ((list_empty(&ac_timers[cpu].timers)) ||
(timer->expires <
(list_entry(&ac_timers[cpu].timers, struct ac_timer, timer_list))->expires)) {
TRC(printk("ACT [%02d] add(): add at head\n", cpu));
/* Reprogramm and add to head of list */
if (!reprogram_ac_timer(timer->expires)) {
/* failed */
TRC(printk("ACT [%02d] add(): add at head failed\n", cpu));
local_irq_restore(flags);
return 1;
}
list_add(&timer->timer_list, &ac_timers[cpu].timers);
} else {
/* find correct entry and add timer */
prev = &ac_timers[cpu].timers;
list_for_each(tmp, &ac_timers[cpu].timers) {
t = list_entry(tmp, struct ac_timer, timer_list);
if (t->expires < timer->expires) {
list_add(&timer->timer_list, prev);
TRC(printk("ACT [%02d] add(): added between %lld and %lld\n",
cpu,
list_entry(prev,struct ac_timer,timer_list)->expires,
list_entry(tmp,struct ac_timer,timer_list)->expires));
break;
}
prev = tmp;
}
}
local_irq_restore(flags);
return 0;
}
/*
* remove a timer
* return values:
* 0: success
* -1: bogus timer
*/
static int detach_ac_timer(struct ac_timer *timer)
{
TRC(int cpu = smp_processor_id());
TRC(printk("ACT [%02d] detach(): \n", cpu));
list_del(&timer->timer_list);
timer->timer_list.next = NULL;
return 0;
}
/*
* remove a timer
* return values:
* 0: success
* -1: bogus timer
*/
int rem_ac_timer(struct ac_timer *timer)
{
int res;
unsigned long flags;
TRC(int cpu = smp_processor_id());
TRC(printk("ACT [%02d] remove(): timo=%lld \n", cpu, timer->expires));
/* sanity checks */
local_irq_save(flags);
res = detach_ac_timer(timer);
local_irq_restore(flags);
return res;
}
/*
* modify a timer, i.e., set a new timeout value
* return value:
* 0: sucess
* -1: error
*/
int mod_ac_timer(struct ac_timer *timer, s_time_t new_time)
{
if (rem_ac_timer(timer) != 0)
return -1;
timer->expires = new_time;
if (add_ac_timer(timer) != 0)
return -1;
return 0;
}
/*
* do_ac_timer
* deal with timeouts and run the handlers
*/
void do_ac_timer(void)
{
int cpu = smp_processor_id();
unsigned long flags;
s_time_t now;
struct ac_timer *t;
struct list_head *tmp;
local_irq_save(flags);
do_timer_again:
now = NOW();
TRC(printk("ACT [%02d] do(): now=%lld\n", cpu, now));
/* Sanity checks */
/* empty time list */
if (list_empty(&ac_timers[cpu].timers)) {
printk("ACT[%02d] do_ac_timer(): timer irq without timer\n", cpu);
local_irq_restore(flags);
return;
}
/* execute the head of timer queue */
t = list_entry(ac_timers[cpu].timers.next, struct ac_timer, timer_list);
detach_ac_timer(t);
#ifdef AC_TIMER_STATS
{
s32 diff;
u32 i;
diff = ((s32)(now - t->expires)) / 1000; /* delta in us */
if (diff < -BUCKETS)
diff = -BUCKETS;
else if (diff > BUCKETS)
diff = BUCKETS;
act_stats[cpu].times[diff+BUCKETS]++;
act_stats[cpu].count++;
if (act_stats[cpu].count >= 5000) {
printk("ACT Stats\n");
for (i=0; i < 2*BUCKETS; i++) {
if (act_stats[cpu].times[i] != 0)
printk("ACT [%02d]: %3dus: %5d\n",
cpu,i-BUCKETS, act_stats[cpu].times[i]);
act_stats[cpu].times[i]=0;
}
act_stats[cpu].count = 0;
printk("\n");
}
}
#endif
if (t->expires > now) {
//printk("ACT [%02d] do(): irq too early (%lld ns)\n",
// cpu, now - t->expires );
}
if (t->function != NULL)
t->function(t->data);
/* check if there are other timer functions on the list */
now = NOW();
if (!list_empty(&ac_timers[cpu].timers)) {
list_for_each(tmp, &ac_timers[cpu].timers) {
t = list_entry(tmp, struct ac_timer, timer_list);
TRC(printk("ACT [%02d] do(): now=%lld timo=%lld\n",
cpu, now, t->expires));
if (t->expires <= now) {
detach_ac_timer(t);
if (t->function != NULL)
t->function(t->data);
now = NOW();
} else {
TRC(printk("ACT [%02d] do(): break1\n", cpu));
break;
}
}
}
/* If list not empty reprogramm timer to new head of list */
if (!list_empty(&ac_timers[cpu].timers)) {
t = list_entry(ac_timers[cpu].timers.next,struct ac_timer,timer_list);
if (t->expires > 0) {
TRC(printk("ACT [%02d] do(): reprog timo=%lld\n",cpu,t->expires));
if (!reprogram_ac_timer(t->expires)) {
TRC(printk("ACT [%02d] do(): again\n", cpu));
goto do_timer_again;
}
}
}
local_irq_restore(flags);
}
/*
* init
*/
void __init ac_timer_init(void)
{
int i;
printk ("ACT: Initialising Accurate timers\n");
for (i = 0; i < NR_CPUS; i++)
{
INIT_LIST_HEAD(&ac_timers[i].timers);
spin_lock_init(&ac_timers[i].lock);
}
/* ac_timer_debug(0); */
}
+11 -194
View File
@@ -14,24 +14,9 @@
rwlock_t tasklist_lock __cacheline_aligned = RW_LOCK_UNLOCKED;
schedule_data_t schedule_data[NR_CPUS];
int wake_up(struct task_struct *p)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&schedule_data[p->processor].lock, flags);
if ( __task_on_runqueue(p) ) goto out;
p->state = TASK_RUNNING;
__add_to_runqueue(p);
ret = 1;
out:
spin_unlock_irqrestore(&schedule_data[p->processor].lock, flags);
return ret;
}
/*
* create a new domain
*/
struct task_struct *do_newdomain(void)
{
int retval;
@@ -66,97 +51,6 @@ struct task_struct *do_newdomain(void)
return(p);
}
void reschedule(struct task_struct *p)
{
int cpu = p->processor;
struct task_struct *curr;
unsigned long flags;
if ( p->has_cpu ) return;
spin_lock_irqsave(&schedule_data[cpu].lock, flags);
curr = schedule_data[cpu].curr;
if ( is_idle_task(curr) )
{
set_bit(_HYP_EVENT_NEED_RESCHED, &curr->hyp_events);
spin_unlock_irqrestore(&schedule_data[cpu].lock, flags);
#ifdef CONFIG_SMP
if ( cpu != smp_processor_id() ) smp_send_event_check_cpu(cpu);
#endif
}
else
{
spin_unlock_irqrestore(&schedule_data[cpu].lock, flags);
}
}
static void process_timeout(unsigned long __data)
{
struct task_struct * p = (struct task_struct *) __data;
wake_up(p);
}
long schedule_timeout(long timeout)
{
struct timer_list timer;
unsigned long expire;
switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
/*
* These two special cases are useful to be comfortable in the caller.
* Nothing more. We could take MAX_SCHEDULE_TIMEOUT from one of the
* negative value but I' d like to return a valid offset (>=0) to allow
* the caller to do everything it want with the retval.
*/
schedule();
goto out;
default:
/*
* Another bit of PARANOID. Note that the retval will be 0 since no
* piece of kernel is supposed to do a check for a negative retval of
* schedule_timeout() (since it should never happens anyway). You just
* have the printk() that will tell you if something is gone wrong and
* where.
*/
if (timeout < 0)
{
printk(KERN_ERR "schedule_timeout: wrong timeout "
"value %lx from %p\n", timeout,
__builtin_return_address(0));
current->state = TASK_RUNNING;
goto out;
}
}
expire = timeout + jiffies;
init_timer(&timer);
timer.expires = expire;
timer.data = (unsigned long) current;
timer.function = process_timeout;
add_timer(&timer);
schedule();
del_timer_sync(&timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
long do_yield(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return 0;
}
/* Get a pointer to the specified domain. Consider replacing this
* with a hash lookup later.
*
@@ -231,7 +125,7 @@ long kill_other_domain(unsigned int dom)
/* Release resources belonging to task @p. */
void release_task(struct task_struct *p)
{
ASSERT(!__task_on_runqueue(p));
//ASSERT(!__task_on_runqueue(p));
ASSERT(p->state == TASK_DYING);
ASSERT(!p->has_cpu);
write_lock_irq(&tasklist_lock);
@@ -252,77 +146,6 @@ void release_task(struct task_struct *p)
}
asmlinkage void schedule(void)
{
struct task_struct *prev, *next;
struct list_head *tmp;
int this_cpu;
need_resched_back:
prev = current;
this_cpu = prev->processor;
spin_lock_irq(&schedule_data[this_cpu].lock);
ASSERT(!in_interrupt());
ASSERT(__task_on_runqueue(prev));
if ( !prev->counter )
{
prev->counter = 2;
__move_last_runqueue(prev);
}
switch ( prev->state )
{
case TASK_INTERRUPTIBLE:
if ( signal_pending(prev) )
{
prev->state = TASK_RUNNING;
break;
}
default:
__del_from_runqueue(prev);
case TASK_RUNNING:;
}
clear_bit(_HYP_EVENT_NEED_RESCHED, &prev->hyp_events);
/* Round-robin, skipping idle where possible. */
next = NULL;
list_for_each(tmp, &schedule_data[smp_processor_id()].runqueue) {
next = list_entry(tmp, struct task_struct, run_list);
if ( next->domain != IDLE_DOMAIN_ID ) break;
}
prev->has_cpu = 0;
next->has_cpu = 1;
schedule_data[this_cpu].prev = prev;
schedule_data[this_cpu].curr = next;
spin_unlock_irq(&schedule_data[this_cpu].lock);
if ( unlikely(prev == next) )
{
/* We won't go through the normal tail, so do this by hand */
prev->policy &= ~SCHED_YIELD;
goto same_process;
}
prepare_to_switch();
switch_to(prev, next);
prev = schedule_data[this_cpu].prev;
prev->policy &= ~SCHED_YIELD;
if ( prev->state == TASK_DYING ) release_task(prev);
same_process:
if ( test_bit(_HYP_EVENT_NEED_RESCHED, &current->hyp_events) )
goto need_resched_back;
return;
}
static unsigned int alloc_new_dom_mem(struct task_struct *p, unsigned int kbytes)
{
struct list_head *temp;
@@ -404,7 +227,6 @@ int setup_guestos(struct task_struct *p, dom0_newdomain_t *params)
unsigned long ft_mapping = (unsigned long)frame_table;
unsigned int ft_size = 0;
start_info_t *virt_startinfo_address;
unsigned long long time;
l2_pgentry_t *l2tab;
l1_pgentry_t *l1tab = NULL;
struct pfn_info *page = NULL;
@@ -543,11 +365,12 @@ int setup_guestos(struct task_struct *p, dom0_newdomain_t *params)
l1tab = map_domain_mem(phys_l1tab);
*l1tab = mk_l1_pgentry(__pa(p->shared_info)|L1_PROT);
/* Set up shared info area. */
rdtscll(time);
p->shared_info->wall_time = time;
p->shared_info->domain_time = time;
/*
* Set up time
*/
update_dom_time(p->shared_info);
p->shared_info->ticks_per_ms = ticks_per_usec * 1000;
p->shared_info->domain_time = 0;
/* for DOM0, setup mapping of frame table */
if ( dom == 0 )
@@ -666,14 +489,8 @@ int setup_guestos(struct task_struct *p, dom0_newdomain_t *params)
void __init domain_init(void)
{
int i;
for ( i = 0; i < NR_CPUS; i++ )
{
INIT_LIST_HEAD(&schedule_data[i].runqueue);
spin_lock_init(&schedule_data[i].lock);
schedule_data[i].prev = &idle0_task;
schedule_data[i].curr = &idle0_task;
}
printk("Initialising domains\n");
// scheduler_init();
}
+355
View File
@@ -169,3 +169,358 @@ unsigned char *quad_to_str(unsigned long q, unsigned char *s)
}
/* a couple of 64 bit operations ported from freebsd */
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. All rights reserved.
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
* contributed to Berkeley.
*
* 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.
*
* $FreeBSD: src/sys/libkern/divdi3.c,v 1.6 1999/08/28 00:46:31 peter Exp $
*/
#include <asm/types.h>
/*
* Depending on the desired operation, we view a `long long' (aka quad_t) in
* one or more of the following formats.
*/
union uu {
s64 q; /* as a (signed) quad */
s64 uq; /* as an unsigned quad */
long sl[2]; /* as two signed longs */
unsigned long ul[2]; /* as two unsigned longs */
};
/* XXX RN: Yuck hardcoded endianess :) */
#define _QUAD_HIGHWORD 1
#define _QUAD_LOWWORD 0
/*
* Define high and low longwords.
*/
#define H _QUAD_HIGHWORD
#define L _QUAD_LOWWORD
/*
* Total number of bits in a quad_t and in the pieces that make it up.
* These are used for shifting, and also below for halfword extraction
* and assembly.
*/
#define CHAR_BIT 8 /* number of bits in a char */
#define QUAD_BITS (sizeof(s64) * CHAR_BIT)
#define LONG_BITS (sizeof(long) * CHAR_BIT)
#define HALF_BITS (sizeof(long) * CHAR_BIT / 2)
/*
* Extract high and low shortwords from longword, and move low shortword of
* longword to upper half of long, i.e., produce the upper longword of
* ((quad_t)(x) << (number_of_bits_in_long/2)). (`x' must actually be u_long.)
*
* These are used in the multiply code, to split a longword into upper
* and lower halves, and to reassemble a product as a quad_t, shifted left
* (sizeof(long)*CHAR_BIT/2).
*/
#define HHALF(x) ((x) >> HALF_BITS)
#define LHALF(x) ((x) & ((1 << HALF_BITS) - 1))
#define LHUP(x) ((x) << HALF_BITS)
/*
* Multiprecision divide. This algorithm is from Knuth vol. 2 (2nd ed),
* section 4.3.1, pp. 257--259.
*/
#define B (1 << HALF_BITS) /* digit base */
/* Combine two `digits' to make a single two-digit number. */
#define COMBINE(a, b) (((u_long)(a) << HALF_BITS) | (b))
/* select a type for digits in base B: use unsigned short if they fit */
#if ULONG_MAX == 0xffffffff && USHRT_MAX >= 0xffff
typedef unsigned short digit;
#else
typedef u_long digit;
#endif
/*
* Shift p[0]..p[len] left `sh' bits, ignoring any bits that
* `fall out' the left (there never will be any such anyway).
* We may assume len >= 0. NOTE THAT THIS WRITES len+1 DIGITS.
*/
static void
shl(register digit *p, register int len, register int sh)
{
register int i;
for (i = 0; i < len; i++)
p[i] = LHALF(p[i] << sh) | (p[i + 1] >> (HALF_BITS - sh));
p[i] = LHALF(p[i] << sh);
}
/*
* __qdivrem(u, v, rem) returns u/v and, optionally, sets *rem to u%v.
*
* We do this in base 2-sup-HALF_BITS, so that all intermediate products
* fit within u_long. As a consequence, the maximum length dividend and
* divisor are 4 `digits' in this base (they are shorter if they have
* leading zeros).
*/
u64
__qdivrem(uq, vq, arq)
u64 uq, vq, *arq;
{
union uu tmp;
digit *u, *v, *q;
register digit v1, v2;
u_long qhat, rhat, t;
int m, n, d, j, i;
digit uspace[5], vspace[5], qspace[5];
/*
* Take care of special cases: divide by zero, and u < v.
*/
if (vq == 0) {
/* divide by zero. */
static volatile const unsigned int zero = 0;
tmp.ul[H] = tmp.ul[L] = 1 / zero;
if (arq)
*arq = uq;
return (tmp.q);
}
if (uq < vq) {
if (arq)
*arq = uq;
return (0);
}
u = &uspace[0];
v = &vspace[0];
q = &qspace[0];
/*
* Break dividend and divisor into digits in base B, then
* count leading zeros to determine m and n. When done, we
* will have:
* u = (u[1]u[2]...u[m+n]) sub B
* v = (v[1]v[2]...v[n]) sub B
* v[1] != 0
* 1 < n <= 4 (if n = 1, we use a different division algorithm)
* m >= 0 (otherwise u < v, which we already checked)
* m + n = 4
* and thus
* m = 4 - n <= 2
*/
tmp.uq = uq;
u[0] = 0;
u[1] = HHALF(tmp.ul[H]);
u[2] = LHALF(tmp.ul[H]);
u[3] = HHALF(tmp.ul[L]);
u[4] = LHALF(tmp.ul[L]);
tmp.uq = vq;
v[1] = HHALF(tmp.ul[H]);
v[2] = LHALF(tmp.ul[H]);
v[3] = HHALF(tmp.ul[L]);
v[4] = LHALF(tmp.ul[L]);
for (n = 4; v[1] == 0; v++) {
if (--n == 1) {
u_long rbj; /* r*B+u[j] (not root boy jim) */
digit q1, q2, q3, q4;
/*
* Change of plan, per exercise 16.
* r = 0;
* for j = 1..4:
* q[j] = floor((r*B + u[j]) / v),
* r = (r*B + u[j]) % v;
* We unroll this completely here.
*/
t = v[2]; /* nonzero, by definition */
q1 = u[1] / t;
rbj = COMBINE(u[1] % t, u[2]);
q2 = rbj / t;
rbj = COMBINE(rbj % t, u[3]);
q3 = rbj / t;
rbj = COMBINE(rbj % t, u[4]);
q4 = rbj / t;
if (arq)
*arq = rbj % t;
tmp.ul[H] = COMBINE(q1, q2);
tmp.ul[L] = COMBINE(q3, q4);
return (tmp.q);
}
}
/*
* By adjusting q once we determine m, we can guarantee that
* there is a complete four-digit quotient at &qspace[1] when
* we finally stop.
*/
for (m = 4 - n; u[1] == 0; u++)
m--;
for (i = 4 - m; --i >= 0;)
q[i] = 0;
q += 4 - m;
/*
* Here we run Program D, translated from MIX to C and acquiring
* a few minor changes.
*
* D1: choose multiplier 1 << d to ensure v[1] >= B/2.
*/
d = 0;
for (t = v[1]; t < B / 2; t <<= 1)
d++;
if (d > 0) {
shl(&u[0], m + n, d); /* u <<= d */
shl(&v[1], n - 1, d); /* v <<= d */
}
/*
* D2: j = 0.
*/
j = 0;
v1 = v[1]; /* for D3 -- note that v[1..n] are constant */
v2 = v[2]; /* for D3 */
do {
register digit uj0, uj1, uj2;
/*
* D3: Calculate qhat (\^q, in TeX notation).
* Let qhat = min((u[j]*B + u[j+1])/v[1], B-1), and
* let rhat = (u[j]*B + u[j+1]) mod v[1].
* While rhat < B and v[2]*qhat > rhat*B+u[j+2],
* decrement qhat and increase rhat correspondingly.
* Note that if rhat >= B, v[2]*qhat < rhat*B.
*/
uj0 = u[j + 0]; /* for D3 only -- note that u[j+...] change */
uj1 = u[j + 1]; /* for D3 only */
uj2 = u[j + 2]; /* for D3 only */
if (uj0 == v1) {
qhat = B;
rhat = uj1;
goto qhat_too_big;
} else {
u_long nn = COMBINE(uj0, uj1);
qhat = nn / v1;
rhat = nn % v1;
}
while (v2 * qhat > COMBINE(rhat, uj2)) {
qhat_too_big:
qhat--;
if ((rhat += v1) >= B)
break;
}
/*
* D4: Multiply and subtract.
* The variable `t' holds any borrows across the loop.
* We split this up so that we do not require v[0] = 0,
* and to eliminate a final special case.
*/
for (t = 0, i = n; i > 0; i--) {
t = u[i + j] - v[i] * qhat - t;
u[i + j] = LHALF(t);
t = (B - HHALF(t)) & (B - 1);
}
t = u[j] - t;
u[j] = LHALF(t);
/*
* D5: test remainder.
* There is a borrow if and only if HHALF(t) is nonzero;
* in that (rare) case, qhat was too large (by exactly 1).
* Fix it by adding v[1..n] to u[j..j+n].
*/
if (HHALF(t)) {
qhat--;
for (t = 0, i = n; i > 0; i--) { /* D6: add back. */
t += u[i + j] + v[i];
u[i + j] = LHALF(t);
t = HHALF(t);
}
u[j] = LHALF(u[j] + t);
}
q[j] = qhat;
} while (++j <= m); /* D7: loop on j. */
/*
* If caller wants the remainder, we have to calculate it as
* u[m..m+n] >> d (this is at most n digits and thus fits in
* u[m+1..m+n], but we may need more source digits).
*/
if (arq) {
if (d) {
for (i = m + n; i > m; --i)
u[i] = (u[i] >> d) |
LHALF(u[i - 1] << (HALF_BITS - d));
u[i] = 0;
}
tmp.ul[H] = COMBINE(uspace[1], uspace[2]);
tmp.ul[L] = COMBINE(uspace[3], uspace[4]);
*arq = tmp.q;
}
tmp.ul[H] = COMBINE(qspace[1], qspace[2]);
tmp.ul[L] = COMBINE(qspace[3], qspace[4]);
return (tmp.q);
}
/*
* Divide two signed quads.
* ??? if -1/2 should produce -1 on this machine, this code is wrong
*/
s64
__divdi3(s64 a, s64 b)
{
u64 ua, ub, uq;
int neg;
if (a < 0)
ua = -(u64)a, neg = 1;
else
ua = a, neg = 0;
if (b < 0)
ub = -(u64)b, neg ^= 1;
else
ub = b;
uq = __qdivrem(ua, ub, (u64 *)0);
return (neg ? -uq : uq);
}
/*
* Divide two unsigned quads.
*/
u64
__udivdi3(a, b)
u64 a, b;
{
return (__qdivrem(a, b, (u64 *)0));
}
+345
View File
@@ -0,0 +1,345 @@
/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
****************************************************************************
* (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
****************************************************************************
*
* File: schedule.c
* Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
* Changes:
*
* Date: Nov 2002
*
* Environment: Xen Hypervisor
* Description: CPU scheduling
* partially moved from domain.c
*
****************************************************************************
* $Id: c-insert.c,v 1.7 2002/11/08 16:04:34 rn Exp $
****************************************************************************
*/
#include <xeno/config.h>
#include <xeno/init.h>
#include <xeno/lib.h>
#include <xeno/sched.h>
#include <xeno/delay.h>
#include <xeno/event.h>
#include <xeno/time.h>
#include <xeno/ac_timer.h>
#undef SCHEDULER_TRACE
#ifdef SCHEDULER_TRACE
#define TRC(_x) _x
#else
#define TRC(_x)
#endif
/*
* per CPU data for the scheduler.
*/
typedef struct schedule_data_st
{
spinlock_t lock;
struct list_head runqueue;
struct task_struct *prev, *curr;
} __cacheline_aligned schedule_data_t;
schedule_data_t schedule_data[NR_CPUS];
static __cacheline_aligned struct ac_timer s_timer[NR_CPUS];
/*
* Some convenience functions
*/
static inline void __add_to_runqueue(struct task_struct * p)
{
list_add(&p->run_list, &schedule_data[p->processor].runqueue);
}
static inline void __move_last_runqueue(struct task_struct * p)
{
list_del(&p->run_list);
list_add_tail(&p->run_list, &schedule_data[p->processor].runqueue);
}
static inline void __move_first_runqueue(struct task_struct * p)
{
list_del(&p->run_list);
list_add(&p->run_list, &schedule_data[p->processor].runqueue);
}
static inline void __del_from_runqueue(struct task_struct * p)
{
list_del(&p->run_list);
p->run_list.next = NULL;
}
static inline int __task_on_runqueue(struct task_struct *p)
{
return (p->run_list.next != NULL);
}
/*
* Add a new domain to the scheduler
*/
void sched_add_domain(struct task_struct *p)
{
p->state = TASK_UNINTERRUPTIBLE;
}
/*
* Remove domain to the scheduler
*/
void sched_rem_domain(struct task_struct *p)
{
p->state = TASK_DYING;
}
/*
* wake up a domain which had been sleeping
*/
int wake_up(struct task_struct *p)
{
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&schedule_data[p->processor].lock, flags);
if ( __task_on_runqueue(p) ) goto out;
p->state = TASK_RUNNING;
__add_to_runqueue(p);
ret = 1;
out:
spin_unlock_irqrestore(&schedule_data[p->processor].lock, flags);
return ret;
}
static void process_timeout(unsigned long __data)
{
struct task_struct * p = (struct task_struct *) __data;
wake_up(p);
}
long schedule_timeout(long timeout)
{
struct timer_list timer;
unsigned long expire;
switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
/*
* These two special cases are useful to be comfortable in the caller.
* Nothing more. We could take MAX_SCHEDULE_TIMEOUT from one of the
* negative value but I' d like to return a valid offset (>=0) to allow
* the caller to do everything it want with the retval.
*/
schedule();
goto out;
default:
/*
* Another bit of PARANOID. Note that the retval will be 0 since no
* piece of kernel is supposed to do a check for a negative retval of
* schedule_timeout() (since it should never happens anyway). You just
* have the printk() that will tell you if something is gone wrong and
* where.
*/
if (timeout < 0)
{
printk(KERN_ERR "schedule_timeout: wrong timeout "
"value %lx from %p\n", timeout,
__builtin_return_address(0));
current->state = TASK_RUNNING;
goto out;
}
}
expire = timeout + jiffies;
init_timer(&timer);
timer.expires = expire;
timer.data = (unsigned long) current;
timer.function = process_timeout;
add_timer(&timer);
schedule();
del_timer_sync(&timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
/* RN: XXX turn this into do_halt() */
/*
* yield the current process
*/
long do_sched_op(void)
{
current->state = TASK_INTERRUPTIBLE;
schedule();
return 0;
}
/*
*
*/
void reschedule(struct task_struct *p)
{
int cpu = p->processor;
struct task_struct *curr;
unsigned long flags;
if (p->has_cpu)
return;
spin_lock_irqsave(&schedule_data[cpu].lock, flags);
curr = schedule_data[cpu].curr;
if (is_idle_task(curr)) {
set_bit(_HYP_EVENT_NEED_RESCHED, &curr->hyp_events);
spin_unlock_irqrestore(&schedule_data[cpu].lock, flags);
#ifdef CONFIG_SMP
if (cpu != smp_processor_id())
smp_send_event_check_cpu(cpu);
#endif
} else {
spin_unlock_irqrestore(&schedule_data[cpu].lock, flags);
}
}
/*
* Pick the next domain to run
*/
asmlinkage void schedule(void)
{
struct task_struct *prev, *next, *p;
struct list_head *tmp;
int this_cpu;
need_resched_back:
prev = current;
this_cpu = prev->processor;
spin_lock_irq(&schedule_data[this_cpu].lock);
//ASSERT(!in_interrupt());
ASSERT(__task_on_runqueue(prev));
__move_last_runqueue(prev);
switch ( prev->state )
{
case TASK_INTERRUPTIBLE:
if ( signal_pending(prev) )
{
prev->state = TASK_RUNNING;
break;
}
default:
__del_from_runqueue(prev);
case TASK_RUNNING:;
}
clear_bit(_HYP_EVENT_NEED_RESCHED, &prev->hyp_events);
next = NULL;
list_for_each(tmp, &schedule_data[smp_processor_id()].runqueue) {
p = list_entry(tmp, struct task_struct, run_list);
next = p;
break;
}
prev->has_cpu = 0;
next->has_cpu = 1;
schedule_data[this_cpu].prev = prev;
schedule_data[this_cpu].curr = next;
spin_unlock_irq(&schedule_data[this_cpu].lock);
if ( unlikely(prev == next) )
{
/* We won't go through the normal tail, so do this by hand */
prev->policy &= ~SCHED_YIELD;
goto same_process;
}
prepare_to_switch();
switch_to(prev, next);
prev = schedule_data[this_cpu].prev;
prev->policy &= ~SCHED_YIELD;
if ( prev->state == TASK_DYING ) release_task(prev);
same_process:
update_dom_time(next->shared_info);
if ( test_bit(_HYP_EVENT_NEED_RESCHED, &current->hyp_events) )
goto need_resched_back;
return;
}
static __cacheline_aligned int count[NR_CPUS];
static void sched_timer(unsigned long foo)
{
int cpu = smp_processor_id();
struct task_struct *curr = schedule_data[cpu].curr;
s_time_t now;
int res;
if (count[cpu] >= 5) {
set_bit(_HYP_EVENT_NEED_RESCHED, &curr->hyp_events);
count[cpu] = 0;
}
count[cpu]++;
again:
now = NOW();
s_timer[cpu].expires = now + MILLISECS(10);
TRC(printk("SCHED[%02d] timer(): now=0x%08X%08X timo=0x%08X%08X\n",
cpu, (u32)(now>>32), (u32)now,
(u32)(s_timer[cpu].expires>>32), (u32)s_timer[cpu].expires));
res=add_ac_timer(&s_timer[cpu]);
if (res==1) {
goto again;
}
}
/*
* Initialise the data structures
*/
void __init scheduler_init(void)
{
int i;
printk("Initialising schedulers\n");
for ( i = 0; i < NR_CPUS; i++ )
{
INIT_LIST_HEAD(&schedule_data[i].runqueue);
spin_lock_init(&schedule_data[i].lock);
schedule_data[i].prev = &idle0_task;
schedule_data[i].curr = &idle0_task;
/* a timer for each CPU */
init_ac_timer(&s_timer[i]);
s_timer[i].function = &sched_timer;
}
}
/*
* Start a scheduler for each CPU
* This has to be done *after* the timers, e.g., APICs, have been initialised
*/
void schedulers_start(void) {
printk("Start schedulers\n");
__cli();
sched_timer(0);
smp_call_function(sched_timer, (void*)0, 1, 1);
__sti();
}
-1
View File
@@ -602,7 +602,6 @@ void do_timer(struct pt_regs *regs)
p = &idle0_task;
do {
s = p->shared_info;
s->wall_time = s->domain_time = wall;
cpu_mask |= mark_guest_event(p, _EVENT_TIMER);
}
while ( (p = p->next_task) != &idle0_task );
-3
View File
@@ -55,17 +55,14 @@ static inline void ack_APIC_irq(void)
}
extern int get_maxlvt(void);
extern void clear_local_APIC(void);
extern void connect_bsp_APIC (void);
extern void disconnect_bsp_APIC (void);
extern void disable_local_APIC (void);
extern int verify_local_APIC (void);
extern void cache_APIC_registers (void);
extern void sync_Arb_IDs (void);
extern void init_bsp_APIC (void);
extern void setup_local_APIC (void);
extern void init_apic_mappings (void);
extern void smp_local_timer_interrupt (struct pt_regs * regs);
extern void setup_APIC_clocks (void);
extern int APIC_init_uniprocessor (void);
+80
View File
@@ -0,0 +1,80 @@
/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
****************************************************************************
* (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
****************************************************************************
*
* File: time.h
* Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
* Changes:
*
* Date: Nov 2002
*
* Environment: Xen Hypervisor
* Description: Architecture dependent definition of time variables
*
****************************************************************************
* $Id: h-insert.h,v 1.4 2002/11/08 16:03:55 rn Exp $
****************************************************************************
*/
#ifndef _ASM_TIME_H_
#define _ASM_TIME_H_
#include <asm/types.h>
#include <asm/msr.h>
/*
* Cycle Counter Time
*/
typedef u64 cc_time_t;
static inline cc_time_t get_cc_time()
{
u64 ret;
rdtscll(ret);
return ret;
}
/*
* System Time
*/
typedef s64 s_time_t; /* System time */
extern u32 stime_pcc; /* cycle counter value at last timer irq */
extern s_time_t stime_now; /* time in ns at last timer IRQ */
extern u32 stime_scale; /* scale factur for converting cc to ns */
/*
* This is the Nemesis implementation.
* The variables are all set in apic.c
* Every timer IRQ time_now and time_pcc is set to the current values
* At callibration time_scale is set
*/
static s_time_t get_s_time(void)
{
u32 delta, low, pcc;
s_time_t now;
s_time_t incr;
/* read two values (pcc, now) "atomically" */
again:
pcc = stime_pcc;
now = stime_now;
if (stime_pcc != pcc) goto again;
/* only use bottom 32bits of TSC. This should be sufficient */
rdtscl(low);
delta = low - pcc;
incr = ((s_time_t)(stime_scale) * delta) >> 10;
return now + incr;
}
/* update time variables once in a while */
extern void update_time(void);
/*
* Domain Virtual Time
*/
typedef u64 dv_time_t;
#endif /* _ASM_TIME_H_ */
@@ -60,12 +60,12 @@ typedef struct
#define __HYPERVISOR_set_guest_stack 4
#define __HYPERVISOR_net_update 5
#define __HYPERVISOR_fpu_taskswitch 6
#define __HYPERVISOR_yield 7
#define __HYPERVISOR_exit 8
#define __HYPERVISOR_dom0_op 9
#define __HYPERVISOR_network_op 10
#define __HYPERVISOR_set_debugreg 11
#define __HYPERVISOR_get_debugreg 12
#define __HYPERVISOR_sched_op 7
#define __HYPERVISOR_exit 10
#define __HYPERVISOR_dom0_op 11
#define __HYPERVISOR_network_op 12
#define __HYPERVISOR_set_debugreg 13
#define __HYPERVISOR_get_debugreg 14
#define TRAP_INSTR "int $0x82"
@@ -142,36 +142,33 @@ typedef struct shared_info_st {
*/
unsigned long failsafe_address;
/*
* CPU ticks since start of day.
* `wall_time' counts CPU ticks in real time.
* `domain_time' counts CPU ticks during which this domain has run.
/*
* Time:
* The following abstractions are exposed: System Time, Wall Clock
* Time, Domain Virtual Time. Domains can access Cycle counter time
* directly.
*/
unsigned long ticks_per_ms; /* CPU ticks per millisecond */
/*
* Current wall_time can be found by rdtsc. Only possible use of
* variable below is that it provides a timestamp for last update
* of domain_time.
*/
unsigned long long wall_time;
unsigned long long domain_time;
/*
* Timeouts for points at which guest OS would like a callback.
* This will probably be backed up by a timer heap in the guest OS.
* In Linux we use timeouts to update 'jiffies'.
/* System Time */
long long system_time; /* in ns */
unsigned long st_timestamp; /* cyclecounter at last update */
unsigned long ticks_per_ms; /* CPU ticks per millisecond */
/* Wall Clock Time */
long tv_sec; /* essentially a struct timeval */
long tv_usec;
long long wc_timestamp; /* system time at last update */
/* Domain Virtual Time */
unsigned long long domain_time;
/*
* Timeout values:
* Allow a domain to specify a timeout value in system time and
* domain virtual time.
*/
unsigned long long wall_timeout;
unsigned long long domain_timeout;
/*
* Real-Time Clock. This shows time, in seconds, since 1.1.1980.
* The timestamp shows the CPU 'wall time' when RTC was last read.
* Thus it allows a mapping between 'real time' and 'wall time'.
*/
unsigned long rtc_time;
unsigned long long rtc_timestamp;
} shared_info_t;
/*
+65
View File
@@ -0,0 +1,65 @@
/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
****************************************************************************
* (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
****************************************************************************
*
* File: ac_timer.h
* Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
* Changes:
*
* Date: Nov 2002
*
* Environment: Xen Hypervisor
* Description: Accurate timer for the Hypervisor
*
****************************************************************************
* $Id: h-insert.h,v 1.4 2002/11/08 16:03:55 rn Exp $
****************************************************************************
*/
#ifndef _AC_TIMER_H_
#define _AC_TIMER_H_
#include <time.h> /* include notion of time */
/*
* The Xen Hypervisor provides two types of timers:
*
* - Linux style, jiffy based timers for legacy code and coarse grain timeouts
* These are defined in ./include/xeno/timer.h and implemented in
* ./common/timer.c. Unlike in Linux they are executed not on a periodic
* timer interrupt but "occasionally" with somewhat lesser accuracy.
*
* - accurate timers defined in this file and implemented in
* ./common/ac_timer.c. These are implemented using a programmable timer
* interrupt and are thus as accurate as the hardware allows. Where possible
* we use the local APIC for this purpose. However, this fact is hidden
* behind a architecture independent layer.
* accurate timers are programmed using system time.
*
* The interface to accurate timers is very similar to Linux timers with the
* exception that the expires value is not expressed in jiffies but in ns from
* boot time. Its implementation however, is entirely different.
*/
struct ac_timer {
struct list_head timer_list;
s_time_t expires; /* system time time out value */
unsigned long data;
void (*function)(unsigned long);
};
/* interface for "clients" */
extern int add_ac_timer(struct ac_timer *timer);
extern int rem_ac_timer(struct ac_timer *timer);
extern int mod_ac_timer(struct ac_timer *timer, s_time_t new_time);
static inline void init_ac_timer(struct ac_timer *timer)
{
//timer->next = NULL;
}
/* interface used by programmable timer, implemented hardware dependent */
extern int reprogram_ac_timer(s_time_t timeout);
extern void do_ac_timer(void);
#endif /* _AC_TIMER_H_ */
+29 -44
View File
@@ -42,7 +42,8 @@ extern struct mm_struct init_mm;
struct task_struct {
int processor;
int state, hyp_events;
int state;
int hyp_events;
unsigned int domain;
/* An unsafe pointer into a shared data area. */
@@ -85,11 +86,24 @@ struct task_struct {
unsigned long flags;
};
/*
* domain states
* TASK_RUNNING: Domain is runable and should be on a run queue
* TASK_INTERRUPTIBLE: Domain is blocked by may be woken up by an event
* or expiring timer
* TASK_UNINTERRUPTIBLE: Domain is blocked but may not be woken up by an
* arbitrary event or timer.
* TASK_WAIT: Domains CPU allocation expired.
* TASK_STOPPED: not really used in Xen
* TASK_DYING: Domain is about to cross over to the land of the dead.
*/
#define TASK_RUNNING 0
#define TASK_INTERRUPTIBLE 1
#define TASK_UNINTERRUPTIBLE 2
#define TASK_STOPPED 4
#define TASK_DYING 8
#define TASK_WAIT 4
#define TASK_DYING 16
/* #define TASK_STOPPED 8 not really used */
#define SCHED_YIELD 0x10
@@ -150,52 +164,23 @@ extern void free_irq(unsigned int, void *);
extern unsigned long wait_init_idle;
#define init_idle() clear_bit(smp_processor_id(), &wait_init_idle);
/*
* Scheduler functions (in schedule.c)
*/
#define set_current_state(_s) do { current->state = (_s); } while (0)
#define MAX_SCHEDULE_TIMEOUT LONG_MAX
void scheduler_init(void);
void start_scheduler(void);
void sched_add_domain(struct task_struct *p);
void sched_rem_domain(struct task_struct *p);
int wake_up(struct task_struct *p);
long schedule_timeout(long timeout);
long do_yield(void);
void reschedule(struct task_struct *p);
asmlinkage void schedule(void);
void reschedule(struct task_struct *p);
typedef struct schedule_data_st
{
spinlock_t lock;
struct list_head runqueue;
struct task_struct *prev, *curr;
} __cacheline_aligned schedule_data_t;
extern schedule_data_t schedule_data[NR_CPUS];
static inline void __add_to_runqueue(struct task_struct * p)
{
list_add(&p->run_list, &schedule_data[p->processor].runqueue);
}
static inline void __move_last_runqueue(struct task_struct * p)
{
list_del(&p->run_list);
list_add_tail(&p->run_list, &schedule_data[p->processor].runqueue);
}
static inline void __move_first_runqueue(struct task_struct * p)
{
list_del(&p->run_list);
list_add(&p->run_list, &schedule_data[p->processor].runqueue);
}
static inline void __del_from_runqueue(struct task_struct * p)
{
list_del(&p->run_list);
p->run_list.next = NULL;
}
static inline int __task_on_runqueue(struct task_struct *p)
{
return (p->run_list.next != NULL);
}
int wake_up(struct task_struct *p);
#define signal_pending(_p) ((_p)->hyp_events || \
(_p)->shared_info->events)
+58 -3
View File
@@ -1,13 +1,61 @@
/******************************************************************************
* time.h
/* -*- Mode:C; c-basic-offset:4; tab-width:4 -*-
****************************************************************************
* (C) 2002 - Rolf Neugebauer - Intel Research Cambridge
****************************************************************************
*
* File: time.h
* Author: Rolf Neugebauer (neugebar@dcs.gla.ac.uk)
* Changes:
*
* Date: Nov 2002
*
* Environment: Xen Hypervisor
* Description: This file provides a one stop shop for all time related
* issues within the hypervisor.
*
* The Hypervisor provides the following notions of time:
* Cycle Counter Time, System Time, Wall Clock Time, and
* Domain Virtual Time.
*
****************************************************************************
* $Id: h-insert.h,v 1.4 2002/11/08 16:03:55 rn Exp $
****************************************************************************
*/
#ifndef __XENO_TIME_H__
#define __XENO_TIME_H__
#include <asm/ptrace.h> /* XXX Only used for do_timer which should be moved */
#include <asm/time.h> /* pull in architecture specific time definition */
#include <xeno/types.h>
#include <asm/ptrace.h>
/*
* Cycle Counter Time (defined in asm/time.h)
*/
/*
* System Time
* 64 bit value containing the nanoseconds elapsed since boot time.
* This value is adjusted by frequency drift.
* NOW() returns the current time.
* The other macros are for convenience to approximate short intervals
* of real time into system time
*/
#define NOW() ((s_time_t)get_s_time())
#define SECONDS(_s) (((s_time_t)(_s)) * 1000000000UL )
#define TENTHS(_ts) (((s_time_t)(_ts)) * 100000000UL )
#define HUNDREDTHS(_hs) (((s_time_t)(_hs)) * 10000000UL )
#define MILLISECS(_ms) (((s_time_t)(_ms)) * 1000000UL )
#define MICROSECS(_us) (((s_time_t)(_us)) * 1000UL )
#define Time_Max ((s_time_t) 0x7fffffffffffffffLL)
#define FOREVER Time_Max
/*
* Wall Clock Time
*/
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
@@ -25,6 +73,13 @@ extern void get_fast_time(struct timeval *tv);
extern void (*do_get_fast_time)(struct timeval *);
#endif
/*
* Domain Virtual Time (defined in asm/time.h)
*/
/* XXX Interface for getting and setting still missing */
/* XXX move this */
extern void do_timer(struct pt_regs *regs);
#endif /* __XENO_TIME_H__ */
+8 -1
View File
@@ -12,10 +12,17 @@
* The "data" field enables use of a common timeout function for several
* timeouts. You can use this field to distinguish between the different
* invocations.
*
* RN: Unlike the Linux timers, which are executed at the periodic timer
* interrupt, in Xen, the timer list is only checked "occasionally", thus
* its accuracy might be somewhat worse than under Linux. However, the
* hypervisor should be purely event-driven and, in fact, in the current
* implementation, timers are only used for watchdog purpose at a very
* coarse granularity anyway. Thus this is not a problem.
*/
struct timer_list {
struct list_head list;
unsigned long expires;
unsigned long expires; /* jiffies */
unsigned long data;
void (*function)(unsigned long);
};
@@ -42,7 +42,7 @@ extern struct drive_info_struct drive_info;
EXPORT_SYMBOL(drive_info);
#endif
extern unsigned long get_cmos_time(void);
//extern unsigned long get_cmos_time(void);
/* platform dependent support */
EXPORT_SYMBOL(boot_cpu_data);
@@ -58,7 +58,7 @@ EXPORT_SYMBOL(probe_irq_mask);
EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(pm_idle);
EXPORT_SYMBOL(pm_power_off);
EXPORT_SYMBOL(get_cmos_time);
//EXPORT_SYMBOL(get_cmos_time);
EXPORT_SYMBOL(apm_info);
#ifdef CONFIG_DEBUG_IOVIRT
@@ -86,7 +86,7 @@ void cpu_idle (void)
while (1) {
while (!current->need_resched)
HYPERVISOR_yield();
HYPERVISOR_do_sched_op(NULL);
schedule();
check_pgt_cache();
}
+26 -59
View File
@@ -102,47 +102,26 @@ static inline unsigned long ticks_to_us(unsigned long ticks)
return(hi);
}
static inline unsigned long do_gettimeoffset(void)
static long long get_s_time(void)
{
#if 0
register unsigned long eax, edx;
u32 delta, low, pcc;
long long now;
long long incr;
/* Read the Time Stamp Counter */
/* read two values (pcc, now) "atomically" */
again:
pcc = HYPERVISOR_shared_info->st_timestamp;
now = HYPERVISOR_shared_info->system_time;
if (HYPERVISOR_shared_info->st_timestamp != pcc) goto again;
rdtsc(eax,edx);
/* only use bottom 32bits of TSC. This should be sufficient */
rdtscl(low);
delta = low - pcc;
/* .. relative to previous jiffy (32 bits is enough) */
eax -= last_tsc_low; /* tsc_low delta */
/*
* Time offset = (tsc_low delta) * fast_gettimeoffset_quotient
* = (tsc_low delta) * (usecs_per_clock)
* = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy)
*
* Using a mull instead of a divl saves up to 31 clock cycles
* in the critical path.
*/
edx = ticks_to_us(eax);
/* our adjusted time offset in microseconds */
return delay_at_last_interrupt + edx;
#else
/*
* We should keep a 'last_tsc_low' thing which incorporates
* delay_at_last_interrupt, adjusted in timer_interrupt after
* do_timer_interrupt. It would look at change in xtime, and
* make appropriate adjustment to a last_tsc variable.
*
* We'd be affected by rounding error in ticks_per_usec, and by
* processor clock drift (which should be no more than in an
* external interrupt source anyhow).
*
* Perhaps a bit rough and ready, but never mind!
*/
return 0;
#endif
incr = ((long long)(ticks_to_us(delta)*1000));
return now + incr;
}
#define NOW() ((long long)get_s_time())
/*
* This version of gettimeofday has microsecond resolution
@@ -151,15 +130,15 @@ static inline unsigned long do_gettimeoffset(void)
void do_gettimeofday(struct timeval *tv)
{
unsigned long flags;
unsigned long usec, sec, lost;
unsigned long usec, sec;
read_lock_irqsave(&xtime_lock, flags);
usec = do_gettimeoffset();
lost = jiffies - wall_jiffies;
if ( lost != 0 ) usec += lost * (1000000 / HZ);
sec = xtime.tv_sec;
usec += xtime.tv_usec;
read_unlock_irqrestore(&xtime_lock, flags);
usec = ((unsigned long)(NOW()-HYPERVISOR_shared_info->wc_timestamp))/1000;
sec = HYPERVISOR_shared_info->tv_sec;
usec += HYPERVISOR_shared_info->tv_usec;
read_unlock_irqrestore(&xtime_lock, flags);
while ( usec >= 1000000 )
{
@@ -173,6 +152,8 @@ void do_gettimeofday(struct timeval *tv)
void do_settimeofday(struct timeval *tv)
{
/* XXX RN: shoudl do something special here for dom0 */
#if 0
write_lock_irq(&xtime_lock);
/*
* This is revolting. We need to set "xtime" correctly. However, the
@@ -195,6 +176,7 @@ void do_settimeofday(struct timeval *tv)
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
write_unlock_irq(&xtime_lock);
#endif
}
@@ -235,19 +217,6 @@ static struct irqaction irq_timer = {
};
unsigned long get_cmos_time(void)
{
unsigned long secs = HYPERVISOR_shared_info->rtc_time;
unsigned long diff;
rdtscl(diff);
diff -= (unsigned long)HYPERVISOR_shared_info->rtc_timestamp;
secs += ticks_to_us(diff);
return(secs + ticks_to_secs(diff));
}
/* Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset(). */
static unsigned long __init calibrate_tsc(void)
@@ -268,7 +237,6 @@ void __init time_init(void)
unsigned long long alarm;
fast_gettimeoffset_quotient = calibrate_tsc();
do_get_fast_time = do_gettimeofday;
/* report CPU clock rate in Hz.
* The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
@@ -299,6 +267,5 @@ void __init time_init(void)
HYPERVISOR_shared_info->domain_timeout = ~0ULL;
clear_bit(_EVENT_TIMER, &HYPERVISOR_shared_info->events);
xtime.tv_sec = get_cmos_time();
xtime.tv_usec = 0;
do_gettimeofday(&xtime);
}
@@ -203,12 +203,13 @@ static inline int HYPERVISOR_fpu_taskswitch(void)
return ret;
}
static inline int HYPERVISOR_yield(void)
static inline int HYPERVISOR_do_sched_op(void *sched_op)
{
int ret;
__asm__ __volatile__ (
TRAP_INSTR
: "=a" (ret) : "0" (__HYPERVISOR_yield) );
: "=a" (ret) : "0" (__HYPERVISOR_sched_op),
"b" (sched_op) );
return ret;
}
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.