forked from Minki/linux
dm integrity: allow separate metadata device
Add the ability to store DM integrity metadata on a separate device. This feature is activated with the option "meta_device:/dev/device". Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:
parent
71e9ddbcb9
commit
356d9d52e1
@ -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_id(struct dm_integrity_c *ic, unsigned i,
|
||||
static 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 dm_integrity_c *ic, sector_t area, sector
|
||||
{
|
||||
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_integrity_c *ic, int op, int op_flags)
|
||||
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_integrity_c *ic, int op, int op_flags, unsigned
|
||||
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;
|
||||
|
||||
@ -2306,12 +2315,15 @@ static void dm_integrity_status(struct dm_target *ti, status_type_t type,
|
||||
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);
|
||||
@ -2341,7 +2353,10 @@ static int dm_integrity_iterate_devices(struct dm_target *ti,
|
||||
{
|
||||
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)
|
||||
@ -2374,26 +2389,38 @@ static void calculate_journal_section_size(struct dm_integrity_c *ic)
|
||||
static 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;
|
||||
}
|
||||
@ -2415,26 +2442,51 @@ static int initialize_superblock(struct dm_integrity_c *ic, unsigned journal_sec
|
||||
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);
|
||||
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->device_sectors) - 1; test_bit >= 3; test_bit--) {
|
||||
__u64 prev_data_sectors = ic->provided_data_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;
|
||||
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);
|
||||
|
||||
}
|
||||
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;
|
||||
@ -2939,9 +2991,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
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;
|
||||
@ -2964,7 +3014,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
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)
|
||||
@ -2973,7 +3023,17 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
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))) {
|
||||
@ -3004,6 +3064,21 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@ -3139,11 +3214,19 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
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)) {
|
||||
@ -3157,20 +3240,28 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
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;
|
||||
@ -3194,8 +3285,8 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
(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";
|
||||
@ -3227,9 +3318,11 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||
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);
|
||||
@ -3265,6 +3358,8 @@ static void dm_integrity_dtr(struct dm_target *ti)
|
||||
dm_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);
|
||||
|
Loading…
Reference in New Issue
Block a user