Files
org-hyperion-cules/timer.c
Fish (David B. Trout) 0316c4f294 More set thread priority tweaking:
1. Move SETMODE calls into hthreads functions.
2. Issue message that identifies source statement that failed.
2014-01-13 02:01:39 -08:00

461 lines
16 KiB
C

/* TIMER.C (c) Copyright Roger Bowler, 1999-2012 */
/* Timer support functions */
/* */
/* Released under "The Q Public License Version 1" */
/* (http://www.hercules-390.org/herclic.html) as modifications to */
/* Hercules. */
/* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2012 */
#include "hstdinc.h"
#define _HENGINE_DLL_
#include "hercules.h"
#include "opcode.h"
#include "feat390.h"
#include "feat370.h"
/*-------------------------------------------------------------------*/
/* Check for timer event */
/* */
/* Checks for the following interrupts: */
/* [1] Clock comparator */
/* [2] CPU timer */
/* [3] Interval timer */
/* CPUs with an outstanding interrupt are signalled */
/* */
/* tod_delta is in hercules internal clock format (>> 8) */
/*-------------------------------------------------------------------*/
void update_cpu_timer(void)
{
int cpu; /* CPU counter */
REGS *regs; /* -> CPU register context */
CPU_BITMAP intmask = 0; /* Interrupt CPU mask */
#if defined(OPTION_MIPS_COUNTING)
/* If no CPUs are available, just return (device server mode) */
if (!sysblk.hicpu)
return;
#endif /*defined(OPTION_MIPS_COUNTING)*/
/* Access the diffent register contexts with the intlock held */
OBTAIN_INTLOCK(NULL);
/* Check for [1] clock comparator, [2] cpu timer, and
* [3] interval timer interrupts for each CPU.
*/
for (cpu = 0; cpu < sysblk.hicpu; cpu++)
{
/* Ignore this CPU if it is not started */
if (!IS_CPU_ONLINE(cpu)
|| CPUSTATE_STOPPED == sysblk.regs[cpu]->cpustate)
continue;
/* Point to the CPU register context */
regs = sysblk.regs[cpu];
/*-------------------------------------------*
* [1] Check for clock comparator interrupt *
*-------------------------------------------*/
if (TOD_CLOCK(regs) > regs->clkc)
{
if (!IS_IC_CLKC(regs))
{
ON_IC_CLKC(regs);
intmask |= regs->cpubit;
}
}
else if (IS_IC_CLKC(regs))
OFF_IC_CLKC(regs);
#if defined(_FEATURE_SIE)
/* If running under SIE also check the SIE copy */
if(regs->sie_active)
{
/* Signal clock comparator interrupt if needed */
if(TOD_CLOCK(regs->guestregs) > regs->guestregs->clkc)
{
ON_IC_CLKC(regs->guestregs);
intmask |= regs->cpubit;
}
else
OFF_IC_CLKC(regs->guestregs);
}
#endif /*defined(_FEATURE_SIE)*/
/*-------------------------------------------*
* [2] Decrement the CPU timer for each CPU *
*-------------------------------------------*/
/* If LPAR mode and not waiting, or in BASIC mode, decrement
* the CPU timer and update the CPU timer interrupt state, if.
* necessary.
*/
if (!WAITSTATE(&regs->psw) || !sysblk.lparmode)
{
/* Set interrupt flag if the CPU timer is negative */
if (cpu_timer(regs) < 0)
{
if (!IS_IC_PTIMER(regs))
{
ON_IC_PTIMER(regs);
intmask |= regs->cpubit;
}
}
else if(IS_IC_PTIMER(regs))
OFF_IC_PTIMER(regs);
}
#if defined(_FEATURE_SIE)
/* When running under SIE also update the SIE copy */
if(regs->sie_active)
{
/* Set interrupt flag if the CPU timer is negative */
if (cpu_timer_SIE(regs) < 0)
{
ON_IC_PTIMER(regs->guestregs);
intmask |= regs->cpubit;
}
else
OFF_IC_PTIMER(regs->guestregs);
}
#endif /*defined(_FEATURE_SIE)*/
#if defined(_FEATURE_INTERVAL_TIMER)
/*-------------------------------------------*
* [3] Check for interval timer interrupt *
*-------------------------------------------*/
if(regs->arch_mode == ARCH_370)
{
if( chk_int_timer(regs) )
intmask |= regs->cpubit;
}
#if defined(_FEATURE_SIE)
/* When running under SIE also update the SIE copy */
if(regs->sie_active)
{
if(SIE_STATB(regs->guestregs, M, 370)
&& SIE_STATNB(regs->guestregs, M, ITMOF))
{
if( chk_int_timer(regs->guestregs) )
intmask |= regs->cpubit;
}
}
#endif /*defined(_FEATURE_SIE)*/
#endif /*defined(_FEATURE_INTERVAL_TIMER)*/
} /* end for(cpu) */
/* If a timer interrupt condition was detected for any CPU
then wake up those CPUs if they are waiting */
WAKEUP_CPUS_MASK (intmask);
RELEASE_INTLOCK(NULL);
} /* end function check_timer_event */
/*-------------------------------------------------------------------*/
/* TOD clock and timer thread */
/* */
/* This function runs as a separate thread. It wakes up every */
/* 1 microsecond, updates the TOD clock, and decrements the */
/* CPU timer for each CPU. If any CPU timer goes negative, or */
/* if the TOD clock exceeds the clock comparator for any CPU, */
/* it signals any waiting CPUs to wake up and process interrupts. */
/*-------------------------------------------------------------------*/
void *timer_update_thread (void *argp)
{
#ifdef OPTION_MIPS_COUNTING
int i; /* Loop index */
REGS *regs; /* -> REGS */
U64 mipsrate; /* Calculated MIPS rate */
U64 siosrate; /* Calculated SIO rate */
U64 total_mips; /* Total MIPS rate */
U64 total_sios; /* Total SIO rate */
/* Clock times use the top 64-bits of the ETOD clock */
U64 now; /* Current time of day */
U64 then; /* Previous time of day */
U64 diff; /* Interval */
U64 halfdiff; /* One-half interval */
U64 waittime; /* Wait time */
const U64 period = ETOD_SEC; /* MIPS calculation period */
#define diffrate(_x,_y) \
((((_x) * (_y)) + halfdiff) / diff)
#endif /*OPTION_MIPS_COUNTING*/
UNREFERENCED(argp);
/* Set timer thread priority */
set_thread_priority(0, sysblk.todprio);
/* Display thread started message on control panel */
WRMSG (HHC00100, "I", thread_id(), get_thread_priority(0), "Timer");
SET_THREAD_NAME_ID(-1, "CPU Timer");
#ifdef OPTION_MIPS_COUNTING
then = host_tod();
while (!sysblk.shutdown)
{
/* Update TOD clock and save TOD clock value */
now = update_tod_clock();
diff = now - then;
if (diff >= period) /* Period expired? */
{
halfdiff = diff / 2; /* One-half interval for rounding */
then = now;
total_mips = total_sios = 0;
#if defined(OPTION_SHARED_DEVICES)
total_sios = sysblk.shrdcount;
sysblk.shrdcount = 0;
#endif
for (i = 0; i < sysblk.hicpu; i++)
{
obtain_lock (&sysblk.cpulock[i]);
if (!IS_CPU_ONLINE(i))
{
release_lock(&sysblk.cpulock[i]);
continue;
}
regs = sysblk.regs[i];
/* 0% if CPU is STOPPED */
if (regs->cpustate == CPUSTATE_STOPPED)
{
regs->mipsrate = regs->siosrate = regs->cpupct = 0;
release_lock(&sysblk.cpulock[i]);
continue;
}
/* Calculate instructions per second */
mipsrate = regs->instcount;
regs->instcount = 0;
regs->prevcount += mipsrate;
mipsrate = diffrate(mipsrate, period);
regs->mipsrate = mipsrate;
total_mips += mipsrate;
/* Calculate SIOs per second */
siosrate = regs->siocount;
regs->siocount = 0;
regs->siototal += siosrate;
siosrate = diffrate(siosrate, period);
regs->siosrate = siosrate;
total_sios += siosrate;
/* Calculate CPU busy percentage */
waittime = regs->waittime;
regs->waittime = 0;
if (regs->waittod)
{
waittime += now - regs->waittod;
regs->waittod = now;
}
regs->cpupct = min((diff > waittime) ?
diffrate(diff - waittime, 100) : 0,
100);
release_lock(&sysblk.cpulock[i]);
} /* end for(cpu) */
/* Total for ALL CPUs together */
sysblk.mipsrate = total_mips;
sysblk.siosrate = total_sios;
update_maxrates_hwm(); // (update high-water-mark values)
} /* end if(diff >= period) */
#else /* ! OPTION_MIPS_COUNTING */
while (sysblk.cpus)
{
/* Update TOD clock */
update_tod_clock();
#endif /*OPTION_MIPS_COUNTING*/
/* Sleep for another timer update interval... */
usleep ( sysblk.timerint );
} /* end while */
WRMSG (HHC00101, "I", thread_id(), get_thread_priority(0), "Timer");
sysblk.todtid = 0;
return NULL;
} /* end function timer_update_thread */
LOCK caplock;
COND capcond;
static void capping_manager_shutdown(void * unused)
{
UNREFERENCED(unused);
if(sysblk.capvalue)
{
sysblk.capvalue = 0;
obtain_lock(&caplock);
timed_wait_condition_relative_usecs(&capcond,&caplock,2*1000*1000,NULL);
release_lock(&caplock);
}
}
/*-------------------------------------------------------------------*/
/* Capping manager thread */
/* */
/* This function runs as a separate thread. It is started when a */
/* value is given on the CAPPING statement within the config file. */
/* It checks every 1/100 second if there are too many CP */
/* instructions executed. In that case the CPs are stopped. Then */
/* the manager counts if the CPs are stopped long enough before */
/* waking them up. Capping does only apply to CP, not specialty */
/* engines. Those engines are untouched by the capping manager. */
/*-------------------------------------------------------------------*/
void *capping_manager_thread (void *unused)
{
U64 diff; /* Time passed during interval */
U64 now; /* Current time */
U64 then = 0; /* Previous interval time */
int cpu; /* cpu index */
U32 allowed; /* Max allowed insts during interval */
U64 instcnt[MAX_CPU_ENGINES]; /* Number of CP insts executed */
U64 prevcnt[MAX_CPU_ENGINES]; /* Inst CP count on previous interval */
U32 prevcap = 0; /* Previous cappling value */
U32 iactual; /* Actual instruction count */
U32 irate[MAX_CPU_ENGINES]; /* Actual instruction rate */
int numcap = 1; /* Number of CPU's being capped */
UNREFERENCED(unused);
initialize_lock (&caplock);
initialize_condition(&capcond);
hdl_adsc("capping_manager_shutdown",capping_manager_shutdown, NULL);
/* Display thread started message on control panel */
WRMSG(HHC00100, "I", thread_id(), get_thread_priority(0), "Capping manager");
/* Initialize interrupt wait locks */
for(cpu = 0; cpu < sysblk.maxcpu; cpu++)
initialize_lock(&sysblk.caplock[cpu]);
/* Check for as long as capping is active */
while(sysblk.capvalue)
{
if(sysblk.capvalue != prevcap)
{
prevcap = sysblk.capvalue;
WRMSG(HHC00877, "I", sysblk.capvalue);
/* Lets get started */
then = host_tod();
for(cpu = 0; cpu < MAX_CPU_ENGINES; cpu++)
{
prevcnt[cpu] = 0xFFFFFFFFFFFFFFFFULL;
irate[cpu] = 0;
}
}
/* Sleep for 1/100 of a second */
usleep(10000);
if ( sysblk.capvalue == 0 )
break;
now = host_tod();
/* Count the number of CPs to be capped */
for(numcap = cpu = 0; cpu < sysblk.hicpu; cpu++)
if(IS_CPU_ONLINE(cpu) && sysblk.ptyp[cpu] == SCCB_PTYP_CP && sysblk.regs[cpu]->cpustate == CPUSTATE_STARTED)
numcap++;
/* Continue if no CPU's to be capped */
if(!numcap)
continue;
/* Actual elapsed time of our approximate 1/100 of a second wait */
diff = now - then;
/* Calculate the allowed amount of executed instructions for this interval */
allowed = sysblk.capvalue * diff;
allowed /= numcap;
/* Calculate the number of executed instructions */
for(cpu = 0; cpu < sysblk.hicpu; cpu++)
{
if(!IS_CPU_ONLINE(cpu) || sysblk.ptyp[cpu] != SCCB_PTYP_CP || sysblk.regs[cpu]->cpustate != CPUSTATE_STARTED)
break;
instcnt[cpu] = sysblk.regs[cpu]->prevcount + sysblk.regs[cpu]->instcount;
/* Check for CP reset */
if(prevcnt[cpu] > instcnt[cpu])
prevcnt[cpu] = instcnt[cpu];
/* Actual number of instructions executed in interval */
iactual = instcnt[cpu] - prevcnt[cpu];
/* Calculate floating average rate over the past second */
irate[cpu] = (irate[cpu] - ((irate[cpu] + 64) >> 7)) + ((iactual + 64) >> 7);
/* Wakeup the capped CP if rate not exceeded */
if(sysblk.caplocked[cpu] && (allowed > irate[cpu]))
{
sysblk.caplocked[cpu] = 0;
release_lock(&sysblk.caplock[cpu]);
}
else /* We will never unlock and relock in the same interval, this to ensure we do not starve a CP */
/* Cap if the rate has exceeded the allowed rate */
if(!sysblk.caplocked[cpu] && (allowed < irate[cpu]))
{
/* Cap the CP */
obtain_lock(&sysblk.caplock[cpu]);
sysblk.caplocked[cpu] = 1;
}
prevcnt[cpu] = instcnt[cpu];
then = now;
}
}
/* Uncap all before exit */
for(cpu = 0; cpu < sysblk.maxcpu; cpu++)
if(sysblk.caplocked[cpu])
{
sysblk.caplocked[cpu] = 0;
release_lock(&sysblk.caplock[cpu]);
}
signal_condition(&capcond);
if ( !sysblk.shutdown )
hdl_rmsc(capping_manager_shutdown, NULL);
sysblk.captid = 0;
WRMSG(HHC00101, "I", thread_id(), get_thread_priority(0), "Capping manager");
return(NULL);
}