Skip to content

Commit 179e29e

Browse files
committed
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 <[email protected]>
1 parent 35ebb93 commit 179e29e

File tree

8 files changed

+135
-38
lines changed

8 files changed

+135
-38
lines changed

fs/btrfs/ctree.c

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1930,7 +1930,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
19301930
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
19311931
struct btrfs_root *root,
19321932
struct btrfs_path *path,
1933-
u32 new_size)
1933+
u32 new_size, int from_end)
19341934
{
19351935
int ret = 0;
19361936
int slot;
@@ -1946,13 +1946,17 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
19461946

19471947
slot_orig = path->slots[0];
19481948
leaf = path->nodes[0];
1949+
slot = path->slots[0];
1950+
1951+
old_size = btrfs_item_size_nr(leaf, slot);
1952+
if (old_size == new_size)
1953+
return 0;
19491954

19501955
nritems = btrfs_header_nritems(leaf);
19511956
data_end = leaf_data_end(root, leaf);
19521957

1953-
slot = path->slots[0];
19541958
old_data_start = btrfs_item_offset_nr(leaf, slot);
1955-
old_size = btrfs_item_size_nr(leaf, slot); BUG_ON(old_size <= new_size);
1959+
19561960
size_diff = old_size - new_size;
19571961

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

19861990
/* shift the data */
1987-
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
1988-
data_end + size_diff, btrfs_leaf_data(leaf) +
1989-
data_end, old_data_start + new_size - data_end);
1991+
if (from_end) {
1992+
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
1993+
data_end + size_diff, btrfs_leaf_data(leaf) +
1994+
data_end, old_data_start + new_size - data_end);
1995+
} else {
1996+
struct btrfs_disk_key disk_key;
1997+
u64 offset;
1998+
1999+
btrfs_item_key(leaf, &disk_key, slot);
2000+
2001+
if (btrfs_disk_key_type(&disk_key) == BTRFS_EXTENT_DATA_KEY) {
2002+
unsigned long ptr;
2003+
struct btrfs_file_extent_item *fi;
2004+
2005+
fi = btrfs_item_ptr(leaf, slot,
2006+
struct btrfs_file_extent_item);
2007+
fi = (struct btrfs_file_extent_item *)(
2008+
(unsigned long)fi - size_diff);
2009+
2010+
if (btrfs_file_extent_type(leaf, fi) ==
2011+
BTRFS_FILE_EXTENT_INLINE) {
2012+
ptr = btrfs_item_ptr_offset(leaf, slot);
2013+
memmove_extent_buffer(leaf, ptr,
2014+
(unsigned long)fi,
2015+
offsetof(struct btrfs_file_extent_item,
2016+
disk_bytenr));
2017+
}
2018+
}
2019+
2020+
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
2021+
data_end + size_diff, btrfs_leaf_data(leaf) +
2022+
data_end, old_data_start - data_end);
2023+
2024+
offset = btrfs_disk_key_offset(&disk_key);
2025+
btrfs_set_disk_key_offset(&disk_key, offset + size_diff);
2026+
btrfs_set_item_key(leaf, &disk_key, slot);
2027+
if (slot == 0)
2028+
fixup_low_keys(trans, root, path, &disk_key, 1);
2029+
}
19902030

19912031
item = btrfs_item_nr(leaf, slot);
19922032
btrfs_set_item_size(leaf, item, new_size);

fs/btrfs/ctree.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,7 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root
907907
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
908908
struct btrfs_root *root,
909909
struct btrfs_path *path,
910-
u32 new_size);
910+
u32 new_size, int from_end);
911911
int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
912912
*root, struct btrfs_key *key, struct btrfs_path *p, int
913913
ins_len, int cow);

fs/btrfs/dir-item.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
249249
memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
250250
item_len - (ptr + sub_item_len - start));
251251
ret = btrfs_truncate_item(trans, root, path,
252-
item_len - sub_item_len);
252+
item_len - sub_item_len, 1);
253253
}
254254
return 0;
255255
}

fs/btrfs/extent_map.c

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,12 @@ int add_extent_mapping(struct extent_map_tree *tree,
263263
if (prev && prev->end + 1 == em->start &&
264264
((em->block_start == EXTENT_MAP_HOLE &&
265265
prev->block_start == EXTENT_MAP_HOLE) ||
266-
(em->block_start == prev->block_end + 1))) {
266+
(em->block_start == EXTENT_MAP_INLINE &&
267+
prev->block_start == EXTENT_MAP_INLINE) ||
268+
(em->block_start == EXTENT_MAP_DELALLOC &&
269+
prev->block_start == EXTENT_MAP_DELALLOC) ||
270+
(em->block_start < EXTENT_MAP_DELALLOC - 1 &&
271+
em->block_start == prev->block_end + 1))) {
267272
em->start = prev->start;
268273
em->block_start = prev->block_start;
269274
rb_erase(&prev->rb_node, &tree->map);
@@ -1618,13 +1623,13 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
16181623
u64 extent_offset;
16191624
u64 last_byte = i_size_read(inode);
16201625
u64 block_start;
1626+
u64 iosize;
16211627
sector_t sector;
16221628
struct extent_map *em;
16231629
struct block_device *bdev;
16241630
int ret;
16251631
int nr = 0;
16261632
size_t page_offset = 0;
1627-
size_t iosize;
16281633
size_t blocksize;
16291634
loff_t i_size = i_size_read(inode);
16301635
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,
16841689
clear_extent_dirty(tree, cur, page_end, GFP_NOFS);
16851690
break;
16861691
}
1687-
em = get_extent(inode, page, page_offset, cur, end, 0);
1692+
em = get_extent(inode, page, page_offset, cur, end, 1);
16881693
if (IS_ERR(em) || !em) {
16891694
SetPageError(page);
16901695
break;

fs/btrfs/extent_map.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
* page->private values. Every page that is controlled by the extent
2525
* map has page->private set to one.
2626
*/
27-
2827
#define EXTENT_PAGE_PRIVATE 1
2928
#define EXTENT_PAGE_PRIVATE_FIRST_PAGE 3
3029

fs/btrfs/file-item.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
291291
new_item_size = blocks * BTRFS_CRC32_SIZE;
292292
if (new_item_size >= btrfs_item_size_nr(leaf, slot))
293293
return 0;
294-
ret = btrfs_truncate_item(trans, root, path, new_item_size);
294+
ret = btrfs_truncate_item(trans, root, path, new_item_size, 1);
295295
BUG_ON(ret);
296296
return ret;
297297
}

fs/btrfs/file.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,20 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
115115
goto fail;
116116
}
117117
if (ret == 1) {
118+
struct btrfs_key found_key;
119+
120+
if (path->slots[0] == 0)
121+
goto insert;
122+
118123
path->slots[0]--;
119124
leaf = path->nodes[0];
125+
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
126+
127+
if (found_key.objectid != inode->i_ino)
128+
goto insert;
129+
130+
if (found_key.type != BTRFS_EXTENT_DATA_KEY)
131+
goto insert;
120132
ei = btrfs_item_ptr(leaf, path->slots[0],
121133
struct btrfs_file_extent_item);
122134

@@ -152,6 +164,7 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
152164
ret = btrfs_search_slot(trans, root, &key, path,
153165
offset + size - found_end, 1);
154166
BUG_ON(ret != 0);
167+
155168
ret = btrfs_extend_item(trans, root, path,
156169
offset + size - found_end);
157170
if (ret) {
@@ -292,7 +305,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
292305
*/
293306
inline_size = end_pos;
294307
if (isize >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
295-
inline_size > 8192 ||
308+
inline_size > 32768 ||
296309
inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
297310
u64 last_end;
298311

@@ -312,7 +325,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
312325
aligned_end = (pos + write_bytes + root->sectorsize - 1) &
313326
~((u64)root->sectorsize - 1);
314327
err = btrfs_drop_extents(trans, root, inode, start_pos,
315-
aligned_end, end_pos, &hint_byte);
328+
aligned_end, aligned_end, &hint_byte);
316329
if (err)
317330
goto failed;
318331
err = insert_inline_extent(trans, root, inode, start_pos,
@@ -456,13 +469,15 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
456469
goto next_slot;
457470
}
458471

459-
/* FIXME, there's only one inline extent allowed right now */
460472
if (found_inline) {
461473
u64 mask = root->sectorsize - 1;
462474
search_start = (extent_end + mask) & ~mask;
463475
} else
464476
search_start = extent_end;
465477

478+
if (end <= extent_end && start >= key.offset && found_inline) {
479+
*hint_byte = EXTENT_MAP_INLINE;
480+
}
466481
if (end < extent_end && end >= key.offset) {
467482
if (found_extent) {
468483
u64 disk_bytenr =
@@ -479,8 +494,10 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
479494
BUG_ON(ret);
480495
}
481496
}
482-
if (!found_inline)
483-
bookend = 1;
497+
bookend = 1;
498+
if (found_inline && start <= key.offset &&
499+
inline_end < extent_end)
500+
keep = 1;
484501
}
485502
/* truncate existing extent */
486503
if (start > key.offset) {
@@ -510,7 +527,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
510527
new_size = btrfs_file_extent_calc_inline_size(
511528
inline_end - key.offset);
512529
btrfs_truncate_item(trans, root, path,
513-
new_size);
530+
new_size, 1);
514531
}
515532
}
516533
/* delete the entire extent */
@@ -551,6 +568,13 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
551568
if (!bookend)
552569
continue;
553570
}
571+
if (bookend && found_inline && start <= key.offset &&
572+
inline_end < extent_end) {
573+
u32 new_size;
574+
new_size = btrfs_file_extent_calc_inline_size(
575+
extent_end - inline_end);
576+
btrfs_truncate_item(trans, root, path, new_size, 0);
577+
}
554578
/* create bookend, splitting the extent in two */
555579
if (bookend && found_extent) {
556580
struct btrfs_key ins;

fs/btrfs/inode.c

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ static int run_delalloc_range(struct inode *inode, u64 start, u64 end)
8989
ret = btrfs_drop_extents(trans, root, inode,
9090
start, start + num_bytes, start, &alloc_hint);
9191

92+
if (alloc_hint == EXTENT_MAP_INLINE)
93+
goto out;
94+
9295
ret = btrfs_alloc_extent(trans, root, inode->i_ino, num_bytes, 0,
9396
alloc_hint, (u64)-1, &ins, 1);
9497
if (ret) {
@@ -558,6 +561,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
558561
u64 item_end = 0;
559562
int found_extent;
560563
int del_item;
564+
int extent_type = -1;
561565

562566
btrfs_drop_extent_cache(inode, inode->i_size, (u64)-1);
563567
path = btrfs_alloc_path();
@@ -597,18 +601,23 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
597601
if (found_type == BTRFS_EXTENT_DATA_KEY) {
598602
fi = btrfs_item_ptr(leaf, path->slots[0],
599603
struct btrfs_file_extent_item);
600-
if (btrfs_file_extent_type(leaf, fi) !=
601-
BTRFS_FILE_EXTENT_INLINE) {
604+
extent_type = btrfs_file_extent_type(leaf, fi);
605+
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
602606
item_end +=
603607
btrfs_file_extent_num_bytes(leaf, fi);
608+
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
609+
struct btrfs_item *item = btrfs_item_nr(leaf,
610+
path->slots[0]);
611+
item_end += btrfs_file_extent_inline_len(leaf,
612+
item);
604613
}
605614
}
606615
if (found_type == BTRFS_CSUM_ITEM_KEY) {
607616
ret = btrfs_csum_truncate(trans, root, path,
608617
inode->i_size);
609618
BUG_ON(ret);
610619
}
611-
if (item_end < inode->i_size) {
620+
if (item_end <= inode->i_size) {
612621
if (found_type == BTRFS_DIR_ITEM_KEY) {
613622
found_type = BTRFS_INODE_ITEM_KEY;
614623
} else if (found_type == BTRFS_EXTENT_ITEM_KEY) {
@@ -629,9 +638,10 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
629638
found_extent = 0;
630639

631640
/* FIXME, shrink the extent if the ref count is only 1 */
632-
if (found_type == BTRFS_EXTENT_DATA_KEY &&
633-
btrfs_file_extent_type(leaf, fi) !=
634-
BTRFS_FILE_EXTENT_INLINE) {
641+
if (found_type != BTRFS_EXTENT_DATA_KEY)
642+
goto delete;
643+
644+
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
635645
u64 num_dec;
636646
extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
637647
if (!del_item) {
@@ -659,7 +669,15 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
659669
inode->i_blocks -= num_dec;
660670
}
661671
}
672+
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE &&
673+
!del_item) {
674+
u32 newsize = inode->i_size - found_key.offset;
675+
newsize = btrfs_file_extent_calc_inline_size(newsize);
676+
ret = btrfs_truncate_item(trans, root, path,
677+
newsize, 1);
678+
BUG_ON(ret);
662679
}
680+
delete:
663681
if (del_item) {
664682
ret = btrfs_del_item(trans, root, path);
665683
if (ret)
@@ -769,7 +787,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
769787
u64 pos = (inode->i_size + mask) & ~mask;
770788
u64 block_end = attr->ia_size | mask;
771789
u64 hole_size;
772-
u64 alloc_hint;
790+
u64 alloc_hint = 0;
773791

774792
if (attr->ia_size <= pos)
775793
goto out;
@@ -786,8 +804,11 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
786804
pos, pos + hole_size, pos,
787805
&alloc_hint);
788806

789-
err = btrfs_insert_file_extent(trans, root, inode->i_ino,
790-
pos, 0, 0, hole_size);
807+
if (alloc_hint != EXTENT_MAP_INLINE) {
808+
err = btrfs_insert_file_extent(trans, root,
809+
inode->i_ino,
810+
pos, 0, 0, hole_size);
811+
}
791812
btrfs_end_transaction(trans, root);
792813
mutex_unlock(&root->fs_info->fs_mutex);
793814
unlock_extent(em_tree, pos, block_end, GFP_NOFS);
@@ -1531,8 +1552,8 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
15311552
em->end = EXTENT_MAP_HOLE;
15321553
}
15331554
em->bdev = inode->i_sb->s_bdev;
1534-
ret = btrfs_lookup_file_extent(NULL, root, path,
1535-
objectid, start, 0);
1555+
ret = btrfs_lookup_file_extent(trans, root, path,
1556+
objectid, start, trans != NULL);
15361557
if (ret < 0) {
15371558
err = ret;
15381559
goto out;
@@ -1627,15 +1648,23 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
16271648
((u64)root->sectorsize -1);
16281649
map = kmap(page);
16291650
ptr = btrfs_file_extent_inline_start(item) + extent_offset;
1630-
read_extent_buffer(leaf, map + page_offset, ptr, copy_size);
1631-
1632-
if (em->start + copy_size <= em->end) {
1633-
size = min_t(u64, em->end + 1 - em->start,
1634-
PAGE_CACHE_SIZE - page_offset) - copy_size;
1635-
memset(map + page_offset + copy_size, 0, size);
1651+
if (create == 0 && !PageUptodate(page)) {
1652+
read_extent_buffer(leaf, map + page_offset, ptr,
1653+
copy_size);
1654+
flush_dcache_page(page);
1655+
} else if (create && PageUptodate(page)) {
1656+
if (!trans) {
1657+
kunmap(page);
1658+
free_extent_map(em);
1659+
em = NULL;
1660+
btrfs_release_path(root, path);
1661+
trans = btrfs_start_transaction(root, 1);
1662+
goto again;
1663+
}
1664+
write_extent_buffer(leaf, map + page_offset, ptr,
1665+
copy_size);
1666+
btrfs_mark_buffer_dirty(leaf);
16361667
}
1637-
1638-
flush_dcache_page(page);
16391668
kunmap(page);
16401669
set_extent_uptodate(em_tree, em->start, em->end, GFP_NOFS);
16411670
goto insert;

0 commit comments

Comments
 (0)