mirror of
https://github.com/ukui/kernel.git
synced 2026-03-09 10:07:04 -07:00
quota: Reduce contention on dq_data_lock
dq_data_lock is currently used to protect all modifications of quota accounting information, consistency of quota accounting on the inode, and dquot pointers from inode. As a result contention on the lock can be pretty heavy. Reduce the contention on the lock by protecting quota accounting information by a new dquot->dq_dqb_lock and consistency of quota accounting with inode usage by inode->i_lock. This change reduces time to create 500000 files on ext4 on ramdisk by 50 different processes in separate directories by 6% when user quota is turned on. When those 50 processes belong to 50 different users, the improvement is about 9%. Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
@@ -5194,7 +5194,7 @@ static int ext4_statfs_project(struct super_block *sb,
|
||||
dquot = dqget(sb, qid);
|
||||
if (IS_ERR(dquot))
|
||||
return PTR_ERR(dquot);
|
||||
spin_lock(&dq_data_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
|
||||
limit = (dquot->dq_dqb.dqb_bsoftlimit ?
|
||||
dquot->dq_dqb.dqb_bsoftlimit :
|
||||
@@ -5217,7 +5217,7 @@ static int ext4_statfs_project(struct super_block *sb,
|
||||
(buf->f_files - dquot->dq_dqb.dqb_curinodes) : 0;
|
||||
}
|
||||
|
||||
spin_unlock(&dq_data_lock);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
dqput(dquot);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -504,7 +504,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
|
||||
/* Update space and inode usage. Get also other information from
|
||||
* global quota file so that we don't overwrite any changes there.
|
||||
* We are */
|
||||
spin_lock(&dq_data_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
spacechange = dquot->dq_dqb.dqb_curspace -
|
||||
OCFS2_DQUOT(dquot)->dq_origspace;
|
||||
inodechange = dquot->dq_dqb.dqb_curinodes -
|
||||
@@ -560,7 +560,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
|
||||
__clear_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags);
|
||||
OCFS2_DQUOT(dquot)->dq_origspace = dquot->dq_dqb.dqb_curspace;
|
||||
OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes;
|
||||
spin_unlock(&dq_data_lock);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
err = ocfs2_qinfo_lock(info, freeing);
|
||||
if (err < 0) {
|
||||
mlog(ML_ERROR, "Failed to lock quota info, losing quota write"
|
||||
@@ -924,10 +924,10 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
|
||||
|
||||
/* In case user set some limits, sync dquot immediately to global
|
||||
* quota file so that information propagates quicker */
|
||||
spin_lock(&dq_data_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
if (dquot->dq_flags & mask)
|
||||
sync = 1;
|
||||
spin_unlock(&dq_data_lock);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
/* This is a slight hack but we can't afford getting global quota
|
||||
* lock if we already have a transaction started. */
|
||||
if (!sync || journal_current_handle()) {
|
||||
|
||||
@@ -521,7 +521,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
|
||||
goto out_drop_lock;
|
||||
}
|
||||
down_write(&sb_dqopt(sb)->dqio_sem);
|
||||
spin_lock(&dq_data_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
/* Add usage from quota entry into quota changes
|
||||
* of our node. Auxiliary variables are important
|
||||
* due to signedness */
|
||||
@@ -529,7 +529,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
|
||||
inodechange = le64_to_cpu(dqblk->dqb_inodemod);
|
||||
dquot->dq_dqb.dqb_curspace += spacechange;
|
||||
dquot->dq_dqb.dqb_curinodes += inodechange;
|
||||
spin_unlock(&dq_data_lock);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
/* We want to drop reference held by the crashed
|
||||
* node. Since we have our own reference we know
|
||||
* global structure actually won't be freed. */
|
||||
@@ -877,12 +877,12 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
|
||||
|
||||
dqblk->dqb_id = cpu_to_le64(from_kqid(&init_user_ns,
|
||||
od->dq_dquot.dq_id));
|
||||
spin_lock(&dq_data_lock);
|
||||
spin_lock(&od->dq_dquot.dq_dqb_lock);
|
||||
dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
|
||||
od->dq_origspace);
|
||||
dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes -
|
||||
od->dq_originodes);
|
||||
spin_unlock(&dq_data_lock);
|
||||
spin_unlock(&od->dq_dquot.dq_dqb_lock);
|
||||
trace_olq_set_dquot(
|
||||
(unsigned long long)le64_to_cpu(dqblk->dqb_spacemod),
|
||||
(unsigned long long)le64_to_cpu(dqblk->dqb_inodemod),
|
||||
|
||||
287
fs/quota/dquot.c
287
fs/quota/dquot.c
File diff suppressed because it is too large
Load Diff
@@ -389,9 +389,9 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
spin_lock(&dq_data_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
info->dqi_ops->mem2disk_dqblk(ddquot, dquot);
|
||||
spin_unlock(&dq_data_lock);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size,
|
||||
dquot->dq_off);
|
||||
if (ret != info->dqi_entry_size) {
|
||||
@@ -649,14 +649,14 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
|
||||
kfree(ddquot);
|
||||
goto out;
|
||||
}
|
||||
spin_lock(&dq_data_lock);
|
||||
spin_lock(&dquot->dq_dqb_lock);
|
||||
info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
|
||||
if (!dquot->dq_dqb.dqb_bhardlimit &&
|
||||
!dquot->dq_dqb.dqb_bsoftlimit &&
|
||||
!dquot->dq_dqb.dqb_ihardlimit &&
|
||||
!dquot->dq_dqb.dqb_isoftlimit)
|
||||
set_bit(DQ_FAKE_B, &dquot->dq_flags);
|
||||
spin_unlock(&dq_data_lock);
|
||||
spin_unlock(&dquot->dq_dqb_lock);
|
||||
kfree(ddquot);
|
||||
out:
|
||||
dqstats_inc(DQST_READS);
|
||||
|
||||
@@ -298,12 +298,13 @@ struct dquot {
|
||||
struct list_head dq_free; /* Free list element */
|
||||
struct list_head dq_dirty; /* List of dirty dquots */
|
||||
struct mutex dq_lock; /* dquot IO lock */
|
||||
spinlock_t dq_dqb_lock; /* Lock protecting dq_dqb changes */
|
||||
atomic_t dq_count; /* Use count */
|
||||
struct super_block *dq_sb; /* superblock this applies to */
|
||||
struct kqid dq_id; /* ID this applies to (uid, gid, projid) */
|
||||
loff_t dq_off; /* Offset of dquot on disk */
|
||||
unsigned long dq_flags; /* See DQ_* */
|
||||
struct mem_dqblk dq_dqb; /* Diskquota usage */
|
||||
struct mem_dqblk dq_dqb; /* Diskquota usage [dq_dqb_lock] */
|
||||
};
|
||||
|
||||
/* Operations which must be implemented by each quota format */
|
||||
|
||||
Reference in New Issue
Block a user