lpar plugin: Move / add comments.
[collectd.git] / src / lpar.c
1 /**
2  * collectd - src/lpar.c
3  * Copyright (C) 2010  AurĂ©lien Reynaud
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   AurĂ©lien Reynaud <collectd at wattapower.net>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25
26 #include <sys/protosw.h>
27 #include <libperfstat.h>
28 #include <sys/utsname.h>
29
30 /* XINTFRAC was defined in libperfstat.h somewhere between AIX 5.3 and 6.1 */
31 #ifndef XINTFRAC
32 # include <sys/systemcfg.h>
33 # define XINTFRAC ((double)(_system_configuration.Xint) / \
34                    (double)(_system_configuration.Xfrac))
35 #endif
36
37 static const char *config_keys[] =
38 {
39   "CpuPoolStats",
40   "ReportBySerial"
41 };
42 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
43
44 static _Bool pool_stats = 0;
45 static _Bool report_by_serial = 0;
46 static _Bool donate_flag = 0;
47 static char serial[SYS_NMLN];
48
49 static u_longlong_t time_old;
50 static u_longlong_t user_old,
51                     syst_old,
52                     idle_old,
53                     wait_old;
54 static u_longlong_t pool_busy_time_old,
55                     pool_max_time_old;
56 static u_longlong_t idle_donated_old,
57                     busy_donated_old,
58                     busy_stolen_old,
59                     idle_stolen_old;
60
61
62 static void save_last_values (perfstat_partition_total_t *lparstats)
63 {
64         time_old = lparstats->timebase_last;
65
66         user_old = lparstats->puser;
67         syst_old = lparstats->psys;
68         idle_old = lparstats->pidle;
69         wait_old = lparstats->pwait;
70
71         if (donate_flag)
72         {
73                 idle_donated_old = lparstats->idle_donated_purr;
74                 busy_donated_old = lparstats->busy_donated_purr;
75                 busy_stolen_old  = lparstats->busy_stolen_purr;
76                 idle_stolen_old  = lparstats->idle_stolen_purr;
77         }
78
79         if (pool_stats)
80         {
81                 pool_busy_time_old = lparstats->pool_busy_time;
82                 pool_max_time_old  = lparstats->pool_max_time;
83         }
84 } /* void save_last_values */
85
86 static int lpar_config (const char *key, const char *value)
87 {
88         if (strcasecmp ("CpuPoolStats", key) == 0)
89         {
90                 if (IS_TRUE (value))
91                         pool_stats = 1;
92                 else
93                         pool_stats = 0;
94         }
95         else if (strcasecmp ("ReportBySerial", key) == 0)
96         {
97                 if (IS_TRUE (value))
98                         report_by_serial = 1;
99                 else
100                         report_by_serial = 0;
101         }
102         else
103         {
104                 return (-1);
105         }
106
107         return (0);
108 } /* int lpar_config */
109
110 static int lpar_init (void)
111 {
112         perfstat_partition_total_t lparstats;
113         int status;
114
115         /* Retrieve the initial metrics. Returns the number of structures filled. */
116         status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */
117                         &lparstats, sizeof (perfstat_partition_total_t),
118                         /* number = */ 1 /* (must be 1) */);
119         if (status != 1)
120         {
121                 char errbuf[1024];
122                 ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)",
123                                 sstrerror (errno, errbuf, sizeof (errbuf)),
124                                 status);
125                 return (-1);
126         }
127
128         if (!lparstats.type.b.shared_enabled && lparstats.type.b.donate_enabled)
129         {
130                 donate_flag = 1;
131         }
132
133         if (pool_stats && !lparstats.type.b.pool_util_authority)
134         {
135                 WARNING ("lpar plugin: This partition does not have pool authority. "
136                                 "Disabling CPU pool statistics collection.");
137                 pool_stats = 0;
138         }
139
140         /* Save the initial data */
141         save_last_values (&lparstats);
142
143         return (0);
144 } /* int lpar_init */
145
146 static void lpar_submit (const char *type_instance, double value)
147 {
148         value_t values[1];
149         value_list_t vl = VALUE_LIST_INIT;
150
151         values[0].gauge = (gauge_t)value;
152
153         vl.values = values;
154         vl.values_len = 1;
155         if (report_by_serial)
156         {
157                 sstrncpy (vl.host, serial, sizeof (vl.host));
158                 sstrncpy (vl.plugin_instance, hostname_g, sizeof (vl.plugin));
159         }
160         else
161         {
162                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
163         }
164         sstrncpy (vl.plugin, "lpar", sizeof (vl.plugin));
165         sstrncpy (vl.type, "vcpu", sizeof (vl.type));
166         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
167
168         plugin_dispatch_values (&vl);
169 } /* void lpar_submit */
170
171 static int lpar_read (void)
172 {
173         perfstat_partition_total_t lparstats;
174         int status;
175         struct utsname name;
176         u_longlong_t ticks;
177         u_longlong_t user_ticks, syst_ticks, wait_ticks, idle_ticks;
178         u_longlong_t consumed_ticks;
179         double entitled_proc_capacity;
180
181         /* An LPAR has the same serial number as the physical system it is currently
182            running on. It is a convenient way of tracking LPARs as they are moved
183            from chassis to chassis through Live Partition Mobility (LPM). */
184         if (uname (&name) != 0)
185         {
186                 ERROR ("lpar plugin: uname failed.");
187                 return (-1);
188         }
189         sstrncpy (serial, name.machine, sizeof (serial));
190
191         /* Retrieve the current metrics. Returns the number of structures filled. */
192         status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */
193                         &lparstats, sizeof (perfstat_partition_total_t),
194                         /* number = */ 1 /* (must be 1) */);
195         if (status != 1)
196         {
197                 char errbuf[1024];
198                 ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)",
199                                 sstrerror (errno, errbuf, sizeof (errbuf)),
200                                 status);
201                 return (-1);
202         }
203
204         /* Number of ticks since we last run. */
205         ticks = lparstats.timebase_last - time_old;
206         if (ticks == 0)
207         {
208                 /* The stats have not been updated. Return now to avoid dividing by zero */
209                 return (0);
210         }
211
212         /*
213          * On a shared partition, we're "entitled" to a certain amount of
214          * processing power, for example 250/100 of a physical CPU. Processing
215          * capacity not used by the partition may be assigned to a different
216          * partition by the hypervisor, so "idle" is hopefully a very small
217          * number.
218          */
219
220         /* entitled_proc_capacity is in 1/100th of a CPU */
221         entitled_proc_capacity = 0.01 * ((double) lparstats.entitled_proc_capacity);
222         lpar_submit ("entitled", entitled_proc_capacity);
223
224         /* The number of ticks actually spent in the various states */
225         user_ticks = lparstats.puser - user_old;
226         syst_ticks = lparstats.psys  - syst_old;
227         wait_ticks = lparstats.pwait - wait_old;
228         idle_ticks = lparstats.pidle - idle_old;
229         consumed_ticks = user_ticks + syst_ticks + wait_ticks + idle_ticks;
230
231         lpar_submit ("user", (double) user_ticks / (double) ticks);
232         lpar_submit ("sys",  (double) syst_ticks / (double) ticks);
233         lpar_submit ("wait", (double) wait_ticks / (double) ticks);
234         lpar_submit ("idle", (double) idle_ticks / (double) ticks);
235
236         if (donate_flag)
237         {
238                 u_longlong_t idle_donated_ticks, busy_donated_ticks;
239                 u_longlong_t idle_stolen_ticks, busy_stolen_ticks;
240
241                 idle_donated_ticks = lparstats.idle_donated_purr - idle_donated_old;
242                 busy_donated_ticks = lparstats.busy_donated_purr - busy_donated_old;
243                 idle_stolen_ticks  = lparstats.idle_stolen_purr  - idle_stolen_old;
244                 busy_stolen_ticks  = lparstats.busy_stolen_purr  - busy_stolen_old;
245
246                 /* FYI:  PURR == Processor Utilization of Resources Register
247                  *      SPURR == Scaled PURR */
248                 /* donated => ticks given to another partition
249                  * stolen  => ticks received from another partition */
250                 lpar_submit ("idle_donated", (double) idle_donated_ticks / (double) ticks);
251                 lpar_submit ("busy_donated", (double) busy_donated_ticks / (double) ticks);
252                 lpar_submit ("idle_stolen",  (double) idle_stolen_ticks  / (double) ticks);
253                 lpar_submit ("busy_stolen",  (double) busy_stolen_ticks  / (double) ticks);
254
255                 /* Donated ticks will be accounted for as stolen ticks in other LPARs */
256                 consumed_ticks += idle_stolen_ticks + busy_stolen_ticks;
257         }
258
259         lpar_submit ("consumed", (double) consumed_ticks / (double) ticks);
260
261         if (pool_stats)
262         {
263                 char typinst[DATA_MAX_NAME_LEN];
264                 u_longlong_t pool_busy_ns, pool_max_ns;
265
266                 pool_busy_ns = lparstats.pool_busy_time - pool_busy_time_old;
267                 pool_max_ns  = lparstats.pool_max_time  - pool_max_time_old;
268
269                 /* Pool stats are in CPU x ns */
270                 ssnprintf (typinst, sizeof (typinst), "pool-%X-busy", lparstats.pool_id);
271                 lpar_submit (typinst, (double) pool_busy_ns / XINTFRAC / (double) ticks);
272
273                 ssnprintf (typinst, sizeof (typinst), "pool-%X-total", lparstats.pool_id);
274                 lpar_submit (typinst, (double) pool_max_ns / XINTFRAC / (double) ticks);
275         }
276
277         save_last_values (&lparstats);
278
279         return (0);
280 } /* int lpar_read */
281
282 void module_register (void)
283 {
284         plugin_register_config ("lpar", lpar_config,
285                                 config_keys, config_keys_num);
286         plugin_register_init ("lpar", lpar_init);
287         plugin_register_read ("lpar", lpar_read);
288 } /* void module_register */
289
290 /* vim: set sw=8 noet : */
291