Commit 7b9ca4c6 authored by Jan Kara's avatar Jan Kara

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: default avatarJan Kara <jack@suse.cz>
parent f4a8116a
...@@ -5194,7 +5194,7 @@ static int ext4_statfs_project(struct super_block *sb, ...@@ -5194,7 +5194,7 @@ static int ext4_statfs_project(struct super_block *sb,
dquot = dqget(sb, qid); dquot = dqget(sb, qid);
if (IS_ERR(dquot)) if (IS_ERR(dquot))
return PTR_ERR(dquot); return PTR_ERR(dquot);
spin_lock(&dq_data_lock); spin_lock(&dquot->dq_dqb_lock);
limit = (dquot->dq_dqb.dqb_bsoftlimit ? limit = (dquot->dq_dqb.dqb_bsoftlimit ?
dquot->dq_dqb.dqb_bsoftlimit : dquot->dq_dqb.dqb_bsoftlimit :
...@@ -5217,7 +5217,7 @@ static int ext4_statfs_project(struct super_block *sb, ...@@ -5217,7 +5217,7 @@ static int ext4_statfs_project(struct super_block *sb,
(buf->f_files - dquot->dq_dqb.dqb_curinodes) : 0; (buf->f_files - dquot->dq_dqb.dqb_curinodes) : 0;
} }
spin_unlock(&dq_data_lock); spin_unlock(&dquot->dq_dqb_lock);
dqput(dquot); dqput(dquot);
return 0; return 0;
} }
......
...@@ -504,7 +504,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing) ...@@ -504,7 +504,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
/* Update space and inode usage. Get also other information from /* Update space and inode usage. Get also other information from
* global quota file so that we don't overwrite any changes there. * global quota file so that we don't overwrite any changes there.
* We are */ * We are */
spin_lock(&dq_data_lock); spin_lock(&dquot->dq_dqb_lock);
spacechange = dquot->dq_dqb.dqb_curspace - spacechange = dquot->dq_dqb.dqb_curspace -
OCFS2_DQUOT(dquot)->dq_origspace; OCFS2_DQUOT(dquot)->dq_origspace;
inodechange = dquot->dq_dqb.dqb_curinodes - inodechange = dquot->dq_dqb.dqb_curinodes -
...@@ -560,7 +560,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing) ...@@ -560,7 +560,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
__clear_bit(DQ_LASTSET_B + QIF_ITIME_B, &dquot->dq_flags); __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_origspace = dquot->dq_dqb.dqb_curspace;
OCFS2_DQUOT(dquot)->dq_originodes = dquot->dq_dqb.dqb_curinodes; 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); err = ocfs2_qinfo_lock(info, freeing);
if (err < 0) { if (err < 0) {
mlog(ML_ERROR, "Failed to lock quota info, losing quota write" mlog(ML_ERROR, "Failed to lock quota info, losing quota write"
...@@ -924,10 +924,10 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot) ...@@ -924,10 +924,10 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
/* In case user set some limits, sync dquot immediately to global /* In case user set some limits, sync dquot immediately to global
* quota file so that information propagates quicker */ * quota file so that information propagates quicker */
spin_lock(&dq_data_lock); spin_lock(&dquot->dq_dqb_lock);
if (dquot->dq_flags & mask) if (dquot->dq_flags & mask)
sync = 1; 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 /* This is a slight hack but we can't afford getting global quota
* lock if we already have a transaction started. */ * lock if we already have a transaction started. */
if (!sync || journal_current_handle()) { if (!sync || journal_current_handle()) {
......
...@@ -521,7 +521,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode, ...@@ -521,7 +521,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
goto out_drop_lock; goto out_drop_lock;
} }
down_write(&sb_dqopt(sb)->dqio_sem); 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 /* Add usage from quota entry into quota changes
* of our node. Auxiliary variables are important * of our node. Auxiliary variables are important
* due to signedness */ * due to signedness */
...@@ -529,7 +529,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode, ...@@ -529,7 +529,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
inodechange = le64_to_cpu(dqblk->dqb_inodemod); inodechange = le64_to_cpu(dqblk->dqb_inodemod);
dquot->dq_dqb.dqb_curspace += spacechange; dquot->dq_dqb.dqb_curspace += spacechange;
dquot->dq_dqb.dqb_curinodes += inodechange; 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 /* We want to drop reference held by the crashed
* node. Since we have our own reference we know * node. Since we have our own reference we know
* global structure actually won't be freed. */ * global structure actually won't be freed. */
...@@ -877,12 +877,12 @@ static void olq_set_dquot(struct buffer_head *bh, void *private) ...@@ -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, dqblk->dqb_id = cpu_to_le64(from_kqid(&init_user_ns,
od->dq_dquot.dq_id)); 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 - dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
od->dq_origspace); od->dq_origspace);
dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes - dqblk->dqb_inodemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curinodes -
od->dq_originodes); od->dq_originodes);
spin_unlock(&dq_data_lock); spin_unlock(&od->dq_dquot.dq_dqb_lock);
trace_olq_set_dquot( trace_olq_set_dquot(
(unsigned long long)le64_to_cpu(dqblk->dqb_spacemod), (unsigned long long)le64_to_cpu(dqblk->dqb_spacemod),
(unsigned long long)le64_to_cpu(dqblk->dqb_inodemod), (unsigned long long)le64_to_cpu(dqblk->dqb_inodemod),
......
This diff is collapsed.
...@@ -389,9 +389,9 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) ...@@ -389,9 +389,9 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
return ret; return ret;
} }
} }
spin_lock(&dq_data_lock); spin_lock(&dquot->dq_dqb_lock);
info->dqi_ops->mem2disk_dqblk(ddquot, dquot); 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, ret = sb->s_op->quota_write(sb, type, ddquot, info->dqi_entry_size,
dquot->dq_off); dquot->dq_off);
if (ret != info->dqi_entry_size) { if (ret != info->dqi_entry_size) {
...@@ -649,14 +649,14 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) ...@@ -649,14 +649,14 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
kfree(ddquot); kfree(ddquot);
goto out; goto out;
} }
spin_lock(&dq_data_lock); spin_lock(&dquot->dq_dqb_lock);
info->dqi_ops->disk2mem_dqblk(dquot, ddquot); info->dqi_ops->disk2mem_dqblk(dquot, ddquot);
if (!dquot->dq_dqb.dqb_bhardlimit && if (!dquot->dq_dqb.dqb_bhardlimit &&
!dquot->dq_dqb.dqb_bsoftlimit && !dquot->dq_dqb.dqb_bsoftlimit &&
!dquot->dq_dqb.dqb_ihardlimit && !dquot->dq_dqb.dqb_ihardlimit &&
!dquot->dq_dqb.dqb_isoftlimit) !dquot->dq_dqb.dqb_isoftlimit)
set_bit(DQ_FAKE_B, &dquot->dq_flags); set_bit(DQ_FAKE_B, &dquot->dq_flags);
spin_unlock(&dq_data_lock); spin_unlock(&dquot->dq_dqb_lock);
kfree(ddquot); kfree(ddquot);
out: out:
dqstats_inc(DQST_READS); dqstats_inc(DQST_READS);
......
...@@ -298,12 +298,13 @@ struct dquot { ...@@ -298,12 +298,13 @@ struct dquot {
struct list_head dq_free; /* Free list element */ struct list_head dq_free; /* Free list element */
struct list_head dq_dirty; /* List of dirty dquots */ struct list_head dq_dirty; /* List of dirty dquots */
struct mutex dq_lock; /* dquot IO lock */ struct mutex dq_lock; /* dquot IO lock */
spinlock_t dq_dqb_lock; /* Lock protecting dq_dqb changes */
atomic_t dq_count; /* Use count */ atomic_t dq_count; /* Use count */
struct super_block *dq_sb; /* superblock this applies to */ struct super_block *dq_sb; /* superblock this applies to */
struct kqid dq_id; /* ID this applies to (uid, gid, projid) */ struct kqid dq_id; /* ID this applies to (uid, gid, projid) */
loff_t dq_off; /* Offset of dquot on disk */ loff_t dq_off; /* Offset of dquot on disk */
unsigned long dq_flags; /* See DQ_* */ 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 */ /* Operations which must be implemented by each quota format */
......
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