Btrfs: Fix a number of inline extent problems that Yan Zheng reported.
authorChris Mason <chris.mason@oracle.com>
Thu, 1 Nov 2007 15:28:41 +0000 (11:28 -0400)
committerChris Mason <chris.mason@oracle.com>
Thu, 25 Sep 2008 15:03:57 +0000 (11:03 -0400)
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: Chris Mason <chris.mason@oracle.com>
fs/btrfs/ctree.c
fs/btrfs/ctree.h
fs/btrfs/dir-item.c
fs/btrfs/extent_map.c
fs/btrfs/extent_map.h
fs/btrfs/file-item.c
fs/btrfs/file.c
fs/btrfs/inode.c

index 516b90224a1b7951245fbd03df2fc99127b07258..eef9c92f86d5408397c98e4e373e57b86d7f0a08 100644 (file)
@@ -1930,7 +1930,7 @@ again:
 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 */
-       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);
+       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);
index 70e143b135779bcf184cdd49936adbe71a83a5e6..d82afb618bf11fc3119a8f0c626a4a0cc7ac0aaf 100644 (file)
@@ -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);
index 6f19de41b8784897c4594a2b1edfe9f1415422b0..514a1dc337a8616bfa919efb6b525dd7d7b3b057 100644 (file)
@@ -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;
 }
index 238cb1d81d56091b2e629263a3dd100ef7486c26..44be9cfd30eeb2b5666a2e1e7241fe2d0b86b184 100644 (file)
@@ -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;
index 39d78d31b6a224ec5f850e91cbf56815e375051b..b6f6519f9ba3d8b0bfc73c21112e0c6d99d0f973 100644 (file)
@@ -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
 
index 7eb9a5412e2f09ca7698024a491e6f65c406bc80..614176e5285182938a05917e0b55ea8d7e20d55e 100644 (file)
@@ -291,7 +291,7 @@ int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
        new_item_size = blocks * BTRFS_CRC32_SIZE;
        if (new_item_size >= btrfs_item_size_nr(leaf, slot))
                return 0;
-       ret = btrfs_truncate_item(trans, root, path, new_item_size);
+       ret = btrfs_truncate_item(trans, root, path, new_item_size, 1);
        BUG_ON(ret);
        return ret;
 }
index abdd9caad94ec57f745061ff9116911c25ee835f..4e52f7ec1cbe737684ffa6d47ed2543edb1fcbc7 100644 (file)
@@ -115,8 +115,20 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
                goto fail;
        }
        if (ret == 1) {
+               struct btrfs_key found_key;
+
+               if (path->slots[0] == 0)
+                       goto insert;
+
                path->slots[0]--;
                leaf = path->nodes[0];
+               btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+
+               if (found_key.objectid != inode->i_ino)
+                       goto insert;
+
+               if (found_key.type != BTRFS_EXTENT_DATA_KEY)
+                       goto insert;
                ei = btrfs_item_ptr(leaf, path->slots[0],
                                    struct btrfs_file_extent_item);
 
@@ -152,6 +164,7 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
                        ret = btrfs_search_slot(trans, root, &key, path,
                                                offset + size - found_end, 1);
                        BUG_ON(ret != 0);
+
                        ret = btrfs_extend_item(trans, root, path,
                                                offset + size - found_end);
                        if (ret) {
@@ -292,7 +305,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
         */
        inline_size = end_pos;
        if (isize >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
-           inline_size > 8192 ||
+           inline_size > 32768 ||
            inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
                u64 last_end;
 
@@ -312,7 +325,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
                aligned_end = (pos + write_bytes + root->sectorsize - 1) &
                        ~((u64)root->sectorsize - 1);
                err = btrfs_drop_extents(trans, root, inode, start_pos,
-                                        aligned_end, end_pos, &hint_byte);
+                                        aligned_end, aligned_end, &hint_byte);
                if (err)
                        goto failed;
                err = insert_inline_extent(trans, root, inode, start_pos,
@@ -456,13 +469,15 @@ next_slot:
                        goto next_slot;
                }
 
-               /* FIXME, there's only one inline extent allowed right now */
                if (found_inline) {
                        u64 mask = root->sectorsize - 1;
                        search_start = (extent_end + mask) & ~mask;
                } else
                        search_start = extent_end;
 
+               if (end <= extent_end && start >= key.offset && found_inline) {
+                       *hint_byte = EXTENT_MAP_INLINE;
+               }
                if (end < extent_end && end >= key.offset) {
                        if (found_extent) {
                                u64 disk_bytenr =
@@ -479,8 +494,10 @@ next_slot:
                                        BUG_ON(ret);
                                }
                        }
-                       if (!found_inline)
-                               bookend = 1;
+                       bookend = 1;
+                       if (found_inline && start <= key.offset &&
+                           inline_end < extent_end)
+                               keep = 1;
                }
                /* truncate existing extent */
                if (start > key.offset) {
@@ -510,7 +527,7 @@ next_slot:
                                new_size = btrfs_file_extent_calc_inline_size(
                                                   inline_end - key.offset);
                                btrfs_truncate_item(trans, root, path,
-                                                   new_size);
+                                                   new_size, 1);
                        }
                }
                /* delete the entire extent */
@@ -551,6 +568,13 @@ next_slot:
                        if (!bookend)
                                continue;
                }
+               if (bookend && found_inline && start <= key.offset &&
+                   inline_end < extent_end) {
+                       u32 new_size;
+                       new_size = btrfs_file_extent_calc_inline_size(
+                                                  extent_end - inline_end);
+                       btrfs_truncate_item(trans, root, path, new_size, 0);
+               }
                /* create bookend, splitting the extent in two */
                if (bookend && found_extent) {
                        struct btrfs_key ins;
index 5d10b64e42df1173d04bd473aa2408ca59cfb1cf..0c65141b99930425c94e90dc9c8e264b9909aa90 100644 (file)
@@ -89,6 +89,9 @@ static int run_delalloc_range(struct inode *inode, u64 start, u64 end)
        ret = btrfs_drop_extents(trans, root, inode,
                                 start, start + num_bytes, start, &alloc_hint);
 
+       if (alloc_hint == EXTENT_MAP_INLINE)
+               goto out;
+
        ret = btrfs_alloc_extent(trans, root, inode->i_ino, num_bytes, 0,
                                 alloc_hint, (u64)-1, &ins, 1);
        if (ret) {
@@ -558,6 +561,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
        u64 item_end = 0;
        int found_extent;
        int del_item;
+       int extent_type = -1;
 
        btrfs_drop_extent_cache(inode, inode->i_size, (u64)-1);
        path = btrfs_alloc_path();
@@ -597,10 +601,15 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
                if (found_type == BTRFS_EXTENT_DATA_KEY) {
                        fi = btrfs_item_ptr(leaf, path->slots[0],
                                            struct btrfs_file_extent_item);
-                       if (btrfs_file_extent_type(leaf, fi) !=
-                           BTRFS_FILE_EXTENT_INLINE) {
+                       extent_type = btrfs_file_extent_type(leaf, fi);
+                       if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
                                item_end +=
                                    btrfs_file_extent_num_bytes(leaf, fi);
+                       } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+                               struct btrfs_item *item = btrfs_item_nr(leaf,
+                                                               path->slots[0]);
+                               item_end += btrfs_file_extent_inline_len(leaf,
+                                                                        item);
                        }
                }
                if (found_type == BTRFS_CSUM_ITEM_KEY) {
@@ -608,7 +617,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
                                                  inode->i_size);
                        BUG_ON(ret);
                }
-               if (item_end < inode->i_size) {
+               if (item_end <= inode->i_size) {
                        if (found_type == BTRFS_DIR_ITEM_KEY) {
                                found_type = BTRFS_INODE_ITEM_KEY;
                        } else if (found_type == BTRFS_EXTENT_ITEM_KEY) {
@@ -629,9 +638,10 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
                found_extent = 0;
 
                /* FIXME, shrink the extent if the ref count is only 1 */
-               if (found_type == BTRFS_EXTENT_DATA_KEY &&
-                          btrfs_file_extent_type(leaf, fi) !=
-                          BTRFS_FILE_EXTENT_INLINE) {
+               if (found_type != BTRFS_EXTENT_DATA_KEY)
+                       goto delete;
+
+               if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
                        u64 num_dec;
                        extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
                        if (!del_item) {
@@ -659,7 +669,15 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
                                        inode->i_blocks -= num_dec;
                                }
                        }
+               } else if (extent_type == BTRFS_FILE_EXTENT_INLINE &&
+                          !del_item) {
+                       u32 newsize = inode->i_size - found_key.offset;
+                       newsize = btrfs_file_extent_calc_inline_size(newsize);
+                       ret = btrfs_truncate_item(trans, root, path,
+                                                 newsize, 1);
+                       BUG_ON(ret);
                }
+delete:
                if (del_item) {
                        ret = btrfs_del_item(trans, root, path);
                        if (ret)
@@ -769,7 +787,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
                u64 pos = (inode->i_size + mask) & ~mask;
                u64 block_end = attr->ia_size | mask;
                u64 hole_size;
-               u64 alloc_hint;
+               u64 alloc_hint = 0;
 
                if (attr->ia_size <= pos)
                        goto out;
@@ -786,8 +804,11 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
                                         pos, pos + hole_size, pos,
                                         &alloc_hint);
 
-               err = btrfs_insert_file_extent(trans, root, inode->i_ino,
-                                              pos, 0, 0, hole_size);
+               if (alloc_hint != EXTENT_MAP_INLINE) {
+                       err = btrfs_insert_file_extent(trans, root,
+                                                      inode->i_ino,
+                                                      pos, 0, 0, hole_size);
+               }
                btrfs_end_transaction(trans, root);
                mutex_unlock(&root->fs_info->fs_mutex);
                unlock_extent(em_tree, pos, block_end, GFP_NOFS);
@@ -1531,8 +1552,8 @@ again:
                em->end = EXTENT_MAP_HOLE;
        }
        em->bdev = inode->i_sb->s_bdev;
-       ret = btrfs_lookup_file_extent(NULL, root, path,
-                                      objectid, start, 0);
+       ret = btrfs_lookup_file_extent(trans, root, path,
+                                      objectid, start, trans != NULL);
        if (ret < 0) {
                err = ret;
                goto out;
@@ -1627,15 +1648,23 @@ again:
                        ((u64)root->sectorsize -1);
                map = kmap(page);
                ptr = btrfs_file_extent_inline_start(item) + extent_offset;
-               read_extent_buffer(leaf, map + page_offset, ptr, copy_size);
-               
-               if (em->start + copy_size <= em->end) {
-                       size = min_t(u64, em->end + 1 - em->start,
-                               PAGE_CACHE_SIZE - page_offset) - copy_size;
-                       memset(map + page_offset + copy_size, 0, size);
+               if (create == 0 && !PageUptodate(page)) {
+                       read_extent_buffer(leaf, map + page_offset, ptr,
+                                          copy_size);
+                       flush_dcache_page(page);
+               } else if (create && PageUptodate(page)) {
+                       if (!trans) {
+                               kunmap(page);
+                               free_extent_map(em);
+                               em = NULL;
+                               btrfs_release_path(root, path);
+                               trans = btrfs_start_transaction(root, 1);
+                               goto again;
+                       }
+                       write_extent_buffer(leaf, map + page_offset, ptr,
+                                           copy_size);
+                       btrfs_mark_buffer_dirty(leaf);
                }
-
-               flush_dcache_page(page);
                kunmap(page);
                set_extent_uptodate(em_tree, em->start, em->end, GFP_NOFS);
                goto insert;