]> git.itanic.dy.fi Git - linux-stable/commit
mm: shrinkers: fix deadlock in shrinker debugfs
authorQi Zheng <zhengqi.arch@bytedance.com>
Thu, 2 Feb 2023 10:56:12 +0000 (18:56 +0800)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 9 Feb 2023 23:56:51 +0000 (15:56 -0800)
commitbadc28d4924bfed73efc93f716a0c3aa3afbdf6f
tree71a94e24eaf573ef94b3544d5db872a15a5fef7d
parent6b970599e807ea95c653926d41b095a92fd381e2
mm: shrinkers: fix deadlock in shrinker debugfs

The debugfs_remove_recursive() is invoked by unregister_shrinker(), which
is holding the write lock of shrinker_rwsem.  It will waits for the
handler of debugfs file complete.  The handler also needs to hold the read
lock of shrinker_rwsem to do something.  So it may cause the following
deadlock:

  CPU0 CPU1

debugfs_file_get()
shrinker_debugfs_count_show()/shrinker_debugfs_scan_write()

      unregister_shrinker()
--> down_write(&shrinker_rwsem);
    debugfs_remove_recursive()
// wait for (A)
    --> wait_for_completion();

    // wait for (B)
--> down_read_killable(&shrinker_rwsem)
debugfs_file_put() -- (A)

    up_write() -- (B)

The down_read_killable() can be killed, so that the above deadlock can be
recovered.  But it still requires an extra kill action, otherwise it will
block all subsequent shrinker-related operations, so it's better to fix
it.

[akpm@linux-foundation.org: fix CONFIG_SHRINKER_DEBUG=n stub]
Link: https://lkml.kernel.org/r/20230202105612.64641-1-zhengqi.arch@bytedance.com
Fixes: 5035ebc644ae ("mm: shrinkers: introduce debugfs interface for memory shrinkers")
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
Reviewed-by: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Kent Overstreet <kent.overstreet@gmail.com>
Cc: Muchun Song <songmuchun@bytedance.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/shrinker.h
mm/shrinker_debug.c
mm/vmscan.c