Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

quota: Check that quota is not dirty before release

There is a race window where quota was redirted once we drop dq_list_lock inside dqput(),
but before we grab dquot->dq_lock inside dquot_release()

TASK1 TASK2 (chowner)
->dqput()
we_slept:
spin_lock(&dq_list_lock)
if (dquot_dirty(dquot)) {
spin_unlock(&dq_list_lock);
dquot->dq_sb->dq_op->write_dquot(dquot);
goto we_slept
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
spin_unlock(&dq_list_lock);
dquot->dq_sb->dq_op->release_dquot(dquot);
dqget()
mark_dquot_dirty()
dqput()
goto we_slept;
}
So dquot dirty quota will be released by TASK1, but on next we_sleept loop
we detect this and call ->write_dquot() for it.
XFSTEST: https://github.com/dmonakhov/xfstests/commit/440a80d4cbb39e9234df4d7240aee1d551c36107

Link: https://lore.kernel.org/r/20191031103920.3919-2-dmonakhov@openvz.org
CC: stable@vger.kernel.org
Signed-off-by: Dmitry Monakhov <dmtrmonakhov@yandex-team.ru>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Dmitry Monakhov and committed by
Jan Kara
df4bb5d1 6ff33d99

+12 -2
+1 -1
fs/ocfs2/quota_global.c
··· 728 728 729 729 mutex_lock(&dquot->dq_lock); 730 730 /* Check whether we are not racing with some other dqget() */ 731 - if (atomic_read(&dquot->dq_count) > 1) 731 + if (dquot_is_busy(dquot)) 732 732 goto out; 733 733 /* Running from downconvert thread? Postpone quota processing to wq */ 734 734 if (current == osb->dc_task) {
+1 -1
fs/quota/dquot.c
··· 497 497 498 498 mutex_lock(&dquot->dq_lock); 499 499 /* Check whether we are not racing with some other dqget() */ 500 - if (atomic_read(&dquot->dq_count) > 1) 500 + if (dquot_is_busy(dquot)) 501 501 goto out_dqlock; 502 502 if (dqopt->ops[dquot->dq_id.type]->release_dqblk) { 503 503 ret = dqopt->ops[dquot->dq_id.type]->release_dqblk(dquot);
+10
include/linux/quotaops.h
··· 54 54 atomic_inc(&dquot->dq_count); 55 55 return dquot; 56 56 } 57 + 58 + static inline bool dquot_is_busy(struct dquot *dquot) 59 + { 60 + if (test_bit(DQ_MOD_B, &dquot->dq_flags)) 61 + return true; 62 + if (atomic_read(&dquot->dq_count) > 1) 63 + return true; 64 + return false; 65 + } 66 + 57 67 void dqput(struct dquot *dquot); 58 68 int dquot_scan_active(struct super_block *sb, 59 69 int (*fn)(struct dquot *dquot, unsigned long priv),