mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Julian Sun <sunjunchao2870@gmail.com>
To: linux-ext4@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: tytso@mit.edu, adilger.kernel@dilger.ca, jack@suse.cz,
	boyu.mt@taobao.com, tm@tao.ma,
	Julian Sun <sunjunchao2870@gmail.com>
Subject: [PATCH 7/7] ext4: Store truncated large files as inline data.
Date: Fri, 20 Dec 2024 23:16:25 +0800	[thread overview]
Message-ID: <20241220151625.19769-8-sunjunchao2870@gmail.com> (raw)
In-Reply-To: <20241220151625.19769-1-sunjunchao2870@gmail.com>

Ext4 provides the flexibility to store file data inline, directly
within the inode, and also supports automatic conversion from inline
to extent data when necessary. However, it lacks a mechanism to convert
extent-based data to inline data, even if the file size permits.

This patch fills the gap by automatically converting truncated files
to inline data when possible, resulting in improved disk space efficiency.
Below is a comparison of results before and after applying the patch set.

Before:
root@q:linux# dd if=/dev/urandom bs=1M count=10 of=/mnt/ext4/test
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.0770325 s, 136 MB/s
root@q:linux# filefrag -v /mnt/ext4/test
Filesystem type is: ef53
File size of /mnt/ext4/test is 10485760 (2560 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..    2559:          0..         0:      0:             last,unknown_loc,delalloc,eof
/mnt/ext4/test: 1 extent found
root@q:linux# echo a > /mnt/ext4/test
root@q:linux# filefrag -v /mnt/ext4/test
Filesystem type is: ef53
File size of /mnt/ext4/test is 2 (1 block of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:      34304..     34304:      1:             last,eof
/mnt/ext4/test: 1 extent found

After:
root@q:linux# dd if=/dev/urandom bs=1M count=10 of=/mnt/ext4/test
10+0 records in
10+0 records out
10485760 bytes (10 MB, 10 MiB) copied, 0.0883107 s, 119 MB/s
root@q:linux# filefrag -v /mnt/ext4/test
Filesystem type is: ef53
File size of /mnt/ext4/test is 10485760 (2560 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..    2559:      38912..     41471:   2560:             last,unknown_loc,delalloc,eof
/mnt/ext4/test: 1 extent found
root@q:linux# echo a > /mnt/ext4/test
root@q:linux# filefrag -v /mnt/ext4/test
Filesystem type is: ef53
Filesystem cylinder groups approximately 78
File size of /mnt/ext4/test is 2 (1 block of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       1:    4340520..   4340521:      2:             last,not_aligned,inline,eof
/mnt/ext4/test: 1 extent found

Using filefrag, we can see that after applying this patch,
large truncated files also utilize the inline data feature.

This patch has been tested with xfstests' check -g and has not
introduced any additional failures.

Signed-off-by: Julian Sun <sunjunchao2870@gmail.com>
---
 fs/ext4/inline.c | 24 +++++++++++++++++++++++-
 fs/ext4/inode.c  |  5 +++++
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 2abb35f1555d..ff107f7ab936 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -667,13 +667,22 @@ static int ext4_generic_write_inline_data(struct address_space *mapping,
 	struct folio *folio;
 	struct ext4_iloc iloc;
 	int retries = 0;
+	bool none_inline_data = inode->i_blocks != 0;
+	int credits;
 
 	ret = ext4_get_inode_loc(inode, &iloc);
 	if (ret)
 		return ret;
 
 retry_journal:
-	handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
+	if (none_inline_data)
+		if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
+			credits = ext4_writepage_trans_blocks(inode);
+		else
+			credits = ext4_blocks_for_truncate(inode);
+	else
+		credits = 1;
+	handle = ext4_journal_start(inode, EXT4_HT_INODE, credits);
 	if (IS_ERR(handle)) {
 		ret = PTR_ERR(handle);
 		goto out_release_bh;
@@ -698,6 +707,19 @@ static int ext4_generic_write_inline_data(struct address_space *mapping,
 		goto out_release_bh;
 	}
 
+	if (none_inline_data) {
+		down_write(&EXT4_I(inode)->i_data_sem);
+		ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
+
+		if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
+			ret = ext4_ext_remove_space(inode, 0,
+						    EXT_MAX_BLOCKS - 1);
+		else
+			ret = ext4_ind_remove_space(handle, inode, 0,
+						    EXT_MAX_BLOCKS);
+		up_write(&EXT4_I(inode)->i_data_sem);
+	}
+
 	folio = __filemap_get_folio(mapping, 0, FGP_WRITEBEGIN | FGP_NOFS,
 					mapping_gfp_mask(mapping));
 	if (IS_ERR(folio)) {
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 54bdd4884fe6..fb1e4caa37b0 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4164,6 +4164,11 @@ int ext4_truncate(struct inode *inode)
 	if (inode->i_size & (inode->i_sb->s_blocksize - 1))
 		ext4_block_truncate_page(handle, mapping, inode->i_size);
 
+	if (ext4_has_feature_inline_data(inode->i_sb) &&
+	    !(ei->i_flags & (EXT4_EA_INODE_FL|EXT4_DAX_FL)) &&
+	    inode->i_size < ext4_get_max_inline_size(inode))
+		ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
+
 	/*
 	 * We add the inode to the orphan list, so that if this
 	 * truncate spans multiple transactions, and we crash, we will
-- 
2.39.5


  parent reply	other threads:[~2024-12-20 15:17 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-12-20 15:16 [PATCH 0/7] ext4: Convert truncated extent data to " Julian Sun
2024-12-20 15:16 ` [PATCH 1/7] ext4: Modify ei->i_flags before calling ext4_mark_iloc_dirty() Julian Sun
2025-01-06 15:25   ` Jan Kara
2025-01-07  2:53     ` Julian Sun
2024-12-20 15:16 ` [PATCH 2/7] ext4: Remove a redundant return statement Julian Sun
2025-01-06 15:23   ` Jan Kara
2024-12-20 15:16 ` [PATCH 3/7] ext4: Don't set EXT4_STATE_MAY_INLINE_DATA for ea inodes Julian Sun
2025-01-06 15:23   ` Jan Kara
2024-12-20 15:16 ` [PATCH 4/7] ext4: Introduce a new helper function ext4_generic_write_inline_data() Julian Sun
2025-01-06 15:35   ` Jan Kara
2025-01-07  2:57     ` Julian Sun
2024-12-20 15:16 ` [PATCH 5/7] ext4: Refactor out ext4_da_write_inline_data_begin() Julian Sun
2025-01-06 15:39   ` Jan Kara
2025-01-07  2:58     ` Julian Sun
2024-12-20 15:16 ` [PATCH 6/7] ext4: Refactor out ext4_try_to_write_inline_data() Julian Sun
2025-01-06 15:40   ` Jan Kara
2024-12-20 15:16 ` Julian Sun [this message]
2025-01-06 15:51 ` [PATCH 0/7] ext4: Convert truncated extent data to inline data Jan Kara
2025-01-07  6:03   ` Julian Sun

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20241220151625.19769-8-sunjunchao2870@gmail.com \
    --to=sunjunchao2870@gmail.com \
    --cc=adilger.kernel@dilger.ca \
    --cc=boyu.mt@taobao.com \
    --cc=jack@suse.cz \
    --cc=linux-ext4@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=tm@tao.ma \
    --cc=tytso@mit.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®