summaryrefslogtreecommitdiff
path: root/src/pmdas/gfs2
diff options
context:
space:
mode:
Diffstat (limited to 'src/pmdas/gfs2')
-rw-r--r--src/pmdas/gfs2/GNUmakefile60
-rw-r--r--src/pmdas/gfs2/Install27
-rw-r--r--src/pmdas/gfs2/README108
-rw-r--r--src/pmdas/gfs2/Remove24
-rw-r--r--src/pmdas/gfs2/control.c123
-rw-r--r--src/pmdas/gfs2/control.h49
-rw-r--r--src/pmdas/gfs2/ftrace.c586
-rw-r--r--src/pmdas/gfs2/ftrace.h142
-rw-r--r--src/pmdas/gfs2/glocks.c93
-rw-r--r--src/pmdas/gfs2/glocks.h36
-rw-r--r--src/pmdas/gfs2/glstats.c117
-rw-r--r--src/pmdas/gfs2/glstats.h57
-rw-r--r--src/pmdas/gfs2/help566
-rw-r--r--src/pmdas/gfs2/latency.c373
-rw-r--r--src/pmdas/gfs2/latency.h70
-rw-r--r--src/pmdas/gfs2/pmda.c1063
-rw-r--r--src/pmdas/gfs2/pmdagfs2.h57
-rw-r--r--src/pmdas/gfs2/pmns270
-rw-r--r--src/pmdas/gfs2/root16
-rw-r--r--src/pmdas/gfs2/sbstats.c264
-rw-r--r--src/pmdas/gfs2/sbstats.h56
-rw-r--r--src/pmdas/gfs2/worst_glock.c391
-rw-r--r--src/pmdas/gfs2/worst_glock.h91
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 */