This is an updated version of the RCU glock patch which I've
been working on, on and off for a while now. It has survived
some basic testing so far, I've not tried running very large
numbers of threads against it so far.
In order to apply this patch you'll need the latest -nmw tree
and also this patch:
http://git.kernel.org/?p=linux/kernel/git/npiggin/linux-npiggin.git;a=commitdiff;h=574d900af0cf01be2ab929a4030ed4a3d2a2b1de
which adds the rculist_bl.h header. Fingers crossed that or
something like it will be merged at the next merge window.
As per the previous posting, the main features are (a) reduced
code size, (b) rcu glock hash table to reduce contention on
lookups. Also, new this time, (c) spin locks for the write side
of hash table manipulation borrow 1 bit from the list heads (i.e.
use rculist_bl) so that the hash lock is now split up.
Further thoughts:
- We should use a per-cpu counter for the per-sb glock counter.
- We could use SLAB_DESTROY_BY_RCU, but I need to figure out how
to alter the lookup routines in order to make that work.
- Maybe add lockdep to spin_lock/unlock_bucket
I'm expecting that there will probably be further versions of this
patch before it is in its final form.
Signed-off-by: Steven Whitehouse swhit...@redhat.com
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 2dd1d72..ecacf3f 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -26,6 +26,8 @@
#include linux/freezer.h
#include linux/workqueue.h
#include linux/jiffies.h
+#include linux/rcupdate.h
+#include linux/rculist_bl.h
#include gfs2.h
#include incore.h
@@ -41,10 +43,6 @@
#define CREATE_TRACE_POINTS
#include trace_gfs2.h
-struct gfs2_gl_hash_bucket {
-struct hlist_head hb_list;
-};
-
struct gfs2_glock_iter {
int hash; /* hash bucket index */
struct gfs2_sbd *sdp; /* incore superblock */
@@ -54,7 +52,6 @@ struct gfs2_glock_iter {
typedef void (*glock_examiner) (struct gfs2_glock * gl);
-static int gfs2_dump_lockstate(struct gfs2_sbd *sdp);
static int __dump_glock(struct seq_file *seq, const struct gfs2_glock *gl);
#define GLOCK_BUG_ON(gl,x) do { if (unlikely(x)) { __dump_glock(NULL, gl);
BUG(); } } while(0)
static void do_xmote(struct gfs2_glock *gl, struct gfs2_holder *gh, unsigned
int target);
@@ -70,57 +67,9 @@ static DEFINE_SPINLOCK(lru_lock);
#define GFS2_GL_HASH_SIZE (1 GFS2_GL_HASH_SHIFT)
#define GFS2_GL_HASH_MASK (GFS2_GL_HASH_SIZE - 1)
-static struct gfs2_gl_hash_bucket gl_hash_table[GFS2_GL_HASH_SIZE];
+static struct hlist_bl_head gl_hash_table[GFS2_GL_HASH_SIZE];
static struct dentry *gfs2_root;
-/*
- * Despite what you might think, the numbers below are not arbitrary :-)
- * They are taken from the ipv4 routing hash code, which is well tested
- * and thus should be nearly optimal. Later on we might tweek the numbers
- * but for now this should be fine.
- *
- * The reason for putting the locks in a separate array from the list heads
- * is that we can have fewer locks than list heads and save memory. We use
- * the same hash function for both, but with a different hash mask.
- */
-#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) || \
- defined(CONFIG_PROVE_LOCKING)
-
-#ifdef CONFIG_LOCKDEP
-# define GL_HASH_LOCK_SZ256
-#else
-# if NR_CPUS = 32
-# define GL_HASH_LOCK_SZ 4096
-# elif NR_CPUS = 16
-# define GL_HASH_LOCK_SZ 2048
-# elif NR_CPUS = 8
-# define GL_HASH_LOCK_SZ 1024
-# elif NR_CPUS = 4
-# define GL_HASH_LOCK_SZ 512
-# else
-# define GL_HASH_LOCK_SZ 256
-# endif
-#endif
-
-/* We never want more locks than chains */
-#if GFS2_GL_HASH_SIZE GL_HASH_LOCK_SZ
-# undef GL_HASH_LOCK_SZ
-# define GL_HASH_LOCK_SZ GFS2_GL_HASH_SIZE
-#endif
-
-static rwlock_t gl_hash_locks[GL_HASH_LOCK_SZ];
-
-static inline rwlock_t *gl_lock_addr(unsigned int x)
-{
- return gl_hash_locks[x (GL_HASH_LOCK_SZ-1)];
-}
-#else /* not SMP, so no spinlocks required */
-static inline rwlock_t *gl_lock_addr(unsigned int x)
-{
- return NULL;
-}
-#endif
-
/**
* gl_hash() - Turn glock number into hash bucket number
* @lock: The glock number
@@ -141,25 +90,30 @@ static unsigned int gl_hash(const struct gfs2_sbd *sdp,
return h;
}
-/**
- * glock_free() - Perform a few checks and then release struct gfs2_glock
- * @gl: The glock to release
- *
- * Also calls lock module to release its internal structure for this glock.
- *
- */
+static inline void spin_lock_bucket(unsigned int hash)
+{
+ struct hlist_bl_head *bl = gl_hash_table[hash];
+ bit_spin_lock(0, (unsigned long *)bl);
+}
+
+static inline void spin_unlock_bucket(unsigned int hash)
+{
+ struct hlist_bl_head *bl = gl_hash_table[hash];
+ __bit_spin_unlock(0, (unsigned long *)bl);
+}
-static void glock_free(struct gfs2_glock *gl)
+void gfs2_glock_free(struct rcu_head *rcu)
{
+ struct gfs2_glock *gl = container_of(rcu, struct gfs2_glock, gl_rcu);