lpar plugin: new attempt
[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  *   Aurelien Reynaud <collectd at wattapower.net>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include <sys/protosw.h>
26 #include <libperfstat.h>
27 #include <sys/utsname.h>
28
29 #ifndef XINTFRAC
30 # include <sys/systemcfg.h>
31 # define XINTFRAC ((double)(_system_configuration.Xint) / \
32                    (double)(_system_configuration.Xfrac))
33 #endif
34 #define HTIC2SEC(x)     ((double)x * XINTFRAC / 1000000000.0)
35
36 /* Max length of the type instance string */
37 #define TYPE_INST_LEN (sizeof("pool--total") + 2*sizeof(int) + 1)
38
39 static const char *config_keys[] =
40 {
41   "CpuPoolStats",
42   "ReportBySerial"
43 };
44 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
45 static int pool_stats = 0,
46            report_by_serial = 0;
47
48 static u_longlong_t last_time_base;
49 static u_longlong_t ent_counter;
50 static int donate_flag = 0;
51
52
53 static int lpar_config (const char *key, const char *value)
54 {
55         if (strcasecmp ("CpuPoolStats", key) == 0)
56         {
57                 if (IS_TRUE (value))
58                         pool_stats = 1;
59         }
60         else if (strcasecmp ("ReportBySerial", key) == 0)
61         {
62                 if (IS_TRUE (value))
63                         report_by_serial = 1;
64         }
65         else
66         {
67                 return (-1);
68         }
69
70         return (0);
71 } /* int lpar_config */
72
73 static int lpar_init (void)
74 {
75         perfstat_partition_total_t lparstats;
76
77         /* Retrieve the initial metrics */
78         if (!perfstat_partition_total (NULL, &lparstats,
79                                        sizeof (perfstat_partition_total_t), 1))
80         {
81                 ERROR ("lpar plugin: perfstat_partition_total failed.");
82                 return (-1);
83         }
84
85         if (!lparstats.type.b.shared_enabled && lparstats.type.b.donate_enabled)
86         {
87                 donate_flag = 1;
88         }
89
90         if (pool_stats && !lparstats.type.b.pool_util_authority)
91         {
92                 WARNING ("lpar plugin: this system does not have pool authority. "
93                         "Disabling CPU pool statistics collection.");
94                 pool_stats = 0;
95         }
96
97         /* Initialize the fake counter for entitled capacity */
98         last_time_base = lparstats.timebase_last;
99         ent_counter = 0;
100
101         return (0);
102 } /* int lpar_init */
103
104 static void lpar_submit (const char *type_instance, double value)
105 {
106         value_t values[1];
107         value_list_t vl = VALUE_LIST_INIT;
108
109         /* Although it appears as a double, value is really a (scaled) counter,
110            expressed in CPU x seconds. At high collection rates (< 1 min), its
111            integer part is very small and the resulting graphs get blocky. We regain
112            some precision by applying a x100 factor before casting it to a counter,
113            turning the final value into CPU units instead of CPUs. */
114         values[0].counter = (counter_t)(value * 100.0 + 0.5);
115
116         vl.values = values;
117         vl.values_len = 1;
118
119         /* An LPAR has the same serial number as the physical system it is currently
120            running on. It is a convenient way of tracking LPARs as they are moved
121            from chassis to chassis through Live Partition Mobility (LPM). */
122         if (report_by_serial)
123         {
124                 struct utsname name;
125                 if (uname (&name) != 0)
126                 {
127                         ERROR ("lpar plugin: uname failed.");
128                         return;
129                 }
130                 sstrncpy (vl.host, name.machine, sizeof (vl.host));
131                 sstrncpy (vl.plugin_instance, hostname_g, sizeof (vl.plugin));
132         }
133         else
134         {
135                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
136         }
137         sstrncpy (vl.plugin, "lpar", sizeof (vl.plugin));
138         sstrncpy (vl.type, "cpu", sizeof (vl.type));
139         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
140
141         plugin_dispatch_values (&vl);
142 }
143
144 static int lpar_read (void)
145 {
146         u_longlong_t delta_time_base;
147         perfstat_partition_total_t lparstats;
148
149         /* Retrieve the current metrics */
150         if (!perfstat_partition_total (NULL, &lparstats,
151                                        sizeof (perfstat_partition_total_t), 1))
152         {
153                 ERROR ("lpar plugin: perfstat_partition_total failed.");
154                 return (-1);
155         }
156
157         delta_time_base = lparstats.timebase_last - last_time_base;
158         last_time_base  = lparstats.timebase_last;
159
160         lpar_submit ("user", HTIC2SEC(lparstats.puser));
161         lpar_submit ("sys",  HTIC2SEC(lparstats.psys));
162         lpar_submit ("wait", HTIC2SEC(lparstats.pwait));
163         lpar_submit ("idle", HTIC2SEC(lparstats.pidle));
164         /* Entitled capacity is reported as an absolute value instead of a counter,
165            so we fake one. It's also in CPU units, hence the division by 100 before
166            submission. */
167         ent_counter += lparstats.entitled_proc_capacity * delta_time_base;
168         lpar_submit ("ent",  HTIC2SEC(ent_counter) / 100.0);
169
170         if (donate_flag)
171         {
172                 lpar_submit ("idle_donated", HTIC2SEC(lparstats.idle_donated_purr));
173                 lpar_submit ("busy_donated", HTIC2SEC(lparstats.busy_donated_purr));
174                 lpar_submit ("idle_stolen",  HTIC2SEC(lparstats.idle_stolen_purr));
175                 lpar_submit ("busy_stolen",  HTIC2SEC(lparstats.busy_stolen_purr));
176         }
177
178         if (pool_stats)
179         {
180                 char typinst[TYPE_INST_LEN];
181
182                 /* Pool stats are in CPU x ns */
183                 ssnprintf (typinst, sizeof(typinst), "pool-%X-busy", lparstats.pool_id);
184                 lpar_submit (typinst, (double)lparstats.pool_busy_time / 1000000000.0);
185
186                 ssnprintf (typinst, sizeof(typinst), "pool-%X-total", lparstats.pool_id);
187                 lpar_submit (typinst, (double)lparstats.pool_max_time / 1000000000.0);
188         }
189
190         return (0);
191 } /* int lpar_read */
192
193 void module_register (void)
194 {
195         plugin_register_config ("lpar", lpar_config,
196                                 config_keys, config_keys_num);
197         plugin_register_init ("lpar", lpar_init);
198         plugin_register_read ("lpar", lpar_read);
199 } /* void module_register */
200
201 /* vim: set sw=2 sts=2 ts=8 : */
202