lpar plugin: Fix error handling of the "perfstat_partition_total" function.
[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
26 #include <sys/protosw.h>
27 #include <libperfstat.h>
28 #include <sys/utsname.h>
29
30 static const char *config_keys[] =
31 {
32   "CpuPoolStats",
33   "ReportBySerial"
34 };
35 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
36
37 static _Bool pool_stats = 0;
38 static _Bool report_by_serial = 0;
39
40 static int lpar_config (const char *key, const char *value)
41 {
42         if (strcasecmp ("CpuPoolStats", key) == 0)
43         {
44                 if (IS_TRUE (value))
45                         pool_stats = 1;
46                 else
47                         pool_stats = 0;
48         }
49         else if (strcasecmp ("ReportBySerial", key) == 0)
50         {
51                 if (IS_TRUE (value))
52                         report_by_serial = 1;
53                 else
54                         report_by_serial = 0;
55         }
56         else
57         {
58                 return (-1);
59         }
60
61         return (0);
62 } /* int lpar_config */
63
64 static void lpar_submit (const char *type_instance, counter_t value)
65 {
66         value_t values[1];
67         value_list_t vl = VALUE_LIST_INIT;
68
69         /* Although it appears as a double, value is really a (scaled) counter,
70            expressed in CPU x seconds. At high collection rates (< 1 min), its
71            integer part is very small and the resulting graphs get blocky. We regain
72            some precision by applying a x100 factor before casting it to a counter,
73            turning the final value into CPU units instead of CPUs. */
74         values[0].counter = value;
75
76         vl.values = values;
77         vl.values_len = 1;
78
79         /* An LPAR has the same serial number as the physical system it is currently
80            running on. It is a convenient way of tracking LPARs as they are moved
81            from chassis to chassis through Live Partition Mobility (LPM). */
82         if (report_by_serial)
83         {
84                 struct utsname name;
85                 if (uname (&name) != 0)
86                 {
87                         ERROR ("lpar plugin: uname failed.");
88                         return;
89                 }
90                 sstrncpy (vl.host, name.machine, sizeof (vl.host));
91                 sstrncpy (vl.plugin_instance, hostname_g, sizeof (vl.plugin));
92         }
93         else
94         {
95                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
96         }
97         sstrncpy (vl.plugin, "lpar", sizeof (vl.plugin));
98         sstrncpy (vl.type, "cpu", sizeof (vl.type));
99         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
100
101         plugin_dispatch_values (&vl);
102 }
103
104 static int lpar_read_shared_partition (const perfstat_partition_total_t *data)
105 {
106         static counter_t time_old;
107         static counter_t user_old;
108         static counter_t syst_old;
109         static counter_t wait_old;
110         static counter_t idle_old;
111         static counter_t unav_old;
112
113         counter_t user = (counter_t) data->puser;
114         counter_t syst = (counter_t) data->psys;
115         counter_t wait = (counter_t) data->pwait;
116         counter_t idle = (counter_t) data->pidle;
117         counter_t unav = 0;
118
119         /*
120          * On a shared partition, we're "entitled" to a certain amount of
121          * processing power, for example 250/100 of a physical CPU. Processing
122          * capacity not used by the partition may be assigned to a different
123          * partition by the hypervisor, so "idle" is hopefully a very small
124          * number.
125          *
126          * We calculate the amount of ticks assigned to a different partition
127          * from the number of ticks we're entitled to and the number of ticks
128          * we used up.
129          */
130         if (time_old != 0)
131         {
132                 counter_t time_diff;
133                 counter_t entitled_ticks;
134                 counter_t consumed_ticks;
135                 counter_t user_diff;
136                 counter_t syst_diff;
137                 counter_t wait_diff;
138                 counter_t idle_diff;
139                 counter_t unav_diff;
140
141                 double entitled_pool_capacity;
142
143                 /* Number of ticks since we last run. */
144                 time_diff = ((counter_t) data->timebase_last) - time_old;
145
146                 /* entitled_pool_capacity is in 1/100th of a CPU */
147                 entitled_pool_capacity = 0.01 * ((double) data->entitled_pool_capacity);
148
149                 /* The number of ticks this partition would have been entitled to. */
150                 entitled_ticks = (counter_t) ((entitled_pool_capacity * ((double) time_diff)) + .5);
151
152                 /* The number of ticks actually spent in the various states */
153                 user_diff = user - user_old;
154                 syst_diff = syst - syst_old;
155                 wait_diff = wait - wait_old;
156                 idle_diff = idle - idle_old;
157                 consumed_ticks = user_diff + syst_diff + wait_diff + idle_diff;
158
159                 /* "uncapped" partitions are allowed to consume more ticks than
160                  * they are entitled to. */
161                 if (entitled_ticks >= consumed_ticks)
162                         unav_diff = entitled_ticks - consumed_ticks;
163                 else
164                         unav_diff = 0;
165                 unav = unav_old + unav_diff;
166
167                 lpar_submit ("user", user);
168                 lpar_submit ("system", syst);
169                 lpar_submit ("wait", wait);
170                 lpar_submit ("idle", idle);
171                 lpar_submit ("unavailable", unav);
172         }
173
174         time_old = (counter_t) data->timebase_last;
175         user_old = user;
176         syst_old = syst;
177         wait_old = wait;
178         idle_old = idle;
179         unav_old = unav;
180
181         return (0);
182 } /* int lpar_read_shared_partition */
183
184 static int lpar_read_dedicated_partition (const perfstat_partition_total_t *data)
185 {
186         lpar_submit ("user",   (counter_t) data->puser);
187         lpar_submit ("system", (counter_t) data->psys);
188         lpar_submit ("wait",   (counter_t) data->pwait);
189         lpar_submit ("idle",   (counter_t) data->pidle);
190
191         if (data->type.b.donate_enabled)
192         {
193                 /* FYI:  PURR == Processor Utilization of Resources Register
194                  *      SPURR == Scaled PURR */
195                 lpar_submit ("idle_donated", (counter_t) data->idle_donated_purr);
196                 lpar_submit ("busy_donated", (counter_t) data->busy_donated_purr);
197                 lpar_submit ("idle_stolen",  (counter_t) data->idle_stolen_purr);
198                 lpar_submit ("busy_stolen",  (counter_t) data->busy_stolen_purr);
199         }
200
201         return (0);
202 } /* int lpar_read_dedicated_partition */
203
204 static int lpar_read (void)
205 {
206         perfstat_partition_total_t lparstats;
207         int status;
208
209         /* Retrieve the current metrics. Returns the number of structures filled. */
210         status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */
211                         &lparstats, sizeof (perfstat_partition_total_t),
212                         /* number = */ 1 /* (must be 1) */);
213         if (status != 1)
214         {
215                 char errbuf[1024];
216                 ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)",
217                                 sstrerror (errno, errbuf, sizeof (errbuf)),
218                                 status);
219                 return (-1);
220         }
221
222         if (lparstats.type.b.shared_enabled)
223                 lpar_read_shared_partition (&lparstats);
224         else /* if (!shared_enabled) */
225                 lpar_read_dedicated_partition (&lparstats);
226
227         if (pool_stats && !lparstats.type.b.pool_util_authority)
228         {
229                 WARNING ("lpar plugin: This partition does not have pool authority. "
230                                 "Disabling CPU pool statistics collection.");
231                 pool_stats = 0;
232         }
233
234         if (pool_stats)
235         {
236                 char typinst[DATA_MAX_NAME_LEN];
237
238                 /* Pool stats are in CPU x ns */
239                 ssnprintf (typinst, sizeof(typinst), "pool-%X-busy", lparstats.pool_id);
240                 lpar_submit (typinst, (double)lparstats.pool_busy_time / 1000000000.0);
241
242                 ssnprintf (typinst, sizeof(typinst), "pool-%X-total", lparstats.pool_id);
243                 lpar_submit (typinst, (double)lparstats.pool_max_time / 1000000000.0);
244         }
245
246         return (0);
247 } /* int lpar_read */
248
249 void module_register (void)
250 {
251         plugin_register_config ("lpar", lpar_config,
252                                 config_keys, config_keys_num);
253         plugin_register_read ("lpar", lpar_read);
254 } /* void module_register */
255
256 /* vim: set sw=8 noet : */
257