diff options
author | 2023-02-07 08:33:06 -0500 | |
---|---|---|
committer | 2023-02-14 14:22:27 -0500 | |
commit | 7533afa1d27ba1234146d31d2402c195cf195962 (patch) | |
tree | 64490afdc31e9b2a2787f9c836de1a50e8ee5e12 /drivers/md/dm.c | |
parent | dm table: check that a dm device doesn't reference itself (diff) | |
download | linux-7533afa1d27ba1234146d31d2402c195cf195962.tar.gz linux-7533afa1d27ba1234146d31d2402c195cf195962.tar.bz2 linux-7533afa1d27ba1234146d31d2402c195cf195962.zip |
dm: send just one event on resize, not two
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 replace the function set_capacity_and_notify with set_capacity, so that
the uevent is not sent at this point. In do_resume, we detect if the
capacity has changed and we pass a boolean variable need_resize_uevent to
dm_kobject_uevent. dm_kobject_uevent adds "RESIZE=1" to the uevent if
need_resize_uevent is set.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Tested-by: Peter Rajnoha <prajnoha@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
Diffstat (limited to 'drivers/md/dm.c')
-rw-r--r-- | drivers/md/dm.c | 27 |
1 files changed, 13 insertions, 14 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index b424a6ee27ba..c391a618fb71 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -2172,10 +2172,7 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t, 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); + set_capacity(md->disk, size); dm_table_event_callback(t, event_callback, md); @@ -2968,23 +2965,25 @@ EXPORT_SYMBOL_GPL(dm_internal_resume_fast); * Event notification. *---------------------------------------------------------------*/ int dm_kobject_uevent(struct mapped_device *md, enum kobject_action action, - unsigned cookie) + unsigned cookie, bool need_resize_uevent) { int r; unsigned noio_flag; char udev_cookie[DM_COOKIE_LENGTH]; - char *envp[] = { udev_cookie, NULL }; - - noio_flag = memalloc_noio_save(); - - if (!cookie) - r = kobject_uevent(&disk_to_dev(md->disk)->kobj, action); - else { + char *envp[3] = { NULL, NULL, NULL }; + char **envpp = envp; + if (cookie) { snprintf(udev_cookie, DM_COOKIE_LENGTH, "%s=%u", DM_COOKIE_ENV_VAR_NAME, cookie); - r = kobject_uevent_env(&disk_to_dev(md->disk)->kobj, - action, envp); + *envpp++ = udev_cookie; } + if (need_resize_uevent) { + *envpp++ = "RESIZE=1"; + } + + noio_flag = memalloc_noio_save(); + + r = kobject_uevent_env(&disk_to_dev(md->disk)->kobj, action, envp); memalloc_noio_restore(noio_flag); |