1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
|
/* TIMER.C (c) Copyright Roger Bowler, 1999-2009 */
/* Timer support functions */
/* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2009 */
#include "hstdinc.h"
#include "hercules.h"
#include "opcode.h"
#include "feat390.h"
#include "feat370.h"
// ZZ int ecpsvm_testvtimer(REGS *,int);
/*-------------------------------------------------------------------*/
/* 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 */
/* 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 < HI_CPU; 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 *
*-------------------------------------------*/
/* 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(regs->guestregs) < 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 now; /* Current time of day (us) */
U64 then; /* Previous time of day (us) */
U64 diff; /* Interval (us) */
U64 mipsrate; /* Calculated MIPS rate */
U64 siosrate; /* Calculated SIO rate */
U64 cpupct; /* Calculated cpu percentage */
U64 total_mips; /* Total MIPS rate */
U64 total_sios; /* Total SIO rate */
#endif /*OPTION_MIPS_COUNTING*/
UNREFERENCED(argp);
/* Set root mode in order to set priority */
SETMODE(ROOT);
/* Set timer thread priority */
if (setpriority(PRIO_PROCESS, 0, sysblk.todprio))
logmsg (_("HHCTT001W Timer thread set priority %d failed: %s\n"),
sysblk.todprio, strerror(errno));
/* Back to user mode */
SETMODE(USER);
/* Display thread started message on control panel */
logmsg (_("HHCTT002I Timer thread started: tid="TIDPAT", pid=%d, "
"priority=%d\n"),
thread_id(), getpid(), getpriority(PRIO_PROCESS,0));
#ifdef OPTION_MIPS_COUNTING
then = host_tod();
#endif
while (sysblk.cpus)
{
/* Update TOD clock */
update_tod_clock();
#ifdef OPTION_MIPS_COUNTING
now = host_tod();
diff = now - then;
if (diff >= 1000000)
{
then = now;
total_mips = total_sios = 0;
#if defined(OPTION_SHARED_DEVICES)
total_sios = sysblk.shrdcount;
sysblk.shrdcount = 0;
#endif
for (i = 0; i < HI_CPU; 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 = (mipsrate*1000000 + diff/2) / diff;
if (mipsrate > MAX_REPORTED_MIPSRATE)
mipsrate = 0;
regs->mipsrate = mipsrate;
total_mips += mipsrate;
/* Calculate SIOs per second */
siosrate = regs->siocount;
regs->siocount = 0;
regs->siototal += siosrate;
siosrate = (siosrate*1000000 + diff/2) / diff;
if (siosrate > MAX_REPORTED_SIOSRATE)
siosrate = 0;
regs->siosrate = siosrate;
total_sios += siosrate;
/* Calculate CPU busy percentage */
cpupct = regs->waittime;
regs->waittime = 0;
if (regs->waittod)
{
cpupct += now - regs->waittod;
regs->waittod = now;
}
cpupct = ((diff - cpupct)*100) / diff;
if (cpupct > 100) cpupct = 100;
regs->cpupct = cpupct;
release_lock(&sysblk.cpulock[i]);
} /* end for(cpu) */
/* Total for ALL CPUs together */
sysblk.mipsrate = total_mips;
sysblk.siosrate = total_sios;
} /* end if(diff >= 1000000) */
#endif /*OPTION_MIPS_COUNTING*/
/* Sleep for another timer update interval... */
usleep ( sysblk.timerint );
} /* end while */
logmsg (_("HHCTT003I Timer thread ended\n"));
sysblk.todtid = 0;
return NULL;
} /* end function timer_update_thread */
|