dm-integrity: allow external devices
Mikulas Patocka
maintainerPATCH 6/9 dm-integrity: allow external devices
This patch adds a possibility to use external device for metadata for dm-integrity. It is activated with the option "meta_device:/dev/device".
Signed-off-by Mikulas Patocka <[email protected]>
--- drivers/md/dm-integrity.c | 203 +++++++++++++++++++++++++++++++++------------- 1 file changed, 149 insertions(+), 54 deletions(-)
drivers/md/dm-integrity.c +149 -54
@@ -139,6 +139,7 @@ struct alg_spec {struct dm_integrity_c {struct dm_dev *dev; +struct dm_dev *meta_dev;unsigned tag_size;__s8 log2_tag_size;sector_t start;
@@ -170,7 +171,8 @@ struct dm_integrity_c {unsigned short journal_section_sectors;unsigned journal_sections;unsigned journal_entries; -sector_t device_sectors; +sector_t data_device_sectors; +sector_t meta_device_sectors;unsigned initial_sectors;unsigned metadata_run;__s8 log2_metadata_run;
@@ -345,10 +347,14 @@ static commit_id_t dm_integrity_commit_istatic void get_area_and_offset (struct dm_integrity_c *ic,sector_t data_sector,sector_t *area,sector_t *offset) { -__u8 log2_interleave_sectors = ic->sb->log2_interleave_sectors; - - *area = data_sector >> log2_interleave_sectors; - *offset = (unsigned )data_sector & ((1U << log2_interleave_sectors) -1 ); +if (!ic->meta_dev) { +__u8 log2_interleave_sectors = ic->sb->log2_interleave_sectors; + *area = data_sector >> log2_interleave_sectors; + *offset = (unsigned )data_sector & ((1U << log2_interleave_sectors) -1 ); + }else { + *area =0 ; + *offset = data_sector; + } }#define sector_to_block (ic, n) \
@@ -387,6 +393,9 @@ static sector_t get_data_sector(struct d {sector_t result; +if (ic->meta_dev) +return offset; + result = area << ic->sb->log2_interleave_sectors;if (likely (ic->log2_metadata_run >=0 )) result += (area +1 ) << ic->log2_metadata_run;
@@ -416,7 +425,7 @@ static int sync_rw_sb(struct dm_integrit io_req.mem.ptr.addr = ic->sb; io_req.notify.fn =NULL ; io_req.client = ic->io; - io_loc.bdev = ic->dev->bdev; + io_loc.bdev = ic->meta_dev ? ic->meta_dev->bdev : ic->dev->bdev; io_loc.sector = ic->start; io_loc.count =SB_SECTORS ;
@@ -763,7 +772,7 @@ static void rw_journal(struct dm_integri io_req.notify.fn =NULL ; } io_req.client = ic->io; - io_loc.bdev = ic->dev->bdev; + io_loc.bdev = ic->meta_dev ? ic->meta_dev->bdev : ic->dev->bdev; io_loc.sector = ic->start +SB_SECTORS + sector; io_loc.count = n_sectors;
@@ -2305,12 +2314,15 @@ static void dm_integrity_status(struct d watermark_percentage += ic->journal_entries /2 ;do_div (watermark_percentage, ic->journal_entries); arg_count =5 ; + arg_count += !!ic->meta_dev; arg_count += ic->sectors_per_block !=1 ; arg_count += !!ic->internal_hash_alg.alg_string; arg_count += !!ic->journal_crypt_alg.alg_string; arg_count += !!ic->journal_mac_alg.alg_string;DMEMIT ("%s %llu %u %c %u" , ic->dev->name, (unsigned long long )ic->start, ic->tag_size, ic->mode, arg_count); +if (ic->meta_dev) +DMEMIT (" meta_device:%s" , ic->meta_dev->name);DMEMIT (" journal_sectors:%u" , ic->initial_sectors -SB_SECTORS );DMEMIT (" interleave_sectors:%u" ,1U << ic->sb->log2_interleave_sectors);DMEMIT (" buffer_sectors:%u" ,1U << ic->log2_buffer_sectors);
@@ -2340,7 +2352,10 @@ static int dm_integrity_iterate_devices( {struct dm_integrity_c *ic = ti->private; -return fn (ti, ic->dev, ic->start + ic->initial_sectors + ic->metadata_run, ti->len, data); +if (!ic->meta_dev) +return fn (ti, ic->dev, ic->start + ic->initial_sectors + ic->metadata_run, ti->len, data); +else +return fn (ti, ic->dev,0 , ti->len, data); }static void dm_integrity_io_hints (struct dm_target *ti,struct queue_limits *limits)
@@ -2373,26 +2388,38 @@ static void calculate_journal_section_sistatic int calculate_device_limits (struct dm_integrity_c *ic) {__u64 initial_sectors; -sector_t last_sector, last_area, last_offset;calculate_journal_section_size (ic); initial_sectors =SB_SECTORS + (__u64 )ic->journal_section_sectors * ic->journal_sections; -if (initial_sectors +METADATA_PADDING_SECTORS >= ic->device_sectors || initial_sectors >UINT_MAX ) +if (initial_sectors +METADATA_PADDING_SECTORS >= ic->meta_device_sectors || initial_sectors >UINT_MAX )return -EINVAL ; ic->initial_sectors = initial_sectors; - ic->metadata_run =roundup ((__u64 )ic->tag_size << (ic->sb->log2_interleave_sectors - ic->sb->log2_sectors_per_block), - (__u64)(1 <<SECTOR_SHIFT <<METADATA_PADDING_SECTORS )) >>SECTOR_SHIFT ; -if (!(ic->metadata_run & (ic->metadata_run -1 ))) - ic->log2_metadata_run =__ffs (ic->metadata_run); -else - ic->log2_metadata_run =-1 ; +if (!ic->meta_dev) { +sector_t last_sector, last_area, last_offset; -get_area_and_offset (ic, ic->provided_data_sectors -1 , &last_area, &last_offset); - last_sector =get_data_sector (ic, last_area, last_offset); + ic->metadata_run =roundup ((__u64 )ic->tag_size << (ic->sb->log2_interleave_sectors - ic->sb->log2_sectors_per_block), + (__u64)(1 <<SECTOR_SHIFT <<METADATA_PADDING_SECTORS )) >>SECTOR_SHIFT ; +if (!(ic->metadata_run & (ic->metadata_run -1 ))) + ic->log2_metadata_run =__ffs (ic->metadata_run); +else + ic->log2_metadata_run =-1 ; -if (last_sector < ic->start || last_sector >= ic->device_sectors) -return -EINVAL ; +get_area_and_offset (ic, ic->provided_data_sectors -1 , &last_area, &last_offset); + last_sector =get_data_sector (ic, last_area, last_offset); +if (last_sector < ic->start || last_sector >= ic->meta_device_sectors) +return -EINVAL ; + }else { +__u64 meta_size = ic->provided_data_sectors * ic->tag_size; + meta_size = (meta_size + ((1U << (ic->log2_buffer_sectors +SECTOR_SHIFT )) -1 )) + >> (ic->log2_buffer_sectors +SECTOR_SHIFT ); + meta_size <<= ic->log2_buffer_sectors; +if (ic->initial_sectors + meta_size < ic->initial_sectors || + ic->initial_sectors + meta_size > ic->meta_device_sectors) +return -EINVAL ; + ic->metadata_run =1 ; + ic->log2_metadata_run =0 ; + }return 0 ; }
@@ -2414,26 +2441,51 @@ static int initialize_superblock(struct journal_sections = journal_sectors / ic->journal_section_sectors;if (!journal_sections) journal_sections =1 ; - ic->sb->journal_sections =cpu_to_le32 (journal_sections); - -if (!interleave_sectors) - interleave_sectors =DEFAULT_INTERLEAVE_SECTORS ; - ic->sb->log2_interleave_sectors =__fls (interleave_sectors); - ic->sb->log2_interleave_sectors =max ((__u8 )MIN_LOG2_INTERLEAVE_SECTORS , ic->sb->log2_interleave_sectors); - ic->sb->log2_interleave_sectors =min ((__u8 )MAX_LOG2_INTERLEAVE_SECTORS , ic->sb->log2_interleave_sectors); - ic->provided_data_sectors =0 ; -for (test_bit =fls64 (ic->device_sectors) -1 ; test_bit >=3 ; test_bit--) { -__u64 prev_data_sectors = ic->provided_data_sectors; +if (!ic->meta_dev) { + ic->sb->journal_sections =cpu_to_le32 (journal_sections); +if (!interleave_sectors) + interleave_sectors =DEFAULT_INTERLEAVE_SECTORS ; + ic->sb->log2_interleave_sectors =__fls (interleave_sectors); + ic->sb->log2_interleave_sectors =max ((__u8 )MIN_LOG2_INTERLEAVE_SECTORS , ic->sb->log2_interleave_sectors); + ic->sb->log2_interleave_sectors =min ((__u8 )MAX_LOG2_INTERLEAVE_SECTORS , ic->sb->log2_interleave_sectors); + + ic->provided_data_sectors =0 ; +for (test_bit =fls64 (ic->meta_device_sectors) -1 ; test_bit >=3 ; test_bit--) { +__u64 prev_data_sectors = ic->provided_data_sectors; + + ic->provided_data_sectors |= (sector_t )1 << test_bit; +if (calculate_device_limits (ic)) + ic->provided_data_sectors = prev_data_sectors; + } +if (!ic->provided_data_sectors) +return -EINVAL ; + }else { + ic->sb->log2_interleave_sectors =0 ; + ic->provided_data_sectors = ic->data_device_sectors; + ic->provided_data_sectors &= ~(sector_t)(ic->sectors_per_block -1 ); + +try_smaller_buffer: + ic->sb->journal_sections =cpu_to_le32 (0 ); +for (test_bit =fls (journal_sections) -1 ; test_bit >=0 ; test_bit--) { +__u32 prev_journal_sections =le32_to_cpu (ic->sb->journal_sections); +__u32 test_journal_sections = prev_journal_sections | (1U << test_bit); +if (test_journal_sections > journal_sections) +continue ; + ic->sb->journal_sections =cpu_to_le32 (test_journal_sections); +if (calculate_device_limits (ic)) + ic->sb->journal_sections =cpu_to_le32 (prev_journal_sections); - ic->provided_data_sectors |= (sector_t )1 << test_bit; -if (calculate_device_limits (ic)) - ic->provided_data_sectors = prev_data_sectors; + } +if (!le32_to_cpu (ic->sb->journal_sections)) { +if (ic->log2_buffer_sectors >3 ) { + ic->log2_buffer_sectors--; + goto try_smaller_buffer; + } +return -EINVAL ; + } } -if (!ic->provided_data_sectors) -return -EINVAL ; - ic->sb->provided_data_sectors =cpu_to_le64 (ic->provided_data_sectors);return 0 ;
@@ -2930,9 +2982,7 @@ static int dm_integrity_ctr(struct dm_ta goto bad; } - ic->device_sectors =i_size_read (ic->dev->bdev->bd_inode) >>SECTOR_SHIFT ; - journal_sectors =min ((sector_t )DEFAULT_MAX_JOURNAL_SECTORS , - ic->device_sectors >>DEFAULT_JOURNAL_SIZE_FACTOR ); + journal_sectors =0 ; interleave_sectors =DEFAULT_INTERLEAVE_SECTORS ; buffer_sectors =DEFAULT_BUFFER_SECTORS ; journal_watermark =DEFAULT_JOURNAL_WATERMARK ;
@@ -2955,7 +3005,7 @@ static int dm_integrity_ctr(struct dm_ta goto bad; }if (sscanf (opt_string,"journal_sectors:%u%c" , &val, &dummy) ==1 ) - journal_sectors = val; + journal_sectors = val ? val :1 ;else if (sscanf (opt_string,"interleave_sectors:%u%c" , &val, &dummy) ==1 ) interleave_sectors = val;else if (sscanf (opt_string,"buffer_sectors:%u%c" , &val, &dummy) ==1 )
@@ -2964,7 +3014,17 @@ static int dm_integrity_ctr(struct dm_ta journal_watermark = val; else if (sscanf(opt_string, "commit_time:%u%c", &val, &dummy) == 1) sync_msec = val; - else if (sscanf(opt_string, "block_size:%u%c", &val, &dummy) == 1) { + else if (!memcmp(opt_string, "meta_device:", strlen("meta_device:"))) { +if (ic->meta_dev) { +dm_put_device (ti, ic->meta_dev); + ic->meta_dev =NULL ; + } + r =dm_get_device (ti,strchr (opt_string,':' ) +1 ,dm_table_get_mode (ti->table), &ic->meta_dev); +if (r) { + ti->error ="Device lookup failed" ; + goto bad; + } + } else if (sscanf(opt_string, "block_size:%u%c", &val, &dummy) == 1) {if (val <1 <<SECTOR_SHIFT || val >MAX_SECTORS_PER_BLOCK <<SECTOR_SHIFT || (val & (val -1 ))) {
@@ -2995,6 +3055,21 @@ static int dm_integrity_ctr(struct dm_ta } } + ic->data_device_sectors =i_size_read (ic->dev->bdev->bd_inode) >>SECTOR_SHIFT ; +if (!ic->meta_dev) + ic->meta_device_sectors = ic->data_device_sectors; +else + ic->meta_device_sectors =i_size_read (ic->meta_dev->bdev->bd_inode) >>SECTOR_SHIFT ; + +if (!journal_sectors) { + journal_sectors =min ((sector_t )DEFAULT_MAX_JOURNAL_SECTORS , + ic->data_device_sectors >>DEFAULT_JOURNAL_SIZE_FACTOR ); + } + +if (!buffer_sectors) + buffer_sectors =1 ; + ic->log2_buffer_sectors =min ((int )__fls (buffer_sectors),31 -SECTOR_SHIFT ); + r =get_mac (&ic->internal_hash, &ic->internal_hash_alg, &ti->error,"Invalid internal hash" ,"Error setting internal hash key" );if (r)
@@ -3131,11 +3206,19 @@ static int dm_integrity_ctr(struct dm_ta goto bad; }/* make sure that ti->max_io_len doesn't overflow */ -if (ic->sb->log2_interleave_sectors <MIN_LOG2_INTERLEAVE_SECTORS || - ic->sb->log2_interleave_sectors >MAX_LOG2_INTERLEAVE_SECTORS ) { - r = -EINVAL ; - ti->error ="Invalid interleave_sectors in the superblock" ; - goto bad; +if (!ic->meta_dev) { +if (ic->sb->log2_interleave_sectors <MIN_LOG2_INTERLEAVE_SECTORS || + ic->sb->log2_interleave_sectors >MAX_LOG2_INTERLEAVE_SECTORS ) { + r = -EINVAL ; + ti->error ="Invalid interleave_sectors in the superblock" ; + goto bad; + } + }else { +if (ic->sb->log2_interleave_sectors) { + r = -EINVAL ; + ti->error ="Invalid interleave_sectors in the superblock" ; + goto bad; + } } ic->provided_data_sectors =le64_to_cpu (ic->sb->provided_data_sectors);if (ic->provided_data_sectors !=le64_to_cpu (ic->sb->provided_data_sectors)) {
@@ -3149,20 +3232,28 @@ static int dm_integrity_ctr(struct dm_ta ti->error ="Journal mac mismatch" ; goto bad; } + +try_smaller_buffer: r =calculate_device_limits (ic);if (r) { +if (ic->meta_dev) { +if (ic->log2_buffer_sectors >3 ) { + ic->log2_buffer_sectors--; + goto try_smaller_buffer; + } + } ti->error ="The device is too small" ; goto bad; } +if (!ic->meta_dev) + ic->log2_buffer_sectors =min (ic->log2_buffer_sectors, (__u8 )__ffs (ic->metadata_run)); +if (ti->len > ic->provided_data_sectors) { r = -EINVAL ; ti->error ="Not enough provided sectors for requested mapping size" ; goto bad; } -if (!buffer_sectors) - buffer_sectors =1 ; - ic->log2_buffer_sectors =min3 ((int )__fls (buffer_sectors), (int )__ffs (ic->metadata_run),31 -SECTOR_SHIFT ); threshold = (__u64 )ic->journal_entries * (100 - journal_watermark); threshold +=50 ;
@@ -3186,8 +3277,8 @@ static int dm_integrity_ctr(struct dm_ta (unsigned long long)ic->provided_data_sectors);DEBUG_print (" log2_buffer_sectors %u\n" , ic->log2_buffer_sectors); - ic->bufio =dm_bufio_client_create (ic->dev->bdev,1U << (SECTOR_SHIFT + ic->log2_buffer_sectors), -1 ,0 ,NULL ,NULL ); + ic->bufio =dm_bufio_client_create (ic->meta_dev ? ic->meta_dev->bdev : ic->dev->bdev, +1U << (SECTOR_SHIFT + ic->log2_buffer_sectors),1 ,0 ,NULL ,NULL );if (IS_ERR (ic->bufio)) { r =PTR_ERR (ic->bufio); ti->error ="Cannot initialize dm-bufio" ;
@@ -3219,9 +3310,11 @@ static int dm_integrity_ctr(struct dm_ta ic->just_formatted = true; } - r =dm_set_target_max_io_len (ti,1U << ic->sb->log2_interleave_sectors); -if (r) - goto bad; +if (!ic->meta_dev) { + r =dm_set_target_max_io_len (ti,1U << ic->sb->log2_interleave_sectors); +if (r) + goto bad; + }if (!ic->internal_hash)dm_integrity_set (ti, ic);
@@ -3257,6 +3350,8 @@ static void dm_integrity_dtr(struct dm_tdm_io_client_destroy (ic->io);if (ic->dev)dm_put_device (ti, ic->dev); +if (ic->meta_dev) +dm_put_device (ti, ic->meta_dev);dm_integrity_free_page_list (ic, ic->journal);dm_integrity_free_page_list (ic, ic->journal_io);dm_integrity_free_page_list (ic, ic->journal_xor);