Skip to content

Commit 9ac239d

Browse files
daandemeyerkawasaki
authored andcommitted
loop: fix partition scan race between udev and loop_reread_partitions()
When LOOP_CONFIGURE is called with LO_FLAGS_PARTSCAN, the following sequence occurs: 1. disk_force_media_change() sets GD_NEED_PART_SCAN 2. Uevent suppression is lifted and a KOBJ_CHANGE uevent is sent 3. loop_global_unlock() releases the lock 4. loop_reread_partitions() calls bdev_disk_changed() to scan There is a race between steps 2 and 4: when udev receives the uevent and opens the device before loop_reread_partitions() runs, blkdev_get_whole() in bdev.c sees GD_NEED_PART_SCAN set and calls bdev_disk_changed() for a first scan. Then loop_reread_partitions() does a second scan. The open_mutex serializes these two scans, but does not prevent both from running. The second scan in bdev_disk_changed() drops all partition devices from the first scan (via blk_drop_partitions()) before re-adding them, causing partition block devices to briefly disappear. This breaks any systemd unit with BindsTo= on the partition device: systemd observes the device going dead, fails the dependent units, and does not retry them when the device reappears. Fix this by removing the GD_NEED_PART_SCAN set from disk_force_media_change() entirely. None of the current callers need the lazy on-open partition scan triggered by this flag: - floppy: sets GENHD_FL_NO_PART, so disk_has_partscan() is always false and GD_NEED_PART_SCAN has no effect. - loop (loop_configure, loop_change_fd): when LO_FLAGS_PARTSCAN is set, loop_reread_partitions() performs an explicit scan. When not set, GD_SUPPRESS_PART_SCAN prevents the lazy scan path. - loop (__loop_clr_fd): calls bdev_disk_changed() explicitly if LO_FLAGS_PARTSCAN is set. - nbd (nbd_clear_sock_ioctl): capacity is set to zero immediately after; nbd manages GD_NEED_PART_SCAN explicitly elsewhere. With GD_NEED_PART_SCAN no longer set by disk_force_media_change(), udev opening the loop device after the uevent no longer triggers a redundant scan in blkdev_get_whole(), and only the single explicit scan from loop_reread_partitions() runs. A regression test for this bug has been submitted to blktests: linux-blktests/blktests#240. Fixes: 9f65c48 ("loop: raise media_change event") Signed-off-by: Daan De Meyer <[email protected]> Acked-by: Christian Brauner <[email protected]>
1 parent 3236861 commit 9ac239d

1 file changed

Lines changed: 2 additions & 1 deletion

File tree

block/disk-events.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,13 +290,14 @@ EXPORT_SYMBOL(disk_check_media_change);
290290
* Should be called when the media changes for @disk. Generates a uevent
291291
* and attempts to free all dentries and inodes and invalidates all block
292292
* device page cache entries in that case.
293+
*
294+
* Callers that need a partition re-scan should arrange for one explicitly.
293295
*/
294296
void disk_force_media_change(struct gendisk *disk)
295297
{
296298
disk_event_uevent(disk, DISK_EVENT_MEDIA_CHANGE);
297299
inc_diskseq(disk);
298300
bdev_mark_dead(disk->part0, true);
299-
set_bit(GD_NEED_PART_SCAN, &disk->state);
300301
}
301302
EXPORT_SYMBOL_GPL(disk_force_media_change);
302303

0 commit comments

Comments
 (0)