diff options
author | Kevin Wolf <kwolf@redhat.com> | 2023-01-26 18:24:20 +0100 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2023-02-17 11:22:19 +0100 |
commit | d6ee2e324ec26a02776d90125e3a55454f0ca57e (patch) | |
tree | 05a2db277d24d721903a5474c308160a59b8f2c1 /scripts | |
parent | 1e84cf79573e364075d6e63a4b00f7dc5f8aa924 (diff) |
block-coroutine-wrapper: Introduce no_co_wrapper
Some functions must not be called from coroutine context. The common
pattern to use them anyway from a coroutine is running them in a BH and
letting the calling coroutine yield to be woken up when the BH is
completed.
Instead of manually writing such wrappers, add support for generating
them to block-coroutine-wrapper.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-Id: <20230126172432.436111-2-kwolf@redhat.com>
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/block-coroutine-wrapper.py | 83 |
1 files changed, 72 insertions, 11 deletions
diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py index e82b648127..60e9b3107c 100644 --- a/scripts/block-coroutine-wrapper.py +++ b/scripts/block-coroutine-wrapper.py @@ -63,8 +63,8 @@ class ParamDecl: class FuncDecl: - def __init__(self, return_type: str, name: str, args: str, - variant: str) -> None: + def __init__(self, wrapper_type: str, return_type: str, name: str, + args: str, variant: str) -> None: self.return_type = return_type.strip() self.name = name.strip() self.struct_name = snake_to_camel(self.name) @@ -72,8 +72,21 @@ class FuncDecl: self.create_only_co = 'mixed' not in variant self.graph_rdlock = 'bdrv_rdlock' in variant - subsystem, subname = self.name.split('_', 1) - self.co_name = f'{subsystem}_co_{subname}' + self.wrapper_type = wrapper_type + + if wrapper_type == 'co': + subsystem, subname = self.name.split('_', 1) + self.target_name = f'{subsystem}_co_{subname}' + else: + assert wrapper_type == 'no_co' + subsystem, co_infix, subname = self.name.split('_', 2) + if co_infix != 'co': + raise ValueError(f"Invalid no_co function name: {self.name}") + if not self.create_only_co: + raise ValueError(f"no_co function can't be mixed: {self.name}") + if self.graph_rdlock: + raise ValueError(f"no_co function can't be rdlock: {self.name}") + self.target_name = f'{subsystem}_{subname}' t = self.args[0].type if t == 'BlockDriverState *': @@ -105,7 +118,8 @@ class FuncDecl: # Match wrappers declared with a co_wrapper mark func_decl_re = re.compile(r'^(?P<return_type>[a-zA-Z][a-zA-Z0-9_]* [\*]?)' - r'\s*co_wrapper' + r'(\s*coroutine_fn)?' + r'\s*(?P<wrapper_type>(no_)?co)_wrapper' r'(?P<variant>(_[a-z][a-z0-9_]*)?)\s*' r'(?P<wrapper_name>[a-z][a-z0-9_]*)' r'\((?P<args>[^)]*)\);$', re.MULTILINE) @@ -113,7 +127,8 @@ func_decl_re = re.compile(r'^(?P<return_type>[a-zA-Z][a-zA-Z0-9_]* [\*]?)' def func_decl_iter(text: str) -> Iterator: for m in func_decl_re.finditer(text): - yield FuncDecl(return_type=m.group('return_type'), + yield FuncDecl(wrapper_type=m.group('wrapper_type'), + return_type=m.group('return_type'), name=m.group('wrapper_name'), args=m.group('args'), variant=m.group('variant')) @@ -133,7 +148,7 @@ def create_mixed_wrapper(func: FuncDecl) -> str: """ Checks if we are already in coroutine """ - name = func.co_name + name = func.target_name struct_name = func.struct_name graph_assume_lock = 'assume_graph_lock();' if func.graph_rdlock else '' @@ -163,7 +178,7 @@ def create_co_wrapper(func: FuncDecl) -> str: """ Assumes we are not in coroutine, and creates one """ - name = func.co_name + name = func.target_name struct_name = func.struct_name return f"""\ {func.return_type} {func.name}({ func.gen_list('{decl}') }) @@ -183,10 +198,11 @@ def create_co_wrapper(func: FuncDecl) -> str: }}""" -def gen_wrapper(func: FuncDecl) -> str: +def gen_co_wrapper(func: FuncDecl) -> str: assert not '_co_' in func.name + assert func.wrapper_type == 'co' - name = func.co_name + name = func.target_name struct_name = func.struct_name graph_lock='' @@ -225,11 +241,56 @@ static void coroutine_fn {name}_entry(void *opaque) {creation_function(func)}""" +def gen_no_co_wrapper(func: FuncDecl) -> str: + assert '_co_' in func.name + assert func.wrapper_type == 'no_co' + + name = func.target_name + struct_name = func.struct_name + + return f"""\ +/* + * Wrappers for {name} + */ + +typedef struct {struct_name} {{ + Coroutine *co; + {func.return_field} +{ func.gen_block(' {decl};') } +}} {struct_name}; + +static void {name}_bh(void *opaque) +{{ + {struct_name} *s = opaque; + + {func.get_result}{name}({ func.gen_list('s->{name}') }); + + aio_co_wake(s->co); +}} + +{func.return_type} coroutine_fn {func.name}({ func.gen_list('{decl}') }) +{{ + {struct_name} s = {{ + .co = qemu_coroutine_self(), +{ func.gen_block(' .{name} = {name},') } + }}; + assert(qemu_in_coroutine()); + + aio_bh_schedule_oneshot(qemu_get_aio_context(), {name}_bh, &s); + qemu_coroutine_yield(); + + {func.ret} +}}""" + + def gen_wrappers(input_code: str) -> str: res = '' for func in func_decl_iter(input_code): res += '\n\n\n' - res += gen_wrapper(func) + if func.wrapper_type == 'co': + res += gen_co_wrapper(func) + else: + res += gen_no_co_wrapper(func) return res |