linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code
@ 2024-01-23 15:24 Jan Kara
  2024-01-23 15:25 ` [PATCH 1/9] udf: Remove GFP_NOFS from dir iteration code Jan Kara
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:24 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

Hello,

inspired by recent Matthew's complaint about frequent use of GFP_NOFS in
filesystems I've audited GFP_NOFS use in ext2, udf, and quota code and removed
the uses that are no longer needed or can be reasonably easily replaced with
the scope API. So far lockdep didn't barf on me during fstests runs but I guess
we'll really know only once syzbot seriously stresses this in Linux next.

Unless somebody objects, I'll queue these patches into my tree.

								Honza

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/9] udf: Remove GFP_NOFS from dir iteration code
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  2024-01-23 15:25 ` [PATCH 2/9] udf: Avoid GFP_NOFS allocation in udf_symlink() Jan Kara
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

Directory iteration code was using GFP_NOFS allocations in two places.
However the code is called only under inode->i_rwsem which is generally
safe wrt reclaim. So we can do the allocations with GFP_KERNEL instead.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/udf/dir.c   | 2 +-
 fs/udf/namei.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/fs/udf/dir.c b/fs/udf/dir.c
index f6533f93851b..f94f45fe2c91 100644
--- a/fs/udf/dir.c
+++ b/fs/udf/dir.c
@@ -67,7 +67,7 @@ static int udf_readdir(struct file *file, struct dir_context *ctx)
 		pos_valid = true;
 	}
 
-	fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
+	fname = kmalloc(UDF_NAME_LEN, GFP_KERNEL);
 	if (!fname) {
 		ret = -ENOMEM;
 		goto out;
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 1bb6ed948927..1f14a0621a91 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -59,7 +59,7 @@ static int udf_fiiter_find_entry(struct inode *dir, const struct qstr *child,
 		child->name[0] == '.' && child->name[1] == '.';
 	int ret;
 
-	fname = kmalloc(UDF_NAME_LEN, GFP_NOFS);
+	fname = kmalloc(UDF_NAME_LEN, GFP_KERNEL);
 	if (!fname)
 		return -ENOMEM;
 
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/9] udf: Avoid GFP_NOFS allocation in udf_symlink()
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
  2024-01-23 15:25 ` [PATCH 1/9] udf: Remove GFP_NOFS from dir iteration code Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  2024-01-23 15:25 ` [PATCH 3/9] udf: Avoid GFP_NOFS allocation in udf_load_pvoldesc() Jan Kara
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

The GFP_NOFS allocation in udf_symlink() is called only under
inode->i_rwsem and UDF_I(inode)->i_data_sem. The first is safe wrt
reclaim, the second should be as well but allocating unde this lock is
actually unnecessary. Move the allocation from under i_data_sem and
change it to GFP_KERNEL.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/udf/namei.c | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 1f14a0621a91..1308109fd42d 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -566,7 +566,7 @@ static int udf_unlink(struct inode *dir, struct dentry *dentry)
 static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir,
 		       struct dentry *dentry, const char *symname)
 {
-	struct inode *inode = udf_new_inode(dir, S_IFLNK | 0777);
+	struct inode *inode;
 	struct pathComponent *pc;
 	const char *compstart;
 	struct extent_position epos = {};
@@ -579,17 +579,20 @@ static int udf_symlink(struct mnt_idmap *idmap, struct inode *dir,
 	struct udf_inode_info *iinfo;
 	struct super_block *sb = dir->i_sb;
 
-	if (IS_ERR(inode))
-		return PTR_ERR(inode);
-
-	iinfo = UDF_I(inode);
-	down_write(&iinfo->i_data_sem);
-	name = kmalloc(UDF_NAME_LEN_CS0, GFP_NOFS);
+	name = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL);
 	if (!name) {
 		err = -ENOMEM;
-		goto out_no_entry;
+		goto out;
+	}
+
+	inode = udf_new_inode(dir, S_IFLNK | 0777);
+	if (IS_ERR(inode)) {
+		err = PTR_ERR(inode);
+		goto out;
 	}
 
+	iinfo = UDF_I(inode);
+	down_write(&iinfo->i_data_sem);
 	inode->i_data.a_ops = &udf_symlink_aops;
 	inode->i_op = &udf_symlink_inode_operations;
 	inode_nohighmem(inode);
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/9] udf: Avoid GFP_NOFS allocation in udf_load_pvoldesc()
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
  2024-01-23 15:25 ` [PATCH 1/9] udf: Remove GFP_NOFS from dir iteration code Jan Kara
  2024-01-23 15:25 ` [PATCH 2/9] udf: Avoid GFP_NOFS allocation in udf_symlink() Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  2024-01-23 15:25 ` [PATCH 4/9] udf: Remove GFP_NOFS allocation in udf_expand_file_adinicb() Jan Kara
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

udf_load_pvoldesc() is called only during mount when it is safe to
enter fs reclaim (we hold only s_umount semaphore). Change GFP_NOFS to
GFP_KERNEL allocation.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/udf/super.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/udf/super.c b/fs/udf/super.c
index 928a04d9d9e0..0a15ea436fc2 100644
--- a/fs/udf/super.c
+++ b/fs/udf/super.c
@@ -864,7 +864,7 @@ static int udf_load_pvoldesc(struct super_block *sb, sector_t block)
 	int ret;
 	struct timestamp *ts;
 
-	outstr = kmalloc(128, GFP_NOFS);
+	outstr = kmalloc(128, GFP_KERNEL);
 	if (!outstr)
 		return -ENOMEM;
 
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/9] udf: Remove GFP_NOFS allocation in udf_expand_file_adinicb()
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
                   ` (2 preceding siblings ...)
  2024-01-23 15:25 ` [PATCH 3/9] udf: Avoid GFP_NOFS allocation in udf_load_pvoldesc() Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  2024-01-23 15:25 ` [PATCH 5/9] ext2: Drop GFP_NOFS allocation from ext2_init_block_alloc_info() Jan Kara
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

udf_expand_file_adinicb() is called under inode->i_rwsem and
mapping->invalidate_lock. i_rwsem is safe wrt fs reclaim,
invalidate_lock on this inode is safe as well (we hold inode reference
so reclaim will not touch it, furthermore even lockdep should not
complain as invalidate_lock is acquired from udf_evict_inode() only when
truncating inode which should not happen from fs reclaim).

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/udf/inode.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index d8493449d4c5..2f831a3a91af 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -357,7 +357,7 @@ int udf_expand_file_adinicb(struct inode *inode)
 		return 0;
 	}
 
-	page = find_or_create_page(inode->i_mapping, 0, GFP_NOFS);
+	page = find_or_create_page(inode->i_mapping, 0, GFP_KERNEL);
 	if (!page)
 		return -ENOMEM;
 
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/9] ext2: Drop GFP_NOFS allocation from ext2_init_block_alloc_info()
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
                   ` (3 preceding siblings ...)
  2024-01-23 15:25 ` [PATCH 4/9] udf: Remove GFP_NOFS allocation in udf_expand_file_adinicb() Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  2024-01-23 15:25 ` [PATCH 6/9] ext2: Drop GFP_NOFS use in ext2_get_blocks() Jan Kara
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

The allocation happens under inode->i_rwsem and
EXT2_I(inode)->i_truncate_mutex. Neither of them is acquired during
direct fs reclaim so the allocation can be changed to GFP_KERNEL.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ext2/balloc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index e124f3d709b2..1bfd6ab11038 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -412,7 +412,7 @@ void ext2_init_block_alloc_info(struct inode *inode)
 	struct ext2_block_alloc_info *block_i;
 	struct super_block *sb = inode->i_sb;
 
-	block_i = kmalloc(sizeof(*block_i), GFP_NOFS);
+	block_i = kmalloc(sizeof(*block_i), GFP_KERNEL);
 	if (block_i) {
 		struct ext2_reserve_window_node *rsv = &block_i->rsv_window_node;
 
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/9] ext2: Drop GFP_NOFS use in ext2_get_blocks()
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
                   ` (4 preceding siblings ...)
  2024-01-23 15:25 ` [PATCH 5/9] ext2: Drop GFP_NOFS allocation from ext2_init_block_alloc_info() Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  2024-01-23 15:25 ` [PATCH 7/9] ext2: Remove GFP_NOFS use in ext2_xattr_cache_insert() Jan Kara
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

ext2_get_blocks() calls sb_issue_zeroout() with GFP_NOFS flag. However
the call is performed under inode->i_rwsem and
EXT2_I(inode)->i_truncate_mutex neither of which is acquired during
direct fs reclaim. So it is safe to change the gfp mask to GFP_KERNEL.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ext2/inode.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index 5a4272b2c6b0..f3d570a9302b 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -754,7 +754,7 @@ static int ext2_get_blocks(struct inode *inode,
 		 */
 		err = sb_issue_zeroout(inode->i_sb,
 				le32_to_cpu(chain[depth-1].key), count,
-				GFP_NOFS);
+				GFP_KERNEL);
 		if (err) {
 			mutex_unlock(&ei->truncate_mutex);
 			goto cleanup;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 7/9] ext2: Remove GFP_NOFS use in ext2_xattr_cache_insert()
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
                   ` (5 preceding siblings ...)
  2024-01-23 15:25 ` [PATCH 6/9] ext2: Drop GFP_NOFS use in ext2_get_blocks() Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  2024-01-23 15:25 ` [PATCH 8/9] quota: Set nofs allocation context when acquiring dqio_sem Jan Kara
  2024-01-23 15:25 ` [PATCH 9/9] quota: Drop GFP_NOFS instances under dquot->dq_lock and dqio_sem Jan Kara
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

ext2_xattr_cache_insert() calls mb_cache_entry_create() with GFP_NOFS
because it is called under EXT2_I(inode)->xattr_sem. However xattr_sem
or any higher ranking lock is not acquired on fs reclaim path for ext2
at least since we don't do page writeback from direct reclaim.  Thus
GFP_NOFS is not needed.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ext2/xattr.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c
index e849241ebb8f..c885dcc3bd0d 100644
--- a/fs/ext2/xattr.c
+++ b/fs/ext2/xattr.c
@@ -874,7 +874,7 @@ ext2_xattr_cache_insert(struct mb_cache *cache, struct buffer_head *bh)
 	__u32 hash = le32_to_cpu(HDR(bh)->h_hash);
 	int error;
 
-	error = mb_cache_entry_create(cache, GFP_NOFS, hash, bh->b_blocknr,
+	error = mb_cache_entry_create(cache, GFP_KERNEL, hash, bh->b_blocknr,
 				      true);
 	if (error) {
 		if (error == -EBUSY) {
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 8/9] quota: Set nofs allocation context when acquiring dqio_sem
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
                   ` (6 preceding siblings ...)
  2024-01-23 15:25 ` [PATCH 7/9] ext2: Remove GFP_NOFS use in ext2_xattr_cache_insert() Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  2024-01-23 15:25 ` [PATCH 9/9] quota: Drop GFP_NOFS instances under dquot->dq_lock and dqio_sem Jan Kara
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

dqio_sem can be acquired during inode reclaim through dquot_drop() ->
dqput() -> dquot_release() -> write_file_info() context. In some cases
(most notably through dquot_get_next_id()) it can be acquired without
holding dquot->dq_lock (which already sets nofs allocation context). So
we need to set nofs allocation context when acquiring it as well.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/ocfs2/quota_global.c | 12 ++++++++++++
 fs/ocfs2/quota_local.c  |  3 +++
 fs/quota/quota_v1.c     |  6 ++++++
 fs/quota/quota_v2.c     | 18 ++++++++++++++++++
 4 files changed, 39 insertions(+)

diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index dc9f76ab7e13..0575c2d060eb 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -447,14 +447,17 @@ int ocfs2_global_write_info(struct super_block *sb, int type)
 	int err;
 	struct quota_info *dqopt = sb_dqopt(sb);
 	struct ocfs2_mem_dqinfo *info = dqopt->info[type].dqi_priv;
+	unsigned int memalloc;
 
 	down_write(&dqopt->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	err = ocfs2_qinfo_lock(info, 1);
 	if (err < 0)
 		goto out_sem;
 	err = __ocfs2_global_write_info(sb, type);
 	ocfs2_qinfo_unlock(info, 1);
 out_sem:
+	memalloc_nofs_restore(memalloc);
 	up_write(&dqopt->dqio_sem);
 	return err;
 }
@@ -601,6 +604,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
 	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
 	struct ocfs2_super *osb = OCFS2_SB(sb);
 	int status = 0;
+	unsigned int memalloc;
 
 	trace_ocfs2_sync_dquot_helper(from_kqid(&init_user_ns, dquot->dq_id),
 				      dquot->dq_id.type,
@@ -618,6 +622,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
 		goto out_ilock;
 	}
 	down_write(&sb_dqopt(sb)->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	status = ocfs2_sync_dquot(dquot);
 	if (status < 0)
 		mlog_errno(status);
@@ -625,6 +630,7 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
 	status = ocfs2_local_write_dquot(dquot);
 	if (status < 0)
 		mlog_errno(status);
+	memalloc_nofs_restore(memalloc);
 	up_write(&sb_dqopt(sb)->dqio_sem);
 	ocfs2_commit_trans(osb, handle);
 out_ilock:
@@ -662,6 +668,7 @@ static int ocfs2_write_dquot(struct dquot *dquot)
 	handle_t *handle;
 	struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
 	int status = 0;
+	unsigned int memalloc;
 
 	trace_ocfs2_write_dquot(from_kqid(&init_user_ns, dquot->dq_id),
 				dquot->dq_id.type);
@@ -673,7 +680,9 @@ static int ocfs2_write_dquot(struct dquot *dquot)
 		goto out;
 	}
 	down_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	status = ocfs2_local_write_dquot(dquot);
+	memalloc_nofs_restore(memalloc);
 	up_write(&sb_dqopt(dquot->dq_sb)->dqio_sem);
 	ocfs2_commit_trans(osb, handle);
 out:
@@ -920,6 +929,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
 	struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
 	handle_t *handle;
 	struct ocfs2_super *osb = OCFS2_SB(sb);
+	unsigned int memalloc;
 
 	trace_ocfs2_mark_dquot_dirty(from_kqid(&init_user_ns, dquot->dq_id),
 				     type);
@@ -946,6 +956,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
 		goto out_ilock;
 	}
 	down_write(&sb_dqopt(sb)->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	status = ocfs2_sync_dquot(dquot);
 	if (status < 0) {
 		mlog_errno(status);
@@ -954,6 +965,7 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
 	/* Now write updated local dquot structure */
 	status = ocfs2_local_write_dquot(dquot);
 out_dlock:
+	memalloc_nofs_restore(memalloc);
 	up_write(&sb_dqopt(sb)->dqio_sem);
 	ocfs2_commit_trans(osb, handle);
 out_ilock:
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index e09842fc9d4d..8ce462c64c51 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -470,6 +470,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
 	int bit, chunk;
 	struct ocfs2_recovery_chunk *rchunk, *next;
 	qsize_t spacechange, inodechange;
+	unsigned int memalloc;
 
 	trace_ocfs2_recover_local_quota_file((unsigned long)lqinode->i_ino, type);
 
@@ -521,6 +522,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
 				goto out_drop_lock;
 			}
 			down_write(&sb_dqopt(sb)->dqio_sem);
+			memalloc = memalloc_nofs_save();
 			spin_lock(&dquot->dq_dqb_lock);
 			/* Add usage from quota entry into quota changes
 			 * of our node. Auxiliary variables are important
@@ -553,6 +555,7 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
 			unlock_buffer(qbh);
 			ocfs2_journal_dirty(handle, qbh);
 out_commit:
+			memalloc_nofs_restore(memalloc);
 			up_write(&sb_dqopt(sb)->dqio_sem);
 			ocfs2_commit_trans(OCFS2_SB(sb), handle);
 out_drop_lock:
diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c
index a0db3f195e95..3f3e8acc05db 100644
--- a/fs/quota/quota_v1.c
+++ b/fs/quota/quota_v1.c
@@ -160,9 +160,11 @@ static int v1_read_file_info(struct super_block *sb, int type)
 {
 	struct quota_info *dqopt = sb_dqopt(sb);
 	struct v1_disk_dqblk dqblk;
+	unsigned int memalloc;
 	int ret;
 
 	down_read(&dqopt->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
 				sizeof(struct v1_disk_dqblk), v1_dqoff(0));
 	if (ret != sizeof(struct v1_disk_dqblk)) {
@@ -179,6 +181,7 @@ static int v1_read_file_info(struct super_block *sb, int type)
 	dqopt->info[type].dqi_bgrace =
 			dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME;
 out:
+	memalloc_nofs_restore(memalloc);
 	up_read(&dqopt->dqio_sem);
 	return ret;
 }
@@ -187,9 +190,11 @@ static int v1_write_file_info(struct super_block *sb, int type)
 {
 	struct quota_info *dqopt = sb_dqopt(sb);
 	struct v1_disk_dqblk dqblk;
+	unsigned int memalloc;
 	int ret;
 
 	down_write(&dqopt->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	ret = sb->s_op->quota_read(sb, type, (char *)&dqblk,
 				sizeof(struct v1_disk_dqblk), v1_dqoff(0));
 	if (ret != sizeof(struct v1_disk_dqblk)) {
@@ -209,6 +214,7 @@ static int v1_write_file_info(struct super_block *sb, int type)
 	else if (ret >= 0)
 		ret = -EIO;
 out:
+	memalloc_nofs_restore(memalloc);
 	up_write(&dqopt->dqio_sem);
 	return ret;
 }
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
index ae99e7b88205..48e0d610ceef 100644
--- a/fs/quota/quota_v2.c
+++ b/fs/quota/quota_v2.c
@@ -96,9 +96,11 @@ static int v2_read_file_info(struct super_block *sb, int type)
 	struct qtree_mem_dqinfo *qinfo;
 	ssize_t size;
 	unsigned int version;
+	unsigned int memalloc;
 	int ret;
 
 	down_read(&dqopt->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	ret = v2_read_header(sb, type, &dqhead);
 	if (ret < 0)
 		goto out;
@@ -183,6 +185,7 @@ static int v2_read_file_info(struct super_block *sb, int type)
 		info->dqi_priv = NULL;
 	}
 out:
+	memalloc_nofs_restore(memalloc);
 	up_read(&dqopt->dqio_sem);
 	return ret;
 }
@@ -195,8 +198,10 @@ static int v2_write_file_info(struct super_block *sb, int type)
 	struct mem_dqinfo *info = &dqopt->info[type];
 	struct qtree_mem_dqinfo *qinfo = info->dqi_priv;
 	ssize_t size;
+	unsigned int memalloc;
 
 	down_write(&dqopt->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	spin_lock(&dq_data_lock);
 	info->dqi_flags &= ~DQF_INFO_DIRTY;
 	dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
@@ -209,6 +214,7 @@ static int v2_write_file_info(struct super_block *sb, int type)
 	dinfo.dqi_free_entry = cpu_to_le32(qinfo->dqi_free_entry);
 	size = sb->s_op->quota_write(sb, type, (char *)&dinfo,
 	       sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
+	memalloc_nofs_restore(memalloc);
 	up_write(&dqopt->dqio_sem);
 	if (size != sizeof(struct v2_disk_dqinfo)) {
 		quota_error(sb, "Can't write info structure");
@@ -328,11 +334,14 @@ static int v2_read_dquot(struct dquot *dquot)
 {
 	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
 	int ret;
+	unsigned int memalloc;
 
 	down_read(&dqopt->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	ret = qtree_read_dquot(
 			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
 			dquot);
+	memalloc_nofs_restore(memalloc);
 	up_read(&dqopt->dqio_sem);
 	return ret;
 }
@@ -342,6 +351,7 @@ static int v2_write_dquot(struct dquot *dquot)
 	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
 	int ret;
 	bool alloc = false;
+	unsigned int memalloc;
 
 	/*
 	 * If space for dquot is already allocated, we don't need any
@@ -355,9 +365,11 @@ static int v2_write_dquot(struct dquot *dquot)
 	} else {
 		down_read(&dqopt->dqio_sem);
 	}
+	memalloc = memalloc_nofs_save();
 	ret = qtree_write_dquot(
 			sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv,
 			dquot);
+	memalloc_nofs_restore(memalloc);
 	if (alloc)
 		up_write(&dqopt->dqio_sem);
 	else
@@ -368,10 +380,13 @@ static int v2_write_dquot(struct dquot *dquot)
 static int v2_release_dquot(struct dquot *dquot)
 {
 	struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
+	unsigned int memalloc;
 	int ret;
 
 	down_write(&dqopt->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	ret = qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
+	memalloc_nofs_restore(memalloc);
 	up_write(&dqopt->dqio_sem);
 
 	return ret;
@@ -386,10 +401,13 @@ static int v2_free_file_info(struct super_block *sb, int type)
 static int v2_get_next_id(struct super_block *sb, struct kqid *qid)
 {
 	struct quota_info *dqopt = sb_dqopt(sb);
+	unsigned int memalloc;
 	int ret;
 
 	down_read(&dqopt->dqio_sem);
+	memalloc = memalloc_nofs_save();
 	ret = qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid);
+	memalloc_nofs_restore(memalloc);
 	up_read(&dqopt->dqio_sem);
 	return ret;
 }
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 9/9] quota: Drop GFP_NOFS instances under dquot->dq_lock and dqio_sem
  2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
                   ` (7 preceding siblings ...)
  2024-01-23 15:25 ` [PATCH 8/9] quota: Set nofs allocation context when acquiring dqio_sem Jan Kara
@ 2024-01-23 15:25 ` Jan Kara
  8 siblings, 0 replies; 10+ messages in thread
From: Jan Kara @ 2024-01-23 15:25 UTC (permalink / raw)
  To: linux-fsdevel; +Cc: linux-ext4, Matthew Wilcox, Jan Kara

Quota code acquires dquot->dq_lock whenever reading / writing dquot.
When reading / writing quota info we hold dqio_sem.  Since these locks
can be acquired during inode reclaim (through dquot_drop() -> dqput() ->
dquot_release()) we are setting nofs allocation context whenever
acquiring these locks. Hence there's no need to use GFP_NOFS allocations
in quota code doing IO. Just switch it to GFP_KERNEL.

Signed-off-by: Jan Kara <jack@suse.cz>
---
 fs/quota/quota_tree.c | 24 ++++++++++++------------
 fs/quota/quota_v2.c   |  2 +-
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
index 0f1493e0f6d0..ef0461542d3a 100644
--- a/fs/quota/quota_tree.c
+++ b/fs/quota/quota_tree.c
@@ -108,7 +108,7 @@ static int check_dquot_block_header(struct qtree_mem_dqinfo *info,
 /* Remove empty block from list and return it */
 static int get_free_dqblk(struct qtree_mem_dqinfo *info)
 {
-	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
 	int ret, blk;
 
@@ -160,7 +160,7 @@ static int put_free_dqblk(struct qtree_mem_dqinfo *info, char *buf, uint blk)
 static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
 			       uint blk)
 {
-	char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
 	uint nextblk = le32_to_cpu(dh->dqdh_next_free);
 	uint prevblk = le32_to_cpu(dh->dqdh_prev_free);
@@ -207,7 +207,7 @@ static int remove_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
 static int insert_free_dqentry(struct qtree_mem_dqinfo *info, char *buf,
 			       uint blk)
 {
-	char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *tmpbuf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	struct qt_disk_dqdbheader *dh = (struct qt_disk_dqdbheader *)buf;
 	int err;
 
@@ -255,7 +255,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
 {
 	uint blk, i;
 	struct qt_disk_dqdbheader *dh;
-	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	char *ddquot;
 
 	*err = 0;
@@ -329,7 +329,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
 static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 			  uint *treeblk, int depth)
 {
-	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	int ret = 0, newson = 0, newact = 0;
 	__le32 *ref;
 	uint newblk;
@@ -410,7 +410,7 @@ int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
 	int type = dquot->dq_id.type;
 	struct super_block *sb = dquot->dq_sb;
 	ssize_t ret;
-	char *ddquot = kmalloc(info->dqi_entry_size, GFP_NOFS);
+	char *ddquot = kmalloc(info->dqi_entry_size, GFP_KERNEL);
 
 	if (!ddquot)
 		return -ENOMEM;
@@ -449,7 +449,7 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 			uint blk)
 {
 	struct qt_disk_dqdbheader *dh;
-	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	int ret = 0;
 
 	if (!buf)
@@ -513,7 +513,7 @@ static int free_dqentry(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
 		       uint *blk, int depth)
 {
-	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	int ret = 0;
 	uint newblk;
 	__le32 *ref = (__le32 *)buf;
@@ -577,7 +577,7 @@ EXPORT_SYMBOL(qtree_delete_dquot);
 static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
 				 struct dquot *dquot, uint blk)
 {
-	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	loff_t ret = 0;
 	int i;
 	char *ddquot;
@@ -615,7 +615,7 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
 static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
 				struct dquot *dquot, uint blk, int depth)
 {
-	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	loff_t ret = 0;
 	__le32 *ref = (__le32 *)buf;
 
@@ -684,7 +684,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
 		}
 		dquot->dq_off = offset;
 	}
-	ddquot = kmalloc(info->dqi_entry_size, GFP_NOFS);
+	ddquot = kmalloc(info->dqi_entry_size, GFP_KERNEL);
 	if (!ddquot)
 		return -ENOMEM;
 	ret = sb->s_op->quota_read(sb, type, ddquot, info->dqi_entry_size,
@@ -728,7 +728,7 @@ EXPORT_SYMBOL(qtree_release_dquot);
 static int find_next_id(struct qtree_mem_dqinfo *info, qid_t *id,
 			unsigned int blk, int depth)
 {
-	char *buf = kmalloc(info->dqi_usable_bs, GFP_NOFS);
+	char *buf = kmalloc(info->dqi_usable_bs, GFP_KERNEL);
 	__le32 *ref = (__le32 *)buf;
 	ssize_t ret;
 	unsigned int epb = info->dqi_usable_bs >> 2;
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
index 48e0d610ceef..5eb0de8e7e40 100644
--- a/fs/quota/quota_v2.c
+++ b/fs/quota/quota_v2.c
@@ -121,7 +121,7 @@ static int v2_read_file_info(struct super_block *sb, int type)
 			ret = -EIO;
 		goto out;
 	}
-	info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_NOFS);
+	info->dqi_priv = kmalloc(sizeof(struct qtree_mem_dqinfo), GFP_KERNEL);
 	if (!info->dqi_priv) {
 		ret = -ENOMEM;
 		goto out;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2024-01-23 15:25 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-01-23 15:24 [PATCH 0/9] Remove GFP_NOFS uses from ext2, udf, and quota code Jan Kara
2024-01-23 15:25 ` [PATCH 1/9] udf: Remove GFP_NOFS from dir iteration code Jan Kara
2024-01-23 15:25 ` [PATCH 2/9] udf: Avoid GFP_NOFS allocation in udf_symlink() Jan Kara
2024-01-23 15:25 ` [PATCH 3/9] udf: Avoid GFP_NOFS allocation in udf_load_pvoldesc() Jan Kara
2024-01-23 15:25 ` [PATCH 4/9] udf: Remove GFP_NOFS allocation in udf_expand_file_adinicb() Jan Kara
2024-01-23 15:25 ` [PATCH 5/9] ext2: Drop GFP_NOFS allocation from ext2_init_block_alloc_info() Jan Kara
2024-01-23 15:25 ` [PATCH 6/9] ext2: Drop GFP_NOFS use in ext2_get_blocks() Jan Kara
2024-01-23 15:25 ` [PATCH 7/9] ext2: Remove GFP_NOFS use in ext2_xattr_cache_insert() Jan Kara
2024-01-23 15:25 ` [PATCH 8/9] quota: Set nofs allocation context when acquiring dqio_sem Jan Kara
2024-01-23 15:25 ` [PATCH 9/9] quota: Drop GFP_NOFS instances under dquot->dq_lock and dqio_sem Jan Kara

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).