diff mbox series

[2/2] nbd/server: Allow users to adjust handshake limit in QMP

Message ID 20250203222722.650694-6-eblake@redhat.com (mailing list archive)
State New
Headers show
Series nbd: Allow debugging tuning of handshake limit | expand

Commit Message

Eric Blake Feb. 3, 2025, 10:26 p.m. UTC
Although defaulting the handshake limit to 10 seconds was a nice QoI
change to weed out intentionally slow clients, it can interfere with
integration testing done with manual NBD_OPT commands over 'nbdsh
--opt-mode'.  Expose a QMP knob 'handshake-max-secs' to allow the user
to alter the timeout away from the default.

The parameter name here intentionally matches the spelling of the
constant added in commit fb1c2aaa98, and not the command-line spelling
added in the previous patch for qemu-nbd; that's because in QMP,
longer names serve as good self-documentation, and unlike the command
line, machines don't have problems generating longer spellings.

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 qapi/block-export.json         | 10 ++++++++++
 include/block/nbd.h            |  6 +++---
 block/monitor/block-hmp-cmds.c |  4 ++--
 blockdev-nbd.c                 | 26 ++++++++++++++++++--------
 4 files changed, 33 insertions(+), 13 deletions(-)

Comments

Markus Armbruster Feb. 5, 2025, 6:55 a.m. UTC | #1
Eric Blake <eblake@redhat.com> writes:

> Although defaulting the handshake limit to 10 seconds was a nice QoI
> change to weed out intentionally slow clients, it can interfere with
> integration testing done with manual NBD_OPT commands over 'nbdsh
> --opt-mode'.  Expose a QMP knob 'handshake-max-secs' to allow the user
> to alter the timeout away from the default.
>
> The parameter name here intentionally matches the spelling of the
> constant added in commit fb1c2aaa98, and not the command-line spelling
> added in the previous patch for qemu-nbd; that's because in QMP,
> longer names serve as good self-documentation, and unlike the command
> line, machines don't have problems generating longer spellings.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
> ---
>  qapi/block-export.json         | 10 ++++++++++
>  include/block/nbd.h            |  6 +++---
>  block/monitor/block-hmp-cmds.c |  4 ++--
>  blockdev-nbd.c                 | 26 ++++++++++++++++++--------
>  4 files changed, 33 insertions(+), 13 deletions(-)
>
> diff --git a/qapi/block-export.json b/qapi/block-export.json
> index ce33fe378df..58ae6a5e1d7 100644
> --- a/qapi/block-export.json
> +++ b/qapi/block-export.json
> @@ -17,6 +17,10 @@
>  #
>  # @addr: Address on which to listen.
>  #
> +# @handshake-max-secs: Time limit, in seconds, at which a client that
> +#     has not completed the negotiation handshake will be disconnected,
> +#     or 0 for no limit (since 10.0; default: 10).
> +#
>  # @tls-creds: ID of the TLS credentials object (since 2.6).
>  #
>  # @tls-authz: ID of the QAuthZ authorization object used to validate
> @@ -34,6 +38,7 @@
>  ##
>  { 'struct': 'NbdServerOptions',
>    'data': { 'addr': 'SocketAddress',
> +            '*handshake-max-secs': 'uint32',
>              '*tls-creds': 'str',
>              '*tls-authz': 'str',
>              '*max-connections': 'uint32' } }

Standard question on time: are we confident the granularity will
suffice?

On naming...  We use "seconds" (StatsUnit in qapi/stats.json), and "sec"
(SnapshotInfo in qapi/block-core.json), but not "secs".  Do we care?

> @@ -52,6 +57,10 @@
>  #
>  # @addr: Address on which to listen.
>  #
> +# @handshake-max-secs: Time limit, in seconds, at which a client that
> +#     has not completed the negotiation handshake will be disconnected,
> +#     or 0 for no limit (since 10.0; default: 10).
> +#
>  # @tls-creds: ID of the TLS credentials object (since 2.6).
>  #
>  # @tls-authz: ID of the QAuthZ authorization object used to validate
> @@ -72,6 +81,7 @@
>  ##
>  { 'command': 'nbd-server-start',
>    'data': { 'addr': 'SocketAddressLegacy',
> +            '*handshake-max-secs': 'uint32',
>              '*tls-creds': 'str',
>              '*tls-authz': 'str',
>              '*max-connections': 'uint32' },

[...]
Eric Blake Feb. 5, 2025, 8:36 p.m. UTC | #2
On Wed, Feb 05, 2025 at 07:55:56AM +0100, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
> > Although defaulting the handshake limit to 10 seconds was a nice QoI
> > change to weed out intentionally slow clients, it can interfere with
> > integration testing done with manual NBD_OPT commands over 'nbdsh
> > --opt-mode'.  Expose a QMP knob 'handshake-max-secs' to allow the user
> > to alter the timeout away from the default.
> >
> > The parameter name here intentionally matches the spelling of the
> > constant added in commit fb1c2aaa98, and not the command-line spelling
> > added in the previous patch for qemu-nbd; that's because in QMP,
> > longer names serve as good self-documentation, and unlike the command
> > line, machines don't have problems generating longer spellings.
> >

> >  { 'struct': 'NbdServerOptions',
> >    'data': { 'addr': 'SocketAddress',
> > +            '*handshake-max-secs': 'uint32',
> >              '*tls-creds': 'str',
> >              '*tls-authz': 'str',
> >              '*max-connections': 'uint32' } }
> 
> Standard question on time: are we confident the granularity will
> suffice?

The default if this is unspecified is 10 seconds.  Anything subsecond
requires a fast negotiation between client and server; the more likely
use of this parameter is setting it extremely large (or to 0 to
disable) in order to allow lengthy gdb sessions without the timeout
cutting things short.  So I think seconds is okay.

> 
> On naming...  We use "seconds" (StatsUnit in qapi/stats.json), and "sec"
> (SnapshotInfo in qapi/block-core.json), but not "secs".  Do we care?

Avoiding abbreviations and using 'seconds' is fine by me, especially
since this won't be typed by many users but remains more of a tool for
use in a debugging arsenel.  I can respin with the longer name, but
will wait for any other review comments first.
Markus Armbruster Feb. 6, 2025, 5:54 a.m. UTC | #3
Eric Blake <eblake@redhat.com> writes:

> On Wed, Feb 05, 2025 at 07:55:56AM +0100, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>> 
>> > Although defaulting the handshake limit to 10 seconds was a nice QoI
>> > change to weed out intentionally slow clients, it can interfere with
>> > integration testing done with manual NBD_OPT commands over 'nbdsh
>> > --opt-mode'.  Expose a QMP knob 'handshake-max-secs' to allow the user
>> > to alter the timeout away from the default.
>> >
>> > The parameter name here intentionally matches the spelling of the
>> > constant added in commit fb1c2aaa98, and not the command-line spelling
>> > added in the previous patch for qemu-nbd; that's because in QMP,
>> > longer names serve as good self-documentation, and unlike the command
>> > line, machines don't have problems generating longer spellings.
>> >
>
>> >  { 'struct': 'NbdServerOptions',
>> >    'data': { 'addr': 'SocketAddress',
>> > +            '*handshake-max-secs': 'uint32',
>> >              '*tls-creds': 'str',
>> >              '*tls-authz': 'str',
>> >              '*max-connections': 'uint32' } }
>> 
>> Standard question on time: are we confident the granularity will
>> suffice?
>
> The default if this is unspecified is 10 seconds.  Anything subsecond
> requires a fast negotiation between client and server; the more likely
> use of this parameter is setting it extremely large (or to 0 to
> disable) in order to allow lengthy gdb sessions without the timeout
> cutting things short.  So I think seconds is okay.

ACK

>> On naming...  We use "seconds" (StatsUnit in qapi/stats.json), and "sec"
>> (SnapshotInfo in qapi/block-core.json), but not "secs".  Do we care?
>
> Avoiding abbreviations and using 'seconds' is fine by me, especially
> since this won't be typed by many users but remains more of a tool for
> use in a debugging arsenel.  I can respin with the longer name, but
> will wait for any other review comments first.

I like "seconds".

Thanks!
Vladimir Sementsov-Ogievskiy Feb. 6, 2025, 7:20 a.m. UTC | #4
On 04.02.25 01:26, Eric Blake wrote:
> Although defaulting the handshake limit to 10 seconds was a nice QoI
> change to weed out intentionally slow clients, it can interfere with
> integration testing done with manual NBD_OPT commands over 'nbdsh
> --opt-mode'.  Expose a QMP knob 'handshake-max-secs' to allow the user
> to alter the timeout away from the default.
> 
> The parameter name here intentionally matches the spelling of the
> constant added in commit fb1c2aaa98, and not the command-line spelling
> added in the previous patch for qemu-nbd; that's because in QMP,
> longer names serve as good self-documentation, and unlike the command
> line, machines don't have problems generating longer spellings.
> 
> Signed-off-by: Eric Blake <eblake@redhat.com>

Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>

(renaming to -seconds is, of course, OK)

> ---
>   qapi/block-export.json         | 10 ++++++++++
>   include/block/nbd.h            |  6 +++---

[..]

> @@ -52,6 +57,10 @@
>   #
>   # @addr: Address on which to listen.
>   #
> +# @handshake-max-secs: Time limit, in seconds, at which a client that
> +#     has not completed the negotiation handshake will be disconnected,
> +#     or 0 for no limit (since 10.0; default: 10).
> +#

Hmm. [not about the series], shouldn't we finally deprecate older interface?

>   # @tls-creds: ID of the TLS credentials object (since 2.6).
>   #
>   # @tls-authz: ID of the QAuthZ authorization object used to validate
> @@ -72,6 +81,7 @@
>   ##
>   { 'command': 'nbd-server-start',
>     'data': { 'addr': 'SocketAddressLegacy',

[..]
Eric Blake Feb. 10, 2025, 9:46 p.m. UTC | #5
On Thu, Feb 06, 2025 at 10:20:09AM +0300, Vladimir Sementsov-Ogievskiy wrote:
> > ---
> >   qapi/block-export.json         | 10 ++++++++++
> >   include/block/nbd.h            |  6 +++---
> 
> [..]
> 
> > @@ -52,6 +57,10 @@
> >   #
> >   # @addr: Address on which to listen.
> >   #
> > +# @handshake-max-secs: Time limit, in seconds, at which a client that
> > +#     has not completed the negotiation handshake will be disconnected,
> > +#     or 0 for no limit (since 10.0; default: 10).
> > +#
> 
> Hmm. [not about the series], shouldn't we finally deprecate older interface?

By older interface, you are asking about the QMP command
'nbd-server-start' as compared to struct NbdServerOptions.  But the
struct is not directly present in any QMP commands; rather, it only
appears to be used by qemu-storage-daemon as one of its command line
options that needs to set up an NBD server with a JSON-like syntax
that has less nesting than QMP nbd-server-start.  blockdev-nbd.c has
two functions [nbd_server_start_options(NbdServerOPtions *arg...)  and
qmp_nbd_server_start(args...)] that both unpack their slightly
different forms and pass them as parameters to nbd_server_start() that
is then agnostic to whether the older QMP command or newer q-s-d CLI
option was specified.

It looks like libvirt is still using QMP nbd-server-start.  If we were
to start the deprecation process for qemu 10.0, what would the new
command look like?  What would everyone be required to use by qemu
10.2?
Vladimir Sementsov-Ogievskiy Feb. 12, 2025, 2:33 p.m. UTC | #6
On 11.02.25 00:46, Eric Blake wrote:
> On Thu, Feb 06, 2025 at 10:20:09AM +0300, Vladimir Sementsov-Ogievskiy wrote:
>>> ---
>>>    qapi/block-export.json         | 10 ++++++++++
>>>    include/block/nbd.h            |  6 +++---
>>
>> [..]
>>
>>> @@ -52,6 +57,10 @@
>>>    #
>>>    # @addr: Address on which to listen.
>>>    #
>>> +# @handshake-max-secs: Time limit, in seconds, at which a client that
>>> +#     has not completed the negotiation handshake will be disconnected,
>>> +#     or 0 for no limit (since 10.0; default: 10).
>>> +#
>>
>> Hmm. [not about the series], shouldn't we finally deprecate older interface?
> 
> By older interface, you are asking about the QMP command
> 'nbd-server-start' as compared to struct NbdServerOptions.  But the
> struct is not directly present in any QMP commands; rather, it only
> appears to be used by qemu-storage-daemon as one of its command line
> options that needs to set up an NBD server with a JSON-like syntax
> that has less nesting than QMP nbd-server-start.  blockdev-nbd.c has
> two functions [nbd_server_start_options(NbdServerOPtions *arg...)  and
> qmp_nbd_server_start(args...)] that both unpack their slightly
> different forms and pass them as parameters to nbd_server_start() that
> is then agnostic to whether the older QMP command or newer q-s-d CLI
> option was specified.
> 
> It looks like libvirt is still using QMP nbd-server-start.  If we were
> to start the deprecation process for qemu 10.0, what would the new
> command look like?  What would everyone be required to use by qemu
> 10.2?
> 

Oh you are right, I was inattentive.

So, probably thing to be deprecated is actually SocketAddressLegacy, which is the only difference.

And why just not move common part of the structures to common base? I'll send a patch with a try.
diff mbox series

Patch

diff --git a/qapi/block-export.json b/qapi/block-export.json
index ce33fe378df..58ae6a5e1d7 100644
--- a/qapi/block-export.json
+++ b/qapi/block-export.json
@@ -17,6 +17,10 @@ 
 #
 # @addr: Address on which to listen.
 #
+# @handshake-max-secs: Time limit, in seconds, at which a client that
+#     has not completed the negotiation handshake will be disconnected,
+#     or 0 for no limit (since 10.0; default: 10).
+#
 # @tls-creds: ID of the TLS credentials object (since 2.6).
 #
 # @tls-authz: ID of the QAuthZ authorization object used to validate
@@ -34,6 +38,7 @@ 
 ##
 { 'struct': 'NbdServerOptions',
   'data': { 'addr': 'SocketAddress',
+            '*handshake-max-secs': 'uint32',
             '*tls-creds': 'str',
             '*tls-authz': 'str',
             '*max-connections': 'uint32' } }
@@ -52,6 +57,10 @@ 
 #
 # @addr: Address on which to listen.
 #
+# @handshake-max-secs: Time limit, in seconds, at which a client that
+#     has not completed the negotiation handshake will be disconnected,
+#     or 0 for no limit (since 10.0; default: 10).
+#
 # @tls-creds: ID of the TLS credentials object (since 2.6).
 #
 # @tls-authz: ID of the QAuthZ authorization object used to validate
@@ -72,6 +81,7 @@ 
 ##
 { 'command': 'nbd-server-start',
   'data': { 'addr': 'SocketAddressLegacy',
+            '*handshake-max-secs': 'uint32',
             '*tls-creds': 'str',
             '*tls-authz': 'str',
             '*max-connections': 'uint32' },
diff --git a/include/block/nbd.h b/include/block/nbd.h
index d4f8b21aecc..92987c76fd6 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -428,9 +428,9 @@  void nbd_client_put(NBDClient *client);
 void nbd_server_is_qemu_nbd(int max_connections);
 bool nbd_server_is_running(void);
 int nbd_server_max_connections(void);
-void nbd_server_start(SocketAddress *addr, const char *tls_creds,
-                      const char *tls_authz, uint32_t max_connections,
-                      Error **errp);
+void nbd_server_start(SocketAddress *addr, uint32_t handshake_max_secs,
+                      const char *tls_creds, const char *tls_authz,
+                      uint32_t max_connections, Error **errp);
 void nbd_server_start_options(NbdServerOptions *arg, Error **errp);

 /* nbd_read
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index 1d312513fc4..0cfcbfe7c21 100644
--- a/block/monitor/block-hmp-cmds.c
+++ b/block/monitor/block-hmp-cmds.c
@@ -402,8 +402,8 @@  void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
         goto exit;
     }

-    nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS,
-                     &local_err);
+    nbd_server_start(addr, NBD_DEFAULT_HANDSHAKE_MAX_SECS, NULL, NULL,
+                     NBD_DEFAULT_MAX_CONNECTIONS, &local_err);
     qapi_free_SocketAddress(addr);
     if (local_err != NULL) {
         goto exit;
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 9e61fbaf2b2..e9f53e83d48 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -28,6 +28,7 @@  typedef struct NBDConn {

 typedef struct NBDServerData {
     QIONetListener *listener;
+    uint32_t handshake_max_secs;
     QCryptoTLSCreds *tlscreds;
     char *tlsauthz;
     uint32_t max_connections;
@@ -84,8 +85,7 @@  static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
     nbd_update_server_watch(nbd_server);

     qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
-    /* TODO - expose handshake timeout as QMP option */
-    nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
+    nbd_client_new(cioc, nbd_server->handshake_max_secs,
                    nbd_server->tlscreds, nbd_server->tlsauthz,
                    nbd_blockdev_client_closed, conn);
 }
@@ -162,9 +162,9 @@  static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp)
 }


-void nbd_server_start(SocketAddress *addr, const char *tls_creds,
-                      const char *tls_authz, uint32_t max_connections,
-                      Error **errp)
+void nbd_server_start(SocketAddress *addr, uint32_t handshake_max_secs,
+                      const char *tls_creds, const char *tls_authz,
+                      uint32_t max_connections, Error **errp)
 {
     if (nbd_server) {
         error_setg(errp, "NBD server already running");
@@ -173,6 +173,7 @@  void nbd_server_start(SocketAddress *addr, const char *tls_creds,

     nbd_server = g_new0(NBDServerData, 1);
     nbd_server->max_connections = max_connections;
+    nbd_server->handshake_max_secs = handshake_max_secs;
     nbd_server->listener = qio_net_listener_new();

     qio_net_listener_set_name(nbd_server->listener,
@@ -210,12 +211,17 @@  void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
     if (!arg->has_max_connections) {
         arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
     }
+    if (!arg->has_handshake_max_secs) {
+        arg->handshake_max_secs = NBD_DEFAULT_HANDSHAKE_MAX_SECS;
+    }

-    nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz,
-                     arg->max_connections, errp);
+    nbd_server_start(arg->addr, arg->handshake_max_secs, arg->tls_creds,
+                     arg->tls_authz, arg->max_connections, errp);
 }

 void qmp_nbd_server_start(SocketAddressLegacy *addr,
+                          bool has_handshake_max_secs,
+                          uint32_t handshake_max_secs,
                           const char *tls_creds,
                           const char *tls_authz,
                           bool has_max_connections, uint32_t max_connections,
@@ -226,8 +232,12 @@  void qmp_nbd_server_start(SocketAddressLegacy *addr,
     if (!has_max_connections) {
         max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
     }
+    if (!has_handshake_max_secs) {
+        handshake_max_secs = NBD_DEFAULT_HANDSHAKE_MAX_SECS;
+    }

-    nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
+    nbd_server_start(addr_flat, handshake_max_secs, tls_creds, tls_authz,
+                     max_connections, errp);
     qapi_free_SocketAddress(addr_flat);
 }