[Devel,rh7] mm/memcg: sleep if mem_cgroup_force_empty_list() stumped on busy page

Submitted by Andrey Ryabinin on Sept. 5, 2017, 3:53 p.m.

Details

Message ID 20170905155306.19073-1-aryabinin@virtuozzo.com
State New
Series "mm/memcg: sleep if mem_cgroup_force_empty_list() stumped on busy page"
Headers show

Commit Message

Andrey Ryabinin Sept. 5, 2017, 3:53 p.m.
mem_cgroup_force_empty_list() executed in workqueue context. If work doesn't
go to sleep the workqueue engine thinks that this work is making progress,
so there is no need to start more workers to execute other works.

So, if we need other works to be executed to unlock those pages we might
have a deadlock. I think this easy might happen with fuse, something
like this:

fuse:                              cgroup_destroy work:
			              mem_cgroup_force_empty_list()
                                          //busy wait for fuse to unlock pages.
 queue_work()

 //this may have to wait
 //for mem_cgroup_force_empty_list
 //to finish
 flush_work()

 read pages and unlock them.

The solution to this problem is to put mem_cgroup_force_empty_list() in short
sleep() instead of cond_resched(). This will allow other works to make progress
if mem_cgroup_force_empty_list() is stuck.

https://jira.sw.ru/browse/PSBM-70021
Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
---
 mm/memcontrol.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Patch hide | download patch | download mbox

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 940cca31ed5d..b09d5be27444 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -4034,7 +4034,7 @@  static void mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
 		if (mem_cgroup_move_parent(page, pc, memcg)) {
 			/* found lock contention or "pc" is obsolete. */
 			busy = page;
-			cond_resched();
+			schedule_timeout_uninterruptible(1);
 		} else
 			busy = NULL;
 	} while (!list_empty(list));