aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry <dmonakhov@openvz.org>2010-10-09 15:15:30 -0400
committerJan Kara <jack@suse.cz>2010-10-27 19:30:02 -0400
commit9e32784b71c2c84895016ca6ab271591669c02aa (patch)
treedb69a152de57f65666408591a06f15cb865c695f
parenta910eefa511f9d1118effc13fba6773163502c4f (diff)
quota: fix dquot_disable vs dquot_transfer race v2
I've got following lockup: dquot_disable dquot_transfer ->dqget() sb_has_quota_active dqopt->flags &= ~dquot_state_flag(f, cnt) atomic_inc(dq->dq_count) ->drop_dquot_ref(sb, cnt); down_write(dqptr_sem) inode->i_dquot[cnt] = NULL ->__dquot_transfer invalidate_dquots(sb, cnt); down_write(&dqptr_sem) ->wait for dq_wait_unused inode->i_dquot = new_dquot /* wait forever */ ^^^^New quota user^^^^^^ We cannot allow new references to dquots from inodes after drop_dquot_ref() has removed them. We have to recheck quota state under dqptr_sem and before assignment, as we do it in dquot_initialize(). Signed-off-by: Dmitry Monakhov <dmonakhov@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r--fs/quota/dquot.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index aad1316a977f..ed14beea8fe3 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -1736,6 +1736,7 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1736 qsize_t rsv_space = 0; 1736 qsize_t rsv_space = 0;
1737 struct dquot *transfer_from[MAXQUOTAS] = {}; 1737 struct dquot *transfer_from[MAXQUOTAS] = {};
1738 int cnt, ret = 0; 1738 int cnt, ret = 0;
1739 char is_valid[MAXQUOTAS] = {};
1739 char warntype_to[MAXQUOTAS]; 1740 char warntype_to[MAXQUOTAS];
1740 char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS]; 1741 char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS];
1741 1742
@@ -1757,8 +1758,15 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1757 space = cur_space + rsv_space; 1758 space = cur_space + rsv_space;
1758 /* Build the transfer_from list and check the limits */ 1759 /* Build the transfer_from list and check the limits */
1759 for (cnt = 0; cnt < MAXQUOTAS; cnt++) { 1760 for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
1761 /*
1762 * Skip changes for same uid or gid or for turned off quota-type.
1763 */
1760 if (!transfer_to[cnt]) 1764 if (!transfer_to[cnt])
1761 continue; 1765 continue;
1766 /* Avoid races with quotaoff() */
1767 if (!sb_has_quota_active(inode->i_sb, cnt))
1768 continue;
1769 is_valid[cnt] = 1;
1762 transfer_from[cnt] = inode->i_dquot[cnt]; 1770 transfer_from[cnt] = inode->i_dquot[cnt];
1763 ret = check_idq(transfer_to[cnt], 1, warntype_to + cnt); 1771 ret = check_idq(transfer_to[cnt], 1, warntype_to + cnt);
1764 if (ret) 1772 if (ret)
@@ -1772,12 +1780,8 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1772 * Finally perform the needed transfer from transfer_from to transfer_to 1780 * Finally perform the needed transfer from transfer_from to transfer_to
1773 */ 1781 */
1774 for (cnt = 0; cnt < MAXQUOTAS; cnt++) { 1782 for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
1775 /* 1783 if (!is_valid[cnt])
1776 * Skip changes for same uid or gid or for turned off quota-type.
1777 */
1778 if (!transfer_to[cnt])
1779 continue; 1784 continue;
1780
1781 /* Due to IO error we might not have transfer_from[] structure */ 1785 /* Due to IO error we might not have transfer_from[] structure */
1782 if (transfer_from[cnt]) { 1786 if (transfer_from[cnt]) {
1783 warntype_from_inodes[cnt] = 1787 warntype_from_inodes[cnt] =
@@ -1803,7 +1807,9 @@ int __dquot_transfer(struct inode *inode, struct dquot **transfer_to)
1803 mark_all_dquot_dirty(transfer_to); 1807 mark_all_dquot_dirty(transfer_to);
1804 /* Pass back references to put */ 1808 /* Pass back references to put */
1805 for (cnt = 0; cnt < MAXQUOTAS; cnt++) 1809 for (cnt = 0; cnt < MAXQUOTAS; cnt++)
1806 transfer_to[cnt] = transfer_from[cnt]; 1810 if (is_valid[cnt])
1811 transfer_to[cnt] = transfer_from[cnt];
1812
1807warn: 1813warn:
1808 flush_warnings(transfer_to, warntype_to); 1814 flush_warnings(transfer_to, warntype_to);
1809 flush_warnings(transfer_from, warntype_from_inodes); 1815 flush_warnings(transfer_from, warntype_from_inodes);