Add QEMU_CHAR_FEATURE_CPR for devices that support cpr. Add the chardev reopen-on-cpr option for devices that can be closed on cpr and reopened after exec. cpr is allowed only if either QEMU_CHAR_FEATURE_CPR or reopen-on-cpr is set for all chardevs in the configuration.
Signed-off-by: Steve Sistare <steven.sist...@oracle.com> --- chardev/char.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- include/chardev/char.h | 5 +++++ migration/cpr.c | 1 + qapi/char.json | 7 ++++++- qemu-options.hx | 26 ++++++++++++++++++++++---- 5 files changed, 76 insertions(+), 8 deletions(-) diff --git a/chardev/char.c b/chardev/char.c index 0169d8d..230bf16 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -36,6 +36,7 @@ #include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/option.h" +#include "migration/cpr.h" #include "qemu/id.h" #include "qemu/coroutine.h" #include "qemu/yank.h" @@ -240,15 +241,24 @@ static void qemu_char_open(Chardev *chr, ChardevBackend *backend, /* Any ChardevCommon member would work */ ChardevCommon *common = backend ? backend->u.null.data : NULL; + chr->reopen_on_cpr = (common && common->reopen_on_cpr); + if (common && common->has_logfile) { int flags = O_WRONLY; + g_autofree char *fdname = g_strdup_printf("%s_log", chr->label); if (common->has_logappend && common->logappend) { flags |= O_APPEND; } else { flags |= O_TRUNC; } - chr->logfd = qemu_create(common->logfile, flags, 0666, errp); + chr->logfd = cpr_find_fd(fdname, 0); + if (chr->logfd < 0) { + chr->logfd = qemu_create(common->logfile, flags, 0666, errp); + if (!chr->reopen_on_cpr) { + cpr_save_fd(fdname, 0, chr->logfd); + } + } if (chr->logfd < 0) { return; } @@ -297,11 +307,15 @@ static void char_finalize(Object *obj) if (chr->be) { chr->be->chr = NULL; } - g_free(chr->filename); - g_free(chr->label); if (chr->logfd != -1) { + g_autofree char *fdname = g_strdup_printf("%s_log", chr->label); + if (!chr->reopen_on_cpr) { + cpr_delete_fd(fdname, 0); + } close(chr->logfd); } + g_free(chr->filename); + g_free(chr->label); qemu_mutex_destroy(&chr->chr_write_lock); } @@ -501,6 +515,8 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend) backend->has_logappend = true; backend->logappend = qemu_opt_get_bool(opts, "logappend", false); + + backend->reopen_on_cpr = qemu_opt_get_bool(opts, "reopen-on-cpr", false); } static const ChardevClass *char_get_class(const char *driver, Error **errp) @@ -942,6 +958,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "abstract", .type = QEMU_OPT_BOOL, + },{ + .name = "reopen-on-cpr", + .type = QEMU_OPT_BOOL, #endif }, { /* end of list */ } @@ -1217,6 +1236,26 @@ GSource *qemu_chr_timeout_add_ms(Chardev *chr, guint ms, return source; } +static int chr_cpr_capable(Object *obj, void *opaque) +{ + Chardev *chr = (Chardev *)obj; + Error **errp = opaque; + + if (qemu_chr_has_feature(chr, QEMU_CHAR_FEATURE_CPR) || + chr->reopen_on_cpr) { + return 0; + } + error_setg(errp, + "chardev %s -> %s is not capable of cpr. See reopen-on-cpr", + chr->label, chr->filename); + return -1; +} + +bool qemu_chr_is_cpr_capable(Error **errp) +{ + return !object_child_foreach(get_chardevs_root(), chr_cpr_capable, errp); +} + void qemu_chr_cleanup(void) { object_unparent(get_chardevs_root()); diff --git a/include/chardev/char.h b/include/chardev/char.h index a319b5f..299e129 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -50,6 +50,8 @@ typedef enum { /* Whether the gcontext can be changed after calling * qemu_chr_be_update_read_handlers() */ QEMU_CHAR_FEATURE_GCONTEXT, + /* Whether the device supports cpr */ + QEMU_CHAR_FEATURE_CPR, QEMU_CHAR_FEATURE_LAST, } ChardevFeature; @@ -67,6 +69,7 @@ struct Chardev { int be_open; /* used to coordinate the chardev-change special-case: */ bool handover_yank_instance; + bool reopen_on_cpr; GSource *gsource; GMainContext *gcontext; DECLARE_BITMAP(features, QEMU_CHAR_FEATURE_LAST); @@ -323,4 +326,6 @@ void resume_mux_open(void); /* console.c */ void qemu_chr_parse_vc(QemuOpts *opts, ChardevBackend *backend, Error **errp); +bool qemu_chr_is_cpr_capable(Error **errp); + #endif diff --git a/migration/cpr.c b/migration/cpr.c index 4229c17..3bda83e 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -6,6 +6,7 @@ */ #include "qemu/osdep.h" +#include "chardev/char.h" #include "exec/memory.h" #include "hw/vfio/vfio-common.h" #include "hw/virtio/vhost.h" diff --git a/qapi/char.json b/qapi/char.json index 7b42151..dfa6baf 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -204,12 +204,17 @@ # @logfile: The name of a logfile to save output # @logappend: true to append instead of truncate # (default to false to truncate) +# @reopen-on-cpr: if true, close device's fd on cpr-save and reopen it after +# cpr-exec. Set this to allow CPR on a device that does not +# support QEMU_CHAR_FEATURE_CPR. defaults to false. +# since 6.2. # # Since: 2.6 ## { 'struct': 'ChardevCommon', 'data': { '*logfile': 'str', - '*logappend': 'bool' } } + '*logappend': 'bool', + '*reopen-on-cpr': 'bool' } } ## # @ChardevFile: diff --git a/qemu-options.hx b/qemu-options.hx index 33c8173..1859b55 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3227,43 +3227,57 @@ DEFHEADING(Character device options:) DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev help\n" - "-chardev null,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + "-chardev null,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off][,reopen-on-cpr=on|off]\n" "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4=on|off][,ipv6=on|off][,nodelay=on|off]\n" " [,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds][,mux=on|off]\n" - " [,logfile=PATH][,logappend=on|off][,tls-creds=ID][,tls-authz=ID] (tcp)\n" + " [,logfile=PATH][,logappend=on|off][,tls-creds=ID][,tls-authz=ID][,reopen-on-cpr=on|off] (tcp)\n" "-chardev socket,id=id,path=path[,server=on|off][,wait=on|off][,telnet=on|off][,websocket=on|off][,reconnect=seconds]\n" - " [,mux=on|off][,logfile=PATH][,logappend=on|off][,abstract=on|off][,tight=on|off] (unix)\n" + " [,mux=on|off][,logfile=PATH][,logappend=on|off][,abstract=on|off][,tight=on|off][,reopen-on-cpr=on|off] (unix)\n" "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n" " [,localport=localport][,ipv4=on|off][,ipv6=on|off][,mux=on|off]\n" - " [,logfile=PATH][,logappend=on|off]\n" + " [,logfile=PATH][,logappend=on|off][,reopen-on-cpr=on|off]\n" "-chardev msmouse,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" "-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]]\n" " [,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" "-chardev ringbuf,id=id[,size=size][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" "-chardev file,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" "-chardev pipe,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" #ifdef _WIN32 "-chardev console,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" "-chardev serial,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" #else "-chardev pty,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" "-chardev stdio,id=id[,mux=on|off][,signal=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" #endif #ifdef CONFIG_BRLAPI "-chardev braille,id=id[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" #endif #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) "-chardev serial,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" "-chardev tty,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" #endif #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) "-chardev parallel,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" "-chardev parport,id=id,path=path[,mux=on|off][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" #endif #if defined(CONFIG_SPICE) "-chardev spicevmc,id=id,name=name[,debug=debug][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" "-chardev spiceport,id=id,name=name[,debug=debug][,logfile=PATH][,logappend=on|off]\n" + " [,reopen-on-cpr=on|off]\n" #endif , QEMU_ARCH_ALL ) @@ -3338,6 +3352,10 @@ The general form of a character device option is: ``logappend`` option controls whether the log file will be truncated or appended to when opened. + Every backend supports the ``reopen-on-cpr`` option. If on, the + devices's descriptor is closed during cpr-save, and reopened after exec. + This is useful for devices that do not support cpr. + The available backends are: ``-chardev null,id=id`` -- 1.8.3.1