aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik2013-03-25 15:03:35 -0500
committerGreg Kroah-Hartman2013-04-05 11:26:16 -0500
commit4ee541ed9f8c55799222434d5c20a3a6417294a9 (patch)
tree736f8da719392720bc37222373782be601fc1c2e /fs
parent711f821384218965ad0071501936bd8e1a0ed5ca (diff)
downloadkernel-omap-4ee541ed9f8c55799222434d5c20a3a6417294a9.tar.gz
kernel-omap-4ee541ed9f8c55799222434d5c20a3a6417294a9.tar.xz
kernel-omap-4ee541ed9f8c55799222434d5c20a3a6417294a9.zip
Btrfs: fix space leak when we fail to reserve metadata space
commit f4881bc7a83eff263789dd524b7c269d138d4af5 upstream. 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 Reported-by: David Sterba <dsterba@suse.cz> Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Lingzhu Xiang <lxiang@redhat.com> Reviewed-by: CAI Qian <caiqian@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs')
-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 1344074c5ebb..d1704126faf2 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4601,14 +4601,49 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
4601 * If the inodes csum_bytes is the same as the original 4601 * If the inodes csum_bytes is the same as the original
4602 * csum_bytes then we know we haven't raced with any free()ers 4602 * csum_bytes then we know we haven't raced with any free()ers
4603 * so we can just reduce our inodes csum bytes and carry on. 4603 * so we can just reduce our inodes csum bytes and carry on.
4604 * Otherwise we have to do the normal free thing to account for
4605 * the case that the free side didn't free up its reserve
4606 * because of this outstanding reservation.
4607 */ 4604 */
4608 if (BTRFS_I(inode)->csum_bytes == csum_bytes) 4605 if (BTRFS_I(inode)->csum_bytes == csum_bytes) {
4609 calc_csum_metadata_size(inode, num_bytes, 0); 4606 calc_csum_metadata_size(inode, num_bytes, 0);
4610 else 4607 } else {
4611 to_free = calc_csum_metadata_size(inode, num_bytes, 0); 4608 u64 orig_csum_bytes = BTRFS_I(inode)->csum_bytes;
4609 u64 bytes;
4610
4611 /*
4612 * This is tricky, but first we need to figure out how much we
4613 * free'd from any free-ers that occured during this
4614 * reservation, so we reset ->csum_bytes to the csum_bytes
4615 * before we dropped our lock, and then call the free for the
4616 * number of bytes that were freed while we were trying our
4617 * reservation.
4618 */
4619 bytes = csum_bytes - BTRFS_I(inode)->csum_bytes;
4620 BTRFS_I(inode)->csum_bytes = csum_bytes;
4621 to_free = calc_csum_metadata_size(inode, bytes, 0);
4622
4623
4624 /*
4625 * Now we need to see how much we would have freed had we not
4626 * been making this reservation and our ->csum_bytes were not
4627 * artificially inflated.
4628 */
4629 BTRFS_I(inode)->csum_bytes = csum_bytes - num_bytes;
4630 bytes = csum_bytes - orig_csum_bytes;
4631 bytes = calc_csum_metadata_size(inode, bytes, 0);
4632
4633 /*
4634 * Now reset ->csum_bytes to what it should be. If bytes is
4635 * more than to_free then we would have free'd more space had we
4636 * not had an artificially high ->csum_bytes, so we need to free
4637 * the remainder. If bytes is the same or less then we don't
4638 * need to do anything, the other free-ers did the correct
4639 * thing.
4640 */
4641 BTRFS_I(inode)->csum_bytes = orig_csum_bytes - num_bytes;
4642 if (bytes > to_free)
4643 to_free = bytes - to_free;
4644 else
4645 to_free = 0;
4646 }
4612 spin_unlock(&BTRFS_I(inode)->lock); 4647 spin_unlock(&BTRFS_I(inode)->lock);
4613 if (dropped) 4648 if (dropped)
4614 to_free += btrfs_calc_trans_metadata_size(root, dropped); 4649 to_free += btrfs_calc_trans_metadata_size(root, dropped);