quota: Make dquot_disable() work without quota inodes
authorJan Kara <jack@suse.cz>
Mon, 4 Nov 2019 10:12:44 +0000 (11:12 +0100)
committerJan Kara <jack@suse.cz>
Mon, 4 Nov 2019 10:21:35 +0000 (11:21 +0100)
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>
fs/quota/dquot.c

index 3e4cf0d..4c3da4e 100644 (file)
@@ -2162,14 +2162,29 @@ int dquot_file_open(struct inode *inode, struct file *file)
 }
 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)
  */
 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 inode *toputinode[MAXQUOTAS];
 
        /* s_umount should be held in exclusive mode */
        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;
 
        for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
-               toputinode[cnt] = NULL;
                if (type != -1 && cnt != type)
                        continue;
                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(
                                                        DQUOT_SUSPENDED, cnt);
                                spin_unlock(&dq_state_lock);
-                               iput(dqopt->files[cnt]);
-                               dqopt->files[cnt] = NULL;
+                               vfs_cleanup_quota_inode(sb, cnt);
                                continue;
                        }
                        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)
                        dqopt->ops[cnt]->free_file_info(sb, cnt);
                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_igrace = 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
         * changes done by userspace on the next quotaon() */
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-               /* This can happen when suspending quotas on remount-ro... */
-               if (toputinode[cnt] && !sb_has_quota_loaded(sb, cnt)) {
-                       inode_lock(toputinode[cnt]);
-                       toputinode[cnt]->i_flags &= ~S_NOQUOTA;
-                       truncate_inode_pages(&toputinode[cnt]->i_data, 0);
-                       inode_unlock(toputinode[cnt]);
-                       mark_inode_dirty_sync(toputinode[cnt]);
+               if (!sb_has_quota_loaded(sb, cnt) && dqopt->files[cnt]) {
+                       inode_lock(dqopt->files[cnt]);
+                       truncate_inode_pages(&dqopt->files[cnt]->i_data, 0);
+                       inode_unlock(dqopt->files[cnt]);
                }
        if (sb->s_bdev)
                invalidate_bdev(sb->s_bdev);
 put_inodes:
+       /* We are done when suspending quotas */
+       if (flags & DQUOT_SUSPENDED)
+               return 0;
+
        for (cnt = 0; cnt < MAXQUOTAS; cnt++)
-               if (toputinode[cnt]) {
-                       /* On remount RO, we keep the inode pointer so that we
-                        * can reenable quota on the subsequent remount RW. We
-                        * 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;
+               if (!sb_has_quota_loaded(sb, cnt))
+                       vfs_cleanup_quota_inode(sb, cnt);
+       return 0;
 }
 EXPORT_SYMBOL(dquot_disable);
 
@@ -2330,20 +2329,6 @@ static int vfs_setup_quota_inode(struct inode *inode, int type)
        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,
        unsigned int flags)
 {