소스 검색

Btrfs: delete checksum items before marking blocks free

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>
Chris Mason 16 년 전
부모
커밋
dcbdd4dcb9
2개의 변경된 파일7개의 추가작업 그리고 3개의 파일을 삭제
  1. 3 3
      fs/btrfs/extent-tree.c
  2. 4 0
      fs/btrfs/file-item.c

+ 3 - 3
fs/btrfs/extent-tree.c

@@ -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,

+ 4 - 0
fs/btrfs/file-item.c

@@ -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);
 	}