All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
From: "Denis V. Lunev" <den@openvz.org>
To: svt-core@virtuozzo.com
Cc: andrey.drobyshev@virtuozzo.com, den@virtuozzo.com
Subject: [PATCH 2/3] chardev: preserve the logfile fd across cpr-exec #VSTOR-148304
Date: Fri,  2 Oct 2026 18:48:52 +0200	[thread overview]
Message-ID: <20261002164853.2130337-3-den@openvz.org> (raw)
In-Reply-To: <20261002164853.2130337-1-den@openvz.org>

From: Denis V. Lunev <den@openvz.org>

A chardev with logfile= opens the log in qemu_char_open() on every
start, including the start of the new QEMU after cpr-exec. The backend
fds are taken from the cpr fd registry there, the log fd is not, so the
new process reopens the path itself.

libvirt normally opens the log through virtlogd and passes a pipe in an
fdset. The qemu-update command line cannot carry that fdset, so it
names the log file path. The exec'd QEMU, running as the qemu user,
then fails to create a log it has no write access to, e.g. a nova
console.log on node-local storage:

  qemu-kvm: -chardev pty,id=charserial0,logfile=.../console.log,
  logappend=on: Could not create '.../console.log': Permission denied

and the VM is destroyed. Where the open succeeds, the new QEMU writes
the file directly, behind virtlogd's back.

Save the log fd in the cpr registry under the chardev label and take it
from there on cpr-exec incoming, as the backends do. The logfile path
is not opened at all then. If the source did not save it, run without
a log rather than reopen the path. A failed chardev-change re-claims
the log through the base class cpr_claim_fds(), since the replacement's
log has displaced the entry.

Signed-off-by: Denis V. Lunev <den@openvz.org>
---
 chardev/char.c          | 30 +++++++++++++++++++++++++-----
 include/migration/cpr.h |  1 +
 2 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/chardev/char.c b/chardev/char.c
index b64767f574e..c037d2e7730 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -40,6 +40,7 @@
 #include "qemu/id.h"
 #include "qemu/coroutine.h"
 #include "qemu/yank.h"
+#include "migration/cpr.h"
 
 #include "chardev-internal.h"
 
@@ -261,9 +262,21 @@ static void qemu_char_open(Chardev *chr, ChardevBackend *backend,
         } else {
             flags |= O_TRUNC;
         }
-        chr->logfd = qemu_create(common->logfile, flags, 0666, errp);
-        if (chr->logfd < 0) {
-            return;
+        if (cpr_exec_is_incoming()) {
+            chr->logfd = cpr_find_fd(chr->label, CPR_CHARDEV_LOG_ID);
+            if (chr->logfd < 0) {
+                warn_report("chardev '%s': log file was not preserved, "
+                            "continuing without it", chr->label);
+            }
+        } else {
+            chr->logfd = qemu_create(common->logfile, flags, 0666, errp);
+            if (chr->logfd < 0) {
+                return;
+            }
+            if (!cpr_is_incoming()) {
+                cpr_delete_fd(chr->label, CPR_CHARDEV_LOG_ID);
+                cpr_save_fd(chr->label, CPR_CHARDEV_LOG_ID, chr->logfd);
+            }
         }
     }
 
@@ -297,6 +310,10 @@ static int null_chr_write(Chardev *chr, const uint8_t *buf, int len)
 
 static void char_cpr_claim_fds(Chardev *chr)
 {
+    if (chr->logfd >= 0) {
+        cpr_delete_fd(chr->label, CPR_CHARDEV_LOG_ID);
+        cpr_save_fd(chr->label, CPR_CHARDEV_LOG_ID, chr->logfd);
+    }
 }
 
 static void char_class_init(ObjectClass *oc, const void *data)
@@ -315,11 +332,14 @@ 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) {
+        if (cpr_find_fd(chr->label, CPR_CHARDEV_LOG_ID) == chr->logfd) {
+            cpr_delete_fd(chr->label, CPR_CHARDEV_LOG_ID);
+        }
         close(chr->logfd);
     }
+    g_free(chr->filename);
+    g_free(chr->label);
     qemu_mutex_destroy(&chr->chr_write_lock);
 }
 
diff --git a/include/migration/cpr.h b/include/migration/cpr.h
index d74ff1ea602..8fb79eb0a28 100644
--- a/include/migration/cpr.h
+++ b/include/migration/cpr.h
@@ -15,6 +15,7 @@
 
 #define CPR_LISTENSOCK_ID       0
 #define CPR_CONNSOCK_ID         1
+#define CPR_CHARDEV_LOG_ID      2
 
 #define QEMU_CPR_FILE_MAGIC     0x51435052
 #define QEMU_CPR_FILE_VERSION   0x00000001
-- 
2.53.0


  parent reply	other threads:[~2026-10-02 16:49 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-02 16:48 [PATCH 0/3] chardev: fix qemu-update destroying VMs with a console log #VSTOR-148304 Denis V. Lunev
2026-10-02 16:48 ` [PATCH 1/3] chardev: implement cpr_claim_fds() in the base class #VSTOR-148304 Denis V. Lunev
2026-10-02 16:50   ` no-reply
2026-10-05  9:05   ` Andrey Drobyshev
2026-10-02 16:48 ` Denis V. Lunev [this message]
2026-10-02 16:54   ` [PATCH 2/3] chardev: preserve the logfile fd across cpr-exec #VSTOR-148304 no-reply
2026-10-05  9:05   ` Andrey Drobyshev
2026-10-02 16:48 ` [PATCH 3/3] iotests/migrate-cpr-exec: cover chardevs with logfile= #VSTOR-148304 Denis V. Lunev
2026-10-02 16:55   ` no-reply
2026-10-05  9:05   ` Andrey Drobyshev

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261002164853.2130337-3-den@openvz.org \
    --to=den@openvz.org \
    --cc=andrey.drobyshev@virtuozzo.com \
    --cc=den@virtuozzo.com \
    --cc=svt-core@virtuozzo.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.