#include <linux/rcupdate.h>
 #include <linux/rculist_bl.h>
 #include <linux/bit_spinlock.h>
+#include <linux/percpu.h>
 
 #include "gfs2.h"
 #include "incore.h"
                do_error(gl, 0); /* Fail queued try locks */
        }
        gl->gl_req = target;
+       set_bit(GLF_BLOCKING, &gl->gl_flags);
+       if ((gl->gl_req == LM_ST_UNLOCKED) ||
+           (gl->gl_state == LM_ST_EXCLUSIVE) ||
+           (lck_flags & (LM_FLAG_TRY|LM_FLAG_TRY_1CB)))
+               clear_bit(GLF_BLOCKING, &gl->gl_flags);
        spin_unlock(&gl->gl_spin);
        if (glops->go_xmote_th)
                glops->go_xmote_th(gl);
                return -ENOMEM;
 
        atomic_inc(&sdp->sd_glock_disposal);
+       gl->gl_sbd = sdp;
        gl->gl_flags = 0;
        gl->gl_name = name;
        atomic_set(&gl->gl_ref, 1);
        gl->gl_demote_state = LM_ST_EXCLUSIVE;
        gl->gl_hash = hash;
        gl->gl_ops = glops;
-       snprintf(gl->gl_strname, GDLM_STRNAME_BYTES, "%8x%16llx", name.ln_type, (unsigned long long)number);
+       gl->gl_dstamp = ktime_set(0, 0);
+       preempt_disable();
+       /* We use the global stats to estimate the initial per-glock stats */
+       gl->gl_stats = this_cpu_ptr(sdp->sd_lkstats)->lkstats[glops->go_type];
+       preempt_enable();
+       gl->gl_stats.stats[GFS2_LKS_DCOUNT] = 0;
+       gl->gl_stats.stats[GFS2_LKS_QCOUNT] = 0;
        memset(&gl->gl_lksb, 0, sizeof(struct dlm_lksb));
        gl->gl_lksb.sb_lvbptr = gl->gl_lvb;
        gl->gl_tchange = jiffies;
        gl->gl_object = NULL;
-       gl->gl_sbd = sdp;
        gl->gl_hold_time = GL_GLOCK_DFT_HOLD;
        INIT_DELAYED_WORK(&gl->gl_work, glock_work_func);
        INIT_WORK(&gl->gl_delete, delete_work_func);
        }
        set_bit(GLF_QUEUED, &gl->gl_flags);
        trace_gfs2_glock_queue(gh, 1);
+       gfs2_glstats_inc(gl, GFS2_LKS_QCOUNT);
+       gfs2_sbstats_inc(gl, GFS2_LKS_QCOUNT);
        if (likely(insert_pt == NULL)) {
                list_add_tail(&gh->gh_list, &gl->gl_holders);
                if (unlikely(gh->gh_flags & LM_FLAG_PRIORITY))
                *p++ = 'L';
        if (gl->gl_object)
                *p++ = 'o';
+       if (test_bit(GLF_BLOCKING, gflags))
+               *p++ = 'b';
        *p = 0;
        return buf;
 }
        return error;
 }
 
+static int gfs2_glstats_seq_show(struct seq_file *seq, void *iter_ptr)
+{
+       struct gfs2_glock *gl = iter_ptr;
+
+       seq_printf(seq, "G: n:%u/%llx rtt:%lld/%lld rttb:%lld/%lld irt:%lld/%lld dcnt: %lld qcnt: %lld\n",
+                  gl->gl_name.ln_type,
+                  (unsigned long long)gl->gl_name.ln_number,
+                  (long long)gl->gl_stats.stats[GFS2_LKS_SRTT],
+                  (long long)gl->gl_stats.stats[GFS2_LKS_SRTTVAR],
+                  (long long)gl->gl_stats.stats[GFS2_LKS_SRTTB],
+                  (long long)gl->gl_stats.stats[GFS2_LKS_SRTTVARB],
+                  (long long)gl->gl_stats.stats[GFS2_LKS_SIRT],
+                  (long long)gl->gl_stats.stats[GFS2_LKS_SIRTVAR],
+                  (long long)gl->gl_stats.stats[GFS2_LKS_DCOUNT],
+                  (long long)gl->gl_stats.stats[GFS2_LKS_QCOUNT]);
+       return 0;
+}
+
+static const char *gfs2_gltype[] = {
+       "type",
+       "reserved",
+       "nondisk",
+       "inode",
+       "rgrp",
+       "meta",
+       "iopen",
+       "flock",
+       "plock",
+       "quota",
+       "journal",
+};
+
+static const char *gfs2_stype[] = {
+       [GFS2_LKS_SRTT]         = "srtt",
+       [GFS2_LKS_SRTTVAR]      = "srttvar",
+       [GFS2_LKS_SRTTB]        = "srttb",
+       [GFS2_LKS_SRTTVARB]     = "srttvarb",
+       [GFS2_LKS_SIRT]         = "sirt",
+       [GFS2_LKS_SIRTVAR]      = "sirtvar",
+       [GFS2_LKS_DCOUNT]       = "dlm",
+       [GFS2_LKS_QCOUNT]       = "queue",
+};
+
+#define GFS2_NR_SBSTATS (ARRAY_SIZE(gfs2_gltype) * ARRAY_SIZE(gfs2_stype))
+
+static int gfs2_sbstats_seq_show(struct seq_file *seq, void *iter_ptr)
+{
+       struct gfs2_glock_iter *gi = seq->private;
+       struct gfs2_sbd *sdp = gi->sdp;
+       unsigned index = gi->hash >> 3;
+       unsigned subindex = gi->hash & 0x07;
+       s64 value;
+       int i;
+
+       if (index == 0 && subindex != 0)
+               return 0;
 
+       seq_printf(seq, "%-10s %8s:", gfs2_gltype[index],
+                  (index == 0) ? "cpu": gfs2_stype[subindex]);
 
+       for_each_possible_cpu(i) {
+                const struct gfs2_pcpu_lkstats *lkstats = per_cpu_ptr(sdp->sd_lkstats, i);
+               if (index == 0) {
+                       value = i;
+               } else {
+                       value = lkstats->lkstats[index - 1].stats[subindex];
+               }
+               seq_printf(seq, " %15lld", (long long)value);
+       }
+       seq_putc(seq, '\n');
+       return 0;
+}
 
 int __init gfs2_glock_init(void)
 {
        return dump_glock(seq, iter_ptr);
 }
 
+static void *gfs2_sbstats_seq_start(struct seq_file *seq, loff_t *pos)
+{
+       struct gfs2_glock_iter *gi = seq->private;
+
+       gi->hash = *pos;
+       if (*pos >= GFS2_NR_SBSTATS)
+               return NULL;
+       preempt_disable();
+       return SEQ_START_TOKEN;
+}
+
+static void *gfs2_sbstats_seq_next(struct seq_file *seq, void *iter_ptr,
+                                  loff_t *pos)
+{
+       struct gfs2_glock_iter *gi = seq->private;
+       (*pos)++;
+       gi->hash++;
+       if (gi->hash >= GFS2_NR_SBSTATS) {
+               preempt_enable();
+               return NULL;
+       }
+       return SEQ_START_TOKEN;
+}
+
+static void gfs2_sbstats_seq_stop(struct seq_file *seq, void *iter_ptr)
+{
+       preempt_enable();
+}
+
 static const struct seq_operations gfs2_glock_seq_ops = {
        .start = gfs2_glock_seq_start,
        .next  = gfs2_glock_seq_next,
        .show  = gfs2_glock_seq_show,
 };
 
-static int gfs2_debugfs_open(struct inode *inode, struct file *file)
+static const struct seq_operations gfs2_glstats_seq_ops = {
+       .start = gfs2_glock_seq_start,
+       .next  = gfs2_glock_seq_next,
+       .stop  = gfs2_glock_seq_stop,
+       .show  = gfs2_glstats_seq_show,
+};
+
+static const struct seq_operations gfs2_sbstats_seq_ops = {
+       .start = gfs2_sbstats_seq_start,
+       .next  = gfs2_sbstats_seq_next,
+       .stop  = gfs2_sbstats_seq_stop,
+       .show  = gfs2_sbstats_seq_show,
+};
+
+static int gfs2_glocks_open(struct inode *inode, struct file *file)
 {
        int ret = seq_open_private(file, &gfs2_glock_seq_ops,
                                   sizeof(struct gfs2_glock_iter));
        return ret;
 }
 
-static const struct file_operations gfs2_debug_fops = {
+static int gfs2_glstats_open(struct inode *inode, struct file *file)
+{
+       int ret = seq_open_private(file, &gfs2_glstats_seq_ops,
+                                  sizeof(struct gfs2_glock_iter));
+       if (ret == 0) {
+               struct seq_file *seq = file->private_data;
+               struct gfs2_glock_iter *gi = seq->private;
+               gi->sdp = inode->i_private;
+       }
+       return ret;
+}
+
+static int gfs2_sbstats_open(struct inode *inode, struct file *file)
+{
+       int ret = seq_open_private(file, &gfs2_sbstats_seq_ops,
+                                  sizeof(struct gfs2_glock_iter));
+       if (ret == 0) {
+               struct seq_file *seq = file->private_data;
+               struct gfs2_glock_iter *gi = seq->private;
+               gi->sdp = inode->i_private;
+       }
+       return ret;
+}
+
+static const struct file_operations gfs2_glocks_fops = {
+       .owner   = THIS_MODULE,
+       .open    = gfs2_glocks_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release_private,
+};
+
+static const struct file_operations gfs2_glstats_fops = {
        .owner   = THIS_MODULE,
-       .open    = gfs2_debugfs_open,
+       .open    = gfs2_glstats_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release_private,
+};
+
+static const struct file_operations gfs2_sbstats_fops = {
+       .owner   = THIS_MODULE,
+       .open    = gfs2_sbstats_open,
        .read    = seq_read,
        .llseek  = seq_lseek,
        .release = seq_release_private,
        sdp->debugfs_dentry_glocks = debugfs_create_file("glocks",
                                                         S_IFREG | S_IRUGO,
                                                         sdp->debugfs_dir, sdp,
-                                                        &gfs2_debug_fops);
+                                                        &gfs2_glocks_fops);
        if (!sdp->debugfs_dentry_glocks)
-               return -ENOMEM;
+               goto fail;
+
+       sdp->debugfs_dentry_glstats = debugfs_create_file("glstats",
+                                                       S_IFREG | S_IRUGO,
+                                                       sdp->debugfs_dir, sdp,
+                                                       &gfs2_glstats_fops);
+       if (!sdp->debugfs_dentry_glstats)
+               goto fail;
+
+       sdp->debugfs_dentry_sbstats = debugfs_create_file("sbstats",
+                                                       S_IFREG | S_IRUGO,
+                                                       sdp->debugfs_dir, sdp,
+                                                       &gfs2_sbstats_fops);
+       if (!sdp->debugfs_dentry_sbstats)
+               goto fail;
 
        return 0;
+fail:
+       gfs2_delete_debugfs_file(sdp);
+       return -ENOMEM;
 }
 
 void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp)
 {
-       if (sdp && sdp->debugfs_dir) {
+       if (sdp->debugfs_dir) {
                if (sdp->debugfs_dentry_glocks) {
                        debugfs_remove(sdp->debugfs_dentry_glocks);
                        sdp->debugfs_dentry_glocks = NULL;
                }
+               if (sdp->debugfs_dentry_glstats) {
+                       debugfs_remove(sdp->debugfs_dentry_glstats);
+                       sdp->debugfs_dentry_glstats = NULL;
+               }
+               if (sdp->debugfs_dentry_sbstats) {
+                       debugfs_remove(sdp->debugfs_dentry_sbstats);
+                       sdp->debugfs_dentry_sbstats = NULL;
+               }
                debugfs_remove(sdp->debugfs_dir);
                sdp->debugfs_dir = NULL;
        }
 
 #include <linux/rculist_bl.h>
 #include <linux/completion.h>
 #include <linux/rbtree.h>
+#include <linux/ktime.h>
+#include <linux/percpu.h>
 
 #define DIO_WAIT       0x00000010
 #define DIO_METADATA   0x00000020
 #define GLOF_ASPACE 1
 };
 
+enum {
+       GFS2_LKS_SRTT = 0,      /* Non blocking smoothed round trip time */
+       GFS2_LKS_SRTTVAR = 1,   /* Non blocking smoothed variance */
+       GFS2_LKS_SRTTB = 2,     /* Blocking smoothed round trip time */
+       GFS2_LKS_SRTTVARB = 3,  /* Blocking smoothed variance */
+       GFS2_LKS_SIRT = 4,      /* Smoothed Inter-request time */
+       GFS2_LKS_SIRTVAR = 5,   /* Smoothed Inter-request variance */
+       GFS2_LKS_DCOUNT = 6,    /* Count of dlm requests */
+       GFS2_LKS_QCOUNT = 7,    /* Count of gfs2_holder queues */
+       GFS2_NR_LKSTATS
+};
+
+struct gfs2_lkstats {
+       s64 stats[GFS2_NR_LKSTATS];
+};
+
 enum {
        /* States */
        HIF_HOLDER              = 6,  /* Set for gh that "holds" the glock */
        GLF_QUEUED                      = 12,
        GLF_LRU                         = 13,
        GLF_OBJECT                      = 14, /* Used only for tracing */
+       GLF_BLOCKING                    = 15,
 };
 
 struct gfs2_glock {
        struct hlist_bl_node gl_list;
+       struct gfs2_sbd *gl_sbd;
        unsigned long gl_flags;         /* GLF_... */
        struct lm_lockname gl_name;
        atomic_t gl_ref;
        struct list_head gl_holders;
 
        const struct gfs2_glock_operations *gl_ops;
-       char gl_strname[GDLM_STRNAME_BYTES];
+       ktime_t gl_dstamp;
+       struct gfs2_lkstats gl_stats;
        struct dlm_lksb gl_lksb;
        char gl_lvb[32];
        unsigned long gl_tchange;
        void *gl_object;
 
        struct list_head gl_lru;
-
-       struct gfs2_sbd *gl_sbd;
-
        struct list_head gl_ail_list;
        atomic_t gl_ail_count;
        atomic_t gl_revokes;
        uint32_t *ls_recover_result; /* result of last jid recovery */
 };
 
+struct gfs2_pcpu_lkstats {
+       /* One struct for each glock type */
+       struct gfs2_lkstats lkstats[10];
+};
+
 struct gfs2_sbd {
        struct super_block *sd_vfs;
+       struct gfs2_pcpu_lkstats __percpu *sd_lkstats;
        struct kobject sd_kobj;
        unsigned long sd_flags; /* SDF_... */
        struct gfs2_sb_host sd_sb;
 
        unsigned long sd_last_warning;
        struct dentry *debugfs_dir;    /* debugfs directory */
-       struct dentry *debugfs_dentry_glocks; /* for debugfs */
+       struct dentry *debugfs_dentry_glocks;
+       struct dentry *debugfs_dentry_glstats;
+       struct dentry *debugfs_dentry_sbstats;
 };
 
+static inline void gfs2_glstats_inc(struct gfs2_glock *gl, int which)
+{
+       gl->gl_stats.stats[which]++;
+}
+
+static inline void gfs2_sbstats_inc(const struct gfs2_glock *gl, int which)
+{
+       const struct gfs2_sbd *sdp = gl->gl_sbd;
+       preempt_disable();
+       this_cpu_ptr(sdp->sd_lkstats)->lkstats[gl->gl_name.ln_type].stats[which]++;
+       preempt_enable();
+}
+
 #endif /* __INCORE_DOT_H__ */
 
 
 #include "glock.h"
 #include "util.h"
 #include "sys.h"
+#include "trace_gfs2.h"
 
 extern struct workqueue_struct *gfs2_control_wq;
 
+/**
+ * gfs2_update_stats - Update time based stats
+ * @mv: Pointer to mean/variance structure to update
+ * @sample: New data to include
+ *
+ * @delta is the difference between the current rtt sample and the
+ * running average srtt. We add 1/8 of that to the srtt in order to
+ * update the current srtt estimate. The varience estimate is a bit
+ * more complicated. We subtract the abs value of the @delta from
+ * the current variance estimate and add 1/4 of that to the running
+ * total.
+ *
+ * Note that the index points at the array entry containing the smoothed
+ * mean value, and the variance is always in the following entry
+ *
+ * Reference: TCP/IP Illustrated, vol 2, p. 831,832
+ * All times are in units of integer nanoseconds. Unlike the TCP/IP case,
+ * they are not scaled fixed point.
+ */
+
+static inline void gfs2_update_stats(struct gfs2_lkstats *s, unsigned index,
+                                    s64 sample)
+{
+       s64 delta = sample - s->stats[index];
+       s->stats[index] += (delta >> 3);
+       index++;
+       s->stats[index] += ((abs64(delta) - s->stats[index]) >> 2);
+}
+
+/**
+ * gfs2_update_reply_times - Update locking statistics
+ * @gl: The glock to update
+ *
+ * This assumes that gl->gl_dstamp has been set earlier.
+ *
+ * The rtt (lock round trip time) is an estimate of the time
+ * taken to perform a dlm lock request. We update it on each
+ * reply from the dlm.
+ *
+ * The blocking flag is set on the glock for all dlm requests
+ * which may potentially block due to lock requests from other nodes.
+ * DLM requests where the current lock state is exclusive, the
+ * requested state is null (or unlocked) or where the TRY or
+ * TRY_1CB flags are set are classified as non-blocking. All
+ * other DLM requests are counted as (potentially) blocking.
+ */
+static inline void gfs2_update_reply_times(struct gfs2_glock *gl)
+{
+       struct gfs2_pcpu_lkstats *lks;
+       const unsigned gltype = gl->gl_name.ln_type;
+       unsigned index = test_bit(GLF_BLOCKING, &gl->gl_flags) ?
+                        GFS2_LKS_SRTTB : GFS2_LKS_SRTT;
+       s64 rtt;
+
+       preempt_disable();
+       rtt = ktime_to_ns(ktime_sub(ktime_get_real(), gl->gl_dstamp));
+       lks = this_cpu_ptr(gl->gl_sbd->sd_lkstats);
+       gfs2_update_stats(&gl->gl_stats, index, rtt);           /* Local */
+       gfs2_update_stats(&lks->lkstats[gltype], index, rtt);   /* Global */
+       preempt_enable();
+
+       trace_gfs2_glock_lock_time(gl, rtt);
+}
+
+/**
+ * gfs2_update_request_times - Update locking statistics
+ * @gl: The glock to update
+ *
+ * The irt (lock inter-request times) measures the average time
+ * between requests to the dlm. It is updated immediately before
+ * each dlm call.
+ */
+
+static inline void gfs2_update_request_times(struct gfs2_glock *gl)
+{
+       struct gfs2_pcpu_lkstats *lks;
+       const unsigned gltype = gl->gl_name.ln_type;
+       ktime_t dstamp;
+       s64 irt;
+
+       preempt_disable();
+       dstamp = gl->gl_dstamp;
+       gl->gl_dstamp = ktime_get_real();
+       irt = ktime_to_ns(ktime_sub(gl->gl_dstamp, dstamp));
+       lks = this_cpu_ptr(gl->gl_sbd->sd_lkstats);
+       gfs2_update_stats(&gl->gl_stats, GFS2_LKS_SIRT, irt);           /* Local */
+       gfs2_update_stats(&lks->lkstats[gltype], GFS2_LKS_SIRT, irt);   /* Global */
+       preempt_enable();
+}
+ 
 static void gdlm_ast(void *arg)
 {
        struct gfs2_glock *gl = arg;
        unsigned ret = gl->gl_state;
 
+       gfs2_update_reply_times(gl);
        BUG_ON(gl->gl_lksb.sb_flags & DLM_SBF_DEMOTED);
 
        if (gl->gl_lksb.sb_flags & DLM_SBF_VALNOTVALID)
 static u32 make_flags(const u32 lkid, const unsigned int gfs_flags,
                      const int req)
 {
-       u32 lkf = 0;
+       u32 lkf = DLM_LKF_VALBLK;
 
        if (gfs_flags & LM_FLAG_TRY)
                lkf |= DLM_LKF_NOQUEUE;
        if (lkid != 0) 
                lkf |= DLM_LKF_CONVERT;
 
-       lkf |= DLM_LKF_VALBLK;
-
        return lkf;
 }
 
+static void gfs2_reverse_hex(char *c, u64 value)
+{
+       while (value) {
+               *c-- = hex_asc[value & 0x0f];
+               value >>= 4;
+       }
+}
+
 static int gdlm_lock(struct gfs2_glock *gl, unsigned int req_state,
                     unsigned int flags)
 {
        struct lm_lockstruct *ls = &gl->gl_sbd->sd_lockstruct;
        int req;
        u32 lkf;
+       char strname[GDLM_STRNAME_BYTES] = "";
 
        req = make_mode(req_state);
        lkf = make_flags(gl->gl_lksb.sb_lkid, flags, req);
-
+       gfs2_glstats_inc(gl, GFS2_LKS_DCOUNT);
+       gfs2_sbstats_inc(gl, GFS2_LKS_DCOUNT);
+       if (gl->gl_lksb.sb_lkid) {
+               gfs2_update_request_times(gl);
+       } else {
+               memset(strname, ' ', GDLM_STRNAME_BYTES - 1);
+               strname[GDLM_STRNAME_BYTES - 1] = '\0';
+               gfs2_reverse_hex(strname + 7, gl->gl_name.ln_type);
+               gfs2_reverse_hex(strname + 23, gl->gl_name.ln_number);
+               gl->gl_dstamp = ktime_get_real();
+       }
        /*
         * Submit the actual lock request.
         */
 
-       return dlm_lock(ls->ls_dlm, req, &gl->gl_lksb, lkf, gl->gl_strname,
+       return dlm_lock(ls->ls_dlm, req, &gl->gl_lksb, lkf, strname,
                        GDLM_STRNAME_BYTES - 1, 0, gdlm_ast, gl, gdlm_bast);
 }
 
                return;
        }
 
+       clear_bit(GLF_BLOCKING, &gl->gl_flags);
+       gfs2_glstats_inc(gl, GFS2_LKS_DCOUNT);
+       gfs2_sbstats_inc(gl, GFS2_LKS_DCOUNT);
+       gfs2_update_request_times(gl);
        error = dlm_unlock(ls->ls_dlm, gl->gl_lksb.sb_lkid, DLM_LKF_VALBLK,
                           NULL, gl);
        if (error) {
 
 
        sb->s_fs_info = sdp;
        sdp->sd_vfs = sb;
+       sdp->sd_lkstats = alloc_percpu(struct gfs2_pcpu_lkstats);
+       if (!sdp->sd_lkstats) {
+               kfree(sdp);
+               return NULL;
+       }
+
        set_bit(SDF_NOJOURNALID, &sdp->sd_flags);
        gfs2_tune_init(&sdp->sd_tune);
 
        gfs2_sys_fs_del(sdp);
 fail:
        gfs2_delete_debugfs_file(sdp);
+       free_percpu(sdp->sd_lkstats);
        kfree(sdp);
        sb->s_fs_info = NULL;
        return error;
        shrink_dcache_sb(sb);
        kill_block_super(sb);
        gfs2_delete_debugfs_file(sdp);
+       free_percpu(sdp->sd_lkstats);
        kfree(sdp);
 }
 
 
 #include <linux/dlmconstants.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/writeback.h>
+#include <linux/ktime.h>
 #include "incore.h"
 #include "glock.h"
 
        {(1UL << GLF_FROZEN),                   "F" },          \
        {(1UL << GLF_QUEUED),                   "q" },          \
        {(1UL << GLF_LRU),                      "L" },          \
-       {(1UL << GLF_OBJECT),                   "o" })
+       {(1UL << GLF_OBJECT),                   "o" },          \
+       {(1UL << GLF_BLOCKING),                 "b" })
 
 #ifndef NUMPTY
 #define NUMPTY
                  glock_trace_name(__entry->state))
 );
 
+/* DLM sends a reply to GFS2 */
+TRACE_EVENT(gfs2_glock_lock_time,
+
+       TP_PROTO(const struct gfs2_glock *gl, s64 tdiff),
+
+       TP_ARGS(gl, tdiff),
+
+       TP_STRUCT__entry(
+               __field(        dev_t,  dev             )
+               __field(        u64,    glnum           )
+               __field(        u32,    gltype          )
+               __field(        int,    status          )
+               __field(        char,   flags           )
+               __field(        s64,    tdiff           )
+               __field(        s64,    srtt            )
+               __field(        s64,    srttvar         )
+               __field(        s64,    srttb           )
+               __field(        s64,    srttvarb        )
+               __field(        s64,    sirt            )
+               __field(        s64,    sirtvar         )
+               __field(        s64,    dcount          )
+               __field(        s64,    qcount          )
+       ),
+
+       TP_fast_assign(
+               __entry->dev            = gl->gl_sbd->sd_vfs->s_dev;
+               __entry->glnum          = gl->gl_name.ln_number;
+               __entry->gltype         = gl->gl_name.ln_type;
+               __entry->status         = gl->gl_lksb.sb_status;
+               __entry->flags          = gl->gl_lksb.sb_flags;
+               __entry->tdiff          = tdiff;
+               __entry->srtt           = gl->gl_stats.stats[GFS2_LKS_SRTT];
+               __entry->srttvar        = gl->gl_stats.stats[GFS2_LKS_SRTTVAR];
+               __entry->srttb          = gl->gl_stats.stats[GFS2_LKS_SRTTB];
+               __entry->srttvarb       = gl->gl_stats.stats[GFS2_LKS_SRTTVARB];
+               __entry->sirt           = gl->gl_stats.stats[GFS2_LKS_SIRT];
+               __entry->sirtvar        = gl->gl_stats.stats[GFS2_LKS_SIRTVAR];
+               __entry->dcount         = gl->gl_stats.stats[GFS2_LKS_DCOUNT];
+               __entry->qcount         = gl->gl_stats.stats[GFS2_LKS_QCOUNT];
+       ),
+
+       TP_printk("%u,%u glock %d:%lld status:%d flags:%02x tdiff:%lld srtt:%lld/%lld srttb:%lld/%lld sirt:%lld/%lld dcnt:%lld qcnt:%lld",
+                 MAJOR(__entry->dev), MINOR(__entry->dev), __entry->gltype,
+                 (unsigned long long)__entry->glnum,
+                 __entry->status, __entry->flags,
+                 (long long)__entry->tdiff,
+                 (long long)__entry->srtt,
+                 (long long)__entry->srttvar,
+                 (long long)__entry->srttb,
+                 (long long)__entry->srttvarb,
+                 (long long)__entry->sirt,
+                 (long long)__entry->sirtvar,
+                 (long long)__entry->dcount,
+                 (long long)__entry->qcount)
+);
+
 /* Section 2 - Log/journal
  *
  * Objectives: