erofs: fix deadlock when shrink erofs slab

[ Upstream commit 57bbeacdbee72a54eb97d56b876cf9c94059fc34 ]

We observed the following deadlock in the stress test under low
memory scenario:

Thread A                               Thread B
- erofs_shrink_scan
 - erofs_try_to_release_workgroup
  - erofs_workgroup_try_to_freeze -- A
                                       - z_erofs_do_read_page
                                        - z_erofs_collection_begin
                                         - z_erofs_register_collection
                                          - erofs_insert_workgroup
                                           - xa_lock(&sbi->managed_pslots) -- B
                                           - erofs_workgroup_get
                                            - erofs_wait_on_workgroup_freezed -- A
  - xa_erase
   - xa_lock(&sbi->managed_pslots) -- B

To fix this, it needs to hold xa_lock before freezing the workgroup
since xarray will be touched then. So let's hold the lock before
accessing each workgroup, just like what we did with the radix tree
before.

[ Gao Xiang: Jianhua Hao also reports this issue at
  https://lore.kernel.org/r/b10b85df30694bac8aadfe43537c897a@xiaomi.com ]

Link: https://lore.kernel.org/r/20211118135844.3559-1-huangjianan@oppo.com
Fixes: 64094a04414f ("erofs: convert workstn to XArray")
Reviewed-by: Chao Yu <chao@kernel.org>
Reviewed-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Change-Id: I40fe8b5aaf319915e9a71282f3007d5e9d57675d
Signed-off-by: Huang Jianan <huangjianan@oppo.com>
Reported-by: Jianhua Hao <haojianhua1@xiaomi.com>
Signed-off-by: Gao Xiang <xiang@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Huang Jianan 2021-11-18 21:58:44 +08:00 committed by Michael Bestas
parent afd83285dd
commit 6524dbf7db
No known key found for this signature in database
GPG Key ID: CC95044519BE6669

View File

@ -142,7 +142,7 @@ static bool erofs_try_to_release_workgroup(struct erofs_sb_info *sbi,
* however in order to avoid some race conditions, add a * however in order to avoid some race conditions, add a
* DBG_BUGON to observe this in advance. * DBG_BUGON to observe this in advance.
*/ */
DBG_BUGON(xa_erase(&sbi->managed_pslots, grp->index) != grp); DBG_BUGON(__xa_erase(&sbi->managed_pslots, grp->index) != grp);
/* last refcount should be connected with its managed pslot. */ /* last refcount should be connected with its managed pslot. */
erofs_workgroup_unfreeze(grp, 0); erofs_workgroup_unfreeze(grp, 0);
@ -157,15 +157,19 @@ static unsigned long erofs_shrink_workstation(struct erofs_sb_info *sbi,
unsigned int freed = 0; unsigned int freed = 0;
unsigned long index; unsigned long index;
xa_lock(&sbi->managed_pslots);
xa_for_each(&sbi->managed_pslots, index, grp) { xa_for_each(&sbi->managed_pslots, index, grp) {
/* try to shrink each valid workgroup */ /* try to shrink each valid workgroup */
if (!erofs_try_to_release_workgroup(sbi, grp)) if (!erofs_try_to_release_workgroup(sbi, grp))
continue; continue;
xa_unlock(&sbi->managed_pslots);
++freed; ++freed;
if (!--nr_shrink) if (!--nr_shrink)
break; return freed;
xa_lock(&sbi->managed_pslots);
} }
xa_unlock(&sbi->managed_pslots);
return freed; return freed;
} }