X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Flpar.c;h=4d534476f62c76754433b2c73d313967595e5843;hb=8eb9e6285f394569d7fe6ad43a0f4e5f9bca454f;hp=2f40691db61331e46f46768cc09996b4ee323e00;hpb=5f1725febf9aaa48fae42058d875202882bd7147;p=collectd.git diff --git a/src/lpar.c b/src/lpar.c index 2f40691d..4d534476 100644 --- a/src/lpar.c +++ b/src/lpar.c @@ -16,7 +16,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: - * Aurelien Reynaud + * Aurélien Reynaud **/ #include "collectd.h" @@ -27,6 +27,15 @@ #include #include +/* XINTFRAC was defined in libperfstat.h somewhere between AIX 5.3 and 6.1 */ +#ifndef XINTFRAC +# include +# define XINTFRAC ((double)(_system_configuration.Xint) / \ + (double)(_system_configuration.Xfrac)) +#endif + +#define CLOCKTICKS_TO_TICKS(cticks) ((cticks) / XINTFRAC) + static const char *config_keys[] = { "CpuPoolStats", @@ -36,6 +45,12 @@ static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); static _Bool pool_stats = 0; static _Bool report_by_serial = 0; +#if PERFSTAT_SUPPORTS_DONATION +static _Bool donate_flag = 0; +#endif +static char serial[SYS_NMLN]; + +static perfstat_partition_total_t lparstats_old; static int lpar_config (const char *key, const char *value) { @@ -61,33 +76,53 @@ static int lpar_config (const char *key, const char *value) return (0); } /* int lpar_config */ -static void lpar_submit (const char *type_instance, counter_t value) +static int lpar_init (void) +{ + int status; + + /* Retrieve the initial metrics. Returns the number of structures filled. */ + status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */ + &lparstats_old, sizeof (perfstat_partition_total_t), + /* number = */ 1 /* (must be 1) */); + if (status != 1) + { + char errbuf[1024]; + ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)", + sstrerror (errno, errbuf, sizeof (errbuf)), + status); + return (-1); + } + +#if PERFSTAT_SUPPORTS_DONATION + if (!lparstats_old.type.b.shared_enabled + && lparstats_old.type.b.donate_enabled) + { + donate_flag = 1; + } +#endif + + if (pool_stats && !lparstats_old.type.b.pool_util_authority) + { + WARNING ("lpar plugin: This partition does not have pool authority. " + "Disabling CPU pool statistics collection."); + pool_stats = 0; + } + + return (0); +} /* int lpar_init */ + +static void lpar_submit (const char *type_instance, double value) { value_t values[1]; value_list_t vl = VALUE_LIST_INIT; - /* Although it appears as a double, value is really a (scaled) counter, - expressed in CPU x seconds. At high collection rates (< 1 min), its - integer part is very small and the resulting graphs get blocky. We regain - some precision by applying a x100 factor before casting it to a counter, - turning the final value into CPU units instead of CPUs. */ - values[0].counter = value; + values[0].gauge = (gauge_t)value; vl.values = values; vl.values_len = 1; - - /* An LPAR has the same serial number as the physical system it is currently - running on. It is a convenient way of tracking LPARs as they are moved - from chassis to chassis through Live Partition Mobility (LPM). */ if (report_by_serial) { - struct utsname name; - if (uname (&name) != 0) - { - ERROR ("lpar plugin: uname failed."); - return; - } - sstrncpy (vl.host, name.machine, sizeof (vl.host)); + sstrncpy (vl.host, serial, sizeof (vl.host)); sstrncpy (vl.plugin_instance, hostname_g, sizeof (vl.plugin)); } else @@ -95,26 +130,53 @@ static void lpar_submit (const char *type_instance, counter_t value) sstrncpy (vl.host, hostname_g, sizeof (vl.host)); } sstrncpy (vl.plugin, "lpar", sizeof (vl.plugin)); - sstrncpy (vl.type, "cpu", sizeof (vl.type)); + sstrncpy (vl.type, "vcpu", sizeof (vl.type)); sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); plugin_dispatch_values (&vl); -} +} /* void lpar_submit */ -static int lpar_read_shared_partition (const perfstat_partition_total_t *data) +static int lpar_read (void) { - static counter_t time_old; - static counter_t user_old; - static counter_t syst_old; - static counter_t wait_old; - static counter_t idle_old; - static counter_t unav_old; - - counter_t user = (counter_t) data->puser; - counter_t syst = (counter_t) data->psys; - counter_t wait = (counter_t) data->pwait; - counter_t idle = (counter_t) data->pidle; - counter_t unav = 0; + perfstat_partition_total_t lparstats; + int status; + struct utsname name; + u_longlong_t ticks; + u_longlong_t user_ticks, syst_ticks, wait_ticks, idle_ticks; + u_longlong_t consumed_ticks; + double entitled_proc_capacity; + + /* An LPAR has the same serial number as the physical system it is currently + running on. It is a convenient way of tracking LPARs as they are moved + from chassis to chassis through Live Partition Mobility (LPM). */ + if (uname (&name) != 0) + { + ERROR ("lpar plugin: uname failed."); + return (-1); + } + sstrncpy (serial, name.machine, sizeof (serial)); + + /* Retrieve the current metrics. Returns the number of structures filled. */ + status = perfstat_partition_total (/* name = */ NULL, /* (must be NULL) */ + &lparstats, sizeof (perfstat_partition_total_t), + /* number = */ 1 /* (must be 1) */); + if (status != 1) + { + char errbuf[1024]; + ERROR ("lpar plugin: perfstat_partition_total failed: %s (%i)", + sstrerror (errno, errbuf, sizeof (errbuf)), + status); + return (-1); + } + + /* Number of ticks since we last run. */ + ticks = lparstats.timebase_last - lparstats_old.timebase_last; + if (ticks == 0) + { + /* The stats have not been updated. Return now to avoid + * dividing by zero */ + return (0); + } /* * On a shared partition, we're "entitled" to a certain amount of @@ -123,116 +185,79 @@ static int lpar_read_shared_partition (const perfstat_partition_total_t *data) * partition by the hypervisor, so "idle" is hopefully a very small * number. * - * We calculate the amount of ticks assigned to a different partition - * from the number of ticks we're entitled to and the number of ticks - * we used up. + * A dedicated partition may donate its CPUs to another partition and + * may steal ticks from somewhere else (another partition or maybe the + * shared pool, I don't know --octo). */ - if (time_old != 0) - { - counter_t time_diff; - counter_t entitled_ticks; - counter_t consumed_ticks; - counter_t user_diff; - counter_t syst_diff; - counter_t wait_diff; - counter_t idle_diff; - counter_t unav_diff; - - double entitled_pool_capacity; - - /* Number of ticks since we last run. */ - time_diff = ((counter_t) data->timebase_last) - time_old; - - /* entitled_pool_capacity is in 1/100th of a CPU */ - entitled_pool_capacity = 0.01 * ((double) data->entitled_pool_capacity); - - /* The number of ticks this partition would have been entitled to. */ - entitled_ticks = (counter_t) ((entitled_pool_capacity * ((double) time_diff)) + .5); - - /* The number of ticks actually spent in the various states */ - user_diff = user - user_old; - syst_diff = syst - syst_old; - wait_diff = wait - wait_old; - idle_diff = idle - idle_old; - consumed_ticks = user_diff + syst_diff + wait_diff + idle_diff; - - if (entitled_ticks >= consumed_ticks) - unav_diff = entitled_ticks - consumed_ticks; - else - unav_diff = 0; - unav = unav_old + unav_diff; - - lpar_submit ("user", user); - lpar_submit ("system", syst); - lpar_submit ("wait", wait); - lpar_submit ("idle", idle); - lpar_submit ("unavailable", unav); - } - - time_old = (counter_t) data->timebase_last; - user_old = user; - syst_old = syst; - wait_old = wait; - idle_old = idle; - unav_old = unav; - - return (0); -} /* int lpar_read_shared_partition */ - -static int lpar_read_dedicated_partition (const perfstat_partition_total_t *data) -{ - lpar_submit ("user", (counter_t) data->puser); - lpar_submit ("system", (counter_t) data->psys); - lpar_submit ("wait", (counter_t) data->pwait); - lpar_submit ("idle", (counter_t) data->pidle); - if (data->type.b.donate_enabled) - { - lpar_submit ("idle_donated", (counter_t) data->idle_donated_purr); - lpar_submit ("busy_donated", (counter_t) data->busy_donated_purr); - lpar_submit ("idle_stolen", (counter_t) data->idle_stolen_purr); - lpar_submit ("busy_stolen", (counter_t) data->busy_stolen_purr); - } + /* entitled_proc_capacity is in 1/100th of a CPU */ + entitled_proc_capacity = 0.01 * ((double) lparstats.entitled_proc_capacity); + lpar_submit ("entitled", entitled_proc_capacity); - return (0); -} /* int lpar_read_dedicated_partition */ + /* The number of ticks actually spent in the various states */ + user_ticks = lparstats.puser - lparstats_old.puser; + syst_ticks = lparstats.psys - lparstats_old.psys; + wait_ticks = lparstats.pwait - lparstats_old.pwait; + idle_ticks = lparstats.pidle - lparstats_old.pidle; + consumed_ticks = user_ticks + syst_ticks + wait_ticks + idle_ticks; -static int lpar_read (void) -{ - perfstat_partition_total_t lparstats; + lpar_submit ("user", (double) user_ticks / (double) ticks); + lpar_submit ("system", (double) syst_ticks / (double) ticks); + lpar_submit ("wait", (double) wait_ticks / (double) ticks); + lpar_submit ("idle", (double) idle_ticks / (double) ticks); - /* Retrieve the current metrics */ - if (!perfstat_partition_total (NULL, &lparstats, - sizeof (perfstat_partition_total_t), 1)) +#if PERFSTAT_SUPPORTS_DONATION + if (donate_flag) { - ERROR ("lpar plugin: perfstat_partition_total failed."); - return (-1); + /* donated => ticks given to another partition + * stolen => ticks received from another partition */ + u_longlong_t idle_donated_ticks, busy_donated_ticks; + u_longlong_t idle_stolen_ticks, busy_stolen_ticks; + + /* FYI: PURR == Processor Utilization of Resources Register + * SPURR == Scaled PURR */ + idle_donated_ticks = lparstats.idle_donated_purr - lparstats_old.idle_donated_purr; + busy_donated_ticks = lparstats.busy_donated_purr - lparstats_old.busy_donated_purr; + idle_stolen_ticks = lparstats.idle_stolen_purr - lparstats_old.idle_stolen_purr; + busy_stolen_ticks = lparstats.busy_stolen_purr - lparstats_old.busy_stolen_purr; + + lpar_submit ("idle_donated", (double) idle_donated_ticks / (double) ticks); + lpar_submit ("busy_donated", (double) busy_donated_ticks / (double) ticks); + lpar_submit ("idle_stolen", (double) idle_stolen_ticks / (double) ticks); + lpar_submit ("busy_stolen", (double) busy_stolen_ticks / (double) ticks); + + /* Donated ticks will be accounted for as stolen ticks in other LPARs */ + consumed_ticks += idle_stolen_ticks + busy_stolen_ticks; } +#endif - if (lparstats.type.b.shared_enabled) - lpar_read_shared_partition (&lparstats); - else /* if (!shared_enabled) */ - lpar_read_dedicated_partition (&lparstats); - - if (pool_stats && !lparstats.type.b.pool_util_authority) - { - WARNING ("lpar plugin: This partition does not have pool authority. " - "Disabling CPU pool statistics collection."); - pool_stats = 0; - } + lpar_submit ("consumed", (double) consumed_ticks / (double) ticks); if (pool_stats) { char typinst[DATA_MAX_NAME_LEN]; - - /* Pool stats are in CPU x ns */ - ssnprintf (typinst, sizeof(typinst), "pool-%X-busy", lparstats.pool_id); - lpar_submit (typinst, (double)lparstats.pool_busy_time / 1000000000.0); - - ssnprintf (typinst, sizeof(typinst), "pool-%X-total", lparstats.pool_id); - lpar_submit (typinst, (double)lparstats.pool_max_time / 1000000000.0); + u_longlong_t pool_idle_cticks; + double pool_idle_cpus; + double pool_busy_cpus; + + /* We're calculating "busy" from "idle" and the total number of + * CPUs, because the "busy" member didn't exist in early versions + * of libperfstat. It was added somewhere between AIX 5.3 ML5 and ML9. */ + pool_idle_cticks = lparstats.pool_idle_time - lparstats_old.pool_idle_time; + pool_idle_cpus = CLOCKTICKS_TO_TICKS ((double) pool_idle_cticks) / (double) ticks; + pool_busy_cpus = ((double) lparstats.phys_cpus_pool) - pool_idle_cpus; + if (pool_busy_cpus < 0.0) + pool_busy_cpus = 0.0; + + ssnprintf (typinst, sizeof (typinst), "pool-%X-busy", lparstats.pool_id); + lpar_submit (typinst, pool_busy_cpus); + + ssnprintf (typinst, sizeof (typinst), "pool-%X-idle", lparstats.pool_id); + lpar_submit (typinst, pool_idle_cpus); } + memcpy (&lparstats_old, &lparstats, sizeof (lparstats_old)); + return (0); } /* int lpar_read */ @@ -240,6 +265,7 @@ void module_register (void) { plugin_register_config ("lpar", lpar_config, config_keys, config_keys_num); + plugin_register_init ("lpar", lpar_init); plugin_register_read ("lpar", lpar_read); } /* void module_register */