From: Dmitry Monakhov <dmonakhov@openvz.org>
To: linux-fsdevel@vger.kernel.org
Cc: jack@suse.cz, hch@infradead.org, Dmitry Monakhov <dmonakhov@gmail.com>
Subject: [PATCH 05/11] quota: make per-sb hash array
Date: Tue, 5 Oct 2010 22:20:21 +0400 [thread overview]
Message-ID: <1286302827-31043-6-git-send-email-dmonakhov@gmail.com> (raw)
In-Reply-To: <1286302827-31043-1-git-send-email-dmonakhov@gmail.com>
Currently quota_hash[] is global, which is bad for scalability.
Also is is the last user of global dq_list_lock.
It is reasonable to introduce didicated hash for each super_block
which use quota.
per-sb hash will be allocated only when necessery (on first quota_on())
Protected by per-sb dq_list_lock.
Signed-off-by: Dmitry Monakhov <dmonakhov@gmail.com>
---
fs/quota/dquot.c | 111 ++++++++++++++++++++++++++++++++----------------
include/linux/quota.h | 10 ++++-
2 files changed, 83 insertions(+), 38 deletions(-)
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index f2092d1..e93b323 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -220,7 +220,7 @@ static void put_quota_format(struct quota_format_type *fmt)
/*
* Dquot List Management:
* The quota code uses three lists for dquot management: the inuse_list,
- * free_dquots, and dquot_hash[] array. A single dquot structure may be
+ * free_dquots, and dq_hash[] array. A single dquot structure may be
* on all three lists, depending on its current state.
*
* All dquots are placed to the end of inuse_list when first created, and this
@@ -233,13 +233,10 @@ static void put_quota_format(struct quota_format_type *fmt)
* dquot is invalidated it's completely released from memory.
*
* Dquots with a specific identity (device, type and id) are placed on
- * one of the dquot_hash[] hash chains. The provides an efficient search
+ * one of the dq_hash[] hash chains. The provides an efficient search
* mechanism to locate a specific dquot.
*/
-static unsigned int dq_hash_bits, dq_hash_mask;
-static struct hlist_head *dquot_hash;
-
struct dqstats dqstats;
EXPORT_SYMBOL(dqstats);
@@ -251,8 +248,9 @@ hashfn(const struct super_block *sb, unsigned int id, int type)
{
unsigned long tmp;
- tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
- return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask;
+ tmp = id * (MAXQUOTAS - type);
+ return (tmp + (tmp >> sb->s_dquot.dq_hash.bits)) &
+ sb->s_dquot.dq_hash.mask;
}
/*
@@ -261,7 +259,8 @@ hashfn(const struct super_block *sb, unsigned int id, int type)
static inline void insert_dquot_hash(struct dquot *dquot)
{
struct hlist_head *head;
- head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
+ head = dq_opt(dquot)->dq_hash.head +
+ hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
hlist_add_head(&dquot->dq_hash, head);
}
@@ -270,13 +269,17 @@ static inline void remove_dquot_hash(struct dquot *dquot)
hlist_del_init(&dquot->dq_hash);
}
-static struct dquot *find_dquot(unsigned int hashent, struct super_block *sb,
+static struct dquot *find_dquot(struct super_block *sb,
unsigned int id, int type)
{
struct hlist_node *node;
struct dquot *dquot;
+ unsigned int hashent = hashfn(sb, id, type);
+
+ if (!sb_dqopt(sb)->dq_hash.head)
+ return NULL;
- hlist_for_each (node, dquot_hash+hashent) {
+ hlist_for_each(node, sb_dqopt(sb)->dq_hash.head + hashent) {
dquot = hlist_entry(node, struct dquot, dq_hash);
if (dquot->dq_sb == sb && dquot->dq_id == id &&
dquot->dq_type == type)
@@ -846,7 +849,6 @@ 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 dquot *dquot = NULL, *empty = NULL;
struct quota_info *dqopt = sb_dqopt(sb);
@@ -864,7 +866,7 @@ we_slept:
}
spin_unlock(&sb_dqopt(sb)->dq_state_lock);
- dquot = find_dquot(hashent, sb, id, type);
+ dquot = find_dquot(sb, id, type);
if (!dquot) {
if (!empty) {
spin_unlock(&dqopt->dq_list_lock);
@@ -1925,6 +1927,47 @@ int dquot_file_open(struct inode *inode, struct file *file)
}
EXPORT_SYMBOL(dquot_file_open);
+int dquot_hash_alloc(struct super_block *sb, int order)
+{
+ unsigned long nr_hash, i;
+ struct hlist_head *hash_array;
+ struct dquot_hash *dq_hash = &sb_dqopt(sb)->dq_hash;
+
+ hash_array = (struct hlist_head *)__get_free_pages(GFP_KERNEL, order);
+ if (!hash_array)
+ return -ENOMEM;
+
+ /* 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(hash_array + i);
+ dq_hash->order = order;
+ dq_hash->head = hash_array;
+ return 0;
+}
+
+void dquot_hash_destroy(struct super_block *sb)
+{
+
+ struct dquot_hash * dq_hash = &sb_dqopt(sb)->dq_hash;
+ unsigned long i, nr_hash = 1UL << dq_hash->bits;
+ unsigned long addr = (unsigned long )dq_hash->head;
+
+ for (i = 0; i < nr_hash; i++)
+ BUG_ON(!hlist_empty(dq_hash->head + i));
+
+ dq_hash->head = NULL;
+ free_pages(addr, dq_hash->order);
+}
+
/*
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
*/
@@ -2006,6 +2049,9 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
dqopt->info[cnt].dqi_bgrace = 0;
dqopt->ops[cnt] = NULL;
}
+ if (!sb_any_quota_loaded(sb))
+ /* All quotas was unloaded, hash no longer needed */
+ dquot_hash_destroy(sb);
mutex_unlock(&dqopt->dqonoff_mutex);
/* Skip syncing and setting flags if quota files are hidden */
@@ -2081,7 +2127,7 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
struct quota_format_type *fmt = find_quota_format(format_id);
struct super_block *sb = inode->i_sb;
struct quota_info *dqopt = sb_dqopt(sb);
- int error;
+ int error, hash_alloc;
int oldflags = -1;
if (!fmt)
@@ -2115,6 +2161,16 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
invalidate_bdev(sb->s_bdev);
}
mutex_lock(&dqopt->dqonoff_mutex);
+
+ hash_alloc = !sb_any_quota_loaded(sb);
+ if (hash_alloc) {
+ error = dquot_hash_alloc(sb, 0);
+ if (error) {
+ hash_alloc = 0;
+ goto out_lock;
+ }
+ }
+
if (sb_has_quota_loaded(sb, type)) {
error = -EBUSY;
goto out_lock;
@@ -2168,6 +2224,8 @@ out_file_init:
dqopt->files[type] = NULL;
iput(inode);
out_lock:
+ if (hash_alloc)
+ dquot_hash_destroy(sb);
if (oldflags != -1) {
mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
/* Set the flags back (in the case of accidental quotaon()
@@ -2224,8 +2282,10 @@ int dquot_quota_on(struct super_block *sb, int type, int format_id,
struct path *path)
{
int error = security_quota_on(path->dentry);
+
if (error)
return error;
+
/* Quota file not on the same filesystem? */
if (path->mnt->mnt_sb != sb)
error = -EXDEV;
@@ -2643,8 +2703,7 @@ static ctl_table sys_table[] = {
static int __init dquot_init(void)
{
int i, ret;
- unsigned long nr_hash, order;
-
+
printk(KERN_NOTICE "VFS: Disk quotas %s\n", __DQUOT_VERSION__);
register_sysctl_table(sys_table);
@@ -2655,33 +2714,11 @@ static int __init dquot_init(void)
SLAB_MEM_SPREAD|SLAB_PANIC),
NULL);
- order = 0;
- dquot_hash = (struct hlist_head *)__get_free_pages(GFP_ATOMIC, order);
- if (!dquot_hash)
- panic("Cannot create dquot hash table");
-
for (i = 0; i < _DQST_DQSTAT_LAST; i++) {
ret = percpu_counter_init(&dqstats.counter[i], 0);
if (ret)
panic("Cannot create dquot stat counters");
}
-
- /* 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));
-
register_shrinker(&dqcache_shrinker);
return 0;
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 134c18d..eb083fc 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -393,15 +393,23 @@ static inline void quota_send_warning(short type, unsigned int id, dev_t dev,
}
#endif /* CONFIG_QUOTA_NETLINK_INTERFACE */
+struct dquot_hash {
+ struct hlist_head *head;
+ unsigned int order;
+ unsigned int bits;
+ unsigned int mask;
+};
+
struct quota_info {
unsigned int flags; /* Flags for diskquotas on this device */
struct mutex dqio_mutex; /* lock device while I/O in progress */
struct mutex dqonoff_mutex; /* Serialize quotaon & quotaoff */
struct rw_semaphore dqptr_sem; /* serialize ops using quota_info struct, pointers from inode to dquots */
spinlock_t dq_state_lock; /* serialize quota state changes*/
- spinlock_t dq_list_lock; /* protect lists */
+ spinlock_t dq_list_lock; /* protect lists and hash */
struct list_head dq_inuse_list; /* list of inused dquotas */
struct list_head dq_free_list; /* list of free dquotas */
+ struct dquot_hash dq_hash; /* dquot lookup hash */
struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */
struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */
--
1.6.6.1
next prev parent reply other threads:[~2010-10-05 18:21 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-10-05 18:20 (unknown), Dmitry Monakhov
2010-10-05 18:20 ` [PATCH 01/11] quota: add wrapper function Dmitry Monakhov
2010-10-06 8:56 ` Christoph Hellwig
2010-10-06 10:01 ` Jan Kara
2010-10-05 18:20 ` [PATCH 02/11] quota: Convert dq_state_lock to per-sb dq_state_lock Dmitry Monakhov
2010-10-06 10:04 ` Jan Kara
2010-10-05 18:20 ` [PATCH 03/11] quota: add quota format lock Dmitry Monakhov
2010-10-06 10:05 ` Jan Kara
2010-10-05 18:20 ` [PATCH 04/11] quota: make dquot lists per-sb Dmitry Monakhov
2010-10-06 8:57 ` Christoph Hellwig
2010-10-06 9:39 ` Dmitry
2010-10-06 10:22 ` Jan Kara
2010-10-06 10:40 ` Dmitry
2010-10-06 10:54 ` Jan Kara
2010-10-05 18:20 ` Dmitry Monakhov [this message]
2010-10-06 10:38 ` [PATCH 05/11] quota: make per-sb hash array Jan Kara
2010-10-05 18:20 ` [PATCH 06/11] quota: remove global dq_list_lock Dmitry Monakhov
2010-10-05 18:20 ` [PATCH 07/11] quota: rename dq_lock Dmitry Monakhov
2010-10-05 18:20 ` [PATCH 08/11] quota: make per-sb dq_data_lock Dmitry Monakhov
2010-10-06 11:01 ` Jan Kara
2010-10-05 18:20 ` [PATCH 09/11] quota: protect dquot mem info with objects's lock Dmitry Monakhov
2010-10-06 12:37 ` Jan Kara
2010-10-06 13:17 ` Dmitry
2010-10-06 13:41 ` Jan Kara
2010-10-06 14:19 ` Dmitry
2010-10-06 13:30 ` Jan Kara
2010-10-06 13:41 ` Dmitry
2010-10-05 18:20 ` [PATCH 10/11] quota: drop dq_data_lock where possible Dmitry Monakhov
2010-10-05 18:20 ` [PATCH 11/11] quota: relax dq_data_lock dq_lock locking consistency Dmitry Monakhov
2010-10-06 11:56 ` Jan Kara
2010-10-06 7:08 ` [PATCH 0/11] RFC quota scalability V1 Dmitry
2010-10-06 9:44 ` Jan Kara
2010-10-06 10:15 ` Dmitry
2010-10-06 10:47 ` Jan Kara
2010-10-10 3:50 ` Brad Boyer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1286302827-31043-6-git-send-email-dmonakhov@gmail.com \
--to=dmonakhov@openvz.org \
--cc=dmonakhov@gmail.com \
--cc=hch@infradead.org \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).