Merge tag 'net-pull-request' of https://github.com/jasowang/qemu into staging
# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEEIV1G9IJGaJ7HfzVi7wSWWzmNYhEFAmXwPUAACgkQ7wSWWzmN
# YhFnIwgAgctDniJwlRxXB01eVlzXz7IulHnpSby07XEJxENSpGB8ufaeE4eK5gJy
# NVK6C2+1EU2vRxm4oIdcvtN4C4/jtRbYYjiSTx7eE4FmSkqshSnR5XCV72LDqG3i
# WbzInjMvYfysmcMXLfrWgxOnVew9WqEzlpEWlc7FfNKnkzBVf+JDztfqCUx0XM7H
# qefw4ImjqQw993QxJpipXC7aEGUyouB0RIBB71FkCa9ihlh9x7W68evbOI/jTn5q
# HWuStgS02sKHjRFliMbdbMY77FNUz4Yroo/GKSvGt64atxkQSJqPNAV+/9n18LNy
# QAH5eK6cXFPOIAaYpADU5kHDVVAFiw==
# =iBdx
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 12 Mar 2024 11:32:16 GMT
# gpg: using RSA key 215D46F48246689EC77F3562EF04965B398D6211
# gpg: Good signature from "Jason Wang (Jason Wang on RedHat) <jasowang@redhat.com>" [marginal]
# gpg: WARNING: This key is not certified with sufficiently trusted signatures!
# gpg: It is not certain that the signature belongs to the owner.
# Primary key fingerprint: 215D 46F4 8246 689E C77F 3562 EF04 965B 398D 6211
* tag 'net-pull-request' of https://github.com/jasowang/qemu:
ebpf: Updated eBPF program and skeleton.
qmp: Added new command to retrieve eBPF blob.
virtio-net: Added property to load eBPF RSS with fds.
ebpf: Added eBPF initialization by fds.
ebpf: Added eBPF map update through mmap.
Avoid unaligned fetch in ladr_match()
e1000e: fix link state on resume
igb: fix link state on resume
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index 4d96f85..0087e4f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2503,6 +2503,12 @@
F: hw/i2c/i2c_mux_pca954x.c
F: include/hw/i2c/i2c_mux_pca954x.h
+pcf8574
+M: Dmitrii Sharikhin <d.sharikhin@yadro.com>
+S: Maintained
+F: hw/gpio/pcf8574.c
+F: include/gpio/pcf8574.h
+
Generic Loader
M: Alistair Francis <alistair@alistair23.me>
S: Maintained
@@ -3587,6 +3593,7 @@
elf2dmp
M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
+R: Akihiko Odaki <akihiko.odaki@daynix.com>
S: Maintained
F: contrib/elf2dmp/
diff --git a/contrib/elf2dmp/addrspace.c b/contrib/elf2dmp/addrspace.c
index 6f608a5..81295a1 100644
--- a/contrib/elf2dmp/addrspace.c
+++ b/contrib/elf2dmp/addrspace.c
@@ -22,7 +22,7 @@
return NULL;
}
-static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa)
+static void *pa_space_resolve(struct pa_space *ps, uint64_t pa)
{
struct pa_block *block = pa_space_find_block(ps, pa);
@@ -33,6 +33,19 @@
return block->addr + (pa - block->paddr);
}
+static bool pa_space_read64(struct pa_space *ps, uint64_t pa, uint64_t *value)
+{
+ uint64_t *resolved = pa_space_resolve(ps, pa);
+
+ if (!resolved) {
+ return false;
+ }
+
+ *value = *resolved;
+
+ return true;
+}
+
static void pa_block_align(struct pa_block *b)
{
uint64_t low_align = ((b->paddr - 1) | ELF2DMP_PAGE_MASK) + 1 - b->paddr;
@@ -57,7 +70,7 @@
b->paddr += low_align;
}
-int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
+void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
{
Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map);
Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map);
@@ -75,11 +88,12 @@
ps->block = g_new(struct pa_block, ps->block_nr);
for (i = 0; i < phdr_nr; i++) {
- if (phdr[i].p_type == PT_LOAD) {
+ if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset < qemu_elf->size) {
ps->block[block_i] = (struct pa_block) {
.addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset,
.paddr = phdr[i].p_paddr,
- .size = phdr[i].p_filesz,
+ .size = MIN(phdr[i].p_filesz,
+ qemu_elf->size - phdr[i].p_offset),
};
pa_block_align(&ps->block[block_i]);
block_i = ps->block[block_i].size ? (block_i + 1) : block_i;
@@ -87,8 +101,6 @@
}
ps->block_nr = block_i;
-
- return 0;
}
void pa_space_destroy(struct pa_space *ps)
@@ -108,19 +120,20 @@
va_space_set_dtb(vs, dtb);
}
-static uint64_t get_pml4e(struct va_space *vs, uint64_t va)
+static bool get_pml4e(struct va_space *vs, uint64_t va, uint64_t *value)
{
uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36);
- return *(uint64_t *)pa_space_resolve(vs->ps, pa);
+ return pa_space_read64(vs->ps, pa, value);
}
-static uint64_t get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e)
+static bool get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e,
+ uint64_t *value)
{
uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) |
((va & 0x7FC0000000) >> 27);
- return *(uint64_t *)pa_space_resolve(vs->ps, pdpte_paddr);
+ return pa_space_read64(vs->ps, pdpte_paddr, value);
}
static uint64_t pde_index(uint64_t va)
@@ -133,11 +146,12 @@
return pdpe & 0xFFFFFFFFFF000;
}
-static uint64_t get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe)
+static bool get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe,
+ uint64_t *value)
{
uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8;
- return *(uint64_t *)pa_space_resolve(vs->ps, pgd_entry);
+ return pa_space_read64(vs->ps, pgd_entry, value);
}
static uint64_t pte_index(uint64_t va)
@@ -150,11 +164,12 @@
return pde & 0xFFFFFFFFFF000;
}
-static uint64_t get_pte(struct va_space *vs, uint64_t va, uint64_t pgd)
+static bool get_pte(struct va_space *vs, uint64_t va, uint64_t pgd,
+ uint64_t *value)
{
uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8;
- return *(uint64_t *)pa_space_resolve(vs->ps, pgd_val);
+ return pa_space_read64(vs->ps, pgd_val, value);
}
static uint64_t get_paddr(uint64_t va, uint64_t pte)
@@ -186,13 +201,11 @@
{
uint64_t pml4e, pdpe, pgd, pte;
- pml4e = get_pml4e(vs, va);
- if (!is_present(pml4e)) {
+ if (!get_pml4e(vs, va, &pml4e) || !is_present(pml4e)) {
return INVALID_PA;
}
- pdpe = get_pdpi(vs, va, pml4e);
- if (!is_present(pdpe)) {
+ if (!get_pdpi(vs, va, pml4e, &pdpe) || !is_present(pdpe)) {
return INVALID_PA;
}
@@ -200,8 +213,7 @@
return get_1GB_paddr(va, pdpe);
}
- pgd = get_pgd(vs, va, pdpe);
- if (!is_present(pgd)) {
+ if (!get_pgd(vs, va, pdpe, &pgd) || !is_present(pgd)) {
return INVALID_PA;
}
@@ -209,8 +221,7 @@
return get_2MB_paddr(va, pgd);
}
- pte = get_pte(vs, va, pgd);
- if (!is_present(pte)) {
+ if (!get_pte(vs, va, pgd, &pte) || !is_present(pte)) {
return INVALID_PA;
}
@@ -228,8 +239,8 @@
return pa_space_resolve(vs->ps, pa);
}
-int va_space_rw(struct va_space *vs, uint64_t addr,
- void *buf, size_t size, int is_write)
+bool va_space_rw(struct va_space *vs, uint64_t addr,
+ void *buf, size_t size, int is_write)
{
while (size) {
uint64_t page = addr & ELF2DMP_PFN_MASK;
@@ -240,7 +251,7 @@
ptr = va_space_resolve(vs, addr);
if (!ptr) {
- return 1;
+ return false;
}
if (is_write) {
@@ -254,5 +265,5 @@
addr += s;
}
- return 0;
+ return true;
}
diff --git a/contrib/elf2dmp/addrspace.h b/contrib/elf2dmp/addrspace.h
index 039c70c..2ad30a9 100644
--- a/contrib/elf2dmp/addrspace.h
+++ b/contrib/elf2dmp/addrspace.h
@@ -33,13 +33,13 @@
struct pa_space *ps;
};
-int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf);
+void pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf);
void pa_space_destroy(struct pa_space *ps);
void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb);
void va_space_set_dtb(struct va_space *vs, uint64_t dtb);
void *va_space_resolve(struct va_space *vs, uint64_t va);
-int va_space_rw(struct va_space *vs, uint64_t addr,
- void *buf, size_t size, int is_write);
+bool va_space_rw(struct va_space *vs, uint64_t addr,
+ void *buf, size_t size, int is_write);
#endif /* ADDRSPACE_H */
diff --git a/contrib/elf2dmp/download.c b/contrib/elf2dmp/download.c
index bd7650a..21306b3 100644
--- a/contrib/elf2dmp/download.c
+++ b/contrib/elf2dmp/download.c
@@ -9,19 +9,18 @@
#include <curl/curl.h>
#include "download.h"
-int download_url(const char *name, const char *url)
+bool download_url(const char *name, const char *url)
{
- int err = 0;
+ bool success = false;
FILE *file;
CURL *curl = curl_easy_init();
if (!curl) {
- return 1;
+ return false;
}
file = fopen(name, "wb");
if (!file) {
- err = 1;
goto out_curl;
}
@@ -33,13 +32,12 @@
|| curl_easy_perform(curl) != CURLE_OK) {
unlink(name);
fclose(file);
- err = 1;
} else {
- err = fclose(file);
+ success = !fclose(file);
}
out_curl:
curl_easy_cleanup(curl);
- return err;
+ return success;
}
diff --git a/contrib/elf2dmp/download.h b/contrib/elf2dmp/download.h
index 5c27492..f65adb5 100644
--- a/contrib/elf2dmp/download.h
+++ b/contrib/elf2dmp/download.h
@@ -8,6 +8,6 @@
#ifndef DOWNLOAD_H
#define DOWNLOAD_H
-int download_url(const char *name, const char *url);
+bool download_url(const char *name, const char *url);
#endif /* DOWNLOAD_H */
diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c
index cbc38a7..d046a72 100644
--- a/contrib/elf2dmp/main.c
+++ b/contrib/elf2dmp/main.c
@@ -6,6 +6,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/bitops.h"
#include "err.h"
#include "addrspace.h"
@@ -47,11 +48,6 @@
s ? printf(#s" = 0x%016"PRIx64"\n", s) :\
eprintf("Failed to resolve "#s"\n"), s)
-static uint64_t rol(uint64_t x, uint64_t y)
-{
- return (x << y) | (x >> (64 - y));
-}
-
/*
* Decoding algorithm can be found in Volatility project
*/
@@ -64,7 +60,7 @@
uint64_t block;
block = src[i];
- block = rol(block ^ kwn, (uint8_t)kwn);
+ block = rol64(block ^ kwn, kwn);
block = __builtin_bswap64(block ^ kdbe) ^ kwa;
dst[i] = block;
}
@@ -79,9 +75,9 @@
bool decode = false;
uint64_t kwn, kwa, KdpDataBlockEncoded;
- if (va_space_rw(vs,
- KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header),
- &kdbg_hdr, sizeof(kdbg_hdr), 0)) {
+ if (!va_space_rw(vs,
+ KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header),
+ &kdbg_hdr, sizeof(kdbg_hdr), 0)) {
eprintf("Failed to extract KDBG header\n");
return NULL;
}
@@ -97,8 +93,8 @@
return NULL;
}
- if (va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) ||
- va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) {
+ if (!va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) ||
+ !va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) {
return NULL;
}
@@ -122,7 +118,7 @@
kdbg = g_malloc(kdbg_hdr.Size);
- if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) {
+ if (!va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) {
eprintf("Failed to extract entire KDBG\n");
g_free(kdbg);
return NULL;
@@ -186,13 +182,13 @@
* Finds paging-structure hierarchy base,
* if previously set doesn't give access to kernel structures
*/
-static int fix_dtb(struct va_space *vs, QEMU_Elf *qe)
+static bool fix_dtb(struct va_space *vs, QEMU_Elf *qe)
{
/*
* Firstly, test previously set DTB.
*/
if (va_space_resolve(vs, SharedUserData)) {
- return 0;
+ return true;
}
/*
@@ -206,7 +202,7 @@
va_space_set_dtb(vs, s->cr[3]);
printf("DTB 0x%016"PRIx64" has been found from CPU #%zu"
" as system task CR3\n", vs->dtb, i);
- return !(va_space_resolve(vs, SharedUserData));
+ return va_space_resolve(vs, SharedUserData);
}
}
@@ -220,16 +216,16 @@
uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000);
if (!cr3) {
- return 1;
+ return false;
}
va_space_set_dtb(vs, *cr3);
printf("DirectoryTableBase = 0x%016"PRIx64" has been found from CPU #0"
" as interrupt handling CR3\n", vs->dtb);
- return !(va_space_resolve(vs, SharedUserData));
+ return va_space_resolve(vs, SharedUserData);
}
- return 1;
+ return true;
}
static void try_merge_runs(struct pa_space *ps,
@@ -268,9 +264,10 @@
}
}
-static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
- struct va_space *vs, uint64_t KdDebuggerDataBlock,
- KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus)
+static bool fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
+ struct va_space *vs, uint64_t KdDebuggerDataBlock,
+ KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock,
+ int nr_cpus)
{
uint32_t *suite_mask = va_space_resolve(vs, SharedUserData +
KUSD_OFFSET_SUITE_MASK);
@@ -283,12 +280,12 @@
QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= ELF2DMP_PAGE_SIZE);
if (!suite_mask || !product_type) {
- return 1;
+ return false;
}
- if (va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) {
+ if (!va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) {
eprintf("Failed to extract KdVersionBlock\n");
- return 1;
+ return false;
}
h = (WinDumpHeader64) {
@@ -333,11 +330,16 @@
*hdr = h;
- return 0;
+ return true;
}
-static int fill_context(KDDEBUGGER_DATA64 *kdbg,
- struct va_space *vs, QEMU_Elf *qe)
+/*
+ * fill_context() continues even if it fails to fill contexts of some CPUs.
+ * A dump may still contain valuable information even if it lacks contexts of
+ * some CPUs due to dump corruption or a failure before starting CPUs.
+ */
+static void fill_context(KDDEBUGGER_DATA64 *kdbg,
+ struct va_space *vs, QEMU_Elf *qe)
{
int i;
@@ -347,10 +349,10 @@
WinContext64 ctx;
QEMUCPUState *s = qe->state[i];
- if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
- &Prcb, sizeof(Prcb), 0)) {
+ if (!va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
+ &Prcb, sizeof(Prcb), 0)) {
eprintf("Failed to read CPU #%d PRCB location\n", i);
- return 1;
+ continue;
}
if (!Prcb) {
@@ -358,26 +360,24 @@
continue;
}
- if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext,
- &Context, sizeof(Context), 0)) {
+ if (!va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext,
+ &Context, sizeof(Context), 0)) {
eprintf("Failed to read CPU #%d ContextFrame location\n", i);
- return 1;
+ continue;
}
printf("Filling context for CPU #%d...\n", i);
win_context_init_from_qemu_cpu_state(&ctx, s);
- if (va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) {
+ if (!va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) {
eprintf("Failed to fill CPU #%d context\n", i);
- return 1;
+ continue;
}
}
-
- return 0;
}
-static int pe_get_data_dir_entry(uint64_t base, void *start_addr, int idx,
- void *entry, size_t size, struct va_space *vs)
+static bool pe_get_data_dir_entry(uint64_t base, void *start_addr, int idx,
+ void *entry, size_t size, struct va_space *vs)
{
const char e_magic[2] = "MZ";
const char Signature[4] = "PE\0\0";
@@ -390,40 +390,38 @@
QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= ELF2DMP_PAGE_SIZE);
if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) {
- return 1;
+ return false;
}
- if (va_space_rw(vs, base + dos_hdr->e_lfanew,
- &nt_hdrs, sizeof(nt_hdrs), 0)) {
- return 1;
+ if (!va_space_rw(vs, base + dos_hdr->e_lfanew,
+ &nt_hdrs, sizeof(nt_hdrs), 0)) {
+ return false;
}
if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) ||
file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) {
- return 1;
+ return false;
}
- if (va_space_rw(vs,
- base + data_dir[idx].VirtualAddress,
- entry, size, 0)) {
- return 1;
+ if (!va_space_rw(vs, base + data_dir[idx].VirtualAddress, entry, size, 0)) {
+ return false;
}
printf("Data directory entry #%d: RVA = 0x%08"PRIx32"\n", idx,
(uint32_t)data_dir[idx].VirtualAddress);
- return 0;
+ return true;
}
-static int write_dump(struct pa_space *ps,
- WinDumpHeader64 *hdr, const char *name)
+static bool write_dump(struct pa_space *ps,
+ WinDumpHeader64 *hdr, const char *name)
{
FILE *dmp_file = fopen(name, "wb");
size_t i;
if (!dmp_file) {
eprintf("Failed to open output file \'%s\'\n", name);
- return 1;
+ return false;
}
printf("Writing header to file...\n");
@@ -431,7 +429,7 @@
if (fwrite(hdr, sizeof(*hdr), 1, dmp_file) != 1) {
eprintf("Failed to write dump header\n");
fclose(dmp_file);
- return 1;
+ return false;
}
for (i = 0; i < ps->block_nr; i++) {
@@ -442,11 +440,11 @@
if (fwrite(b->addr, b->size, 1, dmp_file) != 1) {
eprintf("Failed to write block\n");
fclose(dmp_file);
- return 1;
+ return false;
}
}
- return fclose(dmp_file);
+ return !fclose(dmp_file);
}
static bool pe_check_pdb_name(uint64_t base, void *start_addr,
@@ -456,8 +454,8 @@
IMAGE_DEBUG_DIRECTORY debug_dir;
char pdb_name[sizeof(PDB_NAME)];
- if (pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_DEBUG_DIRECTORY,
- &debug_dir, sizeof(debug_dir), vs)) {
+ if (!pe_get_data_dir_entry(base, start_addr, IMAGE_FILE_DEBUG_DIRECTORY,
+ &debug_dir, sizeof(debug_dir), vs)) {
eprintf("Failed to get Debug Directory\n");
return false;
}
@@ -467,9 +465,8 @@
return false;
}
- if (va_space_rw(vs,
- base + debug_dir.AddressOfRawData,
- rsds, sizeof(*rsds), 0)) {
+ if (!va_space_rw(vs, base + debug_dir.AddressOfRawData,
+ rsds, sizeof(*rsds), 0)) {
eprintf("Failed to resolve OMFSignatureRSDS\n");
return false;
}
@@ -485,9 +482,9 @@
return false;
}
- if (va_space_rw(vs, base + debug_dir.AddressOfRawData +
- offsetof(OMFSignatureRSDS, name), pdb_name, sizeof(PDB_NAME),
- 0)) {
+ if (!va_space_rw(vs, base + debug_dir.AddressOfRawData +
+ offsetof(OMFSignatureRSDS, name),
+ pdb_name, sizeof(PDB_NAME), 0)) {
eprintf("Failed to resolve PDB name\n");
return false;
}
@@ -511,7 +508,7 @@
int main(int argc, char *argv[])
{
- int err = 0;
+ int err = 1;
QEMU_Elf qemu_elf;
struct pa_space ps;
struct va_space vs;
@@ -535,33 +532,27 @@
return 1;
}
- if (QEMU_Elf_init(&qemu_elf, argv[1])) {
+ if (!QEMU_Elf_init(&qemu_elf, argv[1])) {
eprintf("Failed to initialize QEMU ELF dump\n");
return 1;
}
- if (pa_space_create(&ps, &qemu_elf)) {
- eprintf("Failed to initialize physical address space\n");
- err = 1;
- goto out_elf;
- }
+ pa_space_create(&ps, &qemu_elf);
state = qemu_elf.state[0];
printf("CPU #0 CR3 is 0x%016"PRIx64"\n", state->cr[3]);
va_space_create(&vs, &ps, state->cr[3]);
- if (fix_dtb(&vs, &qemu_elf)) {
+ if (!fix_dtb(&vs, &qemu_elf)) {
eprintf("Failed to find paging base\n");
- err = 1;
- goto out_elf;
+ goto out_ps;
}
printf("CPU #0 IDT is at 0x%016"PRIx64"\n", state->idt.base);
- if (va_space_rw(&vs, state->idt.base,
- &first_idt_desc, sizeof(first_idt_desc), 0)) {
+ if (!va_space_rw(&vs, state->idt.base,
+ &first_idt_desc, sizeof(first_idt_desc), 0)) {
eprintf("Failed to get CPU #0 IDT[0]\n");
- err = 1;
goto out_ps;
}
printf("CPU #0 IDT[0] -> 0x%016"PRIx64"\n", idt_desc_addr(first_idt_desc));
@@ -586,7 +577,6 @@
if (!kernel_found) {
eprintf("Failed to find NT kernel image\n");
- err = 1;
goto out_ps;
}
@@ -598,47 +588,40 @@
sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME);
printf("PDB URL is %s\n", pdb_url);
- if (download_url(PDB_NAME, pdb_url)) {
+ if (!download_url(PDB_NAME, pdb_url)) {
eprintf("Failed to download PDB file\n");
- err = 1;
goto out_ps;
}
- if (pdb_init_from_file(PDB_NAME, &pdb)) {
+ if (!pdb_init_from_file(PDB_NAME, &pdb)) {
eprintf("Failed to initialize PDB reader\n");
- err = 1;
goto out_pdb_file;
}
if (!SYM_RESOLVE(KernBase, &pdb, KdDebuggerDataBlock) ||
!SYM_RESOLVE(KernBase, &pdb, KdVersionBlock)) {
- err = 1;
goto out_pdb;
}
kdbg = get_kdbg(KernBase, &pdb, &vs, KdDebuggerDataBlock);
if (!kdbg) {
- err = 1;
goto out_pdb;
}
- if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg,
- KdVersionBlock, qemu_elf.state_nr)) {
- err = 1;
+ if (!fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg,
+ KdVersionBlock, qemu_elf.state_nr)) {
goto out_kdbg;
}
- if (fill_context(kdbg, &vs, &qemu_elf)) {
- err = 1;
- goto out_kdbg;
- }
+ fill_context(kdbg, &vs, &qemu_elf);
- if (write_dump(&ps, &header, argv[2])) {
+ if (!write_dump(&ps, &header, argv[2])) {
eprintf("Failed to save dump\n");
- err = 1;
goto out_kdbg;
}
+ err = 0;
+
out_kdbg:
g_free(kdbg);
out_pdb:
@@ -647,7 +630,6 @@
unlink(PDB_NAME);
out_ps:
pa_space_destroy(&ps);
-out_elf:
QEMU_Elf_exit(&qemu_elf);
return err;
diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c
index 40991f5..492aca4 100644
--- a/contrib/elf2dmp/pdb.c
+++ b/contrib/elf2dmp/pdb.c
@@ -19,6 +19,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/bswap.h"
#include "pdb.h"
#include "err.h"
@@ -158,36 +159,35 @@
return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
}
-static int pdb_init_segments(struct pdb_reader *r)
+static bool pdb_init_segments(struct pdb_reader *r)
{
unsigned stream_idx = r->segments;
r->segs = pdb_ds_read_file(r, stream_idx);
if (!r->segs) {
- return 1;
+ return false;
}
r->segs_size = pdb_get_file_size(r, stream_idx);
if (!r->segs_size) {
- return 1;
+ return false;
}
- return 0;
+ return true;
}
-static int pdb_init_symbols(struct pdb_reader *r)
+static bool pdb_init_symbols(struct pdb_reader *r)
{
- int err = 0;
PDB_SYMBOLS *symbols;
symbols = pdb_ds_read_file(r, 3);
if (!symbols) {
- return 1;
+ return false;
}
r->symbols = symbols;
- r->segments = *(uint16_t *)((const char *)symbols + sizeof(PDB_SYMBOLS) +
+ r->segments = lduw_le_p((const char *)symbols + sizeof(PDB_SYMBOLS) +
symbols->module_size + symbols->offset_size +
symbols->hash_size + symbols->srcmodule_size +
symbols->pdbimport_size + symbols->unknown2_size +
@@ -196,22 +196,21 @@
/* Read global symbol table */
r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
if (!r->modimage) {
- err = 1;
goto out_symbols;
}
- return 0;
+ return true;
out_symbols:
g_free(symbols);
- return err;
+ return false;
}
-static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
+static bool pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
{
if (hdr->block_size == 0) {
- return 1;
+ return false;
}
memset(r->file_used, 0, sizeof(r->file_used));
@@ -220,42 +219,38 @@
hdr->toc_page * hdr->block_size), hdr->toc_size);
if (!r->ds.toc) {
- return 1;
+ return false;
}
- return 0;
+ return true;
}
-static int pdb_reader_init(struct pdb_reader *r, void *data)
+static bool pdb_reader_init(struct pdb_reader *r, void *data)
{
- int err = 0;
const char pdb7[] = "Microsoft C/C++ MSF 7.00";
if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
- return 1;
+ return false;
}
- if (pdb_reader_ds_init(r, data)) {
- return 1;
+ if (!pdb_reader_ds_init(r, data)) {
+ return false;
}
r->ds.root = pdb_ds_read_file(r, 1);
if (!r->ds.root) {
- err = 1;
goto out_ds;
}
- if (pdb_init_symbols(r)) {
- err = 1;
+ if (!pdb_init_symbols(r)) {
goto out_root;
}
- if (pdb_init_segments(r)) {
- err = 1;
+ if (!pdb_init_segments(r)) {
goto out_sym;
}
- return 0;
+ return true;
out_sym:
pdb_exit_symbols(r);
@@ -264,7 +259,7 @@
out_ds:
pdb_reader_ds_exit(r);
- return err;
+ return false;
}
static void pdb_reader_exit(struct pdb_reader *r)
@@ -275,32 +270,30 @@
pdb_reader_ds_exit(r);
}
-int pdb_init_from_file(const char *name, struct pdb_reader *reader)
+bool pdb_init_from_file(const char *name, struct pdb_reader *reader)
{
GError *gerr = NULL;
- int err = 0;
void *map;
reader->gmf = g_mapped_file_new(name, TRUE, &gerr);
if (gerr) {
eprintf("Failed to map PDB file \'%s\'\n", name);
g_error_free(gerr);
- return 1;
+ return false;
}
reader->file_size = g_mapped_file_get_length(reader->gmf);
map = g_mapped_file_get_contents(reader->gmf);
- if (pdb_reader_init(reader, map)) {
- err = 1;
+ if (!pdb_reader_init(reader, map)) {
goto out_unmap;
}
- return 0;
+ return true;
out_unmap:
g_mapped_file_unref(reader->gmf);
- return err;
+ return false;
}
void pdb_exit(struct pdb_reader *reader)
diff --git a/contrib/elf2dmp/pdb.h b/contrib/elf2dmp/pdb.h
index 2a50da5..feddf18 100644
--- a/contrib/elf2dmp/pdb.h
+++ b/contrib/elf2dmp/pdb.h
@@ -233,7 +233,7 @@
size_t segs_size;
};
-int pdb_init_from_file(const char *name, struct pdb_reader *reader);
+bool pdb_init_from_file(const char *name, struct pdb_reader *reader);
void pdb_exit(struct pdb_reader *reader);
uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name);
uint64_t pdb_find_public_v3_symbol(struct pdb_reader *reader, const char *name);
diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c
index 055e6f8..c9bad6e 100644
--- a/contrib/elf2dmp/qemu_elf.c
+++ b/contrib/elf2dmp/qemu_elf.c
@@ -6,6 +6,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/host-utils.h"
#include "err.h"
#include "qemu_elf.h"
@@ -15,36 +16,11 @@
#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
#endif
-#ifndef DIV_ROUND_UP
-#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
-#endif
-
-#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \
- ((DIV_ROUND_UP((hdr_size), 4) + \
- DIV_ROUND_UP((name_size), 4) + \
- DIV_ROUND_UP((desc_size), 4)) * 4)
-
int is_system(QEMUCPUState *s)
{
return s->gs.base >> 63;
}
-static char *nhdr_get_name(Elf64_Nhdr *nhdr)
-{
- return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4);
-}
-
-static void *nhdr_get_desc(Elf64_Nhdr *nhdr)
-{
- return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4);
-}
-
-static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr)
-{
- return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr),
- nhdr->n_namesz, nhdr->n_descsz));
-}
-
Elf64_Phdr *elf64_getphdr(void *map)
{
Elf64_Ehdr *ehdr = map;
@@ -60,54 +36,92 @@
return ehdr->e_phnum;
}
-static int init_states(QEMU_Elf *qe)
+static bool advance_note_offset(uint64_t *offsetp, uint64_t size, uint64_t end)
+{
+ uint64_t offset = *offsetp;
+
+ if (uadd64_overflow(offset, size, &offset) || offset > UINT64_MAX - 3) {
+ return false;
+ }
+
+ offset = ROUND_UP(offset, 4);
+
+ if (offset > end) {
+ return false;
+ }
+
+ *offsetp = offset;
+
+ return true;
+}
+
+static bool init_states(QEMU_Elf *qe)
{
Elf64_Phdr *phdr = elf64_getphdr(qe->map);
- Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset);
- Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz);
Elf64_Nhdr *nhdr;
- size_t cpu_nr = 0;
+ GPtrArray *states;
+ QEMUCPUState *state;
+ uint32_t state_size;
+ uint64_t offset;
+ uint64_t end_offset;
+ char *name;
if (phdr[0].p_type != PT_NOTE) {
eprintf("Failed to find PT_NOTE\n");
- return 1;
+ return false;
}
qe->has_kernel_gs_base = 1;
+ offset = phdr[0].p_offset;
+ states = g_ptr_array_new();
- for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
- if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
- QEMUCPUState *state = nhdr_get_desc(nhdr);
+ if (uadd64_overflow(offset, phdr[0].p_memsz, &end_offset) ||
+ end_offset > qe->size) {
+ end_offset = qe->size;
+ }
- if (state->size < sizeof(*state)) {
- eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n",
- cpu_nr, state->size);
+ while (offset < end_offset) {
+ nhdr = (void *)((uint8_t *)qe->map + offset);
+
+ if (!advance_note_offset(&offset, sizeof(*nhdr), end_offset)) {
+ break;
+ }
+
+ name = (char *)qe->map + offset;
+
+ if (!advance_note_offset(&offset, nhdr->n_namesz, end_offset)) {
+ break;
+ }
+
+ state = (void *)((uint8_t *)qe->map + offset);
+
+ if (!advance_note_offset(&offset, nhdr->n_descsz, end_offset)) {
+ break;
+ }
+
+ if (!strcmp(name, QEMU_NOTE_NAME) &&
+ nhdr->n_descsz >= offsetof(QEMUCPUState, kernel_gs_base)) {
+ state_size = MIN(state->size, nhdr->n_descsz);
+
+ if (state_size < sizeof(*state)) {
+ eprintf("CPU #%u: QEMU CPU state size %u doesn't match\n",
+ states->len, state_size);
/*
* We assume either every QEMU CPU state has KERNEL_GS_BASE or
* no one has.
*/
qe->has_kernel_gs_base = 0;
}
- cpu_nr++;
+ g_ptr_array_add(states, state);
}
}
- printf("%zu CPU states has been found\n", cpu_nr);
+ printf("%u CPU states has been found\n", states->len);
- qe->state = g_new(QEMUCPUState*, cpu_nr);
+ qe->state_nr = states->len;
+ qe->state = (void *)g_ptr_array_free(states, FALSE);
- cpu_nr = 0;
-
- for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
- if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
- qe->state[cpu_nr] = nhdr_get_desc(nhdr);
- cpu_nr++;
- }
- }
-
- qe->state_nr = cpu_nr;
-
- return 0;
+ return true;
}
static void exit_states(QEMU_Elf *qe)
@@ -118,6 +132,7 @@
static bool check_ehdr(QEMU_Elf *qe)
{
Elf64_Ehdr *ehdr = qe->map;
+ uint64_t phendoff;
if (sizeof(Elf64_Ehdr) > qe->size) {
eprintf("Invalid input dump file size\n");
@@ -159,10 +174,17 @@
return false;
}
+ if (umul64_overflow(ehdr->e_phnum, sizeof(Elf64_Phdr), &phendoff) ||
+ uadd64_overflow(phendoff, ehdr->e_phoff, &phendoff) ||
+ phendoff > qe->size) {
+ eprintf("phdrs do not fit in file\n");
+ return false;
+ }
+
return true;
}
-static int QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
+static bool QEMU_Elf_map(QEMU_Elf *qe, const char *filename)
{
#ifdef CONFIG_LINUX
struct stat st;
@@ -173,13 +195,13 @@
fd = open(filename, O_RDONLY, 0);
if (fd == -1) {
eprintf("Failed to open ELF dump file \'%s\'\n", filename);
- return 1;
+ return false;
}
if (fstat(fd, &st)) {
eprintf("Failed to get size of ELF dump file\n");
close(fd);
- return 1;
+ return false;
}
qe->size = st.st_size;
@@ -188,7 +210,7 @@
if (qe->map == MAP_FAILED) {
eprintf("Failed to map ELF file\n");
close(fd);
- return 1;
+ return false;
}
close(fd);
@@ -201,14 +223,14 @@
if (gerr) {
eprintf("Failed to map ELF dump file \'%s\'\n", filename);
g_error_free(gerr);
- return 1;
+ return false;
}
qe->map = g_mapped_file_get_contents(qe->gmf);
qe->size = g_mapped_file_get_length(qe->gmf);
#endif
- return 0;
+ return true;
}
static void QEMU_Elf_unmap(QEMU_Elf *qe)
@@ -220,25 +242,25 @@
#endif
}
-int QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
+bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
{
- if (QEMU_Elf_map(qe, filename)) {
- return 1;
+ if (!QEMU_Elf_map(qe, filename)) {
+ return false;
}
if (!check_ehdr(qe)) {
eprintf("Input file has the wrong format\n");
QEMU_Elf_unmap(qe);
- return 1;
+ return false;
}
- if (init_states(qe)) {
+ if (!init_states(qe)) {
eprintf("Failed to extract QEMU CPU states\n");
QEMU_Elf_unmap(qe);
- return 1;
+ return false;
}
- return 0;
+ return true;
}
void QEMU_Elf_exit(QEMU_Elf *qe)
diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h
index afa75f1..adc5023 100644
--- a/contrib/elf2dmp/qemu_elf.h
+++ b/contrib/elf2dmp/qemu_elf.h
@@ -42,7 +42,7 @@
int has_kernel_gs_base;
} QEMU_Elf;
-int QEMU_Elf_init(QEMU_Elf *qe, const char *filename);
+bool QEMU_Elf_init(QEMU_Elf *qe, const char *filename);
void QEMU_Elf_exit(QEMU_Elf *qe);
Elf64_Phdr *elf64_getphdr(void *map);
diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst
index dfd681c..4fb8660 100644
--- a/docs/about/deprecated.rst
+++ b/docs/about/deprecated.rst
@@ -47,16 +47,6 @@
However, short-form booleans are deprecated and full explicit ``arg_name=on``
form is preferred.
-User-mode emulator command line arguments
------------------------------------------
-
-``-p`` (since 9.0)
-''''''''''''''''''
-
-The ``-p`` option pretends to control the host page size. However,
-it is not possible to change the host page size, and using the
-option only causes failures.
-
``-smp`` (Unsupported "parameter=1" SMP configurations) (since 9.0)
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
@@ -71,6 +61,16 @@
marked deprecated since 9.0, users have to ensure that all the topology members
described with -smp are supported by the target machine.
+User-mode emulator command line arguments
+-----------------------------------------
+
+``-p`` (since 9.0)
+''''''''''''''''''
+
+The ``-p`` option pretends to control the host page size. However,
+it is not possible to change the host page size, and using the
+option only causes failures.
+
QEMU Machine Protocol (QMP) commands
------------------------------------
@@ -245,6 +245,21 @@
no longer maintained and removing them will make the code easier to
read and maintain. Use versions 2.12 and above as a replacement.
+Arm machines ``akita``, ``borzoi``, ``cheetah``, ``connex``, ``mainstone``, ``n800``, ``n810``, ``spitz``, ``terrier``, ``tosa``, ``verdex``, ``z2`` (since 9.0)
+''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+QEMU includes models of some machine types where the QEMU code that
+emulates their SoCs is very old and unmaintained. This code is now
+blocking our ability to move forward with various changes across
+the codebase, and over many years nobody has been interested in
+trying to modernise it. We don't expect any of these machines to have
+a large number of users, because they're all modelling hardware that
+has now passed away into history. We are therefore dropping support
+for all machine types using the PXA2xx and OMAP2 SoCs. We are also
+dropping the ``cheetah`` OMAP1 board, because we don't have any
+test images for it and don't know of anybody who does; the ``sx1``
+and ``sx1-v1`` OMAP1 machines remain supported for now.
+
Backend options
---------------
diff --git a/docs/conf.py b/docs/conf.py
index 1b2afa2..aae0304 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -88,7 +88,7 @@
# General information about the project.
project = u'QEMU'
-copyright = u'2023, The QEMU Project Developers'
+copyright = u'2024, The QEMU Project Developers'
author = u'The QEMU Project Developers'
# The version info for the project you're documenting, acts as replacement for
diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst
index 8024275..54385a2 100644
--- a/docs/devel/migration/main.rst
+++ b/docs/devel/migration/main.rst
@@ -44,7 +44,8 @@
- file migration: do the migration using a file that is passed to QEMU
by path. A file offset option is supported to allow a management
application to add its own metadata to the start of the file without
- QEMU interference.
+ QEMU interference. Note that QEMU does not flush cached file
+ data/metadata at the end of migration.
In addition, support is included for migration using RDMA, which
transports the page data using ``RDMA``, where the hardware takes care of
diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c
index d5de540..9146269 100644
--- a/hw/arm/gumstix.c
+++ b/hw/arm/gumstix.c
@@ -106,6 +106,7 @@
mc->desc = "Gumstix Connex (PXA255)";
mc->init = connex_init;
mc->ignore_memory_transaction_failures = true;
+ mc->deprecation_reason = "machine is old and unmaintained";
}
static const TypeInfo connex_type = {
@@ -121,6 +122,7 @@
mc->desc = "Gumstix Verdex Pro XL6P COMs (PXA270)";
mc->init = verdex_init;
mc->ignore_memory_transaction_failures = true;
+ mc->deprecation_reason = "machine is old and unmaintained";
mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c0");
}
diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c
index d2e2e68..3a6c22f 100644
--- a/hw/arm/mainstone.c
+++ b/hw/arm/mainstone.c
@@ -169,6 +169,7 @@
mc->init = mainstone_init;
mc->ignore_memory_transaction_failures = true;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c5");
+ mc->deprecation_reason = "machine is old and unmaintained";
}
DEFINE_MACHINE("mainstone", mainstone2_machine_init)
diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c
index 35deb74..3536431 100644
--- a/hw/arm/nseries.c
+++ b/hw/arm/nseries.c
@@ -1430,6 +1430,7 @@
/* Actually two chips of 0x4000000 bytes each */
mc->default_ram_size = 0x08000000;
mc->default_ram_id = "omap2.dram";
+ mc->deprecation_reason = "machine is old and unmaintained";
machine_add_audiodev_property(mc);
}
@@ -1452,6 +1453,7 @@
/* Actually two chips of 0x4000000 bytes each */
mc->default_ram_size = 0x08000000;
mc->default_ram_id = "omap2.dram";
+ mc->deprecation_reason = "machine is old and unmaintained";
machine_add_audiodev_property(mc);
}
diff --git a/hw/arm/palm.c b/hw/arm/palm.c
index 8c4c831..e04ac92 100644
--- a/hw/arm/palm.c
+++ b/hw/arm/palm.c
@@ -309,6 +309,7 @@
mc->default_cpu_type = ARM_CPU_TYPE_NAME("ti925t");
mc->default_ram_size = 0x02000000;
mc->default_ram_id = "omap1.dram";
+ mc->deprecation_reason = "machine is old and unmaintained";
machine_add_audiodev_property(mc);
}
diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c
index 643a02b..62cd55b 100644
--- a/hw/arm/spitz.c
+++ b/hw/arm/spitz.c
@@ -1041,6 +1041,7 @@
mc->block_default_type = IF_IDE;
mc->ignore_memory_transaction_failures = true;
mc->init = spitz_common_init;
+ mc->deprecation_reason = "machine is old and unmaintained";
machine_add_audiodev_property(mc);
}
diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c
index 3ca2e44..5891f60 100644
--- a/hw/arm/tosa.c
+++ b/hw/arm/tosa.c
@@ -270,6 +270,7 @@
mc->init = tosa_init;
mc->block_default_type = IF_IDE;
mc->ignore_memory_transaction_failures = true;
+ mc->deprecation_reason = "machine is old and unmaintained";
}
DEFINE_MACHINE("tosa", tosapda_machine_init)
diff --git a/hw/arm/z2.c b/hw/arm/z2.c
index eb2ff8d..fc5672e 100644
--- a/hw/arm/z2.c
+++ b/hw/arm/z2.c
@@ -347,6 +347,7 @@
mc->init = z2_init;
mc->ignore_memory_transaction_failures = true;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("pxa270-c5");
+ mc->deprecation_reason = "machine is old and unmaintained";
machine_add_audiodev_property(mc);
}
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index 0108fb1..4bd9c70 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -22,14 +22,10 @@
#include "qapi/error.h"
#include "hw/core/cpu.h"
#include "sysemu/hw_accel.h"
-#include "qemu/notify.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
#include "exec/log.h"
-#include "exec/cpu-common.h"
#include "exec/gdbstub.h"
-#include "qemu/error-report.h"
-#include "qemu/qemu-print.h"
#include "sysemu/tcg.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c
index b7c7b3b..9f20007 100644
--- a/hw/core/loader-fit.c
+++ b/hw/core/loader-fit.c
@@ -120,6 +120,7 @@
int cfg, void *opaque, hwaddr *pend,
Error **errp)
{
+ ERRP_GUARD();
const char *name;
const void *data;
const void *load_data;
@@ -178,6 +179,7 @@
int cfg, void *opaque, const void *match_data,
hwaddr kernel_end, Error **errp)
{
+ ERRP_GUARD();
Error *err = NULL;
const char *name;
const void *data;
diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c
index 3860a50..4b72009 100644
--- a/hw/core/machine-qmp-cmds.c
+++ b/hw/core/machine-qmp-cmds.c
@@ -19,7 +19,6 @@
#include "qapi/qmp/qobject.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/type-helpers.h"
-#include "qemu/main-loop.h"
#include "qemu/uuid.h"
#include "qom/qom-qobject.h"
#include "sysemu/hostmem.h"
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 9ac5d53..0e9d646 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -32,7 +32,9 @@
#include "hw/virtio/virtio-net.h"
#include "audio/audio.h"
-GlobalProperty hw_compat_8_2[] = {};
+GlobalProperty hw_compat_8_2[] = {
+ { "migration", "zero-page-detection", "legacy"},
+};
const size_t hw_compat_8_2_len = G_N_ELEMENTS(hw_compat_8_2);
GlobalProperty hw_compat_8_1[] = {
diff --git a/hw/core/numa.c b/hw/core/numa.c
index f08956d..81d2124 100644
--- a/hw/core/numa.c
+++ b/hw/core/numa.c
@@ -28,7 +28,6 @@
#include "sysemu/numa.h"
#include "exec/cpu-common.h"
#include "exec/ramlist.h"
-#include "qemu/bitmap.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qapi/opts-visitor.h"
@@ -36,7 +35,6 @@
#include "sysemu/qtest.h"
#include "hw/core/cpu.h"
#include "hw/mem/pc-dimm.h"
-#include "migration/vmstate.h"
#include "hw/boards.h"
#include "hw/mem/memory-device.h"
#include "qemu/option.h"
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index b45e90e..f52073b 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -242,6 +242,7 @@
static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
Error **errp)
{
+ ERRP_GUARD();
Property *prop = opaque;
CharBackend *be = object_field_prop_ptr(obj, prop);
Chardev *s;
@@ -693,6 +694,16 @@
.set_default_value = qdev_propinfo_set_default_value_enum,
};
+const PropertyInfo qdev_prop_zero_page_detection = {
+ .name = "ZeroPageDetection",
+ .description = "zero_page_detection values, "
+ "none,legacy,multifd",
+ .enum_table = &ZeroPageDetection_lookup,
+ .get = qdev_propinfo_get_enum,
+ .set = qdev_propinfo_set_enum,
+ .set_default_value = qdev_propinfo_set_default_value_enum,
+};
+
/* --- Reserved Region --- */
/*
diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
index 712940b..19c97cc 100644
--- a/hw/gpio/Kconfig
+++ b/hw/gpio/Kconfig
@@ -19,3 +19,7 @@
config STM32L4X5_GPIO
bool
+
+config PCF8574
+ bool
+ depends on I2C
diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
index 3454b50..791e93a 100644
--- a/hw/gpio/meson.build
+++ b/hw/gpio/meson.build
@@ -16,3 +16,4 @@
system_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_gpio.c'))
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_gpio.c'))
system_ss.add(when: 'CONFIG_SIFIVE_GPIO', if_true: files('sifive_gpio.c'))
+system_ss.add(when: 'CONFIG_PCF8574', if_true: files('pcf8574.c'))
diff --git a/hw/gpio/pcf8574.c b/hw/gpio/pcf8574.c
new file mode 100644
index 0000000..d37909e
--- /dev/null
+++ b/hw/gpio/pcf8574.c
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * NXP PCF8574 8-port I2C GPIO expansion chip.
+ * Copyright (c) 2024 KNS Group (YADRO).
+ * Written by Dmitrii Sharikhin <d.sharikhin@yadro.com>
+ */
+
+#include "qemu/osdep.h"
+#include "hw/i2c/i2c.h"
+#include "hw/gpio/pcf8574.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qom/object.h"
+
+/*
+ * PCF8574 and compatible chips incorporate quasi-bidirectional
+ * IO. Electrically it means that device sustain pull-up to line
+ * unless IO port is configured as output _and_ driven low.
+ *
+ * IO access is implemented as simple I2C single-byte read
+ * or write operation. So, to configure line to input user write 1
+ * to corresponding bit. To configure line to output and drive it low
+ * user write 0 to corresponding bit.
+ *
+ * In essence, user can think of quasi-bidirectional IO as
+ * open-drain line, except presence of builtin rising edge acceleration
+ * embedded in PCF8574 IC
+ *
+ * PCF8574 has interrupt request line, which is being pulled down when
+ * port line state differs from last read. Port read operation clears
+ * state and INT line returns to high state via pullup.
+ */
+
+OBJECT_DECLARE_SIMPLE_TYPE(PCF8574State, PCF8574)
+
+#define PORTS_COUNT (8)
+
+struct PCF8574State {
+ I2CSlave parent_obj;
+ uint8_t lastrq; /* Last requested state. If changed - assert irq */
+ uint8_t input; /* external electrical line state */
+ uint8_t output; /* Pull-up (1) or drive low (0) on bit */
+ qemu_irq handler[PORTS_COUNT];
+ qemu_irq intrq; /* External irq request */
+};
+
+static void pcf8574_reset(DeviceState *dev)
+{
+ PCF8574State *s = PCF8574(dev);
+ s->lastrq = MAKE_64BIT_MASK(0, PORTS_COUNT);
+ s->input = MAKE_64BIT_MASK(0, PORTS_COUNT);
+ s->output = MAKE_64BIT_MASK(0, PORTS_COUNT);
+}
+
+static inline uint8_t pcf8574_line_state(PCF8574State *s)
+{
+ /* we driving line low or external circuit does that */
+ return s->input & s->output;
+}
+
+static uint8_t pcf8574_rx(I2CSlave *i2c)
+{
+ PCF8574State *s = PCF8574(i2c);
+ uint8_t linestate = pcf8574_line_state(s);
+ if (s->lastrq != linestate) {
+ s->lastrq = linestate;
+ if (s->intrq) {
+ qemu_set_irq(s->intrq, 1);
+ }
+ }
+ return linestate;
+}
+
+static int pcf8574_tx(I2CSlave *i2c, uint8_t data)
+{
+ PCF8574State *s = PCF8574(i2c);
+ uint8_t prev;
+ uint8_t diff;
+ uint8_t actual;
+ int line = 0;
+
+ prev = pcf8574_line_state(s);
+ s->output = data;
+ actual = pcf8574_line_state(s);
+
+ for (diff = (actual ^ prev); diff; diff &= ~(1 << line)) {
+ line = ctz32(diff);
+ if (s->handler[line]) {
+ qemu_set_irq(s->handler[line], (actual >> line) & 1);
+ }
+ }
+
+ if (s->intrq) {
+ qemu_set_irq(s->intrq, actual == s->lastrq);
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_pcf8574 = {
+ .name = "pcf8574",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_I2C_SLAVE(parent_obj, PCF8574State),
+ VMSTATE_UINT8(lastrq, PCF8574State),
+ VMSTATE_UINT8(input, PCF8574State),
+ VMSTATE_UINT8(output, PCF8574State),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pcf8574_gpio_set(void *opaque, int line, int level)
+{
+ PCF8574State *s = (PCF8574State *) opaque;
+ assert(line >= 0 && line < ARRAY_SIZE(s->handler));
+
+ if (level) {
+ s->input |= (1 << line);
+ } else {
+ s->input &= ~(1 << line);
+ }
+
+ if (pcf8574_line_state(s) != s->lastrq && s->intrq) {
+ qemu_set_irq(s->intrq, 0);
+ }
+}
+
+static void pcf8574_realize(DeviceState *dev, Error **errp)
+{
+ PCF8574State *s = PCF8574(dev);
+
+ qdev_init_gpio_in(dev, pcf8574_gpio_set, ARRAY_SIZE(s->handler));
+ qdev_init_gpio_out(dev, s->handler, ARRAY_SIZE(s->handler));
+ qdev_init_gpio_out_named(dev, &s->intrq, "nINT", 1);
+}
+
+static void pcf8574_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
+
+ k->recv = pcf8574_rx;
+ k->send = pcf8574_tx;
+ dc->realize = pcf8574_realize;
+ dc->reset = pcf8574_reset;
+ dc->vmsd = &vmstate_pcf8574;
+}
+
+static const TypeInfo pcf8574_infos[] = {
+ {
+ .name = TYPE_PCF8574,
+ .parent = TYPE_I2C_SLAVE,
+ .instance_size = sizeof(PCF8574State),
+ .class_init = pcf8574_class_init,
+ }
+};
+
+DEFINE_TYPES(pcf8574_infos);
diff --git a/hw/ide/ahci_internal.h b/hw/ide/ahci-internal.h
similarity index 100%
rename from hw/ide/ahci_internal.h
rename to hw/ide/ahci-internal.h
diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c
index b8123bc..bfefad2 100644
--- a/hw/ide/ahci.c
+++ b/hw/ide/ahci.c
@@ -37,7 +37,7 @@
#include "hw/ide/pci.h"
#include "hw/ide/ahci-pci.h"
#include "hw/ide/ahci-sysbus.h"
-#include "ahci_internal.h"
+#include "ahci-internal.h"
#include "ide-internal.h"
#include "trace.h"
diff --git a/hw/ide/ich.c b/hw/ide/ich.c
index 3ea793d..9b909c8 100644
--- a/hw/ide/ich.c
+++ b/hw/ide/ich.c
@@ -70,7 +70,7 @@
#include "sysemu/dma.h"
#include "hw/ide/pci.h"
#include "hw/ide/ahci-pci.h"
-#include "ahci_internal.h"
+#include "ahci-internal.h"
#define ICH9_MSI_CAP_OFFSET 0x80
#define ICH9_SATA_CAP_OFFSET 0xA8
diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c
index a2fd0bc..de49d1b 100644
--- a/hw/misc/ivshmem.c
+++ b/hw/misc/ivshmem.c
@@ -832,6 +832,7 @@
static void ivshmem_common_realize(PCIDevice *dev, Error **errp)
{
+ ERRP_GUARD();
IVShmemState *s = IVSHMEM_COMMON(dev);
Error *err = NULL;
uint8_t *pci_conf;
diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c
index 6db0cf6..f56079a 100644
--- a/hw/pci/pcie.c
+++ b/hw/pci/pcie.c
@@ -28,6 +28,7 @@
#include "hw/pci/pcie_regs.h"
#include "hw/pci/pcie_port.h"
#include "qemu/range.h"
+#include "trace.h"
//#define DEBUG_PCIE
#ifdef DEBUG_PCIE
@@ -45,6 +46,23 @@
&& (sltctl & PCI_EXP_SLTCTL_PIC) == PCI_EXP_SLTCTL_PWR_IND_OFF;
}
+static const char *pcie_led_state_to_str(uint16_t value)
+{
+ switch (value) {
+ case PCI_EXP_SLTCTL_PWR_IND_ON:
+ case PCI_EXP_SLTCTL_ATTN_IND_ON:
+ return "on";
+ case PCI_EXP_SLTCTL_PWR_IND_BLINK:
+ case PCI_EXP_SLTCTL_ATTN_IND_BLINK:
+ return "blink";
+ case PCI_EXP_SLTCTL_PWR_IND_OFF:
+ case PCI_EXP_SLTCTL_ATTN_IND_OFF:
+ return "off";
+ default:
+ return "invalid";
+ }
+}
+
/***************************************************************************
* pci express capability helper functions
*/
@@ -735,6 +753,28 @@
*slt_sta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
}
+static void find_child_fn(PCIBus *bus, PCIDevice *dev, void *opaque)
+{
+ PCIDevice **child = opaque;
+
+ if (!*child) {
+ *child = dev;
+ }
+}
+
+/*
+ * Returns the plugged device or first function of multifunction plugged device
+ */
+static PCIDevice *pcie_cap_slot_find_child(PCIDevice *dev)
+{
+ PCIBus *sec_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(dev));
+ PCIDevice *child = NULL;
+
+ pci_for_each_device(sec_bus, pci_bus_num(sec_bus), find_child_fn, &child);
+
+ return child;
+}
+
void pcie_cap_slot_write_config(PCIDevice *dev,
uint16_t old_slt_ctl, uint16_t old_slt_sta,
uint32_t addr, uint32_t val, int len)
@@ -779,6 +819,22 @@
sltsta);
}
+ if (trace_event_get_state_backends(TRACE_PCIE_CAP_SLOT_WRITE_CONFIG)) {
+ DeviceState *parent = DEVICE(dev);
+ DeviceState *child = DEVICE(pcie_cap_slot_find_child(dev));
+
+ trace_pcie_cap_slot_write_config(
+ parent->canonical_path,
+ child ? child->canonical_path : "no-child",
+ (sltsta & PCI_EXP_SLTSTA_PDS) ? "present" : "not present",
+ pcie_led_state_to_str(old_slt_ctl & PCI_EXP_SLTCTL_PIC),
+ pcie_led_state_to_str(val & PCI_EXP_SLTCTL_PIC),
+ pcie_led_state_to_str(old_slt_ctl & PCI_EXP_SLTCTL_AIC),
+ pcie_led_state_to_str(val & PCI_EXP_SLTCTL_AIC),
+ (old_slt_ctl & PCI_EXP_SLTCTL_PWR_OFF) ? "off" : "on",
+ (val & PCI_EXP_SLTCTL_PWR_OFF) ? "off" : "on");
+ }
+
/*
* If the slot is populated, power indicator is off and power
* controller is off, it is safe to detach the devices.
diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c
index d2a5eea..aac6f2d 100644
--- a/hw/pci/shpc.c
+++ b/hw/pci/shpc.c
@@ -8,6 +8,7 @@
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/msi.h"
+#include "trace.h"
/* TODO: model power only and disabled slot states. */
/* TODO: handle SERR and wakeups */
@@ -123,6 +124,34 @@
#define SHPC_PCI_TO_IDX(pci_slot) ((pci_slot) - 1)
#define SHPC_IDX_TO_PHYSICAL(slot) ((slot) + 1)
+static const char *shpc_led_state_to_str(uint8_t value)
+{
+ switch (value) {
+ case SHPC_LED_ON:
+ return "on";
+ case SHPC_LED_BLINK:
+ return "blink";
+ case SHPC_LED_OFF:
+ return "off";
+ default:
+ return "invalid";
+ }
+}
+
+static const char *shpc_slot_state_to_str(uint8_t value)
+{
+ switch (value) {
+ case SHPC_STATE_PWRONLY:
+ return "power-only";
+ case SHPC_STATE_ENABLED:
+ return "enabled";
+ case SHPC_STATE_DISABLED:
+ return "disabled";
+ default:
+ return "invalid";
+ }
+}
+
static uint8_t shpc_get_status(SHPCDevice *shpc, int slot, uint16_t msk)
{
uint8_t *status = shpc->config + SHPC_SLOT_STATUS(slot);
@@ -302,6 +331,23 @@
shpc_set_status(shpc, slot, state, SHPC_SLOT_STATE_MASK);
}
+ if (trace_event_get_state_backends(TRACE_SHPC_SLOT_COMMAND)) {
+ DeviceState *parent = DEVICE(d);
+ int pci_slot = SHPC_IDX_TO_PCI(slot);
+ DeviceState *child =
+ DEVICE(shpc->sec_bus->devices[PCI_DEVFN(pci_slot, 0)]);
+
+ trace_shpc_slot_command(
+ parent->canonical_path, pci_slot,
+ child ? child->canonical_path : "no-child",
+ shpc_led_state_to_str(old_power),
+ shpc_led_state_to_str(power),
+ shpc_led_state_to_str(old_attn),
+ shpc_led_state_to_str(attn),
+ shpc_slot_state_to_str(old_state),
+ shpc_slot_state_to_str(state));
+ }
+
if (!shpc_slot_is_off(old_state, old_power, old_attn) &&
shpc_slot_is_off(state, power, attn))
{
diff --git a/hw/pci/trace-events b/hw/pci/trace-events
index 4243086..19643aa 100644
--- a/hw/pci/trace-events
+++ b/hw/pci/trace-events
@@ -16,3 +16,9 @@
sriov_register_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: creating %d vf devs"
sriov_unregister_vfs(const char *name, int slot, int function, int num_vfs) "%s %02x:%x: Unregistering %d vf devs"
sriov_config_write(const char *name, int slot, int fun, uint32_t offset, uint32_t val, uint32_t len) "%s %02x:%x: sriov offset 0x%x val 0x%x len %d"
+
+# pcie.c
+pcie_cap_slot_write_config(const char *parent, const char *child, const char *pds, const char *old_pic, const char *new_pic, const char *old_aic, const char *new_aic, const char *old_power, const char *new_power) "%s > %s: pds: %s, pic: %s->%s, aic: %s->%s, power: %s->%s"
+
+# shpc.c
+shpc_slot_command(const char *parent, int pci_slot, const char *child, const char *old_pic, const char *new_pic, const char *old_aic, const char *new_aic, const char *old_state, const char *new_state) "%s[%d] > %s: pic: %s->%s, aic: %s->%s, state: %s->%s"
diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c
index 7e34b6c..d42b677 100644
--- a/hw/ppc/sam460ex.c
+++ b/hw/ppc/sam460ex.c
@@ -33,6 +33,7 @@
#include "hw/char/serial.h"
#include "hw/i2c/ppc4xx_i2c.h"
#include "hw/i2c/smbus_eeprom.h"
+#include "hw/ide/pci.h"
#include "hw/usb/hcd-ehci.h"
#include "hw/ppc/fdt.h"
#include "hw/qdev-properties.h"
@@ -449,15 +450,27 @@
/* PCI devices */
pci_create_simple(pci_bus, PCI_DEVFN(6, 0), "sm501");
- /* SoC has a single SATA port but we don't emulate that yet
+ /*
+ * SoC has a single SATA port but we don't emulate that
* However, firmware and usual clients have driver for SiI311x
- * so add one for convenience by default */
+ * PCI SATA card so add one for convenience by default
+ */
if (defaults_enabled()) {
- pci_create_simple(pci_bus, -1, "sii3112");
+ PCIIDEState *s = PCI_IDE(pci_create_simple(pci_bus, -1, "sii3112"));
+ DriveInfo *di;
+
+ di = drive_get_by_index(IF_IDE, 0);
+ if (di) {
+ ide_bus_create_drive(&s->bus[0], 0, di);
+ }
+ /* Use index 2 only if 1 does not exist, this allows -cdrom */
+ di = drive_get_by_index(IF_IDE, 1) ?: drive_get_by_index(IF_IDE, 2);
+ if (di) {
+ ide_bus_create_drive(&s->bus[1], 0, di);
+ }
}
- /* SoC has 4 UARTs
- * but board has only one wired and two are present in fdt */
+ /* SoC has 4 UARTs but board has only one wired and two described in fdt */
if (serial_hd(0) != NULL) {
serial_mm_init(get_system_memory(), 0x4ef600300, 0,
qdev_get_gpio_in(uic[1], 1),
@@ -531,6 +544,7 @@
{
mc->desc = "aCube Sam460ex";
mc->init = sam460ex_init;
+ mc->block_default_type = IF_IDE;
mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("460exb");
mc->default_ram_size = 512 * MiB;
mc->default_ram_id = "ppc4xx.sdram";
diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c
index eda9b58..cff6d5a 100644
--- a/hw/sparc64/sun4u.c
+++ b/hw/sparc64/sun4u.c
@@ -360,8 +360,13 @@
pci_dev->config[0x09] = 0x00; // programming i/f
pci_dev->config[0x0D] = 0x0a; // latency_timer
- memory_region_init_alias(&s->bar0, OBJECT(s), "bar0",
- pci_address_space_io(pci_dev), 0, 0x1000000);
+ /*
+ * BAR0 is accessed by OpenBSD but not for ebus device access: allow any
+ * memory access to this region to succeed which allows the OpenBSD kernel
+ * to boot.
+ */
+ memory_region_init_io(&s->bar0, OBJECT(s), &unassigned_io_ops, s,
+ "bar0", 0x1000000);
pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar0);
memory_region_init_alias(&s->bar1, OBJECT(s), "bar1",
pci_address_space_io(pci_dev), 0, 0x8000);
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index ff88c3f..011ceaa 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -39,7 +39,6 @@
#include "sysemu/runstate.h"
#include "trace.h"
#include "qapi/error.h"
-#include "migration/migration.h"
#include "migration/misc.h"
#include "migration/blocker.h"
#include "migration/qemu-file.h"
@@ -150,14 +149,8 @@
static void vfio_set_migration_error(int err)
{
- MigrationState *ms = migrate_get_current();
-
- if (migration_is_setup_or_active(ms->state)) {
- WITH_QEMU_LOCK_GUARD(&ms->qemu_file_lock) {
- if (ms->to_dst_file) {
- qemu_file_set_error(ms->to_dst_file, err);
- }
- }
+ if (migration_is_setup_or_active()) {
+ migration_file_set_error(err);
}
}
@@ -180,10 +173,8 @@
static bool vfio_devices_all_dirty_tracking(VFIOContainerBase *bcontainer)
{
VFIODevice *vbasedev;
- MigrationState *ms = migrate_get_current();
- if (ms->state != MIGRATION_STATUS_ACTIVE &&
- ms->state != MIGRATION_STATUS_DEVICE) {
+ if (!migration_is_active() && !migration_is_device()) {
return false;
}
@@ -225,7 +216,7 @@
{
VFIODevice *vbasedev;
- if (!migration_is_active(migrate_get_current())) {
+ if (!migration_is_active()) {
return false;
}
diff --git a/hw/vfio/container.c b/hw/vfio/container.c
index 096d77e..9a775e4 100644
--- a/hw/vfio/container.c
+++ b/hw/vfio/container.c
@@ -32,7 +32,6 @@
#include "sysemu/reset.h"
#include "trace.h"
#include "qapi/error.h"
-#include "migration/migration.h"
#include "pci.h"
VFIOGroupList vfio_group_list =
diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c
index 2050ac8..1149c6b 100644
--- a/hw/vfio/migration.c
+++ b/hw/vfio/migration.c
@@ -17,14 +17,12 @@
#include "sysemu/runstate.h"
#include "hw/vfio/vfio-common.h"
-#include "migration/migration.h"
-#include "migration/options.h"
+#include "migration/misc.h"
#include "migration/savevm.h"
#include "migration/vmstate.h"
#include "migration/qemu-file.h"
#include "migration/register.h"
#include "migration/blocker.h"
-#include "migration/misc.h"
#include "qapi/error.h"
#include "exec/ramlist.h"
#include "exec/ram_addr.h"
@@ -505,6 +503,12 @@
return vfio_device_state_is_precopy(vbasedev);
}
+/*
+ * Note about migration rate limiting: VFIO migration buffer size is currently
+ * limited to 1MB, so there is no need to check if migration rate exceeded (as
+ * in the worst case it will exceed by 1MB). However, if the buffer size is
+ * later changed to a bigger value, migration rate should be enforced here.
+ */
static int vfio_save_iterate(QEMUFile *f, void *opaque)
{
VFIODevice *vbasedev = opaque;
@@ -529,11 +533,7 @@
trace_vfio_save_iterate(vbasedev->name, migration->precopy_init_size,
migration->precopy_dirty_size);
- /*
- * A VFIO device's pre-copy dirty_bytes is not guaranteed to reach zero.
- * Return 1 so following handlers will not be potentially blocked.
- */
- return 1;
+ return !migration->precopy_init_size && !migration->precopy_dirty_size;
}
static int vfio_save_complete_precopy(QEMUFile *f, void *opaque)
@@ -713,9 +713,7 @@
* Migration should be aborted in this case, but vm_state_notify()
* currently does not support reporting failures.
*/
- if (migrate_get_current()->to_dst_file) {
- qemu_file_set_error(migrate_get_current()->to_dst_file, ret);
- }
+ migration_file_set_error(ret);
}
trace_vfio_vmstate_change_prepare(vbasedev->name, running,
@@ -745,9 +743,7 @@
* Migration should be aborted in this case, but vm_state_notify()
* currently does not support reporting failures.
*/
- if (migrate_get_current()->to_dst_file) {
- qemu_file_set_error(migrate_get_current()->to_dst_file, ret);
- }
+ migration_file_set_error(ret);
}
trace_vfio_vmstate_change(vbasedev->name, running, RunState_str(state),
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index a1eea85..1af8621 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -26,7 +26,6 @@
#include "qemu/sockets.h"
#include "sysemu/runstate.h"
#include "sysemu/cryptodev.h"
-#include "migration/migration.h"
#include "migration/postcopy-ram.h"
#include "trace.h"
#include "exec/ramblock.h"
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 89f853f..609e39a 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -31,8 +31,6 @@
#include "trace.h"
#include "qemu/error-report.h"
#include "migration/misc.h"
-#include "migration/migration.h"
-#include "migration/options.h"
#include "hw/virtio/virtio-bus.h"
#include "hw/virtio/virtio-access.h"
diff --git a/include/hw/gpio/pcf8574.h b/include/hw/gpio/pcf8574.h
new file mode 100644
index 0000000..3291d7d
--- /dev/null
+++ b/include/hw/gpio/pcf8574.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * NXP PCF8574 8-port I2C GPIO expansion chip.
+ *
+ * Copyright (c) 2024 KNS Group (YADRO).
+ * Written by Dmitrii Sharikhin <d.sharikhin@yadro.com>
+ */
+
+#ifndef _HW_GPIO_PCF8574
+#define _HW_GPIO_PCF8574
+
+#define TYPE_PCF8574 "pcf8574"
+
+#endif /* _HW_GPIO_PCF8574 */
diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h
index 626be87..438f653 100644
--- a/include/hw/qdev-properties-system.h
+++ b/include/hw/qdev-properties-system.h
@@ -9,6 +9,7 @@
extern const PropertyInfo qdev_prop_multifd_compression;
extern const PropertyInfo qdev_prop_mig_mode;
extern const PropertyInfo qdev_prop_granule_mode;
+extern const PropertyInfo qdev_prop_zero_page_detection;
extern const PropertyInfo qdev_prop_losttickpolicy;
extern const PropertyInfo qdev_prop_blockdev_on_error;
extern const PropertyInfo qdev_prop_bios_chs_trans;
@@ -50,6 +51,9 @@
MigMode)
#define DEFINE_PROP_GRANULE_MODE(_n, _s, _f, _d) \
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_granule_mode, GranuleMode)
+#define DEFINE_PROP_ZERO_PAGE_DETECTION(_n, _s, _f, _d) \
+ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_zero_page_detection, \
+ ZeroPageDetection)
#define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \
DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \
LostTickPolicy)
diff --git a/include/migration/client-options.h b/include/migration/client-options.h
new file mode 100644
index 0000000..59f4b55
--- /dev/null
+++ b/include/migration/client-options.h
@@ -0,0 +1,25 @@
+/*
+ * QEMU public migration capabilities
+ *
+ * Copyright (c) 2012-2023 Red Hat Inc
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_MIGRATION_CLIENT_OPTIONS_H
+#define QEMU_MIGRATION_CLIENT_OPTIONS_H
+
+/* capabilities */
+
+bool migrate_background_snapshot(void);
+bool migrate_dirty_limit(void);
+bool migrate_postcopy_ram(void);
+bool migrate_switchover_ack(void);
+
+/* parameters */
+
+MigMode migrate_mode(void);
+uint64_t migrate_vcpu_dirty_limit_period(void);
+
+#endif
diff --git a/include/migration/misc.h b/include/migration/misc.h
index 5d1aa59..c9e200f 100644
--- a/include/migration/misc.h
+++ b/include/migration/misc.h
@@ -17,6 +17,7 @@
#include "qemu/notify.h"
#include "qapi/qapi-types-migration.h"
#include "qapi/qapi-types-net.h"
+#include "migration/client-options.h"
/* migration/ram.c */
@@ -59,8 +60,10 @@
void migration_object_init(void);
void migration_shutdown(void);
bool migration_is_idle(void);
-bool migration_is_active(MigrationState *);
-bool migrate_mode_is_cpr(MigrationState *);
+bool migration_is_active(void);
+bool migration_is_device(void);
+bool migration_thread_is_self(void);
+bool migration_is_setup_or_active(void);
typedef enum MigrationEventType {
MIG_EVENT_PRECOPY_SETUP,
@@ -99,16 +102,15 @@
MigrationNotifyFunc func, MigMode mode);
void migration_remove_notifier(NotifierWithReturn *notify);
-int migration_call_notifiers(MigrationState *s, MigrationEventType type,
- Error **errp);
-bool migration_in_setup(MigrationState *);
-bool migration_has_finished(MigrationState *);
-bool migration_has_failed(MigrationState *);
-/* ...and after the device transmission */
+bool migration_is_running(void);
+void migration_file_set_error(int err);
+
/* True if incoming migration entered POSTCOPY_INCOMING_DISCARD */
bool migration_in_incoming_postcopy(void);
+
/* True if incoming migration entered POSTCOPY_INCOMING_ADVISE */
bool migration_incoming_postcopy_advised(void);
+
/* True if background snapshot is active */
bool migration_in_bg_snapshot(void);
diff --git a/include/migration/register.h b/include/migration/register.h
index 9ab1f79..d7b70a8 100644
--- a/include/migration/register.h
+++ b/include/migration/register.h
@@ -16,30 +16,130 @@
#include "hw/vmstate-if.h"
+/**
+ * struct SaveVMHandlers: handler structure to finely control
+ * migration of complex subsystems and devices, such as RAM, block and
+ * VFIO.
+ */
typedef struct SaveVMHandlers {
- /* This runs inside the BQL. */
- SaveStateHandler *save_state;
- /*
- * save_prepare is called early, even before migration starts, and can be
- * used to perform early checks.
+ /* The following handlers run inside the BQL. */
+
+ /**
+ * @save_state
+ *
+ * Saves state section on the source using the latest state format
+ * version.
+ *
+ * Legacy method. Should be deprecated when all users are ported
+ * to VMStateDescription.
+ *
+ * @f: QEMUFile where to send the data
+ * @opaque: data pointer passed to register_savevm_live()
+ */
+ void (*save_state)(QEMUFile *f, void *opaque);
+
+ /**
+ * @save_prepare
+ *
+ * Called early, even before migration starts, and can be used to
+ * perform early checks.
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Returns zero to indicate success and negative for error
*/
int (*save_prepare)(void *opaque, Error **errp);
+
+ /**
+ * @save_setup
+ *
+ * Initializes the data structures on the source and transmits
+ * first section containing information on the device
+ *
+ * @f: QEMUFile where to send the data
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*save_setup)(QEMUFile *f, void *opaque);
+
+ /**
+ * @save_cleanup
+ *
+ * Uninitializes the data structures on the source
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ */
void (*save_cleanup)(void *opaque);
+
+ /**
+ * @save_live_complete_postcopy
+ *
+ * Called at the end of postcopy for all postcopyable devices.
+ *
+ * @f: QEMUFile where to send the data
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*save_live_complete_postcopy)(QEMUFile *f, void *opaque);
+
+ /**
+ * @save_live_complete_precopy
+ *
+ * Transmits the last section for the device containing any
+ * remaining data at the end of a precopy phase. When postcopy is
+ * enabled, devices that support postcopy will skip this step,
+ * where the final data will be flushed at the end of postcopy via
+ * @save_live_complete_postcopy instead.
+ *
+ * @f: QEMUFile where to send the data
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*save_live_complete_precopy)(QEMUFile *f, void *opaque);
/* This runs both outside and inside the BQL. */
+
+ /**
+ * @is_active
+ *
+ * Will skip a state section if not active
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns true if state section is active else false
+ */
bool (*is_active)(void *opaque);
+
+ /**
+ * @has_postcopy
+ *
+ * Checks if a device supports postcopy
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns true for postcopy support else false
+ */
bool (*has_postcopy)(void *opaque);
- /* is_active_iterate
- * If it is not NULL then qemu_savevm_state_iterate will skip iteration if
- * it returns false. For example, it is needed for only-postcopy-states,
- * which needs to be handled by qemu_savevm_state_setup and
- * qemu_savevm_state_pending, but do not need iterations until not in
- * postcopy stage.
+ /**
+ * @is_active_iterate
+ *
+ * As #SaveVMHandlers.is_active(), will skip an inactive state
+ * section in qemu_savevm_state_iterate.
+ *
+ * For example, it is needed for only-postcopy-states, which needs
+ * to be handled by qemu_savevm_state_setup() and
+ * qemu_savevm_state_pending(), but do not need iterations until
+ * not in postcopy stage.
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns true if state section is active else false
*/
bool (*is_active_iterate)(void *opaque);
@@ -48,44 +148,155 @@
* use data that is local to the migration thread or protected
* by other locks.
*/
+
+ /**
+ * @save_live_iterate
+ *
+ * Should send a chunk of data until the point that stream
+ * bandwidth limits tell it to stop. Each call generates one
+ * section.
+ *
+ * @f: QEMUFile where to send the data
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns 0 to indicate that there is still more data to send,
+ * 1 that there is no more data to send and
+ * negative to indicate an error.
+ */
int (*save_live_iterate)(QEMUFile *f, void *opaque);
/* This runs outside the BQL! */
- /* Note for save_live_pending:
- * must_precopy:
- * - must be migrated in precopy or in stopped state
- * - i.e. must be migrated before target start
+
+ /**
+ * @state_pending_estimate
*
- * can_postcopy:
- * - can migrate in postcopy or in stopped state
- * - i.e. can migrate after target start
- * - some can also be migrated during precopy (RAM)
- * - some must be migrated after source stops (block-dirty-bitmap)
+ * This estimates the remaining data to transfer
*
- * Sum of can_postcopy and must_postcopy is the whole amount of
+ * Sum of @can_postcopy and @must_postcopy is the whole amount of
* pending data.
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ * @must_precopy: amount of data that must be migrated in precopy
+ * or in stopped state, i.e. that must be migrated
+ * before target start.
+ * @can_postcopy: amount of data that can be migrated in postcopy
+ * or in stopped state, i.e. after target start.
+ * Some can also be migrated during precopy (RAM).
+ * Some must be migrated after source stops
+ * (block-dirty-bitmap)
*/
- /* This estimates the remaining data to transfer */
void (*state_pending_estimate)(void *opaque, uint64_t *must_precopy,
uint64_t *can_postcopy);
- /* This calculate the exact remaining data to transfer */
+
+ /**
+ * @state_pending_exact
+ *
+ * This calculates the exact remaining data to transfer
+ *
+ * Sum of @can_postcopy and @must_postcopy is the whole amount of
+ * pending data.
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ * @must_precopy: amount of data that must be migrated in precopy
+ * or in stopped state, i.e. that must be migrated
+ * before target start.
+ * @can_postcopy: amount of data that can be migrated in postcopy
+ * or in stopped state, i.e. after target start.
+ * Some can also be migrated during precopy (RAM).
+ * Some must be migrated after source stops
+ * (block-dirty-bitmap)
+ */
void (*state_pending_exact)(void *opaque, uint64_t *must_precopy,
uint64_t *can_postcopy);
- LoadStateHandler *load_state;
+
+ /**
+ * @load_state
+ *
+ * Load sections generated by any of the save functions that
+ * generate sections.
+ *
+ * Legacy method. Should be deprecated when all users are ported
+ * to VMStateDescription.
+ *
+ * @f: QEMUFile where to receive the data
+ * @opaque: data pointer passed to register_savevm_live()
+ * @version_id: the maximum version_id supported
+ *
+ * Returns zero to indicate success and negative for error
+ */
+ int (*load_state)(QEMUFile *f, void *opaque, int version_id);
+
+ /**
+ * @load_setup
+ *
+ * Initializes the data structures on the destination.
+ *
+ * @f: QEMUFile where to receive the data
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*load_setup)(QEMUFile *f, void *opaque);
+
+ /**
+ * @load_cleanup
+ *
+ * Uninitializes the data structures on the destination.
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*load_cleanup)(void *opaque);
- /* Called when postcopy migration wants to resume from failure */
+
+ /**
+ * @resume_prepare
+ *
+ * Called when postcopy migration wants to resume from failure
+ *
+ * @s: Current migration state
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns zero to indicate success and negative for error
+ */
int (*resume_prepare)(MigrationState *s, void *opaque);
- /* Checks if switchover ack should be used. Called only in dest */
+
+ /**
+ * @switchover_ack_needed
+ *
+ * Checks if switchover ack should be used. Called only on
+ * destination.
+ *
+ * @opaque: data pointer passed to register_savevm_live()
+ *
+ * Returns true if switchover ack should be used and false
+ * otherwise
+ */
bool (*switchover_ack_needed)(void *opaque);
} SaveVMHandlers;
+/**
+ * register_savevm_live: Register a set of custom migration handlers
+ *
+ * @idstr: state section identifier
+ * @instance_id: instance id
+ * @version_id: version id supported
+ * @ops: SaveVMHandlers structure
+ * @opaque: data pointer passed to SaveVMHandlers handlers
+ */
int register_savevm_live(const char *idstr,
uint32_t instance_id,
int version_id,
const SaveVMHandlers *ops,
void *opaque);
+/**
+ * unregister_savevm: Unregister custom migration handlers
+ *
+ * @obj: object associated with state section
+ * @idstr: state section identifier
+ * @opaque: data pointer passed to register_savevm_live()
+ */
void unregister_savevm(VMStateIf *obj, const char *idstr, void *opaque);
#endif
diff --git a/include/qemu/help-texts.h b/include/qemu/help-texts.h
index d0359f8..353ab2a 100644
--- a/include/qemu/help-texts.h
+++ b/include/qemu/help-texts.h
@@ -2,7 +2,7 @@
#define QEMU_HELP_TEXTS_H
/* Copyright string for -version arguments, About dialogs, etc */
-#define QEMU_COPYRIGHT "Copyright (c) 2003-2023 " \
+#define QEMU_COPYRIGHT "Copyright (c) 2003-2024 " \
"Fabrice Bellard and the QEMU Project developers"
/* Bug reporting information for --help arguments, About dialogs, etc */
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index a028dba..50c277c 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -151,8 +151,6 @@
/*
* Function types
*/
-typedef void SaveStateHandler(QEMUFile *f, void *opaque);
-typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id);
typedef void (*qemu_irq_handler)(void *opaque, int n, int level);
#endif /* QEMU_TYPEDEFS_H */
diff --git a/io/channel-file.c b/io/channel-file.c
index d4706fa..a6ad777 100644
--- a/io/channel-file.c
+++ b/io/channel-file.c
@@ -242,11 +242,6 @@
{
QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
- if (qemu_fdatasync(fioc->fd) < 0) {
- error_setg_errno(errp, errno,
- "Unable to synchronize file data with storage device");
- return -1;
- }
if (qemu_close(fioc->fd) < 0) {
error_setg_errno(errp, errno,
"Unable to close file");
diff --git a/meson.build b/meson.build
index 64fba78..b8ded80 100644
--- a/meson.build
+++ b/meson.build
@@ -66,7 +66,7 @@
cxx = meson.get_compiler('cpp')
endif
if host_os == 'darwin' and \
- add_languages('objc', required: get_option('cocoa'), native: false)
+ add_languages('objc', required: true, native: false)
all_languages += ['objc']
objc = meson.get_compiler('objc')
endif
diff --git a/migration/colo.c b/migration/colo.c
index 315e31f..84632a6 100644
--- a/migration/colo.c
+++ b/migration/colo.c
@@ -63,9 +63,9 @@
return runstate_check(RUN_STATE_COLO) || !runstate_is_running();
}
-static void colo_checkpoint_notify(void *opaque)
+static void colo_checkpoint_notify(void)
{
- MigrationState *s = opaque;
+ MigrationState *s = migrate_get_current();
int64_t next_notify_time;
qemu_event_set(&s->colo_checkpoint_event);
@@ -74,10 +74,15 @@
timer_mod(s->colo_delay_timer, next_notify_time);
}
+static void colo_checkpoint_notify_timer(void *opaque)
+{
+ colo_checkpoint_notify();
+}
+
void colo_checkpoint_delay_set(void)
{
if (migration_in_colo_state()) {
- colo_checkpoint_notify(migrate_get_current());
+ colo_checkpoint_notify();
}
}
@@ -162,7 +167,7 @@
* kick COLO thread which might wait at
* qemu_sem_wait(&s->colo_checkpoint_sem).
*/
- colo_checkpoint_notify(s);
+ colo_checkpoint_notify();
/*
* Wake up COLO thread which may blocked in recv() or send(),
@@ -518,7 +523,7 @@
static void colo_compare_notify_checkpoint(Notifier *notifier, void *data)
{
- colo_checkpoint_notify(data);
+ colo_checkpoint_notify();
}
static void colo_process_checkpoint(MigrationState *s)
@@ -642,7 +647,7 @@
bql_unlock();
qemu_event_init(&s->colo_checkpoint_event, false);
s->colo_delay_timer = timer_new_ms(QEMU_CLOCK_HOST,
- colo_checkpoint_notify, s);
+ colo_checkpoint_notify_timer, NULL);
qemu_sem_init(&s->colo_exit_sem, 0);
colo_process_checkpoint(s);
diff --git a/migration/file.c b/migration/file.c
index 164b079..b0b963e 100644
--- a/migration/file.c
+++ b/migration/file.c
@@ -159,7 +159,7 @@
int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov,
int niov, RAMBlock *block, Error **errp)
{
- ssize_t ret = -1;
+ ssize_t ret = 0;
int i, slice_idx, slice_num;
uintptr_t base, next, offset;
size_t len;
@@ -191,7 +191,7 @@
*/
offset = (uintptr_t) iov[slice_idx].iov_base - (uintptr_t) block->host;
if (offset >= block->used_length) {
- error_setg(errp, "offset " RAM_ADDR_FMT
+ error_setg(errp, "offset %" PRIxPTR
"outside of ramblock %s range", offset, block->idstr);
ret = -1;
break;
diff --git a/migration/meson.build b/migration/meson.build
index 92b1cc4..1eeb915 100644
--- a/migration/meson.build
+++ b/migration/meson.build
@@ -22,6 +22,7 @@
'migration.c',
'multifd.c',
'multifd-zlib.c',
+ 'multifd-zero-page.c',
'ram-compress.c',
'options.c',
'postcopy-ram.c',
diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c
index 99b49df..7e96ae6 100644
--- a/migration/migration-hmp-cmds.c
+++ b/migration/migration-hmp-cmds.c
@@ -344,6 +344,11 @@
monitor_printf(mon, "%s: %s\n",
MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_COMPRESSION),
MultiFDCompression_str(params->multifd_compression));
+ assert(params->has_zero_page_detection);
+ monitor_printf(mon, "%s: %s\n",
+ MigrationParameter_str(MIGRATION_PARAMETER_ZERO_PAGE_DETECTION),
+ qapi_enum_lookup(&ZeroPageDetection_lookup,
+ params->zero_page_detection));
monitor_printf(mon, "%s: %" PRIu64 " bytes\n",
MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE),
params->xbzrle_cache_size);
@@ -634,6 +639,10 @@
p->has_multifd_zstd_level = true;
visit_type_uint8(v, param, &p->multifd_zstd_level, &err);
break;
+ case MIGRATION_PARAMETER_ZERO_PAGE_DETECTION:
+ p->has_zero_page_detection = true;
+ visit_type_ZeroPageDetection(v, param, &p->zero_page_detection, &err);
+ break;
case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE:
p->has_xbzrle_cache_size = true;
if (!visit_type_size(v, param, &cache_size, &err)) {
diff --git a/migration/migration.c b/migration/migration.c
index a49fcd5..644e073 100644
--- a/migration/migration.c
+++ b/migration/migration.c
@@ -1081,9 +1081,11 @@
* Return true if we're already in the middle of a migration
* (i.e. any of the active or setup states)
*/
-bool migration_is_setup_or_active(int state)
+bool migration_is_setup_or_active(void)
{
- switch (state) {
+ MigrationState *s = current_migration;
+
+ switch (s->state) {
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
@@ -1101,9 +1103,11 @@
}
}
-bool migration_is_running(int state)
+bool migration_is_running(void)
{
- switch (state) {
+ MigrationState *s = current_migration;
+
+ switch (s->state) {
case MIGRATION_STATUS_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_ACTIVE:
case MIGRATION_STATUS_POSTCOPY_PAUSED:
@@ -1404,7 +1408,7 @@
qemu_fclose(tmp);
}
- assert(!migration_is_active(s));
+ assert(!migration_is_active());
if (s->state == MIGRATION_STATUS_CANCELLING) {
migrate_set_state(&s->state, MIGRATION_STATUS_CANCELLING,
@@ -1475,7 +1479,7 @@
do {
old_state = s->state;
- if (!migration_is_running(old_state)) {
+ if (!migration_is_running()) {
break;
}
/* If the migration is paused, kick it out of the pause */
@@ -1544,16 +1548,6 @@
return ret;
}
-bool migration_in_setup(MigrationState *s)
-{
- return s->state == MIGRATION_STATUS_SETUP;
-}
-
-bool migration_has_finished(MigrationState *s)
-{
- return s->state == MIGRATION_STATUS_COMPLETED;
-}
-
bool migration_has_failed(MigrationState *s)
{
return (s->state == MIGRATION_STATUS_CANCELLED ||
@@ -1601,10 +1595,8 @@
bool migration_in_bg_snapshot(void)
{
- MigrationState *s = migrate_get_current();
-
return migrate_background_snapshot() &&
- migration_is_setup_or_active(s->state);
+ migration_is_setup_or_active();
}
bool migration_is_idle(void)
@@ -1637,12 +1629,28 @@
return false;
}
-bool migration_is_active(MigrationState *s)
+bool migration_is_active(void)
{
+ MigrationState *s = current_migration;
+
return (s->state == MIGRATION_STATUS_ACTIVE ||
s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE);
}
+bool migration_is_device(void)
+{
+ MigrationState *s = current_migration;
+
+ return s->state == MIGRATION_STATUS_DEVICE;
+}
+
+bool migration_thread_is_self(void)
+{
+ MigrationState *s = current_migration;
+
+ return qemu_thread_is_self(&s->thread);
+}
+
bool migrate_mode_is_cpr(MigrationState *s)
{
return s->parameters.mode == MIG_MODE_CPR_REBOOT;
@@ -1960,7 +1968,7 @@
return true;
}
- if (migration_is_running(s->state)) {
+ if (migration_is_running()) {
error_setg(errp, QERR_MIGRATION_ACTIVE);
return false;
}
@@ -2297,7 +2305,7 @@
trace_source_return_path_thread_entry();
rcu_register_thread();
- while (migration_is_setup_or_active(ms->state)) {
+ while (migration_is_setup_or_active()) {
trace_source_return_path_thread_loop_top();
header_type = qemu_get_be16(rp);
@@ -3020,6 +3028,17 @@
}
}
+void migration_file_set_error(int err)
+{
+ MigrationState *s = current_migration;
+
+ WITH_QEMU_LOCK_GUARD(&s->qemu_file_lock) {
+ if (s->to_dst_file) {
+ qemu_file_set_error(s->to_dst_file, err);
+ }
+ }
+}
+
static MigThrError migration_detect_error(MigrationState *s)
{
int ret;
@@ -3461,7 +3480,7 @@
trace_migration_thread_setup_complete();
- while (migration_is_active(s)) {
+ while (migration_is_active()) {
if (urgent || !migration_rate_exceeded(s->to_dst_file)) {
MigIterateState iter_state = migration_iteration_run(s);
if (iter_state == MIG_ITERATE_SKIP) {
@@ -3607,7 +3626,7 @@
migration_bh_schedule(bg_migration_vm_start_bh, s);
bql_unlock();
- while (migration_is_active(s)) {
+ while (migration_is_active()) {
MigIterateState iter_state = bg_migration_iteration_run(s);
if (iter_state == MIG_ITERATE_SKIP) {
continue;
diff --git a/migration/migration.h b/migration/migration.h
index 65c0b61..8045e39 100644
--- a/migration/migration.h
+++ b/migration/migration.h
@@ -26,6 +26,7 @@
#include "qom/object.h"
#include "postcopy-ram.h"
#include "sysemu/runstate.h"
+#include "migration/misc.h"
struct PostcopyBlocktimeContext;
@@ -479,8 +480,8 @@
void migrate_fd_connect(MigrationState *s, Error *error_in);
-bool migration_is_setup_or_active(int state);
-bool migration_is_running(int state);
+int migration_call_notifiers(MigrationState *s, MigrationEventType type,
+ Error **errp);
int migrate_init(MigrationState *s, Error **errp);
bool migration_is_blocked(Error **errp);
@@ -488,6 +489,8 @@
bool migration_in_postcopy(void);
bool migration_postcopy_is_alive(int state);
MigrationState *migrate_get_current(void);
+bool migration_has_failed(MigrationState *);
+bool migrate_mode_is_cpr(MigrationState *);
uint64_t ram_get_total_transferred_pages(void);
diff --git a/migration/multifd-zero-page.c b/migration/multifd-zero-page.c
new file mode 100644
index 0000000..1ba38be
--- /dev/null
+++ b/migration/multifd-zero-page.c
@@ -0,0 +1,87 @@
+/*
+ * Multifd zero page detection implementation.
+ *
+ * Copyright (c) 2024 Bytedance Inc
+ *
+ * Authors:
+ * Hao Xiang <hao.xiang@bytedance.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "exec/ramblock.h"
+#include "migration.h"
+#include "multifd.h"
+#include "options.h"
+#include "ram.h"
+
+static bool multifd_zero_page_enabled(void)
+{
+ return migrate_zero_page_detection() == ZERO_PAGE_DETECTION_MULTIFD;
+}
+
+static void swap_page_offset(ram_addr_t *pages_offset, int a, int b)
+{
+ ram_addr_t temp;
+
+ if (a == b) {
+ return;
+ }
+
+ temp = pages_offset[a];
+ pages_offset[a] = pages_offset[b];
+ pages_offset[b] = temp;
+}
+
+/**
+ * multifd_send_zero_page_detect: Perform zero page detection on all pages.
+ *
+ * Sorts normal pages before zero pages in p->pages->offset and updates
+ * p->pages->normal_num.
+ *
+ * @param p A pointer to the send params.
+ */
+void multifd_send_zero_page_detect(MultiFDSendParams *p)
+{
+ MultiFDPages_t *pages = p->pages;
+ RAMBlock *rb = pages->block;
+ int i = 0;
+ int j = pages->num - 1;
+
+ if (!multifd_zero_page_enabled()) {
+ pages->normal_num = pages->num;
+ return;
+ }
+
+ /*
+ * Sort the page offset array by moving all normal pages to
+ * the left and all zero pages to the right of the array.
+ */
+ while (i <= j) {
+ uint64_t offset = pages->offset[i];
+
+ if (!buffer_is_zero(rb->host + offset, p->page_size)) {
+ i++;
+ continue;
+ }
+
+ swap_page_offset(pages->offset, i, j);
+ ram_release_page(rb->idstr, offset);
+ j--;
+ }
+
+ pages->normal_num = i;
+}
+
+void multifd_recv_zero_page_process(MultiFDRecvParams *p)
+{
+ for (int i = 0; i < p->zero_num; i++) {
+ void *page = p->host + p->zero[i];
+ if (!buffer_is_zero(page, p->page_size)) {
+ memset(page, 0, p->page_size);
+ }
+ }
+}
diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c
index 6120faa..83c0374 100644
--- a/migration/multifd-zlib.c
+++ b/migration/multifd-zlib.c
@@ -123,13 +123,15 @@
int ret;
uint32_t i;
- multifd_send_prepare_header(p);
+ if (!multifd_send_prepare_common(p)) {
+ goto out;
+ }
- for (i = 0; i < pages->num; i++) {
+ for (i = 0; i < pages->normal_num; i++) {
uint32_t available = z->zbuff_len - out_size;
int flush = Z_NO_FLUSH;
- if (i == pages->num - 1) {
+ if (i == pages->normal_num - 1) {
flush = Z_SYNC_FLUSH;
}
@@ -172,10 +174,10 @@
p->iov[p->iovs_num].iov_len = out_size;
p->iovs_num++;
p->next_packet_size = out_size;
+
+out:
p->flags |= MULTIFD_FLAG_ZLIB;
-
multifd_send_fill_packet(p);
-
return 0;
}
@@ -261,6 +263,14 @@
p->id, flags, MULTIFD_FLAG_ZLIB);
return -1;
}
+
+ multifd_recv_zero_page_process(p);
+
+ if (!p->normal_num) {
+ assert(in_size == 0);
+ return 0;
+ }
+
ret = qio_channel_read_all(p->c, (void *)z->zbuff, in_size, errp);
if (ret != 0) {
@@ -310,6 +320,7 @@
p->id, out_size, expected_size);
return -1;
}
+
return 0;
}
diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c
index cac2368..0211225 100644
--- a/migration/multifd-zstd.c
+++ b/migration/multifd-zstd.c
@@ -118,16 +118,18 @@
int ret;
uint32_t i;
- multifd_send_prepare_header(p);
+ if (!multifd_send_prepare_common(p)) {
+ goto out;
+ }
z->out.dst = z->zbuff;
z->out.size = z->zbuff_len;
z->out.pos = 0;
- for (i = 0; i < pages->num; i++) {
+ for (i = 0; i < pages->normal_num; i++) {
ZSTD_EndDirective flush = ZSTD_e_continue;
- if (i == pages->num - 1) {
+ if (i == pages->normal_num - 1) {
flush = ZSTD_e_flush;
}
z->in.src = p->pages->block->host + pages->offset[i];
@@ -161,10 +163,10 @@
p->iov[p->iovs_num].iov_len = z->out.pos;
p->iovs_num++;
p->next_packet_size = z->out.pos;
+
+out:
p->flags |= MULTIFD_FLAG_ZSTD;
-
multifd_send_fill_packet(p);
-
return 0;
}
@@ -257,6 +259,14 @@
p->id, flags, MULTIFD_FLAG_ZSTD);
return -1;
}
+
+ multifd_recv_zero_page_process(p);
+
+ if (!p->normal_num) {
+ assert(in_size == 0);
+ return 0;
+ }
+
ret = qio_channel_read_all(p->c, (void *)z->zbuff, in_size, errp);
if (ret != 0) {
diff --git a/migration/multifd.c b/migration/multifd.c
index d4a44da..0179422 100644
--- a/migration/multifd.c
+++ b/migration/multifd.c
@@ -11,6 +11,7 @@
*/
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
#include "qemu/rcu.h"
#include "exec/target_page.h"
#include "sysemu/sysemu.h"
@@ -111,11 +112,16 @@
static void multifd_set_file_bitmap(MultiFDSendParams *p)
{
MultiFDPages_t *pages = p->pages;
+ uint32_t zero_num = p->pages->num - p->pages->normal_num;
assert(pages->block);
- for (int i = 0; i < p->pages->num; i++) {
- ramblock_set_file_bmap_atomic(pages->block, pages->offset[i]);
+ for (int i = 0; i < p->pages->normal_num; i++) {
+ ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], true);
+ }
+
+ for (int i = p->pages->num; i < zero_num; i++) {
+ ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], false);
}
}
@@ -153,13 +159,13 @@
{
MultiFDPages_t *pages = p->pages;
- for (int i = 0; i < pages->num; i++) {
+ for (int i = 0; i < pages->normal_num; i++) {
p->iov[p->iovs_num].iov_base = pages->block->host + pages->offset[i];
p->iov[p->iovs_num].iov_len = p->page_size;
p->iovs_num++;
}
- p->next_packet_size = pages->num * p->page_size;
+ p->next_packet_size = pages->normal_num * p->page_size;
}
/**
@@ -178,6 +184,8 @@
bool use_zero_copy_send = migrate_zero_copy_send();
int ret;
+ multifd_send_zero_page_detect(p);
+
if (!multifd_use_packets()) {
multifd_send_prepare_iovs(p);
multifd_set_file_bitmap(p);
@@ -261,6 +269,13 @@
p->id, flags, MULTIFD_FLAG_NOCOMP);
return -1;
}
+
+ multifd_recv_zero_page_process(p);
+
+ if (!p->normal_num) {
+ return 0;
+ }
+
for (int i = 0; i < p->normal_num; i++) {
p->iov[i].iov_base = p->host + p->normal[i];
p->iov[i].iov_len = p->page_size;
@@ -295,6 +310,7 @@
* overwritten later when reused.
*/
pages->num = 0;
+ pages->normal_num = 0;
pages->block = NULL;
}
@@ -386,11 +402,13 @@
MultiFDPacket_t *packet = p->packet;
MultiFDPages_t *pages = p->pages;
uint64_t packet_num;
+ uint32_t zero_num = pages->num - pages->normal_num;
int i;
packet->flags = cpu_to_be32(p->flags);
packet->pages_alloc = cpu_to_be32(p->pages->allocated);
- packet->normal_pages = cpu_to_be32(pages->num);
+ packet->normal_pages = cpu_to_be32(pages->normal_num);
+ packet->zero_pages = cpu_to_be32(zero_num);
packet->next_packet_size = cpu_to_be32(p->next_packet_size);
packet_num = qatomic_fetch_inc(&multifd_send_state->packet_num);
@@ -408,10 +426,11 @@
}
p->packets_sent++;
- p->total_normal_pages += pages->num;
+ p->total_normal_pages += pages->normal_num;
+ p->total_zero_pages += zero_num;
- trace_multifd_send(p->id, packet_num, pages->num, p->flags,
- p->next_packet_size);
+ trace_multifd_send(p->id, packet_num, pages->normal_num, zero_num,
+ p->flags, p->next_packet_size);
}
static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp)
@@ -452,20 +471,29 @@
p->normal_num = be32_to_cpu(packet->normal_pages);
if (p->normal_num > packet->pages_alloc) {
error_setg(errp, "multifd: received packet "
- "with %u pages and expected maximum pages are %u",
+ "with %u normal pages and expected maximum pages are %u",
p->normal_num, packet->pages_alloc) ;
return -1;
}
+ p->zero_num = be32_to_cpu(packet->zero_pages);
+ if (p->zero_num > packet->pages_alloc - p->normal_num) {
+ error_setg(errp, "multifd: received packet "
+ "with %u zero pages and expected maximum zero pages are %u",
+ p->zero_num, packet->pages_alloc - p->normal_num) ;
+ return -1;
+ }
+
p->next_packet_size = be32_to_cpu(packet->next_packet_size);
p->packet_num = be64_to_cpu(packet->packet_num);
p->packets_recved++;
p->total_normal_pages += p->normal_num;
+ p->total_zero_pages += p->zero_num;
- trace_multifd_recv(p->id, p->packet_num, p->normal_num, p->flags,
- p->next_packet_size);
+ trace_multifd_recv(p->id, p->packet_num, p->normal_num, p->zero_num,
+ p->flags, p->next_packet_size);
- if (p->normal_num == 0) {
+ if (p->normal_num == 0 && p->zero_num == 0) {
return 0;
}
@@ -491,6 +519,18 @@
p->normal[i] = offset;
}
+ for (i = 0; i < p->zero_num; i++) {
+ uint64_t offset = be64_to_cpu(packet->offset[p->normal_num + i]);
+
+ if (offset > (p->block->used_length - p->page_size)) {
+ error_setg(errp, "multifd: offset too long %" PRIu64
+ " (max " RAM_ADDR_FMT ")",
+ offset, p->block->used_length);
+ return -1;
+ }
+ p->zero[i] = offset;
+ }
+
return 0;
}
@@ -710,16 +750,26 @@
if (p->c) {
migration_ioc_unregister_yank(p->c);
/*
- * An explicit close() on the channel here is normally not
- * required, but can be helpful for "file:" iochannels, where it
- * will include fdatasync() to make sure the data is flushed to the
- * disk backend.
+ * The object_unref() cannot guarantee the fd will always be
+ * released because finalize() of the iochannel is only
+ * triggered on the last reference and it's not guaranteed
+ * that we always hold the last refcount when reaching here.
*
- * The object_unref() cannot guarantee that because: (1) finalize()
- * of the iochannel is only triggered on the last reference, and
- * it's not guaranteed that we always hold the last refcount when
- * reaching here, and, (2) even if finalize() is invoked, it only
- * does a close(fd) without data flush.
+ * Closing the fd explicitly has the benefit that if there is any
+ * registered I/O handler callbacks on such fd, that will get a
+ * POLLNVAL event and will further trigger the cleanup to finally
+ * release the IOC.
+ *
+ * FIXME: It should logically be guaranteed that all multifd
+ * channels have no I/O handler callback registered when reaching
+ * here, because migration thread will wait for all multifd channel
+ * establishments to complete during setup. Since
+ * migrate_fd_cleanup() will be scheduled in main thread too, all
+ * previous callbacks should guarantee to be completed when
+ * reaching here. See multifd_send_state.channels_created and its
+ * usage. In the future, we could replace this with an assert
+ * making sure we're the last reference, or simply drop it if above
+ * is more clear to be justified.
*/
qio_channel_close(p->c, &error_abort);
object_unref(OBJECT(p->c));
@@ -908,6 +958,8 @@
stat64_add(&mig_stats.multifd_bytes,
p->next_packet_size + p->packet_len);
+ stat64_add(&mig_stats.normal_pages, pages->normal_num);
+ stat64_add(&mig_stats.zero_pages, pages->num - pages->normal_num);
multifd_pages_reset(p->pages);
p->next_packet_size = 0;
@@ -955,7 +1007,8 @@
rcu_unregister_thread();
migration_threads_remove(thread);
- trace_multifd_send_thread_end(p->id, p->packets_sent, p->total_normal_pages);
+ trace_multifd_send_thread_end(p->id, p->packets_sent, p->total_normal_pages,
+ p->total_zero_pages);
return NULL;
}
@@ -1306,6 +1359,8 @@
p->iov = NULL;
g_free(p->normal);
p->normal = NULL;
+ g_free(p->zero);
+ p->zero = NULL;
multifd_recv_state->ops->recv_cleanup(p);
}
@@ -1439,7 +1494,7 @@
flags = p->flags;
/* recv methods don't know how to handle the SYNC flag */
p->flags &= ~MULTIFD_FLAG_SYNC;
- has_data = !!p->normal_num;
+ has_data = p->normal_num || p->zero_num;
qemu_mutex_unlock(&p->mutex);
} else {
/*
@@ -1497,7 +1552,9 @@
}
rcu_unregister_thread();
- trace_multifd_recv_thread_end(p->id, p->packets_recved, p->total_normal_pages);
+ trace_multifd_recv_thread_end(p->id, p->packets_recved,
+ p->total_normal_pages,
+ p->total_zero_pages);
return NULL;
}
@@ -1549,6 +1606,7 @@
p->name = g_strdup_printf("multifdrecv_%d", i);
p->iov = g_new0(struct iovec, page_count);
p->normal = g_new0(ram_addr_t, page_count);
+ p->zero = g_new0(ram_addr_t, page_count);
p->page_count = page_count;
p->page_size = qemu_target_page_size();
}
@@ -1623,3 +1681,17 @@
QEMU_THREAD_JOINABLE);
qatomic_inc(&multifd_recv_state->count);
}
+
+bool multifd_send_prepare_common(MultiFDSendParams *p)
+{
+ multifd_send_zero_page_detect(p);
+
+ if (!p->pages->normal_num) {
+ p->next_packet_size = 0;
+ return false;
+ }
+
+ multifd_send_prepare_header(p);
+
+ return true;
+}
diff --git a/migration/multifd.h b/migration/multifd.h
index 7447c2b..c9d9b09 100644
--- a/migration/multifd.h
+++ b/migration/multifd.h
@@ -55,14 +55,24 @@
/* size of the next packet that contains pages */
uint32_t next_packet_size;
uint64_t packet_num;
- uint64_t unused[4]; /* Reserved for future use */
+ /* zero pages */
+ uint32_t zero_pages;
+ uint32_t unused32[1]; /* Reserved for future use */
+ uint64_t unused64[3]; /* Reserved for future use */
char ramblock[256];
+ /*
+ * This array contains the pointers to:
+ * - normal pages (initial normal_pages entries)
+ * - zero pages (following zero_pages entries)
+ */
uint64_t offset[];
} __attribute__((packed)) MultiFDPacket_t;
typedef struct {
/* number of used pages */
uint32_t num;
+ /* number of normal pages */
+ uint32_t normal_num;
/* number of allocated pages */
uint32_t allocated;
/* offset of each page */
@@ -136,6 +146,8 @@
uint64_t packets_sent;
/* non zero pages sent through this channel */
uint64_t total_normal_pages;
+ /* zero pages sent through this channel */
+ uint64_t total_zero_pages;
/* buffers to send */
struct iovec *iov;
/* number of iovs used */
@@ -194,12 +206,18 @@
uint8_t *host;
/* non zero pages recv through this channel */
uint64_t total_normal_pages;
+ /* zero pages recv through this channel */
+ uint64_t total_zero_pages;
/* buffers to recv */
struct iovec *iov;
/* Pages that are not zero */
ram_addr_t *normal;
/* num of non zero pages */
uint32_t normal_num;
+ /* Pages that are zero */
+ ram_addr_t *zero;
+ /* num of zero pages */
+ uint32_t zero_num;
/* used for de-compression methods */
void *compress_data;
} MultiFDRecvParams;
@@ -221,6 +239,9 @@
void multifd_register_ops(int method, MultiFDMethods *ops);
void multifd_send_fill_packet(MultiFDSendParams *p);
+bool multifd_send_prepare_common(MultiFDSendParams *p);
+void multifd_send_zero_page_detect(MultiFDSendParams *p);
+void multifd_recv_zero_page_process(MultiFDRecvParams *p);
static inline void multifd_send_prepare_header(MultiFDSendParams *p)
{
diff --git a/migration/options.c b/migration/options.c
index 40eb930..9ed2fe4 100644
--- a/migration/options.c
+++ b/migration/options.c
@@ -179,6 +179,9 @@
DEFINE_PROP_MIG_MODE("mode", MigrationState,
parameters.mode,
MIG_MODE_NORMAL),
+ DEFINE_PROP_ZERO_PAGE_DETECTION("zero-page-detection", MigrationState,
+ parameters.zero_page_detection,
+ ZERO_PAGE_DETECTION_MULTIFD),
/* Migration capabilities */
DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE),
@@ -681,7 +684,7 @@
MigrationState *s = migrate_get_current();
bool new_caps[MIGRATION_CAPABILITY__MAX];
- if (migration_is_running(s->state)) {
+ if (migration_is_running()) {
error_setg(errp, QERR_MIGRATION_ACTIVE);
return false;
}
@@ -725,7 +728,7 @@
MigrationCapabilityStatusList *cap;
bool new_caps[MIGRATION_CAPABILITY__MAX];
- if (migration_is_running(s->state) || migration_in_colo_state()) {
+ if (migration_is_running() || migration_in_colo_state()) {
error_setg(errp, QERR_MIGRATION_ACTIVE);
return;
}
@@ -924,6 +927,13 @@
return s->parameters.tls_hostname;
}
+uint64_t migrate_vcpu_dirty_limit_period(void)
+{
+ MigrationState *s = migrate_get_current();
+
+ return s->parameters.x_vcpu_dirty_limit_period;
+}
+
uint64_t migrate_xbzrle_cache_size(void)
{
MigrationState *s = migrate_get_current();
@@ -931,6 +941,13 @@
return s->parameters.xbzrle_cache_size;
}
+ZeroPageDetection migrate_zero_page_detection(void)
+{
+ MigrationState *s = migrate_get_current();
+
+ return s->parameters.zero_page_detection;
+}
+
/* parameter setters */
void migrate_set_block_incremental(bool value)
@@ -1041,6 +1058,8 @@
params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit;
params->has_mode = true;
params->mode = s->parameters.mode;
+ params->has_zero_page_detection = true;
+ params->zero_page_detection = s->parameters.zero_page_detection;
return params;
}
@@ -1077,6 +1096,7 @@
params->has_x_vcpu_dirty_limit_period = true;
params->has_vcpu_dirty_limit = true;
params->has_mode = true;
+ params->has_zero_page_detection = true;
}
/*
@@ -1391,6 +1411,10 @@
if (params->has_mode) {
dest->mode = params->mode;
}
+
+ if (params->has_zero_page_detection) {
+ dest->zero_page_detection = params->zero_page_detection;
+ }
}
static void migrate_params_apply(MigrateSetParameters *params, Error **errp)
@@ -1541,6 +1565,10 @@
if (params->has_mode) {
s->parameters.mode = params->mode;
}
+
+ if (params->has_zero_page_detection) {
+ s->parameters.zero_page_detection = params->zero_page_detection;
+ }
}
void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp)
diff --git a/migration/options.h b/migration/options.h
index 6ddd8da..ab8199e 100644
--- a/migration/options.h
+++ b/migration/options.h
@@ -16,6 +16,7 @@
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
+#include "migration/client-options.h"
/* migration properties */
@@ -24,12 +25,10 @@
/* capabilities */
bool migrate_auto_converge(void);
-bool migrate_background_snapshot(void);
bool migrate_block(void);
bool migrate_colo(void);
bool migrate_compress(void);
bool migrate_dirty_bitmaps(void);
-bool migrate_dirty_limit(void);
bool migrate_events(void);
bool migrate_mapped_ram(void);
bool migrate_ignore_shared(void);
@@ -38,11 +37,9 @@
bool migrate_pause_before_switchover(void);
bool migrate_postcopy_blocktime(void);
bool migrate_postcopy_preempt(void);
-bool migrate_postcopy_ram(void);
bool migrate_rdma_pin_all(void);
bool migrate_release_ram(void);
bool migrate_return_path(void);
-bool migrate_switchover_ack(void);
bool migrate_validate_uuid(void);
bool migrate_xbzrle(void);
bool migrate_zero_blocks(void);
@@ -84,7 +81,6 @@
uint64_t migrate_max_bandwidth(void);
uint64_t migrate_avail_switchover_bandwidth(void);
uint64_t migrate_max_postcopy_bandwidth(void);
-MigMode migrate_mode(void);
int migrate_multifd_channels(void);
MultiFDCompression migrate_multifd_compression(void);
int migrate_multifd_zlib_level(void);
@@ -94,6 +90,7 @@
const char *migrate_tls_creds(void);
const char *migrate_tls_hostname(void);
uint64_t migrate_xbzrle_cache_size(void);
+ZeroPageDetection migrate_zero_page_detection(void);
/* parameters setters */
diff --git a/migration/qemu-file.c b/migration/qemu-file.c
index b10c882..a10882d 100644
--- a/migration/qemu-file.c
+++ b/migration/qemu-file.c
@@ -63,6 +63,8 @@
*/
int qemu_file_shutdown(QEMUFile *f)
{
+ Error *err = NULL;
+
/*
* We must set qemufile error before the real shutdown(), otherwise
* there can be a race window where we thought IO all went though
@@ -91,7 +93,8 @@
return -ENOSYS;
}
- if (qio_channel_shutdown(f->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL) < 0) {
+ if (qio_channel_shutdown(f->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, &err) < 0) {
+ error_report_err(err);
return -EIO;
}
diff --git a/migration/ram.c b/migration/ram.c
index 003c28e..8deb849 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -1140,6 +1140,10 @@
QEMUFile *file = pss->pss_channel;
int len = 0;
+ if (migrate_zero_page_detection() == ZERO_PAGE_DETECTION_NONE) {
+ return 0;
+ }
+
if (!buffer_is_zero(p, TARGET_PAGE_SIZE)) {
return 0;
}
@@ -1284,7 +1288,6 @@
if (!multifd_queue_page(block, offset)) {
return -1;
}
- stat64_add(&mig_stats.normal_pages, 1);
return 1;
}
@@ -2076,7 +2079,6 @@
*/
static int ram_save_target_page_legacy(RAMState *rs, PageSearchStatus *pss)
{
- RAMBlock *block = pss->block;
ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS;
int res;
@@ -2092,17 +2094,33 @@
return 1;
}
+ return ram_save_page(rs, pss);
+}
+
+/**
+ * ram_save_target_page_multifd: send one target page to multifd workers
+ *
+ * Returns 1 if the page was queued, -1 otherwise.
+ *
+ * @rs: current RAM state
+ * @pss: data about the page we want to send
+ */
+static int ram_save_target_page_multifd(RAMState *rs, PageSearchStatus *pss)
+{
+ RAMBlock *block = pss->block;
+ ram_addr_t offset = ((ram_addr_t)pss->page) << TARGET_PAGE_BITS;
+
/*
- * Do not use multifd in postcopy as one whole host page should be
- * placed. Meanwhile postcopy requires atomic update of pages, so even
- * if host page size == guest page size the dest guest during run may
- * still see partially copied pages which is data corruption.
+ * While using multifd live migration, we still need to handle zero
+ * page checking on the migration main thread.
*/
- if (migrate_multifd() && !migration_in_postcopy()) {
- return ram_save_multifd_page(block, offset);
+ if (migrate_zero_page_detection() == ZERO_PAGE_DETECTION_LEGACY) {
+ if (save_zero_page(rs, pss, offset)) {
+ return 1;
+ }
}
- return ram_save_page(rs, pss);
+ return ram_save_multifd_page(block, offset);
}
/* Should be called before sending a host page */
@@ -2909,10 +2927,9 @@
RAMBlock *block;
ram_addr_t offset;
size_t used_len, start, npages;
- MigrationState *s = migrate_get_current();
/* This function is currently expected to be used during live migration */
- if (!migration_is_setup_or_active(s->state)) {
+ if (!migration_is_setup_or_active()) {
return;
}
@@ -3110,7 +3127,12 @@
}
migration_ops = g_malloc0(sizeof(MigrationOps));
- migration_ops->ram_save_target_page = ram_save_target_page_legacy;
+
+ if (migrate_multifd()) {
+ migration_ops->ram_save_target_page = ram_save_target_page_multifd;
+ } else {
+ migration_ops->ram_save_target_page = ram_save_target_page_legacy;
+ }
bql_unlock();
ret = multifd_send_sync_main();
@@ -3150,9 +3172,13 @@
}
}
-void ramblock_set_file_bmap_atomic(RAMBlock *block, ram_addr_t offset)
+void ramblock_set_file_bmap_atomic(RAMBlock *block, ram_addr_t offset, bool set)
{
- set_bit_atomic(offset >> TARGET_PAGE_BITS, block->file_bmap);
+ if (set) {
+ set_bit_atomic(offset >> TARGET_PAGE_BITS, block->file_bmap);
+ } else {
+ clear_bit_atomic(offset >> TARGET_PAGE_BITS, block->file_bmap);
+ }
}
/**
@@ -3263,7 +3289,7 @@
out:
if (ret >= 0
- && migration_is_setup_or_active(migrate_get_current()->state)) {
+ && migration_is_setup_or_active()) {
if (migrate_multifd() && migrate_multifd_flush_after_each_section() &&
!migrate_mapped_ram()) {
ret = multifd_send_sync_main();
@@ -4214,6 +4240,12 @@
i++;
addr = qemu_get_be64(f);
+ ret = qemu_file_get_error(f);
+ if (ret) {
+ error_report("Getting RAM address failed");
+ break;
+ }
+
flags = addr & ~TARGET_PAGE_MASK;
addr &= TARGET_PAGE_MASK;
diff --git a/migration/ram.h b/migration/ram.h
index b9ac0da..08feeca 100644
--- a/migration/ram.h
+++ b/migration/ram.h
@@ -75,7 +75,8 @@
bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start);
void postcopy_preempt_shutdown_file(MigrationState *s);
void *postcopy_preempt_thread(void *opaque);
-void ramblock_set_file_bmap_atomic(RAMBlock *block, ram_addr_t offset);
+void ramblock_set_file_bmap_atomic(RAMBlock *block, ram_addr_t offset,
+ bool set);
/* ram cache */
int colo_init_ram_cache(void);
diff --git a/migration/rdma.c b/migration/rdma.c
index a355dce..855753c 100644
--- a/migration/rdma.c
+++ b/migration/rdma.c
@@ -3357,7 +3357,7 @@
goto err_rdma_dest_wait;
}
- isock->host = rdma->host;
+ isock->host = g_strdup(rdma->host);
isock->port = g_strdup_printf("%d", rdma->port);
/*
diff --git a/migration/savevm.c b/migration/savevm.c
index dc1fb9c..388d7af 100644
--- a/migration/savevm.c
+++ b/migration/savevm.c
@@ -1317,7 +1317,7 @@
MigrationState *ms = migrate_get_current();
SaveStateEntry *se;
Error *local_err = NULL;
- int ret;
+ int ret = 0;
json_writer_int64(ms->vmdesc, "page_size", qemu_target_page_size());
json_writer_start_array(ms->vmdesc, "devices");
@@ -1351,6 +1351,10 @@
}
}
+ if (ret) {
+ return;
+ }
+
if (precopy_notify(PRECOPY_NOTIFY_SETUP, &local_err)) {
error_report_err(local_err);
}
@@ -1390,7 +1394,8 @@
int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy)
{
SaveStateEntry *se;
- int ret = 1;
+ bool all_finished = true;
+ int ret;
trace_savevm_state_iterate();
QTAILQ_FOREACH(se, &savevm_state.handlers, entry) {
@@ -1431,16 +1436,12 @@
"%d(%s): %d",
se->section_id, se->idstr, ret);
qemu_file_set_error(f, ret);
- }
- if (ret <= 0) {
- /* Do not proceed to the next vmstate before this one reported
- completion of the current stage. This serializes the migration
- and reduces the probability that a faster changing state is
- synchronized over and over again. */
- break;
+ return ret;
+ } else if (!ret) {
+ all_finished = false;
}
}
- return ret;
+ return all_finished;
}
static bool should_send_vmdesc(void)
@@ -1705,7 +1706,7 @@
MigrationState *ms = migrate_get_current();
MigrationStatus status;
- if (migration_is_running(ms->state)) {
+ if (migration_is_running()) {
error_setg(errp, QERR_MIGRATION_ACTIVE);
return -EINVAL;
}
diff --git a/migration/trace-events b/migration/trace-events
index bf1a069..f0e1cb8 100644
--- a/migration/trace-events
+++ b/migration/trace-events
@@ -128,21 +128,21 @@
# multifd.c
multifd_new_send_channel_async(uint8_t id) "channel %u"
multifd_new_send_channel_async_error(uint8_t id, void *err) "channel=%u err=%p"
-multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " pages %u flags 0x%x next packet size %u"
+multifd_recv(uint8_t id, uint64_t packet_num, uint32_t normal, uint32_t zero, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u zero pages %u flags 0x%x next packet size %u"
multifd_recv_new_channel(uint8_t id) "channel %u"
multifd_recv_sync_main(long packet_num) "packet num %ld"
multifd_recv_sync_main_signal(uint8_t id) "channel %u"
multifd_recv_sync_main_wait(uint8_t id) "iter %u"
multifd_recv_terminate_threads(bool error) "error %d"
-multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %u packets %" PRIu64 " pages %" PRIu64
+multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages, uint64_t zero_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64 " zero pages %" PRIu64
multifd_recv_thread_start(uint8_t id) "%u"
-multifd_send(uint8_t id, uint64_t packet_num, uint32_t normal, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u flags 0x%x next packet size %u"
+multifd_send(uint8_t id, uint64_t packet_num, uint32_t normal_pages, uint32_t zero_pages, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u zero pages %u flags 0x%x next packet size %u"
multifd_send_error(uint8_t id) "channel %u"
multifd_send_sync_main(long packet_num) "packet num %ld"
multifd_send_sync_main_signal(uint8_t id) "channel %u"
multifd_send_sync_main_wait(uint8_t id) "channel %u"
multifd_send_terminate_threads(void) ""
-multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64
+multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages, uint64_t zero_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64 " zero pages %" PRIu64
multifd_send_thread_start(uint8_t id) "%u"
multifd_tls_outgoing_handshake_start(void *ioc, void *tioc, const char *hostname) "ioc=%p tioc=%p hostname=%s"
multifd_tls_outgoing_handshake_error(void *ioc, const char *err) "ioc=%p err=%s"
diff --git a/net/colo-compare.c b/net/colo-compare.c
index f2dfc0e..c4ad0ab 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -28,7 +28,6 @@
#include "sysemu/iothread.h"
#include "net/colo-compare.h"
#include "migration/colo.h"
-#include "migration/migration.h"
#include "util.h"
#include "block/aio-wait.h"
@@ -189,7 +188,7 @@
notify_remote_frame(s);
} else {
notifier_list_notify(&colo_compare_notifiers,
- migrate_get_current());
+ NULL);
}
}
diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c
index e6bdb45..8564817 100644
--- a/net/vhost-vdpa.c
+++ b/net/vhost-vdpa.c
@@ -26,7 +26,6 @@
#include <err.h>
#include "standard-headers/linux/virtio_net.h"
#include "monitor/monitor.h"
-#include "migration/migration.h"
#include "migration/misc.h"
#include "hw/virtio/vhost.h"
@@ -355,7 +354,7 @@
assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
if (s->always_svq ||
- migration_is_setup_or_active(migrate_get_current()->state)) {
+ migration_is_setup_or_active()) {
v->shadow_vqs_enabled = true;
} else {
v->shadow_vqs_enabled = false;
diff --git a/qapi/migration.json b/qapi/migration.json
index 51d188b..aa1b39b 100644
--- a/qapi/migration.json
+++ b/qapi/migration.json
@@ -671,6 +671,23 @@
'data': [ 'normal', 'cpr-reboot' ] }
##
+# @ZeroPageDetection:
+#
+# @none: Do not perform zero page checking.
+#
+# @legacy: Perform zero page checking in main migration thread.
+#
+# @multifd: Perform zero page checking in multifd sender thread if
+# multifd migration is enabled, else in the main migration
+# thread as for @legacy.
+#
+# Since: 9.0
+#
+##
+{ 'enum': 'ZeroPageDetection',
+ 'data': [ 'none', 'legacy', 'multifd' ] }
+
+##
# @BitmapMigrationBitmapAliasTransform:
#
# @persistent: If present, the bitmap will be made persistent or
@@ -891,6 +908,10 @@
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
+# @zero-page-detection: Whether and how to detect zero pages.
+# See description in @ZeroPageDetection. Default is 'multifd'.
+# (since 9.0)
+#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@@ -924,7 +945,8 @@
'block-bitmap-mapping',
{ 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] },
'vcpu-dirty-limit',
- 'mode'] }
+ 'mode',
+ 'zero-page-detection'] }
##
# @MigrateSetParameters:
@@ -1083,6 +1105,10 @@
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
+# @zero-page-detection: Whether and how to detect zero pages.
+# See description in @ZeroPageDetection. Default is 'multifd'.
+# (since 9.0)
+#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@@ -1136,7 +1162,8 @@
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
'features': [ 'unstable' ] },
'*vcpu-dirty-limit': 'uint64',
- '*mode': 'MigMode'} }
+ '*mode': 'MigMode',
+ '*zero-page-detection': 'ZeroPageDetection'} }
##
# @migrate-set-parameters:
@@ -1311,6 +1338,10 @@
# @mode: Migration mode. See description in @MigMode. Default is 'normal'.
# (Since 8.2)
#
+# @zero-page-detection: Whether and how to detect zero pages.
+# See description in @ZeroPageDetection. Default is 'multifd'.
+# (since 9.0)
+#
# Features:
#
# @deprecated: Member @block-incremental is deprecated. Use
@@ -1361,7 +1392,8 @@
'*x-vcpu-dirty-limit-period': { 'type': 'uint64',
'features': [ 'unstable' ] },
'*vcpu-dirty-limit': 'uint64',
- '*mode': 'MigMode'} }
+ '*mode': 'MigMode',
+ '*zero-page-detection': 'ZeroPageDetection'} }
##
# @query-migrate-parameters:
diff --git a/stubs/colo.c b/stubs/colo.c
index 08c9f98..f8c069b 100644
--- a/stubs/colo.c
+++ b/stubs/colo.c
@@ -2,7 +2,6 @@
#include "qemu/notify.h"
#include "net/colo-compare.h"
#include "migration/colo.h"
-#include "migration/migration.h"
#include "qemu/error-report.h"
#include "qapi/qapi-commands-migration.h"
diff --git a/system/dirtylimit.c b/system/dirtylimit.c
index b5607eb..ab20da3 100644
--- a/system/dirtylimit.c
+++ b/system/dirtylimit.c
@@ -25,8 +25,6 @@
#include "sysemu/kvm.h"
#include "trace.h"
#include "migration/misc.h"
-#include "migration/migration.h"
-#include "migration/options.h"
/*
* Dirtylimit stop working if dirty page rate error
@@ -78,14 +76,13 @@
static void vcpu_dirty_rate_stat_collect(void)
{
- MigrationState *s = migrate_get_current();
VcpuStat stat;
int i = 0;
int64_t period = DIRTYLIMIT_CALC_TIME_MS;
if (migrate_dirty_limit() &&
- migration_is_active(s)) {
- period = s->parameters.x_vcpu_dirty_limit_period;
+ migration_is_active()) {
+ period = migrate_vcpu_dirty_limit_period();
}
/* calculate vcpu dirtyrate */
@@ -450,10 +447,8 @@
*/
static bool dirtylimit_is_allowed(void)
{
- MigrationState *ms = migrate_get_current();
-
- if (migration_is_running(ms->state) &&
- (!qemu_thread_is_self(&ms->thread)) &&
+ if (migration_is_running() &&
+ !migration_thread_is_self() &&
migrate_dirty_limit() &&
dirtylimit_in_service()) {
return false;
diff --git a/system/physmem.c b/system/physmem.c
index 6e9ed97..6cfb7a8 100644
--- a/system/physmem.c
+++ b/system/physmem.c
@@ -2681,53 +2681,69 @@
return false;
}
+static MemTxResult flatview_write_continue_step(MemTxAttrs attrs,
+ const uint8_t *buf,
+ hwaddr len, hwaddr mr_addr,
+ hwaddr *l, MemoryRegion *mr)
+{
+ if (!flatview_access_allowed(mr, attrs, mr_addr, *l)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (!memory_access_is_direct(mr, true)) {
+ uint64_t val;
+ MemTxResult result;
+ bool release_lock = prepare_mmio_access(mr);
+
+ *l = memory_access_size(mr, *l, mr_addr);
+ /*
+ * XXX: could force current_cpu to NULL to avoid
+ * potential bugs
+ */
+
+ /*
+ * Assure Coverity (and ourselves) that we are not going to OVERRUN
+ * the buffer by following ldn_he_p().
+ */
+#ifdef QEMU_STATIC_ANALYSIS
+ assert((*l == 1 && len >= 1) ||
+ (*l == 2 && len >= 2) ||
+ (*l == 4 && len >= 4) ||
+ (*l == 8 && len >= 8));
+#endif
+ val = ldn_he_p(buf, *l);
+ result = memory_region_dispatch_write(mr, mr_addr, val,
+ size_memop(*l), attrs);
+ if (release_lock) {
+ bql_unlock();
+ }
+
+ return result;
+ } else {
+ /* RAM case */
+ uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l,
+ false);
+
+ memmove(ram_ptr, buf, *l);
+ invalidate_and_set_dirty(mr, mr_addr, *l);
+
+ return MEMTX_OK;
+ }
+}
+
/* Called within RCU critical section. */
static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr,
MemTxAttrs attrs,
const void *ptr,
- hwaddr len, hwaddr addr1,
+ hwaddr len, hwaddr mr_addr,
hwaddr l, MemoryRegion *mr)
{
- uint8_t *ram_ptr;
- uint64_t val;
MemTxResult result = MEMTX_OK;
- bool release_lock = false;
const uint8_t *buf = ptr;
for (;;) {
- if (!flatview_access_allowed(mr, attrs, addr1, l)) {
- result |= MEMTX_ACCESS_ERROR;
- /* Keep going. */
- } else if (!memory_access_is_direct(mr, true)) {
- release_lock |= prepare_mmio_access(mr);
- l = memory_access_size(mr, l, addr1);
- /* XXX: could force current_cpu to NULL to avoid
- potential bugs */
-
- /*
- * Assure Coverity (and ourselves) that we are not going to OVERRUN
- * the buffer by following ldn_he_p().
- */
-#ifdef QEMU_STATIC_ANALYSIS
- assert((l == 1 && len >= 1) ||
- (l == 2 && len >= 2) ||
- (l == 4 && len >= 4) ||
- (l == 8 && len >= 8));
-#endif
- val = ldn_he_p(buf, l);
- result |= memory_region_dispatch_write(mr, addr1, val,
- size_memop(l), attrs);
- } else {
- /* RAM case */
- ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false);
- memmove(ram_ptr, buf, l);
- invalidate_and_set_dirty(mr, addr1, l);
- }
-
- if (release_lock) {
- bql_unlock();
- release_lock = false;
- }
+ result |= flatview_write_continue_step(attrs, buf, len, mr_addr, &l,
+ mr);
len -= l;
buf += l;
@@ -2738,7 +2754,7 @@
}
l = len;
- mr = flatview_translate(fv, addr, &addr1, &l, true, attrs);
+ mr = flatview_translate(fv, addr, &mr_addr, &l, true, attrs);
}
return result;
@@ -2749,63 +2765,76 @@
const void *buf, hwaddr len)
{
hwaddr l;
- hwaddr addr1;
+ hwaddr mr_addr;
MemoryRegion *mr;
l = len;
- mr = flatview_translate(fv, addr, &addr1, &l, true, attrs);
+ mr = flatview_translate(fv, addr, &mr_addr, &l, true, attrs);
if (!flatview_access_allowed(mr, attrs, addr, len)) {
return MEMTX_ACCESS_ERROR;
}
return flatview_write_continue(fv, addr, attrs, buf, len,
- addr1, l, mr);
+ mr_addr, l, mr);
+}
+
+static MemTxResult flatview_read_continue_step(MemTxAttrs attrs, uint8_t *buf,
+ hwaddr len, hwaddr mr_addr,
+ hwaddr *l,
+ MemoryRegion *mr)
+{
+ if (!flatview_access_allowed(mr, attrs, mr_addr, *l)) {
+ return MEMTX_ACCESS_ERROR;
+ }
+
+ if (!memory_access_is_direct(mr, false)) {
+ /* I/O case */
+ uint64_t val;
+ MemTxResult result;
+ bool release_lock = prepare_mmio_access(mr);
+
+ *l = memory_access_size(mr, *l, mr_addr);
+ result = memory_region_dispatch_read(mr, mr_addr, &val, size_memop(*l),
+ attrs);
+
+ /*
+ * Assure Coverity (and ourselves) that we are not going to OVERRUN
+ * the buffer by following stn_he_p().
+ */
+#ifdef QEMU_STATIC_ANALYSIS
+ assert((*l == 1 && len >= 1) ||
+ (*l == 2 && len >= 2) ||
+ (*l == 4 && len >= 4) ||
+ (*l == 8 && len >= 8));
+#endif
+ stn_he_p(buf, *l, val);
+
+ if (release_lock) {
+ bql_unlock();
+ }
+ return result;
+ } else {
+ /* RAM case */
+ uint8_t *ram_ptr = qemu_ram_ptr_length(mr->ram_block, mr_addr, l,
+ false);
+
+ memcpy(buf, ram_ptr, *l);
+
+ return MEMTX_OK;
+ }
}
/* Called within RCU critical section. */
MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr,
MemTxAttrs attrs, void *ptr,
- hwaddr len, hwaddr addr1, hwaddr l,
+ hwaddr len, hwaddr mr_addr, hwaddr l,
MemoryRegion *mr)
{
- uint8_t *ram_ptr;
- uint64_t val;
MemTxResult result = MEMTX_OK;
- bool release_lock = false;
uint8_t *buf = ptr;
fuzz_dma_read_cb(addr, len, mr);
for (;;) {
- if (!flatview_access_allowed(mr, attrs, addr1, l)) {
- result |= MEMTX_ACCESS_ERROR;
- /* Keep going. */
- } else if (!memory_access_is_direct(mr, false)) {
- /* I/O case */
- release_lock |= prepare_mmio_access(mr);
- l = memory_access_size(mr, l, addr1);
- result |= memory_region_dispatch_read(mr, addr1, &val,
- size_memop(l), attrs);
-
- /*
- * Assure Coverity (and ourselves) that we are not going to OVERRUN
- * the buffer by following stn_he_p().
- */
-#ifdef QEMU_STATIC_ANALYSIS
- assert((l == 1 && len >= 1) ||
- (l == 2 && len >= 2) ||
- (l == 4 && len >= 4) ||
- (l == 8 && len >= 8));
-#endif
- stn_he_p(buf, l, val);
- } else {
- /* RAM case */
- ram_ptr = qemu_ram_ptr_length(mr->ram_block, addr1, &l, false);
- memcpy(buf, ram_ptr, l);
- }
-
- if (release_lock) {
- bql_unlock();
- release_lock = false;
- }
+ result |= flatview_read_continue_step(attrs, buf, len, mr_addr, &l, mr);
len -= l;
buf += l;
@@ -2816,7 +2845,7 @@
}
l = len;
- mr = flatview_translate(fv, addr, &addr1, &l, false, attrs);
+ mr = flatview_translate(fv, addr, &mr_addr, &l, false, attrs);
}
return result;
@@ -2827,16 +2856,16 @@
MemTxAttrs attrs, void *buf, hwaddr len)
{
hwaddr l;
- hwaddr addr1;
+ hwaddr mr_addr;
MemoryRegion *mr;
l = len;
- mr = flatview_translate(fv, addr, &addr1, &l, false, attrs);
+ mr = flatview_translate(fv, addr, &mr_addr, &l, false, attrs);
if (!flatview_access_allowed(mr, attrs, addr, len)) {
return MEMTX_ACCESS_ERROR;
}
return flatview_read_continue(fv, addr, attrs, buf, len,
- addr1, l, mr);
+ mr_addr, l, mr);
}
MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr,
@@ -3341,6 +3370,59 @@
return section.mr;
}
+/* Called within RCU critical section. */
+static MemTxResult address_space_write_continue_cached(MemTxAttrs attrs,
+ const void *ptr,
+ hwaddr len,
+ hwaddr mr_addr,
+ hwaddr l,
+ MemoryRegion *mr)
+{
+ MemTxResult result = MEMTX_OK;
+ const uint8_t *buf = ptr;
+
+ for (;;) {
+ result |= flatview_write_continue_step(attrs, buf, len, mr_addr, &l,
+ mr);
+
+ len -= l;
+ buf += l;
+ mr_addr += l;
+
+ if (!len) {
+ break;
+ }
+
+ l = len;
+ }
+
+ return result;
+}
+
+/* Called within RCU critical section. */
+static MemTxResult address_space_read_continue_cached(MemTxAttrs attrs,
+ void *ptr, hwaddr len,
+ hwaddr mr_addr, hwaddr l,
+ MemoryRegion *mr)
+{
+ MemTxResult result = MEMTX_OK;
+ uint8_t *buf = ptr;
+
+ for (;;) {
+ result |= flatview_read_continue_step(attrs, buf, len, mr_addr, &l, mr);
+ len -= l;
+ buf += l;
+ mr_addr += l;
+
+ if (!len) {
+ break;
+ }
+ l = len;
+ }
+
+ return result;
+}
+
/* Called from RCU critical section. address_space_read_cached uses this
* out of line function when the target is an MMIO or IOMMU region.
*/
@@ -3348,15 +3430,14 @@
address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr,
void *buf, hwaddr len)
{
- hwaddr addr1, l;
+ hwaddr mr_addr, l;
MemoryRegion *mr;
l = len;
- mr = address_space_translate_cached(cache, addr, &addr1, &l, false,
+ mr = address_space_translate_cached(cache, addr, &mr_addr, &l, false,
MEMTXATTRS_UNSPECIFIED);
- return flatview_read_continue(cache->fv,
- addr, MEMTXATTRS_UNSPECIFIED, buf, len,
- addr1, l, mr);
+ return address_space_read_continue_cached(MEMTXATTRS_UNSPECIFIED,
+ buf, len, mr_addr, l, mr);
}
/* Called from RCU critical section. address_space_write_cached uses this
@@ -3366,15 +3447,14 @@
address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr,
const void *buf, hwaddr len)
{
- hwaddr addr1, l;
+ hwaddr mr_addr, l;
MemoryRegion *mr;
l = len;
- mr = address_space_translate_cached(cache, addr, &addr1, &l, true,
+ mr = address_space_translate_cached(cache, addr, &mr_addr, &l, true,
MEMTXATTRS_UNSPECIFIED);
- return flatview_write_continue(cache->fv,
- addr, MEMTXATTRS_UNSPECIFIED, buf, len,
- addr1, l, mr);
+ return address_space_write_continue_cached(MEMTXATTRS_UNSPECIFIED,
+ buf, len, mr_addr, l, mr);
}
#define ARG1_DECL MemoryRegionCache *cache
diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c
index 09e07ca..c124389 100644
--- a/system/qdev-monitor.c
+++ b/system/qdev-monitor.c
@@ -38,7 +38,6 @@
#include "qemu/option_int.h"
#include "sysemu/block-backend.h"
#include "migration/misc.h"
-#include "migration/migration.h"
#include "qemu/cutils.h"
#include "hw/qdev-properties.h"
#include "hw/clock.h"
diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c
index c19978a..11a69a3 100644
--- a/target/loongarch/kvm/kvm.c
+++ b/target/loongarch/kvm/kvm.c
@@ -22,7 +22,6 @@
#include "hw/irq.h"
#include "qemu/log.h"
#include "hw/loader.h"
-#include "migration/migration.h"
#include "sysemu/runstate.h"
#include "cpu-csr.h"
#include "kvm_loongarch.h"
diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c
index c7afdb1..cda7d78 100644
--- a/target/riscv/kvm/kvm-cpu.c
+++ b/target/riscv/kvm/kvm-cpu.c
@@ -44,7 +44,7 @@
#include "kvm_riscv.h"
#include "sbi_ecall_interface.h"
#include "chardev/char-fe.h"
-#include "migration/migration.h"
+#include "migration/misc.h"
#include "sysemu/runstate.h"
#include "hw/riscv/numa.h"
@@ -729,7 +729,7 @@
* frequency. Therefore, we should check whether they are the same here
* during the migration.
*/
- if (migration_is_running(migrate_get_current()->state)) {
+ if (migration_is_running()) {
KVM_RISCV_GET_TIMER(cs, frequency, reg);
if (reg != env->kvm_timer_frequency) {
error_report("Dst Hosts timer frequency != Src Hosts");
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 4023d80..71895ab 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -2772,6 +2772,24 @@
}
static void *
+test_migrate_precopy_tcp_multifd_start_zero_page_legacy(QTestState *from,
+ QTestState *to)
+{
+ test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
+ migrate_set_parameter_str(from, "zero-page-detection", "legacy");
+ return NULL;
+}
+
+static void *
+test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from,
+ QTestState *to)
+{
+ test_migrate_precopy_tcp_multifd_start_common(from, to, "none");
+ migrate_set_parameter_str(from, "zero-page-detection", "none");
+ return NULL;
+}
+
+static void *
test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from,
QTestState *to)
{
@@ -2812,6 +2830,36 @@
test_precopy_common(&args);
}
+static void test_multifd_tcp_zero_page_legacy(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = test_migrate_precopy_tcp_multifd_start_zero_page_legacy,
+ /*
+ * Multifd is more complicated than most of the features, it
+ * directly takes guest page buffers when sending, make sure
+ * everything will work alright even if guest page is changing.
+ */
+ .live = true,
+ };
+ test_precopy_common(&args);
+}
+
+static void test_multifd_tcp_no_zero_page(void)
+{
+ MigrateCommon args = {
+ .listen_uri = "defer",
+ .start_hook = test_migration_precopy_tcp_multifd_start_no_zero_page,
+ /*
+ * Multifd is more complicated than most of the features, it
+ * directly takes guest page buffers when sending, make sure
+ * everything will work alright even if guest page is changing.
+ */
+ .live = true,
+ };
+ test_precopy_common(&args);
+}
+
static void test_multifd_tcp_zlib(void)
{
MigrateCommon args = {
@@ -3729,6 +3777,10 @@
}
migration_test_add("/migration/multifd/tcp/plain/none",
test_multifd_tcp_none);
+ migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy",
+ test_multifd_tcp_zero_page_legacy);
+ migration_test_add("/migration/multifd/tcp/plain/zero-page/none",
+ test_multifd_tcp_no_zero_page);
migration_test_add("/migration/multifd/tcp/plain/cancel",
test_multifd_tcp_cancel);
migration_test_add("/migration/multifd/tcp/plain/zlib",
diff --git a/tests/unit/test-vmstate.c b/tests/unit/test-vmstate.c
index c4f9faa..63f28f2 100644
--- a/tests/unit/test-vmstate.c
+++ b/tests/unit/test-vmstate.c
@@ -24,7 +24,6 @@
#include "qemu/osdep.h"
-#include "../migration/migration.h"
#include "migration/vmstate.h"
#include "migration/qemu-file-types.h"
#include "../migration/qemu-file.h"