gfs2: Fix glock rhashtable rcu bug
[firefly-linux-kernel-4.4.55.git] / fs / gfs2 / glock.c
index 32e74710b1aae4a68e24b2b417622fe417f76536..070901e76653a306a2c961c5a5b8bafcd40157ca 100644 (file)
@@ -80,9 +80,9 @@ static struct rhashtable_params ht_parms = {
 
 static struct rhashtable gl_hash_table;
 
-void gfs2_glock_free(struct gfs2_glock *gl)
+static void gfs2_glock_dealloc(struct rcu_head *rcu)
 {
-       struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
+       struct gfs2_glock *gl = container_of(rcu, struct gfs2_glock, gl_rcu);
 
        if (gl->gl_ops->go_flags & GLOF_ASPACE) {
                kmem_cache_free(gfs2_glock_aspace_cachep, gl);
@@ -90,6 +90,13 @@ void gfs2_glock_free(struct gfs2_glock *gl)
                kfree(gl->gl_lksb.sb_lvbptr);
                kmem_cache_free(gfs2_glock_cachep, gl);
        }
+}
+
+void gfs2_glock_free(struct gfs2_glock *gl)
+{
+       struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
+
+       call_rcu(&gl->gl_rcu, gfs2_glock_dealloc);
        if (atomic_dec_and_test(&sdp->sd_glock_disposal))
                wake_up(&sdp->sd_glock_wait);
 }
@@ -651,9 +658,11 @@ int gfs2_glock_get(struct gfs2_sbd *sdp, u64 number,
        struct kmem_cache *cachep;
        int ret, tries = 0;
 
+       rcu_read_lock();
        gl = rhashtable_lookup_fast(&gl_hash_table, &name, ht_parms);
        if (gl && !lockref_get_not_dead(&gl->gl_lockref))
                gl = NULL;
+       rcu_read_unlock();
 
        *glp = gl;
        if (gl)
@@ -721,15 +730,18 @@ again:
 
        if (ret == -EEXIST) {
                ret = 0;
+               rcu_read_lock();
                tmp = rhashtable_lookup_fast(&gl_hash_table, &name, ht_parms);
                if (tmp == NULL || !lockref_get_not_dead(&tmp->gl_lockref)) {
                        if (++tries < 100) {
+                               rcu_read_unlock();
                                cond_resched();
                                goto again;
                        }
                        tmp = NULL;
                        ret = -ENOMEM;
                }
+               rcu_read_unlock();
        } else {
                WARN_ON_ONCE(ret);
        }