diff options
author | Joe Thornber <ejt@redhat.com> | 2014-04-03 11:16:44 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2014-04-04 14:53:05 -0400 |
commit | 0596661f0a16d9d69bf1033320e70b6ff52b5e81 (patch) | |
tree | 5f2393ddece9f82f9fa7853c7aa6f9d9dbffddd1 | |
parent | 67324ea18812bc952ef96892fbd5817b9050413f (diff) |
dm cache: fix a lock-inversion
When suspending a cache the policy is walked and the individual policy
hints written to the metadata via sync_metadata(). This led to this
lock order:
policy->lock
cache_metadata->root_lock
When loading the cache target the policy is populated while the metadata
lock is held:
cache_metadata->root_lock
policy->lock
Fix this potential lock-inversion (ABBA) deadlock in sync_metadata() by
ensuring the cache_metadata root_lock is held whilst all the hints are
written, rather than being repeatedly locked while policy->lock is held
(as was the case with each callout that policy_walk_mappings() made to
the old save_hint() method).
Found by turning on the CONFIG_PROVE_LOCKING ("Lock debugging: prove
locking correctness") build option. However, it is not clear how the
LOCKDEP reported paths can lead to a deadlock since the two paths,
suspending a target and loading a target, never occur at the same time.
But that doesn't mean the same lock-inversion couldn't have occurred
elsewhere.
Reported-by: Marian Csontos <mcsontos@redhat.com>
Signed-off-by: Joe Thornber <ejt@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Cc: stable@vger.kernel.org
-rw-r--r-- | drivers/md/dm-cache-metadata.c | 35 | ||||
-rw-r--r-- | drivers/md/dm-cache-metadata.h | 9 | ||||
-rw-r--r-- | drivers/md/dm-cache-target.c | 28 |
3 files changed, 20 insertions, 52 deletions
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 66f102ab102c..4ead4ba60656 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c | |||
@@ -1245,22 +1245,12 @@ static int begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *po | |||
1245 | return 0; | 1245 | return 0; |
1246 | } | 1246 | } |
1247 | 1247 | ||
1248 | int dm_cache_begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy) | 1248 | static int save_hint(void *context, dm_cblock_t cblock, dm_oblock_t oblock, uint32_t hint) |
1249 | { | 1249 | { |
1250 | struct dm_cache_metadata *cmd = context; | ||
1251 | __le32 value = cpu_to_le32(hint); | ||
1250 | int r; | 1252 | int r; |
1251 | 1253 | ||
1252 | down_write(&cmd->root_lock); | ||
1253 | r = begin_hints(cmd, policy); | ||
1254 | up_write(&cmd->root_lock); | ||
1255 | |||
1256 | return r; | ||
1257 | } | ||
1258 | |||
1259 | static int save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock, | ||
1260 | uint32_t hint) | ||
1261 | { | ||
1262 | int r; | ||
1263 | __le32 value = cpu_to_le32(hint); | ||
1264 | __dm_bless_for_disk(&value); | 1254 | __dm_bless_for_disk(&value); |
1265 | 1255 | ||
1266 | r = dm_array_set_value(&cmd->hint_info, cmd->hint_root, | 1256 | r = dm_array_set_value(&cmd->hint_info, cmd->hint_root, |
@@ -1270,16 +1260,25 @@ static int save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock, | |||
1270 | return r; | 1260 | return r; |
1271 | } | 1261 | } |
1272 | 1262 | ||
1273 | int dm_cache_save_hint(struct dm_cache_metadata *cmd, dm_cblock_t cblock, | 1263 | static int write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy) |
1274 | uint32_t hint) | ||
1275 | { | 1264 | { |
1276 | int r; | 1265 | int r; |
1277 | 1266 | ||
1278 | if (!hints_array_initialized(cmd)) | 1267 | r = begin_hints(cmd, policy); |
1279 | return 0; | 1268 | if (r) { |
1269 | DMERR("begin_hints failed"); | ||
1270 | return r; | ||
1271 | } | ||
1272 | |||
1273 | return policy_walk_mappings(policy, save_hint, cmd); | ||
1274 | } | ||
1275 | |||
1276 | int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *policy) | ||
1277 | { | ||
1278 | int r; | ||
1280 | 1279 | ||
1281 | down_write(&cmd->root_lock); | 1280 | down_write(&cmd->root_lock); |
1282 | r = save_hint(cmd, cblock, hint); | 1281 | r = write_hints(cmd, policy); |
1283 | up_write(&cmd->root_lock); | 1282 | up_write(&cmd->root_lock); |
1284 | 1283 | ||
1285 | return r; | 1284 | return r; |
diff --git a/drivers/md/dm-cache-metadata.h b/drivers/md/dm-cache-metadata.h index ce8468bb616e..cd70a78623a3 100644 --- a/drivers/md/dm-cache-metadata.h +++ b/drivers/md/dm-cache-metadata.h | |||
@@ -128,14 +128,7 @@ void dm_cache_dump(struct dm_cache_metadata *cmd); | |||
128 | * rather than querying the policy for each cblock, we let it walk its data | 128 | * rather than querying the policy for each cblock, we let it walk its data |
129 | * structures and fill in the hints in whatever order it wishes. | 129 | * structures and fill in the hints in whatever order it wishes. |
130 | */ | 130 | */ |
131 | 131 | int dm_cache_write_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *p); | |
132 | int dm_cache_begin_hints(struct dm_cache_metadata *cmd, struct dm_cache_policy *p); | ||
133 | |||
134 | /* | ||
135 | * requests hints for every cblock and stores in the metadata device. | ||
136 | */ | ||
137 | int dm_cache_save_hint(struct dm_cache_metadata *cmd, | ||
138 | dm_cblock_t cblock, uint32_t hint); | ||
139 | 132 | ||
140 | /* | 133 | /* |
141 | * Query method. Are all the blocks in the cache clean? | 134 | * Query method. Are all the blocks in the cache clean? |
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 8534679918f4..1bf4a71919ec 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c | |||
@@ -2582,30 +2582,6 @@ static int write_discard_bitset(struct cache *cache) | |||
2582 | return 0; | 2582 | return 0; |
2583 | } | 2583 | } |
2584 | 2584 | ||
2585 | static int save_hint(void *context, dm_cblock_t cblock, dm_oblock_t oblock, | ||
2586 | uint32_t hint) | ||
2587 | { | ||
2588 | struct cache *cache = context; | ||
2589 | return dm_cache_save_hint(cache->cmd, cblock, hint); | ||
2590 | } | ||
2591 | |||
2592 | static int write_hints(struct cache *cache) | ||
2593 | { | ||
2594 | int r; | ||
2595 | |||
2596 | r = dm_cache_begin_hints(cache->cmd, cache->policy); | ||
2597 | if (r) { | ||
2598 | DMERR("dm_cache_begin_hints failed"); | ||
2599 | return r; | ||
2600 | } | ||
2601 | |||
2602 | r = policy_walk_mappings(cache->policy, save_hint, cache); | ||
2603 | if (r) | ||
2604 | DMERR("policy_walk_mappings failed"); | ||
2605 | |||
2606 | return r; | ||
2607 | } | ||
2608 | |||
2609 | /* | 2585 | /* |
2610 | * returns true on success | 2586 | * returns true on success |
2611 | */ | 2587 | */ |
@@ -2623,7 +2599,7 @@ static bool sync_metadata(struct cache *cache) | |||
2623 | 2599 | ||
2624 | save_stats(cache); | 2600 | save_stats(cache); |
2625 | 2601 | ||
2626 | r3 = write_hints(cache); | 2602 | r3 = dm_cache_write_hints(cache->cmd, cache->policy); |
2627 | if (r3) | 2603 | if (r3) |
2628 | DMERR("could not write hints"); | 2604 | DMERR("could not write hints"); |
2629 | 2605 | ||
@@ -3094,7 +3070,7 @@ static void cache_io_hints(struct dm_target *ti, struct queue_limits *limits) | |||
3094 | 3070 | ||
3095 | static struct target_type cache_target = { | 3071 | static struct target_type cache_target = { |
3096 | .name = "cache", | 3072 | .name = "cache", |
3097 | .version = {1, 3, 0}, | 3073 | .version = {1, 4, 0}, |
3098 | .module = THIS_MODULE, | 3074 | .module = THIS_MODULE, |
3099 | .ctr = cache_ctr, | 3075 | .ctr = cache_ctr, |
3100 | .dtr = cache_dtr, | 3076 | .dtr = cache_dtr, |