Message ID | 20210711175415.80173-6-mcroce@linux.microsoft.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | block: add a sequence number to disks | expand |
So when I said plug into the disk events I did not mean that you need to use all the polling stuff - we can just do the parts required which are the uevents and invalidation with a new little helper like this (needs the actual prototype and kerneldoc of course): diff --git a/block/disk-events.c b/block/disk-events.c index a75931ff5da4..0023ab1559b6 100644 --- a/block/disk-events.c +++ b/block/disk-events.c @@ -163,15 +163,31 @@ void disk_flush_events(struct gendisk *disk, unsigned int mask) spin_unlock_irq(&ev->lock); } +/* + * Tell userland about new events. Only the events listed in @disk->events are + * reported, and only if DISK_EVENT_FLAG_UEVENT is set. Otherwise, events are + * processed internally but never get reported to userland. + */ +static void disk_event_uevent(struct gendisk *disk, unsigned int events) +{ + char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; + int nr_events = 0, i; + + for (i = 0; i < ARRAY_SIZE(disk_uevents); i++) + if (events & disk->events & (1 << i)) + envp[nr_events++] = disk_uevents[i]; + + if (nr_events) + kobject_uevent_env(&disk_to_dev(disk)->kobj, KOBJ_CHANGE, envp); +} + static void disk_check_events(struct disk_events *ev, unsigned int *clearing_ptr) { struct gendisk *disk = ev->disk; - char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; unsigned int clearing = *clearing_ptr; unsigned int events; unsigned long intv; - int nr_events = 0, i; /* check events */ events = disk->fops->check_events(disk, clearing); @@ -190,19 +206,8 @@ static void disk_check_events(struct disk_events *ev, spin_unlock_irq(&ev->lock); - /* - * Tell userland about new events. Only the events listed in - * @disk->events are reported, and only if DISK_EVENT_FLAG_UEVENT - * is set. Otherwise, events are processed internally but never - * get reported to userland. - */ - for (i = 0; i < ARRAY_SIZE(disk_uevents); i++) - if ((events & disk->events & (1 << i)) && - (disk->event_flags & DISK_EVENT_FLAG_UEVENT)) - envp[nr_events++] = disk_uevents[i]; - - if (nr_events) - kobject_uevent_env(&disk_to_dev(disk)->kobj, KOBJ_CHANGE, envp); + if (disk->event_flags & DISK_EVENT_FLAG_UEVENT) + disk_event_uevent(disk, events); } /** @@ -281,6 +286,21 @@ bool bdev_check_media_change(struct block_device *bdev) } EXPORT_SYMBOL(bdev_check_media_change); +bool disk_force_media_change(struct gendisk *disk, unsigned int events) +{ + disk_event_uevent(disk, events); + + if (!(events & DISK_EVENT_MEDIA_CHANGE)) + return false; + + if (__invalidate_device(disk->part0, true)) + pr_warn("VFS: busy inodes on changed media %s\n", + disk->disk_name); + set_bit(GD_NEED_PART_SCAN, &disk->state); + return true; +} +EXPORT_SYMBOL_GPL(disk_force_media_change); + /* * Separate this part out so that a different pointer for clearing_ptr can be * passed in for disk_clear_events.
diff --git a/drivers/block/loop.c b/drivers/block/loop.c index f37b9e3d833c..c632f9bd33ba 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -731,6 +731,8 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, goto out_err; /* and ... switch */ + lo->changed = true; + bdev_check_media_change(bdev); blk_mq_freeze_queue(lo->lo_queue); mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); lo->lo_backing_file = file; @@ -1205,6 +1207,9 @@ static int loop_configure(struct loop_device *lo, fmode_t mode, goto out_unlock; } + lo->changed = true; + bdev_check_media_change(bdev); + set_disk_ro(lo->lo_disk, (lo->lo_flags & LO_FLAGS_READ_ONLY) != 0); INIT_WORK(&lo->rootcg_work, loop_rootcg_workfn); @@ -1349,6 +1354,8 @@ static int __loop_clr_fd(struct loop_device *lo, bool release) partscan = lo->lo_flags & LO_FLAGS_PARTSCAN && bdev; lo_number = lo->lo_number; + lo->changed = true; + bdev_check_media_change(bdev); out_unlock: mutex_unlock(&lo->lo_mutex); if (partscan) { @@ -2016,11 +2023,22 @@ static void lo_release(struct gendisk *disk, fmode_t mode) mutex_unlock(&lo->lo_mutex); } +static unsigned int lo_check_events(struct gendisk *disk, unsigned int clearing) +{ + struct loop_device *lo = disk->private_data; + bool changed = lo->changed; + + lo->changed = false; + + return changed ? DISK_EVENT_MEDIA_CHANGE : 0; +} + static const struct block_device_operations lo_fops = { .owner = THIS_MODULE, .open = lo_open, .release = lo_release, .ioctl = lo_ioctl, + .check_events = lo_check_events, #ifdef CONFIG_COMPAT .compat_ioctl = lo_compat_ioctl, #endif @@ -2325,6 +2343,8 @@ static int loop_add(int i) disk->fops = &lo_fops; disk->private_data = lo; disk->queue = lo->lo_queue; + disk->events = DISK_EVENT_MEDIA_CHANGE; + disk->event_flags = DISK_EVENT_FLAG_UEVENT; sprintf(disk->disk_name, "loop%d", i); add_disk(disk); mutex_unlock(&loop_ctl_mutex); diff --git a/drivers/block/loop.h b/drivers/block/loop.h index 1988899db63a..a2fdfd27e6a7 100644 --- a/drivers/block/loop.h +++ b/drivers/block/loop.h @@ -63,6 +63,7 @@ struct loop_device { struct timer_list timer; bool use_dio; bool sysfs_inited; + bool changed; struct request_queue *lo_queue; struct blk_mq_tag_set tag_set;