diff mbox series

[v4,11/30] qcow2: Add l2_entry_size()

Message ID fd0f93353a218ff4518f34ebdbca05c2fc0f1085.1584468723.git.berto@igalia.com (mailing list archive)
State New, archived
Headers show
Series Add subcluster allocation to qcow2 | expand

Commit Message

Alberto Garcia March 17, 2020, 6:16 p.m. UTC
qcow2 images with subclusters have 128-bit L2 entries. The first 64
bits contain the same information as traditional images and the last
64 bits form a bitmap with the status of each individual subcluster.

Because of that we cannot assume that L2 entries are sizeof(uint64_t)
anymore. This function returns the proper value for the image.

Signed-off-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
 block/qcow2.h          |  9 +++++++++
 block/qcow2-cluster.c  | 12 ++++++------
 block/qcow2-refcount.c | 14 ++++++++------
 block/qcow2.c          |  8 ++++----
 4 files changed, 27 insertions(+), 16 deletions(-)

Comments

Vladimir Sementsov-Ogievskiy April 14, 2020, 9:44 a.m. UTC | #1
17.03.2020 21:16, Alberto Garcia wrote:
> qcow2 images with subclusters have 128-bit L2 entries. The first 64
> bits contain the same information as traditional images and the last
> 64 bits form a bitmap with the status of each individual subcluster.
> 
> Because of that we cannot assume that L2 entries are sizeof(uint64_t)
> anymore. This function returns the proper value for the image.
> 
> Signed-off-by: Alberto Garcia <berto@igalia.com>
> Reviewed-by: Max Reitz <mreitz@redhat.com>
> ---
>   block/qcow2.h          |  9 +++++++++
>   block/qcow2-cluster.c  | 12 ++++++------
>   block/qcow2-refcount.c | 14 ++++++++------
>   block/qcow2.c          |  8 ++++----
>   4 files changed, 27 insertions(+), 16 deletions(-)
> 
> diff --git a/block/qcow2.h b/block/qcow2.h
> index 06929072d2..1eb4b46807 100644
> --- a/block/qcow2.h
> +++ b/block/qcow2.h
> @@ -80,6 +80,10 @@
>   
>   #define QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER 32
>   
> +/* Size of normal and extended L2 entries */
> +#define L2E_SIZE_NORMAL   (sizeof(uint64_t))
> +#define L2E_SIZE_EXTENDED (sizeof(uint64_t) * 2)
> +
>   #define MIN_CLUSTER_BITS 9
>   #define MAX_CLUSTER_BITS 21
>   
> @@ -506,6 +510,11 @@ static inline bool has_subclusters(BDRVQcow2State *s)
>       return false;
>   }
>   
> +static inline size_t l2_entry_size(BDRVQcow2State *s)
> +{
> +    return has_subclusters(s) ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL;
> +}
> +
>   static inline uint64_t get_l2_entry(BDRVQcow2State *s, uint64_t *l2_slice,
>                                       int idx)
>   {
> diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
> index cd48ab0223..41a23c5305 100644
> --- a/block/qcow2-cluster.c
> +++ b/block/qcow2-cluster.c
> @@ -208,7 +208,7 @@ static int l2_load(BlockDriverState *bs, uint64_t offset,
>                      uint64_t l2_offset, uint64_t **l2_slice)
>   {
>       BDRVQcow2State *s = bs->opaque;
> -    int start_of_slice = sizeof(uint64_t) *
> +    int start_of_slice = l2_entry_size(s) *
>           (offset_to_l2_index(s, offset) - offset_to_l2_slice_index(s, offset));
>   
>       return qcow2_cache_get(bs, s->l2_table_cache, l2_offset + start_of_slice,
> @@ -281,7 +281,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index)
>   
>       /* allocate a new l2 entry */
>   
> -    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
> +    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * l2_entry_size(s));

hmm. s->l2_size * l2_entry_size, isn't it just s->cluster_size always? Maybe, just refactor these things?


>       if (l2_offset < 0) {
>           ret = l2_offset;
>           goto fail;
> @@ -305,7 +305,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index)

[...]

> @@ -1425,7 +1425,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
>           bs->encrypted = true;
>       }
>   
> -    s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
> +    s->l2_bits = s->cluster_bits - ctz32(l2_entry_size(s));
>       s->l2_size = 1 << s->l2_bits;
>       /* 2^(s->refcount_order - 3) is the refcount width in bytes */
>       s->refcount_block_bits = s->cluster_bits - (s->refcount_order - 3);
> @@ -4104,7 +4104,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
>            *  preallocation. All that matters is that we will not have to allocate
>            *  new refcount structures for them.) */
>           nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
> -                                        s->cluster_size / sizeof(uint64_t));
> +                                        s->cluster_size / l2_entry_size(s));

Isn't it just s->l2_size ?

>           /* The cluster range may not be aligned to L2 boundaries, so add one L2
>            * table for a potential head/tail */
>           nb_new_l2_tables++;
> 


Conversions looks correct, but how to check that we have converted everything?

Trying at least

    cd block; git grep 'sizeof(uint64_t)' qcow2* | grep -v 'l1_size \*' | grep -v 'l1_sz \*' | grep -v refcount | grep -v reftable

I found this not converted chunk:

     /* total size of L2 tables */
     nl2e = aligned_total_size / cluster_size;
     nl2e = ROUND_UP(nl2e, cluster_size / sizeof(uint64_t));
     meta_size += nl2e * sizeof(uint64_t);


Hmm. How to avoid it? Maybe, at least, refactor the code, to drop all sizeof(uint64_t), converting them to L2_ENTRY_SIZE, L1_ENTRY_SIZE, REFTABLE_ENTRY_SIZE etc?
And all occurrences of pure '8' (not many of them exist)
Alberto Garcia April 14, 2020, 12:20 p.m. UTC | #2
On Tue 14 Apr 2020 11:44:57 AM CEST, Vladimir Sementsov-Ogievskiy wrote:
>>       /* allocate a new l2 entry */
>>   
>> -    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
>> +    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * l2_entry_size(s));
>
> hmm. s->l2_size * l2_entry_size, isn't it just s->cluster_size always?
> Maybe, just refactor these things?

I think the patch is simpler to follow if I only do the strictly
necessary changes and don't mix them with other things.

>>           nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
>> -                                        s->cluster_size / sizeof(uint64_t));
>> +                                        s->cluster_size / l2_entry_size(s));
>
> Isn't it just s->l2_size ?

Yes, same as before.

>>           /* The cluster range may not be aligned to L2 boundaries, so add one L2
>>            * table for a potential head/tail */
>>           nb_new_l2_tables++;
>
> Conversions looks correct, but how to check that we have converted
> everything?

I went through all cases, I think I didn't miss any!

> I found this not converted chunk:
>
>      /* total size of L2 tables */
>      nl2e = aligned_total_size / cluster_size;
>      nl2e = ROUND_UP(nl2e, cluster_size / sizeof(uint64_t));
>      meta_size += nl2e * sizeof(uint64_t);

This is used by qcow2_measure() and is fixed on a later patch because,
unlike all other cases, it does not use a BlockDriverState to determine
the size of an L2 entry.

> Hmm. How to avoid it? Maybe, at least, refactor the code, to drop all
> sizeof(uint64_t), converting them to L2_ENTRY_SIZE, L1_ENTRY_SIZE,
> REFTABLE_ENTRY_SIZE etc?

That wouldn't be a bad thing I guess but, again, for a separate patch or
series.

> And all occurrences of pure '8' (not many of them exist)

I think most/all nowadays only refer to the number of bits per byte.

Maybe there's a couple that still need to be fixed, but we have been
removing a lot of numeric literals from the qcow2 code (see for example
b6c246942b, 3afea40243 or a35f87f50d).

Berto
Vladimir Sementsov-Ogievskiy April 14, 2020, 12:29 p.m. UTC | #3
14.04.2020 15:20, Alberto Garcia wrote:
> On Tue 14 Apr 2020 11:44:57 AM CEST, Vladimir Sementsov-Ogievskiy wrote:
>>>        /* allocate a new l2 entry */
>>>    
>>> -    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
>>> +    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * l2_entry_size(s));
>>
>> hmm. s->l2_size * l2_entry_size, isn't it just s->cluster_size always?
>> Maybe, just refactor these things?
> 
> I think the patch is simpler to follow if I only do the strictly
> necessary changes and don't mix them with other things.
> 
>>>            nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
>>> -                                        s->cluster_size / sizeof(uint64_t));
>>> +                                        s->cluster_size / l2_entry_size(s));
>>
>> Isn't it just s->l2_size ?
> 
> Yes, same as before.
> 
>>>            /* The cluster range may not be aligned to L2 boundaries, so add one L2
>>>             * table for a potential head/tail */
>>>            nb_new_l2_tables++;
>>
>> Conversions looks correct, but how to check that we have converted
>> everything?
> 
> I went through all cases, I think I didn't miss any!
> 
>> I found this not converted chunk:
>>
>>       /* total size of L2 tables */
>>       nl2e = aligned_total_size / cluster_size;
>>       nl2e = ROUND_UP(nl2e, cluster_size / sizeof(uint64_t));
>>       meta_size += nl2e * sizeof(uint64_t);
> 
> This is used by qcow2_measure() and is fixed on a later patch because,
> unlike all other cases, it does not use a BlockDriverState to determine
> the size of an L2 entry.
> 
>> Hmm. How to avoid it? Maybe, at least, refactor the code, to drop all
>> sizeof(uint64_t), converting them to L2_ENTRY_SIZE, L1_ENTRY_SIZE,
>> REFTABLE_ENTRY_SIZE etc?
> 
> That wouldn't be a bad thing I guess but, again, for a separate patch or
> series.
> 
>> And all occurrences of pure '8' (not many of them exist)
> 
> I think most/all nowadays only refer to the number of bits per byte.
> 
> Maybe there's a couple that still need to be fixed, but we have been
> removing a lot of numeric literals from the qcow2 code (see for example
> b6c246942b, 3afea40243 or a35f87f50d).
> 


git grep '\<8\>' block/qcow2*

shows at least

qcow2-cluster.c:            s->l1_table_offset + 8 * l1_start_index, bufsize, false);
qcow2-cluster.c:                           s->l1_table_offset + 8 * l1_start_index,
Alberto Garcia April 14, 2020, 12:33 p.m. UTC | #4
On Tue 14 Apr 2020 02:29:13 PM CEST, Vladimir Sementsov-Ogievskiy wrote:
>>> Hmm. How to avoid it? Maybe, at least, refactor the code, to drop all
>>> sizeof(uint64_t), converting them to L2_ENTRY_SIZE, L1_ENTRY_SIZE,
>>> REFTABLE_ENTRY_SIZE etc?
>> 
>> That wouldn't be a bad thing I guess but, again, for a separate patch or
>> series.
>> 
>>> And all occurrences of pure '8' (not many of them exist)
>> 
>> I think most/all nowadays only refer to the number of bits per byte.
>> 
>> Maybe there's a couple that still need to be fixed, but we have been
>> removing a lot of numeric literals from the qcow2 code (see for example
>> b6c246942b, 3afea40243 or a35f87f50d).
>> 
>
>
> git grep '\<8\>' block/qcow2*
>
> shows at least
>
> qcow2-cluster.c:            s->l1_table_offset + 8 * l1_start_index, bufsize, false);
> qcow2-cluster.c:                           s->l1_table_offset + 8 * l1_start_index,

I see, worth replacing with L1_ENTRY_SIZE as you suggest. I can take of
writing the patches if you want.

Berto
Vladimir Sementsov-Ogievskiy April 14, 2020, 12:39 p.m. UTC | #5
14.04.2020 15:33, Alberto Garcia wrote:
> On Tue 14 Apr 2020 02:29:13 PM CEST, Vladimir Sementsov-Ogievskiy wrote:
>>>> Hmm. How to avoid it? Maybe, at least, refactor the code, to drop all
>>>> sizeof(uint64_t), converting them to L2_ENTRY_SIZE, L1_ENTRY_SIZE,
>>>> REFTABLE_ENTRY_SIZE etc?
>>>
>>> That wouldn't be a bad thing I guess but, again, for a separate patch or
>>> series.
>>>
>>>> And all occurrences of pure '8' (not many of them exist)
>>>
>>> I think most/all nowadays only refer to the number of bits per byte.
>>>
>>> Maybe there's a couple that still need to be fixed, but we have been
>>> removing a lot of numeric literals from the qcow2 code (see for example
>>> b6c246942b, 3afea40243 or a35f87f50d).
>>>
>>
>>
>> git grep '\<8\>' block/qcow2*
>>
>> shows at least
>>
>> qcow2-cluster.c:            s->l1_table_offset + 8 * l1_start_index, bufsize, false);
>> qcow2-cluster.c:                           s->l1_table_offset + 8 * l1_start_index,
> 
> I see, worth replacing with L1_ENTRY_SIZE as you suggest. I can take of
> writing the patches if you want.
> 

That would be great, if not too burdensome :)
Eric Blake April 14, 2020, 4:01 p.m. UTC | #6
On 4/14/20 7:20 AM, Alberto Garcia wrote:

>> Hmm. How to avoid it? Maybe, at least, refactor the code, to drop all
>> sizeof(uint64_t), converting them to L2_ENTRY_SIZE, L1_ENTRY_SIZE,
>> REFTABLE_ENTRY_SIZE etc?
> 
> That wouldn't be a bad thing I guess but, again, for a separate patch or
> series.
> 
>> And all occurrences of pure '8' (not many of them exist)
> 
> I think most/all nowadays only refer to the number of bits per byte.

CHAR_BIT (from <limits.h>) is good for that.

> 
> Maybe there's a couple that still need to be fixed, but we have been
> removing a lot of numeric literals from the qcow2 code (see for example
> b6c246942b, 3afea40243 or a35f87f50d).
> 
> Berto
>
Alberto Garcia April 14, 2020, 4:16 p.m. UTC | #7
On Tue 14 Apr 2020 06:01:42 PM CEST, Eric Blake <eblake@redhat.com> wrote:
>>> And all occurrences of pure '8' (not many of them exist)
>> 
>> I think most/all nowadays only refer to the number of bits per byte.
>
> CHAR_BIT (from <limits.h>) is good for that.

Wow, ok, I wonder if that actually makes the code more readable, but
I'll take it into account when writing the patch, thanks.

Berto
diff mbox series

Patch

diff --git a/block/qcow2.h b/block/qcow2.h
index 06929072d2..1eb4b46807 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -80,6 +80,10 @@ 
 
 #define QCOW_EXTL2_SUBCLUSTERS_PER_CLUSTER 32
 
+/* Size of normal and extended L2 entries */
+#define L2E_SIZE_NORMAL   (sizeof(uint64_t))
+#define L2E_SIZE_EXTENDED (sizeof(uint64_t) * 2)
+
 #define MIN_CLUSTER_BITS 9
 #define MAX_CLUSTER_BITS 21
 
@@ -506,6 +510,11 @@  static inline bool has_subclusters(BDRVQcow2State *s)
     return false;
 }
 
+static inline size_t l2_entry_size(BDRVQcow2State *s)
+{
+    return has_subclusters(s) ? L2E_SIZE_EXTENDED : L2E_SIZE_NORMAL;
+}
+
 static inline uint64_t get_l2_entry(BDRVQcow2State *s, uint64_t *l2_slice,
                                     int idx)
 {
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index cd48ab0223..41a23c5305 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -208,7 +208,7 @@  static int l2_load(BlockDriverState *bs, uint64_t offset,
                    uint64_t l2_offset, uint64_t **l2_slice)
 {
     BDRVQcow2State *s = bs->opaque;
-    int start_of_slice = sizeof(uint64_t) *
+    int start_of_slice = l2_entry_size(s) *
         (offset_to_l2_index(s, offset) - offset_to_l2_slice_index(s, offset));
 
     return qcow2_cache_get(bs, s->l2_table_cache, l2_offset + start_of_slice,
@@ -281,7 +281,7 @@  static int l2_allocate(BlockDriverState *bs, int l1_index)
 
     /* allocate a new l2 entry */
 
-    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
+    l2_offset = qcow2_alloc_clusters(bs, s->l2_size * l2_entry_size(s));
     if (l2_offset < 0) {
         ret = l2_offset;
         goto fail;
@@ -305,7 +305,7 @@  static int l2_allocate(BlockDriverState *bs, int l1_index)
 
     /* allocate a new entry in the l2 cache */
 
-    slice_size2 = s->l2_slice_size * sizeof(uint64_t);
+    slice_size2 = s->l2_slice_size * l2_entry_size(s);
     n_slices = s->cluster_size / slice_size2;
 
     trace_qcow2_l2_allocate_get_empty(bs, l1_index);
@@ -369,7 +369,7 @@  fail:
     }
     s->l1_table[l1_index] = old_l2_offset;
     if (l2_offset > 0) {
-        qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t),
+        qcow2_free_clusters(bs, l2_offset, s->l2_size * l2_entry_size(s),
                             QCOW2_DISCARD_ALWAYS);
     }
     return ret;
@@ -718,7 +718,7 @@  static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
 
         /* Then decrease the refcount of the old table */
         if (l2_offset) {
-            qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t),
+            qcow2_free_clusters(bs, l2_offset, s->l2_size * l2_entry_size(s),
                                 QCOW2_DISCARD_OTHER);
         }
 
@@ -1919,7 +1919,7 @@  static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table,
     int ret;
     int i, j;
 
-    slice_size2 = s->l2_slice_size * sizeof(uint64_t);
+    slice_size2 = s->l2_slice_size * l2_entry_size(s);
     n_slices = s->cluster_size / slice_size2;
 
     if (!is_active_l1) {
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 141e4fdcb1..3b89a97fd0 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -1254,7 +1254,7 @@  int qcow2_update_snapshot_refcount(BlockDriverState *bs,
     l2_slice = NULL;
     l1_table = NULL;
     l1_size2 = l1_size * sizeof(uint64_t);
-    slice_size2 = s->l2_slice_size * sizeof(uint64_t);
+    slice_size2 = s->l2_slice_size * l2_entry_size(s);
     n_slices = s->cluster_size / slice_size2;
 
     s->cache_discards = true;
@@ -1605,7 +1605,7 @@  static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
     int i, l2_size, nb_csectors, ret;
 
     /* Read L2 table from disk */
-    l2_size = s->l2_size * sizeof(uint64_t);
+    l2_size = s->l2_size * l2_entry_size(s);
     l2_table = g_malloc(l2_size);
 
     ret = bdrv_pread(bs->file, l2_offset, l2_table, l2_size);
@@ -1680,15 +1680,16 @@  static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
                             fix & BDRV_FIX_ERRORS ? "Repairing" : "ERROR",
                             offset);
                     if (fix & BDRV_FIX_ERRORS) {
+                        int idx = i * (l2_entry_size(s) / sizeof(uint64_t));
                         uint64_t l2e_offset =
-                            l2_offset + (uint64_t)i * sizeof(uint64_t);
+                            l2_offset + (uint64_t)i * l2_entry_size(s);
                         int ign = active ? QCOW2_OL_ACTIVE_L2 :
                                            QCOW2_OL_INACTIVE_L2;
 
                         l2_entry = QCOW_OFLAG_ZERO;
                         set_l2_entry(s, l2_table, i, l2_entry);
                         ret = qcow2_pre_write_overlap_check(bs, ign,
-                                l2e_offset, sizeof(uint64_t), false);
+                                l2e_offset, l2_entry_size(s), false);
                         if (ret < 0) {
                             fprintf(stderr, "ERROR: Overlap check failed\n");
                             res->check_errors++;
@@ -1698,7 +1699,8 @@  static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
                         }
 
                         ret = bdrv_pwrite_sync(bs->file, l2e_offset,
-                                               &l2_table[i], sizeof(uint64_t));
+                                               &l2_table[idx],
+                                               l2_entry_size(s));
                         if (ret < 0) {
                             fprintf(stderr, "ERROR: Failed to overwrite L2 "
                                     "table entry: %s\n", strerror(-ret));
@@ -1905,7 +1907,7 @@  static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
         }
 
         ret = bdrv_pread(bs->file, l2_offset, l2_table,
-                         s->l2_size * sizeof(uint64_t));
+                         s->l2_size * l2_entry_size(s));
         if (ret < 0) {
             fprintf(stderr, "ERROR: Could not read L2 table: %s\n",
                     strerror(-ret));
diff --git a/block/qcow2.c b/block/qcow2.c
index 239e0ad3d9..d3b8581aed 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -870,7 +870,7 @@  static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
     uint64_t max_l2_entries = DIV_ROUND_UP(virtual_disk_size, s->cluster_size);
     /* An L2 table is always one cluster in size so the max cache size
      * should be a multiple of the cluster size. */
-    uint64_t max_l2_cache = ROUND_UP(max_l2_entries * sizeof(uint64_t),
+    uint64_t max_l2_cache = ROUND_UP(max_l2_entries * l2_entry_size(s),
                                      s->cluster_size);
 
     combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE);
@@ -1031,7 +1031,7 @@  static int qcow2_update_options_prepare(BlockDriverState *bs,
         }
     }
 
-    r->l2_slice_size = l2_cache_entry_size / sizeof(uint64_t);
+    r->l2_slice_size = l2_cache_entry_size / l2_entry_size(s);
     r->l2_table_cache = qcow2_cache_create(bs, l2_cache_size,
                                            l2_cache_entry_size);
     r->refcount_block_cache = qcow2_cache_create(bs, refcount_cache_size,
@@ -1425,7 +1425,7 @@  static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
         bs->encrypted = true;
     }
 
-    s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
+    s->l2_bits = s->cluster_bits - ctz32(l2_entry_size(s));
     s->l2_size = 1 << s->l2_bits;
     /* 2^(s->refcount_order - 3) is the refcount width in bytes */
     s->refcount_block_bits = s->cluster_bits - (s->refcount_order - 3);
@@ -4104,7 +4104,7 @@  static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
          *  preallocation. All that matters is that we will not have to allocate
          *  new refcount structures for them.) */
         nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
-                                        s->cluster_size / sizeof(uint64_t));
+                                        s->cluster_size / l2_entry_size(s));
         /* The cluster range may not be aligned to L2 boundaries, so add one L2
          * table for a potential head/tail */
         nb_new_l2_tables++;