mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 02:21:39 -04:00
md/md-llbitmap: stop daemon timer rearm on destroy
llbitmap_destroy() deletes pending_timer before flushing
md_llbitmap_io_wq. However, daemon_work can still be queued or running
after the timer has been deleted, and the daemon path can arm
pending_timer again when it finds dirty chunks that are not ready to
flush yet.
If that happens during teardown, pending_timer can remain armed after
llbitmap is freed and later dereference freed memory.
Add a BITMAP_SHUTDOWN bit to llbitmap->flags, set it before deleting
the timer, and make the timer and daemon paths stop queueing or rearming
work once teardown starts. Cancel daemon_work before flushing the shared
workqueue so no already queued daemon instance can race with the free.
Use timer_shutdown_sync() so a daemon instance that passed the shutdown
check before teardown cannot rearm the timer afterward.
BITMAP_SHUTDOWN is a runtime-only state. Mask it out when reading and
updating the llbitmap superblock so the shutdown state is never loaded
from disk or persisted to disk.
Fixes: 5ab829f197 ("md/md-llbitmap: introduce new lockless bitmap")
Tested-by: Mykola Marzhan <mykola@meshstor.io>
Link: https://patch.msgid.link/20260802195038.164272-10-yukuai@kernel.org
Signed-off-by: Yu Kuai <yukuai@fygo.io>
This commit is contained in:
@@ -29,6 +29,7 @@ enum bitmap_state {
|
||||
BITMAP_FIRST_USE = 3, /* llbitmap is just created */
|
||||
BITMAP_CLEAN = 4, /* llbitmap is created with assume_clean */
|
||||
BITMAP_DAEMON_BUSY = 5, /* llbitmap daemon is not finished after daemon_sleep */
|
||||
BITMAP_SHUTDOWN = 6, /* llbitmap is being destroyed */
|
||||
BITMAP_HOSTENDIAN =15,
|
||||
};
|
||||
|
||||
|
||||
@@ -789,6 +789,7 @@ static enum llbitmap_state llbitmap_state_machine(struct llbitmap *llbitmap,
|
||||
if (state == BitNeedSync || state == BitNeedSyncUnwritten)
|
||||
need_resync = !mddev->degraded;
|
||||
else if (state == BitDirty &&
|
||||
!test_bit(BITMAP_SHUTDOWN, &llbitmap->flags) &&
|
||||
!timer_pending(&llbitmap->pending_timer))
|
||||
mod_timer(&llbitmap->pending_timer,
|
||||
jiffies + mddev->bitmap_info.daemon_sleep * HZ);
|
||||
@@ -981,7 +982,7 @@ static int llbitmap_read_sb(struct llbitmap *llbitmap)
|
||||
else
|
||||
mddev->bitmap_info.space = mddev->bitmap_info.default_space;
|
||||
}
|
||||
llbitmap->flags = le32_to_cpu(sb->state);
|
||||
llbitmap->flags = le32_to_cpu(sb->state) & ~BIT(BITMAP_SHUTDOWN);
|
||||
if (test_and_clear_bit(BITMAP_FIRST_USE, &llbitmap->flags)) {
|
||||
ret = llbitmap_init(llbitmap);
|
||||
goto out_put_page;
|
||||
@@ -1037,6 +1038,9 @@ static void llbitmap_pending_timer_fn(struct timer_list *pending_timer)
|
||||
struct llbitmap *llbitmap =
|
||||
container_of(pending_timer, struct llbitmap, pending_timer);
|
||||
|
||||
if (test_bit(BITMAP_SHUTDOWN, &llbitmap->flags))
|
||||
return;
|
||||
|
||||
if (work_busy(&llbitmap->daemon_work)) {
|
||||
pr_warn("md/llbitmap: %s daemon_work not finished in %lu seconds\n",
|
||||
mdname(llbitmap->mddev),
|
||||
@@ -1057,6 +1061,9 @@ static void md_llbitmap_daemon_fn(struct work_struct *work)
|
||||
bool restart;
|
||||
int idx;
|
||||
|
||||
if (test_bit(BITMAP_SHUTDOWN, &llbitmap->flags))
|
||||
return;
|
||||
|
||||
if (llbitmap->mddev->degraded)
|
||||
return;
|
||||
retry:
|
||||
@@ -1096,7 +1103,7 @@ static void md_llbitmap_daemon_fn(struct work_struct *work)
|
||||
goto retry;
|
||||
|
||||
/* If some page is dirty but not expired, setup timer again */
|
||||
if (restart)
|
||||
if (restart && !test_bit(BITMAP_SHUTDOWN, &llbitmap->flags))
|
||||
mod_timer(&llbitmap->pending_timer,
|
||||
jiffies + llbitmap->mddev->bitmap_info.daemon_sleep * HZ);
|
||||
}
|
||||
@@ -1179,7 +1186,9 @@ static void llbitmap_destroy(struct mddev *mddev)
|
||||
|
||||
mutex_lock(&mddev->bitmap_info.mutex);
|
||||
|
||||
timer_delete_sync(&llbitmap->pending_timer);
|
||||
set_bit(BITMAP_SHUTDOWN, &llbitmap->flags);
|
||||
timer_shutdown_sync(&llbitmap->pending_timer);
|
||||
cancel_work_sync(&llbitmap->daemon_work);
|
||||
flush_workqueue(md_llbitmap_io_wq);
|
||||
flush_workqueue(md_llbitmap_unplug_wq);
|
||||
|
||||
@@ -1523,7 +1532,7 @@ static void llbitmap_update_sb(void *data)
|
||||
|
||||
sb = kmap_local_page(sb_page);
|
||||
sb->events = cpu_to_le64(mddev->events);
|
||||
sb->state = cpu_to_le32(llbitmap->flags);
|
||||
sb->state = cpu_to_le32(llbitmap->flags & ~BIT(BITMAP_SHUTDOWN));
|
||||
sb->chunksize = cpu_to_le32(llbitmap->chunksize);
|
||||
sb->sync_size = cpu_to_le64(mddev->resync_max_sectors);
|
||||
sb->events_cleared = cpu_to_le64(llbitmap->events_cleared);
|
||||
|
||||
Reference in New Issue
Block a user