dm cache: prevent corruption caused by discard_block_size > cache_block_size
authorMike Snitzer <snitzer@redhat.com>
Thu, 20 Mar 2014 14:11:15 +0000 (10:11 -0400)
committerMike Snitzer <snitzer@redhat.com>
Sat, 22 Mar 2014 16:39:37 +0000 (12:39 -0400)
If the discard block size is larger than the cache block size we will
not properly quiesce IO to a region that is about to be discarded.  This
results in a race between a cache migration where no copy is needed, and
a write to an adjacent cache block that's within the same large discard
block.

Workaround this by limiting the discard_block_size to cache_block_size.
Also limit the max_discard_sectors to cache_block_size.

A more comprehensive fix that introduces range locking support in the
bio_prison and proper quiescing of a discard range that spans multiple
cache blocks is already in development.

Reported-by: Morgan Mears <Morgan.Mears@netapp.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Acked-by: Joe Thornber <ejt@redhat.com>
Acked-by: Heinz Mauelshagen <heinzm@redhat.com>
Cc: stable@vger.kernel.org
drivers/md/dm-cache-target.c

index 1af70145fab9bcee990dd54c08224f9e16924851..4a608ea3771af4bfa01061e7e45f6b44579b5106 100644 (file)
@@ -2188,6 +2188,7 @@ static bool too_many_discard_blocks(sector_t discard_block_size,
 static sector_t calculate_discard_block_size(sector_t cache_block_size,
                                             sector_t origin_size)
 {
+#if 0
        sector_t discard_block_size;
 
        discard_block_size = roundup_pow_of_two(cache_block_size);
@@ -2197,6 +2198,13 @@ static sector_t calculate_discard_block_size(sector_t cache_block_size,
                        discard_block_size *= 2;
 
        return discard_block_size;
+#else
+       /*
+        * FIXME: stop-gap workaround for corruption that is
+        * induced by discard_block_size > cache_block_size.
+        */
+       return cache_block_size;
+#endif
 }
 
 #define DEFAULT_MIGRATION_THRESHOLD 2048
@@ -3121,7 +3129,7 @@ static void set_discard_limits(struct cache *cache, struct queue_limits *limits)
        /*
         * FIXME: these limits may be incompatible with the cache device
         */
-       limits->max_discard_sectors = cache->discard_block_size * 1024;
+       limits->max_discard_sectors = cache->discard_block_size;
        limits->discard_granularity = cache->discard_block_size << SECTOR_SHIFT;
 }