1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "vmstats.h"
22 #include "log.h"
23
24 #ifdef _AIX
25
26 #ifndef XINTFRAC /* defined in IBM AIX 7.1 libperfstat.h, not defined in AIX 6.1 */
27 #include <sys/systemcfg.h>
28 #define XINTFRAC ((double)_system_configuration.Xint / _system_configuration.Xfrac)
29 /* Example of XINTFRAC = 125.000000 / 64.000000 = 1.953125. Apparently XINTFRAC is a period (in nanoseconds) */
30 /* of CPU ticks on a machine. For example, 1.953125 could mean there is 1.953125 nanoseconds between ticks */
31 /* and number of ticks in second is 1.0 / (1.953125 * 10^-9) = 512000000. So, tick frequency is 512 MHz. */
32 #endif
33
34 static int last_clock = 0;
35 /* --- kthr --- */
36 static zbx_uint64_t last_runque = 0; /* length of the run queue (processes ready) */
37 static zbx_uint64_t last_swpque = 0; /* length of the swap queue (processes waiting to be paged in) */
38 /* --- page --- */
39 static zbx_uint64_t last_pgins = 0; /* number of pages paged in */
40 static zbx_uint64_t last_pgouts = 0; /* number of pages paged out */
41 static zbx_uint64_t last_pgspins = 0; /* number of page ins from paging space */
42 static zbx_uint64_t last_pgspouts = 0; /* number of page outs from paging space */
43 static zbx_uint64_t last_cycles = 0; /* number of page replacement cycles */
44 static zbx_uint64_t last_scans = 0; /* number of page scans by clock */
45 /* -- faults -- */
46 static zbx_uint64_t last_devintrs = 0; /* number of device interrupts */
47 static zbx_uint64_t last_syscall = 0; /* number of system calls executed */
48 static zbx_uint64_t last_pswitch = 0; /* number of process switches (change in currently running */
49 /* process) */
50 /* --- cpu ---- */
51 /* Raw numbers of ticks are readings from forward-ticking counters. */
52 /* Only difference between 2 readings is meaningful. */
53 static zbx_uint64_t last_puser = 0; /* raw number of physical processor ticks in user mode */
54 static zbx_uint64_t last_psys = 0; /* raw number of physical processor ticks in system mode */
55 static zbx_uint64_t last_pidle = 0; /* raw number of physical processor ticks idle */
56 static zbx_uint64_t last_pwait = 0; /* raw number of physical processor ticks waiting for I/O */
57 static zbx_uint64_t last_user = 0; /* raw total number of clock ticks spent in user mode */
58 static zbx_uint64_t last_sys = 0; /* raw total number of clock ticks spent in system mode */
59 static zbx_uint64_t last_idle = 0; /* raw total number of clock ticks spent idle */
60 static zbx_uint64_t last_wait = 0; /* raw total number of clock ticks spent waiting for I/O */
61 static zbx_uint64_t last_timebase_last = 0; /* most recent processor time base timestamp */
62 static zbx_uint64_t last_pool_idle_time = 0; /* number of clock ticks a processor in the shared pool was */
63 /* idle */
64 static zbx_uint64_t last_idle_donated_purr = 0; /* number of idle cycles donated by a dedicated partition */
65 /* enabled for donation */
66 static zbx_uint64_t last_busy_donated_purr = 0; /* number of busy cycles donated by a dedicated partition */
67 /* enabled for donation */
68 static zbx_uint64_t last_idle_stolen_purr = 0; /* number of idle cycles stolen by the hypervisor from */
69 /* a dedicated partition */
70 static zbx_uint64_t last_busy_stolen_purr = 0; /* number of busy cycles stolen by the hypervisor from */
71 /* a dedicated partition */
72 /* --- disk --- */
73 static zbx_uint64_t last_xfers = 0; /* total number of transfers to/from disk */
74 static zbx_uint64_t last_wblks = 0; /* 512 bytes blocks written to all disks */
75 static zbx_uint64_t last_rblks = 0; /* 512 bytes blocks read from all disks */
76
77 /******************************************************************************
78 * *
79 * Function: update_vmstat *
80 * *
81 * Purpose: update vmstat values at most once per second *
82 * *
83 * Parameters: vmstat - a structure containing vmstat data *
84 * *
85 * Comments: on first iteration only save last data, on second - set vmstat *
86 * data and indicate that it is available *
87 * *
88 ******************************************************************************/
update_vmstat(ZBX_VMSTAT_DATA * vmstat)89 static void update_vmstat(ZBX_VMSTAT_DATA *vmstat)
90 {
91 #if defined(HAVE_LIBPERFSTAT)
92 int now;
93 zbx_uint64_t dlcpu_us, dlcpu_sy, dlcpu_id, dlcpu_wa, lcputime;
94 perfstat_memory_total_t memstats;
95 perfstat_cpu_total_t cpustats;
96 perfstat_disk_total_t diskstats;
97 #ifdef _AIXVERSION_530
98 zbx_uint64_t dpcpu_us, dpcpu_sy, dpcpu_id, dpcpu_wa, pcputime, dtimebase;
99 zbx_uint64_t delta_purr, entitled_purr, unused_purr, r1, r2;
100 perfstat_partition_total_t lparstats;
101 #ifdef HAVE_AIXOSLEVEL_530006
102 zbx_uint64_t didle_donated_purr, dbusy_donated_purr, didle_stolen_purr, dbusy_stolen_purr;
103 #endif /* HAVE_AIXOSLEVEL_530006 */
104 #endif /* _AIXVERSION_530 */
105
106 now = (int)time(NULL);
107
108 /* Retrieve metrics from AIX libperfstat APIs.
109 * Upon successful completion, the number of structures filled is returned.
110 * If unsuccessful, a value of -1 is returned and the errno global variable is set. */
111 #ifdef _AIXVERSION_530
112 if (-1 == perfstat_partition_total(NULL, &lparstats, sizeof(lparstats), 1))
113 {
114 zabbix_log(LOG_LEVEL_DEBUG, "perfstat_partition_total: %s", zbx_strerror(errno));
115 return;
116 }
117 #endif
118
119 if (-1 == perfstat_cpu_total(NULL, &cpustats, sizeof(cpustats), 1))
120 {
121 zabbix_log(LOG_LEVEL_DEBUG, "perfstat_cpu_total: %s", zbx_strerror(errno));
122 return;
123 }
124
125 if (-1 == perfstat_memory_total(NULL, &memstats, sizeof(memstats), 1))
126 {
127 zabbix_log(LOG_LEVEL_DEBUG, "perfstat_memory_total: %s", zbx_strerror(errno));
128 return;
129 }
130
131 if (-1 == perfstat_disk_total(NULL, &diskstats, sizeof(diskstats), 1))
132 {
133 zabbix_log(LOG_LEVEL_DEBUG, "perfstat_disk_total: %s", zbx_strerror(errno));
134 return;
135 }
136
137 /* set static vmstat values on first iteration, dynamic on next iterations (at most once per second) */
138 if (0 == last_clock)
139 {
140 #ifdef _AIXVERSION_530
141 vmstat->shared_enabled = (unsigned char)lparstats.type.b.shared_enabled;
142 vmstat->pool_util_authority = (unsigned char)lparstats.type.b.pool_util_authority;
143 #endif
144 #ifdef HAVE_AIXOSLEVEL_520004
145 vmstat->aix52stats = 1;
146 #endif
147 }
148 else if (now > last_clock)
149 {
150 /* --- kthr --- */
151 vmstat->kthr_r = (double)(cpustats.runque - last_runque) / (now - last_clock);
152 vmstat->kthr_b = (double)(cpustats.swpque - last_swpque) / (now - last_clock);
153 /* --- page --- */
154 vmstat->fi = (double)(memstats.pgins - last_pgins) / (now - last_clock);
155 vmstat->fo = (double)(memstats.pgouts - last_pgouts) / (now - last_clock);
156 vmstat->pi = (double)(memstats.pgspins - last_pgspins) / (now - last_clock);
157 vmstat->po = (double)(memstats.pgspouts - last_pgspouts) / (now - last_clock);
158 vmstat->fr = (double)(memstats.cycles - last_cycles) / (now - last_clock);
159 vmstat->sr = (double)(memstats.scans - last_scans) / (now - last_clock);
160 /* -- faults -- */
161 vmstat->in = (double)(cpustats.devintrs - last_devintrs) / (now - last_clock);
162 vmstat->sy = (double)(cpustats.syscall - last_syscall) / (now - last_clock);
163 vmstat->cs = (double)(cpustats.pswitch - last_pswitch) / (now - last_clock);
164
165 #ifdef _AIXVERSION_530
166 /* number of CPU ticks since the last measurement by mode */
167 dpcpu_us = lparstats.puser - last_puser;
168 dpcpu_sy = lparstats.psys - last_psys;
169 dpcpu_id = lparstats.pidle - last_pidle;
170 dpcpu_wa = lparstats.pwait - last_pwait;
171
172 /* total number of CPU ticks since the last measurement */
173 delta_purr = dpcpu_us + dpcpu_sy + dpcpu_id + dpcpu_wa;
174 pcputime = delta_purr;
175 #endif /* _AIXVERSION_530 */
176 dlcpu_us = cpustats.user - last_user;
177 dlcpu_sy = cpustats.sys - last_sys;
178 dlcpu_id = cpustats.idle - last_idle;
179 dlcpu_wa = cpustats.wait - last_wait;
180
181 lcputime = dlcpu_us + dlcpu_sy + dlcpu_id + dlcpu_wa;
182 #ifdef _AIXVERSION_530
183 /* Distribute the donated and stolen purr to the existing purr buckets in case if donation is enabled. */
184 #ifdef HAVE_AIXOSLEVEL_530006
185 if (lparstats.type.b.donate_enabled)
186 {
187 didle_donated_purr = lparstats.idle_donated_purr - last_idle_donated_purr;
188 dbusy_donated_purr = lparstats.busy_donated_purr - last_busy_donated_purr;
189
190 didle_stolen_purr = lparstats.idle_stolen_purr - last_idle_stolen_purr;
191 dbusy_stolen_purr = lparstats.busy_stolen_purr - last_busy_stolen_purr;
192
193 if (0 != dlcpu_id + dlcpu_wa)
194 {
195 r1 = dlcpu_id / (dlcpu_id + dlcpu_wa);
196 r2 = dlcpu_wa / (dlcpu_id + dlcpu_wa);
197 }
198 else
199 r1 = r2 = 0;
200
201 dpcpu_us += didle_donated_purr * r1 + didle_stolen_purr * r1;
202 dpcpu_wa += didle_donated_purr * r2 + didle_stolen_purr * r2;
203 dpcpu_sy += dbusy_donated_purr + dbusy_stolen_purr;
204
205 delta_purr += didle_donated_purr + dbusy_donated_purr + didle_stolen_purr + dbusy_stolen_purr;
206 pcputime = delta_purr;
207 }
208 #endif /* HAVE_AIXOSLEVEL_530006 */
209
210 /* number of physical processor tics between current and previous measurement */
211 dtimebase = lparstats.timebase_last - last_timebase_last;
212
213 /* 'perfstat_partition_total_t' element 'entitled_proc_capacity' is "number of processor units this */
214 /* partition is entitled to receive". It is expressed as multiplied by 100 and rounded to integer, */
215 /* therefore we divide it by 100 and convert to floating point number to get its real value, as */
216 /* shown by 'lparstat' command. */
217 vmstat->ent = lparstats.entitled_proc_capacity / 100.0;
218
219 if (lparstats.type.b.shared_enabled)
220 {
221 entitled_purr = dtimebase * vmstat->ent;
222 if (entitled_purr < delta_purr)
223 {
224 /* when above entitlement, use consumption in percentages */
225 entitled_purr = delta_purr;
226 }
227 unused_purr = entitled_purr - delta_purr;
228
229 /* distribute unused purr in wait and idle proportionally to logical wait and idle */
230 if (0 != dlcpu_wa + dlcpu_id)
231 {
232 dpcpu_wa += unused_purr * ((double)dlcpu_wa / (dlcpu_wa + dlcpu_id));
233 dpcpu_id += unused_purr * ((double)dlcpu_id / (dlcpu_wa + dlcpu_id));
234 }
235
236 pcputime = entitled_purr;
237 }
238
239 /* Physical Processor Utilization */
240 vmstat->cpu_us = dpcpu_us * 100.0 / pcputime;
241 vmstat->cpu_sy = dpcpu_sy * 100.0 / pcputime;
242 vmstat->cpu_id = dpcpu_id * 100.0 / pcputime;
243 vmstat->cpu_wa = dpcpu_wa * 100.0 / pcputime;
244
245 /* Physical Processor Consumed */
246 /* Interesting values only for "shared" LPARs. */
247 /* For "dedicated" LPARs expect approximately the same value as assigned to the LPAR through HMC. */
248 vmstat->cpu_pc = (double)delta_purr / dtimebase;
249
250 if (lparstats.type.b.shared_enabled)
251 {
252 /* Percentage of Entitlement Consumed */
253 vmstat->cpu_ec = (vmstat->cpu_pc / vmstat->ent) * 100.0;
254
255 /* Logical Processor Utilization */
256 vmstat->cpu_lbusy = (dlcpu_us + dlcpu_sy) * 100.0 / lcputime;
257
258 if (lparstats.type.b.pool_util_authority)
259 {
260 /* Available Pool Processor (app) */
261 vmstat->cpu_app = (lparstats.pool_idle_time - last_pool_idle_time) /
262 (XINTFRAC * dtimebase);
263 }
264 }
265 else
266 vmstat->cpu_ec = 100.0; /* trivial value for LPAR type "dedicated" */
267
268 #else /* not _AIXVERSION_530 */
269
270 /* Physical Processor Utilization */
271 vmstat->cpu_us = dlcpu_us * 100.0 / lcputime;
272 vmstat->cpu_sy = dlcpu_sy * 100.0 / lcputime;
273 vmstat->cpu_id = dlcpu_id * 100.0 / lcputime;
274 vmstat->cpu_wa = dlcpu_wa * 100.0 / lcputime;
275
276 #endif /* _AIXVERSION_530 */
277 /* --- disk --- */
278 vmstat->disk_bps = 512 * ((diskstats.wblks - last_wblks) + (diskstats.rblks - last_rblks)) / (now - last_clock);
279 vmstat->disk_tps = (double)(diskstats.xfers - last_xfers) / (now - last_clock);
280
281 /* -- memory -- */
282 #ifdef HAVE_AIXOSLEVEL_520004
283 vmstat->mem_avm = (zbx_uint64_t)memstats.virt_active; /* Active virtual pages. Virtual pages are considered
284 active if they have been accessed */
285 #endif
286 vmstat->mem_fre = (zbx_uint64_t)memstats.real_free; /* free real memory (in 4KB pages) */
287
288 /* indicate that vmstat data is available */
289 vmstat->data_available = 1;
290 }
291
292 /* saving last values */
293 last_clock = now;
294 /* --- kthr -- */
295 last_runque = (zbx_uint64_t)cpustats.runque;
296 last_swpque = (zbx_uint64_t)cpustats.swpque;
297 /* --- page --- */
298 last_pgins = (zbx_uint64_t)memstats.pgins;
299 last_pgouts = (zbx_uint64_t)memstats.pgouts;
300 last_pgspins = (zbx_uint64_t)memstats.pgspins;
301 last_pgspouts = (zbx_uint64_t)memstats.pgspouts;
302 last_cycles = (zbx_uint64_t)memstats.cycles;
303 last_scans = (zbx_uint64_t)memstats.scans;
304 /* -- faults -- */
305 last_devintrs = (zbx_uint64_t)cpustats.devintrs;
306 last_syscall = (zbx_uint64_t)cpustats.syscall;
307 last_pswitch = (zbx_uint64_t)cpustats.pswitch;
308 /* --- cpu ---- */
309 #ifdef _AIXVERSION_530
310 last_puser = (zbx_uint64_t)lparstats.puser;
311 last_psys = (zbx_uint64_t)lparstats.psys;
312 last_pidle = (zbx_uint64_t)lparstats.pidle;
313 last_pwait = (zbx_uint64_t)lparstats.pwait;
314
315 last_timebase_last = (zbx_uint64_t)lparstats.timebase_last;
316
317 last_pool_idle_time = (zbx_uint64_t)lparstats.pool_idle_time;
318
319 #ifdef HAVE_AIXOSLEVEL_530006
320 last_idle_donated_purr = (zbx_uint64_t)lparstats.idle_donated_purr;
321 last_busy_donated_purr = (zbx_uint64_t)lparstats.busy_donated_purr;
322
323 last_idle_stolen_purr = (zbx_uint64_t)lparstats.idle_stolen_purr;
324 last_busy_stolen_purr = (zbx_uint64_t)lparstats.busy_stolen_purr;
325 #endif /* HAVE_AIXOSLEVEL_530006 */
326 #endif /* _AIXVERSION_530 */
327 last_user = (zbx_uint64_t)cpustats.user;
328 last_sys = (zbx_uint64_t)cpustats.sys;
329 last_idle = (zbx_uint64_t)cpustats.idle;
330 last_wait = (zbx_uint64_t)cpustats.wait;
331
332 last_xfers = (zbx_uint64_t)diskstats.xfers;
333 last_wblks = (zbx_uint64_t)diskstats.wblks;
334 last_rblks = (zbx_uint64_t)diskstats.rblks;
335 #endif /* HAVE_LIBPERFSTAT */
336 }
337
collect_vmstat_data(ZBX_VMSTAT_DATA * vmstat)338 void collect_vmstat_data(ZBX_VMSTAT_DATA *vmstat)
339 {
340 update_vmstat(vmstat);
341 }
342
343 #endif /* _AIX */
344