2f40691db61331e46f46768cc09996b4ee323e00
[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                 if (entitled_ticks >= consumed_ticks)
160                         unav_diff = entitled_ticks - consumed_ticks;
161                 else
162                         unav_diff = 0;
163                 unav = unav_old + unav_diff;
164
165                 lpar_submit ("user", user);
166                 lpar_submit ("system", syst);
167                 lpar_submit ("wait", wait);
168                 lpar_submit ("idle", idle);
169                 lpar_submit ("unavailable", unav);
170         }
171
172         time_old = (counter_t) data->timebase_last;
173         user_old = user;
174         syst_old = syst;
175         wait_old = wait;
176         idle_old = idle;
177         unav_old = unav;
178
179         return (0);
180 } /* int lpar_read_shared_partition */
181
182 static int lpar_read_dedicated_partition (const perfstat_partition_total_t *data)
183 {
184         lpar_submit ("user",   (counter_t) data->puser);
185         lpar_submit ("system", (counter_t) data->psys);
186         lpar_submit ("wait",   (counter_t) data->pwait);
187         lpar_submit ("idle",   (counter_t) data->pidle);
188
189         if (data->type.b.donate_enabled)
190         {
191                 lpar_submit ("idle_donated", (counter_t) data->idle_donated_purr);
192                 lpar_submit ("busy_donated", (counter_t) data->busy_donated_purr);
193                 lpar_submit ("idle_stolen",  (counter_t) data->idle_stolen_purr);
194                 lpar_submit ("busy_stolen",  (counter_t) data->busy_stolen_purr);
195         }
196
197         return (0);
198 } /* int lpar_read_dedicated_partition */
199
200 static int lpar_read (void)
201 {
202         perfstat_partition_total_t lparstats;
203
204         /* Retrieve the current metrics */
205         if (!perfstat_partition_total (NULL, &lparstats,
206                                        sizeof (perfstat_partition_total_t), 1))
207         {
208                 ERROR ("lpar plugin: perfstat_partition_total failed.");
209                 return (-1);
210         }
211
212         if (lparstats.type.b.shared_enabled)
213                 lpar_read_shared_partition (&lparstats);
214         else /* if (!shared_enabled) */
215                 lpar_read_dedicated_partition (&lparstats);
216
217         if (pool_stats && !lparstats.type.b.pool_util_authority)
218         {
219                 WARNING ("lpar plugin: This partition does not have pool authority. "
220                                 "Disabling CPU pool statistics collection.");
221                 pool_stats = 0;
222         }
223
224         if (pool_stats)
225         {
226                 char typinst[DATA_MAX_NAME_LEN];
227
228                 /* Pool stats are in CPU x ns */
229                 ssnprintf (typinst, sizeof(typinst), "pool-%X-busy", lparstats.pool_id);
230                 lpar_submit (typinst, (double)lparstats.pool_busy_time / 1000000000.0);
231
232                 ssnprintf (typinst, sizeof(typinst), "pool-%X-total", lparstats.pool_id);
233                 lpar_submit (typinst, (double)lparstats.pool_max_time / 1000000000.0);
234         }
235
236         return (0);
237 } /* int lpar_read */
238
239 void module_register (void)
240 {
241         plugin_register_config ("lpar", lpar_config,
242                                 config_keys, config_keys_num);
243         plugin_register_read ("lpar", lpar_read);
244 } /* void module_register */
245
246 /* vim: set sw=8 noet : */
247