4.2.8-ckt7 -stable review patch.  If anyone has any objections, please let me 
know.

---8<------------------------------------------------------------

From: Joe Thornber <[email protected]>

commit d14fcf3dd79c0b8a8d0ba469c44a6b04f3a1403b upstream.

Otherwise operations may be attempted that will only ever go on to crash
(since the metadata device is either missing or unreliable if 'fail_io'
is set).

Signed-off-by: Joe Thornber <[email protected]>
Signed-off-by: Mike Snitzer <[email protected]>
Signed-off-by: Kamal Mostafa <[email protected]>
---
 drivers/md/dm-cache-metadata.c | 98 +++++++++++++++++++++++++-----------------
 drivers/md/dm-cache-metadata.h |  4 +-
 drivers/md/dm-cache-target.c   | 12 +++++-
 3 files changed, 71 insertions(+), 43 deletions(-)

diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c
index 0a17d1b..1ed4ab1 100644
--- a/drivers/md/dm-cache-metadata.c
+++ b/drivers/md/dm-cache-metadata.c
@@ -863,19 +863,40 @@ static int blocks_are_unmapped_or_clean(struct 
dm_cache_metadata *cmd,
        return 0;
 }
 
-#define WRITE_LOCK(cmd) \
-       if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \
+#define WRITE_LOCK(cmd)        \
+       down_write(&cmd->root_lock); \
+       if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
+               up_write(&cmd->root_lock); \
                return -EINVAL; \
-       down_write(&cmd->root_lock)
+       }
 
 #define WRITE_LOCK_VOID(cmd) \
-       if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) \
+       down_write(&cmd->root_lock); \
+       if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
+               up_write(&cmd->root_lock); \
                return; \
-       down_write(&cmd->root_lock)
+       }
 
 #define WRITE_UNLOCK(cmd) \
        up_write(&cmd->root_lock)
 
+#define READ_LOCK(cmd) \
+       down_read(&cmd->root_lock); \
+       if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
+               up_read(&cmd->root_lock); \
+               return -EINVAL; \
+       }
+
+#define READ_LOCK_VOID(cmd)    \
+       down_read(&cmd->root_lock); \
+       if (cmd->fail_io || dm_bm_is_read_only(cmd->bm)) { \
+               up_read(&cmd->root_lock); \
+               return; \
+       }
+
+#define READ_UNLOCK(cmd) \
+       up_read(&cmd->root_lock)
+
 int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size)
 {
        int r;
@@ -1011,22 +1032,20 @@ int dm_cache_load_discards(struct dm_cache_metadata 
*cmd,
 {
        int r;
 
-       down_read(&cmd->root_lock);
+       READ_LOCK(cmd);
        r = __load_discards(cmd, fn, context);
-       up_read(&cmd->root_lock);
+       READ_UNLOCK(cmd);
 
        return r;
 }
 
-dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd)
+int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result)
 {
-       dm_cblock_t r;
+       READ_LOCK(cmd);
+       *result = cmd->cache_blocks;
+       READ_UNLOCK(cmd);
 
-       down_read(&cmd->root_lock);
-       r = cmd->cache_blocks;
-       up_read(&cmd->root_lock);
-
-       return r;
+       return 0;
 }
 
 static int __remove(struct dm_cache_metadata *cmd, dm_cblock_t cblock)
@@ -1184,9 +1203,9 @@ int dm_cache_load_mappings(struct dm_cache_metadata *cmd,
 {
        int r;
 
-       down_read(&cmd->root_lock);
+       READ_LOCK(cmd);
        r = __load_mappings(cmd, policy, fn, context);
-       up_read(&cmd->root_lock);
+       READ_UNLOCK(cmd);
 
        return r;
 }
@@ -1211,18 +1230,18 @@ static int __dump_mappings(struct dm_cache_metadata 
*cmd)
 
 void dm_cache_dump(struct dm_cache_metadata *cmd)
 {
-       down_read(&cmd->root_lock);
+       READ_LOCK_VOID(cmd);
        __dump_mappings(cmd);
-       up_read(&cmd->root_lock);
+       READ_UNLOCK(cmd);
 }
 
 int dm_cache_changed_this_transaction(struct dm_cache_metadata *cmd)
 {
        int r;
 
-       down_read(&cmd->root_lock);
+       READ_LOCK(cmd);
        r = cmd->changed;
-       up_read(&cmd->root_lock);
+       READ_UNLOCK(cmd);
 
        return r;
 }
@@ -1272,9 +1291,9 @@ int dm_cache_set_dirty(struct dm_cache_metadata *cmd,
 void dm_cache_metadata_get_stats(struct dm_cache_metadata *cmd,
                                 struct dm_cache_statistics *stats)
 {
-       down_read(&cmd->root_lock);
+       READ_LOCK_VOID(cmd);
        *stats = cmd->stats;
-       up_read(&cmd->root_lock);
+       READ_UNLOCK(cmd);
 }
 
 void dm_cache_metadata_set_stats(struct dm_cache_metadata *cmd,
@@ -1308,9 +1327,9 @@ int dm_cache_get_free_metadata_block_count(struct 
dm_cache_metadata *cmd,
 {
        int r = -EINVAL;
 
-       down_read(&cmd->root_lock);
+       READ_LOCK(cmd);
        r = dm_sm_get_nr_free(cmd->metadata_sm, result);
-       up_read(&cmd->root_lock);
+       READ_UNLOCK(cmd);
 
        return r;
 }
@@ -1320,9 +1339,9 @@ int dm_cache_get_metadata_dev_size(struct 
dm_cache_metadata *cmd,
 {
        int r = -EINVAL;
 
-       down_read(&cmd->root_lock);
+       READ_LOCK(cmd);
        r = dm_sm_get_nr_blocks(cmd->metadata_sm, result);
-       up_read(&cmd->root_lock);
+       READ_UNLOCK(cmd);
 
        return r;
 }
@@ -1413,7 +1432,13 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, 
struct dm_cache_policy *
 
 int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result)
 {
-       return blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result);
+       int r;
+
+       READ_LOCK(cmd);
+       r = blocks_are_unmapped_or_clean(cmd, 0, cmd->cache_blocks, result);
+       READ_UNLOCK(cmd);
+
+       return r;
 }
 
 void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd)
@@ -1436,10 +1461,7 @@ int dm_cache_metadata_set_needs_check(struct 
dm_cache_metadata *cmd)
        struct dm_block *sblock;
        struct cache_disk_superblock *disk_super;
 
-       /*
-        * We ignore fail_io for this function.
-        */
-       down_write(&cmd->root_lock);
+       WRITE_LOCK(cmd);
        set_bit(NEEDS_CHECK, &cmd->flags);
 
        r = superblock_lock(cmd, &sblock);
@@ -1454,19 +1476,17 @@ int dm_cache_metadata_set_needs_check(struct 
dm_cache_metadata *cmd)
        dm_bm_unlock(sblock);
 
 out:
-       up_write(&cmd->root_lock);
+       WRITE_UNLOCK(cmd);
        return r;
 }
 
-bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd)
+int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result)
 {
-       bool needs_check;
+       READ_LOCK(cmd);
+       *result = !!test_bit(NEEDS_CHECK, &cmd->flags);
+       READ_UNLOCK(cmd);
 
-       down_read(&cmd->root_lock);
-       needs_check = !!test_bit(NEEDS_CHECK, &cmd->flags);
-       up_read(&cmd->root_lock);
-
-       return needs_check;
+       return 0;
 }
 
 int dm_cache_metadata_abort(struct dm_cache_metadata *cmd)
diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h
index 2ffee21..8528744 100644
--- a/drivers/md/dm-cache-metadata.h
+++ b/drivers/md/dm-cache-metadata.h
@@ -66,7 +66,7 @@ void dm_cache_metadata_close(struct dm_cache_metadata *cmd);
  * origin blocks to map to.
  */
 int dm_cache_resize(struct dm_cache_metadata *cmd, dm_cblock_t new_cache_size);
-dm_cblock_t dm_cache_size(struct dm_cache_metadata *cmd);
+int dm_cache_size(struct dm_cache_metadata *cmd, dm_cblock_t *result);
 
 int dm_cache_discard_bitset_resize(struct dm_cache_metadata *cmd,
                                   sector_t discard_block_size,
@@ -137,7 +137,7 @@ int dm_cache_write_hints(struct dm_cache_metadata *cmd, 
struct dm_cache_policy *
  */
 int dm_cache_metadata_all_clean(struct dm_cache_metadata *cmd, bool *result);
 
-bool dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd);
+int dm_cache_metadata_needs_check(struct dm_cache_metadata *cmd, bool *result);
 int dm_cache_metadata_set_needs_check(struct dm_cache_metadata *cmd);
 void dm_cache_metadata_set_read_only(struct dm_cache_metadata *cmd);
 void dm_cache_metadata_set_read_write(struct dm_cache_metadata *cmd);
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 9d0672b..6ad5ddf 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -988,9 +988,14 @@ static void notify_mode_switch(struct cache *cache, enum 
cache_metadata_mode mod
 
 static void set_cache_mode(struct cache *cache, enum cache_metadata_mode 
new_mode)
 {
-       bool needs_check = dm_cache_metadata_needs_check(cache->cmd);
+       bool needs_check;
        enum cache_metadata_mode old_mode = get_cache_mode(cache);
 
+       if (dm_cache_metadata_needs_check(cache->cmd, &needs_check)) {
+               DMERR("unable to read needs_check flag, setting failure mode");
+               new_mode = CM_FAIL;
+       }
+
        if (new_mode == CM_WRITE && needs_check) {
                DMERR("%s: unable to switch cache to write mode until 
repaired.",
                      cache_device_name(cache));
@@ -3517,6 +3522,7 @@ static void cache_status(struct dm_target *ti, 
status_type_t type,
        char buf[BDEVNAME_SIZE];
        struct cache *cache = ti->private;
        dm_cblock_t residency;
+       bool needs_check;
 
        switch (type) {
        case STATUSTYPE_INFO:
@@ -3590,7 +3596,9 @@ static void cache_status(struct dm_target *ti, 
status_type_t type,
                else
                        DMEMIT("rw ");
 
-               if (dm_cache_metadata_needs_check(cache->cmd))
+               r = dm_cache_metadata_needs_check(cache->cmd, &needs_check);
+
+               if (r || needs_check)
                        DMEMIT("needs_check ");
                else
                        DMEMIT("- ");
-- 
2.7.4

Reply via email to