aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/extent-tree.c
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-03-25 16:03:35 -0400
committerJosef Bacik <jbacik@fusionio.com>2013-03-28 09:51:26 -0400
commitf4881bc7a83eff263789dd524b7c269d138d4af5 (patch)
treed305caf38f58f140b952e14fa707964c905356b0 /fs/btrfs/extent-tree.c
parentadaa4b8e4d47eeb114513c2f7a172929154b94bd (diff)
Btrfs: fix space leak when we fail to reserve metadata space
Dave reported a warning when running xfstest 275. We have been leaking delalloc metadata space when our reservations fail. This is because we were improperly calculating how much space to free for our checksum reservations. The problem is we would sometimes free up space that had already been freed in another thread and we would end up with negative usage for the delalloc space. This patch fixes the problem by calculating how much space the other threads would have already freed, and then calculate how much space we need to free had we not done the reservation at all, and then freeing any excess space. This makes xfstests 275 no longer have leaked space. Thanks Cc: stable@vger.kernel.org Reported-by: David Sterba <dsterba@suse.cz> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r--fs/btrfs/extent-tree.c47
1 files changed, 41 insertions, 6 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index a8ff25aedca1..a22b5cc921ad 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4815,14 +4815,49 @@ out_fail:
4815 * If the inodes csum_bytes is the same as the original 4815 * If the inodes csum_bytes is the same as the original
4816 * csum_bytes then we know we haven't raced with any free()ers 4816 * csum_bytes then we know we haven't raced with any free()ers
4817 * so we can just reduce our inodes csum bytes and carry on. 4817 * so we can just reduce our inodes csum bytes and carry on.
4818 * Otherwise we have to do the normal free thing to account for
4819 * the case that the free side didn't free up its reserve
4820 * because of this outstanding reservation.
4821 */ 4818 */
4822 if (BTRFS_I(inode)->csum_bytes == csum_bytes) 4819 if (BTRFS_I(inode)->csum_bytes == csum_bytes) {
4823 calc_csum_metadata_size(inode, num_bytes, 0); 4820 calc_csum_metadata_size(inode, num_bytes, 0);
4824 else 4821 } else {
4825 to_free = calc_csum_metadata_size(inode, num_bytes, 0); 4822 u64 orig_csum_bytes = BTRFS_I(inode)->csum_bytes;
4823 u64 bytes;
4824
4825 /*
4826 * This is tricky, but first we need to figure out how much we
4827 * free'd from any free-ers that occured during this
4828 * reservation, so we reset ->csum_bytes to the csum_bytes
4829 * before we dropped our lock, and then call the free for the
4830 * number of bytes that were freed while we were trying our
4831 * reservation.
4832 */
4833 bytes = csum_bytes - BTRFS_I(inode)->csum_bytes;
4834 BTRFS_I(inode)->csum_bytes = csum_bytes;
4835 to_free = calc_csum_metadata_size(inode, bytes, 0);
4836
4837
4838 /*
4839 * Now we need to see how much we would have freed had we not
4840 * been making this reservation and our ->csum_bytes were not
4841 * artificially inflated.
4842 */
4843 BTRFS_I(inode)->csum_bytes = csum_bytes - num_bytes;
4844 bytes = csum_bytes - orig_csum_bytes;
4845 bytes = calc_csum_metadata_size(inode, bytes, 0);
4846
4847 /*
4848 * Now reset ->csum_bytes to what it should be. If bytes is
4849 * more than to_free then we would have free'd more space had we
4850 * not had an artificially high ->csum_bytes, so we need to free
4851 * the remainder. If bytes is the same or less then we don't
4852 * need to do anything, the other free-ers did the correct
4853 * thing.
4854 */
4855 BTRFS_I(inode)->csum_bytes = orig_csum_bytes - num_bytes;
4856 if (bytes > to_free)
4857 to_free = bytes - to_free;
4858 else
4859 to_free = 0;
4860 }
4826 spin_unlock(&BTRFS_I(inode)->lock); 4861 spin_unlock(&BTRFS_I(inode)->lock);
4827 if (dropped) 4862 if (dropped)
4828 to_free += btrfs_calc_trans_metadata_size(root, dropped); 4863 to_free += btrfs_calc_trans_metadata_size(root, dropped);