aboutsummaryrefslogtreecommitdiffstats
path: root/fs/gfs2
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2011-03-09 05:58:04 -0500
committerSteven Whitehouse <swhiteho@redhat.com>2011-03-09 05:58:04 -0500
commitfc0e38dae645f65424d1fb5d2a938aab8ce48a58 (patch)
tree8b0a89a0de01a0504a002a184a290a53545fce82 /fs/gfs2
parent662e3a551b468c7338f5291d7a00389fe85885e2 (diff)
GFS2: Fix glock deallocation race
This patch fixes a race in deallocating glocks which was introduced in the RCU glock patch. We need to ensure that the glock count is kept correct even in the case that there is a race to add a new glock into the hash table. Also, to avoid having to wait for an RCU grace period, the glock counter can be decremented before call_rcu() is called. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
Diffstat (limited to 'fs/gfs2')
-rw-r--r--fs/gfs2/glock.c10
-rw-r--r--fs/gfs2/glock.h2
-rw-r--r--fs/gfs2/lock_dlm.c4
-rw-r--r--fs/gfs2/ops_fstype.c7
4 files changed, 12 insertions, 11 deletions
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index ddc3e1e3faaf..3f45a14009b8 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -103,16 +103,21 @@ static inline void spin_unlock_bucket(unsigned int hash)
103 __bit_spin_unlock(0, (unsigned long *)bl); 103 __bit_spin_unlock(0, (unsigned long *)bl);
104} 104}
105 105
106void gfs2_glock_free(struct rcu_head *rcu) 106static void gfs2_glock_dealloc(struct rcu_head *rcu)
107{ 107{
108 struct gfs2_glock *gl = container_of(rcu, struct gfs2_glock, gl_rcu); 108 struct gfs2_glock *gl = container_of(rcu, struct gfs2_glock, gl_rcu);
109 struct gfs2_sbd *sdp = gl->gl_sbd;
110 109
111 if (gl->gl_ops->go_flags & GLOF_ASPACE) 110 if (gl->gl_ops->go_flags & GLOF_ASPACE)
112 kmem_cache_free(gfs2_glock_aspace_cachep, gl); 111 kmem_cache_free(gfs2_glock_aspace_cachep, gl);
113 else 112 else
114 kmem_cache_free(gfs2_glock_cachep, gl); 113 kmem_cache_free(gfs2_glock_cachep, gl);
114}
115
116void gfs2_glock_free(struct gfs2_glock *gl)
117{
118 struct gfs2_sbd *sdp = gl->gl_sbd;
115 119
120 call_rcu(&gl->gl_rcu, gfs2_glock_dealloc);
116 if (atomic_dec_and_test(&sdp->sd_glock_disposal)) 121 if (atomic_dec_and_test(&sdp->sd_glock_disposal))
117 wake_up(&sdp->sd_glock_wait); 122 wake_up(&sdp->sd_glock_wait);
118} 123}
@@ -760,6 +765,7 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
760 if (tmp) { 765 if (tmp) {
761 spin_unlock_bucket(hash); 766 spin_unlock_bucket(hash);
762 kmem_cache_free(cachep, gl); 767 kmem_cache_free(cachep, gl);
768 atomic_dec(&sdp->sd_glock_disposal);
763 gl = tmp; 769 gl = tmp;
764 } else { 770 } else {
765 hlist_bl_add_head_rcu(&gl->gl_list, &gl_hash_table[hash]); 771 hlist_bl_add_head_rcu(&gl->gl_list, &gl_hash_table[hash]);
diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h
index afa8bfea5647..aea160690e94 100644
--- a/fs/gfs2/glock.h
+++ b/fs/gfs2/glock.h
@@ -230,7 +230,7 @@ extern void gfs2_gl_hash_clear(struct gfs2_sbd *sdp);
230extern void gfs2_glock_finish_truncate(struct gfs2_inode *ip); 230extern void gfs2_glock_finish_truncate(struct gfs2_inode *ip);
231extern void gfs2_glock_thaw(struct gfs2_sbd *sdp); 231extern void gfs2_glock_thaw(struct gfs2_sbd *sdp);
232extern void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl); 232extern void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl);
233extern void gfs2_glock_free(struct rcu_head *rcu); 233extern void gfs2_glock_free(struct gfs2_glock *gl);
234 234
235extern int __init gfs2_glock_init(void); 235extern int __init gfs2_glock_init(void);
236extern void gfs2_glock_exit(void); 236extern void gfs2_glock_exit(void);
diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c
index c80485cb6f25..98c80d8c2a62 100644
--- a/fs/gfs2/lock_dlm.c
+++ b/fs/gfs2/lock_dlm.c
@@ -30,7 +30,7 @@ static void gdlm_ast(void *arg)
30 30
31 switch (gl->gl_lksb.sb_status) { 31 switch (gl->gl_lksb.sb_status) {
32 case -DLM_EUNLOCK: /* Unlocked, so glock can be freed */ 32 case -DLM_EUNLOCK: /* Unlocked, so glock can be freed */
33 call_rcu(&gl->gl_rcu, gfs2_glock_free); 33 gfs2_glock_free(gl);
34 return; 34 return;
35 case -DLM_ECANCEL: /* Cancel while getting lock */ 35 case -DLM_ECANCEL: /* Cancel while getting lock */
36 ret |= LM_OUT_CANCELED; 36 ret |= LM_OUT_CANCELED;
@@ -165,7 +165,7 @@ static void gdlm_put_lock(struct gfs2_glock *gl)
165 int error; 165 int error;
166 166
167 if (gl->gl_lksb.sb_lkid == 0) { 167 if (gl->gl_lksb.sb_lkid == 0) {
168 call_rcu(&gl->gl_rcu, gfs2_glock_free); 168 gfs2_glock_free(gl);
169 return; 169 return;
170 } 170 }
171 171
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index a39c103ba499..67654d0ba15e 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -928,14 +928,9 @@ static const match_table_t nolock_tokens = {
928 { Opt_err, NULL }, 928 { Opt_err, NULL },
929}; 929};
930 930
931static void nolock_put_lock(struct gfs2_glock *gl)
932{
933 call_rcu(&gl->gl_rcu, gfs2_glock_free);
934}
935
936static const struct lm_lockops nolock_ops = { 931static const struct lm_lockops nolock_ops = {
937 .lm_proto_name = "lock_nolock", 932 .lm_proto_name = "lock_nolock",
938 .lm_put_lock = nolock_put_lock, 933 .lm_put_lock = gfs2_glock_free,
939 .lm_tokens = &nolock_tokens, 934 .lm_tokens = &nolock_tokens,
940}; 935};
941 936