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: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index abdd9ca..4e52f7e 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -115,8 +115,20 @@
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 @@
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 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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;