[PATCH v5 7/7] chardev: introduce DEFINE_PROP_CHR_NO_CONNECT

Vladimir Sementsov-Ogievskiy posted 7 patches 2 weeks ago
Maintainers: "Marc-André Lureau" <marcandre.lureau@redhat.com>, Paolo Bonzini <pbonzini@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, Eduardo Habkost <eduardo@habkost.net>
There is a newer version of this series
[PATCH v5 7/7] chardev: introduce DEFINE_PROP_CHR_NO_CONNECT
Posted by Vladimir Sementsov-Ogievskiy 2 weeks ago
For further vhost-user-blk backend-transfer migration realization we
want to give it (vhost-user-blk) a possibility (and responsibility) to
decide when do connect.

For incoming migration we'll need to postpone connect at least until
early stage of migrate-incoming command, when we already know all
migration parameters and can decide, are we going to do incoming
backend-transfer (and get chardev fd from incoming stream), or we
finally need to connect.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
---
 chardev/char-fe.c                   | 32 ++++++++++++++++++++++++-----
 hw/core/qdev-properties-system.c    | 26 ++++++++++++++++++++---
 include/chardev/char-fe.h           |  8 ++++++--
 include/hw/qdev-properties-system.h |  3 +++
 4 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index c67b4d640f..1132ec0501 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -189,15 +189,26 @@ bool qemu_chr_fe_backend_open(CharFrontend *c)
     return c->chr && c->chr->be_open;
 }
 
-bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp)
+bool qemu_chr_fe_init_ex(CharFrontend *c, Chardev *s, bool connect,
+                         Error **errp)
 {
     unsigned int tag = 0;
 
-    if (!qemu_chr_connect(s, errp)) {
-        return false;
-    }
-
     if (s) {
+        if (connect) {
+            if (!qemu_chr_connect(s, errp)) {
+                return false;
+            }
+        } else {
+            /* DEFINE_PROP_CHR_NO_CONNECT */
+            if (!s->connect_postponed) {
+                error_setg(errp,
+                           "Chardev %s does not support postponed connect",
+                           s->label);
+                return false;
+            }
+        }
+
         if (CHARDEV_IS_MUX(s)) {
             MuxChardev *d = MUX_CHARDEV(s);
 
@@ -210,6 +221,12 @@ bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp)
         } else {
             s->fe = c;
         }
+    } else {
+        /*
+         * connect=false comes only from DEFINE_PROP_CHR_NO_CONNECT,
+         * through do_set_chr, which provides chardev ptr.
+         */
+        assert(connect);
     }
 
     c->fe_is_open = false;
@@ -218,6 +235,11 @@ bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp)
     return true;
 }
 
+bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp)
+{
+    return qemu_chr_fe_init_ex(c, s, true, errp);
+}
+
 void qemu_chr_fe_deinit(CharFrontend *c, bool del)
 {
     assert(c);
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index 13cc91680b..00f4b19238 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -266,8 +266,8 @@ static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
     g_free(p);
 }
 
-static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
-                    Error **errp)
+static void do_set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
+                       bool connect, Error **errp)
 {
     ERRP_GUARD();
     const Property *prop = opaque;
@@ -297,13 +297,25 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
     if (s == NULL) {
         error_setg(errp, "Property '%s.%s' can't find value '%s'",
                    object_get_typename(obj), name, str);
-    } else if (!qemu_chr_fe_init(fe, s, errp)) {
+    } else if (!qemu_chr_fe_init_ex(fe, s, connect, errp)) {
         error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
                       object_get_typename(obj), name, str);
     }
     g_free(str);
 }
 
+static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
+                    Error **errp)
+{
+    do_set_chr(obj, v, name, opaque, true, errp);
+}
+
+static void set_chr_no_connect(Object *obj, Visitor *v, const char *name,
+                               void *opaque, Error **errp)
+{
+    do_set_chr(obj, v, name, opaque, false, errp);
+}
+
 static void release_chr(Object *obj, const char *name, void *opaque)
 {
     const Property *prop = opaque;
@@ -320,6 +332,14 @@ const PropertyInfo qdev_prop_chr = {
     .release = release_chr,
 };
 
+const PropertyInfo qdev_prop_chr_no_connect = {
+    .type  = "str",
+    .description = "ID of a chardev to use as a backend",
+    .get   = get_chr,
+    .set   = set_chr_no_connect,
+    .release = release_chr,
+};
+
 /* --- mac address --- */
 
 /*
diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h
index 5f8a6df17d..f3df57afa1 100644
--- a/include/chardev/char-fe.h
+++ b/include/chardev/char-fe.h
@@ -25,14 +25,18 @@ struct CharFrontend {
 };
 
 /**
- * qemu_chr_fe_init:
+ * qemu_chr_fe_init(_ex):
  *
  * Initializes the frontend @c for the given Chardev backend @s. Call
  * qemu_chr_fe_deinit() to remove the association and release the backend.
+ * Call qemu_chr_connect(), except for the case when connect=false
+ * parameter set for _ex() version.
  *
  * Returns: false on error.
  */
-bool qemu_chr_fe_init(CharFrontend *c, Chardev *be, Error **errp);
+bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp);
+bool qemu_chr_fe_init_ex(CharFrontend *c, Chardev *s, bool connect,
+                         Error **errp);
 
 /**
  * qemu_chr_fe_deinit:
diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h
index 5c6cc5eae8..a0b14a2f77 100644
--- a/include/hw/qdev-properties-system.h
+++ b/include/hw/qdev-properties-system.h
@@ -7,6 +7,7 @@ bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm, const char *str,
                                        Error **errp);
 
 extern const PropertyInfo qdev_prop_chr;
+extern const PropertyInfo qdev_prop_chr_no_connect;
 extern const PropertyInfo qdev_prop_macaddr;
 extern const PropertyInfo qdev_prop_reserved_region;
 extern const PropertyInfo qdev_prop_multifd_compression;
@@ -39,6 +40,8 @@ extern const PropertyInfo qdev_prop_virtio_gpu_output_list;
 
 #define DEFINE_PROP_CHR(_n, _s, _f)             \
     DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharFrontend)
+#define DEFINE_PROP_CHR_NO_CONNECT(_n, _s, _f) \
+    DEFINE_PROP(_n, _s, _f, qdev_prop_chr_no_connect, CharFrontend)
 #define DEFINE_PROP_NETDEV(_n, _s, _f)             \
     DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NICPeers)
 #define DEFINE_PROP_DRIVE(_n, _s, _f) \
-- 
2.48.1
Re: [PATCH v5 7/7] chardev: introduce DEFINE_PROP_CHR_NO_CONNECT
Posted by Marc-André Lureau 1 week, 5 days ago
Hi

On Fri, Oct 31, 2025 at 7:59 PM Vladimir Sementsov-Ogievskiy <
vsementsov@yandex-team.ru> wrote:

> For further vhost-user-blk backend-transfer migration realization we
> want to give it (vhost-user-blk) a possibility (and responsibility) to
> decide when do connect.
>
> For incoming migration we'll need to postpone connect at least until
> early stage of migrate-incoming command, when we already know all
> migration parameters and can decide, are we going to do incoming
> backend-transfer (and get chardev fd from incoming stream), or we
> finally need to connect.
>
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
> ---
>  chardev/char-fe.c                   | 32 ++++++++++++++++++++++++-----
>  hw/core/qdev-properties-system.c    | 26 ++++++++++++++++++++---
>  include/chardev/char-fe.h           |  8 ++++++--
>  include/hw/qdev-properties-system.h |  3 +++
>  4 files changed, 59 insertions(+), 10 deletions(-)
>
> diff --git a/chardev/char-fe.c b/chardev/char-fe.c
> index c67b4d640f..1132ec0501 100644
> --- a/chardev/char-fe.c
> +++ b/chardev/char-fe.c
> @@ -189,15 +189,26 @@ bool qemu_chr_fe_backend_open(CharFrontend *c)
>      return c->chr && c->chr->be_open;
>  }
>
> -bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp)
> +bool qemu_chr_fe_init_ex(CharFrontend *c, Chardev *s, bool connect,
> +                         Error **errp)
>  {
>      unsigned int tag = 0;
>
> -    if (!qemu_chr_connect(s, errp)) {
> -        return false;
> -    }
> -
>      if (s) {
> +        if (connect) {
> +            if (!qemu_chr_connect(s, errp)) {
> +                return false;
> +            }
> +        } else {
> +            /* DEFINE_PROP_CHR_NO_CONNECT */
> +            if (!s->connect_postponed) {
> +                error_setg(errp,
> +                           "Chardev %s does not support postponed
> connect",
> +                           s->label);
> +                return false;
> +            }
> +        }
> +
>          if (CHARDEV_IS_MUX(s)) {
>              MuxChardev *d = MUX_CHARDEV(s);
>
> @@ -210,6 +221,12 @@ bool qemu_chr_fe_init(CharFrontend *c, Chardev *s,
> Error **errp)
>          } else {
>              s->fe = c;
>          }
> +    } else {
> +        /*
> +         * connect=false comes only from DEFINE_PROP_CHR_NO_CONNECT,
> +         * through do_set_chr, which provides chardev ptr.
> +         */
> +        assert(connect);
>

Is this useful to assert?


>      }
>
>      c->fe_is_open = false;
> @@ -218,6 +235,11 @@ bool qemu_chr_fe_init(CharFrontend *c, Chardev *s,
> Error **errp)
>      return true;
>  }
>
> +bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp)
> +{
> +    return qemu_chr_fe_init_ex(c, s, true, errp);
> +}
> +
>  void qemu_chr_fe_deinit(CharFrontend *c, bool del)
>  {
>      assert(c);
> diff --git a/hw/core/qdev-properties-system.c
> b/hw/core/qdev-properties-system.c
> index 13cc91680b..00f4b19238 100644
> --- a/hw/core/qdev-properties-system.c
> +++ b/hw/core/qdev-properties-system.c
> @@ -266,8 +266,8 @@ static void get_chr(Object *obj, Visitor *v, const
> char *name, void *opaque,
>      g_free(p);
>  }
>
> -static void set_chr(Object *obj, Visitor *v, const char *name, void
> *opaque,
> -                    Error **errp)
> +static void do_set_chr(Object *obj, Visitor *v, const char *name, void
> *opaque,
> +                       bool connect, Error **errp)
>  {
>      ERRP_GUARD();
>      const Property *prop = opaque;
> @@ -297,13 +297,25 @@ static void set_chr(Object *obj, Visitor *v, const
> char *name, void *opaque,
>      if (s == NULL) {
>          error_setg(errp, "Property '%s.%s' can't find value '%s'",
>                     object_get_typename(obj), name, str);
> -    } else if (!qemu_chr_fe_init(fe, s, errp)) {
> +    } else if (!qemu_chr_fe_init_ex(fe, s, connect, errp)) {
>          error_prepend(errp, "Property '%s.%s' can't take value '%s': ",
>                        object_get_typename(obj), name, str);
>      }
>      g_free(str);
>  }
>
> +static void set_chr(Object *obj, Visitor *v, const char *name, void
> *opaque,
> +                    Error **errp)
> +{
> +    do_set_chr(obj, v, name, opaque, true, errp);
> +}
> +
> +static void set_chr_no_connect(Object *obj, Visitor *v, const char *name,
> +                               void *opaque, Error **errp)
> +{
> +    do_set_chr(obj, v, name, opaque, false, errp);
> +}
> +
>  static void release_chr(Object *obj, const char *name, void *opaque)
>  {
>      const Property *prop = opaque;
> @@ -320,6 +332,14 @@ const PropertyInfo qdev_prop_chr = {
>      .release = release_chr,
>  };
>
> +const PropertyInfo qdev_prop_chr_no_connect = {
> +    .type  = "str",
> +    .description = "ID of a chardev to use as a backend",
> +    .get   = get_chr,
> +    .set   = set_chr_no_connect,
> +    .release = release_chr,
> +};
> +
>  /* --- mac address --- */
>
>  /*
> diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h
> index 5f8a6df17d..f3df57afa1 100644
> --- a/include/chardev/char-fe.h
> +++ b/include/chardev/char-fe.h
> @@ -25,14 +25,18 @@ struct CharFrontend {
>  };
>
>  /**
> - * qemu_chr_fe_init:
> + * qemu_chr_fe_init(_ex):
>   *
>   * Initializes the frontend @c for the given Chardev backend @s. Call
>   * qemu_chr_fe_deinit() to remove the association and release the backend.
> + * Call qemu_chr_connect(), except for the case when connect=false
> + * parameter set for _ex() version.
>   *
>   * Returns: false on error.
>   */
> -bool qemu_chr_fe_init(CharFrontend *c, Chardev *be, Error **errp);
> +bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp);
> +bool qemu_chr_fe_init_ex(CharFrontend *c, Chardev *s, bool connect,
> +                         Error **errp);
>
>  /**
>   * qemu_chr_fe_deinit:
> diff --git a/include/hw/qdev-properties-system.h
> b/include/hw/qdev-properties-system.h
> index 5c6cc5eae8..a0b14a2f77 100644
> --- a/include/hw/qdev-properties-system.h
> +++ b/include/hw/qdev-properties-system.h
> @@ -7,6 +7,7 @@ bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm,
> const char *str,
>                                         Error **errp);
>
>  extern const PropertyInfo qdev_prop_chr;
> +extern const PropertyInfo qdev_prop_chr_no_connect;
>  extern const PropertyInfo qdev_prop_macaddr;
>  extern const PropertyInfo qdev_prop_reserved_region;
>  extern const PropertyInfo qdev_prop_multifd_compression;
> @@ -39,6 +40,8 @@ extern const PropertyInfo
> qdev_prop_virtio_gpu_output_list;
>
>  #define DEFINE_PROP_CHR(_n, _s, _f)             \
>      DEFINE_PROP(_n, _s, _f, qdev_prop_chr, CharFrontend)
> +#define DEFINE_PROP_CHR_NO_CONNECT(_n, _s, _f) \
> +    DEFINE_PROP(_n, _s, _f, qdev_prop_chr_no_connect, CharFrontend)
>  #define DEFINE_PROP_NETDEV(_n, _s, _f)             \
>      DEFINE_PROP(_n, _s, _f, qdev_prop_netdev, NICPeers)
>  #define DEFINE_PROP_DRIVE(_n, _s, _f) \
> --
> 2.48.1
>
>
ok otherwise,
 Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Re: [PATCH v5 7/7] chardev: introduce DEFINE_PROP_CHR_NO_CONNECT
Posted by Vladimir Sementsov-Ogievskiy 1 week, 3 days ago
On 02.11.25 14:39, Marc-André Lureau wrote:
> Hi
> 
> On Fri, Oct 31, 2025 at 7:59 PM Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru <mailto:vsementsov@yandex-team.ru>> wrote:
> 
>     For further vhost-user-blk backend-transfer migration realization we
>     want to give it (vhost-user-blk) a possibility (and responsibility) to
>     decide when do connect.
> 
>     For incoming migration we'll need to postpone connect at least until
>     early stage of migrate-incoming command, when we already know all
>     migration parameters and can decide, are we going to do incoming
>     backend-transfer (and get chardev fd from incoming stream), or we
>     finally need to connect.
> 
>     Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru <mailto:vsementsov@yandex-team.ru>>
>     ---
>       chardev/char-fe.c                   | 32 ++++++++++++++++++++++++-----
>       hw/core/qdev-properties-system.c    | 26 ++++++++++++++++++++---
>       include/chardev/char-fe.h           |  8 ++++++--
>       include/hw/qdev-properties-system.h |  3 +++
>       4 files changed, 59 insertions(+), 10 deletions(-)
> 
>     diff --git a/chardev/char-fe.c b/chardev/char-fe.c
>     index c67b4d640f..1132ec0501 100644
>     --- a/chardev/char-fe.c
>     +++ b/chardev/char-fe.c
>     @@ -189,15 +189,26 @@ bool qemu_chr_fe_backend_open(CharFrontend *c)
>           return c->chr && c->chr->be_open;
>       }
> 
>     -bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp)
>     +bool qemu_chr_fe_init_ex(CharFrontend *c, Chardev *s, bool connect,
>     +                         Error **errp)
>       {
>           unsigned int tag = 0;
> 
>     -    if (!qemu_chr_connect(s, errp)) {
>     -        return false;
>     -    }
>     -
>           if (s) {
>     +        if (connect) {
>     +            if (!qemu_chr_connect(s, errp)) {
>     +                return false;
>     +            }
>     +        } else {
>     +            /* DEFINE_PROP_CHR_NO_CONNECT */
>     +            if (!s->connect_postponed) {
>     +                error_setg(errp,
>     +                           "Chardev %s does not support postponed connect",
>     +                           s->label);
>     +                return false;
>     +            }
>     +        }
>     +
>               if (CHARDEV_IS_MUX(s)) {
>                   MuxChardev *d = MUX_CHARDEV(s);
> 
>     @@ -210,6 +221,12 @@ bool qemu_chr_fe_init(CharFrontend *c, Chardev *s, Error **errp)
>               } else {
>                   s->fe = c;
>               }
>     +    } else {
>     +        /*
>     +         * connect=false comes only from DEFINE_PROP_CHR_NO_CONNECT,
>     +         * through do_set_chr, which provides chardev ptr.
>     +         */
>     +        assert(connect);
> 
> 
> Is this useful to assert?


Hmm. I started to write "yes, because, if in future we introduce code path, which will
somehow pass s=NULL and connect=false, we'll do a wrong thing.."

But looking more closely at semantics of s=0, looks like connect could be just ignored:
no backend, nothing to connect.. So connect=true with s=0 looks maybe more wrong
than connect=false.

I'll drop the assertion, and add not into qemu_chr_fe_init_ex(), that @connect is ignored
for s=NULL case.

-- 
Best regards,
Vladimir