2 * collectd - src/lpar.c
3 * Copyright (C) 2010 Aurélien Reynaud
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.
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.
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
19 * Aurelien Reynaud <collectd at wattapower.net>
26 #include <sys/protosw.h>
27 #include <libperfstat.h>
28 #include <sys/utsname.h>
30 static const char *config_keys[] =
35 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
37 static _Bool pool_stats = 0;
38 static _Bool report_by_serial = 0;
40 static int lpar_config (const char *key, const char *value)
42 if (strcasecmp ("CpuPoolStats", key) == 0)
49 else if (strcasecmp ("ReportBySerial", key) == 0)
62 } /* int lpar_config */
64 static void lpar_submit (const char *type_instance, counter_t value)
67 value_list_t vl = VALUE_LIST_INIT;
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;
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). */
85 if (uname (&name) != 0)
87 ERROR ("lpar plugin: uname failed.");
90 sstrncpy (vl.host, name.machine, sizeof (vl.host));
91 sstrncpy (vl.plugin_instance, hostname_g, sizeof (vl.plugin));
95 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
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));
101 plugin_dispatch_values (&vl);
104 static int lpar_read_shared_partition (const perfstat_partition_total_t *data)
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;
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;
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
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
133 counter_t entitled_ticks;
134 counter_t consumed_ticks;
141 double entitled_pool_capacity;
143 /* Number of ticks since we last run. */
144 time_diff = ((counter_t) data->timebase_last) - time_old;
146 /* entitled_pool_capacity is in 1/100th of a CPU */
147 entitled_pool_capacity = 0.01 * ((double) data->entitled_pool_capacity);
149 /* The number of ticks this partition would have been entitled to. */
150 entitled_ticks = (counter_t) ((entitled_pool_capacity * ((double) time_diff)) + .5);
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;
159 if (entitled_ticks >= consumed_ticks)
160 unav_diff = entitled_ticks - consumed_ticks;
163 unav = unav_old + unav_diff;
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);
172 time_old = (counter_t) data->timebase_last;
180 } /* int lpar_read_shared_partition */
182 static int lpar_read_dedicated_partition (const perfstat_partition_total_t *data)
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);
189 if (data->type.b.donate_enabled)
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);
198 } /* int lpar_read_dedicated_partition */
200 static int lpar_read (void)
202 perfstat_partition_total_t lparstats;
204 /* Retrieve the current metrics */
205 if (!perfstat_partition_total (NULL, &lparstats,
206 sizeof (perfstat_partition_total_t), 1))
208 ERROR ("lpar plugin: perfstat_partition_total failed.");
212 if (lparstats.type.b.shared_enabled)
213 lpar_read_shared_partition (&lparstats);
214 else /* if (!shared_enabled) */
215 lpar_read_dedicated_partition (&lparstats);
217 if (pool_stats && !lparstats.type.b.pool_util_authority)
219 WARNING ("lpar plugin: This partition does not have pool authority. "
220 "Disabling CPU pool statistics collection.");
226 char typinst[DATA_MAX_NAME_LEN];
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);
232 ssnprintf (typinst, sizeof(typinst), "pool-%X-total", lparstats.pool_id);
233 lpar_submit (typinst, (double)lparstats.pool_max_time / 1000000000.0);
237 } /* int lpar_read */
239 void module_register (void)
241 plugin_register_config ("lpar", lpar_config,
242 config_keys, config_keys_num);
243 plugin_register_read ("lpar", lpar_read);
244 } /* void module_register */
246 /* vim: set sw=8 noet : */