From nobody Tue Sep 30 22:48:13 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.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 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=virtuozzo.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1550657221406413.3024998991466; Wed, 20 Feb 2019 02:07:01 -0800 (PST) Received: from localhost ([127.0.0.1]:37256 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gwOmI-0001Bs-CN for importer@patchew.org; Wed, 20 Feb 2019 05:06:58 -0500 Received: from eggs.gnu.org ([209.51.188.92]:40356) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gwOjt-00083T-0y for qemu-devel@nongnu.org; Wed, 20 Feb 2019 05:04:30 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gwOjp-0004Tv-DD for qemu-devel@nongnu.org; Wed, 20 Feb 2019 05:04:27 -0500 Received: from relay.sw.ru ([185.231.240.75]:43622) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gwOjc-00046L-QL; Wed, 20 Feb 2019 05:04:14 -0500 Received: from [10.28.8.145] (helo=kvm.sw.ru) by relay.sw.ru with esmtp (Exim 4.91) (envelope-from ) id 1gwOjO-00013h-GL; Wed, 20 Feb 2019 13:03:58 +0300 From: Vladimir Sementsov-Ogievskiy To: qemu-block@nongnu.org, qemu-devel@nongnu.org Date: Wed, 20 Feb 2019 13:03:58 +0300 Message-Id: <20190220100358.25566-1-vsementsov@virtuozzo.com> X-Mailer: git-send-email 2.18.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 185.231.240.75 Subject: [Qemu-devel] [RFC PATCH v2] coroutines: generate wrapper code 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: kwolf@redhat.com, vsementsov@virtuozzo.com, ehabkost@redhat.com, mreitz@redhat.com, stefanha@redhat.com, crosa@redhat.com Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Hi all! We have a very frequent pattern of creating coroutine from function with several arguments: - create structure to pack parameters - create _entry function to call original function taking parameters from struct - do different magic to handle completion: set ret to NOT_DONE or EINPROGRESS, use separate bool for void functions - fill the struct and create coroutine from _entry function and this struct as a parameter Here is a template code + example how it can be used to drop a lot of similar code. TODO: make coroutine-wrapper template file to be header itself, or generate header from it Signed-off-by: Vladimir Sementsov-Ogievskiy --- v2: - don't generate poll-loop wrappers - drop jinja Makefile | 5 ++ Makefile.objs | 2 +- include/block/block.h | 3 ++ include/block/block_int.h | 8 +++ block.c | 76 +++++++--------------------- coroutine-wrapper | 2 + scripts/coroutine-wrapper.py | 98 ++++++++++++++++++++++++++++++++++++ 7 files changed, 136 insertions(+), 58 deletions(-) create mode 100644 coroutine-wrapper create mode 100755 scripts/coroutine-wrapper.py diff --git a/Makefile b/Makefile index 1278a3eb52..8d19b06cf1 100644 --- a/Makefile +++ b/Makefile @@ -122,6 +122,8 @@ endif =20 GENERATED_FILES +=3D module_block.h =20 +GENERATED_FILES +=3D block-gen.c + TRACE_HEADERS =3D trace-root.h $(trace-events-subdirs:%=3D%/trace.h) TRACE_SOURCES =3D trace-root.c $(trace-events-subdirs:%=3D%/trace.c) TRACE_DTRACE =3D @@ -138,6 +140,9 @@ GENERATED_FILES +=3D $(TRACE_SOURCES) GENERATED_FILES +=3D $(BUILD_DIR)/trace-events-all GENERATED_FILES +=3D .git-submodule-status =20 +block-gen.c: coroutine-wrapper $(SRC_PATH)/scripts/coroutine-wrapper.py + $(call quiet-command, python $(SRC_PATH)/scripts/coroutine-wrapper.py < $= < > $@,"GEN","$(TARGET_DIR)$@") + trace-group-name =3D $(shell dirname $1 | sed -e 's/[^a-zA-Z0-9]/_/g') =20 tracetool-y =3D $(SRC_PATH)/scripts/tracetool.py diff --git a/Makefile.objs b/Makefile.objs index 67a054b08a..16159d3e0f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -23,7 +23,7 @@ slirp-obj-$(CONFIG_SLIRP) =3D slirp/ # block-obj-y is code used by both qemu system emulation and qemu-img =20 block-obj-y +=3D nbd/ -block-obj-y +=3D block.o blockjob.o job.o +block-obj-y +=3D block.o blockjob.o job.o block-gen.o block-obj-y +=3D block/ scsi/ block-obj-y +=3D qemu-io-cmds.o block-obj-$(CONFIG_REPLICATION) +=3D replication.o diff --git a/include/block/block.h b/include/block/block.h index 57233cf2c0..61b2ca6fe5 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -371,6 +371,8 @@ typedef enum { } BdrvCheckMode; =20 int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode f= ix); +int coroutine_fn bdrv_co_check(BlockDriverState *bs, + BdrvCheckResult *res, BdrvCheckMode fix); =20 /* The units of offset and total_work_size may be chosen arbitrarily by the * block driver; total_work_size may change during the course of the amend= ment @@ -399,6 +401,7 @@ int bdrv_co_ioctl(BlockDriverState *bs, int req, void *= buf); =20 /* Invalidate any cached metadata used by image formats */ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp); +void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **e= rrp); void bdrv_invalidate_cache_all(Error **errp); int bdrv_inactivate_all(void); =20 diff --git a/include/block/block_int.h b/include/block/block_int.h index f605622216..9af6507be7 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -1175,4 +1175,12 @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *sr= c, uint64_t src_offset, =20 int refresh_total_sectors(BlockDriverState *bs, int64_t hint); =20 + +Coroutine *bdrv_co_invalidate_cache__create_co(bool *_in_progress, + BlockDriverState *bs, + Error **errp); +Coroutine *bdrv_co_check__create_co(bool *_in_progress, int *_ret, + BlockDriverState *bs, BdrvCheckResult = *res, + BdrvCheckMode fix); + #endif /* BLOCK_INT_H */ diff --git a/block.c b/block.c index b67d9b7b65..94999bc0fc 100644 --- a/block.c +++ b/block.c @@ -3706,8 +3706,8 @@ static void bdrv_delete(BlockDriverState *bs) * free of errors) or -errno when an internal error occurred. The results = of the * check are stored in res. */ -static int coroutine_fn bdrv_co_check(BlockDriverState *bs, - BdrvCheckResult *res, BdrvCheckMode = fix) +int coroutine_fn bdrv_co_check(BlockDriverState *bs, + BdrvCheckResult *res, BdrvCheckMode fix) { if (bs->drv =3D=3D NULL) { return -ENOMEDIUM; @@ -3720,44 +3720,25 @@ static int coroutine_fn bdrv_co_check(BlockDriverSt= ate *bs, return bs->drv->bdrv_co_check(bs, res, fix); } =20 -typedef struct CheckCo { - BlockDriverState *bs; - BdrvCheckResult *res; - BdrvCheckMode fix; - int ret; -} CheckCo; - -static void bdrv_check_co_entry(void *opaque) -{ - CheckCo *cco =3D opaque; - cco->ret =3D bdrv_co_check(cco->bs, cco->res, cco->fix); - aio_wait_kick(); -} - int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { + int ret; + bool in_progress; Coroutine *co; - CheckCo cco =3D { - .bs =3D bs, - .res =3D res, - .ret =3D -EINPROGRESS, - .fix =3D fix, - }; =20 if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - bdrv_check_co_entry(&cco); - } else { - co =3D qemu_coroutine_create(bdrv_check_co_entry, &cco); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, cco.ret =3D=3D -EINPROGRESS); + return bdrv_co_check(bs, res, fix); } =20 - return cco.ret; -} + co =3D bdrv_co_check__create_co(&in_progress, &ret, bs, res, fix); + bdrv_coroutine_enter(bs, co); + BDRV_POLL_WHILE(bs, in_progress); =20 + return ret; +} /* + * * Return values: * 0 - success * -EINVAL - backing format specified, but no file @@ -4623,8 +4604,7 @@ void bdrv_init_with_whitelist(void) bdrv_init(); } =20 -static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, - Error **errp) +void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, Error **e= rrp) { BdrvChild *child, *parent; uint64_t perm, shared_perm; @@ -4705,37 +4685,19 @@ static void coroutine_fn bdrv_co_invalidate_cache(B= lockDriverState *bs, } } =20 -typedef struct InvalidateCacheCo { - BlockDriverState *bs; - Error **errp; - bool done; -} InvalidateCacheCo; - -static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque) -{ - InvalidateCacheCo *ico =3D opaque; - bdrv_co_invalidate_cache(ico->bs, ico->errp); - ico->done =3D true; - aio_wait_kick(); -} - void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) { + bool in_progress; Coroutine *co; - InvalidateCacheCo ico =3D { - .bs =3D bs, - .done =3D false, - .errp =3D errp - }; =20 if (qemu_in_coroutine()) { - /* Fast-path if already in coroutine context */ - bdrv_invalidate_cache_co_entry(&ico); - } else { - co =3D qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico); - bdrv_coroutine_enter(bs, co); - BDRV_POLL_WHILE(bs, !ico.done); + bdrv_co_invalidate_cache(bs, errp); + return; } + + co =3D bdrv_co_invalidate_cache__create_co(&in_progress, bs, errp); + bdrv_coroutine_enter(bs, co); + BDRV_POLL_WHILE(bs, in_progress); } =20 void bdrv_invalidate_cache_all(Error **errp) diff --git a/coroutine-wrapper b/coroutine-wrapper new file mode 100644 index 0000000000..47bcd2e6a0 --- /dev/null +++ b/coroutine-wrapper @@ -0,0 +1,2 @@ +int bdrv_co_check(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMod= e fix) +void bdrv_co_invalidate_cache(BlockDriverState *bs, Error **errp) diff --git a/scripts/coroutine-wrapper.py b/scripts/coroutine-wrapper.py new file mode 100755 index 0000000000..35b0ba8c59 --- /dev/null +++ b/scripts/coroutine-wrapper.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +import re + +header =3D """/* + * File is generated by scripts/coroutine-wrapper.py + */ + +#include "qemu/osdep.h" +#include "block/block_int.h" +""" + +template =3D """ +/* + * Wrappers for $name$ + */ + +typedef struct $name$__ArgumentsPack { + $fields$ +} $name$__ArgumentsPack; + +static void $name$__entry(void *opaque) +{ + $name$__ArgumentsPack *pack =3D opaque; + + $call$; + + *pack->_in_progress =3D false; + aio_wait_kick(); + g_free(opaque); +} + +Coroutine *$name$__create_co($args_def$) +{ + $name$__ArgumentsPack *pack =3D g_new($name$__ArgumentsPack, 1); + + *pack =3D ($name$__ArgumentsPack) { + $initializers$ + }; + + *_in_progress =3D true; + + return qemu_coroutine_create($name$__entry, pack); +} +""" + +# We want to use python string.format() formatter, which uses curly bracke= ts +# as separators. But it's not comfortable with C. So, we used dollars inst= ead, +# and now is the time to escape curly brackets and convert dollars. +template =3D template.replace('{', '{{').replace('}', '}}') +template =3D re.sub(r'\$(\w+)\$', r'{\1}', template) + +func_decl_re =3D re.compile(r'^([^(]+) ([a-z][a-z0-9_]*)\((.*)\)$') +param_re =3D re.compile(r'(?P.*[ *](?P[a-z][a-z0-9_]*))') + + +def format_args(args, format, separator): + return separator.join(format.format(**arg) for arg in args) + + +def make_wrapper(function_declaration): + try: + m =3D func_decl_re.match(function_declaration) + ret_type =3D m.group(1).strip() + name =3D m.group(2) + raw_args =3D m.group(3).split(', ') + args =3D [param_re.match(arg).groupdict() for arg in raw_args] + except AttributeError: + raise ValueError('Failed to parse function declaration') + + xargs =3D [{'def': 'bool *_in_progress', 'name': '_in_progress'}] + ar= gs + + has_ret =3D ret_type !=3D 'void' + if has_ret: + xargs.insert(1, {'def': ret_type + ' * _ret', 'name': '_ret'}) + + params =3D { + 'name': name, + 'args_def': format_args(xargs, '{def}', ', '), + 'fields': format_args(xargs, '{def};', '\n '), + 'initializers': format_args(xargs, '.{name} =3D {name},', '\n = '), + 'call': '{}{}({})'.format('*pack->_ret =3D ' if has_ret else '', + name, + format_args(args, 'pack->{name}', ', ')) + } + + return template.format(**params) + + +if __name__ =3D=3D '__main__': + import sys + + print(header) + try: + for ind, line in enumerate(sys.stdin): + print(make_wrapper(line)) + except ValueError as e: + sys.exit(('ERROR: {} at line {}:\n{}').format(e, ind + 1, line)) --=20 2.18.0