[PATCH 00/14] ext4l: Linux adaptation patches for ext4 write support
From: Simon Glass <simon.glass@canonical.com> This series contains adaptations to Linux-imported files needed for ext4l write support in U-Boot. These changes are separated from the main ext4l implementation to make it easier to track modifications to imported code. The patches include: - Bit position fixes for REQ_OP and BH_OwnsData to avoid conflicts - JBD2 journal adaptations for U-Boot's single-threaded environment - Function exports to allow calling ext4 internals from U-Boot code - Cache management fixes for multiple mount/unmount cycles - Compiler warning fixes for Clang compatibility These changes are minimal modifications to the Linux ext4 and jbd2 code, using #ifdef __UBOOT__ guards where appropriate to ease future Linux updates. Simon Glass (14): linux: ext4l: Fix REQ_OP and BH_OwnsData bit positions linux: jbd2: Add synchronous commit on transaction stop linux: ext4l: Export ext4 functions for U-Boot interface linux: ext4l: Skip orphan handling in U-Boot linux: fs: Add folio cache to address_space linux: percpu_counter: Add initialized field linux: ext4l: Mark journaled metadata buffers dirty for bh_cache_sync linux: jbd2: Add validation in jbd2_journal_write_metadata_buffer linux: jbd2: Add jbd2_journal_exit_global for clean shutdown linux: slab: Convert kmem_cache functions to declarations linux: ext4l: Mark ext4_groupinfo_slab_names unused linux: ext4l: Export ext4_commit_super function linux: ext4l: Make cache init/exit functions reentrant for U-Boot linux: ext4l: Initialize len in ext4_ext_find_hole fs/ext4l/block_validity.c | 10 ++++++ fs/ext4l/ext4.h | 9 ++++++ fs/ext4l/ext4_jbd2.c | 8 +++++ fs/ext4l/ext4_uboot.h | 17 +++++++--- fs/ext4l/extents.c | 2 +- fs/ext4l/extents_status.c | 6 ++++ fs/ext4l/mballoc.c | 15 ++++++++- fs/ext4l/namei.c | 18 +++++------ fs/ext4l/orphan.c | 9 ++++++ fs/ext4l/super.c | 3 +- fs/ext4l/support.c | 2 +- fs/jbd2/journal.c | 57 +++++++++++++++++++++++++++++++++- fs/jbd2/transaction.c | 18 +++++++++++ include/linux/buffer_head.h | 2 -- include/linux/fs.h | 10 ++++++ include/linux/jbd2.h | 1 + include/linux/percpu_counter.h | 4 ++- include/linux/slab.h | 15 ++------- 18 files changed, 172 insertions(+), 34 deletions(-) -- 2.43.0 base-commit: aebb9f0ed65190ea07b5aed38206e40500674182 branch: extj
From: Simon Glass <simon.glass@canonical.com> The REQ_OP flags (REQ_SYNC, REQ_FUA) use bits 0-1, which collide with REQ_OP_WRITE (value 1). Move the flags to bits 8+ and add REQ_OP_MASK to properly separate operation from flags. Move BH_OwnsData from buffer_head.h to ext4_uboot.h to keep Linux headers unmodified. Define it as BH_JBDPrivateStart to avoid conflicts with JBD2 state bits (17-25). Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4_uboot.h | 17 +++++++++++++---- fs/ext4l/support.c | 2 +- include/linux/buffer_head.h | 2 -- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index a90289c8fa5..39bade68654 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -515,8 +515,14 @@ struct sb_writers { /* Buffer head - from linux/buffer_head.h */ #include <linux/buffer_head.h> +#include <linux/jbd2.h> -/* BH_JBDPrivateStart is defined in jbd2.h as an enum value */ +/* + * U-Boot: marks buffer owns b_data and should free it. + * Use BH_JBDPrivateStart to avoid conflicts with JBD2 state bits. + */ +#define BH_OwnsData BH_JBDPrivateStart +BUFFER_FNS(OwnsData, ownsdata) /* Forward declare for get_block_t */ struct inode; @@ -2153,10 +2159,13 @@ struct fs_parse_result { #define BLK_OPEN_WRITE (1 << 1) #define BLK_OPEN_RESTRICT_WRITES (1 << 2) -/* Request flags */ +/* Request operation (bits 0-7) and flags (bits 8+) */ #define REQ_OP_WRITE 1 -#define REQ_SYNC (1 << 0) -#define REQ_FUA (1 << 1) +#define REQ_OP_MASK 0xff + +/* ensure these values are outside the operations mask */ +#define REQ_SYNC (1 << 8) +#define REQ_FUA (1 << 9) /* blk_holder_ops for block device */ struct blk_holder_ops { diff --git a/fs/ext4l/support.c b/fs/ext4l/support.c index 05efa8d067c..a046654ca54 100644 --- a/fs/ext4l/support.c +++ b/fs/ext4l/support.c @@ -565,7 +565,7 @@ struct buffer_head *__bread(struct block_device *bdev, sector_t block, int submit_bh(int op, struct buffer_head *bh) { int ret; - int op_type = op & 0xff; /* Mask out flags, keep operation type */ + int op_type = op & REQ_OP_MASK; /* Mask out flags, keep operation type */ if (op_type == REQ_OP_READ) { ret = ext4l_read_block(bh->b_blocknr, bh->b_size, bh->b_data); diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 5fbcc757c80..b22df564119 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -37,8 +37,6 @@ enum bh_state_bits { BH_PrivateStart,/* not a state bit, but the first bit available * for private allocation by other entities */ - /* U-Boot specific: marks buffer owns b_data and should free it */ - BH_OwnsData = BH_PrivateStart, }; #define MAX_BUF_PER_PAGE (PAGE_SIZE / 512) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> U-Boot operates in a single-threaded environment without a journal daemon. Commit transactions synchronously when jbd2_journal_stop() is called and there are no active handles (t_updates == 0). This ensures crash-safety by writing journal entries to disk immediately after each file-operation completes. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/jbd2/transaction.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index a524f490c79..b997c8495f5 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1938,6 +1938,24 @@ int jbd2_journal_stop(handle_t *handle) */ stop_this_handle(handle); +#ifdef __UBOOT__ + /* + * U-Boot: Always commit synchronously for crash safety. + * In single-threaded mode, we commit immediately after each + * operation completes to ensure durability. + */ + if (IS_ENABLED(CONFIG_EXT4_WRITE) && + journal->j_running_transaction && + atomic_read(&journal->j_running_transaction->t_updates) == 0) { + jbd2_journal_commit_transaction(journal); + /* + * Check if journal was aborted during commit + * (e.g., due to I/O error) and propagate the error. + */ + if (is_journal_aborted(journal) && !err) + err = -EIO; + } else +#endif if (wait_for_commit) err = jbd2_log_wait_commit(journal, tid); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Remove static from ext4_create(), ext4_mkdir(), ext4_symlink(), and ext4_rename2() and add declarations in ext4.h to allow calling them from ext4l interface code. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4.h | 9 +++++++++ fs/ext4l/namei.c | 18 +++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/fs/ext4l/ext4.h b/fs/ext4l/ext4.h index 1c2d5beb121..669d5522f27 100644 --- a/fs/ext4l/ext4.h +++ b/fs/ext4l/ext4.h @@ -3137,6 +3137,15 @@ extern int ext4_ext_migrate(struct inode *); extern int ext4_ind_migrate(struct inode *inode); /* namei.c */ +extern int ext4_create(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl); +extern struct dentry *ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode); +extern int ext4_symlink(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, const char *symname); +extern int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir, + struct dentry *old_dentry, struct inode *new_dir, + struct dentry *new_dentry, unsigned int flags); extern int ext4_init_new_dir(handle_t *handle, struct inode *dir, struct inode *inode); extern int ext4_dirblock_csum_verify(struct inode *inode, diff --git a/fs/ext4l/namei.c b/fs/ext4l/namei.c index 53c48d12918..bede355d497 100644 --- a/fs/ext4l/namei.c +++ b/fs/ext4l/namei.c @@ -2792,8 +2792,8 @@ static int ext4_add_nondir(handle_t *handle, * If the create succeeds, we fill in the inode information * with d_instantiate(). */ -static int ext4_create(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, umode_t mode, bool excl) +int ext4_create(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, bool excl) { handle_t *handle; struct inode *inode; @@ -2974,8 +2974,8 @@ out: return err; } -static struct dentry *ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, umode_t mode) +struct dentry *ext4_mkdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode) { handle_t *handle; struct inode *inode; @@ -3340,8 +3340,8 @@ out: return err; } -static int ext4_symlink(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, const char *symname) +int ext4_symlink(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, const char *symname) { handle_t *handle; struct inode *inode; @@ -3773,9 +3773,9 @@ retry: * while new_{dentry,inode) refers to the destination dentry/inode * This comes from rename(const char *oldpath, const char *newpath) */ -static int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir, - struct dentry *old_dentry, struct inode *new_dir, - struct dentry *new_dentry, unsigned int flags) +int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir, + struct dentry *old_dentry, struct inode *new_dir, + struct dentry *new_dentry, unsigned int flags) { handle_t *handle = NULL; struct ext4_renament old = { -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Skip orphan handling in U-Boot. We do synchronous journal commits after each operation, so orphan recovery is not needed. Adding to the orphan list without proper locking can corrupt the list. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/orphan.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/fs/ext4l/orphan.c b/fs/ext4l/orphan.c index 32ec59b2978..b142c1bd21f 100644 --- a/fs/ext4l/orphan.c +++ b/fs/ext4l/orphan.c @@ -103,6 +103,15 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode) int err = 0, rc; bool dirty = false; +#ifdef __UBOOT__ + /* + * Skip orphan handling in U-Boot. We do synchronous journal commits + * after each operation, so orphan recovery is not needed. Adding to + * the orphan list without proper locking can corrupt the list. + */ + return 0; +#endif + if (!sbi->s_journal || is_bad_inode(inode)) return 0; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a simple folio cache array to struct address_space for U-Boot's folio management, avoiding the need for Linux's XArray/radix tree infrastructure. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- include/linux/fs.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/linux/fs.h b/include/linux/fs.h index 090ee192061..0bf0d3b0379 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -50,6 +50,11 @@ struct path { /* Buffer operations are in buffer_head.h */ +#ifdef __UBOOT__ +/* Maximum number of cached folios per address_space */ +#define FOLIO_CACHE_MAX 64 +#endif + /* address_space - extended for inode.c */ struct address_space { struct inode *host; @@ -58,6 +63,11 @@ struct address_space { unsigned long writeback_index; struct list_head i_private_list; const struct address_space_operations *a_ops; +#ifdef __UBOOT__ + /* Simple folio cache for U-Boot (no XA/radix tree) */ + struct folio *folio_cache[FOLIO_CACHE_MAX]; + int folio_cache_count; +#endif }; /* block_device - minimal stub */ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add an 'initialized' field to struct percpu_counter to track whether a counter has been properly initialized. Update percpu_counter_init() to set this field and percpu_counter_initialized() to check it. This is needed because ext4 uses percpu_counter_initialized() to check if counters are ready before accessing them. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- include/linux/percpu_counter.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/linux/percpu_counter.h b/include/linux/percpu_counter.h index 58d52d01310..6a8544f7f79 100644 --- a/include/linux/percpu_counter.h +++ b/include/linux/percpu_counter.h @@ -15,6 +15,7 @@ struct percpu_counter { s64 count; s64 counter; /* Alias for count - some code uses this name */ + bool initialized; /* Track if counter has been initialized */ }; static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount, @@ -22,6 +23,7 @@ static inline int percpu_counter_init(struct percpu_counter *fbc, s64 amount, { fbc->count = amount; fbc->counter = amount; + fbc->initialized = true; return 0; } @@ -76,7 +78,7 @@ static inline s64 percpu_counter_sum_positive(struct percpu_counter *fbc) static inline bool percpu_counter_initialized(struct percpu_counter *fbc) { - return true; + return fbc->initialized; } #endif /* _LINUX_PERCPU_COUNTER_H */ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> In U-Boot's ext4l implementation, bh_cache_sync() writes all dirty buffers to disk. However, buffers passed through the journal via jbd2_journal_dirty_metadata() were not being marked dirty for bh_cache_sync() to pick up. Add mark_buffer_dirty() after jbd2_journal_dirty_metadata() to ensure these buffers are written by bh_cache_sync(). This is needed because U-Boot's journal implementation does not write buffers to their final locations. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4_jbd2.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fs/ext4l/ext4_jbd2.c b/fs/ext4l/ext4_jbd2.c index 84a985ce3a2..d8ae15099e1 100644 --- a/fs/ext4l/ext4_jbd2.c +++ b/fs/ext4l/ext4_jbd2.c @@ -367,6 +367,14 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, set_buffer_uptodate(bh); if (ext4_handle_valid(handle)) { err = jbd2_journal_dirty_metadata(handle, bh); +#ifdef __UBOOT__ + /* + * Also mark buffer dirty for bh_cache_sync(). + * The journal may not write buffers to final locations, + * so we need bh_cache_sync() to write them. + */ + mark_buffer_dirty(bh); +#endif /* Errors can only happen due to aborted journal or a nasty bug */ if (!is_handle_aborted(handle) && WARN_ON_ONCE(err)) { ext4_journal_abort_handle(where, line, __func__, bh, -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> When debugging journal corruption issues, invalid journal_head or buffer_head pointers can cause crashes that are difficult to diagnose. Add explicit validation of jh_in and its associated buffer_head at the start of jbd2_journal_write_metadata_buffer() to catch corruption early and provide useful debug output rather than crashing with a SIGSEGV. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/jbd2/journal.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 0cd95df8192..138b650fae9 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -305,9 +305,31 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction, struct buffer_head *new_bh; struct folio *new_folio; unsigned int new_offset; - struct buffer_head *bh_in = jh2bh(jh_in); + struct buffer_head *bh_in; journal_t *journal = transaction->t_journal; +#ifdef __UBOOT__ + /* Validate jh_in before dereferencing */ + if (!jh_in || !jh_in->b_bh) { + printf("jbd2: ERROR: invalid jh_in=%p b_bh=%p\n", + jh_in, jh_in ? jh_in->b_bh : NULL); + return -EIO; + } +#endif + bh_in = jh2bh(jh_in); +#ifdef __UBOOT__ + /* Additional validation for buffer head */ + if (!bh_in->b_folio || !bh_in->b_blocknr) { + printf("jbd2: ERROR: bh=%p folio=%p blocknr=%llu b_data=%p b_count=%d\n", + bh_in, bh_in->b_folio, (unsigned long long)bh_in->b_blocknr, + bh_in->b_data, atomic_read(&bh_in->b_count)); + printf("jbd2: ERROR: jh=%p b_jlist=%d b_jcount=%d b_next=%p\n", + jh_in, jh_in->b_jlist, jh_in->b_jcount, + jh_in->b_tnext); + return -EIO; + } +#endif + /* * The buffer really shouldn't be locked: only the current committing * transaction is allowed to write it, so nobody else is allowed -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> In U-Boot, filesystems may be mounted and unmounted multiple times in a single session. The JBD2 global state (caches) was only initialized once and never cleaned up, preventing proper reinitialization. Add jbd2_journal_exit_global() to properly destroy caches and reset the initialization flag. This allows the JBD2 subsystem to be cleanly reinitialized on subsequent mounts. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/jbd2/journal.c | 33 +++++++++++++++++++++++++++++++++ include/linux/jbd2.h | 1 + 2 files changed, 34 insertions(+) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 138b650fae9..f6478c6ca94 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -3140,6 +3140,10 @@ static int __init journal_init(void) return ret; } +#ifdef __UBOOT__ +static bool jbd2_initialized; +#endif + /** * jbd2_journal_init_global() - Initialize JBD2 global state * @@ -3150,18 +3154,47 @@ static int __init journal_init(void) */ int jbd2_journal_init_global(void) { +#ifdef __UBOOT__ + if (jbd2_initialized) + return 0; +#else static bool initialized; if (initialized) return 0; +#endif if (journal_init()) return -ENOMEM; +#ifdef __UBOOT__ + jbd2_initialized = true; +#else initialized = true; +#endif return 0; } +#ifdef __UBOOT__ +/** + * jbd2_journal_exit_global() - Clean up JBD2 global state + * + * This should be called when unmounting the last ext4 filesystem to + * properly clean up all JBD2 caches and reset global state. This is + * important in U-Boot where we may mount/unmount filesystems multiple + * times in a single session. + */ +void jbd2_journal_exit_global(void) +{ + if (!jbd2_initialized) + return; + + jbd2_remove_jbd_stats_proc_entry(); + jbd2_journal_destroy_caches(); + jbd2_initialized = false; +} +#endif + static void __exit journal_exit(void) { #ifdef CONFIG_JBD2_DEBUG diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 41725fb42af..796d896f70f 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -75,6 +75,7 @@ void __jbd2_debug(int level, const char *file, const char *func, extern void *jbd2_alloc(size_t size, gfp_t flags); extern void jbd2_free(void *ptr, size_t size); extern int jbd2_journal_init_global(void); +extern void jbd2_journal_exit_global(void); #define JBD2_MIN_JOURNAL_BLOCKS 1024 #define JBD2_DEFAULT_FAST_COMMIT_BLOCKS 256 -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Convert kmem_cache_free() and kmem_cache_destroy() from inline functions to declarations, so implementations can be provided in lib/linux_compat.c Also simplify kmem_cache_create macro. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- include/linux/slab.h | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/include/linux/slab.h b/include/linux/slab.h index 2b374641534..599916218d7 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -84,14 +84,15 @@ static inline void *krealloc(const void *p, size_t new_size, gfp_t flags) void *kmemdup(const void *src, size_t len, gfp_t gfp); -/* kmem_cache stubs */ struct kmem_cache { int sz; }; struct kmem_cache *get_mem(int element_sz); -#define kmem_cache_create(a, sz, c, d, e) ({ (void)(a); (void)(e); get_mem(sz); }) +#define kmem_cache_create(a, sz, c, d, e) get_mem(sz) void *kmem_cache_alloc(struct kmem_cache *obj, gfp_t flag); +void kmem_cache_free(struct kmem_cache *cachep, void *obj); +void kmem_cache_destroy(struct kmem_cache *cachep); static inline void *kmem_cache_zalloc(struct kmem_cache *obj, gfp_t flags) { @@ -102,14 +103,4 @@ static inline void *kmem_cache_zalloc(struct kmem_cache *obj, gfp_t flags) return ret; } -static inline void kmem_cache_free(struct kmem_cache *cachep, void *obj) -{ - free(obj); -} - -static inline void kmem_cache_destroy(struct kmem_cache *cachep) -{ - free(cachep); -} - #endif /* _LINUX_SLAB_H */ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add __maybe_unused to ext4_groupinfo_slab_names since U-Boot's kmem_cache_create macro ignores the name parameter, making the array appear unused to the compiler. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/mballoc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4l/mballoc.c b/fs/ext4l/mballoc.c index 6b0926a597b..1d44d9b5cb6 100644 --- a/fs/ext4l/mballoc.c +++ b/fs/ext4l/mballoc.c @@ -409,7 +409,7 @@ static struct kmem_cache *ext4_free_data_cachep; #define NR_GRPINFO_CACHES 8 static struct kmem_cache *ext4_groupinfo_caches[NR_GRPINFO_CACHES]; -static const char * const ext4_groupinfo_slab_names[NR_GRPINFO_CACHES] = { +static const char * const ext4_groupinfo_slab_names[NR_GRPINFO_CACHES] __maybe_unused = { "ext4_groupinfo_1k", "ext4_groupinfo_2k", "ext4_groupinfo_4k", "ext4_groupinfo_8k", "ext4_groupinfo_16k", "ext4_groupinfo_32k", "ext4_groupinfo_64k", "ext4_groupinfo_128k" -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Remove static from ext4_commit_super() to allow calling it from ext4l interface code to sync superblock after write operations. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/super.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ext4l/super.c b/fs/ext4l/super.c index 57653c8acb7..9ed6f907b7a 100644 --- a/fs/ext4l/super.c +++ b/fs/ext4l/super.c @@ -38,7 +38,6 @@ static int ext4_load_journal(struct super_block *, struct ext4_super_block *, unsigned long journal_devnum); static int ext4_show_options(struct seq_file *seq, struct dentry *root); static void ext4_update_super(struct super_block *sb); -static int ext4_commit_super(struct super_block *sb); static int ext4_mark_recovery_complete(struct super_block *sb, struct ext4_super_block *es); static int ext4_clear_journal_err(struct super_block *sb, @@ -6153,7 +6152,7 @@ static void ext4_update_super(struct super_block *sb) unlock_buffer(sbh); } -static int ext4_commit_super(struct super_block *sb) +int ext4_commit_super(struct super_block *sb) { struct buffer_head *sbh = EXT4_SB(sb)->s_sbh; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> In U-Boot, filesystems may be mounted and unmounted multiple times in a single session. The ext4 cache initialization functions would fail on subsequent mounts because the caches were already initialized but pointers were not reset on exit. Add early return checks in init functions when already initialized, and reset cache pointers to NULL in exit functions to allow clean reinitialization. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/block_validity.c | 10 ++++++++++ fs/ext4l/extents_status.c | 6 ++++++ fs/ext4l/mballoc.c | 13 +++++++++++++ 3 files changed, 29 insertions(+) diff --git a/fs/ext4l/block_validity.c b/fs/ext4l/block_validity.c index bff72dcd27c..f110d7ef4c7 100644 --- a/fs/ext4l/block_validity.c +++ b/fs/ext4l/block_validity.c @@ -23,6 +23,12 @@ static struct kmem_cache *ext4_system_zone_cachep; int __init ext4_init_system_zone(void) { +#ifdef __UBOOT__ + /* Already initialized - skip in multiple mount scenarios */ + if (ext4_system_zone_cachep) + return 0; +#endif + ext4_system_zone_cachep = KMEM_CACHE(ext4_system_zone, 0); if (ext4_system_zone_cachep == NULL) return -ENOMEM; @@ -33,6 +39,10 @@ void ext4_exit_system_zone(void) { rcu_barrier(); kmem_cache_destroy(ext4_system_zone_cachep); +#ifdef __UBOOT__ + /* Reset pointer for clean reinitialization */ + ext4_system_zone_cachep = NULL; +#endif } static inline int can_merge(struct ext4_system_zone *entry1, diff --git a/fs/ext4l/extents_status.c b/fs/ext4l/extents_status.c index a3ab26624e7..f557c7c23c2 100644 --- a/fs/ext4l/extents_status.c +++ b/fs/ext4l/extents_status.c @@ -189,6 +189,12 @@ static int __revise_pending(struct inode *inode, ext4_lblk_t lblk, int __init ext4_init_es(void) { +#ifdef __UBOOT__ + /* Already initialized - skip in multiple mount scenarios */ + if (ext4_es_cachep) + return 0; +#endif + ext4_es_cachep = KMEM_CACHE(extent_status, SLAB_RECLAIM_ACCOUNT); if (ext4_es_cachep == NULL) return -ENOMEM; diff --git a/fs/ext4l/mballoc.c b/fs/ext4l/mballoc.c index 1d44d9b5cb6..47863efd1cb 100644 --- a/fs/ext4l/mballoc.c +++ b/fs/ext4l/mballoc.c @@ -4008,6 +4008,12 @@ void ext4_process_freed_data(struct super_block *sb, tid_t commit_tid) int __init ext4_init_mballoc(void) { +#ifdef __UBOOT__ + /* Already initialized - skip in multiple mount scenarios */ + if (ext4_pspace_cachep) + return 0; +#endif + ext4_pspace_cachep = KMEM_CACHE(ext4_prealloc_space, SLAB_RECLAIM_ACCOUNT); if (ext4_pspace_cachep == NULL) @@ -4044,6 +4050,13 @@ void ext4_exit_mballoc(void) kmem_cache_destroy(ext4_ac_cachep); kmem_cache_destroy(ext4_free_data_cachep); ext4_groupinfo_destroy_slabs(); + +#ifdef __UBOOT__ + /* Reset pointers for clean reinitialization */ + ext4_pspace_cachep = NULL; + ext4_ac_cachep = NULL; + ext4_free_data_cachep = NULL; +#endif } #define EXT4_MB_BITMAP_MARKED_CHECK 0x0001 -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Initialize 'len' to 0 to silence a Clang warning about the potential use of an uninitialised variable. The else branch calls BUG() which never returns, but Clang cannot determine this. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/extents.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/ext4l/extents.c b/fs/ext4l/extents.c index 7690f4075db..955f587426b 100644 --- a/fs/ext4l/extents.c +++ b/fs/ext4l/extents.c @@ -2252,7 +2252,7 @@ static ext4_lblk_t ext4_ext_find_hole(struct inode *inode, { int depth = ext_depth(inode); struct ext4_extent *ex; - ext4_lblk_t len; + ext4_lblk_t len = 0; ex = path[depth].p_ext; if (ex == NULL) { -- 2.43.0
participants (1)
-
Simon Glass