dm: allow remove to be deferred
This patch allows the removal of an open device to be deferred until it is closed. (Previously such a removal attempt would fail.) The deferred remove functionality is enabled by setting the flag DM_DEFERRED_REMOVE in the ioctl structure on DM_DEV_REMOVE or DM_REMOVE_ALL ioctl. On return from DM_DEV_REMOVE, the flag DM_DEFERRED_REMOVE indicates if the device was removed immediately or flagged to be removed on close - if the flag is clear, the device was removed. On return from DM_DEV_STATUS and other ioctls, the flag DM_DEFERRED_REMOVE is set if the device is scheduled to be removed on closure. A device that is scheduled to be deleted can be revived using the message "@cancel_deferred_remove". This message clears the DMF_DEFERRED_REMOVE flag so that the device won't be deleted on close. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:

committed by
Mike Snitzer

parent
7833b08e18
commit
2c140a246d
@@ -49,6 +49,11 @@ static unsigned int _major = 0;
|
||||
static DEFINE_IDR(_minor_idr);
|
||||
|
||||
static DEFINE_SPINLOCK(_minor_lock);
|
||||
|
||||
static void do_deferred_remove(struct work_struct *w);
|
||||
|
||||
static DECLARE_WORK(deferred_remove_work, do_deferred_remove);
|
||||
|
||||
/*
|
||||
* For bio-based dm.
|
||||
* One of these is allocated per bio.
|
||||
@@ -116,6 +121,7 @@ EXPORT_SYMBOL_GPL(dm_get_rq_mapinfo);
|
||||
#define DMF_DELETING 4
|
||||
#define DMF_NOFLUSH_SUSPENDING 5
|
||||
#define DMF_MERGE_IS_OPTIONAL 6
|
||||
#define DMF_DEFERRED_REMOVE 7
|
||||
|
||||
/*
|
||||
* A dummy definition to make RCU happy.
|
||||
@@ -299,6 +305,8 @@ out_free_io_cache:
|
||||
|
||||
static void local_exit(void)
|
||||
{
|
||||
flush_scheduled_work();
|
||||
|
||||
kmem_cache_destroy(_rq_tio_cache);
|
||||
kmem_cache_destroy(_io_cache);
|
||||
unregister_blkdev(_major, _name);
|
||||
@@ -404,7 +412,10 @@ static void dm_blk_close(struct gendisk *disk, fmode_t mode)
|
||||
|
||||
spin_lock(&_minor_lock);
|
||||
|
||||
atomic_dec(&md->open_count);
|
||||
if (atomic_dec_and_test(&md->open_count) &&
|
||||
(test_bit(DMF_DEFERRED_REMOVE, &md->flags)))
|
||||
schedule_work(&deferred_remove_work);
|
||||
|
||||
dm_put(md);
|
||||
|
||||
spin_unlock(&_minor_lock);
|
||||
@@ -418,14 +429,18 @@ int dm_open_count(struct mapped_device *md)
|
||||
/*
|
||||
* Guarantees nothing is using the device before it's deleted.
|
||||
*/
|
||||
int dm_lock_for_deletion(struct mapped_device *md)
|
||||
int dm_lock_for_deletion(struct mapped_device *md, bool mark_deferred, bool only_deferred)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
spin_lock(&_minor_lock);
|
||||
|
||||
if (dm_open_count(md))
|
||||
if (dm_open_count(md)) {
|
||||
r = -EBUSY;
|
||||
if (mark_deferred)
|
||||
set_bit(DMF_DEFERRED_REMOVE, &md->flags);
|
||||
} else if (only_deferred && !test_bit(DMF_DEFERRED_REMOVE, &md->flags))
|
||||
r = -EEXIST;
|
||||
else
|
||||
set_bit(DMF_DELETING, &md->flags);
|
||||
|
||||
@@ -434,6 +449,27 @@ int dm_lock_for_deletion(struct mapped_device *md)
|
||||
return r;
|
||||
}
|
||||
|
||||
int dm_cancel_deferred_remove(struct mapped_device *md)
|
||||
{
|
||||
int r = 0;
|
||||
|
||||
spin_lock(&_minor_lock);
|
||||
|
||||
if (test_bit(DMF_DELETING, &md->flags))
|
||||
r = -EBUSY;
|
||||
else
|
||||
clear_bit(DMF_DEFERRED_REMOVE, &md->flags);
|
||||
|
||||
spin_unlock(&_minor_lock);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void do_deferred_remove(struct work_struct *w)
|
||||
{
|
||||
dm_deferred_remove();
|
||||
}
|
||||
|
||||
sector_t dm_get_size(struct mapped_device *md)
|
||||
{
|
||||
return get_capacity(md->disk);
|
||||
@@ -2894,6 +2930,11 @@ int dm_suspended_md(struct mapped_device *md)
|
||||
return test_bit(DMF_SUSPENDED, &md->flags);
|
||||
}
|
||||
|
||||
int dm_test_deferred_remove_flag(struct mapped_device *md)
|
||||
{
|
||||
return test_bit(DMF_DEFERRED_REMOVE, &md->flags);
|
||||
}
|
||||
|
||||
int dm_suspended(struct dm_target *ti)
|
||||
{
|
||||
return dm_suspended_md(dm_table_get_md(ti->table));
|
||||
|
Reference in New Issue
Block a user