static int discard_single_l2(BlockDriverState *bs, uint64_t offset, unsigned int nb_clusters, enum qcow2_discard_type type, bool full_discard) { BDRVQcow2State *s = bs->opaque; uint64_t *l2_table; int l2_index; int ret; int i; ret = get_cluster_table(bs, offset, &l2_table, &l2_index); if (ret < 0) { return ret; } /* Limit nb_clusters to one L2 table */ nb_clusters = MIN(nb_clusters, s->l2_size - l2_index); for (i = 0; i < nb_clusters; i++) { uint64_t old_l2_entry; old_l2_entry = be64_to_cpu(l2_table[l2_index + i]); /* * 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 * writing a zero-filled buffer). We can skip the operation if the * cluster is already marked as zero, or if it's unallocated and we * don't have a backing file. * * TODO We might want to use bdrv_get_block_status(bs) here, but we're * 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(old_l2_entry)) { case QCOW2_CLUSTER_UNALLOCATED: if (full_discard || !bs->backing_hd) { continue; } break; case QCOW2_CLUSTER_ZERO: if (!full_discard) { continue; } break; case QCOW2_CLUSTER_NORMAL: case QCOW2_CLUSTER_COMPRESSED: break; default: abort(); } /* First remove L2 entries */ qcow2_cache_entry_mark_dirty(bs, s->l2_table_cache, l2_table); if (!full_discard && s->qcow_version >= 3) { l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO); } else { l2_table[l2_index + i] = cpu_to_be64(0); } /* Then decrease the refcount */ qcow2_free_any_clusters(bs, old_l2_entry, 1, type); } qcow2_cache_put(bs, s->l2_table_cache, (void **) &l2_table); return nb_clusters; }