[GFS2] Rewrite of examine_bucket()
Steven Whitehouse [Tue, 12 Sep 2006 01:40:30 +0000 (21:40 -0400)]
The existing implementation of this function in glock.c was not
very efficient as it relied upon keeping a cursor element upon the
hash chain in question and moving it along. This new version improves
upon this by using the current element as a cursor. This is possible
since we only look at the "next" element in the list after we've
taken the read_lock() subsequent to calling the examiner function.
Obviously we have to eventually drop the ref count that we are then
left with and we cannot do that while holding the read_lock, so we
do that next time we drop the lock. That means either just before
we examine another glock, or when the loop has terminated.

The new implementation has several advantages: it uses only a
read_lock() rather than a write_lock(), so it can run simnultaneously
with other code, it doesn't need a "plug" element, so that it removes
a test not only from this list iterator, but from all the other glock
list iterators too. So it makes things faster and smaller.

Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>

fs/gfs2/glock.c
fs/gfs2/incore.h

index b348053..b5effb9 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/kref.h>
 #include <linux/kallsyms.h>
 #include <linux/gfs2_ondisk.h>
+#include <linux/list.h>
 #include <asm/uaccess.h>
 
 #include "gfs2.h"
 #include "super.h"
 #include "util.h"
 
-/*  Must be kept in sync with the beginning of struct gfs2_glock  */
-struct glock_plug {
-       struct list_head gl_list;
-       unsigned long gl_flags;
-};
-
 struct greedy {
        struct gfs2_holder gr_gh;
        struct work_struct gr_work;
@@ -52,6 +47,7 @@ typedef void (*glock_examiner) (struct gfs2_glock * gl);
 
 static int gfs2_dump_lockstate(struct gfs2_sbd *sdp);
 static int dump_glock(struct gfs2_glock *gl);
+static int dump_inode(struct gfs2_inode *ip);
 
 #define GFS2_GL_HASH_SHIFT      13
 #define GFS2_GL_HASH_SIZE       (1 << GFS2_GL_HASH_SHIFT)
@@ -214,7 +210,7 @@ int gfs2_glock_put(struct gfs2_glock *gl)
 
        write_lock(gl_lock_addr(gl->gl_hash));
        if (kref_put(&gl->gl_ref, kill_glock)) {
-               list_del_init(&gl_hash_table[gl->gl_hash].hb_list);
+               list_del_init(&gl->gl_list);
                write_unlock(gl_lock_addr(gl->gl_hash));
                BUG_ON(spin_is_locked(&gl->gl_spin));
                glock_free(gl);
@@ -265,8 +261,6 @@ static struct gfs2_glock *search_bucket(unsigned int hash,
        struct gfs2_glock *gl;
 
        list_for_each_entry(gl, &gl_hash_table[hash].hb_list, gl_list) {
-               if (test_bit(GLF_PLUG, &gl->gl_flags))
-                       continue;
                if (!lm_name_equal(&gl->gl_name, name))
                        continue;
                if (gl->gl_sbd != sdp)
@@ -1899,51 +1893,33 @@ void gfs2_reclaim_glock(struct gfs2_sbd *sdp)
 static int examine_bucket(glock_examiner examiner, struct gfs2_sbd *sdp,
                          unsigned int hash)
 {
-       struct glock_plug plug;
-       struct list_head *tmp;
-       struct gfs2_glock *gl;
-       int entries;
-
-       /* Add "plug" to end of bucket list, work back up list from there */
-       memset(&plug.gl_flags, 0, sizeof(unsigned long));
-       set_bit(GLF_PLUG, &plug.gl_flags);
-
-       write_lock(gl_lock_addr(hash));
-       list_add(&plug.gl_list, &gl_hash_table[hash].hb_list);
-       write_unlock(gl_lock_addr(hash));
-
-       for (;;) {
-               write_lock(gl_lock_addr(hash));
-
-               for (;;) {
-                       tmp = plug.gl_list.next;
+       struct gfs2_glock *gl, *prev = NULL;
+       int has_entries = 0;
+       struct list_head *head = &gl_hash_table[hash].hb_list;
 
-                       if (tmp == &gl_hash_table[hash].hb_list) {
-                               list_del(&plug.gl_list);
-                               entries = !list_empty(&gl_hash_table[hash].hb_list);
-                               write_unlock(gl_lock_addr(hash));
-                               return entries;
-                       }
-                       gl = list_entry(tmp, struct gfs2_glock, gl_list);
-
-                       /* Move plug up list */
-                       list_move(&plug.gl_list, &gl->gl_list);
-
-                       if (test_bit(GLF_PLUG, &gl->gl_flags))
-                               continue;
-                       if (gl->gl_sbd != sdp)
-                               continue;
-
-                       /* examiner() must glock_put() */
+       read_lock(gl_lock_addr(hash));
+       /* Can't use list_for_each_entry - don't want prefetch here */
+       if (list_empty(head))
+               goto out;
+       has_entries = 1;
+       gl = list_entry(head->next, struct gfs2_glock, gl_list);
+       while(&gl->gl_list != head) {
+               if (gl->gl_sbd == sdp) {
                        gfs2_glock_hold(gl);
-
-                       break;
+                       read_unlock(gl_lock_addr(hash));
+                       if (prev)
+                               gfs2_glock_put(prev);
+                       prev = gl;
+                       examiner(gl);
+                       read_lock(gl_lock_addr(hash));
                }
-
-               write_unlock(gl_lock_addr(hash));
-
-               examiner(gl);
+               gl = list_entry(gl->gl_list.next, struct gfs2_glock, gl_list);
        }
+out:
+       read_unlock(gl_lock_addr(hash));
+       if (prev)
+               gfs2_glock_put(prev);
+       return has_entries;
 }
 
 /**
@@ -1955,23 +1931,19 @@ static int examine_bucket(glock_examiner examiner, struct gfs2_sbd *sdp,
 static void scan_glock(struct gfs2_glock *gl)
 {
        if (gl->gl_ops == &gfs2_inode_glops)
-               goto out;
+               return;
 
        if (gfs2_glmutex_trylock(gl)) {
                if (queue_empty(gl, &gl->gl_holders) &&
-                   gl->gl_state != LM_ST_UNLOCKED &&
-                   demote_ok(gl))
+                   gl->gl_state != LM_ST_UNLOCKED && demote_ok(gl))
                        goto out_schedule;
                gfs2_glmutex_unlock(gl);
        }
-out:
-       gfs2_glock_put(gl);
        return;
 
 out_schedule:
        gfs2_glmutex_unlock(gl);
        gfs2_glock_schedule_for_reclaim(gl);
-       gfs2_glock_put(gl);
 }
 
 /**
@@ -2014,11 +1986,8 @@ static void clear_glock(struct gfs2_glock *gl)
                if (queue_empty(gl, &gl->gl_holders) &&
                    gl->gl_state != LM_ST_UNLOCKED)
                        handle_callback(gl, LM_ST_UNLOCKED);
-
                gfs2_glmutex_unlock(gl);
        }
-
-       gfs2_glock_put(gl);
 }
 
 /**
@@ -2040,10 +2009,10 @@ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp, int wait)
 
        for (;;) {
                cont = 0;
-
-               for (x = 0; x < GFS2_GL_HASH_SIZE; x++)
-                       if (examine_bucket(clear_glock, sdp, x))
+               for (x = 0; x < GFS2_GL_HASH_SIZE; x++) {
+                       if (examine_bucket(clear_glock, sdp, x)) 
                                cont = 1;
+               }
 
                if (!wait || !cont)
                        break;
@@ -2234,8 +2203,6 @@ static int gfs2_dump_lockstate(struct gfs2_sbd *sdp)
                read_lock(gl_lock_addr(x));
 
                list_for_each_entry(gl, &gl_hash_table[x].hb_list, gl_list) {
-                       if (test_bit(GLF_PLUG, &gl->gl_flags))
-                               continue;
                        if (gl->gl_sbd != sdp)
                                continue;
 
index 64e1343..1fed8d1 100644 (file)
@@ -152,7 +152,6 @@ struct gfs2_holder {
 };
 
 enum {
-       GLF_PLUG                = 0,
        GLF_LOCK                = 1,
        GLF_STICKY              = 2,
        GLF_PREFETCH            = 3,