Commit ae8ac6b7 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'quota_scaling' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull quota scaling updates from Jan Kara:
 "This contains changes to make the quota subsystem more scalable.

  Reportedly it improves number of files created per second on ext4
  filesystem on fast storage by about a factor of 2x"

* 'quota_scaling' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: (28 commits)
  quota: Add lock annotations to struct members
  quota: Reduce contention on dq_data_lock
  fs: Provide __inode_get_bytes()
  quota: Inline dquot_[re]claim_reserved_space() into callsite
  quota: Inline inode_{incr,decr}_space() into callsites
  quota: Inline functions into their callsites
  ext4: Disable dirty list tracking of dquots when journalling quotas
  quota: Allow disabling tracking of dirty dquots in a list
  quota: Remove dq_wait_unused from dquot
  quota: Move locking into clear_dquot_dirty()
  quota: Do not dirty bad dquots
  quota: Fix possible corruption of dqi_flags
  quota: Propagate ->quota_read errors from v2_read_file_info()
  quota: Fix error codes in v2_read_file_info()
  quota: Push dqio_sem down to ->read_file_info()
  quota: Push dqio_sem down to ->write_file_info()
  quota: Push dqio_sem down to ->get_next_id()
  quota: Push dqio_sem down to ->release_dqblk()
  quota: Remove locking for writing to the old quota format
  quota: Do not acquire dqio_sem for dquot overwrites in v2 format
  ...
parents 460352c2 6c83fd51
...@@ -5215,7 +5215,7 @@ static int ext4_statfs_project(struct super_block *sb, ...@@ -5215,7 +5215,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 :
...@@ -5238,7 +5238,7 @@ static int ext4_statfs_project(struct super_block *sb, ...@@ -5238,7 +5238,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;
} }
...@@ -5284,18 +5284,13 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -5284,18 +5284,13 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0; return 0;
} }
/* Helper function for writing quotas on sync - we need to start transaction
* before quota file is locked for write. Otherwise the are possible deadlocks:
* Process 1 Process 2
* ext4_create() quota_sync()
* jbd2_journal_start() write_dquot()
* dquot_initialize() down(dqio_mutex)
* down(dqio_mutex) jbd2_journal_start()
*
*/
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
/*
* Helper functions so that transaction is started before we acquire dqio_sem
* to keep correct lock ordering of transaction > dqio_sem
*/
static inline struct inode *dquot_to_inode(struct dquot *dquot) static inline struct inode *dquot_to_inode(struct dquot *dquot)
{ {
return sb_dqopt(dquot->dq_sb)->files[dquot->dq_id.type]; return sb_dqopt(dquot->dq_sb)->files[dquot->dq_id.type];
...@@ -5430,6 +5425,13 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, ...@@ -5430,6 +5425,13 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
ext4_msg(sb, KERN_WARNING, ext4_msg(sb, KERN_WARNING,
"Quota file not on filesystem root. " "Quota file not on filesystem root. "
"Journaled quota will not work"); "Journaled quota will not work");
sb_dqopt(sb)->flags |= DQUOT_NOLIST_DIRTY;
} else {
/*
* Clear the flag just in case mount options changed since
* last time.
*/
sb_dqopt(sb)->flags &= ~DQUOT_NOLIST_DIRTY;
} }
/* /*
...@@ -5526,7 +5528,7 @@ static int ext4_enable_quotas(struct super_block *sb) ...@@ -5526,7 +5528,7 @@ static int ext4_enable_quotas(struct super_block *sb)
test_opt(sb, PRJQUOTA), test_opt(sb, PRJQUOTA),
}; };
sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE; sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE | DQUOT_NOLIST_DIRTY;
for (type = 0; type < EXT4_MAXQUOTAS; type++) { for (type = 0; type < EXT4_MAXQUOTAS; type++) {
if (qf_inums[type]) { if (qf_inums[type]) {
err = ext4_quota_enable(sb, type, QFMT_VFS_V1, err = ext4_quota_enable(sb, type, QFMT_VFS_V1,
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
* Locking of quotas with OCFS2 is rather complex. Here are rules that * Locking of quotas with OCFS2 is rather complex. Here are rules that
* should be obeyed by all the functions: * should be obeyed by all the functions:
* - any write of quota structure (either to local or global file) is protected * - any write of quota structure (either to local or global file) is protected
* by dqio_mutex or dquot->dq_lock. * by dqio_sem or dquot->dq_lock.
* - any modification of global quota file holds inode cluster lock, i_mutex, * - any modification of global quota file holds inode cluster lock, i_mutex,
* and ip_alloc_sem of the global quota file (achieved by * and ip_alloc_sem of the global quota file (achieved by
* ocfs2_lock_global_qf). It also has to hold qinfo_lock. * ocfs2_lock_global_qf). It also has to hold qinfo_lock.
...@@ -42,9 +42,9 @@ ...@@ -42,9 +42,9 @@
* *
* A rough sketch of locking dependencies (lf = local file, gf = global file): * A rough sketch of locking dependencies (lf = local file, gf = global file):
* Normal filesystem operation: * Normal filesystem operation:
* start_trans -> dqio_mutex -> write to lf * start_trans -> dqio_sem -> write to lf
* Syncing of local and global file: * Syncing of local and global file:
* ocfs2_lock_global_qf -> start_trans -> dqio_mutex -> qinfo_lock -> * ocfs2_lock_global_qf -> start_trans -> dqio_sem -> qinfo_lock ->
* write to gf * write to gf
* -> write to lf * -> write to lf
* Acquire dquot for the first time: * Acquire dquot for the first time:
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
* Recovery: * Recovery:
* inode cluster lock of recovered lf * inode cluster lock of recovered lf
* -> read bitmaps -> ip_alloc_sem of lf * -> read bitmaps -> ip_alloc_sem of lf
* -> ocfs2_lock_global_qf -> start_trans -> dqio_mutex -> qinfo_lock -> * -> ocfs2_lock_global_qf -> start_trans -> dqio_sem -> qinfo_lock ->
* write to gf * write to gf
*/ */
...@@ -443,13 +443,17 @@ static int __ocfs2_global_write_info(struct super_block *sb, int type) ...@@ -443,13 +443,17 @@ static int __ocfs2_global_write_info(struct super_block *sb, int type)
int ocfs2_global_write_info(struct super_block *sb, int type) int ocfs2_global_write_info(struct super_block *sb, int type)
{ {
int err; int err;
struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv; struct quota_info *dqopt = sb_dqopt(sb);
struct ocfs2_mem_dqinfo *info = dqopt->info[type].dqi_priv;
down_write(&dqopt->dqio_sem);
err = ocfs2_qinfo_lock(info, 1); err = ocfs2_qinfo_lock(info, 1);
if (err < 0) if (err < 0)
return err; goto out_sem;
err = __ocfs2_global_write_info(sb, type); err = __ocfs2_global_write_info(sb, type);
ocfs2_qinfo_unlock(info, 1); ocfs2_qinfo_unlock(info, 1);
out_sem:
up_write(&dqopt->dqio_sem);
return err; return err;
} }
...@@ -500,7 +504,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing) ...@@ -500,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 -
...@@ -556,7 +560,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing) ...@@ -556,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"
...@@ -611,7 +615,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type) ...@@ -611,7 +615,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
mlog_errno(status); mlog_errno(status);
goto out_ilock; goto out_ilock;
} }
mutex_lock(&sb_dqopt(sb)->dqio_mutex); down_write(&sb_dqopt(sb)->dqio_sem);
status = ocfs2_sync_dquot(dquot); status = ocfs2_sync_dquot(dquot);
if (status < 0) if (status < 0)
mlog_errno(status); mlog_errno(status);
...@@ -619,7 +623,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type) ...@@ -619,7 +623,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
status = ocfs2_local_write_dquot(dquot); status = ocfs2_local_write_dquot(dquot);
if (status < 0) if (status < 0)
mlog_errno(status); mlog_errno(status);
mutex_unlock(&sb_dqopt(sb)->dqio_mutex); up_write(&sb_dqopt(sb)->dqio_sem);
ocfs2_commit_trans(osb, handle); ocfs2_commit_trans(osb, handle);
out_ilock: out_ilock:
ocfs2_unlock_global_qf(oinfo, 1); ocfs2_unlock_global_qf(oinfo, 1);
...@@ -666,9 +670,9 @@ static int ocfs2_write_dquot(struct dquot *dquot) ...@@ -666,9 +670,9 @@ static int ocfs2_write_dquot(struct dquot *dquot)
mlog_errno(status); mlog_errno(status);
goto out; goto out;
} }
mutex_lock(&sb_dqopt(dquot->dq_sb)->dqio_mutex); down_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
status = ocfs2_local_write_dquot(dquot); status = ocfs2_local_write_dquot(dquot);
mutex_unlock(&sb_dqopt(dquot->dq_sb)->dqio_mutex); up_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
ocfs2_commit_trans(osb, handle); ocfs2_commit_trans(osb, handle);
out: out:
return status; return status;
...@@ -920,10 +924,10 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot) ...@@ -920,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()) {
...@@ -939,7 +943,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot) ...@@ -939,7 +943,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
mlog_errno(status); mlog_errno(status);
goto out_ilock; goto out_ilock;
} }
mutex_lock(&sb_dqopt(sb)->dqio_mutex); down_write(&sb_dqopt(sb)->dqio_sem);
status = ocfs2_sync_dquot(dquot); status = ocfs2_sync_dquot(dquot);
if (status < 0) { if (status < 0) {
mlog_errno(status); mlog_errno(status);
...@@ -948,7 +952,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot) ...@@ -948,7 +952,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
/* Now write updated local dquot structure */ /* Now write updated local dquot structure */
status = ocfs2_local_write_dquot(dquot); status = ocfs2_local_write_dquot(dquot);
out_dlock: out_dlock:
mutex_unlock(&sb_dqopt(sb)->dqio_mutex); up_write(&sb_dqopt(sb)->dqio_sem);
ocfs2_commit_trans(osb, handle); ocfs2_commit_trans(osb, handle);
out_ilock: out_ilock:
ocfs2_unlock_global_qf(oinfo, 1); ocfs2_unlock_global_qf(oinfo, 1);
......
...@@ -520,8 +520,8 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode, ...@@ -520,8 +520,8 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
mlog_errno(status); mlog_errno(status);
goto out_drop_lock; goto out_drop_lock;
} }
mutex_lock(&sb_dqopt(sb)->dqio_mutex); 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. */
...@@ -553,7 +553,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode, ...@@ -553,7 +553,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
unlock_buffer(qbh); unlock_buffer(qbh);
ocfs2_journal_dirty(handle, qbh); ocfs2_journal_dirty(handle, qbh);
out_commit: out_commit:
mutex_unlock(&sb_dqopt(sb)->dqio_mutex); up_write(&sb_dqopt(sb)->dqio_sem);
ocfs2_commit_trans(OCFS2_SB(sb), handle); ocfs2_commit_trans(OCFS2_SB(sb), handle);
out_drop_lock: out_drop_lock:
ocfs2_unlock_global_qf(oinfo, 1); ocfs2_unlock_global_qf(oinfo, 1);
...@@ -691,9 +691,6 @@ static int ocfs2_local_read_info(struct super_block *sb, int type) ...@@ -691,9 +691,6 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
struct ocfs2_quota_recovery *rec; struct ocfs2_quota_recovery *rec;
int locked = 0; int locked = 0;
/* We don't need the lock and we have to acquire quota file locks
* which will later depend on this lock */
mutex_unlock(&sb_dqopt(sb)->dqio_mutex);
info->dqi_max_spc_limit = 0x7fffffffffffffffLL; info->dqi_max_spc_limit = 0x7fffffffffffffffLL;
info->dqi_max_ino_limit = 0x7fffffffffffffffLL; info->dqi_max_ino_limit = 0x7fffffffffffffffLL;
oinfo = kmalloc(sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS); oinfo = kmalloc(sizeof(struct ocfs2_mem_dqinfo), GFP_NOFS);
...@@ -772,7 +769,6 @@ static int ocfs2_local_read_info(struct super_block *sb, int type) ...@@ -772,7 +769,6 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
goto out_err; goto out_err;
} }
mutex_lock(&sb_dqopt(sb)->dqio_mutex);
return 0; return 0;
out_err: out_err:
if (oinfo) { if (oinfo) {
...@@ -786,7 +782,6 @@ static int ocfs2_local_read_info(struct super_block *sb, int type) ...@@ -786,7 +782,6 @@ static int ocfs2_local_read_info(struct super_block *sb, int type)
kfree(oinfo); kfree(oinfo);
} }
brelse(bh); brelse(bh);
mutex_lock(&sb_dqopt(sb)->dqio_mutex);
return -1; return -1;
} }
...@@ -882,12 +877,12 @@ static void olq_set_dquot(struct buffer_head *bh, void *private) ...@@ -882,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.
...@@ -379,7 +379,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) ...@@ -379,7 +379,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
if (!ddquot) if (!ddquot)
return -ENOMEM; return -ENOMEM;
/* dq_off is guarded by dqio_mutex */ /* dq_off is guarded by dqio_sem */
if (!dquot->dq_off) { if (!dquot->dq_off) {
ret = dq_insert_tree(info, dquot); ret = dq_insert_tree(info, dquot);
if (ret < 0) { if (ret < 0) {
...@@ -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);
......
...@@ -56,8 +56,9 @@ static int v1_read_dqblk(struct dquot *dquot) ...@@ -56,8 +56,9 @@ static int v1_read_dqblk(struct dquot *dquot)
{ {
int type = dquot->dq_id.type; int type = dquot->dq_id.type;
struct v1_disk_dqblk dqblk; struct v1_disk_dqblk dqblk;
struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
if (!sb_dqopt(dquot->dq_sb)->files[type]) if (!dqopt->files[type])
return -EINVAL; return -EINVAL;
/* Set structure to 0s in case read fails/is after end of file */ /* Set structure to 0s in case read fails/is after end of file */
...@@ -160,6 +161,7 @@ static int v1_read_file_info(struct super_block *sb, int type) ...@@ -160,6 +161,7 @@ static int v1_read_file_info(struct super_block *sb, int type)
struct v1_disk_dqblk dqblk; struct v1_disk_dqblk dqblk;
int ret; int ret;
down_read(&dqopt->dqio_sem);
ret = sb->s_op->quota_read(sb, type, (char *)&dqblk, ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
sizeof(struct v1_disk_dqblk), v1_dqoff(0)); sizeof(struct v1_disk_dqblk), v1_dqoff(0));
if (ret != sizeof(struct v1_disk_dqblk)) { if (ret != sizeof(struct v1_disk_dqblk)) {
...@@ -176,6 +178,7 @@ static int v1_read_file_info(struct super_block *sb, int type) ...@@ -176,6 +178,7 @@ static int v1_read_file_info(struct super_block *sb, int type)
dqopt->info[type].dqi_bgrace = dqopt->info[type].dqi_bgrace =
dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME; dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
out: out:
up_read(&dqopt->dqio_sem);
return ret; return ret;
} }
...@@ -185,7 +188,7 @@ static int v1_write_file_info(struct super_block *sb, int type) ...@@ -185,7 +188,7 @@ static int v1_write_file_info(struct super_block *sb, int type)
struct v1_disk_dqblk dqblk; struct v1_disk_dqblk dqblk;
int ret; int ret;
dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY; down_write(&dqopt->dqio_sem);
ret = sb->s_op->quota_read(sb, type, (char *)&dqblk, ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
sizeof(struct v1_disk_dqblk), v1_dqoff(0)); sizeof(struct v1_disk_dqblk), v1_dqoff(0));
if (ret != sizeof(struct v1_disk_dqblk)) { if (ret != sizeof(struct v1_disk_dqblk)) {
...@@ -193,8 +196,11 @@ static int v1_write_file_info(struct super_block *sb, int type) ...@@ -193,8 +196,11 @@ static int v1_write_file_info(struct super_block *sb, int type)
ret = -EIO; ret = -EIO;
goto out; goto out;
} }
spin_lock(&dq_data_lock);
dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY;
dqblk.dqb_itime = dqopt->info[type].dqi_igrace; dqblk.dqb_itime = dqopt->info[type].dqi_igrace;
dqblk.dqb_btime = dqopt->info[type].dqi_bgrace; dqblk.dqb_btime = dqopt->info[type].dqi_bgrace;
spin_unlock(&dq_data_lock);
ret = sb->s_op->quota_write(sb, type, (char *)&dqblk, ret = sb->s_op->quota_write(sb, type, (char *)&dqblk,
sizeof(struct v1_disk_dqblk), v1_dqoff(0)); sizeof(struct v1_disk_dqblk), v1_dqoff(0));
if (ret == sizeof(struct v1_disk_dqblk)) if (ret == sizeof(struct v1_disk_dqblk))
...@@ -202,6 +208,7 @@ static int v1_write_file_info(struct super_block *sb, int type) ...@@ -202,6 +208,7 @@ static int v1_write_file_info(struct super_block *sb, int type)
else if (ret > 0) else if (ret > 0)
ret = -EIO; ret = -EIO;
out: out:
up_write(&dqopt->dqio_sem);
return ret; return ret;
} }
......
...@@ -65,9 +65,11 @@ static int v2_read_header(struct super_block *sb, int type, ...@@ -65,9 +65,11 @@ static int v2_read_header(struct super_block *sb, int type,
if (size != sizeof(struct v2_disk_dqheader)) { if (size != sizeof(struct v2_disk_dqheader)) {
quota_error(sb, "Failed header read: expected=%zd got=%zd", quota_error(sb, "Failed header read: expected=%zd got=%zd",
sizeof(struct v2_disk_dqheader), size); sizeof(struct v2_disk_dqheader), size);
return 0; if (size < 0)
return size;
return -EIO;
} }
return 1; return 0;
} }
/* Check whether given file is really vfsv0 quotafile */ /* Check whether given file is really vfsv0 quotafile */
...@@ -77,7 +79,7 @@ static int v2_check_quota_file(struct super_block *sb, int type) ...@@ -77,7 +79,7 @@ static int v2_check_quota_file(struct super_block *sb, int type)
static const uint quota_magics[] = V2_INITQMAGICS; static const uint quota_magics[] = V2_INITQMAGICS;
static const uint quota_versions[] = V2_INITQVERSIONS; static const uint quota_versions[] = V2_INITQVERSIONS;
if (!v2_read_header(sb, type, &dqhead)) if (v2_read_header(sb, type, &dqhead))
return 0; return 0;
if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] || if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
le32_to_cpu(dqhead.dqh_version) > quota_versions[type]) le32_to_cpu(dqhead.dqh_version) > quota_versions[type])
...@@ -90,28 +92,39 @@ static int v2_read_file_info(struct super_block *sb, int type) ...@@ -90,28 +92,39 @@ static int v2_read_file_info(struct super_block *sb, int type)
{ {
struct v2_disk_dqinfo dinfo; struct v2_disk_dqinfo dinfo;
struct v2_disk_dqheader dqhead; struct v2_disk_dqheader dqhead;
struct mem_dqinfo *info = sb_dqinfo(sb, type); struct quota_info *dqopt = sb_dqopt(sb);
struct mem_dqinfo *info = &dqopt->info[type];
struct qtree_mem_dqinfo *qinfo; struct qtree_mem_dqinfo *qinfo;
ssize_t size; ssize_t size;
unsigned int version; unsigned int version;
int ret;
if (!v2_read_header(sb, type, &dqhead)) down_read(&dqopt->dqio_sem);
return -1; ret = v2_read_header(sb, type, &dqhead);
if (ret < 0)
goto out;
version = le32_to_cpu(dqhead.dqh_version); version = le32_to_cpu(dqhead.dqh_version);
if ((info->dqi_fmt_id == QFMT_VFS_V0 && version != 0) || if ((info->dqi_fmt_id == QFMT_VFS_V0 && version != 0) ||
(info->dqi_fmt_id == QFMT_VFS_V1 && version != 1)) (info->dqi_fmt_id == QFMT_VFS_V1 && version != 1)) {
return -1; ret = -EINVAL;
goto out;
}
size = sb->s_op->quota_read(sb, type, (char *)&dinfo, size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
if (size != sizeof(struct v2_disk_dqinfo)) { if (size != sizeof(struct v2_disk_dqinfo)) {
quota_error(sb, "Can't read info structure"); quota_error(sb, "Can't read info structure");
return -1; if (size < 0)
ret = size;
else
ret = -EIO;
goto out;
} }
info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_NOFS); info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_NOFS);
if (!info->dqi_priv) if (!info->dqi_priv) {
return -ENOMEM; ret = -ENOMEM;
goto out;
}
qinfo = info->dqi_priv; qinfo = info->dqi_priv;
if (version == 0) { if (version == 0) {
/* limits are stored as unsigned 32-bit data */ /* limits are stored as unsigned 32-bit data */
...@@ -145,17 +158,22 @@ static int v2_read_file_info(struct super_block *sb, int type) ...@@ -145,17 +158,22 @@ static int v2_read_file_info(struct super_block *sb, int type)
qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk); qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk);
qinfo->dqi_ops = &v2r1_qtree_ops; qinfo->dqi_ops = &v2r1_qtree_ops;
} }
return 0; ret = 0;
out:
up_read(&dqopt->dqio_sem);
return ret;
} }
/* Write information header to quota file */ /* Write information header to quota file */
static int v2_write_file_info(struct super_block *sb, int type) static int v2_write_file_info(struct super_block *sb, int type)
{ {
struct v2_disk_dqinfo dinfo; struct v2_disk_dqinfo dinfo;
struct mem_dqinfo *info = sb_dqinfo(sb, type); struct quota_info *dqopt = sb_dqopt(sb);
struct mem_dqinfo *info = &dqopt->info[type];
struct qtree_mem_dqinfo *qinfo = info->dqi_priv; struct qtree_mem_dqinfo *qinfo = info->dqi_priv;
ssize_t size; ssize_t size;
down_write(&dqopt->dqio_sem);
spin_lock(&dq_data_lock); spin_lock(&dq_data_lock);
info->dqi_flags &= ~DQF_INFO_DIRTY; info->dqi_flags &= ~DQF_INFO_DIRTY;
dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace); dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
...@@ -168,6 +186,7 @@ static int v2_write_file_info(struct super_block *sb, int type) ...@@ -168,6 +186,7 @@ static int v2_write_file_info(struct super_block *sb, int type)
dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry); dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry);
size = sb->s_op->quota_write(sb, type, (char *)&dinfo, size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
up_write(&dqopt->dqio_sem);
if (size != sizeof(struct v2_disk_dqinfo)) { if (size != sizeof(struct v2_disk_dqinfo)) {
quota_error(sb, "Can't write info structure"); quota_error(sb, "Can't write info structure");
return -1; return -1;
...@@ -283,17 +302,51 @@ static int v2r1_is_id(void *dp, struct dquot *dquot) ...@@ -283,17 +302,51 @@ static int v2r1_is_id(void *dp, struct dquot *dquot)
static int v2_read_dquot(struct dquot *dquot) static int v2_read_dquot(struct dquot *dquot)
{ {
return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot); struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
int ret;
down_read(&dqopt->dqio_sem);
ret = qtree_read_dquot(
sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
dquot);
up_read(&dqopt->dqio_sem);
return ret;
} }
static int v2_write_dquot(struct dquot *dquot) static int v2_write_dquot(struct dquot *dquot)
{ {
return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot); struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
int ret;
bool alloc = false;
/*
* If space for dquot is already allocated, we don't need any
* protection as we'll only overwrite the place of dquot. We are
* still protected by concurrent writes of the same dquot by
* dquot->dq_lock.
*/
if (!dquot->dq_off) {
alloc = true;
down_write(&dqopt->dqio_sem);
}
ret = qtree_write_dquot(
sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
dquot);
if (alloc)
up_write(&dqopt->dqio_sem);
return ret;
} }
static int v2_release_dquot(struct dquot *dquot) static int v2_release_dquot(struct dquot *dquot)
{ {
return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot); struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
int ret;
down_write(&dqopt->dqio_sem);
ret = qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
up_write(&dqopt->dqio_sem);
return ret;
} }
static int v2_free_file_info(struct super_block *sb, int type) static int v2_free_file_info(struct super_block *sb, int type)
...@@ -304,7 +357,13 @@ static int v2_free_file_info(struct super_block *sb, int type) ...@@ -304,7 +357,13 @@ static int v2_free_file_info(struct super_block *sb, int type)
static int v2_get_next_id(struct super_block *sb, struct kqid *qid) static int v2_get_next_id(struct super_block *sb, struct kqid *qid)
{ {
return qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid); struct quota_info *dqopt = sb_dqopt(sb);
int ret;
down_read(&dqopt->dqio_sem);
ret = qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);
up_read(&dqopt->dqio_sem);
return ret;
} }
static const struct quota_format_ops v2_format_ops = { static const struct quota_format_ops v2_format_ops = {
......
...@@ -710,7 +710,7 @@ loff_t inode_get_bytes(struct inode *inode) ...@@ -710,7 +710,7 @@ loff_t inode_get_bytes(struct inode *inode)
loff_t ret; loff_t ret;
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
ret = (((loff_t)inode->i_blocks) << 9) + inode->i_bytes; ret = __inode_get_bytes(inode);
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
return ret; return ret;
} }
......
...@@ -242,7 +242,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags, ...@@ -242,7 +242,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags,
atomic_set(&s->s_active, 1); atomic_set(&s->s_active, 1);
mutex_init(&s->s_vfs_rename_mutex); mutex_init(&s->s_vfs_rename_mutex);
lockdep_set_class(&s->s_vfs_rename_mutex, &type->s_vfs_rename_key); lockdep_set_class(&s->s_vfs_rename_mutex, &type->s_vfs_rename_key);
mutex_init(&s->s_dquot.dqio_mutex); init_rwsem(&s->s_dquot.dqio_sem);
s->s_maxbytes = MAX_NON_LFS; s->s_maxbytes = MAX_NON_LFS;
s->s_op = &default_op; s->s_op = &default_op;
s->s_time_gran = 1000000000; s->s_time_gran = 1000000000;
......
...@@ -3015,6 +3015,10 @@ void __inode_add_bytes(struct inode *inode, loff_t bytes); ...@@ -3015,6 +3015,10 @@ void __inode_add_bytes(struct inode *inode, loff_t bytes);
void inode_add_bytes(struct inode *inode, loff_t bytes); void inode_add_bytes(struct inode *inode, loff_t bytes);
void __inode_sub_bytes(struct inode *inode, loff_t bytes); void __inode_sub_bytes(struct inode *inode, loff_t bytes);
void inode_sub_bytes(struct inode *inode, loff_t bytes); void inode_sub_bytes(struct inode *inode, loff_t bytes);
static inline loff_t __inode_get_bytes(struct inode *inode)
{
return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes;
}
loff_t inode_get_bytes(struct inode *inode); loff_t inode_get_bytes(struct inode *inode);
void inode_set_bytes(struct inode *inode, loff_t bytes); void inode_set_bytes(struct inode *inode, loff_t bytes);
const char *simple_get_link(struct dentry *, struct inode *, const char *simple_get_link(struct dentry *, struct inode *,
......
...@@ -223,12 +223,12 @@ struct mem_dqinfo { ...@@ -223,12 +223,12 @@ struct mem_dqinfo {
struct quota_format_type *dqi_format; struct quota_format_type *dqi_format;
int dqi_fmt_id; /* Id of the dqi_format - used when turning int dqi_fmt_id; /* Id of the dqi_format - used when turning
* quotas on after remount RW */ * quotas on after remount RW */
struct list_head dqi_dirty_list; /* List of dirty dquots */ struct list_head dqi_dirty_list; /* List of dirty dquots [dq_list_lock] */
unsigned long dqi_flags; unsigned long dqi_flags; /* DFQ_ flags [dq_data_lock] */
unsigned int dqi_bgrace; unsigned int dqi_bgrace; /* Space grace time [dq_data_lock] */
unsigned int dqi_igrace; unsigned int dqi_igrace; /* Inode grace time [dq_data_lock] */
qsize_t dqi_max_spc_limit; qsize_t dqi_max_spc_limit; /* Maximum space limit [static] */
qsize_t dqi_max_ino_limit; qsize_t dqi_max_ino_limit; /* Maximum inode limit [static] */
void *dqi_priv; void *dqi_priv;
}; };
...@@ -293,18 +293,18 @@ static inline void dqstats_dec(unsigned int type) ...@@ -293,18 +293,18 @@ static inline void dqstats_dec(unsigned int type)
* clear them when it sees fit. */ * clear them when it sees fit. */
struct dquot { struct dquot {
struct hlist_node dq_hash; /* Hash list in memory */ struct hlist_node dq_hash; /* Hash list in memory [dq_list_lock] */
struct list_head dq_inuse; /* List of all quotas */ struct list_head dq_inuse; /* List of all quotas [dq_list_lock] */
struct list_head dq_free; /* Free list element */ struct list_head dq_free; /* Free list element [dq_list_lock] */
struct list_head dq_dirty; /* List of dirty dquots */ struct list_head dq_dirty; /* List of dirty dquots [dq_list_lock] */
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 */
wait_queue_head_t dq_wait_unused; /* Wait queue for dquot to become unused */
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 [dq_lock, stable once set] */
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 */
...@@ -491,6 +491,9 @@ enum { ...@@ -491,6 +491,9 @@ enum {
*/ */
#define DQUOT_NEGATIVE_USAGE (1 << (DQUOT_STATE_LAST + 1)) #define DQUOT_NEGATIVE_USAGE (1 << (DQUOT_STATE_LAST + 1))
/* Allow negative quota usage */ /* Allow negative quota usage */
/* Do not track dirty dquots in a list */
#define DQUOT_NOLIST_DIRTY (1 << (DQUOT_STATE_LAST + 2))
static inline unsigned int dquot_state_flag(unsigned int flags, int type) static inline unsigned int dquot_state_flag(unsigned int flags, int type)
{ {
return flags << type; return flags << type;
...@@ -521,7 +524,7 @@ static inline void quota_send_warning(struct kqid qid, dev_t dev, ...@@ -521,7 +524,7 @@ static inline void quota_send_warning(struct kqid qid, dev_t dev,
struct quota_info { struct quota_info {
unsigned int flags; /* Flags for diskquotas on this device */ unsigned int flags; /* Flags for diskquotas on this device */
struct mutex dqio_mutex; /* lock device while I/O in progress */ struct rw_semaphore dqio_sem; /* Lock quota file while I/O in progress */
struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */ struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */
struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */ struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */
const struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */ const struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */
......
...@@ -38,11 +38,6 @@ void __quota_error(struct super_block *sb, const char *func, ...@@ -38,11 +38,6 @@ void __quota_error(struct super_block *sb, const char *func,
/* /*
* declaration of quota_function calls in kernel. * declaration of quota_function calls in kernel.
*/ */
void inode_add_rsv_space(struct inode *inode, qsize_t number);
void inode_claim_rsv_space(struct inode *inode, qsize_t number);
void inode_sub_rsv_space(struct inode *inode, qsize_t number);
void inode_reclaim_rsv_space(struct inode *inode, qsize_t number);
int dquot_initialize(struct inode *inode); int dquot_initialize(struct inode *inode);
bool dquot_initialize_needed(struct inode *inode); bool dquot_initialize_needed(struct inode *inode);
void dquot_drop(struct inode *inode); void dquot_drop(struct inode *inode);
......
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