diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c index 23c3eeb58dc1..1be632c742bd 100644 --- a/fs/btrfs/backref.c +++ b/fs/btrfs/backref.c @@ -3054,7 +3054,10 @@ void btrfs_backref_free_node(struct btrfs_backref_cache *cache, if (node) { ASSERT(list_empty(&node->list)); ASSERT(list_empty(&node->lower)); - ASSERT(node->eb == NULL, "node->eb->start=%llu", node->eb->start); + ASSERT(node->eb == NULL, "node->eb->start=%llu level=%d owner=%llu", + node->eb ? node->eb->start : 0, + node->eb ? btrfs_header_level(node->eb) : 0, + node->eb ? btrfs_header_owner(node->eb) : 0); cache->nr_nodes--; btrfs_put_root(node->root); kfree(node); diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index d5d81f9546c3..7fdc6c3fd066 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -476,6 +476,8 @@ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode) if (inode->flags & BTRFS_INODE_NODATACOW || inode->flags & BTRFS_INODE_NODATASUM) return false; + if (btrfs_is_data_reloc_root(inode->root)) + return false; return true; } diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 7d604524e83c..de5785117a47 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -2004,7 +2004,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, - -eb->len, + -(s64)eb->len, fs_info->dirty_metadata_batch); ret = true; } else { @@ -3774,7 +3774,7 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans, return; buffer_tree_clear_mark(eb, PAGECACHE_TAG_DIRTY); - percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -eb->len, + percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -(s64)eb->len, fs_info->dirty_metadata_batch); for (int i = 0; i < num_extent_folios(eb); i++) { diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index fce9c5cc0122..6ad7b39ae358 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -866,13 +866,13 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end, goto next; } - flags = em->flags; /* * In case we split the extent map, we want to preserve the * EXTENT_FLAG_LOGGING flag on our extent map, but we don't want * it on the new extent maps. */ - em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING); + flags = em->flags & ~EXTENT_FLAG_LOGGING; + em->flags &= ~EXTENT_FLAG_PINNED; modified = !list_empty(&em->list); /* diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 272598f6ae77..b446c3014b24 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -8068,7 +8068,8 @@ static int btrfs_getattr(struct mnt_idmap *idmap, stat->result_mask |= STATX_SUBVOL; spin_lock(&BTRFS_I(inode)->lock); - delalloc_bytes = BTRFS_I(inode)->new_delalloc_bytes; + delalloc_bytes = S_ISREG(inode->i_mode) ? + BTRFS_I(inode)->new_delalloc_bytes : 0; inode_bytes = inode_get_bytes(inode); spin_unlock(&BTRFS_I(inode)->lock); stat->blocks = (ALIGN(inode_bytes, blocksize) + diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 81e87bc39828..68b33f365fda 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2048,6 +2048,7 @@ static int _btrfs_ioctl_get_subvol_info(struct inode *inode, ret = -ENOENT; goto out; } + ret = 0; } out: diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 0d63d117db59..fc5c14b5adad 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -588,6 +588,7 @@ static int __add_reloc_root(struct btrfs_root *root, struct reloc_control *rc) btrfs_err(fs_info, "Duplicate root found for start=%llu while inserting into relocation tree", node->bytenr); + kfree(node); return -EEXIST; } @@ -890,6 +891,13 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr, leaf = path->nodes[0]; fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item); + if (unlikely(btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE)) { + btrfs_print_leaf(leaf); + btrfs_err(fs_info, + "unexpected inline file extent item for data reloc inode %llu key offset %llu", + btrfs_ino(BTRFS_I(reloc_inode)), bytenr); + return -EUCLEAN; + } /* * The cluster-boundary key searched above is always written by @@ -1518,6 +1526,17 @@ static int insert_dirty_subvol(struct btrfs_trans_handle *trans, return 0; } +static void clear_reloc_root(struct btrfs_root *root) +{ + root->reloc_root = NULL; + /* + * Need barrier to ensure clear_bit() only happens after + * root->reloc_root = NULL. Pairs with have_reloc_root(). + */ + smp_wmb(); + clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); +} + static int clean_dirty_subvols(struct reloc_control *rc) { struct btrfs_root *root; @@ -1532,13 +1551,7 @@ static int clean_dirty_subvols(struct reloc_control *rc) struct btrfs_root *reloc_root = root->reloc_root; list_del_init(&root->reloc_dirty_list); - root->reloc_root = NULL; - /* - * Need barrier to ensure clear_bit() only happens after - * root->reloc_root = NULL. Pairs with have_reloc_root. - */ - smp_wmb(); - clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state); + clear_reloc_root(root); if (reloc_root) { /* * btrfs_drop_snapshot drops our ref we hold for @@ -1914,21 +1927,39 @@ void merge_reloc_roots(struct reloc_control *rc) goto out; } ret = merge_reloc_root(rc, root); - btrfs_put_root(root); if (ret) { - if (list_empty(&reloc_root->root_list)) + /* + * Clear the reloc root since below we will call + * free_reloc_roots(), otherwise we leave + * root->reloc_root pointing to a freed reloc + * root and trigger a use-after-free during + * unmount or elsewhere. + */ + clear_reloc_root(root); + btrfs_put_root(root); + /* + * We are adding the reloc_root to the local + * reloc_roots list, so we add a ref for this + * list which will be dropped below by the call + * to free_reloc_roots(). + */ + if (list_empty(&reloc_root->root_list)) { list_add_tail(&reloc_root->root_list, &reloc_roots); + btrfs_grab_root(reloc_root); + } + /* Now drop the ref for root->reloc_root. */ + btrfs_put_root(reloc_root); goto out; } + btrfs_put_root(root); } else { if (!IS_ERR(root)) { if (root->reloc_root == reloc_root) { - root->reloc_root = NULL; + clear_reloc_root(root); + /* Drop the ref for root->reloc_root. */ btrfs_put_root(reloc_root); } - clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, - &root->state); btrfs_put_root(root); } diff --git a/fs/btrfs/subpage.c b/fs/btrfs/subpage.c index 56060acac2e9..2a9397be8116 100644 --- a/fs/btrfs/subpage.c +++ b/fs/btrfs/subpage.c @@ -359,6 +359,23 @@ void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info, folio_mark_dirty(folio); } +static void folio_clear_tags(struct folio *folio) +{ + struct address_space *mapping = folio_mapping(folio); + XA_STATE(xas, &mapping->i_pages, folio->index); + unsigned long flags; + + ASSERT(folio_test_locked(folio)); + ASSERT(mapping); + ASSERT(mapping_use_writeback_tags(mapping)); + + xas_lock_irqsave(&xas, flags); + xas_load(&xas); + xas_clear_mark(&xas, PAGECACHE_TAG_DIRTY); + xas_clear_mark(&xas, PAGECACHE_TAG_TOWRITE); + xas_unlock_irqrestore(&xas, flags); +} + /* * Extra clear_and_test function for subpage dirty bitmap. * @@ -403,7 +420,6 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info, unsigned int start_bit = subpage_calc_start_bit(fs_info, folio, writeback, start, len); unsigned long flags; - bool keep_write; spin_lock_irqsave(&bfs->lock, flags); bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits); @@ -413,10 +429,14 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info, * folio. Doing so can cause WB_SYNC_ALL writepages() to overlook it, * assume writeback is complete, and exit too early — violating sync * ordering guarantees. + * + * Instead we manually clear the DIRTY and TOWRITE tags after the folio + * is no longer dirty. */ - keep_write = folio_test_dirty(folio); if (!folio_test_writeback(folio)) - __folio_start_writeback(folio, keep_write); + __folio_start_writeback(folio, true); + if (!folio_test_dirty(folio)) + folio_clear_tags(folio); spin_unlock_irqrestore(&bfs->lock, flags); } diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h index 9b576603b3f1..0a13baf3d8d1 100644 --- a/include/uapi/linux/btrfs.h +++ b/include/uapi/linux/btrfs.h @@ -598,7 +598,7 @@ struct btrfs_ioctl_search_args_v2 { __u64 buf_size; /* in - size of buffer * out - on EOVERFLOW: needed size * to store item */ - __u64 buf[]; /* out - found items */ + __u8 buf[]; /* out - found items */ }; /* With a @src_length of zero, the range from @src_offset->EOF is cloned! */