|
@@ -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);
|