fs: ext4l: Support mounting a filesystem read-only

Fix several issues preventing ext4_fill_super() from completing:

- Fix shrinker_alloc() to return a dummy shrinker instead of NULL
- Fix getblk_unmovable() to use sb_getblk() instead of returning NULL
- Fix mb_cache_create() to allocate a cache structure
- Implement d_make_root() to create root dentry for mount
- Add ext4_init_system_zone() call for block validity checking
- Add global task_struct for journal_info consistency
- Store super_block pointer after successful mount

The filesystem now mounts successfully in read-only mode.

Series-to: concept
Series-links: 87
Cover-letter:
fs: ext4l: Add support for mounting ext4 filesystems (part G)
This series adds the ext4l filesystem driver, which is a port of the
Linux ext4 driver to U-Boot. It allows mounting ext4 filesystems in
read-only mode.

The driver uses Linux kernel code directly where possible, with
compatibility shims to handle differences between Linux and U-Boot.
This approach makes it easier to keep the driver up to date with
upstream changes.

Key features:
- Read-only mounting of ext4 filesystems
- Support for extents, flex_bg, and other ext4 features
- Buffer cache for improved performance
- CRC32C checksums for metadata verification
END

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
Simon Glass
2025-12-21 15:41:45 -07:00
parent 0553cf40c7
commit 5144ac35d2
5 changed files with 81 additions and 16 deletions

View File

@@ -58,16 +58,14 @@ struct timespec64 {
};
/*
* ktime_t, sector_t are in linux/types.h
* atomic_t, atomic64_t are in asm-generic/atomic.h
* MAX_JIFFY_OFFSET is in linux/jiffies.h
* BDEVNAME_SIZE is in linux/blkdev.h
* ktime_t, atomic_t, atomic64_t, sector_t are now in linux/types.h
* MAX_JIFFY_OFFSET is now in linux/jiffies.h
* BDEVNAME_SIZE is now in linux/blkdev.h
*/
#include <asm-generic/atomic.h>
#include <linux/jiffies.h>
#include <linux/blkdev.h>
/* Extra atomic operations not in asm-generic/atomic.h */
/* Extra atomic operation not in linux/types.h */
#define atomic_dec_if_positive(v) (--(v)->counter)
/* SMP stubs - U-Boot is single-threaded */
@@ -1138,7 +1136,10 @@ struct shrinker {
static inline struct shrinker *shrinker_alloc(unsigned int flags,
const char *fmt, ...)
{
return NULL;
/* Return static dummy - U-Boot doesn't need memory reclamation */
static struct shrinker dummy_shrinker;
return &dummy_shrinker;
}
static inline void shrinker_register(struct shrinker *s)
@@ -1506,7 +1507,7 @@ static inline char *d_path(const struct path *path, char *buf, int buflen)
#define filemap_splice_read(i, p, pi, l, f) ({ (void)(i); (void)(p); (void)(pi); (void)(l); (void)(f); 0L; })
/* Buffer operations - additional */
#define getblk_unmovable(bd, b, s) ((struct buffer_head *)NULL)
#define getblk_unmovable(bdev, block, size) sb_getblk(bdev->bd_super, block)
#define create_empty_buffers(f, s, flags) ({ (void)(f); (void)(s); (void)(flags); (struct buffer_head *)NULL; })
#define bh_offset(bh) (0UL)
#define block_invalidate_folio(f, o, l) do { } while (0)
@@ -2314,8 +2315,8 @@ struct mb_cache_entry {
/* MB cache flags */
#define MBE_REUSABLE_B 0
#define mb_cache_create(bits) ((struct mb_cache *)NULL)
#define mb_cache_destroy(cache) do { (void)(cache); } while (0)
#define mb_cache_create(bits) kzalloc(sizeof(struct mb_cache), GFP_KERNEL)
#define mb_cache_destroy(cache) do { kfree(cache); } while (0)
#define mb_cache_entry_find_first(c, h) ((struct mb_cache_entry *)NULL)
#define mb_cache_entry_find_next(c, e) ((struct mb_cache_entry *)NULL)
#define mb_cache_entry_delete_or_get(c, k, v) ((struct mb_cache_entry *)NULL)

View File

@@ -28,6 +28,9 @@ static struct blk_desc *ext4l_blk_dev;
static struct disk_partition ext4l_partition;
static int ext4l_mounted;
/* Global super_block pointer for filesystem operations */
static struct super_block *ext4l_sb;
/**
* ext4l_get_blk_dev() - Get the current block device
* Return: Block device descriptor or NULL if not mounted
@@ -89,9 +92,7 @@ int ext4l_probe(struct blk_desc *fs_dev_desc,
if (!fs_dev_desc)
return -EINVAL;
/* Set up block device for buffer I/O */
ext4l_set_blk_dev(fs_dev_desc, fs_partition);
/* Initialise CRC32C table for checksum verification */
ext4l_crc32c_init();
/* Initialise journal subsystem if enabled */
@@ -113,6 +114,11 @@ int ext4l_probe(struct blk_desc *fs_dev_desc,
if (ret)
return ret;
/* Initialise system zone for block validity checking */
ret = ext4_init_system_zone();
if (ret)
goto err_exit_es;
/* Allocate super_block */
sb = kzalloc(sizeof(struct super_block), GFP_KERNEL);
if (!sb) {
@@ -193,11 +199,18 @@ int ext4l_probe(struct blk_desc *fs_dev_desc,
if (fs_partition)
memcpy(&ext4l_part, fs_partition, sizeof(ext4l_part));
/* Set block device for buffer I/O */
ext4l_set_blk_dev(fs_dev_desc, fs_partition);
/* Mount the filesystem */
ret = ext4_fill_super(sb, fc);
if (ret)
if (ret) {
printf("ext4l: ext4_fill_super failed: %d\n", ret);
goto err_free_ctx;
}
/* Store super_block for later operations */
ext4l_sb = sb;
return 0;
err_free_buf:
@@ -219,6 +232,7 @@ err_exit_es:
void ext4l_close(void)
{
ext4l_clear_blk_dev();
ext4l_dev_desc = NULL;
ext4l_sb = NULL;
ext4l_clear_blk_dev();
}

View File

@@ -561,9 +561,30 @@ void generic_set_sb_d_ops(struct super_block *sb)
{
}
/**
* d_make_root() - Create a root dentry for an inode
* @inode: Inode to create dentry for
* Return: Allocated dentry or NULL on failure
*/
struct dentry *d_make_root(struct inode *inode)
{
return NULL;
struct dentry *de;
if (!inode)
return NULL;
de = kzalloc(sizeof(struct dentry), GFP_KERNEL);
if (!de) {
iput(inode);
return NULL;
}
de->d_inode = inode;
de->d_sb = inode->i_sb;
de->d_name.name = "/";
de->d_name.len = 1;
return de;
}
/* percpu init rwsem */

View File

@@ -19,6 +19,13 @@
#include "ext4_uboot.h"
#include "ext4.h"
/*
* Global task_struct for U-Boot.
* This must be a single global instance shared across all translation units,
* so that journal_info remains consistent.
*/
struct task_struct ext4l_current_task = { .comm = "u-boot", .pid = 1 };
/*
* CRC32C support - uses Castagnoli polynomial 0x82F63B78
* Table is initialised on first mount

View File

@@ -165,6 +165,28 @@ typedef s64 ktime_t;
typedef u64 sector_t;
typedef u64 blkcnt_t;
/* Atomic types - stubs for single-threaded U-Boot */
typedef struct {
int counter;
} atomic_t;
#ifdef CONFIG_64BIT
typedef struct {
s64 counter;
} atomic64_t;
#else
typedef struct {
long counter;
} atomic64_t;
#endif
#define atomic_read(v) ((v)->counter)
#define atomic_set(v, i) ((v)->counter = (i))
#define atomic_inc(v) ((v)->counter++)
#define atomic_dec(v) ((v)->counter--)
#define atomic64_read(v) ((v)->counter)
#define atomic64_set(v, i) ((v)->counter = (i))
#ifdef __linux__
struct ustat {
__kernel_daddr_t f_tfree;