aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/dm.c
diff options
context:
space:
mode:
authorAlasdair G Kergon <agk@redhat.com>2006-06-26 03:27:34 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 12:58:36 -0400
commit5c6bd75d06db512515a3781aa97e42df2faf0815 (patch)
tree43d68c6d3174e1ae8a778acd834665c122f8b2a4 /drivers/md/dm.c
parentc2ade42dd35466d90aa6fc7cc717f396e165492f (diff)
[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>
Diffstat (limited to 'drivers/md/dm.c')
-rw-r--r--drivers/md/dm.c32
1 files changed, 31 insertions, 1 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 952c49c3b9aa..6982f86db6dc 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -64,12 +64,14 @@ union map_info *dm_get_mapinfo(struct bio *bio)
64#define DMF_SUSPENDED 1 64#define DMF_SUSPENDED 1
65#define DMF_FROZEN 2 65#define DMF_FROZEN 2
66#define DMF_FREEING 3 66#define DMF_FREEING 3
67#define DMF_DELETING 4
67 68
68struct mapped_device { 69struct mapped_device {
69 struct rw_semaphore io_lock; 70 struct rw_semaphore io_lock;
70 struct semaphore suspend_lock; 71 struct semaphore suspend_lock;
71 rwlock_t map_lock; 72 rwlock_t map_lock;
72 atomic_t holders; 73 atomic_t holders;
74 atomic_t open_count;
73 75
74 unsigned long flags; 76 unsigned long flags;
75 77
@@ -228,12 +230,14 @@ static int dm_blk_open(struct inode *inode, struct file *file)
228 if (!md) 230 if (!md)
229 goto out; 231 goto out;
230 232
231 if (test_bit(DMF_FREEING, &md->flags)) { 233 if (test_bit(DMF_FREEING, &md->flags) ||
234 test_bit(DMF_DELETING, &md->flags)) {
232 md = NULL; 235 md = NULL;
233 goto out; 236 goto out;
234 } 237 }
235 238
236 dm_get(md); 239 dm_get(md);
240 atomic_inc(&md->open_count);
237 241
238out: 242out:
239 spin_unlock(&_minor_lock); 243 spin_unlock(&_minor_lock);
@@ -246,10 +250,35 @@ static int dm_blk_close(struct inode *inode, struct file *file)
246 struct mapped_device *md; 250 struct mapped_device *md;
247 251
248 md = inode->i_bdev->bd_disk->private_data; 252 md = inode->i_bdev->bd_disk->private_data;
253 atomic_dec(&md->open_count);
249 dm_put(md); 254 dm_put(md);
250 return 0; 255 return 0;
251} 256}
252 257
258int dm_open_count(struct mapped_device *md)
259{
260 return atomic_read(&md->open_count);
261}
262
263/*
264 * Guarantees nothing is using the device before it's deleted.
265 */
266int dm_lock_for_deletion(struct mapped_device *md)
267{
268 int r = 0;
269
270 spin_lock(&_minor_lock);
271
272 if (dm_open_count(md))
273 r = -EBUSY;
274 else
275 set_bit(DMF_DELETING, &md->flags);
276
277 spin_unlock(&_minor_lock);
278
279 return r;
280}
281
253static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) 282static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
254{ 283{
255 struct mapped_device *md = bdev->bd_disk->private_data; 284 struct mapped_device *md = bdev->bd_disk->private_data;
@@ -867,6 +896,7 @@ static struct mapped_device *alloc_dev(int minor)
867 init_MUTEX(&md->suspend_lock); 896 init_MUTEX(&md->suspend_lock);
868 rwlock_init(&md->map_lock); 897 rwlock_init(&md->map_lock);
869 atomic_set(&md->holders, 1); 898 atomic_set(&md->holders, 1);
899 atomic_set(&md->open_count, 0);
870 atomic_set(&md->event_nr, 0); 900 atomic_set(&md->event_nr, 0);
871 901
872 md->queue = blk_alloc_queue(GFP_KERNEL); 902 md->queue = blk_alloc_queue(GFP_KERNEL);