The atomic write unit max value is limited by any stacked device stripe
size.

It is required that the atomic write unit is a power-of-2 factor of the
stripe size.

Currently we use io_min limit to hold the stripe size, and check for a
io_min <= SECTOR_SIZE when deciding if we have a striped stacked device.

Nilay reports that this causes a problem when the physical block size is
greater than SECTOR_SIZE [0].

Furthermore, io_min may be mutated when stacking devices, and this makes
it a poor candidate to hold the stripe size. Such an example (of when
io_min may change) would be when the io_min is less than the physical
block size.

Use chunk_sectors to hold the stripe size, which is more appropriate.

[0] 
https://lore.kernel.org/linux-block/888f3b1d-7817-4007-b3b3-1a2ea04df...@linux.ibm.com/T/#mecca17129f72811137d3c2f1e477634e77f06781

Reviewed-by: Nilay Shroff <ni...@linux.ibm.com>
Tested-by: Nilay Shroff <ni...@linux.ibm.com>
Signed-off-by: John Garry <john.g.ga...@oracle.com>
---
 block/blk-settings.c | 56 ++++++++++++++++++++++++++------------------
 1 file changed, 33 insertions(+), 23 deletions(-)

diff --git a/block/blk-settings.c b/block/blk-settings.c
index 3425ae1b1f014..a6ac293f47e34 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -595,41 +595,50 @@ static bool blk_stack_atomic_writes_boundary_head(struct 
queue_limits *t,
        return true;
 }
 
-
-/* Check stacking of first bottom device */
-static bool blk_stack_atomic_writes_head(struct queue_limits *t,
-                               struct queue_limits *b)
+static void blk_stack_atomic_writes_chunk_sectors(struct queue_limits *t)
 {
-       if (b->atomic_write_hw_boundary &&
-           !blk_stack_atomic_writes_boundary_head(t, b))
-               return false;
+       unsigned int chunk_bytes;
 
-       if (t->io_min <= SECTOR_SIZE) {
-               /* No chunk sectors, so use bottom device values directly */
-               t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max;
-               t->atomic_write_hw_unit_min = b->atomic_write_hw_unit_min;
-               t->atomic_write_hw_max = b->atomic_write_hw_max;
-               return true;
-       }
+       if (!t->chunk_sectors)
+               return;
+
+       /*
+        * If chunk sectors is so large that its value in bytes overflows
+        * UINT_MAX, then just shift it down so it definitely will fit.
+        * We don't support atomic writes of such a large size anyway.
+        */
+       if (check_shl_overflow(t->chunk_sectors, SECTOR_SHIFT, &chunk_bytes))
+               chunk_bytes = t->chunk_sectors;
 
        /*
         * Find values for limits which work for chunk size.
         * b->atomic_write_hw_unit_{min, max} may not be aligned with chunk
-        * size (t->io_min), as chunk size is not restricted to a power-of-2.
+        * size, as the chunk size is not restricted to a power-of-2.
         * So we need to find highest power-of-2 which works for the chunk
         * size.
-        * As an example scenario, we could have b->unit_max = 16K and
-        * t->io_min = 24K. For this case, reduce t->unit_max to a value
-        * aligned with both limits, i.e. 8K in this example.
+        * As an example scenario, we could have t->unit_max = 16K and
+        * t->chunk_sectors = 24KB. For this case, reduce t->unit_max to a
+        * value aligned with both limits, i.e. 8K in this example.
         */
-       t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max;
-       while (t->io_min % t->atomic_write_hw_unit_max)
-               t->atomic_write_hw_unit_max /= 2;
+       t->atomic_write_hw_unit_max = min(t->atomic_write_hw_unit_max,
+                                       max_pow_of_two_factor(chunk_bytes));
 
-       t->atomic_write_hw_unit_min = min(b->atomic_write_hw_unit_min,
+       t->atomic_write_hw_unit_min = min(t->atomic_write_hw_unit_min,
                                          t->atomic_write_hw_unit_max);
-       t->atomic_write_hw_max = min(b->atomic_write_hw_max, t->io_min);
+       t->atomic_write_hw_max = min(t->atomic_write_hw_max, chunk_bytes);
+}
+
+/* Check stacking of first bottom device */
+static bool blk_stack_atomic_writes_head(struct queue_limits *t,
+                               struct queue_limits *b)
+{
+       if (b->atomic_write_hw_boundary &&
+           !blk_stack_atomic_writes_boundary_head(t, b))
+               return false;
 
+       t->atomic_write_hw_unit_max = b->atomic_write_hw_unit_max;
+       t->atomic_write_hw_unit_min = b->atomic_write_hw_unit_min;
+       t->atomic_write_hw_max = b->atomic_write_hw_max;
        return true;
 }
 
@@ -657,6 +666,7 @@ static void blk_stack_atomic_writes_limits(struct 
queue_limits *t,
 
        if (!blk_stack_atomic_writes_head(t, b))
                goto unsupported;
+       blk_stack_atomic_writes_chunk_sectors(t);
        return;
 
 unsupported:
-- 
2.43.5


Reply via email to