@@ -253,28 +253,17 @@ void ext4_unregister_li_request(struct super_block *sb);
/* ext4l support functions (support.c) */
int bh_cache_sync(void);
-int ext4l_read_block(sector_t block, size_t size, void *buffer);
-int ext4l_write_block(sector_t block, size_t size, void *buffer);
+int ext4l_read_block(struct block_device *bdev, sector_t block, size_t size,
+ void *buffer);
+int ext4l_write_block(struct block_device *bdev, sector_t block, size_t size,
+ void *buffer);
struct membuf *ext4l_get_msg_buf(void);
-void bh_cache_clear(void);
-void bh_cache_release_jbd(void);
+void bh_cache_clear(struct block_device *bdev);
+void bh_cache_release_jbd(struct block_device *bdev);
void ext4l_crc32c_init(void);
void ext4l_msg_init(void);
void ext4l_print_msgs(void);
void ext4l_record_msg(const char *msg, int len);
-/**
- * ext4l_get_blk() - Get the current block device
- *
- * Return: Block udevice, or NULL if not mounted
- */
-struct udevice *ext4l_get_blk(void);
-
-/**
- * ext4l_get_partition() - Get the current partition info
- *
- * Return: Partition info pointer, or NULL if not mounted
- */
-struct disk_partition *ext4l_get_partition(void);
#endif /* __EXT4_UBOOT_H__ */
@@ -233,9 +233,8 @@ static ssize_t ext4l_read_iter(struct udevice *dev, struct iov_iter *iter,
loff_t actual;
int ret;
- ret = ext4l_read(&fspriv->state, priv->path,
- iter_iov_ptr(iter), pos, iter_iov_avail(iter),
- &actual);
+ ret = ext4l_read(&fspriv->state, priv->path, iter_iov_ptr(iter), pos,
+ iter_iov_avail(iter), &actual);
if (ret)
return log_msg_ret("efr", ret);
iter_advance(iter, actual);
@@ -31,29 +31,6 @@
/* Global state for the legacy filesystem interface */
static struct ext4l_state efs;
-/**
- * ext4l_get_blk() - Get the current block device
- *
- * Return: Block device descriptor or NULL if not mounted
- */
-struct udevice *ext4l_get_blk(void)
-{
- if (!efs.mounted)
- return NULL;
-
- return efs.blk;
-}
-
-/**
- * ext4l_get_partition() - Get the current partition info
- *
- * Return: Partition info pointer
- */
-struct disk_partition *ext4l_get_partition(void)
-{
- return &efs.partition;
-}
-
/**
* ext4l_get_uuid() - Get the filesystem UUID
*
@@ -110,34 +87,6 @@ int ext4l_statfs(struct ext4l_state *state, struct fs_statfs *stats)
return 0;
}
-/**
- * ext4l_set_blk() - Set the block device for ext4l operations
- *
- * @blk: Block device descriptor
- * @partition: Partition info (can be NULL for whole disk)
- */
-void ext4l_set_blk(struct udevice *blk, struct disk_partition *partition)
-{
- efs.blk = blk;
- if (partition)
- memcpy(&efs.partition, partition, sizeof(efs.partition));
- else
- memset(&efs.partition, 0, sizeof(efs.partition));
- efs.mounted = true;
-}
-
-/**
- * ext4l_clear_blk() - Clear block device (unmount)
- */
-void ext4l_clear_blk(void)
-{
- /* Clear buffer cache before unmounting */
- bh_cache_clear();
-
- efs.blk = NULL;
- efs.mounted = false;
-}
-
/**
* ext4l_free_sb() - Free superblock and associated resources
* @sb: Superblock to free
@@ -209,6 +158,10 @@ static void ext4l_free_sb(struct super_block *sb, bool skip_io)
kfree(sbi->s_blockgroup_lock);
kfree(sbi);
+ /* Clear cached buffers for this device before freeing it */
+ bh_cache_release_jbd(sb->s_bdev);
+ bh_cache_clear(sb->s_bdev);
+
/* Free structures allocated in ext4l_mount() */
kfree(sb->s_bdev->bd_mapping);
kfree(sb->s_bdev);
@@ -235,17 +188,8 @@ static void ext4l_umount_internal(struct ext4l_state *state, bool skip_io)
ext4l_free_sb(sb, skip_io);
state->sb = NULL;
-
- /*
- * Force cleanup of any remaining journal_heads before clearing
- * the buffer cache. This ensures no stale journal_head references
- * survive to the next mount. This is critical even when skip_io
- * is true - we MUST disconnect journal_heads before freeing
- * buffer_heads to avoid dangling pointers.
- */
- bh_cache_release_jbd();
-
- ext4l_clear_blk();
+ state->blk = NULL;
+ state->mounted = false;
/*
* Clean up ext4 and JBD2 global state so it can be properly
@@ -269,7 +213,7 @@ static void ext4l_umount_internal(struct ext4l_state *state, bool skip_io)
int ext4l_mount(struct ext4l_state *state, struct udevice *dev,
struct disk_partition *fs_partition)
{
- struct blk_desc *fs_dev_desc = dev_get_uclass_plat(dev);
+ struct blk_desc *desc = dev_get_uclass_plat(dev);
struct ext4_fs_context *ctx;
struct super_block *sb;
struct fs_context *fc;
@@ -336,10 +280,12 @@ int ext4l_mount(struct ext4l_state *state, struct udevice *dev,
/* Initialise super_block fields */
sb->s_bdev->bd_super = sb;
+ sb->s_bdev->bd_blk = dev;
+ sb->s_bdev->bd_part_start = fs_partition ? fs_partition->start : 0;
sb->s_blocksize = 1024;
sb->s_blocksize_bits = 10;
snprintf(sb->s_id, sizeof(sb->s_id), "ext4l_mmc%d",
- fs_dev_desc->devnum);
+ desc->devnum);
sb->s_flags = 0;
sb->s_fs_info = NULL;
@@ -369,18 +315,18 @@ int ext4l_mount(struct ext4l_state *state, struct udevice *dev,
}
/* Calculate partition offset in bytes */
- part_offset = fs_partition ? (loff_t)fs_partition->start * fs_dev_desc->blksz : 0;
+ part_offset = fs_partition ?
+ (loff_t)fs_partition->start * desc->blksz : 0;
/* Read sectors containing the superblock */
- if (blk_dread(fs_dev_desc,
- div_u64(part_offset + BLOCK_SIZE, fs_dev_desc->blksz),
- 2, buf) != 2) {
+ if (blk_read(dev, div_u64(part_offset + BLOCK_SIZE, desc->blksz),
+ 2, buf) != 2) {
ret = -EIO;
goto err_free_buf;
}
/* Check magic number within superblock */
- magic = (__le16 *)(buf + (BLOCK_SIZE % fs_dev_desc->blksz) +
+ magic = (__le16 *)(buf + (BLOCK_SIZE % desc->blksz) +
offsetof(struct ext4_super_block, s_magic));
if (le16_to_cpu(*magic) != EXT4_SUPER_MAGIC) {
ret = -EINVAL;
@@ -388,15 +334,20 @@ int ext4l_mount(struct ext4l_state *state, struct udevice *dev,
}
/* Set block device for buffer I/O */
- ext4l_set_blk(dev, fs_partition);
+ state->blk = dev;
+ if (fs_partition)
+ memcpy(&state->partition, fs_partition,
+ sizeof(state->partition));
+ else
+ memset(&state->partition, 0, sizeof(state->partition));
+ state->mounted = true;
/*
* Test if device supports writes by writing back the same data.
* If write returns 0, the device is read-only (e.g. LUKS/blkmap_crypt)
*/
- if (blk_dwrite(fs_dev_desc,
- div_u64(part_offset + BLOCK_SIZE, fs_dev_desc->blksz),
- 2, buf) != 2) {
+ if (blk_write(dev, div_u64(part_offset + BLOCK_SIZE, desc->blksz),
+ 2, buf) != 2) {
sb->s_bdev->read_only = true;
sb->s_flags |= SB_RDONLY;
}
@@ -706,6 +657,12 @@ static int ext4l_resolve_path(struct ext4l_state *state, const char *path,
return ext4l_resolve_path_internal(state, path, inodep, 0);
}
+/* Context for ext4l_dir_actor carrying the superblock */
+struct ext4l_ls_ctx {
+ struct dir_context ctx;
+ struct super_block *sb;
+};
+
/**
* ext4l_dir_actor() - Directory entry callback for ext4_readdir
*
@@ -721,6 +678,7 @@ static int ext4l_dir_actor(struct dir_context *ctx, const char *name,
int namelen, loff_t offset, u64 ino,
unsigned int d_type)
{
+ struct ext4l_ls_ctx *ls_ctx = container_of(ctx, struct ext4l_ls_ctx, ctx);
struct inode *inode;
char namebuf[256];
@@ -731,7 +689,7 @@ static int ext4l_dir_actor(struct dir_context *ctx, const char *name,
namebuf[namelen] = '\0';
/* Look up the inode to get file size */
- inode = ext4_iget(efs.sb, ino, 0);
+ inode = ext4_iget(ls_ctx->sb, ino, 0);
if (IS_ERR(inode)) {
printf(" %8s %s\n", "?", namebuf);
return 0;
@@ -749,9 +707,9 @@ static int ext4l_dir_actor(struct dir_context *ctx, const char *name,
int ext4l_ls(struct ext4l_state *state, const char *dirname)
{
+ struct ext4l_ls_ctx ls_ctx;
struct inode *dir;
struct file file;
- struct dir_context ctx;
int ret;
ret = ext4l_resolve_path(state, dirname, &dir);
@@ -770,10 +728,11 @@ int ext4l_ls(struct ext4l_state *state, const char *dirname)
if (!file.private_data)
return -ENOMEM;
- memset(&ctx, 0, sizeof(ctx));
- ctx.actor = ext4l_dir_actor;
+ memset(&ls_ctx, 0, sizeof(ls_ctx));
+ ls_ctx.ctx.actor = ext4l_dir_actor;
+ ls_ctx.sb = state->sb;
- ret = ext4_readdir(&file, &ctx);
+ ret = ext4_readdir(&file, &ls_ctx.ctx);
if (file.private_data)
ext4_htree_free_dir_info(file.private_data);
@@ -1435,6 +1394,7 @@ struct ext4l_dir {
struct ext4l_readdir_ctx {
struct dir_context ctx;
struct ext4l_dir *dir;
+ struct super_block *sb;
};
/**
@@ -1487,7 +1447,7 @@ static int ext4l_opendir_actor(struct dir_context *ctx, const char *name,
}
/* Look up inode to get size and other attributes */
- inode = ext4_iget(efs.sb, ino, 0);
+ inode = ext4_iget(rctx->sb, ino, 0);
if (!IS_ERR(inode)) {
dent->size = inode->i_size;
/* Refine type from inode mode if needed */
@@ -1575,6 +1535,7 @@ int ext4l_readdir(struct ext4l_state *state, struct fs_dir_stream *dirs,
/* Set up extended dir_context for this iteration */
memset(&ctx, '\0', sizeof(ctx));
ctx.ctx.actor = ext4l_opendir_actor;
+ ctx.sb = state->sb;
ctx.ctx.pos = dir->file.f_pos;
ctx.dir = dir;
@@ -231,17 +231,20 @@ static inline unsigned int bh_cache_hash(sector_t block)
/**
* bh_cache_lookup() - Look up a buffer in the cache
+ * @bdev: Block device to match
* @block: Block number to look up
* @size: Expected block size
- * Return: Buffer head if found with matching size, NULL otherwise
+ * Return: Buffer head if found with matching device and size, NULL otherwise
*/
-static struct buffer_head *bh_cache_lookup(sector_t block, size_t size)
+static struct buffer_head *bh_cache_lookup(struct block_device *bdev,
+ sector_t block, size_t size)
{
unsigned int hash = bh_cache_hash(block);
struct bh_cache_entry *entry;
for (entry = bh_cache[hash]; entry; entry = entry->next) {
- if (entry->bh && entry->bh->b_blocknr == block &&
+ if (entry->bh && entry->bh->b_bdev == bdev &&
+ entry->bh->b_blocknr == block &&
entry->bh->b_size == size) {
atomic_inc(&entry->bh->b_count);
return entry->bh;
@@ -259,9 +262,10 @@ static void bh_cache_insert(struct buffer_head *bh)
unsigned int hash = bh_cache_hash(bh->b_blocknr);
struct bh_cache_entry *entry;
- /* Check if already in cache - must match block AND size */
+ /* Check if already in cache - must match device, block AND size */
for (entry = bh_cache[hash]; entry; entry = entry->next) {
- if (entry->bh && entry->bh->b_blocknr == bh->b_blocknr &&
+ if (entry->bh && entry->bh->b_bdev == bh->b_bdev &&
+ entry->bh->b_blocknr == bh->b_blocknr &&
entry->bh->b_size == bh->b_size)
return; /* Already cached */
}
@@ -309,30 +313,28 @@ static void bh_clear_stale_jbd(struct buffer_head *bh)
}
}
-void bh_cache_clear(void)
+void bh_cache_clear(struct block_device *bdev)
{
int i;
- struct bh_cache_entry *entry, *next;
+ struct bh_cache_entry *entry, *next, **prev;
for (i = 0; i < BH_CACHE_SIZE; i++) {
- for (entry = bh_cache[i]; entry; entry = next) {
+ prev = &bh_cache[i];
+ for (entry = *prev; entry; entry = next) {
next = entry->next;
- if (entry->bh) {
+ if (entry->bh && entry->bh->b_bdev == bdev) {
struct buffer_head *bh = entry->bh;
bh_clear_stale_jbd(bh);
- /*
- * Force count to 1 so the buffer will be freed.
- * On unmount, ext4 code won't access these
- * buffers again, so extra references are stale.
- */
atomic_set(&bh->b_count, 1);
if (atomic_dec_and_test(&bh->b_count))
free_buffer_head(bh);
+ *prev = next;
+ free(entry);
+ } else {
+ prev = &entry->next;
}
- free(entry);
}
- bh_cache[i] = NULL;
}
}
@@ -343,14 +345,15 @@ void bh_cache_clear(void)
* It ensures all journal_heads are properly released from buffer_heads
* even if the journal destroy didn't fully clean up (e.g., on abort).
*/
-void bh_cache_release_jbd(void)
+void bh_cache_release_jbd(struct block_device *bdev)
{
int i;
struct bh_cache_entry *entry;
for (i = 0; i < BH_CACHE_SIZE; i++) {
for (entry = bh_cache[i]; entry; entry = entry->next) {
- if (entry->bh && buffer_jbd(entry->bh)) {
+ if (entry->bh && entry->bh->b_bdev == bdev &&
+ buffer_jbd(entry->bh)) {
struct buffer_head *bh = entry->bh;
struct journal_head *jh = bh2jh(bh);
@@ -388,7 +391,8 @@ int bh_cache_sync(void)
for (i = 0; i < BH_CACHE_SIZE; i++) {
for (entry = bh_cache[i]; entry; entry = entry->next) {
if (entry->bh && buffer_dirty(entry->bh)) {
- int err = ext4l_write_block(entry->bh->b_blocknr,
+ int err = ext4l_write_block(entry->bh->b_bdev,
+ entry->bh->b_blocknr,
entry->bh->b_size,
entry->bh->b_data);
if (err && !ret)
@@ -499,29 +503,26 @@ void free_buffer_head(struct buffer_head *bh)
* @buffer: Destination buffer
* Return: 0 on success, negative on error
*/
-int ext4l_read_block(sector_t block, size_t size, void *buffer)
+int ext4l_read_block(struct block_device *bdev, sector_t block, size_t size,
+ void *buffer)
{
- struct disk_partition *part;
- lbaint_t sector, count;
struct blk_desc *desc;
- struct udevice *blk;
- ulong n;
+ lbaint_t sector, count;
+ long n;
- blk = ext4l_get_blk();
- part = ext4l_get_partition();
- if (!blk)
+ if (!bdev || !bdev->bd_blk)
return -EIO;
- desc = dev_get_uclass_plat(blk);
+ desc = dev_get_uclass_plat(bdev->bd_blk);
/* Convert block to sector */
- sector = (block * size) / desc->blksz + part->start;
+ sector = (block * size) / desc->blksz + bdev->bd_part_start;
count = size / desc->blksz;
if (count == 0)
count = 1;
- n = blk_read(blk, sector, count, buffer);
+ n = blk_read(bdev->bd_blk, sector, count, buffer);
if (n != count)
return -EIO;
@@ -535,29 +536,26 @@ int ext4l_read_block(sector_t block, size_t size, void *buffer)
* @buffer: Source buffer
* Return: 0 on success, negative on error
*/
-int ext4l_write_block(sector_t block, size_t size, void *buffer)
+int ext4l_write_block(struct block_device *bdev, sector_t block, size_t size,
+ void *buffer)
{
- struct disk_partition *part;
- lbaint_t sector, count;
struct blk_desc *desc;
- struct udevice *blk;
- ulong n;
+ lbaint_t sector, count;
+ long n;
- blk = ext4l_get_blk();
- part = ext4l_get_partition();
- if (!blk)
+ if (!bdev || !bdev->bd_blk)
return -EIO;
- desc = dev_get_uclass_plat(blk);
+ desc = dev_get_uclass_plat(bdev->bd_blk);
/* Convert block to sector */
- sector = (block * size) / desc->blksz + part->start;
+ sector = (block * size) / desc->blksz + bdev->bd_part_start;
count = size / desc->blksz;
if (count == 0)
count = 1;
- n = blk_write(blk, sector, count, buffer);
+ n = blk_write(bdev->bd_blk, sector, count, buffer);
if (n != count)
return -EIO;
@@ -578,7 +576,7 @@ struct buffer_head *sb_getblk(struct super_block *sb, sector_t block)
return NULL;
/* Check cache first - must match block number AND size */
- bh = bh_cache_lookup(block, sb->s_blocksize);
+ bh = bh_cache_lookup(sb->s_bdev, block, sb->s_blocksize);
if (bh)
return bh;
@@ -622,7 +620,7 @@ struct buffer_head *__getblk(struct block_device *bdev, sector_t block,
return NULL;
/* Check cache first - must match block number AND size */
- bh = bh_cache_lookup(block, size);
+ bh = bh_cache_lookup(bdev, block, size);
if (bh)
return bh;
@@ -673,7 +671,7 @@ struct buffer_head *sb_bread(struct super_block *sb, sector_t block)
bh->b_bdev = sb->s_bdev;
bh->b_size = sb->s_blocksize;
- ret = ext4l_read_block(block, sb->s_blocksize, bh->b_data);
+ ret = ext4l_read_block(sb->s_bdev, block, sb->s_blocksize, bh->b_data);
if (ret) {
brelse(bh);
return NULL;
@@ -737,7 +735,7 @@ struct buffer_head *bdev_getblk(struct block_device *bdev, sector_t block,
struct buffer_head *bh;
/* Check cache first - must match block number AND size */
- bh = bh_cache_lookup(block, size);
+ bh = bh_cache_lookup(bdev, block, size);
if (bh)
return bh;
@@ -782,7 +780,7 @@ struct buffer_head *__bread(struct block_device *bdev, sector_t block,
bh->b_bdev = bdev;
bh->b_size = size;
- ret = ext4l_read_block(block, size, bh->b_data);
+ ret = ext4l_read_block(bdev, block, size, bh->b_data);
if (ret) {
free_buffer_head(bh);
return NULL;
@@ -824,7 +822,8 @@ int submit_bh(int op, struct buffer_head *bh)
int uptodate;
if (op_type == REQ_OP_READ) {
- ret = ext4l_read_block(bh->b_blocknr, bh->b_size, bh->b_data);
+ ret = ext4l_read_block(bh->b_bdev, bh->b_blocknr, bh->b_size,
+ bh->b_data);
if (ret) {
clear_buffer_uptodate(bh);
uptodate = 0;
@@ -833,7 +832,8 @@ int submit_bh(int op, struct buffer_head *bh)
uptodate = 1;
}
} else if (op_type == REQ_OP_WRITE) {
- ret = ext4l_write_block(bh->b_blocknr, bh->b_size, bh->b_data);
+ ret = ext4l_write_block(bh->b_bdev, bh->b_blocknr, bh->b_size,
+ bh->b_data);
if (ret) {
clear_buffer_uptodate(bh);
set_buffer_write_io_error(bh);
@@ -273,13 +273,17 @@ static inline void inode_state_assign(struct inode *inode, unsigned long flags)
inode->i_state = flags;
}
-/* block_device - minimal stub */
+struct udevice;
+
+/* block_device - minimal stub with U-Boot block I/O fields */
struct block_device {
struct address_space *bd_mapping;
void *bd_disk;
struct super_block *bd_super;
dev_t bd_dev;
bool read_only;
+ struct udevice *bd_blk; /* U-Boot block device */
+ unsigned long bd_part_start; /* partition start (in device sectors) */
};
/* errseq functions - stubs */