Eric Blake
2023-Jul-26 17:29 UTC
[Libguestfs] [libnbd PATCH 0/7] Preliminary GoLang cleanups
In Laszlo's review of my 64-bit extensions, he pointed out some questionable code in the GoLang.ml generated code, including some inconsistencies on whitespace before '('. Fixing that in turn inspired me to learn about gofmt, so I attempted to address the issues it points out. (One perk of being a modern language: you can enforce everyone to share the same style, rather than having different camps each defend their years-long but slightly distinct styles...) We may want to optionally run gofmt as part of the build process (I know Tage proposed doing similar with rustfmt), and/or write a further patch to have the const() blocks in GoLang.ml do a two-pass iteration over lists (first to learn the maximum column width, second to do the output). Eric Blake (7): api: Expose "qemu:" meta-context constants golang: Export meta-context constants golang: Simplify RBool return Revert "generator/Go.ml: Simplify copy_uint32_array" golang: Use 'gofmt' style recommendations on manual files golang: Improve whitespace style in generated bindings golang: Enforce coding style during 'make check' generator/utils.mli | 1 + generator/API.ml | 9 +- generator/C.ml | 22 +- generator/GoLang.ml | 246 +++++++++--------- generator/OCaml.ml | 4 +- generator/Python.ml | 3 +- generator/utils.ml | 5 + interop/dirty-bitmap.c | 6 +- golang/Makefile.am | 2 +- golang/codestyle-tests.sh | 45 ++++ golang/configure/test.go | 14 +- golang/examples/aio_copy/aio_copy.go | 7 +- golang/examples/simple_copy/simple_copy.go | 3 +- golang/libnbd_220_opt_list_test.go | 4 +- golang/libnbd_230_opt_info_test.go | 24 +- golang/libnbd_240_opt_list_meta_test.go | 44 ++-- .../libnbd_245_opt_list_meta_queries_test.go | 14 +- golang/libnbd_250_opt_set_meta_test.go | 20 +- .../libnbd_255_opt_set_meta_queries_test.go | 14 +- golang/libnbd_405_pread_structured_test.go | 10 +- golang/libnbd_460_block_status_test.go | 4 +- golang/libnbd_590_aio_copy_test.go | 21 +- golang/libnbd_620_stats_test.go | 12 +- 23 files changed, 307 insertions(+), 227 deletions(-) create mode 100755 golang/codestyle-tests.sh base-commit: 5c2fc3cc7e14146d000b65b191e70d9a0585a395 -- 2.41.0
Eric Blake
2023-Jul-26 17:29 UTC
[Libguestfs] [libnbd PATCH 1/7] api: Expose "qemu:" meta-context constants
Qemu has a couple of documented meta-context definitions[1]; we might as well expose constants for these namespaces. "qemu:dirty-bitmap:NAME" is a set of namespaces for any arbitrary dirty bitmap name; we can't define constants for every bitspace name, but it is possible to do NBD_OPT_LIST_META_CONTEXT on "qemu:dirty-bitmap:" to see which dirty bitmaps are available. When a dirty bitmap is negotiated, only one bit is defined (an extent is dirty or not). The presence of '-' and ':' in the context name beyond the namespace requires a new helper function. "qemu:allocation-depth" returns an integer rather than a bitmap (0 for unmapped, 1 for current image, 2 and beyond for number of files in the backing chain before the data was supplied), so we can't really define any constants for interpreting its values. [1] https://gitlab.com/qemu-project/qemu/-/blob/master/docs/interop/nbd.txt For libnbd.h, the generated diff is: | --- include/libnbd.h.bak 2023-07-26 11:01:45.401328604 -0500 | +++ include/libnbd.h 2023-07-26 11:59:38.289021067 -0500 | @@ -1083,6 +1083,16 @@ | #define LIBNBD_STATE_HOLE 1 | #define LIBNBD_STATE_ZERO 2 | | +/* "qemu" namespace */ | +#define LIBNBD_NAMESPACE_QEMU "qemu:" | + | +/* "qemu" namespace contexts */ | +#define LIBNBD_CONTEXT_QEMU_DIRTY_BITMAP "qemu:dirty-bitmap:" | +#define LIBNBD_CONTEXT_QEMU_ALLOCATION_DEPTH "qemu:allocation-depth" | + | +/* "qemu:dirty-bitmap:" context related constants */ | +#define LIBNBD_STATE_DIRTY 1 | + | #ifdef __cplusplus | } | #endif Signed-off-by: Eric Blake <eblake at redhat.com> --- generator/utils.mli | 1 + generator/API.ml | 9 ++++++--- generator/C.ml | 22 +++++++++++++++------- generator/GoLang.ml | 3 ++- generator/OCaml.ml | 4 ++-- generator/Python.ml | 3 ++- generator/utils.ml | 5 +++++ interop/dirty-bitmap.c | 6 ++++-- 8 files changed, 37 insertions(+), 16 deletions(-) diff --git a/generator/utils.mli b/generator/utils.mli index 773e705f..c4d47a34 100644 --- a/generator/utils.mli +++ b/generator/utils.mli @@ -46,6 +46,7 @@ val val cspan : string -> string -> int val quote : string -> string val spaces : int -> string +val macro_name : string -> string val files_equal : string -> string -> bool val generate_header : diff --git a/generator/API.ml b/generator/API.ml index 02c1260d..72c81657 100644 --- a/generator/API.ml +++ b/generator/API.ml @@ -3919,9 +3919,12 @@ let constants let metadata_namespaces = [ "base", [ "allocation", [ - "STATE_HOLE", 1 lsl 0; - "STATE_ZERO", 1 lsl 1; - ] ]; + "STATE_HOLE", 1 lsl 0; + "STATE_ZERO", 1 lsl 1; + ] ]; + "qemu", [ "dirty-bitmap:", [ "STATE_DIRTY", 1 lsl 0; ]; + "allocation-depth", []; + ]; ] let pod_of_link = function diff --git a/generator/C.ml b/generator/C.ml index f772117c..a2670f70 100644 --- a/generator/C.ml +++ b/generator/C.ml @@ -373,13 +373,18 @@ let pr "#define LIBNBD_HAVE_NBD_%s 1\n" name_upper; pr "\n" -let print_ns_ctxt ns ns_upper ctxt consts - let ctxt_upper = String.uppercase_ascii ctxt in +let print_ns_ctxt ns ns_upper ctxt + let ctxt_macro = macro_name ctxt in + let ctxt_upper = String.uppercase_ascii ctxt_macro in pr "#define LIBNBD_CONTEXT_%s_%s \"%s:%s\"\n" - ns_upper ctxt_upper ns ctxt; - pr "\n"; - pr "/* \"%s:%s\" context related constants */\n" ns ctxt; - List.iter (fun (n, i) -> pr "#define LIBNBD_%-30s %d\n" n i) consts + ns_upper ctxt_upper ns ctxt + +let print_ns_ctxt_bits ns ctxt consts + if consts <> [] then ( + pr "\n"; + pr "/* \"%s:%s\" context related constants */\n" ns ctxt; + List.iter (fun (n, i) -> pr "#define LIBNBD_%-30s %d\n" n i) consts + ) let print_ns ns ctxts let ns_upper = String.uppercase_ascii ns in @@ -388,7 +393,10 @@ let pr "\n"; pr "/* \"%s\" namespace contexts */\n" ns; List.iter ( - fun (ctxt, consts) -> print_ns_ctxt ns ns_upper ctxt consts + fun (ctxt, _) -> print_ns_ctxt ns ns_upper ctxt + ) ctxts; + List.iter ( + fun (ctxt, consts) -> print_ns_ctxt_bits ns ctxt consts ) ctxts; pr "\n" diff --git a/generator/GoLang.ml b/generator/GoLang.ml index 50ed7226..7a7e7f4b 100644 --- a/generator/GoLang.ml +++ b/generator/GoLang.ml @@ -472,7 +472,8 @@ let pr " namespace_%s = \"%s:\"\n" ns ns; List.iter ( fun (ctxt, consts) -> - pr " context_%s_%s = \"%s:%s\"\n" ns ctxt ns ctxt; + let ctxt_macro = macro_name ctxt in + pr " context_%s_%s = \"%s:%s\"\n" ns ctxt_macro ns ctxt; List.iter (fun (n, v) -> pr " %s uint32 = %d\n" n v ) consts diff --git a/generator/OCaml.ml b/generator/OCaml.ml index edb81f25..efc1af22 100644 --- a/generator/OCaml.ml +++ b/generator/OCaml.ml @@ -183,7 +183,7 @@ type pr "val namespace_%s : string\n" ns; List.iter ( fun (ctxt, consts) -> - pr "val context_%s_%s : string\n" ns ctxt; + pr "val context_%s_%s : string\n" ns (macro_name ctxt); List.iter (fun (n, _) -> pr "val %s : int32\n" (String.lowercase_ascii n) ) consts @@ -315,7 +315,7 @@ let pr "let namespace_%s = \"%s:\"\n" ns ns; List.iter ( fun (ctxt, consts) -> - pr "let context_%s_%s = \"%s:%s\"\n" ns ctxt ns ctxt; + pr "let context_%s_%s = \"%s:%s\"\n" ns (macro_name ctxt) ns ctxt; List.iter (fun (n, i) -> pr "let %s = %d_l\n" (String.lowercase_ascii n) i ) consts diff --git a/generator/Python.ml b/generator/Python.ml index c81878de..beaeaa4c 100644 --- a/generator/Python.ml +++ b/generator/Python.ml @@ -745,7 +745,8 @@ let pr "NAMESPACE_%s = \"%s:\"\n" ns_upper ns; List.iter ( fun (ctxt, consts) -> - let ctxt_upper = String.uppercase_ascii ctxt in + let ctxt_macro = macro_name ctxt in + let ctxt_upper = String.uppercase_ascii ctxt_macro in pr "%s = \"%s:%s\"\n" (sprintf "CONTEXT_%s_%s" ns_upper ctxt_upper) ns ctxt; List.iter (fun (n, i) -> pr "%s = %d\n" n i) consts diff --git a/generator/utils.ml b/generator/utils.ml index e0c67d20..61cce877 100644 --- a/generator/utils.ml +++ b/generator/utils.ml @@ -151,6 +151,11 @@ let let spaces n = String.make n ' ' +(* Convert s to macro name by changing '-' to '_' and eliding ':'. *) +let macro_name s + let underscore = Str.global_replace (Str.regexp_string "-") "_" s in + Str.global_replace (Str.regexp ":") "" underscore + (* Save the current output channel and replace it with a temporary buffer while * running ???code???. Return the buffer. *) diff --git a/interop/dirty-bitmap.c b/interop/dirty-bitmap.c index 05f6e9db..16faaed9 100644 --- a/interop/dirty-bitmap.c +++ b/interop/dirty-bitmap.c @@ -127,8 +127,10 @@ main (int argc, char *argv[]) nbd_extent_callback extent_callback = { .callback = cb, .user_data = &data }; char c; - if (argc < 3) { - fprintf (stderr, "%s bitmap qemu-nbd [args ...]\n", argv[0]); + if (argc < 3 || strncmp (argv[1], LIBNBD_CONTEXT_QEMU_DIRTY_BITMAP, + strlen (LIBNBD_CONTEXT_QEMU_DIRTY_BITMAP)) != 0) { + fprintf (stderr, "%s qemu:dirty-bitmap:name qemu-nbd [args ...]\n", + argv[0]); exit (EXIT_FAILURE); } bitmap = argv[1]; -- 2.41.0
Eric Blake
2023-Jul-26 17:29 UTC
[Libguestfs] [libnbd PATCH 2/7] golang: Export meta-context constants
Go insists that a module's members are not public unless they start with a capital letter. Our attempt at defining constants 'namespace_base' and 'context_base_allocation' were thus only visible to our in-package tests, and not for external clients. Although this patch does not completely address gofmt complaints, adding some strategic grouping comments inside the const() block has an additional benefit of reducing the amount of whitespace that gofmt wants to inject into unrelated lines. The change to generated golang/bindigs.go is: | --- golang/bindings.go.bak 2023-07-26 12:06:47.618324437 -0500 | +++ golang/bindings.go 2023-07-26 12:06:55.521385295 -0500 | @@ -111,14 +111,18 @@ | READ_DATA uint32 = 1 | READ_HOLE uint32 = 2 | READ_ERROR uint32 = 3 | - namespace_base = "base:" | - context_base_allocation = "base:allocation" | + /* Meta-context namespace "base" */ | + NAMESPACE_BASE = "base:" | + CONTEXT_BASE_ALLOCATION = "base:allocation" | + /* Defined bits in "base:allocation" */ | STATE_HOLE uint32 = 1 | STATE_ZERO uint32 = 2 | - namespace_qemu = "qemu:" | - context_qemu_dirty_bitmap = "qemu:dirty-bitmap:" | + /* Meta-context namespace "qemu" */ | + NAMESPACE_QEMU = "qemu:" | + CONTEXT_QEMU_DIRTY_BITMAP = "qemu:dirty-bitmap:" | + /* Defined bits in "qemu:dirty-bitmap:" */ | STATE_DIRTY uint32 = 1 | - context_qemu_allocation_depth = "qemu:allocation-depth" | + CONTEXT_QEMU_ALLOCATION_DEPTH = "qemu:allocation-depth" | ) | | /* SetDebug: set or clear the debug flag */ Signed-off-by: Eric Blake <eblake at redhat.com> --- RFC: Do we want MixedCase rather than ALL_CAPS names? If so, this patch needs to be rethought a bit, as it would impact API for existing public constants as well as the ones changed here. But we don't promise API stability for Go. --- generator/GoLang.ml | 10 +++++--- golang/libnbd_230_opt_info_test.go | 24 +++++++++---------- golang/libnbd_240_opt_list_meta_test.go | 14 +++++------ .../libnbd_245_opt_list_meta_queries_test.go | 4 ++-- golang/libnbd_250_opt_set_meta_test.go | 20 ++++++++-------- .../libnbd_255_opt_set_meta_queries_test.go | 12 +++++----- golang/libnbd_460_block_status_test.go | 4 ++-- 7 files changed, 46 insertions(+), 42 deletions(-) diff --git a/generator/GoLang.ml b/generator/GoLang.ml index 7a7e7f4b..82d73ed6 100644 --- a/generator/GoLang.ml +++ b/generator/GoLang.ml @@ -469,11 +469,15 @@ let ) constants; List.iter ( fun (ns, ctxts) -> - pr " namespace_%s = \"%s:\"\n" ns ns; + let ns_upper = String.uppercase_ascii ns in + pr " /* Meta-context namespace \"%s\" */\n" ns; + pr " NAMESPACE_%s = \"%s:\"\n" ns_upper ns; List.iter ( fun (ctxt, consts) -> - let ctxt_macro = macro_name ctxt in - pr " context_%s_%s = \"%s:%s\"\n" ns ctxt_macro ns ctxt; + let ctxt_macro = String.uppercase_ascii (macro_name ctxt) in + pr " CONTEXT_%s_%s = \"%s:%s\"\n" ns_upper ctxt_macro ns ctxt; + if consts <> [] then + pr " /* Defined bits in \"%s:%s\" */\n" ns ctxt; List.iter (fun (n, v) -> pr " %s uint32 = %d\n" n v ) consts diff --git a/golang/libnbd_230_opt_info_test.go b/golang/libnbd_230_opt_info_test.go index cd00be9e..cc7c2d91 100644 --- a/golang/libnbd_230_opt_info_test.go +++ b/golang/libnbd_230_opt_info_test.go @@ -45,7 +45,7 @@ func Test230OptInfo(t *testing.T) { t.Fatalf("could not connect: %s", err) } - err = h.AddMetaContext(context_base_allocation) + err = h.AddMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not add meta context: %s", err) } @@ -59,7 +59,7 @@ func Test230OptInfo(t *testing.T) { if err == nil { t.Fatalf("expected error") } - _, err = h.CanMetaContext(context_base_allocation) + _, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err == nil { t.Fatalf("expected error") } @@ -83,7 +83,7 @@ func Test230OptInfo(t *testing.T) { if !ro { t.Fatalf("unexpected readonly") } - meta, err := h.CanMetaContext(context_base_allocation) + meta, err := h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("can_meta failed unexpectedly: %s", err) } @@ -104,7 +104,7 @@ func Test230OptInfo(t *testing.T) { if err == nil { t.Fatalf("expected error") } - _, err = h.CanMetaContext(context_base_allocation) + _, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err == nil { t.Fatalf("expected error") } @@ -151,7 +151,7 @@ func Test230OptInfo(t *testing.T) { if ro { t.Fatalf("unexpected readonly") } - _, err = h.CanMetaContext(context_base_allocation) + _, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err == nil { t.Fatalf("expected error") } @@ -177,7 +177,7 @@ func Test230OptInfo(t *testing.T) { if err == nil { t.Fatalf("expected error") } - _, err = h.CanMetaContext(context_base_allocation) + _, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err == nil { t.Fatalf("expected error") } @@ -205,7 +205,7 @@ func Test230OptInfo(t *testing.T) { if !ro { t.Fatalf("unexpected readonly") } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("can_meta failed unexpectedly: %s", err) } @@ -236,7 +236,7 @@ func Test230OptInfo(t *testing.T) { if size != 4 { t.Fatalf("unexpected size") } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("can_meta failed unexpectedly: %s", err) } @@ -270,7 +270,7 @@ func Test230OptInfo(t *testing.T) { t.Fatalf("could not add meta context: %s", err) } - _, err = h.CanMetaContext(context_base_allocation) + _, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err == nil { t.Fatalf("expected error") } @@ -278,7 +278,7 @@ func Test230OptInfo(t *testing.T) { if err != nil { t.Fatalf("opt_info failed unexpectedly: %s", err) } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("can_meta failed unexpectedly: %s", err) } @@ -290,7 +290,7 @@ func Test230OptInfo(t *testing.T) { t.Fatalf("set request meta context failed unexpectedly: %s", err) } /* Adding to the request list now won't matter */ - err = h.AddMetaContext(context_base_allocation) + err = h.AddMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not add meta context: %s", err) } @@ -298,7 +298,7 @@ func Test230OptInfo(t *testing.T) { if err != nil { t.Fatalf("opt_go failed unexpectedly: %s", err) } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("can_meta failed unexpectedly: %s", err) } diff --git a/golang/libnbd_240_opt_list_meta_test.go b/golang/libnbd_240_opt_list_meta_test.go index 0235fe3f..011b5704 100644 --- a/golang/libnbd_240_opt_list_meta_test.go +++ b/golang/libnbd_240_opt_list_meta_test.go @@ -31,7 +31,7 @@ func listmetaf(user_data int, name string) int { panic("expected user_data == 42") } list_count++ - if (name == context_base_allocation) { + if (name == CONTEXT_BASE_ALLOCATION) { list_seen = true } return 0 @@ -92,7 +92,7 @@ func Test240OptListMeta(t *testing.T) { /* Third pass: specific query should have one match. */ list_count = 0 list_seen = false - err = h.AddMetaContext(context_base_allocation) + err = h.AddMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not request add_meta_context: %s", err) } @@ -107,7 +107,7 @@ func Test240OptListMeta(t *testing.T) { if err != nil { t.Fatalf("could not request get_meta_context: %s", err) } - if *tmp != context_base_allocation { + if *tmp != CONTEXT_BASE_ALLOCATION { t.Fatalf("wrong result of get_meta_context: %s", *tmp) } r, err = h.OptListMetaContext(func(name string) int { @@ -129,7 +129,7 @@ func Test240OptListMeta(t *testing.T) { if err == nil { t.Fatalf("expected error") } - _, err = h.CanMetaContext(context_base_allocation) + _, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err == nil { t.Fatalf("expected error") } @@ -144,7 +144,7 @@ func Test240OptListMeta(t *testing.T) { if size != 1048576 { t.Fatalf("get_size gave wrong size") } - meta, err := h.CanMetaContext(context_base_allocation) + meta, err := h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("can_meta_context failed unexpectedly: %s", err) } @@ -175,7 +175,7 @@ func Test240OptListMeta(t *testing.T) { if size != 1048576 { t.Fatalf("get_size gave wrong size") } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("can_meta_context failed unexpectedly: %s", err) } @@ -187,7 +187,7 @@ func Test240OptListMeta(t *testing.T) { /* Final pass: "base:" query should get at least "base:allocation" */ list_count = 0 list_seen = false - err = h.AddMetaContext("base:") + err = h.AddMetaContext(NAMESPACE_BASE) if err != nil { t.Fatalf("could not request add_meta_context: %s", err) } diff --git a/golang/libnbd_245_opt_list_meta_queries_test.go b/golang/libnbd_245_opt_list_meta_queries_test.go index fc1ab60c..3ae2d854 100644 --- a/golang/libnbd_245_opt_list_meta_queries_test.go +++ b/golang/libnbd_245_opt_list_meta_queries_test.go @@ -28,7 +28,7 @@ func listmetaqf(user_data int, name string) int { panic("expected user_data == 42") } listq_count++ - if (name == context_base_allocation) { + if (name == CONTEXT_BASE_ALLOCATION) { listq_seen = true } return 0 @@ -97,7 +97,7 @@ func(name string) int { listq_count = 0 listq_seen = false r, err = h.OptListMetaContextQueries([]string{ - "x-nosuch:", context_base_allocation }, + "x-nosuch:", CONTEXT_BASE_ALLOCATION }, func(name string) int { return listmetaqf(42, name) }) diff --git a/golang/libnbd_250_opt_set_meta_test.go b/golang/libnbd_250_opt_set_meta_test.go index 6d27fa22..4263ede5 100644 --- a/golang/libnbd_250_opt_set_meta_test.go +++ b/golang/libnbd_250_opt_set_meta_test.go @@ -28,7 +28,7 @@ func setmetaf(user_data int, name string) int { panic("expected user_data == 42") } set_count++ - if (name == context_base_allocation) { + if (name == CONTEXT_BASE_ALLOCATION) { set_seen = true } return 0 @@ -67,18 +67,18 @@ func Test250OptSetMeta(t *testing.T) { if sr { t.Fatalf("unexpected structured replies state") } - meta, err := h.CanMetaContext(context_base_allocation) + meta, err := h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not check can meta context: %s", err) } if meta { t.Fatalf("unexpected can meta context state") } - err = h.AddMetaContext(context_base_allocation) + err = h.AddMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not request add_meta_context: %s", err) } - _, err = h.CanMetaContext(context_base_allocation) + _, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err == nil { t.Fatalf("expected error") } @@ -109,7 +109,7 @@ func Test250OptSetMeta(t *testing.T) { if !sr { t.Fatalf("unexpected structured replies state") } - _, err = h.CanMetaContext(context_base_allocation) + _, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err == nil { t.Fatalf("expected error") } @@ -151,7 +151,7 @@ func Test250OptSetMeta(t *testing.T) { if r != set_count || r != 0 || set_seen { t.Fatalf("unexpected set_count after opt_set_meta_context") } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not check can meta context: %s", err) } @@ -166,7 +166,7 @@ func Test250OptSetMeta(t *testing.T) { if err != nil { t.Fatalf("could not request add_meta_context: %s", err) } - err = h.AddMetaContext(context_base_allocation) + err = h.AddMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not request add_meta_context: %s", err) } @@ -183,7 +183,7 @@ func Test250OptSetMeta(t *testing.T) { if r != 1 || r != set_count || !set_seen { t.Fatalf("unexpected set_count after opt_set_meta_context") } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not check can meta context: %s", err) } @@ -204,7 +204,7 @@ func Test250OptSetMeta(t *testing.T) { if err != nil { t.Fatalf("could not request opt_go: %s", err) } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not check can meta context: %s", err) } @@ -224,7 +224,7 @@ func Test250OptSetMeta(t *testing.T) { if set_count != 0 || set_seen { t.Fatalf("unexpected set_count after opt_set_meta_context") } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not check can meta context: %s", err) } diff --git a/golang/libnbd_255_opt_set_meta_queries_test.go b/golang/libnbd_255_opt_set_meta_queries_test.go index cca25ae7..232560bc 100644 --- a/golang/libnbd_255_opt_set_meta_queries_test.go +++ b/golang/libnbd_255_opt_set_meta_queries_test.go @@ -28,7 +28,7 @@ func setmetaqf(user_data int, name string) int { panic("expected user_data == 42") } setq_count++ - if (name == context_base_allocation) { + if (name == CONTEXT_BASE_ALLOCATION) { setq_seen = true } return 0 @@ -74,7 +74,7 @@ func(name string) int { */ setq_count = 0 setq_seen = false - err = h.AddMetaContext(context_base_allocation) + err = h.AddMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not request add_meta_context: %s", err) } @@ -87,7 +87,7 @@ func(name string) int { if r != setq_count || r != 0 || setq_seen { t.Fatalf("unexpected set_count after opt_set_meta_context_queries") } - meta, err := h.CanMetaContext(context_base_allocation) + meta, err := h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not check can meta context: %s", err) } @@ -99,7 +99,7 @@ func(name string) int { setq_count = 0 setq_seen = false r, err = h.OptSetMetaContextQueries([]string{ - "x-nosuch:context", context_base_allocation}, + "x-nosuch:context", CONTEXT_BASE_ALLOCATION}, func(name string) int { return setmetaqf(42, name) }) @@ -109,7 +109,7 @@ func(name string) int { if r != 1 || r != setq_count || !setq_seen { t.Fatalf("unexpected set_count after opt_set_meta_context_queries") } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not check can meta context: %s", err) } @@ -126,7 +126,7 @@ func(name string) int { if err != nil { t.Fatalf("could not request opt_go: %s", err) } - meta, err = h.CanMetaContext(context_base_allocation) + meta, err = h.CanMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("could not check can meta context: %s", err) } diff --git a/golang/libnbd_460_block_status_test.go b/golang/libnbd_460_block_status_test.go index dca0d1b8..d2d11e9c 100644 --- a/golang/libnbd_460_block_status_test.go +++ b/golang/libnbd_460_block_status_test.go @@ -31,7 +31,7 @@ func mcf(metacontext string, offset uint64, e []uint32, error *int) int { if *error != 0 { panic("expected *error == 0") } - if metacontext == "base:allocation" { + if metacontext == CONTEXT_BASE_ALLOCATION { entries = e } return 0 @@ -69,7 +69,7 @@ func Test460BlockStatus(t *testing.T) { } defer h.Close() - err = h.AddMetaContext("base:allocation") + err = h.AddMetaContext(CONTEXT_BASE_ALLOCATION) if err != nil { t.Fatalf("%s", err) } -- 2.41.0
Eric Blake
2023-Jul-26 17:29 UTC
[Libguestfs] [libnbd PATCH 3/7] golang: Simplify RBool return
Among other things, the 'gofmt' tool flagged our use of a single-line if/else statement for returning an RBool value, recommending we expand it to multiple lines. But we don't need that much typing, when we can just directly compute the boolean result in place. Signed-off-by: Eric Blake <eblake at redhat.com> --- generator/GoLang.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/generator/GoLang.ml b/generator/GoLang.ml index 82d73ed6..0aa83bdc 100644 --- a/generator/GoLang.ml +++ b/generator/GoLang.ml @@ -366,8 +366,7 @@ let | RErr -> pr " return nil\n" | RBool -> - pr " r := int (ret)\n"; - pr " if r != 0 { return true, nil } else { return false, nil }\n" + pr " return int (ret) != 0, nil\n" | RStaticString -> pr " /* ret is statically allocated, do not free it. */\n"; pr " r := C.GoString (ret);\n"; -- 2.41.0
Eric Blake
2023-Jul-26 17:49 UTC
[Libguestfs] [libnbd PATCH 1/7] api: Expose "qemu:" meta-context constants
Qemu has a couple of documented meta-context definitions[1]; we might as well expose constants for these namespaces. "qemu:dirty-bitmap:NAME" is a set of namespaces for any arbitrary dirty bitmap name; we can't define constants for every bitspace name, but it is possible to do NBD_OPT_LIST_META_CONTEXT on "qemu:dirty-bitmap:" to see which dirty bitmaps are available. When a dirty bitmap is negotiated, only one bit is defined (an extent is dirty or not). The presence of '-' and ':' in the context name beyond the namespace requires a new helper function. "qemu:allocation-depth" returns an integer rather than a bitmap (0 for unmapped, 1 for current image, 2 and beyond for number of files in the backing chain before the data was supplied), so we can't really define any constants for interpreting its values. [1] https://gitlab.com/qemu-project/qemu/-/blob/master/docs/interop/nbd.txt For libnbd.h, the generated diff is: | --- include/libnbd.h.bak 2023-07-26 11:01:45.401328604 -0500 | +++ include/libnbd.h 2023-07-26 11:59:38.289021067 -0500 | @@ -1083,6 +1083,16 @@ | #define LIBNBD_STATE_HOLE 1 | #define LIBNBD_STATE_ZERO 2 | | +/* "qemu" namespace */ | +#define LIBNBD_NAMESPACE_QEMU "qemu:" | + | +/* "qemu" namespace contexts */ | +#define LIBNBD_CONTEXT_QEMU_DIRTY_BITMAP "qemu:dirty-bitmap:" | +#define LIBNBD_CONTEXT_QEMU_ALLOCATION_DEPTH "qemu:allocation-depth" | + | +/* "qemu:dirty-bitmap:" context related constants */ | +#define LIBNBD_STATE_DIRTY 1 | + | #ifdef __cplusplus | } | #endif Signed-off-by: Eric Blake <eblake at redhat.com> --- generator/utils.mli | 1 + generator/API.ml | 9 ++++++--- generator/C.ml | 22 +++++++++++++++------- generator/GoLang.ml | 3 ++- generator/OCaml.ml | 4 ++-- generator/Python.ml | 3 ++- generator/utils.ml | 5 +++++ interop/dirty-bitmap.c | 6 ++++-- 8 files changed, 37 insertions(+), 16 deletions(-) diff --git a/generator/utils.mli b/generator/utils.mli index 773e705f..c4d47a34 100644 --- a/generator/utils.mli +++ b/generator/utils.mli @@ -46,6 +46,7 @@ val val cspan : string -> string -> int val quote : string -> string val spaces : int -> string +val macro_name : string -> string val files_equal : string -> string -> bool val generate_header : diff --git a/generator/API.ml b/generator/API.ml index 02c1260d..72c81657 100644 --- a/generator/API.ml +++ b/generator/API.ml @@ -3919,9 +3919,12 @@ let constants let metadata_namespaces = [ "base", [ "allocation", [ - "STATE_HOLE", 1 lsl 0; - "STATE_ZERO", 1 lsl 1; - ] ]; + "STATE_HOLE", 1 lsl 0; + "STATE_ZERO", 1 lsl 1; + ] ]; + "qemu", [ "dirty-bitmap:", [ "STATE_DIRTY", 1 lsl 0; ]; + "allocation-depth", []; + ]; ] let pod_of_link = function diff --git a/generator/C.ml b/generator/C.ml index f772117c..a2670f70 100644 --- a/generator/C.ml +++ b/generator/C.ml @@ -373,13 +373,18 @@ let pr "#define LIBNBD_HAVE_NBD_%s 1\n" name_upper; pr "\n" -let print_ns_ctxt ns ns_upper ctxt consts - let ctxt_upper = String.uppercase_ascii ctxt in +let print_ns_ctxt ns ns_upper ctxt + let ctxt_macro = macro_name ctxt in + let ctxt_upper = String.uppercase_ascii ctxt_macro in pr "#define LIBNBD_CONTEXT_%s_%s \"%s:%s\"\n" - ns_upper ctxt_upper ns ctxt; - pr "\n"; - pr "/* \"%s:%s\" context related constants */\n" ns ctxt; - List.iter (fun (n, i) -> pr "#define LIBNBD_%-30s %d\n" n i) consts + ns_upper ctxt_upper ns ctxt + +let print_ns_ctxt_bits ns ctxt consts + if consts <> [] then ( + pr "\n"; + pr "/* \"%s:%s\" context related constants */\n" ns ctxt; + List.iter (fun (n, i) -> pr "#define LIBNBD_%-30s %d\n" n i) consts + ) let print_ns ns ctxts let ns_upper = String.uppercase_ascii ns in @@ -388,7 +393,10 @@ let pr "\n"; pr "/* \"%s\" namespace contexts */\n" ns; List.iter ( - fun (ctxt, consts) -> print_ns_ctxt ns ns_upper ctxt consts + fun (ctxt, _) -> print_ns_ctxt ns ns_upper ctxt + ) ctxts; + List.iter ( + fun (ctxt, consts) -> print_ns_ctxt_bits ns ctxt consts ) ctxts; pr "\n" diff --git a/generator/GoLang.ml b/generator/GoLang.ml index 50ed7226..7a7e7f4b 100644 --- a/generator/GoLang.ml +++ b/generator/GoLang.ml @@ -472,7 +472,8 @@ let pr " namespace_%s = \"%s:\"\n" ns ns; List.iter ( fun (ctxt, consts) -> - pr " context_%s_%s = \"%s:%s\"\n" ns ctxt ns ctxt; + let ctxt_macro = macro_name ctxt in + pr " context_%s_%s = \"%s:%s\"\n" ns ctxt_macro ns ctxt; List.iter (fun (n, v) -> pr " %s uint32 = %d\n" n v ) consts diff --git a/generator/OCaml.ml b/generator/OCaml.ml index edb81f25..efc1af22 100644 --- a/generator/OCaml.ml +++ b/generator/OCaml.ml @@ -183,7 +183,7 @@ type pr "val namespace_%s : string\n" ns; List.iter ( fun (ctxt, consts) -> - pr "val context_%s_%s : string\n" ns ctxt; + pr "val context_%s_%s : string\n" ns (macro_name ctxt); List.iter (fun (n, _) -> pr "val %s : int32\n" (String.lowercase_ascii n) ) consts @@ -315,7 +315,7 @@ let pr "let namespace_%s = \"%s:\"\n" ns ns; List.iter ( fun (ctxt, consts) -> - pr "let context_%s_%s = \"%s:%s\"\n" ns ctxt ns ctxt; + pr "let context_%s_%s = \"%s:%s\"\n" ns (macro_name ctxt) ns ctxt; List.iter (fun (n, i) -> pr "let %s = %d_l\n" (String.lowercase_ascii n) i ) consts diff --git a/generator/Python.ml b/generator/Python.ml index c81878de..beaeaa4c 100644 --- a/generator/Python.ml +++ b/generator/Python.ml @@ -745,7 +745,8 @@ let pr "NAMESPACE_%s = \"%s:\"\n" ns_upper ns; List.iter ( fun (ctxt, consts) -> - let ctxt_upper = String.uppercase_ascii ctxt in + let ctxt_macro = macro_name ctxt in + let ctxt_upper = String.uppercase_ascii ctxt_macro in pr "%s = \"%s:%s\"\n" (sprintf "CONTEXT_%s_%s" ns_upper ctxt_upper) ns ctxt; List.iter (fun (n, i) -> pr "%s = %d\n" n i) consts diff --git a/generator/utils.ml b/generator/utils.ml index e0c67d20..61cce877 100644 --- a/generator/utils.ml +++ b/generator/utils.ml @@ -151,6 +151,11 @@ let let spaces n = String.make n ' ' +(* Convert s to macro name by changing '-' to '_' and eliding ':'. *) +let macro_name s + let underscore = Str.global_replace (Str.regexp_string "-") "_" s in + Str.global_replace (Str.regexp ":") "" underscore + (* Save the current output channel and replace it with a temporary buffer while * running ???code???. Return the buffer. *) diff --git a/interop/dirty-bitmap.c b/interop/dirty-bitmap.c index 05f6e9db..16faaed9 100644 --- a/interop/dirty-bitmap.c +++ b/interop/dirty-bitmap.c @@ -127,8 +127,10 @@ main (int argc, char *argv[]) nbd_extent_callback extent_callback = { .callback = cb, .user_data = &data }; char c; - if (argc < 3) { - fprintf (stderr, "%s bitmap qemu-nbd [args ...]\n", argv[0]); + if (argc < 3 || strncmp (argv[1], LIBNBD_CONTEXT_QEMU_DIRTY_BITMAP, + strlen (LIBNBD_CONTEXT_QEMU_DIRTY_BITMAP)) != 0) { + fprintf (stderr, "%s qemu:dirty-bitmap:name qemu-nbd [args ...]\n", + argv[0]); exit (EXIT_FAILURE); } bitmap = argv[1]; base-commit: 5c2fc3cc7e14146d000b65b191e70d9a0585a395 -- 2.41.0
Eric Blake
2023-Jul-26 17:50 UTC
[Libguestfs] [libnbd PATCH 4/7] Revert "generator/Go.ml: Simplify copy_uint32_array"
This reverts commit 6725fa0e129f9a60d7b89707ef8604e0aeeeaf43, although with a more modern style. Casting a C array to a Go slice just to benefit from potential optimizations in Go's copy(), is rather complex to understand, especially when we are still copying things (the main reason to treat a C array as a Go slice is when avoiding a copy has a benefit). Without a benchmark showing measurable difference in runtime speed, and considering that network transit time probably dominates the time spent on block status and its callback, it is not worth the complexity. Furthermore, an upcoming patch wants to add a similar helper function for converting between a list of C and Go structs, where the copy() trick will not work; and having the two helpers look alike is beneficial. Suggested-by: Laszlo Ersek <lersek at redhat.com> CC: Nir Soffer <nsoffer at redhat.com> Signed-off-by: Eric Blake <eblake at redhat.com> --- generator/GoLang.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/generator/GoLang.ml b/generator/GoLang.ml index 0aa83bdc..77dacadb 100644 --- a/generator/GoLang.ml +++ b/generator/GoLang.ml @@ -509,12 +509,14 @@ let /* Closures. */ -func copy_uint32_array (entries *C.uint32_t, count C.size_t) []uint32 { - ret := make([]uint32, int (count)) - // See https://github.com/golang/go/wiki/cgo#turning-c-arrays-into-go-slices - // TODO: Use unsafe.Slice() when we require Go 1.17. - s := (*[1<<30]uint32)(unsafe.Pointer(entries))[:count:count] - copy(ret, s) +func copy_uint32_array(entries *C.uint32_t, count C.size_t) []uint32 { + ret := make([]uint32, int(count)) + addr := uintptr(unsafe.Pointer(entries)) + for i := 0; i < int(count); i++ { + ptr := (*C.uint32_t)(unsafe.Pointer(addr)) + ret[i] = uint32(*ptr) + addr += unsafe.Sizeof(*ptr) + } return ret } "; base-commit: 5c2fc3cc7e14146d000b65b191e70d9a0585a395 prerequisite-patch-id: 4db98f7b211c0de9a4095b300970e1973cf0716c prerequisite-patch-id: 0bb320af5109c1c21e5b76d44e6ec1e7e685fd9f prerequisite-patch-id: 205525d8ea09e77ea13f43d0720153ed5904dbcd -- 2.41.0