Skip to contentSkip to conversation

Fix generic/390 failure due to quota release after freeze

PATCH In review Ojaswin Mujoo 7 participants 19 messages Nov 21, 2024 – Mar 6, 2025

Subsystems (3)
lore
Share & tools
lore

Ojaswin Mujoo

author reviewer
#0 lore raw

v2 cover Fix generic/390 failure due to quota release after freeze

Changes since v1:

* Patch 1: Move flush_delayed_work() to start of function
* Patch 2: Guard ext4_release_dquot against freeze

Regarding patch 2, as per my understanding of the journalling code, right now ext4_release_dquot() can only be called from the quota_realease_work workqueue and hence ideally should never have a journal open but to future-proof it we make sure the journal is not opened when calling sb_start_inwrite().

** Original Cover **

Recently we noticed generic/390 failing on powerpc systems. This test basically does a freeze-unfreeze loop in parallel with fsstress on the FS to detect any races in the code paths.

We noticed that the test started failing due to kernel WARN_ONs because quota_release_work workqueue started executing while the FS was frozen which led to creating new transactions in ext4_release_quota.

Most of the details are in the bug however I'd just like to add that I'm completely new to quota code so the patch, although fixing the issue, might be not be logically the right thing to do. So reviews and suggestions are welcome.

Also, I can only replicate this race on one of my machines reliably and does not appear on others. I've tested with with fstests -g quota and don't see any new failures.

Ojaswin Mujoo (2):
quota: flush quota_release_work upon quota writeback
ext4: protect ext4_release_dquot against freezing

 fs/ext4/super.c  | 17 +++++++++++++++++
 fs/quota/dquot.c |  2 ++
 2 files changed, 19 insertions(+)
Signature · 1 line
2.43.5
author reviewer
#1 lore raw

PATCH v2 1/2 quota: flush quota_release_work upon quota writeback

One of the paths quota writeback is called from is:

freeze_super()
sync_filesystem()

    ext4_sync_fs()
      dquot_writeback_dquots()

Since we currently don't always flush the quota_release_work queue in this path, we can end up with the following race:

1. dquot are added to releasing_dquots list during regular operations.
2. FS Freeze starts, however, this does not flush the quota_release_work queue.
3. Freeze completes.
4. Kernel eventually tries to flush the workqueue while FS is frozen which hits a WARN_ON since transaction gets started during frozen state:

  ext4_journal_check_start+0x28/0x110 [ext4] (unreliable)
  __ext4_journal_start_sb+0x64/0x1c0 [ext4]
  ext4_release_dquot+0x90/0x1d0 [ext4]
  quota_release_workfn+0x43c/0x4d0

Which is the following line:

  WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);

Which ultimately results in generic/390 failing due to dmesg noise. This was detected on powerpc machine 15 cores.

To avoid this, make sure to flush the workqueue during dquot_writeback_dquots() so we dont have any pending workitems after freeze.

---
 fs/quota/dquot.c | 2 ++
 1 file changed, 2 insertions(+)
fs/quota/dquot.c +2 -0
index 3dd8d6f27725..f9578918cfb2 100644
@@ -688,6 +688,8 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
 
 	WARN_ON_ONCE(!rwsem_is_locked(&sb->s_umount));
 
+	flush_delayed_work(&quota_release_work);
+
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (type != -1 && cnt != type)
 			continue;
Signature · 1 line
2.43.5
reviewer
#2 lore raw

Re: [PATCH v2 1/2] quota: flush quota_release_work upon quota writeback

Quoted 28 lines · Ojaswin Mujoo

One of the paths quota writeback is called from is:

freeze_super()
sync_filesystem()

    ext4_sync_fs()
      dquot_writeback_dquots()

Since we currently don't always flush the quota_release_work queue in this path, we can end up with the following race:

1. dquot are added to releasing_dquots list during regular operations.
2. FS Freeze starts, however, this does not flush the quota_release_work queue.
3. Freeze completes.
4. Kernel eventually tries to flush the workqueue while FS is frozen which hits a WARN_ON since transaction gets started during frozen state:

  ext4_journal_check_start+0x28/0x110 [ext4] (unreliable)
  __ext4_journal_start_sb+0x64/0x1c0 [ext4]
  ext4_release_dquot+0x90/0x1d0 [ext4]
  quota_release_workfn+0x43c/0x4d0

Which is the following line:

  WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);

Which ultimately results in generic/390 failing due to dmesg noise. This was detected on powerpc machine 15 cores.

Quoted 6 lines

To avoid this, make sure to flush the workqueue during dquot_writeback_dquots() so we dont have any pending workitems after freeze.

Thanks. Since this patch is independent, I've picked it up into my tree (will push it to Linus for rc2).

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
Reviewed-by on v2 1/2
#3 lore raw

Re: [PATCH v2 1/2] quota: flush quota_release_work upon quota writeback

In reply to Ojaswin Mujoo
Quoted 28 lines · Ojaswin Mujoo

One of the paths quota writeback is called from is:

freeze_super()
sync_filesystem()

     ext4_sync_fs()
       dquot_writeback_dquots()

Since we currently don't always flush the quota_release_work queue in this path, we can end up with the following race:

1. dquot are added to releasing_dquots list during regular operations.
2. FS Freeze starts, however, this does not flush the quota_release_work queue.
3. Freeze completes.
4. Kernel eventually tries to flush the workqueue while FS is frozen which hits a WARN_ON since transaction gets started during frozen state:

   ext4_journal_check_start+0x28/0x110 [ext4] (unreliable)
   __ext4_journal_start_sb+0x64/0x1c0 [ext4]
   ext4_release_dquot+0x90/0x1d0 [ext4]
   quota_release_workfn+0x43c/0x4d0

Which is the following line:

   WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);

Which ultimately results in generic/390 failing due to dmesg noise. This was detected on powerpc machine 15 cores.

Quoted 6 lines

To avoid this, make sure to flush the workqueue during dquot_writeback_dquots() so we dont have any pending workitems after freeze.

Looks good, thanks for the patch!

Quoted 17 lines
---
  fs/quota/dquot.c | 2 ++
  1 file changed, 2 insertions(+)
fs/quota/dquot.c +2 -0
index 3dd8d6f27725..f9578918cfb2 100644
@@ -688,6 +688,8 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
 
  	WARN_ON_ONCE(!rwsem_is_locked(&sb->s_umount));
 
+	flush_delayed_work(&quota_release_work);
+
  	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
  		if (type != -1 && cnt != type)
  			continue;
Tested-by on v2 1/2
#4 lore raw

Re: [PATCH v2 1/2] quota: flush quota_release_work upon quota writeback

In reply to Ojaswin Mujoo

On 21/11/24 6:08 pm, Ojaswin Mujoo wrote:

One of the paths quota writeback is called from is:

freeze_super()
sync_filesystem()

    ext4_sync_fs()
      dquot_writeback_dquots()

Since we currently don't always flush the quota_release_work queue in this path, we can end up with the following race:

1. dquot are added to releasing_dquots list during regular operations.
2. FS Freeze starts, however, this does not flush the quota_release_work queue.
3. Freeze completes.
4. Kernel eventually tries to flush the workqueue while FS is frozen which hits a WARN_ON since transaction gets started during frozen state:

  ext4_journal_check_start+0x28/0x110 [ext4] (unreliable)
  __ext4_journal_start_sb+0x64/0x1c0 [ext4]
  ext4_release_dquot+0x90/0x1d0 [ext4]
  quota_release_workfn+0x43c/0x4d0

Which is the following line:

  WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);

Which ultimately results in generic/390 failing due to dmesg noise. This was detected on powerpc machine 15 cores.

To avoid this, make sure to flush the workqueue during dquot_writeback_dquots() so we dont have any pending workitems after freeze.

Thanks for the fix patch Ojaswin.
I have tested the patch on powerpc machine, and it fixes the generic/390 test failure.

---
 fs/quota/dquot.c | 2 ++
 1 file changed, 2 insertions(+)
fs/quota/dquot.c +2 -0
index 3dd8d6f27725..f9578918cfb2 100644
@@ -688,6 +688,8 @@ int dquot_writeback_dquots(struct super_block *sb, int type)
 
 	WARN_ON_ONCE(!rwsem_is_locked(&sb->s_umount));
 
+	flush_delayed_work(&quota_release_work);
+
 	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
 		if (type != -1 && cnt != type)
 			continue;
author reviewer
#5 lore raw

PATCH v2 2/2 ext4: protect ext4_release_dquot against freezing

In reply to Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

---
 fs/ext4/super.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
fs/ext4/super.c +17 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
 {
 	int ret, err;
 	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
 
 	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
 				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
 	if (IS_ERR(handle)) {
 		/* Release dquot anyway to avoid endless cycle in dqput() */
 		dquot_release(dquot);
+		if (freeze_protected)
+			sb_end_intwrite(dquot->dq_sb);
 		return PTR_ERR(handle);
 	}
 	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)
 	err = ext4_journal_stop(handle);
 	if (!ret)
 		ret = err;
+
+	if (freeze_protected)
+		sb_end_intwrite(dquot->dq_sb);
+
 	return ret;
 }
 
Signature · 1 line
2.43.5
#6 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

kernel test robot noticed the following build errors:

Hi Ojaswin,

kernel test robot noticed the following build errors:

[auto build test ERROR on tytso-ext4/dev]
[also build test ERROR on brauner-vfs/vfs.all jack-fs/for_next linus/master v6.12 next-20241121] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Ojaswin-Mujoo/quota-flush-quota_release_work-upon-quota-writeback/20241121-204331
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
patch link:    https://lore.kernel.org/r/20241121123855.645335-3-ojaswin%40linux.ibm.com
patch subject: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing
config: i386-buildonly-randconfig-004-20241122 (https://download.01.org/0day-ci/archive/20241122/[email protected]/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241122/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

In file included from fs/ext4/super.c:27:
In file included from include/linux/blkdev.h:9:
In file included from include/linux/blk_types.h:10:
In file included from include/linux/bvec.h:10:
In file included from include/linux/highmem.h:8:
In file included from include/linux/cacheflush.h:5:
In file included from arch/x86/include/asm/cacheflush.h:5:
In file included from include/linux/mm.h:2213:
include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]

     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
>> fs/ext4/super.c:6912:6: error: use of undeclared identifier 'freeze_protected'; did you mean 'freeze_processes'?
    6912 |         if (freeze_protected)
         |             ^~~~~~~~~~~~~~~~
         |             freeze_processes
   include/linux/freezer.h:46:12: note: 'freeze_processes' declared here
      46 | extern int freeze_processes(void);
         |            ^

1 warning and 1 error generated.

vim +6912 fs/ext4/super.c

6893
6894 static int ext4_acquire_dquot(struct dquot *dquot)
6895 {
6896 int ret, err;
6897 handle_t *handle;
6898
6899 handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
6900 EXT4_QUOTA_INIT_BLOCKS(dquot->dq_sb));
6901 if (IS_ERR(handle))
6902 return PTR_ERR(handle);
6903 ret = dquot_acquire(dquot);
6904 if (ret < 0)
6905 ext4_error_err(dquot->dq_sb, -ret,
6906 "Failed to acquire dquot type %d",
6907 dquot->dq_id.type);
6908 err = ext4_journal_stop(handle);
6909 if (!ret)
6910 ret = err;
6911

> 6912		if (freeze_protected)

6913 sb_end_intwrite(dquot->dq_sb);
6914
6915 return ret;
6916 }
6917

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#7 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

In reply to Ojaswin Mujoo
kernel test robot noticed the following build errors:

Hi Ojaswin,

kernel test robot noticed the following build errors:

[auto build test ERROR on tytso-ext4/dev]
[also build test ERROR on brauner-vfs/vfs.all jack-fs/for_next linus/master v6.12 next-20241121] [If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Ojaswin-Mujoo/quota-flush-quota_release_work-upon-quota-writeback/20241121-204331
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4.git dev
patch link:    https://lore.kernel.org/r/20241121123855.645335-3-ojaswin%40linux.ibm.com
patch subject: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing
config: arc-randconfig-001-20241122 (https://download.01.org/0day-ci/archive/20241122/[email protected]/config)
compiler: arc-elf-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241122/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

fs/ext4/super.c: In function 'ext4_acquire_dquot':

>> fs/ext4/super.c:6912:13: error: 'freeze_protected' undeclared (first use in this function); did you mean 'freeze_processes'?
    6912 |         if (freeze_protected)
         |             ^~~~~~~~~~~~~~~~
         |             freeze_processes

fs/ext4/super.c:6912:13: note: each undeclared identifier is reported only once for each function it appears in

vim +6912 fs/ext4/super.c

6893
6894 static int ext4_acquire_dquot(struct dquot *dquot)
6895 {
6896 int ret, err;
6897 handle_t *handle;
6898
6899 handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
6900 EXT4_QUOTA_INIT_BLOCKS(dquot->dq_sb));
6901 if (IS_ERR(handle))
6902 return PTR_ERR(handle);
6903 ret = dquot_acquire(dquot);
6904 if (ret < 0)
6905 ext4_error_err(dquot->dq_sb, -ret,
6906 "Failed to acquire dquot type %d",
6907 dquot->dq_id.type);
6908 err = ext4_journal_stop(handle);
6909 if (!ret)
6910 ret = err;
6911

> 6912		if (freeze_protected)

6913 sb_end_intwrite(dquot->dq_sb);
6914
6915 return ret;
6916 }
6917

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
reviewer Reviewed-by on v2 2/2
#8 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

In reply to Ojaswin Mujoo
Quoted 4 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Quoted 8 lines

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

Looks good to me (the 0-day reports seem to be due to wrong merge). Feel free to add:

Honza

Quoted 48 lines
---
 fs/ext4/super.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
fs/ext4/super.c +17 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
 {
 	int ret, err;
 	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
 
 	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
 				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
 	if (IS_ERR(handle)) {
 		/* Release dquot anyway to avoid endless cycle in dqput() */
 		dquot_release(dquot);
+		if (freeze_protected)
+			sb_end_intwrite(dquot->dq_sb);
 		return PTR_ERR(handle);
 	}
 	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)
 	err = ext4_journal_stop(handle);
 	if (!ret)
 		ret = err;
+
+	if (freeze_protected)
+		sb_end_intwrite(dquot->dq_sb);
+
 	return ret;
 }
 
Signature · 1 line
2.43.5
Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
author reviewer
#9 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

Quoted 14 lines · Jan Kara
Quoted 4 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Quoted 8 lines

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

Quoted 6 lines

Looks good to me (the 0-day reports seem to be due to wrong merge). Feel free to add:

Honza

Thanks Jan, yes it does seem like an incorrect merge.

Quoted 52 lines
Quoted 49 lines
---
 fs/ext4/super.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
fs/ext4/super.c +17 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
 {
 	int ret, err;
 	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
 
 	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
 				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
 	if (IS_ERR(handle)) {
 		/* Release dquot anyway to avoid endless cycle in dqput() */
 		dquot_release(dquot);
+		if (freeze_protected)
+			sb_end_intwrite(dquot->dq_sb);
 		return PTR_ERR(handle);
 	}
 	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)
 	err = ext4_journal_stop(handle);
 	if (!ret)
 		ret = err;
+
+	if (freeze_protected)
+		sb_end_intwrite(dquot->dq_sb);
+
 	return ret;
 }
 
Signature · 1 line
2.43.5
Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
Reviewed-by on v2 2/2
#10 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

In reply to Ojaswin Mujoo

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot)

Quoted 47 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

---
  fs/ext4/super.c | 17 +++++++++++++++++
  1 file changed, 17 insertions(+)
fs/ext4/super.c +13 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
  {
  	int ret, err;
  	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
 
  	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
  				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
  	if (IS_ERR(handle)) {
  		/* Release dquot anyway to avoid endless cycle in dqput() */
  		dquot_release(dquot);
+		if (freeze_protected)
+			sb_end_intwrite(dquot->dq_sb);
  		return PTR_ERR(handle);
  	}
  	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)

The git am command looks for the following context code from line 6903 to apply the changes. But there are many functions in fs/ext4/super.c that have similar code, such as ext4_write_dquot() and ext4_acquire_dquot().

So when the code before ext4_release_dquot() is added, the first matching context found could be in ext4_write_dquot() or ext4_acquire_dquot().

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot) · line 6925

Quoted 10 lines
fs/ext4/super.c +4 -0
  	err = ext4_journal_stop(handle);
  	if (!ret)
  		ret = err;
+
+	if (freeze_protected)
+		sb_end_intwrite(dquot->dq_sb);
+
  	return ret;
  }
 

Thus this is actually a bug in git am, which can be avoided by increasing the number of context lines with git format-patch -U8 -1.

Otherwise it looks good. Feel free to add:

author reviewer
#11 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

Quoted 42 lines · Baokun Li
Quoted 41 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

---
  fs/ext4/super.c | 17 +++++++++++++++++
  1 file changed, 17 insertions(+)
fs/ext4/super.c +12 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
  {
  	int ret, err;
  	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
  	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
  				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
  	if (IS_ERR(handle)) {
  		/* Release dquot anyway to avoid endless cycle in dqput() */
  		dquot_release(dquot);
+		if (freeze_protected)
Quoted 8 lines

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot)

Quoted 5 lines
fs/ext4/super.c +1 -0
+			sb_end_intwrite(dquot->dq_sb);
  		return PTR_ERR(handle);
  	}
  	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)

The git am command looks for the following context code from line 6903 to apply the changes. But there are many functions in fs/ext4/super.c that have similar code, such as ext4_write_dquot() and ext4_acquire_dquot().

Oh that's strange, shouldn't it match the complete line like:

Quoted 1 line

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot)

Quoted 1 line
fs/ext4/super.c +0 -0
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)

That should only have one occurence around line 6903? Or does it try to fuzzy match which ends up matching ext4_write_dquot etc?

Quoted 16 lines

So when the code before ext4_release_dquot() is added, the first matching context found could be in ext4_write_dquot() or ext4_acquire_dquot().

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot) · line 6924

Quoted 9 lines
fs/ext4/super.c +4 -0
  	err = ext4_journal_stop(handle);
  	if (!ret)
  		ret = err;
+
+	if (freeze_protected)
+		sb_end_intwrite(dquot->dq_sb);
+
  	return ret;
  }

Thus this is actually a bug in git am, which can be avoided by increasing the number of context lines with git format-patch -U8 -1.

Quoted 3 lines

Otherwise it looks good. Feel free to add:

Thanks Baokun!

Regards,
ojaswin

Quoted 1 line
#12 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

Quoted 51 lines · Ojaswin Mujoo
Quoted 42 lines · Baokun Li
Quoted 41 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

---
   fs/ext4/super.c | 17 +++++++++++++++++
   1 file changed, 17 insertions(+)
fs/ext4/super.c +12 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
   {
   	int ret, err;
   	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
   	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
   				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
   	if (IS_ERR(handle)) {
   		/* Release dquot anyway to avoid endless cycle in dqput() */
   		dquot_release(dquot);
+		if (freeze_protected)
Quoted 8 lines

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot)

Quoted 5 lines
fs/ext4/super.c +1 -0
+			sb_end_intwrite(dquot->dq_sb);
   		return PTR_ERR(handle);
   	}
   	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)

The git am command looks for the following context code from line 6903 to apply the changes. But there are many functions in fs/ext4/super.c that have similar code, such as ext4_write_dquot() and ext4_acquire_dquot().

Quoted 1 line

Oh that's strange, shouldn't it match the complete line like:

A rough look at the git am source code looks like it only focuses on line numbers between two ‘@@’.

am_run
 run_apply
  apply_all_patches
   apply_patch
    parse_chunk
     find_header
      parse_fragment_header
    check_patch_list
     check_patch
      apply_data
       load_preimage
       apply_fragments
        apply_one_fragment
         find_pos
          match_fragment

Quoted 3 lines
Quoted 1 line

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot)

Quoted 1 line
fs/ext4/super.c +0 -0
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)

That should only have one occurence around line 6903? Or does it try to fuzzy match which ends up matching ext4_write_dquot etc?

In find_pos(), start from line 6903, compare the hash value of each line of code line by line in forward direction, if it can't match, then match the hash value of each line of code line by line in reverse direction from line 6903. Fuzzy matching is used if some whitespace characters should be ignored.

Regards,
Baokun

author reviewer
#13 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

Quoted 71 lines · Baokun Li
Quoted 51 lines · Ojaswin Mujoo
Quoted 42 lines · Baokun Li
Quoted 41 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

---
   fs/ext4/super.c | 17 +++++++++++++++++
   1 file changed, 17 insertions(+)
fs/ext4/super.c +12 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
   {
   	int ret, err;
   	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
   	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
   				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
   	if (IS_ERR(handle)) {
   		/* Release dquot anyway to avoid endless cycle in dqput() */
   		dquot_release(dquot);
+		if (freeze_protected)
Quoted 8 lines

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot)

Quoted 5 lines
fs/ext4/super.c +1 -0
+			sb_end_intwrite(dquot->dq_sb);
   		return PTR_ERR(handle);
   	}
   	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)

The git am command looks for the following context code from line 6903 to apply the changes. But there are many functions in fs/ext4/super.c that have similar code, such as ext4_write_dquot() and ext4_acquire_dquot().

Quoted 1 line

Oh that's strange, shouldn't it match the complete line like:

A rough look at the git am source code looks like it only focuses on line numbers between two ‘@@’.

am_run
 run_apply
  apply_all_patches
   apply_patch
    parse_chunk
     find_header
      parse_fragment_header
    check_patch_list
     check_patch
      apply_data
       load_preimage
       apply_fragments
        apply_one_fragment
         find_pos
          match_fragment

Quoted 8 lines
Quoted 3 lines
Quoted 1 line

fs/ext4/super.c static int ext4_release_dquot(struct dquot *dquot)

Quoted 1 line
fs/ext4/super.c +0 -0
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)

That should only have one occurence around line 6903? Or does it try to fuzzy match which ends up matching ext4_write_dquot etc?

In find_pos(), start from line 6903, compare the hash value of each line of code line by line in forward direction, if it can't match, then match the hash value of each line of code line by line in reverse direction from line 6903. Fuzzy matching is used if some whitespace characters should be ignored.

Ahh okay thanks for looking into this Baokun, pretty interesting since I've never looked at git code :)

Regards,
ojaswin

Quoted 5 lines

Regards,
Baokun

#14 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

In reply to Ojaswin Mujoo
Quoted 4 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Quoted 8 lines

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

Sorry for being late on this. Ideally, shouldn't it be the responsibility of higher level FS (ext4) to make sure that FS never freezes while there is pending work for releasing dquot structures and that it should also prevent any context where such dquot structures gets added for release/delayed release.

e.g. this is what FS takes care during freeze path i.e.
freeze_super() -> sync_fs -> ext4_sync_fs()-> dquot_writeback_dquots() -> flush_delayed_work() (now fixed)

Now coming to iput() case which Jan mentioned [1] which could still be called after FS have frozen. As I see we have a protection from FS freeze in the ext4_evict_path() right? So ideally we should never see dquot_drop() w/o fs freeze protection. And say, if the FS freezing immediately happened after we scheduled this delayed work (but before the work gets scheduled), then that will be taken care in the freeze_super() chain, where we will flush all the delayed work no? - which is what Patch-1 is fixing.

(There still might be an error handling path in ext4_evict_inode() -> ext4_clear_inode() which we don't freeze protect. I still need to take a closer look at that though).

So.. isn't this patch trying to hide the problem where FS failed to freeze protect some code path?

-ritesh

Quoted 47 lines
---
 fs/ext4/super.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
fs/ext4/super.c +17 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
 {
 	int ret, err;
 	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
 
 	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
 				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
 	if (IS_ERR(handle)) {
 		/* Release dquot anyway to avoid endless cycle in dqput() */
 		dquot_release(dquot);
+		if (freeze_protected)
+			sb_end_intwrite(dquot->dq_sb);
 		return PTR_ERR(handle);
 	}
 	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)
 	err = ext4_journal_stop(handle);
 	if (!ret)
 		ret = err;
+
+	if (freeze_protected)
+		sb_end_intwrite(dquot->dq_sb);
+
 	return ret;
 }
 
Signature · 1 line
2.43.5
reviewer
#15 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

Quoted 21 lines · Ritesh Harjani
Quoted 4 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Quoted 8 lines

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

Sorry for being late on this. Ideally, shouldn't it be the responsibility of higher level FS (ext4) to make sure that FS never freezes while there is pending work for releasing dquot structures and that it should also prevent any context where such dquot structures gets added for release/delayed release.

Quoted 6 lines

e.g. this is what FS takes care during freeze path i.e.
freeze_super() -> sync_fs -> ext4_sync_fs()-> dquot_writeback_dquots() -> flush_delayed_work() (now fixed)

Now coming to iput() case which Jan mentioned [1] which could still be called after FS have frozen. As I see we have a protection from FS freeze in the ext4_evict_path() right? So ideally we should never see

We don't if we go through:

ext4_evict_inode()

  if (inode->i_nlink) {
    truncate_inode_pages_final(&inode->i_data);
    goto no_delete;
  }
no_delete:
  ext4_clear_inode(inode)
    ...
    dquot_drop()
Quoted 9 lines

dquot_drop() w/o fs freeze protection. And say, if the FS freezing immediately happened after we scheduled this delayed work (but before the work gets scheduled), then that will be taken care in the freeze_super() chain, where we will flush all the delayed work no? - which is what Patch-1 is fixing.

(There still might be an error handling path in ext4_evict_inode() -> ext4_clear_inode() which we don't freeze protect. I still need to take a closer look at that though).

It isn't error handling. It is a standard inode eviction path if the inode isn't being deleted.

Quoted 2 lines

So.. isn't this patch trying to hide the problem where FS failed to freeze protect some code path?

Well, it is kind of self-inflicted damage of ext4_dquot_release() because it starts a transaction even if there will be nothing to do. We could add checks to ext4_dquot_release() to start a transaction only if dquot structure will need to be deleted but that's a layering violation because it would have to make assumptions about how quota format code is going to behave.

Honza

Signature · 2 lines
Jan Kara <[email protected]>
SUSE Labs, CR
author reviewer
#16 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

In reply to Ojaswin Mujoo
Quoted 4 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Quoted 8 lines

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

Hey Ted,

Just a ping, I think you might have missed this patch. Let me know if anything else is needed from my side.

Regards,
ojaswin

Quoted 48 lines
---
 fs/ext4/super.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
fs/ext4/super.c +17 -0
index 16a4ce704460..f7437a592359 100644
@@ -6887,12 +6887,25 @@ static int ext4_release_dquot(struct dquot *dquot)
 {
 	int ret, err;
 	handle_t *handle;
+	bool freeze_protected = false;
+
+	/*
+	 * Trying to sb_start_intwrite() in a running transaction
+	 * can result in a deadlock. Further, running transactions
+	 * are already protected from freezing.
+	 */
+	if (!ext4_journal_current_handle()) {
+		sb_start_intwrite(dquot->dq_sb);
+		freeze_protected = true;
+	}
 
 	handle = ext4_journal_start(dquot_to_inode(dquot), EXT4_HT_QUOTA,
 				    EXT4_QUOTA_DEL_BLOCKS(dquot->dq_sb));
 	if (IS_ERR(handle)) {
 		/* Release dquot anyway to avoid endless cycle in dqput() */
 		dquot_release(dquot);
+		if (freeze_protected)
+			sb_end_intwrite(dquot->dq_sb);
 		return PTR_ERR(handle);
 	}
 	ret = dquot_release(dquot);
@@ -6903,6 +6916,10 @@ static int ext4_release_dquot(struct dquot *dquot)
 	err = ext4_journal_stop(handle);
 	if (!ret)
 		ret = err;
+
+	if (freeze_protected)
+		sb_end_intwrite(dquot->dq_sb);
+
 	return ret;
 }
 
Signature · 1 line
2.43.5
maintainer
#17 lore raw

Re: [PATCH v2 2/2] ext4: protect ext4_release_dquot against freezing

Quoted 11 lines · Ojaswin Mujoo
Quoted 10 lines · Ojaswin Mujoo

Protect ext4_release_dquot against freezing so that we don't try to start a transaction when FS is frozen, leading to warnings.

Further, avoid taking the freeze protection if a transaction is already running so that we don't need end up in a deadlock as described in

46e294efc355 ext4: fix deadlock with fs freezing and EA inodes

Quoted 7 lines
Quoted 2 lines

Hey Ted,

Just a ping, I think you might have missed this patch. Let me know if anything else is needed from my side.

Yes, I did miss this patch; thanks for the reminder. It looks good and I've added it to my tree.

- Ted

author reviewer
#18 lore raw
In reply to Ojaswin Mujoo
Quoted 1 line · Ojaswin Mujoo

Changes since v1:

Forgot to link v1:

https://lore.kernel.org/linux-ext4/[email protected]/T/#t

Quoted 40 lines

* Patch 1: Move flush_delayed_work() to start of function
* Patch 2: Guard ext4_release_dquot against freeze

Regarding patch 2, as per my understanding of the journalling code, right now ext4_release_dquot() can only be called from the quota_realease_work workqueue and hence ideally should never have a journal open but to future-proof it we make sure the journal is not opened when calling sb_start_inwrite().

** Original Cover **

Recently we noticed generic/390 failing on powerpc systems. This test basically does a freeze-unfreeze loop in parallel with fsstress on the FS to detect any races in the code paths.

We noticed that the test started failing due to kernel WARN_ONs because quota_release_work workqueue started executing while the FS was frozen which led to creating new transactions in ext4_release_quota.

Most of the details are in the bug however I'd just like to add that I'm completely new to quota code so the patch, although fixing the issue, might be not be logically the right thing to do. So reviews and suggestions are welcome.

Also, I can only replicate this race on one of my machines reliably and does not appear on others. I've tested with with fstests -g quota and don't see any new failures.

Ojaswin Mujoo (2):
quota: flush quota_release_work upon quota writeback
ext4: protect ext4_release_dquot against freezing

 fs/ext4/super.c  | 17 +++++++++++++++++
 fs/quota/dquot.c |  2 ++
 2 files changed, 19 insertions(+)
Signature · 1 line
2.43.5