qcow2: Add subcluster support to discard_in_l2_slice()

Two things need to be taken into account here:

1) With full_discard == true the L2 entry must be cleared completely.
   This also includes the L2 bitmap if the image has extended L2
   entries.

2) With full_discard == false we have to make the discarded cluster
   read back as zeroes. With normal L2 entries this is done with the
   QCOW_OFLAG_ZERO bit, whereas with extended L2 entries this is done
   with the individual 'all zeroes' bits for each subcluster.

   Note however that QCOW_OFLAG_ZERO is not supported in v2 qcow2
   images so, if there is a backing file, discard cannot guarantee
   that the image will read back as zeroes. If this is important for
   the caller it should forbid it as qcow2_co_pdiscard() does (see
   80f5c01183 for more details).

Signed-off-by: Alberto Garcia <berto@igalia.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-Id: <5ef8274e628aa3ab559bfac467abf488534f2b76.1594396418.git.berto@igalia.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
Alberto Garcia 2020-07-10 18:13:05 +02:00 committed by Max Reitz
parent 205fa50750
commit a68cd70326

View File

@ -1848,11 +1848,17 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset,
assert(nb_clusters <= INT_MAX); assert(nb_clusters <= INT_MAX);
for (i = 0; i < nb_clusters; i++) { for (i = 0; i < nb_clusters; i++) {
uint64_t old_l2_entry; uint64_t old_l2_entry = get_l2_entry(s, l2_slice, l2_index + i);
uint64_t old_l2_bitmap = get_l2_bitmap(s, l2_slice, l2_index + i);
old_l2_entry = get_l2_entry(s, l2_slice, l2_index + i); uint64_t new_l2_entry = old_l2_entry;
uint64_t new_l2_bitmap = old_l2_bitmap;
QCow2ClusterType cluster_type =
qcow2_get_cluster_type(bs, old_l2_entry);
/* /*
* If full_discard is true, the cluster should not read back as zeroes,
* but rather fall through to the backing file.
*
* If full_discard is false, make sure that a discarded area reads back * If full_discard is false, make sure that a discarded area reads back
* as zeroes for v3 images (we cannot do it for v2 without actually * as zeroes for v3 images (we cannot do it for v2 without actually
* writing a zero-filled buffer). We can skip the operation if the * writing a zero-filled buffer). We can skip the operation if the
@ -1861,40 +1867,28 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset,
* *
* TODO We might want to use bdrv_block_status(bs) here, but we're * TODO We might want to use bdrv_block_status(bs) here, but we're
* holding s->lock, so that doesn't work today. * holding s->lock, so that doesn't work today.
*
* If full_discard is true, the sector should not read back as zeroes,
* but rather fall through to the backing file.
*/ */
switch (qcow2_get_cluster_type(bs, old_l2_entry)) { if (full_discard) {
case QCOW2_CLUSTER_UNALLOCATED: new_l2_entry = new_l2_bitmap = 0;
if (full_discard || !bs->backing) { } else if (bs->backing || qcow2_cluster_is_allocated(cluster_type)) {
continue; if (has_subclusters(s)) {
new_l2_entry = 0;
new_l2_bitmap = QCOW_L2_BITMAP_ALL_ZEROES;
} else {
new_l2_entry = s->qcow_version >= 3 ? QCOW_OFLAG_ZERO : 0;
} }
break; }
case QCOW2_CLUSTER_ZERO_PLAIN: if (old_l2_entry == new_l2_entry && old_l2_bitmap == new_l2_bitmap) {
if (!full_discard) { continue;
continue;
}
break;
case QCOW2_CLUSTER_ZERO_ALLOC:
case QCOW2_CLUSTER_NORMAL:
case QCOW2_CLUSTER_COMPRESSED:
break;
default:
abort();
} }
/* First remove L2 entries */ /* First remove L2 entries */
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice); qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_slice);
if (!full_discard && s->qcow_version >= 3) { set_l2_entry(s, l2_slice, l2_index + i, new_l2_entry);
set_l2_entry(s, l2_slice, l2_index + i, QCOW_OFLAG_ZERO); if (has_subclusters(s)) {
} else { set_l2_bitmap(s, l2_slice, l2_index + i, new_l2_bitmap);
set_l2_entry(s, l2_slice, l2_index + i, 0);
} }
/* Then decrease the refcount */ /* Then decrease the refcount */
qcow2_free_any_clusters(bs, old_l2_entry, 1, type); qcow2_free_any_clusters(bs, old_l2_entry, 1, type);
} }