]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] Bigger quota hashtable
authorAndrew Morton <akpm@osdl.org>
Mon, 26 Apr 2004 15:56:11 +0000 (08:56 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Mon, 26 Apr 2004 15:56:11 +0000 (08:56 -0700)
From: Jan Kara <jack@ucw.cz>

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.

fs/dquot.c
include/linux/quota.h

index eabeb6d6d456a100a7137ee53adf5876812b6188..e3bd99508c3908b72d9d0ce8f1f27f83f1ec8659 100644 (file)
@@ -194,7 +194,8 @@ static void put_quota_format(struct quota_format_type *fmt)
 
 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 super_block *sb, unsigned int id, int type
  */
 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(struct super_block *sb, int type)
        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;
index 7d8c601ec2da3b8edadb5758ac4feaa45a546da0..7f43ae55e891941c4d483522bb8bcd6d7fc5c246 100644 (file)
@@ -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 */