Skip to content

Commit

Permalink
GFS2: Umount recovery race fix
Browse files Browse the repository at this point in the history
This patch fixes a race condition where we can receive recovery
requests part way through processing a umount. This was causing
problems since the recovery thread had already gone away.

Looking in more detail at the recovery code, it was really trying
to implement a slight variation on a work queue, and that happens to
align nicely with the recently introduced slow-work subsystem. As a
result I've updated the code to use slow-work, rather than its own home
grown variety of work queue.

When using the wait_on_bit() function, I noticed that the wait function
that was supplied as an argument was appearing in the WCHAN field, so
I've updated the function names in order to produce more meaningful
output.

Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
  • Loading branch information
Steven Whitehouse committed May 19, 2009
1 parent 9582d41 commit fe64d51
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 124 deletions.
1 change: 1 addition & 0 deletions fs/gfs2/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ config GFS2_FS
select IP_SCTP if DLM_SCTP
select FS_POSIX_ACL
select CRC32
select SLOW_WORK
help
A cluster filesystem.

Expand Down
21 changes: 18 additions & 3 deletions fs/gfs2/glock.c
Original file line number Diff line number Diff line change
Expand Up @@ -796,22 +796,37 @@ void gfs2_holder_uninit(struct gfs2_holder *gh)
gh->gh_ip = 0;
}

static int just_schedule(void *word)
/**
* gfs2_glock_holder_wait
* @word: unused
*
* This function and gfs2_glock_demote_wait both show up in the WCHAN
* field. Thus I've separated these otherwise identical functions in
* order to be more informative to the user.
*/

static int gfs2_glock_holder_wait(void *word)
{
schedule();
return 0;
}

static int gfs2_glock_demote_wait(void *word)
{
schedule();
return 0;
}

static void wait_on_holder(struct gfs2_holder *gh)
{
might_sleep();
wait_on_bit(&gh->gh_iflags, HIF_WAIT, just_schedule, TASK_UNINTERRUPTIBLE);
wait_on_bit(&gh->gh_iflags, HIF_WAIT, gfs2_glock_holder_wait, TASK_UNINTERRUPTIBLE);
}

static void wait_on_demote(struct gfs2_glock *gl)
{
might_sleep();
wait_on_bit(&gl->gl_flags, GLF_DEMOTE, just_schedule, TASK_UNINTERRUPTIBLE);
wait_on_bit(&gl->gl_flags, GLF_DEMOTE, gfs2_glock_demote_wait, TASK_UNINTERRUPTIBLE);
}

/**
Expand Down
14 changes: 5 additions & 9 deletions fs/gfs2/incore.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <linux/fs.h>
#include <linux/workqueue.h>
#include <linux/slow-work.h>
#include <linux/dlm.h>
#include <linux/buffer_head.h>

Expand Down Expand Up @@ -376,11 +377,11 @@ struct gfs2_journal_extent {
struct gfs2_jdesc {
struct list_head jd_list;
struct list_head extent_list;

struct slow_work jd_work;
struct inode *jd_inode;
unsigned long jd_flags;
#define JDF_RECOVERY 1
unsigned int jd_jid;
int jd_dirty;

unsigned int jd_blocks;
};

Expand All @@ -390,9 +391,6 @@ struct gfs2_statfs_change_host {
s64 sc_dinodes;
};

#define GFS2_GLOCKD_DEFAULT 1
#define GFS2_GLOCKD_MAX 16

#define GFS2_QUOTA_DEFAULT GFS2_QUOTA_OFF
#define GFS2_QUOTA_OFF 0
#define GFS2_QUOTA_ACCOUNT 1
Expand Down Expand Up @@ -427,7 +425,6 @@ struct gfs2_tune {
unsigned int gt_incore_log_blocks;
unsigned int gt_log_flush_secs;

unsigned int gt_recoverd_secs;
unsigned int gt_logd_secs;

unsigned int gt_quota_simul_sync; /* Max quotavals to sync at once */
Expand All @@ -448,6 +445,7 @@ enum {
SDF_JOURNAL_LIVE = 1,
SDF_SHUTDOWN = 2,
SDF_NOBARRIERS = 3,
SDF_NORECOVERY = 4,
};

#define GFS2_FSNAME_LEN 256
Expand Down Expand Up @@ -494,7 +492,6 @@ struct lm_lockstruct {
unsigned long ls_flags;
dlm_lockspace_t *ls_dlm;

int ls_recover_jid;
int ls_recover_jid_done;
int ls_recover_jid_status;
};
Expand Down Expand Up @@ -583,7 +580,6 @@ struct gfs2_sbd {

/* Daemon stuff */

struct task_struct *sd_recoverd_process;
struct task_struct *sd_logd_process;
struct task_struct *sd_quotad_process;

Expand Down
8 changes: 8 additions & 0 deletions fs/gfs2/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/gfs2_ondisk.h>
#include <asm/atomic.h>
#include <linux/slow-work.h>

#include "gfs2.h"
#include "incore.h"
Expand Down Expand Up @@ -113,12 +114,18 @@ static int __init init_gfs2_fs(void)
if (error)
goto fail_unregister;

error = slow_work_register_user();
if (error)
goto fail_slow;

gfs2_register_debugfs();

printk("GFS2 (built %s %s) installed\n", __DATE__, __TIME__);

return 0;

fail_slow:
unregister_filesystem(&gfs2meta_fs_type);
fail_unregister:
unregister_filesystem(&gfs2_fs_type);
fail:
Expand Down Expand Up @@ -156,6 +163,7 @@ static void __exit exit_gfs2_fs(void)
gfs2_unregister_debugfs();
unregister_filesystem(&gfs2_fs_type);
unregister_filesystem(&gfs2meta_fs_type);
slow_work_unregister_user();

kmem_cache_destroy(gfs2_quotad_cachep);
kmem_cache_destroy(gfs2_rgrpd_cachep);
Expand Down
20 changes: 6 additions & 14 deletions fs/gfs2/ops_fstype.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/gfs2_ondisk.h>
#include <linux/slow-work.h>

#include "gfs2.h"
#include "incore.h"
Expand Down Expand Up @@ -55,7 +56,6 @@ static void gfs2_tune_init(struct gfs2_tune *gt)
spin_lock_init(&gt->gt_spin);

gt->gt_incore_log_blocks = 1024;
gt->gt_recoverd_secs = 60;
gt->gt_logd_secs = 1;
gt->gt_quota_simul_sync = 64;
gt->gt_quota_warn_period = 10;
Expand Down Expand Up @@ -675,6 +675,7 @@ static int gfs2_jindex_hold(struct gfs2_sbd *sdp, struct gfs2_holder *ji_gh)
break;

INIT_LIST_HEAD(&jd->extent_list);
slow_work_init(&jd->jd_work, &gfs2_recover_ops);
jd->jd_inode = gfs2_lookupi(sdp->sd_jindex, &name, 1);
if (!jd->jd_inode || IS_ERR(jd->jd_inode)) {
if (!jd->jd_inode)
Expand All @@ -700,14 +701,13 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
{
struct inode *master = sdp->sd_master_dir->d_inode;
struct gfs2_holder ji_gh;
struct task_struct *p;
struct gfs2_inode *ip;
int jindex = 1;
int error = 0;

if (undo) {
jindex = 0;
goto fail_recoverd;
goto fail_jinode_gh;
}

sdp->sd_jindex = gfs2_lookup_simple(master, "jindex");
Expand Down Expand Up @@ -800,18 +800,8 @@ static int init_journal(struct gfs2_sbd *sdp, int undo)
gfs2_glock_dq_uninit(&ji_gh);
jindex = 0;

p = kthread_run(gfs2_recoverd, sdp, "gfs2_recoverd");
error = IS_ERR(p);
if (error) {
fs_err(sdp, "can't start recoverd thread: %d\n", error);
goto fail_jinode_gh;
}
sdp->sd_recoverd_process = p;

return 0;

fail_recoverd:
kthread_stop(sdp->sd_recoverd_process);
fail_jinode_gh:
if (!sdp->sd_args.ar_spectator)
gfs2_glock_dq_uninit(&sdp->sd_jinode_gh);
Expand Down Expand Up @@ -1172,8 +1162,10 @@ static int fill_super(struct super_block *sb, void *data, int silent)
goto fail;
}

if (sdp->sd_args.ar_spectator)
if (sdp->sd_args.ar_spectator) {
sb->s_flags |= MS_RDONLY;
set_bit(SDF_NORECOVERY, &sdp->sd_flags);
}
if (sdp->sd_args.ar_posix_acl)
sb->s_flags |= MS_POSIXACL;

Expand Down
25 changes: 24 additions & 1 deletion fs/gfs2/ops_super.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ static int gfs2_make_fs_ro(struct gfs2_sbd *sdp)
return error;
}

static int gfs2_umount_recovery_wait(void *word)
{
schedule();
return 0;
}

/**
* gfs2_put_super - Unmount the filesystem
* @sb: The VFS superblock
Expand All @@ -131,6 +137,7 @@ static void gfs2_put_super(struct super_block *sb)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
int error;
struct gfs2_jdesc *jd;

/* Unfreeze the filesystem, if we need to */

Expand All @@ -139,9 +146,25 @@ static void gfs2_put_super(struct super_block *sb)
gfs2_glock_dq_uninit(&sdp->sd_freeze_gh);
mutex_unlock(&sdp->sd_freeze_lock);

/* No more recovery requests */
set_bit(SDF_NORECOVERY, &sdp->sd_flags);
smp_mb();

/* Wait on outstanding recovery */
restart:
spin_lock(&sdp->sd_jindex_spin);
list_for_each_entry(jd, &sdp->sd_jindex_list, jd_list) {
if (!test_bit(JDF_RECOVERY, &jd->jd_flags))
continue;
spin_unlock(&sdp->sd_jindex_spin);
wait_on_bit(&jd->jd_flags, JDF_RECOVERY,
gfs2_umount_recovery_wait, TASK_UNINTERRUPTIBLE);
goto restart;
}
spin_unlock(&sdp->sd_jindex_spin);

kthread_stop(sdp->sd_quotad_process);
kthread_stop(sdp->sd_logd_process);
kthread_stop(sdp->sd_recoverd_process);

if (!(sb->s_flags & MS_RDONLY)) {
error = gfs2_make_fs_ro(sdp);
Expand Down
Loading

0 comments on commit fe64d51

Please sign in to comment.