[RFC PATCH 5/9] ext4: remove dead inline data write code

From: Yun Zhou

Date: Mon Jul 27 2026 - 07:06:07 EST


Now that inline data write paths for regular files always convert to
block format, the following unreachable dead code is removed:

- ext4_write_inline_data_end(): was called from write_end when
EXT4_WRITE_DATA_INLINE was set in fsdata, which no longer happens
- ext4_convert_inline_data_to_extent(): was the unsafe convert path
(no restore on failure), replaced by ext4_convert_inline_data()
- EXT4_WRITE_DATA_INLINE flag and all associated checks in inode.c

Signed-off-by: Yun Zhou <yun.zhou@xxxxxxxxxxxxx>
---
fs/ext4/ext4.h | 3 -
fs/ext4/inline.c | 210 -----------------------------------------------
fs/ext4/inode.c | 22 -----
3 files changed, 235 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5f0448b960fe..832bc1f3b600 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3148,7 +3148,6 @@ int do_journal_get_write_access(handle_t *handle, struct inode *inode,
struct buffer_head *bh);
void ext4_set_inode_mapping_order(struct inode *inode);
#define FALL_BACK_TO_NONDELALLOC 1
-#define EXT4_WRITE_DATA_INLINE 2

typedef enum {
EXT4_IGET_NORMAL = 0,
@@ -3759,8 +3758,6 @@ extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode);
extern void ext4_update_final_de(void *de_buf, int old_size, int new_size);

int ext4_readpage_inline(struct inode *inode, struct folio *folio);
-int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
- unsigned copied, struct folio *folio);
extern int ext4_generic_write_inline_data(struct address_space *mapping,
struct inode *inode,
loff_t pos, unsigned len,
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 0803fa8eade7..5c6202add3d8 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -568,122 +568,6 @@ int ext4_readpage_inline(struct inode *inode, struct folio *folio)
return ret >= 0 ? 0 : ret;
}

-static int ext4_convert_inline_data_to_extent(struct address_space *mapping,
- struct inode *inode)
-{
- int ret, needed_blocks, no_expand;
- handle_t *handle = NULL;
- int retries = 0, sem_held = 0;
- struct folio *folio = NULL;
- unsigned from, to;
- struct ext4_iloc iloc;
-
- if (!ext4_has_inline_data(inode)) {
- /*
- * clear the flag so that no new write
- * will trap here again.
- */
- ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
- return 0;
- }
-
- needed_blocks = ext4_chunk_trans_extent(inode, 1);
-
- ret = ext4_get_inode_loc(inode, &iloc);
- if (ret)
- return ret;
-
-retry:
- handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, needed_blocks);
- if (IS_ERR(handle)) {
- ret = PTR_ERR(handle);
- handle = NULL;
- goto out;
- }
-
- /* We cannot recurse into the filesystem as the transaction is already
- * started */
- folio = __filemap_get_folio(mapping, 0, FGP_WRITEBEGIN | FGP_NOFS,
- mapping_gfp_mask(mapping));
- if (IS_ERR(folio)) {
- ret = PTR_ERR(folio);
- goto out_nofolio;
- }
-
- ext4_write_lock_xattr(inode, &no_expand);
- sem_held = 1;
- /* If some one has already done this for us, just exit. */
- if (!ext4_has_inline_data(inode)) {
- ret = 0;
- goto out;
- }
-
- from = 0;
- to = ext4_get_inline_size(inode);
- if (!folio_test_uptodate(folio)) {
- ret = ext4_read_inline_folio(inode, folio);
- if (ret < 0)
- goto out;
- }
-
- ext4_fc_track_inode(handle, inode);
- ret = ext4_destroy_inline_data_nolock(handle, inode);
- if (ret)
- goto out;
-
- if (ext4_should_dioread_nolock(inode)) {
- ret = ext4_block_write_begin(handle, folio, from, to,
- ext4_get_block_unwritten);
- } else
- ret = ext4_block_write_begin(handle, folio, from, to,
- ext4_get_block);
- clear_buffer_new(folio_buffers(folio));
-
- if (!ret && ext4_should_journal_data(inode)) {
- ret = ext4_walk_page_buffers(handle, inode,
- folio_buffers(folio), from, to,
- NULL, do_journal_get_write_access);
- }
-
- if (ret) {
- folio_unlock(folio);
- folio_put(folio);
- folio = NULL;
- ext4_orphan_add(handle, inode);
- ext4_write_unlock_xattr(inode, &no_expand);
- sem_held = 0;
- ext4_journal_stop(handle);
- handle = NULL;
- ext4_truncate_failed_write(inode);
- /*
- * If truncate failed early the inode might
- * still be on the orphan list; we need to
- * make sure the inode is removed from the
- * orphan list in that case.
- */
- if (inode->i_nlink)
- ext4_orphan_del(NULL, inode);
- }
-
- if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
- goto retry;
-
- if (folio)
- block_commit_write(folio, from, to);
-out:
- if (folio) {
- folio_unlock(folio);
- folio_put(folio);
- }
-out_nofolio:
- if (sem_held)
- ext4_write_unlock_xattr(inode, &no_expand);
- if (handle)
- ext4_journal_stop(handle);
- brelse(iloc.bh);
- return ret;
-}
-
/*
* Prepare the write for the inline data.
* If the data can be written into the inode, we just read
@@ -712,100 +596,6 @@ int ext4_generic_write_inline_data(struct address_space *mapping,
return ret;
}

-int ext4_write_inline_data_end(struct inode *inode, loff_t pos, unsigned len,
- unsigned copied, struct folio *folio)
-{
- handle_t *handle = ext4_journal_current_handle();
- int no_expand;
- void *kaddr;
- struct ext4_iloc iloc;
- int ret = 0, ret2;
-
- if (unlikely(copied < len) && !folio_test_uptodate(folio))
- copied = 0;
-
- if (likely(copied)) {
- ret = ext4_get_inode_loc(inode, &iloc);
- if (ret) {
- folio_unlock(folio);
- folio_put(folio);
- ext4_std_error(inode->i_sb, ret);
- goto out;
- }
- ext4_write_lock_xattr(inode, &no_expand);
- /*
- * We could have raced with ext4_page_mkwrite() converting
- * the inode and clearing the inline data flag, so we just
- * release resources and retry the whole write.
- */
- if (unlikely(!ext4_has_inline_data(inode))) {
- ext4_write_unlock_xattr(inode, &no_expand);
- brelse(iloc.bh);
- folio_unlock(folio);
- folio_put(folio);
- ext4_journal_stop(handle);
- return 0;
- }
-
- /*
- * ei->i_inline_off may have changed since
- * ext4_write_begin() called
- * ext4_convert_inline_data()
- */
- (void) ext4_find_inline_data_nolock(inode);
-
- kaddr = kmap_local_folio(folio, 0);
- ext4_write_inline_data(inode, &iloc, kaddr, pos, copied);
- kunmap_local(kaddr);
- folio_mark_uptodate(folio);
- /* clear dirty flag so that writepages wouldn't work for us. */
- folio_clear_dirty(folio);
-
- ext4_write_unlock_xattr(inode, &no_expand);
- brelse(iloc.bh);
-
- /*
- * It's important to update i_size while still holding folio
- * lock: page writeout could otherwise come in and zero
- * beyond i_size.
- */
- ext4_update_inode_size(inode, pos + copied);
- }
- folio_unlock(folio);
- folio_put(folio);
-
- /*
- * Don't mark the inode dirty under folio lock. First, it unnecessarily
- * makes the holding time of folio lock longer. Second, it forces lock
- * ordering of folio lock and transaction start for journaling
- * filesystems.
- */
- if (likely(copied))
- mark_inode_dirty(inode);
-out:
- /*
- * If we didn't copy as much data as expected, we need to trim back
- * size of xattr containing inline data.
- */
- if (pos + len > inode->i_size && ext4_can_truncate(inode))
- ext4_orphan_add(handle, inode);
-
- ret2 = ext4_journal_stop(handle);
- if (!ret)
- ret = ret2;
- if (pos + len > inode->i_size) {
- ext4_truncate_failed_write(inode);
- /*
- * If truncate failed early the inode might still be
- * on the orphan list; we need to make sure the inode
- * is removed from the orphan list in that case.
- */
- if (inode->i_nlink)
- ext4_orphan_del(NULL, inode);
- }
- return ret ? ret : copied;
-}
-
/*
* Try to make the page cache and handle ready for the inline data case.
* We can call this function in 2 cases:
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index f9e1f0a6024d..2256fb3826a9 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1300,8 +1300,6 @@ static int ext4_write_begin(const struct kiocb *iocb,
if (unlikely(ret))
return ret;

- *fsdata = (void *)((unsigned long)*fsdata & ~EXT4_WRITE_DATA_INLINE);
-
trace_ext4_write_begin(inode, pos, len);
/*
* Reserve one block more for addition to orphan list in case
@@ -1315,10 +1313,6 @@ static int ext4_write_begin(const struct kiocb *iocb,
ret = ext4_convert_inline_data(inode);
if (ret < 0)
return ret;
- if (ret == 1) {
- *fsdata = (void *)((unsigned long)*fsdata | EXT4_WRITE_DATA_INLINE);
- return 0;
- }
}

/*
@@ -1451,10 +1445,6 @@ static int ext4_write_end(const struct kiocb *iocb,

trace_ext4_write_end(inode, pos, len, copied);

- if ((unsigned long)fsdata & EXT4_WRITE_DATA_INLINE)
- return ext4_write_inline_data_end(inode, pos, len, copied,
- folio);
-
copied = block_write_end(pos, len, copied, folio);
/*
* it's important to update i_size while still holding folio lock:
@@ -1560,10 +1550,6 @@ static int ext4_journalled_write_end(const struct kiocb *iocb,

BUG_ON(!ext4_handle_valid(handle));

- if ((unsigned long)fsdata & EXT4_WRITE_DATA_INLINE)
- return ext4_write_inline_data_end(inode, pos, len, copied,
- folio);
-
if (unlikely(copied < len) && !folio_test_uptodate(folio)) {
copied = 0;
ext4_journalled_zero_new_buffers(handle, inode, folio,
@@ -3172,10 +3158,6 @@ static int ext4_da_write_begin(const struct kiocb *iocb,
foliop, true);
if (ret < 0)
return ret;
- if (ret == 1) {
- *fsdata = (void *)((unsigned long)*fsdata | EXT4_WRITE_DATA_INLINE);
- return 0;
- }
}

retry:
@@ -3312,10 +3294,6 @@ static int ext4_da_write_end(const struct kiocb *iocb,

trace_ext4_da_write_end(inode, pos, len, copied);

- if (write_mode & EXT4_WRITE_DATA_INLINE)
- return ext4_write_inline_data_end(inode, pos, len, copied,
- folio);
-
if (unlikely(copied < len) && !folio_test_uptodate(folio))
copied = 0;

--
2.43.0