1a21aa
From 06cafbbbbff0aae3634eb2908d25d0dc46c2048b Mon Sep 17 00:00:00 2001
1a21aa
From: Vojtech Trefny <vtrefny@redhat.com>
1a21aa
Date: Tue, 9 Nov 2021 15:52:48 +0100
1a21aa
Subject: [PATCH] Use bigger chunk size for thinpools bigger than ~15.88 TiB
1a21aa
1a21aa
With our default chunk size of 64 KiB we cannot create bigger
1a21aa
thin pools than 15.88 TiB. Unfortunately we need to specify chunk
1a21aa
size to be able to calculate thin metadata properly so we can't
1a21aa
simply leave this to LVM to determine the correct chunk size.
1a21aa
---
1a21aa
 blivet/devicelibs/lvm.py       | 11 +++++++++++
1a21aa
 blivet/devices/lvm.py          |  6 +++---
1a21aa
 tests/devices_test/lvm_test.py | 11 +++++++++++
1a21aa
 3 files changed, 25 insertions(+), 3 deletions(-)
1a21aa
1a21aa
diff --git a/blivet/devicelibs/lvm.py b/blivet/devicelibs/lvm.py
1a21aa
index d56a76ed..cb6f655e 100644
1a21aa
--- a/blivet/devicelibs/lvm.py
1a21aa
+++ b/blivet/devicelibs/lvm.py
1a21aa
@@ -20,6 +20,7 @@
1a21aa
 # Author(s): Dave Lehman <dlehman@redhat.com>
1a21aa
 #
1a21aa
 
1a21aa
+import math
1a21aa
 import os
1a21aa
 import re
1a21aa
 
1a21aa
@@ -51,6 +52,7 @@ LVM_THINP_MIN_METADATA_SIZE = Size("2 MiB")
1a21aa
 LVM_THINP_MAX_METADATA_SIZE = Size("16 GiB")
1a21aa
 LVM_THINP_MIN_CHUNK_SIZE = Size("64 KiB")
1a21aa
 LVM_THINP_MAX_CHUNK_SIZE = Size("1 GiB")
1a21aa
+LVM_THINP_ADDRESSABLE_CHUNK_SIZE = Size("17455015526400 B")  # 15.88 TiB
1a21aa
 
1a21aa
 raid_levels = raid.RAIDLevels(["linear", "striped", "raid1", "raid4", "raid5", "raid6", "raid10"])
1a21aa
 raid_seg_types = list(itertools.chain.from_iterable([level.names for level in raid_levels if level.name != "linear"]))
1a21aa
@@ -225,3 +227,12 @@ def is_lvm_name_valid(name):
1a21aa
         return False
1a21aa
 
1a21aa
     return True
1a21aa
+
1a21aa
+
1a21aa
+def recommend_thpool_chunk_size(thpool_size):
1a21aa
+    # calculation of the recommended chunk size by LVM is so complicated that we
1a21aa
+    # can't really replicate it, but we know that 64 KiB chunk size gives us
1a21aa
+    # upper limit of ~15.88 TiB so we will just add 64 KiB to the chunk size
1a21aa
+    # for every ~15.88 TiB of thinpool data size
1a21aa
+    return min(math.ceil(thpool_size / LVM_THINP_ADDRESSABLE_CHUNK_SIZE) * LVM_THINP_MIN_CHUNK_SIZE,
1a21aa
+               LVM_THINP_MAX_CHUNK_SIZE)
1a21aa
diff --git a/blivet/devices/lvm.py b/blivet/devices/lvm.py
1a21aa
index 51d785d9..c61eeb4b 100644
1a21aa
--- a/blivet/devices/lvm.py
1a21aa
+++ b/blivet/devices/lvm.py
1a21aa
@@ -1634,9 +1634,9 @@ class LVMThinPoolMixin(object):
1a21aa
             return
1a21aa
 
1a21aa
         # we need to know chunk size to calculate recommended metadata size
1a21aa
-        if self._chunk_size == 0:
1a21aa
-            self._chunk_size = Size(blockdev.LVM_DEFAULT_CHUNK_SIZE)
1a21aa
-            log.debug("Using default chunk size: %s", self._chunk_size)
1a21aa
+        if self._chunk_size == 0 or enforced:
1a21aa
+            self._chunk_size = lvm.recommend_thpool_chunk_size(self._size)
1a21aa
+            log.debug("Using recommended chunk size: %s", self._chunk_size)
1a21aa
 
1a21aa
         old_md_size = self._metadata_size
1a21aa
         old_pmspare_size = self.vg.pmspare_size
1a21aa
diff --git a/tests/devices_test/lvm_test.py b/tests/devices_test/lvm_test.py
1a21aa
index 4156d0bf..336c5b99 100644
1a21aa
--- a/tests/devices_test/lvm_test.py
1a21aa
+++ b/tests/devices_test/lvm_test.py
1a21aa
@@ -442,6 +442,17 @@ class LVMDeviceTest(unittest.TestCase):
1a21aa
             self.assertFalse(pool.exists)
1a21aa
             self.assertTrue(lvm.lvremove.called)
1a21aa
 
1a21aa
+    def test_lvmthinpool_chunk_size(self):
1a21aa
+        pv = StorageDevice("pv1", fmt=blivet.formats.get_format("lvmpv"),
1a21aa
+                           size=Size("100 TiB"))
1a21aa
+        vg = LVMVolumeGroupDevice("testvg", parents=[pv])
1a21aa
+        pool = LVMLogicalVolumeDevice("pool1", parents=[vg], size=Size("500 MiB"), seg_type="thin-pool")
1a21aa
+        self.assertEqual(pool.chunk_size, Size("64 KiB"))
1a21aa
+
1a21aa
+        pool.size = Size("16 TiB")
1a21aa
+        pool.autoset_md_size(enforced=True)
1a21aa
+        self.assertEqual(pool.chunk_size, Size("128 KiB"))
1a21aa
+
1a21aa
 
1a21aa
 class TypeSpecificCallsTest(unittest.TestCase):
1a21aa
     def test_type_specific_calls(self):
1a21aa
-- 
1a21aa
2.31.1
1a21aa