Merge tag 'chr-pull-request' of https://gitlab.com/marcandre.lureau/qemu into staging
chardev patch queue
# -----BEGIN PGP SIGNATURE-----
#
# iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmcOLLEcHG1hcmNhbmRy
# ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5eTaD/0Strje27bLtepVKLQU
# ZbD9X89G8lI2l87j9odwLjWcUEFCTVBhVgSCzrRdllWTlJoc5NVSUPQT9KcuWRpx
# MOjaiR+BO/QnflAKN0GfxCQAnPfYb/29rIe2Xr8co8byVUuE32GPcAy+0xoScOHD
# tpFbZegESsJ8MJN/pmYX83rt82mcNnCxIJxSxm7f4W8+3pdmFPsGQ8Ph+oVnz2n0
# Ag32e9rPUON6yeIZDyyc9Bn+JDxH+DsB8kK5N+KwoKvFIH3Knu725m8skwZvXjl0
# HmscpivJ4JS2N//Y0s891PaBFBzQOjWgv/EtBdW/LQZTcQdjaV4ZF1mUuom/oVPJ
# ydtoJz44dkSC/dOf4J6uwWTk7PNCqlqQfHRuZqosedIAw9MFvBSq5NhfPTut2qKr
# AWPu4r82zM18a/GEjRLPXCnF2APPGa56WhSxn8jGe+FIxcCADDCA9TAzSJLPMHJ8
# 5moXFmPXjYNrT/Wj4jsso73GCCGNPSNcJa+6/avn7SW8SjJGtpj3f7Qj5Aou5i+j
# zsFyzFlAKnGQMBi1Qv4kfd4H87hqGvVahT9+uG/pKfvNQw1VB4dKMWSNs1Utiuim
# sydt2VBwx6B923/6bmZ5HTbO5S9Hfep9jgwemPONNiIi5UeUCv6wTs0f8aallrv0
# idOZFmMK5JNn2NybEgALL1x6lg==
# =+W1p
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 15 Oct 2024 09:49:53 BST
# gpg: using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5
# gpg: issuer "marcandre.lureau@redhat.com"
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full]
# gpg: aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full]
# Primary key fingerprint: 87A9 BD93 3F87 C606 D276 F62D DAE8 E109 7596 9CE5
* tag 'chr-pull-request' of https://gitlab.com/marcandre.lureau/qemu:
tests/unit/test-char: implement a few mux remove test cases
chardev/mux: implement detach of frontends from mux
chardev/mux: switch mux frontends management to bitset
chardev/mux: introduce `mux_chr_attach_frontend() call
chardev/mux: convert size members to unsigned int
chardev/mux: use bool type for `linestart` and `term_got_escape`
chardev/chardev-internal: remove unused `max_size` struct member
chardev/char: fix qemu_chr_is_busy() check
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/chardev/char-fe.c b/chardev/char-fe.c
index b214ba3..8ac6beb 100644
--- a/chardev/char-fe.c
+++ b/chardev/char-fe.c
@@ -191,22 +191,15 @@
bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp)
{
- int tag = 0;
+ unsigned int tag = 0;
if (s) {
if (CHARDEV_IS_MUX(s)) {
MuxChardev *d = MUX_CHARDEV(s);
- if (d->mux_cnt >= MAX_MUX) {
- error_setg(errp,
- "too many uses of multiplexed chardev '%s'"
- " (maximum is " stringify(MAX_MUX) ")",
- s->label);
+ if (!mux_chr_attach_frontend(d, b, &tag, errp)) {
return false;
}
-
- d->backends[d->mux_cnt] = b;
- tag = d->mux_cnt++;
} else if (s->be) {
error_setg(errp, "chardev '%s' is already in use", s->label);
return false;
@@ -232,7 +225,7 @@
}
if (CHARDEV_IS_MUX(b->chr)) {
MuxChardev *d = MUX_CHARDEV(b->chr);
- d->backends[b->tag] = NULL;
+ mux_chr_detach_frontend(d, b->tag);
}
if (del) {
Object *obj = OBJECT(b->chr);
diff --git a/chardev/char-mux.c b/chardev/char-mux.c
index ee2d47b..bda5c45 100644
--- a/chardev/char-mux.c
+++ b/chardev/char-mux.c
@@ -26,6 +26,7 @@
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/option.h"
+#include "qemu/bitops.h"
#include "chardev/char.h"
#include "sysemu/block-backend.h"
#include "qapi/qapi-commands-control.h"
@@ -73,11 +74,11 @@
* qemu_chr_fe_write and background I/O callbacks */
qemu_chr_fe_write_all(&d->chr,
(uint8_t *)buf1, strlen(buf1));
- d->linestart = 0;
+ d->linestart = false;
}
ret += qemu_chr_fe_write(&d->chr, buf + i, 1);
if (buf[i] == '\n') {
- d->linestart = 1;
+ d->linestart = true;
}
}
}
@@ -124,7 +125,8 @@
}
}
-static void mux_chr_send_event(MuxChardev *d, int mux_nr, QEMUChrEvent event)
+static void mux_chr_send_event(MuxChardev *d, unsigned int mux_nr,
+ QEMUChrEvent event)
{
CharBackend *be = d->backends[mux_nr];
@@ -145,7 +147,7 @@
static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
{
if (d->term_got_escape) {
- d->term_got_escape = 0;
+ d->term_got_escape = false;
if (ch == term_escape_char) {
goto send_char;
}
@@ -167,19 +169,26 @@
case 'b':
qemu_chr_be_event(chr, CHR_EVENT_BREAK);
break;
- case 'c':
- assert(d->mux_cnt > 0); /* handler registered with first fe */
+ case 'c': {
+ unsigned int bit;
+
+ /* Handler registered with first fe */
+ assert(d->mux_bitset != 0);
/* Switch to the next registered device */
- mux_set_focus(chr, (d->focus + 1) % d->mux_cnt);
+ bit = find_next_bit(&d->mux_bitset, MAX_MUX, d->focus + 1);
+ if (bit >= MAX_MUX) {
+ bit = find_next_bit(&d->mux_bitset, MAX_MUX, 0);
+ }
+ mux_set_focus(chr, bit);
break;
- case 't':
+ } case 't':
d->timestamps = !d->timestamps;
d->timestamps_start = -1;
- d->linestart = 0;
+ d->linestart = false;
break;
}
} else if (ch == term_escape_char) {
- d->term_got_escape = 1;
+ d->term_got_escape = true;
} else {
send_char:
return 1;
@@ -242,15 +251,16 @@
void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event)
{
MuxChardev *d = MUX_CHARDEV(chr);
- int i;
+ int bit;
if (!muxes_opened) {
return;
}
/* Send the event to all registered listeners */
- for (i = 0; i < d->mux_cnt; i++) {
- mux_chr_send_event(d, i, event);
+ bit = -1;
+ while ((bit = find_next_bit(&d->mux_bitset, MAX_MUX, bit + 1)) < MAX_MUX) {
+ mux_chr_send_event(d, bit, event);
}
}
@@ -275,14 +285,15 @@
static void char_mux_finalize(Object *obj)
{
MuxChardev *d = MUX_CHARDEV(obj);
- int i;
+ int bit;
- for (i = 0; i < d->mux_cnt; i++) {
- CharBackend *be = d->backends[i];
- if (be) {
- be->chr = NULL;
- }
+ bit = -1;
+ while ((bit = find_next_bit(&d->mux_bitset, MAX_MUX, bit + 1)) < MAX_MUX) {
+ CharBackend *be = d->backends[bit];
+ be->chr = NULL;
+ d->backends[bit] = NULL;
}
+ d->mux_bitset = 0;
qemu_chr_fe_deinit(&d->chr, false);
}
@@ -300,12 +311,47 @@
chr->gcontext, true, false);
}
-void mux_set_focus(Chardev *chr, int focus)
+bool mux_chr_attach_frontend(MuxChardev *d, CharBackend *b,
+ unsigned int *tag, Error **errp)
+{
+ unsigned int bit;
+
+ bit = find_next_zero_bit(&d->mux_bitset, MAX_MUX, 0);
+ if (bit >= MAX_MUX) {
+ error_setg(errp,
+ "too many uses of multiplexed chardev '%s'"
+ " (maximum is " stringify(MAX_MUX) ")",
+ d->parent.label);
+ return false;
+ }
+
+ d->mux_bitset |= (1 << bit);
+ d->backends[bit] = b;
+ *tag = bit;
+
+ return true;
+}
+
+bool mux_chr_detach_frontend(MuxChardev *d, unsigned int tag)
+{
+ unsigned int bit;
+
+ bit = find_next_bit(&d->mux_bitset, MAX_MUX, tag);
+ if (bit != tag) {
+ return false;
+ }
+
+ d->mux_bitset &= ~(1 << bit);
+ d->backends[bit] = NULL;
+
+ return true;
+}
+
+void mux_set_focus(Chardev *chr, unsigned int focus)
{
MuxChardev *d = MUX_CHARDEV(chr);
- assert(focus >= 0);
- assert(focus < d->mux_cnt);
+ assert(find_next_bit(&d->mux_bitset, MAX_MUX, focus) == focus);
if (d->focus != -1) {
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
diff --git a/chardev/char.c b/chardev/char.c
index c0cc528..a1722aa 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -333,7 +333,7 @@
{
if (CHARDEV_IS_MUX(s)) {
MuxChardev *d = MUX_CHARDEV(s);
- return d->mux_cnt >= 0;
+ return d->mux_bitset != 0;
} else {
return s->be != NULL;
}
diff --git a/chardev/chardev-internal.h b/chardev/chardev-internal.h
index 4e03af3..853807f 100644
--- a/chardev/chardev-internal.h
+++ b/chardev/chardev-internal.h
@@ -37,20 +37,19 @@
Chardev parent;
CharBackend *backends[MAX_MUX];
CharBackend chr;
+ unsigned long mux_bitset;
int focus;
- int mux_cnt;
- int term_got_escape;
- int max_size;
+ bool term_got_escape;
/* Intermediate input buffer catches escape sequences even if the
currently active device is not accepting any input - but only until it
is full as well. */
unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE];
- int prod[MAX_MUX];
- int cons[MAX_MUX];
+ unsigned int prod[MAX_MUX];
+ unsigned int cons[MAX_MUX];
int timestamps;
/* Protected by the Chardev chr_write_lock. */
- int linestart;
+ bool linestart;
int64_t timestamps_start;
};
typedef struct MuxChardev MuxChardev;
@@ -60,7 +59,10 @@
#define CHARDEV_IS_MUX(chr) \
object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX)
-void mux_set_focus(Chardev *chr, int focus);
+bool mux_chr_attach_frontend(MuxChardev *d, CharBackend *b,
+ unsigned int *tag, Error **errp);
+bool mux_chr_detach_frontend(MuxChardev *d, unsigned int tag);
+void mux_set_focus(Chardev *chr, unsigned int focus);
void mux_chr_send_all_event(Chardev *chr, QEMUChrEvent event);
Object *get_chardevs_root(void);
diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h
index 3310449..8ef05b3 100644
--- a/include/chardev/char-fe.h
+++ b/include/chardev/char-fe.h
@@ -20,7 +20,7 @@
IOReadHandler *chr_read;
BackendChangeHandler *chr_be_change;
void *opaque;
- int tag;
+ unsigned int tag;
bool fe_is_open;
};
diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c
index f273ce5..a1c6bb8 100644
--- a/tests/unit/test-char.c
+++ b/tests/unit/test-char.c
@@ -1,6 +1,7 @@
#include "qemu/osdep.h"
#include <glib/gstdio.h>
+#include "qapi/error.h"
#include "qemu/config-file.h"
#include "qemu/module.h"
#include "qemu/option.h"
@@ -184,6 +185,21 @@
char *data;
FeHandler h1 = { 0, false, 0, false, }, h2 = { 0, false, 0, false, };
CharBackend chr_be1, chr_be2;
+ Error *error = NULL;
+
+ /* Create mux and chardev to be immediately removed */
+ opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
+ 1, &error_abort);
+ qemu_opt_set(opts, "backend", "ringbuf", &error_abort);
+ qemu_opt_set(opts, "size", "128", &error_abort);
+ qemu_opt_set(opts, "mux", "on", &error_abort);
+ chr = qemu_chr_new_from_opts(opts, NULL, &error_abort);
+ g_assert_nonnull(chr);
+ qemu_opts_del(opts);
+
+ /* Remove just created mux and chardev */
+ qmp_chardev_remove("mux-label", &error_abort);
+ qmp_chardev_remove("mux-label-base", &error_abort);
opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label",
1, &error_abort);
@@ -334,7 +350,13 @@
g_free(data);
qemu_chr_fe_deinit(&chr_be1, false);
- qemu_chr_fe_deinit(&chr_be2, true);
+
+ qmp_chardev_remove("mux-label", &error);
+ g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'mux-label' is busy");
+ error_free(error);
+
+ qemu_chr_fe_deinit(&chr_be2, false);
+ qmp_chardev_remove("mux-label", &error_abort);
}