diff options
Diffstat (limited to 'src/pmdas/gfs2')
-rw-r--r-- | src/pmdas/gfs2/GNUmakefile | 60 | ||||
-rw-r--r-- | src/pmdas/gfs2/Install | 27 | ||||
-rw-r--r-- | src/pmdas/gfs2/README | 108 | ||||
-rw-r--r-- | src/pmdas/gfs2/Remove | 24 | ||||
-rw-r--r-- | src/pmdas/gfs2/control.c | 123 | ||||
-rw-r--r-- | src/pmdas/gfs2/control.h | 49 | ||||
-rw-r--r-- | src/pmdas/gfs2/ftrace.c | 586 | ||||
-rw-r--r-- | src/pmdas/gfs2/ftrace.h | 142 | ||||
-rw-r--r-- | src/pmdas/gfs2/glocks.c | 93 | ||||
-rw-r--r-- | src/pmdas/gfs2/glocks.h | 36 | ||||
-rw-r--r-- | src/pmdas/gfs2/glstats.c | 117 | ||||
-rw-r--r-- | src/pmdas/gfs2/glstats.h | 57 | ||||
-rw-r--r-- | src/pmdas/gfs2/help | 566 | ||||
-rw-r--r-- | src/pmdas/gfs2/latency.c | 373 | ||||
-rw-r--r-- | src/pmdas/gfs2/latency.h | 70 | ||||
-rw-r--r-- | src/pmdas/gfs2/pmda.c | 1063 | ||||
-rw-r--r-- | src/pmdas/gfs2/pmdagfs2.h | 57 | ||||
-rw-r--r-- | src/pmdas/gfs2/pmns | 270 | ||||
-rw-r--r-- | src/pmdas/gfs2/root | 16 | ||||
-rw-r--r-- | src/pmdas/gfs2/sbstats.c | 264 | ||||
-rw-r--r-- | src/pmdas/gfs2/sbstats.h | 56 | ||||
-rw-r--r-- | src/pmdas/gfs2/worst_glock.c | 391 | ||||
-rw-r--r-- | src/pmdas/gfs2/worst_glock.h | 91 |
23 files changed, 4639 insertions, 0 deletions
diff --git a/src/pmdas/gfs2/GNUmakefile b/src/pmdas/gfs2/GNUmakefile new file mode 100644 index 0000000..03fce96 --- /dev/null +++ b/src/pmdas/gfs2/GNUmakefile @@ -0,0 +1,60 @@ +# +# Copyright (c) 2013 - 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# + +TOPDIR = ../../.. +include $(TOPDIR)/src/include/builddefs + +CFILES = latency.c control.c glstats.c worst_glock.c glocks.c sbstats.c ftrace.c pmda.c +HFILES = latency.c control.h glstats.h worst_glock.h glocks.h sbstats.h ftrace.h pmdagfs2.h +CMDTARGET = pmdagfs2 +LLDLIBS = $(PCP_PMDALIB) +LSRCFILES = Install Remove pmns root help README + +IAM = gfs2 +DOMAIN = GFS2 +PMDADIR = $(PCP_PMDAS_DIR)/$(IAM) + +LDIRT = domain.h $(IAM).log + +default: build-me + +include $(BUILDRULES) + +ifeq "$(TARGET_OS)" "linux" +build-me: domain.h $(CMDTARGET) + +install: default + $(INSTALL) -m 755 -d $(PMDADIR) + $(INSTALL) -m 755 Install Remove $(CMDTARGET) $(PMDADIR) + $(INSTALL) -m 644 root pmns domain.h help $(PMDADIR) +else +build-me: +install: +endif + +default_pcp : default + +install_pcp : install + +domain.h: ../../pmns/stdpmid + $(DOMAIN_MAKERULE) + +pmda.o glocks.o: glocks.h +pmda.o sbstats.o: sbstats.h +pmda.o: pmdagfs2.h +pmda.o glstats.o: glstats.h +pmda.o ftrace.o: ftrace.h +pmda.o ftrace.o worst_glock.o: worst_glock.h +pmda.o ftrace.o latency.o: latency.h +pmda.o control.o: control.h diff --git a/src/pmdas/gfs2/Install b/src/pmdas/gfs2/Install new file mode 100644 index 0000000..5f60ce2 --- /dev/null +++ b/src/pmdas/gfs2/Install @@ -0,0 +1,27 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Install the GFS2 PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=gfs2 +forced_restart=true +pmda_interface=4 + +pmdaSetup +pmdaInstall +exit 0 diff --git a/src/pmdas/gfs2/README b/src/pmdas/gfs2/README new file mode 100644 index 0000000..13e5228 --- /dev/null +++ b/src/pmdas/gfs2/README @@ -0,0 +1,108 @@ +Performance Co-Pilot PMDA for Monitoring GFS2 Filesystems +========================================================= + +This PMDA is capable of collecting glock statistics from GFS2 filesystems +mounted on the system in both local and clustered configurations. + +The PMDA collects its data from the trace-point output given by GFS2 as +the filesystem is working, this information is provided in userland +through debugfs. In order for pmdagfs2 to be able to provide any metric +data the user must have debugfs mounted and at least on GFS2 filesystem +mounted on the system to be monitored. + +As mentioned above the PMDA can be used both situations where GFS2 +filesystems are mounted as local filesystems and in cluster configurations +where GFS2 is used as a shared disk filesystem. When the PMDA is being +used in conjunction with a locally mounted filesystem (no clustering) only +a base number of metrics will be available to provide metric information +back to PMCD, these metrics can be recognised by their lack of +corresponding “control” metrics. + +For configurations where GFS2 is used in a clustered environment the +additional “cluster only” metrics are able to collect data through the +cache control mechanism of the cluster. This data being passed between +cluster nodes regarding the state of glocks is unavailable in local +filesystem configurations leading the requirement for a cluster +configuration for these metrics. + +For more information on GFS2 or cluster setups please visit www.redhat.com + +Metrics +======= + +The file ./help contains descriptions for all of the metrics which are +exposed by this PMDA. + +Once the PMDA has been installed, the following command will list all of +the available metrics and their explanatory “help” text: + + + # $ pminfo -fT gfs2 + +Installation +============ + + + # cd $PCP_PMDAS_DIR/gfs2 + + + Check that there is no clash in the Performance Metrics Domain + defined in ./domain.h and the other PMDA's currently in use (see + $PCP_PMCDCONF_PATH). If there is, edit ./domain.h to choose another + domain number (This should only be an issue on installations with + third party PMDA's installed as the domain number given has been + reserved for the GFS2 PMDA with base PCP installations). + + + Then simply use + + # ./Install + + and choose both the “collector” and “monitor” installation + configuration options. + +Configuration +============= + +Some of the metrics provided by the PMDA can be configured to whether they +are turned on or off with regards to collecting metric data. These metrics +are distinguished by having a corresponding “control” metric. + +Identification of these metrics which have this control can be found by +issuing the following command. + + + $ pminfo -fT gfs2.control + +The metrics given as output through pminfo in this way can be controlled +by setting their metric value to either 0 (Off: no collection of metric +data) or 1 (On: collection of metric data) using the provided command +pmstore whilst specifying the metric to set the value for and a valid +value. + + + $ pmstore gfs2.control.tracepoints.all 1 + +Any metrics without a corresponding control metric cannot have their +functionality toggled in this way. + +De-Installation +=============== + + + Simply use + + # cd $PCP_PMDAS_DIR/gfs2 + #./Remove + +Troubleshooting +=============== + + + After installing or restarting the agent, the PMCD log file + ($PCP_LOG_DIR/pmcd/pmcd.log) and the PMDA log file + ($PCP_LOG_DIR/PMCD/gfs2.log) should be checked for any warnings or + errors. + + + In an event where no values are being returned for most of the + metrics check ensure that both debugfs is mounted, metrics with + control options are enabled and your distribution supports the + full range of GFS2 trace-points. + + $ mount -t debugfs none /sys/kernel/debug + + $ pminfo -f gfs2.control + + $ pmstore gfs2.control.tracepoints.all 1 diff --git a/src/pmdas/gfs2/Remove b/src/pmdas/gfs2/Remove new file mode 100644 index 0000000..ba5409c --- /dev/null +++ b/src/pmdas/gfs2/Remove @@ -0,0 +1,24 @@ +#! /bin/sh +# +# Copyright (c) 2013 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# Remove the GFS2 PMDA +# + +. $PCP_DIR/etc/pcp.env +. $PCP_SHARE_DIR/lib/pmdaproc.sh + +iam=gfs2 +pmdaSetup +pmdaRemove +exit 0 diff --git a/src/pmdas/gfs2/control.c b/src/pmdas/gfs2/control.c new file mode 100644 index 0000000..20d5c6f --- /dev/null +++ b/src/pmdas/gfs2/control.c @@ -0,0 +1,123 @@ +/* + * GFS2 trace-point metrics control. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "control.h" +#include "ftrace.h" +#include "worst_glock.h" +#include "latency.h" + +/* Locations of the enable files for the gfs2 tracepoints */ +const char *control_locations[] = { + [CONTROL_ALL] = "/sys/kernel/debug/tracing/events/gfs2/enable", + [CONTROL_GLOCK_STATE_CHANGE] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_glock_state_change/enable", + [CONTROL_GLOCK_PUT] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_glock_put/enable", + [CONTROL_DEMOTE_RQ] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_demote_rq/enable", + [CONTROL_PROMOTE] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_promote/enable", + [CONTROL_GLOCK_QUEUE] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_glock_queue/enable", + [CONTROL_GLOCK_LOCK_TIME] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_glock_lock_time/enable", + [CONTROL_PIN] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_pin/enable", + [CONTROL_LOG_FLUSH] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_log_flush/enable", + [CONTROL_LOG_BLOCKS] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_log_blocks/enable", + [CONTROL_AIL_FLUSH] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_ail_flush/enable", + [CONTROL_BLOCK_ALLOC] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_block_alloc/enable", + [CONTROL_BMAP] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_bmap/enable", + [CONTROL_RS] = "/sys/kernel/debug/tracing/events/gfs2/gfs2_rs/enable", + [CONTROL_BUFFER_SIZE_KB] = "/sys/kernel/debug/tracing/buffer_size_kb", + [CONTROL_GLOBAL_TRACING] = "/sys/kernel/debug/tracing/tracing_on" +}; + +/* + * Refresh callback for the control metrics; For traceppoints that have file + * based enabling we use gfs2_control_check_value(), for other metrics we + * call their corresponding "get" value. + */ +int +gfs2_control_fetch(int item, pmAtomValue *atom) +{ + if (item >= CONTROL_ALL && item <= CONTROL_GLOBAL_TRACING) { + atom->ul = gfs2_control_check_value(control_locations[item]); + + } else if (item == CONTROL_WORSTGLOCK) { + atom->ul = worst_glock_get_state(); + + } else if (item == CONTROL_LATENCY) { + atom->ul = latency_get_state(); + + } else if (item == CONTROL_FTRACE_GLOCK_THRESHOLD) { + atom->ul = ftrace_get_threshold(); + + } else { + return PM_ERR_PMID; + + } + return 1; +} + +/* + * Attempt open the enable file for the given filename and set the value + * contained in pmValueSet. The enable file for the tracepoint only accept + * 0 for disabled or 1 for enabled. + */ +int +gfs2_control_set_value(const char *filename, pmValueSet *vsp) +{ + FILE *fp; + int sts = 0; + int value = vsp->vlist[0].value.lval; + + if (strncmp(filename, control_locations[CONTROL_BUFFER_SIZE_KB], + sizeof(control_locations[CONTROL_BUFFER_SIZE_KB])-1) == 0) { + /* Special case for buffer_size_kb */ + if (value < 0 || value > 131072) /* Allow upto 128mb buffer per CPU */ + return - oserror(); + + } else if (value < 0 || value > 1) { + return -oserror(); + } + + fp = fopen(filename, "w"); + if (!fp) { + sts = -oserror(); /* EACCESS, File not found (stats not supported) */; + } else { + fprintf(fp, "%d\n", value); + fclose(fp); + } + return sts; +} + +/* + * We check the tracepoint enable file given by filename and return the value + * contained. This should either be 0 for disabled or 1 for enabled. In the + * event of permissions or file not found we will return zero. + */ +int +gfs2_control_check_value(const char *filename) +{ + FILE *fp; + char buffer[16]; + int value = 0; + + fp = fopen(filename, "r"); + if (fp) { + while (fgets(buffer, sizeof(buffer), fp) != NULL) + sscanf(buffer, "%d", &value); + fclose(fp); + } + return value; +} diff --git a/src/pmdas/gfs2/control.h b/src/pmdas/gfs2/control.h new file mode 100644 index 0000000..e687dad --- /dev/null +++ b/src/pmdas/gfs2/control.h @@ -0,0 +1,49 @@ +/* + * GFS2 trace-point enable controls. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef CONTROL_H +#define CONTROL_H + +enum { + CONTROL_ALL = 0, + CONTROL_GLOCK_STATE_CHANGE, + CONTROL_GLOCK_PUT, + CONTROL_DEMOTE_RQ, + CONTROL_PROMOTE, + CONTROL_GLOCK_QUEUE, + CONTROL_GLOCK_LOCK_TIME, + CONTROL_PIN, + CONTROL_LOG_FLUSH, + CONTROL_LOG_BLOCKS, + CONTROL_AIL_FLUSH, + CONTROL_BLOCK_ALLOC, + CONTROL_BMAP, + CONTROL_RS, + CONTROL_BUFFER_SIZE_KB, + CONTROL_GLOBAL_TRACING, + CONTROL_WORSTGLOCK, + CONTROL_LATENCY, + CONTROL_FTRACE_GLOCK_THRESHOLD, + NUM_CONTROL_STATS +}; + +extern const char *control_locations[]; + +extern int gfs2_control_fetch(int, pmAtomValue *); +extern int gfs2_control_set_value(const char *, pmValueSet *); +extern int gfs2_control_check_value(const char *); + +#endif /* CONTROL_H */ diff --git a/src/pmdas/gfs2/ftrace.c b/src/pmdas/gfs2/ftrace.c new file mode 100644 index 0000000..6485c00 --- /dev/null +++ b/src/pmdas/gfs2/ftrace.c @@ -0,0 +1,586 @@ +/* + * GFS2 ftrace based trace-point metrics. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "pmdagfs2.h" +#include "ftrace.h" +#include "worst_glock.h" +#include "latency.h" + +#include <fcntl.h> +#include <string.h> +#include <inttypes.h> +#include <sys/sysmacros.h> +#include <sys/types.h> + + +static char *TRACE_PIPE = "/sys/kernel/debug/tracing/trace_pipe"; +static int max_glock_throughput = INITIAL_GLOBAL_MAX_GLOCK_THROUGHPUT; + +static struct ftrace_data ftrace_data; +static int num_accepted_entries; + +/* + * Fetches the value for the given metric item and then assigns to pmAtomValue. + * We check to see if item is in valid range for the metric. + */ +int +gfs2_ftrace_fetch(int item, struct ftrace *ftrace, pmAtomValue *atom) +{ + /* Ensure that metric value wanted is valid */ + if ((item < 0 || item >= NUM_TRACEPOINT_STATS)) + return PM_ERR_PMID; + + atom->ull = ftrace->values[item]; + return 1; +} + +/* + * External function to allow the increment of the num_accepted_locks + * variable from pmStore. + */ +void +ftrace_increase_num_accepted_entries(){ + num_accepted_entries++; +} + +/* + * Sets the value of max_glock_throughput using pmstore, value should + * must be positive. + */ +int +ftrace_set_threshold(pmValueSet *vsp) +{ + int value = vsp->vlist[0].value.lval; + + if (value < 0) /* Ensure positive value */ + return PM_ERR_SIGN; + + max_glock_throughput = value; + + return 0; +} + +/* + * Returns the max number of glocks we allow per run through the trace_pipe, + * Used by the fetch for the control metrics. + */ +int +ftrace_get_threshold() +{ + return max_glock_throughput; +} + +/* + * We open the ftrace trace file in write mode and straight after + * close it. This acts as a way to completely clear the trace ring- + * buffer when needed. + */ +static int +ftrace_clear_buffer() +{ + char *TRACE = "/sys/kernel/debug/tracing/trace"; + FILE *fp; + + /* Open in write mode and then straight close will purge buffer contents */ + if (( fp = fopen(TRACE, "w")) == NULL ) + return -oserror(); + + fclose(fp); + + return 0; +} + +/* + * We take tracedata from the trace pipe and store only the data which is + * from GFS2 metrics. We collect all the data in one array to be worked + * through later, this is because all trace data comes through the + * trace pipe mixed. + */ +static int +gfs2_extract_trace_values(char *buffer, pmInDom gfs_fs_indom) +{ + struct ftrace_data temp; + + unsigned int major, minor; + char *data; + + /* Interpret data, we work out what tracepoint it belongs to first */ + if ((data = strstr(buffer, "gfs2_glock_state_change: "))) { + temp.tracepoint = GLOCK_STATE_CHANGE; + sscanf(data, "gfs2_glock_state_change: %"SCNu32",%"SCNu32"", &major, &minor); + + /* + * Pass tracepoint data over for latency metrics for processing, + * only if the metrics are enabled. + */ + if (latency_get_state() == 1) + gfs2_extract_latency(major, minor, temp.tracepoint, buffer, gfs_fs_indom); + + } else if ((data = strstr(buffer, "gfs2_glock_put: "))) { + temp.tracepoint = GLOCK_PUT; + sscanf(data, "gfs2_glock_put: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_demote_rq: "))) { + temp.tracepoint = DEMOTE_RQ; + sscanf(data, "gfs2_demote_rq: %"SCNu32",%"SCNu32"", &major, &minor); + + /* + * Pass tracepoint data over for latency metrics for processing, + * only if the metrics are enabled. + */ + if (latency_get_state() == 1) + gfs2_extract_latency(major, minor, temp.tracepoint, buffer, gfs_fs_indom); + + } else if ((data = strstr(buffer, "gfs2_promote: "))) { + temp.tracepoint = PROMOTE; + sscanf(data, "gfs2_promote: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_glock_queue: "))) { + temp.tracepoint = GLOCK_QUEUE; + sscanf(data, "gfs2_glock_queue: %"SCNu32",%"SCNu32"", &major, &minor); + + /* + * Pass tracepoint data over for latency metrics for processing, + * only if the metrics are enabled. + */ + if (latency_get_state() == 1) + gfs2_extract_latency(major, minor, temp.tracepoint, buffer, gfs_fs_indom); + + } else if ((data = strstr(buffer, "gfs2_glock_lock_time: "))) { + temp.tracepoint = GLOCK_LOCK_TIME; + sscanf(data, "gfs2_glock_lock_time: %"SCNu32",%"SCNu32"", &major, &minor); + + /* + * Pass tracepoint data over for worst_glock metrics for processing, + * only if the metrics are enabled. + */ + if (worst_glock_get_state() == 1) + gfs2_extract_worst_glock(&data, gfs_fs_indom); + + } else if ((data = strstr(buffer, "gfs2_pin: "))) { + temp.tracepoint = PIN; + sscanf(data, "gfs2_pin: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_log_flush: "))) { + temp.tracepoint = LOG_FLUSH; + sscanf(data, "gfs2_log_flush: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_log_blocks: "))) { + temp.tracepoint = LOG_BLOCKS; + sscanf(data, "gfs2_log_blocks: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_ail_flush: "))) { + temp.tracepoint = AIL_FLUSH; + sscanf(data, "gfs2_ail_flush: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_block_alloc: "))) { + temp.tracepoint = BLOCK_ALLOC; + sscanf(data, "gfs2_block_alloc: %"SCNu32",%"SCNu32" %s", &major, &minor, data); + + } else if ((data = strstr(buffer, "gfs2_bmap: "))) { + temp.tracepoint = BMAP; + sscanf(data, "gfs2_bmap: %"SCNu32",%"SCNu32"", &major, &minor); + + } else if ((data = strstr(buffer, "gfs2_rs: "))) { + temp.tracepoint = RS; + sscanf(data, "gfs2_rs: %"SCNu32",%"SCNu32" %s", &major, &minor, data); + } else { + return 0; /* If we do not have matching data, return and continue */ + } + + temp.dev_id = makedev(major, minor); + strncpy(temp.data, data, sizeof(temp.data)-1); + + /* Assign data in the array and update counters */ + ftrace_data = temp; + num_accepted_entries++; + + return 0; +} + +/* + * We work though each mounted filesystem and update the metric data based + * off what tracepoint information we have collected from the trace pipe. + */ +static void +gfs2_assign_ftrace(pmInDom gfs2_fs_indom, int reset_flag) +{ + int i, j, sts; + struct gfs2_fs *fs; + + /* We walk through for each filesystem */ + for (pmdaCacheOp(gfs2_fs_indom, PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(gfs2_fs_indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + sts = pmdaCacheLookup(gfs2_fs_indom, i, NULL, (void **)&fs); + if (sts != PMDA_CACHE_ACTIVE) + continue; + + if(reset_flag == 1){ + for (j = 0; j < NUM_TRACEPOINT_STATS; j++) { + /* Reset old metric data for all tracepoints */ + fs->ftrace.values[j] = 0; + reset_flag = 0; + } + } + + if (fs->dev_id == ftrace_data.dev_id) { + + /* Work through the data, increasing metric counters */ + if (ftrace_data.tracepoint == GLOCK_STATE_CHANGE) { + char state[3], target[3]; + + sscanf(ftrace_data.data, + "gfs2_glock_state_change: %*d,%*d glock %*d:%*d state %*s to %s tgt:%s dmt:%*s flags:%*s", + state, target + ); + + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_GLOCKSTATE_TOTAL]++; + + if (strncmp(state, target, 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKSTATE_GLOCK_CHANGEDTARGET]++; + } else { + fs->ftrace.values[FTRACE_GLOCKSTATE_GLOCK_MISSEDTARGET]++; + } + + } else if (ftrace_data.tracepoint == GLOCK_PUT) { + char state[3]; + + sscanf(ftrace_data.data, + "gfs2_glock_put: %*d,%*d glock %*d:%*d state %*s => %s flags:%*s", + state + ); + + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKPUT_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_GLOCKPUT_TOTAL]++; + + } else if (ftrace_data.tracepoint == DEMOTE_RQ) { + char state[3], remote[7]; + + sscanf(ftrace_data.data, + "gfs2_demote_rq: %*d,%*d glock %*d:%*d demote %*s to %s flags:%*s %s", + state, remote + ); + + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_DEMOTERQ_TOTAL]++; + + if (strncmp(remote, "remote", 6) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_REQUESTED_REMOTE]++; + } else if (strncmp(remote, "local", 6) == 0) { + fs->ftrace.values[FTRACE_DEMOTERQ_REQUESTED_LOCAL]++; + } + + } else if (ftrace_data.tracepoint == PROMOTE) { + char state[3], first[6]; + + sscanf(ftrace_data.data, + "gfs2_promote: %*d,%*d glock %*d:%*d promote %s %s", + first, state + ); + + if (strncmp(first, "first", 5) == 0) { + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_FIRST_EXCLUSIVE]++; + } + } else if (strncmp(first, "other", 5) == 0) { + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_PROMOTE_OTHER_EXCLUSIVE]++; + } + } + fs->ftrace.values[FTRACE_PROMOTE_TOTAL]++; + + } else if (ftrace_data.tracepoint == GLOCK_QUEUE) { + char state[3], queue[8]; + + sscanf(ftrace_data.data, + "gfs2_glock_queue: %*d,%*d glock %*d:%*d %s %s", + queue, state + ); + + if (strncmp(queue, "queue", 6) == 0) { + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_GLOCKQUEUE_QUEUE_TOTAL]++; + + } else if (strncmp(queue, "dequeue", 8) == 0) { + if (strncmp(state, "NL", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_NULLLOCK]++; + } else if (strncmp(state, "CR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTREAD]++; + } else if (strncmp(state, "CW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTWRITE]++; + } else if (strncmp(state, "PR", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDREAD]++; + } else if (strncmp(state, "PW", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDWRITE]++; + } else if (strncmp(state, "EX", 2) == 0) { + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_EXCLUSIVE]++; + } + fs->ftrace.values[FTRACE_GLOCKQUEUE_DEQUEUE_TOTAL]++; + } + fs->ftrace.values[FTRACE_GLOCKQUEUE_TOTAL]++; + + } else if (ftrace_data.tracepoint == GLOCK_LOCK_TIME) { + uint32_t lock_type; + + sscanf(ftrace_data.data, + "gfs2_glock_lock_time: %*d,%*d glock %"SCNu32":%*d status:%*d flags:%*x tdiff:%*d srtt:%*d/%*d srttb:%*d/%*d sirt:%*d/%*d dcnt:%*d qcnt:%*d", + &lock_type + ); + + if (lock_type == 1) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_TRANS]++; + } else if (lock_type == 2) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_INDOE]++; + } else if (lock_type == 3) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_RGRP]++; + } else if (lock_type == 4) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_META]++; + } else if (lock_type == 5) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_IOPEN]++; + } else if (lock_type == 6) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_FLOCK]++; + } else if (lock_type == 8) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_QUOTA]++; + } else if (lock_type == 9) { + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_JOURNAL]++; + } + fs->ftrace.values[FTRACE_GLOCKLOCKTIME_TOTAL]++; + + } else if (ftrace_data.tracepoint == PIN) { + char pinned[6]; + uint32_t length; + + sscanf(ftrace_data.data, + "gfs2_pin: %*d,%*d log %s %*d/%"SCNu32" inode: %*d", + pinned, &length + ); + + if (strncmp(pinned, "pin", 5) == 0) { + fs->ftrace.values[FTRACE_PIN_PINTOTAL]++; + } else if (strncmp(pinned, "unpin", 5) == 0) { + fs->ftrace.values[FTRACE_PIN_UNPINTOTAL]++; + } + fs->ftrace.values[FTRACE_PIN_TOTAL]++; + + if(fs->ftrace.values[FTRACE_PIN_LONGESTPINNED] < length) + fs->ftrace.values[FTRACE_PIN_LONGESTPINNED] = length; + + } else if (ftrace_data.tracepoint == LOG_FLUSH) { + char end[6]; + + sscanf(ftrace_data.data, + "gfs2_log_flush: %*d,%*d log flush %s %*u", + end + ); + + if (strncmp(end, "end", 6) == 0) + fs->ftrace.values[FTRACE_LOGFLUSH_TOTAL]++; + + } else if (ftrace_data.tracepoint == LOG_BLOCKS) { + + fs->ftrace.values[FTRACE_LOGBLOCKS_TOTAL]++; + + } else if (ftrace_data.tracepoint == AIL_FLUSH) { + char end[6]; + + sscanf(ftrace_data.data, + "gfs2_ail_flush: %*d,%*d ail flush %s %*s %*u", + end + ); + + if (strncmp(end, "end", 6) == 0) + fs->ftrace.values[FTRACE_AILFLUSH_TOTAL]++; + + } else if (ftrace_data.tracepoint == BLOCK_ALLOC) { + char type[8]; + + sscanf(ftrace_data.data, + "gfs2_block_alloc: %*d,%*d bmap %*u alloc %*u/%*u %s rg:%*u rf:%*u rr:%*u", + type + ); + + if (strncmp(type, "free", 8) == 0) { + fs->ftrace.values[FTRACE_BLOCKALLOC_FREE]++; + } else if (strncmp(type, "used", 8) == 0) { + fs->ftrace.values[FTRACE_BLOCKALLOC_USED]++; + } else if (strncmp(type, "dinode", 8) == 0) { + fs->ftrace.values[FTRACE_BLOCKALLOC_DINODE]++; + } else if (strncmp(type, "unlinked", 8) == 0) { + fs->ftrace.values[FTRACE_BLOCKALLOC_UNLINKED]++; + } + fs->ftrace.values[FTRACE_BLOCKALLOC_TOTAL]++; + + } else if (ftrace_data.tracepoint == BMAP) { + char type[8]; + + sscanf(ftrace_data.data, + "gfs2_bmap: %*d,%*d bmap %*u map %*u/%*u to %*u flags:%*x %s %*d", + type + ); + + if (strncmp(type, "create", 8) == 0) { + fs->ftrace.values[FTRACE_BMAP_CREATE]++; + } else if (strncmp(type, "nocreate", 8) == 0) { + fs->ftrace.values[FTRACE_BMAP_NOCREATE]++; + } + fs->ftrace.values[FTRACE_BMAP_TOTAL]++; + + } else if (ftrace_data.tracepoint == RS) { + char type[8]; + + sscanf(ftrace_data.data, + "gfs2_rs: %*d,%*d bmap %*u resrv %*u rg:%*u rf:%*u rr:%*u %s f:%*u", + type + ); + + if (strncmp(type, "del", 4) == 0) { + fs->ftrace.values[FTRACE_RS_DEL]++; + } else if (strncmp(type, "tdel", 4) == 0) { + fs->ftrace.values[FTRACE_RS_TDEL]++; + } else if (strncmp(type, "ins", 4) == 0) { + fs->ftrace.values[FTRACE_RS_INS]++; + } else if (strncmp(type, "clm", 4) == 0) { + fs->ftrace.values[FTRACE_RS_CLM]++; + } + fs->ftrace.values[FTRACE_RS_TOTAL]++; + + } + } + } +} + +/* + * We take all required data from the trace_pipe. Whilst keeping track of + * the number of locks we have seen so far. After locks have been collected + * we assign values and return. + */ +int +gfs2_refresh_ftrace_stats(pmInDom gfs_fs_indom) +{ + FILE *fp; + int fd, flags, reset_flag; + char buffer[8196]; + + /* Reset the metric types we have found */ + num_accepted_entries = 0; + reset_flag = 1; + + /* We open the pipe in both read-only and non-blocking mode */ + if ((fp = fopen(TRACE_PIPE, "r")) == NULL) + return -oserror(); + + /* Set flags of fp as non-blocking */ + fd = fileno(fp); + flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, flags | O_RDONLY | O_NONBLOCK) < 0) { + fclose(fp); + return -oserror(); + } + + /* Extract data from the trace_pipe */ + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + if (num_accepted_entries >= max_glock_throughput) + break; + + /* In the event of an allocation error */ + if (gfs2_extract_trace_values(buffer, gfs_fs_indom) != 0) + break; + + /* Processing here */ + gfs2_assign_ftrace(gfs_fs_indom, reset_flag); + } + + fclose(fp); + + /* Clear the rest of the ring buffer after passing max_glock_throughput */ + ftrace_clear_buffer(); + + return 0; +} diff --git a/src/pmdas/gfs2/ftrace.h b/src/pmdas/gfs2/ftrace.h new file mode 100644 index 0000000..7974783 --- /dev/null +++ b/src/pmdas/gfs2/ftrace.h @@ -0,0 +1,142 @@ +/* + * GFS2 ftrace based trace-point metrics. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef FTRACE_H +#define FTRACE_H + +#define INITIAL_GLOBAL_MAX_GLOCK_THROUGHPUT 750000 + +enum { + GLOCK_STATE_CHANGE = 0, + GLOCK_PUT, + DEMOTE_RQ, + PROMOTE, + GLOCK_QUEUE, + GLOCK_LOCK_TIME, + PIN, + LOG_FLUSH, + LOG_BLOCKS, + AIL_FLUSH, + BLOCK_ALLOC, + BMAP, + RS, + NUM_FTRACE_TRACEPOINTS +}; + +enum { + FTRACE_GLOCKSTATE_TOTAL = 0, + FTRACE_GLOCKSTATE_NULLLOCK, + FTRACE_GLOCKSTATE_CONCURRENTREAD, + FTRACE_GLOCKSTATE_CONCURRENTWRITE, + FTRACE_GLOCKSTATE_PROTECTEDREAD, + FTRACE_GLOCKSTATE_PROTECTEDWRITE, + FTRACE_GLOCKSTATE_EXCLUSIVE, + FTRACE_GLOCKSTATE_GLOCK_CHANGEDTARGET, + FTRACE_GLOCKSTATE_GLOCK_MISSEDTARGET, + FTRACE_GLOCKPUT_TOTAL, + FTRACE_GLOCKPUT_NULLLOCK, + FTRACE_GLOCKPUT_CONCURRENTREAD, + FTRACE_GLOCKPUT_CONCURRENTWRITE, + FTRACE_GLOCKPUT_PROTECTEDREAD, + FTRACE_GLOCKPUT_PROTECTEDWRITE, + FTRACE_GLOCKPUT_EXCLUSIVE, + FTRACE_DEMOTERQ_TOTAL, + FTRACE_DEMOTERQ_NULLLOCK, + FTRACE_DEMOTERQ_CONCURRENTREAD, + FTRACE_DEMOTERQ_CONCURRENTWRITE, + FTRACE_DEMOTERQ_PROTECTEDREAD, + FTRACE_DEMOTERQ_PROTECTEDWRITE, + FTRACE_DEMOTERQ_EXCLUSIVE, + FTRACE_DEMOTERQ_REQUESTED_REMOTE, + FTRACE_DEMOTERQ_REQUESTED_LOCAL, + FTRACE_PROMOTE_TOTAL, + FTRACE_PROMOTE_FIRST_NULLLOCK, + FTRACE_PROMOTE_FIRST_CONCURRENTREAD, + FTRACE_PROMOTE_FIRST_CONCURRENTWRITE, + FTRACE_PROMOTE_FIRST_PROTECTEDREAD, + FTRACE_PROMOTE_FIRST_PROTECTEDWRITE, + FTRACE_PROMOTE_FIRST_EXCLUSIVE, + FTRACE_PROMOTE_OTHER_NULLLOCK, + FTRACE_PROMOTE_OTHER_CONCURRENTREAD, + FTRACE_PROMOTE_OTHER_CONCURRENTWRITE, + FTRACE_PROMOTE_OTHER_PROTECTEDREAD, + FTRACE_PROMOTE_OTHER_PROTECTEDWRITE, + FTRACE_PROMOTE_OTHER_EXCLUSIVE, + FTRACE_GLOCKQUEUE_TOTAL, + FTRACE_GLOCKQUEUE_QUEUE_TOTAL, + FTRACE_GLOCKQUEUE_QUEUE_NULLLOCK, + FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTREAD, + FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTWRITE, + FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDREAD, + FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDWRITE, + FTRACE_GLOCKQUEUE_QUEUE_EXCLUSIVE, + FTRACE_GLOCKQUEUE_DEQUEUE_TOTAL, + FTRACE_GLOCKQUEUE_DEQUEUE_NULLLOCK, + FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTREAD, + FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTWRITE, + FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDREAD, + FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDWRITE, + FTRACE_GLOCKQUEUE_DEQUEUE_EXCLUSIVE, + FTRACE_GLOCKLOCKTIME_TOTAL, + FTRACE_GLOCKLOCKTIME_TRANS, + FTRACE_GLOCKLOCKTIME_INDOE, + FTRACE_GLOCKLOCKTIME_RGRP, + FTRACE_GLOCKLOCKTIME_META, + FTRACE_GLOCKLOCKTIME_IOPEN, + FTRACE_GLOCKLOCKTIME_FLOCK, + FTRACE_GLOCKLOCKTIME_QUOTA, + FTRACE_GLOCKLOCKTIME_JOURNAL, + FTRACE_PIN_TOTAL, + FTRACE_PIN_PINTOTAL, + FTRACE_PIN_UNPINTOTAL, + FTRACE_PIN_LONGESTPINNED, + FTRACE_LOGFLUSH_TOTAL, + FTRACE_LOGBLOCKS_TOTAL, + FTRACE_AILFLUSH_TOTAL, + FTRACE_BLOCKALLOC_TOTAL, + FTRACE_BLOCKALLOC_FREE, + FTRACE_BLOCKALLOC_USED, + FTRACE_BLOCKALLOC_DINODE, + FTRACE_BLOCKALLOC_UNLINKED, + FTRACE_BMAP_TOTAL, + FTRACE_BMAP_CREATE, + FTRACE_BMAP_NOCREATE, + FTRACE_RS_TOTAL, + FTRACE_RS_DEL, + FTRACE_RS_TDEL, + FTRACE_RS_INS, + FTRACE_RS_CLM, + NUM_TRACEPOINT_STATS +}; + +struct ftrace { + uint64_t values[NUM_TRACEPOINT_STATS]; +}; + +struct ftrace_data { + dev_t dev_id; + int tracepoint; + char data[512]; +}; + +extern void ftrace_increase_num_accepted_entries(); +extern int gfs2_ftrace_fetch(int, struct ftrace *, pmAtomValue *); +extern int gfs2_refresh_ftrace_stats(pmInDom); + +extern int ftrace_get_threshold(); +extern int ftrace_set_threshold(pmValueSet *vsp); + +#endif /*FTRACE_H*/ diff --git a/src/pmdas/gfs2/glocks.c b/src/pmdas/gfs2/glocks.c new file mode 100644 index 0000000..e8dc81e --- /dev/null +++ b/src/pmdas/gfs2/glocks.c @@ -0,0 +1,93 @@ +/* + * GFS2 glocks sysfs file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "glocks.h" + +#include <ctype.h> + +int +gfs2_glocks_fetch(int item, struct glocks *glocks, pmAtomValue *atom) +{ + /* Check for valid metric count */ + if (item < 0 || item >= NUM_GLOCKS_STATS) + return PM_ERR_PMID; + + /* Check for no values recorded */ + if(glocks->values[item] == UINT64_MAX) + return 0; + + atom->ull = glocks->values[item]; + return 1; +} + +int +gfs2_refresh_glocks(const char *sysfs, const char *name, struct glocks *glocks) +{ + char buffer[4096]; + FILE *fp; + + /* Reset all counter for this fs */ + memset(glocks, 0, sizeof(*glocks)); + + snprintf(buffer, sizeof(buffer), "%s/%s/glocks", sysfs, name); + buffer[sizeof(buffer)-1] = '\0'; + + if ((fp = fopen(buffer, "r")) == NULL) { + /* + * We set the values to UINT64_MAX to signify we have no + * current values (no metric support or debugfs not mounted) + * + */ + memset(glocks, -1, sizeof(*glocks)); + return -oserror(); + } + + /* + * Read through glocks file accumulating statistics as we go; + * as an early starting point, we're simply binning aggregate + * glock state counts. + * + */ + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + char *p = buffer; + + /* interested in glock lines only for now */ + if (strncmp(p, "G:", 2) != 0) + continue; + for (p += 2; isspace((int)*p); p++) {;} + + /* pick out the various state fields next */ + if (strncmp(p, "s:SH", 4) == 0) + glocks->values[GLOCKS_SHARED]++; + else if (strncmp(p, "s:UN ", 4) == 0) + glocks->values[GLOCKS_UNLOCKED]++; + else if (strncmp(p, "s:DF ", 4) == 0) + glocks->values[GLOCKS_DEFERRED]++; + else if (strncmp(p, "s:EX", 4) == 0) + glocks->values[GLOCKS_EXCLUSIVE]++; + glocks->values[GLOCKS_TOTAL]++; + for (p += 4; isspace((int)*p); p++) {;} + + /* [ extract any other field stats here ] */ + } + + fclose(fp); + return 0; +} diff --git a/src/pmdas/gfs2/glocks.h b/src/pmdas/gfs2/glocks.h new file mode 100644 index 0000000..b56a574 --- /dev/null +++ b/src/pmdas/gfs2/glocks.h @@ -0,0 +1,36 @@ +/* + * GFS2 glock file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef GLOCKS_H +#define GLOCKS_H + +enum { + GLOCKS_TOTAL = 0, + GLOCKS_SHARED = 1, + GLOCKS_UNLOCKED = 2, + GLOCKS_DEFERRED = 3, + GLOCKS_EXCLUSIVE = 4, + NUM_GLOCKS_STATS +}; + +struct glocks { + __uint64_t values[NUM_GLOCKS_STATS]; +}; + +extern int gfs2_glocks_fetch(int, struct glocks *, pmAtomValue *); +extern int gfs2_refresh_glocks(const char *, const char *, struct glocks *); + +#endif /*GLOCKS_H*/ diff --git a/src/pmdas/gfs2/glstats.c b/src/pmdas/gfs2/glstats.c new file mode 100644 index 0000000..bcbd7b6 --- /dev/null +++ b/src/pmdas/gfs2/glstats.c @@ -0,0 +1,117 @@ +/* + * GFS2 glstats file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "glstats.h" + +/* + * GFS2 glock type identification; note that the glock type 7 is currently not + * used and is reserved to be on the safe side. + * + * Type Lock type Use + * 1 Trans Transaction lock + * 2 Inode Inode metadata and data + * 3 Rgrp Resource group metadata + * 4 Meta The superblock + * 5 Iopen Inode last closer detection + * 6 Flock flock(2) syscall + * 8 Quota Quota operations + * 9 Journal Journal mutex + * + */ + +int +gfs2_glstats_fetch(int item, struct glstats *glstats, pmAtomValue *atom) +{ + /* Handle the case for our reserved but not used glock type 7 */ + if ((item < 0 || item >= NUM_GLSTATS_STATS) && item != 7) + return PM_ERR_PMID; + + /* Check for no values recorded */ + if(glstats->values[item] == UINT64_MAX) + return 0; + + atom->ull = glstats->values[item]; + return 1; +} + +int +gfs2_refresh_glstats(const char *sysfs, const char *name, struct glstats *glstats){ + char buffer[4096]; + FILE *fp; + + /* Reset all counter for this fs */ + memset(glstats, 0, sizeof(*glstats)); + + snprintf(buffer, sizeof(buffer), "%s/%s/glstats", sysfs, name); + buffer[sizeof(buffer) - 1] = '\0'; + + if ((fp = fopen(buffer, "r")) == NULL){ + /* + * We set the values to UINT64_MAX to signify we have no + * current values (no metric support or debugfs not mounted) + * + */ + memset(glstats, -1, sizeof(*glstats)); + return -oserror(); + } + + /* + * We read through the glstats file, finding out what glock types we are + * coming across and tally up the number of each type of glock we find. + * This file however contains the total number of locks at this time, + * on a large, heavy utilized filesystem there could be millions of entries + * so needs to be quick and efficient. + * + */ + while(fgets(buffer, sizeof(buffer), fp) != NULL){ + char *p = buffer; + + /* We pick out the various glock types by the identifying number */ + if (strncmp(p, "G: n:1", 6) == 0){ + glstats->values[GLSTATS_TRANS]++; + } else if (strncmp(p, "G: n:2 ", 6) == 0){ + glstats->values[GLSTATS_INODE]++; + } else if (strncmp(p, "G: n:3 ", 6) == 0){ + glstats->values[GLSTATS_RGRP]++; + } else if (strncmp(p, "G: n:4 ", 6) == 0){ + glstats->values[GLSTATS_META]++; + } else if (strncmp(p, "G: n:5 ", 6) == 0){ + glstats->values[GLSTATS_IOPEN]++; + } else if (strncmp(p, "G: n:6 ", 6) == 0){ + glstats->values[GLSTATS_FLOCK]++; + } else if (strncmp(p, "G: n:8 ", 6) == 0){ + glstats->values[GLSTATS_QUOTA]++; + } else if (strncmp(p, "G: n:9 ", 6) == 0){ + glstats->values[GLSTATS_JOURNAL]++; + } + glstats->values[GLSTATS_TOTAL]++; + + /* + * We advance the cursor for after we read what type of lock we have + * for (p += 6; isspace((int)*p); p++) {;} + * + * [ We can extract any other future fields from here on] + * + */ + } + + fclose(fp); + return 0; +} diff --git a/src/pmdas/gfs2/glstats.h b/src/pmdas/gfs2/glstats.h new file mode 100644 index 0000000..09e5264 --- /dev/null +++ b/src/pmdas/gfs2/glstats.h @@ -0,0 +1,57 @@ +/* + * GFS2 glstats file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef GLSTATS_H +#define GLSTATS_H + +/* + * GFS2 glock type identification; note that the glock type 7 is currently not + * used and is reserved to be on the safe side. + * + * Type Lock type Use + * 1 Trans Transaction lock + * 2 Inode Inode metadata and data + * 3 Rgrp Resource group metadata + * 4 Meta The superblock + * 5 Iopen Inode last closer detection + * 6 Flock flock(2) syscall + * 8 Quota Quota operations + * 9 Journal Journal mutex + * + */ + +enum { + GLSTATS_TOTAL = 0, + GLSTATS_TRANS = 1, + GLSTATS_INODE = 2, + GLSTATS_RGRP = 3, + GLSTATS_META = 4, + GLSTATS_IOPEN = 5, + GLSTATS_FLOCK = 6, + GLSTATS_RESERVED_NOT_USED = 7, + GLSTATS_QUOTA = 8, + GLSTATS_JOURNAL = 9, + NUM_GLSTATS_STATS +}; + +struct glstats { + __uint64_t values[NUM_GLSTATS_STATS]; +}; + +extern int gfs2_glstats_fetch(int, struct glstats *, pmAtomValue *); +extern int gfs2_refresh_glstats(const char *, const char *, struct glstats *); + +#endif /* GLSTATS_H */ diff --git a/src/pmdas/gfs2/help b/src/pmdas/gfs2/help new file mode 100644 index 0000000..350bd2b --- /dev/null +++ b/src/pmdas/gfs2/help @@ -0,0 +1,566 @@ +# +# Copyright (c) 2013 - 2014 Red Hat. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# GFS2 PMDA help file in the ASCII format +# +# lines beginning with a # are ignored +# lines beginning @ introduce a new entry of the form +# @ metric_name oneline-text +# help test goes +# here over multiple lines +# ... +# +# the metric_name is decoded against the default PMNS -- as a special case, +# a name of the form NNN.MM (for numeric NNN and MM) is interpreted as an +# instance domain identification, and the text describes the instance domain +# +# blank lines before the @ line are ignored +# +@ GFS2.0 Instance domain for mounted GFS2 filesystems + +@ gfs2.glocks.total Count of total observed incore GFS2 global locks +Count of total incore GFS2 glock data structures based on parsing the contents +of the /sys/kernel/debug/gfs2/<bdev>/glocks files. + +@ gfs2.glocks.shared GFS2 global locks in shared state +Count of incore GFS2 glock data structures in shared state, based on parsing +/sys/kernel/debug/gfs2/<bdev>/glocks entries with state field (s:) value "SH". + +@ gfs2.glocks.unlocked GFS2 global locks in unlocked state +Count of incore GFS2 glock data structures in unlocked state, based on parsing +/sys/kernel/debug/gfs2/<bdev>/glocks entries with state field (s:) value "UN". + +@ gfs2.glocks.deferred GFS2 global locks in deferred state +Count of incore GFS2 glock data structures in deferred state, based on parsing +/sys/kernel/debug/gfs2/<bdev>/glocks entries with state field (s:) value "DF". + +@ gfs2.glocks.exclusive GFS2 global locks in exclusive state +Count of incore GFS2 glock data structures in exclusive state, based on parsing +/sys/kernel/debug/gfs2/<bdev>/glocks entries with state field (s:) value "EX". + +# help text for gfs2.sbstats.*.* is generated dynamically + +@ gfs2.glstats.total The total number of current glocks +Total count of the number of glocks which currently reside for filesystem on +the given node. Data is based from /sys/kernel/debug/gfs2/<bdev>/glstats +counting the total number of glock entries. + +@ gfs2.glstats.trans The number of transaction glocks +The count of the current number of transaction type glocks that currently exist +for the given filesystem. The data is recorded and counted from /sys/kernel/ +debug/gfs2/<bdev>glstats file entries for this type of glock. + +@ gfs2.glstats.inode The number of inode (metadata and data) glocks +The count of the current number of inode metadata and data type glocks that +currently exist for the given filesystem. The data is recorded and counted from +/sys/kernel/debug/gfs2/<bdev>glstats file entries for this type of glock. + +@ gfs2.glstats.rgrp The number of resource group metadata glocks +The count of the current number of resource group metadata type glocks that +currently exist for the given filesystem. The data is recorded and counted from +/sys/kernel/debug/gfs2/<bdev>glstats file entries for this type of glock. + +@ gfs2.glstats.meta The number of superblock glocks +The count of the current number of superblock type glocks that currently exist +for the given filesystem. The data is recorded and counted from /sys/kernel/ +debug/gfs2/<bdev>glstats file entries for this type of glock. + +@ gfs2.glstats.iopen The number of inode last closer detection glocks +The count of the current number of inode last closer detection type glocks that +currently exist for the given filesystem. The data is recorded and counted from +/sys/kernel/debug/gfs2/<bdev>glstats file entries for this type of glock. + +@ gfs2.glstats.flock The number of flock(2) syscall glocks +The count of the current number of flock(2) syscall type glocks that currently +exist for the given filesystem. The data is recorded and counted from /sys/ +kernel/debug/gfs2/<bdev>glstats file entries for this type of glock. + +@ gfs2.glstats.quota The number of quota operations glocks +The count of the current number of quota operations type glocks that currently +exist for the given filesystem. The data is recorded and counted from /sys/ +kernel/debug/gfs2/<bdev>glstats file entries for this type of glock. + +@ gfs2.glstats.journal The number of journal mutex glocks +The count of the current number of journal mutex type glocks that currently +exist for the given filesystem. The data is recorded and counted from /sys/ +kernel/debug/gfs2/<bdev>glstats file entries for this type of glock. + +@ gfs2.tracepoints.glock_state_change.total Total number of glock state +changes. The total number of counted glock state changes. + +@ gfs2.tracepoints.glock_state_change.null_lock Number of null_lock state +changes. The total number of glock state changes to the null_lock state. + +@ gfs2.tracepoints.glock_state_change.concurrent_read Number of +concurrent_read state changes. The total number of glock state changes +to current_read state. + +@ gfs2.tracepoints.glock_state_change.concurrent_write Number of +concurrent_write state changes. The total number of glock state changes +to current_write state. + +@ gfs2.tracepoints.glock_state_change.protected_read Number of +protected_read state changes. The total number of glock state changes to +protected_read state. + +@ gfs2.tracepoints.glock_state_change.protected_write Number of +protected_write state changes. The total number of glock state changes to +protected_write state. + +@ gfs2.tracepoints.glock_state_change.exclusive Number of exclusive state +changes. The total number of glock state changes to exclusive state. + +@ gfs2.tracepoints.glock_state_change.glocks.changed_target Number of +changed locks. The number of state changes that achieved their expected +state change. + +@ gfs2.tracepoints.glock_state_change.glocks.missed_target Number of +missed locks. The number of state changes that did not achieve their +expected state change. + +@ gfs2.tracepoints.glock_put.total Total number of glocks changed. +The total number of glocks that have been changed. + +@ gfs2.tracepoints.glock_put.null_lock Number of released locks. +The number of glocks put into the null_lock state. + +@ gfs2.tracepoints.glock_put.concurrent_read Number of glocks put +in concurrent_read. The number of glocks put into the concurrent_read +state. + +@ gfs2.tracepoints.glock_put.concurrent_write Number of glocks put +in concurrent_write. The number of glocks put into the concurrent_write +state. + +@ gfs2.tracepoints.glock_put.protected_read Number of glocks put +in protected_read. The number of glocks put into the protected_read +state. + +@ gfs2.tracepoints.glock_put.protected_write Number of glocks put +in protected_wirte. The number of glocks put into the protected_write +state. + +@ gfs2.tracepoints.glock_put.exclusive Number of glocks put +in exclusive. The number of glocks put into the exclusive +state. + +@ gfs2.tracepoints.demote_rq.total Total number of lock demote requests. +The total number of lock demotion requests. + +@ gfs2.tracepoints.demote_rq.null_lock Number of lock demote requests to +null_lock. The total number of lock demotion requests to the null_lock state. + +@ gfs2.tracepoints.demote_rq.concurrent_read Number of lock demote requests to +concurrent_read. The total number of lock demotion requests to the +concurrent_read state. + +@ gfs2.tracepoints.demote_rq.concurrent_write Number of lock demote requests to +concurrent_write. The total number of lock demotion requests to the +concurrent_write state. + +@ gfs2.tracepoints.demote_rq.protected_read Number of lock demote requests to +protected_read. The total number of lock demotion requests to the +protected_read state. + +@ gfs2.tracepoints.demote_rq.protected_write Number of lock demote requests to +protected_write. The total number of lock demotion requests to the +protected_write state. + +@ gfs2.tracepoints.demote_rq.exclusive Number of lock demote requests to +exclusive. The total number of lock demotion requests to the +exclusive state. + +@gfs2.tracepoints.demote_rq.requested.remote Number of demote requests (remote). +The total number of demote requests which were requested by a remote node of +the cluster. + +@gfs2.tracepoints.demote_rq.requested.local Number of demote requests (local). +The total number of demote requests which were requested by a local node of +the cluster + +@ gfs2.tracepoints.promote.total Total number of lock state. The total number +of lock state. + +@ gfs2.tracepoints.promote.first.null_lock Number of lock state to null_lock. +The total number of successful first time lock state to the null_lock state. + +@ gfs2.tracepoints.promote.first.concurrent_read Number of lock state to +concurrent_read. The total number of successful first time lock state to the +concurrent_read state. + +@ gfs2.tracepoints.promote.first.concurrent_write Number of lock state to +concurrent_write. The total number of successful first time lock state to the +concurrent_write state. + +@ gfs2.tracepoints.promote.first.protected_read Number of lock state to +protected_read. The total number of successful first time lock state to the +protected_read state. + +@ gfs2.tracepoints.promote.first.protected_write Number of lock state to +protected_write. The total number of successful first time lock state to the +protected_write state. + +@ gfs2.tracepoints.promote.first.exclusive Number of lock state to +exclusive. The total number of successful first time lock state to the +exclusive state. + +@ gfs2.tracepoints.promote.other.null_lock Number of lock state to null_lock. +The total number of successful other time lock state to the null_lock state. + +@ gfs2.tracepoints.promote.other.concurrent_read Number of lock state to +concurrent_read. The total number of successful other time lock state to the +concurrent_read state. + +@ gfs2.tracepoints.promote.other.concurrent_write Number of lock state to +concurrent_write. The total number of successful other time lock state to the +concurrent_write state. + +@ gfs2.tracepoints.promote.other.protected_read Number of lock state to +protected_read. The total number of successful other time lock state to the +protected_read state. + +@ gfs2.tracepoints.promote.other.protected_write Number of lock state to +protected_write. The total number of successful other time lock state to the +protected_write state. + +@ gfs2.tracepoints.promote.other.exclusive Number of lock state to +exclusive. The total number of successful other time lock state to the +exclusive state. + +@ gfs2.tracepoints.glock_queue.total Total numbe rof queued and dequeued +requests. The total number of both queued and dequeued requests. + +@ gfs2.tracepoints.glock_queue.queue.total Total number of queued lock requests. +The total number of queued lock requests. + +@ gfs2.tracepoints.glock_queue.queue.null_lock Number of null_lock requests. The +number of lock requests to the null_lock state. + +@ gfs2.tracepoints.glock_queue.queue.concurrent_read Number of concurrent_read +requests. The number of lock requests to the concurrent_read state. + +@ gfs2.tracepoints.glock_queue.queue.concurrent_write Number of concurrent_write +requests. The number of lock requests to the concurrent_write state. + +@ gfs2.tracepoints.glock_queue.queue.protected_read Number of protected_read +requests. The number of lock requests to the protected_read state. + +@ gfs2.tracepoints.glock_queue.queue.protected_write Number of protected_write +requests. The number of lock requests to the protected_write state. + +@ gfs2.tracepoints.glock_queue.queue.exclusive Number of exclusive +requests. The number of lock requests to the exclusive state. + +@ gfs2.tracepoints.glock_queue.dequeue.total Total number of dequeued lock requests. +The total number of dequeued lock requests. + +@ gfs2.tracepoints.glock_queue.dequeue.null_lock Number of null_lock requests. The +number of lock requests to the null_lock state. + +@ gfs2.tracepoints.glock_queue.dequeue.concurrent_read Number of concurrent_read +requests. The number of lock requests to the concurrent_read state. + +@ gfs2.tracepoints.glock_queue.dequeue.concurrent_write Number of concurrent_write +requests. The number of lock requests to the concurrent_write state. + +@ gfs2.tracepoints.glock_queue.dequeue.protected_read Number of protected_read +requests. The number of lock requests to the protected_read state. + +@ gfs2.tracepoints.glock_queue.dequeue.protected_write Number of protected_write +requests. The number of lock requests to the protected_write state. + +@ gfs2.tracepoints.glock_queue.dequeue.exclusive Number of exclusive +requests. The number of lock requests to the exclusive state. + +@ gfs2.tracepoints.glock_lock_time.total Total number of lock updates. +The total number of lock updates. + +@ gfs2.tracepoints.glock_lock_time.trans Number of transaction lock updates. +The number of updates for transaction based glocks. + +@ gfs2.tracepoints.glock_lock_time.inode Number of inode lock updates. +The number of updates for inode based glocks. + +@ gfs2.tracepoints.glock_lock_time.rgrp Number of resource group lock updates. +The number of updates for resource group based glocks. + +@ gfs2.tracepoints.glock_lock_time.meta Number of metadata lock updates. +The number of updates for metadata based glocks. + +@ gfs2.tracepoints.glock_lock_time.iopen Number of iopen lock updates. +The number of updates for iopen based glocks. + +@ gfs2.tracepoints.glock_lock_time.flock Number of flock lock updates. +The number of updates for flock based glocks. + +@ gfs2.tracepoints.glock_lock_time.quota Number of quota lock updates. +The number of updates for quota based glocks. + +@ gfs2.tracepoints.glock_lock_time.journal Number of journal lock updates. +The number of updates for journal based glocks. + +@ gfs2.tracepoints.pin.total Total number of Pin/Unpin requests. The total +number of requests to pin/unpin blocks on the log. + +@ gfs2.tracepoints.pin.pin_total Number of pin requests. The total number of +requests to pin blocks on the log. + +@ gfs2.tracepoints.pin.unpin_total Number of unpin requests. The total number +requests to unpin blocks on the log. + +@ gfs2.tracepoints.pin.longest_pinned Longest pinned. The longest pinned +inode or resource group log block + +@ gfs2.tracepoints.log_flush.total Total log flushes. The total number of +log flushes observed + +@ gfs2.tracepoints.log_block.total Total log blocks. The total number of +blocks placed upon the log. + +@ gfs2.tracepoints.ail_flush.total Total AIL flushes. The total number of +flushes back to the AIL. + +@ gfs2.tracepoints.block_alloc.total Total blocks allocated/deallocated. +The total number of allocated/freed blocks this call. + +@ gfs2.tracepoints.block_alloc.free Freed blocks. The number of blocks +freed. + +@ gfs2.tracepoints.block_alloc.used Used blocks. The number of blocks +used. + +@ gfs2.tracepoints.block_alloc.dinode Dinode blocks. The number of blocks +used for dinode. + +@ gfs2.tracepoints.block_alloc.unlinked Unlinked blocks. The number of +unlinked blocks. + +@ gfs2.tracepoints.bmap.total Total number of bmap allocations. The total +number of bmap allocations. + +@ gfs2.tracepoints.bmap.create Number of create bmap allocations. The number +of create bmap allocations. + +@ gfs2.tracepoints.bmap.nocreate Number of nocreate bmap allocations. The +number of nocreate bmap allocations. + +@ gfs2.tracepoints.rs.total Total multi-block allocations. The total number +of multi-block allocations. + +@ gfs2.tracepoints.rs.del Number of resource group delete. The total number of +resource group delete calls. + +@ gfs2.tracepoints.rs.tdel Number of resource group tree delete. The total number +of resource group tree delete calls. + +@ gfs2.tracepoints.rs.ins Number of resource group insert. The total number of +resource group insert calls. + +@ gfs2.tracepoints.rs.clm Number of resource group claims. The total number of +resource group claim calls. + +# help text for gfs2.worst_glock.*.* is generated dynamically + +@ gfs2.latency.grant.all Average time in ms for all states. The total average +latency time in ms for all lock states for grants. + +@ gfs2.latency.grant.null_lock Average time in ms to null lock state. The +total average latency time in ms to change to null lock state for grants. + +@ gfs2.latency.grant.concurrent_read Average time in ms to concurrent read +lock state. The total average latency time in ms to change to concurrent read +lock state for grants. + +@ gfs2.latency.grant.concurrent_write Average time in ms to concurrent write +lock state. The total average latency time in ms to change to concurrent write +lock state for grants. + +@ gfs2.latency.grant.protected_read Average time in ms to protected read +lock state. The total average latency time in ms to change to protected read +lock state for grants. + +@ gfs2.latency.grant.protected_write Average time in ms to protected write +lock state. The total average latency time in ms to change to protected write +lock state for grants. + +@ gfs2.latency.grant.exclusive Average time in ms to exclusive lock state. The +total average latency time in ms to change to exclusive lock state for grants. + +@ gfs2.latency.demote.all Average time in ms for all states. The total average +latency time in ms for all lock states for demotes. + +@ gfs2.latency.demote.null_lock Average time in ms to null lock state. The +total average latency time in ms to change to null lock state for demotes. + +@ gfs2.latency.demote.concurrent_read Average time in ms to concurrent read +lock state. The total average latency time in ms to change to concurrent read +lock state for demotes. + +@ gfs2.latency.demote.concurrent_write Average time in ms to concurrent write +lock state. The total average latency time in ms to change to concurrent write +lock state for demotes. + +@ gfs2.latency.demote.protected_read Average time in ms to protected read +lock state. The total average latency time in ms to change to protected read +lock state for demotes. + +@ gfs2.latency.demote.protected_write Average time in ms to protected write +lock state. The total average latency time in ms to change to protected write +lock state for demotes. + +@ gfs2.latency.demote.exclusive Average time in ms to exclusive lock state. +The total average latency time in ms to change to exclusive lock state for +demotes. + +@ gfs2.latency.queue.all Average time in ms for all states. The total average +latency time in ms for all lock states for queues. + +@ gfs2.latency.queue.null_lock Average time in ms to null lock state. The +total average latency time in ms to change to null lock state for queues. + +@ gfs2.latency.queue.concurrent_read Average time in ms to concurrent read +lock state. The total average latency time in ms to change to concurrent read +lock state for queues. + +@ gfs2.latency.queue.concurrent_write Average time in ms to concurrent write +lock state. The total average latency time in ms to change to concurrent write +lock state for queues. + +@ gfs2.latency.queue.protected_read Average time in ms to protected read +lock state. The total average latency time in ms to change to protected read +lock state for queues. + +@ gfs2.latency.queue.protected_write Average time in ms to protected write +lock state. The total average latency time in ms to change to protected write +lock state for queues. + +@ gfs2.latency.queue.exclusive Average time in ms to exclusive lock state. +The total average latency time in ms to change to exclusive lock state for +queues. + +@ gfs2.control.tracepoints.all Indication whether glock statistics are enabled +The gfs2 tracepoint statistics can be manually controlled using pmstore +gfs2.control.tracepoints.all 0 [off] or 1 [on]. Setting the value of the metric +controls the behavior of the PMDA to whether it tries to collect from tracepoint +metrics or not. + +@ gfs2.control.tracepoints.glock_state_change Indication whether +glock_state_change glock stats are enabled. The gfs2 tracepoint statistics +can be manually controlled using pmstore +gfs2.control.tracepoints.glock_state_change 0 [off] or 1 [on]. Setting the +value of the metric controls the behavior of the PMDA to whether it tries to +collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.glock_put Indication whether glock_put glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.glock_put 0 [off] or 1 [on]. Setting the value +of the metric controls the behavior of the PMDA to whether it tries to collect +from tracepoint metrics or not. + +@ gfs2.control.tracepoints.demote_rq Indication whether glock_demote_rq glock +stats are enabled. The gfs2 tracepoint statistics can be manually controlled +using pmstore gfs2.control.tracepoints.glock_demote_rq 0 [off] or 1 [on]. +Setting the value of the metric controls the behavior of the PMDA to whether +it tries to collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.promote Indication whether glock_promote glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.glock_promte 0 [off] or 1 [on]. Setting the +value of the metric controls the behavior of the PMDA to whether it tries to +collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.glock_queue Indication whether glock_queue glock +stats are enabled. The gfs2 tracepoint statistics can be manually controlled +using pmstore gfs2.control.tracepoints.glock_queue 0 [off] or 1 [on]. Setting +the value of the metric controls the behavior of the PMDA to whether it tries +to collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.glock_lock_time Indication whether glock_lock_time +glock stats are enabled. The gfs2 tracepoint statistics can be manually +controlled using pmstore gfs2.control.tracepoints.glock_lock_time 0 [off] or 1 +[on]. Setting the value of the metric controls the behavior of the PMDA to +whether it tries to collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.pin Indication whether pin glock stats are enabled. +The gfs2 tracepoint statistics can be manually controlled using pmstore +gfs2.control.tracepoints.pin 0 [off] or 1 [on]. Setting the value of the +metric controls the behavior of the PMDA to whether it tries to collect from +tracepoint metrics or not. + +@ gfs2.control.tracepoints.log_flush Indication whether log_flush glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.log_flush 0 [off] or 1 [on]. Setting the +value of the metric controls the behavior of the PMDA to whether it tries to +collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.log_blocks Indication whether log_blocks glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.log_blocks 0 [off] or 1 [on]. Setting the +value of the metric controls the behavior of the PMDA to whether it tries to +collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.ail_flush Indication whether ail_flush glock stats +are enabled. The gfs2 tracepoint statistics can be manually controlled using +pmstore gfs2.control.tracepoints.ail_flush 0 [off] or 1 [on]. Setting the value +of the metric controls the behavior of the PMDA to whether it tries to collect +from tracepoint metrics or not. + +@ gfs2.control.tracepoints.block_alloc Indication whether block_alloc glock +stats are enabled. The gfs2 tracepoint statistics can be manually controlled +using pmstore gfs2.control.tracepoints.block_alloc 0 [off] or 1 [on]. Setting +the value of the metric controls the behavior of the PMDA to whether it tries +to collect from tracepoint metrics or not. + +@ gfs2.control.tracepoints.bmap Indication whether bmap glock stats are enabled. +The gfs2 tracepoint statistics can be manually controlled using pmstore +gfs2.control.tracepoints.bmap 0 [off] or 1 [on]. Setting the value of the +metric controls the behavior of the PMDA to whether it tries to collect from +tracepoint metrics or not. + +@ gfs2.control.tracepoints.rs Indication whether rs glock stats are enabled. +The gfs2 tracepoint statistics can be manually controlled using pmstore +gfs2.control.tracepoints.rs 0 [off] or 1 [on]. Setting the value of the metric +controls the behavior of the PMDA to whether it tries to collect from +tracepoint metrics or not. + +@ gfs2.control.buffer_size_kb Sets the buffer size for trace_pipe (per cpu). +The size of the trace_pipe buffer can be controlled with this metrics, it +allows the increase of the trace_pipe buffer to 128MB (131072KB) per cpu +on the system. It is useful to increase the size of the buffer when there +is expected to be heavy load on the file system in order to reduce the +risk of overwritten entries in the trace_pipe before they are read (default +value is 32MB (32768KB). + +@ gfs2.control.global_tracing Indication whether global tracing is enabled. +The global tracing can be controlled using pmstore gfs2.control.global_tracing +0 [off] or 1 [on]. This is required to be on for most of the gfs2 metrics to +function. + +@ gfs2.control.worst_glock Indication whether gfs2.glock_lock_time statistics +are enabled. The gfs2.glock_lock_time statistics can be manually controlled +using pmstore gfs2.control.glock_lock_time 0 [off] or 1 [on]. Setting the value +of the metric controls the behavior of the PMDA to whether it tries to collect +the lock_time metrics or not. The machine must have the gfs2 trace-points +available for the glock_lock_time based metrics to function. + +@ gfs2.control.latency Indication whether gfs2.latency statistics are enabled. +The gfs2.latency statistics can be manually controlled using pmstore +gfs2.control.latency 0 [off] or 1 [on]. Setting the value of the metric +controls the behaviour of the PMDA to whether it tries to collect the latency +metrics or not. The machice must have the gfs2 trace-points available for the +latency metrics to function. + +@ gfs2.control.glock_threshold Threshold for maximum number of glocks accepted +per fetch. The number of glocks that will be processed and accepted over all +ftrace read trace statistics. This number can be manually altered using pmstore +in order to tailor the number of glocks processed. This value must be positive. diff --git a/src/pmdas/gfs2/latency.c b/src/pmdas/gfs2/latency.c new file mode 100644 index 0000000..0b93ca9 --- /dev/null +++ b/src/pmdas/gfs2/latency.c @@ -0,0 +1,373 @@ +/* + * GFS2 latency metrics. + * + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "pmdagfs2.h" + +#include "ftrace.h" +#include "worst_glock.h" +#include "latency.h" + +#include <string.h> +#include <inttypes.h> +#include <sys/sysmacros.h> +#include <sys/types.h> + +static struct ftrace_data latency_data; +static int reset_flag; + +static int latency_state = DEFAULT_LATENCY_STATE; + +/* + * Calculates the offset position for the flat array as if it was + * a three dimentional array containing the latency values. + */ +int +offset(int x, int y, int z) +{ + return (z * NUM_LATENCY_STATS * NUM_LATENCY_VALUES) + (y * NUM_LATENCY_STATS) + x ; +} + +/* + * Fetches the value for the given metric item and then assigns to pmAtomValue. + * We check to see if item is in valid range for the metric. + */ +int +gfs2_latency_fetch(int item, struct latency *latency, pmAtomValue *atom) +{ + int i, counter, position, results_used = 0; + int64_t result = 0; + + /* We are assigning values so we want to reset on next extract */ + reset_flag = 1; + + /* Ensure that metric value wanted is valid */ + if ((item < 0 || item >= NUM_LATENCY_STATS)) + return PM_ERR_PMID; + + counter = latency->counter[item]; + + /* Calculate latency for the metric (deduct start time and add the matching finish time) */ + for (i = 0; i < counter; i++) { + position = offset(item, i, END); + result += latency->values[position].usecs; + + position = offset(item, i, START); + result -= latency->values[position].usecs; + + results_used++; + } + /* If we have no values return no values */ + if (results_used == 0) + return 0; + + /* Return no values if negative result */ + if (result < 0 ) + return 0; + + /* Divide final value by number of counts */ + result /= results_used; + + /* Assign value to the metric */ + atom->ll = result; + + return 1; +} + +/* + * Sets the value of latency using pmstore, value + * must be 0 or 1. + */ +int +latency_set_state(pmValueSet *vsp) +{ + int value = vsp->vlist[0].value.lval; + + if (value == 0 || value == 1) { + latency_state = value; + + return 0; + } else { + return PM_ERR_SIGN; + } +} + +/* + * Used to see whether the latency metrics are enabled or disabled. Should + * only return either 0 or 1. + */ +int +latency_get_state() +{ + return latency_state; +} + +/* + * Concatenates the ftrace time stamp (major and minor) components into one + * uint64_t timestamp in usecs. + * + */ +static int64_t +concatenate(int64_t a, int64_t b) +{ + unsigned int power = 10; + + while(a >= power) + power *= 10; + + return a * power + b; +} + +/* + * Converts lock state to an integer + */ +static int +lock_to_decimal(char *state) +{ + if (strncmp(state, "NL", 2) == 0) { + return 0; + } else if (strncmp(state, "CR", 2) == 0) { + return 1; + } else if (strncmp(state, "CW", 2) == 0) { + return 2; + } else if (strncmp(state, "PR", 2) == 0) { + return 3; + } else if (strncmp(state, "PW", 2) == 0) { + return 4; + } else if (strncmp(state, "EX", 2) == 0) { + return 5; + } + + return 0; +} + +/* + * Updates the records held in the fs->latency structure with new latency + * stats. + */ +static int +update_records(struct gfs2_fs *fs, int metric, struct latency_data data, int placement) +{ + int i, position, counter; + struct latency_data blank = { 0 }; + + counter = fs->latency.counter[metric]; + + /* If we have an intial value */ + if (placement == START) { + position = offset(metric, counter, START); + fs->latency.values[position] = data; + + position = offset(metric, counter, END); + fs->latency.values[position] = blank; + + fs->latency.counter[metric] = (counter + 1) % NUM_LATENCY_VALUES; + + /* If we have a final value */ + } else if (placement == END) { + for (i = 0; i < counter; i++){ + position = offset(metric, i, START); + + if ((fs->latency.values[position].lock_type == data.lock_type) && + (fs->latency.values[position].number == data.number) && + (fs->latency.values[position].usecs < data.usecs )) { + + position = offset(metric, i, END); + fs->latency.values[position] = data; + + return 0; + } + } + } + return 0; +} + +/* + * We work out the individual metric values from our buffer input and store + * them for processing after all of the values have been extracted from the + * trace pipe. + */ +int +gfs2_extract_latency(unsigned int major, unsigned int minor, int tracepoint, char *data, pmInDom gfs_fs_indom) +{ + latency_data.dev_id = makedev(major, minor); + latency_data.tracepoint = tracepoint; + strncpy(latency_data.data, data, sizeof(latency_data.data)-1); + + int i, sts; + struct gfs2_fs *fs; + + /* We walk through for each filesystem */ + for (pmdaCacheOp(gfs_fs_indom, PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(gfs_fs_indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + sts = pmdaCacheLookup(gfs_fs_indom, i, NULL, (void **)&fs); + if (sts != PMDA_CACHE_ACTIVE) + continue; + + /* Clear old entries if reset is set */ + if (reset_flag == 1) { + memset(fs->latency.values, 0, sizeof fs->latency.values); + memset(fs->latency.counter, 0, sizeof fs->latency.counter); + reset_flag = 0; + } + + /* Is the entry matching the filesystem we are on? */ + if (fs->dev_id != latency_data.dev_id) + continue; + + if (latency_data.tracepoint == GLOCK_QUEUE) { + + struct latency_data data; + int64_t time_major, time_minor; + char queue[8], state[3]; + + sscanf(latency_data.data, + "%*s [%*d] %"SCNd64".%"SCNd64": gfs2_glock_queue: %*d,%*d glock %"SCNu32":%"SCNu64" %s %s", + &time_major, &time_minor, &data.lock_type, &data.number, queue, state + ); + data.usecs = concatenate(time_major, time_minor); + + if (data.lock_type == WORSTGLOCK_INODE || data.lock_type == WORSTGLOCK_RGRP) { + + /* queue trace data is used both for latency.grant and latency.queue */ + if (strncmp(queue, "queue", 6) == 0) { + if (strncmp(state, "NL", 2) == 0) { + update_records(fs, LATENCY_GRANT_NL, data, START); + update_records(fs, LATENCY_QUEUE_NL, data, START); + } else if (strncmp(state, "CR", 2) == 0) { + update_records(fs, LATENCY_GRANT_CR, data, START); + update_records(fs, LATENCY_QUEUE_CR, data, START); + } else if (strncmp(state, "CW", 2) == 0) { + update_records(fs, LATENCY_GRANT_CW, data, START); + update_records(fs, LATENCY_QUEUE_CW, data, START); + } else if (strncmp(state, "PR", 2) == 0) { + update_records(fs, LATENCY_GRANT_PR, data, START); + update_records(fs, LATENCY_QUEUE_PR, data, START); + } else if (strncmp(state, "PW", 2) == 0) { + update_records(fs, LATENCY_GRANT_PW, data, START); + update_records(fs, LATENCY_QUEUE_PW, data, START); + } else if (strncmp(state, "EX", 2) == 0) { + update_records(fs, LATENCY_GRANT_EX, data, START); + update_records(fs, LATENCY_QUEUE_EX, data, START); + } + update_records(fs, LATENCY_GRANT_ALL, data, START); + update_records(fs, LATENCY_QUEUE_ALL, data, START); + + } else if (strncmp(queue, "dequeue", 8) == 0) { + if (strncmp(state, "NL", 2) == 0) { + update_records(fs, LATENCY_QUEUE_NL, data, END); + } else if (strncmp(state, "CR", 2) == 0) { + update_records(fs, LATENCY_QUEUE_CR, data, END); + } else if (strncmp(state, "CW", 2) == 0) { + update_records(fs, LATENCY_QUEUE_CW, data, END); + } else if (strncmp(state, "PR", 2) == 0) { + update_records(fs, LATENCY_QUEUE_PR, data, END); + } else if (strncmp(state, "PW", 2) == 0) { + update_records(fs, LATENCY_QUEUE_PW, data, END); + } else if (strncmp(state, "EX", 2) == 0) { + update_records(fs, LATENCY_QUEUE_EX, data, END); + } + update_records(fs, LATENCY_QUEUE_ALL, data, END); + } + } + } else if (latency_data.tracepoint == GLOCK_STATE_CHANGE) { + + struct latency_data data; + int64_t time_major, time_minor; + char state[3], to[3], target[3]; + int state_decimal, to_decimal; + + sscanf(latency_data.data, + "%*s [%*d] %"SCNd64".%"SCNd64": gfs2_glock_state_change: %*d,%*d glock %"SCNu32":%"SCNu64" state %s to %s tgt:%s dmt:%*s flags:%*s", + &time_major, &time_minor, &data.lock_type, &data.number, state, to, target + ); + data.usecs = concatenate(time_major, time_minor); + state_decimal = lock_to_decimal(state); + to_decimal = lock_to_decimal(to); + + if (data.lock_type == WORSTGLOCK_INODE || data.lock_type == WORSTGLOCK_RGRP) { + + /* state change trace data is used both for latency.grant and latency.demote */ + if ((state_decimal < to_decimal) && (strncmp(to, target, 2) == 0)) { + if (strncmp(to, "NL", 2) == 0) { + update_records(fs, LATENCY_GRANT_NL, data, END); + } else if (strncmp(to, "CR", 2) == 0) { + update_records(fs, LATENCY_GRANT_CR, data, END); + } else if (strncmp(to, "CW", 2) == 0) { + update_records(fs, LATENCY_GRANT_CW, data, END); + } else if (strncmp(to, "PR", 2) == 0) { + update_records(fs, LATENCY_GRANT_PR, data, END); + } else if (strncmp(to, "PW", 2) == 0) { + update_records(fs, LATENCY_GRANT_PW, data, END); + } else if (strncmp(to, "EX", 2) == 0) { + update_records(fs, LATENCY_GRANT_EX, data, END); + } + update_records(fs, LATENCY_GRANT_ALL, data, END); + + } else if ((state_decimal > to_decimal) && (strncmp(to, target, 2) == 0)) { + if (strncmp(state, "NL", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_NL, data, END); + } else if (strncmp(state, "CR", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_CR, data, END); + } else if (strncmp(state, "CW", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_CW, data, END); + } else if (strncmp(state, "PR", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_PR, data, END); + } else if (strncmp(state, "PW", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_PW, data, END); + } else if (strncmp(state, "EX", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_EX, data, END); + } + update_records(fs, LATENCY_DEMOTE_ALL, data, END); + } + } + } else if (latency_data.tracepoint == DEMOTE_RQ) { + + struct latency_data data; + int64_t time_major, time_minor; + char state[3]; + + sscanf(latency_data.data, + "%*s [%*d] %"SCNd64".%"SCNd64": gfs2_demote_rq: %*d,%*d glock %"SCNu32":%"SCNu64" demote %s to %*s flags:%*s %*s", + &time_major, &time_minor, &data.lock_type, &data.number, state + ); + data.usecs = concatenate(time_major, time_minor); + + if ((data.lock_type == WORSTGLOCK_INODE) || (data.lock_type == WORSTGLOCK_RGRP)) { + + /* demote rq trace data is used for latency.demote */ + if (strncmp(state, "NL", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_NL, data, START); + } else if (strncmp(state, "CR", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_CR, data, START); + } else if (strncmp(state, "CW", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_CW, data, START); + } else if (strncmp(state, "PR", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_PR, data, START); + } else if (strncmp(state, "PW", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_PW, data, START); + } else if (strncmp(state, "EX", 2) == 0) { + update_records(fs, LATENCY_DEMOTE_EX, data, START); + } + update_records(fs, LATENCY_DEMOTE_ALL, data, START); + } + } + } + return 0; +} diff --git a/src/pmdas/gfs2/latency.h b/src/pmdas/gfs2/latency.h new file mode 100644 index 0000000..aa2a1ef --- /dev/null +++ b/src/pmdas/gfs2/latency.h @@ -0,0 +1,70 @@ +/* + * GFS2 latency metrics. + * + * Copyright (c) 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef LATENCY_H +#define LATENCY_H + +#define DEFAULT_LATENCY_STATE 1 +#define NUM_LATENCY_VALUES 512 + +enum{ + START = 0, + END +}; + +enum { + LATENCY_GRANT_ALL = 0, + LATENCY_GRANT_NL, + LATENCY_GRANT_CR, + LATENCY_GRANT_CW, + LATENCY_GRANT_PR, + LATENCY_GRANT_PW, + LATENCY_GRANT_EX, + LATENCY_DEMOTE_ALL, + LATENCY_DEMOTE_NL, + LATENCY_DEMOTE_CR, + LATENCY_DEMOTE_CW, + LATENCY_DEMOTE_PR, + LATENCY_DEMOTE_PW, + LATENCY_DEMOTE_EX, + LATENCY_QUEUE_ALL, + LATENCY_QUEUE_NL, + LATENCY_QUEUE_CR, + LATENCY_QUEUE_CW, + LATENCY_QUEUE_PR, + LATENCY_QUEUE_PW, + LATENCY_QUEUE_EX, + NUM_LATENCY_STATS +}; + +struct latency_data { + uint32_t lock_type; + uint64_t number; + int64_t usecs; +}; + +struct latency { + struct latency_data values [NUM_LATENCY_STATS * NUM_LATENCY_VALUES * 2]; /* START and STOP values */ + int counter [NUM_LATENCY_STATS]; +}; + +extern int gfs2_latency_fetch(int, struct latency *, pmAtomValue *); +extern int gfs2_extract_latency(unsigned int, unsigned int, int, char *, pmInDom); + +extern int latency_get_state(); +extern int latency_set_state(pmValueSet *vsp); + +#endif /* LATENCY_H */ diff --git a/src/pmdas/gfs2/pmda.c b/src/pmdas/gfs2/pmda.c new file mode 100644 index 0000000..2090296 --- /dev/null +++ b/src/pmdas/gfs2/pmda.c @@ -0,0 +1,1063 @@ +/* + * Global Filesystem v2 (GFS2) PMDA + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "domain.h" + +#include "pmdagfs2.h" + +#include <sys/types.h> +#include <ctype.h> + +static char *gfs2_sysfsdir = "/sys/kernel/debug/gfs2"; +static char *gfs2_sysdir = "/sys/fs/gfs2"; + +pmdaIndom indomtable[] = { + { .it_indom = GFS_FS_INDOM }, +}; + +#define INDOM(x) (indomtable[x].it_indom) + +/* + * all metrics supported in this PMDA - one table entry for each + * + */ +pmdaMetric metrictable[] = { + /* GLOCK */ + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_SHARED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_UNLOCKED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_DEFERRED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLOCKS, GLOCKS_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) } }, + /* SBSTATS */ + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SRTT), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SRTTVAR), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) } }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SRTTB), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SRTTVARB), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SIRT), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_SIRTVAR), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,PM_TIME_NSEC,0,0,1,0) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_DCOUNT), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_SBSTATS, LOCKSTAT_QCOUNT), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_COUNTER, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + /* GLSTATS */ + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_TRANS), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_INODE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_RGRP), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_META), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_IOPEN), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_FLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_QUOTA), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_GLSTATS, GLSTATS_JOURNAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + /* TRACEPOINTS */ + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_GLOCK_CHANGEDTARGET), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKSTATE_GLOCK_MISSEDTARGET), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKPUT_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_REQUESTED_REMOTE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_DEMOTERQ_REQUESTED_LOCAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_FIRST_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PROMOTE_OTHER_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_QUEUE_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_NULLLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_CONCURRENTWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDREAD), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_PROTECTEDWRITE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKQUEUE_DEQUEUE_EXCLUSIVE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_TRANS), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_INDOE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_RGRP), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_META), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_IOPEN), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_FLOCK), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_QUOTA), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_GLOCKLOCKTIME_JOURNAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PIN_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PIN_PINTOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PIN_UNPINTOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_PIN_LONGESTPINNED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_LOGFLUSH_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_LOGBLOCKS_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_AILFLUSH_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_FREE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_USED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_DINODE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BLOCKALLOC_UNLINKED), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BMAP_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BMAP_CREATE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_BMAP_NOCREATE), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_TOTAL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_DEL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_TDEL), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_INS), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_TRACEPOINTS, FTRACE_RS_CLM), + PM_TYPE_U64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + /* WORST_GLOCKS */ + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_LOCK_TYPE), + PM_TYPE_U32, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_NUMBER), + PM_TYPE_U32, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SRTT), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SRTTVAR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SRTTB), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SRTTVARB), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SIRT), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_SIRTVAR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_DLM), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_WORSTGLOCK, WORSTGLOCK_QUEUE), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,0,PM_COUNT_ONE) }, }, + /* LATENCY */ + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_ALL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_NL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_CR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_CW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_PR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_PW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_GRANT_EX), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_ALL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_NL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_CR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_CW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_PR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_PW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_DEMOTE_EX), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_ALL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_NL), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_CR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_CW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_PR), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_PW), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + { .m_desc = { + PMDA_PMID(CLUSTER_LATENCY, LATENCY_QUEUE_EX), + PM_TYPE_64, GFS_FS_INDOM, PM_SEM_INSTANT, + PMDA_PMUNITS(0,0,1,0,PM_TIME_MSEC,PM_COUNT_ONE) }, }, + /* CONTROL */ + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_ALL), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOCK_STATE_CHANGE), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOCK_PUT), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_DEMOTE_RQ), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_PROMOTE), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOCK_QUEUE), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOCK_LOCK_TIME), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_PIN), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_LOG_FLUSH), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_LOG_BLOCKS), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_AIL_FLUSH), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_BLOCK_ALLOC), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_BMAP), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_RS), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_BUFFER_SIZE_KB), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_GLOBAL_TRACING), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_WORSTGLOCK), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_LATENCY), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, + { NULL, { + PMDA_PMID(CLUSTER_CONTROL, CONTROL_FTRACE_GLOCK_THRESHOLD), + PM_TYPE_U32, PM_INDOM_NULL, PM_SEM_DISCRETE, + PMDA_PMUNITS(0,0,0,0,0,0) }, }, +}; + +int +metrictable_size(void) +{ + return sizeof(metrictable)/sizeof(metrictable[0]); +} + +static dev_t +gfs2_device_identifier(const char *name) +{ + char buffer[4096]; + int major, minor; + dev_t dev_id; + FILE *fp; + + dev_id = makedev(0, 0); /* Error case */ + + /* gfs2_glock_lock_time requires block device id for each filesystem + * in order to match to the lock data, this info can be found in + * /sys/fs/gfs2/NAME/id, we extract the data and store it in gfs2_fs->dev + * + */ + snprintf(buffer, sizeof(buffer), "%s/%s/id", gfs2_sysdir, name); + buffer[sizeof(buffer)-1] = '\0'; + + if ((fp = fopen(buffer, "r")) == NULL) + return oserror(); + + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + sscanf(buffer, "%d:%d", &major, &minor); + dev_id = makedev(major, minor); + } + fclose(fp); + + return dev_id; +} + +/* + * Update the GFS2 filesystems instance domain. This will change + * as filesystems are mounted and unmounted (and re-mounted, etc). + * Using the pmdaCache interfaces simplifies things and provides us + * with guarantees around consistent instance numbering in all of + * those interesting corner cases. + */ +static int +gfs2_instance_refresh(void) +{ + int i, sts, count, gfs2_status; + struct dirent **files; + pmInDom indom = INDOM(GFS_FS_INDOM); + + pmdaCacheOp(indom, PMDA_CACHE_INACTIVE); + + /* update indom cache based on scan of /sys/fs/gfs2 */ + count = scandir(gfs2_sysdir, &files, NULL, NULL); + if (count < 0) { /* give some feedback as to GFS2 kernel state */ + if (oserror() == EPERM) + gfs2_status = PM_ERR_PERMISSION; + else if (oserror() == ENOENT) + gfs2_status = PM_ERR_AGAIN; /* we might see a mount later */ + else + gfs2_status = PM_ERR_APPVERSION; + } else { + gfs2_status = 0; /* we possibly have stats available */ + } + + for (i = 0; i < count; i++) { + struct gfs2_fs *fs; + const char *name = files[i]->d_name; + + if (name[0] == '.') + continue; + + sts = pmdaCacheLookupName(indom, name, NULL, (void **)&fs); + if (sts == PM_ERR_INST || (sts >= 0 && fs == NULL)){ + fs = calloc(1, sizeof(struct gfs2_fs)); + if (fs == NULL) + return PM_ERR_AGAIN; + + fs->dev_id = gfs2_device_identifier(name); + + if ((major(fs->dev_id) == 0) && (minor(fs->dev_id) == 0)) { + free(fs); + return PM_ERR_AGAIN; + } + } + else if (sts < 0) + continue; + + /* (re)activate this entry for the current query */ + pmdaCacheStore(indom, PMDA_CACHE_ADD, name, (void *)fs); + } + + for (i = 0; i < count; i++) + free(files[i]); + if (count > 0) + free(files); + return gfs2_status; +} + +static int +gfs2_instance(pmInDom indom, int inst, char *name, __pmInResult **result, pmdaExt *pmda) +{ + gfs2_instance_refresh(); + return pmdaInstance(indom, inst, name, result, pmda); +} + +static int +gfs2_fetch_refresh(pmdaExt *pmda, int *need_refresh) +{ + pmInDom indom = INDOM(GFS_FS_INDOM); + struct gfs2_fs *fs; + char *name; + int i, sts; + + if ((sts = gfs2_instance_refresh()) < 0) + return sts; + + for (pmdaCacheOp(indom, PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + if (!pmdaCacheLookup(indom, i, &name, (void **)&fs) || !fs) + continue; + if (need_refresh[CLUSTER_GLOCKS]) + gfs2_refresh_glocks(gfs2_sysfsdir, name, &fs->glocks); + if (need_refresh[CLUSTER_SBSTATS]) + gfs2_refresh_sbstats(gfs2_sysfsdir, name, &fs->sbstats); + if (need_refresh[CLUSTER_GLSTATS]) + gfs2_refresh_glstats(gfs2_sysfsdir, name, &fs->glstats); + } + + if (need_refresh[CLUSTER_TRACEPOINTS] || need_refresh[CLUSTER_WORSTGLOCK] || need_refresh[CLUSTER_LATENCY]) + gfs2_refresh_ftrace_stats(indom); + + return sts; +} + +static int +gfs2_fetch(int numpmid, pmID pmidlist[], pmResult **resp, pmdaExt *pmda) +{ + int i, sts, need_refresh[NUM_CLUSTERS] = { 0 }; + + for (i = 0; i < numpmid; i++) { + __pmID_int *idp = (__pmID_int *)&(pmidlist[i]); + if (idp->cluster < NUM_CLUSTERS) + need_refresh[idp->cluster]++; + } + + if ((sts = gfs2_fetch_refresh(pmda, need_refresh)) < 0) + return sts; + return pmdaFetch(numpmid, pmidlist, resp, pmda); +} + +/* + * callback provided to pmdaFetch + */ +static int +gfs2_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom) +{ + __pmID_int *idp = (__pmID_int *)&(mdesc->m_desc.pmid); + struct gfs2_fs *fs; + int sts; + + switch (idp->cluster) { + case CLUSTER_GLOCKS: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + return gfs2_glocks_fetch(idp->item, &fs->glocks, atom); + + case CLUSTER_SBSTATS: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + return gfs2_sbstats_fetch(idp->item, &fs->sbstats, atom); + + case CLUSTER_GLSTATS: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + return gfs2_glstats_fetch(idp->item, &fs->glstats, atom); + + case CLUSTER_TRACEPOINTS: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void **)&fs); + if (sts < 0) + return sts; + return gfs2_ftrace_fetch(idp->item, &fs->ftrace, atom); + + case CLUSTER_WORSTGLOCK: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void**)&fs); + if (sts < 0) + return sts; + return gfs2_worst_glock_fetch(idp->item, &fs->worst_glock, atom); + + case CLUSTER_LATENCY: + sts = pmdaCacheLookup(INDOM(GFS_FS_INDOM), inst, NULL, (void**)&fs); + if (sts < 0) + return sts; + return gfs2_latency_fetch(idp->item, &fs->latency, atom); + + case CLUSTER_CONTROL: + return gfs2_control_fetch(idp->item, atom); + + default: /* unknown cluster */ + return PM_ERR_PMID; + } + + return 1; +} + +/* + * Enable all tracepoints by default on init + */ +static void +gfs2_tracepoints_init() +{ + FILE *fp; + + fp = fopen("/sys/kernel/debug/tracing/events/gfs2/enable", "w"); + if (!fp) { + fprintf(stderr, "Unable to automatically enable GFS2 tracepoints"); + } else { + fprintf(fp, "%d\n", 1); + fclose(fp); + } +} + +/* + * Set default trace_pipe buffer size per cpu on init (32MB) + */ +static void +gfs2_buffer_default_size_set() +{ + FILE *fp; + + fp = fopen("/sys/kernel/debug/tracing/buffer_size_kb", "w"); + if (!fp) { + fprintf(stderr, "Unable to set default buffer size"); + } else { + fprintf(fp, "%d\n", 32768); /* Default 32MB per cpu */ + fclose(fp); + } +} + +/* + * Some version of ftrace are missing the irq-info option which alters the + * trace-pipe output, because of this we check for the option and if exists + * we switch off irq info output in trace_pipe + */ +static void +gfs2_ftrace_irq_info_set() +{ + FILE *fp; + + fp = fopen("/sys/kernel/debug/tracing/options/irq-info", "w"); + if (fp) { + /* We only need to set value if irq-info exists */ + fprintf(fp, "0"); /* Switch off irq-info in trace_pipe */ + fclose(fp); + } +} + +static int +gfs2_store(pmResult *result, pmdaExt *pmda) +{ + int i; + int sts = 0; + pmValueSet *vsp; + __pmID_int *pmidp; + + for (i = 0; i < result->numpmid && !sts; i++) { + vsp = result->vset[i]; + pmidp = (__pmID_int *)&vsp->pmid; + + if (pmidp->cluster == CLUSTER_CONTROL && pmidp->item <= CONTROL_BUFFER_SIZE_KB) { + sts = gfs2_control_set_value(control_locations[pmidp->item], vsp); + } + + if (pmidp->cluster == CLUSTER_CONTROL && pmidp->item == CONTROL_WORSTGLOCK) { + sts = worst_glock_set_state(vsp); + } + + if (pmidp->cluster == CLUSTER_CONTROL && pmidp->item == CONTROL_LATENCY) { + sts = latency_set_state(vsp); + } + + if (pmidp->cluster == CLUSTER_CONTROL && pmidp->item == CONTROL_FTRACE_GLOCK_THRESHOLD) { + sts = ftrace_set_threshold(vsp); + } + } + return sts; +} + +static int +gfs2_text(int ident, int type, char **buf, pmdaExt *pmda) +{ + if ((type & PM_TEXT_PMID) == PM_TEXT_PMID) { + int sts = pmdaDynamicLookupText(ident, type, buf, pmda); + if (sts != -ENOENT) + return sts; + } + return pmdaText(ident, type, buf, pmda); +} + +static int +gfs2_pmid(const char *name, pmID *pmid, pmdaExt *pmda) +{ + __pmnsTree *tree = pmdaDynamicLookupName(pmda, name); + return pmdaTreePMID(tree, name, pmid); +} + +static int +gfs2_name(pmID pmid, char ***nameset, pmdaExt *pmda) +{ + __pmnsTree *tree = pmdaDynamicLookupPMID(pmda, pmid); + return pmdaTreeName(tree, pmid, nameset); +} + +static int +gfs2_children(const char *name, int flag, char ***kids, int **sts, pmdaExt *pmda) +{ + __pmnsTree *tree = pmdaDynamicLookupName(pmda, name); + return pmdaTreeChildren(tree, name, flag, kids, sts); +} + +/* + * Initialise the agent (both daemon and DSO). + */ +void +gfs2_init(pmdaInterface *dp) +{ + int nindoms = sizeof(indomtable)/sizeof(indomtable[0]); + int nmetrics = sizeof(metrictable)/sizeof(metrictable[0]); + + if (dp->status != 0) + return; + + dp->version.four.instance = gfs2_instance; + dp->version.four.store = gfs2_store; + dp->version.four.fetch = gfs2_fetch; + dp->version.four.text = gfs2_text; + dp->version.four.pmid = gfs2_pmid; + dp->version.four.name = gfs2_name; + dp->version.four.children = gfs2_children; + pmdaSetFetchCallBack(dp, gfs2_fetchCallBack); + + gfs2_sbstats_init(metrictable, nmetrics); + gfs2_worst_glock_init(metrictable, nmetrics); + + pmdaSetFlags(dp, PMDA_EXT_FLAG_HASHED); + pmdaInit(dp, indomtable, nindoms, metrictable, nmetrics); + + /* Set defaults */ + gfs2_tracepoints_init(); /* Enables gfs2 tracepoints */ + gfs2_buffer_default_size_set(); /* Sets default buffer size */ + gfs2_ftrace_irq_info_set(); /* Disables irq-info output with trace_pipe */ +} + +static pmLongOptions longopts[] = { + PMDA_OPTIONS_HEADER("Options"), + PMOPT_DEBUG, + PMDAOPT_DOMAIN, + PMDAOPT_LOGFILE, + PMOPT_HELP, + PMDA_OPTIONS_END +}; + +static pmdaOptions opts = { + .short_options = "D:d:l:?", + .long_options = longopts, +}; + +/* + * Set up the agent if running as a daemon. + */ +int +main(int argc, char **argv) +{ + int sep = __pmPathSeparator(); + pmdaInterface dispatch; + char helppath[MAXPATHLEN]; + + __pmSetProgname(argv[0]); + snprintf(helppath, sizeof(helppath), "%s%c" "gfs2" "%c" "help", + pmGetConfig("PCP_PMDAS_DIR"), sep, sep); + pmdaDaemon(&dispatch, PMDA_INTERFACE_4, pmProgname, GFS2, "gfs2.log", helppath); + + pmdaGetOptions(argc, argv, &opts, &dispatch); + if (opts.errors) { + pmdaUsageMessage(&opts); + exit(1); + } + + pmdaOpenLog(&dispatch); + gfs2_init(&dispatch); + pmdaConnect(&dispatch); + pmdaMain(&dispatch); + exit(0); +} diff --git a/src/pmdas/gfs2/pmdagfs2.h b/src/pmdas/gfs2/pmdagfs2.h new file mode 100644 index 0000000..f897d13 --- /dev/null +++ b/src/pmdas/gfs2/pmdagfs2.h @@ -0,0 +1,57 @@ +/* + * Global Filesystem v2 (GFS2) PMDA + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef PMDAGFS2_H +#define PMDAGFS2_H + +#include "glocks.h" +#include "sbstats.h" +#include "glstats.h" +#include "worst_glock.h" +#include "ftrace.h" +#include "latency.h" +#include "control.h" + +enum { + CLUSTER_GLOCKS = 0, /* 0 - /sys/kernel/debug/gfs2/<fs>/glocks */ + CLUSTER_SBSTATS, /* 1 - /sys/kernel/debug/gfs2/<fs>/sbstats */ + CLUSTER_GLSTATS, /* 2 - /sys/kernel/debug/gfs2/<fs>/glstats */ + CLUSTER_TRACEPOINTS, /* 3 - /sys/kernel/debug/tracing/events/gfs2/ */ + CLUSTER_WORSTGLOCK, /* 4 - Custom metric for worst glock */ + CLUSTER_LATENCY, /* 5 - Custom metric for working out latency of filesystem operations */ + CLUSTER_CONTROL, /* 6 - Control for specific tracepoint enabling (for installs without all of the GFS2 tracepoints) */ + NUM_CLUSTERS +}; + +enum { + GFS_FS_INDOM = 0, /* 0 -- mounted gfs filesystem names */ + NUM_INDOMS +}; + +struct gfs2_fs { + dev_t dev_id; + struct glocks glocks; + struct sbstats sbstats; + struct glstats glstats; + struct ftrace ftrace; + struct worst_glock worst_glock; + struct latency latency; +}; + +extern pmdaMetric metrictable[]; +extern int metrictable_size(); + +#endif /*PMDAGFS2_H*/ diff --git a/src/pmdas/gfs2/pmns b/src/pmdas/gfs2/pmns new file mode 100644 index 0000000..7d8a13a --- /dev/null +++ b/src/pmdas/gfs2/pmns @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/* + * Changed GFS2:*:* to root number 115 to remove ASCII parse errors during + * install of PMDA whilst testing. + */ +gfs2 { + glocks + sbstats GFS2:*:* + glstats + tracepoints + worst_glock GFS2:*:* + latency + control +} + +gfs2.glocks { + total GFS2:0:0 + shared GFS2:0:1 + unlocked GFS2:0:2 + deferred GFS2:0:3 + exclusive GFS2:0:4 +} + +gfs2.glstats { + total GFS2:2:0 + trans GFS2:2:1 + inode GFS2:2:2 + rgrp GFS2:2:3 + meta GFS2:2:4 + iopen GFS2:2:5 + flock GFS2:2:6 + quota GFS2:2:8 + journal GFS2:2:9 +} + +gfs2.tracepoints { + glock_state_change + glock_put + demote_rq + promote + glock_queue + glock_lock_time + pin + log_flush + log_block + ail_flush + block_alloc + bmap + rs +} + +gfs2.tracepoints.glock_state_change { + total GFS2:3:0 + null_lock GFS2:3:1 + concurrent_read GFS2:3:2 + concurrent_write GFS2:3:3 + protected_read GFS2:3:4 + protected_write GFS2:3:5 + exclusive GFS2:3:6 + glocks +} + +gfs2.tracepoints.glock_state_change.glocks { + changed_target GFS2:3:7 + missed_target GFS2:3:8 +} + +gfs2.tracepoints.glock_put { + total GFS2:3:9 + null_lock GFS2:3:10 + concurrent_read GFS2:3:11 + concurrent_write GFS2:3:12 + protected_read GFS2:3:13 + protected_write GFS2:3:14 + exclusive GFS2:3:15 +} + +gfs2.tracepoints.demote_rq { + total GFS2:3:16 + null_lock GFS2:3:17 + concurrent_read GFS2:3:18 + concurrent_write GFS2:3:19 + protected_read GFS2:3:20 + protected_write GFS2:3:21 + exclusive GFS2:3:22 + requested +} + +gfs2.tracepoints.demote_rq.requested { + remote GFS2:3:23 + local GFS2:3:24 +} + +gfs2.tracepoints.promote { + total GFS2:3:25 + first + other +} + +gfs2.tracepoints.promote.first { + null_lock GFS2:3:26 + concurrent_read GFS2:3:27 + concurrent_write GFS2:3:28 + protected_read GFS2:3:29 + protected_write GFS2:3:30 + exclusive GFS2:3:31 +} + +gfs2.tracepoints.promote.other { + null_lock GFS2:3:32 + concurrent_read GFS2:3:33 + concurrent_write GFS2:3:34 + protected_read GFS2:3:35 + protected_write GFS2:3:36 + exclusive GFS2:3:37 +} + +gfs2.tracepoints.glock_queue { + total GFS2:3:38 + queue + dequeue +} + +gfs2.tracepoints.glock_queue.queue { + total GFS2:3:39 + null_lock GFS2:3:40 + concurrent_read GFS2:3:41 + concurrent_write GFS2:3:42 + protected_read GFS2:3:43 + protected_write GFS2:3:44 + exclusive GFS2:3:45 +} + +gfs2.tracepoints.glock_queue.dequeue { + total GFS2:3:46 + null_lock GFS2:3:47 + concurrent_read GFS2:3:48 + concurrent_write GFS2:3:49 + protected_read GFS2:3:50 + protected_write GFS2:3:51 + exclusive GFS2:3:52 +} + +gfs2.tracepoints.glock_lock_time { + total GFS2:3:53 + trans GFS2:3:54 + inode GFS2:3:55 + rgrp GFS2:3:56 + meta GFS2:3:57 + iopen GFS2:3:58 + flock GFS2:3:59 + quota GFS2:3:60 + journal GFS2:3:61 +} + +gfs2.tracepoints.pin { + total GFS2:3:62 + pin_total GFS2:3:63 + unpin_total GFS2:3:64 + longest_pinned GFS2:3:65 +} + +gfs2.tracepoints.log_flush { + total GFS2:3:66 +} + +gfs2.tracepoints.log_block { + total GFS2:3:67 +} + +gfs2.tracepoints.ail_flush { + total GFS2:3:68 +} + +gfs2.tracepoints.block_alloc { + total GFS2:3:69 + free GFS2:3:70 + used GFS2:3:71 + dinode GFS2:3:72 + unlinked GFS2:3:73 +} + +gfs2.tracepoints.bmap { + total GFS2:3:74 + create GFS2:3:75 + nocreate GFS2:3:76 +} + +gfs2.tracepoints.rs { + total GFS2:3:77 + del GFS2:3:78 + tdel GFS2:3:79 + ins GFS2:3:80 + clm GFS2:3:81 +} + +gfs2.latency { + grant + demote + queue +} + +gfs2.latency.grant { + all GFS2:5:0 + null_lock GFS2:5:1 + concurrent_read GFS2:5:2 + concurrent_write GFS2:5:3 + protected_read GFS2:5:4 + protected_write GFS2:5:5 + exclusive GFS2:5:6 +} + +gfs2.latency.demote { + all GFS2:5:7 + null_lock GFS2:5:8 + concurrent_read GFS2:5:9 + concurrent_write GFS2:5:10 + protected_read GFS2:5:11 + protected_write GFS2:5:12 + exclusive GFS2:5:13 +} + +gfs2.latency.queue { + all GFS2:5:14 + null_lock GFS2:5:15 + concurrent_read GFS2:5:16 + concurrent_write GFS2:5:17 + protected_read GFS2:5:18 + protected_write GFS2:5:19 + exclusive GFS2:5:20 +} + +gfs2.control { + tracepoints + buffer_size_kb GFS2:6:14 + global_tracing GFS2:6:15 + worst_glock GFS2:6:16 + latency GFS2:6:17 + glock_threshold GFS2:6:18 +} + +gfs2.control.tracepoints { + all GFS2:6:0 + glock_state_change GFS2:6:1 + glock_put GFS2:6:2 + demote_rq GFS2:6:3 + promote GFS2:6:4 + glock_queue GFS2:6:5 + glock_lock_time GFS2:6:6 + pin GFS2:6:7 + log_flush GFS2:6:8 + log_blocks GFS2:6:9 + ail_flush GFS2:6:10 + block_alloc GFS2:6:11 + bmap GFS2:6:12 + rs GFS2:6:13 +} diff --git a/src/pmdas/gfs2/root b/src/pmdas/gfs2/root new file mode 100644 index 0000000..5f8b73b --- /dev/null +++ b/src/pmdas/gfs2/root @@ -0,0 +1,16 @@ +/* + * fake "root" for validating the local PMNS subtree + */ + +#include <stdpmid> + +root { + gfs2 + +} + +#ifndef GFS2 +#define GFS2 115 +#endif + +#include "pmns" diff --git a/src/pmdas/gfs2/sbstats.c b/src/pmdas/gfs2/sbstats.c new file mode 100644 index 0000000..d8d4668 --- /dev/null +++ b/src/pmdas/gfs2/sbstats.c @@ -0,0 +1,264 @@ +/* + * GFS2 sbstats sysfs file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" + +#include "pmdagfs2.h" + +#include <ctype.h> + +static const char *stattype[] = { + [LOCKSTAT_SRTT] = "srtt", + [LOCKSTAT_SRTTVAR] = "srttvar", + [LOCKSTAT_SRTTB] = "srttb", + [LOCKSTAT_SRTTVARB] = "srttvarb", + [LOCKSTAT_SIRT] = "sirt", + [LOCKSTAT_SIRTVAR] = "sirtvar", + [LOCKSTAT_DCOUNT] = "dlm", + [LOCKSTAT_QCOUNT] = "queue", +}; + +static const char *stattext[] = { + [LOCKSTAT_SRTT] = "Non-blocking smoothed round trip time", + [LOCKSTAT_SRTTVAR] = "Non-blocking smoothed variance", + [LOCKSTAT_SRTTB] = "Blocking smoothed round trip time", + [LOCKSTAT_SRTTVARB] = "Blocking smoothed variance", + [LOCKSTAT_SIRT] = "Smoothed inter-request time", + [LOCKSTAT_SIRTVAR] = "Smoothed inter-request variance", + [LOCKSTAT_DCOUNT] = "Count of Distributed Lock Manager requests", + [LOCKSTAT_QCOUNT] = "Count of gfs2_holder queues", +}; + +static const char *locktype[] = { + [LOCKTYPE_RESERVED] = "reserved", + [LOCKTYPE_NONDISK] = "nondisk", + [LOCKTYPE_INODE] = "inode", + [LOCKTYPE_RGRB] = "rgrp", + [LOCKTYPE_META] = "meta", + [LOCKTYPE_IOPEN] = "iopen", + [LOCKTYPE_FLOCK] = "flock", + [LOCKTYPE_PLOCK] = "plock", + [LOCKTYPE_QUOTA] = "quota", + [LOCKTYPE_JOURNAL] = "journal", +}; + +int +gfs2_sbstats_fetch(int item, struct sbstats *fs, pmAtomValue *atom) +{ + /* Check for valid metric count */ + if (item < 0 || item >= SBSTATS_COUNT) + return PM_ERR_PMID; + + /* Check for no values recorded */ + if(fs->values[item] == UINT64_MAX) + return 0; + + atom->ull = fs->values[item]; + return 1; +} + +int +gfs2_refresh_sbstats(const char *sysfs, const char *name, struct sbstats *sb) +{ + unsigned int id = 0; + char buffer[4096]; + FILE *fp; + + /* Reset all counter for this fs */ + memset(sb, 0, sizeof(*sb)); + + snprintf(buffer, sizeof(buffer), "%s/%s/sbstats", sysfs, name); + buffer[sizeof(buffer)-1] = '\0'; + + if ((fp = fopen(buffer, "r")) == NULL){ + /* + * We set the values to UINT64_MAX to signify we have no + * current values (no metric support or debugfs not mounted) + * + */ + memset(sb, -1, sizeof(*sb)); + return -oserror(); + } + + /* + * Read through sbstats file one line at a time. This is called on each + * and every fetch request, so should be as quick as we can make it. + * + * Once we've skipped over the heading, the format is fixed so to make this + * faster (we expect the kernel has a fixed set of types/stats) we go right + * ahead and index directly into the stats structure for each line. We do + * check validity too - if there's a mismatch, we throw our toys. + * + * Also, we aggregate the per-CPU data for each statistic - we could go for + * separate per-CPU metrics as well, but for now just keep it simple until + * there's a real need for that extra complexity. + * + */ + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + char *typestr, *statstr, *end; + char *p = buffer; + + if (strncmp(p, "type", 4) == 0) + continue; + if (id > SBSTATS_COUNT) + break; + + typestr = p; + for (typestr = p; !isspace((int)*p); p++) { } /* skip lock type */ + for (; isspace((int)*p); p++) { *p = '\0'; } /* eat whitespace */ + for (statstr = p; *p != ':'; p++) { } /* skip stat type */ + *p = '\0'; + + /* verify that the id we are up to matches what we see in the file */ + unsigned int type = id / NUM_LOCKSTATS; + unsigned int stat = id % NUM_LOCKSTATS; + if (strcmp(typestr, locktype[type]) != 0) { + __pmNotifyErr(LOG_ERR, + "unexpected sbstat type \"%s\" (want %s at line %u)", + typestr, locktype[type], id); + break; /* eh? */ + } + if (strcmp(statstr, stattype[stat]) != 0) { + __pmNotifyErr(LOG_ERR, + "unexpected sbstat stat \"%s\" (want %s at line %u)", + statstr, stattype[stat], id); + break; /* wha? */ + } + + /* decode all of the (per-CPU) values until the end of line */ + for (p++; *p != '\0'; p++) { + __uint64_t value; + + value = strtoull(p, &end, 10); + if (end == p) + break; + sb->values[id] += value; + p = end; + } + + if (pmDebug & DBG_TRACE_APPL0) + __pmNotifyErr(LOG_INFO, + "got expected sbstat type \"%s\", stat \"%s\" at line %u", + typestr, statstr, id); + + /* now we can move on to the next metric (on the next line) */ + id++; + } + fclose(fp); + return (id == SBSTATS_COUNT) ? 0 : -EINVAL; +} + +static void +add_pmns_node(__pmnsTree *tree, int domain, int cluster, int lock, int stat) +{ + char entry[64]; + pmID pmid = pmid_build(domain, cluster, (lock * NUM_LOCKSTATS) + stat); + + snprintf(entry, sizeof(entry), + "gfs2.sbstats.%s.%s", locktype[lock], stattype[stat]); + __pmAddPMNSNode(tree, pmid, entry); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "GFS2 sbstats added %s (%s)", entry, pmIDStr(pmid)); +} + +static int +refresh_sbstats(pmdaExt *pmda, __pmnsTree **tree) +{ + int t, s, sts; + static __pmnsTree *sbstats_tree; + + if (sbstats_tree) { + *tree = sbstats_tree; + } else if ((sts = __pmNewPMNS(&sbstats_tree)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create sbstats names: %s\n", + pmProgname, pmErrStr(sts)); + *tree = NULL; + } else { + for (t = 0; t < NUM_LOCKTYPES; t++) + for (s = 0; s < NUM_LOCKSTATS; s++) + add_pmns_node(sbstats_tree, pmda->e_domain, CLUSTER_SBSTATS, t, s); + *tree = sbstats_tree; + return 1; + } + return 0; +} + +/* + * Create a new metric table entry based on an existing one. + * + */ +static void +refresh_metrictable(pmdaMetric *source, pmdaMetric *dest, int lock) +{ + int item = pmid_item(source->m_desc.pmid); + int domain = pmid_domain(source->m_desc.pmid); + int cluster = pmid_cluster(source->m_desc.pmid); + + memcpy(dest, source, sizeof(pmdaMetric)); + item += lock * NUM_LOCKSTATS; + dest->m_desc.pmid = pmid_build(domain, cluster, item); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "GFS2 sbstats refresh_metrictable: (%p -> %p) " + "metric ID dup: %d.%d.%d -> %d.%d.%d\n", + source, dest, domain, cluster, + pmid_item(source->m_desc.pmid), domain, cluster, item); +} + +/* + * Used to answer the question: how much extra space needs to be + * allocated in the metric table for (dynamic) sbstats metrics? + * + */ +static void +size_metrictable(int *total, int *trees) +{ + *total = NUM_LOCKSTATS; + *trees = NUM_LOCKTYPES; +} + +static int +sbstats_text(pmdaExt *pmda, pmID pmid, int type, char **buf) +{ + int item = pmid_item(pmid); + static char text[128]; + + if (pmid_cluster(pmid) != CLUSTER_SBSTATS) + return PM_ERR_PMID; + if (item < 0 || item >= SBSTATS_COUNT) + return PM_ERR_PMID; + snprintf(text, sizeof(text), "%s for %s glocks", + stattext[item % NUM_LOCKSTATS], locktype[item / NUM_LOCKSTATS]); + + *buf = text; + + return 0; +} + +void +gfs2_sbstats_init(pmdaMetric *metrics, int nmetrics) +{ + int set[] = { CLUSTER_SBSTATS }; + + pmdaDynamicPMNS("gfs2.sbstats", + set, sizeof(set)/sizeof(int), + refresh_sbstats, sbstats_text, + refresh_metrictable, size_metrictable, + metrics, nmetrics); +} diff --git a/src/pmdas/gfs2/sbstats.h b/src/pmdas/gfs2/sbstats.h new file mode 100644 index 0000000..5e71791 --- /dev/null +++ b/src/pmdas/gfs2/sbstats.h @@ -0,0 +1,56 @@ +/* + * GFS2 sbstats sysfs file statistics. + * + * Copyright (c) 2013 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef SBSTATS_H +#define SBSTATS_H + +enum { + LOCKSTAT_SRTT = 0, /* Non-blocking smoothed round trip time */ + LOCKSTAT_SRTTVAR, /* Non-blocking smoothed variance */ + LOCKSTAT_SRTTB, /* Blocking smoothed round trip time */ + LOCKSTAT_SRTTVARB, /* Blocking smoothed variance */ + LOCKSTAT_SIRT, /* Smoothed inter-request time */ + LOCKSTAT_SIRTVAR, /* Smoothed inter-request variance */ + LOCKSTAT_DCOUNT, /* Count of DLM requests */ + LOCKSTAT_QCOUNT, /* Count of gfs2_holder queues */ + NUM_LOCKSTATS +}; + +enum { + LOCKTYPE_RESERVED = 0, + LOCKTYPE_NONDISK, + LOCKTYPE_INODE, + LOCKTYPE_RGRB, + LOCKTYPE_META, + LOCKTYPE_IOPEN, + LOCKTYPE_FLOCK, + LOCKTYPE_PLOCK, + LOCKTYPE_QUOTA, + LOCKTYPE_JOURNAL, + NUM_LOCKTYPES +}; + +#define SBSTATS_COUNT (NUM_LOCKSTATS*NUM_LOCKTYPES) + +struct sbstats { + __uint64_t values[SBSTATS_COUNT]; +}; + +extern void gfs2_sbstats_init(pmdaMetric *, int); +extern int gfs2_sbstats_fetch(int, struct sbstats *, pmAtomValue *); +extern int gfs2_refresh_sbstats(const char *, const char *, struct sbstats *); + +#endif /* SBSTATS_H */ diff --git a/src/pmdas/gfs2/worst_glock.c b/src/pmdas/gfs2/worst_glock.c new file mode 100644 index 0000000..ed851c3 --- /dev/null +++ b/src/pmdas/gfs2/worst_glock.c @@ -0,0 +1,391 @@ +/* + * GFS2 gfs2_glock_lock_time trace-point metrics. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "pmapi.h" +#include "impl.h" +#include "pmda.h" +#include "pmdagfs2.h" + +#include "ftrace.h" +#include "worst_glock.h" + +#include <string.h> +#include <inttypes.h> +#include <sys/sysmacros.h> +#include <sys/types.h> + +static struct glock glock_data; +static int reset_flag; + +static int worst_glock_state = DEFAULT_WORST_GLOCK_STATE; + +static const char *stattype[] = { + [WORSTGLOCK_LOCK_TYPE] = "lock_type", + [WORSTGLOCK_NUMBER] = "number", + [WORSTGLOCK_SRTT] = "srtt", + [WORSTGLOCK_SRTTVAR] = "srttvar", + [WORSTGLOCK_SRTTB] = "srttb", + [WORSTGLOCK_SRTTVARB] = "srttvarb", + [WORSTGLOCK_SIRT] = "sirt", + [WORSTGLOCK_SIRTVAR] = "sirtvar", + [WORSTGLOCK_DLM] = "dlm", + [WORSTGLOCK_QUEUE] = "queue", +}; + +static const char *stattext[] = { + [WORSTGLOCK_LOCK_TYPE] = "Glock type number", + [WORSTGLOCK_NUMBER] = "Inode or resource group number", + [WORSTGLOCK_SRTT] = "Non-blocking smoothed round trip time", + [WORSTGLOCK_SRTTVAR] = "Non-blocking smoothed variance", + [WORSTGLOCK_SRTTB] = "Blocking smoothed round trip time", + [WORSTGLOCK_SRTTVARB] = "Blocking smoothed variance", + [WORSTGLOCK_SIRT] = "Smoothed Inter-request time", + [WORSTGLOCK_SIRTVAR] = "Smoothed Inter-request variance", + [WORSTGLOCK_DLM] = "Count of Distributed Lock Manager requests", + [WORSTGLOCK_QUEUE] = "Count of gfs2_holder queues", +}; + +static const char *topnum[] = { + [TOPNUM_FIRST] = "first", + [TOPNUM_SECOND] = "second", + [TOPNUM_THIRD] = "third", + [TOPNUM_FOURTH] = "fourth", + [TOPNUM_FIFTH] = "fifth", + [TOPNUM_SIXTH] = "sixth", + [TOPNUM_SEVENTH] = "seventh", + [TOPNUM_EIGHTH] = "eighth", + [TOPNUM_NINTH] = "ninth", + [TOPNUM_TENTH] = "tenth", +}; + +/* + * Sets the value of worst_glock_state using pmstore, value + * must be 0 or 1. + */ +int +worst_glock_set_state(pmValueSet *vsp) +{ + int value = vsp->vlist[0].value.lval; + + if (value == 0 || value == 1) { + worst_glock_state = value; + + return 0; + } else { + return PM_ERR_SIGN; + } +} + +/* + * Used to see whether the worst_glock metrics are enabled or disabled. Should + * only return either 0 or 1. + */ +int +worst_glock_get_state() +{ + return worst_glock_state; +} + +/* + * Refreshing of the metrics for gfs2.lock_time, some of metrics are of + * a different typing. + */ +int +gfs2_worst_glock_fetch(int item, struct worst_glock *worst_glock, pmAtomValue *atom) +{ + /* If we are assigning, we should set the reset flag for next assign */ + reset_flag = 1; + + int pmid = (item % 10); + int position = (item / 10); + + /* Check if tracepoint is enabled */ + if (worst_glock_get_state() == 0) + return 0; + + /* Check to see if we have values to assign */ + if (worst_glock->glocks[position].lock_type == WORSTGLOCK_INODE || + worst_glock->glocks[position].lock_type == WORSTGLOCK_RGRP){ + switch(pmid){ + case WORSTGLOCK_LOCK_TYPE: + atom->ul = worst_glock->glocks[position].lock_type; /* Glock type number */ + break; + case WORSTGLOCK_NUMBER: + atom->ull = worst_glock->glocks[position].number; /* Inode or resource group number */ + break; + case WORSTGLOCK_SRTT: + atom->ll = worst_glock->glocks[position].srtt; /* Non blocking smoothed round trip time */ + break; + case WORSTGLOCK_SRTTVAR: + atom->ll = worst_glock->glocks[position].srttvar; /* Non blocking smoothed variance */ + break; + case WORSTGLOCK_SRTTB: + atom->ll = worst_glock->glocks[position].srttb; /* Blocking smoothed round trip time */ + break; + case WORSTGLOCK_SRTTVARB: + atom->ll = worst_glock->glocks[position].srttvarb; /* Blocking smoothed variance */ + break; + case WORSTGLOCK_SIRT: + atom->ll = worst_glock->glocks[position].sirt; /* Smoothed Inter-request time */ + break; + case WORSTGLOCK_SIRTVAR: + atom->ll = worst_glock->glocks[position].sirtvar; /* Smoothed Inter-request variance */ + break; + case WORSTGLOCK_DLM: + atom->ll = worst_glock->glocks[position].dlm; /* Count of dlm requests */ + break; + case WORSTGLOCK_QUEUE: + atom->ll = worst_glock->glocks[position].queue; /* Count of gfs2_holder queues */ + break; + default: + return PM_ERR_PMID; + } + return 1; /* Return we have had values */ + } else { + return 0; /* If we have no valid values */ + } +} + +/* + * Comparison function we compare the values; we return the lock which + * is deemed to be the worst. + */ +static int +lock_comparison(const void *a, const void *b) +{ + struct glock *aa = (struct glock *)a; + struct glock *bb = (struct glock *)b; + int true_count = 0; + + /* Case to deal with the empty entries */ + if (aa->lock_type == 0) + return 1; /* Move empty a up the list, b moves down list */ + + if (bb->lock_type == 0) + return -1; /* Move a down the list, empty b moves up list*/ + + /* A sirt (LESS THAN) B sirt = A worse */ + if (aa->sirtvar < bb->sirtvar) + true_count++; + + /* A srtt (MORE THAN) B srtt = A worse */ + if (aa->srttvarb > bb->srttvarb) + true_count++; + + /* A srttb (MORE THAN) B srttb = A worse */ + if (aa->srttvar > bb->srttvar) + true_count++; + + /* If there are more counts where A is worse than B? */ + if ( true_count > 1 ) { + return -1; /* a is worse than b (move a down the list) */ + } else if ( true_count == 1 ){ + /* Tie break condition */ + if ( aa->dlm > bb->queue ) return -1; /* a is worse than b (move a down the list) */ + } + return 1; /* b is worse than a (move b up the list) */ +} + +/* + * We loop through each of our available file-sytems, find the locks that corr- + * esspond to the filesystem. With these locks we find the worst and assign it + * to the filesystem before returning the metric values. + */ +static void +worst_glock_assign_glocks(pmInDom gfs_fs_indom) +{ + int i, j, sts; + struct gfs2_fs *fs; + + /* We walk through for each filesystem */ + for (pmdaCacheOp(gfs_fs_indom, PMDA_CACHE_WALK_REWIND);;) { + if ((i = pmdaCacheOp(gfs_fs_indom, PMDA_CACHE_WALK_NEXT)) < 0) + break; + sts = pmdaCacheLookup(gfs_fs_indom, i, NULL, (void **)&fs); + if (sts != PMDA_CACHE_ACTIVE) + continue; + + /* Clear old entries if reset is set */ + if(reset_flag == 1){ + memset(&fs->worst_glock, 0, sizeof(struct worst_glock)); + reset_flag = 0; + } + + /* Is the entry matching the filesystem we are on? */ + if (fs->dev_id != glock_data.dev_id) + continue; + + /* Check if we are updating an existing entry */ + for (j = 0; j < WORST_GLOCK_TOP; j++) { + if ((fs->worst_glock.glocks[j].lock_type == glock_data.lock_type) && + (fs->worst_glock.glocks[j].number == glock_data.number)) { + fs->worst_glock.glocks[j] = glock_data; + return; + } + } + + /* If not place in next available slot */ + if (fs->worst_glock.assigned_entries < WORST_GLOCK_TOP) { + fs->worst_glock.glocks[fs->worst_glock.assigned_entries] = glock_data; + fs->worst_glock.assigned_entries++; + } else { + fs->worst_glock.glocks[WORST_GLOCK_TOP] = glock_data; /* Place in slot 11 */ + } + qsort(fs->worst_glock.glocks, (WORST_GLOCK_TOP + 1), sizeof(struct glock), lock_comparison); + } +} + +/* + * We work out the individual metric values from our buffer input and store + * them for processing after all of the values have been extracted from the + * trace pipe. + */ +int +gfs2_extract_worst_glock(char **buffer, pmInDom gfs_fs_indom) +{ + struct glock temp; + unsigned int major, minor; + + /* Assign data */ + sscanf(*buffer, + "gfs2_glock_lock_time: %"SCNu32",%"SCNu32" glock %"SCNu32":%"SCNu64" status:%*d flags:%*x tdiff:%*d srtt:%"SCNd64"/%"SCNd64" srttb:%"SCNd64"/%"SCNd64" sirt:%"SCNd64"/%"SCNd64" dcnt:%"SCNd64" qcnt:%"SCNd64, + &major, + &minor, + &temp.lock_type, + &temp.number, + &temp.srtt, + &temp.srttvar, + &temp.srttb, + &temp.srttvarb, + &temp.sirt, + &temp.sirtvar, + &temp.dlm, + &temp.queue + ); + temp.dev_id = makedev(major, minor); + + /* Filter on required lock types */ + if ((temp.lock_type == WORSTGLOCK_INODE || temp.lock_type == WORSTGLOCK_RGRP) && + (temp.dlm > COUNT_THRESHOLD || temp.queue > COUNT_THRESHOLD)) { + + /* Increase counters */ + glock_data = temp; + ftrace_increase_num_accepted_entries(); + } + + worst_glock_assign_glocks(gfs_fs_indom); + return 0; +} + +static void +add_pmns_node(__pmnsTree *tree, int domain, int cluster, int lock, int stat) +{ + char entry[64]; + pmID pmid = pmid_build(domain, cluster, (lock * NUM_GLOCKSTATS) + stat); + + snprintf(entry, sizeof(entry), + "gfs2.worst_glock.%s.%s", topnum[lock], stattype[stat]); + __pmAddPMNSNode(tree, pmid, entry); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "GFS2 worst_glock added %s (%s)", entry, pmIDStr(pmid)); +} + +static int +refresh_worst_glock(pmdaExt *pmda, __pmnsTree **tree) +{ + int t, s, sts; + static __pmnsTree *worst_glock_tree; + + if (worst_glock_tree) { + *tree = worst_glock_tree; + } else if ((sts = __pmNewPMNS(&worst_glock_tree)) < 0) { + __pmNotifyErr(LOG_ERR, "%s: failed to create worst_glock names: %s\n", + pmProgname, pmErrStr(sts)); + *tree = NULL; + } else { + for (t = 0; t < NUM_TOPNUM; t++) + for (s = 0; s < NUM_GLOCKSTATS; s++) + add_pmns_node(worst_glock_tree, pmda->e_domain, CLUSTER_WORSTGLOCK, t, s); + *tree = worst_glock_tree; + return 1; + } + return 0; +} + +/* + * Create a new metric table entry based on an existing one. + * + */ +static void +refresh_metrictable(pmdaMetric *source, pmdaMetric *dest, int lock) +{ + int item = pmid_item(source->m_desc.pmid); + int domain = pmid_domain(source->m_desc.pmid); + int cluster = pmid_cluster(source->m_desc.pmid); + + memcpy(dest, source, sizeof(pmdaMetric)); + item += lock * NUM_GLOCKSTATS; + dest->m_desc.pmid = pmid_build(domain, cluster, item); + + if (pmDebug & DBG_TRACE_APPL0) + fprintf(stderr, "GFS2 worst_glock refresh_metrictable: (%p -> %p) " + "metric ID dup: %d.%d.%d -> %d.%d.%d\n", + source, dest, domain, cluster, + pmid_item(source->m_desc.pmid), domain, cluster, item); +} + +/* + * Used to answer the question: how much extra space needs to be + * allocated in the metric table for (dynamic) worst_glock metrics? + * + */ +static void +size_metrictable(int *total, int *trees) +{ + *total = NUM_GLOCKSTATS; + *trees = NUM_TOPNUM; +} + +static int +worst_glock_text(pmdaExt *pmda, pmID pmid, int type, char **buf) +{ + int item = pmid_item(pmid); + static char text[128]; + + if (pmid_cluster(pmid) != CLUSTER_WORSTGLOCK) + return PM_ERR_PMID; + if (item < 0 || item >= WORST_GLOCK_COUNT) + return PM_ERR_PMID; + snprintf(text, sizeof(text), "%s for %s worst glock", + stattext[item % NUM_GLOCKSTATS], topnum[item / NUM_TOPNUM]); + + *buf = text; + + return 0; +} + +void +gfs2_worst_glock_init(pmdaMetric *metrics, int nmetrics) +{ + int set[] = { CLUSTER_WORSTGLOCK }; + + pmdaDynamicPMNS("gfs2.worst_glock", + set, sizeof(set)/sizeof(int), + refresh_worst_glock, worst_glock_text, + refresh_metrictable, size_metrictable, + metrics, nmetrics); +} diff --git a/src/pmdas/gfs2/worst_glock.h b/src/pmdas/gfs2/worst_glock.h new file mode 100644 index 0000000..6d1108c --- /dev/null +++ b/src/pmdas/gfs2/worst_glock.h @@ -0,0 +1,91 @@ +/* + * GFS2 gfs2_glock_lock_time trace-point metrics. + * + * Copyright (c) 2013 - 2014 Red Hat. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef LOCK_TIME_H +#define LOCK_TIME_H + +#define COUNT_THRESHOLD 350 +#define DEFAULT_WORST_GLOCK_STATE 1 +#define WORST_GLOCK_TOP 10 +#define WORST_GLOCK_COUNT (NUM_GLOCKSTATS*NUM_TOPNUM) + +enum { + WORSTGLOCK_LOCK_TYPE = 0, + WORSTGLOCK_NUMBER, + WORSTGLOCK_SRTT, + WORSTGLOCK_SRTTVAR, + WORSTGLOCK_SRTTB, + WORSTGLOCK_SRTTVARB, + WORSTGLOCK_SIRT, + WORSTGLOCK_SIRTVAR, + WORSTGLOCK_DLM, + WORSTGLOCK_QUEUE, + NUM_GLOCKSTATS +}; + +enum { + WORSTGLOCK_TRANS = 1, + WORSTGLOCK_INODE = 2, + WORSTGLOCK_RGRP = 3, + WORSTGLOCK_META = 4, + WORSTGLOCK_IOPEN = 5, + WORSTGLOCK_FLOCK = 6, + WORSTGLOCK_RESERVED = 7, + WORSTGLOCK_QUOTA = 8, + WORSTGLOCK_JOURNAL = 9 +}; + +enum { + TOPNUM_FIRST = 0, + TOPNUM_SECOND, + TOPNUM_THIRD, + TOPNUM_FOURTH, + TOPNUM_FIFTH, + TOPNUM_SIXTH, + TOPNUM_SEVENTH, + TOPNUM_EIGHTH, + TOPNUM_NINTH, + TOPNUM_TENTH, + NUM_TOPNUM +}; + +struct glock { + dev_t dev_id; + uint32_t lock_type; /* Glock type number */ + uint64_t number; /* Inode or resource group number */ + int64_t srtt; /* Non blocking smoothed round trip time */ + int64_t srttvar; /* Non blocking smoothed variance */ + int64_t srttb; /* Blocking smoothed round trip time */ + int64_t srttvarb; /* Blocking smoothed variance */ + int64_t sirt; /* Smoothed Inter-request time */ + int64_t sirtvar; /* Smoothed Inter-request variance */ + int64_t dlm; /* Count of dlm requests */ + int64_t queue; /* Count of gfs2_holder queues */ +}; + +struct worst_glock { + struct glock glocks[WORST_GLOCK_TOP + 1]; + int assigned_entries; +}; + +extern void gfs2_worst_glock_init(pmdaMetric *, int); +extern int gfs2_worst_glock_fetch(int, struct worst_glock *, pmAtomValue *); +extern int gfs2_extract_worst_glock(char **, pmInDom); + +extern int worst_glock_get_state(); +extern int worst_glock_set_state(pmValueSet *vsp); + +#endif /* LOCK_TIME_H */ |