[PATCH 10/19] quota: user per-backet hlist lock for dquot_hash

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch simply transform hlist_head to hlist_bl_head to allow us
to remove dq_list_lock. Later hash lookup will be transformed to
lock less procedure with help of RCU.

Signed-off-by: Dmitry Monakhov <dmonakhov@xxxxxxxxxx>
---
 fs/quota/dquot.c        |   56 ++++++++++++++++++++++------------------------
 include/linux/list_bl.h |   10 ++++++++
 include/linux/quota.h   |    3 +-
 3 files changed, 39 insertions(+), 30 deletions(-)

diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index c06f969..99dc7a3 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -88,7 +88,7 @@
  * in inode_add_bytes() and inode_sub_bytes().
  *
  * The spinlock ordering is hence: dq_data_lock > dq_list_lock > i_lock,
- * dq_list_lock > sb->s_dquot->dq_list_lock
+ * dq_list_lock > sb->s_dquot->dq_list_lock > hlist_bl_head
  * Note that some things (eg. sb pointer, type, id) doesn't change during
  * the life of the dquot structure and so needn't to be protected by a lock
  *
@@ -234,7 +234,7 @@ static void put_quota_format(struct quota_format_type *fmt)
  */
 
 static unsigned int dq_hash_bits, dq_hash_mask;
-static struct hlist_head *dquot_hash;
+static struct hlist_bl_head *dquot_hash;
 
 struct dqstats dqstats;
 EXPORT_SYMBOL(dqstats);
@@ -251,29 +251,28 @@ hashfn(const struct super_block *sb, unsigned int id, int type)
 	return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask;
 }
 
-/*
- * Following list functions expect dq_list_lock to be held
- */
-static inline void insert_dquot_hash(struct dquot *dquot)
+static inline void insert_dquot_hash(struct dquot *dquot, struct hlist_bl_head *blh)
 {
-	struct hlist_head *head;
-	head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
-	hlist_add_head(&dquot->dq_hash, head);
+	hlist_bl_add_head(&dquot->dq_hash, blh);
 }
 
 static inline void remove_dquot_hash(struct dquot *dquot)
 {
-	hlist_del_init(&dquot->dq_hash);
+	struct hlist_bl_head *blh;
+	blh = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
+	hlist_bl_lock(blh);
+	hlist_bl_del_init(&dquot->dq_hash);
+	hlist_bl_unlock(blh);
 }
 
-static struct dquot *find_dquot(unsigned int hashent, struct super_block *sb,
-				unsigned int id, int type)
+static struct dquot *find_dquot(struct hlist_bl_head *blh,
+				struct super_block *sb, unsigned int id,
+				int type)
 {
-	struct hlist_node *node;
+	struct hlist_bl_node *node;
 	struct dquot *dquot;
 
-	hlist_for_each (node, dquot_hash+hashent) {
-		dquot = hlist_entry(node, struct dquot, dq_hash);
+	hlist_bl_for_each_entry(dquot, node, blh, dq_hash) {
 		if (dquot->dq_sb == sb && dquot->dq_id == id &&
 		    dquot->dq_type == type)
 			return dquot;
@@ -830,8 +829,8 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
 	mutex_init(&dquot->dq_lock);
 	INIT_LIST_HEAD(&dquot->dq_free);
 	INIT_LIST_HEAD(&dquot->dq_inuse);
-	INIT_HLIST_NODE(&dquot->dq_hash);
 	INIT_LIST_HEAD(&dquot->dq_dirty);
+	INIT_HLIST_BL_NODE(&dquot->dq_hash);
 	init_waitqueue_head(&dquot->dq_wait_unused);
 	dquot->dq_sb = sb;
 	dquot->dq_type = type;
@@ -850,7 +849,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
  */
 struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
 {
-	unsigned int hashent = hashfn(sb, id, type);
+	struct hlist_bl_head * blh = dquot_hash + hashfn(sb, id, type);
 	struct dquot *dquot = NULL, *empty = NULL;
 	struct quota_info *dqopt;
 	int idx;
@@ -871,9 +870,11 @@ we_slept:
 		spin_unlock(&dq_list_lock);
 		goto out;
 	}
-	dquot = find_dquot(hashent, sb, id, type);
+	hlist_bl_lock(blh);
+	dquot = find_dquot(blh, sb, id, type);
 	if (!dquot) {
 		if (!empty) {
+			hlist_bl_unlock(blh);
 			spin_unlock(&dqopt->dq_list_lock);
 			spin_unlock(&dq_list_lock);
 			empty = get_empty_dquot(sb, type);
@@ -884,10 +885,11 @@ we_slept:
 		dquot = empty;
 		empty = NULL;
 		dquot->dq_id = id;
+		/* hash it first so it can be found */
+		insert_dquot_hash(dquot, blh);
+		hlist_bl_unlock(blh);
 		/* all dquots go on the inuse_list */
 		put_inuse(dquot);
-		/* hash it first so it can be found */
-		insert_dquot_hash(dquot);
 		spin_unlock(&dqopt->dq_list_lock);
 		spin_unlock(&dq_list_lock);
 		dqstats_inc(DQST_LOOKUPS);
@@ -895,6 +897,7 @@ we_slept:
 		if (!atomic_read(&dquot->dq_count))
 			remove_free_dquot(dquot);
 		atomic_inc(&dquot->dq_count);
+		hlist_bl_unlock(blh);
 		spin_unlock(&dqopt->dq_list_lock);
 		spin_unlock(&dq_list_lock);
 		dqstats_inc(DQST_CACHE_HITS);
@@ -2802,7 +2805,7 @@ static int __init dquot_init(void)
 			NULL);
 
 	order = 0;
-	dquot_hash = (struct hlist_head *)__get_free_pages(GFP_ATOMIC, order);
+	dquot_hash = (struct hlist_bl_head *)__get_free_pages(GFP_ATOMIC, order);
 	if (!dquot_hash)
 		panic("Cannot create dquot hash table");
 
@@ -2813,17 +2816,12 @@ static int __init dquot_init(void)
 	}
 
 	/* Find power-of-two hlist_heads which can fit into allocation */
-	nr_hash = (1UL << order) * PAGE_SIZE / sizeof(struct hlist_head);
-	dq_hash_bits = 0;
-	do {
-		dq_hash_bits++;
-	} while (nr_hash >> dq_hash_bits);
-	dq_hash_bits--;
-
+	nr_hash = (1UL << order) * PAGE_SIZE / sizeof(*dquot_hash);
+	dq_hash_bits = ilog2(nr_hash);
 	nr_hash = 1UL << dq_hash_bits;
 	dq_hash_mask = nr_hash - 1;
 	for (i = 0; i < nr_hash; i++)
-		INIT_HLIST_HEAD(dquot_hash + i);
+		INIT_HLIST_BL_HEAD(dquot_hash + i);
 
 	printk("Dquot-cache hash table entries: %ld (order %ld, %ld bytes)\n",
 			nr_hash, order, (PAGE_SIZE << order));
diff --git a/include/linux/list_bl.h b/include/linux/list_bl.h
index cf8acfc..bea750f 100644
--- a/include/linux/list_bl.h
+++ b/include/linux/list_bl.h
@@ -110,6 +110,16 @@ static inline void hlist_bl_del_init(struct hlist_bl_node *n)
 	}
 }
 
+static inline void hlist_bl_lock(struct hlist_bl_head *b)
+{
+	bit_spin_lock(0, (unsigned long *)b);
+}
+
+static inline void hlist_bl_unlock(struct hlist_bl_head *b)
+{
+	__bit_spin_unlock(0, (unsigned long *)b);
+}
+
 /**
  * hlist_bl_for_each_entry	- iterate over list of given type
  * @tpos:	the type * to use as a loop cursor.
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 4ca03aa..1661afa 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -170,6 +170,7 @@ enum {
 
 #ifdef __KERNEL__
 #include <linux/list.h>
+#include <linux/list_bl.h>
 #include <linux/mutex.h>
 #include <linux/rwsem.h>
 #include <linux/spinlock.h>
@@ -284,7 +285,7 @@ static inline void dqstats_dec(unsigned int type)
 				 * clear them when it sees fit. */
 
 struct dquot {
-	struct hlist_node dq_hash;	/* Hash list in memory */
+	struct hlist_bl_node dq_hash;	/* Hash list in memory */
 	struct list_head dq_inuse;	/* List of all quotas */
 	struct list_head dq_free;	/* Free list element */
 	struct list_head dq_dirty;	/* List of dirty dquots */
-- 
1.6.5.2

--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux