From patchwork Fri Jan 28 11:41:50 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin Wolf X-Patchwork-Id: 80832 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 902B5B710E for ; Fri, 28 Jan 2011 22:42:37 +1100 (EST) Received: from localhost ([127.0.0.1]:40788 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pimhb-0006RY-Qf for incoming@patchwork.ozlabs.org; Fri, 28 Jan 2011 06:41:19 -0500 Received: from [140.186.70.92] (port=53903 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Pimgi-0006R7-2U for qemu-devel@nongnu.org; Fri, 28 Jan 2011 06:40:25 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Pimgg-00043j-Tc for qemu-devel@nongnu.org; Fri, 28 Jan 2011 06:40:23 -0500 Received: from mx1.redhat.com ([209.132.183.28]:14475) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Pimgg-00043O-Km for qemu-devel@nongnu.org; Fri, 28 Jan 2011 06:40:22 -0500 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id p0SBeK8N001127 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 28 Jan 2011 06:40:20 -0500 Received: from dhcp-5-188.str.redhat.com (vpn2-8-185.ams2.redhat.com [10.36.8.185]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id p0SBeHYY004400; Fri, 28 Jan 2011 06:40:18 -0500 From: Kevin Wolf To: qemu-devel@nongnu.org Date: Fri, 28 Jan 2011 12:41:50 +0100 Message-Id: <1296214910-5131-1-git-send-email-kwolf@redhat.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.12 X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.132.183.28 Cc: kwolf@redhat.com Subject: [Qemu-devel] [PATCH v2] qcow2: Add bdrv_discard support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This adds a bdrv_discard function to qcow2 that frees the discarded clusters. It does not yet pass the discard on to the underlying file system driver, but the space can be reused by future writes to the image. Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi --- v2: - Added offset > end_offset check block/qcow2-cluster.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ block/qcow2.c | 8 +++++ block/qcow2.h | 2 + 3 files changed, 92 insertions(+), 0 deletions(-) diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 1c2003a..5fb8c66 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -888,3 +888,85 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) } return 0; } + +/* + * This discards as many clusters of nb_clusters as possible at once (i.e. + * all clusters in the same L2 table) and returns the number of discarded + * clusters. + */ +static int discard_single_l2(BlockDriverState *bs, uint64_t offset, + unsigned int nb_clusters) +{ + BDRVQcowState *s = bs->opaque; + uint64_t l2_offset, *l2_table; + int l2_index; + int ret; + int i; + + ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &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_offset; + + old_offset = be64_to_cpu(l2_table[l2_index + i]); + old_offset &= ~QCOW_OFLAG_COPIED; + + if (old_offset == 0) { + continue; + } + + /* First remove L2 entries */ + qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table); + l2_table[l2_index + i] = cpu_to_be64(0); + + /* Then decrease the refcount */ + qcow2_free_any_clusters(bs, old_offset, 1); + } + + ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); + if (ret < 0) { + return ret; + } + + return nb_clusters; +} + +int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, + int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + uint64_t end_offset; + unsigned int nb_clusters; + int ret; + + end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS); + + /* Round start up and end down */ + offset = align_offset(offset, s->cluster_size); + end_offset &= ~(s->cluster_size - 1); + + if (offset > end_offset) { + return 0; + } + + nb_clusters = size_to_clusters(s, end_offset - offset); + + /* Each L2 table is handled by its own loop iteration */ + while (nb_clusters > 0) { + ret = discard_single_l2(bs, offset, nb_clusters); + if (ret < 0) { + return ret; + } + + nb_clusters -= ret; + offset += (ret * s->cluster_size); + } + + return 0; +} diff --git a/block/qcow2.c b/block/qcow2.c index 49bf7b9..dbe4fdd 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1084,6 +1084,13 @@ static int qcow2_make_empty(BlockDriverState *bs) return 0; } +static int qcow2_discard(BlockDriverState *bs, int64_t sector_num, + int nb_sectors) +{ + return qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS, + nb_sectors); +} + static int qcow2_truncate(BlockDriverState *bs, int64_t offset) { BDRVQcowState *s = bs->opaque; @@ -1349,6 +1356,7 @@ static BlockDriver bdrv_qcow2 = { .bdrv_aio_writev = qcow2_aio_writev, .bdrv_aio_flush = qcow2_aio_flush, + .bdrv_discard = qcow2_discard, .bdrv_truncate = qcow2_truncate, .bdrv_write_compressed = qcow2_write_compressed, diff --git a/block/qcow2.h b/block/qcow2.h index 6d80120..a019831 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -209,6 +209,8 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int compressed_size); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); +int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset, + int nb_sectors); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);