Skip to content

Commit

Permalink
Merge tag 'gfs2-v6.0-rc2-fixes' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/kernel/git/gfs2/linux-gfs2

Pull gfs2 updates from Andreas Gruenbacher:

 - Make sure to initialize the filesystem work queues before registering
   the filesystem; this prevents them from being used uninitialized.

 - On filesystem withdraw: prevent a a double iput() and immediately
   reject pending locking requests that can no longer succeed.

 - Use TRY lock in gfs2_inode_lookup() to prevent a rare glock hang
   during evict.

 - During filesystem mount, explicitly make sure that the sb_bsize and
   sb_bsize_shift super block fields are consistent with each other.
   This prevents messy error messages during fuzz testing.

 - Switch from strlcpy to strscpy.

* tag 'gfs2-v6.0-rc2-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: Register fs after creating workqueues
  gfs2: Check sb_bsize_shift after reading superblock
  gfs2: Switch from strlcpy to strscpy
  gfs2: Clear flags when withdraw prevents xmote
  gfs2: Dequeue waiters when withdrawn
  gfs2: Prevent double iput for journal on error
  gfs2: Use TRY lock in gfs2_inode_lookup for UNLINKED inodes
  • Loading branch information
torvalds committed Oct 11, 2022
2 parents ac1e8c6 + 74b1b10 commit 4875d2f
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 25 deletions.
44 changes: 39 additions & 5 deletions fs/gfs2/glock.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ typedef void (*glock_examiner) (struct gfs2_glock * gl);

static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh, unsigned int target);
static void __gfs2_glock_dq(struct gfs2_holder *gh);
static void handle_callback(struct gfs2_glock *gl, unsigned int state,
unsigned long delay, bool remote);

static struct dentry *gfs2_root;
static struct workqueue_struct *glock_workqueue;
Expand Down Expand Up @@ -730,7 +732,8 @@ static bool is_system_glock(struct gfs2_glock *gl)
*
*/

static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh, unsigned int target)
static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh,
unsigned int target)
__releases(&gl->gl_lockref.lock)
__acquires(&gl->gl_lockref.lock)
{
Expand All @@ -741,7 +744,8 @@ __acquires(&gl->gl_lockref.lock)

if (target != LM_ST_UNLOCKED && glock_blocked_by_withdraw(gl) &&
gh && !(gh->gh_flags & LM_FLAG_NOEXP))
return;
goto skip_inval;

lck_flags &= (LM_FLAG_TRY | LM_FLAG_TRY_1CB | LM_FLAG_NOEXP |
LM_FLAG_PRIORITY);
GLOCK_BUG_ON(gl, gl->gl_state == target);
Expand Down Expand Up @@ -826,6 +830,20 @@ __acquires(&gl->gl_lockref.lock)
(target != LM_ST_UNLOCKED ||
test_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags))) {
if (!is_system_glock(gl)) {
handle_callback(gl, LM_ST_UNLOCKED, 0, false); /* sets demote */
/*
* Ordinarily, we would call dlm and its callback would call
* finish_xmote, which would call state_change() to the new state.
* Since we withdrew, we won't call dlm, so call state_change
* manually, but to the UNLOCKED state we desire.
*/
state_change(gl, LM_ST_UNLOCKED);
/*
* We skip telling dlm to do the locking, so we won't get a
* reply that would otherwise clear GLF_LOCK. So we clear it here.
*/
clear_bit(GLF_LOCK, &gl->gl_flags);
clear_bit(GLF_DEMOTE_IN_PROGRESS, &gl->gl_flags);
gfs2_glock_queue_work(gl, GL_GLOCK_DFT_HOLD);
goto out;
} else {
Expand Down Expand Up @@ -1018,16 +1036,18 @@ static void delete_work_func(struct work_struct *work)
if (gfs2_queue_delete_work(gl, 5 * HZ))
return;
}
goto out;
}

inode = gfs2_lookup_by_inum(sdp, no_addr, gl->gl_no_formal_ino,
GFS2_BLKST_UNLINKED);
if (!IS_ERR_OR_NULL(inode)) {
if (IS_ERR(inode)) {
if (PTR_ERR(inode) == -EAGAIN &&
(gfs2_queue_delete_work(gl, 5 * HZ)))
return;
} else {
d_prune_aliases(inode);
iput(inode);
}
out:
gfs2_glock_put(gl);
}

Expand Down Expand Up @@ -2194,6 +2214,20 @@ static void dump_glock_func(struct gfs2_glock *gl)
dump_glock(NULL, gl, true);
}

static void withdraw_dq(struct gfs2_glock *gl)
{
spin_lock(&gl->gl_lockref.lock);
if (!__lockref_is_dead(&gl->gl_lockref) &&
glock_blocked_by_withdraw(gl))
do_error(gl, LM_OUT_ERROR); /* remove pending waiters */
spin_unlock(&gl->gl_lockref.lock);
}

void gfs2_gl_dq_holders(struct gfs2_sbd *sdp)
{
glock_hash_walk(withdraw_dq, sdp);
}

/**
* gfs2_gl_hash_clear - Empty out the glock hash table
* @sdp: the filesystem
Expand Down
1 change: 1 addition & 0 deletions fs/gfs2/glock.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ extern void gfs2_cancel_delete_work(struct gfs2_glock *gl);
extern bool gfs2_delete_work_queued(const struct gfs2_glock *gl);
extern void gfs2_flush_delete_work(struct gfs2_sbd *sdp);
extern void gfs2_gl_hash_clear(struct gfs2_sbd *sdp);
extern void gfs2_gl_dq_holders(struct gfs2_sbd *sdp);
extern void gfs2_glock_thaw(struct gfs2_sbd *sdp);
extern void gfs2_glock_add_to_lru(struct gfs2_glock *gl);
extern void gfs2_glock_free(struct gfs2_glock *gl);
Expand Down
10 changes: 8 additions & 2 deletions fs/gfs2/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
if (inode->i_state & I_NEW) {
struct gfs2_sbd *sdp = GFS2_SB(inode);
struct gfs2_glock *io_gl;
int extra_flags = 0;

error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE,
&ip->i_gl);
Expand All @@ -141,9 +142,12 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
if (unlikely(error))
goto fail;

if (blktype != GFS2_BLKST_UNLINKED)
if (blktype == GFS2_BLKST_UNLINKED)
extra_flags |= LM_FLAG_TRY;
else
gfs2_cancel_delete_work(io_gl);
error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED, GL_EXACT,
error = gfs2_glock_nq_init(io_gl, LM_ST_SHARED,
GL_EXACT | extra_flags,
&ip->i_iopen_gh);
gfs2_glock_put(io_gl);
if (unlikely(error))
Expand Down Expand Up @@ -210,6 +214,8 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
return inode;

fail:
if (error == GLR_TRYFAILED)
error = -EAGAIN;
if (gfs2_holder_initialized(&ip->i_iopen_gh))
gfs2_glock_dq_uninit(&ip->i_iopen_gh);
if (gfs2_holder_initialized(&i_gh))
Expand Down
24 changes: 12 additions & 12 deletions fs/gfs2/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,6 @@ static int __init init_gfs2_fs(void)
if (error)
goto fail_shrinker;

error = register_filesystem(&gfs2_fs_type);
if (error)
goto fail_fs1;

error = register_filesystem(&gfs2meta_fs_type);
if (error)
goto fail_fs2;

error = -ENOMEM;
gfs_recovery_wq = alloc_workqueue("gfs_recovery",
WQ_MEM_RECLAIM | WQ_FREEZABLE, 0);
Expand All @@ -180,22 +172,30 @@ static int __init init_gfs2_fs(void)
goto fail_mempool;

gfs2_register_debugfs();
error = register_filesystem(&gfs2_fs_type);
if (error)
goto fail_fs1;

error = register_filesystem(&gfs2meta_fs_type);
if (error)
goto fail_fs2;


pr_info("GFS2 installed\n");

return 0;

fail_fs2:
unregister_filesystem(&gfs2_fs_type);
fail_fs1:
mempool_destroy(gfs2_page_pool);
fail_mempool:
destroy_workqueue(gfs2_freeze_wq);
fail_wq3:
destroy_workqueue(gfs2_control_wq);
fail_wq2:
destroy_workqueue(gfs_recovery_wq);
fail_wq1:
unregister_filesystem(&gfs2meta_fs_type);
fail_fs2:
unregister_filesystem(&gfs2_fs_type);
fail_fs1:
unregister_shrinker(&gfs2_qd_shrinker);
fail_shrinker:
kmem_cache_destroy(gfs2_trans_cachep);
Expand Down
17 changes: 11 additions & 6 deletions fs/gfs2/ops_fstype.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ static int gfs2_check_sb(struct gfs2_sbd *sdp, int silent)
pr_warn("Invalid block size\n");
return -EINVAL;
}

if (sb->sb_bsize_shift != ffs(sb->sb_bsize) - 1) {
pr_warn("Invalid block size shift\n");
return -EINVAL;
}
return 0;
}

Expand Down Expand Up @@ -381,8 +384,10 @@ static int init_names(struct gfs2_sbd *sdp, int silent)
if (!table[0])
table = sdp->sd_vfs->s_id;

strlcpy(sdp->sd_proto_name, proto, GFS2_FSNAME_LEN);
strlcpy(sdp->sd_table_name, table, GFS2_FSNAME_LEN);
BUILD_BUG_ON(GFS2_LOCKNAME_LEN > GFS2_FSNAME_LEN);

strscpy(sdp->sd_proto_name, proto, GFS2_LOCKNAME_LEN);
strscpy(sdp->sd_table_name, table, GFS2_LOCKNAME_LEN);

table = sdp->sd_table_name;
while ((table = strchr(table, '/')))
Expand Down Expand Up @@ -1439,13 +1444,13 @@ static int gfs2_parse_param(struct fs_context *fc, struct fs_parameter *param)

switch (o) {
case Opt_lockproto:
strlcpy(args->ar_lockproto, param->string, GFS2_LOCKNAME_LEN);
strscpy(args->ar_lockproto, param->string, GFS2_LOCKNAME_LEN);
break;
case Opt_locktable:
strlcpy(args->ar_locktable, param->string, GFS2_LOCKNAME_LEN);
strscpy(args->ar_locktable, param->string, GFS2_LOCKNAME_LEN);
break;
case Opt_hostdata:
strlcpy(args->ar_hostdata, param->string, GFS2_LOCKNAME_LEN);
strscpy(args->ar_hostdata, param->string, GFS2_LOCKNAME_LEN);
break;
case Opt_spectator:
args->ar_spectator = 1;
Expand Down
6 changes: 6 additions & 0 deletions fs/gfs2/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
}
if (!ret)
gfs2_make_fs_ro(sdp);
/*
* Dequeue any pending non-system glock holders that can no
* longer be granted because the file system is withdrawn.
*/
gfs2_gl_dq_holders(sdp);
gfs2_freeze_unlock(&freeze_gh);
}

Expand Down Expand Up @@ -204,6 +209,7 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
* exception code in glock_dq.
*/
iput(inode);
sdp->sd_jdesc->jd_inode = NULL;
/*
* Wait until the journal inode's glock is freed. This allows try locks
* on other nodes to be successful, otherwise we remain the owner of
Expand Down

0 comments on commit 4875d2f

Please sign in to comment.