From patchwork Wed May 15 14:34:29 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stefan Hajnoczi X-Patchwork-Id: 244097 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id E4E292C00A4 for ; Thu, 16 May 2013 00:39:01 +1000 (EST) Received: from localhost ([::1]:37878 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Uccr6-0003TN-52 for incoming@patchwork.ozlabs.org; Wed, 15 May 2013 10:39:00 -0400 Received: from eggs.gnu.org ([208.118.235.92]:50167) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Uccos-0000jf-89 for qemu-devel@nongnu.org; Wed, 15 May 2013 10:36:48 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Uccol-0000Gq-D9 for qemu-devel@nongnu.org; Wed, 15 May 2013 10:36:42 -0400 Received: from mx1.redhat.com ([209.132.183.28]:36918) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Uccol-0000Gj-3J for qemu-devel@nongnu.org; Wed, 15 May 2013 10:36:35 -0400 Received: from int-mx12.intmail.prod.int.phx2.redhat.com (int-mx12.intmail.prod.int.phx2.redhat.com [10.5.11.25]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r4FEYnYd029539 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 15 May 2013 10:34:49 -0400 Received: from localhost (dhcp-64-106.muc.redhat.com [10.32.64.106]) by int-mx12.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id r4FEYmL3001548; Wed, 15 May 2013 10:34:48 -0400 From: Stefan Hajnoczi To: Date: Wed, 15 May 2013 16:34:29 +0200 Message-Id: <1368628476-19622-2-git-send-email-stefanha@redhat.com> In-Reply-To: <1368628476-19622-1-git-send-email-stefanha@redhat.com> References: <1368628476-19622-1-git-send-email-stefanha@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.25 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: Kevin Wolf , Fam Zheng , dietmar@proxmox.com, imain@redhat.com, Stefan Hajnoczi , Paolo Bonzini , xiawenc@linux.vnet.ibm.com Subject: [Qemu-devel] [PATCH v3 1/8] block: add bdrv_add_before_write_cb() X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org The bdrv_add_before_write_cb() function installs a callback that is invoked before a write request is processed. This will be used to implement copy-on-write point-in-time snapshots where we need to copy out old data before overwriting it. Signed-off-by: Stefan Hajnoczi --- block.c | 37 +++++++++++++++++++++++++++++++++++++ include/block/block_int.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/block.c b/block.c index 3f87489..0fd7167 100644 --- a/block.c +++ b/block.c @@ -308,6 +308,7 @@ BlockDriverState *bdrv_new(const char *device_name) } bdrv_iostatus_disable(bs); notifier_list_init(&bs->close_notifiers); + QTAILQ_INIT(&bs->before_write_cbs); return bs; } @@ -1383,6 +1384,8 @@ void bdrv_close(BlockDriverState *bs) bs->growable = 0; QDECREF(bs->options); bs->options = NULL; + assert(QTAILQ_EMPTY(&bs->before_write_cbs)); + QTAILQ_INIT(&bs->before_write_cbs); if (bs->file != NULL) { bdrv_delete(bs->file); @@ -2587,6 +2590,22 @@ static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs, return ret; } +struct BDRVBeforeWrite { + BDRVBeforeWriteFunc *cb; + QTAILQ_ENTRY(BDRVBeforeWrite) list; +}; + +static void invoke_before_write_cb(BlockDriverState *bs, int64_t sector_num, + int nb_sectors, QEMUIOVector *qiov) +{ + BDRVBeforeWrite *before_write; + BDRVBeforeWrite *tmp; + QTAILQ_FOREACH_SAFE(before_write, &bs->before_write_cbs, list, tmp) { + before_write->cb(bs, sector_num, nb_sectors, qiov); + } +} + + /* * Handle a write request in coroutine context */ @@ -2619,6 +2638,8 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs, tracked_request_begin(&req, bs, sector_num, nb_sectors, true); + invoke_before_write_cb(bs, sector_num, nb_sectors, qiov); + if (flags & BDRV_REQ_ZERO_WRITE) { ret = bdrv_co_do_write_zeroes(bs, sector_num, nb_sectors); } else { @@ -4883,3 +4904,19 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs) /* Currently BlockDriverState always uses the main loop AioContext */ return qemu_get_aio_context(); } + +BDRVBeforeWrite *bdrv_add_before_write_cb(BlockDriverState *bs, + BDRVBeforeWriteFunc *cb) +{ + BDRVBeforeWrite *elem = g_slice_new(BDRVBeforeWrite); + elem->cb = cb; + QTAILQ_INSERT_TAIL(&bs->before_write_cbs, elem, list); + return elem; +} + +void bdrv_remove_before_write_cb(BlockDriverState *bs, + BDRVBeforeWrite *before_write) +{ + QTAILQ_REMOVE(&bs->before_write_cbs, before_write, list); + g_slice_free(BDRVBeforeWrite, before_write); +} diff --git a/include/block/block_int.h b/include/block/block_int.h index 6078dd3..e2299df 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -211,6 +211,16 @@ struct BlockDriver { QLIST_ENTRY(BlockDriver) list; }; +/** + * BDRVBeforeWriteFunc: + * + * See #bdrv_add_before_write_cb(). + */ +typedef void coroutine_fn BDRVBeforeWriteFunc(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); + +typedef struct BDRVBeforeWrite BDRVBeforeWrite; + /* * Note: the function bdrv_append() copies and swaps contents of * BlockDriverStates, so if you add new fields to this struct, please @@ -289,6 +299,9 @@ struct BlockDriverState { /* long-running background operation */ BlockJob *job; + /* Callback before write request is processed */ + QTAILQ_HEAD(, BDRVBeforeWrite) before_write_cbs; + QDict *options; }; @@ -298,6 +311,25 @@ void bdrv_set_io_limits(BlockDriverState *bs, BlockIOLimit *io_limits); /** + * bdrv_add_before_write_cb: + * + * Register a callback that is invoked before write requests are processed but + * after any throttling or waiting for overlapping requests. + * + * Returns: a #BDRVBeforeWrite to use with bdrv_remove_before_write_cb() + */ +BDRVBeforeWrite *bdrv_add_before_write_cb(BlockDriverState *bs, + BDRVBeforeWriteFunc *cb); + +/** + * bdrv_remove_before_write_cb: + * + * Unregister a before write callback. + */ +void bdrv_remove_before_write_cb(BlockDriverState *bs, + BDRVBeforeWrite *before_write); + +/** * bdrv_get_aio_context: * * Returns: the currently bound #AioContext