Commit 4875d2ff authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents ac1e8c6c 74b1b10e
Loading
Loading
Loading
Loading
+39 −5
Original line number Diff line number Diff line
@@ -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;
@@ -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)
{
@@ -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);
@@ -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 {
@@ -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);
}

@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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);
+8 −2
Original line number Diff line number Diff line
@@ -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);
@@ -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))
@@ -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))
+12 −12
Original line number Diff line number Diff line
@@ -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);
@@ -180,11 +172,23 @@ 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:
@@ -192,10 +196,6 @@ static int __init init_gfs2_fs(void)
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);
+11 −6
Original line number Diff line number Diff line
@@ -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;
}

@@ -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, '/')))
@@ -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;
Loading