[dm-devel] [PATCH 08/10] dm-integrity: add a bitmap mode

Mike Snitzer snitzer at redhat.com
Wed May 8 17:53:50 UTC 2019


On Mon, Apr 29 2019 at  8:57am -0400,
Mikulas Patocka <mpatocka at redhat.com> wrote:

> Add a new bitmap mode for dm-integrity.
> 
> Signed-off-by: Mikulas Patocka <mpatocka at redhat.com>
> 
> ---
>  Documentation/device-mapper/dm-integrity.txt |   23 +
>  drivers/md/dm-integrity.c                    |  534 +++++++++++++++++++++++++--
>  2 files changed, 525 insertions(+), 32 deletions(-)
> 
> Index: linux-2.6/drivers/md/dm-integrity.c
> ===================================================================
> --- linux-2.6.orig/drivers/md/dm-integrity.c	2019-04-27 10:28:49.000000000 +0200
> +++ linux-2.6/drivers/md/dm-integrity.c	2019-04-29 11:43:38.000000000 +0200
> @@ -24,6 +24,7 @@
>  
>  #define DEFAULT_INTERLEAVE_SECTORS	32768
>  #define DEFAULT_JOURNAL_SIZE_FACTOR	7
> +#define DEFAULT_SECTORS_PER_BITMAP_BIT	32768
>  #define DEFAULT_BUFFER_SECTORS		128
>  #define DEFAULT_JOURNAL_WATERMARK	50
>  #define DEFAULT_SYNC_MSEC		10000
> @@ -33,6 +34,8 @@
>  #define METADATA_WORKQUEUE_MAX_ACTIVE	16
>  #define RECALC_SECTORS			8192
>  #define RECALC_WRITE_SUPER		16
> +#define BITMAP_BLOCK_SIZE		4096	/* don't change it */
> +#define BITMAP_FLUSH_INTERVAL		(10 * HZ)
>  
>  /*
>   * Warning - DEBUG_PRINT prints security-sensitive data to the log,
> @@ -48,6 +51,7 @@
>  #define SB_MAGIC			"integrt"
>  #define SB_VERSION_1			1
>  #define SB_VERSION_2			2
> +#define SB_VERSION_3			3
>  #define SB_SECTORS			8
>  #define MAX_SECTORS_PER_BLOCK		8
>  
> @@ -60,12 +64,14 @@ struct superblock {
>  	__u64 provided_data_sectors;	/* userspace uses this value */
>  	__u32 flags;
>  	__u8 log2_sectors_per_block;
> -	__u8 pad[3];
> +	__u8 log2_blocks_per_bitmap_bit;
> +	__u8 pad[2];
>  	__u64 recalc_sector;
>  };
>  
>  #define SB_FLAG_HAVE_JOURNAL_MAC	0x1
>  #define SB_FLAG_RECALCULATING		0x2
> +#define SB_FLAG_DIRTY_BITMAP		0x4
>  
>  #define	JOURNAL_ENTRY_ROUNDUP		8
>  
> @@ -155,9 +161,16 @@ struct dm_integrity_c {
>  	struct workqueue_struct *metadata_wq;
>  	struct superblock *sb;
>  	unsigned journal_pages;
> +	unsigned n_bitmap_blocks;
> +
>  	struct page_list *journal;
>  	struct page_list *journal_io;
>  	struct page_list *journal_xor;
> +	struct page_list *recalc_bitmap;
> +	struct page_list *may_write_bitmap;
> +	struct bitmap_block_status *bbs;
> +	unsigned bitmap_flush_interval;
> +	struct delayed_work bitmap_flush_work;
>  
>  	struct crypto_skcipher *journal_crypt;
>  	struct scatterlist **journal_scatterlist;
> @@ -184,6 +197,7 @@ struct dm_integrity_c {
>  	__s8 log2_metadata_run;
>  	__u8 log2_buffer_sectors;
>  	__u8 sectors_per_block;
> +	__u8 log2_blocks_per_bitmap_bit;
>  
>  	unsigned char mode;
>  	int suspending;
> @@ -236,6 +250,7 @@ struct dm_integrity_c {
>  
>  	bool journal_uptodate;
>  	bool just_formatted;
> +	bool recalculate_flag;
>  
>  	struct alg_spec internal_hash_alg;
>  	struct alg_spec journal_crypt_alg;
> @@ -292,6 +307,16 @@ struct journal_io {
>  	struct journal_completion *comp;
>  };
>  
> +struct bitmap_block_status {
> +	struct work_struct work;
> +	struct dm_integrity_c *ic;
> +	unsigned idx;
> +	unsigned long *bitmap;
> +	struct bio_list bio_queue;
> +	spinlock_t bio_queue_lock;
> +
> +};
> +
>  static struct kmem_cache *journal_io_cache;
>  
>  #define JOURNAL_IO_MEMPOOL	32
> @@ -427,7 +452,9 @@ static void wraparound_section(struct dm
>  
>  static void sb_set_version(struct dm_integrity_c *ic)
>  {
> -	if (ic->meta_dev || ic->sb->flags & cpu_to_le32(SB_FLAG_RECALCULATING))
> +	if (ic->mode == 'B' || ic->sb->flags & cpu_to_le32(SB_FLAG_DIRTY_BITMAP))
> +		ic->sb->version = SB_VERSION_3;
> +	else if (ic->meta_dev || ic->sb->flags & cpu_to_le32(SB_FLAG_RECALCULATING))
>  		ic->sb->version = SB_VERSION_2;
>  	else
>  		ic->sb->version = SB_VERSION_1;
> @@ -451,6 +478,135 @@ static int sync_rw_sb(struct dm_integrit
>  	return dm_io(&io_req, 1, &io_loc, NULL);
>  }
>  
> +#define BITMAP_OP_TEST_ALL_SET		0
> +#define BITMAP_OP_TEST_ALL_CLEAR	1
> +#define BITMAP_OP_SET			2
> +#define BITMAP_OP_CLEAR			3
> +
> +static bool block_bitmap_op(struct dm_integrity_c *ic, struct page_list *bitmap, sector_t sector, sector_t n_sectors, int mode)
> +{
> +	unsigned long bit, end_bit, this_end_bit, page, end_page;
> +	unsigned long *data;
> +
> +	if (unlikely(((sector | n_sectors) & ((1 << ic->sb->log2_sectors_per_block) - 1)) != 0)) {
> +		DMCRIT("invalid bitmap access (%llx,%llx,%d,%d,%d)\n",
> +			(unsigned long long)sector,
> +			(unsigned long long)n_sectors,
> +			ic->sb->log2_sectors_per_block,
> +			ic->log2_blocks_per_bitmap_bit,
> +			mode);
> +		BUG();
> +	}

<snip>

> +
> +	if (unlikely(!n_sectors))
> +		return true;
> +
> +	bit = sector >> (ic->sb->log2_sectors_per_block + ic->log2_blocks_per_bitmap_bit);
> +	end_bit = (sector + n_sectors - 1) >> (ic->sb->log2_sectors_per_block + ic->log2_blocks_per_bitmap_bit);
> +
> +	page = bit / (PAGE_SIZE * 8);
> +	bit %= PAGE_SIZE * 8;
> +
> +	end_page = end_bit / (PAGE_SIZE * 8);
> +	end_bit %= PAGE_SIZE * 8;
> +
> +repeat:
> +	if (page < end_page) {
> +		this_end_bit = PAGE_SIZE * 8 - 1;
> +	} else {
> +		this_end_bit = end_bit;
> +	}
> +
> +	data = lowmem_page_address(bitmap[page].page);
> +
> +	if (mode == BITMAP_OP_TEST_ALL_SET) {
> +		while (bit <= this_end_bit) {
> +			if (!(bit % BITS_PER_LONG) && this_end_bit >= bit + BITS_PER_LONG - 1) {
> +				do {
> +					if (data[bit / BITS_PER_LONG] != -1)
> +						return false;
> +					bit += BITS_PER_LONG;
> +				} while (this_end_bit >= bit + BITS_PER_LONG - 1);
> +				continue;
> +			}
> +			if (!test_bit(bit, data))
> +				return false;
> +			bit++;
> +		}
> +	} else if (mode == BITMAP_OP_TEST_ALL_CLEAR) {
> +		while (bit <= this_end_bit) {
> +			if (!(bit % BITS_PER_LONG) && this_end_bit >= bit + BITS_PER_LONG - 1) {
> +				do {
> +					if (data[bit / BITS_PER_LONG] != 0)
> +						return false;
> +					bit += BITS_PER_LONG;
> +				} while (this_end_bit >= bit + BITS_PER_LONG - 1);
> +				continue;
> +			}
> +			if (test_bit(bit, data))
> +				return false;
> +			bit++;
> +		}
> +	} else if (mode == BITMAP_OP_SET) {
> +		while (bit <= this_end_bit) {
> +			if (!(bit % BITS_PER_LONG) && this_end_bit >= bit + BITS_PER_LONG - 1) {
> +				do {
> +					data[bit / BITS_PER_LONG] = -1;
> +					bit += BITS_PER_LONG;
> +				} while (this_end_bit >= bit + BITS_PER_LONG - 1);
> +				continue;
> +			}
> +			__set_bit(bit, data);
> +			bit++;
> +		}
> +	} else if (mode == BITMAP_OP_CLEAR) {
> +		if (!bit && this_end_bit == PAGE_SIZE * 8 - 1)
> +			clear_page(data);
> +		else while (bit <= this_end_bit) {
> +			if (!(bit % BITS_PER_LONG) && this_end_bit >= bit + BITS_PER_LONG - 1) {
> +				do {
> +					data[bit / BITS_PER_LONG] = 0;
> +					bit += BITS_PER_LONG;
> +				} while (this_end_bit >= bit + BITS_PER_LONG - 1);
> +				continue;
> +			}
> +			__clear_bit(bit, data);
> +			bit++;
> +		}
> +	} else {
> +		BUG();
> +	}

<snip>

> +static struct bitmap_block_status *sector_to_bitmap_block(struct dm_integrity_c *ic, sector_t sector)
> +{
> +	unsigned bit = sector >> (ic->sb->log2_sectors_per_block + ic->log2_blocks_per_bitmap_bit);
> +	unsigned bitmap_block = bit / (BITMAP_BLOCK_SIZE * 8);
> +
> +	BUG_ON(bitmap_block >= ic->n_bitmap_blocks);
> +	return &ic->bbs[bitmap_block];
> +}
> +

Too many BUG()s and BUG_ON.  The BUG_ON could be left if you think it
sufficiently unlikely.

But in general I'd prefer factoring out an &error return but needing to
check for such a return would slow things down.. so that's probably not
an option.

Maybe we just set a new 'invalid' flag and disallow all operations and
fail IO?  Point is it is really bad to crash the system because
dm-integrity lost its way.  Instead we should just mark the device
invalid (like dm-snapshot does).

Mike




More information about the dm-devel mailing list