Hello Daniel,
Thank you for your review.

On Thu, Feb 5, 2026 at 9:28 PM Daniel P. Berrangé <[email protected]> wrote:
>
> On Thu, Feb 05, 2026 at 08:58:03PM +0100, Ruslan Ruslichenko wrote:
> > From: Ruslan Ruslichenko <[email protected]>
> >
> > Add initialization of communication channels with
> > remote peer.
> >
> > This includes character device backend, which can be
> > configured based on QOM properties or automatic socket
> > creation based on the machine path. The patch also
> > initializes the signaling event pipes.
> >
> > Signed-off-by: Edgar E. Iglesias <[email protected]>
> > Signed-off-by: Takahiro Nakata <[email protected]>
> > Signed-off-by: Ruslan Ruslichenko <[email protected]>
> > ---
> >  hw/core/remote-port.c         | 187 ++++++++++++++++++++++++++++++++++
> >  include/hw/core/remote-port.h |  16 +++
> >  2 files changed, 203 insertions(+)
> >
> > diff --git a/hw/core/remote-port.c b/hw/core/remote-port.c
> > index c909a825f3..5154c1bc2a 100644
> > --- a/hw/core/remote-port.c
> > +++ b/hw/core/remote-port.c
> > @@ -52,6 +52,58 @@
> >  #define REMOTE_PORT_CLASS(klass)    \
> >       OBJECT_CLASS_CHECK(RemotePortClass, (klass), TYPE_REMOTE_PORT)
> >
> > +static char *rp_sanitize_prefix(RemotePort *s)
> > +{
> > +    char *sanitized_name;
> > +    char *c;
> > +
> > +    sanitized_name = g_strdup(s->prefix);
> > +    for (c = sanitized_name; *c != '\0'; c++) {
> > +        if (*c == '/') {
> > +            *c = '_';
> > +        }
> > +    }
> > +    return sanitized_name;
> > +}
> > +
> > +static char *rp_autocreate_chardesc(RemotePort *s, bool server)
> > +{
> > +    char *prefix;
> > +    char *chardesc;
> > +    int r;
> > +
> > +    prefix = rp_sanitize_prefix(s);
> > +    r = asprintf(&chardesc, "unix:%s/qemu-rport-%s%s",
> > +                 machine_path, prefix, server ? ",wait,server" : "");
> > +    assert(r > 0);
> > +    free(prefix);
> > +    return chardesc;
> > +}
> > +
> > +static Chardev *rp_autocreate_chardev(RemotePort *s, char *name)
> > +{
> > +    Chardev *chr = NULL;
> > +    char *chardesc;
> > +    char *s_path;
> > +    int r;
> > +
> > +    r = asprintf(&s_path, "%s/qemu-rport-%s", machine_path,
> > +                 rp_sanitize_prefix(s));
> > +    assert(r > 0);
> > +    if (g_file_test(s_path, G_FILE_TEST_EXISTS)) {
> > +        chardesc = rp_autocreate_chardesc(s, false);
> > +        chr = qemu_chr_new_noreplay(name, chardesc, false, NULL);
> > +        free(chardesc);
> > +    }
> > +    free(s_path);
> > +
> > +    if (!chr) {
> > +        chardesc = rp_autocreate_chardesc(s, true);
> > +        chr = qemu_chr_new_noreplay(name, chardesc, false, NULL);
> > +        free(chardesc);
> > +    }
> > +    return chr;
> > +}
> >
> >  static void rp_reset(DeviceState *dev)
> >  {
> > @@ -66,6 +118,127 @@ static void rp_reset(DeviceState *dev)
> >
> >  static void rp_realize(DeviceState *dev, Error **errp)
> >  {
> > +    RemotePort *s = REMOTE_PORT(dev);
> > +    int r;
> > +    Error *err = NULL;
> > +
> > +    s->prefix = object_get_canonical_path(OBJECT(dev));
> > +
> > +    if (!qemu_chr_fe_get_driver(&s->chr)) {
> > +        char *name;
> > +        Chardev *chr = NULL;
> > +        static int nr;
> > +
> > +        r = asprintf(&name, "rport%d", nr);
> > +        nr++;
> > +        assert(r > 0);
> > +
> > +        if (s->chrdev_id) {
> > +            chr = qemu_chr_find(s->chrdev_id);
> > +        }
> > +
> > +        if (chr) {
> > +            /* Found the chardev via commandline */
> > +        } else if (s->chardesc) {
> > +            chr = qemu_chr_new(name, s->chardesc, NULL);
> > +        } else {
> > +            if (!machine_path) {
> > +                error_report("%s: Missing chardesc prop."
> > +                             " Forgot -machine-path?",
> > +                             s->prefix);
> > +                exit(EXIT_FAILURE);
> > +            }
> > +            chr = rp_autocreate_chardev(s, name);
> > +        }
>
> Having three different ways to configure the chardev rather
> feels like overkill. IMHO it should be sufficient to just
> accept thue chardev ID as a mandatory argument, and not
> attempt to create chardevs from this code. That would
> in turn seem to avoid the need for the -machine-path arg
> to exist ?
>

I think we can consider using only chrdev_id, for now. This loses some
flexibility, but definitely simplifies things a lot.

>
> > +#ifdef _WIN32
> > +    /*
> > +     * Create a socket connection between two sockets. We auto-bind
> > +     * and read out the port selected by the kernel.
> > +     */
> > +    {
> > +        char *name;
> > +        SocketAddress *sock;
> > +        int port;
> > +        int listen_sk;
> > +
> > +        sock = socket_parse("127.0.0.1:0", &error_abort);
> > +        listen_sk = socket_listen(sock, 1, &error_abort);
>
> Please avoid introducing new usage of low level sockets APIs. The higher
> level QIOChannelSocket APIs is preferred, that said.....
>
> > +
> > +        if (s->event.pipe.read < 0) {
> > +            perror("socket read");
> > +            exit(EXIT_FAILURE);
> > +        }
> > +
> > +        {
> > +            struct sockaddr_in saddr;
> > +            socklen_t slen = sizeof saddr;
> > +            int r;
> > +
> > +            r = getsockname(listen_sk, (struct sockaddr *) &saddr, &slen);
> > +            if (r < 0) {
> > +                perror("getsockname");
> > +                exit(EXIT_FAILURE);
> > +            }
> > +            port = htons(saddr.sin_port);
> > +        }
> > +
> > +        name = g_strdup_printf("127.0.0.1:%d", port);
> > +        s->event.pipe.write = inet_connect(name, &error_abort);
> > +        g_free(name);
> > +        if (s->event.pipe.write < 0) {
> > +            perror("socket write");
> > +            exit(EXIT_FAILURE);
> > +        }
> > +
> > +        for (;;) {
> > +            struct sockaddr_in saddr;
> > +            socklen_t slen = sizeof saddr;
> > +            int fd;
> > +
> > +            slen = sizeof(saddr);
> > +            fd = qemu_accept(listen_sk, (struct sockaddr *)&saddr, &slen);
> > +            if (fd < 0 && errno != EINTR) {
> > +                close(listen_sk);
> > +                return;
> > +            } else if (fd >= 0) {
> > +                close(listen_sk);
> > +                s->event.pipe.read = fd;
> > +                break;
> > +            }
> > +        }
> > +
> > +        if (!qemu_set_blocking(s->event.pipe.read, false, &err)) {
> > +            error_report("%s: Unable to set non-block for internal pipes",
> > +                        s->prefix);
> > +            exit(EXIT_FAILURE);
> > +        }
> > +    }
> > +#else
> > +    if (!g_unix_open_pipe(s->event.pipes, FD_CLOEXEC, NULL)) {
> > +        error_report("%s: Unable to create remort-port internal pipes",
> > +                    s->prefix);
> > +        exit(EXIT_FAILURE);
> > +    }
> > +
> > +    if (!qemu_set_blocking(s->event.pipe.read, false, &err)) {
> > +        error_report("%s: Unable to set non-block for internal pipes",
> > +                    s->prefix);
> > +        exit(EXIT_FAILURE);
> > +    }
> > +
> > +#endif
>
> ... I can't help thinking we have this code "self event pipe" design
> somewhere else in QEMU. Ideally we would have a helper API for this
> task and avoid OS conditional code in this device impl.
>
>
Thanks! Looks like EventNotifier may work here and it supports both platforms.
I'll try to use it instead then.

> >  }
> >
> >  static void rp_unrealize(DeviceState *dev)
> > @@ -73,6 +246,13 @@ static void rp_unrealize(DeviceState *dev)
> >      RemotePort *s = REMOTE_PORT(dev);
> >
> >      s->finalizing = true;
> > +
> > +    info_report("%s: Wait for remote-port to disconnect", s->prefix);
> > +    qemu_chr_fe_disconnect(&s->chr);
> > +
> > +    close(s->event.pipe.read);
> > +    close(s->event.pipe.write);
> > +    object_unparent(OBJECT(s->chrdev));
> >  }
> >
> >  static const VMStateDescription vmstate_rp = {
> > @@ -84,6 +264,12 @@ static const VMStateDescription vmstate_rp = {
> >      }
> >  };
> >
> > +static Property rp_properties[] = {
> > +    DEFINE_PROP_CHR("chardev", RemotePort, chr),
> > +    DEFINE_PROP_STRING("chardesc", RemotePort, chardesc),
> > +    DEFINE_PROP_STRING("chrdev-id", RemotePort, chrdev_id),
> > +};
>
> This really feels lik
>
> > +
> >  static void rp_prop_allow_set_link(const Object *obj, const char *name,
> >                                     Object *val, Error **errp)
> >  {
> > @@ -112,6 +298,7 @@ static void rp_class_init(ObjectClass *klass, const 
> > void *data)
> >      dc->realize = rp_realize;
> >      dc->unrealize = rp_unrealize;
> >      dc->vmsd = &vmstate_rp;
> > +    device_class_set_props_n(dc, rp_properties, ARRAY_SIZE(rp_properties));
> >  }
> >
> >  static const TypeInfo rp_info = {
> > diff --git a/include/hw/core/remote-port.h b/include/hw/core/remote-port.h
> > index db71071c8e..0f40018cdb 100644
> > --- a/include/hw/core/remote-port.h
> > +++ b/include/hw/core/remote-port.h
> > @@ -56,8 +56,24 @@ typedef struct RemotePortDeviceClass {
> >  struct RemotePort {
> >      DeviceState parent;
> >
> > +    union {
> > +       int pipes[2];
> > +       struct {
> > +           int read;
> > +           int write;
> > +       } pipe;
> > +    } event;
> > +    Chardev *chrdev;
> > +    CharFrontend chr;
> >      bool finalizing;
> >
> > +    char *chardesc;
> > +    char *chrdev_id;
> > +
> > +    const char *prefix;
> > +    const char *remote_prefix;
> > +
> > +    uint32_t current_id;
> >      bool reset_done;
> >
> >  #define REMOTE_PORT_MAX_DEVS 1024
> > --
> > 2.43.0
> >
> >
>
> With regards,
> Daniel
> --
> |: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org         -o-            https://fstop138.berrange.com :|
> |: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
>

--
BR,
Ruslan

Reply via email to