[PATCH 0/9] Align ext4l with Linux kernel v6.19 implementation
From: Simon Glass <simon.glass@canonical.com> This series continues aligning ext4l with the Linux kernel ext4 code. It adds helper macros for block/page conversions, folio operations, and inode state accessors, then uses them throughout the codebase. Several files are updated to match Linux more closely, reducing the diff when comparing against upstream. This makes future syncs easier and improves code consistency. Simon Glass (9): fs: ext4l: Add inode state accessor functions fs: ext4l: Add block conversion macros and use them fs: ext4l: Add m_seq field to ext4_map_blocks struct fs: ext4l: Add folio helper macros fs: ext4l: Align verity.c, symlink.c and hash.c with Linux fs: ext4l: Use EXT4_MAX_ORPHAN_FILE_BLOCKS in orphan.c fs: ext4l: Align ialloc.c, inode.c et al with Linux fs: ext4l: Rename EXT4_GET_BLOCKS_PRE_IO flag fs: ext4l: Use conversion macros in inode.c fs/ext4l/ext4.h | 27 +++++++++++--- fs/ext4l/ext4_uboot.h | 60 ++++++++++++++++++++++++++++++ fs/ext4l/extents.c | 20 +++++----- fs/ext4l/extents_status.c | 2 +- fs/ext4l/extents_status.h | 2 +- fs/ext4l/hash.c | 2 +- fs/ext4l/ialloc.c | 1 - fs/ext4l/inode.c | 77 +++++++++++++++++---------------------- fs/ext4l/ioctl.c | 4 ++ fs/ext4l/orphan.c | 8 ++-- fs/ext4l/readpage.c | 7 +--- fs/ext4l/verity.c | 2 +- fs/ext4l/xattr.c | 6 ++- 13 files changed, 146 insertions(+), 72 deletions(-) -- 2.43.0 base-commit: bcb7ccd3c38defb1d68dcd881ca837affbb33d33 branch: exto
From: Simon Glass <simon.glass@canonical.com> Add inode_state_read_once(), inode_state_clear() and related accessor functions to ext4_uboot.h to match the Linux kernel API. These are simplified versions for U-Boot's single-threaded environment - Linux uses READ_ONCE/WRITE_ONCE with lockdep assertions, while U-Boot uses direct access. Update inode.c and orphan.c to use these new accessors instead of direct i_state field access, aligning them with the Linux kernel source. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> --- fs/ext4l/ext4_uboot.h | 46 +++++++++++++++++++++++++++++++++++++++++++ fs/ext4l/inode.c | 10 +++++----- fs/ext4l/orphan.c | 4 ++-- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index b3da5bfa002..943cb31d694 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -934,6 +934,52 @@ static inline void simple_inode_init_ts(struct inode *inode) inode->i_ctime = ts; } +/* + * Inode state accessors - simplified for single-threaded U-Boot. + * Linux uses READ_ONCE/WRITE_ONCE and lockdep assertions; we use direct access. + */ +static inline unsigned long inode_state_read_once(struct inode *inode) +{ + return inode->i_state; +} + +static inline unsigned long inode_state_read(struct inode *inode) +{ + return inode->i_state; +} + +static inline void inode_state_set_raw(struct inode *inode, unsigned long flags) +{ + inode->i_state |= flags; +} + +static inline void inode_state_set(struct inode *inode, unsigned long flags) +{ + inode->i_state |= flags; +} + +static inline void inode_state_clear_raw(struct inode *inode, + unsigned long flags) +{ + inode->i_state &= ~flags; +} + +static inline void inode_state_clear(struct inode *inode, unsigned long flags) +{ + inode->i_state &= ~flags; +} + +static inline void inode_state_assign_raw(struct inode *inode, + unsigned long flags) +{ + inode->i_state = flags; +} + +static inline void inode_state_assign(struct inode *inode, unsigned long flags) +{ + inode->i_state = flags; +} + #define QSTR_INIT(n, l) { .name = (const unsigned char *)(n), .len = (l) } /* dotdot_name for ".." lookups */ diff --git a/fs/ext4l/inode.c b/fs/ext4l/inode.c index dc151c068bb..415c9f7f62f 100644 --- a/fs/ext4l/inode.c +++ b/fs/ext4l/inode.c @@ -403,7 +403,7 @@ void ext4_check_map_extents_env(struct inode *inode) if (!S_ISREG(inode->i_mode) || IS_NOQUOTA(inode) || IS_VERITY(inode) || is_special_ino(inode->i_sb, inode->i_ino) || - (inode->i_state & (I_FREEING | I_WILL_FREE | I_NEW)) || + (inode_state_read_once(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) || ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE) || ext4_verity_in_progress(inode)) return; @@ -3451,7 +3451,7 @@ static bool ext4_inode_datasync_dirty(struct inode *inode) /* Any metadata buffers to write? */ if (!list_empty(&inode->i_mapping->i_private_list)) return true; - return inode->i_state & I_DIRTY_DATASYNC; + return inode_state_read_once(inode) & I_DIRTY_DATASYNC; } static void ext4_set_iomap(struct inode *inode, struct iomap *iomap, @@ -4530,7 +4530,7 @@ int ext4_truncate(struct inode *inode) * or it's a completely new inode. In those cases we might not * have i_rwsem locked because it's not necessary. */ - if (!(inode->i_state & (I_NEW|I_FREEING))) + if (!(inode_state_read_once(inode) & (I_NEW | I_FREEING))) WARN_ON(!inode_is_locked(inode)); trace_ext4_truncate_enter(inode); @@ -5188,7 +5188,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, inode = iget_locked(sb, ino); if (!inode) return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) { + if (!(inode_state_read_once(inode) & I_NEW)) { ret = check_igot_inode(inode, flags, function, line); if (ret) { iput(inode); @@ -5527,7 +5527,7 @@ static void __ext4_update_other_inode_time(struct super_block *sb, if (inode_is_dirtytime_only(inode)) { struct ext4_inode_info *ei = EXT4_I(inode); - inode->i_state &= ~I_DIRTY_TIME; + inode_state_clear(inode, I_DIRTY_TIME); spin_unlock(&inode->i_lock); spin_lock(&ei->i_raw_lock); diff --git a/fs/ext4l/orphan.c b/fs/ext4l/orphan.c index b142c1bd21f..65d0d177c5d 100644 --- a/fs/ext4l/orphan.c +++ b/fs/ext4l/orphan.c @@ -115,7 +115,7 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode) if (!sbi->s_journal || is_bad_inode(inode)) return 0; - WARN_ON_ONCE(!(inode->i_state & (I_NEW | I_FREEING)) && + WARN_ON_ONCE(!(inode_state_read_once(inode) & (I_NEW | I_FREEING)) && !inode_is_locked(inode)); if (ext4_inode_orphan_tracked(inode)) return 0; @@ -240,7 +240,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode) if (!sbi->s_journal && !(sbi->s_mount_state & EXT4_ORPHAN_FS)) return 0; - WARN_ON_ONCE(!(inode->i_state & (I_NEW | I_FREEING)) && + WARN_ON_ONCE(!(inode_state_read_once(inode) & (I_NEW | I_FREEING)) && !inode_is_locked(inode)); if (ext4_test_inode_state(inode, EXT4_STATE_ORPHAN_FILE)) return ext4_orphan_file_del(handle, inode); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add EXT4_LBLK_TO_B(), EXT4_LBLK_TO_PG() and EXT4_PG_TO_LBLK() macros to ext4.h to match the Linux kernel API. These convert between logical block numbers, byte offsets and page indices. Update inode.c to use these macros instead of inline shift operations, aligning the code with the Linux kernel source. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4.h | 8 ++++++++ fs/ext4l/inode.c | 15 +++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/fs/ext4l/ext4.h b/fs/ext4l/ext4.h index a1c80dd7cdf..47054daffc2 100644 --- a/fs/ext4l/ext4.h +++ b/fs/ext4l/ext4.h @@ -342,6 +342,14 @@ struct ext4_io_submit { blkbits)) #define EXT4_B_TO_LBLK(inode, offset) \ (round_up((offset), i_blocksize(inode)) >> (inode)->i_blkbits) +#define EXT4_LBLK_TO_B(inode, lblk) ((loff_t)(lblk) << (inode)->i_blkbits) + +/* Translate a block number to a page index */ +#define EXT4_LBLK_TO_PG(inode, lblk) (EXT4_LBLK_TO_B((inode), (lblk)) >> \ + PAGE_SHIFT) +/* Translate a page index to a block number */ +#define EXT4_PG_TO_LBLK(inode, pnum) (((loff_t)(pnum) << PAGE_SHIFT) >> \ + (inode)->i_blkbits) /* Translate a block number to a cluster number */ #define EXT4_B2C(sbi, blk) ((blk) >> (sbi)->s_cluster_bits) diff --git a/fs/ext4l/inode.c b/fs/ext4l/inode.c index 415c9f7f62f..7001c7299cd 100644 --- a/fs/ext4l/inode.c +++ b/fs/ext4l/inode.c @@ -803,9 +803,8 @@ found: !(flags & EXT4_GET_BLOCKS_ZERO) && !ext4_is_quota_file(inode) && ext4_should_order_data(inode)) { - loff_t start_byte = - (loff_t)map->m_lblk << inode->i_blkbits; - loff_t length = (loff_t)map->m_len << inode->i_blkbits; + loff_t start_byte = EXT4_LBLK_TO_B(inode, map->m_lblk); + loff_t length = EXT4_LBLK_TO_B(inode, map->m_len); if (flags & EXT4_GET_BLOCKS_IO_SUBMIT) ret = ext4_jbd2_inode_add_wait(handle, inode, @@ -2384,7 +2383,7 @@ static int mpage_submit_partial_folio(struct mpage_da_data *mpd) * The mapped position should be within the current processing folio * but must not be the folio start position. */ - pos = ((loff_t)mpd->map.m_lblk) << inode->i_blkbits; + pos = EXT4_LBLK_TO_B(inode, mpd->map.m_lblk); if (WARN_ON_ONCE((folio_pos(folio) == pos) || !folio_contains(folio, pos >> PAGE_SHIFT))) return -EINVAL; @@ -2441,7 +2440,7 @@ static int mpage_map_and_submit_extent(handle_t *handle, io_end_vec = ext4_alloc_io_end_vec(io_end); if (IS_ERR(io_end_vec)) return PTR_ERR(io_end_vec); - io_end_vec->offset = ((loff_t)map->m_lblk) << inode->i_blkbits; + io_end_vec->offset = EXT4_LBLK_TO_B(inode, map->m_lblk); do { err = mpage_map_one_extent(handle, mpd); if (err < 0) { @@ -3481,8 +3480,8 @@ static void ext4_set_iomap(struct inode *inode, struct iomap *iomap, iomap->dax_dev = EXT4_SB(inode->i_sb)->s_daxdev; else iomap->bdev = inode->i_sb->s_bdev; - iomap->offset = (u64) map->m_lblk << blkbits; - iomap->length = (u64) map->m_len << blkbits; + iomap->offset = EXT4_LBLK_TO_B(inode, map->m_lblk); + iomap->length = EXT4_LBLK_TO_B(inode, map->m_len); if ((map->m_flags & EXT4_MAP_MAPPED) && !ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) @@ -3715,7 +3714,7 @@ retry: * i_disksize out to i_size. This could be beyond where direct I/O is * happening and thus expose allocated blocks to direct I/O reads. */ - else if (((loff_t)map->m_lblk << blkbits) >= i_size_read(inode)) + else if (EXT4_LBLK_TO_B(inode, map->m_lblk) >= i_size_read(inode)) m_flags = EXT4_GET_BLOCKS_CREATE; else if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) m_flags = EXT4_GET_BLOCKS_IO_CREATE_EXT; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add the m_seq (extent status sequence) field to struct ext4_map_blocks to match the Linux kernel structure. This field is used in Linux for tracking extent status cache coherency but is unused in U-Boot's single-threaded environment. Update ext4_es_lookup_extent() to accept an optional m_seqp parameter matching the Linux kernel API, and update all call sites accordingly. The sequence value is never actually used in U-Boot, but having the field and parameter present allows the code to match Linux more closely. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4.h | 1 + fs/ext4l/extents.c | 2 +- fs/ext4l/extents_status.c | 2 +- fs/ext4l/extents_status.h | 2 +- fs/ext4l/inode.c | 8 ++++---- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/ext4l/ext4.h b/fs/ext4l/ext4.h index 47054daffc2..1cbeebaf0a6 100644 --- a/fs/ext4l/ext4.h +++ b/fs/ext4l/ext4.h @@ -235,6 +235,7 @@ struct ext4_map_blocks { ext4_lblk_t m_lblk; unsigned int m_len; unsigned int m_flags; + u64 m_seq; /* Extent status sequence (unused in U-Boot) */ }; /* diff --git a/fs/ext4l/extents.c b/fs/ext4l/extents.c index 955f587426b..d2103fbac83 100644 --- a/fs/ext4l/extents.c +++ b/fs/ext4l/extents.c @@ -2200,7 +2200,7 @@ static int ext4_fill_es_cache_info(struct inode *inode, while (block <= end) { next = 0; flags = 0; - if (!ext4_es_lookup_extent(inode, block, &next, &es)) + if (!ext4_es_lookup_extent(inode, block, &next, &es, NULL)) break; if (ext4_es_is_unwritten(&es)) flags |= FIEMAP_EXTENT_UNWRITTEN; diff --git a/fs/ext4l/extents_status.c b/fs/ext4l/extents_status.c index f557c7c23c2..706e267a79f 100644 --- a/fs/ext4l/extents_status.c +++ b/fs/ext4l/extents_status.c @@ -1037,7 +1037,7 @@ void ext4_es_cache_extent(struct inode *inode, ext4_lblk_t lblk, */ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t *next_lblk, - struct extent_status *es) + struct extent_status *es, u64 *m_seqp) { struct ext4_es_tree *tree; struct ext4_es_stats *stats; diff --git a/fs/ext4l/extents_status.h b/fs/ext4l/extents_status.h index 8f9c008d11e..e668cda0d92 100644 --- a/fs/ext4l/extents_status.h +++ b/fs/ext4l/extents_status.h @@ -148,7 +148,7 @@ extern void ext4_es_find_extent_range(struct inode *inode, struct extent_status *es); extern int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk, ext4_lblk_t *next_lblk, - struct extent_status *es); + struct extent_status *es, u64 *m_seqp); extern bool ext4_es_scan_range(struct inode *inode, int (*matching_fn)(struct extent_status *es), ext4_lblk_t lblk, ext4_lblk_t end); diff --git a/fs/ext4l/inode.c b/fs/ext4l/inode.c index 7001c7299cd..04b7c734f7f 100644 --- a/fs/ext4l/inode.c +++ b/fs/ext4l/inode.c @@ -627,7 +627,7 @@ static int ext4_map_create_blocks(handle_t *handle, struct inode *inode, * extent status tree. */ if (flags & EXT4_GET_BLOCKS_PRE_IO && - ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) { + ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, &map->m_seq)) { if (ext4_es_is_written(&es)) return retval; } @@ -701,7 +701,7 @@ int ext4_map_blocks(handle_t *handle, struct inode *inode, ext4_check_map_extents_env(inode); /* Lookup extent status tree firstly */ - if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) { + if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, &map->m_seq)) { if (ext4_es_is_written(&es) || ext4_es_is_unwritten(&es)) { map->m_pblk = ext4_es_pblock(&es) + map->m_lblk - es.es_lblk; @@ -1885,7 +1885,7 @@ static int ext4_da_map_blocks(struct inode *inode, struct ext4_map_blocks *map) ext4_check_map_extents_env(inode); /* Lookup extent status tree firstly */ - if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) { + if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, NULL)) { map->m_len = min_t(unsigned int, map->m_len, es.es_len - (map->m_lblk - es.es_lblk)); @@ -1938,7 +1938,7 @@ add_delayed: * is held in write mode, before inserting a new da entry in * the extent status tree. */ - if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es)) { + if (ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, NULL)) { map->m_len = min_t(unsigned int, map->m_len, es.es_len - (map->m_lblk - es.es_lblk)); -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add folio_next_pos() and fgf_set_order() macros to ext4_uboot.h to match the Linux kernel API. folio_next_pos() returns the byte position following a folio, useful for checking if a folio extends past a given position. fgf_set_order() is a stub that returns 0 since U-Boot doesn't support large folios. In Linux, this is used to request a specific folio size when allocating. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4_uboot.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index 943cb31d694..2487d6fc70f 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -1565,7 +1565,14 @@ static inline char *d_path(const struct path *path, char *buf, int buflen) #define folio_zero_range(f, s, l) do { } while (0) #define folio_mark_uptodate(f) do { } while (0) #define folio_next_index(f) ((f)->index + 1) +#define folio_next_pos(f) ((loff_t)folio_next_index(f) << PAGE_SHIFT) #define folio_mapped(f) (0) + +/* + * fgf_set_order - Set the order (size) for folio allocation + * U-Boot doesn't support large folios, so this is a no-op stub. + */ +#define fgf_set_order(size) (0) #define folio_clear_dirty_for_io(f) ({ (void)(f); 1; }) #define folio_clear_uptodate(f) do { } while (0) #define folio_batch_release(fb) do { } while (0) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> verity.c: - Use EXT4_LBLK_TO_B() macro instead of inline shift symlink.c: - Change ext4_free_link() parameter from const void* to void* to match Linux kernel signature and remove unnecessary cast hash.c: - Return -EINVAL instead of -1 for error consistency Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/hash.c | 2 +- fs/ext4l/verity.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/ext4l/hash.c b/fs/ext4l/hash.c index 1ce0c2bcc84..58063a872d3 100644 --- a/fs/ext4l/hash.c +++ b/fs/ext4l/hash.c @@ -267,7 +267,7 @@ static int __ext4fs_dirhash(const struct inode *dir, const char *name, int len, combined_hash = fscrypt_fname_siphash(dir, &qname); } else { ext4_warning_inode(dir, "Siphash requires key"); - return -1; + return -EINVAL; } hash = (__u32)(combined_hash >> 32); diff --git a/fs/ext4l/verity.c b/fs/ext4l/verity.c index b0acb0c5031..415d9c4d8a3 100644 --- a/fs/ext4l/verity.c +++ b/fs/ext4l/verity.c @@ -302,7 +302,7 @@ static int ext4_get_verity_descriptor_location(struct inode *inode, end_lblk = le32_to_cpu(last_extent->ee_block) + ext4_ext_get_actual_len(last_extent); - desc_size_pos = (u64)end_lblk << inode->i_blkbits; + desc_size_pos = EXT4_LBLK_TO_B(inode, end_lblk); ext4_free_ext_path(path); if (desc_size_pos < sizeof(desc_size_disk)) -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add the EXT4_MAX_ORPHAN_FILE_BLOCKS constant (512) and use it in the orphan file size check, matching the Linux kernel implementation. The previous hardcoded value of (8 << 20) assumed 16KB blocks to get 512 blocks, but the proper calculation uses the actual block size from the inode. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/orphan.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/ext4l/orphan.c b/fs/ext4l/orphan.c index 65d0d177c5d..0f5dcca1d96 100644 --- a/fs/ext4l/orphan.c +++ b/fs/ext4l/orphan.c @@ -7,6 +7,8 @@ #include "ext4.h" #include "ext4_jbd2.h" +#define EXT4_MAX_ORPHAN_FILE_BLOCKS 512 + static int ext4_orphan_file_add(handle_t *handle, struct inode *inode) { int i, j, start; @@ -596,7 +598,7 @@ int ext4_init_orphan_info(struct super_block *sb) * consuming absurd amounts of memory when pinning blocks of orphan * file in memory. */ - if (inode->i_size > 8 << 20) { + if (inode->i_size > (EXT4_MAX_ORPHAN_FILE_BLOCKS << inode->i_blkbits)) { ext4_msg(sb, KERN_ERR, "orphan file too big: %llu", (unsigned long long)inode->i_size); ret = -EFSCORRUPTED; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Apply several small fixes to align with the Linux kernel ext4 code: - readpage.c: Use EXT4_PG_TO_LBLK macro instead of inline shifts for block calculations, matching the macro added in an earlier commit - xattr.c: Add error handling for ext4_get_inode_loc() call in ext4_xattr_inode_dec_ref_all() - ioctl.c: Add strnlen check for mount_opts to prevent reading past the buffer end - ialloc.c, inode.c: Remove ext4_clear_state_flags() calls which were removed in upstream Linux (the subsequent ext4_set_inode_state() handles the initialization) Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ialloc.c | 1 - fs/ext4l/inode.c | 1 - fs/ext4l/ioctl.c | 4 ++++ fs/ext4l/readpage.c | 7 ++----- fs/ext4l/xattr.c | 6 +++++- 5 files changed, 11 insertions(+), 8 deletions(-) diff --git a/fs/ext4l/ialloc.c b/fs/ext4l/ialloc.c index 5bbe8d6d359..baef744892b 100644 --- a/fs/ext4l/ialloc.c +++ b/fs/ext4l/ialloc.c @@ -1279,7 +1279,6 @@ got: ei->i_csum_seed = ext4_chksum(csum, (__u8 *)&gen, sizeof(gen)); } - ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */ ext4_set_inode_state(inode, EXT4_STATE_NEW); ei->i_extra_isize = sbi->s_want_extra_isize; diff --git a/fs/ext4l/inode.c b/fs/ext4l/inode.c index 04b7c734f7f..d0262fa53f2 100644 --- a/fs/ext4l/inode.c +++ b/fs/ext4l/inode.c @@ -5265,7 +5265,6 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, ei->i_projid = make_kprojid(&init_user_ns, i_projid); set_nlink(inode, le16_to_cpu(raw_inode->i_links_count)); - ext4_clear_state_flags(ei); /* Only relevant on 32-bit archs */ ei->i_inline_off = 0; ei->i_dir_start_lookup = 0; ei->i_dtime = le32_to_cpu(raw_inode->i_dtime); diff --git a/fs/ext4l/ioctl.c b/fs/ext4l/ioctl.c index a93a7baae99..3dec26c939f 100644 --- a/fs/ext4l/ioctl.c +++ b/fs/ext4l/ioctl.c @@ -1394,6 +1394,10 @@ static int ext4_ioctl_set_tune_sb(struct file *filp, if (copy_from_user(¶ms, in, sizeof(params))) return -EFAULT; + if (strnlen(params.mount_opts, sizeof(params.mount_opts)) == + sizeof(params.mount_opts)) + return -E2BIG; + if ((params.set_flags & ~TUNE_OPS_SUPPORTED) != 0) return -EOPNOTSUPP; diff --git a/fs/ext4l/readpage.c b/fs/ext4l/readpage.c index 94361c01f08..7f1cb419339 100644 --- a/fs/ext4l/readpage.c +++ b/fs/ext4l/readpage.c @@ -199,9 +199,7 @@ int ext4_mpage_readpages(struct inode *inode, { struct bio *bio = NULL; sector_t last_block_in_bio = 0; - const unsigned blkbits = inode->i_blkbits; - const unsigned blocks_per_page = PAGE_SIZE >> blkbits; const unsigned blocksize = 1 << blkbits; sector_t next_block; sector_t block_in_file; @@ -237,9 +235,8 @@ int ext4_mpage_readpages(struct inode *inode, blocks_per_folio = folio_size(folio) >> blkbits; first_hole = blocks_per_folio; - block_in_file = next_block = - (sector_t)folio->index << (PAGE_SHIFT - blkbits); - last_block = block_in_file + nr_pages * blocks_per_page; + block_in_file = next_block = EXT4_PG_TO_LBLK(inode, folio->index); + last_block = EXT4_PG_TO_LBLK(inode, folio->index + nr_pages); last_block_in_file = (ext4_readpage_limit(inode) + blocksize - 1) >> blkbits; if (last_block > last_block_in_file) diff --git a/fs/ext4l/xattr.c b/fs/ext4l/xattr.c index bd89822a682..a17ce41eba3 100644 --- a/fs/ext4l/xattr.c +++ b/fs/ext4l/xattr.c @@ -1170,7 +1170,11 @@ ext4_xattr_inode_dec_ref_all(handle_t *handle, struct inode *parent, if (block_csum) end = (void *)bh->b_data + bh->b_size; else { - ext4_get_inode_loc(parent, &iloc); + err = ext4_get_inode_loc(parent, &iloc); + if (err) { + EXT4_ERROR_INODE(parent, "parent inode loc (error %d)", err); + return; + } end = (void *)ext4_raw_inode(&iloc) + EXT4_SB(parent->i_sb)->s_inode_size; } -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Align with the Linux kernel's rename of this flag. The flag is used to indicate that newly allocated extents cannot be merged, and if an unwritten extent is found, it needs to be split. Rename to EXT4_GET_BLOCKS_SPLIT_NOMERGE to match Linux v6.12 Also update extents.c to: - Use EXT4_LBLK_TO_B() macro for block-to-byte conversion - Remove unnecessary initialisation of the 'len' variable in ext4_ext_find_hole() Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4.h | 18 +++++++++++++----- fs/ext4l/extents.c | 18 +++++++++--------- fs/ext4l/inode.c | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/fs/ext4l/ext4.h b/fs/ext4l/ext4.h index 1cbeebaf0a6..34dbc4f12b8 100644 --- a/fs/ext4l/ext4.h +++ b/fs/ext4l/ext4.h @@ -678,12 +678,20 @@ enum { /* Caller is from the delayed allocation writeout path * finally doing the actual allocation of delayed blocks */ #define EXT4_GET_BLOCKS_DELALLOC_RESERVE 0x0004 - /* caller is from the direct IO path, request to creation of an - unwritten extents if not allocated, split the unwritten - extent if blocks has been preallocated already*/ -#define EXT4_GET_BLOCKS_PRE_IO 0x0008 + /* + * This means that we cannot merge newly allocated extents, and if we + * found an unwritten extent, we need to split it. + */ +#define EXT4_GET_BLOCKS_SPLIT_NOMERGE 0x0008 #define EXT4_GET_BLOCKS_CONVERT 0x0010 -#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_PRE_IO|\ + /* + * Caller is from the dio or dioread_nolock buffered IO, request to + * create an unwritten extent if it does not exist or split the + * found unwritten extent. Also do not merge the newly created + * unwritten extent, io end will convert unwritten to written, + * and try to merge the written extent. + */ +#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_SPLIT_NOMERGE|\ EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT) /* Eventual metadata allocation (due to growing extent tree) * should not fail, so try to use reserved blocks for that.*/ diff --git a/fs/ext4l/extents.c b/fs/ext4l/extents.c index d2103fbac83..028d77272b5 100644 --- a/fs/ext4l/extents.c +++ b/fs/ext4l/extents.c @@ -320,7 +320,7 @@ ext4_force_split_extent_at(handle_t *handle, struct inode *inode, int nofail) { int unwritten = ext4_ext_is_unwritten(path[path->p_depth].p_ext); - int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_PRE_IO; + int flags = EXT4_EX_NOCACHE | EXT4_GET_BLOCKS_SPLIT_NOMERGE; if (nofail) flags |= EXT4_GET_BLOCKS_METADATA_NOFAIL | EXT4_EX_NOFAIL; @@ -1989,7 +1989,7 @@ ext4_ext_insert_extent(handle_t *handle, struct inode *inode, } /* try to insert block into found extent and return */ - if (ex && !(gb_flags & EXT4_GET_BLOCKS_PRE_IO)) { + if (ex && !(gb_flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE)) { /* * Try to see whether we should rather test the extent on @@ -2168,7 +2168,7 @@ has_space: merge: /* try to merge extents */ - if (!(gb_flags & EXT4_GET_BLOCKS_PRE_IO)) + if (!(gb_flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE)) ext4_ext_try_to_merge(handle, inode, path, nearex); /* time to correct all indexes above */ @@ -3211,7 +3211,7 @@ static struct ext4_ext_path *ext4_split_extent_at(handle_t *handle, else ext4_ext_mark_initialized(ex); - if (!(flags & EXT4_GET_BLOCKS_PRE_IO)) + if (!(flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE)) ext4_ext_try_to_merge(handle, inode, path, ex); err = ext4_ext_dirty(handle, inode, path + path->p_depth); @@ -3355,7 +3355,7 @@ static struct ext4_ext_path *ext4_split_extent(handle_t *handle, if (map->m_lblk + map->m_len < ee_block + ee_len) { split_flag1 = split_flag & EXT4_EXT_MAY_ZEROOUT; - flags1 = flags | EXT4_GET_BLOCKS_PRE_IO; + flags1 = flags | EXT4_GET_BLOCKS_SPLIT_NOMERGE; if (unwritten) split_flag1 |= EXT4_EXT_MARK_UNWRIT1 | EXT4_EXT_MARK_UNWRIT2; @@ -3726,7 +3726,7 @@ static struct ext4_ext_path *ext4_split_convert_extents(handle_t *handle, EXT4_EXT_MAY_ZEROOUT : 0; split_flag |= (EXT4_EXT_MARK_UNWRIT2 | EXT4_EXT_DATA_VALID2); } - flags |= EXT4_GET_BLOCKS_PRE_IO; + flags |= EXT4_GET_BLOCKS_SPLIT_NOMERGE; return ext4_split_extent(handle, inode, path, map, split_flag, flags, allocated); } @@ -3898,7 +3898,7 @@ ext4_ext_handle_unwritten_extents(handle_t *handle, struct inode *inode, *allocated, newblock); /* get_block() before submitting IO, split the extent */ - if (flags & EXT4_GET_BLOCKS_PRE_IO) { + if (flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE) { path = ext4_split_convert_extents(handle, inode, map, path, flags | EXT4_GET_BLOCKS_CONVERT, allocated); if (IS_ERR(path)) @@ -4549,7 +4549,7 @@ retry: * allow a full retry cycle for any remaining allocations */ retries = 0; - epos = (loff_t)(map.m_lblk + ret) << blkbits; + epos = EXT4_LBLK_TO_B(inode, map.m_lblk + ret); inode_set_ctime_current(inode); if (new_size) { if (epos > new_size) @@ -5605,7 +5605,7 @@ static int ext4_insert_range(struct file *file, loff_t offset, loff_t len) path = ext4_split_extent_at(handle, inode, path, start_lblk, split_flag, EXT4_EX_NOCACHE | - EXT4_GET_BLOCKS_PRE_IO | + EXT4_GET_BLOCKS_SPLIT_NOMERGE | EXT4_GET_BLOCKS_METADATA_NOFAIL); } diff --git a/fs/ext4l/inode.c b/fs/ext4l/inode.c index d0262fa53f2..12f1333660f 100644 --- a/fs/ext4l/inode.c +++ b/fs/ext4l/inode.c @@ -626,7 +626,7 @@ static int ext4_map_create_blocks(handle_t *handle, struct inode *inode, * If the extent has been zeroed out, we don't need to update * extent status tree. */ - if (flags & EXT4_GET_BLOCKS_PRE_IO && + if (flags & EXT4_GET_BLOCKS_SPLIT_NOMERGE && ext4_es_lookup_extent(inode, map->m_lblk, NULL, &es, &map->m_seq)) { if (ext4_es_is_written(&es)) return retval; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Update inode.c to use the block conversion macros and other helpers to align with the Linux kernel implementation: - Add wbc_to_tag() helper to ext4_uboot.h matching the Linux kernel version from include/linux/writeback.h - Use EXT4_LBLK_TO_B() for logical block to byte conversion - Use EXT4_PG_TO_LBLK() for page index to logical block conversion - Use EXT4_LBLK_TO_PG() for logical block to page conversion - Use folio_next_pos() instead of folio_pos() + folio_size() - Use i_blocksize() instead of (1 << blkbits) - Remove now-unused blkbits and bpp_bits local variables - Add WARN_ON_ONCE() check in ext4_block_write_begin() These changes make the code more consistent with Linux and reduce the line count difference. Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- fs/ext4l/ext4_uboot.h | 7 +++++++ fs/ext4l/inode.c | 41 +++++++++++++++++------------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h index 2487d6fc70f..c013b9605a4 100644 --- a/fs/ext4l/ext4_uboot.h +++ b/fs/ext4l/ext4_uboot.h @@ -1337,6 +1337,13 @@ typedef unsigned int xa_mark_t; #define PAGECACHE_TAG_TOWRITE 1 #define PAGECACHE_TAG_WRITEBACK 2 +static inline xa_mark_t wbc_to_tag(struct writeback_control *wbc) +{ + if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) + return PAGECACHE_TAG_TOWRITE; + return PAGECACHE_TAG_DIRTY; +} + /* blk_plug - block I/O plugging stub */ struct blk_plug { int dummy; diff --git a/fs/ext4l/inode.c b/fs/ext4l/inode.c index 12f1333660f..2c60eaefa14 100644 --- a/fs/ext4l/inode.c +++ b/fs/ext4l/inode.c @@ -1140,8 +1140,7 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio, unsigned block_start, block_end; sector_t block; int err = 0; - unsigned blocksize = inode->i_sb->s_blocksize; - unsigned bbits; + unsigned int blocksize = i_blocksize(inode); struct buffer_head *bh, *head, *wait[2]; int nr_wait = 0; int i; @@ -1150,12 +1149,12 @@ int ext4_block_write_begin(handle_t *handle, struct folio *folio, BUG_ON(!folio_test_locked(folio)); BUG_ON(to > folio_size(folio)); BUG_ON(from > to); + WARN_ON_ONCE(blocksize > folio_size(folio)); head = folio_buffers(folio); if (!head) head = create_empty_buffers(folio, blocksize, 0); - bbits = ilog2(blocksize); - block = (sector_t)folio->index << (PAGE_SHIFT - bbits); + block = EXT4_PG_TO_LBLK(inode, folio->index); for (bh = head, block_start = 0; bh != head || !block_start; block++, block_start = block_end, bh = bh->b_this_page) { @@ -1296,8 +1295,8 @@ retry_grab: if (IS_ERR(folio)) return PTR_ERR(folio); - if (pos + len > folio_pos(folio) + folio_size(folio)) - len = folio_pos(folio) + folio_size(folio) - pos; + if (len > folio_next_pos(folio) - pos) + len = folio_next_pos(folio) - pos; from = offset_in_folio(folio, pos); to = from + len; @@ -2202,7 +2201,6 @@ static int mpage_process_folio(struct mpage_da_data *mpd, struct folio *folio, ext4_lblk_t lblk = *m_lblk; ext4_fsblk_t pblock = *m_pblk; int err = 0; - int blkbits = mpd->inode->i_blkbits; ssize_t io_end_size = 0; struct ext4_io_end_vec *io_end_vec = ext4_last_io_end_vec(io_end); @@ -2228,7 +2226,8 @@ static int mpage_process_folio(struct mpage_da_data *mpd, struct folio *folio, err = PTR_ERR(io_end_vec); goto out; } - io_end_vec->offset = (loff_t)mpd->map.m_lblk << blkbits; + io_end_vec->offset = EXT4_LBLK_TO_B(mpd->inode, + mpd->map.m_lblk); } *map_bh = true; goto out; @@ -2238,7 +2237,7 @@ static int mpage_process_folio(struct mpage_da_data *mpd, struct folio *folio, bh->b_blocknr = pblock++; } clear_buffer_unwritten(bh); - io_end_size += (1 << blkbits); + io_end_size += i_blocksize(mpd->inode); } while (lblk++, (bh = bh->b_this_page) != head); io_end_vec->size += io_end_size; @@ -2268,15 +2267,14 @@ static int mpage_map_and_submit_buffers(struct mpage_da_data *mpd) struct folio_batch fbatch; unsigned nr, i; struct inode *inode = mpd->inode; - int bpp_bits = PAGE_SHIFT - inode->i_blkbits; pgoff_t start, end; ext4_lblk_t lblk; ext4_fsblk_t pblock; int err; bool map_bh = false; - start = mpd->map.m_lblk >> bpp_bits; - end = (mpd->map.m_lblk + mpd->map.m_len - 1) >> bpp_bits; + start = EXT4_LBLK_TO_PG(inode, mpd->map.m_lblk); + end = EXT4_LBLK_TO_PG(inode, mpd->map.m_lblk + mpd->map.m_len - 1); pblock = mpd->map.m_pblk; folio_batch_init(&fbatch); @@ -2287,7 +2285,7 @@ static int mpage_map_and_submit_buffers(struct mpage_da_data *mpd) for (i = 0; i < nr; i++) { struct folio *folio = fbatch.folios[i]; - lblk = folio->index << bpp_bits; + lblk = EXT4_PG_TO_LBLK(inode, folio->index); err = mpage_process_folio(mpd, folio, &lblk, &pblock, &map_bh); /* @@ -2590,16 +2588,12 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd) pgoff_t end = mpd->end_pos >> PAGE_SHIFT; xa_mark_t tag; int i, err = 0; - int blkbits = mpd->inode->i_blkbits; ext4_lblk_t lblk; struct buffer_head *head; handle_t *handle = NULL; int bpp = ext4_journal_blocks_per_folio(mpd->inode); - if (mpd->wbc->sync_mode == WB_SYNC_ALL || mpd->wbc->tagged_writepages) - tag = PAGECACHE_TAG_TOWRITE; - else - tag = PAGECACHE_TAG_DIRTY; + tag = wbc_to_tag(mpd->wbc); mpd->map.m_len = 0; mpd->next_pos = mpd->start_pos; @@ -2629,7 +2623,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd) */ if (mpd->wbc->sync_mode == WB_SYNC_NONE && mpd->wbc->nr_to_write <= - mpd->map.m_len >> (PAGE_SHIFT - blkbits)) + EXT4_LBLK_TO_PG(mpd->inode, mpd->map.m_len)) goto out; /* If we can't merge this page, we are done. */ @@ -2681,7 +2675,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd) if (mpd->map.m_len == 0) mpd->start_pos = folio_pos(folio); - mpd->next_pos = folio_pos(folio) + folio_size(folio); + mpd->next_pos = folio_next_pos(folio); /* * Writeout when we cannot modify metadata is simple. * Just submit the page. For data=journal mode we @@ -2707,8 +2701,7 @@ static int mpage_prepare_extent_to_map(struct mpage_da_data *mpd) mpage_folio_done(mpd, folio); } else { /* Add all dirty buffers to mpd */ - lblk = ((ext4_lblk_t)folio->index) << - (PAGE_SHIFT - blkbits); + lblk = EXT4_PG_TO_LBLK(mpd->inode, folio->index); head = folio_buffers(folio); err = mpage_process_page_bufs(mpd, head, head, lblk); @@ -3123,8 +3116,8 @@ retry: if (IS_ERR(folio)) return PTR_ERR(folio); - if (pos + len > folio_pos(folio) + folio_size(folio)) - len = folio_pos(folio) + folio_size(folio) - pos; + if (len > folio_next_pos(folio) - pos) + len = folio_next_pos(folio) - pos; ret = ext4_block_write_begin(NULL, folio, pos, len, ext4_da_get_block_prep); -- 2.43.0
participants (1)
-
Simon Glass