2019-05-31 15:09:56 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2006-01-16 23:50:04 +07:00
|
|
|
/*
|
|
|
|
* Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
|
2006-05-19 02:09:15 +07:00
|
|
|
* Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
|
2006-01-16 23:50:04 +07:00
|
|
|
*/
|
|
|
|
|
2014-03-07 03:10:45 +07:00
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
#include <linux/sched.h>
|
2017-02-02 23:54:15 +07:00
|
|
|
#include <linux/cred.h>
|
2006-01-16 23:50:04 +07:00
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/completion.h>
|
|
|
|
#include <linux/buffer_head.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/kobject.h>
|
2016-12-25 02:46:01 +07:00
|
|
|
#include <linux/uaccess.h>
|
2009-01-12 17:43:39 +07:00
|
|
|
#include <linux/gfs2_ondisk.h>
|
2009-08-13 18:18:08 +07:00
|
|
|
#include <linux/genhd.h>
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
#include "gfs2.h"
|
2006-02-28 05:23:27 +07:00
|
|
|
#include "incore.h"
|
2006-01-16 23:50:04 +07:00
|
|
|
#include "sys.h"
|
|
|
|
#include "super.h"
|
|
|
|
#include "glock.h"
|
|
|
|
#include "quota.h"
|
2006-02-28 05:23:27 +07:00
|
|
|
#include "util.h"
|
GFS2: Add a "demote a glock" interface to sysfs
This adds a sysfs file called demote_rq to GFS2's
per filesystem directory. Its possible to use this
file to demote arbitrary glocks in exactly the same
way as if a request had come in from a remote node.
This is intended for testing issues relating to caching
of data under glocks. Despite that, the interface is
generic enough to send requests to any type of glock,
but be careful as its not always safe to send an
arbitrary message to an arbitrary glock. For that reason
and to prevent DoS, this interface is restricted to root
only.
The messages look like this:
<type>:<glocknumber> <mode>
Example:
echo -n "2:13324 EX" >/sys/fs/gfs2/unity:myfs/demote_rq
Which means "please demote inode glock (type 2) number 13324 so that
I can get an EX (exclusive) lock". The lock modes are those which
would normally be sent by a remote node in its callback so if you
want to unlock a glock, you use EX, to demote to shared, use SH or PR
(depending on whether you like GFS2 or DLM lock modes better!).
If the glock doesn't exist, you'll get -ENOENT returned. If the
arguments don't make sense, you'll get -EINVAL returned.
The plan is that this interface will be used in combination with
the blktrace patch which I recently posted for comments although
it is, of course, still useful in its own right.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
2009-02-12 20:31:58 +07:00
|
|
|
#include "glops.h"
|
2010-07-21 03:09:02 +07:00
|
|
|
#include "recovery.h"
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2009-05-13 20:49:48 +07:00
|
|
|
struct gfs2_attr {
|
|
|
|
struct attribute attr;
|
|
|
|
ssize_t (*show)(struct gfs2_sbd *, char *);
|
|
|
|
ssize_t (*store)(struct gfs2_sbd *, const char *, size_t);
|
|
|
|
};
|
|
|
|
|
|
|
|
static ssize_t gfs2_attr_show(struct kobject *kobj, struct attribute *attr,
|
|
|
|
char *buf)
|
|
|
|
{
|
|
|
|
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
|
|
|
|
struct gfs2_attr *a = container_of(attr, struct gfs2_attr, attr);
|
|
|
|
return a->show ? a->show(sdp, buf) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t gfs2_attr_store(struct kobject *kobj, struct attribute *attr,
|
|
|
|
const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
|
|
|
|
struct gfs2_attr *a = container_of(attr, struct gfs2_attr, attr);
|
|
|
|
return a->store ? a->store(sdp, buf, len) : len;
|
|
|
|
}
|
|
|
|
|
2010-01-19 08:58:23 +07:00
|
|
|
static const struct sysfs_ops gfs2_attr_ops = {
|
2009-05-13 20:49:48 +07:00
|
|
|
.show = gfs2_attr_show,
|
|
|
|
.store = gfs2_attr_store,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct kset *gfs2_kset;
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
static ssize_t id_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
2007-11-02 21:37:15 +07:00
|
|
|
return snprintf(buf, PAGE_SIZE, "%u:%u\n",
|
|
|
|
MAJOR(sdp->sd_vfs->s_dev), MINOR(sdp->sd_vfs->s_dev));
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t fsname_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
2006-09-07 04:57:06 +07:00
|
|
|
return snprintf(buf, PAGE_SIZE, "%s\n", sdp->sd_fsname);
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
2009-02-10 20:48:30 +07:00
|
|
|
static ssize_t uuid_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
2011-05-10 21:01:59 +07:00
|
|
|
struct super_block *s = sdp->sd_vfs;
|
2017-05-10 20:06:33 +07:00
|
|
|
|
2009-02-10 20:48:30 +07:00
|
|
|
buf[0] = '\0';
|
2017-05-10 20:06:33 +07:00
|
|
|
if (uuid_is_null(&s->s_uuid))
|
2009-02-10 20:48:30 +07:00
|
|
|
return 0;
|
2017-05-10 20:06:33 +07:00
|
|
|
return snprintf(buf, PAGE_SIZE, "%pUB\n", &s->s_uuid);
|
2009-02-10 20:48:30 +07:00
|
|
|
}
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
static ssize_t freeze_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
2013-01-11 17:49:34 +07:00
|
|
|
struct super_block *sb = sdp->sd_vfs;
|
|
|
|
int frozen = (sb->s_writers.frozen == SB_UNFROZEN) ? 0 : 1;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2015-01-12 18:01:03 +07:00
|
|
|
return snprintf(buf, PAGE_SIZE, "%d\n", frozen);
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t freeze_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|
|
|
{
|
2015-05-06 01:23:22 +07:00
|
|
|
int error, n;
|
|
|
|
|
|
|
|
error = kstrtoint(buf, 0, &n);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
switch (n) {
|
|
|
|
case 0:
|
2013-01-11 17:49:34 +07:00
|
|
|
error = thaw_super(sdp->sd_vfs);
|
2006-01-16 23:50:04 +07:00
|
|
|
break;
|
|
|
|
case 1:
|
2013-01-11 17:49:34 +07:00
|
|
|
error = freeze_super(sdp->sd_vfs);
|
2006-01-16 23:50:04 +07:00
|
|
|
break;
|
|
|
|
default:
|
2013-01-11 17:49:34 +07:00
|
|
|
return -EINVAL;
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
2013-01-11 17:49:34 +07:00
|
|
|
if (error) {
|
2018-01-31 00:32:30 +07:00
|
|
|
fs_warn(sdp, "freeze %d error %d\n", n, error);
|
2013-01-11 17:49:34 +07:00
|
|
|
return error;
|
|
|
|
}
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2013-01-11 17:49:34 +07:00
|
|
|
return len;
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t withdraw_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
2019-11-14 21:52:15 +07:00
|
|
|
unsigned int b = gfs2_withdrawn(sdp);
|
2006-09-07 04:57:06 +07:00
|
|
|
return snprintf(buf, PAGE_SIZE, "%u\n", b);
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t withdraw_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|
|
|
{
|
2015-05-06 01:23:22 +07:00
|
|
|
int error, val;
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2015-05-06 01:23:22 +07:00
|
|
|
error = kstrtoint(buf, 0, &val);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (val != 1)
|
2006-01-16 23:50:04 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2020-01-24 00:41:00 +07:00
|
|
|
gfs2_lm(sdp, "withdrawing from cluster at user's request\n");
|
|
|
|
gfs2_withdraw(sdp);
|
2014-03-07 03:17:21 +07:00
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t statfs_sync_store(struct gfs2_sbd *sdp, const char *buf,
|
|
|
|
size_t len)
|
|
|
|
{
|
2015-05-06 01:23:22 +07:00
|
|
|
int error, val;
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2015-05-06 01:23:22 +07:00
|
|
|
error = kstrtoint(buf, 0, &val);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (val != 1)
|
2006-01-16 23:50:04 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2009-09-11 20:36:44 +07:00
|
|
|
gfs2_statfs_sync(sdp->sd_vfs, 0);
|
2006-01-16 23:50:04 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t quota_sync_store(struct gfs2_sbd *sdp, const char *buf,
|
|
|
|
size_t len)
|
|
|
|
{
|
2015-05-06 01:23:22 +07:00
|
|
|
int error, val;
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2015-05-06 01:23:22 +07:00
|
|
|
error = kstrtoint(buf, 0, &val);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
if (val != 1)
|
2006-01-16 23:50:04 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-07-03 21:45:28 +07:00
|
|
|
gfs2_quota_sync(sdp->sd_vfs, 0);
|
2006-01-16 23:50:04 +07:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t quota_refresh_user_store(struct gfs2_sbd *sdp, const char *buf,
|
|
|
|
size_t len)
|
|
|
|
{
|
2013-02-01 10:42:40 +07:00
|
|
|
struct kqid qid;
|
2009-09-15 22:20:30 +07:00
|
|
|
int error;
|
2006-09-04 23:49:07 +07:00
|
|
|
u32 id;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2015-05-06 01:23:22 +07:00
|
|
|
error = kstrtou32(buf, 0, &id);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2013-02-01 10:42:40 +07:00
|
|
|
qid = make_kqid(current_user_ns(), USRQUOTA, id);
|
|
|
|
if (!qid_valid(qid))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
error = gfs2_quota_refresh(sdp, qid);
|
2009-09-15 22:20:30 +07:00
|
|
|
return error ? error : len;
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t quota_refresh_group_store(struct gfs2_sbd *sdp, const char *buf,
|
|
|
|
size_t len)
|
|
|
|
{
|
2013-02-01 10:42:40 +07:00
|
|
|
struct kqid qid;
|
2009-09-15 22:20:30 +07:00
|
|
|
int error;
|
2006-09-04 23:49:07 +07:00
|
|
|
u32 id;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2015-05-06 01:23:22 +07:00
|
|
|
error = kstrtou32(buf, 0, &id);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2013-02-01 10:42:40 +07:00
|
|
|
qid = make_kqid(current_user_ns(), GRPQUOTA, id);
|
|
|
|
if (!qid_valid(qid))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
error = gfs2_quota_refresh(sdp, qid);
|
2009-09-15 22:20:30 +07:00
|
|
|
return error ? error : len;
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
GFS2: Add a "demote a glock" interface to sysfs
This adds a sysfs file called demote_rq to GFS2's
per filesystem directory. Its possible to use this
file to demote arbitrary glocks in exactly the same
way as if a request had come in from a remote node.
This is intended for testing issues relating to caching
of data under glocks. Despite that, the interface is
generic enough to send requests to any type of glock,
but be careful as its not always safe to send an
arbitrary message to an arbitrary glock. For that reason
and to prevent DoS, this interface is restricted to root
only.
The messages look like this:
<type>:<glocknumber> <mode>
Example:
echo -n "2:13324 EX" >/sys/fs/gfs2/unity:myfs/demote_rq
Which means "please demote inode glock (type 2) number 13324 so that
I can get an EX (exclusive) lock". The lock modes are those which
would normally be sent by a remote node in its callback so if you
want to unlock a glock, you use EX, to demote to shared, use SH or PR
(depending on whether you like GFS2 or DLM lock modes better!).
If the glock doesn't exist, you'll get -ENOENT returned. If the
arguments don't make sense, you'll get -EINVAL returned.
The plan is that this interface will be used in combination with
the blktrace patch which I recently posted for comments although
it is, of course, still useful in its own right.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
2009-02-12 20:31:58 +07:00
|
|
|
static ssize_t demote_rq_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct gfs2_glock *gl;
|
|
|
|
const struct gfs2_glock_operations *glops;
|
|
|
|
unsigned int glmode;
|
|
|
|
unsigned int gltype;
|
|
|
|
unsigned long long glnum;
|
|
|
|
char mode[16];
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
GFS2: Add a "demote a glock" interface to sysfs
This adds a sysfs file called demote_rq to GFS2's
per filesystem directory. Its possible to use this
file to demote arbitrary glocks in exactly the same
way as if a request had come in from a remote node.
This is intended for testing issues relating to caching
of data under glocks. Despite that, the interface is
generic enough to send requests to any type of glock,
but be careful as its not always safe to send an
arbitrary message to an arbitrary glock. For that reason
and to prevent DoS, this interface is restricted to root
only.
The messages look like this:
<type>:<glocknumber> <mode>
Example:
echo -n "2:13324 EX" >/sys/fs/gfs2/unity:myfs/demote_rq
Which means "please demote inode glock (type 2) number 13324 so that
I can get an EX (exclusive) lock". The lock modes are those which
would normally be sent by a remote node in its callback so if you
want to unlock a glock, you use EX, to demote to shared, use SH or PR
(depending on whether you like GFS2 or DLM lock modes better!).
If the glock doesn't exist, you'll get -ENOENT returned. If the
arguments don't make sense, you'll get -EINVAL returned.
The plan is that this interface will be used in combination with
the blktrace patch which I recently posted for comments although
it is, of course, still useful in its own right.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
2009-02-12 20:31:58 +07:00
|
|
|
|
|
|
|
rv = sscanf(buf, "%u:%llu %15s", &gltype, &glnum,
|
|
|
|
mode);
|
|
|
|
if (rv != 3)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (strcmp(mode, "EX") == 0)
|
|
|
|
glmode = LM_ST_UNLOCKED;
|
|
|
|
else if ((strcmp(mode, "CW") == 0) || (strcmp(mode, "DF") == 0))
|
|
|
|
glmode = LM_ST_DEFERRED;
|
|
|
|
else if ((strcmp(mode, "PR") == 0) || (strcmp(mode, "SH") == 0))
|
|
|
|
glmode = LM_ST_SHARED;
|
|
|
|
else
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (gltype > LM_TYPE_JOURNAL)
|
|
|
|
return -EINVAL;
|
GFS2: remove transaction glock
GFS2 has a transaction glock, which must be grabbed for every
transaction, whose purpose is to deal with freezing the filesystem.
Aside from this involving a large amount of locking, it is very easy to
make the current fsfreeze code hang on unfreezing.
This patch rewrites how gfs2 handles freezing the filesystem. The
transaction glock is removed. In it's place is a freeze glock, which is
cached (but not held) in a shared state by every node in the cluster
when the filesystem is mounted. This lock only needs to be grabbed on
freezing, and actions which need to be safe from freezing, like
recovery.
When a node wants to freeze the filesystem, it grabs this glock
exclusively. When the freeze glock state changes on the nodes (either
from shared to unlocked, or shared to exclusive), the filesystem does a
special log flush. gfs2_log_flush() does all the work for flushing out
the and shutting down the incore log, and then it tries to grab the
freeze glock in a shared state again. Since the filesystem is stuck in
gfs2_log_flush, no new transaction can start, and nothing can be written
to disk. Unfreezing the filesytem simply involes dropping the freeze
glock, allowing gfs2_log_flush() to grab and then release the shared
lock, so it is cached for next time.
However, in order for the unfreezing ioctl to occur, gfs2 needs to get a
shared lock on the filesystem root directory inode to check permissions.
If that glock has already been grabbed exclusively, fsfreeze will be
unable to get the shared lock and unfreeze the filesystem.
In order to allow the unfreeze, this patch makes gfs2 grab a shared lock
on the filesystem root directory during the freeze, and hold it until it
unfreezes the filesystem. The functions which need to grab a shared
lock in order to allow the unfreeze ioctl to be issued now use the lock
grabbed by the freeze code instead.
The freeze and unfreeze code take care to make sure that this shared
lock will not be dropped while another process is using it.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
2014-05-02 10:26:55 +07:00
|
|
|
if (gltype == LM_TYPE_NONDISK && glnum == GFS2_FREEZE_LOCK)
|
|
|
|
glops = &gfs2_freeze_glops;
|
2010-10-06 15:58:44 +07:00
|
|
|
else
|
|
|
|
glops = gfs2_glops_list[gltype];
|
GFS2: Add a "demote a glock" interface to sysfs
This adds a sysfs file called demote_rq to GFS2's
per filesystem directory. Its possible to use this
file to demote arbitrary glocks in exactly the same
way as if a request had come in from a remote node.
This is intended for testing issues relating to caching
of data under glocks. Despite that, the interface is
generic enough to send requests to any type of glock,
but be careful as its not always safe to send an
arbitrary message to an arbitrary glock. For that reason
and to prevent DoS, this interface is restricted to root
only.
The messages look like this:
<type>:<glocknumber> <mode>
Example:
echo -n "2:13324 EX" >/sys/fs/gfs2/unity:myfs/demote_rq
Which means "please demote inode glock (type 2) number 13324 so that
I can get an EX (exclusive) lock". The lock modes are those which
would normally be sent by a remote node in its callback so if you
want to unlock a glock, you use EX, to demote to shared, use SH or PR
(depending on whether you like GFS2 or DLM lock modes better!).
If the glock doesn't exist, you'll get -ENOENT returned. If the
arguments don't make sense, you'll get -EINVAL returned.
The plan is that this interface will be used in combination with
the blktrace patch which I recently posted for comments although
it is, of course, still useful in its own right.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
2009-02-12 20:31:58 +07:00
|
|
|
if (glops == NULL)
|
|
|
|
return -EINVAL;
|
2010-05-14 20:05:51 +07:00
|
|
|
if (!test_and_set_bit(SDF_DEMOTE, &sdp->sd_flags))
|
2010-05-06 17:03:29 +07:00
|
|
|
fs_info(sdp, "demote interface used\n");
|
GFS2: Add a "demote a glock" interface to sysfs
This adds a sysfs file called demote_rq to GFS2's
per filesystem directory. Its possible to use this
file to demote arbitrary glocks in exactly the same
way as if a request had come in from a remote node.
This is intended for testing issues relating to caching
of data under glocks. Despite that, the interface is
generic enough to send requests to any type of glock,
but be careful as its not always safe to send an
arbitrary message to an arbitrary glock. For that reason
and to prevent DoS, this interface is restricted to root
only.
The messages look like this:
<type>:<glocknumber> <mode>
Example:
echo -n "2:13324 EX" >/sys/fs/gfs2/unity:myfs/demote_rq
Which means "please demote inode glock (type 2) number 13324 so that
I can get an EX (exclusive) lock". The lock modes are those which
would normally be sent by a remote node in its callback so if you
want to unlock a glock, you use EX, to demote to shared, use SH or PR
(depending on whether you like GFS2 or DLM lock modes better!).
If the glock doesn't exist, you'll get -ENOENT returned. If the
arguments don't make sense, you'll get -EINVAL returned.
The plan is that this interface will be used in combination with
the blktrace patch which I recently posted for comments although
it is, of course, still useful in its own right.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
2009-02-12 20:31:58 +07:00
|
|
|
rv = gfs2_glock_get(sdp, glnum, glops, 0, &gl);
|
|
|
|
if (rv)
|
|
|
|
return rv;
|
|
|
|
gfs2_glock_cb(gl, glmode);
|
|
|
|
gfs2_glock_put(gl);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
#define GFS2_ATTR(name, mode, show, store) \
|
|
|
|
static struct gfs2_attr gfs2_attr_##name = __ATTR(name, mode, show, store)
|
|
|
|
|
|
|
|
GFS2_ATTR(id, 0444, id_show, NULL);
|
|
|
|
GFS2_ATTR(fsname, 0444, fsname_show, NULL);
|
2009-02-10 20:48:30 +07:00
|
|
|
GFS2_ATTR(uuid, 0444, uuid_show, NULL);
|
2006-01-16 23:50:04 +07:00
|
|
|
GFS2_ATTR(freeze, 0644, freeze_show, freeze_store);
|
|
|
|
GFS2_ATTR(withdraw, 0644, withdraw_show, withdraw_store);
|
|
|
|
GFS2_ATTR(statfs_sync, 0200, NULL, statfs_sync_store);
|
|
|
|
GFS2_ATTR(quota_sync, 0200, NULL, quota_sync_store);
|
|
|
|
GFS2_ATTR(quota_refresh_user, 0200, NULL, quota_refresh_user_store);
|
|
|
|
GFS2_ATTR(quota_refresh_group, 0200, NULL, quota_refresh_group_store);
|
GFS2: Add a "demote a glock" interface to sysfs
This adds a sysfs file called demote_rq to GFS2's
per filesystem directory. Its possible to use this
file to demote arbitrary glocks in exactly the same
way as if a request had come in from a remote node.
This is intended for testing issues relating to caching
of data under glocks. Despite that, the interface is
generic enough to send requests to any type of glock,
but be careful as its not always safe to send an
arbitrary message to an arbitrary glock. For that reason
and to prevent DoS, this interface is restricted to root
only.
The messages look like this:
<type>:<glocknumber> <mode>
Example:
echo -n "2:13324 EX" >/sys/fs/gfs2/unity:myfs/demote_rq
Which means "please demote inode glock (type 2) number 13324 so that
I can get an EX (exclusive) lock". The lock modes are those which
would normally be sent by a remote node in its callback so if you
want to unlock a glock, you use EX, to demote to shared, use SH or PR
(depending on whether you like GFS2 or DLM lock modes better!).
If the glock doesn't exist, you'll get -ENOENT returned. If the
arguments don't make sense, you'll get -EINVAL returned.
The plan is that this interface will be used in combination with
the blktrace patch which I recently posted for comments although
it is, of course, still useful in its own right.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
2009-02-12 20:31:58 +07:00
|
|
|
GFS2_ATTR(demote_rq, 0200, NULL, demote_rq_store);
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
static struct attribute *gfs2_attrs[] = {
|
|
|
|
&gfs2_attr_id.attr,
|
|
|
|
&gfs2_attr_fsname.attr,
|
2009-02-10 20:48:30 +07:00
|
|
|
&gfs2_attr_uuid.attr,
|
2006-01-16 23:50:04 +07:00
|
|
|
&gfs2_attr_freeze.attr,
|
|
|
|
&gfs2_attr_withdraw.attr,
|
|
|
|
&gfs2_attr_statfs_sync.attr,
|
|
|
|
&gfs2_attr_quota_sync.attr,
|
|
|
|
&gfs2_attr_quota_refresh_user.attr,
|
|
|
|
&gfs2_attr_quota_refresh_group.attr,
|
GFS2: Add a "demote a glock" interface to sysfs
This adds a sysfs file called demote_rq to GFS2's
per filesystem directory. Its possible to use this
file to demote arbitrary glocks in exactly the same
way as if a request had come in from a remote node.
This is intended for testing issues relating to caching
of data under glocks. Despite that, the interface is
generic enough to send requests to any type of glock,
but be careful as its not always safe to send an
arbitrary message to an arbitrary glock. For that reason
and to prevent DoS, this interface is restricted to root
only.
The messages look like this:
<type>:<glocknumber> <mode>
Example:
echo -n "2:13324 EX" >/sys/fs/gfs2/unity:myfs/demote_rq
Which means "please demote inode glock (type 2) number 13324 so that
I can get an EX (exclusive) lock". The lock modes are those which
would normally be sent by a remote node in its callback so if you
want to unlock a glock, you use EX, to demote to shared, use SH or PR
(depending on whether you like GFS2 or DLM lock modes better!).
If the glock doesn't exist, you'll get -ENOENT returned. If the
arguments don't make sense, you'll get -EINVAL returned.
The plan is that this interface will be used in combination with
the blktrace patch which I recently posted for comments although
it is, of course, still useful in its own right.
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
2009-02-12 20:31:58 +07:00
|
|
|
&gfs2_attr_demote_rq.attr,
|
2006-01-16 23:50:04 +07:00
|
|
|
NULL,
|
|
|
|
};
|
2019-06-08 01:23:00 +07:00
|
|
|
ATTRIBUTE_GROUPS(gfs2);
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2012-06-13 21:27:41 +07:00
|
|
|
static void gfs2_sbd_release(struct kobject *kobj)
|
|
|
|
{
|
|
|
|
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
|
|
|
|
|
2020-10-12 20:13:09 +07:00
|
|
|
complete(&sdp->sd_kobj_unregister);
|
2012-06-13 21:27:41 +07:00
|
|
|
}
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
static struct kobj_type gfs2_ktype = {
|
2012-06-13 21:27:41 +07:00
|
|
|
.release = gfs2_sbd_release,
|
2019-06-08 01:23:00 +07:00
|
|
|
.default_groups = gfs2_groups,
|
2006-01-16 23:50:04 +07:00
|
|
|
.sysfs_ops = &gfs2_attr_ops,
|
|
|
|
};
|
|
|
|
|
2009-01-12 17:43:39 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* lock_module. Originally from lock_dlm
|
|
|
|
*/
|
|
|
|
|
|
|
|
static ssize_t proto_name_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
|
|
|
const struct lm_lockops *ops = sdp->sd_lockstruct.ls_ops;
|
|
|
|
return sprintf(buf, "%s\n", ops->lm_proto_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t block_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
|
|
|
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
|
|
|
|
ssize_t ret;
|
|
|
|
int val = 0;
|
|
|
|
|
2012-01-10 05:18:05 +07:00
|
|
|
if (test_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags))
|
2009-01-12 17:43:39 +07:00
|
|
|
val = 1;
|
|
|
|
ret = sprintf(buf, "%d\n", val);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
|
2015-05-06 01:23:22 +07:00
|
|
|
int ret, val;
|
2009-01-12 17:43:39 +07:00
|
|
|
|
2015-05-06 01:23:22 +07:00
|
|
|
ret = kstrtoint(buf, 0, &val);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2009-01-12 17:43:39 +07:00
|
|
|
|
|
|
|
if (val == 1)
|
2012-01-10 05:18:05 +07:00
|
|
|
set_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags);
|
2009-01-12 17:43:39 +07:00
|
|
|
else if (val == 0) {
|
2012-01-10 05:18:05 +07:00
|
|
|
clear_bit(DFL_BLOCK_LOCKS, &ls->ls_recover_flags);
|
2014-03-18 00:06:10 +07:00
|
|
|
smp_mb__after_atomic();
|
2009-01-12 17:43:39 +07:00
|
|
|
gfs2_glock_thaw(sdp);
|
|
|
|
} else {
|
2015-05-06 01:23:22 +07:00
|
|
|
return -EINVAL;
|
2009-01-12 17:43:39 +07:00
|
|
|
}
|
2015-05-06 01:23:22 +07:00
|
|
|
return len;
|
2009-01-12 17:43:39 +07:00
|
|
|
}
|
|
|
|
|
2013-02-13 19:21:40 +07:00
|
|
|
static ssize_t wdack_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
|
|
|
int val = completion_done(&sdp->sd_wdack) ? 1 : 0;
|
|
|
|
|
|
|
|
return sprintf(buf, "%d\n", val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t wdack_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|
|
|
{
|
2015-05-06 01:23:22 +07:00
|
|
|
int ret, val;
|
2013-02-13 19:21:40 +07:00
|
|
|
|
2015-05-06 01:23:22 +07:00
|
|
|
ret = kstrtoint(buf, 0, &val);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2013-02-13 19:21:40 +07:00
|
|
|
|
|
|
|
if ((val == 1) &&
|
|
|
|
!strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm"))
|
|
|
|
complete(&sdp->sd_wdack);
|
|
|
|
else
|
2015-05-06 01:23:22 +07:00
|
|
|
return -EINVAL;
|
|
|
|
return len;
|
2013-02-13 19:21:40 +07:00
|
|
|
}
|
|
|
|
|
2009-01-12 17:43:39 +07:00
|
|
|
static ssize_t lkfirst_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
|
|
|
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
|
|
|
|
return sprintf(buf, "%d\n", ls->ls_first);
|
|
|
|
}
|
|
|
|
|
2010-06-14 16:01:30 +07:00
|
|
|
static ssize_t lkfirst_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
unsigned first;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
rv = sscanf(buf, "%u", &first);
|
|
|
|
if (rv != 1 || first > 1)
|
|
|
|
return -EINVAL;
|
2011-07-11 14:53:30 +07:00
|
|
|
rv = wait_for_completion_killable(&sdp->sd_locking_init);
|
|
|
|
if (rv)
|
|
|
|
return rv;
|
2010-06-14 16:01:30 +07:00
|
|
|
spin_lock(&sdp->sd_jindex_spin);
|
|
|
|
rv = -EBUSY;
|
|
|
|
if (test_bit(SDF_NOJOURNALID, &sdp->sd_flags) == 0)
|
|
|
|
goto out;
|
|
|
|
rv = -EINVAL;
|
|
|
|
if (sdp->sd_args.ar_spectator)
|
|
|
|
goto out;
|
|
|
|
if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL)
|
|
|
|
goto out;
|
2012-01-10 05:18:05 +07:00
|
|
|
sdp->sd_lockstruct.ls_first = first;
|
|
|
|
rv = 0;
|
2010-06-14 16:01:30 +07:00
|
|
|
out:
|
|
|
|
spin_unlock(&sdp->sd_jindex_spin);
|
|
|
|
return rv ? rv : len;
|
|
|
|
}
|
|
|
|
|
2009-01-12 17:43:39 +07:00
|
|
|
static ssize_t first_done_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
|
|
|
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
|
2012-01-10 05:18:05 +07:00
|
|
|
return sprintf(buf, "%d\n", !!test_bit(DFL_FIRST_MOUNT_DONE, &ls->ls_recover_flags));
|
2009-01-12 17:43:39 +07:00
|
|
|
}
|
|
|
|
|
2012-01-10 05:18:05 +07:00
|
|
|
int gfs2_recover_set(struct gfs2_sbd *sdp, unsigned jid)
|
2009-01-12 17:43:39 +07:00
|
|
|
{
|
|
|
|
struct gfs2_jdesc *jd;
|
2009-05-19 16:01:18 +07:00
|
|
|
int rv;
|
|
|
|
|
2014-06-02 20:40:25 +07:00
|
|
|
/* Wait for our primary journal to be initialized */
|
|
|
|
wait_for_completion(&sdp->sd_journal_ready);
|
|
|
|
|
2009-01-12 17:43:39 +07:00
|
|
|
spin_lock(&sdp->sd_jindex_spin);
|
2009-05-19 16:01:18 +07:00
|
|
|
rv = -EBUSY;
|
GFS2: Fix recovery issues for spectators
This patch fixes a couple problems dealing with spectators who
remain with gfs2 mounts after the last non-spectator node fails.
Before this patch, spectator mounts would try to acquire the dlm's
mounted lock EX as part of its normal recovery sequence.
The mounted lock is only used to determine whether the node is
the first mounter, the first node to mount the file system, for
the purposes of file system recovery and journal replay.
It's not necessary for spectators: they should never do journal
recovery. If they acquire the lock it will prevent another "real"
first-mounter from acquiring the lock in EX mode, which means it
also cannot do journal recovery because it doesn't think it's the
first node to mount the file system.
This patch checks if the mounter is a spectator, and if so, avoids
grabbing the mounted lock. This allows a secondary mounter who is
really the first non-spectator mounter, to do journal recovery:
since the spectator doesn't acquire the lock, it can grab it in
EX mode, and therefore consider itself to be the first mounter
both as a "real" first mount, and as a first-real-after-spectator.
Note that the control lock still needs to be taken in PR mode
in order to fetch the lvb value so it has the current status of
all journal's recovery. This is used as it is today by a first
mounter to replay the journals. For spectators, it's merely
used to fetch the status bits. All recovery is bypassed and the
node waits until recovery is completed by a non-spectator node.
I also improved the cryptic message given by control_mount when
a spectator is waiting for a non-spectator to perform recovery.
It also fixes a problem in gfs2_recover_set whereby spectators
were never queueing recovery work for their own journal.
They cannot do recovery themselves, but they still need to queue
the work so they can check the recovery bits and clear the
DFL_BLOCK_LOCKS bit once the recovery happens on another node.
When the work queue runs on a spectator, it bypasses most of the
work so it won't print a bunch of annoying messages. All it will
print is a bunch of messages that look like this until recovery
completes on the non-spectator node:
GFS2: fsid=mycluster:scratch.s: recover generation 3 jid 0
GFS2: fsid=mycluster:scratch.s: recover jid 0 result busy
These continue every 1.5 seconds until the recovery is done by
the non-spectator, at which time it says:
GFS2: fsid=mycluster:scratch.s: recover generation 4 done
Then it proceeds with its mount.
If the file system is mounted in spectator node and the last
remaining non-spectator is fenced, any IO to the file system is
blocked by dlm and the spectator waits until recovery is
performed by a non-spectator.
If a spectator tries to mount the file system before any
non-spectators, it blocks and repeatedly gives this kernel
message:
GFS2: fsid=mycluster:scratch: Recovery is required. Waiting for a non-spectator to mount.
GFS2: fsid=mycluster:scratch: Recovery is required. Waiting for a non-spectator to mount.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
2018-07-06 02:40:46 +07:00
|
|
|
/**
|
|
|
|
* If we're a spectator, we use journal0, but it's not really ours.
|
|
|
|
* So we need to wait for its recovery too. If we skip it we'd never
|
|
|
|
* queue work to the recovery workqueue, and so its completion would
|
|
|
|
* never clear the DFL_BLOCK_LOCKS flag, so all our locks would
|
|
|
|
* permanently stop working.
|
|
|
|
*/
|
gfs2: Force withdraw to replay journals and wait for it to finish
When a node withdraws from a file system, it often leaves its journal
in an incomplete state. This is especially true when the withdraw is
caused by io errors writing to the journal. Before this patch, a
withdraw would try to write a "shutdown" record to the journal, tell
dlm it's done with the file system, and none of the other nodes
know about the problem. Later, when the problem is fixed and the
withdrawn node is rebooted, it would then discover that its own
journal was incomplete, and replay it. However, replaying it at this
point is almost guaranteed to introduce corruption because the other
nodes are likely to have used affected resource groups that appeared
in the journal since the time of the withdraw. Replaying the journal
later will overwrite any changes made, and not through any fault of
dlm, which was instructed during the withdraw to release those
resources.
This patch makes file system withdraws seen by the entire cluster.
Withdrawing nodes dequeue their journal glock to allow recovery.
The remaining nodes check all the journals to see if they are
clean or in need of replay. They try to replay dirty journals, but
only the journals of withdrawn nodes will be "not busy" and
therefore available for replay.
Until the journal replay is complete, no i/o related glocks may be
given out, to ensure that the replay does not cause the
aforementioned corruption: We cannot allow any journal replay to
overwrite blocks associated with a glock once it is held.
The "live" glock which is now used to signal when a withdraw
occurs. When a withdraw occurs, the node signals its withdraw by
dequeueing the "live" glock and trying to enqueue it in EX mode,
thus forcing the other nodes to all see a demote request, by way
of a "1CB" (one callback) try lock. The "live" glock is not
granted in EX; the callback is only just used to indicate a
withdraw has occurred.
Note that all nodes in the cluster must wait for the recovering
node to finish replaying the withdrawing node's journal before
continuing. To this end, it checks that the journals are clean
multiple times in a retry loop.
Also note that the withdraw function may be called from a wide
variety of situations, and therefore, we need to take extra
precautions to make sure pointers are valid before using them in
many circumstances.
We also need to take care when glocks decide to withdraw, since
the withdraw code now uses glocks.
Also, before this patch, if a process encountered an error and
decided to withdraw, if another process was already withdrawing,
the second withdraw would be silently ignored, which set it free
to unlock its glocks. That's correct behavior if the original
withdrawer encounters further errors down the road. But if
secondary waiters don't wait for the journal replay, unlocking
glocks will allow other nodes to use them, despite the fact that
the journal containing those blocks is being replayed. The
replay needs to finish before our glocks are released to other
nodes. IOW, secondary withdraws need to wait for the first
withdraw to finish.
For example, if an rgrp glock is unlocked by a process that didn't
wait for the first withdraw, a journal replay could introduce file
system corruption by replaying a rgrp block that has already been
granted to a different cluster node.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
2020-01-29 02:23:45 +07:00
|
|
|
if (!sdp->sd_jdesc)
|
|
|
|
goto out;
|
GFS2: Fix recovery issues for spectators
This patch fixes a couple problems dealing with spectators who
remain with gfs2 mounts after the last non-spectator node fails.
Before this patch, spectator mounts would try to acquire the dlm's
mounted lock EX as part of its normal recovery sequence.
The mounted lock is only used to determine whether the node is
the first mounter, the first node to mount the file system, for
the purposes of file system recovery and journal replay.
It's not necessary for spectators: they should never do journal
recovery. If they acquire the lock it will prevent another "real"
first-mounter from acquiring the lock in EX mode, which means it
also cannot do journal recovery because it doesn't think it's the
first node to mount the file system.
This patch checks if the mounter is a spectator, and if so, avoids
grabbing the mounted lock. This allows a secondary mounter who is
really the first non-spectator mounter, to do journal recovery:
since the spectator doesn't acquire the lock, it can grab it in
EX mode, and therefore consider itself to be the first mounter
both as a "real" first mount, and as a first-real-after-spectator.
Note that the control lock still needs to be taken in PR mode
in order to fetch the lvb value so it has the current status of
all journal's recovery. This is used as it is today by a first
mounter to replay the journals. For spectators, it's merely
used to fetch the status bits. All recovery is bypassed and the
node waits until recovery is completed by a non-spectator node.
I also improved the cryptic message given by control_mount when
a spectator is waiting for a non-spectator to perform recovery.
It also fixes a problem in gfs2_recover_set whereby spectators
were never queueing recovery work for their own journal.
They cannot do recovery themselves, but they still need to queue
the work so they can check the recovery bits and clear the
DFL_BLOCK_LOCKS bit once the recovery happens on another node.
When the work queue runs on a spectator, it bypasses most of the
work so it won't print a bunch of annoying messages. All it will
print is a bunch of messages that look like this until recovery
completes on the non-spectator node:
GFS2: fsid=mycluster:scratch.s: recover generation 3 jid 0
GFS2: fsid=mycluster:scratch.s: recover jid 0 result busy
These continue every 1.5 seconds until the recovery is done by
the non-spectator, at which time it says:
GFS2: fsid=mycluster:scratch.s: recover generation 4 done
Then it proceeds with its mount.
If the file system is mounted in spectator node and the last
remaining non-spectator is fenced, any IO to the file system is
blocked by dlm and the spectator waits until recovery is
performed by a non-spectator.
If a spectator tries to mount the file system before any
non-spectators, it blocks and repeatedly gives this kernel
message:
GFS2: fsid=mycluster:scratch: Recovery is required. Waiting for a non-spectator to mount.
GFS2: fsid=mycluster:scratch: Recovery is required. Waiting for a non-spectator to mount.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
2018-07-06 02:40:46 +07:00
|
|
|
if (sdp->sd_jdesc->jd_jid == jid && !sdp->sd_args.ar_spectator)
|
2009-05-19 16:01:18 +07:00
|
|
|
goto out;
|
|
|
|
rv = -ENOENT;
|
2009-01-12 17:43:39 +07:00
|
|
|
list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
|
GFS2: Fix recovery issues for spectators
This patch fixes a couple problems dealing with spectators who
remain with gfs2 mounts after the last non-spectator node fails.
Before this patch, spectator mounts would try to acquire the dlm's
mounted lock EX as part of its normal recovery sequence.
The mounted lock is only used to determine whether the node is
the first mounter, the first node to mount the file system, for
the purposes of file system recovery and journal replay.
It's not necessary for spectators: they should never do journal
recovery. If they acquire the lock it will prevent another "real"
first-mounter from acquiring the lock in EX mode, which means it
also cannot do journal recovery because it doesn't think it's the
first node to mount the file system.
This patch checks if the mounter is a spectator, and if so, avoids
grabbing the mounted lock. This allows a secondary mounter who is
really the first non-spectator mounter, to do journal recovery:
since the spectator doesn't acquire the lock, it can grab it in
EX mode, and therefore consider itself to be the first mounter
both as a "real" first mount, and as a first-real-after-spectator.
Note that the control lock still needs to be taken in PR mode
in order to fetch the lvb value so it has the current status of
all journal's recovery. This is used as it is today by a first
mounter to replay the journals. For spectators, it's merely
used to fetch the status bits. All recovery is bypassed and the
node waits until recovery is completed by a non-spectator node.
I also improved the cryptic message given by control_mount when
a spectator is waiting for a non-spectator to perform recovery.
It also fixes a problem in gfs2_recover_set whereby spectators
were never queueing recovery work for their own journal.
They cannot do recovery themselves, but they still need to queue
the work so they can check the recovery bits and clear the
DFL_BLOCK_LOCKS bit once the recovery happens on another node.
When the work queue runs on a spectator, it bypasses most of the
work so it won't print a bunch of annoying messages. All it will
print is a bunch of messages that look like this until recovery
completes on the non-spectator node:
GFS2: fsid=mycluster:scratch.s: recover generation 3 jid 0
GFS2: fsid=mycluster:scratch.s: recover jid 0 result busy
These continue every 1.5 seconds until the recovery is done by
the non-spectator, at which time it says:
GFS2: fsid=mycluster:scratch.s: recover generation 4 done
Then it proceeds with its mount.
If the file system is mounted in spectator node and the last
remaining non-spectator is fenced, any IO to the file system is
blocked by dlm and the spectator waits until recovery is
performed by a non-spectator.
If a spectator tries to mount the file system before any
non-spectators, it blocks and repeatedly gives this kernel
message:
GFS2: fsid=mycluster:scratch: Recovery is required. Waiting for a non-spectator to mount.
GFS2: fsid=mycluster:scratch: Recovery is required. Waiting for a non-spectator to mount.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
2018-07-06 02:40:46 +07:00
|
|
|
if (jd->jd_jid != jid && !sdp->sd_args.ar_spectator)
|
2009-01-12 17:43:39 +07:00
|
|
|
continue;
|
2010-07-21 03:09:02 +07:00
|
|
|
rv = gfs2_recover_journal(jd, false);
|
2009-01-12 17:43:39 +07:00
|
|
|
break;
|
|
|
|
}
|
2009-05-19 16:01:18 +07:00
|
|
|
out:
|
2009-01-12 17:43:39 +07:00
|
|
|
spin_unlock(&sdp->sd_jindex_spin);
|
2012-01-10 05:18:05 +07:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t recover_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
unsigned jid;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
rv = sscanf(buf, "%u", &jid);
|
|
|
|
if (rv != 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2012-05-02 03:50:48 +07:00
|
|
|
if (test_bit(SDF_NORECOVERY, &sdp->sd_flags)) {
|
|
|
|
rv = -ESHUTDOWN;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-01-10 05:18:05 +07:00
|
|
|
|
2012-05-02 03:50:48 +07:00
|
|
|
rv = gfs2_recover_set(sdp, jid);
|
|
|
|
out:
|
2009-05-19 16:01:18 +07:00
|
|
|
return rv ? rv : len;
|
2009-01-12 17:43:39 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t recover_done_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
|
|
|
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
|
|
|
|
return sprintf(buf, "%d\n", ls->ls_recover_jid_done);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t recover_status_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
|
|
|
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
|
|
|
|
return sprintf(buf, "%d\n", ls->ls_recover_jid_status);
|
|
|
|
}
|
|
|
|
|
2009-05-26 21:41:27 +07:00
|
|
|
static ssize_t jid_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
2010-09-29 21:04:18 +07:00
|
|
|
return sprintf(buf, "%d\n", sdp->sd_lockstruct.ls_jid);
|
2009-05-26 21:41:27 +07:00
|
|
|
}
|
|
|
|
|
2010-06-14 16:01:30 +07:00
|
|
|
static ssize_t jid_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
|
|
|
|
{
|
2010-09-29 21:04:18 +07:00
|
|
|
int jid;
|
2010-06-14 16:01:30 +07:00
|
|
|
int rv;
|
|
|
|
|
2010-09-29 21:04:18 +07:00
|
|
|
rv = sscanf(buf, "%d", &jid);
|
2010-06-14 16:01:30 +07:00
|
|
|
if (rv != 1)
|
|
|
|
return -EINVAL;
|
2011-07-11 14:53:30 +07:00
|
|
|
rv = wait_for_completion_killable(&sdp->sd_locking_init);
|
|
|
|
if (rv)
|
|
|
|
return rv;
|
2010-06-14 16:01:30 +07:00
|
|
|
spin_lock(&sdp->sd_jindex_spin);
|
|
|
|
rv = -EINVAL;
|
|
|
|
if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL)
|
|
|
|
goto out;
|
|
|
|
rv = -EBUSY;
|
2010-09-29 21:04:18 +07:00
|
|
|
if (test_bit(SDF_NOJOURNALID, &sdp->sd_flags) == 0)
|
2010-06-14 16:01:30 +07:00
|
|
|
goto out;
|
2010-09-29 21:04:18 +07:00
|
|
|
rv = 0;
|
|
|
|
if (sdp->sd_args.ar_spectator && jid > 0)
|
|
|
|
rv = jid = -EINVAL;
|
2010-06-14 16:01:30 +07:00
|
|
|
sdp->sd_lockstruct.ls_jid = jid;
|
2010-09-29 21:04:18 +07:00
|
|
|
clear_bit(SDF_NOJOURNALID, &sdp->sd_flags);
|
2014-03-18 00:06:10 +07:00
|
|
|
smp_mb__after_atomic();
|
2010-06-14 16:01:30 +07:00
|
|
|
wake_up_bit(&sdp->sd_flags, SDF_NOJOURNALID);
|
|
|
|
out:
|
|
|
|
spin_unlock(&sdp->sd_jindex_spin);
|
|
|
|
return rv ? rv : len;
|
|
|
|
}
|
|
|
|
|
2009-01-12 17:43:39 +07:00
|
|
|
#define GDLM_ATTR(_name,_mode,_show,_store) \
|
2009-05-13 20:49:48 +07:00
|
|
|
static struct gfs2_attr gdlm_attr_##_name = __ATTR(_name,_mode,_show,_store)
|
2009-01-12 17:43:39 +07:00
|
|
|
|
2009-08-11 17:20:11 +07:00
|
|
|
GDLM_ATTR(proto_name, 0444, proto_name_show, NULL);
|
|
|
|
GDLM_ATTR(block, 0644, block_show, block_store);
|
2013-02-13 19:21:40 +07:00
|
|
|
GDLM_ATTR(withdraw, 0644, wdack_show, wdack_store);
|
2010-06-14 16:01:30 +07:00
|
|
|
GDLM_ATTR(jid, 0644, jid_show, jid_store);
|
|
|
|
GDLM_ATTR(first, 0644, lkfirst_show, lkfirst_store);
|
2009-08-11 17:20:11 +07:00
|
|
|
GDLM_ATTR(first_done, 0444, first_done_show, NULL);
|
|
|
|
GDLM_ATTR(recover, 0600, NULL, recover_store);
|
|
|
|
GDLM_ATTR(recover_done, 0444, recover_done_show, NULL);
|
|
|
|
GDLM_ATTR(recover_status, 0444, recover_status_show, NULL);
|
2009-01-12 17:43:39 +07:00
|
|
|
|
|
|
|
static struct attribute *lock_module_attrs[] = {
|
|
|
|
&gdlm_attr_proto_name.attr,
|
|
|
|
&gdlm_attr_block.attr,
|
|
|
|
&gdlm_attr_withdraw.attr,
|
2009-05-26 21:41:27 +07:00
|
|
|
&gdlm_attr_jid.attr,
|
2009-01-12 17:43:39 +07:00
|
|
|
&gdlm_attr_first.attr,
|
|
|
|
&gdlm_attr_first_done.attr,
|
|
|
|
&gdlm_attr_recover.attr,
|
|
|
|
&gdlm_attr_recover_done.attr,
|
|
|
|
&gdlm_attr_recover_status.attr,
|
2006-09-05 21:53:09 +07:00
|
|
|
NULL,
|
2006-01-16 23:50:04 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* get and set struct gfs2_tune fields
|
|
|
|
*/
|
|
|
|
|
|
|
|
static ssize_t quota_scale_show(struct gfs2_sbd *sdp, char *buf)
|
|
|
|
{
|
2006-09-07 04:57:06 +07:00
|
|
|
return snprintf(buf, PAGE_SIZE, "%u %u\n",
|
|
|
|
sdp->sd_tune.gt_quota_scale_num,
|
|
|
|
sdp->sd_tune.gt_quota_scale_den);
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t quota_scale_store(struct gfs2_sbd *sdp, const char *buf,
|
|
|
|
size_t len)
|
|
|
|
{
|
|
|
|
struct gfs2_tune *gt = &sdp->sd_tune;
|
|
|
|
unsigned int x, y;
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
if (sscanf(buf, "%u %u", &x, &y) != 2 || !y)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
spin_lock(>->gt_spin);
|
|
|
|
gt->gt_quota_scale_num = x;
|
|
|
|
gt->gt_quota_scale_den = y;
|
|
|
|
spin_unlock(>->gt_spin);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t tune_set(struct gfs2_sbd *sdp, unsigned int *field,
|
|
|
|
int check_zero, const char *buf, size_t len)
|
|
|
|
{
|
|
|
|
struct gfs2_tune *gt = &sdp->sd_tune;
|
|
|
|
unsigned int x;
|
2015-05-06 01:23:22 +07:00
|
|
|
int error;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
if (!capable(CAP_SYS_ADMIN))
|
2013-02-20 09:13:55 +07:00
|
|
|
return -EPERM;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2015-05-06 01:23:22 +07:00
|
|
|
error = kstrtouint(buf, 0, &x);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
if (check_zero && !x)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
spin_lock(>->gt_spin);
|
|
|
|
*field = x;
|
|
|
|
spin_unlock(>->gt_spin);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define TUNE_ATTR_3(name, show, store) \
|
2009-05-13 20:49:48 +07:00
|
|
|
static struct gfs2_attr tune_attr_##name = __ATTR(name, 0644, show, store)
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
#define TUNE_ATTR_2(name, store) \
|
|
|
|
static ssize_t name##_show(struct gfs2_sbd *sdp, char *buf) \
|
|
|
|
{ \
|
2006-09-07 04:57:06 +07:00
|
|
|
return snprintf(buf, PAGE_SIZE, "%u\n", sdp->sd_tune.gt_##name); \
|
2006-01-16 23:50:04 +07:00
|
|
|
} \
|
|
|
|
TUNE_ATTR_3(name, name##_show, store)
|
|
|
|
|
|
|
|
#define TUNE_ATTR(name, check_zero) \
|
|
|
|
static ssize_t name##_store(struct gfs2_sbd *sdp, const char *buf, size_t len)\
|
|
|
|
{ \
|
|
|
|
return tune_set(sdp, &sdp->sd_tune.gt_##name, check_zero, buf, len); \
|
|
|
|
} \
|
|
|
|
TUNE_ATTR_2(name, name##_store)
|
|
|
|
|
|
|
|
TUNE_ATTR(quota_warn_period, 0);
|
|
|
|
TUNE_ATTR(quota_quantum, 0);
|
|
|
|
TUNE_ATTR(max_readahead, 0);
|
|
|
|
TUNE_ATTR(complain_secs, 0);
|
|
|
|
TUNE_ATTR(statfs_slow, 0);
|
|
|
|
TUNE_ATTR(new_files_jdata, 0);
|
|
|
|
TUNE_ATTR(statfs_quantum, 1);
|
|
|
|
TUNE_ATTR_3(quota_scale, quota_scale_show, quota_scale_store);
|
|
|
|
|
|
|
|
static struct attribute *tune_attrs[] = {
|
|
|
|
&tune_attr_quota_warn_period.attr,
|
|
|
|
&tune_attr_quota_quantum.attr,
|
|
|
|
&tune_attr_max_readahead.attr,
|
|
|
|
&tune_attr_complain_secs.attr,
|
|
|
|
&tune_attr_statfs_slow.attr,
|
|
|
|
&tune_attr_statfs_quantum.attr,
|
|
|
|
&tune_attr_quota_scale.attr,
|
|
|
|
&tune_attr_new_files_jdata.attr,
|
2006-09-05 21:53:09 +07:00
|
|
|
NULL,
|
2006-01-16 23:50:04 +07:00
|
|
|
};
|
|
|
|
|
2017-06-30 20:33:54 +07:00
|
|
|
static const struct attribute_group tune_group = {
|
2006-01-16 23:50:04 +07:00
|
|
|
.name = "tune",
|
2006-09-05 21:53:09 +07:00
|
|
|
.attrs = tune_attrs,
|
2006-01-16 23:50:04 +07:00
|
|
|
};
|
|
|
|
|
2017-06-30 20:33:54 +07:00
|
|
|
static const struct attribute_group lock_module_group = {
|
2009-01-12 17:43:39 +07:00
|
|
|
.name = "lock_module",
|
|
|
|
.attrs = lock_module_attrs,
|
|
|
|
};
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
|
|
|
|
{
|
2009-07-31 18:16:25 +07:00
|
|
|
struct super_block *sb = sdp->sd_vfs;
|
2006-01-16 23:50:04 +07:00
|
|
|
int error;
|
2009-07-31 18:16:25 +07:00
|
|
|
char ro[20];
|
|
|
|
char spectator[20];
|
|
|
|
char *envp[] = { ro, spectator, NULL };
|
|
|
|
|
2017-07-17 14:45:34 +07:00
|
|
|
sprintf(ro, "RDONLY=%d", sb_rdonly(sb));
|
2009-07-31 18:16:25 +07:00
|
|
|
sprintf(spectator, "SPECTATOR=%d", sdp->sd_args.ar_spectator ? 1 : 0);
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2020-10-12 20:13:09 +07:00
|
|
|
init_completion(&sdp->sd_kobj_unregister);
|
2007-10-30 02:13:17 +07:00
|
|
|
sdp->sd_kobj.kset = gfs2_kset;
|
2007-12-18 02:54:39 +07:00
|
|
|
error = kobject_init_and_add(&sdp->sd_kobj, &gfs2_ktype, NULL,
|
|
|
|
"%s", sdp->sd_table_name);
|
2006-01-16 23:50:04 +07:00
|
|
|
if (error)
|
2012-06-13 21:27:41 +07:00
|
|
|
goto fail_reg;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
|
|
|
error = sysfs_create_group(&sdp->sd_kobj, &tune_group);
|
|
|
|
if (error)
|
2009-05-26 21:50:25 +07:00
|
|
|
goto fail_reg;
|
2006-01-16 23:50:04 +07:00
|
|
|
|
2009-01-12 17:43:39 +07:00
|
|
|
error = sysfs_create_group(&sdp->sd_kobj, &lock_module_group);
|
|
|
|
if (error)
|
|
|
|
goto fail_tune;
|
|
|
|
|
2009-08-13 18:18:08 +07:00
|
|
|
error = sysfs_create_link(&sdp->sd_kobj,
|
|
|
|
&disk_to_dev(sb->s_bdev->bd_disk)->kobj,
|
|
|
|
"device");
|
|
|
|
if (error)
|
|
|
|
goto fail_lock_module;
|
|
|
|
|
2009-07-31 18:16:25 +07:00
|
|
|
kobject_uevent_env(&sdp->sd_kobj, KOBJ_ADD, envp);
|
2006-01-16 23:50:04 +07:00
|
|
|
return 0;
|
|
|
|
|
2009-08-13 18:18:08 +07:00
|
|
|
fail_lock_module:
|
|
|
|
sysfs_remove_group(&sdp->sd_kobj, &lock_module_group);
|
2009-01-12 17:43:39 +07:00
|
|
|
fail_tune:
|
|
|
|
sysfs_remove_group(&sdp->sd_kobj, &tune_group);
|
2006-09-04 23:04:26 +07:00
|
|
|
fail_reg:
|
2018-01-31 00:32:30 +07:00
|
|
|
fs_err(sdp, "error %d adding sysfs files\n", error);
|
2019-05-14 02:59:04 +07:00
|
|
|
kobject_put(&sdp->sd_kobj);
|
2020-10-12 20:13:09 +07:00
|
|
|
wait_for_completion(&sdp->sd_kobj_unregister);
|
2012-06-13 21:27:41 +07:00
|
|
|
sb->s_fs_info = NULL;
|
2006-01-16 23:50:04 +07:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
void gfs2_sys_fs_del(struct gfs2_sbd *sdp)
|
|
|
|
{
|
2009-08-13 18:18:08 +07:00
|
|
|
sysfs_remove_link(&sdp->sd_kobj, "device");
|
2006-01-16 23:50:04 +07:00
|
|
|
sysfs_remove_group(&sdp->sd_kobj, &tune_group);
|
2009-01-12 17:43:39 +07:00
|
|
|
sysfs_remove_group(&sdp->sd_kobj, &lock_module_group);
|
2007-12-20 23:13:05 +07:00
|
|
|
kobject_put(&sdp->sd_kobj);
|
2020-10-12 20:13:09 +07:00
|
|
|
wait_for_completion(&sdp->sd_kobj_unregister);
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
2008-11-26 17:26:38 +07:00
|
|
|
static int gfs2_uevent(struct kset *kset, struct kobject *kobj,
|
|
|
|
struct kobj_uevent_env *env)
|
|
|
|
{
|
|
|
|
struct gfs2_sbd *sdp = container_of(kobj, struct gfs2_sbd, sd_kobj);
|
2011-05-10 21:01:59 +07:00
|
|
|
struct super_block *s = sdp->sd_vfs;
|
2009-02-10 20:48:30 +07:00
|
|
|
|
2008-11-26 17:26:38 +07:00
|
|
|
add_uevent_var(env, "LOCKTABLE=%s", sdp->sd_table_name);
|
|
|
|
add_uevent_var(env, "LOCKPROTO=%s", sdp->sd_proto_name);
|
2010-06-14 16:01:30 +07:00
|
|
|
if (!test_bit(SDF_NOJOURNALID, &sdp->sd_flags))
|
2010-09-29 21:04:18 +07:00
|
|
|
add_uevent_var(env, "JOURNALID=%d", sdp->sd_lockstruct.ls_jid);
|
2017-05-10 20:06:33 +07:00
|
|
|
if (!uuid_is_null(&s->s_uuid))
|
|
|
|
add_uevent_var(env, "UUID=%pUB", &s->s_uuid);
|
2008-11-26 17:26:38 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-12-31 20:52:51 +07:00
|
|
|
static const struct kset_uevent_ops gfs2_uevent_ops = {
|
2008-11-26 17:26:38 +07:00
|
|
|
.uevent = gfs2_uevent,
|
|
|
|
};
|
|
|
|
|
2006-01-16 23:50:04 +07:00
|
|
|
int gfs2_sys_init(void)
|
|
|
|
{
|
2008-11-26 17:26:38 +07:00
|
|
|
gfs2_kset = kset_create_and_add("gfs2", &gfs2_uevent_ops, fs_kobj);
|
2007-10-30 02:13:17 +07:00
|
|
|
if (!gfs2_kset)
|
|
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
void gfs2_sys_uninit(void)
|
|
|
|
{
|
2007-10-30 02:13:17 +07:00
|
|
|
kset_unregister(gfs2_kset);
|
2006-01-16 23:50:04 +07:00
|
|
|
}
|
|
|
|
|