quota: atomic dq_flags operations in do_set_dqblk
PATCH 1/1 quota: atomic dq_flags operations in do_set_dqblk
From: Andrew Perepechko <[email protected]>
Non-atomic __set_bit calls are replaced with atomic set_bit calls in do_set_dqblk so that individual bits should not be lost in a possible race condition.
---
fs/dquot.c +6 -6
@@ -2065,32 +2065,32 @@ static int do_set_dqblk(struct dquot *dqif (di->dqb_valid &QIF_SPACE ) { dm->dqb_curspace = di->dqb_curspace; check_blim =1 ; -__set_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_BLIMITS ) { dm->dqb_bsoftlimit =qbtos (di->dqb_bsoftlimit); dm->dqb_bhardlimit =qbtos (di->dqb_bhardlimit); check_blim =1 ; -__set_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_INODES ) { dm->dqb_curinodes = di->dqb_curinodes; check_ilim =1 ; -__set_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_ILIMITS ) { dm->dqb_isoftlimit = di->dqb_isoftlimit; dm->dqb_ihardlimit = di->dqb_ihardlimit; check_ilim =1 ; -__set_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_BTIME ) { dm->dqb_btime = di->dqb_btime; -__set_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_ITIME ) { dm->dqb_itime = di->dqb_itime; -__set_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags); }if (check_blim) {
Jan Kara
reviewerQuoted 4 lines · Andrew Perepechko
From: Andrew Perepechko <[email protected]>
Non-atomic
__set_bitcalls are replaced with atomicset_bitcalls indo_set_dqblkso that individual bits should not be lost in a possible race condition.
I took a liberty of modifying the changelog to be more descriptive:
quota: Fix possible dq_flags corruption
dq_flags are modified non-atomically in do_set_dqblk via __set_bit calls and atomically for example in mark_dquot_dirty or clear_dquot_dirty. Hence a change done by an atomic operation can be overwritten by a change done by a non-atomic one. Fix the problem by using atomic bitops even in do_set_dqblk.
Otherwise I've merged the patch.
Honza
Quoted 44 lines
Signed-off-by Andrew Perepechko <[email protected]>---
diff -puNfs/dquot.cfs/dquot.cfs/dquot.c +6
-6@@ -2065,32 +2065,32 @@ static int do_set_dqblk(struct dquot *dqif (di->dqb_valid &QIF_SPACE ) { dm->dqb_curspace = di->dqb_curspace; check_blim =1 ; -__set_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_BLIMITS ) { dm->dqb_bsoftlimit =qbtos (di->dqb_bsoftlimit); dm->dqb_bhardlimit =qbtos (di->dqb_bhardlimit); check_blim =1 ; -__set_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_INODES ) { dm->dqb_curinodes = di->dqb_curinodes; check_ilim =1 ; -__set_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_ILIMITS ) { dm->dqb_isoftlimit = di->dqb_isoftlimit; dm->dqb_ihardlimit = di->dqb_ihardlimit; check_ilim =1 ; -__set_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_BTIME ) { dm->dqb_btime = di->dqb_btime; -__set_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_ITIME ) { dm->dqb_itime = di->dqb_itime; -__set_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags); }if (check_blim) {
Signature · 2 lines
Jan Kara <[email protected]> SUSE Labs, CR
Thanks a lot, Jan!
Andrew.
Quoted 84 lines · Jan Kara
Quoted 5 lines · Andrew Perepechko
From: Andrew Perepechko <[email protected]>
Non-atomic
__set_bitcalls are replaced with atomicset_bitcalls indo_set_dqblkso that individual bits should not be lost in a possible race condition.I took a liberty of modifying the changelog to be more descriptive:
quota: Fix possible
dq_flagscorruption
dq_flagsare modified non-atomically indo_set_dqblkvia__set_bitcalls and atomically for example inmark_dquot_dirtyorclear_dquot_dirty. Hence a change done by an atomic operation can be overwritten by a change done by a non-atomic one. Fix the problem by using atomic bitops even indo_set_dqblk.Otherwise I've merged the patch.
Honza
Quoted 63 lines
Signed-off-by Andrew Perepechko <[email protected]>---
diff -puNfs/dquot.cfs/dquot.cfs/dquot.c +6
-6@@ -2065,32 +2065,32 @@ static int do_set_dqblk(struct dquot *dqif (di->dqb_valid &QIF_SPACE ) { dm->dqb_curspace = di->dqb_curspace; check_blim =1 ; -__set_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_SPACE_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_BLIMITS ) { dm->dqb_bsoftlimit =qbtos (di->dqb_bsoftlimit); dm->dqb_bhardlimit =qbtos (di->dqb_bhardlimit); check_blim =1 ; -__set_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_BLIMITS_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_INODES ) { dm->dqb_curinodes = di->dqb_curinodes; check_ilim =1 ; -__set_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_INODES_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_ILIMITS ) { dm->dqb_isoftlimit = di->dqb_isoftlimit; dm->dqb_ihardlimit = di->dqb_ihardlimit; check_ilim =1 ; -__set_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_ILIMITS_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_BTIME ) { dm->dqb_btime = di->dqb_btime; -__set_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_BTIME_B , &dquot->dq_flags); }if (di->dqb_valid &QIF_ITIME ) { dm->dqb_itime = di->dqb_itime; -__set_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags); +set_bit (DQ_LASTSET_B +QIF_ITIME_B , &dquot->dq_flags); }if (check_blim) {