-
Notifications
You must be signed in to change notification settings - Fork 71
/
Copy pathtimer.c
454 lines (365 loc) · 15.6 KB
/
timer.c
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
/* 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 *
*-------------------------------------------*/
/* 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 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_accumulated += 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);
}