diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2011-03-09 05:58:04 -0500 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2011-03-09 05:58:04 -0500 |
commit | fc0e38dae645f65424d1fb5d2a938aab8ce48a58 (patch) | |
tree | 8b0a89a0de01a0504a002a184a290a53545fce82 /fs | |
parent | 662e3a551b468c7338f5291d7a00389fe85885e2 (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')
-rw-r--r-- | fs/gfs2/glock.c | 10 | ||||
-rw-r--r-- | fs/gfs2/glock.h | 2 | ||||
-rw-r--r-- | fs/gfs2/lock_dlm.c | 4 | ||||
-rw-r--r-- | fs/gfs2/ops_fstype.c | 7 |
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 | ||
106 | void gfs2_glock_free(struct rcu_head *rcu) | 106 | static 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 | |||
116 | void 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); | |||
230 | extern void gfs2_glock_finish_truncate(struct gfs2_inode *ip); | 230 | extern void gfs2_glock_finish_truncate(struct gfs2_inode *ip); |
231 | extern void gfs2_glock_thaw(struct gfs2_sbd *sdp); | 231 | extern void gfs2_glock_thaw(struct gfs2_sbd *sdp); |
232 | extern void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl); | 232 | extern void gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl); |
233 | extern void gfs2_glock_free(struct rcu_head *rcu); | 233 | extern void gfs2_glock_free(struct gfs2_glock *gl); |
234 | 234 | ||
235 | extern int __init gfs2_glock_init(void); | 235 | extern int __init gfs2_glock_init(void); |
236 | extern void gfs2_glock_exit(void); | 236 | extern 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 | ||
931 | static void nolock_put_lock(struct gfs2_glock *gl) | ||
932 | { | ||
933 | call_rcu(&gl->gl_rcu, gfs2_glock_free); | ||
934 | } | ||
935 | |||
936 | static const struct lm_lockops nolock_ops = { | 931 | static 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 | ||