On Thu, Sep 01, 2022 at 11:21:38AM +0200, Laszlo Ersek wrote:
On 08/31/22 16:39, Eric Blake wrote:
> Upstream NBD clarified (see NBD commit 13a4e33a8) that since
> NBD_OPT_LIST_META_CONTEXT is stateless on the server side, it is
> acceptable (but not mandatory) for servers to accept it without the
> client having pre-negotiated structured replies. We aren't quite
> stateless on the client side yet - that will be fixed in a later patch
> to keep this one small and easier to test. The testsuite changes pass
> when using modern nbdkit; however, it skips[*] if we talk to an older
> server. But since the test also skips with our pre-patch behavior,
> it's not worth separating it into a separate patch.
>
> For more history, qemu-nbd prior to commit da24597d [v6.2] and nbdkit
> prior to commit c39cba73 [v1.27.3, also backported to 1.26.6] are
> servers that chose to reject NBD_OPT_LIST_META_CONTEXT without a prior
> NBD_OPT_STRUCTURED_REPLY.
>
> [*]It's easier to skip on server failure than to try and write an
> nbdkit patch to add yet another --config feature probe just to depend
> on new-enough nbdkit to gracefully probe in advance if the server
> should succeed.
> ---
> generator/states-newstyle-opt-meta-context.c | 1 -
> lib/opt.c | 6 +----
> tests/opt-list-meta.c | 28 +++++++++++++++++---
> 3 files changed, 25 insertions(+), 10 deletions(-)
>
> diff --git a/generator/states-newstyle-opt-meta-context.c
b/generator/states-newstyle-opt-meta-context.c
> index a6a5271..5c65454 100644
> --- a/generator/states-newstyle-opt-meta-context.c
> +++ b/generator/states-newstyle-opt-meta-context.c
> @@ -34,7 +34,6 @@ STATE_MACHINE {
> meta_vector_reset (&h->meta_contexts);
> if (h->opt_current == NBD_OPT_LIST_META_CONTEXT) {
> assert (h->opt_mode);
> - assert (h->structured_replies);
> assert (CALLBACK_IS_NOT_NULL (h->opt_cb.fn.context));
> opt = h->opt_current;
> }
Can we introduce some xfuncname pattern for these "state machine" C
files? The STATE_MACHINE hunk header is totally useless. I'd like
"NEWSTYLE.OPT_META_CONTEXT.START". :)
Yes, that's a side ask, but I would love it too. Git has a default
xfuncname pattern for .c files, so it will be interesting to see if I
can figure out .gitattributes that would let us attach additional
patterns on to the generator/*.c files without penalizing normal .c
files.
Regarding the code -- with the removal of the assertion from the LIST
branch, but preserving a similar check (albeit not an assert()) on the
SET branch, the comment covering *both* branches is now out of date:
/* If the server doesn't support SRs then we must skip this group.
Indeed it is. I already posted a followup patch to 11/12 where I
improved the comment to its final state; I will grab the appropriate
parts of that comment and revise it through the series to match
changes as they come in.
For 2/12, that results in:
diff --git c/generator/states-newstyle-opt-meta-context.c
w/generator/states-newstyle-opt-meta-context.c
index a6a5271a..dd5a6ded 100644
--- c/generator/states-newstyle-opt-meta-context.c
+++ w/generator/states-newstyle-opt-meta-context.c
@@ -23,9 +23,27 @@ STATE_MACHINE {
size_t i;
uint32_t len, opt;
- /* If the server doesn't support SRs then we must skip this group.
- * Also we skip the group if the client didn't request any metadata
- * contexts, when doing SET (but an empty LIST is okay).
+ /* This state group is reached from:
+ * h->opt_mode == false (h->current_opt == 0):
+ * nbd_connect_*()
+ * -> conditionally use SET, next state OPT_GO for NBD_OPT_GO
+ * h->opt_mode == true (h->current_opt matches calling API):
+ * nbd_opt_info()
+ * -> conditionally use SET, next state OPT_GO for NBD_OPT_INFO
+ * nbd_opt_go()
+ * -> conditionally use SET, next state OPT_GO for NBD_OPT_GO
+ * nbd_opt_list_meta_context()
+ * -> conditionally use LIST, next state NEGOTIATING
+ *
+ * For now, we start by unconditionally clearing h->exportsize and friends,
+ * as well as h->meta_contexts and h->meta_valid.
+ * SET then manipulates h->meta_contexts, and sets h->meta_valid on success.
+ * If SET is conditional, we skip it if structured replies were
+ * not negotiated, or if there were no contexts to request.
+ * If OPT_GO is later successful, it populates h->exportsize and friends,
+ * and also sets h->meta_valid if we skipped SET here.
+ * LIST is conditional, skipped if structured replies were not negotiated.
+ * There is a callback if and only if the command is LIST.
*/
assert (h->gflags & LIBNBD_HANDSHAKE_FLAG_FIXED_NEWSTYLE);
nbd_internal_reset_size_and_flags (h);
For this patch, it changes as:
diff --git c/generator/states-newstyle-opt-meta-context.c
w/generator/states-newstyle-opt-meta-context.c
index c45ff129..020a7adf 100644
--- c/generator/states-newstyle-opt-meta-context.c
+++ w/generator/states-newstyle-opt-meta-context.c
@@ -33,7 +33,7 @@ STATE_MACHINE {
* nbd_opt_go()
* -> conditionally use SET, next state OPT_GO for NBD_OPT_GO
* nbd_opt_list_meta_context()
- * -> conditionally use LIST, next state NEGOTIATING
+ * -> unconditionally use LIST, next state NEGOTIATING
*
* For now, we start by unconditionally clearing h->exportsize and friends,
* as well as h->meta_contexts and h->meta_valid.
@@ -42,8 +42,7 @@ STATE_MACHINE {
* not negotiated, or if there were no contexts to request.
* If OPT_GO is later successful, it populates h->exportsize and friends,
* and also sets h->meta_valid if we skipped SET here.
- * LIST is conditional, skipped if structured replies were not negotiated.
- * There is a callback if and only if the command is LIST.
+ * There is a callback if and only if the command is unconditional.
*/
assert (h->gflags & LIBNBD_HANDSHAKE_FLAG_FIXED_NEWSTYLE);
nbd_internal_reset_size_and_flags (h);
In 4/12, it is further modified:
diff --git c/generator/states-newstyle-opt-meta-context.c
w/generator/states-newstyle-opt-meta-context.c
index 37022594..28100199 100644
--- c/generator/states-newstyle-opt-meta-context.c
+++ w/generator/states-newstyle-opt-meta-context.c
@@ -35,13 +35,12 @@ STATE_MACHINE {
* nbd_opt_list_meta_context()
* -> unconditionally use LIST, next state NEGOTIATING
*
- * For now, we start by unconditionally clearing h->exportsize and friends,
- * as well as h->meta_contexts and h->meta_valid.
- * SET then manipulates h->meta_contexts, and sets h->meta_valid on success.
- * If SET is conditional, we skip it if structured replies were
- * not negotiated, or if there were no contexts to request.
+ * For now, we start by unconditionally clearing h->exportsize and friends.
+ * If SET is conditional, we skip it if h->request_meta is false, if
+ * structured replies were not negotiated, or if no contexts to request.
* If OPT_GO is later successful, it populates h->exportsize and friends,
- * and also sets h->meta_valid if we skipped SET here.
+ * and also sets h->meta_valid if h->request_meta but we skipped SET here.
+ * SET then manipulates h->meta_contexts, and sets h->meta_valid on success.
* There is a callback if and only if the command is unconditional.
*/
assert (h->gflags & LIBNBD_HANDSHAKE_FLAG_FIXED_NEWSTYLE);
And having typed that up, I'm now starting to think I should tweak
patch 7/12 to move the call to nbd_internal_reset_size_and_flags() out
of NEWSTYLE.OPT_META_CONTEXT.START and into NEWSTYLE.OPT_GO.START, to
further solidify that it is the act of NBD_OPT_INFO/GO that wipes
h->exportsize, not the act of NBD_OPT_SET/LIST_META_CONTEXT.
> +++ b/tests/opt-list-meta.c
...
>
> + nbd_opt_abort (nbd);
> + nbd_close (nbd);
> +
> exit (EXIT_SUCCESS);
> }
>
On a tangent: why do we have nbd_opt_abort() separately from
nbd_close()? What further steps would be valid between the two?
nbd_opt_abort() is optional; it says to inform the server of our
intent to do a clean disconnect during negotiation. If you skip it and
just do nbd_close(), the server is more likely to see an unexpected
EOF and warn that we went away unexpectedly. It is a direct
counterpart to nbd_shutdown(0) informing the server that we want a
clean disconnect during transmission.
As for what we can do between the two - nbd_opt_abort() generally
moves the handle to state CLOSED, but there you can still do things
like nbd_get_size() to see what had happened while the handle was
alive; while nbd_close() wipes the handle altogether.
Reviewed-by: Laszlo Ersek <lersek(a)redhat.com>
Should I apply your R-b with my incremental doc tweaks incorporated
along the way, or are you going to want to see a v3 respin with
everything in one place (especially now that I'm thinking of further
tweaks in 7/12)?
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization:
qemu.org |
libvirt.org