1 /* TIMER.C (c) Copyright Roger Bowler, 1999-2009 */
2 /* Timer support functions */
3
4 /* z/Architecture support - (c) Copyright Jan Jaeger, 1999-2009 */
5
6 #include "hstdinc.h"
7
8 #include "hercules.h"
9
10 #include "opcode.h"
11
12 #include "feat390.h"
13 #include "feat370.h"
14
15 // ZZ int ecpsvm_testvtimer(REGS *,int);
16
17 /*-------------------------------------------------------------------*/
18 /* Check for timer event */
19 /* */
20 /* Checks for the following interrupts: */
21 /* [1] Clock comparator */
22 /* [2] CPU timer */
23 /* [3] Interval timer */
24 /* CPUs with an outstanding interrupt are signalled */
25 /* */
26 /* tod_delta is in hercules internal clock format (>> 8) */
27 /*-------------------------------------------------------------------*/
update_cpu_timer(void)28 void update_cpu_timer(void)
29 {
30 int cpu; /* CPU counter */
31 REGS *regs; /* -> CPU register context */
32 CPU_BITMAP intmask = 0; /* Interrupt CPU mask */
33
34 /* Access the diffent register contexts with the intlock held */
35 OBTAIN_INTLOCK(NULL);
36
37 /* Check for [1] clock comparator, [2] cpu timer, and
38 * [3] interval timer interrupts for each CPU.
39 */
40 for (cpu = 0; cpu < HI_CPU; cpu++)
41 {
42 /* Ignore this CPU if it is not started */
43 if (!IS_CPU_ONLINE(cpu)
44 || CPUSTATE_STOPPED == sysblk.regs[cpu]->cpustate)
45 continue;
46
47 /* Point to the CPU register context */
48 regs = sysblk.regs[cpu];
49
50 /*-------------------------------------------*
51 * [1] Check for clock comparator interrupt *
52 *-------------------------------------------*/
53 if (TOD_CLOCK(regs) > regs->clkc)
54 {
55 if (!IS_IC_CLKC(regs))
56 {
57 ON_IC_CLKC(regs);
58 intmask |= regs->cpubit;
59 }
60 }
61 else if (IS_IC_CLKC(regs))
62 OFF_IC_CLKC(regs);
63
64 #if defined(_FEATURE_SIE)
65 /* If running under SIE also check the SIE copy */
66 if(regs->sie_active)
67 {
68 /* Signal clock comparator interrupt if needed */
69 if(TOD_CLOCK(regs->guestregs) > regs->guestregs->clkc)
70 {
71 ON_IC_CLKC(regs->guestregs);
72 intmask |= regs->cpubit;
73 }
74 else
75 OFF_IC_CLKC(regs->guestregs);
76 }
77 #endif /*defined(_FEATURE_SIE)*/
78
79 /*-------------------------------------------*
80 * [2] Decrement the CPU timer for each CPU *
81 *-------------------------------------------*/
82
83 /* Set interrupt flag if the CPU timer is negative */
84 if (CPU_TIMER(regs) < 0)
85 {
86 if (!IS_IC_PTIMER(regs))
87 {
88 ON_IC_PTIMER(regs);
89 intmask |= regs->cpubit;
90 }
91 }
92 else if(IS_IC_PTIMER(regs))
93 OFF_IC_PTIMER(regs);
94
95 #if defined(_FEATURE_SIE)
96 /* When running under SIE also update the SIE copy */
97 if(regs->sie_active)
98 {
99 /* Set interrupt flag if the CPU timer is negative */
100 if (CPU_TIMER(regs->guestregs) < 0)
101 {
102 ON_IC_PTIMER(regs->guestregs);
103 intmask |= regs->cpubit;
104 }
105 else
106 OFF_IC_PTIMER(regs->guestregs);
107 }
108 #endif /*defined(_FEATURE_SIE)*/
109
110 #if defined(_FEATURE_INTERVAL_TIMER)
111 /*-------------------------------------------*
112 * [3] Check for interval timer interrupt *
113 *-------------------------------------------*/
114
115 if(regs->arch_mode == ARCH_370)
116 {
117 if( chk_int_timer(regs) )
118 intmask |= regs->cpubit;
119 }
120
121
122 #if defined(_FEATURE_SIE)
123 /* When running under SIE also update the SIE copy */
124 if(regs->sie_active)
125 {
126 if(SIE_STATB(regs->guestregs, M, 370)
127 && SIE_STATNB(regs->guestregs, M, ITMOF))
128 {
129 if( chk_int_timer(regs->guestregs) )
130 intmask |= regs->cpubit;
131 }
132 }
133 #endif /*defined(_FEATURE_SIE)*/
134
135 #endif /*defined(_FEATURE_INTERVAL_TIMER)*/
136
137 } /* end for(cpu) */
138
139 /* If a timer interrupt condition was detected for any CPU
140 then wake up those CPUs if they are waiting */
141 WAKEUP_CPUS_MASK (intmask);
142
143 RELEASE_INTLOCK(NULL);
144
145 } /* end function check_timer_event */
146
147 /*-------------------------------------------------------------------*/
148 /* TOD clock and timer thread */
149 /* */
150 /* This function runs as a separate thread. It wakes up every */
151 /* 1 microsecond, updates the TOD clock, and decrements the */
152 /* CPU timer for each CPU. If any CPU timer goes negative, or */
153 /* if the TOD clock exceeds the clock comparator for any CPU, */
154 /* it signals any waiting CPUs to wake up and process interrupts. */
155 /*-------------------------------------------------------------------*/
timer_update_thread(void * argp)156 void *timer_update_thread (void *argp)
157 {
158 #ifdef OPTION_MIPS_COUNTING
159 int i; /* Loop index */
160 REGS *regs; /* -> REGS */
161 U64 now; /* Current time of day (us) */
162 U64 then; /* Previous time of day (us) */
163 U64 diff; /* Interval (us) */
164 U64 mipsrate; /* Calculated MIPS rate */
165 U64 siosrate; /* Calculated SIO rate */
166 U64 cpupct; /* Calculated cpu percentage */
167 U64 total_mips; /* Total MIPS rate */
168 U64 total_sios; /* Total SIO rate */
169 #endif /*OPTION_MIPS_COUNTING*/
170
171 UNREFERENCED(argp);
172
173 /* Set root mode in order to set priority */
174 SETMODE(ROOT);
175
176 /* Set timer thread priority */
177 if (setpriority(PRIO_PROCESS, 0, sysblk.todprio))
178 logmsg (_("HHCTT001W Timer thread set priority %d failed: %s\n"),
179 sysblk.todprio, strerror(errno));
180
181 /* Back to user mode */
182 SETMODE(USER);
183
184 /* Display thread started message on control panel */
185 logmsg (_("HHCTT002I Timer thread started: tid="TIDPAT", pid=%d, "
186 "priority=%d\n"),
187 thread_id(), getpid(), getpriority(PRIO_PROCESS,0));
188
189 #ifdef OPTION_MIPS_COUNTING
190 then = host_tod();
191 #endif
192
193 while (sysblk.cpus)
194 {
195 /* Update TOD clock */
196 update_tod_clock();
197
198 #ifdef OPTION_MIPS_COUNTING
199 now = host_tod();
200 diff = now - then;
201
202 if (diff >= 1000000)
203 {
204 then = now;
205 total_mips = total_sios = 0;
206 #if defined(OPTION_SHARED_DEVICES)
207 total_sios = sysblk.shrdcount;
208 sysblk.shrdcount = 0;
209 #endif
210
211 for (i = 0; i < HI_CPU; i++)
212 {
213 obtain_lock (&sysblk.cpulock[i]);
214
215 if (!IS_CPU_ONLINE(i))
216 {
217 release_lock(&sysblk.cpulock[i]);
218 continue;
219 }
220
221 regs = sysblk.regs[i];
222
223 /* 0% if CPU is STOPPED */
224 if (regs->cpustate == CPUSTATE_STOPPED)
225 {
226 regs->mipsrate = regs->siosrate = regs->cpupct = 0;
227 release_lock(&sysblk.cpulock[i]);
228 continue;
229 }
230
231 /* Calculate instructions per second */
232 mipsrate = regs->instcount;
233 regs->instcount = 0;
234 regs->prevcount += mipsrate;
235 mipsrate = (mipsrate*1000000 + diff/2) / diff;
236 if (mipsrate > MAX_REPORTED_MIPSRATE)
237 mipsrate = 0;
238 regs->mipsrate = mipsrate;
239 total_mips += mipsrate;
240
241 /* Calculate SIOs per second */
242 siosrate = regs->siocount;
243 regs->siocount = 0;
244 regs->siototal += siosrate;
245 siosrate = (siosrate*1000000 + diff/2) / diff;
246 if (siosrate > MAX_REPORTED_SIOSRATE)
247 siosrate = 0;
248 regs->siosrate = siosrate;
249 total_sios += siosrate;
250
251 /* Calculate CPU busy percentage */
252 cpupct = regs->waittime;
253 regs->waittime = 0;
254 if (regs->waittod)
255 {
256 cpupct += now - regs->waittod;
257 regs->waittod = now;
258 }
259 cpupct = ((diff - cpupct)*100) / diff;
260 if (cpupct > 100) cpupct = 100;
261 regs->cpupct = cpupct;
262
263 release_lock(&sysblk.cpulock[i]);
264
265 } /* end for(cpu) */
266
267 /* Total for ALL CPUs together */
268 sysblk.mipsrate = total_mips;
269 sysblk.siosrate = total_sios;
270 } /* end if(diff >= 1000000) */
271 #endif /*OPTION_MIPS_COUNTING*/
272
273 /* Sleep for another timer update interval... */
274 usleep ( sysblk.timerint );
275
276 } /* end while */
277
278 logmsg (_("HHCTT003I Timer thread ended\n"));
279
280 sysblk.todtid = 0;
281
282 return NULL;
283
284 } /* end function timer_update_thread */
285