Commit 3326d1b0 authored by Chris Mason's avatar Chris Mason
Browse files

Btrfs: Allow tails larger than one page

parent 4dc11904
Loading
Loading
Loading
Loading
+19 −5
Original line number Diff line number Diff line
@@ -1766,8 +1766,9 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root

	/* first try to make some room by pushing left and right */
	wret = push_leaf_left(trans, root, path, data_size);
	if (wret < 0)
	if (wret < 0) {
		return wret;
	}
	if (wret) {
		wret = push_leaf_right(trans, root, path, data_size);
		if (wret < 0)
@@ -1777,8 +1778,9 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root

	/* did the pushes work? */
	if (btrfs_leaf_free_space(root, l) >=
	    sizeof(struct btrfs_item) + data_size)
	    sizeof(struct btrfs_item) + data_size) {
		return 0;
	}

	if (!path->nodes[1]) {
		ret = insert_new_root(trans, root, path, 1);
@@ -1822,8 +1824,12 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
				return ret;
			}
			mid = slot;
			if (mid != nritems &&
			    leaf_space_used(l, mid, nritems - mid) +
			    space_needed > BTRFS_LEAF_DATA_SIZE(root)) {
				double_split = 1;
			}
		}
	} else {
		if (leaf_space_used(l, 0, mid + 1) + space_needed >
			BTRFS_LEAF_DATA_SIZE(root)) {
@@ -1910,8 +1916,9 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root

	BUG_ON(path->slots[0] < 0);

	if (!double_split)
	if (!double_split) {
		return ret;
	}

	right = btrfs_alloc_free_block(trans, root, root->leafsize,
				       l->start, 0);
@@ -2048,7 +2055,11 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans,
	old_data = btrfs_item_end_nr(leaf, slot);

	BUG_ON(slot < 0);
	BUG_ON(slot >= nritems);
	if (slot >= nritems) {
		btrfs_print_leaf(root, leaf);
		printk("slot %d too large, nritems %d\n", slot, nritems);
		BUG_ON(1);
	}

	/*
	 * item0..itemN ... dataN.offset..dataN.size .. data0.size
@@ -2132,6 +2143,9 @@ int btrfs_insert_empty_item(struct btrfs_trans_handle *trans,

	if (btrfs_leaf_free_space(root, leaf) <
	    sizeof(struct btrfs_item) + data_size) {
		btrfs_print_leaf(root, leaf);
		printk("not enough freespace need %u have %d\n",
		       data_size, btrfs_leaf_free_space(root, leaf));
		BUG();
	}

+1 −1
Original line number Diff line number Diff line
@@ -1137,7 +1137,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end);
extern struct file_operations btrfs_file_operations;
int btrfs_drop_extents(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root, struct inode *inode,
		       u64 start, u64 end, u64 *hint_block);
		       u64 start, u64 end, u64 inline_end, u64 *hint_block);
/* tree-defrag.c */
int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
			struct btrfs_root *root, int cache_only);
+116 −36
Original line number Diff line number Diff line
@@ -82,8 +82,9 @@ static void btrfs_drop_pages(struct page **pages, size_t num_pages)

static int insert_inline_extent(struct btrfs_trans_handle *trans,
				struct btrfs_root *root, struct inode *inode,
				u64 offset, ssize_t size,
				struct page *page, size_t page_offset)
				u64 offset, size_t size,
				struct page **pages, size_t page_offset,
				int num_pages)
{
	struct btrfs_key key;
	struct btrfs_path *path;
@@ -91,9 +92,12 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
	char *kaddr;
	unsigned long ptr;
	struct btrfs_file_extent_item *ei;
	struct page *page;
	u32 datasize;
	int err = 0;
	int ret;
	int i;
	ssize_t cur_size;

	path = btrfs_alloc_path();
	if (!path)
@@ -104,13 +108,71 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
	key.objectid = inode->i_ino;
	key.offset = offset;
	btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY);
	BUG_ON(size >= PAGE_CACHE_SIZE);
	datasize = btrfs_file_extent_calc_inline_size(size);
	datasize = btrfs_file_extent_calc_inline_size(offset + size);

	ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
	if (ret < 0) {
		err = ret;
		goto fail;
	}
	if (ret == 1) {
		path->slots[0]--;
		leaf = path->nodes[0];
		ei = btrfs_item_ptr(leaf, path->slots[0],
				    struct btrfs_file_extent_item);

		if (btrfs_file_extent_type(leaf, ei) !=
		    BTRFS_FILE_EXTENT_INLINE) {
			goto insert;
		}
		btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
		ret = 0;
	}
	if (ret == 0) {
		u32 found_size;
		u64 found_start;

		leaf = path->nodes[0];
		ei = btrfs_item_ptr(leaf, path->slots[0],
				    struct btrfs_file_extent_item);

		if (btrfs_file_extent_type(leaf, ei) !=
		    BTRFS_FILE_EXTENT_INLINE) {
			err = ret;
			btrfs_print_leaf(root, leaf);
			printk("found wasn't inline offset %Lu inode %lu\n",
			       offset, inode->i_ino);
			goto fail;
		}
		found_start = key.offset;
		found_size = btrfs_file_extent_inline_len(leaf,
					  btrfs_item_nr(leaf, path->slots[0]));

		if (found_size < offset + size) {
			btrfs_release_path(root, path);
			ret = btrfs_search_slot(trans, root, &key, path,
						offset + size - found_size -
						found_start, 1);
			BUG_ON(ret != 0);
			ret = btrfs_extend_item(trans, root, path,
						offset + size - found_size -
						found_start);
			if (ret) {
				err = ret;
				goto fail;
			}
			leaf = path->nodes[0];
			ei = btrfs_item_ptr(leaf, path->slots[0],
					    struct btrfs_file_extent_item);
		}
	} else {
insert:
		btrfs_release_path(root, path);
		ret = btrfs_insert_empty_item(trans, root, path, &key,
					      datasize);
		if (ret) {
			err = ret;
			printk("got bad ret %d\n", ret);
			goto fail;
		}
		leaf = path->nodes[0];
@@ -118,11 +180,25 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
				    struct btrfs_file_extent_item);
		btrfs_set_file_extent_generation(leaf, ei, trans->transid);
		btrfs_set_file_extent_type(leaf, ei, BTRFS_FILE_EXTENT_INLINE);
	ptr = btrfs_file_extent_inline_start(ei);
	}
	ptr = btrfs_file_extent_inline_start(ei) + offset;

	kaddr = kmap_atomic(page, KM_USER1);
	write_extent_buffer(leaf, kaddr + page_offset, ptr, size);
	kunmap_atomic(kaddr, KM_USER1);
	cur_size = size;
	i = 0;
	while (size > 0) {
		page = pages[i];
		kaddr = kmap_atomic(page, KM_USER0);
		cur_size = min(PAGE_CACHE_SIZE - page_offset, size);
		write_extent_buffer(leaf, kaddr + page_offset, ptr, cur_size);
		kunmap_atomic(kaddr, KM_USER0);
		page_offset = 0;
		ptr += cur_size;
		size -= cur_size;
		if (i >= num_pages) {
			printk("i %d num_pages %d\n", i, num_pages);
		}
		i++;
	}
	btrfs_mark_buffer_dirty(leaf);
fail:
	btrfs_free_path(path);
@@ -193,6 +269,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
			err = btrfs_drop_extents(trans, root, inode,
						 last_pos_in_file,
						 last_pos_in_file + hole_size,
						 last_pos_in_file,
						 &hint_byte);
			if (err)
				goto failed;
@@ -210,11 +287,12 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
	 * either allocate an extent for the new bytes or setup the key
	 * to show we are doing inline data in the extent
	 */
	inline_size = end_pos - start_pos;
	if (isize >= PAGE_CACHE_SIZE || pos + write_bytes < inode->i_size ||
	    inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
	    inline_size >= PAGE_CACHE_SIZE) {
	inline_size = end_pos;
	if (isize >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
	    inline_size > 16384 ||
	    inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
		u64 last_end;

		for (i = 0; i < num_pages; i++) {
			struct page *p = pages[i];
			SetPageUptodate(p);
@@ -225,22 +303,18 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
		set_extent_delalloc(em_tree, start_pos, end_of_last_block,
				 GFP_NOFS);
	} else {
		struct page *p = pages[0];
		u64 aligned_end;
		/* step one, delete the existing extents in this range */
		aligned_end = (pos + write_bytes + root->sectorsize - 1) &
			~((u64)root->sectorsize - 1);
		err = btrfs_drop_extents(trans, root, inode, start_pos,
			 (pos + write_bytes + root->sectorsize -1) &
			 ~((u64)root->sectorsize - 1), &hint_byte);
					 aligned_end, end_pos, &hint_byte);
		if (err)
			goto failed;

		err = insert_inline_extent(trans, root, inode, start_pos,
					   end_pos - start_pos, p, 0);
					   end_pos - start_pos, pages, 0,
					   num_pages);
		BUG_ON(err);
		em->start = start_pos;
		em->end = end_pos - 1;
		em->block_start = EXTENT_MAP_INLINE;
		em->block_end = EXTENT_MAP_INLINE;
		add_extent_mapping(em_tree, em);
	}
	if (end_pos > isize) {
		i_size_write(inode, end_pos);
@@ -285,7 +359,7 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
 */
int btrfs_drop_extents(struct btrfs_trans_handle *trans,
		       struct btrfs_root *root, struct inode *inode,
		       u64 start, u64 end, u64 *hint_byte)
		       u64 start, u64 end, u64 inline_end, u64 *hint_byte)
{
	int ret;
	struct btrfs_key key;
@@ -401,7 +475,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
					BUG_ON(ret);
				}
			}
			WARN_ON(found_inline);
			if (!found_inline)
				bookend = 1;
		}
		/* truncate existing extent */
@@ -425,8 +499,14 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
				btrfs_set_file_extent_num_bytes(leaf, extent,
								new_num);
				btrfs_mark_buffer_dirty(leaf);
			} else {
				WARN_ON(1);
			} else if (end > extent_end &&
				   key.offset < inline_end &&
				   inline_end < extent_end) {
				u32 new_size;
				new_size = btrfs_file_extent_calc_inline_size(
						   inline_end - key.offset);
				btrfs_truncate_item(trans, root, path,
						    new_size);
			}
		}
		/* delete the entire extent */
+20 −12
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ static int run_delalloc_range(struct inode *inode, u64 start, u64 end)
	BUG_ON(!trans);
	num_bytes = (end - start + blocksize) & ~(blocksize - 1);
	ret = btrfs_drop_extents(trans, root, inode,
				 start, start + num_bytes, &alloc_hint);
				 start, start + num_bytes, start, &alloc_hint);

	ret = btrfs_alloc_extent(trans, root, inode->i_ino, num_bytes, 0,
				 alloc_hint, (u64)-1, &ins, 1);
@@ -776,7 +776,8 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
		trans = btrfs_start_transaction(root, 1);
		btrfs_set_trans_block_group(trans, inode);
		err = btrfs_drop_extents(trans, root, inode,
					 pos, pos + hole_size, &alloc_hint);
					 pos, pos + hole_size, pos,
					 &alloc_hint);

		err = btrfs_insert_file_extent(trans, root, inode->i_ino,
					       pos, 0, 0, hole_size);
@@ -1581,7 +1582,9 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
	} else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
		unsigned long ptr;
		char *map;
		u32 size;
		size_t size;
		size_t extent_offset;
		size_t copy_size;

		size = btrfs_file_extent_inline_len(leaf, btrfs_item_nr(leaf,
						    path->slots[0]));
@@ -1600,26 +1603,31 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
			goto not_found_em;
		}

		extent_offset = (page->index << PAGE_CACHE_SHIFT) -
			extent_start;
		ptr = btrfs_file_extent_inline_start(item) + extent_offset;
		map = kmap(page);
		copy_size = min(PAGE_CACHE_SIZE - page_offset,
				size - extent_offset);

		em->block_start = EXTENT_MAP_INLINE;
		em->block_end = EXTENT_MAP_INLINE;
		em->start = extent_start;
		em->end = extent_end;
		em->start = extent_start + extent_offset;
		em->end = (em->start + copy_size -1) |
			((u64)root->sectorsize -1);

		if (!page) {
			goto insert;
		}

		ptr = btrfs_file_extent_inline_start(item);
		map = kmap(page);
		read_extent_buffer(leaf, map + page_offset, ptr, size);
		read_extent_buffer(leaf, map + page_offset, ptr, copy_size);
		/*
		memset(map + page_offset + size, 0,
		       root->sectorsize - (page_offset + size));
		memset(map + page_offset + copy_size, 0,
		       PAGE_CACHE_SIZE - copy_size - page_offset);
		       */
		flush_dcache_page(page);
		kunmap(page);
		set_extent_uptodate(em_tree, extent_start,
				    extent_end, GFP_NOFS);
		set_extent_uptodate(em_tree, em->start, em->end, GFP_NOFS);
		goto insert;
	} else {
		printk("unkknown found_type %d\n", found_type);
+0 −2
Original line number Diff line number Diff line
@@ -280,7 +280,6 @@ int btrfs_get_sb_bdev(struct file_system_type *fs_type,
error_bdev:
	close_bdev_excl(bdev);
error:
printk("get_sb failed\n");
	return error;
}
/* end copy & paste */
@@ -295,7 +294,6 @@ static int btrfs_get_sb(struct file_system_type *fs_type,
	ret = btrfs_get_sb_bdev(fs_type, flags, dev_name, data,
			btrfs_fill_super, mnt,
			subvol_name ? subvol_name : "default");
printk("btrfs_get_sb returns %d\n", ret);
	return ret;
}