Device mapper sends an uevent when the device is suspended, using the function set_capacity_and_notify. However, this causes a race condition with udev. Udev skips scanning dm devices that are suspended. If we send an uevent while we are suspended, udev will be racing with device mapper resume code. If the device mapper resume code wins the race, udev will process the uevent after the device is resumed and it will properly scan the device. However, if udev wins the race, it will receive the uevent, find out that the dm device is suspended and skip scanning the device. This causes bugs such as systemd unmounting the device - see https://bugzilla.redhat.com/show_bug.cgi?id=2158628 This commit fixes this race. We move the code that sends the uevents from __bind to dm_resume, so that they are sent just before the dm-device is resumed. We hold md->suspend_lock while sending the uevent. We change __dev_status so that it grabs md->suspend_lock while querying the suspend state. If udev responds too quickly after the uevent is generated, it will go to __dev_status, call dm_suspended_md_locked and that function will block until md->suspend_lock is released by the resume code - so, udev won't see the device in the suspended state. Signed-off-by: Mikulas Patocka <mpatocka@xxxxxxxxxx> Cc: stable@xxxxxxxxxxxxxxx --- drivers/md/dm-ioctl.c | 6 ++--- drivers/md/dm.c | 44 +++++++++++++++++++++++++----------------- drivers/md/dm.h | 1 include/linux/device-mapper.h | 2 - 4 files changed, 32 insertions(+), 21 deletions(-) Index: linux-2.6/drivers/md/dm.c =================================================================== --- linux-2.6.orig/drivers/md/dm.c 2023-01-20 13:22:38.000000000 +0100 +++ linux-2.6/drivers/md/dm.c 2023-02-06 19:54:32.000000000 +0100 @@ -2159,26 +2159,10 @@ static struct dm_table *__bind(struct ma struct queue_limits *limits) { struct dm_table *old_map; - sector_t size; int ret; lockdep_assert_held(&md->suspend_lock); - size = dm_table_get_size(t); - - /* - * Wipe any geometry if the size of the table changed. - */ - if (size != dm_get_size(md)) - memset(&md->geometry, 0, sizeof(md->geometry)); - - if (!get_capacity(md->disk)) - set_capacity(md->disk, size); - else - set_capacity_and_notify(md->disk, size); - - dm_table_event_callback(t, event_callback, md); - if (dm_table_request_based(t)) { /* * Leverage the fact that request-based DM targets are @@ -2824,7 +2808,7 @@ static int __dm_resume(struct mapped_dev return 0; } -int dm_resume(struct mapped_device *md) +int dm_resume(struct mapped_device *md, bool table_swapped) { int r; struct dm_table *map = NULL; @@ -2849,6 +2833,23 @@ retry: if (!map || !dm_table_get_size(map)) goto out; + if (table_swapped) { + sector_t size = dm_table_get_size(map); + + /* + * Wipe any geometry if the size of the table changed. + */ + if (size != dm_get_size(md)) + memset(&md->geometry, 0, sizeof(md->geometry)); + + if (!get_capacity(md->disk)) + set_capacity(md->disk, size); + else + set_capacity_and_notify(md->disk, size); + + dm_table_event_callback(map, event_callback, md); + } + r = __dm_resume(md, map); if (r) goto out; @@ -3054,6 +3055,15 @@ int dm_suspended_md(struct mapped_device return test_bit(DMF_SUSPENDED, &md->flags); } +int dm_suspended_md_locked(struct mapped_device *md) +{ + int ret; + mutex_lock(&md->suspend_lock); + ret = test_bit(DMF_SUSPENDED, &md->flags); + mutex_unlock(&md->suspend_lock); + return ret; +} + static int dm_post_suspending_md(struct mapped_device *md) { return test_bit(DMF_POST_SUSPENDING, &md->flags); Index: linux-2.6/drivers/md/dm-ioctl.c =================================================================== --- linux-2.6.orig/drivers/md/dm-ioctl.c 2023-02-06 19:36:42.000000000 +0100 +++ linux-2.6/drivers/md/dm-ioctl.c 2023-02-06 19:56:13.000000000 +0100 @@ -806,7 +806,7 @@ static void __dev_status(struct mapped_d param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | DM_ACTIVE_PRESENT_FLAG | DM_INTERNAL_SUSPEND_FLAG); - if (dm_suspended_md(md)) + if (dm_suspended_md_locked(md)) param->flags |= DM_SUSPEND_FLAG; if (dm_suspended_internally_md(md)) @@ -1172,7 +1172,7 @@ static int do_resume(struct dm_ioctl *pa } if (dm_suspended_md(md)) { - r = dm_resume(md); + r = dm_resume(md, new_map != NULL); if (!r) { dm_ima_measure_on_device_resume(md, new_map ? true : false); @@ -2230,7 +2230,7 @@ int __init dm_early_create(struct dm_ioc set_disk_ro(dm_disk(md), !!(dmi->flags & DM_READONLY_FLAG)); /* resume device */ - r = dm_resume(md); + r = dm_resume(md, true); if (r) goto err_destroy_table; Index: linux-2.6/drivers/md/dm.h =================================================================== --- linux-2.6.orig/drivers/md/dm.h 2022-10-03 14:36:40.000000000 +0200 +++ linux-2.6/drivers/md/dm.h 2023-02-06 19:51:12.000000000 +0100 @@ -140,6 +140,7 @@ int dm_deleting_md(struct mapped_device * Is this mapped_device suspended? */ int dm_suspended_md(struct mapped_device *md); +int dm_suspended_md_locked(struct mapped_device *md); /* * Internal suspend and resume methods. Index: linux-2.6/include/linux/device-mapper.h =================================================================== --- linux-2.6.orig/include/linux/device-mapper.h 2023-02-06 19:36:42.000000000 +0100 +++ linux-2.6/include/linux/device-mapper.h 2023-02-06 19:54:59.000000000 +0100 @@ -462,7 +462,7 @@ void *dm_get_mdptr(struct mapped_device * A device can still be used while suspended, but I/O is deferred. */ int dm_suspend(struct mapped_device *md, unsigned suspend_flags); -int dm_resume(struct mapped_device *md); +int dm_resume(struct mapped_device *md, bool table_swapped); /* * Event functions. -- dm-devel mailing list dm-devel@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/dm-devel