]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
dm: send just one event on resize, not two
authorMikulas Patocka <mpatocka@redhat.com>
Tue, 7 Feb 2023 13:33:06 +0000 (08:33 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 08:34:21 +0000 (09:34 +0100)
commit 7533afa1d27ba1234146d31d2402c195cf195962 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/md/dm-ioctl.c
drivers/md/dm.c
drivers/md/dm.h

index 3bfc1583c20a2d348b3642739b0faf5b58ee8e62..fdb7846a97a40c5958076d8251cc584d58d452df 100644 (file)
@@ -482,7 +482,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
                dm_table_event(table);
        dm_put_live_table(hc->md, srcu_idx);
 
-       if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr))
+       if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr, false))
                param->flags |= DM_UEVENT_GENERATED_FLAG;
 
        md = hc->md;
@@ -995,7 +995,7 @@ static int dev_remove(struct file *filp, struct dm_ioctl *param, size_t param_si
 
        dm_ima_measure_on_device_remove(md, false);
 
-       if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr))
+       if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr, false))
                param->flags |= DM_UEVENT_GENERATED_FLAG;
 
        dm_put(md);
@@ -1129,6 +1129,7 @@ static int do_resume(struct dm_ioctl *param)
        struct hash_cell *hc;
        struct mapped_device *md;
        struct dm_table *new_map, *old_map = NULL;
+       bool need_resize_uevent = false;
 
        down_write(&_hash_lock);
 
@@ -1149,6 +1150,8 @@ static int do_resume(struct dm_ioctl *param)
 
        /* Do we need to load a new map ? */
        if (new_map) {
+               sector_t old_size, new_size;
+
                /* Suspend if it isn't already suspended */
                if (param->flags & DM_SKIP_LOCKFS_FLAG)
                        suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG;
@@ -1157,6 +1160,7 @@ static int do_resume(struct dm_ioctl *param)
                if (!dm_suspended_md(md))
                        dm_suspend(md, suspend_flags);
 
+               old_size = dm_get_size(md);
                old_map = dm_swap_table(md, new_map);
                if (IS_ERR(old_map)) {
                        dm_sync_table(md);
@@ -1164,6 +1168,9 @@ static int do_resume(struct dm_ioctl *param)
                        dm_put(md);
                        return PTR_ERR(old_map);
                }
+               new_size = dm_get_size(md);
+               if (old_size && new_size && old_size != new_size)
+                       need_resize_uevent = true;
 
                if (dm_table_get_mode(new_map) & FMODE_WRITE)
                        set_disk_ro(dm_disk(md), 0);
@@ -1176,7 +1183,7 @@ static int do_resume(struct dm_ioctl *param)
                if (!r) {
                        dm_ima_measure_on_device_resume(md, new_map ? true : false);
 
-                       if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr))
+                       if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr, need_resize_uevent))
                                param->flags |= DM_UEVENT_GENERATED_FLAG;
                }
        }
index 2ce16e6c1e357e1b3cc0cb225f51c643b9681f8f..c9d034c952a4edf5f05f443eafe527b14abc6a5b 100644 (file)
@@ -2184,10 +2184,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);
 
@@ -2980,23 +2977,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);
 
index 5201df03ce402f3710ee45ae02b31dbd6efdfd68..a9a3ffcad084c20c907d8ad8c8c2565166789afe 100644 (file)
@@ -203,7 +203,7 @@ int dm_get_table_device(struct mapped_device *md, dev_t dev, fmode_t mode,
 void dm_put_table_device(struct mapped_device *md, struct dm_dev *d);
 
 int dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
-                     unsigned cookie);
+                     unsigned cookie, bool need_resize_uevent);
 
 void dm_internal_suspend(struct mapped_device *md);
 void dm_internal_resume(struct mapped_device *md);