Btrfs: Allow tails larger than one page
authorChris Mason <chris.mason@oracle.com>
Mon, 15 Oct 2007 20:18:25 +0000 (16:18 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:03:56 +0000 (11:03 -0400)
Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/file.c
fs/btrfs/inode.c
fs/btrfs/super.c

index bef61ee883e1f1093b252dce81b78cc67d510252..f691542dfa1537e8d1e5a1bb3220c2fe9b997e29 100644 (file)
@@ -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,7 +1824,11 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
                                return ret;
                        }
                        mid = slot;
-                       double_split = 1;
+                       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 >
@@ -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();
        }
 
index 54c754dd9a148a0bbd7a56dfa713b92e0efc0041..18994c53106cf1125c7a74e7d12ee713a008a61a 100644 (file)
@@ -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);
index 844d8807e44a6aee49c9b9b1ec5d31dfb6875b96..1af2b6534dadd190db5585ad67e9afacadf93401 100644 (file)
@@ -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,25 +108,97 @@ 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_insert_empty_item(trans, root, path, &key,
-                                     datasize);
-       if (ret) {
+       ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
+       if (ret < 0) {
                err = ret;
                goto fail;
        }
-       leaf = path->nodes[0];
-       ei = btrfs_item_ptr(leaf, path->slots[0],
-                           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);
-
-       kaddr = kmap_atomic(page, KM_USER1);
-       write_extent_buffer(leaf, kaddr + page_offset, ptr, size);
-       kunmap_atomic(kaddr, KM_USER1);
+       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];
+               ei = btrfs_item_ptr(leaf, path->slots[0],
+                                   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) + offset;
+
+       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,8 +475,8 @@ next_slot:
                                        BUG_ON(ret);
                                }
                        }
-                       WARN_ON(found_inline);
-                       bookend = 1;
+                       if (!found_inline)
+                               bookend = 1;
                }
                /* truncate existing extent */
                if (start > key.offset) {
@@ -425,8 +499,14 @@ next_slot:
                                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 */
index d6b3a55ed8e0ce462b1522e882ace5a8fd72a018..84f496c838c99d5e25b35d57a820947634946f58 100644 (file)
@@ -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 @@ again:
        } 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 @@ again:
                        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);
index 5c4370f3a5b85046a5f64e677b35bdd1951d6f98..f94aa1f97a0a8999f6c9b1b0e46585a475bd94ed 100644 (file)
@@ -280,7 +280,6 @@ error_s:
 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;
 }