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