[PATCH v2] ext4: move journal state away from orphan list updates

JonasZhou-oc posted 1 patch 2 weeks, 4 days ago
fs/ext4/ext4.h | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
[PATCH v2] ext4: move journal state away from orphan list updates
Posted by JonasZhou-oc 2 weeks, 4 days ago
From: Jonas Zhou <jonaszhou@zhaoxin.com>

In the tested x86-64 layout, s_journal and s_ext4_flags share a cache
line with s_orphan_lock and s_orphan. Legacy orphan-list updates
invalidate the same line used by unrelated journal and inode paths.

Exchange s_journal and s_ext4_flags with s_err_report_sec and
s_li_request, which are used for error reporting and lazy-init
management. This separates the common journal and flag readers from
orphan-list writes without adding padding or shifting the intervening
allocator fields. Preserve the comment describing s_orphan_lock.

Only these four member offsets change, and sizeof(struct ext4_sb_info)
remains 2496 bytes with the tested x86-64 configuration. Compiled layout
checks with quota disabled, lockdep, PREEMPT_RT and i386 also preserve
the respective structure sizes and all other member offsets.

On a system with two Xeon Silver 4208 processors and a filesystem
without orphan_file enabled, mean throughput improved by 13.52% for
stress-ng dnotify and 31.70% for unlink.

This targets users of the legacy orphan list. The orphan_file feature
avoids this lock in normal operation and remains the main scalability
improvement. Tests with orphan_file enabled show no clear throughput
change within the observed boot-to-boot variation.

Results from repeated tests comparing v7.3-rc1 and v2:

                       Mean throughput       Mean change
Feature      Test          base         v2           (%)
legacy       dnotify    19562.38   22206.66        +13.52
legacy       unlink        38.82      51.13        +31.70
legacy       fallocate    233.55     234.14         +0.25
orphan_file  dnotify    27702.16   27582.55         -0.43
orphan_file  unlink       116.93     116.52         -0.34
orphan_file  fallocate    235.20     235.89         +0.29

Descriptive 95% t intervals from the four paired log ratios (df=3),
expressed as percentage changes:
  legacy:      dnotify [+4.83, +22.92], unlink [+22.53, +41.48],
               fallocate [-1.40, +1.94]
  orphan_file: dnotify [-5.44, +4.96], unlink [-2.76, +2.13],
               fallocate [-0.82, +1.42]
These are intervals for the paired geometric ratio, not the arithmetic
mean ratio above. Four pairs do not establish performance equivalence.

For legacy dnotify, perf c2c shows remote HITM samples on the original
journal/flags offsets, but none on their new cache line after the
change. Local HITM samples and orphan-lock contention remain. The
combined HITM sample density of the orphan and journal lines is lower
than that of the original shared line.

Signed-off-by: Jonas Zhou <jonaszhou@zhaoxin.com>
---
Changes since v1:
- Replace explicit cache-line alignment with a four-member exchange.
- Keep the mutex comment and all other member offsets unchanged.
- Retest this layout against v7.3-rc1, including orphan_file controls.

v1:
https://lore.kernel.org/all/20260903021836.962624-1-jonaszhou-oc@zhaoxin.com/

 fs/ext4/ext4.h | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 724a27e8be61..16ef01078233 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1641,9 +1641,13 @@ struct ext4_sb_info {
 	struct super_block *s_sb;
 	struct buffer_head *s_mmp_bh;
 
+	/* timeout in seconds for s_err_report; 0 disables the timer. */
+	unsigned long s_err_report_sec;
+
+	/* Lazy inode table initialization info */
+	struct ext4_li_request *s_li_request;
+
 	/* Journaling */
-	struct journal_s *s_journal;
-	unsigned long s_ext4_flags;		/* Ext4 superblock flags */
 	struct mutex s_orphan_lock;	/* Protects on disk list changes */
 	struct list_head s_orphan;	/* List of orphaned inodes in on disk
 					   list */
@@ -1747,11 +1751,10 @@ struct ext4_sb_info {
 
 	/* timer for periodic error stats printing */
 	struct timer_list s_err_report;
-	/* timeout in seconds for s_err_report; 0 disables the timer. */
-	unsigned long s_err_report_sec;
+	/* Keep journal state away from orphan list updates. */
+	struct journal_s *s_journal;
+	unsigned long s_ext4_flags;		/* Ext4 superblock flags */
 
-	/* Lazy inode table initialization info */
-	struct ext4_li_request *s_li_request;
 	/* Wait multiplier for lazy initialization thread */
 	unsigned int s_li_wait_mult;
 

base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
-- 
2.43.0
Re: [PATCH v2] ext4: move journal state away from orphan list updates
Posted by Jan Kara 2 weeks, 3 days ago
On Mon 07-09-26 19:12:42, JonasZhou-oc wrote:
> From: Jonas Zhou <jonaszhou@zhaoxin.com>
> 
> In the tested x86-64 layout, s_journal and s_ext4_flags share a cache
> line with s_orphan_lock and s_orphan. Legacy orphan-list updates
> invalidate the same line used by unrelated journal and inode paths.
> 
> Exchange s_journal and s_ext4_flags with s_err_report_sec and
> s_li_request, which are used for error reporting and lazy-init
> management. This separates the common journal and flag readers from
> orphan-list writes without adding padding or shifting the intervening
> allocator fields. Preserve the comment describing s_orphan_lock.
> 
> Only these four member offsets change, and sizeof(struct ext4_sb_info)
> remains 2496 bytes with the tested x86-64 configuration. Compiled layout
> checks with quota disabled, lockdep, PREEMPT_RT and i386 also preserve
> the respective structure sizes and all other member offsets.
> 
> On a system with two Xeon Silver 4208 processors and a filesystem
> without orphan_file enabled, mean throughput improved by 13.52% for
> stress-ng dnotify and 31.70% for unlink.
> 
> This targets users of the legacy orphan list. The orphan_file feature
> avoids this lock in normal operation and remains the main scalability
> improvement. Tests with orphan_file enabled show no clear throughput
> change within the observed boot-to-boot variation.
> 
> Results from repeated tests comparing v7.3-rc1 and v2:
> 
>                        Mean throughput       Mean change
> Feature      Test          base         v2           (%)
> legacy       dnotify    19562.38   22206.66        +13.52
> legacy       unlink        38.82      51.13        +31.70
> legacy       fallocate    233.55     234.14         +0.25
> orphan_file  dnotify    27702.16   27582.55         -0.43
> orphan_file  unlink       116.93     116.52         -0.34
> orphan_file  fallocate    235.20     235.89         +0.29
> 
> Descriptive 95% t intervals from the four paired log ratios (df=3),
> expressed as percentage changes:
>   legacy:      dnotify [+4.83, +22.92], unlink [+22.53, +41.48],
>                fallocate [-1.40, +1.94]
>   orphan_file: dnotify [-5.44, +4.96], unlink [-2.76, +2.13],
>                fallocate [-0.82, +1.42]
> These are intervals for the paired geometric ratio, not the arithmetic
> mean ratio above. Four pairs do not establish performance equivalence.
> 
> For legacy dnotify, perf c2c shows remote HITM samples on the original
> journal/flags offsets, but none on their new cache line after the
> change. Local HITM samples and orphan-lock contention remain. The
> combined HITM sample density of the orphan and journal lines is lower
> than that of the original shared line.
> 
> Signed-off-by: Jonas Zhou <jonaszhou@zhaoxin.com>

Sorry, but we should keep things logically belonging together close. I'd
keep s_journal & s_ext4_flags in the read-mostly part of the sb and perhaps
move orphan-related things (s_orphan_lock, s_orphan, s_orphan_info) later.
Perhaps below s_journal_triggers definition. And add a comment there like:

	/* Orphan inode tracking */

								Honza

> ---
> Changes since v1:
> - Replace explicit cache-line alignment with a four-member exchange.
> - Keep the mutex comment and all other member offsets unchanged.
> - Retest this layout against v7.3-rc1, including orphan_file controls.
> 
> v1:
> https://lore.kernel.org/all/20260903021836.962624-1-jonaszhou-oc@zhaoxin.com/
> 
>  fs/ext4/ext4.h | 15 +++++++++------
>  1 file changed, 9 insertions(+), 6 deletions(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 724a27e8be61..16ef01078233 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -1641,9 +1641,13 @@ struct ext4_sb_info {
>  	struct super_block *s_sb;
>  	struct buffer_head *s_mmp_bh;
>  
> +	/* timeout in seconds for s_err_report; 0 disables the timer. */
> +	unsigned long s_err_report_sec;
> +
> +	/* Lazy inode table initialization info */
> +	struct ext4_li_request *s_li_request;
> +
>  	/* Journaling */
> -	struct journal_s *s_journal;
> -	unsigned long s_ext4_flags;		/* Ext4 superblock flags */
>  	struct mutex s_orphan_lock;	/* Protects on disk list changes */
>  	struct list_head s_orphan;	/* List of orphaned inodes in on disk
>  					   list */
> @@ -1747,11 +1751,10 @@ struct ext4_sb_info {
>  
>  	/* timer for periodic error stats printing */
>  	struct timer_list s_err_report;
> -	/* timeout in seconds for s_err_report; 0 disables the timer. */
> -	unsigned long s_err_report_sec;
> +	/* Keep journal state away from orphan list updates. */
> +	struct journal_s *s_journal;
> +	unsigned long s_ext4_flags;		/* Ext4 superblock flags */
>  
> -	/* Lazy inode table initialization info */
> -	struct ext4_li_request *s_li_request;
>  	/* Wait multiplier for lazy initialization thread */
>  	unsigned int s_li_wait_mult;
>  
> 
> base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
> -- 
> 2.43.0
> 
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR
[PATCH v3] ext4: move orphan tracking away from journal state
Posted by JonasZhou-oc 2 weeks ago
From: Jonas Zhou <jonaszhou@zhaoxin.com>

In the tested x86-64 layout, s_journal and s_ext4_flags share a cache
line with s_orphan_lock and s_orphan. Legacy orphan-list updates
invalidate the same line used by unrelated journal and inode paths.

Move s_orphan_lock, s_orphan and s_orphan_info together below
s_journal_triggers. This separates orphan-list writes from journal
and flag readers while keeping the related fields in logical groups.
Add a comment identifying the orphan tracking group and preserve the
existing member comments.

On a system with two Xeon Silver 4208 processors and a filesystem
without orphan_file enabled, repeated tests against v7.3-rc2 recorded
mean throughput increases of 11.06% for stress-ng dnotify and 35.93%
for unlink.

Tests with orphan_file enabled showed no clear throughput change
within the observed boot-to-boot variation.

perf c2c shows remote HITM samples on the original shared line, but
none on the separate journal line after the change. Local HITM samples
and orphan-lock contention remain.

Suggested-by: Jan Kara <jack@suse.cz>
Link: https://lore.kernel.org/all/575wyoks3ldokhrg6v43rxwsegaecod4bgrpbii6jeczy7czbi@a3yq26u52orp/
Signed-off-by: Jonas Zhou <jonaszhou@zhaoxin.com>
---
Changes since v2:
- Follow Jan's suggestion to move the orphan tracking fields together
  below s_journal_triggers, instead of exchanging journal state with
  the error-reporting and lazy-init fields.
- Add an Orphan inode tracking comment.
- Rebase and retest against v7.3-rc2.

v2:
https://lore.kernel.org/all/a33183c1c24ea47a98afdd4756b49365593a2762.1788779199.git.jonaszhou@zhaoxin.com/
v1:
https://lore.kernel.org/all/20260903021836.962624-1-jonaszhou-oc@zhaoxin.com/

 fs/ext4/ext4.h | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 724a27e8be61..166910914973 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -1644,10 +1644,6 @@ struct ext4_sb_info {
 	/* Journaling */
 	struct journal_s *s_journal;
 	unsigned long s_ext4_flags;		/* Ext4 superblock flags */
-	struct mutex s_orphan_lock;	/* Protects on disk list changes */
-	struct list_head s_orphan;	/* List of orphaned inodes in on disk
-					   list */
-	struct ext4_orphan_info s_orphan_info;
 	unsigned long s_commit_interval;
 	u32 s_max_batch_time;
 	u32 s_min_batch_time;
@@ -1786,6 +1782,12 @@ struct ext4_sb_info {
 	/* Journal triggers for checksum computation */
 	struct ext4_journal_trigger s_journal_triggers[EXT4_JOURNAL_TRIGGER_COUNT];
 
+	/* Orphan inode tracking */
+	struct mutex s_orphan_lock;	/* Protects on disk list changes */
+	struct list_head s_orphan;	/* List of orphaned inodes in on disk
+					   list */
+	struct ext4_orphan_info s_orphan_info;
+
 	/* Ratelimit ext4 messages. */
 	struct ratelimit_state s_err_ratelimit_state;
 	struct ratelimit_state s_warning_ratelimit_state;

base-commit: df2908090cda368b01ff43709f51890076c56157
-- 
2.43.0
Re: [PATCH v3] ext4: move orphan tracking away from journal state
Posted by Jan Kara 2 weeks ago
On Fri 11-09-26 11:31:13, JonasZhou-oc wrote:
> From: Jonas Zhou <jonaszhou@zhaoxin.com>
> 
> In the tested x86-64 layout, s_journal and s_ext4_flags share a cache
> line with s_orphan_lock and s_orphan. Legacy orphan-list updates
> invalidate the same line used by unrelated journal and inode paths.
> 
> Move s_orphan_lock, s_orphan and s_orphan_info together below
> s_journal_triggers. This separates orphan-list writes from journal
> and flag readers while keeping the related fields in logical groups.
> Add a comment identifying the orphan tracking group and preserve the
> existing member comments.
> 
> On a system with two Xeon Silver 4208 processors and a filesystem
> without orphan_file enabled, repeated tests against v7.3-rc2 recorded
> mean throughput increases of 11.06% for stress-ng dnotify and 35.93%
> for unlink.
> 
> Tests with orphan_file enabled showed no clear throughput change
> within the observed boot-to-boot variation.
> 
> perf c2c shows remote HITM samples on the original shared line, but
> none on the separate journal line after the change. Local HITM samples
> and orphan-lock contention remain.
> 
> Suggested-by: Jan Kara <jack@suse.cz>
> Link: https://lore.kernel.org/all/575wyoks3ldokhrg6v43rxwsegaecod4bgrpbii6jeczy7czbi@a3yq26u52orp/
> Signed-off-by: Jonas Zhou <jonaszhou@zhaoxin.com>

Looks good. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
> Changes since v2:
> - Follow Jan's suggestion to move the orphan tracking fields together
>   below s_journal_triggers, instead of exchanging journal state with
>   the error-reporting and lazy-init fields.
> - Add an Orphan inode tracking comment.
> - Rebase and retest against v7.3-rc2.
> 
> v2:
> https://lore.kernel.org/all/a33183c1c24ea47a98afdd4756b49365593a2762.1788779199.git.jonaszhou@zhaoxin.com/
> v1:
> https://lore.kernel.org/all/20260903021836.962624-1-jonaszhou-oc@zhaoxin.com/
> 
>  fs/ext4/ext4.h | 10 ++++++----
>  1 file changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 724a27e8be61..166910914973 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -1644,10 +1644,6 @@ struct ext4_sb_info {
>  	/* Journaling */
>  	struct journal_s *s_journal;
>  	unsigned long s_ext4_flags;		/* Ext4 superblock flags */
> -	struct mutex s_orphan_lock;	/* Protects on disk list changes */
> -	struct list_head s_orphan;	/* List of orphaned inodes in on disk
> -					   list */
> -	struct ext4_orphan_info s_orphan_info;
>  	unsigned long s_commit_interval;
>  	u32 s_max_batch_time;
>  	u32 s_min_batch_time;
> @@ -1786,6 +1782,12 @@ struct ext4_sb_info {
>  	/* Journal triggers for checksum computation */
>  	struct ext4_journal_trigger s_journal_triggers[EXT4_JOURNAL_TRIGGER_COUNT];
>  
> +	/* Orphan inode tracking */
> +	struct mutex s_orphan_lock;	/* Protects on disk list changes */
> +	struct list_head s_orphan;	/* List of orphaned inodes in on disk
> +					   list */
> +	struct ext4_orphan_info s_orphan_info;
> +
>  	/* Ratelimit ext4 messages. */
>  	struct ratelimit_state s_err_ratelimit_state;
>  	struct ratelimit_state s_warning_ratelimit_state;
> 
> base-commit: df2908090cda368b01ff43709f51890076c56157
> -- 
> 2.43.0
> 
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR