d52dce
From 16db72b7adc5e1a295ecd52c0a53ee5a12111878 Mon Sep 17 00:00:00 2001
d52dce
From: David Lehman <dlehman@redhat.com>
d52dce
Date: Tue, 7 Jan 2020 17:10:24 -0500
d52dce
Subject: [PATCH 1/2] Make minimal and optimal alignment getters public.
d52dce
d52dce
Related: rhbz#1781106
d52dce
---
d52dce
 blivet/formats/disklabel.py          | 10 +++++-----
d52dce
 tests/formats_test/disklabel_test.py |  6 +++---
d52dce
 2 files changed, 8 insertions(+), 8 deletions(-)
d52dce
d52dce
diff --git a/blivet/formats/disklabel.py b/blivet/formats/disklabel.py
d52dce
index a435bc59..a3f9d04b 100644
d52dce
--- a/blivet/formats/disklabel.py
d52dce
+++ b/blivet/formats/disklabel.py
d52dce
@@ -462,7 +462,7 @@ class DiskLabel(DeviceFormat):
d52dce
 
d52dce
         return self._disk_label_alignment
d52dce
 
d52dce
-    def _get_minimal_alignment(self):
d52dce
+    def get_minimal_alignment(self):
d52dce
         """ Return the device's minimal alignment for new partitions.
d52dce
 
d52dce
             :rtype: :class:`parted.Alignment`
d52dce
@@ -484,7 +484,7 @@ class DiskLabel(DeviceFormat):
d52dce
 
d52dce
         return self._minimal_alignment
d52dce
 
d52dce
-    def _get_optimal_alignment(self):
d52dce
+    def get_optimal_alignment(self):
d52dce
         """ Return the device's optimal alignment for new partitions.
d52dce
 
d52dce
             :rtype: :class:`parted.Alignment`
d52dce
@@ -502,7 +502,7 @@ class DiskLabel(DeviceFormat):
d52dce
                 # if there is no optimal alignment, use the minimal alignment,
d52dce
                 # which has already been intersected with the disklabel
d52dce
                 # alignment
d52dce
-                alignment = self._get_minimal_alignment()
d52dce
+                alignment = self.get_minimal_alignment()
d52dce
             else:
d52dce
                 try:
d52dce
                     alignment = optimal_alignment.intersect(disklabel_alignment)
d52dce
@@ -524,13 +524,13 @@ class DiskLabel(DeviceFormat):
d52dce
                                                          small to be aligned
d52dce
         """
d52dce
         # default to the optimal alignment
d52dce
-        alignment = self._get_optimal_alignment()
d52dce
+        alignment = self.get_optimal_alignment()
d52dce
         if size is None:
d52dce
             return alignment
d52dce
 
d52dce
         # use the minimal alignment if the requested size is smaller than the
d52dce
         # optimal io size
d52dce
-        minimal_alignment = self._get_minimal_alignment()
d52dce
+        minimal_alignment = self.get_minimal_alignment()
d52dce
         optimal_grain_size = Size(alignment.grainSize * self.sector_size)
d52dce
         minimal_grain_size = Size(minimal_alignment.grainSize * self.sector_size)
d52dce
         if size < minimal_grain_size:
d52dce
diff --git a/tests/formats_test/disklabel_test.py b/tests/formats_test/disklabel_test.py
d52dce
index 93ce8c4a..6a1187e1 100644
d52dce
--- a/tests/formats_test/disklabel_test.py
d52dce
+++ b/tests/formats_test/disklabel_test.py
d52dce
@@ -41,8 +41,8 @@ class DiskLabelTestCase(unittest.TestCase):
d52dce
 
d52dce
         # make sure the private methods all return the expected values
d52dce
         self.assertEqual(dl._get_disk_label_alignment(), disklabel_alignment)
d52dce
-        self.assertEqual(dl._get_minimal_alignment(), minimal_alignment)
d52dce
-        self.assertEqual(dl._get_optimal_alignment(), optimal_alignment)
d52dce
+        self.assertEqual(dl.get_minimal_alignment(), minimal_alignment)
d52dce
+        self.assertEqual(dl.get_optimal_alignment(), optimal_alignment)
d52dce
 
d52dce
         # validate result when passing a start alignment to get_end_alignment
d52dce
         self.assertEqual(dl.get_end_alignment(alignment=optimal_alignment),
d52dce
@@ -61,7 +61,7 @@ class DiskLabelTestCase(unittest.TestCase):
d52dce
                          minimal_end_alignment)
d52dce
 
d52dce
         # test the old deprecated properties' values
d52dce
-        self.assertEqual(dl.alignment, dl._get_optimal_alignment())
d52dce
+        self.assertEqual(dl.alignment, dl.get_optimal_alignment())
d52dce
         self.assertEqual(dl.end_alignment, dl.get_end_alignment())
d52dce
 
d52dce
     @patch("blivet.formats.disklabel.arch")
d52dce
-- 
d52dce
2.24.1
d52dce
d52dce
d52dce
From f5810a412048bd445dbed02ce0d01e50a1d083ec Mon Sep 17 00:00:00 2001
d52dce
From: David Lehman <dlehman@redhat.com>
d52dce
Date: Tue, 7 Jan 2020 17:11:43 -0500
d52dce
Subject: [PATCH 2/2] Align base sizes up if smaller than min I/O size.
d52dce
d52dce
Resolves: rhbz#1781106
d52dce
---
d52dce
 blivet/partitioning.py     | 18 +++++++++++++++---
d52dce
 tests/partitioning_test.py | 34 ++++++++++++++++++++++++++++++++++
d52dce
 2 files changed, 49 insertions(+), 3 deletions(-)
d52dce
d52dce
diff --git a/blivet/partitioning.py b/blivet/partitioning.py
d52dce
index 026a3f8c..bc0fe237 100644
d52dce
--- a/blivet/partitioning.py
d52dce
+++ b/blivet/partitioning.py
d52dce
@@ -408,7 +408,11 @@ def add_partition(disklabel, free, part_type, size, start=None, end=None):
d52dce
         else:
d52dce
             _size = size
d52dce
 
d52dce
-        alignment = disklabel.get_alignment(size=_size)
d52dce
+        try:
d52dce
+            alignment = disklabel.get_alignment(size=_size)
d52dce
+        except AlignmentError:
d52dce
+            alignment = disklabel.get_minimal_alignment()
d52dce
+
d52dce
         end_alignment = disklabel.get_end_alignment(alignment=alignment)
d52dce
     else:
d52dce
         alignment = parted.Alignment(grainSize=1, offset=0)
d52dce
@@ -646,7 +650,12 @@ def do_partitioning(storage, boot_disk=None):
d52dce
 
d52dce
 def align_size_for_disklabel(size, disklabel):
d52dce
     # Align the base size to the disk's grain size.
d52dce
-    grain_size = Size(disklabel.alignment.grainSize)
d52dce
+    try:
d52dce
+        alignment = disklabel.get_alignment(size=size)
d52dce
+    except AlignmentError:
d52dce
+        alignment = disklabel.get_minimal_alignment()
d52dce
+
d52dce
+    grain_size = Size(alignment.grainSize)
d52dce
     grains, rem = divmod(size, grain_size)
d52dce
     return (grains * grain_size) + (grain_size if rem else Size(0))
d52dce
 
d52dce
@@ -751,7 +760,10 @@ def allocate_partitions(storage, disks, partitions, freespace, boot_disk=None):
d52dce
             disklabel = disklabels[_disk.path]
d52dce
             best = None
d52dce
             current_free = free
d52dce
-            alignment = disklabel.get_alignment(size=_part.req_size)
d52dce
+            try:
d52dce
+                alignment = disklabel.get_alignment(size=_part.req_size)
d52dce
+            except AlignmentError:
d52dce
+                alignment = disklabel.get_minimal_alignment()
d52dce
 
d52dce
             # for growable requests, we don't want to pass the current free
d52dce
             # geometry to get_best_free_region -- this allows us to try the
d52dce
diff --git a/tests/partitioning_test.py b/tests/partitioning_test.py
d52dce
index ebd05260..4fe87ebe 100644
d52dce
--- a/tests/partitioning_test.py
d52dce
+++ b/tests/partitioning_test.py
d52dce
@@ -179,6 +179,8 @@ class PartitioningTestCase(unittest.TestCase):
d52dce
             min_str = 'parted.Device.minimumAlignment'
d52dce
             opt_al = parted.Alignment(offset=0, grainSize=8192)  # 4 MiB
d52dce
             min_al = parted.Alignment(offset=0, grainSize=2048)  # 1 MiB
d52dce
+            disk.format._minimal_alignment = None  # drop cache
d52dce
+            disk.format._optimal_alignment = None  # drop cache
d52dce
             with patch(opt_str, opt_al) as optimal, patch(min_str, min_al) as minimal:
d52dce
                 optimal_end = disk.format.get_end_alignment(alignment=optimal)
d52dce
                 minimal_end = disk.format.get_end_alignment(alignment=minimal)
d52dce
@@ -201,6 +203,38 @@ class PartitioningTestCase(unittest.TestCase):
d52dce
                 disk.format.remove_partition(part)
d52dce
                 self.assertEqual(len(disk.format.partitions), 0)
d52dce
 
d52dce
+            #
d52dce
+            # adding a partition smaller than the minimal io size should yield
d52dce
+            # a partition whose size is aligned up to the minimal io size
d52dce
+            #
d52dce
+            opt_str = 'parted.Device.optimumAlignment'
d52dce
+            min_str = 'parted.Device.minimumAlignment'
d52dce
+            opt_al = parted.Alignment(offset=0, grainSize=8192)  # 4 MiB
d52dce
+            min_al = parted.Alignment(offset=0, grainSize=2048)  # 1 MiB
d52dce
+            disk.format._minimal_alignment = None  # drop cache
d52dce
+            disk.format._optimal_alignment = None  # drop cache
d52dce
+            with patch(opt_str, opt_al) as optimal, patch(min_str, min_al) as minimal:
d52dce
+                optimal_end = disk.format.get_end_alignment(alignment=optimal)
d52dce
+                minimal_end = disk.format.get_end_alignment(alignment=minimal)
d52dce
+
d52dce
+                sector_size = Size(disk.format.sector_size)
d52dce
+                length = 1024  # 512 KiB
d52dce
+                size = Size(sector_size * length)
d52dce
+                part = add_partition(disk.format, free, parted.PARTITION_NORMAL,
d52dce
+                                     size)
d52dce
+                self.assertEqual(part.geometry.length, min_al.grainSize)
d52dce
+                self.assertEqual(optimal.isAligned(free, part.geometry.start),
d52dce
+                                 False)
d52dce
+                self.assertEqual(minimal.isAligned(free, part.geometry.start),
d52dce
+                                 True)
d52dce
+                self.assertEqual(optimal_end.isAligned(free, part.geometry.end),
d52dce
+                                 False)
d52dce
+                self.assertEqual(minimal_end.isAligned(free, part.geometry.end),
d52dce
+                                 True)
d52dce
+
d52dce
+                disk.format.remove_partition(part)
d52dce
+                self.assertEqual(len(disk.format.partitions), 0)
d52dce
+
d52dce
             #
d52dce
             # add a partition with an unaligned start sector
d52dce
             #
d52dce
-- 
d52dce
2.24.1
d52dce