Commit 2ec1f301 authored by Jan Kara's avatar Jan Kara

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 7d6cd73d "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: default avatarJan Kara <jack@suse.cz>
parent 069a9166
...@@ -2162,14 +2162,29 @@ int dquot_file_open(struct inode *inode, struct file *file) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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)
{ {
......
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