Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] dm: prevent removal if open

If you misuse the device-mapper interface (or there's a bug in your userspace
tools) it's possible to end up with 'unlinked' mapped devices that cannot be
removed until you reboot (along with uninterruptible processes).

This patch prevents you from removing a device that is still open.

It introduces dm_lock_for_deletion() which is called when a device is about to
be removed to ensure that nothing has it open and nothing further can open it.
It uses a private open_count for this which also lets us remove one of the
problematic bdget_disk() calls elsewhere.

Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

Alasdair G Kergon and committed by
Linus Torvalds
5c6bd75d c2ade42d

+81 -24
+45 -20
drivers/md/dm-ioctl.c
··· 48 48 static struct list_head _name_buckets[NUM_BUCKETS]; 49 49 static struct list_head _uuid_buckets[NUM_BUCKETS]; 50 50 51 - static void dm_hash_remove_all(void); 51 + static void dm_hash_remove_all(int keep_open_devices); 52 52 53 53 /* 54 54 * Guards access to both hash tables. ··· 73 73 74 74 static void dm_hash_exit(void) 75 75 { 76 - dm_hash_remove_all(); 76 + dm_hash_remove_all(0); 77 77 devfs_remove(DM_DIR); 78 78 } 79 79 ··· 260 260 free_cell(hc); 261 261 } 262 262 263 - static void dm_hash_remove_all(void) 263 + static void dm_hash_remove_all(int keep_open_devices) 264 264 { 265 - int i; 265 + int i, dev_skipped, dev_removed; 266 266 struct hash_cell *hc; 267 267 struct list_head *tmp, *n; 268 268 269 269 down_write(&_hash_lock); 270 + 271 + retry: 272 + dev_skipped = dev_removed = 0; 270 273 for (i = 0; i < NUM_BUCKETS; i++) { 271 274 list_for_each_safe (tmp, n, _name_buckets + i) { 272 275 hc = list_entry(tmp, struct hash_cell, name_list); 276 + 277 + if (keep_open_devices && 278 + dm_lock_for_deletion(hc->md)) { 279 + dev_skipped++; 280 + continue; 281 + } 273 282 __hash_remove(hc); 283 + dev_removed = 1; 274 284 } 275 285 } 286 + 287 + /* 288 + * Some mapped devices may be using other mapped devices, so if any 289 + * still exist, repeat until we make no further progress. 290 + */ 291 + if (dev_skipped) { 292 + if (dev_removed) 293 + goto retry; 294 + 295 + DMWARN("remove_all left %d open device(s)", dev_skipped); 296 + } 297 + 276 298 up_write(&_hash_lock); 277 299 } 278 300 ··· 377 355 378 356 static int remove_all(struct dm_ioctl *param, size_t param_size) 379 357 { 380 - dm_hash_remove_all(); 358 + dm_hash_remove_all(1); 381 359 param->data_size = 0; 382 360 return 0; 383 361 } ··· 557 535 { 558 536 struct gendisk *disk = dm_disk(md); 559 537 struct dm_table *table; 560 - struct block_device *bdev; 561 538 562 539 param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | 563 540 DM_ACTIVE_PRESENT_FLAG); ··· 566 545 567 546 param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); 568 547 569 - if (!(param->flags & DM_SKIP_BDGET_FLAG)) { 570 - bdev = bdget_disk(disk, 0); 571 - if (!bdev) 572 - return -ENXIO; 573 - 574 - /* 575 - * Yes, this will be out of date by the time it gets back 576 - * to userland, but it is still very useful for 577 - * debugging. 578 - */ 579 - param->open_count = bdev->bd_openers; 580 - bdput(bdev); 581 - } else 582 - param->open_count = -1; 548 + /* 549 + * Yes, this will be out of date by the time it gets back 550 + * to userland, but it is still very useful for 551 + * debugging. 552 + */ 553 + param->open_count = dm_open_count(md); 583 554 584 555 if (disk->policy) 585 556 param->flags |= DM_READONLY_FLAG; ··· 674 661 { 675 662 struct hash_cell *hc; 676 663 struct mapped_device *md; 664 + int r; 677 665 678 666 down_write(&_hash_lock); 679 667 hc = __find_device_hash_cell(param); ··· 686 672 } 687 673 688 674 md = hc->md; 675 + 676 + /* 677 + * Ensure the device is not open and nothing further can open it. 678 + */ 679 + r = dm_lock_for_deletion(md); 680 + if (r) { 681 + DMWARN("unable to remove open device %s", hc->name); 682 + up_write(&_hash_lock); 683 + dm_put(md); 684 + return r; 685 + } 689 686 690 687 __hash_remove(hc); 691 688 up_write(&_hash_lock);
+31 -1
drivers/md/dm.c
··· 64 64 #define DMF_SUSPENDED 1 65 65 #define DMF_FROZEN 2 66 66 #define DMF_FREEING 3 67 + #define DMF_DELETING 4 67 68 68 69 struct mapped_device { 69 70 struct rw_semaphore io_lock; 70 71 struct semaphore suspend_lock; 71 72 rwlock_t map_lock; 72 73 atomic_t holders; 74 + atomic_t open_count; 73 75 74 76 unsigned long flags; 75 77 ··· 230 228 if (!md) 231 229 goto out; 232 230 233 - if (test_bit(DMF_FREEING, &md->flags)) { 231 + if (test_bit(DMF_FREEING, &md->flags) || 232 + test_bit(DMF_DELETING, &md->flags)) { 234 233 md = NULL; 235 234 goto out; 236 235 } 237 236 238 237 dm_get(md); 238 + atomic_inc(&md->open_count); 239 239 240 240 out: 241 241 spin_unlock(&_minor_lock); ··· 250 246 struct mapped_device *md; 251 247 252 248 md = inode->i_bdev->bd_disk->private_data; 249 + atomic_dec(&md->open_count); 253 250 dm_put(md); 254 251 return 0; 252 + } 253 + 254 + int dm_open_count(struct mapped_device *md) 255 + { 256 + return atomic_read(&md->open_count); 257 + } 258 + 259 + /* 260 + * Guarantees nothing is using the device before it's deleted. 261 + */ 262 + int dm_lock_for_deletion(struct mapped_device *md) 263 + { 264 + int r = 0; 265 + 266 + spin_lock(&_minor_lock); 267 + 268 + if (dm_open_count(md)) 269 + r = -EBUSY; 270 + else 271 + set_bit(DMF_DELETING, &md->flags); 272 + 273 + spin_unlock(&_minor_lock); 274 + 275 + return r; 255 276 } 256 277 257 278 static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) ··· 896 867 init_MUTEX(&md->suspend_lock); 897 868 rwlock_init(&md->map_lock); 898 869 atomic_set(&md->holders, 1); 870 + atomic_set(&md->open_count, 0); 899 871 atomic_set(&md->event_nr, 0); 900 872 901 873 md->queue = blk_alloc_queue(GFP_KERNEL);
+2
drivers/md/dm.h
··· 123 123 124 124 void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); 125 125 union map_info *dm_get_mapinfo(struct bio *bio); 126 + int dm_open_count(struct mapped_device *md); 127 + int dm_lock_for_deletion(struct mapped_device *md); 126 128 127 129 #endif
+3 -3
include/linux/dm-ioctl.h
··· 285 285 #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) 286 286 287 287 #define DM_VERSION_MAJOR 4 288 - #define DM_VERSION_MINOR 6 288 + #define DM_VERSION_MINOR 7 289 289 #define DM_VERSION_PATCHLEVEL 0 290 - #define DM_VERSION_EXTRA "-ioctl (2006-02-17)" 290 + #define DM_VERSION_EXTRA "-ioctl (2006-06-24)" 291 291 292 292 /* Status bits */ 293 293 #define DM_READONLY_FLAG (1 << 0) /* In/Out */ ··· 314 314 #define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ 315 315 316 316 /* 317 - * Set this to improve performance when you aren't going to use open_count. 317 + * This flag is now ignored. 318 318 */ 319 319 #define DM_SKIP_BDGET_FLAG (1 << 9) /* In */ 320 320