[PATCH 1/2] ext4: enable scoped NOFS when starting a handle in nojournal mode
"Theodore Ts'o" <[email protected]>
| Newsgroups | org.kernel.vger.linux-ext4 |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]>
---
fs/ext4/ext4_jbd2.c | 36 ++++++++++++++++++++++--------------
fs/ext4/ext4_jbd2.h | 6 +-----
fs/jbd2/journal.c | 1 +
include/linux/jbd2.h | 1 +
4 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 9a8c225f2753..b4dacd1a89e7 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -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);
-
- ref_cnt++;
- handle = (handle_t *)ref_cnt;
-
- current->journal_info = handle;
+ BUG_ON(handle && !handle->h_invalid);
+
+ 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;
+ }
}
/*
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index 63d17c5201b5..2fbf48b3dfe2 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -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)
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index 4fdf089500f6..f0eab6151f48 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -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,
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
index b68561187e90..7348fdadc810 100644
--- a/include/linux/jbd2.h
+++ b/include/linux/jbd2.h
@@ -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;
--
2.53.0