In a future commit we want to add (non-optional) Flags arg. As a step to doing this, generalize OFlags so it's not tied to just NBD_CMD_FLAG_*. This does not change the C API. It does introduce a small change to the OCaml API -- putting related flags into a submodule, basically renaming them. Note we don't provide guarantees for non-C APIs. --- generator/generator | 180 +++++++++++++++++------ ocaml/nbd-c.h | 15 -- ocaml/tests/test_405_pread_structured.ml | 6 +- ocaml/tests/test_410_pwrite.ml | 3 +- ocaml/tests/test_460_block_status.ml | 3 +- ocaml/tests/test_510_aio_pwrite.ml | 3 +- 6 files changed, 142 insertions(+), 68 deletions(-) diff --git a/generator/generator b/generator/generator index 78c6ca6..5823686 100755 --- a/generator/generator +++ b/generator/generator @@ -865,7 +865,7 @@ and arg = | UInt32 of string (* 32 bit unsigned int *) | UInt64 of string (* 64 bit unsigned int *) and optarg = -| OFlags of string (* NBD_CMD_FLAG_* flags *) +| OFlags of string * flags (* optional flags, uint32_t in C *) and ret = | RBool (* return a boolean, or error *) | RStaticString (* return a static string (must be located in @@ -890,6 +890,10 @@ and cbarg = | CBString of string (* like String *) | CBUInt of string (* like UInt *) | CBUInt64 of string (* like UInt64 *) +and flags = { + flag_prefix : string; (* prefix of each flag name *) + flags : (string * int) list (* flag names and their values in C *) +} and permitted_state = | Created (* can be called in the START state *) | Connecting (* can be called when connecting/handshaking *) @@ -906,6 +910,18 @@ let non_blocking_test_call_description = "\n This call does not block, because it returns data that is saved in the handle from the NBD protocol handshake." +(* Flags. *) +let cmd_flags = { + flag_prefix = "CMD_FLAG"; + flags = [ + "FUA", 1 lsl 0; + "NO_HOLE", 1 lsl 1; + "DF", 1 lsl 2; + "REQ_ONE", 1 lsl 3; + ] +} +let all_flags = [ cmd_flags ] + (* Calls. * * The first parameter [struct nbd_handle *nbd] is implicit. @@ -1387,7 +1403,7 @@ Returns the size in bytes of the NBD export." "pread", { default_call with args = [ BytesOut ("buf", "count"); UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "read from the NBD server"; @@ -1411,7 +1427,7 @@ protocol extensions)."; cbargs=[CBBytesIn ("subbuf", "count"); CBUInt64 "offset"; CBUInt "status"; CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "read from the NBD server"; @@ -1485,7 +1501,7 @@ actually obeys the flag."; "pwrite", { default_call with args = [ BytesIn ("buf", "count"); UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "write to the NBD server"; @@ -1505,7 +1521,7 @@ C<nbd_can_fua>)."; "shutdown", { default_call with - args = []; optargs = [ OFlags "flags" ]; ret = RErr; + args = []; optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "disconnect from the NBD server"; longdesc = "\ @@ -1525,7 +1541,7 @@ protocol extensions)."; "flush", { default_call with - args = []; optargs = [ OFlags "flags" ]; ret = RErr; + args = []; optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "send flush command to the NBD server"; longdesc = "\ @@ -1541,7 +1557,7 @@ protocol extensions)."; "trim", { default_call with args = [ UInt64 "count"; UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "send trim command to the NBD server"; @@ -1562,7 +1578,7 @@ C<nbd_can_fua>)."; "cache", { default_call with args = [ UInt64 "count"; UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "send cache (prefetch) command to the NBD server"; @@ -1581,7 +1597,7 @@ protocol extensions)."; "zero", { default_call with args = [ UInt64 "count"; UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "send write zeroes command to the NBD server"; @@ -1610,7 +1626,7 @@ punching a hole."; CBArrayAndLen (UInt32 "entries", "nr_entries"); CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "send block status command to the NBD server"; @@ -1765,7 +1781,7 @@ on the connection."; "aio_pread", { default_call with args = [ BytesPersistOut ("buf", "count"); UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "read from the NBD server"; @@ -1784,7 +1800,7 @@ C<nbd_pread>."; args = [ BytesPersistOut ("buf", "count"); UInt64 "offset"; Closure { cbname="completion"; cbargs=[CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "read from the NBD server, with callback on completion"; @@ -1808,7 +1824,7 @@ completed. Other parameters behave as documented in C<nbd_pread>."; CBUInt64 "offset"; CBUInt "status"; CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "read from the NBD server"; @@ -1831,7 +1847,7 @@ documented in C<nbd_pread_structured>."; CBMutable (Int "error")] }; Closure { cbname="completion"; cbargs=[CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "read from the NBD server, with callback on completion"; @@ -1849,7 +1865,7 @@ Other parameters behave as documented in C<nbd_pread_structured>."; "aio_pwrite", { default_call with args = [ BytesPersistIn ("buf", "count"); UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "write to the NBD server"; @@ -1868,7 +1884,7 @@ C<nbd_pwrite>."; args = [ BytesPersistIn ("buf", "count"); UInt64 "offset"; Closure { cbname="completion"; cbargs=[CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "write to the NBD server, with callback on completion"; @@ -1886,7 +1902,7 @@ completed. Other parameters behave as documented in C<nbd_pwrite>."; "aio_disconnect", { default_call with - args = []; optargs = [ OFlags "flags" ]; ret = RErr; + args = []; optargs = [ OFlags ("flags", cmd_flags) ]; ret = RErr; permitted_states = [ Connected ]; shortdesc = "disconnect from the NBD server"; longdesc = "\ @@ -1909,7 +1925,7 @@ however, C<nbd_shutdown> will call this function if appropriate."; "aio_flush", { default_call with - args = []; optargs = [ OFlags "flags" ]; ret = RInt64; + args = []; optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send flush command to the NBD server"; longdesc = "\ @@ -1924,7 +1940,7 @@ Parameters behave as documented in C<nbd_flush>."; default_call with args = [ Closure { cbname="completion"; cbargs=[CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send flush command to the NBD server, with callback on completion"; @@ -1942,7 +1958,7 @@ Other parameters behave as documented in C<nbd_flush>."; "aio_trim", { default_call with args = [ UInt64 "count"; UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send trim command to the NBD server"; @@ -1959,7 +1975,7 @@ Parameters behave as documented in C<nbd_trim>."; args = [ UInt64 "count"; UInt64 "offset"; Closure { cbname="completion"; cbargs=[CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send trim command to the NBD server, with callback on completion"; @@ -1977,7 +1993,7 @@ Other parameters behave as documented in C<nbd_trim>."; "aio_cache", { default_call with args = [ UInt64 "count"; UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send cache (prefetch) command to the NBD server"; @@ -1994,7 +2010,7 @@ Parameters behave as documented in C<nbd_cache>."; args = [ UInt64 "count"; UInt64 "offset"; Closure { cbname="completion"; cbargs=[CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send cache (prefetch) command to the NBD server, with callback on completion"; @@ -2012,7 +2028,7 @@ Other parameters behave as documented in C<nbd_cache>."; "aio_zero", { default_call with args = [ UInt64 "count"; UInt64 "offset" ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send write zeroes command to the NBD server"; @@ -2029,7 +2045,7 @@ Parameters behave as documented in C<nbd_zero>."; args = [ UInt64 "count"; UInt64 "offset"; Closure { cbname="completion"; cbargs=[CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send write zeroes command to the NBD server, with callback on completion"; @@ -2052,7 +2068,7 @@ Other parameters behave as documented in C<nbd_zero>."; CBArrayAndLen (UInt32 "entries", "nr_entries"); CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send block status command to the NBD server"; @@ -2074,7 +2090,7 @@ Parameters behave as documented in C<nbd_block_status>."; CBMutable (Int "error")] }; Closure { cbname="completion"; cbargs=[CBMutable (Int "error")] } ]; - optargs = [ OFlags "flags" ]; + optargs = [ OFlags ("flags", cmd_flags) ]; ret = RInt64; permitted_states = [ Connected ]; shortdesc = "send block status command to the NBD server, with callback on completion"; @@ -2369,17 +2385,12 @@ C<nbd_aio_connect_uri>."; ] -(* Constants, flags, etc. *) +(* Constants, etc. *) let constants = [ "AIO_DIRECTION_READ", 1; "AIO_DIRECTION_WRITE", 2; "AIO_DIRECTION_BOTH", 3; - "CMD_FLAG_FUA", 1 lsl 0; - "CMD_FLAG_NO_HOLE", 1 lsl 1; - "CMD_FLAG_DF", 1 lsl 2; - "CMD_FLAG_REQ_ONE", 1 lsl 3; - "READ_DATA", 1; "READ_HOLE", 2; "READ_ERROR", 3; @@ -3373,7 +3384,7 @@ let rec print_arg_list ?(handle = false) ?(types = true) args optargs = if !comma then pr ", "; comma := true; match optarg with - | OFlags n -> + | OFlags (n, _) -> if types then pr "uint32_t "; pr "%s" n ) optargs; @@ -3501,7 +3512,20 @@ let generate_include_libnbd_h () = pr "\n"; pr "struct nbd_handle;\n"; pr "\n"; - List.iter (fun (n, i) -> pr "#define LIBNBD_%-30s %d\n" n i) constants; + List.iter ( + fun { flag_prefix; flags } -> + List.iter ( + fun (flag, i) -> + let flag = sprintf "LIBNBD_%s_%s" flag_prefix flag in + pr "#define %-40s %d\n" flag i + ) flags; + pr "\n" + ) all_flags; + List.iter ( + fun (n, i) -> + let n = sprintf "LIBNBD_%s" n in + pr "#define %-40s %d\n" n i + ) constants; pr "\n"; pr "#define LIBNBD_CALLBACK_VALID 1\n"; pr "#define LIBNBD_CALLBACK_FREE 2\n"; @@ -3689,7 +3713,7 @@ let generate_lib_api_c () = ) args; List.iter ( function - | OFlags n -> pr " %s=0x%%x" n + | OFlags (n, _) -> pr " %s=0x%%x" n ) optargs; pr "\""; List.iter ( @@ -3709,7 +3733,7 @@ let generate_lib_api_c () = ) args; List.iter ( function - | OFlags n -> pr ", %s" n + | OFlags (n, _) -> pr ", %s" n ) optargs; pr ");\n" (* Print the trace when we leave a call with debugging enabled. *) @@ -4208,7 +4232,7 @@ let print_python_binding name { args; optargs; ret; may_set_error } = ) args; List.iter ( function - | OFlags n -> + | OFlags (n, _) -> pr " uint32_t %s_u32;\n" n; pr " unsigned int %s; /* really uint32_t */\n" n ) optargs; @@ -4236,7 +4260,7 @@ let print_python_binding name { args; optargs; ret; may_set_error } = ) args; List.iter ( function - | OFlags n -> pr " \"I\"" + | OFlags _ -> pr " \"I\"" ) optargs; pr "\n"; pr " \":nbd_%s\",\n" name; @@ -4260,7 +4284,7 @@ let print_python_binding name { args; optargs; ret; may_set_error } = ) args; List.iter ( function - | OFlags n -> pr ", &%s" n + | OFlags (n, _) -> pr ", &%s" n ) optargs; pr "))\n"; pr " return NULL;\n"; @@ -4299,7 +4323,7 @@ let print_python_binding name { args; optargs; ret; may_set_error } = ) args; List.iter ( function - | OFlags n -> pr " %s_u32 = %s;\n" n n + | OFlags (n, _) -> pr " %s_u32 = %s;\n" n n ) optargs; (* Call the underlying C function. *) @@ -4326,7 +4350,7 @@ let print_python_binding name { args; optargs; ret; may_set_error } = ) args; List.iter ( function - | OFlags n -> pr ", %s_u32" n + | OFlags (n, _) -> pr ", %s_u32" n ) optargs; pr ");\n"; if may_set_error then ( @@ -4481,6 +4505,15 @@ Error.__str__ = _str "; + List.iter ( + fun { flag_prefix; flags } -> + List.iter ( + fun (flag, i) -> + let flag = sprintf "%s_%s" flag_prefix flag in + pr "%-30s = %d\n" flag i + ) flags; + pr "\n" + ) all_flags; List.iter (fun (n, i) -> pr "%-30s = %d\n" n i) constants; List.iter ( fun (ns, ctxts) -> @@ -4545,7 +4578,7 @@ class NBD (object): let optargs = List.map ( function - | OFlags n -> n, "0" + | OFlags (n, _) -> n, "0" ) optargs in let () = let params = args @ List.map (fun (n, def) -> n ^ "=" ^ def) optargs in @@ -4628,7 +4661,7 @@ and ocaml_ret_to_string = function | RUInt -> "int" and ocaml_optarg_to_string = function - | OFlags n -> sprintf "?%s:int32 list" n + | OFlags (n, { flag_prefix }) -> sprintf "?%s:%s.t list" n flag_prefix and ocaml_closuredecl_to_string cbargs = let cbargs = List.map ocaml_cbarg_to_string cbargs in @@ -4664,7 +4697,7 @@ let ocaml_name_of_arg = function | UInt64 n -> n let ocaml_name_of_optarg = function - | OFlags n -> n + | OFlags (n, _) -> n let num_params args optargs = List.length optargs + 1 (* handle *) + List.length args @@ -4694,6 +4727,17 @@ exception Closed of string "; + List.iter ( + fun { flag_prefix; flags } -> + pr "module %s : sig\n" flag_prefix; + pr " type t =\n"; + List.iter ( + fun (flag, _) -> + pr " | %s\n" flag + ) flags; + pr "end\n"; + pr "\n" + ) all_flags; List.iter ( fun (n, _) -> pr "val %s : int32\n" (String.lowercase_ascii n) ) constants; @@ -4775,6 +4819,17 @@ let () = "; + List.iter ( + fun { flag_prefix; flags } -> + pr "module %s = struct\n" flag_prefix; + pr " type t =\n"; + List.iter ( + fun (flag, _) -> + pr " | %s\n" flag + ) flags; + pr "end\n"; + pr "\n" + ) all_flags; List.iter ( fun (n, i) -> pr "let %s = %d_l\n" (String.lowercase_ascii n) i ) constants; @@ -4819,6 +4874,33 @@ external close : t -> unit = \"nbd_internal_ocaml_nbd_close\" pr "\"nbd_internal_ocaml_nbd_%s\"\n" name ) handle_calls +let print_ocaml_flag_val { flag_prefix; flags } = + pr "/* Convert OCaml %s.t list to uint32_t bitmask. */\n" flag_prefix; + pr "static uint32_t\n"; + pr "%s_val (value v)\n" flag_prefix; + pr "{\n"; + pr " CAMLparam1 (v);\n"; + pr " int i;\n"; + pr " uint32_t r = 0;\n"; + pr "\n"; + pr " for (; v != Val_emptylist; v = Field (v, 1)) {\n"; + pr " i = Int_val (Field (v, 0));\n"; + pr " /* i is the index of the flag in the type\n"; + pr " * (eg. i = 0 => flag = %s.%s).\n" flag_prefix (fst (List.hd flags)); + pr " * Convert it to the C representation.\n"; + pr " */\n"; + pr " switch (i) {\n"; + List.iteri ( + fun i (flag, _) -> + pr " case %d: r |= LIBNBD_%s_%s; break;\n" i flag_prefix flag + ) flags; + pr " }\n"; + pr " }\n"; + pr "\n"; + pr " CAMLreturnT (uint32_t, r);\n"; + pr "}\n"; + pr "\n" + let print_ocaml_binding (name, { args; optargs; ret }) = (* Functions with a callback parameter require special handling. *) List.iter ( @@ -4978,10 +5060,11 @@ let print_ocaml_binding (name, { args; optargs; ret }) = List.iter ( function - | OFlags n -> + | OFlags (n, { flag_prefix }) -> pr " uint32_t %s;\n" n; - pr " if (%sv != Val_int (0)) /* Some flags */\n" n; - pr " %s = Flags_val (Field (%sv, 0));\n" n n; + pr " if (%sv != Val_int (0)) /* Some [ list of %s.t ] */\n" + n flag_prefix; + pr " %s = %s_val (Field (%sv, 0));\n" n flag_prefix n; pr " else /* None */\n"; pr " %s = 0;\n" n ) optargs; @@ -5126,6 +5209,7 @@ let generate_ocaml_nbd_c () = pr "#pragma GCC diagnostic ignored \"-Wmissing-prototypes\"\n"; pr "\n"; + List.iter print_ocaml_flag_val all_flags; List.iter print_ocaml_binding handle_calls end diff --git a/ocaml/nbd-c.h b/ocaml/nbd-c.h index 0c12dc2..ffd51d2 100644 --- a/ocaml/nbd-c.h +++ b/ocaml/nbd-c.h @@ -99,21 +99,6 @@ Val_nbd_buffer (struct nbd_buffer b) CAMLreturn (rv); } -/* Convert flags to uint32_t. This is simple because flags are just - * lists of int32 values so we only have to add them together. - */ -static inline uint32_t -Flags_val (value v) -{ - CAMLparam1 (v); - uint32_t r = 0; - - for (; v != Val_emptylist; v = Field (v, 1)) - r += Int32_val (Field (v, 0)); - - CAMLreturnT (uint32_t, r); -} - struct callback_data { value *cb; value *data; diff --git a/ocaml/tests/test_405_pread_structured.ml b/ocaml/tests/test_405_pread_structured.ml index d226af0..e6a3b15 100644 --- a/ocaml/tests/test_405_pread_structured.ml +++ b/ocaml/tests/test_405_pread_structured.ml @@ -54,11 +54,13 @@ let () = NBD.pread_structured nbd buf 0_L (f 42); assert (buf = expected); - NBD.pread_structured nbd buf 0_L (f 42) ~flags:[NBD.cmd_flag_df]; + let flags = let open NBD.CMD_FLAG in [DF] in + + NBD.pread_structured nbd buf 0_L (f 42) ~flags; assert (buf = expected); try - NBD.pread_structured nbd buf 0_L (f 43) ~flags:[NBD.cmd_flag_df]; + NBD.pread_structured nbd buf 0_L (f 43) ~flags; assert false with NBD.Error (_, errno) -> diff --git a/ocaml/tests/test_410_pwrite.ml b/ocaml/tests/test_410_pwrite.ml index eb4fc7a..ab0feda 100644 --- a/ocaml/tests/test_410_pwrite.ml +++ b/ocaml/tests/test_410_pwrite.ml @@ -33,7 +33,8 @@ let () = let nbd = NBD.create () in NBD.connect_command nbd ["nbdkit"; "-s"; "--exit-with-parent"; "-v"; "file"; datafile]; - NBD.pwrite nbd buf1 0_L ~flags:[NBD.cmd_flag_fua]; + let flags = let open NBD.CMD_FLAG in [FUA] in + NBD.pwrite nbd buf1 0_L ~flags; let buf2 = Bytes.create 512 in NBD.pread nbd buf2 0_L; diff --git a/ocaml/tests/test_460_block_status.ml b/ocaml/tests/test_460_block_status.ml index df861c9..69635b4 100644 --- a/ocaml/tests/test_460_block_status.ml +++ b/ocaml/tests/test_460_block_status.ml @@ -50,7 +50,8 @@ let () = assert (!entries = [| 512_l; 3_l; 16384_l; 2_l |]); - NBD.block_status nbd 1024_L 32256_L (f 42) ~flags:[NBD.cmd_flag_req_one]; + let flags = let open NBD.CMD_FLAG in [REQ_ONE] in + NBD.block_status nbd 1024_L 32256_L (f 42) ~flags; assert (!entries = [| 512_l; 3_l |]) let () = Gc.compact () diff --git a/ocaml/tests/test_510_aio_pwrite.ml b/ocaml/tests/test_510_aio_pwrite.ml index 9a46b83..3d04e41 100644 --- a/ocaml/tests/test_510_aio_pwrite.ml +++ b/ocaml/tests/test_510_aio_pwrite.ml @@ -35,7 +35,8 @@ let () = "file"; datafile]; let buf1 = NBD.Buffer.of_bytes buf in - let cookie = NBD.aio_pwrite nbd buf1 0_L ~flags:[NBD.cmd_flag_fua] in + let flags = let open NBD.CMD_FLAG in [FUA] in + let cookie = NBD.aio_pwrite nbd buf1 0_L ~flags in while not (NBD.aio_command_completed nbd cookie) do ignore (NBD.poll nbd (-1)) done; -- 2.22.0