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