[PATCH v5 3/6] locking/pvqspinlock: Collect slowpath lock statistics

2015-08-07 Thread Waiman Long
This patch enables the accumulation of kicking and waiting related
PV qspinlock statistics when the new QUEUED_LOCK_STAT configuration
option is selected. It also enables the collection of kicking and
wakeup latencies which have a heavy dependency on the CPUs being used.

The measured latencies for different CPUs are:

CPU Wakeup  Kicking
--- --  ---
Haswell-EX  89.8us   7.4us
Westmere-EX 67.6us   9.3us

The measured latencies varied a bit from run-to-run. The wakeup
latency is much higher than the kicking latency.

A sample of statistics counts after a kernel build (no CPU overcommit)
was:

hash_hops_count=576912
kick_latencies=5258025484
kick_unlock_count=576911
kick_wait_count=576903
pending_fail_count=10722
pending_lock_count=6123545
spurious_wakeup=92
wait_again_count=75
wait_head_count=60
wait_node_count=576936
wake_latencies=37061460652

Signed-off-by: Waiman Long 
---
 arch/x86/Kconfig|7 ++
 kernel/locking/qspinlock_paravirt.h |  178 ++-
 2 files changed, 180 insertions(+), 5 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 4c9c8b8..86bf53e 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -719,6 +719,13 @@ config PARAVIRT_SPINLOCKS
 
  If you are unsure how to answer this question, answer Y.
 
+config QUEUED_LOCK_STAT
+   bool "Paravirt queued lock statistics"
+   depends on PARAVIRT && DEBUG_FS && QUEUED_SPINLOCKS
+   ---help---
+ Enable the collection of statistical data on the behavior of
+ paravirtualized queued spinlocks and report them on debugfs.
+
 source "arch/x86/xen/Kconfig"
 
 config KVM_GUEST
diff --git a/kernel/locking/qspinlock_paravirt.h 
b/kernel/locking/qspinlock_paravirt.h
index 94f9adf..5eb5dea 100644
--- a/kernel/locking/qspinlock_paravirt.h
+++ b/kernel/locking/qspinlock_paravirt.h
@@ -49,6 +49,151 @@ struct pv_node {
 };
 
 /*
+ * PV qspinlock statistics
+ */
+enum pv_qlock_stat {
+   pvstat_wait_head,
+   pvstat_wait_node,
+   pvstat_wait_again,
+   pvstat_kick_wait,
+   pvstat_kick_unlock,
+   pvstat_pend_lock,
+   pvstat_pend_fail,
+   pvstat_spurious,
+   pvstat_hops,
+   pvstat_num  /* Total number of statistics counts */
+};
+
+#ifdef CONFIG_QUEUED_LOCK_STAT
+/*
+ * Collect pvqspinlock statiatics
+ */
+#include 
+#include 
+
+static const char * const stat_fsnames[pvstat_num] = {
+   [pvstat_wait_head]   = "wait_head_count",
+   [pvstat_wait_node]   = "wait_node_count",
+   [pvstat_wait_again]  = "wait_again_count",
+   [pvstat_kick_wait]   = "kick_wait_count",
+   [pvstat_kick_unlock] = "kick_unlock_count",
+   [pvstat_pend_lock]   = "pending_lock_count",
+   [pvstat_pend_fail]   = "pending_fail_count",
+   [pvstat_spurious]= "spurious_wakeup",
+   [pvstat_hops]= "hash_hops_count",
+};
+
+static atomic_t pvstats[pvstat_num];
+
+/*
+ * pv_kick_latencies = sum of all pv_kick latencies in ns
+ * pv_wake_latencies = sum of all wakeup latencies in ns
+ *
+ * Avg kick latency   = pv_kick_latencies/kick_unlock_count
+ * Avg wake latency   = pv_wake_latencies/kick_wait_count
+ * Avg # of hops/hash = hash_hops_count/kick_unlock_count
+ */
+static atomic64_t pv_kick_latencies, pv_wake_latencies;
+static DEFINE_PER_CPU(u64, pv_kick_time);
+
+/*
+ * Reset all the statistics counts if set
+ */
+static bool reset_cnts __read_mostly;
+
+/*
+ * Initialize debugfs for the PV qspinlock statistics
+ */
+static int __init pv_qspinlock_debugfs(void)
+{
+   struct dentry *d_pvqlock = debugfs_create_dir("pv-qspinlock", NULL);
+   int i;
+
+   if (!d_pvqlock)
+   pr_warn("Could not create 'pv-qspinlock' debugfs directory\n");
+
+   for (i = 0; i < pvstat_num; i++)
+   debugfs_create_u32(stat_fsnames[i], 0444, d_pvqlock,
+ (u32 *)[i]);
+   debugfs_create_u64("kick_latencies", 0444, d_pvqlock,
+  (u64 *)_kick_latencies);
+   debugfs_create_u64("wake_latencies", 0444, d_pvqlock,
+  (u64 *)_wake_latencies);
+   debugfs_create_bool("reset_cnts", 0644, d_pvqlock, (u32 *)_cnts);
+   return 0;
+}
+fs_initcall(pv_qspinlock_debugfs);
+
+/*
+ * Reset all the counts
+ */
+static noinline void pvstat_reset(void)
+{
+   int i;
+
+   for (i = 0; i < pvstat_num; i++)
+   atomic_set([i], 0);
+   atomic64_set(_kick_latencies, 0);
+   atomic64_set(_wake_latencies, 0);
+   reset_cnts = 0;
+}
+
+/*
+ * Increment the PV qspinlock statistics counts
+ */
+static inline void pvstat_inc(enum pv_qlock_stat stat)
+{
+   atomic_inc([stat]);
+   if (unlikely(reset_cnts))
+   pvstat_reset();
+}
+
+/*
+ * PV hash hop count
+ */
+static inline void pvstat_hop(int hopcnt)
+{
+   atomic_add(hopcnt, [pvstat_hops]);
+}
+
+/*
+ * 

[PATCH v5 3/6] locking/pvqspinlock: Collect slowpath lock statistics

2015-08-07 Thread Waiman Long
This patch enables the accumulation of kicking and waiting related
PV qspinlock statistics when the new QUEUED_LOCK_STAT configuration
option is selected. It also enables the collection of kicking and
wakeup latencies which have a heavy dependency on the CPUs being used.

The measured latencies for different CPUs are:

CPU Wakeup  Kicking
--- --  ---
Haswell-EX  89.8us   7.4us
Westmere-EX 67.6us   9.3us

The measured latencies varied a bit from run-to-run. The wakeup
latency is much higher than the kicking latency.

A sample of statistics counts after a kernel build (no CPU overcommit)
was:

hash_hops_count=576912
kick_latencies=5258025484
kick_unlock_count=576911
kick_wait_count=576903
pending_fail_count=10722
pending_lock_count=6123545
spurious_wakeup=92
wait_again_count=75
wait_head_count=60
wait_node_count=576936
wake_latencies=37061460652

Signed-off-by: Waiman Long waiman.l...@hp.com
---
 arch/x86/Kconfig|7 ++
 kernel/locking/qspinlock_paravirt.h |  178 ++-
 2 files changed, 180 insertions(+), 5 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 4c9c8b8..86bf53e 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -719,6 +719,13 @@ config PARAVIRT_SPINLOCKS
 
  If you are unsure how to answer this question, answer Y.
 
+config QUEUED_LOCK_STAT
+   bool Paravirt queued lock statistics
+   depends on PARAVIRT  DEBUG_FS  QUEUED_SPINLOCKS
+   ---help---
+ Enable the collection of statistical data on the behavior of
+ paravirtualized queued spinlocks and report them on debugfs.
+
 source arch/x86/xen/Kconfig
 
 config KVM_GUEST
diff --git a/kernel/locking/qspinlock_paravirt.h 
b/kernel/locking/qspinlock_paravirt.h
index 94f9adf..5eb5dea 100644
--- a/kernel/locking/qspinlock_paravirt.h
+++ b/kernel/locking/qspinlock_paravirt.h
@@ -49,6 +49,151 @@ struct pv_node {
 };
 
 /*
+ * PV qspinlock statistics
+ */
+enum pv_qlock_stat {
+   pvstat_wait_head,
+   pvstat_wait_node,
+   pvstat_wait_again,
+   pvstat_kick_wait,
+   pvstat_kick_unlock,
+   pvstat_pend_lock,
+   pvstat_pend_fail,
+   pvstat_spurious,
+   pvstat_hops,
+   pvstat_num  /* Total number of statistics counts */
+};
+
+#ifdef CONFIG_QUEUED_LOCK_STAT
+/*
+ * Collect pvqspinlock statiatics
+ */
+#include linux/debugfs.h
+#include linux/sched.h
+
+static const char * const stat_fsnames[pvstat_num] = {
+   [pvstat_wait_head]   = wait_head_count,
+   [pvstat_wait_node]   = wait_node_count,
+   [pvstat_wait_again]  = wait_again_count,
+   [pvstat_kick_wait]   = kick_wait_count,
+   [pvstat_kick_unlock] = kick_unlock_count,
+   [pvstat_pend_lock]   = pending_lock_count,
+   [pvstat_pend_fail]   = pending_fail_count,
+   [pvstat_spurious]= spurious_wakeup,
+   [pvstat_hops]= hash_hops_count,
+};
+
+static atomic_t pvstats[pvstat_num];
+
+/*
+ * pv_kick_latencies = sum of all pv_kick latencies in ns
+ * pv_wake_latencies = sum of all wakeup latencies in ns
+ *
+ * Avg kick latency   = pv_kick_latencies/kick_unlock_count
+ * Avg wake latency   = pv_wake_latencies/kick_wait_count
+ * Avg # of hops/hash = hash_hops_count/kick_unlock_count
+ */
+static atomic64_t pv_kick_latencies, pv_wake_latencies;
+static DEFINE_PER_CPU(u64, pv_kick_time);
+
+/*
+ * Reset all the statistics counts if set
+ */
+static bool reset_cnts __read_mostly;
+
+/*
+ * Initialize debugfs for the PV qspinlock statistics
+ */
+static int __init pv_qspinlock_debugfs(void)
+{
+   struct dentry *d_pvqlock = debugfs_create_dir(pv-qspinlock, NULL);
+   int i;
+
+   if (!d_pvqlock)
+   pr_warn(Could not create 'pv-qspinlock' debugfs directory\n);
+
+   for (i = 0; i  pvstat_num; i++)
+   debugfs_create_u32(stat_fsnames[i], 0444, d_pvqlock,
+ (u32 *)pvstats[i]);
+   debugfs_create_u64(kick_latencies, 0444, d_pvqlock,
+  (u64 *)pv_kick_latencies);
+   debugfs_create_u64(wake_latencies, 0444, d_pvqlock,
+  (u64 *)pv_wake_latencies);
+   debugfs_create_bool(reset_cnts, 0644, d_pvqlock, (u32 *)reset_cnts);
+   return 0;
+}
+fs_initcall(pv_qspinlock_debugfs);
+
+/*
+ * Reset all the counts
+ */
+static noinline void pvstat_reset(void)
+{
+   int i;
+
+   for (i = 0; i  pvstat_num; i++)
+   atomic_set(pvstats[i], 0);
+   atomic64_set(pv_kick_latencies, 0);
+   atomic64_set(pv_wake_latencies, 0);
+   reset_cnts = 0;
+}
+
+/*
+ * Increment the PV qspinlock statistics counts
+ */
+static inline void pvstat_inc(enum pv_qlock_stat stat)
+{
+   atomic_inc(pvstats[stat]);
+   if (unlikely(reset_cnts))
+   pvstat_reset();
+}
+
+/*
+ * PV hash hop count
+ */
+static inline void pvstat_hop(int hopcnt)
+{
+