From: Jan Kara I found out that quota uses hash table with just 43 entries to hash dquot entries. I guess that we can afford using one page for that (quotactl(Q_GETQUOTA...), got faster like 3x for 4000 users). Attached patch implements that. --- 25-akpm/fs/dquot.c | 47 +++++++++++++++++++++++++++++++----------- 25-akpm/include/linux/quota.h | 4 --- 2 files changed, 36 insertions(+), 15 deletions(-) diff -puN fs/dquot.c~bigger-quota-hashtable fs/dquot.c --- 25/fs/dquot.c~bigger-quota-hashtable Thu Apr 22 13:48:35 2004 +++ 25-akpm/fs/dquot.c Thu Apr 22 13:48:35 2004 @@ -194,7 +194,8 @@ static void put_quota_format(struct quot static LIST_HEAD(inuse_list); static LIST_HEAD(free_dquots); -static struct list_head dquot_hash[NR_DQHASH]; +unsigned int dq_hash_bits, dq_hash_mask; +static struct hlist_head *dquot_hash; struct dqstats dqstats; @@ -202,7 +203,8 @@ static void dqput(struct dquot *dquot); static inline int const hashfn(struct super_block *sb, unsigned int id, int type) { - return((((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH; + unsigned long tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type); + return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask; } /* @@ -210,22 +212,22 @@ static inline int const hashfn(struct su */ static inline void insert_dquot_hash(struct dquot *dquot) { - struct list_head *head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type); - list_add(&dquot->dq_hash, head); + struct hlist_head *head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type); + hlist_add_head(&dquot->dq_hash, head); } static inline void remove_dquot_hash(struct dquot *dquot) { - list_del_init(&dquot->dq_hash); + hlist_del_init(&dquot->dq_hash); } static inline struct dquot *find_dquot(unsigned int hashent, struct super_block *sb, unsigned int id, int type) { - struct list_head *head; + struct hlist_node *node; struct dquot *dquot; - for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) { - dquot = list_entry(head, struct dquot, dq_hash); + hlist_for_each (node, dquot_hash+hashent) { + dquot = hlist_entry(node, struct dquot, dq_hash); if (dquot->dq_sb == sb && dquot->dq_id == id && dquot->dq_type == type) return dquot; } @@ -541,7 +543,7 @@ static struct dquot *get_empty_dquot(str sema_init(&dquot->dq_lock, 1); INIT_LIST_HEAD(&dquot->dq_free); INIT_LIST_HEAD(&dquot->dq_inuse); - INIT_LIST_HEAD(&dquot->dq_hash); + INIT_HLIST_NODE(&dquot->dq_hash); dquot->dq_sb = sb; dquot->dq_type = type; atomic_set(&dquot->dq_count, 1); @@ -1695,18 +1697,39 @@ kmem_cache_t *dquot_cachep; static int __init dquot_init(void) { int i; + unsigned long nr_hash, order; - register_sysctl_table(sys_table, 0); - for (i = 0; i < NR_DQHASH; i++) - INIT_LIST_HEAD(dquot_hash + i); printk(KERN_NOTICE "VFS: Disk quotas %s\n", __DQUOT_VERSION__); + register_sysctl_table(sys_table, 0); + dquot_cachep = kmem_cache_create("dquot", sizeof(struct dquot), sizeof(unsigned long) * 4, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, NULL, NULL); if (!dquot_cachep) panic("Cannot create dquot SLAB cache"); + order = 0; + dquot_hash = (struct hlist_head *)__get_free_pages(GFP_ATOMIC, order); + if (!dquot_hash) + panic("Cannot create dquot hash table"); + + /* 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 << dq_hash_bits; + dq_hash_mask = nr_hash - 1; + for (i = 0; i < nr_hash; i++) + INIT_HLIST_HEAD(dquot_hash + i); + + printk("Dquot-cache hash table entries: %ld (order %ld, %ld bytes)\n", + nr_hash, order, (PAGE_SIZE << order)); + set_shrinker(DEFAULT_SEEKS, shrink_dqcache_memory); return 0; diff -puN include/linux/quota.h~bigger-quota-hashtable include/linux/quota.h --- 25/include/linux/quota.h~bigger-quota-hashtable Thu Apr 22 13:48:35 2004 +++ 25-akpm/include/linux/quota.h Thu Apr 22 13:48:35 2004 @@ -201,8 +201,6 @@ struct dqstats { extern struct dqstats dqstats; -#define NR_DQHASH 43 /* Just an arbitrary number */ - #define DQ_MOD_B 0 /* dquot modified since read */ #define DQ_BLKS_B 1 /* uid/gid has been warned about blk limit */ #define DQ_INODES_B 2 /* uid/gid has been warned about inode limit */ @@ -212,7 +210,7 @@ extern struct dqstats dqstats; #define DQ_WAITFREE_B 6 /* dquot being waited (by invalidate_dquots) */ struct dquot { - struct list_head dq_hash; /* Hash list in memory */ + struct hlist_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 semaphore dq_lock; /* dquot IO lock */ _