diff options
author | Mike Snitzer <snitzer@redhat.com> | 2009-05-15 09:12:34 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-05-15 09:12:34 +1000 |
commit | 64b184139ca6cd3d53dc45d7782c8be50b3e0331 (patch) | |
tree | a8933dab30a2ddc29c42262a96484f9aac8a1d80 /drivers/md/dm-table.c | |
parent | cc1018ae8a413b595a1f0f822928dd9e81a75e59 (diff) |
dm-table-validate-device-hardsect_size
Impose necessary and sufficient conditions on a devices's table such
that any incoming bio which respects its hardsect_size can be processed
successfully.
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Diffstat (limited to 'drivers/md/dm-table.c')
-rw-r--r-- | drivers/md/dm-table.c | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 77da472cdb61..443fd6a1702d 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -721,6 +721,71 @@ static void check_for_valid_limits(struct io_restrictions *rs) rs->bounce_pfn = -1; } +/* + * Impose necessary and sufficient conditions on a devices's table such + * that any incoming bio which respects its hardsect_size can be + * processed successfully. If it falls across the boundary between + * two or more targets, the size of each piece it gets split into must + * be compatible with the hardsect_size of the target processing it. + */ +static int validate_hardsect_alignment(struct dm_table *table) +{ + /* + * This function uses arithmetic modulo the hardsect_size + * (in units of 512-byte sectors). + */ + unsigned short device_hardsect_size_sects = + table->limits.hardsect_size >> SECTOR_SHIFT; + + /* + * Offset of the start of the next table entry, mod hardsect_size. + */ + unsigned short next_target_start = 0; + + /* + * Given an aligned bio that extends beyond the end of a + * target, how many sectors must the next target handle? + */ + unsigned short remaining = 0; + + struct dm_target *uninitialized_var(ti); + unsigned i = 0; + + /* + * Check each entry in the table in turn. + */ + while (i < dm_table_get_num_targets(table)) { + ti = dm_table_get_target(table, i++); + + /* + * If the remaining sectors fall entirely within this + * table entry are they compatible with its hardsect_size? + */ + if (remaining < ti->len && + remaining & ((ti->limits.hardsect_size >> + SECTOR_SHIFT) - 1)) + break; /* Error */ + + next_target_start = + (unsigned short) ((next_target_start + ti->len) & + (device_hardsect_size_sects - 1)); + remaining = next_target_start ? + device_hardsect_size_sects - next_target_start : 0; + } + + if (remaining) { + DMWARN("%s: table line %u (start sect %llu len %llu) " + "not aligned to hardware sector size %hu", + dm_device_name(table->md), i, + (unsigned long long) ti->begin, + (unsigned long long) ti->len, + table->limits.hardsect_size); + return -EINVAL; + } + + return 0; +} + int dm_table_add_target(struct dm_table *t, const char *type, sector_t start, sector_t len, char *params) { @@ -820,6 +885,10 @@ int dm_table_complete(struct dm_table *t) check_for_valid_limits(&t->limits); + r = validate_hardsect_alignment(t); + if (r) + return r; + /* how many indexes will the btree have ? */ leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE); t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); |