Commit 179e29e4 authored by Chris Mason's avatar Chris Mason
Browse files

Btrfs: Fix a number of inline extent problems that Yan Zheng reported.



The fixes do a number of things:

1) Most btrfs_drop_extent callers will try to leave the inline extents in
place.  It can truncate bytes off the beginning of the inline extent if
required.

2) writepage can now update the inline extent, allowing mmap writes to
go directly into the inline extent.

3) btrfs_truncate_in_transaction truncates inline extents

4) extent_map.c fixed to not merge inline extent mappings and hole
mappings together

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 35ebb934
Loading
Loading
Loading
Loading
+46 −6
Original line number Diff line number Diff line
@@ -1930,7 +1930,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
			struct btrfs_root *root,
			struct btrfs_path *path,
			u32 new_size)
			u32 new_size, int from_end)
{
	int ret = 0;
	int slot;
@@ -1946,13 +1946,17 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,

	slot_orig = path->slots[0];
	leaf = path->nodes[0];
	slot = path->slots[0];

	old_size = btrfs_item_size_nr(leaf, slot);
	if (old_size == new_size)
		return 0;

	nritems = btrfs_header_nritems(leaf);
	data_end = leaf_data_end(root, leaf);

	slot = path->slots[0];
	old_data_start = btrfs_item_offset_nr(leaf, slot);
	old_size = btrfs_item_size_nr(leaf, slot); BUG_ON(old_size <= new_size);

	size_diff = old_size - new_size;

	BUG_ON(slot < 0);
@@ -1984,9 +1988,45 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
	}

	/* shift the data */
	if (from_end) {
		memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
			      data_end + size_diff, btrfs_leaf_data(leaf) +
			      data_end, old_data_start + new_size - data_end);
	} else {
		struct btrfs_disk_key disk_key;
		u64 offset;

		btrfs_item_key(leaf, &disk_key, slot);

		if (btrfs_disk_key_type(&disk_key) == BTRFS_EXTENT_DATA_KEY) {
			unsigned long ptr;
			struct btrfs_file_extent_item *fi;

			fi = btrfs_item_ptr(leaf, slot,
					    struct btrfs_file_extent_item);
			fi = (struct btrfs_file_extent_item *)(
			     (unsigned long)fi - size_diff);

			if (btrfs_file_extent_type(leaf, fi) ==
			    BTRFS_FILE_EXTENT_INLINE) {
				ptr = btrfs_item_ptr_offset(leaf, slot);
				memmove_extent_buffer(leaf, ptr,
				        (unsigned long)fi,
				        offsetof(struct btrfs_file_extent_item,
						 disk_bytenr));
			}
		}

		memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
			      data_end + size_diff, btrfs_leaf_data(leaf) +
			      data_end, old_data_start - data_end);

		offset = btrfs_disk_key_offset(&disk_key);
		btrfs_set_disk_key_offset(&disk_key, offset + size_diff);
		btrfs_set_item_key(leaf, &disk_key, slot);
		if (slot == 0)
			fixup_low_keys(trans, root, path, &disk_key, 1);
	}

	item = btrfs_item_nr(leaf, slot);
	btrfs_set_item_size(leaf, item, new_size);
+1 −1
Original line number Diff line number Diff line
@@ -907,7 +907,7 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
			struct btrfs_root *root,
			struct btrfs_path *path,
			u32 new_size);
			u32 new_size, int from_end);
int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
		      *root, struct btrfs_key *key, struct btrfs_path *p, int
		      ins_len, int cow);
+1 −1
Original line number Diff line number Diff line
@@ -249,7 +249,7 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
		memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
			item_len - (ptr + sub_item_len - start));
		ret = btrfs_truncate_item(trans, root, path,
					  item_len - sub_item_len);
					  item_len - sub_item_len, 1);
	}
	return 0;
}
+8 −3
Original line number Diff line number Diff line
@@ -263,7 +263,12 @@ int add_extent_mapping(struct extent_map_tree *tree,
		if (prev && prev->end + 1 == em->start &&
		    ((em->block_start == EXTENT_MAP_HOLE &&
		      prev->block_start == EXTENT_MAP_HOLE) ||
			     (em->block_start == prev->block_end + 1))) {
		     (em->block_start == EXTENT_MAP_INLINE &&
		      prev->block_start == EXTENT_MAP_INLINE) ||
		     (em->block_start == EXTENT_MAP_DELALLOC &&
		      prev->block_start == EXTENT_MAP_DELALLOC) ||
		     (em->block_start < EXTENT_MAP_DELALLOC - 1 &&
		      em->block_start == prev->block_end + 1))) {
			em->start = prev->start;
			em->block_start = prev->block_start;
			rb_erase(&prev->rb_node, &tree->map);
@@ -1618,13 +1623,13 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
	u64 extent_offset;
	u64 last_byte = i_size_read(inode);
	u64 block_start;
	u64 iosize;
	sector_t sector;
	struct extent_map *em;
	struct block_device *bdev;
	int ret;
	int nr = 0;
	size_t page_offset = 0;
	size_t iosize;
	size_t blocksize;
	loff_t i_size = i_size_read(inode);
	unsigned long end_index = i_size >> PAGE_CACHE_SHIFT;
@@ -1684,7 +1689,7 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
			clear_extent_dirty(tree, cur, page_end, GFP_NOFS);
			break;
		}
		em = get_extent(inode, page, page_offset, cur, end, 0);
		em = get_extent(inode, page, page_offset, cur, end, 1);
		if (IS_ERR(em) || !em) {
			SetPageError(page);
			break;
+0 −1
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@
 * page->private values.  Every page that is controlled by the extent
 * map has page->private set to one.
 */

#define EXTENT_PAGE_PRIVATE 1
#define EXTENT_PAGE_PRIVATE_FIRST_PAGE 3

Loading