diff options
author | Dmitry <dmonakhov@openvz.org> | 2010-10-09 15:15:30 -0400 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2010-10-27 19:30:02 -0400 |
commit | 9e32784b71c2c84895016ca6ab271591669c02aa (patch) | |
tree | db69a152de57f65666408591a06f15cb865c695f | |
parent | a910eefa511f9d1118effc13fba6773163502c4f (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.c | 18 |
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 | |||
1807 | warn: | 1813 | warn: |
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); |