* Miscellaneous exec.c fixes (Markus, myself)
* Q35 support for -machine kernel_irqchip=split (Rita) * Chardev replay support (Pavel) * icount "warping" cleanups (Pavel) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQEcBAABAgAGBQJW6EU4AAoJEL/70l94x66D3aAIAIHUnfaFTrPz4k+hX3rfGVV6 0sFeuG7DlVKptitLtw69xxwWPVmwNT+SdrrffUClEWquKfKASUgZ46a7sImc9dO+ nYk2mseZyfr4SfMaEPHdtBtoTyghpBSLZgpH9uLxes+9a1OG2+KrshhtTPepFrbo R22Xba2M0P0FkoN/lZFKX2qXGJU+ILX4pe7VSieTPdfAY4iSqTwuj3zW/PSwCRP7 pFj27M9YzFQCm86PAOwDjJILxx11Cr2byzlwLniLc/2Mz70+vDhr7ojMd88ldCDF xMfVmlKVgG0bWp9K9R/JK3wJJanXXckikcF274JRVp6cXoeLz4HkWukLsoCaXPI= =tTqH -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging * Miscellaneous exec.c fixes (Markus, myself) * Q35 support for -machine kernel_irqchip=split (Rita) * Chardev replay support (Pavel) * icount "warping" cleanups (Pavel) # gpg: Signature made Tue 15 Mar 2016 17:24:08 GMT using RSA key ID 78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" * remotes/bonzini/tags/for-upstream: icount: decouple warp calls icount: remove obsolete warp call replay: character devices exec: fix early return from ram_block_add exec: Fix memory allocation when memory path isn't on hugetlbfs exec: Fix memory allocation when memory path names new file update-linux-headers: Add userfaultfd.h kvm: x86: q35: Add support for -machine kernel_irqchip=split for q35 Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
4caecccbc1
61
cpus.c
61
cpus.c
@ -370,9 +370,12 @@ static void icount_warp_rt(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void icount_dummy_timer(void *opaque)
|
||||
static void icount_timer_cb(void *opaque)
|
||||
{
|
||||
(void)opaque;
|
||||
/* No need for a checkpoint because the timer already synchronizes
|
||||
* with CHECKPOINT_CLOCK_VIRTUAL_RT.
|
||||
*/
|
||||
icount_warp_rt();
|
||||
}
|
||||
|
||||
void qtest_clock_warp(int64_t dest)
|
||||
@ -396,17 +399,12 @@ void qtest_clock_warp(int64_t dest)
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
|
||||
void qemu_clock_warp(QEMUClockType type)
|
||||
void qemu_start_warp_timer(void)
|
||||
{
|
||||
int64_t clock;
|
||||
int64_t deadline;
|
||||
|
||||
/*
|
||||
* There are too many global variables to make the "warp" behavior
|
||||
* applicable to other clocks. But a clock argument removes the
|
||||
* need for if statements all over the place.
|
||||
*/
|
||||
if (type != QEMU_CLOCK_VIRTUAL || !use_icount) {
|
||||
if (!use_icount) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -418,29 +416,17 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
}
|
||||
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP)) {
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (icount_sleep) {
|
||||
/*
|
||||
* If the CPUs have been sleeping, advance QEMU_CLOCK_VIRTUAL timer now.
|
||||
* This ensures that the deadline for the timer is computed correctly
|
||||
* below.
|
||||
* This also makes sure that the insn counter is synchronized before
|
||||
* the CPU starts running, in case the CPU is woken by an event other
|
||||
* than the earliest QEMU_CLOCK_VIRTUAL timer.
|
||||
*/
|
||||
icount_warp_rt();
|
||||
timer_del(icount_warp_timer);
|
||||
}
|
||||
if (!all_cpu_threads_idle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* When testing, qtest commands advance icount. */
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
/* We want to use the earliest deadline from ALL vm_clocks */
|
||||
@ -496,6 +482,28 @@ void qemu_clock_warp(QEMUClockType type)
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_account_warp_timer(void)
|
||||
{
|
||||
if (!use_icount || !icount_sleep) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Nothing to do if the VM is stopped: QEMU_CLOCK_VIRTUAL timers
|
||||
* do not fire, so computing the deadline does not make sense.
|
||||
*/
|
||||
if (!runstate_is_running()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_ACCOUNT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
timer_del(icount_warp_timer);
|
||||
icount_warp_rt();
|
||||
}
|
||||
|
||||
static bool icount_state_needed(void *opaque)
|
||||
{
|
||||
return use_icount;
|
||||
@ -624,7 +632,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
||||
icount_sleep = qemu_opt_get_bool(opts, "sleep", true);
|
||||
if (icount_sleep) {
|
||||
icount_warp_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||
icount_dummy_timer, NULL);
|
||||
icount_timer_cb, NULL);
|
||||
}
|
||||
|
||||
icount_align_option = qemu_opt_get_bool(opts, "align", false);
|
||||
@ -995,9 +1003,6 @@ static void qemu_wait_io_event_common(CPUState *cpu)
|
||||
static void qemu_tcg_wait_io_event(CPUState *cpu)
|
||||
{
|
||||
while (all_cpu_threads_idle()) {
|
||||
/* Start accounting real time to the virtual clock if the CPUs
|
||||
are idle. */
|
||||
qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
|
||||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||
}
|
||||
|
||||
@ -1499,7 +1504,7 @@ static void tcg_exec_all(void)
|
||||
int r;
|
||||
|
||||
/* Account partial waits to QEMU_CLOCK_VIRTUAL. */
|
||||
qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
|
||||
qemu_account_warp_timer();
|
||||
|
||||
if (next_cpu == NULL) {
|
||||
next_cpu = first_cpu;
|
||||
|
@ -107,7 +107,7 @@ at the specified moments of time. There are several kinds of timers:
|
||||
sources (e.g. real time clock chip). Host clock is the one of the sources
|
||||
of non-determinism. Host clock read operations should be logged to
|
||||
make the execution deterministic.
|
||||
* Real time clock for icount. This clock is similar to real time clock but
|
||||
* Virtual real time clock. This clock is similar to real time clock but
|
||||
it is used only for increasing virtual clock while virtual machine is
|
||||
sleeping. Due to its nature it is also non-deterministic as the host clock
|
||||
and has to be logged too.
|
||||
@ -134,11 +134,20 @@ of time. That's why we do not process a group of timers until the checkpoint
|
||||
event will be read from the log. Such an event allows synchronizing CPU
|
||||
execution and timer events.
|
||||
|
||||
Another checkpoints application in record/replay is instruction counting
|
||||
while the virtual machine is idle. This function (qemu_clock_warp) is called
|
||||
from the wait loop. It changes virtual machine state and must be deterministic
|
||||
then. That is why we added checkpoint to this function to prevent its
|
||||
operation in replay mode when it does not correspond to record mode.
|
||||
Two other checkpoints govern the "warping" of the virtual clock.
|
||||
While the virtual machine is idle, the virtual clock increments at
|
||||
1 ns per *real time* nanosecond. This is done by setting up a timer
|
||||
(called the warp timer) on the virtual real time clock, so that the
|
||||
timer fires at the next deadline of the virtual clock; the virtual clock
|
||||
is then incremented (which is called "warping" the virtual clock) as
|
||||
soon as the timer fires or the CPUs need to go out of the idle state.
|
||||
Two functions are used for this purpose; because these actions change
|
||||
virtual machine state and must be deterministic, each of them creates a
|
||||
checkpoint. qemu_start_warp_timer checks if the CPUs are idle and if so
|
||||
starts accounting real time to virtual clock. qemu_account_warp_timer
|
||||
is called when the CPUs get an interrupt or when the warp timer fires,
|
||||
and it warps the virtual clock by the amount of real time that has passed
|
||||
since qemu_start_warp_timer.
|
||||
|
||||
Bottom halves
|
||||
-------------
|
||||
|
127
exec.c
127
exec.c
@ -1228,92 +1228,83 @@ void qemu_mutex_unlock_ramlist(void)
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
#include <sys/vfs.h>
|
||||
|
||||
#define HUGETLBFS_MAGIC 0x958458f6
|
||||
|
||||
static long gethugepagesize(const char *path, Error **errp)
|
||||
{
|
||||
struct statfs fs;
|
||||
int ret;
|
||||
|
||||
do {
|
||||
ret = statfs(path, &fs);
|
||||
} while (ret != 0 && errno == EINTR);
|
||||
|
||||
if (ret != 0) {
|
||||
error_setg_errno(errp, errno, "failed to get page size of file %s",
|
||||
path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fs.f_bsize;
|
||||
}
|
||||
|
||||
static void *file_ram_alloc(RAMBlock *block,
|
||||
ram_addr_t memory,
|
||||
const char *path,
|
||||
Error **errp)
|
||||
{
|
||||
struct stat st;
|
||||
bool unlink_on_error = false;
|
||||
char *filename;
|
||||
char *sanitized_name;
|
||||
char *c;
|
||||
void *area;
|
||||
int fd;
|
||||
uint64_t hpagesize;
|
||||
Error *local_err = NULL;
|
||||
|
||||
hpagesize = gethugepagesize(path, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto error;
|
||||
}
|
||||
block->mr->align = hpagesize;
|
||||
|
||||
if (memory < hpagesize) {
|
||||
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
||||
"or larger than huge page size 0x%" PRIx64,
|
||||
memory, hpagesize);
|
||||
goto error;
|
||||
}
|
||||
int64_t page_size;
|
||||
|
||||
if (kvm_enabled() && !kvm_has_sync_mmu()) {
|
||||
error_setg(errp,
|
||||
"host lacks kvm mmu notifiers, -mem-path unsupported");
|
||||
goto error;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!stat(path, &st) && S_ISDIR(st.st_mode)) {
|
||||
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
||||
sanitized_name = g_strdup(memory_region_name(block->mr));
|
||||
for (c = sanitized_name; *c != '\0'; c++) {
|
||||
if (*c == '/') {
|
||||
*c = '_';
|
||||
}
|
||||
}
|
||||
|
||||
filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path,
|
||||
sanitized_name);
|
||||
g_free(sanitized_name);
|
||||
|
||||
fd = mkstemp(filename);
|
||||
for (;;) {
|
||||
fd = open(path, O_RDWR);
|
||||
if (fd >= 0) {
|
||||
unlink(filename);
|
||||
/* @path names an existing file, use it */
|
||||
break;
|
||||
}
|
||||
g_free(filename);
|
||||
} else {
|
||||
fd = open(path, O_RDWR | O_CREAT, 0644);
|
||||
if (errno == ENOENT) {
|
||||
/* @path names a file that doesn't exist, create it */
|
||||
fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
|
||||
if (fd >= 0) {
|
||||
unlink_on_error = true;
|
||||
break;
|
||||
}
|
||||
} else if (errno == EISDIR) {
|
||||
/* @path names a directory, create a file there */
|
||||
/* Make name safe to use with mkstemp by replacing '/' with '_'. */
|
||||
sanitized_name = g_strdup(memory_region_name(block->mr));
|
||||
for (c = sanitized_name; *c != '\0'; c++) {
|
||||
if (*c == '/') {
|
||||
*c = '_';
|
||||
}
|
||||
}
|
||||
|
||||
filename = g_strdup_printf("%s/qemu_back_mem.%s.XXXXXX", path,
|
||||
sanitized_name);
|
||||
g_free(sanitized_name);
|
||||
|
||||
fd = mkstemp(filename);
|
||||
if (fd >= 0) {
|
||||
unlink(filename);
|
||||
g_free(filename);
|
||||
break;
|
||||
}
|
||||
g_free(filename);
|
||||
}
|
||||
if (errno != EEXIST && errno != EINTR) {
|
||||
error_setg_errno(errp, errno,
|
||||
"can't open backing store %s for guest RAM",
|
||||
path);
|
||||
goto error;
|
||||
}
|
||||
/*
|
||||
* Try again on EINTR and EEXIST. The latter happens when
|
||||
* something else creates the file between our two open().
|
||||
*/
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
error_setg_errno(errp, errno,
|
||||
"unable to create backing store for hugepages");
|
||||
page_size = qemu_fd_getpagesize(fd);
|
||||
block->mr->align = page_size;
|
||||
|
||||
if (memory < page_size) {
|
||||
error_setg(errp, "memory size 0x" RAM_ADDR_FMT " must be equal to "
|
||||
"or larger than page size 0x%" PRIx64,
|
||||
memory, page_size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
memory = ROUND_UP(memory, hpagesize);
|
||||
memory = ROUND_UP(memory, page_size);
|
||||
|
||||
/*
|
||||
* ftruncate is not supported by hugetlbfs in older
|
||||
@ -1325,10 +1316,10 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
perror("ftruncate");
|
||||
}
|
||||
|
||||
area = qemu_ram_mmap(fd, memory, hpagesize, block->flags & RAM_SHARED);
|
||||
area = qemu_ram_mmap(fd, memory, page_size, block->flags & RAM_SHARED);
|
||||
if (area == MAP_FAILED) {
|
||||
error_setg_errno(errp, errno,
|
||||
"unable to map backing store for hugepages");
|
||||
"unable to map backing store for guest RAM");
|
||||
close(fd);
|
||||
goto error;
|
||||
}
|
||||
@ -1341,6 +1332,10 @@ static void *file_ram_alloc(RAMBlock *block,
|
||||
return area;
|
||||
|
||||
error:
|
||||
if (unlink_on_error) {
|
||||
unlink(path);
|
||||
}
|
||||
close(fd);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@ -1594,6 +1589,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
qemu_mutex_unlock_ramlist();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
new_block->host = phys_mem_alloc(new_block->max_length,
|
||||
@ -1603,6 +1599,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp)
|
||||
"cannot set up guest memory '%s'",
|
||||
memory_region_name(new_block->mr));
|
||||
qemu_mutex_unlock_ramlist();
|
||||
return;
|
||||
}
|
||||
memory_try_enable_merging(new_block->host, new_block->max_length);
|
||||
}
|
||||
|
@ -1752,7 +1752,7 @@ int gdbserver_start(const char *device)
|
||||
sigaction(SIGINT, &act, NULL);
|
||||
}
|
||||
#endif
|
||||
chr = qemu_chr_new("gdb", device, NULL);
|
||||
chr = qemu_chr_new_noreplay("gdb", device, NULL);
|
||||
if (!chr)
|
||||
return -1;
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "hw/kvm/clock.h"
|
||||
#include "hw/pci-host/q35.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/i386/ich9.h"
|
||||
#include "hw/smbios/smbios.h"
|
||||
#include "hw/ide/pci.h"
|
||||
@ -146,7 +147,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
|
||||
/* irq lines */
|
||||
gsi_state = g_malloc0(sizeof(*gsi_state));
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
if (kvm_ioapic_in_kernel()) {
|
||||
kvm_pc_setup_irq_routing(pcmc->pci_enabled);
|
||||
gsi = qemu_allocate_irqs(kvm_pc_gsi_handler, gsi_state,
|
||||
GSI_NUM_PINS);
|
||||
@ -193,7 +194,7 @@ static void pc_q35_init(MachineState *machine)
|
||||
/*end early*/
|
||||
isa_bus_irqs(isa_bus, gsi);
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
if (kvm_pic_in_kernel()) {
|
||||
i8259 = kvm_i8259_init(isa_bus);
|
||||
} else if (xen_enabled()) {
|
||||
i8259 = xen_interrupt_controller_init();
|
||||
|
@ -210,12 +210,11 @@ void qemu_clock_notify(QEMUClockType type);
|
||||
void qemu_clock_enable(QEMUClockType type, bool enabled);
|
||||
|
||||
/**
|
||||
* qemu_clock_warp:
|
||||
* @type: the clock type
|
||||
* qemu_start_warp_timer:
|
||||
*
|
||||
* Warp a clock to a new value
|
||||
* Starts a timer for virtual clock update
|
||||
*/
|
||||
void qemu_clock_warp(QEMUClockType type);
|
||||
void qemu_start_warp_timer(void);
|
||||
|
||||
/**
|
||||
* qemu_clock_register_reset_notifier:
|
||||
|
@ -86,6 +86,7 @@ struct CharDriverState {
|
||||
int is_mux;
|
||||
guint fd_in_tag;
|
||||
QemuOpts *opts;
|
||||
bool replay;
|
||||
QTAILQ_ENTRY(CharDriverState) next;
|
||||
};
|
||||
|
||||
@ -138,6 +139,22 @@ void qemu_chr_parse_common(QemuOpts *opts, ChardevCommon *backend);
|
||||
CharDriverState *qemu_chr_new(const char *label, const char *filename,
|
||||
void (*init)(struct CharDriverState *s));
|
||||
|
||||
/**
|
||||
* @qemu_chr_new_noreplay:
|
||||
*
|
||||
* Create a new character backend from a URI.
|
||||
* Character device communications are not written
|
||||
* into the replay log.
|
||||
*
|
||||
* @label the name of the backend
|
||||
* @filename the URI
|
||||
* @init not sure..
|
||||
*
|
||||
* Returns: a new character backend
|
||||
*/
|
||||
CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
|
||||
void (*init)(struct CharDriverState *s));
|
||||
|
||||
/**
|
||||
* @qemu_chr_delete:
|
||||
*
|
||||
@ -341,6 +358,15 @@ int qemu_chr_be_can_write(CharDriverState *s);
|
||||
*/
|
||||
void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len);
|
||||
|
||||
/**
|
||||
* @qemu_chr_be_write_impl:
|
||||
*
|
||||
* Implementation of back end writing. Used by replay module.
|
||||
*
|
||||
* @buf a buffer to receive data from the front end
|
||||
* @len the number of bytes to receive from the front end
|
||||
*/
|
||||
void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len);
|
||||
|
||||
/**
|
||||
* @qemu_chr_be_event:
|
||||
|
@ -27,7 +27,8 @@ typedef enum ReplayClockKind ReplayClockKind;
|
||||
|
||||
/* IDs of the checkpoints */
|
||||
enum ReplayCheckpoint {
|
||||
CHECKPOINT_CLOCK_WARP,
|
||||
CHECKPOINT_CLOCK_WARP_START,
|
||||
CHECKPOINT_CLOCK_WARP_ACCOUNT,
|
||||
CHECKPOINT_RESET_REQUESTED,
|
||||
CHECKPOINT_SUSPEND_REQUESTED,
|
||||
CHECKPOINT_CLOCK_VIRTUAL,
|
||||
@ -114,4 +115,21 @@ void replay_input_event(QemuConsole *src, InputEvent *evt);
|
||||
/*! Adds input sync event to the queue */
|
||||
void replay_input_sync_event(void);
|
||||
|
||||
/* Character device */
|
||||
|
||||
/*! Registers char driver to save it's events */
|
||||
void replay_register_char_driver(struct CharDriverState *chr);
|
||||
/*! Saves write to char device event to the log */
|
||||
void replay_chr_be_write(struct CharDriverState *s, uint8_t *buf, int len);
|
||||
/*! Writes char write return value to the replay log. */
|
||||
void replay_char_write_event_save(int res, int offset);
|
||||
/*! Reads char write return value from the replay log. */
|
||||
void replay_char_write_event_load(int *res, int *offset);
|
||||
/*! Reads information about read_all character event. */
|
||||
int replay_char_read_all_load(uint8_t *buf);
|
||||
/*! Writes character read_all error code into the replay log. */
|
||||
void replay_char_read_all_save_error(int res);
|
||||
/*! Writes character read_all execution result into the replay log. */
|
||||
void replay_char_read_all_save_buf(uint8_t *buf, int offset);
|
||||
|
||||
#endif
|
||||
|
@ -509,7 +509,7 @@ int main_loop_wait(int nonblocking)
|
||||
|
||||
/* CPU thread can infinitely wait for event after
|
||||
missing the warp */
|
||||
qemu_clock_warp(QEMU_CLOCK_VIRTUAL);
|
||||
qemu_start_warp_timer();
|
||||
qemu_clock_run_all_timers();
|
||||
|
||||
return ret;
|
||||
|
144
qemu-char.c
144
qemu-char.c
@ -37,6 +37,7 @@
|
||||
#include "io/channel-socket.h"
|
||||
#include "io/channel-file.h"
|
||||
#include "io/channel-tls.h"
|
||||
#include "sysemu/replay.h"
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
@ -234,30 +235,15 @@ static void qemu_chr_fe_write_log(CharDriverState *s,
|
||||
}
|
||||
}
|
||||
|
||||
int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
|
||||
static int qemu_chr_fe_write_buffer(CharDriverState *s, const uint8_t *buf, int len, int *offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
qemu_mutex_lock(&s->chr_write_lock);
|
||||
ret = s->chr_write(s, buf, len);
|
||||
|
||||
if (ret > 0) {
|
||||
qemu_chr_fe_write_log(s, buf, ret);
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&s->chr_write_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
|
||||
{
|
||||
int offset = 0;
|
||||
int res = 0;
|
||||
*offset = 0;
|
||||
|
||||
qemu_mutex_lock(&s->chr_write_lock);
|
||||
while (offset < len) {
|
||||
while (*offset < len) {
|
||||
do {
|
||||
res = s->chr_write(s, buf + offset, len - offset);
|
||||
res = s->chr_write(s, buf + *offset, len - *offset);
|
||||
if (res == -1 && errno == EAGAIN) {
|
||||
g_usleep(100);
|
||||
}
|
||||
@ -267,13 +253,61 @@ int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
|
||||
break;
|
||||
}
|
||||
|
||||
offset += res;
|
||||
*offset += res;
|
||||
}
|
||||
if (offset > 0) {
|
||||
qemu_chr_fe_write_log(s, buf, offset);
|
||||
if (*offset > 0) {
|
||||
qemu_chr_fe_write_log(s, buf, *offset);
|
||||
}
|
||||
qemu_mutex_unlock(&s->chr_write_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int qemu_chr_fe_write(CharDriverState *s, const uint8_t *buf, int len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
|
||||
int offset;
|
||||
replay_char_write_event_load(&ret, &offset);
|
||||
assert(offset <= len);
|
||||
qemu_chr_fe_write_buffer(s, buf, offset, &offset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
qemu_mutex_lock(&s->chr_write_lock);
|
||||
ret = s->chr_write(s, buf, len);
|
||||
|
||||
if (ret > 0) {
|
||||
qemu_chr_fe_write_log(s, buf, ret);
|
||||
}
|
||||
|
||||
qemu_mutex_unlock(&s->chr_write_lock);
|
||||
|
||||
if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
|
||||
replay_char_write_event_save(ret, ret < 0 ? 0 : ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qemu_chr_fe_write_all(CharDriverState *s, const uint8_t *buf, int len)
|
||||
{
|
||||
int offset;
|
||||
int res;
|
||||
|
||||
if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
|
||||
replay_char_write_event_load(&res, &offset);
|
||||
assert(offset <= len);
|
||||
qemu_chr_fe_write_buffer(s, buf, offset, &offset);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = qemu_chr_fe_write_buffer(s, buf, len, &offset);
|
||||
|
||||
if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
|
||||
replay_char_write_event_save(res, offset);
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
return res;
|
||||
@ -289,6 +323,10 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
|
||||
if (!s->chr_sync_read) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (s->replay && replay_mode == REPLAY_MODE_PLAY) {
|
||||
return replay_char_read_all_load(buf);
|
||||
}
|
||||
|
||||
while (offset < len) {
|
||||
do {
|
||||
@ -303,6 +341,9 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
|
||||
}
|
||||
|
||||
if (res < 0) {
|
||||
if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
|
||||
replay_char_read_all_save_error(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -313,14 +354,22 @@ int qemu_chr_fe_read_all(CharDriverState *s, uint8_t *buf, int len)
|
||||
}
|
||||
}
|
||||
|
||||
if (s->replay && replay_mode == REPLAY_MODE_RECORD) {
|
||||
replay_char_read_all_save_buf(buf, offset);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
int qemu_chr_fe_ioctl(CharDriverState *s, int cmd, void *arg)
|
||||
{
|
||||
if (!s->chr_ioctl)
|
||||
return -ENOTSUP;
|
||||
return s->chr_ioctl(s, cmd, arg);
|
||||
int res;
|
||||
if (!s->chr_ioctl || s->replay) {
|
||||
res = -ENOTSUP;
|
||||
} else {
|
||||
res = s->chr_ioctl(s, cmd, arg);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int qemu_chr_be_can_write(CharDriverState *s)
|
||||
@ -330,17 +379,35 @@ int qemu_chr_be_can_write(CharDriverState *s)
|
||||
return s->chr_can_read(s->handler_opaque);
|
||||
}
|
||||
|
||||
void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
|
||||
void qemu_chr_be_write_impl(CharDriverState *s, uint8_t *buf, int len)
|
||||
{
|
||||
if (s->chr_read) {
|
||||
s->chr_read(s->handler_opaque, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
|
||||
{
|
||||
if (s->replay) {
|
||||
if (replay_mode == REPLAY_MODE_PLAY) {
|
||||
return;
|
||||
}
|
||||
replay_chr_be_write(s, buf, len);
|
||||
} else {
|
||||
qemu_chr_be_write_impl(s, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
int qemu_chr_fe_get_msgfd(CharDriverState *s)
|
||||
{
|
||||
int fd;
|
||||
return (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
|
||||
int res = (qemu_chr_fe_get_msgfds(s, &fd, 1) == 1) ? fd : -1;
|
||||
if (s->replay) {
|
||||
fprintf(stderr,
|
||||
"Replay: get msgfd is not supported for serial devices yet\n");
|
||||
exit(1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int qemu_chr_fe_get_msgfds(CharDriverState *s, int *fds, int len)
|
||||
@ -3821,7 +3888,8 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
|
||||
CharDriverState *qemu_chr_new_noreplay(const char *label, const char *filename,
|
||||
void (*init)(struct CharDriverState *s))
|
||||
{
|
||||
const char *p;
|
||||
CharDriverState *chr;
|
||||
@ -3847,6 +3915,21 @@ CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*in
|
||||
return chr;
|
||||
}
|
||||
|
||||
CharDriverState *qemu_chr_new(const char *label, const char *filename, void (*init)(struct CharDriverState *s))
|
||||
{
|
||||
CharDriverState *chr;
|
||||
chr = qemu_chr_new_noreplay(label, filename, init);
|
||||
if (chr) {
|
||||
chr->replay = replay_mode != REPLAY_MODE_NONE;
|
||||
if (chr->replay && chr->chr_ioctl) {
|
||||
fprintf(stderr,
|
||||
"Replay: ioctl is not supported for serial devices yet\n");
|
||||
}
|
||||
replay_register_char_driver(chr);
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
void qemu_chr_fe_set_echo(struct CharDriverState *chr, bool echo)
|
||||
{
|
||||
if (chr->chr_set_echo) {
|
||||
@ -4455,6 +4538,11 @@ void qmp_chardev_remove(const char *id, Error **errp)
|
||||
error_setg(errp, "Chardev '%s' is busy", id);
|
||||
return;
|
||||
}
|
||||
if (chr->replay) {
|
||||
error_setg(errp,
|
||||
"Chardev '%s' cannot be unplugged in record/replay mode", id);
|
||||
return;
|
||||
}
|
||||
qemu_chr_delete(chr);
|
||||
}
|
||||
|
||||
|
@ -394,7 +394,9 @@ static bool timer_mod_ns_locked(QEMUTimerList *timer_list,
|
||||
static void timerlist_rearm(QEMUTimerList *timer_list)
|
||||
{
|
||||
/* Interrupt execution to force deadline recalculation. */
|
||||
qemu_clock_warp(timer_list->clock->type);
|
||||
if (timer_list->clock->type == QEMU_CLOCK_VIRTUAL) {
|
||||
qemu_start_warp_timer();
|
||||
}
|
||||
timerlist_notify(timer_list);
|
||||
}
|
||||
|
||||
|
@ -3,3 +3,4 @@ common-obj-y += replay-internal.o
|
||||
common-obj-y += replay-events.o
|
||||
common-obj-y += replay-time.o
|
||||
common-obj-y += replay-input.o
|
||||
common-obj-y += replay-char.o
|
||||
|
168
replay/replay-char.c
Executable file
168
replay/replay-char.c
Executable file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* replay-char.c
|
||||
*
|
||||
* Copyright (c) 2010-2016 Institute for System Programming
|
||||
* of the Russian Academy of Sciences.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "replay-internal.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/char.h"
|
||||
|
||||
/* Char drivers that generate qemu_chr_be_write events
|
||||
that should be saved into the log. */
|
||||
static CharDriverState **char_drivers;
|
||||
static int drivers_count;
|
||||
|
||||
/* Char event attributes. */
|
||||
typedef struct CharEvent {
|
||||
int id;
|
||||
uint8_t *buf;
|
||||
size_t len;
|
||||
} CharEvent;
|
||||
|
||||
static int find_char_driver(CharDriverState *chr)
|
||||
{
|
||||
int i = 0;
|
||||
for ( ; i < drivers_count ; ++i) {
|
||||
if (char_drivers[i] == chr) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void replay_register_char_driver(CharDriverState *chr)
|
||||
{
|
||||
if (replay_mode == REPLAY_MODE_NONE) {
|
||||
return;
|
||||
}
|
||||
char_drivers = g_realloc(char_drivers,
|
||||
sizeof(*char_drivers) * (drivers_count + 1));
|
||||
char_drivers[drivers_count++] = chr;
|
||||
}
|
||||
|
||||
void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
|
||||
{
|
||||
CharEvent *event = g_malloc0(sizeof(CharEvent));
|
||||
|
||||
event->id = find_char_driver(s);
|
||||
if (event->id < 0) {
|
||||
fprintf(stderr, "Replay: cannot find char driver\n");
|
||||
exit(1);
|
||||
}
|
||||
event->buf = g_malloc(len);
|
||||
memcpy(event->buf, buf, len);
|
||||
event->len = len;
|
||||
|
||||
replay_add_event(REPLAY_ASYNC_EVENT_CHAR_READ, event, NULL, 0);
|
||||
}
|
||||
|
||||
void replay_event_char_read_run(void *opaque)
|
||||
{
|
||||
CharEvent *event = (CharEvent *)opaque;
|
||||
|
||||
qemu_chr_be_write_impl(char_drivers[event->id], event->buf,
|
||||
(int)event->len);
|
||||
|
||||
g_free(event->buf);
|
||||
g_free(event);
|
||||
}
|
||||
|
||||
void replay_event_char_read_save(void *opaque)
|
||||
{
|
||||
CharEvent *event = (CharEvent *)opaque;
|
||||
|
||||
replay_put_byte(event->id);
|
||||
replay_put_array(event->buf, event->len);
|
||||
}
|
||||
|
||||
void *replay_event_char_read_load(void)
|
||||
{
|
||||
CharEvent *event = g_malloc0(sizeof(CharEvent));
|
||||
|
||||
event->id = replay_get_byte();
|
||||
replay_get_array_alloc(&event->buf, &event->len);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
void replay_char_write_event_save(int res, int offset)
|
||||
{
|
||||
replay_save_instructions();
|
||||
replay_mutex_lock();
|
||||
replay_put_event(EVENT_CHAR_WRITE);
|
||||
replay_put_dword(res);
|
||||
replay_put_dword(offset);
|
||||
replay_mutex_unlock();
|
||||
}
|
||||
|
||||
void replay_char_write_event_load(int *res, int *offset)
|
||||
{
|
||||
replay_account_executed_instructions();
|
||||
replay_mutex_lock();
|
||||
if (replay_next_event_is(EVENT_CHAR_WRITE)) {
|
||||
*res = replay_get_dword();
|
||||
*offset = replay_get_dword();
|
||||
replay_finish_event();
|
||||
replay_mutex_unlock();
|
||||
} else {
|
||||
replay_mutex_unlock();
|
||||
error_report("Missing character write event in the replay log");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int replay_char_read_all_load(uint8_t *buf)
|
||||
{
|
||||
replay_mutex_lock();
|
||||
if (replay_next_event_is(EVENT_CHAR_READ_ALL)) {
|
||||
size_t size;
|
||||
int res;
|
||||
replay_get_array(buf, &size);
|
||||
replay_finish_event();
|
||||
replay_mutex_unlock();
|
||||
res = (int)size;
|
||||
assert(res >= 0);
|
||||
return res;
|
||||
} else if (replay_next_event_is(EVENT_CHAR_READ_ALL_ERROR)) {
|
||||
int res = replay_get_dword();
|
||||
replay_finish_event();
|
||||
replay_mutex_unlock();
|
||||
return res;
|
||||
} else {
|
||||
replay_mutex_unlock();
|
||||
error_report("Missing character read all event in the replay log");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void replay_char_read_all_save_error(int res)
|
||||
{
|
||||
assert(res < 0);
|
||||
replay_save_instructions();
|
||||
replay_mutex_lock();
|
||||
replay_put_event(EVENT_CHAR_READ_ALL_ERROR);
|
||||
replay_put_dword(res);
|
||||
replay_mutex_unlock();
|
||||
}
|
||||
|
||||
void replay_char_read_all_save_buf(uint8_t *buf, int offset)
|
||||
{
|
||||
replay_save_instructions();
|
||||
replay_mutex_lock();
|
||||
replay_put_event(EVENT_CHAR_READ_ALL);
|
||||
replay_put_array(buf, offset);
|
||||
replay_mutex_unlock();
|
||||
}
|
@ -48,6 +48,9 @@ static void replay_run_event(Event *event)
|
||||
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
|
||||
qemu_input_event_sync_impl();
|
||||
break;
|
||||
case REPLAY_ASYNC_EVENT_CHAR_READ:
|
||||
replay_event_char_read_run(event->opaque);
|
||||
break;
|
||||
default:
|
||||
error_report("Replay: invalid async event ID (%d) in the queue",
|
||||
event->event_kind);
|
||||
@ -102,9 +105,9 @@ void replay_clear_events(void)
|
||||
}
|
||||
|
||||
/*! Adds specified async event to the queue */
|
||||
static void replay_add_event(ReplayAsyncEventKind event_kind,
|
||||
void *opaque,
|
||||
void *opaque2, uint64_t id)
|
||||
void replay_add_event(ReplayAsyncEventKind event_kind,
|
||||
void *opaque,
|
||||
void *opaque2, uint64_t id)
|
||||
{
|
||||
assert(event_kind < REPLAY_ASYNC_COUNT);
|
||||
|
||||
@ -168,6 +171,9 @@ static void replay_save_event(Event *event, int checkpoint)
|
||||
break;
|
||||
case REPLAY_ASYNC_EVENT_INPUT_SYNC:
|
||||
break;
|
||||
case REPLAY_ASYNC_EVENT_CHAR_READ:
|
||||
replay_event_char_read_save(event->opaque);
|
||||
break;
|
||||
default:
|
||||
error_report("Unknown ID %d of replay event", read_event_kind);
|
||||
exit(1);
|
||||
@ -221,6 +227,11 @@ static Event *replay_read_event(int checkpoint)
|
||||
event->event_kind = read_event_kind;
|
||||
event->opaque = 0;
|
||||
return event;
|
||||
case REPLAY_ASYNC_EVENT_CHAR_READ:
|
||||
event = g_malloc0(sizeof(Event));
|
||||
event->event_kind = read_event_kind;
|
||||
event->opaque = replay_event_char_read_load();
|
||||
return event;
|
||||
default:
|
||||
error_report("Unknown ID %d of replay event", read_event_kind);
|
||||
exit(1);
|
||||
|
@ -24,6 +24,11 @@ enum ReplayEvents {
|
||||
EVENT_ASYNC,
|
||||
/* for shutdown request */
|
||||
EVENT_SHUTDOWN,
|
||||
/* for character device write event */
|
||||
EVENT_CHAR_WRITE,
|
||||
/* for character device read all event */
|
||||
EVENT_CHAR_READ_ALL,
|
||||
EVENT_CHAR_READ_ALL_ERROR,
|
||||
/* for clock read/writes */
|
||||
/* some of greater codes are reserved for clocks */
|
||||
EVENT_CLOCK,
|
||||
@ -43,6 +48,7 @@ enum ReplayAsyncEventKind {
|
||||
REPLAY_ASYNC_EVENT_BH,
|
||||
REPLAY_ASYNC_EVENT_INPUT,
|
||||
REPLAY_ASYNC_EVENT_INPUT_SYNC,
|
||||
REPLAY_ASYNC_EVENT_CHAR_READ,
|
||||
REPLAY_ASYNC_COUNT
|
||||
};
|
||||
|
||||
@ -124,6 +130,9 @@ bool replay_has_events(void);
|
||||
void replay_save_events(int checkpoint);
|
||||
/*! Read events from the file into the input queue */
|
||||
void replay_read_events(int checkpoint);
|
||||
/*! Adds specified async event to the queue */
|
||||
void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque,
|
||||
void *opaque2, uint64_t id);
|
||||
|
||||
/* Input events */
|
||||
|
||||
@ -136,4 +145,13 @@ void replay_add_input_event(struct InputEvent *event);
|
||||
/*! Adds input sync event to the queue */
|
||||
void replay_add_input_sync_event(void);
|
||||
|
||||
/* Character devices */
|
||||
|
||||
/*! Called to run char device read event. */
|
||||
void replay_event_char_read_run(void *opaque);
|
||||
/*! Writes char read event to the file. */
|
||||
void replay_event_char_read_save(void *opaque);
|
||||
/*! Reads char event read from the file. */
|
||||
void *replay_event_char_read_load(void);
|
||||
|
||||
#endif
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
/* Current version of the replay mechanism.
|
||||
Increase it when file format changes. */
|
||||
#define REPLAY_VERSION 0xe02002
|
||||
#define REPLAY_VERSION 0xe02003
|
||||
/* Size of replay log header */
|
||||
#define HEADER_SIZE (sizeof(uint32_t) + sizeof(uint64_t))
|
||||
|
||||
|
@ -103,7 +103,7 @@ done
|
||||
rm -rf "$output/linux-headers/linux"
|
||||
mkdir -p "$output/linux-headers/linux"
|
||||
for header in kvm.h kvm_para.h vfio.h vhost.h \
|
||||
psci.h; do
|
||||
psci.h userfaultfd.h; do
|
||||
cp "$tmpdir/include/linux/$header" "$output/linux-headers/linux"
|
||||
done
|
||||
rm -rf "$output/linux-headers/asm-generic"
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
void qemu_clock_warp(QEMUClockType type)
|
||||
void qemu_start_warp_timer(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -29,3 +29,37 @@ bool replay_events_enabled(void)
|
||||
void replay_finish(void)
|
||||
{
|
||||
}
|
||||
|
||||
void replay_register_char_driver(CharDriverState *chr)
|
||||
{
|
||||
}
|
||||
|
||||
void replay_chr_be_write(CharDriverState *s, uint8_t *buf, int len)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void replay_char_write_event_save(int res, int offset)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void replay_char_write_event_load(int *res, int *offset)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
int replay_char_read_all_load(uint8_t *buf)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void replay_char_read_all_save_error(int res)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
||||
void replay_char_read_all_save_buf(uint8_t *buf, int offset)
|
||||
{
|
||||
abort();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user