From nobody Tue Feb 10 17:14:45 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1502778275841598.230123603339; Mon, 14 Aug 2017 23:24:35 -0700 (PDT) Received: from localhost ([::1]:41776 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dhVHG-0002FD-Fr for importer@patchew.org; Tue, 15 Aug 2017 02:24:34 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49147) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dhVEg-0000V4-4Z for qemu-devel@nongnu.org; Tue, 15 Aug 2017 02:21:56 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dhVEd-0004DX-O5 for qemu-devel@nongnu.org; Tue, 15 Aug 2017 02:21:54 -0400 Received: from smtp1.ntua.gr ([2001:648:2000:de::183]:48386) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dhVEV-00047t-Ia; Tue, 15 Aug 2017 02:21:44 -0400 Received: from mail.ntua.gr (internet4-5-144-200-220.pat.nym.cosmote.net [5.144.200.220]) (authenticated bits=0) by smtp1.ntua.gr (8.15.2/8.15.2) with ESMTPSA id v7F6KjJB005255 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 15 Aug 2017 09:21:00 +0300 (EEST) (envelope-from el13635@mail.ntua.gr) X-Authentication-Warning: smtp1.ntua.gr: Host internet4-5-144-200-220.pat.nym.cosmote.net [5.144.200.220] claimed to be mail.ntua.gr From: Manos Pitsidianakis To: qemu-devel Date: Tue, 15 Aug 2017 09:19:21 +0300 Message-Id: <20170815061921.31596-3-el13635@mail.ntua.gr> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20170815061921.31596-1-el13635@mail.ntua.gr> References: <20170815061921.31596-1-el13635@mail.ntua.gr> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2001:648:2000:de::183 Subject: [Qemu-devel] [PATCH 2/2] block: add filter driver to block/write-threshold.c X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf , Alberto Garcia , Stefan Hajnoczi , qemu-block Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" With runtime insertion and removal of filters, write-threshold.c can provide more flexible deliveries of BLOCK_WRITE_THRESHOLD events. After the event trigger, the filter nodes are no longer useful and must be removed. The existing write-threshold cannot be easily converted to using the filter driver, so it is not affected. Signed-off-by: Manos Pitsidianakis --- block/qapi.c | 2 +- block/write-threshold.c | 264 +++++++++++++++++++++++++++++++++++-= ---- include/block/write-threshold.h | 22 ++-- qapi/block-core.json | 19 ++- tests/test-write-threshold.c | 40 +++--- 5 files changed, 281 insertions(+), 66 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index 2be44a6758..fe6cf2eae5 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -122,7 +122,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *b= lk, info->group =3D g_strdup(throttle_group_get_name(tgm)); } =20 - info->write_threshold =3D bdrv_write_threshold_get(bs); + info->write_threshold =3D bdrv_write_threshold_get_legacy(bs); =20 bs0 =3D bs; p_image_info =3D &info->image; diff --git a/block/write-threshold.c b/block/write-threshold.c index 0bd1a01c86..4a67188ea3 100644 --- a/block/write-threshold.c +++ b/block/write-threshold.c @@ -2,9 +2,11 @@ * QEMU System Emulator block write threshold notification * * Copyright Red Hat, Inc. 2014 + * Copyright 2017 Manos Pitsidianakis * * Authors: * Francesco Romani + * Manos Pitsidianakis * * This work is licensed under the terms of the GNU LGPL, version 2 or lat= er. * See the COPYING.LIB file in the top-level directory. @@ -19,46 +21,35 @@ #include "qmp-commands.h" =20 =20 -uint64_t bdrv_write_threshold_get(const BlockDriverState *bs) +uint64_t bdrv_write_threshold_get_legacy(const BlockDriverState *bs) { return bs->write_threshold_offset; } =20 -bool bdrv_write_threshold_is_set(const BlockDriverState *bs) +bool bdrv_write_threshold_is_set_legacy(const BlockDriverState *bs) { return bs->write_threshold_offset > 0; } =20 -static void write_threshold_disable(BlockDriverState *bs) +static void write_threshold_disable_legacy(BlockDriverState *bs) { - if (bdrv_write_threshold_is_set(bs)) { + if (bdrv_write_threshold_is_set_legacy(bs)) { notifier_with_return_remove(&bs->write_threshold_notifier); bs->write_threshold_offset =3D 0; } } =20 -uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, - const BdrvTrackedRequest *req) -{ - if (bdrv_write_threshold_is_set(bs)) { - if (req->offset > bs->write_threshold_offset) { - return (req->offset - bs->write_threshold_offset) + req->bytes; - } - if ((req->offset + req->bytes) > bs->write_threshold_offset) { - return (req->offset + req->bytes) - bs->write_threshold_offset; - } - } - return 0; -} - static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, void *opaque) { BdrvTrackedRequest *req =3D opaque; BlockDriverState *bs =3D req->bs; uint64_t amount =3D 0; + uint64_t threshold =3D bdrv_write_threshold_get_legacy(bs); + uint64_t offset =3D req->offset; + uint64_t bytes =3D req->bytes; =20 - amount =3D bdrv_write_threshold_exceeded(bs, req); + amount =3D bdrv_write_threshold_exceeded(threshold, offset, bytes); if (amount > 0) { qapi_event_send_block_write_threshold( bs->node_name, @@ -67,7 +58,7 @@ static int coroutine_fn before_write_notify(NotifierWithR= eturn *notifier, &error_abort); =20 /* autodisable to avoid flooding the monitor */ - write_threshold_disable(bs); + write_threshold_disable_legacy(bs); } =20 return 0; /* should always let other notifiers run */ @@ -79,25 +70,26 @@ static void write_threshold_register_notifier(BlockDriv= erState *bs) bdrv_add_before_write_notifier(bs, &bs->write_threshold_notifier); } =20 -static void write_threshold_update(BlockDriverState *bs, - int64_t threshold_bytes) +static void write_threshold_update_legacy(BlockDriverState *bs, + int64_t threshold_bytes) { bs->write_threshold_offset =3D threshold_bytes; } =20 -void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_byt= es) +void bdrv_write_threshold_set_legacy(BlockDriverState *bs, + uint64_t threshold_bytes) { - if (bdrv_write_threshold_is_set(bs)) { + if (bdrv_write_threshold_is_set_legacy(bs)) { if (threshold_bytes > 0) { - write_threshold_update(bs, threshold_bytes); + write_threshold_update_legacy(bs, threshold_bytes); } else { - write_threshold_disable(bs); + write_threshold_disable_legacy(bs); } } else { if (threshold_bytes > 0) { /* avoid multiple registration */ write_threshold_register_notifier(bs); - write_threshold_update(bs, threshold_bytes); + write_threshold_update_legacy(bs, threshold_bytes); } /* discard bogus disable request */ } @@ -119,7 +111,223 @@ void qmp_block_set_write_threshold(const char *node_n= ame, aio_context =3D bdrv_get_aio_context(bs); aio_context_acquire(aio_context); =20 - bdrv_write_threshold_set(bs, threshold_bytes); + bdrv_write_threshold_set_legacy(bs, threshold_bytes); =20 aio_context_release(aio_context); } + + +/* The write-threshold filter drivers delivers a one-time BLOCK_WRITE_THRE= SHOLD + * event when a passing write request exceeds the configured write thresho= ld + * offset of the filter. + * + * This is useful to transparently resize thin-provisioned drives without + * the guest OS noticing. + */ + +#define QEMU_OPT_WRITE_THRESHOLD "write-threshold" +static BlockDriver write_threshold; +static QemuOptsList write_threshold_opts =3D { + .name =3D "write-threshold", + .head =3D QTAILQ_HEAD_INITIALIZER(write_threshold_opts.head), + .desc =3D { + { + .name =3D QEMU_OPT_WRITE_THRESHOLD, + .type =3D QEMU_OPT_NUMBER, + .help =3D "configured threshold for the block device, bytes. U= se 0" + "to disable the threshold", + }, + { /* end of list */ } + }, +}; + +static bool bdrv_write_threshold_is_set(const BlockDriverState *bs) +{ + uint64_t threshold =3D *(uint64_t *)bs->opaque; + return threshold > 0; +} + +static void bdrv_write_threshold_disable(BlockDriverState *bs) +{ + uint64_t *threshold =3D (uint64_t *)bs->opaque; + if (bdrv_write_threshold_is_set(bs)) { + *threshold =3D 0; + } +} + +uint64_t bdrv_write_threshold_exceeded(uint64_t threshold, uint64_t offset, + uint64_t bytes) +{ + if (threshold) { + if (offset > threshold) { + return (offset - threshold) + bytes; + } + if ((offset + bytes) > threshold) { + return (offset + bytes) - threshold; + } + } + return 0; +} + + +static void bdrv_write_threshold_update(BlockDriverState *bs, + int64_t threshold_bytes) +{ + uint64_t *threshold =3D (uint64_t *)bs->opaque; + *threshold =3D threshold_bytes; +} + +static void bdrv_write_threshold_check_amount(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes) +{ + uint64_t threshold =3D *(uint64_t *)bs->opaque; + uint64_t amount =3D 0; + + amount =3D bdrv_write_threshold_exceeded(threshold, offset, bytes); + if (amount > 0) { + qapi_event_send_block_write_threshold(child_bs(bs)->node_name, + amount, + threshold, + &error_abort); + /* autodisable to avoid flooding the monitor */ + bdrv_write_threshold_disable(bs); + } +} + +/* Filter driver methods */ + +static int coroutine_fn write_threshold_co_preadv(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + int flags) +{ + return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); +} + +static int coroutine_fn write_threshold_co_pwritev(BlockDriverState *bs, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov, + int flags) +{ + bdrv_write_threshold_check_amount(bs, offset, bytes); + return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); +} + +static int coroutine_fn write_threshold_co_pwrite_zeroes( + BlockDriverState *= bs, + int64_t offset, + int bytes, + BdrvRequestFlags f= lags) +{ + bdrv_write_threshold_check_amount(bs, offset, bytes); + return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); +} + +static int coroutine_fn write_threshold_co_pdiscard(BlockDriverState *bs, + int64_t offset, int by= tes) +{ + bdrv_write_threshold_check_amount(bs, offset, bytes); + return bdrv_co_pdiscard(bs->file->bs, offset, bytes); +} + + +static int64_t write_threshold_getlength(BlockDriverState *bs) +{ + return bdrv_getlength(bs->file->bs); +} + +static int write_threshold_open(BlockDriverState *bs, QDict *options, + int flags, Error **errp) +{ + Error *local_err =3D NULL; + int ret =3D 0; + QemuOpts *opts =3D NULL; + uint64_t threshold =3D 0; + + bs->file =3D bdrv_open_child(NULL, options, "file", bs, &child_file, + false, errp); + if (!bs->file) { + return -EINVAL; + } + + bs->supported_write_flags =3D bs->file->bs->supported_write_flags; + bs->supported_zero_flags =3D bs->file->bs->supported_zero_flags; + + opts =3D qemu_opts_create(&write_threshold_opts, NULL, 0, &error_abort= ); + + qemu_opts_absorb_qdict(opts, options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret =3D -EINVAL; + goto ret; + } + + threshold =3D qemu_opt_get_number(opts, QEMU_OPT_WRITE_THRESHOLD, 0); + bdrv_write_threshold_update(bs, threshold); + +ret: + qemu_opts_del(opts); + return ret; +} + +static void write_threshold_close(BlockDriverState *bs) +{ +} + +static int write_threshold_co_flush(BlockDriverState *bs) +{ + return bdrv_co_flush(bs->file->bs); +} + +static int64_t coroutine_fn write_threshold_co_get_block_status( + BlockDriverState *b= s, + int64_t sector_num, + int nb_sectors, + int *pnum, + BlockDriverState **= file) +{ + assert(child_bs(bs)); + *pnum =3D nb_sectors; + *file =3D child_bs(bs); + return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | + (sector_num << BDRV_SECTOR_BITS); +} + +static bool write_threshold_recurse_is_first_non_filter( + BlockDriverState *bs, + BlockDriverState *candi= date) +{ + return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); +} + +static BlockDriver write_threshold =3D { + .format_name =3D "write-threshold", + .instance_size =3D sizeof(uint64_t), + + .bdrv_open =3D write_threshold_open, + .bdrv_close =3D write_threshold_close, + + .bdrv_co_flush =3D write_threshold_co_flush, + .bdrv_co_preadv =3D write_threshold_co_preadv, + .bdrv_co_pwritev =3D write_threshold_co_pwritev, + .bdrv_co_pwrite_zeroes =3D write_threshold_co_pwrite_zeroes, + .bdrv_co_pdiscard =3D write_threshold_co_pdiscard, + + .bdrv_getlength =3D write_threshold_getlength, + .bdrv_child_perm =3D bdrv_filter_default_perms, + .bdrv_co_get_block_status =3D write_threshold_co_get_block_sta= tus, + .bdrv_recurse_is_first_non_filter =3D + write_threshold_recurse_is_first_non_fi= lter, + + .is_filter =3D true, +}; + +static void bdrv_write_threshold_init(void) +{ + bdrv_register(&write_threshold); +} + +block_init(bdrv_write_threshold_init); diff --git a/include/block/write-threshold.h b/include/block/write-threshol= d.h index 234d2193e0..5cf378564d 100644 --- a/include/block/write-threshold.h +++ b/include/block/write-threshold.h @@ -15,7 +15,7 @@ #include "qemu-common.h" =20 /* - * bdrv_write_threshold_set: + * bdrv_write_threshold_set_legacy: * * Set the write threshold for block devices, in bytes. * Notify when a write exceeds the threshold, meaning the device @@ -24,22 +24,25 @@ * * Use threshold_bytes =3D=3D 0 to disable. */ -void bdrv_write_threshold_set(BlockDriverState *bs, uint64_t threshold_byt= es); +void bdrv_write_threshold_set_legacy(BlockDriverState *bs, + uint64_t threshold_bytes); + =20 /* - * bdrv_write_threshold_get + * bdrv_write_threshold_get_legacy * * Get the configured write threshold, in bytes. * Zero means no threshold configured. + * */ -uint64_t bdrv_write_threshold_get(const BlockDriverState *bs); +uint64_t bdrv_write_threshold_get_legacy(const BlockDriverState *bs); =20 /* - * bdrv_write_threshold_is_set + * bdrv_write_threshold_is_set_legacy * * Tell if a write threshold is set for a given BDS. */ -bool bdrv_write_threshold_is_set(const BlockDriverState *bs); +bool bdrv_write_threshold_is_set_legacy(const BlockDriverState *bs); =20 /* * bdrv_write_threshold_exceeded @@ -51,11 +54,10 @@ bool bdrv_write_threshold_is_set(const BlockDriverState= *bs); * NOTE: here we assume the following holds for each request this code * deals with: * - * assert((req->offset + req->bytes) <=3D UINT64_MAX) + * assert((offset + bytes) <=3D UINT64_MAX) * * Please not there is *not* an actual C assert(). */ -uint64_t bdrv_write_threshold_exceeded(const BlockDriverState *bs, - const BdrvTrackedRequest *req); - +uint64_t bdrv_write_threshold_exceeded(uint64_t threshold, uint64_t offset, + uint64_t bytes); #endif diff --git a/qapi/block-core.json b/qapi/block-core.json index 12fd749a94..4d6ba1baef 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2232,7 +2232,8 @@ 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', - 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } + 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs', + 'write-threshold'] } =20 ## # @BlockdevOptionsFile: @@ -3113,6 +3114,21 @@ 'file' : 'BlockdevRef', '*limits' : 'ThrottleLimits' } } + +## +# @BlockdevOptionsWriteThreshold: +# +# Driver specific block device options for the write-threshold driver +# +# @file: reference to or definition of the data source block d= evice +# @write-threshold: threshold in bytes +# Since: 2.11 +## +{ 'struct': 'BlockdevOptionsWriteThreshold', + 'data': { '*file' : 'BlockdevRef', + 'write-threshold' : 'int' + } } + ## # @BlockdevOptions: # @@ -3175,6 +3191,7 @@ 'sheepdog': 'BlockdevOptionsSheepdog', 'ssh': 'BlockdevOptionsSsh', 'throttle': 'BlockdevOptionsThrottle', + 'write-threshold': 'BlockdevOptionsWriteThreshold', 'vdi': 'BlockdevOptionsGenericFormat', 'vhdx': 'BlockdevOptionsGenericFormat', 'vmdk': 'BlockdevOptionsGenericCOWFormat', diff --git a/tests/test-write-threshold.c b/tests/test-write-threshold.c index 97ca12f710..1c802d2de4 100644 --- a/tests/test-write-threshold.c +++ b/tests/test-write-threshold.c @@ -17,9 +17,9 @@ static void test_threshold_not_set_on_init(void) BlockDriverState bs; memset(&bs, 0, sizeof(bs)); =20 - g_assert(!bdrv_write_threshold_is_set(&bs)); + g_assert(!bdrv_write_threshold_is_set_legacy(&bs)); =20 - res =3D bdrv_write_threshold_get(&bs); + res =3D bdrv_write_threshold_get_legacy(&bs); g_assert_cmpint(res, =3D=3D, 0); } =20 @@ -30,11 +30,11 @@ static void test_threshold_set_get(void) BlockDriverState bs; memset(&bs, 0, sizeof(bs)); =20 - bdrv_write_threshold_set(&bs, threshold); + bdrv_write_threshold_set_legacy(&bs, threshold); =20 - g_assert(bdrv_write_threshold_is_set(&bs)); + g_assert(bdrv_write_threshold_is_set_legacy(&bs)); =20 - res =3D bdrv_write_threshold_get(&bs); + res =3D bdrv_write_threshold_get_legacy(&bs); g_assert_cmpint(res, =3D=3D, threshold); } =20 @@ -46,9 +46,9 @@ static void test_threshold_multi_set_get(void) BlockDriverState bs; memset(&bs, 0, sizeof(bs)); =20 - bdrv_write_threshold_set(&bs, threshold1); - bdrv_write_threshold_set(&bs, threshold2); - res =3D bdrv_write_threshold_get(&bs); + bdrv_write_threshold_set_legacy(&bs, threshold1); + bdrv_write_threshold_set_legacy(&bs, threshold2); + res =3D bdrv_write_threshold_get_legacy(&bs); g_assert_cmpint(res, =3D=3D, threshold2); } =20 @@ -56,16 +56,10 @@ static void test_threshold_not_trigger(void) { uint64_t amount =3D 0; uint64_t threshold =3D 4 * 1024 * 1024; - BlockDriverState bs; - BdrvTrackedRequest req; + uint64_t offset =3D 1024; + uint64_t bytes =3D 1024; =20 - memset(&bs, 0, sizeof(bs)); - memset(&req, 0, sizeof(req)); - req.offset =3D 1024; - req.bytes =3D 1024; - - bdrv_write_threshold_set(&bs, threshold); - amount =3D bdrv_write_threshold_exceeded(&bs, &req); + amount =3D bdrv_write_threshold_exceeded(threshold, offset, bytes); g_assert_cmpuint(amount, =3D=3D, 0); } =20 @@ -74,16 +68,10 @@ static void test_threshold_trigger(void) { uint64_t amount =3D 0; uint64_t threshold =3D 4 * 1024 * 1024; - BlockDriverState bs; - BdrvTrackedRequest req; + uint64_t offset =3D (4 * 1024 * 1024) - 1024; + uint64_t bytes =3D 2 * 1024; =20 - memset(&bs, 0, sizeof(bs)); - memset(&req, 0, sizeof(req)); - req.offset =3D (4 * 1024 * 1024) - 1024; - req.bytes =3D 2 * 1024; - - bdrv_write_threshold_set(&bs, threshold); - amount =3D bdrv_write_threshold_exceeded(&bs, &req); + amount =3D bdrv_write_threshold_exceeded(threshold, offset, bytes); g_assert_cmpuint(amount, >=3D, 1024); } =20 --=20 2.11.0