mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
gfs2 fixes
- 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. -----BEGIN PGP SIGNATURE----- iQJIBAABCAAyFiEEJZs3krPW0xkhLMTc1b+f6wMTZToFAmNCoXgUHGFncnVlbmJh QHJlZGhhdC5jb20ACgkQ1b+f6wMTZTph4xAAg+SnmCkvh07ww1CLLGxPWFK41toZ zBYbwXb3h4qeFXQ4XSI4ZYnAjWTwIK0yjz0lp6pRnEoh0ih+IuEuIZLVls/ggEn6 /BxRDKWHE6fJwkemsB/V8XAu3UTemG0BOo6kkoYYnkjd5qZuqcqMkUEvc4xwsIxu 2tfzKfPoIgn95HVjiQboViwEEa/HzwaxxudWaVoa/Gn54wWxFfrQplGoYftS/8ah 5d0ho1hOLPXJNNeKUXVnu8buKs3Oh59OpMXoI4gzvUfh2YWYqm5giP0Dwnv5qf6F NeMFqUxUKoMKL5nofkWI6FqRoWLbmattrL8nu0Fpn/tlzuMivlI25PB4k37R3tGx GILncsnLOMxzUx69aTnZLyn/8RWkgiHaQ3Nm4OJPTENd7/V7o/5O1BRInRzdeHdN LEfD6Fsi+muK5V6aIe1LIELSB0i/C7753pF0BWQ93KItE6hpufTct9YPXgmn+k/v gxHy+laPWhKXdPriBnO6RyeV7DJr/Ai+7ga3e35UxRqr9DvIpltNUvODqxyXPymS EIC45zQD6fVLOIFCxwLPrTbc2isrcvu+TYlcByaUVSc5weK3ZbdWz4Pm3Wd59K3n 7oJRx6oWMemVcTgUsZQ7Lr7lPlM47O9kiUJy9RNCeefPChQ/5+OgxfKOcdqQZK8E z4a3efQ5APqp308= =h/LV -----END PGP SIGNATURE----- Merge tag 'gfs2-v6.0-rc2-fixes' of git://git.kernel.org/pub/scm/linux/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
This commit is contained in:
commit
4875d2ffb0
@ -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 @@ skip_inval:
|
||||
(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
|
||||
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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 @@ fail_wq3:
|
||||
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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user