forked from luck/tmp_suning_uos_patched
quota: Cleanup S_NOQUOTA handling
Cleanup handling of S_NOQUOTA inode flag and document it a bit. The flag does not have to be set under dqptr_sem. Only functions modifying inode's dquot pointers have to check the flag under dqptr_sem before going forward with the modification. This way we are sure that we cannot add new dquot pointers to the inode which is just becoming a quota file. The good thing about this cleanup is that there are no more places in quota code which enforce i_mutex vs. dqptr_sem lock ordering (in particular that dqptr_sem -> i_mutex of quota file). This should silence some (false) lockdep warnings with ext4 + quota and generally make life of some filesystems easier. Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
3a5b27bf6f
commit
26245c949c
|
@ -100,9 +100,13 @@
|
||||||
*
|
*
|
||||||
* Any operation working on dquots via inode pointers must hold dqptr_sem. If
|
* Any operation working on dquots via inode pointers must hold dqptr_sem. If
|
||||||
* operation is just reading pointers from inode (or not using them at all) the
|
* operation is just reading pointers from inode (or not using them at all) the
|
||||||
* read lock is enough. If pointers are altered function must hold write lock
|
* read lock is enough. If pointers are altered function must hold write lock.
|
||||||
* (these locking rules also apply for S_NOQUOTA flag in the inode - note that
|
* Special care needs to be taken about S_NOQUOTA inode flag (marking that
|
||||||
* for altering the flag i_mutex is also needed).
|
* inode is a quota file). Functions adding pointers from inode to dquots have
|
||||||
|
* to check this flag under dqptr_sem and then (if S_NOQUOTA is not set) they
|
||||||
|
* have to do all pointer modifications before dropping dqptr_sem. This makes
|
||||||
|
* sure they cannot race with quotaon which first sets S_NOQUOTA flag and
|
||||||
|
* then drops all pointers to dquots from an inode.
|
||||||
*
|
*
|
||||||
* Each dquot has its dq_lock mutex. Locked dquots might not be referenced
|
* Each dquot has its dq_lock mutex. Locked dquots might not be referenced
|
||||||
* from inodes (dquot_alloc_space() and such don't check the dq_lock).
|
* from inodes (dquot_alloc_space() and such don't check the dq_lock).
|
||||||
|
@ -1275,7 +1279,6 @@ int dquot_initialize(struct inode *inode, int type)
|
||||||
}
|
}
|
||||||
|
|
||||||
down_write(&sb_dqopt(sb)->dqptr_sem);
|
down_write(&sb_dqopt(sb)->dqptr_sem);
|
||||||
/* Having dqptr_sem we know NOQUOTA flags can't be altered... */
|
|
||||||
if (IS_NOQUOTA(inode))
|
if (IS_NOQUOTA(inode))
|
||||||
goto out_err;
|
goto out_err;
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
|
@ -1431,11 +1434,6 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number,
|
||||||
}
|
}
|
||||||
|
|
||||||
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
if (IS_NOQUOTA(inode)) {
|
|
||||||
inode_incr_space(inode, number, reserve);
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
||||||
warntype[cnt] = QUOTA_NL_NOWARN;
|
warntype[cnt] = QUOTA_NL_NOWARN;
|
||||||
|
|
||||||
|
@ -1466,7 +1464,6 @@ int __dquot_alloc_space(struct inode *inode, qsize_t number,
|
||||||
mark_all_dquot_dirty(inode->i_dquot);
|
mark_all_dquot_dirty(inode->i_dquot);
|
||||||
out_flush_warn:
|
out_flush_warn:
|
||||||
flush_warnings(inode->i_dquot, warntype);
|
flush_warnings(inode->i_dquot, warntype);
|
||||||
out_unlock:
|
|
||||||
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1499,10 +1496,6 @@ int dquot_alloc_inode(const struct inode *inode, qsize_t number)
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
||||||
warntype[cnt] = QUOTA_NL_NOWARN;
|
warntype[cnt] = QUOTA_NL_NOWARN;
|
||||||
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
if (IS_NOQUOTA(inode)) {
|
|
||||||
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
||||||
return QUOTA_OK;
|
|
||||||
}
|
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (!inode->i_dquot[cnt])
|
if (!inode->i_dquot[cnt])
|
||||||
|
@ -1539,12 +1532,6 @@ int dquot_claim_space(struct inode *inode, qsize_t number)
|
||||||
}
|
}
|
||||||
|
|
||||||
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
if (IS_NOQUOTA(inode)) {
|
|
||||||
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
||||||
inode_claim_rsv_space(inode, number);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
/* Claim reserved quotas to allocated quotas */
|
/* Claim reserved quotas to allocated quotas */
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
|
@ -1573,17 +1560,11 @@ int __dquot_free_space(struct inode *inode, qsize_t number, int reserve)
|
||||||
/* First test before acquiring mutex - solves deadlocks when we
|
/* First test before acquiring mutex - solves deadlocks when we
|
||||||
* re-enter the quota code and are already holding the mutex */
|
* re-enter the quota code and are already holding the mutex */
|
||||||
if (IS_NOQUOTA(inode)) {
|
if (IS_NOQUOTA(inode)) {
|
||||||
out_sub:
|
|
||||||
inode_decr_space(inode, number, reserve);
|
inode_decr_space(inode, number, reserve);
|
||||||
return QUOTA_OK;
|
return QUOTA_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
/* Now recheck reliably when holding dqptr_sem */
|
|
||||||
if (IS_NOQUOTA(inode)) {
|
|
||||||
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
||||||
goto out_sub;
|
|
||||||
}
|
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (!inode->i_dquot[cnt])
|
if (!inode->i_dquot[cnt])
|
||||||
|
@ -1636,11 +1617,6 @@ int dquot_free_inode(const struct inode *inode, qsize_t number)
|
||||||
return QUOTA_OK;
|
return QUOTA_OK;
|
||||||
|
|
||||||
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
down_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
/* Now recheck reliably when holding dqptr_sem */
|
|
||||||
if (IS_NOQUOTA(inode)) {
|
|
||||||
up_read(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
|
||||||
return QUOTA_OK;
|
|
||||||
}
|
|
||||||
spin_lock(&dq_data_lock);
|
spin_lock(&dq_data_lock);
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
if (!inode->i_dquot[cnt])
|
if (!inode->i_dquot[cnt])
|
||||||
|
@ -1692,7 +1668,6 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
|
||||||
GRPQUOTA);
|
GRPQUOTA);
|
||||||
|
|
||||||
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
down_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
/* Now recheck reliably when holding dqptr_sem */
|
|
||||||
if (IS_NOQUOTA(inode)) { /* File without quota accounting? */
|
if (IS_NOQUOTA(inode)) { /* File without quota accounting? */
|
||||||
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
up_write(&sb_dqopt(inode->i_sb)->dqptr_sem);
|
||||||
goto put_all;
|
goto put_all;
|
||||||
|
@ -2010,13 +1985,15 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
|
||||||
/* We don't want quota and atime on quota files (deadlocks
|
/* We don't want quota and atime on quota files (deadlocks
|
||||||
* possible) Also nobody should write to the file - we use
|
* possible) Also nobody should write to the file - we use
|
||||||
* special IO operations which ignore the immutable bit. */
|
* special IO operations which ignore the immutable bit. */
|
||||||
down_write(&dqopt->dqptr_sem);
|
|
||||||
mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
|
mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
|
||||||
oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE |
|
oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE |
|
||||||
S_NOQUOTA);
|
S_NOQUOTA);
|
||||||
inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE;
|
inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE;
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
up_write(&dqopt->dqptr_sem);
|
/*
|
||||||
|
* When S_NOQUOTA is set, remove dquot references as no more
|
||||||
|
* references can be added
|
||||||
|
*/
|
||||||
sb->dq_op->drop(inode);
|
sb->dq_op->drop(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2053,14 +2030,12 @@ static int vfs_load_quota_inode(struct inode *inode, int type, int format_id,
|
||||||
iput(inode);
|
iput(inode);
|
||||||
out_lock:
|
out_lock:
|
||||||
if (oldflags != -1) {
|
if (oldflags != -1) {
|
||||||
down_write(&dqopt->dqptr_sem);
|
|
||||||
mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
|
mutex_lock_nested(&inode->i_mutex, I_MUTEX_QUOTA);
|
||||||
/* Set the flags back (in the case of accidental quotaon()
|
/* Set the flags back (in the case of accidental quotaon()
|
||||||
* on a wrong file we don't want to mess up the flags) */
|
* on a wrong file we don't want to mess up the flags) */
|
||||||
inode->i_flags &= ~(S_NOATIME | S_NOQUOTA | S_IMMUTABLE);
|
inode->i_flags &= ~(S_NOATIME | S_NOQUOTA | S_IMMUTABLE);
|
||||||
inode->i_flags |= oldflags;
|
inode->i_flags |= oldflags;
|
||||||
mutex_unlock(&inode->i_mutex);
|
mutex_unlock(&inode->i_mutex);
|
||||||
up_write(&dqopt->dqptr_sem);
|
|
||||||
}
|
}
|
||||||
mutex_unlock(&dqopt->dqonoff_mutex);
|
mutex_unlock(&dqopt->dqonoff_mutex);
|
||||||
out_fmt:
|
out_fmt:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user