Merge tag 'pull-sp-20231105' of https://gitlab.com/rth7680/qemu into staging
target/sparc: Explicitly compute condition codes
# -----BEGIN PGP SIGNATURE-----
#
# iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAmVH9oodHHJpY2hhcmQu
# aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV/M8QgAgPTp/wFLVnSRFLaN
# fBoelVhM4WTWMQ+SUwZMtCvqcMHaBxIMu+hyk5MI11hFOUi9N+vWvRb+NZ6JbK+1
# sqWcx0NdYfNdOeoi1dgzGgcCkFA8u9zW/K7Ih0W8WuU20uiJ4Zw/qmnEELIl/mZR
# 5Ft1mhLMhQSYsH0KSypugLWBxR9SFNH1cV3C1SG2q+6snm/mhKk9NN18zJGFdmmY
# 4CQThx159P/DaPUONZbSAMN94opu6K8FSymELPDUZBYwJRq7fyGKYuDUGRvN1kxx
# I8p/MF1V5Vcth9lvGyBYulFWjo9BDMpkIdmWzXZLOWfzZVAed8PcglxoQqgMbU5u
# eyY/Cw==
# =Tv1h
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 06 Nov 2023 04:09:46 HKT
# gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F
# gpg: issuer "richard.henderson@linaro.org"
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full]
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F
* tag 'pull-sp-20231105' of https://gitlab.com/rth7680/qemu: (21 commits)
target/sparc: Check for invalid cond in gen_compare_reg
target/sparc: Implement UDIV inline
target/sparc: Implement UDIVX and SDIVX inline
target/sparc: Discard cpu_cond at the end of each insn
target/sparc: Record entire jump condition in DisasContext
target/sparc: Merge gen_op_next_insn into only caller
target/sparc: Pass displacement to advance_jump_cond
target/sparc: Merge advance_jump_uncond_{never,always} into advance_jump_cond
target/sparc: Merge gen_branch2 into advance_pc
target/sparc: Do flush_cond in advance_jump_cond
target/sparc: Always copy conditions into a new temporary
target/sparc: Change DisasCompare.c2 to int
target/sparc: Remove DisasCompare.is_bool
target/sparc: Remove CC_OP leftovers
target/sparc: Remove CC_OP_TADDTV, CC_OP_TSUBTV
target/sparc: Remove CC_OP_SUB, CC_OP_SUBX, CC_OP_TSUB
target/sparc: Remove CC_OP_ADD, CC_OP_ADDX, CC_OP_TADD
target/sparc: Remove CC_OP_DIV
target/sparc: Remove CC_OP_LOGIC
target/sparc: Split psr and xcc into components
...
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst
index 240eb16..5adf4f1 100644
--- a/docs/devel/migration.rst
+++ b/docs/devel/migration.rst
@@ -594,6 +594,77 @@
Return path - opened by main thread, written by main thread AND postcopy
thread (protected by rp_mutex)
+Dirty limit
+=====================
+The dirty limit, short for dirty page rate upper limit, is a new capability
+introduced in the 8.1 QEMU release that uses a new algorithm based on the KVM
+dirty ring to throttle down the guest during live migration.
+
+The algorithm framework is as follows:
+
+::
+
+ ------------------------------------------------------------------------------
+ main --------------> throttle thread ------------> PREPARE(1) <--------
+ thread \ | |
+ \ | |
+ \ V |
+ -\ CALCULATE(2) |
+ \ | |
+ \ | |
+ \ V |
+ \ SET PENALTY(3) -----
+ -\ |
+ \ |
+ \ V
+ -> virtual CPU thread -------> ACCEPT PENALTY(4)
+ ------------------------------------------------------------------------------
+
+When the qmp command qmp_set_vcpu_dirty_limit is called for the first time,
+the QEMU main thread starts the throttle thread. The throttle thread, once
+launched, executes the loop, which consists of three steps:
+
+ - PREPARE (1)
+
+ The entire work of PREPARE (1) is preparation for the second stage,
+ CALCULATE(2), as the name implies. It involves preparing the dirty
+ page rate value and the corresponding upper limit of the VM:
+ The dirty page rate is calculated via the KVM dirty ring mechanism,
+ which tells QEMU how many dirty pages a virtual CPU has had since the
+ last KVM_EXIT_DIRTY_RING_FULL exception; The dirty page rate upper
+ limit is specified by caller, therefore fetch it directly.
+
+ - CALCULATE (2)
+
+ Calculate a suitable sleep period for each virtual CPU, which will be
+ used to determine the penalty for the target virtual CPU. The
+ computation must be done carefully in order to reduce the dirty page
+ rate progressively down to the upper limit without oscillation. To
+ achieve this, two strategies are provided: the first is to add or
+ subtract sleep time based on the ratio of the current dirty page rate
+ to the limit, which is used when the current dirty page rate is far
+ from the limit; the second is to add or subtract a fixed time when
+ the current dirty page rate is close to the limit.
+
+ - SET PENALTY (3)
+
+ Set the sleep time for each virtual CPU that should be penalized based
+ on the results of the calculation supplied by step CALCULATE (2).
+
+After completing the three above stages, the throttle thread loops back
+to step PREPARE (1) until the dirty limit is reached.
+
+On the other hand, each virtual CPU thread reads the sleep duration and
+sleeps in the path of the KVM_EXIT_DIRTY_RING_FULL exception handler, that
+is ACCEPT PENALTY (4). Virtual CPUs tied with writing processes will
+obviously exit to the path and get penalized, whereas virtual CPUs involved
+with read processes will not.
+
+In summary, thanks to the KVM dirty ring technology, the dirty limit
+algorithm will restrict virtual CPUs as needed to keep their dirty page
+rate inside the limit. This leads to more steady reading performance during
+live migration and can aid in improving large guest responsiveness.
+
Postcopy
========
diff --git a/dump/dump-hmp-cmds.c b/dump/dump-hmp-cmds.c
index b038785..b428ec3 100644
--- a/dump/dump-hmp-cmds.c
+++ b/dump/dump-hmp-cmds.c
@@ -19,6 +19,7 @@
bool paging = qdict_get_try_bool(qdict, "paging", false);
bool zlib = qdict_get_try_bool(qdict, "zlib", false);
bool lzo = qdict_get_try_bool(qdict, "lzo", false);
+ bool raw = qdict_get_try_bool(qdict, "raw", false);
bool snappy = qdict_get_try_bool(qdict, "snappy", false);
const char *file = qdict_get_str(qdict, "filename");
bool has_begin = qdict_haskey(qdict, "begin");
@@ -40,16 +41,28 @@
dump_format = DUMP_GUEST_MEMORY_FORMAT_WIN_DMP;
}
- if (zlib) {
- dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB;
+ if (zlib && raw) {
+ if (raw) {
+ dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB;
+ } else {
+ dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB;
+ }
}
if (lzo) {
- dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO;
+ if (raw) {
+ dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO;
+ } else {
+ dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO;
+ }
}
if (snappy) {
- dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY;
+ if (raw) {
+ dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY;
+ } else {
+ dump_format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY;
+ }
}
if (has_begin) {
diff --git a/dump/dump.c b/dump/dump.c
index d355ada..1c304ca 100644
--- a/dump/dump.c
+++ b/dump/dump.c
@@ -100,7 +100,7 @@
memory_mapping_list_free(&s->list);
close(s->fd);
g_free(s->guest_note);
- g_array_unref(s->string_table_buf);
+ g_clear_pointer(&s->string_table_buf, g_array_unref);
s->guest_note = NULL;
if (s->resume) {
if (s->detached) {
@@ -809,11 +809,15 @@
dump_end(s, errp);
}
-static int write_start_flat_header(int fd)
+static int write_start_flat_header(DumpState *s)
{
MakedumpfileHeader *mh;
int ret = 0;
+ if (s->kdump_raw) {
+ return 0;
+ }
+
QEMU_BUILD_BUG_ON(sizeof *mh > MAX_SIZE_MDF_HEADER);
mh = g_malloc0(MAX_SIZE_MDF_HEADER);
@@ -824,7 +828,7 @@
mh->version = cpu_to_be64(VERSION_FLAT_HEADER);
size_t written_size;
- written_size = qemu_write_full(fd, mh, MAX_SIZE_MDF_HEADER);
+ written_size = qemu_write_full(s->fd, mh, MAX_SIZE_MDF_HEADER);
if (written_size != MAX_SIZE_MDF_HEADER) {
ret = -1;
}
@@ -833,15 +837,19 @@
return ret;
}
-static int write_end_flat_header(int fd)
+static int write_end_flat_header(DumpState *s)
{
MakedumpfileDataHeader mdh;
+ if (s->kdump_raw) {
+ return 0;
+ }
+
mdh.offset = END_FLAG_FLAT_HEADER;
mdh.buf_size = END_FLAG_FLAT_HEADER;
size_t written_size;
- written_size = qemu_write_full(fd, &mdh, sizeof(mdh));
+ written_size = qemu_write_full(s->fd, &mdh, sizeof(mdh));
if (written_size != sizeof(mdh)) {
return -1;
}
@@ -849,20 +857,28 @@
return 0;
}
-static int write_buffer(int fd, off_t offset, const void *buf, size_t size)
+static int write_buffer(DumpState *s, off_t offset, const void *buf, size_t size)
{
size_t written_size;
MakedumpfileDataHeader mdh;
+ off_t seek_loc;
- mdh.offset = cpu_to_be64(offset);
- mdh.buf_size = cpu_to_be64(size);
+ if (s->kdump_raw) {
+ seek_loc = lseek(s->fd, offset, SEEK_SET);
+ if (seek_loc == (off_t) -1) {
+ return -1;
+ }
+ } else {
+ mdh.offset = cpu_to_be64(offset);
+ mdh.buf_size = cpu_to_be64(size);
- written_size = qemu_write_full(fd, &mdh, sizeof(mdh));
- if (written_size != sizeof(mdh)) {
- return -1;
+ written_size = qemu_write_full(s->fd, &mdh, sizeof(mdh));
+ if (written_size != sizeof(mdh)) {
+ return -1;
+ }
}
- written_size = qemu_write_full(fd, buf, size);
+ written_size = qemu_write_full(s->fd, buf, size);
if (written_size != size) {
return -1;
}
@@ -982,7 +998,7 @@
#endif
dh->status = cpu_to_dump32(s, status);
- if (write_buffer(s->fd, 0, dh, size) < 0) {
+ if (write_buffer(s, 0, dh, size) < 0) {
error_setg(errp, "dump: failed to write disk dump header");
goto out;
}
@@ -1012,7 +1028,7 @@
kh->offset_note = cpu_to_dump64(s, offset_note);
kh->note_size = cpu_to_dump32(s, s->note_size);
- if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
+ if (write_buffer(s, DISKDUMP_HEADER_BLOCKS *
block_size, kh, size) < 0) {
error_setg(errp, "dump: failed to write kdump sub header");
goto out;
@@ -1027,7 +1043,7 @@
if (*errp) {
goto out;
}
- if (write_buffer(s->fd, offset_note, s->note_buf,
+ if (write_buffer(s, offset_note, s->note_buf,
s->note_size) < 0) {
error_setg(errp, "dump: failed to write notes");
goto out;
@@ -1093,7 +1109,7 @@
#endif
dh->status = cpu_to_dump32(s, status);
- if (write_buffer(s->fd, 0, dh, size) < 0) {
+ if (write_buffer(s, 0, dh, size) < 0) {
error_setg(errp, "dump: failed to write disk dump header");
goto out;
}
@@ -1123,7 +1139,7 @@
kh->offset_note = cpu_to_dump64(s, offset_note);
kh->note_size = cpu_to_dump64(s, s->note_size);
- if (write_buffer(s->fd, DISKDUMP_HEADER_BLOCKS *
+ if (write_buffer(s, DISKDUMP_HEADER_BLOCKS *
block_size, kh, size) < 0) {
error_setg(errp, "dump: failed to write kdump sub header");
goto out;
@@ -1139,7 +1155,7 @@
goto out;
}
- if (write_buffer(s->fd, offset_note, s->note_buf,
+ if (write_buffer(s, offset_note, s->note_buf,
s->note_size) < 0) {
error_setg(errp, "dump: failed to write notes");
goto out;
@@ -1204,7 +1220,7 @@
while (old_offset < new_offset) {
/* calculate the offset and write dump_bitmap */
offset_bitmap1 = s->offset_dump_bitmap + old_offset;
- if (write_buffer(s->fd, offset_bitmap1, buf,
+ if (write_buffer(s, offset_bitmap1, buf,
bitmap_bufsize) < 0) {
return -1;
}
@@ -1212,7 +1228,7 @@
/* dump level 1 is chosen, so 1st and 2nd bitmap are same */
offset_bitmap2 = s->offset_dump_bitmap + s->len_dump_bitmap +
old_offset;
- if (write_buffer(s->fd, offset_bitmap2, buf,
+ if (write_buffer(s, offset_bitmap2, buf,
bitmap_bufsize) < 0) {
return -1;
}
@@ -1380,7 +1396,7 @@
static void prepare_data_cache(DataCache *data_cache, DumpState *s,
off_t offset)
{
- data_cache->fd = s->fd;
+ data_cache->state = s;
data_cache->data_size = 0;
data_cache->buf_size = 4 * dump_bitmap_get_bufsize(s);
data_cache->buf = g_malloc0(data_cache->buf_size);
@@ -1399,11 +1415,11 @@
/*
* if flag_sync is set, synchronize data in dc->buf into vmcore.
* otherwise check if the space is enough for caching data in buf, if not,
- * write the data in dc->buf to dc->fd and reset dc->buf
+ * write the data in dc->buf to dc->state->fd and reset dc->buf
*/
if ((!flag_sync && dc->data_size + size > dc->buf_size) ||
(flag_sync && dc->data_size > 0)) {
- if (write_buffer(dc->fd, dc->offset, dc->buf, dc->data_size) < 0) {
+ if (write_buffer(dc->state, dc->offset, dc->buf, dc->data_size) < 0) {
return -1;
}
@@ -1644,7 +1660,7 @@
* +------------------------------------------+
*/
- ret = write_start_flat_header(s->fd);
+ ret = write_start_flat_header(s);
if (ret < 0) {
error_setg(errp, "dump: failed to write start flat header");
return;
@@ -1665,33 +1681,13 @@
return;
}
- ret = write_end_flat_header(s->fd);
+ ret = write_end_flat_header(s);
if (ret < 0) {
error_setg(errp, "dump: failed to write end flat header");
return;
}
}
-static int validate_start_block(DumpState *s)
-{
- GuestPhysBlock *block;
-
- if (!dump_has_filter(s)) {
- return 0;
- }
-
- QTAILQ_FOREACH(block, &s->guest_phys_blocks.head, next) {
- /* This block is out of the range */
- if (block->target_start >= s->filter_area_begin + s->filter_area_length ||
- block->target_end <= s->filter_area_begin) {
- continue;
- }
- return 0;
- }
-
- return -1;
-}
-
static void get_max_mapnr(DumpState *s)
{
GuestPhysBlock *last_block;
@@ -1775,7 +1771,8 @@
static void dump_init(DumpState *s, int fd, bool has_format,
DumpGuestMemoryFormat format, bool paging, bool has_filter,
- int64_t begin, int64_t length, Error **errp)
+ int64_t begin, int64_t length, bool kdump_raw,
+ Error **errp)
{
ERRP_GUARD();
VMCoreInfoState *vmci = vmcoreinfo_find();
@@ -1786,6 +1783,7 @@
s->has_format = has_format;
s->format = format;
s->written_size = 0;
+ s->kdump_raw = kdump_raw;
/* kdump-compressed is conflict with paging and filter */
if (has_format && format != DUMP_GUEST_MEMORY_FORMAT_ELF) {
@@ -1810,7 +1808,7 @@
s->fd = fd;
if (has_filter && !length) {
- error_setg(errp, QERR_INVALID_PARAMETER, "length");
+ error_setg(errp, "parameter 'length' expects a non-zero size");
goto cleanup;
}
s->filter_area_begin = begin;
@@ -1839,12 +1837,6 @@
goto cleanup;
}
- /* Is the filter filtering everything? */
- if (validate_start_block(s) == -1) {
- error_setg(errp, QERR_INVALID_PARAMETER, "begin");
- goto cleanup;
- }
-
/* get dump info: endian, class and architecture.
* If the target architecture is not supported, cpu_get_dump_info() will
* return -1.
@@ -2061,17 +2053,19 @@
return result;
}
-void qmp_dump_guest_memory(bool paging, const char *file,
+void qmp_dump_guest_memory(bool paging, const char *protocol,
bool has_detach, bool detach,
- bool has_begin, int64_t begin, bool has_length,
- int64_t length, bool has_format,
- DumpGuestMemoryFormat format, Error **errp)
+ bool has_begin, int64_t begin,
+ bool has_length, int64_t length,
+ bool has_format, DumpGuestMemoryFormat format,
+ Error **errp)
{
ERRP_GUARD();
const char *p;
- int fd = -1;
+ int fd;
DumpState *s;
bool detach_p = false;
+ bool kdump_raw = false;
if (runstate_check(RUN_STATE_INMIGRATE)) {
error_setg(errp, "Dump not allowed during incoming migration.");
@@ -2086,6 +2080,29 @@
}
/*
+ * externally, we represent kdump-raw-* as separate formats, but internally
+ * they are handled the same, except for the "raw" flag
+ */
+ if (has_format) {
+ switch (format) {
+ case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB:
+ format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB;
+ kdump_raw = true;
+ break;
+ case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO:
+ format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO;
+ kdump_raw = true;
+ break;
+ case DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY:
+ format = DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY;
+ kdump_raw = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
* kdump-compressed format need the whole memory dumped, so paging or
* filter is not supported here.
*/
@@ -2127,25 +2144,23 @@
return;
}
-#if !defined(WIN32)
- if (strstart(file, "fd:", &p)) {
+ if (strstart(protocol, "fd:", &p)) {
fd = monitor_get_fd(monitor_cur(), p, errp);
if (fd == -1) {
return;
}
- }
-#endif
-
- if (strstart(file, "file:", &p)) {
- fd = qemu_open_old(p, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR);
+ } else if (strstart(protocol, "file:", &p)) {
+ fd = qemu_create(p, O_WRONLY | O_TRUNC | O_BINARY, S_IRUSR, errp);
if (fd < 0) {
- error_setg_file_open(errp, errno, p);
return;
}
+ } else {
+ error_setg(errp,
+ "parameter 'protocol' must start with 'file:' or 'fd:'");
+ return;
}
-
- if (fd == -1) {
- error_setg(errp, QERR_INVALID_PARAMETER, "protocol");
+ if (kdump_raw && lseek(fd, 0, SEEK_CUR) == (off_t) -1) {
+ error_setg(errp, "kdump-raw formats require a seekable file");
return;
}
@@ -2168,7 +2183,7 @@
dump_state_prepare(s);
dump_init(s, fd, has_format, format, paging, has_begin,
- begin, length, errp);
+ begin, length, kdump_raw, errp);
if (*errp) {
qatomic_set(&s->status, DUMP_STATUS_FAILED);
return;
@@ -2196,15 +2211,18 @@
/* kdump-zlib is always available */
QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_ZLIB);
+ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_ZLIB);
/* add new item if kdump-lzo is available */
#ifdef CONFIG_LZO
QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_LZO);
+ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_LZO);
#endif
/* add new item if kdump-snappy is available */
#ifdef CONFIG_SNAPPY
QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_SNAPPY);
+ QAPI_LIST_APPEND(tail, DUMP_GUEST_MEMORY_FORMAT_KDUMP_RAW_SNAPPY);
#endif
if (win_dump_available(NULL)) {
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 63eac22..c0a2768 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1085,14 +1085,16 @@
{
.name = "dump-guest-memory",
- .args_type = "paging:-p,detach:-d,windmp:-w,zlib:-z,lzo:-l,snappy:-s,filename:F,begin:l?,length:l?",
- .params = "[-p] [-d] [-z|-l|-s|-w] filename [begin length]",
+ .args_type = "paging:-p,detach:-d,windmp:-w,zlib:-z,lzo:-l,snappy:-s,raw:-R,filename:F,begin:l?,length:l?",
+ .params = "[-p] [-d] [-z|-l|-s|-w] [-R] filename [begin length]",
.help = "dump guest memory into file 'filename'.\n\t\t\t"
"-p: do paging to get guest's memory mapping.\n\t\t\t"
"-d: return immediately (do not wait for completion).\n\t\t\t"
"-z: dump in kdump-compressed format, with zlib compression.\n\t\t\t"
"-l: dump in kdump-compressed format, with lzo compression.\n\t\t\t"
"-s: dump in kdump-compressed format, with snappy compression.\n\t\t\t"
+ "-R: when using kdump (-z, -l, -s), use raw rather than makedumpfile-flattened\n\t\t\t"
+ " format\n\t\t\t"
"-w: dump in Windows crashdump format (can be used instead of ELF-dump converting),\n\t\t\t"
" for Windows x86 and x64 guests with vmcoreinfo driver only.\n\t\t\t"
"begin: the starting physical address.\n\t\t\t"
@@ -1115,6 +1117,9 @@
dump in kdump-compressed format, with lzo compression.
``-s``
dump in kdump-compressed format, with snappy compression.
+ ``-R``
+ when using kdump (-z, -l, -s), use raw rather than makedumpfile-flattened
+ format
``-w``
dump in Windows crashdump format (can be used instead of ELF-dump converting),
for Windows x64 guests with vmcoreinfo driver only
diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h
index 7008d43..d702854 100644
--- a/include/sysemu/dump.h
+++ b/include/sysemu/dump.h
@@ -137,7 +137,7 @@
} KdumpSubHeader64;
typedef struct DataCache {
- int fd; /* fd of the file where to write the cached data */
+ DumpState *state; /* dump state related to this data */
uint8_t *buf; /* buffer for cached data */
size_t buf_size; /* size of the buf */
size_t data_size; /* size of cached data in buf */
@@ -157,6 +157,7 @@
MemoryMappingList list;
bool resume;
bool detached;
+ bool kdump_raw;
hwaddr memory_offset;
int fd;
diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c
index 894fdd1..73d7b67 100644
--- a/linux-user/loongarch64/cpu_loop.c
+++ b/linux-user/loongarch64/cpu_loop.c
@@ -72,6 +72,19 @@
case EXCCODE_BCE:
force_sig_fault(TARGET_SIGSYS, TARGET_SI_KERNEL, env->pc);
break;
+
+ /*
+ * Begin with LSX and LASX disabled, then enable on the first trap.
+ * In this way we can tell if the unit is in use. This is used to
+ * choose the layout of any signal frame.
+ */
+ case EXCCODE_SXD:
+ env->CSR_EUEN |= R_CSR_EUEN_SXE_MASK;
+ break;
+ case EXCCODE_ASXD:
+ env->CSR_EUEN |= R_CSR_EUEN_ASXE_MASK;
+ break;
+
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c
index afcee64..39ea82c 100644
--- a/linux-user/loongarch64/signal.c
+++ b/linux-user/loongarch64/signal.c
@@ -18,10 +18,10 @@
#define SC_USED_FP (1 << 0)
struct target_sigcontext {
- uint64_t sc_pc;
- uint64_t sc_regs[32];
- uint32_t sc_flags;
- uint64_t sc_extcontext[0] QEMU_ALIGNED(16);
+ abi_ulong sc_pc;
+ abi_ulong sc_regs[32];
+ abi_uint sc_flags;
+ abi_ulong sc_extcontext[0] QEMU_ALIGNED(16);
};
QEMU_BUILD_BUG_ON(sizeof(struct target_sigcontext) != sizeof_sigcontext);
@@ -33,19 +33,35 @@
#define FPU_CTX_MAGIC 0x46505501
#define FPU_CTX_ALIGN 8
struct target_fpu_context {
- uint64_t regs[32];
- uint64_t fcc;
- uint32_t fcsr;
+ abi_ulong regs[32];
+ abi_ulong fcc;
+ abi_uint fcsr;
} QEMU_ALIGNED(FPU_CTX_ALIGN);
QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs)
!= offsetof_fpucontext_fr);
+#define LSX_CTX_MAGIC 0x53580001
+#define LSX_CTX_ALIGN 16
+struct target_lsx_context {
+ abi_ulong regs[2 * 32];
+ abi_ulong fcc;
+ abi_uint fcsr;
+} QEMU_ALIGNED(LSX_CTX_ALIGN);
+
+#define LASX_CTX_MAGIC 0x41535801
+#define LASX_CTX_ALIGN 32
+struct target_lasx_context {
+ abi_ulong regs[4 * 32];
+ abi_ulong fcc;
+ abi_uint fcsr;
+} QEMU_ALIGNED(LASX_CTX_ALIGN);
+
#define CONTEXT_INFO_ALIGN 16
struct target_sctx_info {
- uint32_t magic;
- uint32_t size;
- uint64_t padding;
+ abi_uint magic;
+ abi_uint size;
+ abi_ulong padding;
} QEMU_ALIGNED(CONTEXT_INFO_ALIGN);
QEMU_BUILD_BUG_ON(sizeof(struct target_sctx_info) != sizeof_sctx_info);
@@ -81,9 +97,11 @@
};
struct extctx_layout {
- unsigned int size;
+ unsigned long size;
unsigned int flags;
struct ctx_layout fpu;
+ struct ctx_layout lsx;
+ struct ctx_layout lasx;
struct ctx_layout end;
};
@@ -105,7 +123,8 @@
return sp;
}
-static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp)
+static abi_ptr setup_extcontext(CPULoongArchState *env,
+ struct extctx_layout *extctx, abi_ptr sp)
{
memset(extctx, 0, sizeof(struct extctx_layout));
@@ -114,8 +133,17 @@
/* For qemu, there is no lazy fp context switch, so fp always present. */
extctx->flags = SC_USED_FP;
- sp = extframe_alloc(extctx, &extctx->fpu,
- sizeof(struct target_rt_sigframe), FPU_CTX_ALIGN, sp);
+
+ if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) {
+ sp = extframe_alloc(extctx, &extctx->lasx,
+ sizeof(struct target_lasx_context), LASX_CTX_ALIGN, sp);
+ } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) {
+ sp = extframe_alloc(extctx, &extctx->lsx,
+ sizeof(struct target_lsx_context), LSX_CTX_ALIGN, sp);
+ } else {
+ sp = extframe_alloc(extctx, &extctx->fpu,
+ sizeof(struct target_fpu_context), FPU_CTX_ALIGN, sp);
+ }
return sp;
}
@@ -125,7 +153,6 @@
struct extctx_layout *extctx)
{
struct target_sctx_info *info;
- struct target_fpu_context *fpu_ctx;
int i;
__put_user(extctx->flags, &sc->sc_flags);
@@ -136,25 +163,63 @@
}
/*
- * Set fpu context
+ * Set extension context
*/
- info = extctx->fpu.haddr;
- __put_user(FPU_CTX_MAGIC, &info->magic);
- __put_user(extctx->fpu.size, &info->size);
- fpu_ctx = (struct target_fpu_context *)(info + 1);
- for (i = 0; i < 32; ++i) {
- __put_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]);
+ if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) {
+ struct target_lasx_context *lasx_ctx;
+ info = extctx->lasx.haddr;
+
+ __put_user(LASX_CTX_MAGIC, &info->magic);
+ __put_user(extctx->lasx.size, &info->size);
+
+ lasx_ctx = (struct target_lasx_context *)(info + 1);
+
+ for (i = 0; i < 32; ++i) {
+ __put_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]);
+ __put_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]);
+ __put_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]);
+ __put_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]);
+ }
+ __put_user(read_fcc(env), &lasx_ctx->fcc);
+ __put_user(env->fcsr0, &lasx_ctx->fcsr);
+ } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) {
+ struct target_lsx_context *lsx_ctx;
+ info = extctx->lsx.haddr;
+
+ __put_user(LSX_CTX_MAGIC, &info->magic);
+ __put_user(extctx->lsx.size, &info->size);
+
+ lsx_ctx = (struct target_lsx_context *)(info + 1);
+
+ for (i = 0; i < 32; ++i) {
+ __put_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]);
+ __put_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]);
+ }
+ __put_user(read_fcc(env), &lsx_ctx->fcc);
+ __put_user(env->fcsr0, &lsx_ctx->fcsr);
+ } else {
+ struct target_fpu_context *fpu_ctx;
+ info = extctx->fpu.haddr;
+
+ __put_user(FPU_CTX_MAGIC, &info->magic);
+ __put_user(extctx->fpu.size, &info->size);
+
+ fpu_ctx = (struct target_fpu_context *)(info + 1);
+
+ for (i = 0; i < 32; ++i) {
+ __put_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]);
+ }
+ __put_user(read_fcc(env), &fpu_ctx->fcc);
+ __put_user(env->fcsr0, &fpu_ctx->fcsr);
}
- __put_user(read_fcc(env), &fpu_ctx->fcc);
- __put_user(env->fcsr0, &fpu_ctx->fcsr);
/*
* Set end context
*/
info = extctx->end.haddr;
__put_user(0, &info->magic);
- __put_user(extctx->end.size, &info->size);
+ __put_user(0, &info->size);
}
static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame)
@@ -162,7 +227,7 @@
memset(extctx, 0, sizeof(*extctx));
while (1) {
- uint32_t magic, size;
+ abi_uint magic, size;
if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) {
return false;
@@ -184,6 +249,24 @@
extctx->fpu.size = size;
extctx->size += size;
break;
+ case LSX_CTX_MAGIC:
+ if (size < (sizeof(struct target_sctx_info) +
+ sizeof(struct target_lsx_context))) {
+ return false;
+ }
+ extctx->lsx.gaddr = frame;
+ extctx->lsx.size = size;
+ extctx->size += size;
+ break;
+ case LASX_CTX_MAGIC:
+ if (size < (sizeof(struct target_sctx_info) +
+ sizeof(struct target_lasx_context))) {
+ return false;
+ }
+ extctx->lasx.gaddr = frame;
+ extctx->lasx.size = size;
+ extctx->size += size;
+ break;
default:
return false;
}
@@ -197,19 +280,45 @@
struct extctx_layout *extctx)
{
int i;
+ abi_ulong fcc;
__get_user(env->pc, &sc->sc_pc);
for (i = 1; i < 32; ++i) {
__get_user(env->gpr[i], &sc->sc_regs[i]);
}
- if (extctx->fpu.haddr) {
- struct target_fpu_context *fpu_ctx =
- extctx->fpu.haddr + sizeof(struct target_sctx_info);
- uint64_t fcc;
+ if (extctx->lasx.haddr) {
+ struct target_lasx_context *lasx_ctx =
+ extctx->lasx.haddr + sizeof(struct target_sctx_info);
for (i = 0; i < 32; ++i) {
- __get_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]);
+ __get_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]);
+ __get_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]);
+ __get_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]);
+ __get_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]);
+ }
+ __get_user(fcc, &lasx_ctx->fcc);
+ write_fcc(env, fcc);
+ __get_user(env->fcsr0, &lasx_ctx->fcsr);
+ restore_fp_status(env);
+ } else if (extctx->lsx.haddr) {
+ struct target_lsx_context *lsx_ctx =
+ extctx->lsx.haddr + sizeof(struct target_sctx_info);
+
+ for (i = 0; i < 32; ++i) {
+ __get_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]);
+ __get_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]);
+ }
+ __get_user(fcc, &lsx_ctx->fcc);
+ write_fcc(env, fcc);
+ __get_user(env->fcsr0, &lsx_ctx->fcsr);
+ restore_fp_status(env);
+ } else if (extctx->fpu.haddr) {
+ struct target_fpu_context *fpu_ctx =
+ extctx->fpu.haddr + sizeof(struct target_sctx_info);
+
+ for (i = 0; i < 32; ++i) {
+ __get_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]);
}
__get_user(fcc, &fpu_ctx->fcc);
write_fcc(env, fcc);
@@ -229,7 +338,7 @@
sp = target_sigsp(get_sp_from_cpustate(env), ka);
sp = ROUND_DOWN(sp, 16);
- sp = setup_extcontext(extctx, sp);
+ sp = setup_extcontext(env, extctx, sp);
sp -= sizeof(struct target_rt_sigframe);
assert(QEMU_IS_ALIGNED(sp, 16));
@@ -255,8 +364,17 @@
force_sigsegv(sig);
return;
}
- extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
- extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
+
+ if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) {
+ extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr);
+ extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
+ } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) {
+ extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr);
+ extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
+ } else {
+ extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
+ extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
+ }
tswap_siginfo(&frame->rs_info, info);
@@ -299,7 +417,12 @@
if (!frame) {
goto badframe;
}
- if (extctx.fpu.gaddr) {
+
+ if (extctx.lasx.gaddr) {
+ extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr);
+ } else if (extctx.lsx.gaddr) {
+ extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr);
+ } else if (extctx.fpu.gaddr) {
extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
}
diff --git a/migration/ram.c b/migration/ram.c
index a0f3b86..8c7886a 100644
--- a/migration/ram.c
+++ b/migration/ram.c
@@ -3030,71 +3030,71 @@
* MAX_WAIT (if curious, further see commit 4508bd9ed8053ce) below, which
* guarantees that we'll at least released it in a regular basis.
*/
- qemu_mutex_lock(&rs->bitmap_mutex);
- WITH_RCU_READ_LOCK_GUARD() {
- if (ram_list.version != rs->last_version) {
- ram_state_reset(rs);
- }
-
- /* Read version before ram_list.blocks */
- smp_rmb();
-
- ret = rdma_registration_start(f, RAM_CONTROL_ROUND);
- if (ret < 0) {
- qemu_file_set_error(f, ret);
- goto out;
- }
-
- t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
- i = 0;
- while ((ret = migration_rate_exceeded(f)) == 0 ||
- postcopy_has_request(rs)) {
- int pages;
-
- if (qemu_file_get_error(f)) {
- break;
+ WITH_QEMU_LOCK_GUARD(&rs->bitmap_mutex) {
+ WITH_RCU_READ_LOCK_GUARD() {
+ if (ram_list.version != rs->last_version) {
+ ram_state_reset(rs);
}
- pages = ram_find_and_save_block(rs);
- /* no more pages to sent */
- if (pages == 0) {
- done = 1;
- break;
+ /* Read version before ram_list.blocks */
+ smp_rmb();
+
+ ret = rdma_registration_start(f, RAM_CONTROL_ROUND);
+ if (ret < 0) {
+ qemu_file_set_error(f, ret);
+ goto out;
}
- if (pages < 0) {
- qemu_file_set_error(f, pages);
- break;
- }
+ t0 = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ i = 0;
+ while ((ret = migration_rate_exceeded(f)) == 0 ||
+ postcopy_has_request(rs)) {
+ int pages;
- rs->target_page_count += pages;
-
- /*
- * During postcopy, it is necessary to make sure one whole host
- * page is sent in one chunk.
- */
- if (migrate_postcopy_ram()) {
- compress_flush_data();
- }
-
- /*
- * we want to check in the 1st loop, just in case it was the 1st
- * time and we had to sync the dirty bitmap.
- * qemu_clock_get_ns() is a bit expensive, so we only check each
- * some iterations
- */
- if ((i & 63) == 0) {
- uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) /
- 1000000;
- if (t1 > MAX_WAIT) {
- trace_ram_save_iterate_big_wait(t1, i);
+ if (qemu_file_get_error(f)) {
break;
}
+
+ pages = ram_find_and_save_block(rs);
+ /* no more pages to sent */
+ if (pages == 0) {
+ done = 1;
+ break;
+ }
+
+ if (pages < 0) {
+ qemu_file_set_error(f, pages);
+ break;
+ }
+
+ rs->target_page_count += pages;
+
+ /*
+ * During postcopy, it is necessary to make sure one whole host
+ * page is sent in one chunk.
+ */
+ if (migrate_postcopy_ram()) {
+ compress_flush_data();
+ }
+
+ /*
+ * we want to check in the 1st loop, just in case it was the 1st
+ * time and we had to sync the dirty bitmap.
+ * qemu_clock_get_ns() is a bit expensive, so we only check each
+ * some iterations
+ */
+ if ((i & 63) == 0) {
+ uint64_t t1 = (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - t0) /
+ 1000000;
+ if (t1 > MAX_WAIT) {
+ trace_ram_save_iterate_big_wait(t1, i);
+ break;
+ }
+ }
+ i++;
}
- i++;
}
}
- qemu_mutex_unlock(&rs->bitmap_mutex);
/*
* Must occur before EOS (or any QEMUFile operation)
diff --git a/qapi/dump.json b/qapi/dump.json
index 4ae1f72..5cbc237 100644
--- a/qapi/dump.json
+++ b/qapi/dump.json
@@ -15,11 +15,23 @@
#
# @elf: elf format
#
-# @kdump-zlib: kdump-compressed format with zlib-compressed
+# @kdump-zlib: makedumpfile flattened, kdump-compressed format with zlib
+# compression
#
-# @kdump-lzo: kdump-compressed format with lzo-compressed
+# @kdump-lzo: makedumpfile flattened, kdump-compressed format with lzo
+# compression
#
-# @kdump-snappy: kdump-compressed format with snappy-compressed
+# @kdump-snappy: makedumpfile flattened, kdump-compressed format with snappy
+# compression
+#
+# @kdump-raw-zlib: raw assembled kdump-compressed format with zlib compression
+# (since 8.2)
+#
+# @kdump-raw-lzo: raw assembled kdump-compressed format with lzo compression
+# (since 8.2)
+#
+# @kdump-raw-snappy: raw assembled kdump-compressed format with snappy
+# compression (since 8.2)
#
# @win-dmp: Windows full crashdump format, can be used instead of ELF
# converting (since 2.13)
@@ -27,7 +39,11 @@
# Since: 2.0
##
{ 'enum': 'DumpGuestMemoryFormat',
- 'data': [ 'elf', 'kdump-zlib', 'kdump-lzo', 'kdump-snappy', 'win-dmp' ] }
+ 'data': [
+ 'elf',
+ 'kdump-zlib', 'kdump-lzo', 'kdump-snappy',
+ 'kdump-raw-zlib', 'kdump-raw-lzo', 'kdump-raw-snappy',
+ 'win-dmp' ] }
##
# @dump-guest-memory:
diff --git a/qapi/machine-target.json b/qapi/machine-target.json
index 4e55adb..c8d7d98 100644
--- a/qapi/machine-target.json
+++ b/qapi/machine-target.json
@@ -230,7 +230,8 @@
'data': { 'model': 'CpuModelInfo' },
'if': { 'any': [ 'TARGET_S390X',
'TARGET_I386',
- 'TARGET_ARM' ] } }
+ 'TARGET_ARM',
+ 'TARGET_LOONGARCH64' ] } }
##
# @query-cpu-model-expansion:
@@ -275,7 +276,8 @@
'returns': 'CpuModelExpansionInfo',
'if': { 'any': [ 'TARGET_S390X',
'TARGET_I386',
- 'TARGET_ARM' ] } }
+ 'TARGET_ARM',
+ 'TARGET_LOONGARCH64' ] } }
##
# @CpuDefinitionInfo:
diff --git a/system/dirtylimit.c b/system/dirtylimit.c
index fa959d7..495c7a7 100644
--- a/system/dirtylimit.c
+++ b/system/dirtylimit.c
@@ -411,12 +411,20 @@
void dirtylimit_vcpu_execute(CPUState *cpu)
{
- if (dirtylimit_in_service() &&
- dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled &&
- cpu->throttle_us_per_full) {
- trace_dirtylimit_vcpu_execute(cpu->cpu_index,
- cpu->throttle_us_per_full);
- usleep(cpu->throttle_us_per_full);
+ if (cpu->throttle_us_per_full) {
+ dirtylimit_state_lock();
+
+ if (dirtylimit_in_service() &&
+ dirtylimit_vcpu_get_state(cpu->cpu_index)->enabled) {
+ dirtylimit_state_unlock();
+ trace_dirtylimit_vcpu_execute(cpu->cpu_index,
+ cpu->throttle_us_per_full);
+
+ g_usleep(cpu->throttle_us_per_full);
+ return;
+ }
+
+ dirtylimit_state_unlock();
}
}
@@ -644,10 +652,6 @@
struct DirtyLimitInfoList *qmp_query_vcpu_dirty_limit(Error **errp)
{
- if (!dirtylimit_in_service()) {
- return NULL;
- }
-
return dirtylimit_query_all();
}
diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h
index 1265dc7..cfe195d 100644
--- a/target/loongarch/cpu-param.h
+++ b/target/loongarch/cpu-param.h
@@ -12,6 +12,6 @@
#define TARGET_PHYS_ADDR_SPACE_BITS 48
#define TARGET_VIRT_ADDR_SPACE_BITS 48
-#define TARGET_PAGE_BITS 14
+#define TARGET_PAGE_BITS 12
#endif
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index ef1bf89..a60d07a 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -443,6 +443,7 @@
env->cpucfg[20] = data;
env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
+ loongarch_cpu_post_init(obj);
}
static void loongarch_la132_initfn(Object *obj)
@@ -474,6 +475,12 @@
env->cpucfg[1] = data;
}
+static void loongarch_max_initfn(Object *obj)
+{
+ /* '-cpu max' for TCG: we use cpu la464. */
+ loongarch_la464_initfn(obj);
+}
+
static void loongarch_cpu_list_entry(gpointer data, gpointer user_data)
{
const char *typename = object_class_get_name(OBJECT_CLASS(data));
@@ -616,6 +623,72 @@
};
#endif
+static bool loongarch_get_lsx(Object *obj, Error **errp)
+{
+ LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+ bool ret;
+
+ if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) {
+ ret = true;
+ } else {
+ ret = false;
+ }
+ return ret;
+}
+
+static void loongarch_set_lsx(Object *obj, bool value, Error **errp)
+{
+ LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+
+ if (value) {
+ cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1);
+ } else {
+ cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 0);
+ cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0);
+ }
+}
+
+static bool loongarch_get_lasx(Object *obj, Error **errp)
+{
+ LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+ bool ret;
+
+ if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) {
+ ret = true;
+ } else {
+ ret = false;
+ }
+ return ret;
+}
+
+static void loongarch_set_lasx(Object *obj, bool value, Error **errp)
+{
+ LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+
+ if (value) {
+ if (!FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) {
+ cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1);
+ }
+ cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 1);
+ } else {
+ cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0);
+ }
+}
+
+void loongarch_cpu_post_init(Object *obj)
+{
+ LoongArchCPU *cpu = LOONGARCH_CPU(obj);
+
+ if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) {
+ object_property_add_bool(obj, "lsx", loongarch_get_lsx,
+ loongarch_set_lsx);
+ }
+ if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) {
+ object_property_add_bool(obj, "lasx", loongarch_get_lasx,
+ loongarch_set_lasx);
+ }
+}
+
static void loongarch_cpu_init(Object *obj)
{
#ifndef CONFIG_USER_ONLY
@@ -829,6 +902,7 @@
},
DEFINE_LOONGARCH_CPU_TYPE(64, "la464", loongarch_la464_initfn),
DEFINE_LOONGARCH_CPU_TYPE(32, "la132", loongarch_la132_initfn),
+ DEFINE_LOONGARCH_CPU_TYPE(64, "max", loongarch_max_initfn),
};
DEFINE_TYPES(loongarch_cpu_type_infos)
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 8b54cf1..9d0f79f8 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -486,4 +486,6 @@
#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX
#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU
+void loongarch_cpu_post_init(Object *obj);
+
#endif /* LOONGARCH_CPU_H */
diff --git a/target/loongarch/insn_trans/trans_vec.c.inc b/target/loongarch/insn_trans/trans_vec.c.inc
index 98f856b..92b1d22 100644
--- a/target/loongarch/insn_trans/trans_vec.c.inc
+++ b/target/loongarch/insn_trans/trans_vec.c.inc
@@ -4,8 +4,6 @@
* Copyright (c) 2022-2023 Loongson Technology Corporation Limited
*/
-#ifndef CONFIG_USER_ONLY
-
static bool check_vec(DisasContext *ctx, uint32_t oprsz)
{
if ((oprsz == 16) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_SXE) == 0)) {
@@ -21,15 +19,6 @@
return true;
}
-#else
-
-static bool check_vec(DisasContext *ctx, uint32_t oprsz)
-{
- return true;
-}
-
-#endif
-
static bool gen_vvvv_ptr_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz,
gen_helper_gvec_4_ptr *fn)
{
diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c
index 6c25957..645672f 100644
--- a/target/loongarch/loongarch-qmp-cmds.c
+++ b/target/loongarch/loongarch-qmp-cmds.c
@@ -7,8 +7,13 @@
*/
#include "qemu/osdep.h"
+#include "qapi/error.h"
#include "qapi/qapi-commands-machine-target.h"
#include "cpu.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qobject-input-visitor.h"
+#include "qom/qom-qobject.h"
static void loongarch_cpu_add_definition(gpointer data, gpointer user_data)
{
@@ -35,3 +40,62 @@
return cpu_list;
}
+
+static const char *cpu_model_advertised_features[] = {
+ "lsx", "lasx", NULL
+};
+
+CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
+ CpuModelInfo *model,
+ Error **errp)
+{
+ CpuModelExpansionInfo *expansion_info;
+ QDict *qdict_out;
+ ObjectClass *oc;
+ Object *obj;
+ const char *name;
+ int i;
+
+ if (type != CPU_MODEL_EXPANSION_TYPE_STATIC) {
+ error_setg(errp, "The requested expansion type is not supported");
+ return NULL;
+ }
+
+ oc = cpu_class_by_name(TYPE_LOONGARCH_CPU, model->name);
+ if (!oc) {
+ error_setg(errp, "The CPU type '%s' is not a recognized LoongArch CPU type",
+ model->name);
+ return NULL;
+ }
+
+ obj = object_new(object_class_get_name(oc));
+
+ expansion_info = g_new0(CpuModelExpansionInfo, 1);
+ expansion_info->model = g_malloc0(sizeof(*expansion_info->model));
+ expansion_info->model->name = g_strdup(model->name);
+
+ qdict_out = qdict_new();
+
+ i = 0;
+ while ((name = cpu_model_advertised_features[i++]) != NULL) {
+ ObjectProperty *prop = object_property_find(obj, name);
+ if (prop) {
+ QObject *value;
+
+ assert(prop->get);
+ value = object_property_get_qobject(obj, name, &error_abort);
+
+ qdict_put_obj(qdict_out, name, value);
+ }
+ }
+
+ if (!qdict_size(qdict_out)) {
+ qobject_unref(qdict_out);
+ } else {
+ expansion_info->model->props = QOBJECT(qdict_out);
+ }
+
+ object_unref(obj);
+
+ return expansion_info;
+}
diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c
index c8b8b04..449043c 100644
--- a/target/loongarch/tlb_helper.c
+++ b/target/loongarch/tlb_helper.c
@@ -60,6 +60,9 @@
tlb_rplv = 0;
}
+ /* Remove sw bit between bit12 -- bit PS*/
+ tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1));
+
/* Check access rights */
if (!tlb_v) {
return TLBRET_INVALID;
@@ -82,10 +85,6 @@
return TLBRET_DIRTY;
}
- /*
- * tlb_entry contains ppn[47:12] while 16KiB ppn is [47:15]
- * need adjust.
- */
*physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) |
(address & MAKE_64BIT_MASK(0, tlb_ps));
*prot = PAGE_READ;
@@ -774,7 +773,7 @@
/* Move Global bit */
tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >>
LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT |
- (tmp0 & (~(1 << R_TLBENTRY_G_SHIFT)));
+ (tmp0 & (~(1 << LOONGARCH_HGLOBAL_SHIFT)));
ps = ptbase + ptwidth - 1;
if (odd) {
tmp0 += MAKE_64BIT_MASK(ps, 1);
diff --git a/tests/migration/guestperf/comparison.py b/tests/migration/guestperf/comparison.py
index c03b3f6..42cc037 100644
--- a/tests/migration/guestperf/comparison.py
+++ b/tests/migration/guestperf/comparison.py
@@ -135,4 +135,27 @@ def __init__(self, name, scenarios):
Scenario("compr-multifd-channels-64",
multifd=True, multifd_channels=64),
]),
+
+ # Looking at effect of dirty-limit with
+ # varying x_vcpu_dirty_limit_period
+ Comparison("compr-dirty-limit-period", scenarios = [
+ Scenario("compr-dirty-limit-period-500",
+ dirty_limit=True, x_vcpu_dirty_limit_period=500),
+ Scenario("compr-dirty-limit-period-800",
+ dirty_limit=True, x_vcpu_dirty_limit_period=800),
+ Scenario("compr-dirty-limit-period-1000",
+ dirty_limit=True, x_vcpu_dirty_limit_period=1000),
+ ]),
+
+
+ # Looking at effect of dirty-limit with
+ # varying vcpu_dirty_limit
+ Comparison("compr-dirty-limit", scenarios = [
+ Scenario("compr-dirty-limit-10MB",
+ dirty_limit=True, vcpu_dirty_limit=10),
+ Scenario("compr-dirty-limit-20MB",
+ dirty_limit=True, vcpu_dirty_limit=20),
+ Scenario("compr-dirty-limit-50MB",
+ dirty_limit=True, vcpu_dirty_limit=50),
+ ]),
]
diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py
index da96ca0..608d727 100644
--- a/tests/migration/guestperf/engine.py
+++ b/tests/migration/guestperf/engine.py
@@ -102,6 +102,8 @@ def _migrate_progress(self, vm):
info.get("expected-downtime", 0),
info.get("setup-time", 0),
info.get("cpu-throttle-percentage", 0),
+ info.get("dirty-limit-throttle-time-per-round", 0),
+ info.get("dirty-limit-ring-full-time", 0),
)
def _migrate(self, hardware, scenario, src, dst, connect_uri):
@@ -203,6 +205,21 @@ def _migrate(self, hardware, scenario, src, dst, connect_uri):
resp = dst.cmd("migrate-set-parameters",
multifd_channels=scenario._multifd_channels)
+ if scenario._dirty_limit:
+ if not hardware._dirty_ring_size:
+ raise Exception("dirty ring size must be configured when "
+ "testing dirty limit migration")
+
+ resp = src.cmd("migrate-set-capabilities",
+ capabilities = [
+ { "capability": "dirty-limit",
+ "state": True }
+ ])
+ resp = src.cmd("migrate-set-parameters",
+ x_vcpu_dirty_limit_period=scenario._x_vcpu_dirty_limit_period)
+ resp = src.cmd("migrate-set-parameters",
+ vcpu_dirty_limit=scenario._vcpu_dirty_limit)
+
resp = src.cmd("migrate", uri=connect_uri)
post_copy = False
@@ -325,7 +342,6 @@ def _get_common_args(self, hardware, tunnelled=False):
cmdline = "'" + cmdline + "'"
argv = [
- "-accel", "kvm",
"-cpu", "host",
"-kernel", self._kernel,
"-initrd", self._initrd,
@@ -333,6 +349,11 @@ def _get_common_args(self, hardware, tunnelled=False):
"-m", str((hardware._mem * 1024) + 512),
"-smp", str(hardware._cpus),
]
+ if hardware._dirty_ring_size:
+ argv.extend(["-accel", "kvm,dirty-ring-size=%s" %
+ hardware._dirty_ring_size])
+ else:
+ argv.extend(["-accel", "kvm"])
argv.extend(self._get_qemu_serial_args())
diff --git a/tests/migration/guestperf/hardware.py b/tests/migration/guestperf/hardware.py
index 3145785..f779cc0 100644
--- a/tests/migration/guestperf/hardware.py
+++ b/tests/migration/guestperf/hardware.py
@@ -23,7 +23,8 @@ def __init__(self, cpus=1, mem=1,
src_cpu_bind=None, src_mem_bind=None,
dst_cpu_bind=None, dst_mem_bind=None,
prealloc_pages = False,
- huge_pages=False, locked_pages=False):
+ huge_pages=False, locked_pages=False,
+ dirty_ring_size=0):
self._cpus = cpus
self._mem = mem # GiB
self._src_mem_bind = src_mem_bind # List of NUMA nodes
@@ -33,6 +34,7 @@ def __init__(self, cpus=1, mem=1,
self._prealloc_pages = prealloc_pages
self._huge_pages = huge_pages
self._locked_pages = locked_pages
+ self._dirty_ring_size = dirty_ring_size
def serialize(self):
@@ -46,6 +48,7 @@ def serialize(self):
"prealloc_pages": self._prealloc_pages,
"huge_pages": self._huge_pages,
"locked_pages": self._locked_pages,
+ "dirty_ring_size": self._dirty_ring_size,
}
@classmethod
@@ -59,4 +62,5 @@ def deserialize(cls, data):
data["dst_mem_bind"],
data["prealloc_pages"],
data["huge_pages"],
- data["locked_pages"])
+ data["locked_pages"],
+ data["dirty_ring_size"])
diff --git a/tests/migration/guestperf/progress.py b/tests/migration/guestperf/progress.py
index ab1ee57..d490584 100644
--- a/tests/migration/guestperf/progress.py
+++ b/tests/migration/guestperf/progress.py
@@ -81,7 +81,9 @@ def __init__(self,
downtime,
downtime_expected,
setup_time,
- throttle_pcent):
+ throttle_pcent,
+ dirty_limit_throttle_time_per_round,
+ dirty_limit_ring_full_time):
self._status = status
self._ram = ram
@@ -91,6 +93,10 @@ def __init__(self,
self._downtime_expected = downtime_expected
self._setup_time = setup_time
self._throttle_pcent = throttle_pcent
+ self._dirty_limit_throttle_time_per_round = \
+ dirty_limit_throttle_time_per_round
+ self._dirty_limit_ring_full_time = \
+ dirty_limit_ring_full_time
def serialize(self):
return {
@@ -102,6 +108,10 @@ def serialize(self):
"downtime_expected": self._downtime_expected,
"setup_time": self._setup_time,
"throttle_pcent": self._throttle_pcent,
+ "dirty_limit_throttle_time_per_round":
+ self._dirty_limit_throttle_time_per_round,
+ "dirty_limit_ring_full_time":
+ self._dirty_limit_ring_full_time,
}
@classmethod
@@ -114,4 +124,6 @@ def deserialize(cls, data):
data["downtime"],
data["downtime_expected"],
data["setup_time"],
- data["throttle_pcent"])
+ data["throttle_pcent"],
+ data["dirty_limit_throttle_time_per_round"],
+ data["dirty_limit_ring_full_time"])
diff --git a/tests/migration/guestperf/scenario.py b/tests/migration/guestperf/scenario.py
index de70d9b..154c4f5 100644
--- a/tests/migration/guestperf/scenario.py
+++ b/tests/migration/guestperf/scenario.py
@@ -30,7 +30,9 @@ def __init__(self, name,
auto_converge=False, auto_converge_step=10,
compression_mt=False, compression_mt_threads=1,
compression_xbzrle=False, compression_xbzrle_cache=10,
- multifd=False, multifd_channels=2):
+ multifd=False, multifd_channels=2,
+ dirty_limit=False, x_vcpu_dirty_limit_period=500,
+ vcpu_dirty_limit=1):
self._name = name
@@ -60,6 +62,10 @@ def __init__(self, name,
self._multifd = multifd
self._multifd_channels = multifd_channels
+ self._dirty_limit = dirty_limit
+ self._x_vcpu_dirty_limit_period = x_vcpu_dirty_limit_period
+ self._vcpu_dirty_limit = vcpu_dirty_limit
+
def serialize(self):
return {
"name": self._name,
@@ -79,6 +85,9 @@ def serialize(self):
"compression_xbzrle_cache": self._compression_xbzrle_cache,
"multifd": self._multifd,
"multifd_channels": self._multifd_channels,
+ "dirty_limit": self._dirty_limit,
+ "x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period,
+ "vcpu_dirty_limit": self._vcpu_dirty_limit,
}
@classmethod
diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py
index 8a809e3..c85d89e 100644
--- a/tests/migration/guestperf/shell.py
+++ b/tests/migration/guestperf/shell.py
@@ -60,6 +60,8 @@ def __init__(self):
parser.add_argument("--prealloc-pages", dest="prealloc_pages", default=False)
parser.add_argument("--huge-pages", dest="huge_pages", default=False)
parser.add_argument("--locked-pages", dest="locked_pages", default=False)
+ parser.add_argument("--dirty-ring-size", dest="dirty_ring_size",
+ default=0, type=int)
self._parser = parser
@@ -89,7 +91,9 @@ def split_map(value):
locked_pages=args.locked_pages,
huge_pages=args.huge_pages,
- prealloc_pages=args.prealloc_pages)
+ prealloc_pages=args.prealloc_pages,
+
+ dirty_ring_size=args.dirty_ring_size)
class Shell(BaseShell):
@@ -127,6 +131,17 @@ def __init__(self):
parser.add_argument("--multifd-channels", dest="multifd_channels",
default=2, type=int)
+ parser.add_argument("--dirty-limit", dest="dirty_limit", default=False,
+ action="store_true")
+
+ parser.add_argument("--x-vcpu-dirty-limit-period",
+ dest="x_vcpu_dirty_limit_period",
+ default=500, type=int)
+
+ parser.add_argument("--vcpu-dirty-limit",
+ dest="vcpu_dirty_limit",
+ default=1, type=int)
+
def get_scenario(self, args):
return Scenario(name="perfreport",
downtime=args.downtime,
@@ -150,7 +165,12 @@ def get_scenario(self, args):
compression_xbzrle_cache=args.compression_xbzrle_cache,
multifd=args.multifd,
- multifd_channels=args.multifd_channels)
+ multifd_channels=args.multifd_channels,
+
+ dirty_limit=args.dirty_limit,
+ x_vcpu_dirty_limit_period=\
+ args.x_vcpu_dirty_limit_period,
+ vcpu_dirty_limit=args.vcpu_dirty_limit)
def run(self, argv):
args = self._parser.parse_args(argv)
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index e803b46..5752412 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -3091,6 +3091,166 @@
dirtylimit_stop_vm(vm);
}
+static void migrate_dirty_limit_wait_showup(QTestState *from,
+ const int64_t period,
+ const int64_t value)
+{
+ /* Enable dirty limit capability */
+ migrate_set_capability(from, "dirty-limit", true);
+
+ /* Set dirty limit parameters */
+ migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period);
+ migrate_set_parameter_int(from, "vcpu-dirty-limit", value);
+
+ /* Make sure migrate can't converge */
+ migrate_ensure_non_converge(from);
+
+ /* To check limit rate after precopy */
+ migrate_set_capability(from, "pause-before-switchover", true);
+
+ /* Wait for the serial output from the source */
+ wait_for_serial("src_serial");
+}
+
+/*
+ * This test does:
+ * source destination
+ * start vm
+ * start incoming vm
+ * migrate
+ * wait dirty limit to begin
+ * cancel migrate
+ * cancellation check
+ * restart incoming vm
+ * migrate
+ * wait dirty limit to begin
+ * wait pre-switchover event
+ * convergence condition check
+ *
+ * And see if dirty limit migration works correctly.
+ * This test case involves many passes, so it runs in slow mode only.
+ */
+static void test_migrate_dirty_limit(void)
+{
+ g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs);
+ QTestState *from, *to;
+ int64_t remaining;
+ uint64_t throttle_us_per_full;
+ /*
+ * We want the test to be stable and as fast as possible.
+ * E.g., with 1Gb/s bandwith migration may pass without dirty limit,
+ * so we need to decrease a bandwidth.
+ */
+ const int64_t dirtylimit_period = 1000, dirtylimit_value = 50;
+ const int64_t max_bandwidth = 400000000; /* ~400Mb/s */
+ const int64_t downtime_limit = 250; /* 250ms */
+ /*
+ * We migrate through unix-socket (> 500Mb/s).
+ * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s).
+ * So, we can predict expected_threshold
+ */
+ const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000;
+ int max_try_count = 10;
+ MigrateCommon args = {
+ .start = {
+ .hide_stderr = true,
+ .use_dirty_ring = true,
+ },
+ .listen_uri = uri,
+ .connect_uri = uri,
+ };
+
+ /* Start src, dst vm */
+ if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) {
+ return;
+ }
+
+ /* Prepare for dirty limit migration and wait src vm show up */
+ migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value);
+
+ /* Start migrate */
+ migrate_qmp(from, uri, "{}");
+
+ /* Wait for dirty limit throttle begin */
+ throttle_us_per_full = 0;
+ while (throttle_us_per_full == 0) {
+ throttle_us_per_full =
+ read_migrate_property_int(from, "dirty-limit-throttle-time-per-round");
+ usleep(100);
+ g_assert_false(got_src_stop);
+ }
+
+ /* Now cancel migrate and wait for dirty limit throttle switch off */
+ migrate_cancel(from);
+ wait_for_migration_status(from, "cancelled", NULL);
+
+ /* Check if dirty limit throttle switched off, set timeout 1ms */
+ do {
+ throttle_us_per_full =
+ read_migrate_property_int(from, "dirty-limit-throttle-time-per-round");
+ usleep(100);
+ g_assert_false(got_src_stop);
+ } while (throttle_us_per_full != 0 && --max_try_count);
+
+ /* Assert dirty limit is not in service */
+ g_assert_cmpint(throttle_us_per_full, ==, 0);
+
+ args = (MigrateCommon) {
+ .start = {
+ .only_target = true,
+ .use_dirty_ring = true,
+ },
+ .listen_uri = uri,
+ .connect_uri = uri,
+ };
+
+ /* Restart dst vm, src vm already show up so we needn't wait anymore */
+ if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) {
+ return;
+ }
+
+ /* Start migrate */
+ migrate_qmp(from, uri, "{}");
+
+ /* Wait for dirty limit throttle begin */
+ throttle_us_per_full = 0;
+ while (throttle_us_per_full == 0) {
+ throttle_us_per_full =
+ read_migrate_property_int(from, "dirty-limit-throttle-time-per-round");
+ usleep(100);
+ g_assert_false(got_src_stop);
+ }
+
+ /*
+ * The dirty limit rate should equals the return value of
+ * query-vcpu-dirty-limit if dirty limit cap set
+ */
+ g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from));
+
+ /* Now, we have tested if dirty limit works, let it converge */
+ migrate_set_parameter_int(from, "downtime-limit", downtime_limit);
+ migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth);
+
+ /*
+ * Wait for pre-switchover status to check if migration
+ * satisfy the convergence condition
+ */
+ wait_for_migration_status(from, "pre-switchover", NULL);
+
+ remaining = read_ram_property_int(from, "remaining");
+ g_assert_cmpint(remaining, <,
+ (expected_threshold + expected_threshold / 100));
+
+ migrate_continue(from, "pre-switchover");
+
+ qtest_qmp_eventwait(to, "RESUME");
+
+ wait_for_serial("dest_serial");
+ wait_for_migration_complete(from);
+
+ test_migrate_end(from, to, true);
+}
+
static bool kvm_dirty_ring_supported(void)
{
#if defined(__linux__) && defined(HOST_X86_64)
@@ -3301,6 +3461,10 @@
*/
if (g_test_slow()) {
qtest_add_func("/migration/auto_converge", test_migrate_auto_converge);
+ if (g_str_equal(arch, "x86_64") &&
+ has_kvm && kvm_dirty_ring_supported()) {
+ qtest_add_func("/migration/dirty_limit", test_migrate_dirty_limit);
+ }
}
qtest_add_func("/migration/multifd/tcp/plain/none",
test_multifd_tcp_none);