[dm-devel] [PATCH 2/6] dm ioctl: interlock resume and table clear
Mike Snitzer
snitzer at redhat.com
Sun May 23 21:44:57 UTC 2010
When resuming an inactive table there is a lack of testable state during
its transition to being live (hc->new_map is NULL and md->map isn't
set). Interlock allows table_clear() to _know_ there isn't a live table
yet.
This interlock also has the side-effect of serializing N resumes to the
_same_ mapped_device (more coarsely than the suspend_lock provides).
Signed-off-by: Mike Snitzer <snitzer at redhat.com>
---
drivers/md/dm-ioctl.c | 34 +++++++++++++++++++++++++++++++---
drivers/md/dm.c | 17 +++++++++++++++++
drivers/md/dm.h | 3 +++
3 files changed, 51 insertions(+), 3 deletions(-)
Index: linux-2.6/drivers/md/dm-ioctl.c
===================================================================
--- linux-2.6.orig/drivers/md/dm-ioctl.c
+++ linux-2.6/drivers/md/dm-ioctl.c
@@ -871,6 +871,17 @@ static int do_resume(struct dm_ioctl *pa
}
md = hc->md;
+ up_write(&_hash_lock);
+
+ dm_lock_resume(md);
+
+ down_write(&_hash_lock);
+ if (!hc || hc->md != md) {
+ DMWARN("device has been removed from the dev hash table.");
+ up_write(&_hash_lock);
+ r = -ENXIO;
+ goto out;
+ }
new_map = hc->new_map;
hc->new_map = NULL;
@@ -891,8 +902,8 @@ static int do_resume(struct dm_ioctl *pa
old_map = dm_swap_table(md, new_map);
if (IS_ERR(old_map)) {
dm_table_destroy(new_map);
- dm_put(md);
- return PTR_ERR(old_map);
+ r = PTR_ERR(old_map);
+ goto out;
}
if (dm_table_get_mode(new_map) & FMODE_WRITE)
@@ -913,6 +924,8 @@ static int do_resume(struct dm_ioctl *pa
if (!r)
r = __dev_status(md, param);
+out:
+ dm_unlock_resume(md);
dm_put(md);
return r;
}
@@ -1209,6 +1222,20 @@ static int table_clear(struct dm_ioctl *
return -ENXIO;
}
+ md = hc->md;
+ up_write(&_hash_lock);
+
+ dm_lock_resume(md);
+
+ down_write(&_hash_lock);
+ hc = dm_get_mdptr(md);
+ if (!hc || hc->md != md) {
+ DMWARN("device has been removed from the dev hash table.");
+ up_write(&_hash_lock);
+ r = -ENXIO;
+ goto out;
+ }
+
if (hc->new_map) {
dm_table_destroy(hc->new_map);
hc->new_map = NULL;
@@ -1217,8 +1244,9 @@ static int table_clear(struct dm_ioctl *
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
r = __dev_status(hc->md, param);
- md = hc->md;
up_write(&_hash_lock);
+out:
+ dm_unlock_resume(md);
dm_put(md);
return r;
}
Index: linux-2.6/drivers/md/dm.c
===================================================================
--- linux-2.6.orig/drivers/md/dm.c
+++ linux-2.6/drivers/md/dm.c
@@ -116,6 +116,12 @@ EXPORT_SYMBOL_GPL(dm_get_rq_mapinfo);
struct mapped_device {
struct rw_semaphore io_lock;
struct mutex suspend_lock;
+ /*
+ * Resuming inactive table lacks testable state during its
+ * transition to being live. Interlock allows other operations
+ * (e.g. table_clear) to _know_ there isn't a live table yet.
+ */
+ struct mutex resume_lock;
rwlock_t map_lock;
atomic_t holders;
atomic_t open_count;
@@ -1876,6 +1882,7 @@ static struct mapped_device *alloc_dev(i
init_rwsem(&md->io_lock);
mutex_init(&md->suspend_lock);
+ mutex_init(&md->resume_lock);
spin_lock_init(&md->deferred_lock);
spin_lock_init(&md->barrier_error_lock);
rwlock_init(&md->map_lock);
@@ -1997,6 +2004,16 @@ static void free_dev(struct mapped_devic
kfree(md);
}
+void dm_lock_resume(struct mapped_device *md)
+{
+ mutex_lock(&md->resume_lock);
+}
+
+void dm_unlock_resume(struct mapped_device *md)
+{
+ mutex_unlock(&md->resume_lock);
+}
+
static void __bind_mempools(struct mapped_device *md, struct dm_table *t)
{
struct dm_md_mempools *p;
Index: linux-2.6/drivers/md/dm.h
===================================================================
--- linux-2.6.orig/drivers/md/dm.h
+++ linux-2.6/drivers/md/dm.h
@@ -66,6 +66,9 @@ int dm_table_alloc_md_mempools(struct dm
void dm_table_free_md_mempools(struct dm_table *t);
struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
+void dm_lock_resume(struct mapped_device *md);
+void dm_unlock_resume(struct mapped_device *md);
+
/*
* To check the return value from dm_table_find_target().
*/
More information about the dm-devel
mailing list