|
@@ -29,6 +29,7 @@
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/rcupdate.h>
|
|
#include <linux/rculist_bl.h>
|
|
#include <linux/rculist_bl.h>
|
|
#include <linux/bit_spinlock.h>
|
|
#include <linux/bit_spinlock.h>
|
|
|
|
+#include <linux/percpu.h>
|
|
|
|
|
|
#include "gfs2.h"
|
|
#include "gfs2.h"
|
|
#include "incore.h"
|
|
#include "incore.h"
|
|
@@ -543,6 +544,11 @@ __acquires(&gl->gl_spin)
|
|
do_error(gl, 0); /* Fail queued try locks */
|
|
do_error(gl, 0); /* Fail queued try locks */
|
|
}
|
|
}
|
|
gl->gl_req = target;
|
|
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);
|
|
spin_unlock(&gl->gl_spin);
|
|
if (glops->go_xmote_th)
|
|
if (glops->go_xmote_th)
|
|
glops->go_xmote_th(gl);
|
|
glops->go_xmote_th(gl);
|
|
@@ -744,6 +750,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
|
|
|
|
atomic_inc(&sdp->sd_glock_disposal);
|
|
atomic_inc(&sdp->sd_glock_disposal);
|
|
|
|
+ gl->gl_sbd = sdp;
|
|
gl->gl_flags = 0;
|
|
gl->gl_flags = 0;
|
|
gl->gl_name = name;
|
|
gl->gl_name = name;
|
|
atomic_set(&gl->gl_ref, 1);
|
|
atomic_set(&gl->gl_ref, 1);
|
|
@@ -752,12 +759,17 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
|
|
gl->gl_demote_state = LM_ST_EXCLUSIVE;
|
|
gl->gl_demote_state = LM_ST_EXCLUSIVE;
|
|
gl->gl_hash = hash;
|
|
gl->gl_hash = hash;
|
|
gl->gl_ops = glops;
|
|
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));
|
|
memset(&gl->gl_lksb, 0, sizeof(struct dlm_lksb));
|
|
gl->gl_lksb.sb_lvbptr = gl->gl_lvb;
|
|
gl->gl_lksb.sb_lvbptr = gl->gl_lvb;
|
|
gl->gl_tchange = jiffies;
|
|
gl->gl_tchange = jiffies;
|
|
gl->gl_object = NULL;
|
|
gl->gl_object = NULL;
|
|
- gl->gl_sbd = sdp;
|
|
|
|
gl->gl_hold_time = GL_GLOCK_DFT_HOLD;
|
|
gl->gl_hold_time = GL_GLOCK_DFT_HOLD;
|
|
INIT_DELAYED_WORK(&gl->gl_work, glock_work_func);
|
|
INIT_DELAYED_WORK(&gl->gl_work, glock_work_func);
|
|
INIT_WORK(&gl->gl_delete, delete_work_func);
|
|
INIT_WORK(&gl->gl_delete, delete_work_func);
|
|
@@ -999,6 +1011,8 @@ fail:
|
|
}
|
|
}
|
|
set_bit(GLF_QUEUED, &gl->gl_flags);
|
|
set_bit(GLF_QUEUED, &gl->gl_flags);
|
|
trace_gfs2_glock_queue(gh, 1);
|
|
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)) {
|
|
if (likely(insert_pt == NULL)) {
|
|
list_add_tail(&gh->gh_list, &gl->gl_holders);
|
|
list_add_tail(&gh->gh_list, &gl->gl_holders);
|
|
if (unlikely(gh->gh_flags & LM_FLAG_PRIORITY))
|
|
if (unlikely(gh->gh_flags & LM_FLAG_PRIORITY))
|
|
@@ -1658,6 +1672,8 @@ static const char *gflags2str(char *buf, const struct gfs2_glock *gl)
|
|
*p++ = 'L';
|
|
*p++ = 'L';
|
|
if (gl->gl_object)
|
|
if (gl->gl_object)
|
|
*p++ = 'o';
|
|
*p++ = 'o';
|
|
|
|
+ if (test_bit(GLF_BLOCKING, gflags))
|
|
|
|
+ *p++ = 'b';
|
|
*p = 0;
|
|
*p = 0;
|
|
return buf;
|
|
return buf;
|
|
}
|
|
}
|
|
@@ -1714,8 +1730,78 @@ out:
|
|
return error;
|
|
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)
|
|
int __init gfs2_glock_init(void)
|
|
{
|
|
{
|
|
@@ -1828,6 +1914,35 @@ static int gfs2_glock_seq_show(struct seq_file *seq, void *iter_ptr)
|
|
return dump_glock(seq, iter_ptr);
|
|
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 = {
|
|
static const struct seq_operations gfs2_glock_seq_ops = {
|
|
.start = gfs2_glock_seq_start,
|
|
.start = gfs2_glock_seq_start,
|
|
.next = gfs2_glock_seq_next,
|
|
.next = gfs2_glock_seq_next,
|
|
@@ -1835,7 +1950,21 @@ static const struct seq_operations gfs2_glock_seq_ops = {
|
|
.show = gfs2_glock_seq_show,
|
|
.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,
|
|
int ret = seq_open_private(file, &gfs2_glock_seq_ops,
|
|
sizeof(struct gfs2_glock_iter));
|
|
sizeof(struct gfs2_glock_iter));
|
|
@@ -1847,9 +1976,49 @@ static int gfs2_debugfs_open(struct inode *inode, struct file *file)
|
|
return ret;
|
|
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,
|
|
.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,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.llseek = seq_lseek,
|
|
.release = seq_release_private,
|
|
.release = seq_release_private,
|
|
@@ -1863,20 +2032,45 @@ int gfs2_create_debugfs_file(struct gfs2_sbd *sdp)
|
|
sdp->debugfs_dentry_glocks = debugfs_create_file("glocks",
|
|
sdp->debugfs_dentry_glocks = debugfs_create_file("glocks",
|
|
S_IFREG | S_IRUGO,
|
|
S_IFREG | S_IRUGO,
|
|
sdp->debugfs_dir, sdp,
|
|
sdp->debugfs_dir, sdp,
|
|
- &gfs2_debug_fops);
|
|
|
|
|
|
+ &gfs2_glocks_fops);
|
|
if (!sdp->debugfs_dentry_glocks)
|
|
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;
|
|
return 0;
|
|
|
|
+fail:
|
|
|
|
+ gfs2_delete_debugfs_file(sdp);
|
|
|
|
+ return -ENOMEM;
|
|
}
|
|
}
|
|
|
|
|
|
void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp)
|
|
void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp)
|
|
{
|
|
{
|
|
- if (sdp && sdp->debugfs_dir) {
|
|
|
|
|
|
+ if (sdp->debugfs_dir) {
|
|
if (sdp->debugfs_dentry_glocks) {
|
|
if (sdp->debugfs_dentry_glocks) {
|
|
debugfs_remove(sdp->debugfs_dentry_glocks);
|
|
debugfs_remove(sdp->debugfs_dentry_glocks);
|
|
sdp->debugfs_dentry_glocks = NULL;
|
|
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);
|
|
debugfs_remove(sdp->debugfs_dir);
|
|
sdp->debugfs_dir = NULL;
|
|
sdp->debugfs_dir = NULL;
|
|
}
|
|
}
|