aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
authorMike Snitzer <snitzer@redhat.com>2009-06-22 05:12:31 -0400
committerAlasdair G Kergon <agk@redhat.com>2009-06-22 05:12:31 -0400
commitbe6d4305db093ad1cc623f7dd3d2470b7bd73fa4 (patch)
tree27935ca83626cba6ba2fac2cc83deeca0ff15492 /drivers/md
parent02acc3a4fa0a6c2a5ccc4fb722b55fb710265882 (diff)
dm table: validate device logical_block_size
Impose necessary and sufficient conditions on a devices's table such that any incoming bio which respects its logical_block_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')
-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 535fdaf2473d..e3bcfb8b15a1 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -724,6 +724,71 @@ static void check_for_valid_limits(struct io_restrictions *rs)
724 rs->bounce_pfn = -1; 724 rs->bounce_pfn = -1;
725} 725}
726 726
727/*
728 * Impose necessary and sufficient conditions on a devices's table such
729 * that any incoming bio which respects its logical_block_size can be
730 * processed successfully. If it falls across the boundary between
731 * two or more targets, the size of each piece it gets split into must
732 * be compatible with the logical_block_size of the target processing it.
733 */
734static int validate_hardware_logical_block_alignment(struct dm_table *table)
735{
736 /*
737 * This function uses arithmetic modulo the logical_block_size
738 * (in units of 512-byte sectors).
739 */
740 unsigned short device_logical_block_size_sects =
741 table->limits.logical_block_size >> SECTOR_SHIFT;
742
743 /*
744 * Offset of the start of the next table entry, mod logical_block_size.
745 */
746 unsigned short next_target_start = 0;
747
748 /*
749 * Given an aligned bio that extends beyond the end of a
750 * target, how many sectors must the next target handle?
751 */
752 unsigned short remaining = 0;
753
754 struct dm_target *uninitialized_var(ti);
755 unsigned i = 0;
756
757 /*
758 * Check each entry in the table in turn.
759 */
760 while (i < dm_table_get_num_targets(table)) {
761 ti = dm_table_get_target(table, i++);
762
763 /*
764 * If the remaining sectors fall entirely within this
765 * table entry are they compatible with its logical_block_size?
766 */
767 if (remaining < ti->len &&
768 remaining & ((ti->limits.logical_block_size >>
769 SECTOR_SHIFT) - 1))
770 break; /* Error */
771
772 next_target_start =
773 (unsigned short) ((next_target_start + ti->len) &
774 (device_logical_block_size_sects - 1));
775 remaining = next_target_start ?
776 device_logical_block_size_sects - next_target_start : 0;
777 }
778
779 if (remaining) {
780 DMWARN("%s: table line %u (start sect %llu len %llu) "
781 "not aligned to hardware logical block size %hu",
782 dm_device_name(table->md), i,
783 (unsigned long long) ti->begin,
784 (unsigned long long) ti->len,
785 table->limits.logical_block_size);
786 return -EINVAL;
787 }
788
789 return 0;
790}
791
727int dm_table_add_target(struct dm_table *t, const char *type, 792int dm_table_add_target(struct dm_table *t, const char *type,
728 sector_t start, sector_t len, char *params) 793 sector_t start, sector_t len, char *params)
729{ 794{
@@ -823,6 +888,10 @@ int dm_table_complete(struct dm_table *t)
823 888
824 check_for_valid_limits(&t->limits); 889 check_for_valid_limits(&t->limits);
825 890
891 r = validate_hardware_logical_block_alignment(t);
892 if (r)
893 return r;
894
826 /* how many indexes will the btree have ? */ 895 /* how many indexes will the btree have ? */
827 leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE); 896 leaf_nodes = dm_div_up(t->num_targets, KEYS_PER_NODE);
828 t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE); 897 t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);