Btrfs: delete checksum items before marking blocks free
authorChris Mason <chris.mason@oracle.com>
Tue, 16 Dec 2008 18:51:01 +0000 (13:51 -0500)
committerChris Mason <chris.mason@oracle.com>
Tue, 16 Dec 2008 18:51:01 +0000 (13:51 -0500)
Btrfs maintains a cache of blocks available for allocation in ram.  The
code that frees extents was marking the extents free and then deleting
the checksum items.

This meant it was possible the extent would be reallocated before the
checksum item was actually deleted, leading to races and other
problems as the checksums were updated for the newly allocated extent.

The fix is to delete the checksum before marking the extent free.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/extent-tree.c
fs/btrfs/file-item.c

index fbd6a8f28b52c4748ba9d7a805e425633c5bf5e2..9ef2a2be2686714518fc839d382f8f4249f35b96 100644 (file)
@@ -2477,15 +2477,15 @@ static int __free_extent(struct btrfs_trans_handle *trans,
                                      num_to_del);
                BUG_ON(ret);
                btrfs_release_path(extent_root, path);
-               ret = update_block_group(trans, root, bytenr, num_bytes, 0,
-                                        mark_free);
-               BUG_ON(ret);
 
                if (owner_objectid >= BTRFS_FIRST_FREE_OBJECTID) {
                        ret = btrfs_del_csums(trans, root, bytenr, num_bytes);
                        BUG_ON(ret);
                }
 
+               ret = update_block_group(trans, root, bytenr, num_bytes, 0,
+                                        mark_free);
+               BUG_ON(ret);
 #ifdef BIO_RW_DISCARD
                /* Tell the block device(s) that the sectors can be discarded */
                ret = btrfs_map_block(&root->fs_info->mapping_tree, READ,
index df0447632dbd1c495ba7369525abeca0991cec9e..7acadf3b742a3feed260ab58e9ec834e2bef3a44 100644 (file)
@@ -537,6 +537,8 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
                if (key.offset >= bytenr && csum_end <= end_byte) {
                        ret = btrfs_del_item(trans, root, path);
                        BUG_ON(ret);
+                       if (key.offset == bytenr)
+                               break;
                } else if (key.offset < bytenr && csum_end > end_byte) {
                        unsigned long offset;
                        unsigned long shift_len;
@@ -583,6 +585,8 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
                        ret = truncate_one_csum(trans, root, path,
                                                &key, bytenr, len);
                        BUG_ON(ret);
+                       if (key.offset < bytenr)
+                               break;
                }
                btrfs_release_path(root, path);
        }