Commit 26245c94 authored by Jan Kara's avatar Jan Kara

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: default avatarJan Kara <jack@suse.cz>
parent 3a5b27bf
...@@ -100,9 +100,13 @@ ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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:
......
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