[Concept,15/26] ext4l: Add bh_cache_release_jbd() to clean up journal references
Commit Message
From: Simon Glass <simon.glass@canonical.com>
Add bh_cache_release_jbd() to forcibly release any journal_heads still
attached to buffer_heads after journal destroy. This must be called
after journal destroy but before bh_cache_clear() to ensure all
journal_heads are properly released, even if journal destroy did not
fully clean up (e.g., on abort).
The function clears b_bh in each journal_head to prevent use-after-free
when the buffer_head is later freed, and resets transaction pointers.
Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---
fs/ext4l/ext4_uboot.h | 1 +
fs/ext4l/support.c | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+)
@@ -2891,6 +2891,7 @@ void free_buffer_head(struct buffer_head *bh);
/* ext4l support functions (support.c) */
void ext4l_crc32c_init(void);
+void bh_cache_release_jbd(void);
void bh_cache_clear(void);
int bh_cache_sync(void);
int ext4l_read_block(sector_t block, size_t size, void *buffer);
@@ -332,6 +332,42 @@ void bh_cache_clear(void)
}
}
+/**
+ * bh_cache_release_jbd() - Release all JBD references from buffer cache
+ *
+ * This must be called after journal destroy but before bh_cache_clear().
+ * 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)
+{
+ 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)) {
+ struct buffer_head *bh = entry->bh;
+ struct journal_head *jh = bh2jh(bh);
+
+ /*
+ * Forcibly release the journal_head.
+ * Clear b_bh to prevent use-after-free when
+ * the buffer_head is later freed.
+ */
+ if (jh) {
+ jh->b_bh = NULL;
+ jh->b_transaction = NULL;
+ jh->b_next_transaction = NULL;
+ jh->b_cp_transaction = NULL;
+ }
+ clear_buffer_jbd(bh);
+ bh->b_private = NULL;
+ }
+ }
+ }
+}
+
/**
* bh_cache_sync() - Sync all dirty buffers to disk
*