diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c index ee5cca5926ed6fa72c6ffa56c0554b8c7f2e7da0..c854e4851e26d88f12feb740da6ec1372621b6a2 100644 --- a/fs/gfs2/glock.c +++ b/fs/gfs2/glock.c @@ -159,17 +159,44 @@ static bool glock_blocked_by_withdraw(struct gfs2_glock *gl) return true; } -void gfs2_glock_free(struct gfs2_glock *gl) +static void __gfs2_glock_free(struct gfs2_glock *gl) { - struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; - gfs2_glock_assert_withdraw(gl, atomic_read(&gl->gl_revokes) == 0); rhashtable_remove_fast(&gl_hash_table, &gl->gl_node, ht_parms); smp_mb(); wake_up_glock(gl); call_rcu(&gl->gl_rcu, gfs2_glock_dealloc); +} + +void gfs2_glock_free(struct gfs2_glock *gl) { + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + + __gfs2_glock_free(gl); + if (atomic_dec_and_test(&sdp->sd_glock_disposal)) + wake_up(&sdp->sd_kill_wait); +} + +void gfs2_glock_free_later(struct gfs2_glock *gl) { + struct gfs2_sbd *sdp = gl->gl_name.ln_sbd; + + spin_lock(&lru_lock); + list_add(&gl->gl_lru, &sdp->sd_dead_glocks); + spin_unlock(&lru_lock); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) - wake_up(&sdp->sd_glock_wait); + wake_up(&sdp->sd_kill_wait); +} + +static void gfs2_free_dead_glocks(struct gfs2_sbd *sdp) +{ + struct list_head *list = &sdp->sd_dead_glocks; + + while(!list_empty(list)) { + struct gfs2_glock *gl; + + gl = list_first_entry(list, struct gfs2_glock, gl_lru); + list_del_init(&gl->gl_lru); + __gfs2_glock_free(gl); + } } /** @@ -369,7 +396,7 @@ static void do_error(struct gfs2_glock *gl, const int ret) /** * do_promote - promote as many requests as possible on the current queue * @gl: The glock - * + * * Returns: 1 if there is a blocked holder at the head of the list, or 2 * if a type specific operation is underway. */ @@ -1097,7 +1124,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number, kfree(gl->gl_lksb.sb_lvbptr); kmem_cache_free(cachep, gl); if (atomic_dec_and_test(&sdp->sd_glock_disposal)) - wake_up(&sdp->sd_glock_wait); + wake_up(&sdp->sd_kill_wait); out: return ret; @@ -1327,7 +1354,7 @@ void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...) * Eventually we should move the recursive locking trap to a * debugging option or something like that. This is the fast * path and needs to have the minimum number of distractions. - * + * */ static inline void add_to_queue(struct gfs2_holder *gh) @@ -2008,9 +2035,11 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp) { set_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags); flush_workqueue(glock_workqueue); + gfs2_lm_unmount(sdp); + gfs2_free_dead_glocks(sdp); glock_hash_walk(clear_glock, sdp); flush_workqueue(glock_workqueue); - wait_event_timeout(sdp->sd_glock_wait, + wait_event_timeout(sdp->sd_kill_wait, atomic_read(&sdp->sd_glock_disposal) == 0, HZ * 600); glock_hash_walk(dump_glock_func, sdp); diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h index 53813364517bd5a1464849da53d5c434c8c1cad9..67717cc60806331062f1565c3b640977d70ff396 100644 --- a/fs/gfs2/glock.h +++ b/fs/gfs2/glock.h @@ -253,6 +253,7 @@ extern void gfs2_glock_finish_truncate(struct gfs2_inode *ip); 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); +void gfs2_glock_free_later(struct gfs2_glock *gl); extern int __init gfs2_glock_init(void); extern void gfs2_glock_exit(void); diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index f8858d995b241093063f676165aee5441b014a7a..d3dbcf23e304e738ccad4cf545986d20e241a47c 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -741,7 +741,7 @@ struct gfs2_sbd { struct gfs2_glock *sd_rename_gl; struct gfs2_glock *sd_freeze_gl; struct work_struct sd_freeze_work; - wait_queue_head_t sd_glock_wait; + wait_queue_head_t sd_kill_wait; wait_queue_head_t sd_async_glock_wait; atomic_t sd_glock_disposal; struct completion sd_locking_init; @@ -863,6 +863,7 @@ struct gfs2_sbd { struct gfs2_holder sd_freeze_gh; atomic_t sd_freeze_state; struct mutex sd_freeze_mutex; + struct list_head sd_dead_glocks; char sd_fsname[GFS2_FSNAME_LEN + 3 * sizeof(int) + 2]; char sd_table_name[GFS2_FSNAME_LEN]; diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index 5564aa8b4592927a21e0fd0582122db786d8af62..98dce5826d66e27730a7381f56e26687ee42377d 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c @@ -112,12 +112,17 @@ static inline void gfs2_update_request_times(struct gfs2_glock *gl) gfs2_update_stats(&lks->lkstats[gltype], GFS2_LKS_SIRT, irt); /* Global */ preempt_enable(); } - + static void gdlm_ast(void *arg) { struct gfs2_glock *gl = arg; unsigned ret = gl->gl_state; + /* If the glock is dead, we only react to a dlm_unlock() reply. */ + if (__lockref_is_dead(&gl->gl_lockref) && + gl->gl_lksb.sb_status != -DLM_EUNLOCK) + return; + gfs2_update_reply_times(gl); BUG_ON(gl->gl_lksb.sb_flags & DLM_SBF_DEMOTED); @@ -168,6 +173,9 @@ static void gdlm_bast(void *arg, int mode) { struct gfs2_glock *gl = arg; + if (__lockref_is_dead(&gl->gl_lockref)) + return; + switch (mode) { case DLM_LOCK_EX: gfs2_glock_cb(gl, LM_ST_UNLOCKED); @@ -286,6 +294,8 @@ static void gdlm_put_lock(struct gfs2_glock *gl) struct lm_lockstruct *ls = &sdp->sd_lockstruct; int error; + BUG_ON(!__lockref_is_dead(&gl->gl_lockref)); + if (gl->gl_lksb.sb_lkid == 0) { gfs2_glock_free(gl); return; @@ -305,7 +315,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl) if (test_bit(SDF_SKIP_DLM_UNLOCK, &sdp->sd_flags) && !gl->gl_lksb.sb_lvbptr) { - gfs2_glock_free(gl); + gfs2_glock_free_later(gl); return; } diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index 648f7336043f6a7ec5239d005153d426c64390dd..f7c232f3ecac6f8214d227f6ce1921b7312f01c7 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -87,7 +87,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) set_bit(SDF_NOJOURNALID, &sdp->sd_flags); gfs2_tune_init(&sdp->sd_tune); - init_waitqueue_head(&sdp->sd_glock_wait); + init_waitqueue_head(&sdp->sd_kill_wait); init_waitqueue_head(&sdp->sd_async_glock_wait); atomic_set(&sdp->sd_glock_disposal, 0); init_completion(&sdp->sd_locking_init); @@ -141,6 +141,7 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb) init_waitqueue_head(&sdp->sd_log_flush_wait); atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN); mutex_init(&sdp->sd_freeze_mutex); + INIT_LIST_HEAD(&sdp->sd_dead_glocks); return sdp; @@ -1015,7 +1016,7 @@ static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent) switch (token) { case Opt_jid: ret = match_int(&tmp[0], &option); - if (ret || option < 0) + if (ret || option < 0) goto hostdata_error; if (test_and_clear_bit(SDF_NOJOURNALID, &sdp->sd_flags)) ls->ls_jid = option; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 8cf4ef61cdc41d6cfa3305c6a9bd43d702591994..039d678b1689fac888472f9620054e9cb5e82de7 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -662,10 +662,7 @@ static void gfs2_put_super(struct super_block *sb) gfs2_gl_hash_clear(sdp); truncate_inode_pages_final(&sdp->sd_aspace); gfs2_delete_debugfs_file(sdp); - /* Unmount the locking protocol */ - gfs2_lm_unmount(sdp); - /* At this point, we're through participating in the lockspace */ gfs2_sys_fs_del(sdp); free_sbd(sdp); }