Commit 2187a5e4 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Bigger quota hashtable

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.
parent fa24b7f2
...@@ -194,7 +194,8 @@ static void put_quota_format(struct quota_format_type *fmt) ...@@ -194,7 +194,8 @@ static void put_quota_format(struct quota_format_type *fmt)
static LIST_HEAD(inuse_list); static LIST_HEAD(inuse_list);
static LIST_HEAD(free_dquots); 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; struct dqstats dqstats;
...@@ -202,7 +203,8 @@ static void dqput(struct dquot *dquot); ...@@ -202,7 +203,8 @@ static void dqput(struct dquot *dquot);
static inline int const hashfn(struct super_block *sb, unsigned int id, int type) 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 ...@@ -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) 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); struct hlist_head *head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
list_add(&dquot->dq_hash, head); hlist_add_head(&dquot->dq_hash, head);
} }
static inline void remove_dquot_hash(struct dquot *dquot) 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) 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; struct dquot *dquot;
for (head = dquot_hash[hashent].next; head != dquot_hash+hashent; head = head->next) { hlist_for_each (node, dquot_hash+hashent) {
dquot = list_entry(head, struct dquot, dq_hash); dquot = hlist_entry(node, struct dquot, dq_hash);
if (dquot->dq_sb == sb && dquot->dq_id == id && dquot->dq_type == type) if (dquot->dq_sb == sb && dquot->dq_id == id && dquot->dq_type == type)
return dquot; return dquot;
} }
...@@ -541,7 +543,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type) ...@@ -541,7 +543,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
sema_init(&dquot->dq_lock, 1); sema_init(&dquot->dq_lock, 1);
INIT_LIST_HEAD(&dquot->dq_free); INIT_LIST_HEAD(&dquot->dq_free);
INIT_LIST_HEAD(&dquot->dq_inuse); INIT_LIST_HEAD(&dquot->dq_inuse);
INIT_LIST_HEAD(&dquot->dq_hash); INIT_HLIST_NODE(&dquot->dq_hash);
dquot->dq_sb = sb; dquot->dq_sb = sb;
dquot->dq_type = type; dquot->dq_type = type;
atomic_set(&dquot->dq_count, 1); atomic_set(&dquot->dq_count, 1);
...@@ -1695,18 +1697,39 @@ kmem_cache_t *dquot_cachep; ...@@ -1695,18 +1697,39 @@ kmem_cache_t *dquot_cachep;
static int __init dquot_init(void) static int __init dquot_init(void)
{ {
int i; 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__); printk(KERN_NOTICE "VFS: Disk quotas %s\n", __DQUOT_VERSION__);
register_sysctl_table(sys_table, 0);
dquot_cachep = kmem_cache_create("dquot", dquot_cachep = kmem_cache_create("dquot",
sizeof(struct dquot), sizeof(unsigned long) * 4, sizeof(struct dquot), sizeof(unsigned long) * 4,
SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, NULL, NULL); SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, NULL, NULL);
if (!dquot_cachep) if (!dquot_cachep)
panic("Cannot create dquot SLAB cache"); 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); set_shrinker(DEFAULT_SEEKS, shrink_dqcache_memory);
return 0; return 0;
......
...@@ -201,8 +201,6 @@ struct dqstats { ...@@ -201,8 +201,6 @@ struct dqstats {
extern struct dqstats dqstats; extern struct dqstats dqstats;
#define NR_DQHASH 43 /* Just an arbitrary number */
#define DQ_MOD_B 0 /* dquot modified since read */ #define DQ_MOD_B 0 /* dquot modified since read */
#define DQ_BLKS_B 1 /* uid/gid has been warned about blk limit */ #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 */ #define DQ_INODES_B 2 /* uid/gid has been warned about inode limit */
...@@ -212,7 +210,7 @@ extern struct dqstats dqstats; ...@@ -212,7 +210,7 @@ extern struct dqstats dqstats;
#define DQ_WAITFREE_B 6 /* dquot being waited (by invalidate_dquots) */ #define DQ_WAITFREE_B 6 /* dquot being waited (by invalidate_dquots) */
struct dquot { 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_inuse; /* List of all quotas */
struct list_head dq_free; /* Free list element */ struct list_head dq_free; /* Free list element */
struct semaphore dq_lock; /* dquot IO lock */ struct semaphore dq_lock; /* dquot IO lock */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment