aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Snitzer <snitzer@redhat.com>2020-11-30 10:57:43 -0500
committerMike Snitzer <snitzer@redhat.com>2020-12-02 18:37:44 -0500
commit6bb38bcc33bf3093c08bd1b71e4f20c82bb60dd1 (patch)
tree121442a9818077e004c455bd133296d9c59df9bb
parent857c4c0a8b2888d806f4308c58f59a6a81a1dee9 (diff)
downloadlinux-dm-6bb38bcc33bf3093c08bd1b71e4f20c82bb60dd1.tar.gz
dm: fix IO splitting
Notice: this object is not reachable from any branch.
Commit 882ec4e609c1 ("dm table: stack 'chunk_sectors' limit to account for target-specific splitting") caused a couple regressions: 1) Using lcm_not_zero() when stacking chunk_sectors was a bug because chunk_sectors must reflect the most limited of all devices in the IO stack. 2) DM targets that set max_io_len but that do _not_ provide an .iterate_devices method no longer had there IO split properly. And commit 5091cdec56fa ("dm: change max_io_len() to use blk_max_size_offset()") also caused a regression where DM no longer supported varied (per target) IO splitting. The implication being the potential for severely reduced performance for IO stacks that use a DM target like dm-cache to hide performance limitations of a slower device (e.g. one that requires 4K IO splitting). Coming full circle: Fix all these issues by discontinuing stacking chunk_sectors up using ti->max_io_len in dm_calculate_queue_limits() and revert max_io_len() back to doing its own specialized splitting rather than using blk_max_size_offset(). Fixes: 882ec4e609c1 ("dm table: stack 'chunk_sectors' limit to account for target-specific splitting") Fixes: 5091cdec56fa ("dm: change max_io_len() to use blk_max_size_offset()") Cc: stable@vger.kernel.org Reported-by: John Dorminy <jdorminy@redhat.com> Reported-by: Bruce Johnston <bjohnsto@redhat.com> Reported-by: Kirill Tkhai <ktkhai@virtuozzo.com> Reviewed-by: John Dorminy <jdorminy@redhat.com> Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Notice: this object is not reachable from any branch.
-rw-r--r--drivers/md/dm-table.c5
-rw-r--r--drivers/md/dm.c27
2 files changed, 16 insertions, 16 deletions
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 2073ee8d18f4f..7eeb7c4169c94 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -18,7 +18,6 @@
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/atomic.h>
-#include <linux/lcm.h>
#include <linux/blk-mq.h>
#include <linux/mount.h>
#include <linux/dax.h>
@@ -1449,10 +1448,6 @@ int dm_calculate_queue_limits(struct dm_table *table,
zone_sectors = ti_limits.chunk_sectors;
}
- /* Stack chunk_sectors if target-specific splitting is required */
- if (ti->max_io_len)
- ti_limits.chunk_sectors = lcm_not_zero(ti->max_io_len,
- ti_limits.chunk_sectors);
/* Set I/O hints portion of queue limits */
if (ti->type->io_hints)
ti->type->io_hints(ti, &ti_limits);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 98866e725f255..c57e3f74cd7d6 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1034,20 +1034,25 @@ static inline sector_t max_io_len_target_boundary(struct dm_target *ti,
static sector_t max_io_len(struct dm_target *ti, sector_t sector)
{
- sector_t target_offset = dm_target_offset(ti, sector);
- sector_t len = max_io_len_target_boundary(ti, target_offset);
- sector_t max_len;
+ sector_t offset = dm_target_offset(ti, sector);
+ sector_t len = max_io_len_target_boundary(ti, offset);
+ sector_t max_io_len = ti->max_io_len;
/*
- * Does the target need to split even further?
- * - q->limits.chunk_sectors reflects ti->max_io_len so
- * blk_max_size_offset() provides required splitting.
- * - blk_max_size_offset() also respects q->limits.max_sectors
+ * Does the target need to split IO even further?
+ * - varied (per target) IO splitting is a tenet of DM; this
+ * explains why stacked chunk_sectors based splitting via
+ * blk_max_size_offset() isn't possible here.
*/
- max_len = blk_max_size_offset(ti->table->md->queue,
- target_offset);
- if (len > max_len)
- len = max_len;
+ if (max_io_len) {
+ if (likely(is_power_of_2(max_io_len)))
+ max_io_len -= offset & (max_io_len - 1);
+ else
+ max_io_len -= sector_div(offset, max_io_len);
+
+ if (len > max_io_len)
+ len = max_io_len;
+ }
return len;
}