aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/dm-cache-target.c94
1 files changed, 87 insertions, 7 deletions
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 41e7cfdb450d..2c66315553f2 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -2817,17 +2817,86 @@ static int load_mapping(void *context, dm_oblock_t oblock, dm_cblock_t cblock,
2817 return 0; 2817 return 0;
2818} 2818}
2819 2819
2820/*
2821 * The discard block size in the on disk metadata is not
2822 * neccessarily the same as we're currently using. So we have to
2823 * be careful to only set the discarded attribute if we know it
2824 * covers a complete block of the new size.
2825 */
2826struct discard_load_info {
2827 struct cache *cache;
2828
2829 /*
2830 * These blocks are sized using the on disk dblock size, rather
2831 * than the current one.
2832 */
2833 dm_block_t block_size;
2834 dm_block_t discard_begin, discard_end;
2835};
2836
2837static void discard_load_info_init(struct cache *cache,
2838 struct discard_load_info *li)
2839{
2840 li->cache = cache;
2841 li->discard_begin = li->discard_end = 0;
2842}
2843
2844static void set_discard_range(struct discard_load_info *li)
2845{
2846 sector_t b, e;
2847
2848 if (li->discard_begin == li->discard_end)
2849 return;
2850
2851 /*
2852 * Convert to sectors.
2853 */
2854 b = li->discard_begin * li->block_size;
2855 e = li->discard_end * li->block_size;
2856
2857 /*
2858 * Then convert back to the current dblock size.
2859 */
2860 b = dm_sector_div_up(b, li->cache->discard_block_size);
2861 sector_div(e, li->cache->discard_block_size);
2862
2863 /*
2864 * The origin may have shrunk, so we need to check we're still in
2865 * bounds.
2866 */
2867 if (e > from_dblock(li->cache->discard_nr_blocks))
2868 e = from_dblock(li->cache->discard_nr_blocks);
2869
2870 for (; b < e; b++)
2871 set_discard(li->cache, to_dblock(b));
2872}
2873
2820static int load_discard(void *context, sector_t discard_block_size, 2874static int load_discard(void *context, sector_t discard_block_size,
2821 dm_dblock_t dblock, bool discard) 2875 dm_dblock_t dblock, bool discard)
2822{ 2876{
2823 struct cache *cache = context; 2877 struct discard_load_info *li = context;
2824 2878
2825 /* FIXME: handle mis-matched block size */ 2879 li->block_size = discard_block_size;
2826 2880
2827 if (discard) 2881 if (discard) {
2828 set_discard(cache, dblock); 2882 if (from_dblock(dblock) == li->discard_end)
2829 else 2883 /*
2830 clear_discard(cache, dblock); 2884 * We're already in a discard range, just extend it.
2885 */
2886 li->discard_end = li->discard_end + 1ULL;
2887
2888 else {
2889 /*
2890 * Emit the old range and start a new one.
2891 */
2892 set_discard_range(li);
2893 li->discard_begin = from_dblock(dblock);
2894 li->discard_end = li->discard_begin + 1ULL;
2895 }
2896 } else {
2897 set_discard_range(li);
2898 li->discard_begin = li->discard_end = 0;
2899 }
2831 2900
2832 return 0; 2901 return 0;
2833} 2902}
@@ -2911,11 +2980,22 @@ static int cache_preresume(struct dm_target *ti)
2911 } 2980 }
2912 2981
2913 if (!cache->loaded_discards) { 2982 if (!cache->loaded_discards) {
2914 r = dm_cache_load_discards(cache->cmd, load_discard, cache); 2983 struct discard_load_info li;
2984
2985 /*
2986 * The discard bitset could have been resized, or the
2987 * discard block size changed. To be safe we start by
2988 * setting every dblock to not discarded.
2989 */
2990 clear_bitset(cache->discard_bitset, from_dblock(cache->discard_nr_blocks));
2991
2992 discard_load_info_init(cache, &li);
2993 r = dm_cache_load_discards(cache->cmd, load_discard, &li);
2915 if (r) { 2994 if (r) {
2916 DMERR("could not load origin discards"); 2995 DMERR("could not load origin discards");
2917 return r; 2996 return r;
2918 } 2997 }
2998 set_discard_range(&li);
2919 2999
2920 cache->loaded_discards = true; 3000 cache->loaded_discards = true;
2921 } 3001 }