Re: [PATCH v2] kasan: fix cache shrink race with CPU hotplug
Andrey Ryabinin <[email protected]>
| Newsgroups | org.kernel.vger.stable,org.kernel.vger.linux-kernel,org.kvack.linux-mm |
|---|---|
| Message-ID | <CAPAsAGxBpKDLNRoFCzX2+Wm5HC07c0uaoRsTtSOHhFsB4Bx-1w@mail.gmail.com> |
Hui Su <[email protected]> writes: > kasan_quarantine_remove_cache() first invokes per_cpu_remove_cache() on > all online CPUs. Each callback moves objects belonging to the cache from > cpu_quarantine to the CPU's shrink_qlist, where they can later be freed > from task context. > > kmem_cache_destroy() invokes the quarantine removal path while holding > cpus_read_lock(), but kmem_cache_shrink() does not. The latter can > therefore race with CPU offlining as follows: > > kmem_cache_shrink() CPU hotplug > ------------------- ----------- > on_each_cpu() > CPU1 moves objects to > CPU1's shrink_qlist > on_each_cpu() returns > CPU1 goes offline > kasan_cpu_offline() > drains cpu_quarantine > leaves shrink_qlist untouched > for_each_online_cpu() > skips CPU1 > > The objects left on CPU1's shrink_qlist are not returned to the slab > allocator. This may prevent kmem_cache_shrink() from releasing slabs > that would otherwise become empty. If CPU1 remains offline, a later > kmem_cache_destroy() also skips the list and can report that the cache > still contains objects. > > An intermittent occurrence was observed with a virtio-9p filesystem. > The mount and umount commands both returned 0, but the kernel logged > the following during the userspace-triggered teardown: > > [ 2994.380134][ T111] BUG 9p-fcall-cache-1 (Tainted: G B ): Objects remaining on __kmem_cache_shutdown() > [ 2994.381140][ T111] Object 0xff11000004361118 @offset=4376 > [ 2994.381607][ T111] Allocated in p9_fcall_init+0x201/0x400 age=19564 cpu=1 pid=104 > [ 2994.382591][ T111] p9_fcall_init+0x201/0x400 > [ 2994.382810][ T111] p9_tag_alloc+0x12f/0x700 > [ 2994.382982][ T111] p9_client_prepare_req+0x102/0x3e0 > [ 2994.383165][ T111] p9_client_rpc+0x1ab/0xa50 > [ 2994.383334][ T111] p9_client_getattr_dotl+0xb0/0x1a0 > [ 2994.383515][ T111] v9fs_vfs_getattr_dotl+0x115/0x360 > [ 2994.383719][ T111] vfs_getattr_nosec+0x22c/0x3a0 > [ 2994.383910][ T111] vfs_statx+0xd7/0x170 > [ 2994.384062][ T111] vfs_fstatat+0x45/0x80 > [ 2994.384215][ T111] __do_sys_newfstatat+0x84/0xe0 > [ 2994.384386][ T111] do_syscall_64+0x115/0x6a0 > [ 2994.384566][ T111] entry_SYSCALL_64_after_hwframe+0x77/0x7f > [ 2994.399720][ T111] WARNING: mm/slub.c:1244 at __kmem_cache_shutdown+0x363/0x500, CPU#0: busybox/111 > [ 2994.405655][ T111] Call Trace: > [ 2994.406325][ T111] kmem_cache_destroy+0x73/0x1b0 > [ 2994.406630][ T111] p9_client_destroy+0x271/0x3c0 > [ 2994.407210][ T111] v9fs_session_close+0x3c/0x260 > [ 2994.407409][ T111] v9fs_kill_super+0x48/0x90 > [ 2994.407584][ T111] deactivate_locked_super+0xa3/0x160 > [ 2994.407778][ T111] cleanup_mnt+0x1dd/0x3e0 > > Thus, a successful umount left objects in the 9p fcall cache and > prevented the cache from being destroyed cleanly. > > Per-CPU shrink_qlist storage exists for every possible CPU, and each > list is protected by its own raw spinlock. Iterate over possible CPUs > so that a list populated before its CPU went offline is drained as well. > > for_each_possible_cpu() can do more work than for_each_online_cpu(), but > this change only affects CONFIG_KASAN_GENERIC kernels. The extra work is > limited to cache shrink and cache destruction paths and does not affect > the normal allocation/free fast path. It adds one raw-spinlock-protected > scan of each possible CPU's shrink list. These lists are normally empty; > a non-empty list is traversed to remove objects belonging to the cache > being shrunk or destroyed. > > Fixes: 07d067e4f2ce ("kasan: fix sleeping function called from invalid context on RT kernel") > Cc: [email protected] > Signed-off-by: Hui Su <[email protected]> Reviewed-by: Andrey Ryabinin <[email protected]>