ext4: enable scoped NOFS when starting a handle in nojournal mode

The jbd2 layer enables NOFS mode using memalloc_nofs_{save,restore}()
while a handle is active.  We need to do the same in nojournal mode so
that it is safe to remove GFP_NOFS flags while a jbd2 handle is
active.

This will require that we actually allocate a real handle, but with an
h_invalid flag set, so there is a place to put the saved memalloc
context.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Reviewed-by: Andreas Dilger <adilger@dilger.ca>
This commit is contained in:
Theodore Ts'o
2026-07-22 23:47:06 -04:00
parent d19d239ada
commit 61e2a90a91
4 changed files with 24 additions and 18 deletions
+21 -13
View File
@@ -33,14 +33,22 @@ int ext4_inode_journal_mode(struct inode *inode)
static handle_t *ext4_get_nojournal(void)
{
handle_t *handle = current->journal_info;
unsigned long ref_cnt = (unsigned long)handle;
BUG_ON(ref_cnt >= EXT4_NOJOURNAL_MAX_REF_COUNT);
BUG_ON(handle && !handle->h_invalid);
ref_cnt++;
handle = (handle_t *)ref_cnt;
current->journal_info = handle;
if (!handle) {
handle = jbd2_alloc_handle(GFP_NOFS);
if (!handle)
return ERR_PTR(-ENOMEM);
handle->h_invalid = 1;
/*
* This is done by start_this_handle() if journalling
* is enabled.
*/
handle->saved_alloc_context = memalloc_nofs_save();
current->journal_info = handle;
}
handle->h_ref++;
return handle;
}
@@ -48,14 +56,14 @@ static handle_t *ext4_get_nojournal(void)
/* Decrement the non-pointer handle value */
static void ext4_put_nojournal(handle_t *handle)
{
unsigned long ref_cnt = (unsigned long)handle;
BUG_ON(handle->h_ref == 0);
BUG_ON(ref_cnt == 0);
ref_cnt--;
handle = (handle_t *)ref_cnt;
current->journal_info = handle;
handle->h_ref--;
if (handle->h_ref == 0) {
memalloc_nofs_restore(handle->saved_alloc_context);
jbd2_free_handle(handle);
current->journal_info = NULL;
}
}
/*
+1 -5
View File
@@ -182,15 +182,11 @@ handle_t *__ext4_journal_start_sb(struct inode *inode, struct super_block *sb,
int rsv_blocks, int revoke_creds);
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);
#define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096)
/* Note: Do not use this for NULL handles. This is only to determine if
* a properly allocated handle is using a journal or not. */
static inline int ext4_handle_valid(handle_t *handle)
{
if ((unsigned long)handle < EXT4_NOJOURNAL_MAX_REF_COUNT)
return 0;
return 1;
return (handle && !handle->h_invalid);
}
static inline void ext4_handle_sync(handle_t *handle)
+1
View File
@@ -94,6 +94,7 @@ EXPORT_SYMBOL(jbd2_journal_init_jbd_inode);
EXPORT_SYMBOL(jbd2_journal_release_jbd_inode);
EXPORT_SYMBOL(jbd2_journal_begin_ordered_truncate);
EXPORT_SYMBOL(jbd2_inode_cache);
EXPORT_SYMBOL(jbd2_handle_cache);
#ifdef CONFIG_JBD2_DEBUG
void __jbd2_debug(int level, const char *file, const char *func,
+1
View File
@@ -513,6 +513,7 @@ struct jbd2_journal_handle
unsigned int h_sync: 1;
unsigned int h_reserved: 1;
unsigned int h_aborted: 1;
unsigned int h_invalid: 1;
unsigned int h_type: 8;
unsigned int h_line_no: 16;