diff options
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r-- | drivers/md/dm-ioctl.c | 65 |
1 files changed, 45 insertions, 20 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index c826b3e1799a..c6ce13b18747 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -48,7 +48,7 @@ struct vers_iter { static struct list_head _name_buckets[NUM_BUCKETS]; static struct list_head _uuid_buckets[NUM_BUCKETS]; -static void dm_hash_remove_all(void); +static void dm_hash_remove_all(int keep_open_devices); /* * Guards access to both hash tables. @@ -73,7 +73,7 @@ static int dm_hash_init(void) static void dm_hash_exit(void) { - dm_hash_remove_all(); + dm_hash_remove_all(0); devfs_remove(DM_DIR); } @@ -260,19 +260,41 @@ static void __hash_remove(struct hash_cell *hc) free_cell(hc); } -static void dm_hash_remove_all(void) +static void dm_hash_remove_all(int keep_open_devices) { - int i; + int i, dev_skipped, dev_removed; struct hash_cell *hc; struct list_head *tmp, *n; down_write(&_hash_lock); + +retry: + dev_skipped = dev_removed = 0; for (i = 0; i < NUM_BUCKETS; i++) { list_for_each_safe (tmp, n, _name_buckets + i) { hc = list_entry(tmp, struct hash_cell, name_list); + + if (keep_open_devices && + dm_lock_for_deletion(hc->md)) { + dev_skipped++; + continue; + } __hash_remove(hc); + dev_removed = 1; } } + + /* + * Some mapped devices may be using other mapped devices, so if any + * still exist, repeat until we make no further progress. + */ + if (dev_skipped) { + if (dev_removed) + goto retry; + + DMWARN("remove_all left %d open device(s)", dev_skipped); + } + up_write(&_hash_lock); } @@ -355,7 +377,7 @@ typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size); static int remove_all(struct dm_ioctl *param, size_t param_size) { - dm_hash_remove_all(); + dm_hash_remove_all(1); param->data_size = 0; return 0; } @@ -535,7 +557,6 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) { struct gendisk *disk = dm_disk(md); struct dm_table *table; - struct block_device *bdev; param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | DM_ACTIVE_PRESENT_FLAG); @@ -545,20 +566,12 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); - if (!(param->flags & DM_SKIP_BDGET_FLAG)) { - bdev = bdget_disk(disk, 0); - if (!bdev) - return -ENXIO; - - /* - * Yes, this will be out of date by the time it gets back - * to userland, but it is still very useful for - * debugging. - */ - param->open_count = bdev->bd_openers; - bdput(bdev); - } else - param->open_count = -1; + /* + * Yes, this will be out of date by the time it gets back + * to userland, but it is still very useful for + * debugging. + */ + param->open_count = dm_open_count(md); if (disk->policy) param->flags |= DM_READONLY_FLAG; @@ -661,6 +674,7 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size) { struct hash_cell *hc; struct mapped_device *md; + int r; down_write(&_hash_lock); hc = __find_device_hash_cell(param); @@ -673,6 +687,17 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size) md = hc->md; + /* + * Ensure the device is not open and nothing further can open it. + */ + r = dm_lock_for_deletion(md); + if (r) { + DMWARN("unable to remove open device %s", hc->name); + up_write(&_hash_lock); + dm_put(md); + return r; + } + __hash_remove(hc); up_write(&_hash_lock); dm_put(md); |