quota: Make dquot_disable() work without quota inodes
Quota on and quota off are protected by s_umount semaphore held in exclusive mode since commit 7d6cd73d33b6 "quota: Hold s_umount in exclusive mode when enabling / disabling quotas". This makes it impossible for dquot_disable() to race with other enabling or disabling of quotas. Simplify the cleanup done by dquot_disable() based on this fact and also remove some stale comments. As a bonus this cleanup makes dquot_disable() properly handle a case when there are no quota inodes. Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
parent
069a916636
commit
2ec1f3011f
@ -2162,14 +2162,29 @@ int dquot_file_open(struct inode *inode, struct file *file)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dquot_file_open);
|
EXPORT_SYMBOL(dquot_file_open);
|
||||||
|
|
||||||
|
static void vfs_cleanup_quota_inode(struct super_block *sb, int type)
|
||||||
|
{
|
||||||
|
struct quota_info *dqopt = sb_dqopt(sb);
|
||||||
|
struct inode *inode = dqopt->files[type];
|
||||||
|
|
||||||
|
if (!inode)
|
||||||
|
return;
|
||||||
|
if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
|
||||||
|
inode_lock(inode);
|
||||||
|
inode->i_flags &= ~S_NOQUOTA;
|
||||||
|
inode_unlock(inode);
|
||||||
|
}
|
||||||
|
dqopt->files[type] = NULL;
|
||||||
|
iput(inode);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
|
* Turn quota off on a device. type == -1 ==> quotaoff for all types (umount)
|
||||||
*/
|
*/
|
||||||
int dquot_disable(struct super_block *sb, int type, unsigned int flags)
|
int dquot_disable(struct super_block *sb, int type, unsigned int flags)
|
||||||
{
|
{
|
||||||
int cnt, ret = 0;
|
int cnt;
|
||||||
struct quota_info *dqopt = sb_dqopt(sb);
|
struct quota_info *dqopt = sb_dqopt(sb);
|
||||||
struct inode *toputinode[MAXQUOTAS];
|
|
||||||
|
|
||||||
/* s_umount should be held in exclusive mode */
|
/* s_umount should be held in exclusive mode */
|
||||||
if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount)))
|
if (WARN_ON_ONCE(down_read_trylock(&sb->s_umount)))
|
||||||
@ -2191,7 +2206,6 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
|
||||||
toputinode[cnt] = NULL;
|
|
||||||
if (type != -1 && cnt != type)
|
if (type != -1 && cnt != type)
|
||||||
continue;
|
continue;
|
||||||
if (!sb_has_quota_loaded(sb, cnt))
|
if (!sb_has_quota_loaded(sb, cnt))
|
||||||
@ -2211,8 +2225,7 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
|
|||||||
dqopt->flags &= ~dquot_state_flag(
|
dqopt->flags &= ~dquot_state_flag(
|
||||||
DQUOT_SUSPENDED, cnt);
|
DQUOT_SUSPENDED, cnt);
|
||||||
spin_unlock(&dq_state_lock);
|
spin_unlock(&dq_state_lock);
|
||||||
iput(dqopt->files[cnt]);
|
vfs_cleanup_quota_inode(sb, cnt);
|
||||||
dqopt->files[cnt] = NULL;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
spin_unlock(&dq_state_lock);
|
spin_unlock(&dq_state_lock);
|
||||||
@ -2234,10 +2247,6 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
|
|||||||
if (dqopt->ops[cnt]->free_file_info)
|
if (dqopt->ops[cnt]->free_file_info)
|
||||||
dqopt->ops[cnt]->free_file_info(sb, cnt);
|
dqopt->ops[cnt]->free_file_info(sb, cnt);
|
||||||
put_quota_format(dqopt->info[cnt].dqi_format);
|
put_quota_format(dqopt->info[cnt].dqi_format);
|
||||||
|
|
||||||
toputinode[cnt] = dqopt->files[cnt];
|
|
||||||
if (!sb_has_quota_loaded(sb, cnt))
|
|
||||||
dqopt->files[cnt] = NULL;
|
|
||||||
dqopt->info[cnt].dqi_flags = 0;
|
dqopt->info[cnt].dqi_flags = 0;
|
||||||
dqopt->info[cnt].dqi_igrace = 0;
|
dqopt->info[cnt].dqi_igrace = 0;
|
||||||
dqopt->info[cnt].dqi_bgrace = 0;
|
dqopt->info[cnt].dqi_bgrace = 0;
|
||||||
@ -2259,32 +2268,22 @@ int dquot_disable(struct super_block *sb, int type, unsigned int flags)
|
|||||||
* must also discard the blockdev buffers so that we see the
|
* must also discard the blockdev buffers so that we see the
|
||||||
* changes done by userspace on the next quotaon() */
|
* changes done by userspace on the next quotaon() */
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
||||||
/* This can happen when suspending quotas on remount-ro... */
|
if (!sb_has_quota_loaded(sb, cnt) && dqopt->files[cnt]) {
|
||||||
if (toputinode[cnt] && !sb_has_quota_loaded(sb, cnt)) {
|
inode_lock(dqopt->files[cnt]);
|
||||||
inode_lock(toputinode[cnt]);
|
truncate_inode_pages(&dqopt->files[cnt]->i_data, 0);
|
||||||
toputinode[cnt]->i_flags &= ~S_NOQUOTA;
|
inode_unlock(dqopt->files[cnt]);
|
||||||
truncate_inode_pages(&toputinode[cnt]->i_data, 0);
|
|
||||||
inode_unlock(toputinode[cnt]);
|
|
||||||
mark_inode_dirty_sync(toputinode[cnt]);
|
|
||||||
}
|
}
|
||||||
if (sb->s_bdev)
|
if (sb->s_bdev)
|
||||||
invalidate_bdev(sb->s_bdev);
|
invalidate_bdev(sb->s_bdev);
|
||||||
put_inodes:
|
put_inodes:
|
||||||
|
/* We are done when suspending quotas */
|
||||||
|
if (flags & DQUOT_SUSPENDED)
|
||||||
|
return 0;
|
||||||
|
|
||||||
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
for (cnt = 0; cnt < MAXQUOTAS; cnt++)
|
||||||
if (toputinode[cnt]) {
|
if (!sb_has_quota_loaded(sb, cnt))
|
||||||
/* On remount RO, we keep the inode pointer so that we
|
vfs_cleanup_quota_inode(sb, cnt);
|
||||||
* can reenable quota on the subsequent remount RW. We
|
return 0;
|
||||||
* have to check 'flags' variable and not use sb_has_
|
|
||||||
* function because another quotaon / quotaoff could
|
|
||||||
* change global state before we got here. We refuse
|
|
||||||
* to suspend quotas when there is pending delete on
|
|
||||||
* the quota file... */
|
|
||||||
if (!(flags & DQUOT_SUSPENDED))
|
|
||||||
iput(toputinode[cnt]);
|
|
||||||
else if (!toputinode[cnt]->i_nlink)
|
|
||||||
ret = -EBUSY;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dquot_disable);
|
EXPORT_SYMBOL(dquot_disable);
|
||||||
|
|
||||||
@ -2330,20 +2329,6 @@ static int vfs_setup_quota_inode(struct inode *inode, int type)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfs_cleanup_quota_inode(struct super_block *sb, int type)
|
|
||||||
{
|
|
||||||
struct quota_info *dqopt = sb_dqopt(sb);
|
|
||||||
struct inode *inode = dqopt->files[type];
|
|
||||||
|
|
||||||
if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) {
|
|
||||||
inode_lock(inode);
|
|
||||||
inode->i_flags &= ~S_NOQUOTA;
|
|
||||||
inode_unlock(inode);
|
|
||||||
}
|
|
||||||
dqopt->files[type] = NULL;
|
|
||||||
iput(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dquot_load_quota_sb(struct super_block *sb, int type, int format_id,
|
int dquot_load_quota_sb(struct super_block *sb, int type, int format_id,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user