summaryrefslogtreecommitdiff
path: root/drivers/md/dm-table.c
diff options
context:
space:
mode:
authorMike Snitzer <snitzer@redhat.com>2009-05-15 09:12:34 +1000
committerStephen Rothwell <sfr@canb.auug.org.au>2009-05-15 09:12:34 +1000
commit64b184139ca6cd3d53dc45d7782c8be50b3e0331 (patch)
treea8933dab30a2ddc29c42262a96484f9aac8a1d80 /drivers/md/dm-table.c
parentcc1018ae8a413b595a1f0f822928dd9e81a75e59 (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.c69
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);