mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-23 11:39:53 +00:00
Merge remote-tracking branch 'origin/master' into HEAD
Resolve conflicts around apb. Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
commit
acc95bc850
1
.gitignore
vendored
1
.gitignore
vendored
@ -53,7 +53,6 @@
|
||||
/qemu-version.h.tmp
|
||||
/module_block.h
|
||||
/scsi/qemu-pr-helper
|
||||
/vscclient
|
||||
/vhost-user-scsi
|
||||
/fsdev/virtfs-proxy-helper
|
||||
*.tmp
|
||||
|
27
MAINTAINERS
27
MAINTAINERS
@ -259,6 +259,7 @@ S: Maintained
|
||||
F: target/xtensa/
|
||||
F: hw/xtensa/
|
||||
F: tests/tcg/xtensa/
|
||||
F: disas/xtensa.c
|
||||
|
||||
TriCore
|
||||
M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
|
||||
@ -543,7 +544,7 @@ F: include/hw/*/xlnx*.h
|
||||
|
||||
ARM ACPI Subsystem
|
||||
M: Shannon Zhao <zhaoshenglong@huawei.com>
|
||||
M: Shannon Zhao <shannon.zhao@linaro.org>
|
||||
M: Shannon Zhao <shannon.zhaosl@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
@ -732,7 +733,11 @@ F: hw/ppc/prep.c
|
||||
F: hw/ppc/prep_systemio.c
|
||||
F: hw/ppc/rs6000_mc.c
|
||||
F: hw/pci-host/prep.[hc]
|
||||
F: hw/isa/i82378.c
|
||||
F: hw/isa/pc87312.[hc]
|
||||
F: hw/dma/i82374.c
|
||||
F: hw/timer/m48t59-isa.c
|
||||
F: include/hw/timer/m48t59.h
|
||||
F: pc-bios/ppc_rom.bin
|
||||
|
||||
sPAPR
|
||||
@ -761,6 +766,12 @@ L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/ppc/virtex_ml507.c
|
||||
|
||||
sam460ex
|
||||
M: BALATON Zoltan <balaton@eik.bme.hu>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ide/sii3112.c
|
||||
|
||||
SH4 Machines
|
||||
------------
|
||||
R2D
|
||||
@ -861,12 +872,13 @@ F: hw/misc/sga.c
|
||||
PC Chipset
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Support
|
||||
S: Supported
|
||||
F: hw/char/debugcon.c
|
||||
F: hw/char/parallel.c
|
||||
F: hw/char/serial*
|
||||
F: hw/dma/i8257*
|
||||
F: hw/i2c/pm_smbus.c
|
||||
F: hw/input/pckbd.c
|
||||
F: hw/intc/apic*
|
||||
F: hw/intc/ioapic*
|
||||
F: hw/intc/i8259*
|
||||
@ -875,7 +887,10 @@ F: hw/misc/pc-testdev.c
|
||||
F: hw/timer/hpet*
|
||||
F: hw/timer/i8254*
|
||||
F: hw/timer/mc146818rtc*
|
||||
F: hw/watchdog/wdt_ib700.c
|
||||
F: include/hw/display/vga.h
|
||||
F: include/hw/i2c/pm_smbus.h
|
||||
F: include/hw/isa/i8257.h
|
||||
F: include/hw/timer/hpet.h
|
||||
F: include/hw/timer/i8254*
|
||||
F: include/hw/timer/mc146818rtc*
|
||||
@ -976,7 +991,9 @@ M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/ppc/ppc4*.c
|
||||
F: hw/i2c/ppc4xx_i2c.c
|
||||
F: include/hw/ppc/ppc4xx.h
|
||||
F: include/hw/i2c/ppc4xx_i2c.h
|
||||
|
||||
ppce500
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
@ -995,11 +1012,13 @@ Network devices
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: hw/net/
|
||||
F: include/hw/net/
|
||||
F: tests/virtio-net-test.c
|
||||
T: git git://github.com/jasowang/qemu.git net
|
||||
|
||||
SCSI
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Fam Zheng <famz@redhat.com>
|
||||
S: Supported
|
||||
F: include/hw/scsi/*
|
||||
F: hw/scsi/*
|
||||
@ -1071,13 +1090,11 @@ F: include/hw/virtio/
|
||||
F: tests/virtio-balloon-test.c
|
||||
|
||||
virtio-9p
|
||||
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
M: Greg Kurz <groug@kaod.org>
|
||||
S: Supported
|
||||
F: hw/9pfs/
|
||||
F: fsdev/
|
||||
F: tests/virtio-9p-test.c
|
||||
T: git git://github.com/kvaneesh/QEMU.git
|
||||
T: git git://github.com/gkurz/qemu.git 9p-next
|
||||
|
||||
virtio-blk
|
||||
@ -1260,6 +1277,7 @@ T: git git://github.com/stefanha/qemu.git block
|
||||
|
||||
Block SCSI subsystem
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Fam Zheng <famz@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: include/scsi/*
|
||||
@ -1538,6 +1556,7 @@ M: Alistair Francis <alistair.francis@xilinx.com>
|
||||
S: Maintained
|
||||
F: hw/core/register.c
|
||||
F: include/hw/register.h
|
||||
F: include/hw/registerfields.h
|
||||
|
||||
SLIRP
|
||||
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
|
12
Makefile
12
Makefile
@ -6,7 +6,10 @@ BUILD_DIR=$(CURDIR)
|
||||
# Before including a proper config-host.mak, assume we are in the source tree
|
||||
SRC_PATH=.
|
||||
|
||||
UNCHECKED_GOALS := %clean TAGS cscope ctags docker docker-% help
|
||||
UNCHECKED_GOALS := %clean TAGS cscope ctags dist \
|
||||
html info pdf txt \
|
||||
help check-help \
|
||||
docker docker-% vm-test vm-build-%
|
||||
|
||||
# All following code might depend on configuration variables
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
@ -50,7 +53,7 @@ ifneq ($(realpath $(SRC_PATH)),$(realpath .))
|
||||
ifneq ($(wildcard $(SRC_PATH)/config-host.mak),)
|
||||
$(error This is an out of tree build but your source tree ($(SRC_PATH)) \
|
||||
seems to have been used for an in-tree build. You can fix this by running \
|
||||
"make distclean && rm -rf *-linux-user *-softmmu" in your source tree)
|
||||
"$(MAKE) distclean && rm -rf *-linux-user *-softmmu" in your source tree)
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -229,6 +232,7 @@ KEYCODEMAP_FILES = \
|
||||
ui/input-keymap-linux-to-qcode.c \
|
||||
ui/input-keymap-qcode-to-qnum.c \
|
||||
ui/input-keymap-qnum-to-qcode.c \
|
||||
ui/input-keymap-qcode-to-linux.c \
|
||||
$(NULL)
|
||||
|
||||
GENERATED_FILES += $(KEYCODEMAP_FILES)
|
||||
@ -303,7 +307,7 @@ endif
|
||||
else \
|
||||
echo "WARNING: $@ out of date.";\
|
||||
fi; \
|
||||
echo "Run \"make defconfig\" to regenerate."; \
|
||||
echo "Run \"$(MAKE) defconfig\" to regenerate."; \
|
||||
rm $@.tmp; \
|
||||
fi; \
|
||||
else \
|
||||
@ -933,4 +937,4 @@ ifdef QEMU_GA_MSI_ENABLED
|
||||
endif
|
||||
@echo ''
|
||||
endif
|
||||
@echo ' make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
|
||||
@echo ' $(MAKE) V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
|
||||
|
@ -140,6 +140,7 @@ trace-events-subdirs += hw/input
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sparc64
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/isa
|
||||
trace-events-subdirs += hw/mem
|
||||
|
@ -1,3 +1,4 @@
|
||||
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
|
||||
obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
|
||||
|
31
accel/stubs/hvf-stub.c
Normal file
31
accel/stubs/hvf-stub.c
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QEMU HVF support
|
||||
*
|
||||
* Copyright 2017 Red Hat, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2 or later, as published by the Free Software Foundation,
|
||||
* and may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/hvf.h"
|
||||
|
||||
int hvf_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int hvf_vcpu_exec(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void hvf_vcpu_destroy(CPUState *cpu)
|
||||
{
|
||||
}
|
@ -21,7 +21,6 @@
|
||||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/memory-internal.h"
|
||||
|
||||
bool tcg_allowed;
|
||||
|
||||
|
@ -146,8 +146,10 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
|
||||
uint8_t *tb_ptr = itb->tc.ptr;
|
||||
|
||||
qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc,
|
||||
"Trace %p [%d: " TARGET_FMT_lx "] %s\n",
|
||||
itb->tc.ptr, cpu->cpu_index, itb->pc,
|
||||
"Trace %d: %p ["
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n",
|
||||
cpu->cpu_index, itb->tc.ptr,
|
||||
itb->cs_base, itb->pc, itb->flags,
|
||||
lookup_symbol(itb->pc));
|
||||
|
||||
#if defined(DEBUG_DISAS)
|
||||
@ -525,19 +527,13 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
TranslationBlock **last_tb)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
int32_t insns_left;
|
||||
|
||||
/* Clear the interrupt flag now since we're processing
|
||||
* cpu->interrupt_request and cpu->exit_request.
|
||||
* Ensure zeroing happens before reading cpu->exit_request or
|
||||
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
|
||||
*/
|
||||
insns_left = atomic_read(&cpu->icount_decr.u32);
|
||||
atomic_set(&cpu->icount_decr.u16.high, 0);
|
||||
if (unlikely(insns_left < 0)) {
|
||||
/* Ensure the zeroing of icount_decr comes before the next read
|
||||
* of cpu->exit_request or cpu->interrupt_request.
|
||||
*/
|
||||
smp_mb();
|
||||
}
|
||||
atomic_mb_set(&cpu->icount_decr.u16.high, 0);
|
||||
|
||||
if (unlikely(atomic_read(&cpu->interrupt_request))) {
|
||||
int interrupt_request;
|
||||
|
@ -156,8 +156,9 @@ void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
||||
return tcg_ctx->code_gen_epilogue;
|
||||
}
|
||||
qemu_log_mask_and_addr(CPU_LOG_EXEC, pc,
|
||||
"Chain %p [%d: " TARGET_FMT_lx "] %s\n",
|
||||
tb->tc.ptr, cpu->cpu_index, pc,
|
||||
"Chain %d: %p ["
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n",
|
||||
cpu->cpu_index, tb->tc.ptr, cs_base, pc, flags,
|
||||
lookup_symbol(pc));
|
||||
return tb->tc.ptr;
|
||||
}
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "tcg.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include "qemu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#include <sys/param.h>
|
||||
#if __FreeBSD_version >= 700104
|
||||
@ -257,7 +256,7 @@ static target_long decode_sleb128(uint8_t **pp)
|
||||
/* Encode the data collected about the instructions while compiling TB.
|
||||
Place the data at BLOCK, and return the number of bytes consumed.
|
||||
|
||||
The logical table consisits of TARGET_INSN_START_WORDS target_ulong's,
|
||||
The logical table consists of TARGET_INSN_START_WORDS target_ulong's,
|
||||
which come from the target's insn_start data, followed by a uintptr_t
|
||||
which comes from the host pc of the end of the code implementing the insn.
|
||||
|
||||
|
@ -17,16 +17,25 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "sysemu/tpm.h"
|
||||
#include "hw/tpm/tpm_int.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
static void tpm_backend_request_completed_bh(void *opaque)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(opaque);
|
||||
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
|
||||
|
||||
tic->request_completed(s->tpmif);
|
||||
}
|
||||
|
||||
static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(user_data);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
|
||||
assert(k->handle_request != NULL);
|
||||
k->handle_request(s, (TPMBackendCmd *)data);
|
||||
|
||||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
|
||||
static void tpm_backend_thread_end(TPMBackend *s)
|
||||
@ -44,15 +53,22 @@ enum TpmType tpm_backend_get_type(TPMBackend *s)
|
||||
return k->type;
|
||||
}
|
||||
|
||||
int tpm_backend_init(TPMBackend *s, TPMState *state)
|
||||
int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp)
|
||||
{
|
||||
s->tpm_state = state;
|
||||
if (s->tpmif) {
|
||||
error_setg(errp, "TPM backend '%s' is already initialized", s->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->tpmif = tpmif;
|
||||
object_ref(OBJECT(tpmif));
|
||||
|
||||
s->had_startup_error = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm_backend_startup_tpm(TPMBackend *s)
|
||||
int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
|
||||
{
|
||||
int res = 0;
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
@ -63,7 +79,7 @@ int tpm_backend_startup_tpm(TPMBackend *s)
|
||||
s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
|
||||
NULL);
|
||||
|
||||
res = k->startup_tpm ? k->startup_tpm(s) : 0;
|
||||
res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0;
|
||||
|
||||
s->had_startup_error = (res != 0);
|
||||
|
||||
@ -97,8 +113,6 @@ void tpm_backend_cancel_cmd(TPMBackend *s)
|
||||
{
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
|
||||
assert(k->cancel_cmd);
|
||||
|
||||
k->cancel_cmd(s);
|
||||
}
|
||||
|
||||
@ -122,80 +136,44 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
|
||||
{
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
|
||||
assert(k->get_tpm_version);
|
||||
|
||||
return k->get_tpm_version(s);
|
||||
}
|
||||
|
||||
size_t tpm_backend_get_buffer_size(TPMBackend *s)
|
||||
{
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
|
||||
return k->get_buffer_size(s);
|
||||
}
|
||||
|
||||
TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
|
||||
{
|
||||
TPMInfo *info = g_new0(TPMInfo, 1);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
|
||||
|
||||
info->id = g_strdup(s->id);
|
||||
info->model = s->fe_model;
|
||||
if (k->get_tpm_options) {
|
||||
info->options = k->get_tpm_options(s);
|
||||
}
|
||||
info->model = tic->model;
|
||||
info->options = k->get_tpm_options(s);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
|
||||
return s->opened;
|
||||
}
|
||||
|
||||
void tpm_backend_open(TPMBackend *s, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(s), true, "opened", errp);
|
||||
}
|
||||
|
||||
static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (value == s->opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value && s->opened) {
|
||||
error_setg(errp, QERR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (k->opened) {
|
||||
k->opened(s, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
s->opened = true;
|
||||
}
|
||||
|
||||
static void tpm_backend_instance_init(Object *obj)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
|
||||
object_property_add_bool(obj, "opened",
|
||||
tpm_backend_prop_get_opened,
|
||||
tpm_backend_prop_set_opened,
|
||||
NULL);
|
||||
s->fe_model = -1;
|
||||
s->bh = qemu_bh_new(tpm_backend_request_completed_bh, s);
|
||||
}
|
||||
|
||||
static void tpm_backend_instance_finalize(Object *obj)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
|
||||
object_unref(OBJECT(s->tpmif));
|
||||
g_free(s->id);
|
||||
tpm_backend_thread_end(s);
|
||||
qemu_bh_delete(s->bh);
|
||||
}
|
||||
|
||||
static const TypeInfo tpm_backend_info = {
|
||||
|
89
block.c
89
block.c
@ -822,6 +822,18 @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
|
||||
bdrv_drained_end(bs);
|
||||
}
|
||||
|
||||
static void bdrv_child_cb_attach(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
bdrv_apply_subtree_drain(child, bs);
|
||||
}
|
||||
|
||||
static void bdrv_child_cb_detach(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
bdrv_unapply_subtree_drain(child, bs);
|
||||
}
|
||||
|
||||
static int bdrv_child_cb_inactivate(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
@ -889,6 +901,8 @@ const BdrvChildRole child_file = {
|
||||
.inherit_options = bdrv_inherited_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.attach = bdrv_child_cb_attach,
|
||||
.detach = bdrv_child_cb_detach,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
};
|
||||
|
||||
@ -911,6 +925,8 @@ const BdrvChildRole child_format = {
|
||||
.inherit_options = bdrv_inherited_fmt_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.attach = bdrv_child_cb_attach,
|
||||
.detach = bdrv_child_cb_detach,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
};
|
||||
|
||||
@ -953,6 +969,8 @@ static void bdrv_backing_attach(BdrvChild *c)
|
||||
parent->backing_blocker);
|
||||
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
|
||||
parent->backing_blocker);
|
||||
|
||||
bdrv_child_cb_attach(c);
|
||||
}
|
||||
|
||||
static void bdrv_backing_detach(BdrvChild *c)
|
||||
@ -963,6 +981,8 @@ static void bdrv_backing_detach(BdrvChild *c)
|
||||
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
|
||||
error_free(parent->backing_blocker);
|
||||
parent->backing_blocker = NULL;
|
||||
|
||||
bdrv_child_cb_detach(c);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1924,6 +1944,8 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
assert(role == &child_backing || role == &child_file);
|
||||
|
||||
if (!backing) {
|
||||
int flags = bdrv_reopen_get_flags(reopen_queue, bs);
|
||||
|
||||
/* Apart from the modifications below, the same permissions are
|
||||
* forwarded and left alone as for filters */
|
||||
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
|
||||
@ -1936,7 +1958,9 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
|
||||
/* bs->file always needs to be consistent because of the metadata. We
|
||||
* can never allow other users to resize or write to it. */
|
||||
perm |= BLK_PERM_CONSISTENT_READ;
|
||||
if (!(flags & BDRV_O_NO_IO)) {
|
||||
perm |= BLK_PERM_CONSISTENT_READ;
|
||||
}
|
||||
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
} else {
|
||||
/* We want consistent read from backing files if the parent needs it.
|
||||
@ -1968,17 +1992,23 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
BlockDriverState *new_bs)
|
||||
{
|
||||
BlockDriverState *old_bs = child->bs;
|
||||
int i;
|
||||
|
||||
if (old_bs && new_bs) {
|
||||
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
|
||||
}
|
||||
if (old_bs) {
|
||||
if (old_bs->quiesce_counter && child->role->drained_end) {
|
||||
child->role->drained_end(child);
|
||||
}
|
||||
/* Detach first so that the recursive drain sections coming from @child
|
||||
* are already gone and we only end the drain sections that came from
|
||||
* elsewhere. */
|
||||
if (child->role->detach) {
|
||||
child->role->detach(child);
|
||||
}
|
||||
if (old_bs->quiesce_counter && child->role->drained_end) {
|
||||
for (i = 0; i < old_bs->quiesce_counter; i++) {
|
||||
child->role->drained_end(child);
|
||||
}
|
||||
}
|
||||
QLIST_REMOVE(child, next_parent);
|
||||
}
|
||||
|
||||
@ -1987,9 +2017,14 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
if (new_bs) {
|
||||
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
|
||||
if (new_bs->quiesce_counter && child->role->drained_begin) {
|
||||
child->role->drained_begin(child);
|
||||
for (i = 0; i < new_bs->quiesce_counter; i++) {
|
||||
child->role->drained_begin(child);
|
||||
}
|
||||
}
|
||||
|
||||
/* Attach only after starting new drained sections, so that recursive
|
||||
* drain sections coming from @child don't get an extra .drained_begin
|
||||
* callback. */
|
||||
if (child->role->attach) {
|
||||
child->role->attach(child);
|
||||
}
|
||||
@ -2731,6 +2766,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
|
||||
* returns a pointer to bs_queue, which is either the newly allocated
|
||||
* bs_queue, or the existing bs_queue being used.
|
||||
*
|
||||
* bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
|
||||
*/
|
||||
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
BlockDriverState *bs,
|
||||
@ -2746,6 +2782,11 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
BdrvChild *child;
|
||||
QDict *old_options, *explicit_options;
|
||||
|
||||
/* Make sure that the caller remembered to use a drained section. This is
|
||||
* important to avoid graph changes between the recursive queuing here and
|
||||
* bdrv_reopen_multiple(). */
|
||||
assert(bs->quiesce_counter > 0);
|
||||
|
||||
if (bs_queue == NULL) {
|
||||
bs_queue = g_new0(BlockReopenQueue, 1);
|
||||
QSIMPLEQ_INIT(bs_queue);
|
||||
@ -2870,6 +2911,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
* If all devices prepare successfully, then the changes are committed
|
||||
* to all devices.
|
||||
*
|
||||
* All affected nodes must be drained between bdrv_reopen_queue() and
|
||||
* bdrv_reopen_multiple().
|
||||
*/
|
||||
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
|
||||
{
|
||||
@ -2879,11 +2922,8 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
|
||||
|
||||
assert(bs_queue != NULL);
|
||||
|
||||
aio_context_release(ctx);
|
||||
bdrv_drain_all_begin();
|
||||
aio_context_acquire(ctx);
|
||||
|
||||
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
assert(bs_entry->state.bs->quiesce_counter > 0);
|
||||
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
@ -2912,8 +2952,6 @@ cleanup:
|
||||
}
|
||||
g_free(bs_queue);
|
||||
|
||||
bdrv_drain_all_end();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -2923,12 +2961,18 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
|
||||
{
|
||||
int ret = -1;
|
||||
Error *local_err = NULL;
|
||||
BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
|
||||
BlockReopenQueue *queue;
|
||||
|
||||
bdrv_subtree_drained_begin(bs);
|
||||
|
||||
queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
|
||||
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_end(bs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -4320,9 +4364,15 @@ int bdrv_inactivate_all(void)
|
||||
BdrvNextIterator it;
|
||||
int ret = 0;
|
||||
int pass;
|
||||
GSList *aio_ctxs = NULL, *ctx;
|
||||
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
aio_context_acquire(bdrv_get_aio_context(bs));
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
if (!g_slist_find(aio_ctxs, aio_context)) {
|
||||
aio_ctxs = g_slist_prepend(aio_ctxs, aio_context);
|
||||
aio_context_acquire(aio_context);
|
||||
}
|
||||
}
|
||||
|
||||
/* We do two passes of inactivation. The first pass calls to drivers'
|
||||
@ -4340,9 +4390,11 @@ int bdrv_inactivate_all(void)
|
||||
}
|
||||
|
||||
out:
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
aio_context_release(bdrv_get_aio_context(bs));
|
||||
for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
|
||||
AioContext *aio_context = ctx->data;
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
g_slist_free(aio_ctxs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -4593,10 +4645,11 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
back_flags = flags;
|
||||
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||
|
||||
backing_options = qdict_new();
|
||||
if (backing_fmt) {
|
||||
backing_options = qdict_new();
|
||||
qdict_put_str(backing_options, "driver", backing_fmt);
|
||||
}
|
||||
qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
|
||||
|
||||
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
|
||||
&local_err);
|
||||
@ -4746,7 +4799,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_disable_external(ctx);
|
||||
bdrv_parent_drained_begin(bs);
|
||||
bdrv_parent_drained_begin(bs, NULL);
|
||||
bdrv_drain(bs); /* ensure there are no in-flight requests */
|
||||
|
||||
while (aio_poll(ctx, false)) {
|
||||
@ -4760,7 +4813,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
||||
*/
|
||||
aio_context_acquire(new_context);
|
||||
bdrv_attach_aio_context(bs, new_context);
|
||||
bdrv_parent_drained_end(bs);
|
||||
bdrv_parent_drained_end(bs, NULL);
|
||||
aio_enable_external(ctx);
|
||||
aio_context_release(new_context);
|
||||
}
|
||||
|
116
block/backup.c
116
block/backup.c
@ -40,11 +40,12 @@ typedef struct BackupBlockJob {
|
||||
BlockdevOnError on_target_error;
|
||||
CoRwlock flush_rwlock;
|
||||
uint64_t bytes_read;
|
||||
unsigned long *done_bitmap;
|
||||
int64_t cluster_size;
|
||||
bool compress;
|
||||
NotifierWithReturn before_write;
|
||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||
|
||||
HBitmap *copy_bitmap;
|
||||
} BackupBlockJob;
|
||||
|
||||
/* See if in-flight requests overlap and wait for them to complete */
|
||||
@ -109,10 +110,11 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
cow_request_begin(&cow_request, job, start, end);
|
||||
|
||||
for (; start < end; start += job->cluster_size) {
|
||||
if (test_bit(start / job->cluster_size, job->done_bitmap)) {
|
||||
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
|
||||
trace_backup_do_cow_skip(job, start);
|
||||
continue; /* already copied */
|
||||
}
|
||||
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
|
||||
trace_backup_do_cow_process(job, start);
|
||||
|
||||
@ -132,6 +134,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
if (error_is_read) {
|
||||
*error_is_read = true;
|
||||
}
|
||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -148,11 +151,10 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
if (error_is_read) {
|
||||
*error_is_read = false;
|
||||
}
|
||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_bit(start / job->cluster_size, job->done_bitmap);
|
||||
|
||||
/* Publish progress, guest I/O counts as progress too. Note that the
|
||||
* offset field is an opaque progress value, it is not a disk offset.
|
||||
*/
|
||||
@ -260,7 +262,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
||||
}
|
||||
|
||||
len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size);
|
||||
bitmap_zero(backup_job->done_bitmap, len);
|
||||
hbitmap_set(backup_job->copy_bitmap, 0, len);
|
||||
}
|
||||
|
||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
||||
@ -360,64 +362,68 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
||||
|
||||
static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||
{
|
||||
int ret;
|
||||
bool error_is_read;
|
||||
int ret = 0;
|
||||
int clusters_per_iter;
|
||||
uint32_t granularity;
|
||||
int64_t offset;
|
||||
int64_t cluster;
|
||||
int64_t end;
|
||||
int64_t last_cluster = -1;
|
||||
HBitmapIter hbi;
|
||||
|
||||
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
||||
while ((cluster = hbitmap_iter_next(&hbi)) != -1) {
|
||||
do {
|
||||
if (yield_and_check(job)) {
|
||||
return 0;
|
||||
}
|
||||
ret = backup_do_cow(job, cluster * job->cluster_size,
|
||||
job->cluster_size, &error_is_read, false);
|
||||
if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
|
||||
BLOCK_ERROR_ACTION_REPORT)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
} while (ret < 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init copy_bitmap from sync_bitmap */
|
||||
static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
|
||||
{
|
||||
BdrvDirtyBitmapIter *dbi;
|
||||
int64_t offset;
|
||||
int64_t end = DIV_ROUND_UP(bdrv_dirty_bitmap_size(job->sync_bitmap),
|
||||
job->cluster_size);
|
||||
|
||||
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||
clusters_per_iter = MAX((granularity / job->cluster_size), 1);
|
||||
dbi = bdrv_dirty_iter_new(job->sync_bitmap);
|
||||
while ((offset = bdrv_dirty_iter_next(dbi)) != -1) {
|
||||
int64_t cluster = offset / job->cluster_size;
|
||||
int64_t next_cluster;
|
||||
|
||||
/* Find the next dirty sector(s) */
|
||||
while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) {
|
||||
cluster = offset / job->cluster_size;
|
||||
|
||||
/* Fake progress updates for any clusters we skipped */
|
||||
if (cluster != last_cluster + 1) {
|
||||
job->common.offset += ((cluster - last_cluster - 1) *
|
||||
job->cluster_size);
|
||||
offset += bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||
if (offset >= bdrv_dirty_bitmap_size(job->sync_bitmap)) {
|
||||
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
|
||||
break;
|
||||
}
|
||||
|
||||
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
||||
do {
|
||||
if (yield_and_check(job)) {
|
||||
goto out;
|
||||
}
|
||||
ret = backup_do_cow(job, cluster * job->cluster_size,
|
||||
job->cluster_size, &error_is_read,
|
||||
false);
|
||||
if ((ret < 0) &&
|
||||
backup_error_action(job, error_is_read, -ret) ==
|
||||
BLOCK_ERROR_ACTION_REPORT) {
|
||||
goto out;
|
||||
}
|
||||
} while (ret < 0);
|
||||
offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
|
||||
if (offset == -1) {
|
||||
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the bitmap granularity is smaller than the backup granularity,
|
||||
* we need to advance the iterator pointer to the next cluster. */
|
||||
if (granularity < job->cluster_size) {
|
||||
bdrv_set_dirty_iter(dbi, cluster * job->cluster_size);
|
||||
next_cluster = DIV_ROUND_UP(offset, job->cluster_size);
|
||||
hbitmap_set(job->copy_bitmap, cluster, next_cluster - cluster);
|
||||
if (next_cluster >= end) {
|
||||
break;
|
||||
}
|
||||
|
||||
last_cluster = cluster - 1;
|
||||
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
|
||||
}
|
||||
|
||||
/* Play some final catchup with the progress meter */
|
||||
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
if (last_cluster + 1 < end) {
|
||||
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
|
||||
}
|
||||
job->common.offset = job->common.len -
|
||||
hbitmap_count(job->copy_bitmap) * job->cluster_size;
|
||||
|
||||
out:
|
||||
bdrv_dirty_iter_free(dbi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void coroutine_fn backup_run(void *opaque)
|
||||
@ -425,19 +431,27 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
BackupBlockJob *job = opaque;
|
||||
BackupCompleteData *data;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
int64_t offset;
|
||||
int64_t offset, nb_clusters;
|
||||
int ret = 0;
|
||||
|
||||
QLIST_INIT(&job->inflight_reqs);
|
||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||
|
||||
job->done_bitmap = bitmap_new(DIV_ROUND_UP(job->common.len,
|
||||
job->cluster_size));
|
||||
nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
backup_incremental_init_copy_bitmap(job);
|
||||
} else {
|
||||
hbitmap_set(job->copy_bitmap, 0, nb_clusters);
|
||||
}
|
||||
|
||||
|
||||
job->before_write.notify = backup_before_write_notify;
|
||||
bdrv_add_before_write_notifier(bs, &job->before_write);
|
||||
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
||||
* This does not actually require them to be copied. */
|
||||
while (!block_job_is_cancelled(&job->common)) {
|
||||
/* Yield until the job is cancelled. We just let our before_write
|
||||
* notify callback service CoW requests. */
|
||||
@ -512,7 +526,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
/* wait until pending backup_do_cow() calls have completed */
|
||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
g_free(job->done_bitmap);
|
||||
hbitmap_free(job->copy_bitmap);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
|
@ -277,7 +277,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
const char *filter_node_name, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s;
|
||||
BlockReopenQueue *reopen_queue = NULL;
|
||||
int orig_base_flags;
|
||||
BlockDriverState *iter;
|
||||
BlockDriverState *commit_top_bs = NULL;
|
||||
@ -299,12 +298,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
/* convert base to r/w, if necessary */
|
||||
orig_base_flags = bdrv_get_flags(base);
|
||||
if (!(orig_base_flags & BDRV_O_RDWR)) {
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
|
||||
orig_base_flags | BDRV_O_RDWR);
|
||||
}
|
||||
|
||||
if (reopen_queue) {
|
||||
bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
|
||||
bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
|
24
block/curl.c
24
block/curl.c
@ -89,6 +89,8 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
||||
|
||||
struct BDRVCURLState;
|
||||
|
||||
static bool libcurl_initialized;
|
||||
|
||||
typedef struct CURLAIOCB {
|
||||
Coroutine *co;
|
||||
QEMUIOVector *qiov;
|
||||
@ -686,14 +688,23 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
double d;
|
||||
const char *secretid;
|
||||
const char *protocol_delimiter;
|
||||
int ret;
|
||||
|
||||
static int inited = 0;
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
error_setg(errp, "curl block device does not support writes");
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
if (!libcurl_initialized) {
|
||||
ret = curl_global_init(CURL_GLOBAL_ALL);
|
||||
if (ret) {
|
||||
error_setg(errp, "libcurl initialization failed with %d", ret);
|
||||
return -EIO;
|
||||
}
|
||||
libcurl_initialized = true;
|
||||
}
|
||||
|
||||
qemu_mutex_init(&s->mutex);
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
@ -772,11 +783,6 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
if (!inited) {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
inited = 1;
|
||||
}
|
||||
|
||||
DPRINTF("CURL: Opening %s\n", file);
|
||||
QSIMPLEQ_INIT(&s->free_state_waitq);
|
||||
s->aio_context = bdrv_get_aio_context(bs);
|
||||
@ -851,6 +857,9 @@ out_noclean:
|
||||
qemu_mutex_destroy(&s->mutex);
|
||||
g_free(s->cookie);
|
||||
g_free(s->url);
|
||||
g_free(s->username);
|
||||
g_free(s->proxyusername);
|
||||
g_free(s->proxypassword);
|
||||
qemu_opts_del(opts);
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -949,6 +958,9 @@ static void curl_close(BlockDriverState *bs)
|
||||
|
||||
g_free(s->cookie);
|
||||
g_free(s->url);
|
||||
g_free(s->username);
|
||||
g_free(s->proxyusername);
|
||||
g_free(s->proxypassword);
|
||||
}
|
||||
|
||||
static int64_t curl_getlength(BlockDriverState *bs)
|
||||
|
@ -715,3 +715,8 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
|
||||
{
|
||||
return hbitmap_sha256(bitmap->bitmap, errp);
|
||||
}
|
||||
|
||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
|
||||
{
|
||||
return hbitmap_next_zero(bitmap->bitmap, offset);
|
||||
}
|
||||
|
@ -26,7 +26,6 @@
|
||||
#ifndef BLOCK_DMG_H
|
||||
#define BLOCK_DMG_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include <zlib.h>
|
||||
|
172
block/io.c
172
block/io.c
@ -40,22 +40,28 @@
|
||||
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes, BdrvRequestFlags flags);
|
||||
|
||||
void bdrv_parent_drained_begin(BlockDriverState *bs)
|
||||
void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
|
||||
{
|
||||
BdrvChild *c, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
|
||||
if (c == ignore) {
|
||||
continue;
|
||||
}
|
||||
if (c->role->drained_begin) {
|
||||
c->role->drained_begin(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_parent_drained_end(BlockDriverState *bs)
|
||||
void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
|
||||
{
|
||||
BdrvChild *c, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
|
||||
if (c == ignore) {
|
||||
continue;
|
||||
}
|
||||
if (c->role->drained_end) {
|
||||
c->role->drained_end(c);
|
||||
}
|
||||
@ -134,29 +140,13 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
||||
assert(old >= 1);
|
||||
}
|
||||
|
||||
/* Check if any requests are in-flight (including throttled requests) */
|
||||
bool bdrv_requests_pending(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
|
||||
if (atomic_read(&bs->in_flight)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
if (bdrv_requests_pending(child->bs)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Coroutine *co;
|
||||
BlockDriverState *bs;
|
||||
bool done;
|
||||
bool begin;
|
||||
bool recursive;
|
||||
BdrvChild *parent;
|
||||
} BdrvCoDrainData;
|
||||
|
||||
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
|
||||
@ -175,8 +165,10 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
|
||||
bdrv_wakeup(bs);
|
||||
}
|
||||
|
||||
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
|
||||
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
|
||||
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
|
||||
{
|
||||
BdrvChild *child, *tmp;
|
||||
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
|
||||
|
||||
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
|
||||
@ -187,16 +179,19 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
|
||||
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
|
||||
bdrv_coroutine_enter(bs, data.co);
|
||||
BDRV_POLL_WHILE(bs, !data.done);
|
||||
|
||||
if (recursive) {
|
||||
QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
|
||||
bdrv_drain_invoke(child->bs, begin, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
|
||||
static bool bdrv_drain_recurse(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child, *tmp;
|
||||
bool waited;
|
||||
|
||||
/* Ensure any pending metadata writes are submitted to bs->file. */
|
||||
bdrv_drain_invoke(bs, begin);
|
||||
|
||||
/* Wait for drained requests to finish */
|
||||
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
|
||||
|
||||
@ -215,7 +210,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
|
||||
*/
|
||||
bdrv_ref(bs);
|
||||
}
|
||||
waited |= bdrv_drain_recurse(bs, begin);
|
||||
waited |= bdrv_drain_recurse(bs);
|
||||
if (in_main_loop) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
@ -224,6 +219,11 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
|
||||
return waited;
|
||||
}
|
||||
|
||||
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *parent);
|
||||
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *parent);
|
||||
|
||||
static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
{
|
||||
BdrvCoDrainData *data = opaque;
|
||||
@ -232,9 +232,9 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
|
||||
bdrv_dec_in_flight(bs);
|
||||
if (data->begin) {
|
||||
bdrv_drained_begin(bs);
|
||||
bdrv_do_drained_begin(bs, data->recursive, data->parent);
|
||||
} else {
|
||||
bdrv_drained_end(bs);
|
||||
bdrv_do_drained_end(bs, data->recursive, data->parent);
|
||||
}
|
||||
|
||||
data->done = true;
|
||||
@ -242,7 +242,8 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
}
|
||||
|
||||
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
bool begin)
|
||||
bool begin, bool recursive,
|
||||
BdrvChild *parent)
|
||||
{
|
||||
BdrvCoDrainData data;
|
||||
|
||||
@ -256,6 +257,8 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
.bs = bs,
|
||||
.done = false,
|
||||
.begin = begin,
|
||||
.recursive = recursive,
|
||||
.parent = parent,
|
||||
};
|
||||
bdrv_inc_in_flight(bs);
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
|
||||
@ -267,35 +270,97 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
assert(data.done);
|
||||
}
|
||||
|
||||
void bdrv_drained_begin(BlockDriverState *bs)
|
||||
void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *parent)
|
||||
{
|
||||
BdrvChild *child, *next;
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
bdrv_co_yield_to_drain(bs, true);
|
||||
bdrv_co_yield_to_drain(bs, true, recursive, parent);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stop things in parent-to-child order */
|
||||
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
|
||||
aio_disable_external(bdrv_get_aio_context(bs));
|
||||
bdrv_parent_drained_begin(bs);
|
||||
}
|
||||
|
||||
bdrv_drain_recurse(bs, true);
|
||||
bdrv_parent_drained_begin(bs, parent);
|
||||
bdrv_drain_invoke(bs, true, false);
|
||||
bdrv_drain_recurse(bs);
|
||||
|
||||
if (recursive) {
|
||||
bs->recursive_quiesce_counter++;
|
||||
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
|
||||
bdrv_do_drained_begin(child->bs, true, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_drained_begin(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_drained_begin(bs, false, NULL);
|
||||
}
|
||||
|
||||
void bdrv_subtree_drained_begin(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_drained_begin(bs, true, NULL);
|
||||
}
|
||||
|
||||
void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *parent)
|
||||
{
|
||||
BdrvChild *child, *next;
|
||||
int old_quiesce_counter;
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
bdrv_co_yield_to_drain(bs, false, recursive, parent);
|
||||
return;
|
||||
}
|
||||
assert(bs->quiesce_counter > 0);
|
||||
old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
|
||||
|
||||
/* Re-enable things in child-to-parent order */
|
||||
bdrv_drain_invoke(bs, false, false);
|
||||
bdrv_parent_drained_end(bs, parent);
|
||||
if (old_quiesce_counter == 1) {
|
||||
aio_enable_external(bdrv_get_aio_context(bs));
|
||||
}
|
||||
|
||||
if (recursive) {
|
||||
bs->recursive_quiesce_counter--;
|
||||
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
|
||||
bdrv_do_drained_end(child->bs, true, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_drained_end(BlockDriverState *bs)
|
||||
{
|
||||
if (qemu_in_coroutine()) {
|
||||
bdrv_co_yield_to_drain(bs, false);
|
||||
return;
|
||||
}
|
||||
assert(bs->quiesce_counter > 0);
|
||||
if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
|
||||
return;
|
||||
}
|
||||
bdrv_do_drained_end(bs, false, NULL);
|
||||
}
|
||||
|
||||
bdrv_parent_drained_end(bs);
|
||||
bdrv_drain_recurse(bs, false);
|
||||
aio_enable_external(bdrv_get_aio_context(bs));
|
||||
void bdrv_subtree_drained_end(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_drained_end(bs, true, NULL);
|
||||
}
|
||||
|
||||
void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
|
||||
bdrv_do_drained_begin(child->bs, true, child);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
|
||||
bdrv_do_drained_end(child->bs, true, child);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -342,14 +407,20 @@ void bdrv_drain_all_begin(void)
|
||||
BdrvNextIterator it;
|
||||
GSList *aio_ctxs = NULL, *ctx;
|
||||
|
||||
block_job_pause_all();
|
||||
/* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
|
||||
* or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
|
||||
* nodes in several different AioContexts, so make sure we're in the main
|
||||
* context. */
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
/* Stop things in parent-to-child order */
|
||||
aio_context_acquire(aio_context);
|
||||
bdrv_parent_drained_begin(bs);
|
||||
aio_disable_external(aio_context);
|
||||
bdrv_parent_drained_begin(bs, NULL);
|
||||
bdrv_drain_invoke(bs, true, true);
|
||||
aio_context_release(aio_context);
|
||||
|
||||
if (!g_slist_find(aio_ctxs, aio_context)) {
|
||||
@ -372,7 +443,7 @@ void bdrv_drain_all_begin(void)
|
||||
aio_context_acquire(aio_context);
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
if (aio_context == bdrv_get_aio_context(bs)) {
|
||||
waited |= bdrv_drain_recurse(bs, true);
|
||||
waited |= bdrv_drain_recurse(bs);
|
||||
}
|
||||
}
|
||||
aio_context_release(aio_context);
|
||||
@ -390,14 +461,13 @@ void bdrv_drain_all_end(void)
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
/* Re-enable things in child-to-parent order */
|
||||
aio_context_acquire(aio_context);
|
||||
bdrv_drain_invoke(bs, false, true);
|
||||
bdrv_parent_drained_end(bs, NULL);
|
||||
aio_enable_external(aio_context);
|
||||
bdrv_parent_drained_end(bs);
|
||||
bdrv_drain_recurse(bs, false);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
block_job_resume_all();
|
||||
}
|
||||
|
||||
void bdrv_drain_all(void)
|
||||
|
@ -2,7 +2,7 @@
|
||||
* QEMU Block driver for iSCSI images
|
||||
*
|
||||
* Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
* Copyright (c) 2012-2016 Peter Lieven <pl@kamp.de>
|
||||
* Copyright (c) 2012-2017 Peter Lieven <pl@kamp.de>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -104,6 +104,7 @@ typedef struct IscsiTask {
|
||||
IscsiLun *iscsilun;
|
||||
QEMUTimer retry_timer;
|
||||
int err_code;
|
||||
char *err_str;
|
||||
} IscsiTask;
|
||||
|
||||
typedef struct IscsiAIOCB {
|
||||
@ -265,7 +266,7 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||
}
|
||||
}
|
||||
iTask->err_code = iscsi_translate_sense(&task->sense);
|
||||
error_report("iSCSI Failure: %s", iscsi_get_error(iscsi));
|
||||
iTask->err_str = g_strdup(iscsi_get_error(iscsi));
|
||||
}
|
||||
|
||||
out:
|
||||
@ -629,6 +630,8 @@ retry:
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors);
|
||||
error_report("iSCSI WRITE10/16 failed at lba %" PRIu64 ": %s", lba,
|
||||
iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -637,6 +640,7 @@ retry:
|
||||
|
||||
out_unlock:
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -651,10 +655,9 @@ static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
||||
struct scsi_get_lba_status *lbas = NULL;
|
||||
struct scsi_lba_status_descriptor *lbasd = NULL;
|
||||
struct IscsiTask iTask;
|
||||
uint64_t lba;
|
||||
int64_t ret;
|
||||
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
|
||||
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@ -670,11 +673,13 @@ static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
||||
goto out;
|
||||
}
|
||||
|
||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
retry:
|
||||
if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
|
||||
sector_qemu2lun(sector_num, iscsilun),
|
||||
8 + 16, iscsi_co_generic_cb,
|
||||
lba, 8 + 16, iscsi_co_generic_cb,
|
||||
&iTask) == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out_unlock;
|
||||
@ -701,6 +706,8 @@ retry:
|
||||
* because the device is busy or the cmd is not
|
||||
* supported) we pretend all blocks are allocated
|
||||
* for backwards compatibility */
|
||||
error_report("iSCSI GET_LBA_STATUS failed at lba %" PRIu64 ": %s",
|
||||
lba, iTask.err_str);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
@ -738,6 +745,7 @@ retry:
|
||||
}
|
||||
out_unlock:
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
out:
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
@ -756,6 +764,7 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
||||
struct IscsiTask iTask;
|
||||
uint64_t lba;
|
||||
uint32_t num_sectors;
|
||||
int r = 0;
|
||||
|
||||
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||
return -EINVAL;
|
||||
@ -853,19 +862,23 @@ retry:
|
||||
iTask.complete = 0;
|
||||
goto retry;
|
||||
}
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
error_report("iSCSI READ10/16 failed at lba %" PRIu64 ": %s",
|
||||
lba, iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
}
|
||||
|
||||
return 0;
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int coroutine_fn iscsi_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct IscsiTask iTask;
|
||||
int r = 0;
|
||||
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
@ -892,13 +905,15 @@ retry:
|
||||
iTask.complete = 0;
|
||||
goto retry;
|
||||
}
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
error_report("iSCSI SYNCHRONIZECACHE10 failed: %s", iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
}
|
||||
|
||||
return 0;
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
@ -1128,6 +1143,9 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
|
||||
bytes >> BDRV_SECTOR_BITS);
|
||||
|
||||
if (iTask.status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
/* the target might fail with a check condition if it
|
||||
is not happy with the alignment of the UNMAP request
|
||||
@ -1136,15 +1154,15 @@ retry:
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
error_report("iSCSI UNMAP failed at lba %" PRIu64 ": %s",
|
||||
list.lba, iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
|
||||
bytes >> BDRV_SECTOR_BITS);
|
||||
|
||||
out_unlock:
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -1241,6 +1259,8 @@ retry:
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
|
||||
bytes >> BDRV_SECTOR_BITS);
|
||||
error_report("iSCSI WRITESAME10/16 failed at lba %" PRIu64 ": %s",
|
||||
lba, iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -1255,6 +1275,7 @@ retry:
|
||||
|
||||
out_unlock:
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -388,6 +388,7 @@ static QemuOptsList nbd_runtime_opts = {
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "ID of the TLS credentials to use",
|
||||
},
|
||||
{ /* end of list */ }
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -110,8 +110,7 @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
|
||||
BDRVNullState *s = bs->opaque;
|
||||
|
||||
if (s->latency_ns) {
|
||||
co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME,
|
||||
s->latency_ns);
|
||||
qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, s->latency_ns);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1672,34 +1672,12 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||
return status;
|
||||
}
|
||||
|
||||
/* handle reading after the end of the backing file */
|
||||
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
int n1;
|
||||
|
||||
if ((offset + bytes) <= bs_size) {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
if (offset >= bs_size) {
|
||||
n1 = 0;
|
||||
} else {
|
||||
n1 = bs_size - offset;
|
||||
}
|
||||
|
||||
qemu_iovec_memset(qiov, n1, 0, bytes - n1);
|
||||
|
||||
return n1;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
int offset_in_cluster, n1;
|
||||
int offset_in_cluster;
|
||||
int ret;
|
||||
unsigned int cur_bytes; /* number of bytes in current iteration */
|
||||
uint64_t cluster_offset = 0;
|
||||
@ -1734,26 +1712,13 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||
case QCOW2_CLUSTER_UNALLOCATED:
|
||||
|
||||
if (bs->backing) {
|
||||
/* read from the base image */
|
||||
n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
|
||||
offset, cur_bytes);
|
||||
if (n1 > 0) {
|
||||
QEMUIOVector local_qiov;
|
||||
|
||||
qemu_iovec_init(&local_qiov, hd_qiov.niov);
|
||||
qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_preadv(bs->backing, offset, n1,
|
||||
&local_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
qemu_iovec_destroy(&local_qiov);
|
||||
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
|
||||
&hd_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
/* Note: in this case, no need to wait */
|
||||
|
@ -528,9 +528,6 @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
|
||||
}
|
||||
|
||||
/* qcow2.c functions */
|
||||
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
int64_t sector_num, int nb_sectors);
|
||||
|
||||
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
|
||||
int refcount_order, bool generous_increase,
|
||||
uint64_t *refblock_count);
|
||||
|
@ -394,6 +394,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
||||
new_secondary_flags = s->orig_secondary_flags;
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_begin(s->hidden_disk->bs);
|
||||
bdrv_subtree_drained_begin(s->secondary_disk->bs);
|
||||
|
||||
if (orig_hidden_flags != new_hidden_flags) {
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
|
||||
new_hidden_flags);
|
||||
@ -409,6 +412,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
|
||||
reopen_queue, &local_err);
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_end(s->hidden_disk->bs);
|
||||
bdrv_subtree_drained_end(s->secondary_disk->bs);
|
||||
}
|
||||
|
||||
static void backup_job_cleanup(BlockDriverState *bs)
|
||||
|
169
block/sheepdog.c
169
block/sheepdog.c
@ -400,7 +400,7 @@ typedef struct BDRVSheepdogReopenState {
|
||||
int cache_flags;
|
||||
} BDRVSheepdogReopenState;
|
||||
|
||||
static const char * sd_strerror(int err)
|
||||
static const char *sd_strerror(int err)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -776,8 +776,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
|
||||
if (s->fd < 0) {
|
||||
DPRINTF("Wait for connection to be established\n");
|
||||
error_report_err(local_err);
|
||||
co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME,
|
||||
1000000000ULL);
|
||||
qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1632,7 +1631,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
if (!tag) {
|
||||
tag = "";
|
||||
}
|
||||
if (tag && strlen(tag) >= SD_MAX_VDI_TAG_LEN) {
|
||||
if (strlen(tag) >= SD_MAX_VDI_TAG_LEN) {
|
||||
error_setg(errp, "value of parameter 'tag' is too long");
|
||||
ret = -EINVAL;
|
||||
goto err_no_fd;
|
||||
@ -3078,111 +3077,111 @@ static QemuOptsList sd_create_opts = {
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_sheepdog = {
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_parse_filename = sd_parse_filename,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_parse_filename = sd_parse_filename,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||
.bdrv_co_pdiscard = sd_co_pdiscard,
|
||||
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||
.bdrv_co_pdiscard = sd_co_pdiscard,
|
||||
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||
|
||||
.bdrv_snapshot_create = sd_snapshot_create,
|
||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||
.bdrv_snapshot_delete = sd_snapshot_delete,
|
||||
.bdrv_snapshot_list = sd_snapshot_list,
|
||||
.bdrv_snapshot_create = sd_snapshot_create,
|
||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||
.bdrv_snapshot_delete = sd_snapshot_delete,
|
||||
.bdrv_snapshot_list = sd_snapshot_list,
|
||||
|
||||
.bdrv_save_vmstate = sd_save_vmstate,
|
||||
.bdrv_load_vmstate = sd_load_vmstate,
|
||||
.bdrv_save_vmstate = sd_save_vmstate,
|
||||
.bdrv_load_vmstate = sd_load_vmstate,
|
||||
|
||||
.bdrv_detach_aio_context = sd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = sd_attach_aio_context,
|
||||
.bdrv_detach_aio_context = sd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = sd_attach_aio_context,
|
||||
|
||||
.create_opts = &sd_create_opts,
|
||||
.create_opts = &sd_create_opts,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_sheepdog_tcp = {
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog+tcp",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_parse_filename = sd_parse_filename,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog+tcp",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_parse_filename = sd_parse_filename,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||
.bdrv_co_pdiscard = sd_co_pdiscard,
|
||||
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||
.bdrv_co_pdiscard = sd_co_pdiscard,
|
||||
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||
|
||||
.bdrv_snapshot_create = sd_snapshot_create,
|
||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||
.bdrv_snapshot_delete = sd_snapshot_delete,
|
||||
.bdrv_snapshot_list = sd_snapshot_list,
|
||||
.bdrv_snapshot_create = sd_snapshot_create,
|
||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||
.bdrv_snapshot_delete = sd_snapshot_delete,
|
||||
.bdrv_snapshot_list = sd_snapshot_list,
|
||||
|
||||
.bdrv_save_vmstate = sd_save_vmstate,
|
||||
.bdrv_load_vmstate = sd_load_vmstate,
|
||||
.bdrv_save_vmstate = sd_save_vmstate,
|
||||
.bdrv_load_vmstate = sd_load_vmstate,
|
||||
|
||||
.bdrv_detach_aio_context = sd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = sd_attach_aio_context,
|
||||
.bdrv_detach_aio_context = sd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = sd_attach_aio_context,
|
||||
|
||||
.create_opts = &sd_create_opts,
|
||||
.create_opts = &sd_create_opts,
|
||||
};
|
||||
|
||||
static BlockDriver bdrv_sheepdog_unix = {
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog+unix",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_parse_filename = sd_parse_filename,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.format_name = "sheepdog",
|
||||
.protocol_name = "sheepdog+unix",
|
||||
.instance_size = sizeof(BDRVSheepdogState),
|
||||
.bdrv_parse_filename = sd_parse_filename,
|
||||
.bdrv_file_open = sd_open,
|
||||
.bdrv_reopen_prepare = sd_reopen_prepare,
|
||||
.bdrv_reopen_commit = sd_reopen_commit,
|
||||
.bdrv_reopen_abort = sd_reopen_abort,
|
||||
.bdrv_close = sd_close,
|
||||
.bdrv_create = sd_create,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||
.bdrv_co_pdiscard = sd_co_pdiscard,
|
||||
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||
.bdrv_co_pdiscard = sd_co_pdiscard,
|
||||
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||
|
||||
.bdrv_snapshot_create = sd_snapshot_create,
|
||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||
.bdrv_snapshot_delete = sd_snapshot_delete,
|
||||
.bdrv_snapshot_list = sd_snapshot_list,
|
||||
.bdrv_snapshot_create = sd_snapshot_create,
|
||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||
.bdrv_snapshot_delete = sd_snapshot_delete,
|
||||
.bdrv_snapshot_list = sd_snapshot_list,
|
||||
|
||||
.bdrv_save_vmstate = sd_save_vmstate,
|
||||
.bdrv_load_vmstate = sd_load_vmstate,
|
||||
.bdrv_save_vmstate = sd_save_vmstate,
|
||||
.bdrv_load_vmstate = sd_load_vmstate,
|
||||
|
||||
.bdrv_detach_aio_context = sd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = sd_attach_aio_context,
|
||||
.bdrv_detach_aio_context = sd_detach_aio_context,
|
||||
.bdrv_attach_aio_context = sd_attach_aio_context,
|
||||
|
||||
.create_opts = &sd_create_opts,
|
||||
.create_opts = &sd_create_opts,
|
||||
};
|
||||
|
||||
static void bdrv_sheepdog_init(void)
|
||||
|
@ -18,10 +18,10 @@
|
||||
#include "qmp-commands.h"
|
||||
#include "block/nbd.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "io/net-listener.h"
|
||||
|
||||
typedef struct NBDServerData {
|
||||
QIOChannelSocket *listen_ioc;
|
||||
int watch;
|
||||
QIONetListener *listener;
|
||||
QCryptoTLSCreds *tlscreds;
|
||||
} NBDServerData;
|
||||
|
||||
@ -32,27 +32,13 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
|
||||
nbd_client_put(client);
|
||||
}
|
||||
|
||||
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition,
|
||||
gpointer opaque)
|
||||
static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
gpointer opaque)
|
||||
{
|
||||
QIOChannelSocket *cioc;
|
||||
|
||||
if (!nbd_server) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc),
|
||||
NULL);
|
||||
if (!cioc) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
nbd_client_new(NULL, cioc,
|
||||
nbd_server->tlscreds, NULL,
|
||||
nbd_blockdev_client_closed);
|
||||
object_unref(OBJECT(cioc));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@ -62,10 +48,8 @@ static void nbd_server_free(NBDServerData *server)
|
||||
return;
|
||||
}
|
||||
|
||||
if (server->watch != -1) {
|
||||
g_source_remove(server->watch);
|
||||
}
|
||||
object_unref(OBJECT(server->listen_ioc));
|
||||
qio_net_listener_disconnect(server->listener);
|
||||
object_unref(OBJECT(server->listener));
|
||||
if (server->tlscreds) {
|
||||
object_unref(OBJECT(server->tlscreds));
|
||||
}
|
||||
@ -112,12 +96,12 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
}
|
||||
|
||||
nbd_server = g_new0(NBDServerData, 1);
|
||||
nbd_server->watch = -1;
|
||||
nbd_server->listen_ioc = qio_channel_socket_new();
|
||||
qio_channel_set_name(QIO_CHANNEL(nbd_server->listen_ioc),
|
||||
"nbd-listener");
|
||||
if (qio_channel_socket_listen_sync(
|
||||
nbd_server->listen_ioc, addr, errp) < 0) {
|
||||
nbd_server->listener = qio_net_listener_new();
|
||||
|
||||
qio_net_listener_set_name(nbd_server->listener,
|
||||
"nbd-listener");
|
||||
|
||||
if (qio_net_listener_open_sync(nbd_server->listener, addr, errp) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -134,12 +118,10 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
|
||||
}
|
||||
}
|
||||
|
||||
nbd_server->watch = qio_channel_add_watch(
|
||||
QIO_CHANNEL(nbd_server->listen_ioc),
|
||||
G_IO_IN,
|
||||
nbd_accept,
|
||||
NULL,
|
||||
NULL);
|
||||
qio_net_listener_set_client_func(nbd_server->listener,
|
||||
nbd_accept,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
return;
|
||||
|
||||
|
270
blockdev.c
270
blockdev.c
@ -45,6 +45,7 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qobject-output-visitor.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "block/trace.h"
|
||||
@ -733,10 +734,6 @@ QemuOptsList qemu_legacy_drive_opts = {
|
||||
.name = "trans",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "chs translation (auto, lba, none)",
|
||||
},{
|
||||
.name = "boot",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "(deprecated, ignored)",
|
||||
},{
|
||||
.name = "addr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
@ -872,13 +869,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Deprecated option boot=[on|off] */
|
||||
if (qemu_opt_get(legacy_opts, "boot") != NULL) {
|
||||
fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
|
||||
"ignored. Future versions will reject this parameter. Please "
|
||||
"update your scripts.\n");
|
||||
}
|
||||
|
||||
/* Other deprecated options */
|
||||
if (!qtest_enabled()) {
|
||||
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
|
||||
@ -1454,7 +1444,6 @@ struct BlkActionState {
|
||||
typedef struct InternalSnapshotState {
|
||||
BlkActionState common;
|
||||
BlockDriverState *bs;
|
||||
AioContext *aio_context;
|
||||
QEMUSnapshotInfo sn;
|
||||
bool created;
|
||||
} InternalSnapshotState;
|
||||
@ -1485,6 +1474,7 @@ static void internal_snapshot_prepare(BlkActionState *common,
|
||||
qemu_timeval tv;
|
||||
BlockdevSnapshotInternal *internal;
|
||||
InternalSnapshotState *state;
|
||||
AioContext *aio_context;
|
||||
int ret1;
|
||||
|
||||
g_assert(common->action->type ==
|
||||
@ -1506,32 +1496,33 @@ static void internal_snapshot_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
/* AioContext is released in .clean() */
|
||||
state->aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(state->aio_context);
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
state->bs = bs;
|
||||
|
||||
/* Paired with .clean() */
|
||||
bdrv_drained_begin(bs);
|
||||
|
||||
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) {
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bdrv_is_read_only(bs)) {
|
||||
error_setg(errp, "Device '%s' is read only", device);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!bdrv_can_snapshot(bs)) {
|
||||
error_setg(errp, "Block format '%s' used by device '%s' "
|
||||
"does not support internal snapshots",
|
||||
bs->drv->format_name, device);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!strlen(name)) {
|
||||
error_setg(errp, "Name is empty");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check whether a snapshot with name exist */
|
||||
@ -1539,12 +1530,12 @@ static void internal_snapshot_prepare(BlkActionState *common,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto out;
|
||||
} else if (ret) {
|
||||
error_setg(errp,
|
||||
"Snapshot with name '%s' already exists on device '%s'",
|
||||
name, device);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 3. take the snapshot */
|
||||
@ -1560,11 +1551,14 @@ static void internal_snapshot_prepare(BlkActionState *common,
|
||||
error_setg_errno(errp, -ret1,
|
||||
"Failed to create snapshot '%s' on device '%s'",
|
||||
name, device);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* 4. succeed, mark a snapshot is created */
|
||||
state->created = true;
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void internal_snapshot_abort(BlkActionState *common)
|
||||
@ -1573,12 +1567,16 @@ static void internal_snapshot_abort(BlkActionState *common)
|
||||
DO_UPCAST(InternalSnapshotState, common, common);
|
||||
BlockDriverState *bs = state->bs;
|
||||
QEMUSnapshotInfo *sn = &state->sn;
|
||||
AioContext *aio_context;
|
||||
Error *local_error = NULL;
|
||||
|
||||
if (!state->created) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
|
||||
error_reportf_err(local_error,
|
||||
"Failed to delete snapshot with id '%s' and "
|
||||
@ -1586,19 +1584,26 @@ static void internal_snapshot_abort(BlkActionState *common)
|
||||
sn->id_str, sn->name,
|
||||
bdrv_get_device_name(bs));
|
||||
}
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void internal_snapshot_clean(BlkActionState *common)
|
||||
{
|
||||
InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
|
||||
common, common);
|
||||
AioContext *aio_context;
|
||||
|
||||
if (state->aio_context) {
|
||||
if (state->bs) {
|
||||
bdrv_drained_end(state->bs);
|
||||
}
|
||||
aio_context_release(state->aio_context);
|
||||
if (!state->bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_drained_end(state->bs);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
/* external snapshot private data */
|
||||
@ -1606,7 +1611,6 @@ typedef struct ExternalSnapshotState {
|
||||
BlkActionState common;
|
||||
BlockDriverState *old_bs;
|
||||
BlockDriverState *new_bs;
|
||||
AioContext *aio_context;
|
||||
bool overlay_appended;
|
||||
} ExternalSnapshotState;
|
||||
|
||||
@ -1626,6 +1630,7 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
ExternalSnapshotState *state =
|
||||
DO_UPCAST(ExternalSnapshotState, common, common);
|
||||
TransactionAction *action = common->action;
|
||||
AioContext *aio_context;
|
||||
|
||||
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
|
||||
* purpose but a different set of parameters */
|
||||
@ -1662,31 +1667,32 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Acquire AioContext now so any threads operating on old_bs stop */
|
||||
state->aio_context = bdrv_get_aio_context(state->old_bs);
|
||||
aio_context_acquire(state->aio_context);
|
||||
aio_context = bdrv_get_aio_context(state->old_bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
/* Paired with .clean() */
|
||||
bdrv_drained_begin(state->old_bs);
|
||||
|
||||
if (!bdrv_is_inserted(state->old_bs)) {
|
||||
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(state->old_bs,
|
||||
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!bdrv_is_read_only(state->old_bs)) {
|
||||
if (bdrv_flush(state->old_bs)) {
|
||||
error_setg(errp, QERR_IO_ERROR);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bdrv_is_first_non_filter(state->old_bs)) {
|
||||
error_setg(errp, QERR_FEATURE_DISABLED, "snapshot");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
|
||||
@ -1698,13 +1704,13 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
|
||||
if (node_name && !snapshot_node_name) {
|
||||
error_setg(errp, "New snapshot node name missing");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (snapshot_node_name &&
|
||||
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
|
||||
error_setg(errp, "New snapshot node name already in use");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flags = state->old_bs->open_flags;
|
||||
@ -1717,7 +1723,7 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
int64_t size = bdrv_getlength(state->old_bs);
|
||||
if (size < 0) {
|
||||
error_setg_errno(errp, -size, "bdrv_getlength failed");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
bdrv_img_create(new_image_file, format,
|
||||
state->old_bs->filename,
|
||||
@ -1725,7 +1731,7 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
NULL, size, flags, false, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1740,30 +1746,30 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
errp);
|
||||
/* We will manually add the backing_hd field to the bs later */
|
||||
if (!state->new_bs) {
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bdrv_has_blk(state->new_bs)) {
|
||||
error_setg(errp, "The snapshot is already in use");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
|
||||
errp)) {
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (state->new_bs->backing != NULL) {
|
||||
error_setg(errp, "The snapshot already has a backing image");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!state->new_bs->drv->supports_backing) {
|
||||
error_setg(errp, "The snapshot does not support backing images");
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bdrv_set_aio_context(state->new_bs, state->aio_context);
|
||||
bdrv_set_aio_context(state->new_bs, aio_context);
|
||||
|
||||
/* This removes our old bs and adds the new bs. This is an operation that
|
||||
* can fail, so we need to do it in .prepare; undoing it for abort is
|
||||
@ -1772,15 +1778,22 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
bdrv_append(state->new_bs, state->old_bs, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
state->overlay_appended = true;
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void external_snapshot_commit(BlkActionState *common)
|
||||
{
|
||||
ExternalSnapshotState *state =
|
||||
DO_UPCAST(ExternalSnapshotState, common, common);
|
||||
AioContext *aio_context;
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->old_bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
/* We don't need (or want) to use the transactional
|
||||
* bdrv_reopen_multiple() across all the entries at once, because we
|
||||
@ -1789,6 +1802,8 @@ static void external_snapshot_commit(BlkActionState *common)
|
||||
bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
|
||||
NULL);
|
||||
}
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void external_snapshot_abort(BlkActionState *common)
|
||||
@ -1797,11 +1812,18 @@ static void external_snapshot_abort(BlkActionState *common)
|
||||
DO_UPCAST(ExternalSnapshotState, common, common);
|
||||
if (state->new_bs) {
|
||||
if (state->overlay_appended) {
|
||||
AioContext *aio_context;
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->old_bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd()
|
||||
close state->old_bs; we need it */
|
||||
bdrv_set_backing_hd(state->new_bs, NULL, &error_abort);
|
||||
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
|
||||
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1810,17 +1832,24 @@ static void external_snapshot_clean(BlkActionState *common)
|
||||
{
|
||||
ExternalSnapshotState *state =
|
||||
DO_UPCAST(ExternalSnapshotState, common, common);
|
||||
if (state->aio_context) {
|
||||
bdrv_drained_end(state->old_bs);
|
||||
aio_context_release(state->aio_context);
|
||||
bdrv_unref(state->new_bs);
|
||||
AioContext *aio_context;
|
||||
|
||||
if (!state->old_bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->old_bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_drained_end(state->old_bs);
|
||||
bdrv_unref(state->new_bs);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
typedef struct DriveBackupState {
|
||||
BlkActionState common;
|
||||
BlockDriverState *bs;
|
||||
AioContext *aio_context;
|
||||
BlockJob *job;
|
||||
} DriveBackupState;
|
||||
|
||||
@ -1832,6 +1861,7 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
|
||||
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
||||
BlockDriverState *bs;
|
||||
DriveBackup *backup;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
|
||||
@ -1842,24 +1872,36 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/* AioContext is released in .clean() */
|
||||
state->aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(state->aio_context);
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
/* Paired with .clean() */
|
||||
bdrv_drained_begin(bs);
|
||||
|
||||
state->bs = bs;
|
||||
|
||||
state->job = do_drive_backup(backup, common->block_job_txn, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void drive_backup_commit(BlkActionState *common)
|
||||
{
|
||||
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
||||
AioContext *aio_context;
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
assert(state->job);
|
||||
block_job_start(state->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void drive_backup_abort(BlkActionState *common)
|
||||
@ -1867,25 +1909,38 @@ static void drive_backup_abort(BlkActionState *common)
|
||||
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
||||
|
||||
if (state->job) {
|
||||
AioContext *aio_context;
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
block_job_cancel_sync(state->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void drive_backup_clean(BlkActionState *common)
|
||||
{
|
||||
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
|
||||
AioContext *aio_context;
|
||||
|
||||
if (state->aio_context) {
|
||||
bdrv_drained_end(state->bs);
|
||||
aio_context_release(state->aio_context);
|
||||
if (!state->bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_drained_end(state->bs);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
typedef struct BlockdevBackupState {
|
||||
BlkActionState common;
|
||||
BlockDriverState *bs;
|
||||
BlockJob *job;
|
||||
AioContext *aio_context;
|
||||
} BlockdevBackupState;
|
||||
|
||||
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
||||
@ -1896,6 +1951,7 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
|
||||
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
||||
BlockdevBackup *backup;
|
||||
BlockDriverState *bs, *target;
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
|
||||
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
|
||||
@ -1911,29 +1967,39 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
/* AioContext is released in .clean() */
|
||||
state->aio_context = bdrv_get_aio_context(bs);
|
||||
if (state->aio_context != bdrv_get_aio_context(target)) {
|
||||
state->aio_context = NULL;
|
||||
aio_context = bdrv_get_aio_context(bs);
|
||||
if (aio_context != bdrv_get_aio_context(target)) {
|
||||
error_setg(errp, "Backup between two IO threads is not implemented");
|
||||
return;
|
||||
}
|
||||
aio_context_acquire(state->aio_context);
|
||||
aio_context_acquire(aio_context);
|
||||
state->bs = bs;
|
||||
|
||||
/* Paired with .clean() */
|
||||
bdrv_drained_begin(state->bs);
|
||||
|
||||
state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void blockdev_backup_commit(BlkActionState *common)
|
||||
{
|
||||
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
||||
AioContext *aio_context;
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
assert(state->job);
|
||||
block_job_start(state->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void blockdev_backup_abort(BlkActionState *common)
|
||||
@ -1941,25 +2007,38 @@ static void blockdev_backup_abort(BlkActionState *common)
|
||||
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
||||
|
||||
if (state->job) {
|
||||
AioContext *aio_context;
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
block_job_cancel_sync(state->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void blockdev_backup_clean(BlkActionState *common)
|
||||
{
|
||||
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
|
||||
AioContext *aio_context;
|
||||
|
||||
if (state->aio_context) {
|
||||
bdrv_drained_end(state->bs);
|
||||
aio_context_release(state->aio_context);
|
||||
if (!state->bs) {
|
||||
return;
|
||||
}
|
||||
|
||||
aio_context = bdrv_get_aio_context(state->bs);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
bdrv_drained_end(state->bs);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
typedef struct BlockDirtyBitmapState {
|
||||
BlkActionState common;
|
||||
BdrvDirtyBitmap *bitmap;
|
||||
BlockDriverState *bs;
|
||||
AioContext *aio_context;
|
||||
HBitmap *backup;
|
||||
bool prepared;
|
||||
} BlockDirtyBitmapState;
|
||||
@ -2038,7 +2117,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
||||
}
|
||||
|
||||
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
|
||||
/* AioContext is released in .clean() */
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_clear_abort(BlkActionState *common)
|
||||
@ -2059,16 +2137,6 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common)
|
||||
hbitmap_free(state->backup);
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_clear_clean(BlkActionState *common)
|
||||
{
|
||||
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
||||
common, common);
|
||||
|
||||
if (state->aio_context) {
|
||||
aio_context_release(state->aio_context);
|
||||
}
|
||||
}
|
||||
|
||||
static void abort_prepare(BlkActionState *common, Error **errp)
|
||||
{
|
||||
error_setg(errp, "Transaction aborted using Abort action");
|
||||
@ -2129,7 +2197,6 @@ static const BlkActionOps actions[] = {
|
||||
.prepare = block_dirty_bitmap_clear_prepare,
|
||||
.commit = block_dirty_bitmap_clear_commit,
|
||||
.abort = block_dirty_bitmap_clear_abort,
|
||||
.clean = block_dirty_bitmap_clear_clean,
|
||||
}
|
||||
};
|
||||
|
||||
@ -4052,6 +4119,47 @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
|
||||
return head;
|
||||
}
|
||||
|
||||
void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
|
||||
bool has_force, bool force, Error **errp)
|
||||
{
|
||||
AioContext *old_context;
|
||||
AioContext *new_context;
|
||||
BlockDriverState *bs;
|
||||
|
||||
bs = bdrv_find_node(node_name);
|
||||
if (!bs) {
|
||||
error_setg(errp, "Cannot find node %s", node_name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Protects against accidents. */
|
||||
if (!(has_force && force) && bdrv_has_blk(bs)) {
|
||||
error_setg(errp, "Node %s is associated with a BlockBackend and could "
|
||||
"be in use (use force=true to override this check)",
|
||||
node_name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (iothread->type == QTYPE_QSTRING) {
|
||||
IOThread *obj = iothread_by_id(iothread->u.s);
|
||||
if (!obj) {
|
||||
error_setg(errp, "Cannot find iothread %s", iothread->u.s);
|
||||
return;
|
||||
}
|
||||
|
||||
new_context = iothread_get_aio_context(obj);
|
||||
} else {
|
||||
new_context = qemu_get_aio_context();
|
||||
}
|
||||
|
||||
old_context = bdrv_get_aio_context(bs);
|
||||
aio_context_acquire(old_context);
|
||||
|
||||
bdrv_set_aio_context(bs, new_context);
|
||||
|
||||
aio_context_release(old_context);
|
||||
}
|
||||
|
||||
QemuOptsList qemu_common_drive_opts = {
|
||||
.name = "drive",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
|
||||
|
52
blockjob.c
52
blockjob.c
@ -59,6 +59,7 @@ static void __attribute__((__constructor__)) block_job_init(void)
|
||||
|
||||
static void block_job_event_cancelled(BlockJob *job);
|
||||
static void block_job_event_completed(BlockJob *job, const char *msg);
|
||||
static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job));
|
||||
|
||||
/* Transactional group of block jobs */
|
||||
struct BlockJobTxn {
|
||||
@ -233,26 +234,23 @@ static char *child_job_get_parent_desc(BdrvChild *c)
|
||||
job->id);
|
||||
}
|
||||
|
||||
static const BdrvChildRole child_job = {
|
||||
.get_parent_desc = child_job_get_parent_desc,
|
||||
.stay_at_node = true,
|
||||
};
|
||||
|
||||
static void block_job_drained_begin(void *opaque)
|
||||
static void child_job_drained_begin(BdrvChild *c)
|
||||
{
|
||||
BlockJob *job = opaque;
|
||||
BlockJob *job = c->opaque;
|
||||
block_job_pause(job);
|
||||
}
|
||||
|
||||
static void block_job_drained_end(void *opaque)
|
||||
static void child_job_drained_end(BdrvChild *c)
|
||||
{
|
||||
BlockJob *job = opaque;
|
||||
BlockJob *job = c->opaque;
|
||||
block_job_resume(job);
|
||||
}
|
||||
|
||||
static const BlockDevOps block_job_dev_ops = {
|
||||
.drained_begin = block_job_drained_begin,
|
||||
.drained_end = block_job_drained_end,
|
||||
static const BdrvChildRole child_job = {
|
||||
.get_parent_desc = child_job_get_parent_desc,
|
||||
.drained_begin = child_job_drained_begin,
|
||||
.drained_end = child_job_drained_end,
|
||||
.stay_at_node = true,
|
||||
};
|
||||
|
||||
void block_job_remove_all_bdrv(BlockJob *job)
|
||||
@ -480,9 +478,16 @@ static void block_job_completed_txn_success(BlockJob *job)
|
||||
}
|
||||
}
|
||||
|
||||
/* Assumes the block_job_mutex is held */
|
||||
static bool block_job_timer_pending(BlockJob *job)
|
||||
{
|
||||
return timer_pending(&job->sleep_timer);
|
||||
}
|
||||
|
||||
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int64_t old_speed = job->speed;
|
||||
|
||||
if (!job->driver->set_speed) {
|
||||
error_setg(errp, QERR_UNSUPPORTED);
|
||||
@ -495,6 +500,12 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
}
|
||||
|
||||
job->speed = speed;
|
||||
if (speed <= old_speed) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* kick only if a timer is pending */
|
||||
block_job_enter_cond(job, block_job_timer_pending);
|
||||
}
|
||||
|
||||
void block_job_complete(BlockJob *job, Error **errp)
|
||||
@ -701,7 +712,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
|
||||
bs->job = job;
|
||||
|
||||
blk_set_dev_ops(blk, &block_job_dev_ops, job);
|
||||
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
|
||||
|
||||
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
|
||||
@ -821,7 +831,11 @@ void block_job_resume_all(void)
|
||||
}
|
||||
}
|
||||
|
||||
void block_job_enter(BlockJob *job)
|
||||
/*
|
||||
* Conditionally enter a block_job pending a call to fn() while
|
||||
* under the block_job_lock critical section.
|
||||
*/
|
||||
static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
|
||||
{
|
||||
if (!block_job_started(job)) {
|
||||
return;
|
||||
@ -836,6 +850,11 @@ void block_job_enter(BlockJob *job)
|
||||
return;
|
||||
}
|
||||
|
||||
if (fn && !fn(job)) {
|
||||
block_job_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!job->deferred_to_main_loop);
|
||||
timer_del(&job->sleep_timer);
|
||||
job->busy = true;
|
||||
@ -843,6 +862,11 @@ void block_job_enter(BlockJob *job)
|
||||
aio_co_wake(job->co);
|
||||
}
|
||||
|
||||
void block_job_enter(BlockJob *job)
|
||||
{
|
||||
block_job_enter_cond(job, NULL);
|
||||
}
|
||||
|
||||
bool block_job_is_cancelled(BlockJob *job)
|
||||
{
|
||||
return job->cancelled;
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include "qemu/envlist.h"
|
||||
#include "exec/log.h"
|
||||
#include "trace/control.h"
|
||||
#include "glib-compat.h"
|
||||
|
||||
int singlestep;
|
||||
unsigned long mmap_min_addr;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* QEMU Baum Braille Device
|
||||
*
|
||||
* Copyright (c) 2008, 2010-2011, 2016 Samuel Thibault
|
||||
* Copyright (c) 2008, 2010-2011, 2016-2017 Samuel Thibault
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -239,6 +239,12 @@ static int baum_deferred_init(BaumChardev *baum)
|
||||
brlapi_perror("baum: brlapi__getDisplaySize");
|
||||
return 0;
|
||||
}
|
||||
if (baum->y > 1) {
|
||||
baum->y = 1;
|
||||
}
|
||||
if (baum->x > 84) {
|
||||
baum->x = 84;
|
||||
}
|
||||
|
||||
con = qemu_console_lookup_by_index(0);
|
||||
if (con && qemu_console_is_graphic(con)) {
|
||||
|
@ -123,6 +123,15 @@ static void mux_chr_send_event(MuxChardev *d, int mux_nr, int event)
|
||||
}
|
||||
}
|
||||
|
||||
static void mux_chr_be_event(Chardev *chr, int event)
|
||||
{
|
||||
MuxChardev *d = MUX_CHARDEV(chr);
|
||||
|
||||
if (d->focus != -1) {
|
||||
mux_chr_send_event(d, d->focus, event);
|
||||
}
|
||||
}
|
||||
|
||||
static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
|
||||
{
|
||||
if (d->term_got_escape) {
|
||||
@ -346,6 +355,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_write = mux_chr_write;
|
||||
cc->chr_accept_input = mux_chr_accept_input;
|
||||
cc->chr_add_watch = mux_chr_add_watch;
|
||||
cc->chr_be_event = mux_chr_be_event;
|
||||
}
|
||||
|
||||
static const TypeInfo char_mux_type_info = {
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "chardev/char.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "io/channel-tls.h"
|
||||
#include "io/net-listener.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
@ -40,8 +41,7 @@ typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc; /* Client I/O channel */
|
||||
QIOChannelSocket *sioc; /* Client master channel */
|
||||
QIOChannelSocket *listen_ioc;
|
||||
guint listen_tag;
|
||||
QIONetListener *listener;
|
||||
QCryptoTLSCreds *tls_creds;
|
||||
int connected;
|
||||
int max_size;
|
||||
@ -93,9 +93,9 @@ static void check_report_connect_error(Chardev *chr,
|
||||
qemu_chr_socket_restart_timer(chr);
|
||||
}
|
||||
|
||||
static gboolean tcp_chr_accept(QIOChannel *chan,
|
||||
GIOCondition cond,
|
||||
void *opaque);
|
||||
static void tcp_chr_accept(QIONetListener *listener,
|
||||
QIOChannelSocket *cioc,
|
||||
void *opaque);
|
||||
|
||||
static int tcp_chr_read_poll(void *opaque);
|
||||
static void tcp_chr_disconnect(Chardev *chr);
|
||||
@ -401,9 +401,9 @@ static void tcp_chr_disconnect(Chardev *chr)
|
||||
|
||||
tcp_chr_free_connection(chr);
|
||||
|
||||
if (s->listen_ioc && s->listen_tag == 0) {
|
||||
s->listen_tag = qio_channel_add_watch(
|
||||
QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL);
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
|
||||
chr, NULL);
|
||||
}
|
||||
update_disconnected_filename(s);
|
||||
if (emit_close) {
|
||||
@ -702,9 +702,8 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
|
||||
if (s->do_nodelay) {
|
||||
qio_channel_set_delay(s->ioc, false);
|
||||
}
|
||||
if (s->listen_tag) {
|
||||
g_source_remove(s->listen_tag);
|
||||
s->listen_tag = 0;
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if (s->tls_creds) {
|
||||
@ -736,24 +735,14 @@ static int tcp_chr_add_client(Chardev *chr, int fd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static gboolean tcp_chr_accept(QIOChannel *channel,
|
||||
GIOCondition cond,
|
||||
void *opaque)
|
||||
static void tcp_chr_accept(QIONetListener *listener,
|
||||
QIOChannelSocket *cioc,
|
||||
void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
QIOChannelSocket *sioc;
|
||||
|
||||
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel),
|
||||
NULL);
|
||||
if (!sioc) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
tcp_chr_new_client(chr, sioc);
|
||||
|
||||
object_unref(OBJECT(sioc));
|
||||
|
||||
return TRUE;
|
||||
tcp_chr_set_client_ioc_name(chr, cioc);
|
||||
tcp_chr_new_client(chr, cioc);
|
||||
}
|
||||
|
||||
static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
|
||||
@ -767,9 +756,10 @@ static int tcp_chr_wait_connected(Chardev *chr, Error **errp)
|
||||
if (s->is_listen) {
|
||||
info_report("QEMU waiting for connection on: %s",
|
||||
chr->filename);
|
||||
qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL);
|
||||
tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr);
|
||||
qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL);
|
||||
sioc = qio_net_listener_wait_client(s->listener);
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
tcp_chr_new_client(chr, sioc);
|
||||
object_unref(OBJECT(sioc));
|
||||
} else {
|
||||
sioc = qio_channel_socket_new();
|
||||
tcp_chr_set_client_ioc_name(chr, sioc);
|
||||
@ -797,12 +787,9 @@ static void char_socket_finalize(Object *obj)
|
||||
s->reconnect_timer = 0;
|
||||
}
|
||||
qapi_free_SocketAddress(s->addr);
|
||||
if (s->listen_tag) {
|
||||
g_source_remove(s->listen_tag);
|
||||
s->listen_tag = 0;
|
||||
}
|
||||
if (s->listen_ioc) {
|
||||
object_unref(OBJECT(s->listen_ioc));
|
||||
if (s->listener) {
|
||||
qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
|
||||
object_unref(OBJECT(s->listener));
|
||||
}
|
||||
if (s->tls_creds) {
|
||||
object_unref(OBJECT(s->tls_creds));
|
||||
@ -935,29 +922,29 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
} else {
|
||||
if (s->is_listen) {
|
||||
char *name;
|
||||
sioc = qio_channel_socket_new();
|
||||
s->listener = qio_net_listener_new();
|
||||
|
||||
name = g_strdup_printf("chardev-tcp-listener-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(sioc), name);
|
||||
qio_net_listener_set_name(s->listener, name);
|
||||
g_free(name);
|
||||
|
||||
if (qio_channel_socket_listen_sync(sioc, s->addr, errp) < 0) {
|
||||
if (qio_net_listener_open_sync(s->listener, s->addr, errp) < 0) {
|
||||
object_unref(OBJECT(s->listener));
|
||||
s->listener = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
qapi_free_SocketAddress(s->addr);
|
||||
s->addr = socket_local_address(sioc->fd, errp);
|
||||
s->addr = socket_local_address(s->listener->sioc[0]->fd, errp);
|
||||
update_disconnected_filename(s);
|
||||
|
||||
s->listen_ioc = sioc;
|
||||
if (is_waitconnect &&
|
||||
qemu_chr_wait_connected(chr, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
if (!s->ioc) {
|
||||
s->listen_tag = qio_channel_add_watch(
|
||||
QIO_CHANNEL(s->listen_ioc), G_IO_IN,
|
||||
tcp_chr_accept, chr, NULL);
|
||||
qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
|
||||
chr, NULL);
|
||||
}
|
||||
} else if (qemu_chr_wait_connected(chr, errp) < 0) {
|
||||
goto error;
|
||||
|
@ -43,10 +43,19 @@ static Object *get_chardevs_root(void)
|
||||
return container_get(object_get_root(), "/chardevs");
|
||||
}
|
||||
|
||||
void qemu_chr_be_event(Chardev *s, int event)
|
||||
static void chr_be_event(Chardev *s, int event)
|
||||
{
|
||||
CharBackend *be = s->be;
|
||||
|
||||
if (!be || !be->chr_event) {
|
||||
return;
|
||||
}
|
||||
|
||||
be->chr_event(be->opaque, event);
|
||||
}
|
||||
|
||||
void qemu_chr_be_event(Chardev *s, int event)
|
||||
{
|
||||
/* Keep track if the char device is open */
|
||||
switch (event) {
|
||||
case CHR_EVENT_OPENED:
|
||||
@ -57,11 +66,7 @@ void qemu_chr_be_event(Chardev *s, int event)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!be || !be->chr_event) {
|
||||
return;
|
||||
}
|
||||
|
||||
be->chr_event(be->opaque, event);
|
||||
CHARDEV_GET_CLASS(s)->chr_be_event(s, event);
|
||||
}
|
||||
|
||||
/* Not reporting errors from writing to logfile, as logs are
|
||||
@ -244,6 +249,7 @@ static void char_class_init(ObjectClass *oc, void *data)
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
|
||||
cc->chr_write = null_chr_write;
|
||||
cc->chr_be_event = chr_be_event;
|
||||
}
|
||||
|
||||
static void char_finalize(Object *obj)
|
||||
|
@ -25,10 +25,6 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
|
105
configure
vendored
105
configure
vendored
@ -211,6 +211,17 @@ supported_xen_target() {
|
||||
return 1
|
||||
}
|
||||
|
||||
supported_hvf_target() {
|
||||
test "$hvf" = "yes" || return 1
|
||||
glob "$1" "*-softmmu" || return 1
|
||||
case "${1%-softmmu}" in
|
||||
x86_64)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
supported_target() {
|
||||
case "$1" in
|
||||
*-softmmu)
|
||||
@ -236,6 +247,7 @@ supported_target() {
|
||||
supported_kvm_target "$1" && return 0
|
||||
supported_xen_target "$1" && return 0
|
||||
supported_hax_target "$1" && return 0
|
||||
supported_hvf_target "$1" && return 0
|
||||
print_error "TCG disabled, but hardware accelerator not available for '$target'"
|
||||
return 1
|
||||
}
|
||||
@ -325,6 +337,7 @@ vhost_vsock="no"
|
||||
vhost_user=""
|
||||
kvm="no"
|
||||
hax="no"
|
||||
hvf="no"
|
||||
rdma=""
|
||||
gprof="no"
|
||||
debug_tcg="no"
|
||||
@ -426,6 +439,7 @@ vxhs=""
|
||||
supported_cpu="no"
|
||||
supported_os="no"
|
||||
bogus_os="no"
|
||||
malloc_trim=""
|
||||
|
||||
# parse CC options first
|
||||
for opt do
|
||||
@ -740,6 +754,7 @@ Darwin)
|
||||
bsd="yes"
|
||||
darwin="yes"
|
||||
hax="yes"
|
||||
hvf="yes"
|
||||
LDFLAGS_SHARED="-bundle -undefined dynamic_lookup"
|
||||
if [ "$cpu" = "x86_64" ] ; then
|
||||
QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
|
||||
@ -1035,6 +1050,10 @@ for opt do
|
||||
;;
|
||||
--enable-hax) hax="yes"
|
||||
;;
|
||||
--disable-hvf) hvf="no"
|
||||
;;
|
||||
--enable-hvf) hvf="yes"
|
||||
;;
|
||||
--disable-tcg-interpreter) tcg_interpreter="no"
|
||||
;;
|
||||
--enable-tcg-interpreter) tcg_interpreter="yes"
|
||||
@ -1047,6 +1066,10 @@ for opt do
|
||||
;;
|
||||
--enable-tcg) tcg="yes"
|
||||
;;
|
||||
--disable-malloc-trim) malloc_trim="no"
|
||||
;;
|
||||
--enable-malloc-trim) malloc_trim="yes"
|
||||
;;
|
||||
--disable-spice) spice="no"
|
||||
;;
|
||||
--enable-spice) spice="yes"
|
||||
@ -1466,6 +1489,7 @@ Advanced options (experts only):
|
||||
Default:trace-<pid>
|
||||
--disable-slirp disable SLIRP userspace network connectivity
|
||||
--enable-tcg-interpreter enable TCG with bytecode interpreter (TCI)
|
||||
--enable-malloc-trim enable libc malloc_trim() for memory optimization
|
||||
--oss-lib path to OSS library
|
||||
--cpu=CPU Build for host CPU [$cpu]
|
||||
--with-coroutine=BACKEND coroutine backend. Supported options:
|
||||
@ -1523,6 +1547,7 @@ disabled with --disable-FEATURE, default is enabled if available:
|
||||
bluez bluez stack connectivity
|
||||
kvm KVM acceleration support
|
||||
hax HAX acceleration support
|
||||
hvf Hypervisor.framework acceleration support
|
||||
rdma RDMA-based migration support
|
||||
vde support for vde network
|
||||
netmap support for netmap network
|
||||
@ -1582,6 +1607,20 @@ fi
|
||||
# Suppress writing compiled files
|
||||
python="$python -B"
|
||||
|
||||
# Check that the C compiler works. Doing this here before testing
|
||||
# the host CPU ensures that we had a valid CC to autodetect the
|
||||
# $cpu var (and we should bail right here if that's not the case).
|
||||
# It also allows the help message to be printed without a CC.
|
||||
write_c_skeleton;
|
||||
if compile_object ; then
|
||||
: C compiler works ok
|
||||
else
|
||||
error_exit "\"$cc\" either does not exist or does not work"
|
||||
fi
|
||||
if ! compile_prog ; then
|
||||
error_exit "\"$cc\" cannot build an executable (is your linker broken?)"
|
||||
fi
|
||||
|
||||
# Now we have handled --enable-tcg-interpreter and know we're not just
|
||||
# printing the help message, bail out if the host CPU isn't supported.
|
||||
if test "$ARCH" = "unknown"; then
|
||||
@ -1603,17 +1642,6 @@ if test -z "$werror" ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# check that the C compiler works.
|
||||
write_c_skeleton;
|
||||
if compile_object ; then
|
||||
: C compiler works ok
|
||||
else
|
||||
error_exit "\"$cc\" either does not exist or does not work"
|
||||
fi
|
||||
if ! compile_prog ; then
|
||||
error_exit "\"$cc\" cannot build an executable (is your linker broken?)"
|
||||
fi
|
||||
|
||||
if test "$bogus_os" = "yes"; then
|
||||
# Now that we know that we're not printing the help and that
|
||||
# the compiler works (so the results of the check_defines we used
|
||||
@ -3857,6 +3885,30 @@ if test "$tcmalloc" = "yes" && test "$jemalloc" = "yes" ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Even if malloc_trim() is available, these non-libc memory allocators
|
||||
# do not support it.
|
||||
if test "$tcmalloc" = "yes" || test "$jemalloc" = "yes" ; then
|
||||
if test "$malloc_trim" = "yes" ; then
|
||||
echo "Disabling malloc_trim with non-libc memory allocator"
|
||||
fi
|
||||
malloc_trim="no"
|
||||
fi
|
||||
|
||||
#######################################
|
||||
# malloc_trim
|
||||
|
||||
if test "$malloc_trim" != "no" ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <malloc.h>
|
||||
int main(void) { malloc_trim(0); return 0; }
|
||||
EOF
|
||||
if compile_prog "" "" ; then
|
||||
malloc_trim="yes"
|
||||
else
|
||||
malloc_trim="no"
|
||||
fi
|
||||
fi
|
||||
|
||||
##########################################
|
||||
# tcmalloc probe
|
||||
|
||||
@ -3920,7 +3972,7 @@ fi
|
||||
# check if memfd is supported
|
||||
memfd=no
|
||||
cat > $TMPC << EOF
|
||||
#include <sys/memfd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
@ -5022,6 +5074,21 @@ then
|
||||
fi
|
||||
|
||||
|
||||
#################################################
|
||||
# Check to see if we have the Hypervisor framework
|
||||
if [ "$darwin" = "yes" ] ; then
|
||||
cat > $TMPC << EOF
|
||||
#include <Hypervisor/hv.h>
|
||||
int main() { return 0;}
|
||||
EOF
|
||||
if ! compile_object ""; then
|
||||
hvf='no'
|
||||
else
|
||||
hvf='yes'
|
||||
LDFLAGS="-framework Hypervisor $LDFLAGS"
|
||||
fi
|
||||
fi
|
||||
|
||||
#################################################
|
||||
# Sparc implicitly links with --relax, which is
|
||||
# incompatible with -r, so --no-relax should be
|
||||
@ -5497,11 +5564,13 @@ echo "ATTR/XATTR support $attr"
|
||||
echo "Install blobs $blobs"
|
||||
echo "KVM support $kvm"
|
||||
echo "HAX support $hax"
|
||||
echo "HVF support $hvf"
|
||||
echo "TCG support $tcg"
|
||||
if test "$tcg" = "yes" ; then
|
||||
echo "TCG debug enabled $debug_tcg"
|
||||
echo "TCG interpreter $tcg_interpreter"
|
||||
fi
|
||||
echo "malloc trim support $malloc_trim"
|
||||
echo "RDMA support $rdma"
|
||||
echo "fdt support $fdt"
|
||||
echo "preadv support $preadv"
|
||||
@ -6012,6 +6081,10 @@ if test "$opengl" = "yes" ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$malloc_trim" = "yes" ; then
|
||||
echo "CONFIG_MALLOC_TRIM=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
if test "$avx2_opt" = "yes" ; then
|
||||
echo "CONFIG_AVX2_OPT=y" >> $config_host_mak
|
||||
fi
|
||||
@ -6366,7 +6439,7 @@ target_name=$(echo $target | cut -d '-' -f 1)
|
||||
target_bigendian="no"
|
||||
|
||||
case "$target_name" in
|
||||
armeb|hppa|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or1k|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb)
|
||||
armeb|aarch64_be|hppa|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or1k|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb)
|
||||
target_bigendian=yes
|
||||
;;
|
||||
esac
|
||||
@ -6421,7 +6494,8 @@ case "$target_name" in
|
||||
mttcg="yes"
|
||||
gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
|
||||
;;
|
||||
aarch64)
|
||||
aarch64|aarch64_be)
|
||||
TARGET_ARCH=aarch64
|
||||
TARGET_BASE_ARCH=arm
|
||||
bflt="yes"
|
||||
mttcg="yes"
|
||||
@ -6564,6 +6638,9 @@ fi
|
||||
if supported_hax_target $target; then
|
||||
echo "CONFIG_HAX=y" >> $config_target_mak
|
||||
fi
|
||||
if supported_hvf_target $target; then
|
||||
echo "CONFIG_HVF=y" >> $config_target_mak
|
||||
fi
|
||||
if test "$target_bigendian" = "yes" ; then
|
||||
echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
|
||||
fi
|
||||
|
11
contrib/systemd/qemu-guest-agent.service
Normal file
11
contrib/systemd/qemu-guest-agent.service
Normal file
@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=QEMU Guest Agent
|
||||
BindTo=dev-virtio\x2dports-org.qemu.guest_agent.0.device
|
||||
After=dev-virtio\x2dports-org.qemu.guest_agent.0.device
|
||||
|
||||
[Service]
|
||||
ExecStart=-/usr/bin/qemu-ga
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
|
||||
[Install]
|
15
contrib/systemd/qemu-pr-helper.service
Normal file
15
contrib/systemd/qemu-pr-helper.service
Normal file
@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Persistent Reservation Daemon for QEMU
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/tmp
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/qemu-pr-helper
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/var/run
|
||||
RestrictAddressFamilies=AF_UNIX
|
||||
Restart=always
|
||||
RestartSec=0
|
||||
|
||||
[Install]
|
9
contrib/systemd/qemu-pr-helper.socket
Normal file
9
contrib/systemd/qemu-pr-helper.socket
Normal file
@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Persistent Reservation Daemon for QEMU
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/qemu-pr-helper.sock
|
||||
SocketMode=0600
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
123
cpus.c
123
cpus.c
@ -37,6 +37,7 @@
|
||||
#include "sysemu/hw_accel.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/hax.h"
|
||||
#include "sysemu/hvf.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
@ -900,6 +901,10 @@ void cpu_synchronize_all_states(void)
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
cpu_synchronize_state(cpu);
|
||||
/* TODO: move to cpu_synchronize_state() */
|
||||
if (hvf_enabled()) {
|
||||
hvf_cpu_synchronize_state(cpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -909,6 +914,10 @@ void cpu_synchronize_all_post_reset(void)
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
cpu_synchronize_post_reset(cpu);
|
||||
/* TODO: move to cpu_synchronize_post_reset() */
|
||||
if (hvf_enabled()) {
|
||||
hvf_cpu_synchronize_post_reset(cpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,6 +927,10 @@ void cpu_synchronize_all_post_init(void)
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
cpu_synchronize_post_init(cpu);
|
||||
/* TODO: move to cpu_synchronize_post_init() */
|
||||
if (hvf_enabled()) {
|
||||
hvf_cpu_synchronize_post_init(cpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1057,13 +1070,22 @@ static void qemu_tcg_destroy_vcpu(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
|
||||
static void qemu_cpu_stop(CPUState *cpu, bool exit)
|
||||
{
|
||||
g_assert(qemu_cpu_is_self(cpu));
|
||||
cpu->stop = false;
|
||||
cpu->stopped = true;
|
||||
if (exit) {
|
||||
cpu_exit(cpu);
|
||||
}
|
||||
qemu_cond_broadcast(&qemu_pause_cond);
|
||||
}
|
||||
|
||||
static void qemu_wait_io_event_common(CPUState *cpu)
|
||||
{
|
||||
atomic_mb_set(&cpu->thread_kicked, false);
|
||||
if (cpu->stop) {
|
||||
cpu->stop = false;
|
||||
cpu->stopped = true;
|
||||
qemu_cond_broadcast(&qemu_pause_cond);
|
||||
qemu_cpu_stop(cpu, false);
|
||||
}
|
||||
process_queued_cpu_work(cpu);
|
||||
}
|
||||
@ -1098,6 +1120,14 @@ static void qemu_kvm_wait_io_event(CPUState *cpu)
|
||||
qemu_wait_io_event_common(cpu);
|
||||
}
|
||||
|
||||
static void qemu_hvf_wait_io_event(CPUState *cpu)
|
||||
{
|
||||
while (cpu_thread_is_idle(cpu)) {
|
||||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||
}
|
||||
qemu_wait_io_event_common(cpu);
|
||||
}
|
||||
|
||||
static void *qemu_kvm_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
@ -1435,6 +1465,48 @@ static void *qemu_hax_cpu_thread_fn(void *arg)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The HVF-specific vCPU thread function. This one should only run when the host
|
||||
* CPU supports the VMX "unrestricted guest" feature. */
|
||||
static void *qemu_hvf_cpu_thread_fn(void *arg)
|
||||
{
|
||||
CPUState *cpu = arg;
|
||||
|
||||
int r;
|
||||
|
||||
assert(hvf_enabled());
|
||||
|
||||
rcu_register_thread();
|
||||
|
||||
qemu_mutex_lock_iothread();
|
||||
qemu_thread_get_self(cpu->thread);
|
||||
|
||||
cpu->thread_id = qemu_get_thread_id();
|
||||
cpu->can_do_io = 1;
|
||||
current_cpu = cpu;
|
||||
|
||||
hvf_init_vcpu(cpu);
|
||||
|
||||
/* signal CPU creation */
|
||||
cpu->created = true;
|
||||
qemu_cond_signal(&qemu_cpu_cond);
|
||||
|
||||
do {
|
||||
if (cpu_can_run(cpu)) {
|
||||
r = hvf_vcpu_exec(cpu);
|
||||
if (r == EXCP_DEBUG) {
|
||||
cpu_handle_guest_debug(cpu);
|
||||
}
|
||||
}
|
||||
qemu_hvf_wait_io_event(cpu);
|
||||
} while (!cpu->unplug || cpu_can_run(cpu));
|
||||
|
||||
hvf_vcpu_destroy(cpu);
|
||||
cpu->created = false;
|
||||
qemu_cond_signal(&qemu_cpu_cond);
|
||||
qemu_mutex_unlock_iothread();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void CALLBACK dummy_apc_func(ULONG_PTR unused)
|
||||
{
|
||||
@ -1610,12 +1682,12 @@ void pause_all_vcpus(void)
|
||||
|
||||
qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false);
|
||||
CPU_FOREACH(cpu) {
|
||||
cpu->stop = true;
|
||||
qemu_cpu_kick(cpu);
|
||||
}
|
||||
|
||||
if (qemu_in_vcpu_thread()) {
|
||||
cpu_stop_current();
|
||||
if (qemu_cpu_is_self(cpu)) {
|
||||
qemu_cpu_stop(cpu, true);
|
||||
} else {
|
||||
cpu->stop = true;
|
||||
qemu_cpu_kick(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
while (!all_vcpus_paused()) {
|
||||
@ -1752,6 +1824,27 @@ static void qemu_kvm_start_vcpu(CPUState *cpu)
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_hvf_start_vcpu(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
|
||||
/* HVF currently does not support TCG, and only runs in
|
||||
* unrestricted-guest mode. */
|
||||
assert(hvf_enabled());
|
||||
|
||||
cpu->thread = g_malloc0(sizeof(QemuThread));
|
||||
cpu->halt_cond = g_malloc0(sizeof(QemuCond));
|
||||
qemu_cond_init(cpu->halt_cond);
|
||||
|
||||
snprintf(thread_name, VCPU_THREAD_NAME_SIZE, "CPU %d/HVF",
|
||||
cpu->cpu_index);
|
||||
qemu_thread_create(cpu->thread, thread_name, qemu_hvf_cpu_thread_fn,
|
||||
cpu, QEMU_THREAD_JOINABLE);
|
||||
while (!cpu->created) {
|
||||
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_dummy_start_vcpu(CPUState *cpu)
|
||||
{
|
||||
char thread_name[VCPU_THREAD_NAME_SIZE];
|
||||
@ -1778,17 +1871,16 @@ void qemu_init_vcpu(CPUState *cpu)
|
||||
/* If the target cpu hasn't set up any address spaces itself,
|
||||
* give it the default one.
|
||||
*/
|
||||
AddressSpace *as = g_new0(AddressSpace, 1);
|
||||
|
||||
address_space_init(as, cpu->memory, "cpu-memory");
|
||||
cpu->num_ases = 1;
|
||||
cpu_address_space_init(cpu, as, 0);
|
||||
cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory);
|
||||
}
|
||||
|
||||
if (kvm_enabled()) {
|
||||
qemu_kvm_start_vcpu(cpu);
|
||||
} else if (hax_enabled()) {
|
||||
qemu_hax_start_vcpu(cpu);
|
||||
} else if (hvf_enabled()) {
|
||||
qemu_hvf_start_vcpu(cpu);
|
||||
} else if (tcg_enabled()) {
|
||||
qemu_tcg_init_vcpu(cpu);
|
||||
} else {
|
||||
@ -1799,10 +1891,7 @@ void qemu_init_vcpu(CPUState *cpu)
|
||||
void cpu_stop_current(void)
|
||||
{
|
||||
if (current_cpu) {
|
||||
current_cpu->stop = false;
|
||||
current_cpu->stopped = true;
|
||||
cpu_exit(current_cpu);
|
||||
qemu_cond_broadcast(&qemu_pause_cond);
|
||||
qemu_cpu_stop(current_cpu, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
1
default-configs/aarch64_be-linux-user.mak
Normal file
1
default-configs/aarch64_be-linux-user.mak
Normal file
@ -0,0 +1 @@
|
||||
# Default configuration for aarch64_be-linux-user
|
@ -130,5 +130,5 @@ CONFIG_SMBIOS=y
|
||||
CONFIG_ASPEED_SOC=y
|
||||
CONFIG_GPIO_KEY=y
|
||||
CONFIG_MSF2=y
|
||||
|
||||
CONFIG_FW_CFG_DMA=y
|
||||
CONFIG_XILINX_AXI=y
|
||||
|
@ -16,3 +16,4 @@ CONFIG_I8259=y
|
||||
CONFIG_XILINX=y
|
||||
CONFIG_XILINX_ETHLITE=y
|
||||
CONFIG_SM501=y
|
||||
CONFIG_IDE_SII3112=y
|
||||
|
@ -21,6 +21,7 @@ common-obj-$(CONFIG_S390_DIS) += s390.o
|
||||
common-obj-$(CONFIG_SH4_DIS) += sh4.o
|
||||
common-obj-$(CONFIG_SPARC_DIS) += sparc.o
|
||||
common-obj-$(CONFIG_LM32_DIS) += lm32.o
|
||||
common-obj-$(CONFIG_XTENSA_DIS) += xtensa.o
|
||||
|
||||
# TODO: As long as the TCG interpreter and its generated code depend
|
||||
# on the QEMU target, we cannot compile the disassembler here.
|
||||
|
@ -1662,7 +1662,7 @@ print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given,
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Only match unconditional instuctions against unconditional
|
||||
/* Only match unconditional instructions against unconditional
|
||||
patterns. */
|
||||
if ((given & 0xf0000000) == 0xf0000000)
|
||||
{
|
||||
|
@ -1756,7 +1756,6 @@ extern const int nios2_num_r2_reg_range_mappings;
|
||||
#endif /* _NIOS2_H */
|
||||
|
||||
/*#include "sysdep.h"
|
||||
#include <stdio.h>
|
||||
#include "opcode/nios2.h"
|
||||
*/
|
||||
/* Register string table */
|
||||
@ -2521,8 +2520,6 @@ const int nios2_num_r2_reg_range_mappings = 8;
|
||||
#include "dis-asm.h"
|
||||
#include "opcode/nios2.h"
|
||||
#include "libiberty.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
*/
|
||||
/* No symbol table is available when this code runs out in an embedded
|
||||
system as when it is used for disassembler support in a monitor. */
|
||||
|
133
disas/xtensa.c
Normal file
133
disas/xtensa.c
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Max Filippov, Open Source and Linux Lab.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the Open Source and Linux Lab nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "disas/bfd.h"
|
||||
#include "hw/xtensa/xtensa-isa.h"
|
||||
|
||||
int print_insn_xtensa(bfd_vma memaddr, struct disassemble_info *info)
|
||||
{
|
||||
xtensa_isa isa = info->private_data;
|
||||
xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc(isa);
|
||||
xtensa_insnbuf slotbuf = xtensa_insnbuf_alloc(isa);
|
||||
bfd_byte *buffer = g_malloc(1);
|
||||
int status = info->read_memory_func(memaddr, buffer, 1, info);
|
||||
xtensa_format fmt;
|
||||
unsigned slot, slots;
|
||||
unsigned len;
|
||||
|
||||
if (status) {
|
||||
info->memory_error_func(status, memaddr, info);
|
||||
len = -1;
|
||||
goto out;
|
||||
}
|
||||
len = xtensa_isa_length_from_chars(isa, buffer);
|
||||
if (len == XTENSA_UNDEFINED) {
|
||||
info->fprintf_func(info->stream, ".byte 0x%02x", buffer[0]);
|
||||
len = 1;
|
||||
goto out;
|
||||
}
|
||||
buffer = g_realloc(buffer, len);
|
||||
status = info->read_memory_func(memaddr + 1, buffer + 1, len - 1, info);
|
||||
if (status) {
|
||||
info->fprintf_func(info->stream, ".byte 0x%02x", buffer[0]);
|
||||
info->memory_error_func(status, memaddr + 1, info);
|
||||
len = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
xtensa_insnbuf_from_chars(isa, insnbuf, buffer, len);
|
||||
fmt = xtensa_format_decode(isa, insnbuf);
|
||||
if (fmt == XTENSA_UNDEFINED) {
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
info->fprintf_func(info->stream, "%s 0x%02x",
|
||||
i ? ", " : ".byte ", buffer[i]);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
slots = xtensa_format_num_slots(isa, fmt);
|
||||
|
||||
if (slots > 1) {
|
||||
info->fprintf_func(info->stream, "{ ");
|
||||
}
|
||||
|
||||
for (slot = 0; slot < slots; ++slot) {
|
||||
xtensa_opcode opc;
|
||||
unsigned opnd, vopnd, opnds;
|
||||
|
||||
if (slot) {
|
||||
info->fprintf_func(info->stream, "; ");
|
||||
}
|
||||
xtensa_format_get_slot(isa, fmt, slot, insnbuf, slotbuf);
|
||||
opc = xtensa_opcode_decode(isa, fmt, slot, slotbuf);
|
||||
if (opc == XTENSA_UNDEFINED) {
|
||||
info->fprintf_func(info->stream, "???");
|
||||
continue;
|
||||
}
|
||||
opnds = xtensa_opcode_num_operands(isa, opc);
|
||||
|
||||
info->fprintf_func(info->stream, "%s", xtensa_opcode_name(isa, opc));
|
||||
|
||||
for (opnd = vopnd = 0; opnd < opnds; ++opnd) {
|
||||
if (xtensa_operand_is_visible(isa, opc, opnd)) {
|
||||
uint32_t v = 0xbadc0de;
|
||||
int rc;
|
||||
|
||||
info->fprintf_func(info->stream, vopnd ? ", " : "\t");
|
||||
xtensa_operand_get_field(isa, opc, opnd, fmt, slot,
|
||||
slotbuf, &v);
|
||||
rc = xtensa_operand_decode(isa, opc, opnd, &v);
|
||||
if (rc == XTENSA_UNDEFINED) {
|
||||
info->fprintf_func(info->stream, "???");
|
||||
} else if (xtensa_operand_is_register(isa, opc, opnd)) {
|
||||
xtensa_regfile rf = xtensa_operand_regfile(isa, opc, opnd);
|
||||
|
||||
info->fprintf_func(info->stream, "%s%d",
|
||||
xtensa_regfile_shortname(isa, rf), v);
|
||||
} else if (xtensa_operand_is_PCrelative(isa, opc, opnd)) {
|
||||
xtensa_operand_undo_reloc(isa, opc, opnd, &v, memaddr);
|
||||
info->fprintf_func(info->stream, "0x%x", v);
|
||||
} else {
|
||||
info->fprintf_func(info->stream, "%d", v);
|
||||
}
|
||||
++vopnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (slots > 1) {
|
||||
info->fprintf_func(info->stream, " }");
|
||||
}
|
||||
|
||||
out:
|
||||
g_free(buffer);
|
||||
xtensa_insnbuf_free(isa, insnbuf);
|
||||
xtensa_insnbuf_free(isa, slotbuf);
|
||||
|
||||
return len;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2014 Red Hat Inc.
|
||||
Copyright (c) 2014-2017 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.
|
||||
@ -92,8 +92,9 @@ aio_context_acquire()/aio_context_release() for mutual exclusion. Once the
|
||||
context is acquired no other thread can access it or run event loop iterations
|
||||
in this AioContext.
|
||||
|
||||
aio_context_acquire()/aio_context_release() calls may be nested. This
|
||||
means you can call them if you're not sure whether #2 applies.
|
||||
Legacy code sometimes nests aio_context_acquire()/aio_context_release() calls.
|
||||
Do not use nesting anymore, it is incompatible with the BDRV_POLL_WHILE() macro
|
||||
used in the block layer and can lead to hangs.
|
||||
|
||||
There is currently no lock ordering rule if a thread needs to acquire multiple
|
||||
AioContexts simultaneously. Therefore, it is only safe for code holding the
|
||||
|
@ -63,7 +63,7 @@ Comment text starting with '=' is a section title:
|
||||
|
||||
Double the '=' for a subsection title:
|
||||
|
||||
# == Subection title
|
||||
# == Subsection title
|
||||
|
||||
'|' denotes examples:
|
||||
|
||||
|
2
dtc
2
dtc
@ -1 +1 @@
|
||||
Subproject commit 558cd81bdd432769b59bff01240c44f82cfb1a9d
|
||||
Subproject commit e54388015af1fb4bf04d0bca99caba1074d9cc42
|
7
dump.c
7
dump.c
@ -788,12 +788,7 @@ static bool note_name_equal(DumpState *s,
|
||||
get_note_sizes(s, note, &head_size, &name_size, NULL);
|
||||
head_size = ROUND_UP(head_size, 4);
|
||||
|
||||
if (name_size != len ||
|
||||
memcmp(note + head_size, "VMCOREINFO", len)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return name_size == len && memcmp(note + head_size, name, len) == 0;
|
||||
}
|
||||
|
||||
/* write common header, sub header and elf note to vmcore */
|
||||
|
47
exec.c
47
exec.c
@ -18,8 +18,6 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#ifndef _WIN32
|
||||
#endif
|
||||
|
||||
#include "qemu/cutils.h"
|
||||
#include "cpu.h"
|
||||
@ -51,7 +49,6 @@
|
||||
#include "trace-root.h"
|
||||
|
||||
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE
|
||||
#include <fcntl.h>
|
||||
#include <linux/falloc.h>
|
||||
#endif
|
||||
|
||||
@ -708,9 +705,17 @@ CPUState *qemu_get_cpu(int index)
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
void cpu_address_space_init(CPUState *cpu, AddressSpace *as, int asidx)
|
||||
void cpu_address_space_init(CPUState *cpu, int asidx,
|
||||
const char *prefix, MemoryRegion *mr)
|
||||
{
|
||||
CPUAddressSpace *newas;
|
||||
AddressSpace *as = g_new0(AddressSpace, 1);
|
||||
char *as_name;
|
||||
|
||||
assert(mr);
|
||||
as_name = g_strdup_printf("%s-%d", prefix, cpu->cpu_index);
|
||||
address_space_init(as, mr, as_name);
|
||||
g_free(as_name);
|
||||
|
||||
/* Target code should have set num_ases before calling us */
|
||||
assert(asidx < cpu->num_ases);
|
||||
@ -2720,6 +2725,37 @@ static uint16_t dummy_section(PhysPageMap *map, FlatView *fv, MemoryRegion *mr)
|
||||
return phys_section_add(map, §ion);
|
||||
}
|
||||
|
||||
static void readonly_mem_write(void *opaque, hwaddr addr,
|
||||
uint64_t val, unsigned size)
|
||||
{
|
||||
/* Ignore any write to ROM. */
|
||||
}
|
||||
|
||||
static bool readonly_mem_accepts(void *opaque, hwaddr addr,
|
||||
unsigned size, bool is_write)
|
||||
{
|
||||
return is_write;
|
||||
}
|
||||
|
||||
/* This will only be used for writes, because reads are special cased
|
||||
* to directly access the underlying host ram.
|
||||
*/
|
||||
static const MemoryRegionOps readonly_mem_ops = {
|
||||
.write = readonly_mem_write,
|
||||
.valid.accepts = readonly_mem_accepts,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
.unaligned = false,
|
||||
},
|
||||
.impl = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 8,
|
||||
.unaligned = false,
|
||||
},
|
||||
};
|
||||
|
||||
MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index, MemTxAttrs attrs)
|
||||
{
|
||||
int asidx = cpu_asidx_from_attrs(cpu, attrs);
|
||||
@ -2732,7 +2768,8 @@ MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index, MemTxAttrs attrs)
|
||||
|
||||
static void io_mem_init(void)
|
||||
{
|
||||
memory_region_init_io(&io_mem_rom, NULL, &unassigned_mem_ops, NULL, NULL, UINT64_MAX);
|
||||
memory_region_init_io(&io_mem_rom, NULL, &readonly_mem_ops,
|
||||
NULL, NULL, UINT64_MAX);
|
||||
memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
|
||||
NULL, UINT64_MAX);
|
||||
|
||||
|
@ -22,22 +22,19 @@
|
||||
#define SM_LOCAL_MODE_BITS 0600
|
||||
#define SM_LOCAL_DIR_MODE_BITS 0700
|
||||
|
||||
typedef struct FsCred
|
||||
{
|
||||
typedef struct FsCred {
|
||||
uid_t fc_uid;
|
||||
gid_t fc_gid;
|
||||
mode_t fc_mode;
|
||||
dev_t fc_rdev;
|
||||
} FsCred;
|
||||
|
||||
struct xattr_operations;
|
||||
struct FsContext;
|
||||
struct V9fsPath;
|
||||
typedef struct FsContext FsContext;
|
||||
typedef struct V9fsPath V9fsPath;
|
||||
|
||||
typedef struct extended_ops {
|
||||
int (*get_st_gen)(struct FsContext *, struct V9fsPath *,
|
||||
mode_t, uint64_t *);
|
||||
} extended_ops;
|
||||
typedef struct ExtendedOps {
|
||||
int (*get_st_gen)(FsContext *, V9fsPath *, mode_t, uint64_t *);
|
||||
} ExtendedOps;
|
||||
|
||||
/* export flags */
|
||||
#define V9FS_IMMEDIATE_WRITEOUT 0x00000001
|
||||
@ -67,6 +64,8 @@ typedef struct extended_ops {
|
||||
|
||||
|
||||
typedef struct FileOperations FileOperations;
|
||||
typedef struct XattrOperations XattrOperations;
|
||||
|
||||
/*
|
||||
* Structure to store the various fsdev's passed through command line.
|
||||
*/
|
||||
@ -80,24 +79,23 @@ typedef struct FsDriverEntry {
|
||||
mode_t dmode;
|
||||
} FsDriverEntry;
|
||||
|
||||
typedef struct FsContext
|
||||
{
|
||||
struct FsContext {
|
||||
uid_t uid;
|
||||
char *fs_root;
|
||||
int export_flags;
|
||||
struct xattr_operations **xops;
|
||||
struct extended_ops exops;
|
||||
XattrOperations **xops;
|
||||
ExtendedOps exops;
|
||||
FsThrottle *fst;
|
||||
/* fs driver specific data */
|
||||
void *private;
|
||||
mode_t fmode;
|
||||
mode_t dmode;
|
||||
} FsContext;
|
||||
};
|
||||
|
||||
typedef struct V9fsPath {
|
||||
struct V9fsPath {
|
||||
uint16_t size;
|
||||
char *data;
|
||||
} V9fsPath;
|
||||
};
|
||||
|
||||
typedef union V9fsFidOpenState V9fsFidOpenState;
|
||||
|
||||
@ -105,9 +103,9 @@ void cred_init(FsCred *);
|
||||
|
||||
struct FileOperations
|
||||
{
|
||||
int (*parse_opts)(QemuOpts *, struct FsDriverEntry *);
|
||||
int (*init)(struct FsContext *);
|
||||
void (*cleanup)(struct FsContext *);
|
||||
int (*parse_opts)(QemuOpts *, FsDriverEntry *, Error **errp);
|
||||
int (*init)(FsContext *, Error **errp);
|
||||
void (*cleanup)(FsContext *);
|
||||
int (*lstat)(FsContext *, V9fsPath *, struct stat *);
|
||||
ssize_t (*readlink)(FsContext *, V9fsPath *, char *, size_t);
|
||||
int (*chmod)(FsContext *, V9fsPath *, FsCred *);
|
||||
|
@ -37,6 +37,7 @@ int qemu_fsdev_add(QemuOpts *opts)
|
||||
const char *fsdriver = qemu_opt_get(opts, "fsdriver");
|
||||
const char *writeout = qemu_opt_get(opts, "writeout");
|
||||
bool ro = qemu_opt_get_bool(opts, "readonly", 0);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!fsdev_id) {
|
||||
error_report("fsdev: No id specified");
|
||||
@ -74,7 +75,8 @@ int qemu_fsdev_add(QemuOpts *opts)
|
||||
}
|
||||
|
||||
if (fsle->fse.ops->parse_opts) {
|
||||
if (fsle->fse.ops->parse_opts(opts, &fsle->fse)) {
|
||||
if (fsle->fse.ops->parse_opts(opts, &fsle->fse, &local_err)) {
|
||||
error_report_err(local_err);
|
||||
g_free(fsle->fse.fsdev_id);
|
||||
g_free(fsle);
|
||||
return -1;
|
||||
|
113
gdbstub.c
113
gdbstub.c
@ -21,6 +21,7 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "cpu.h"
|
||||
#include "trace-root.h"
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
#include "qemu.h"
|
||||
#else
|
||||
@ -287,21 +288,6 @@ static int gdb_signal_to_target (int sig)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* #define DEBUG_GDB */
|
||||
|
||||
#ifdef DEBUG_GDB
|
||||
# define DEBUG_GDB_GATE 1
|
||||
#else
|
||||
# define DEBUG_GDB_GATE 0
|
||||
#endif
|
||||
|
||||
#define gdb_debug(fmt, ...) do { \
|
||||
if (DEBUG_GDB_GATE) { \
|
||||
fprintf(stderr, "%s: " fmt, __func__, ## __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
typedef struct GDBRegisterState {
|
||||
int base_reg;
|
||||
int num_regs;
|
||||
@ -410,10 +396,13 @@ int use_gdb_syscalls(void)
|
||||
/* Resume execution. */
|
||||
static inline void gdb_continue(GDBState *s)
|
||||
{
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
s->running_state = 1;
|
||||
trace_gdbstub_op_continue();
|
||||
#else
|
||||
if (!runstate_needs_reset()) {
|
||||
trace_gdbstub_op_continue();
|
||||
vm_start();
|
||||
}
|
||||
#endif
|
||||
@ -434,6 +423,7 @@ static int gdb_continue_partial(GDBState *s, char *newstates)
|
||||
*/
|
||||
CPU_FOREACH(cpu) {
|
||||
if (newstates[cpu->cpu_index] == 's') {
|
||||
trace_gdbstub_op_stepping(cpu->cpu_index);
|
||||
cpu_single_step(cpu, sstep_flags);
|
||||
}
|
||||
}
|
||||
@ -452,11 +442,13 @@ static int gdb_continue_partial(GDBState *s, char *newstates)
|
||||
case 1:
|
||||
break; /* nothing to do here */
|
||||
case 's':
|
||||
trace_gdbstub_op_stepping(cpu->cpu_index);
|
||||
cpu_single_step(cpu, sstep_flags);
|
||||
cpu_resume(cpu);
|
||||
flag = 1;
|
||||
break;
|
||||
case 'c':
|
||||
trace_gdbstub_op_continue_cpu(cpu->cpu_index);
|
||||
cpu_resume(cpu);
|
||||
flag = 1;
|
||||
break;
|
||||
@ -538,12 +530,49 @@ static void hextomem(uint8_t *mem, const char *buf, int len)
|
||||
}
|
||||
}
|
||||
|
||||
static void hexdump(const char *buf, int len,
|
||||
void (*trace_fn)(size_t ofs, char const *text))
|
||||
{
|
||||
char line_buffer[3 * 16 + 4 + 16 + 1];
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < len || (i & 0xF); ++i) {
|
||||
size_t byte_ofs = i & 15;
|
||||
|
||||
if (byte_ofs == 0) {
|
||||
memset(line_buffer, ' ', 3 * 16 + 4 + 16);
|
||||
line_buffer[3 * 16 + 4 + 16] = 0;
|
||||
}
|
||||
|
||||
size_t col_group = (i >> 2) & 3;
|
||||
size_t hex_col = byte_ofs * 3 + col_group;
|
||||
size_t txt_col = 3 * 16 + 4 + byte_ofs;
|
||||
|
||||
if (i < len) {
|
||||
char value = buf[i];
|
||||
|
||||
line_buffer[hex_col + 0] = tohex((value >> 4) & 0xF);
|
||||
line_buffer[hex_col + 1] = tohex((value >> 0) & 0xF);
|
||||
line_buffer[txt_col + 0] = (value >= ' ' && value < 127)
|
||||
? value
|
||||
: '.';
|
||||
}
|
||||
|
||||
if (byte_ofs == 0xF)
|
||||
trace_fn(i & -16, line_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/* return -1 if error, 0 if OK */
|
||||
static int put_packet_binary(GDBState *s, const char *buf, int len)
|
||||
static int put_packet_binary(GDBState *s, const char *buf, int len, bool dump)
|
||||
{
|
||||
int csum, i;
|
||||
uint8_t *p;
|
||||
|
||||
if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) {
|
||||
hexdump(buf, len, trace_gdbstub_io_binaryreply);
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
p = s->last_packet;
|
||||
*(p++) = '$';
|
||||
@ -576,9 +605,9 @@ static int put_packet_binary(GDBState *s, const char *buf, int len)
|
||||
/* return -1 if error, 0 if OK */
|
||||
static int put_packet(GDBState *s, const char *buf)
|
||||
{
|
||||
gdb_debug("reply='%s'\n", buf);
|
||||
trace_gdbstub_io_reply(buf);
|
||||
|
||||
return put_packet_binary(s, buf, strlen(buf));
|
||||
return put_packet_binary(s, buf, strlen(buf), false);
|
||||
}
|
||||
|
||||
/* Encode data using the encoding for 'x' packets. */
|
||||
@ -975,8 +1004,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
uint8_t *registers;
|
||||
target_ulong addr, len;
|
||||
|
||||
|
||||
gdb_debug("command='%s'\n", line_buf);
|
||||
trace_gdbstub_io_command(line_buf);
|
||||
|
||||
p = line_buf;
|
||||
ch = *p++;
|
||||
@ -999,7 +1027,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
}
|
||||
s->signal = 0;
|
||||
gdb_continue(s);
|
||||
return RS_IDLE;
|
||||
return RS_IDLE;
|
||||
case 'C':
|
||||
s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16));
|
||||
if (s->signal == -1)
|
||||
@ -1045,7 +1073,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
}
|
||||
cpu_single_step(s->c_cpu, sstep_flags);
|
||||
gdb_continue(s);
|
||||
return RS_IDLE;
|
||||
return RS_IDLE;
|
||||
case 'F':
|
||||
{
|
||||
target_ulong ret;
|
||||
@ -1267,6 +1295,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
len = snprintf((char *)mem_buf, sizeof(buf) / 2,
|
||||
"CPU#%d [%s]", cpu->cpu_index,
|
||||
cpu->halted ? "halted " : "running");
|
||||
trace_gdbstub_op_extra_info((char *)mem_buf);
|
||||
memtohex(buf, mem_buf, len);
|
||||
put_packet(s, buf);
|
||||
}
|
||||
@ -1350,7 +1379,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
|
||||
buf[0] = 'l';
|
||||
len = memtox(buf + 1, xml + addr, total_len - addr);
|
||||
}
|
||||
put_packet_binary(s, buf, len + 1);
|
||||
put_packet_binary(s, buf, len + 1, true);
|
||||
break;
|
||||
}
|
||||
if (is_query_packet(p, "Attached", ':')) {
|
||||
@ -1407,29 +1436,38 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
||||
type = "";
|
||||
break;
|
||||
}
|
||||
trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu),
|
||||
(target_ulong)cpu->watchpoint_hit->vaddr);
|
||||
snprintf(buf, sizeof(buf),
|
||||
"T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
|
||||
GDB_SIGNAL_TRAP, cpu_gdb_index(cpu), type,
|
||||
(target_ulong)cpu->watchpoint_hit->vaddr);
|
||||
cpu->watchpoint_hit = NULL;
|
||||
goto send_packet;
|
||||
} else {
|
||||
trace_gdbstub_hit_break();
|
||||
}
|
||||
tb_flush(cpu);
|
||||
ret = GDB_SIGNAL_TRAP;
|
||||
break;
|
||||
case RUN_STATE_PAUSED:
|
||||
trace_gdbstub_hit_paused();
|
||||
ret = GDB_SIGNAL_INT;
|
||||
break;
|
||||
case RUN_STATE_SHUTDOWN:
|
||||
trace_gdbstub_hit_shutdown();
|
||||
ret = GDB_SIGNAL_QUIT;
|
||||
break;
|
||||
case RUN_STATE_IO_ERROR:
|
||||
trace_gdbstub_hit_io_error();
|
||||
ret = GDB_SIGNAL_IO;
|
||||
break;
|
||||
case RUN_STATE_WATCHDOG:
|
||||
trace_gdbstub_hit_watchdog();
|
||||
ret = GDB_SIGNAL_ALRM;
|
||||
break;
|
||||
case RUN_STATE_INTERNAL_ERROR:
|
||||
trace_gdbstub_hit_internal_error();
|
||||
ret = GDB_SIGNAL_ABRT;
|
||||
break;
|
||||
case RUN_STATE_SAVE_VM:
|
||||
@ -1439,6 +1477,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
|
||||
ret = GDB_SIGNAL_XCPU;
|
||||
break;
|
||||
default:
|
||||
trace_gdbstub_hit_unknown(state);
|
||||
ret = GDB_SIGNAL_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
@ -1538,12 +1577,12 @@ static void gdb_read_byte(GDBState *s, int ch)
|
||||
/* Waiting for a response to the last packet. If we see the start
|
||||
of a new command then abandon the previous response. */
|
||||
if (ch == '-') {
|
||||
gdb_debug("Got NACK, retransmitting\n");
|
||||
trace_gdbstub_err_got_nack();
|
||||
put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len);
|
||||
} else if (ch == '+') {
|
||||
gdb_debug("Got ACK\n");
|
||||
trace_gdbstub_io_got_ack();
|
||||
} else {
|
||||
gdb_debug("Got '%c' when expecting ACK/NACK\n", ch);
|
||||
trace_gdbstub_io_got_unexpected((uint8_t)ch);
|
||||
}
|
||||
|
||||
if (ch == '+' || ch == '$')
|
||||
@ -1566,7 +1605,7 @@ static void gdb_read_byte(GDBState *s, int ch)
|
||||
s->line_sum = 0;
|
||||
s->state = RS_GETLINE;
|
||||
} else {
|
||||
gdb_debug("received garbage between packets: 0x%x\n", ch);
|
||||
trace_gdbstub_err_garbage((uint8_t)ch);
|
||||
}
|
||||
break;
|
||||
case RS_GETLINE:
|
||||
@ -1582,7 +1621,7 @@ static void gdb_read_byte(GDBState *s, int ch)
|
||||
/* end of command, start of checksum*/
|
||||
s->state = RS_CHKSUM1;
|
||||
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
||||
gdb_debug("command buffer overrun, dropping command\n");
|
||||
trace_gdbstub_err_overrun();
|
||||
s->state = RS_IDLE;
|
||||
} else {
|
||||
/* unescaped command character */
|
||||
@ -1596,7 +1635,7 @@ static void gdb_read_byte(GDBState *s, int ch)
|
||||
s->state = RS_CHKSUM1;
|
||||
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
|
||||
/* command buffer overrun */
|
||||
gdb_debug("command buffer overrun, dropping command\n");
|
||||
trace_gdbstub_err_overrun();
|
||||
s->state = RS_IDLE;
|
||||
} else {
|
||||
/* parse escaped character and leave escape state */
|
||||
@ -1608,18 +1647,18 @@ static void gdb_read_byte(GDBState *s, int ch)
|
||||
case RS_GETLINE_RLE:
|
||||
if (ch < ' ') {
|
||||
/* invalid RLE count encoding */
|
||||
gdb_debug("got invalid RLE count: 0x%x\n", ch);
|
||||
trace_gdbstub_err_invalid_repeat((uint8_t)ch);
|
||||
s->state = RS_GETLINE;
|
||||
} else {
|
||||
/* decode repeat length */
|
||||
int repeat = (unsigned char)ch - ' ' + 3;
|
||||
if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) {
|
||||
/* that many repeats would overrun the command buffer */
|
||||
gdb_debug("command buffer overrun, dropping command\n");
|
||||
trace_gdbstub_err_overrun();
|
||||
s->state = RS_IDLE;
|
||||
} else if (s->line_buf_index < 1) {
|
||||
/* got a repeat but we have nothing to repeat */
|
||||
gdb_debug("got invalid RLE sequence\n");
|
||||
trace_gdbstub_err_invalid_rle();
|
||||
s->state = RS_GETLINE;
|
||||
} else {
|
||||
/* repeat the last character */
|
||||
@ -1634,7 +1673,7 @@ static void gdb_read_byte(GDBState *s, int ch)
|
||||
case RS_CHKSUM1:
|
||||
/* get high hex digit of checksum */
|
||||
if (!isxdigit(ch)) {
|
||||
gdb_debug("got invalid command checksum digit\n");
|
||||
trace_gdbstub_err_checksum_invalid((uint8_t)ch);
|
||||
s->state = RS_GETLINE;
|
||||
break;
|
||||
}
|
||||
@ -1645,14 +1684,14 @@ static void gdb_read_byte(GDBState *s, int ch)
|
||||
case RS_CHKSUM2:
|
||||
/* get low hex digit of checksum */
|
||||
if (!isxdigit(ch)) {
|
||||
gdb_debug("got invalid command checksum digit\n");
|
||||
trace_gdbstub_err_checksum_invalid((uint8_t)ch);
|
||||
s->state = RS_GETLINE;
|
||||
break;
|
||||
}
|
||||
s->line_csum |= fromhex(ch);
|
||||
|
||||
if (s->line_csum != (s->line_sum & 0xff)) {
|
||||
gdb_debug("got command packet with incorrect checksum\n");
|
||||
trace_gdbstub_err_checksum_incorrect(s->line_sum, s->line_csum);
|
||||
/* send NAK reply */
|
||||
reply = '-';
|
||||
put_buffer(s, &reply, 1);
|
||||
@ -1686,6 +1725,8 @@ void gdb_exit(CPUArchState *env, int code)
|
||||
}
|
||||
#endif
|
||||
|
||||
trace_gdbstub_op_exiting((uint8_t)code);
|
||||
|
||||
snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
|
||||
put_packet(s, buf);
|
||||
|
||||
@ -1944,6 +1985,8 @@ static const TypeInfo char_gdb_type_info = {
|
||||
|
||||
int gdbserver_start(const char *device)
|
||||
{
|
||||
trace_gdbstub_op_start(device);
|
||||
|
||||
GDBState *s;
|
||||
char gdbstub_device_name[128];
|
||||
Chardev *chr = NULL;
|
||||
|
@ -663,39 +663,6 @@ STEXI
|
||||
@item sum @var{addr} @var{size}
|
||||
@findex sum
|
||||
Compute the checksum of a memory region.
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "usb_add",
|
||||
.args_type = "devname:s",
|
||||
.params = "device",
|
||||
.help = "add USB device (e.g. 'host:bus.addr' or 'host:vendor_id:product_id')",
|
||||
.cmd = hmp_usb_add,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item usb_add @var{devname}
|
||||
@findex usb_add
|
||||
Add the USB device @var{devname}. This command is deprecated, please
|
||||
use @code{device_add} instead. For details of available devices see
|
||||
@ref{usb_devices}
|
||||
ETEXI
|
||||
|
||||
{
|
||||
.name = "usb_del",
|
||||
.args_type = "devname:s",
|
||||
.params = "device",
|
||||
.help = "remove USB device 'bus.addr'",
|
||||
.cmd = hmp_usb_del,
|
||||
},
|
||||
|
||||
STEXI
|
||||
@item usb_del @var{devname}
|
||||
@findex usb_del
|
||||
Remove the USB device @var{devname} from the QEMU virtual USB
|
||||
hub. @var{devname} has the syntax @code{bus.addr}. Use the monitor
|
||||
command @code{info usb} to see the devices you can remove. This
|
||||
command is deprecated, please use @code{device_del} instead.
|
||||
ETEXI
|
||||
|
||||
{
|
||||
|
6
hmp.c
6
hmp.c
@ -2318,7 +2318,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockBackend *local_blk = NULL;
|
||||
AioContext *aio_context;
|
||||
const char* device = qdict_get_str(qdict, "device");
|
||||
const char* command = qdict_get_str(qdict, "command");
|
||||
Error *err = NULL;
|
||||
@ -2338,9 +2337,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
|
||||
}
|
||||
}
|
||||
|
||||
aio_context = blk_get_aio_context(blk);
|
||||
aio_context_acquire(aio_context);
|
||||
|
||||
/*
|
||||
* Notably absent: Proper permission management. This is sad, but it seems
|
||||
* almost impossible to achieve without changing the semantics and thereby
|
||||
@ -2368,8 +2364,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
|
||||
*/
|
||||
qemuio_command(blk, command);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
|
||||
fail:
|
||||
blk_unref(local_blk);
|
||||
hmp_handle_error(mon, &err);
|
||||
|
@ -41,10 +41,10 @@
|
||||
#define BTRFS_SUPER_MAGIC 0x9123683E
|
||||
#endif
|
||||
|
||||
struct handle_data {
|
||||
typedef struct HandleData {
|
||||
int mountfd;
|
||||
int handle_bytes;
|
||||
};
|
||||
} HandleData;
|
||||
|
||||
static inline int name_to_handle(int dirfd, const char *name,
|
||||
struct file_handle *fh, int *mnt_id, int flags)
|
||||
@ -79,7 +79,7 @@ static int handle_lstat(FsContext *fs_ctx, V9fsPath *fs_path,
|
||||
struct stat *stbuf)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)fs_ctx->private;
|
||||
HandleData *data = (HandleData *) fs_ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
|
||||
if (fd < 0) {
|
||||
@ -94,7 +94,7 @@ static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
|
||||
char *buf, size_t bufsz)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)fs_ctx->private;
|
||||
HandleData *data = (HandleData *) fs_ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
|
||||
if (fd < 0) {
|
||||
@ -118,7 +118,7 @@ static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs)
|
||||
static int handle_open(FsContext *ctx, V9fsPath *fs_path,
|
||||
int flags, V9fsFidOpenState *fs)
|
||||
{
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
fs->fd = open_by_handle(data->mountfd, fs_path->data, flags);
|
||||
return fs->fd;
|
||||
@ -207,7 +207,7 @@ static ssize_t handle_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
|
||||
static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)fs_ctx->private;
|
||||
HandleData *data = (HandleData *) fs_ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
@ -222,7 +222,7 @@ static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
|
||||
const char *name, FsCred *credp)
|
||||
{
|
||||
int dirfd, ret;
|
||||
struct handle_data *data = (struct handle_data *)fs_ctx->private;
|
||||
HandleData *data = (HandleData *) fs_ctx->private;
|
||||
|
||||
dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
|
||||
if (dirfd < 0) {
|
||||
@ -240,7 +240,7 @@ static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
|
||||
const char *name, FsCred *credp)
|
||||
{
|
||||
int dirfd, ret;
|
||||
struct handle_data *data = (struct handle_data *)fs_ctx->private;
|
||||
HandleData *data = (HandleData *) fs_ctx->private;
|
||||
|
||||
dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
|
||||
if (dirfd < 0) {
|
||||
@ -272,7 +272,7 @@ static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
|
||||
{
|
||||
int ret;
|
||||
int dirfd, fd;
|
||||
struct handle_data *data = (struct handle_data *)fs_ctx->private;
|
||||
HandleData *data = (HandleData *) fs_ctx->private;
|
||||
|
||||
dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
|
||||
if (dirfd < 0) {
|
||||
@ -297,7 +297,7 @@ static int handle_symlink(FsContext *fs_ctx, const char *oldpath,
|
||||
V9fsPath *dir_path, const char *name, FsCred *credp)
|
||||
{
|
||||
int fd, dirfd, ret;
|
||||
struct handle_data *data = (struct handle_data *)fs_ctx->private;
|
||||
HandleData *data = (HandleData *) fs_ctx->private;
|
||||
|
||||
dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
|
||||
if (dirfd < 0) {
|
||||
@ -322,7 +322,7 @@ static int handle_link(FsContext *ctx, V9fsPath *oldpath,
|
||||
V9fsPath *dirpath, const char *name)
|
||||
{
|
||||
int oldfd, newdirfd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH);
|
||||
if (oldfd < 0) {
|
||||
@ -342,7 +342,7 @@ static int handle_link(FsContext *ctx, V9fsPath *oldpath,
|
||||
static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY);
|
||||
if (fd < 0) {
|
||||
@ -363,7 +363,7 @@ static int handle_rename(FsContext *ctx, const char *oldpath,
|
||||
static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)fs_ctx->private;
|
||||
HandleData *data = (HandleData *) fs_ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
|
||||
if (fd < 0) {
|
||||
@ -379,7 +379,7 @@ static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
|
||||
{
|
||||
int ret;
|
||||
int fd;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
@ -418,7 +418,7 @@ static int handle_statfs(FsContext *ctx, V9fsPath *fs_path,
|
||||
struct statfs *stbuf)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
@ -433,7 +433,7 @@ static ssize_t handle_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
|
||||
const char *name, void *value, size_t size)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
@ -448,7 +448,7 @@ static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path,
|
||||
void *value, size_t size)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
@ -463,7 +463,7 @@ static int handle_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
|
||||
void *value, size_t size, int flags)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
@ -478,7 +478,7 @@ static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
|
||||
const char *name)
|
||||
{
|
||||
int fd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
|
||||
if (fd < 0) {
|
||||
@ -495,7 +495,7 @@ static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path,
|
||||
char *buffer;
|
||||
struct file_handle *fh;
|
||||
int dirfd, ret, mnt_id;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
/* "." and ".." are not allowed */
|
||||
if (!strcmp(name, ".") || !strcmp(name, "..")) {
|
||||
@ -536,7 +536,7 @@ static int handle_renameat(FsContext *ctx, V9fsPath *olddir,
|
||||
const char *new_name)
|
||||
{
|
||||
int olddirfd, newdirfd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
|
||||
olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH);
|
||||
if (olddirfd < 0) {
|
||||
@ -557,7 +557,7 @@ static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
|
||||
const char *name, int flags)
|
||||
{
|
||||
int dirfd, ret;
|
||||
struct handle_data *data = (struct handle_data *)ctx->private;
|
||||
HandleData *data = (HandleData *) ctx->private;
|
||||
int rflags;
|
||||
|
||||
dirfd = open_by_handle(data->mountfd, dir->data, O_PATH);
|
||||
@ -604,12 +604,12 @@ static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
||||
#endif
|
||||
}
|
||||
|
||||
static int handle_init(FsContext *ctx)
|
||||
static int handle_init(FsContext *ctx, Error **errp)
|
||||
{
|
||||
int ret, mnt_id;
|
||||
struct statfs stbuf;
|
||||
struct file_handle fh;
|
||||
struct handle_data *data = g_malloc(sizeof(struct handle_data));
|
||||
HandleData *data = g_malloc(sizeof(HandleData));
|
||||
|
||||
data->mountfd = open(ctx->fs_root, O_DIRECTORY);
|
||||
if (data->mountfd < 0) {
|
||||
@ -646,17 +646,19 @@ out:
|
||||
|
||||
static void handle_cleanup(FsContext *ctx)
|
||||
{
|
||||
struct handle_data *data = ctx->private;
|
||||
HandleData *data = ctx->private;
|
||||
|
||||
close(data->mountfd);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static int handle_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
|
||||
static int handle_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
|
||||
{
|
||||
const char *sec_model = qemu_opt_get(opts, "security_model");
|
||||
const char *path = qemu_opt_get(opts, "path");
|
||||
|
||||
warn_report("handle backend is deprecated");
|
||||
|
||||
if (sec_model) {
|
||||
error_report("Invalid argument security_model specified with handle fsdriver");
|
||||
return -1;
|
||||
|
@ -1400,13 +1400,14 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
|
||||
#endif
|
||||
}
|
||||
|
||||
static int local_init(FsContext *ctx)
|
||||
static int local_init(FsContext *ctx, Error **errp)
|
||||
{
|
||||
struct statfs stbuf;
|
||||
LocalData *data = g_malloc(sizeof(*data));
|
||||
|
||||
data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
|
||||
if (data->mountfd == -1) {
|
||||
error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@ -1459,16 +1460,21 @@ static void local_cleanup(FsContext *ctx)
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
|
||||
static void error_append_security_model_hint(Error **errp)
|
||||
{
|
||||
error_append_hint(errp, "Valid options are: security_model="
|
||||
"[passthrough|mapped-xattr|mapped-file|none]\n");
|
||||
}
|
||||
|
||||
static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp)
|
||||
{
|
||||
const char *sec_model = qemu_opt_get(opts, "security_model");
|
||||
const char *path = qemu_opt_get(opts, "path");
|
||||
Error *err = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!sec_model) {
|
||||
error_report("Security model not specified, local fs needs security model");
|
||||
error_printf("valid options are:"
|
||||
"\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
|
||||
error_setg(errp, "security_model property not set");
|
||||
error_append_security_model_hint(errp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1482,20 +1488,20 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
|
||||
} else if (!strcmp(sec_model, "mapped-file")) {
|
||||
fse->export_flags |= V9FS_SM_MAPPED_FILE;
|
||||
} else {
|
||||
error_report("Invalid security model %s specified", sec_model);
|
||||
error_printf("valid options are:"
|
||||
"\t[passthrough|mapped-xattr|mapped-file|none]\n");
|
||||
error_setg(errp, "invalid security_model property '%s'", sec_model);
|
||||
error_append_security_model_hint(errp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
error_report("fsdev: No path specified");
|
||||
error_setg(errp, "path property not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fsdev_throttle_parse_opts(opts, &fse->fst, &err);
|
||||
if (err) {
|
||||
error_reportf_err(err, "Throttle configuration is not valid: ");
|
||||
fsdev_throttle_parse_opts(opts, &fse->fst, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "invalid throttle configuration: ");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1507,11 +1513,11 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
|
||||
qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777;
|
||||
} else {
|
||||
if (qemu_opt_find(opts, "fmode")) {
|
||||
error_report("fmode is only valid for mapped 9p modes");
|
||||
error_setg(errp, "fmode is only valid for mapped security modes");
|
||||
return -1;
|
||||
}
|
||||
if (qemu_opt_find(opts, "dmode")) {
|
||||
error_report("dmode is only valid for mapped 9p modes");
|
||||
error_setg(errp, "dmode is only valid for mapped security modes");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -1083,25 +1083,25 @@ static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int connect_namedsocket(const char *path)
|
||||
static int connect_namedsocket(const char *path, Error **errp)
|
||||
{
|
||||
int sockfd, size;
|
||||
struct sockaddr_un helper;
|
||||
|
||||
if (strlen(path) >= sizeof(helper.sun_path)) {
|
||||
error_report("Socket name too long");
|
||||
error_setg(errp, "socket name too long");
|
||||
return -1;
|
||||
}
|
||||
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sockfd < 0) {
|
||||
error_report("Failed to create socket: %s", strerror(errno));
|
||||
error_setg_errno(errp, errno, "failed to create client socket");
|
||||
return -1;
|
||||
}
|
||||
strcpy(helper.sun_path, path);
|
||||
helper.sun_family = AF_UNIX;
|
||||
size = strlen(helper.sun_path) + sizeof(helper.sun_family);
|
||||
if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) {
|
||||
error_report("Failed to connect to %s: %s", path, strerror(errno));
|
||||
error_setg_errno(errp, errno, "failed to connect to '%s'", path);
|
||||
close(sockfd);
|
||||
return -1;
|
||||
}
|
||||
@ -1111,17 +1111,27 @@ static int connect_namedsocket(const char *path)
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
|
||||
static void error_append_socket_sockfd_hint(Error **errp)
|
||||
{
|
||||
error_append_hint(errp, "Either specify socket=/some/path where /some/path"
|
||||
" points to a listening AF_UNIX socket or sock_fd=fd"
|
||||
" where fd is a file descriptor to a connected AF_UNIX"
|
||||
" socket\n");
|
||||
}
|
||||
|
||||
static int proxy_parse_opts(QemuOpts *opts, FsDriverEntry *fs, Error **errp)
|
||||
{
|
||||
const char *socket = qemu_opt_get(opts, "socket");
|
||||
const char *sock_fd = qemu_opt_get(opts, "sock_fd");
|
||||
|
||||
if (!socket && !sock_fd) {
|
||||
error_report("Must specify either socket or sock_fd");
|
||||
error_setg(errp, "both socket and sock_fd properties are missing");
|
||||
error_append_socket_sockfd_hint(errp);
|
||||
return -1;
|
||||
}
|
||||
if (socket && sock_fd) {
|
||||
error_report("Both socket and sock_fd options specified");
|
||||
error_setg(errp, "both socket and sock_fd properties are set");
|
||||
error_append_socket_sockfd_hint(errp);
|
||||
return -1;
|
||||
}
|
||||
if (socket) {
|
||||
@ -1134,17 +1144,17 @@ static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int proxy_init(FsContext *ctx)
|
||||
static int proxy_init(FsContext *ctx, Error **errp)
|
||||
{
|
||||
V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy));
|
||||
int sock_id;
|
||||
|
||||
if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
|
||||
sock_id = connect_namedsocket(ctx->fs_root);
|
||||
sock_id = connect_namedsocket(ctx->fs_root, errp);
|
||||
} else {
|
||||
sock_id = atoi(ctx->fs_root);
|
||||
if (sock_id < 0) {
|
||||
error_report("Socket descriptor not initialized");
|
||||
error_setg(errp, "socket descriptor not initialized");
|
||||
}
|
||||
}
|
||||
if (sock_id < 0) {
|
||||
|
@ -514,7 +514,7 @@ static int synth_unlinkat(FsContext *ctx, V9fsPath *dir,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int synth_init(FsContext *ctx)
|
||||
static int synth_init(FsContext *ctx, Error **errp)
|
||||
{
|
||||
QLIST_INIT(&synth_root.child);
|
||||
qemu_mutex_init(&synth_mutex);
|
||||
|
@ -16,8 +16,7 @@
|
||||
|
||||
#include "qemu/xattr.h"
|
||||
|
||||
typedef struct xattr_operations
|
||||
{
|
||||
struct XattrOperations {
|
||||
const char *name;
|
||||
ssize_t (*getxattr)(FsContext *ctx, const char *path,
|
||||
const char *name, void *value, size_t size);
|
||||
@ -27,7 +26,7 @@ typedef struct xattr_operations
|
||||
void *value, size_t size, int flags);
|
||||
int (*removexattr)(FsContext *ctx,
|
||||
const char *path, const char *name);
|
||||
} XattrOperations;
|
||||
};
|
||||
|
||||
ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
|
||||
const char *name, void *value, size_t size);
|
||||
|
22
hw/9pfs/9p.c
22
hw/9pfs/9p.c
@ -41,7 +41,7 @@ enum {
|
||||
Oappend = 0x80,
|
||||
};
|
||||
|
||||
ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
||||
static ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
||||
{
|
||||
ssize_t ret;
|
||||
va_list ap;
|
||||
@ -53,7 +53,7 @@ ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
||||
static ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
||||
{
|
||||
ssize_t ret;
|
||||
va_list ap;
|
||||
@ -99,10 +99,10 @@ static int omode_to_uflags(int8_t mode)
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct dotl_openflag_map {
|
||||
typedef struct DotlOpenflagMap {
|
||||
int dotl_flag;
|
||||
int open_flag;
|
||||
};
|
||||
} DotlOpenflagMap;
|
||||
|
||||
static int dotl_to_open_flags(int flags)
|
||||
{
|
||||
@ -113,7 +113,7 @@ static int dotl_to_open_flags(int flags)
|
||||
*/
|
||||
int oflags = flags & O_ACCMODE;
|
||||
|
||||
struct dotl_openflag_map dotl_oflag_map[] = {
|
||||
DotlOpenflagMap dotl_oflag_map[] = {
|
||||
{ P9_DOTL_CREATE, O_CREAT },
|
||||
{ P9_DOTL_EXCL, O_EXCL },
|
||||
{ P9_DOTL_NOCTTY , O_NOCTTY },
|
||||
@ -3473,14 +3473,12 @@ void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr)
|
||||
if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
|
||||
(pdu_co_handlers[pdu->id] == NULL)) {
|
||||
handler = v9fs_op_not_supp;
|
||||
} else if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
|
||||
handler = v9fs_fs_ro;
|
||||
} else {
|
||||
handler = pdu_co_handlers[pdu->id];
|
||||
}
|
||||
|
||||
if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
|
||||
handler = v9fs_fs_ro;
|
||||
}
|
||||
|
||||
qemu_co_queue_init(&pdu->complete);
|
||||
co = qemu_coroutine_create(handler, pdu);
|
||||
qemu_coroutine_enter(co);
|
||||
@ -3544,9 +3542,9 @@ int v9fs_device_realize_common(V9fsState *s, Error **errp)
|
||||
s->fid_list = NULL;
|
||||
qemu_co_rwlock_init(&s->rename_lock);
|
||||
|
||||
if (s->ops->init(&s->ctx) < 0) {
|
||||
error_setg(errp, "9pfs Failed to initialize fs-driver with id:%s"
|
||||
" and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root);
|
||||
if (s->ops->init(&s->ctx, errp) < 0) {
|
||||
error_prepend(errp, "cannot initialize fsdev '%s': ",
|
||||
s->fsconf.fsdev_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
14
hw/9pfs/9p.h
14
hw/9pfs/9p.h
@ -94,10 +94,10 @@ enum {
|
||||
P9_QTFILE = 0x00,
|
||||
};
|
||||
|
||||
enum p9_proto_version {
|
||||
typedef enum P9ProtoVersion {
|
||||
V9FS_PROTO_2000U = 0x01,
|
||||
V9FS_PROTO_2000L = 0x02,
|
||||
};
|
||||
} P9ProtoVersion;
|
||||
|
||||
#define P9_NOTAG UINT16_MAX
|
||||
#define P9_NOFID UINT32_MAX
|
||||
@ -118,6 +118,7 @@ static inline char *rpath(FsContext *ctx, const char *path)
|
||||
|
||||
typedef struct V9fsPDU V9fsPDU;
|
||||
typedef struct V9fsState V9fsState;
|
||||
typedef struct V9fsTransport V9fsTransport;
|
||||
|
||||
typedef struct {
|
||||
uint32_t size_le;
|
||||
@ -238,10 +239,10 @@ struct V9fsState
|
||||
FileOperations *ops;
|
||||
FsContext ctx;
|
||||
char *tag;
|
||||
enum p9_proto_version proto_version;
|
||||
P9ProtoVersion proto_version;
|
||||
int32_t msize;
|
||||
V9fsPDU pdus[MAX_REQ];
|
||||
const struct V9fsTransport *transport;
|
||||
const V9fsTransport *transport;
|
||||
/*
|
||||
* lock ensuring atomic path update
|
||||
* on rename.
|
||||
@ -348,8 +349,6 @@ int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
|
||||
int v9fs_device_realize_common(V9fsState *s, Error **errp);
|
||||
void v9fs_device_unrealize_common(V9fsState *s, Error **errp);
|
||||
|
||||
ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...);
|
||||
ssize_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...);
|
||||
V9fsPDU *pdu_alloc(V9fsState *s);
|
||||
void pdu_free(V9fsPDU *pdu);
|
||||
void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr);
|
||||
@ -367,8 +366,7 @@ struct V9fsTransport {
|
||||
void (*push_and_notify)(V9fsPDU *pdu);
|
||||
};
|
||||
|
||||
static inline int v9fs_register_transport(V9fsState *s,
|
||||
const struct V9fsTransport *t)
|
||||
static inline int v9fs_register_transport(V9fsState *s, const V9fsTransport *t)
|
||||
{
|
||||
assert(!s->transport);
|
||||
s->transport = t;
|
||||
|
@ -20,8 +20,6 @@
|
||||
#include "hw/virtio/virtio-access.h"
|
||||
#include "qemu/iov.h"
|
||||
|
||||
static const struct V9fsTransport virtio_9p_transport;
|
||||
|
||||
static void virtio_9p_push_and_notify(V9fsPDU *pdu)
|
||||
{
|
||||
V9fsState *s = pdu->s;
|
||||
@ -104,35 +102,6 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
|
||||
g_free(cfg);
|
||||
}
|
||||
|
||||
static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
V9fsVirtioState *v = VIRTIO_9P(dev);
|
||||
V9fsState *s = &v->state;
|
||||
|
||||
if (v9fs_device_realize_common(s, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag);
|
||||
virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size);
|
||||
v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output);
|
||||
v9fs_register_transport(s, &virtio_9p_transport);
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
V9fsVirtioState *v = VIRTIO_9P(dev);
|
||||
V9fsState *s = &v->state;
|
||||
|
||||
virtio_cleanup(vdev);
|
||||
v9fs_device_unrealize_common(s, errp);
|
||||
}
|
||||
|
||||
static void virtio_9p_reset(VirtIODevice *vdev)
|
||||
{
|
||||
V9fsVirtioState *v = (V9fsVirtioState *)vdev;
|
||||
@ -215,7 +184,7 @@ static void virtio_init_out_iov_from_pdu(V9fsPDU *pdu, struct iovec **piov,
|
||||
*pniov = elem->out_num;
|
||||
}
|
||||
|
||||
static const struct V9fsTransport virtio_9p_transport = {
|
||||
static const V9fsTransport virtio_9p_transport = {
|
||||
.pdu_vmarshal = virtio_pdu_vmarshal,
|
||||
.pdu_vunmarshal = virtio_pdu_vunmarshal,
|
||||
.init_in_iov_from_pdu = virtio_init_in_iov_from_pdu,
|
||||
@ -223,6 +192,35 @@ static const struct V9fsTransport virtio_9p_transport = {
|
||||
.push_and_notify = virtio_9p_push_and_notify,
|
||||
};
|
||||
|
||||
static void virtio_9p_device_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
V9fsVirtioState *v = VIRTIO_9P(dev);
|
||||
V9fsState *s = &v->state;
|
||||
|
||||
if (v9fs_device_realize_common(s, errp)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
v->config_size = sizeof(struct virtio_9p_config) + strlen(s->fsconf.tag);
|
||||
virtio_init(vdev, "virtio-9p", VIRTIO_ID_9P, v->config_size);
|
||||
v->vq = virtio_add_queue(vdev, MAX_REQ, handle_9p_output);
|
||||
v9fs_register_transport(s, &virtio_9p_transport);
|
||||
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
static void virtio_9p_device_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||
V9fsVirtioState *v = VIRTIO_9P(dev);
|
||||
V9fsState *s = &v->state;
|
||||
|
||||
virtio_cleanup(vdev);
|
||||
v9fs_device_unrealize_common(s, errp);
|
||||
}
|
||||
|
||||
/* virtio-9p device */
|
||||
|
||||
static const VMStateDescription vmstate_virtio_9p = {
|
||||
|
@ -233,7 +233,7 @@ static void xen_9pfs_push_and_notify(V9fsPDU *pdu)
|
||||
qemu_bh_schedule(ring->bh);
|
||||
}
|
||||
|
||||
static const struct V9fsTransport xen_9p_transport = {
|
||||
static const V9fsTransport xen_9p_transport = {
|
||||
.pdu_vmarshal = xen_9pfs_pdu_vmarshal,
|
||||
.pdu_vunmarshal = xen_9pfs_pdu_vunmarshal,
|
||||
.init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu,
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/nvram/fw_cfg.h"
|
||||
#include "qemu/config-file.h"
|
||||
|
@ -7,6 +7,7 @@
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/acpi/ipmi.h"
|
||||
|
||||
void build_acpi_ipmi_devices(Aml *table, BusState *bus)
|
||||
|
@ -78,9 +78,9 @@ static void clipper_init(MachineState *machine)
|
||||
clipper_pci_map_irq);
|
||||
|
||||
/* Since we have an SRM-compatible PALcode, use the SRM epoch. */
|
||||
rtc_init(isa_bus, 1900, rtc_irq);
|
||||
mc146818_rtc_init(isa_bus, 1900, rtc_irq);
|
||||
|
||||
pit_init(isa_bus, 0x40, 0, NULL);
|
||||
i8254_pit_init(isa_bus, 0x40, 0, NULL);
|
||||
isa_create_simple(isa_bus, "i8042");
|
||||
|
||||
/* VGA setup. Don't bother loading the bios. */
|
||||
|
@ -385,6 +385,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
|
||||
spi_table[i].irq));
|
||||
}
|
||||
|
||||
qdev_set_nic_properties(DEVICE(&s->eth), &nd_table[0]);
|
||||
object_property_set_bool(OBJECT(&s->eth), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#undef REG_FMT
|
||||
|
@ -453,6 +453,7 @@ build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
AcpiSerialPortConsoleRedirection *spcr;
|
||||
const MemMapEntry *uart_memmap = &vms->memmap[VIRT_UART];
|
||||
int irq = vms->irqmap[VIRT_UART] + ARM_SPI_BASE;
|
||||
int spcr_start = table_data->len;
|
||||
|
||||
spcr = acpi_data_push(table_data, sizeof(*spcr));
|
||||
|
||||
@ -476,8 +477,8 @@ build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
spcr->pci_device_id = 0xffff; /* PCI Device ID: not a PCI device */
|
||||
spcr->pci_vendor_id = 0xffff; /* PCI Vendor ID: not a PCI device */
|
||||
|
||||
build_header(linker, table_data, (void *)spcr, "SPCR", sizeof(*spcr), 2,
|
||||
NULL, NULL);
|
||||
build_header(linker, table_data, (void *)(table_data->data + spcr_start),
|
||||
"SPCR", table_data->len - spcr_start, 2, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -512,8 +513,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
mem_base += numa_info[i].node_mem;
|
||||
}
|
||||
|
||||
build_header(linker, table_data, (void *)srat, "SRAT",
|
||||
table_data->len - srat_start, 3, NULL, NULL);
|
||||
build_header(linker, table_data, (void *)(table_data->data + srat_start),
|
||||
"SRAT", table_data->len - srat_start, 3, NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -522,6 +523,7 @@ build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
AcpiTableMcfg *mcfg;
|
||||
const MemMapEntry *memmap = vms->memmap;
|
||||
int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]);
|
||||
int mcfg_start = table_data->len;
|
||||
|
||||
mcfg = acpi_data_push(table_data, len);
|
||||
mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base);
|
||||
@ -532,7 +534,8 @@ build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
|
||||
/ PCIE_MMCFG_SIZE_MIN) - 1;
|
||||
|
||||
build_header(linker, table_data, (void *)mcfg, "MCFG", len, 1, NULL, NULL);
|
||||
build_header(linker, table_data, (void *)(table_data->data + mcfg_start),
|
||||
"MCFG", table_data->len - mcfg_start, 1, NULL, NULL);
|
||||
}
|
||||
|
||||
/* GTDT */
|
||||
@ -651,6 +654,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
|
||||
static void build_fadt(GArray *table_data, BIOSLinker *linker,
|
||||
VirtMachineState *vms, unsigned dsdt_tbl_offset)
|
||||
{
|
||||
int fadt_start = table_data->len;
|
||||
AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
|
||||
unsigned xdsdt_entry_offset = (char *)&fadt->x_dsdt - table_data->data;
|
||||
uint16_t bootflags;
|
||||
@ -681,8 +685,8 @@ static void build_fadt(GArray *table_data, BIOSLinker *linker,
|
||||
ACPI_BUILD_TABLE_FILE, xdsdt_entry_offset, sizeof(fadt->x_dsdt),
|
||||
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
|
||||
|
||||
build_header(linker, table_data,
|
||||
(void *)fadt, "FACP", sizeof(*fadt), 5, NULL, NULL);
|
||||
build_header(linker, table_data, (void *)(table_data->data + fadt_start),
|
||||
"FACP", table_data->len - fadt_start, 5, NULL, NULL);
|
||||
}
|
||||
|
||||
/* DSDT */
|
||||
|
@ -151,6 +151,29 @@ static void xlnx_zynqmp_init(XlnxZCU102 *s, MachineState *machine)
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.spi[i]), 1, cs_line);
|
||||
}
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_FLASH; i++) {
|
||||
SSIBus *spi_bus;
|
||||
DeviceState *flash_dev;
|
||||
qemu_irq cs_line;
|
||||
DriveInfo *dinfo = drive_get_next(IF_MTD);
|
||||
int bus = i / XLNX_ZYNQMP_NUM_QSPI_BUS_CS;
|
||||
gchar *bus_name = g_strdup_printf("qspi%d", bus);
|
||||
|
||||
spi_bus = (SSIBus *)qdev_get_child_bus(DEVICE(&s->soc), bus_name);
|
||||
g_free(bus_name);
|
||||
|
||||
flash_dev = ssi_create_slave_no_init(spi_bus, "n25q512a11");
|
||||
if (dinfo) {
|
||||
qdev_prop_set_drive(flash_dev, "drive", blk_by_legacy_dinfo(dinfo),
|
||||
&error_fatal);
|
||||
}
|
||||
qdev_init_nofail(flash_dev);
|
||||
|
||||
cs_line = qdev_get_gpio_in_named(flash_dev, SSI_GPIO_CS, 0);
|
||||
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->soc.qspi), i + 1, cs_line);
|
||||
}
|
||||
|
||||
/* TODO create and connect IDE devices for ide_drive_get() */
|
||||
|
||||
xlnx_zcu102_binfo.ram_size = ram_size;
|
||||
|
@ -40,6 +40,10 @@
|
||||
#define SATA_ADDR 0xFD0C0000
|
||||
#define SATA_NUM_PORTS 2
|
||||
|
||||
#define QSPI_ADDR 0xff0f0000
|
||||
#define LQSPI_ADDR 0xc0000000
|
||||
#define QSPI_IRQ 15
|
||||
|
||||
#define DP_ADDR 0xfd4a0000
|
||||
#define DP_IRQ 113
|
||||
|
||||
@ -171,6 +175,9 @@ static void xlnx_zynqmp_init(Object *obj)
|
||||
qdev_set_parent_bus(DEVICE(&s->spi[i]), sysbus_get_default());
|
||||
}
|
||||
|
||||
object_initialize(&s->qspi, sizeof(s->qspi), TYPE_XLNX_ZYNQMP_QSPIPS);
|
||||
qdev_set_parent_bus(DEVICE(&s->qspi), sysbus_get_default());
|
||||
|
||||
object_initialize(&s->dp, sizeof(s->dp), TYPE_XLNX_DP);
|
||||
qdev_set_parent_bus(DEVICE(&s->dp), sysbus_get_default());
|
||||
|
||||
@ -411,6 +418,25 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
|
||||
g_free(bus_name);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->qspi), true, "realized", &err);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 0, QSPI_ADDR);
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->qspi), 1, LQSPI_ADDR);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->qspi), 0, gic_spi[QSPI_IRQ]);
|
||||
|
||||
for (i = 0; i < XLNX_ZYNQMP_NUM_QSPI_BUS; i++) {
|
||||
gchar *bus_name;
|
||||
gchar *target_bus;
|
||||
|
||||
/* Alias controller SPI bus to the SoC itself */
|
||||
bus_name = g_strdup_printf("qspi%d", i);
|
||||
target_bus = g_strdup_printf("spi%d", i);
|
||||
object_property_add_alias(OBJECT(s), bus_name,
|
||||
OBJECT(&s->qspi), target_bus,
|
||||
&error_abort);
|
||||
g_free(bus_name);
|
||||
g_free(target_bus);
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(&s->dp), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <math.h>
|
||||
//#include "driver.h" /* use M.A.M.E. */
|
||||
#include "fmopl.h"
|
||||
#include "qemu/osdep.h"
|
||||
#ifndef PI
|
||||
#define PI 3.14159265358979323846
|
||||
#endif
|
||||
|
@ -1,7 +1,6 @@
|
||||
#ifndef FMOPL_H
|
||||
#define FMOPL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef void (*OPL_TIMERHANDLER)(void *param, int channel, double interval_Sec);
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/audio/soundhw.h"
|
||||
#include "audio/audio.h"
|
||||
|
@ -51,7 +51,7 @@ void blkconf_blocksizes(BlockConf *conf)
|
||||
}
|
||||
}
|
||||
|
||||
void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
|
||||
bool blkconf_apply_backend_options(BlockConf *conf, bool readonly,
|
||||
bool resizable, Error **errp)
|
||||
{
|
||||
BlockBackend *blk = conf->blk;
|
||||
@ -76,7 +76,7 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
|
||||
|
||||
ret = blk_set_perm(blk, perm, shared_perm, errp);
|
||||
if (ret < 0) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (conf->wce) {
|
||||
@ -99,9 +99,11 @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
|
||||
|
||||
blk_set_enable_write_cache(blk, wce);
|
||||
blk_set_on_error(blk, rerror, werror);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void blkconf_geometry(BlockConf *conf, int *ptrans,
|
||||
bool blkconf_geometry(BlockConf *conf, int *ptrans,
|
||||
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
|
||||
Error **errp)
|
||||
{
|
||||
@ -129,15 +131,16 @@ void blkconf_geometry(BlockConf *conf, int *ptrans,
|
||||
if (conf->cyls || conf->heads || conf->secs) {
|
||||
if (conf->cyls < 1 || conf->cyls > cyls_max) {
|
||||
error_setg(errp, "cyls must be between 1 and %u", cyls_max);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (conf->heads < 1 || conf->heads > heads_max) {
|
||||
error_setg(errp, "heads must be between 1 and %u", heads_max);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (conf->secs < 1 || conf->secs > secs_max) {
|
||||
error_setg(errp, "secs must be between 1 and %u", secs_max);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ static void notify_guest_bh(void *opaque)
|
||||
}
|
||||
|
||||
/* Context: QEMU global mutex held */
|
||||
void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
|
||||
bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
|
||||
VirtIOBlockDataPlane **dataplane,
|
||||
Error **errp)
|
||||
{
|
||||
@ -91,11 +91,11 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
|
||||
error_setg(errp,
|
||||
"device is incompatible with iothread "
|
||||
"(transport does not support notifiers)");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (!virtio_device_ioeventfd_enabled(vdev)) {
|
||||
error_setg(errp, "ioeventfd is required for iothread");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If dataplane is (re-)enabled while the guest is running there could
|
||||
@ -103,12 +103,12 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
|
||||
*/
|
||||
if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
|
||||
error_prepend(errp, "cannot start virtio-blk dataplane: ");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* Don't try if transport does not support notifiers. */
|
||||
if (!virtio_device_ioeventfd_enabled(vdev)) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
s = g_new0(VirtIOBlockDataPlane, 1);
|
||||
@ -126,6 +126,8 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
|
||||
s->batch_notify_vqs = bitmap_new(conf->num_queues);
|
||||
|
||||
*dataplane = s;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Context: QEMU global mutex held */
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane;
|
||||
|
||||
void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
|
||||
bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
|
||||
VirtIOBlockDataPlane **dataplane,
|
||||
Error **errp);
|
||||
void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);
|
||||
|
@ -473,16 +473,13 @@ static void fd_revalidate(FDrive *drv)
|
||||
static void fd_change_cb(void *opaque, bool load, Error **errp)
|
||||
{
|
||||
FDrive *drive = opaque;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!load) {
|
||||
blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
|
||||
} else {
|
||||
blkconf_apply_backend_options(drive->conf,
|
||||
blk_is_read_only(drive->blk), false,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (!blkconf_apply_backend_options(drive->conf,
|
||||
blk_is_read_only(drive->blk), false,
|
||||
errp)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -522,7 +519,6 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
|
||||
FloppyDrive *dev = FLOPPY_DRIVE(qdev);
|
||||
FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus);
|
||||
FDrive *drive;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
if (dev->unit == -1) {
|
||||
@ -568,10 +564,9 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
|
||||
dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
|
||||
dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
|
||||
|
||||
blkconf_apply_backend_options(&dev->conf, blk_is_read_only(dev->conf.blk),
|
||||
false, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
if (!blkconf_apply_backend_options(&dev->conf,
|
||||
blk_is_read_only(dev->conf.blk),
|
||||
false, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -240,6 +240,8 @@ static const FlashPartInfo known_devices[] = {
|
||||
{ INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) },
|
||||
{ INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) },
|
||||
{ INFO("n25q256a13", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
|
||||
{ INFO("n25q512a11", 0x20bb20, 0, 64 << 10, 1024, ER_4K) },
|
||||
{ INFO("n25q512a13", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
|
||||
{ INFO("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
|
||||
{ INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
|
||||
{ INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
|
||||
@ -331,7 +333,10 @@ typedef enum {
|
||||
WRDI = 0x4,
|
||||
RDSR = 0x5,
|
||||
WREN = 0x6,
|
||||
BRRD = 0x16,
|
||||
BRWR = 0x17,
|
||||
JEDEC_READ = 0x9f,
|
||||
BULK_ERASE_60 = 0x60,
|
||||
BULK_ERASE = 0xc7,
|
||||
READ_FSR = 0x70,
|
||||
RDCR = 0x15,
|
||||
@ -355,6 +360,8 @@ typedef enum {
|
||||
DPP = 0xa2,
|
||||
QPP = 0x32,
|
||||
QPP_4 = 0x34,
|
||||
RDID_90 = 0x90,
|
||||
RDID_AB = 0xab,
|
||||
|
||||
ERASE_4K = 0x20,
|
||||
ERASE4_4K = 0x21,
|
||||
@ -405,6 +412,7 @@ typedef enum {
|
||||
MAN_MACRONIX,
|
||||
MAN_NUMONYX,
|
||||
MAN_WINBOND,
|
||||
MAN_SST,
|
||||
MAN_GENERIC,
|
||||
} Manufacturer;
|
||||
|
||||
@ -423,6 +431,7 @@ typedef struct Flash {
|
||||
uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ];
|
||||
uint32_t len;
|
||||
uint32_t pos;
|
||||
bool data_read_loop;
|
||||
uint8_t needed_bytes;
|
||||
uint8_t cmd_in_progress;
|
||||
uint32_t cur_addr;
|
||||
@ -475,6 +484,8 @@ static inline Manufacturer get_man(Flash *s)
|
||||
return MAN_SPANSION;
|
||||
case 0xC2:
|
||||
return MAN_MACRONIX;
|
||||
case 0xBF:
|
||||
return MAN_SST;
|
||||
default:
|
||||
return MAN_GENERIC;
|
||||
}
|
||||
@ -698,6 +709,7 @@ static void complete_collecting_data(Flash *s)
|
||||
s->write_enable = false;
|
||||
}
|
||||
break;
|
||||
case BRWR:
|
||||
case EXTEND_ADDR_WRITE:
|
||||
s->ear = s->data[0];
|
||||
break;
|
||||
@ -710,6 +722,31 @@ static void complete_collecting_data(Flash *s)
|
||||
case WEVCR:
|
||||
s->enh_volatile_cfg = s->data[0];
|
||||
break;
|
||||
case RDID_90:
|
||||
case RDID_AB:
|
||||
if (get_man(s) == MAN_SST) {
|
||||
if (s->cur_addr <= 1) {
|
||||
if (s->cur_addr) {
|
||||
s->data[0] = s->pi->id[2];
|
||||
s->data[1] = s->pi->id[0];
|
||||
} else {
|
||||
s->data[0] = s->pi->id[0];
|
||||
s->data[1] = s->pi->id[2];
|
||||
}
|
||||
s->pos = 0;
|
||||
s->len = 2;
|
||||
s->data_read_loop = true;
|
||||
s->state = STATE_READING_DATA;
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"M25P80: Invalid read id address\n");
|
||||
}
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"M25P80: Read id (command 0x90/0xAB) is not supported"
|
||||
" by device\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -925,6 +962,8 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||
case PP4:
|
||||
case PP4_4:
|
||||
case DIE_ERASE:
|
||||
case RDID_90:
|
||||
case RDID_AB:
|
||||
s->needed_bytes = get_addr_length(s);
|
||||
s->pos = 0;
|
||||
s->len = 0;
|
||||
@ -983,6 +1022,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||
}
|
||||
s->pos = 0;
|
||||
s->len = 1;
|
||||
s->data_read_loop = true;
|
||||
s->state = STATE_READING_DATA;
|
||||
break;
|
||||
|
||||
@ -993,6 +1033,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||
}
|
||||
s->pos = 0;
|
||||
s->len = 1;
|
||||
s->data_read_loop = true;
|
||||
s->state = STATE_READING_DATA;
|
||||
break;
|
||||
|
||||
@ -1015,6 +1056,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||
s->state = STATE_READING_DATA;
|
||||
break;
|
||||
|
||||
case BULK_ERASE_60:
|
||||
case BULK_ERASE:
|
||||
if (s->write_enable) {
|
||||
DB_PRINT_L(0, "chip erase\n");
|
||||
@ -1032,12 +1074,14 @@ static void decode_new_cmd(Flash *s, uint32_t value)
|
||||
case EX_4BYTE_ADDR:
|
||||
s->four_bytes_address_mode = false;
|
||||
break;
|
||||
case BRRD:
|
||||
case EXTEND_ADDR_READ:
|
||||
s->data[0] = s->ear;
|
||||
s->pos = 0;
|
||||
s->len = 1;
|
||||
s->state = STATE_READING_DATA;
|
||||
break;
|
||||
case BRWR:
|
||||
case EXTEND_ADDR_WRITE:
|
||||
if (s->write_enable) {
|
||||
s->needed_bytes = 1;
|
||||
@ -1133,6 +1177,7 @@ static int m25p80_cs(SSISlave *ss, bool select)
|
||||
s->pos = 0;
|
||||
s->state = STATE_IDLE;
|
||||
flash_sync_dirty(s, -1);
|
||||
s->data_read_loop = false;
|
||||
}
|
||||
|
||||
DB_PRINT_L(0, "%sselect\n", select ? "de" : "");
|
||||
@ -1198,7 +1243,9 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
|
||||
s->pos++;
|
||||
if (s->pos == s->len) {
|
||||
s->pos = 0;
|
||||
s->state = STATE_IDLE;
|
||||
if (!s->data_read_loop) {
|
||||
s->state = STATE_IDLE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1269,11 +1316,38 @@ static Property m25p80_properties[] = {
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static int m25p80_pre_load(void *opaque)
|
||||
{
|
||||
Flash *s = (Flash *)opaque;
|
||||
|
||||
s->data_read_loop = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool m25p80_data_read_loop_needed(void *opaque)
|
||||
{
|
||||
Flash *s = (Flash *)opaque;
|
||||
|
||||
return s->data_read_loop;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_m25p80_data_read_loop = {
|
||||
.name = "m25p80/data_read_loop",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.needed = m25p80_data_read_loop_needed,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_BOOL(data_read_loop, Flash),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_m25p80 = {
|
||||
.name = "m25p80",
|
||||
.version_id = 0,
|
||||
.minimum_version_id = 0,
|
||||
.pre_save = m25p80_pre_save,
|
||||
.pre_load = m25p80_pre_load,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_UINT8(state, Flash),
|
||||
VMSTATE_UINT8_ARRAY(data, Flash, M25P80_INTERNAL_DATA_BUFFER_SZ),
|
||||
@ -1295,6 +1369,10 @@ static const VMStateDescription vmstate_m25p80 = {
|
||||
VMSTATE_UINT8(spansion_cr3nv, Flash),
|
||||
VMSTATE_UINT8(spansion_cr4nv, Flash),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
.subsections = (const VMStateDescription * []) {
|
||||
&vmstate_m25p80_data_read_loop,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
||||
|
372
hw/block/nvme.c
372
hw/block/nvme.c
@ -34,8 +34,17 @@
|
||||
#include "qapi/visitor.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
|
||||
#include "qemu/log.h"
|
||||
#include "trace.h"
|
||||
#include "nvme.h"
|
||||
|
||||
#define NVME_GUEST_ERR(trace, fmt, ...) \
|
||||
do { \
|
||||
(trace_##trace)(__VA_ARGS__); \
|
||||
qemu_log_mask(LOG_GUEST_ERROR, #trace \
|
||||
" in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
static void nvme_process_sq(void *opaque);
|
||||
|
||||
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
|
||||
@ -86,10 +95,14 @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
|
||||
{
|
||||
if (cq->irq_enabled) {
|
||||
if (msix_enabled(&(n->parent_obj))) {
|
||||
trace_nvme_irq_msix(cq->vector);
|
||||
msix_notify(&(n->parent_obj), cq->vector);
|
||||
} else {
|
||||
trace_nvme_irq_pin();
|
||||
pci_irq_pulse(&n->parent_obj);
|
||||
}
|
||||
} else {
|
||||
trace_nvme_irq_masked();
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +113,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
|
||||
trans_len = MIN(len, trans_len);
|
||||
int num_prps = (len >> n->page_bits) + 1;
|
||||
|
||||
if (!prp1) {
|
||||
if (unlikely(!prp1)) {
|
||||
trace_nvme_err_invalid_prp();
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
|
||||
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
|
||||
@ -113,7 +127,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
|
||||
}
|
||||
len -= trans_len;
|
||||
if (len) {
|
||||
if (!prp2) {
|
||||
if (unlikely(!prp2)) {
|
||||
trace_nvme_err_invalid_prp2_missing();
|
||||
goto unmap;
|
||||
}
|
||||
if (len > n->page_size) {
|
||||
@ -128,7 +143,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
|
||||
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
|
||||
|
||||
if (i == n->max_prp_ents - 1 && len > n->page_size) {
|
||||
if (!prp_ent || prp_ent & (n->page_size - 1)) {
|
||||
if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
|
||||
trace_nvme_err_invalid_prplist_ent(prp_ent);
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
@ -140,7 +156,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
|
||||
prp_ent = le64_to_cpu(prp_list[i]);
|
||||
}
|
||||
|
||||
if (!prp_ent || prp_ent & (n->page_size - 1)) {
|
||||
if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
|
||||
trace_nvme_err_invalid_prplist_ent(prp_ent);
|
||||
goto unmap;
|
||||
}
|
||||
|
||||
@ -154,7 +171,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
if (prp2 & (n->page_size - 1)) {
|
||||
if (unlikely(prp2 & (n->page_size - 1))) {
|
||||
trace_nvme_err_invalid_prp2_align(prp2);
|
||||
goto unmap;
|
||||
}
|
||||
if (qsg->nsg) {
|
||||
@ -178,16 +196,20 @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
|
||||
QEMUIOVector iov;
|
||||
uint16_t status = NVME_SUCCESS;
|
||||
|
||||
trace_nvme_dma_read(prp1, prp2);
|
||||
|
||||
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
if (qsg.nsg > 0) {
|
||||
if (dma_buf_read(ptr, len, &qsg)) {
|
||||
if (unlikely(dma_buf_read(ptr, len, &qsg))) {
|
||||
trace_nvme_err_invalid_dma();
|
||||
status = NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
qemu_sglist_destroy(&qsg);
|
||||
} else {
|
||||
if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
|
||||
if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
|
||||
trace_nvme_err_invalid_dma();
|
||||
status = NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
qemu_iovec_destroy(&iov);
|
||||
@ -273,7 +295,8 @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
|
||||
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
|
||||
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
|
||||
|
||||
if (slba + nlb > ns->id_ns.nsze) {
|
||||
if (unlikely(slba + nlb > ns->id_ns.nsze)) {
|
||||
trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
|
||||
return NVME_LBA_RANGE | NVME_DNR;
|
||||
}
|
||||
|
||||
@ -301,8 +324,11 @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
|
||||
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
|
||||
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
|
||||
|
||||
if ((slba + nlb) > ns->id_ns.nsze) {
|
||||
trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
|
||||
|
||||
if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
|
||||
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
|
||||
trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
|
||||
return NVME_LBA_RANGE | NVME_DNR;
|
||||
}
|
||||
|
||||
@ -336,7 +362,8 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
||||
NvmeNamespace *ns;
|
||||
uint32_t nsid = le32_to_cpu(cmd->nsid);
|
||||
|
||||
if (nsid == 0 || nsid > n->num_namespaces) {
|
||||
if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
|
||||
trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
|
||||
return NVME_INVALID_NSID | NVME_DNR;
|
||||
}
|
||||
|
||||
@ -350,6 +377,7 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
||||
case NVME_CMD_READ:
|
||||
return nvme_rw(n, ns, cmd, req);
|
||||
default:
|
||||
trace_nvme_err_invalid_opc(cmd->opcode);
|
||||
return NVME_INVALID_OPCODE | NVME_DNR;
|
||||
}
|
||||
}
|
||||
@ -373,10 +401,13 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
|
||||
NvmeCQueue *cq;
|
||||
uint16_t qid = le16_to_cpu(c->qid);
|
||||
|
||||
if (!qid || nvme_check_sqid(n, qid)) {
|
||||
if (unlikely(!qid || nvme_check_sqid(n, qid))) {
|
||||
trace_nvme_err_invalid_del_sq(qid);
|
||||
return NVME_INVALID_QID | NVME_DNR;
|
||||
}
|
||||
|
||||
trace_nvme_del_sq(qid);
|
||||
|
||||
sq = n->sq[qid];
|
||||
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
|
||||
req = QTAILQ_FIRST(&sq->out_req_list);
|
||||
@ -439,19 +470,26 @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
|
||||
uint16_t qflags = le16_to_cpu(c->sq_flags);
|
||||
uint64_t prp1 = le64_to_cpu(c->prp1);
|
||||
|
||||
if (!cqid || nvme_check_cqid(n, cqid)) {
|
||||
trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags);
|
||||
|
||||
if (unlikely(!cqid || nvme_check_cqid(n, cqid))) {
|
||||
trace_nvme_err_invalid_create_sq_cqid(cqid);
|
||||
return NVME_INVALID_CQID | NVME_DNR;
|
||||
}
|
||||
if (!sqid || !nvme_check_sqid(n, sqid)) {
|
||||
if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) {
|
||||
trace_nvme_err_invalid_create_sq_sqid(sqid);
|
||||
return NVME_INVALID_QID | NVME_DNR;
|
||||
}
|
||||
if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
|
||||
if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
|
||||
trace_nvme_err_invalid_create_sq_size(qsize);
|
||||
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
|
||||
}
|
||||
if (!prp1 || prp1 & (n->page_size - 1)) {
|
||||
if (unlikely(!prp1 || prp1 & (n->page_size - 1))) {
|
||||
trace_nvme_err_invalid_create_sq_addr(prp1);
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
if (!(NVME_SQ_FLAGS_PC(qflags))) {
|
||||
if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) {
|
||||
trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags));
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
sq = g_malloc0(sizeof(*sq));
|
||||
@ -476,14 +514,17 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
|
||||
NvmeCQueue *cq;
|
||||
uint16_t qid = le16_to_cpu(c->qid);
|
||||
|
||||
if (!qid || nvme_check_cqid(n, qid)) {
|
||||
if (unlikely(!qid || nvme_check_cqid(n, qid))) {
|
||||
trace_nvme_err_invalid_del_cq_cqid(qid);
|
||||
return NVME_INVALID_CQID | NVME_DNR;
|
||||
}
|
||||
|
||||
cq = n->cq[qid];
|
||||
if (!QTAILQ_EMPTY(&cq->sq_list)) {
|
||||
if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) {
|
||||
trace_nvme_err_invalid_del_cq_notempty(qid);
|
||||
return NVME_INVALID_QUEUE_DEL;
|
||||
}
|
||||
trace_nvme_del_cq(qid);
|
||||
nvme_free_cq(cq, n);
|
||||
return NVME_SUCCESS;
|
||||
}
|
||||
@ -516,19 +557,27 @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
|
||||
uint16_t qflags = le16_to_cpu(c->cq_flags);
|
||||
uint64_t prp1 = le64_to_cpu(c->prp1);
|
||||
|
||||
if (!cqid || !nvme_check_cqid(n, cqid)) {
|
||||
trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags,
|
||||
NVME_CQ_FLAGS_IEN(qflags) != 0);
|
||||
|
||||
if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) {
|
||||
trace_nvme_err_invalid_create_cq_cqid(cqid);
|
||||
return NVME_INVALID_CQID | NVME_DNR;
|
||||
}
|
||||
if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
|
||||
if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
|
||||
trace_nvme_err_invalid_create_cq_size(qsize);
|
||||
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
|
||||
}
|
||||
if (!prp1) {
|
||||
if (unlikely(!prp1)) {
|
||||
trace_nvme_err_invalid_create_cq_addr(prp1);
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
if (vector > n->num_queues) {
|
||||
if (unlikely(vector > n->num_queues)) {
|
||||
trace_nvme_err_invalid_create_cq_vector(vector);
|
||||
return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
|
||||
}
|
||||
if (!(NVME_CQ_FLAGS_PC(qflags))) {
|
||||
if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) {
|
||||
trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags));
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
|
||||
@ -543,6 +592,8 @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c)
|
||||
uint64_t prp1 = le64_to_cpu(c->prp1);
|
||||
uint64_t prp2 = le64_to_cpu(c->prp2);
|
||||
|
||||
trace_nvme_identify_ctrl();
|
||||
|
||||
return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
|
||||
prp1, prp2);
|
||||
}
|
||||
@ -554,11 +605,15 @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c)
|
||||
uint64_t prp1 = le64_to_cpu(c->prp1);
|
||||
uint64_t prp2 = le64_to_cpu(c->prp2);
|
||||
|
||||
if (nsid == 0 || nsid > n->num_namespaces) {
|
||||
trace_nvme_identify_ns(nsid);
|
||||
|
||||
if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
|
||||
trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
|
||||
return NVME_INVALID_NSID | NVME_DNR;
|
||||
}
|
||||
|
||||
ns = &n->namespaces[nsid - 1];
|
||||
|
||||
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
|
||||
prp1, prp2);
|
||||
}
|
||||
@ -573,6 +628,8 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
|
||||
uint16_t ret;
|
||||
int i, j = 0;
|
||||
|
||||
trace_nvme_identify_nslist(min_nsid);
|
||||
|
||||
list = g_malloc0(data_len);
|
||||
for (i = 0; i < n->num_namespaces; i++) {
|
||||
if (i < min_nsid) {
|
||||
@ -601,6 +658,7 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
|
||||
case 0x02:
|
||||
return nvme_identify_nslist(n, c);
|
||||
default:
|
||||
trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
}
|
||||
@ -613,11 +671,14 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
||||
switch (dw10) {
|
||||
case NVME_VOLATILE_WRITE_CACHE:
|
||||
result = blk_enable_write_cache(n->conf.blk);
|
||||
trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
|
||||
break;
|
||||
case NVME_NUMBER_OF_QUEUES:
|
||||
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
|
||||
trace_nvme_getfeat_numq(result);
|
||||
break;
|
||||
default:
|
||||
trace_nvme_err_invalid_getfeat(dw10);
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
|
||||
@ -635,10 +696,14 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
||||
blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
|
||||
break;
|
||||
case NVME_NUMBER_OF_QUEUES:
|
||||
trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1,
|
||||
((dw11 >> 16) & 0xFFFF) + 1,
|
||||
n->num_queues - 1, n->num_queues - 1);
|
||||
req->cqe.result =
|
||||
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
|
||||
break;
|
||||
default:
|
||||
trace_nvme_err_invalid_setfeat(dw10);
|
||||
return NVME_INVALID_FIELD | NVME_DNR;
|
||||
}
|
||||
return NVME_SUCCESS;
|
||||
@ -662,6 +727,7 @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
|
||||
case NVME_ADM_CMD_GET_FEATURES:
|
||||
return nvme_get_feature(n, cmd, req);
|
||||
default:
|
||||
trace_nvme_err_invalid_admin_opc(cmd->opcode);
|
||||
return NVME_INVALID_OPCODE | NVME_DNR;
|
||||
}
|
||||
}
|
||||
@ -721,15 +787,78 @@ static int nvme_start_ctrl(NvmeCtrl *n)
|
||||
uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
|
||||
uint32_t page_size = 1 << page_bits;
|
||||
|
||||
if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
|
||||
n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
|
||||
NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
|
||||
NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
|
||||
NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
|
||||
NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
|
||||
NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
|
||||
NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
|
||||
!NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) {
|
||||
if (unlikely(n->cq[0])) {
|
||||
trace_nvme_err_startfail_cq();
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(n->sq[0])) {
|
||||
trace_nvme_err_startfail_sq();
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(!n->bar.asq)) {
|
||||
trace_nvme_err_startfail_nbarasq();
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(!n->bar.acq)) {
|
||||
trace_nvme_err_startfail_nbaracq();
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(n->bar.asq & (page_size - 1))) {
|
||||
trace_nvme_err_startfail_asq_misaligned(n->bar.asq);
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(n->bar.acq & (page_size - 1))) {
|
||||
trace_nvme_err_startfail_acq_misaligned(n->bar.acq);
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(NVME_CC_MPS(n->bar.cc) <
|
||||
NVME_CAP_MPSMIN(n->bar.cap))) {
|
||||
trace_nvme_err_startfail_page_too_small(
|
||||
NVME_CC_MPS(n->bar.cc),
|
||||
NVME_CAP_MPSMIN(n->bar.cap));
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(NVME_CC_MPS(n->bar.cc) >
|
||||
NVME_CAP_MPSMAX(n->bar.cap))) {
|
||||
trace_nvme_err_startfail_page_too_large(
|
||||
NVME_CC_MPS(n->bar.cc),
|
||||
NVME_CAP_MPSMAX(n->bar.cap));
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(NVME_CC_IOCQES(n->bar.cc) <
|
||||
NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) {
|
||||
trace_nvme_err_startfail_cqent_too_small(
|
||||
NVME_CC_IOCQES(n->bar.cc),
|
||||
NVME_CTRL_CQES_MIN(n->bar.cap));
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(NVME_CC_IOCQES(n->bar.cc) >
|
||||
NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) {
|
||||
trace_nvme_err_startfail_cqent_too_large(
|
||||
NVME_CC_IOCQES(n->bar.cc),
|
||||
NVME_CTRL_CQES_MAX(n->bar.cap));
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(NVME_CC_IOSQES(n->bar.cc) <
|
||||
NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) {
|
||||
trace_nvme_err_startfail_sqent_too_small(
|
||||
NVME_CC_IOSQES(n->bar.cc),
|
||||
NVME_CTRL_SQES_MIN(n->bar.cap));
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(NVME_CC_IOSQES(n->bar.cc) >
|
||||
NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) {
|
||||
trace_nvme_err_startfail_sqent_too_large(
|
||||
NVME_CC_IOSQES(n->bar.cc),
|
||||
NVME_CTRL_SQES_MAX(n->bar.cap));
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) {
|
||||
trace_nvme_err_startfail_asqent_sz_zero();
|
||||
return -1;
|
||||
}
|
||||
if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) {
|
||||
trace_nvme_err_startfail_acqent_sz_zero();
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -749,16 +878,48 @@ static int nvme_start_ctrl(NvmeCtrl *n)
|
||||
static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
|
||||
unsigned size)
|
||||
{
|
||||
if (unlikely(offset & (sizeof(uint32_t) - 1))) {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32,
|
||||
"MMIO write not 32-bit aligned,"
|
||||
" offset=0x%"PRIx64"", offset);
|
||||
/* should be ignored, fall through for now */
|
||||
}
|
||||
|
||||
if (unlikely(size < sizeof(uint32_t))) {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall,
|
||||
"MMIO write smaller than 32-bits,"
|
||||
" offset=0x%"PRIx64", size=%u",
|
||||
offset, size);
|
||||
/* should be ignored, fall through for now */
|
||||
}
|
||||
|
||||
switch (offset) {
|
||||
case 0xc:
|
||||
case 0xc: /* INTMS */
|
||||
if (unlikely(msix_enabled(&(n->parent_obj)))) {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
|
||||
"undefined access to interrupt mask set"
|
||||
" when MSI-X is enabled");
|
||||
/* should be ignored, fall through for now */
|
||||
}
|
||||
n->bar.intms |= data & 0xffffffff;
|
||||
n->bar.intmc = n->bar.intms;
|
||||
trace_nvme_mmio_intm_set(data & 0xffffffff,
|
||||
n->bar.intmc);
|
||||
break;
|
||||
case 0x10:
|
||||
case 0x10: /* INTMC */
|
||||
if (unlikely(msix_enabled(&(n->parent_obj)))) {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
|
||||
"undefined access to interrupt mask clr"
|
||||
" when MSI-X is enabled");
|
||||
/* should be ignored, fall through for now */
|
||||
}
|
||||
n->bar.intms &= ~(data & 0xffffffff);
|
||||
n->bar.intmc = n->bar.intms;
|
||||
trace_nvme_mmio_intm_clr(data & 0xffffffff,
|
||||
n->bar.intmc);
|
||||
break;
|
||||
case 0x14:
|
||||
case 0x14: /* CC */
|
||||
trace_nvme_mmio_cfg(data & 0xffffffff);
|
||||
/* Windows first sends data, then sends enable bit */
|
||||
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
|
||||
!NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
|
||||
@ -768,40 +929,82 @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
|
||||
|
||||
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
|
||||
n->bar.cc = data;
|
||||
if (nvme_start_ctrl(n)) {
|
||||
if (unlikely(nvme_start_ctrl(n))) {
|
||||
trace_nvme_err_startfail();
|
||||
n->bar.csts = NVME_CSTS_FAILED;
|
||||
} else {
|
||||
trace_nvme_mmio_start_success();
|
||||
n->bar.csts = NVME_CSTS_READY;
|
||||
}
|
||||
} else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
|
||||
trace_nvme_mmio_stopped();
|
||||
nvme_clear_ctrl(n);
|
||||
n->bar.csts &= ~NVME_CSTS_READY;
|
||||
}
|
||||
if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
|
||||
nvme_clear_ctrl(n);
|
||||
n->bar.cc = data;
|
||||
n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
|
||||
trace_nvme_mmio_shutdown_set();
|
||||
nvme_clear_ctrl(n);
|
||||
n->bar.cc = data;
|
||||
n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
|
||||
} else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
|
||||
n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
|
||||
n->bar.cc = data;
|
||||
trace_nvme_mmio_shutdown_cleared();
|
||||
n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
|
||||
n->bar.cc = data;
|
||||
}
|
||||
break;
|
||||
case 0x24:
|
||||
case 0x1C: /* CSTS */
|
||||
if (data & (1 << 4)) {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported,
|
||||
"attempted to W1C CSTS.NSSRO"
|
||||
" but CAP.NSSRS is zero (not supported)");
|
||||
} else if (data != 0) {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts,
|
||||
"attempted to set a read only bit"
|
||||
" of controller status");
|
||||
}
|
||||
break;
|
||||
case 0x20: /* NSSR */
|
||||
if (data == 0x4E564D65) {
|
||||
trace_nvme_ub_mmiowr_ssreset_unsupported();
|
||||
} else {
|
||||
/* The spec says that writes of other values have no effect */
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 0x24: /* AQA */
|
||||
n->bar.aqa = data & 0xffffffff;
|
||||
trace_nvme_mmio_aqattr(data & 0xffffffff);
|
||||
break;
|
||||
case 0x28:
|
||||
case 0x28: /* ASQ */
|
||||
n->bar.asq = data;
|
||||
trace_nvme_mmio_asqaddr(data);
|
||||
break;
|
||||
case 0x2c:
|
||||
case 0x2c: /* ASQ hi */
|
||||
n->bar.asq |= data << 32;
|
||||
trace_nvme_mmio_asqaddr_hi(data, n->bar.asq);
|
||||
break;
|
||||
case 0x30:
|
||||
case 0x30: /* ACQ */
|
||||
trace_nvme_mmio_acqaddr(data);
|
||||
n->bar.acq = data;
|
||||
break;
|
||||
case 0x34:
|
||||
case 0x34: /* ACQ hi */
|
||||
n->bar.acq |= data << 32;
|
||||
trace_nvme_mmio_acqaddr_hi(data, n->bar.acq);
|
||||
break;
|
||||
case 0x38: /* CMBLOC */
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved,
|
||||
"invalid write to reserved CMBLOC"
|
||||
" when CMBSZ is zero, ignored");
|
||||
return;
|
||||
case 0x3C: /* CMBSZ */
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly,
|
||||
"invalid write to read only CMBSZ, ignored");
|
||||
return;
|
||||
default:
|
||||
NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
|
||||
"invalid MMIO write,"
|
||||
" offset=0x%"PRIx64", data=%"PRIx64"",
|
||||
offset, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -812,9 +1015,26 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
|
||||
uint8_t *ptr = (uint8_t *)&n->bar;
|
||||
uint64_t val = 0;
|
||||
|
||||
if (unlikely(addr & (sizeof(uint32_t) - 1))) {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32,
|
||||
"MMIO read not 32-bit aligned,"
|
||||
" offset=0x%"PRIx64"", addr);
|
||||
/* should RAZ, fall through for now */
|
||||
} else if (unlikely(size < sizeof(uint32_t))) {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiord_toosmall,
|
||||
"MMIO read smaller than 32-bits,"
|
||||
" offset=0x%"PRIx64"", addr);
|
||||
/* should RAZ, fall through for now */
|
||||
}
|
||||
|
||||
if (addr < sizeof(n->bar)) {
|
||||
memcpy(&val, ptr + addr, size);
|
||||
} else {
|
||||
NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs,
|
||||
"MMIO read beyond last register,"
|
||||
" offset=0x%"PRIx64", returning 0", addr);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@ -822,22 +1042,36 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
|
||||
{
|
||||
uint32_t qid;
|
||||
|
||||
if (addr & ((1 << 2) - 1)) {
|
||||
if (unlikely(addr & ((1 << 2) - 1))) {
|
||||
NVME_GUEST_ERR(nvme_ub_db_wr_misaligned,
|
||||
"doorbell write not 32-bit aligned,"
|
||||
" offset=0x%"PRIx64", ignoring", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (((addr - 0x1000) >> 2) & 1) {
|
||||
/* Completion queue doorbell write */
|
||||
|
||||
uint16_t new_head = val & 0xffff;
|
||||
int start_sqs;
|
||||
NvmeCQueue *cq;
|
||||
|
||||
qid = (addr - (0x1000 + (1 << 2))) >> 3;
|
||||
if (nvme_check_cqid(n, qid)) {
|
||||
if (unlikely(nvme_check_cqid(n, qid))) {
|
||||
NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq,
|
||||
"completion queue doorbell write"
|
||||
" for nonexistent queue,"
|
||||
" sqid=%"PRIu32", ignoring", qid);
|
||||
return;
|
||||
}
|
||||
|
||||
cq = n->cq[qid];
|
||||
if (new_head >= cq->size) {
|
||||
if (unlikely(new_head >= cq->size)) {
|
||||
NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead,
|
||||
"completion queue doorbell write value"
|
||||
" beyond queue size, sqid=%"PRIu32","
|
||||
" new_head=%"PRIu16", ignoring",
|
||||
qid, new_head);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -855,16 +1089,27 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
|
||||
nvme_isr_notify(n, cq);
|
||||
}
|
||||
} else {
|
||||
/* Submission queue doorbell write */
|
||||
|
||||
uint16_t new_tail = val & 0xffff;
|
||||
NvmeSQueue *sq;
|
||||
|
||||
qid = (addr - 0x1000) >> 3;
|
||||
if (nvme_check_sqid(n, qid)) {
|
||||
if (unlikely(nvme_check_sqid(n, qid))) {
|
||||
NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq,
|
||||
"submission queue doorbell write"
|
||||
" for nonexistent queue,"
|
||||
" sqid=%"PRIu32", ignoring", qid);
|
||||
return;
|
||||
}
|
||||
|
||||
sq = n->sq[qid];
|
||||
if (new_tail >= sq->size) {
|
||||
if (unlikely(new_tail >= sq->size)) {
|
||||
NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail,
|
||||
"submission queue doorbell write value"
|
||||
" beyond queue size, sqid=%"PRIu32","
|
||||
" new_tail=%"PRIu16", ignoring",
|
||||
qid, new_tail);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -920,7 +1165,7 @@ static const MemoryRegionOps nvme_cmb_ops = {
|
||||
},
|
||||
};
|
||||
|
||||
static int nvme_init(PCIDevice *pci_dev)
|
||||
static void nvme_realize(PCIDevice *pci_dev, Error **errp)
|
||||
{
|
||||
NvmeCtrl *n = NVME(pci_dev);
|
||||
NvmeIdCtrl *id = &n->id_ctrl;
|
||||
@ -928,27 +1173,27 @@ static int nvme_init(PCIDevice *pci_dev)
|
||||
int i;
|
||||
int64_t bs_size;
|
||||
uint8_t *pci_conf;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!n->conf.blk) {
|
||||
return -1;
|
||||
error_setg(errp, "drive property not set");
|
||||
return;
|
||||
}
|
||||
|
||||
bs_size = blk_getlength(n->conf.blk);
|
||||
if (bs_size < 0) {
|
||||
return -1;
|
||||
error_setg(errp, "could not get backing file size");
|
||||
return;
|
||||
}
|
||||
|
||||
blkconf_serial(&n->conf, &n->serial);
|
||||
if (!n->serial) {
|
||||
return -1;
|
||||
error_setg(errp, "serial property not set");
|
||||
return;
|
||||
}
|
||||
blkconf_blocksizes(&n->conf);
|
||||
blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
|
||||
false, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
return -1;
|
||||
if (!blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
|
||||
false, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pci_conf = pci_dev->config;
|
||||
@ -1046,7 +1291,6 @@ static int nvme_init(PCIDevice *pci_dev)
|
||||
cpu_to_le64(n->ns_size >>
|
||||
id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nvme_exit(PCIDevice *pci_dev)
|
||||
@ -1081,7 +1325,7 @@ static void nvme_class_init(ObjectClass *oc, void *data)
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
|
||||
|
||||
pc->init = nvme_init;
|
||||
pc->realize = nvme_realize;
|
||||
pc->exit = nvme_exit;
|
||||
pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
|
||||
pc->vendor_id = PCI_VENDOR_ID_INTEL;
|
||||
|
@ -10,3 +10,103 @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
|
||||
# hw/block/hd-geometry.c
|
||||
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
|
||||
hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
|
||||
|
||||
# hw/block/nvme.c
|
||||
# nvme traces for successful events
|
||||
nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
|
||||
nvme_irq_pin(void) "pulsing IRQ pin"
|
||||
nvme_irq_masked(void) "IRQ is masked"
|
||||
nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
|
||||
nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64""
|
||||
nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16""
|
||||
nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d"
|
||||
nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
|
||||
nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
|
||||
nvme_identify_ctrl(void) "identify controller"
|
||||
nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
|
||||
nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
|
||||
nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
|
||||
nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
|
||||
nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d"
|
||||
nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
|
||||
nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
|
||||
nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
|
||||
nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
|
||||
nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
|
||||
nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
|
||||
nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
|
||||
nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
|
||||
nvme_mmio_start_success(void) "setting controller enable bit succeeded"
|
||||
nvme_mmio_stopped(void) "cleared controller enable bit"
|
||||
nvme_mmio_shutdown_set(void) "shutdown bit set"
|
||||
nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
|
||||
|
||||
# nvme traces for error conditions
|
||||
nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
|
||||
nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
|
||||
nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
|
||||
nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
|
||||
nvme_err_invalid_field(void) "invalid field"
|
||||
nvme_err_invalid_prp(void) "invalid PRP"
|
||||
nvme_err_invalid_sgl(void) "invalid SGL"
|
||||
nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
|
||||
nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
|
||||
nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
|
||||
nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
|
||||
nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
|
||||
nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
|
||||
nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
|
||||
nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
|
||||
nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
|
||||
nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
|
||||
nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
|
||||
nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
|
||||
nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
|
||||
nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
|
||||
nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
|
||||
nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
|
||||
nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
|
||||
nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
|
||||
nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
|
||||
nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
|
||||
nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
|
||||
nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
|
||||
nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
|
||||
nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
|
||||
nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
|
||||
nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
|
||||
nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u"
|
||||
nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u"
|
||||
nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u"
|
||||
nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u"
|
||||
nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u"
|
||||
nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u"
|
||||
nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero"
|
||||
nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero"
|
||||
nvme_err_startfail(void) "setting controller enable bit failed"
|
||||
|
||||
# Traces for undefined behavior
|
||||
nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64""
|
||||
nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u"
|
||||
nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled"
|
||||
nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status"
|
||||
nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)"
|
||||
nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
|
||||
nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
|
||||
nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
|
||||
nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
|
||||
nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
|
||||
nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
|
||||
nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0"
|
||||
nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring"
|
||||
nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring"
|
||||
nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring"
|
||||
nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring"
|
||||
nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
|
||||
|
||||
# hw/block/xen_disk.c
|
||||
xen_disk_alloc(char *name) "%s"
|
||||
xen_disk_init(char *name) "%s"
|
||||
xen_disk_connect(char *name) "%s"
|
||||
xen_disk_disconnect(char *name) "%s"
|
||||
xen_disk_free(char *name) "%s"
|
||||
|
@ -928,23 +928,34 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
||||
error_setg(errp, "num-queues property must be larger than 0");
|
||||
return;
|
||||
}
|
||||
if (!is_power_of_2(conf->queue_size) ||
|
||||
conf->queue_size > VIRTQUEUE_MAX_SIZE) {
|
||||
error_setg(errp, "invalid queue-size property (%" PRIu16 "), "
|
||||
"must be a power of 2 (max %d)",
|
||||
conf->queue_size, VIRTQUEUE_MAX_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
blkconf_serial(&conf->conf, &conf->serial);
|
||||
blkconf_apply_backend_options(&conf->conf,
|
||||
blk_is_read_only(conf->conf.blk), true,
|
||||
&err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
if (!blkconf_apply_backend_options(&conf->conf,
|
||||
blk_is_read_only(conf->conf.blk), true,
|
||||
errp)) {
|
||||
return;
|
||||
}
|
||||
s->original_wce = blk_enable_write_cache(conf->conf.blk);
|
||||
blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
if (!blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
blkconf_blocksizes(&conf->conf);
|
||||
|
||||
if (conf->conf.logical_block_size >
|
||||
conf->conf.physical_block_size) {
|
||||
error_setg(errp,
|
||||
"logical_block_size > physical_block_size not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
|
||||
sizeof(struct virtio_blk_config));
|
||||
|
||||
@ -953,7 +964,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
||||
s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
|
||||
|
||||
for (i = 0; i < conf->num_queues; i++) {
|
||||
virtio_add_queue(vdev, 128, virtio_blk_handle_output);
|
||||
virtio_add_queue(vdev, conf->queue_size, virtio_blk_handle_output);
|
||||
}
|
||||
virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
|
||||
if (err != NULL) {
|
||||
@ -1012,6 +1023,7 @@ static Property virtio_blk_properties[] = {
|
||||
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
|
||||
true),
|
||||
DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1),
|
||||
DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128),
|
||||
DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD,
|
||||
IOThread *),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
|
@ -27,10 +27,12 @@
|
||||
#include "hw/xen/xen_backend.h"
|
||||
#include "xen_blkif.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
|
||||
@ -125,6 +127,9 @@ struct XenBlkDev {
|
||||
DriveInfo *dinfo;
|
||||
BlockBackend *blk;
|
||||
QEMUBH *bh;
|
||||
|
||||
IOThread *iothread;
|
||||
AioContext *ctx;
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------- */
|
||||
@ -596,9 +601,12 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq);
|
||||
static void qemu_aio_complete(void *opaque, int ret)
|
||||
{
|
||||
struct ioreq *ioreq = opaque;
|
||||
struct XenBlkDev *blkdev = ioreq->blkdev;
|
||||
|
||||
aio_context_acquire(blkdev->ctx);
|
||||
|
||||
if (ret != 0) {
|
||||
xen_pv_printf(&ioreq->blkdev->xendev, 0, "%s I/O error\n",
|
||||
xen_pv_printf(&blkdev->xendev, 0, "%s I/O error\n",
|
||||
ioreq->req.operation == BLKIF_OP_READ ? "read" : "write");
|
||||
ioreq->aio_errors++;
|
||||
}
|
||||
@ -607,10 +615,10 @@ static void qemu_aio_complete(void *opaque, int ret)
|
||||
if (ioreq->presync) {
|
||||
ioreq->presync = 0;
|
||||
ioreq_runio_qemu_aio(ioreq);
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
if (ioreq->aio_inflight > 0) {
|
||||
return;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (xen_feature_grant_copy) {
|
||||
@ -647,16 +655,19 @@ static void qemu_aio_complete(void *opaque, int ret)
|
||||
}
|
||||
case BLKIF_OP_READ:
|
||||
if (ioreq->status == BLKIF_RSP_OKAY) {
|
||||
block_acct_done(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
|
||||
block_acct_done(blk_get_stats(blkdev->blk), &ioreq->acct);
|
||||
} else {
|
||||
block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct);
|
||||
block_acct_failed(blk_get_stats(blkdev->blk), &ioreq->acct);
|
||||
}
|
||||
break;
|
||||
case BLKIF_OP_DISCARD:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
qemu_bh_schedule(ioreq->blkdev->bh);
|
||||
qemu_bh_schedule(blkdev->bh);
|
||||
|
||||
done:
|
||||
aio_context_release(blkdev->ctx);
|
||||
}
|
||||
|
||||
static bool blk_split_discard(struct ioreq *ioreq, blkif_sector_t sector_number,
|
||||
@ -913,17 +924,29 @@ static void blk_handle_requests(struct XenBlkDev *blkdev)
|
||||
static void blk_bh(void *opaque)
|
||||
{
|
||||
struct XenBlkDev *blkdev = opaque;
|
||||
|
||||
aio_context_acquire(blkdev->ctx);
|
||||
blk_handle_requests(blkdev);
|
||||
aio_context_release(blkdev->ctx);
|
||||
}
|
||||
|
||||
static void blk_alloc(struct XenDevice *xendev)
|
||||
{
|
||||
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
|
||||
Error *err = NULL;
|
||||
|
||||
trace_xen_disk_alloc(xendev->name);
|
||||
|
||||
QLIST_INIT(&blkdev->inflight);
|
||||
QLIST_INIT(&blkdev->finished);
|
||||
QLIST_INIT(&blkdev->freelist);
|
||||
blkdev->bh = qemu_bh_new(blk_bh, blkdev);
|
||||
|
||||
blkdev->iothread = iothread_create(xendev->name, &err);
|
||||
assert(!err);
|
||||
|
||||
blkdev->ctx = iothread_get_aio_context(blkdev->iothread);
|
||||
blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev);
|
||||
|
||||
if (xen_mode != XEN_EMULATE) {
|
||||
batch_maps = 1;
|
||||
}
|
||||
@ -950,6 +973,8 @@ static int blk_init(struct XenDevice *xendev)
|
||||
int info = 0;
|
||||
char *directiosafe = NULL;
|
||||
|
||||
trace_xen_disk_init(xendev->name);
|
||||
|
||||
/* read xenstore entries */
|
||||
if (blkdev->params == NULL) {
|
||||
char *h = NULL;
|
||||
@ -1062,6 +1087,8 @@ static int blk_connect(struct XenDevice *xendev)
|
||||
unsigned int i;
|
||||
uint32_t *domids;
|
||||
|
||||
trace_xen_disk_connect(xendev->name);
|
||||
|
||||
/* read-only ? */
|
||||
if (blkdev->directiosafe) {
|
||||
qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO;
|
||||
@ -1287,6 +1314,8 @@ static int blk_connect(struct XenDevice *xendev)
|
||||
blkdev->persistent_gnt_count = 0;
|
||||
}
|
||||
|
||||
blk_set_aio_context(blkdev->blk, blkdev->ctx);
|
||||
|
||||
xen_be_bind_evtchn(&blkdev->xendev);
|
||||
|
||||
xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, nr-ring-ref %u, "
|
||||
@ -1300,13 +1329,20 @@ static void blk_disconnect(struct XenDevice *xendev)
|
||||
{
|
||||
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
|
||||
|
||||
trace_xen_disk_disconnect(xendev->name);
|
||||
|
||||
aio_context_acquire(blkdev->ctx);
|
||||
|
||||
if (blkdev->blk) {
|
||||
blk_set_aio_context(blkdev->blk, qemu_get_aio_context());
|
||||
blk_detach_dev(blkdev->blk, blkdev);
|
||||
blk_unref(blkdev->blk);
|
||||
blkdev->blk = NULL;
|
||||
}
|
||||
xen_pv_unbind_evtchn(&blkdev->xendev);
|
||||
|
||||
aio_context_release(blkdev->ctx);
|
||||
|
||||
if (blkdev->sring) {
|
||||
xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring,
|
||||
blkdev->nr_ring_ref);
|
||||
@ -1345,6 +1381,8 @@ static int blk_free(struct XenDevice *xendev)
|
||||
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
|
||||
struct ioreq *ioreq;
|
||||
|
||||
trace_xen_disk_free(xendev->name);
|
||||
|
||||
blk_disconnect(xendev);
|
||||
|
||||
while (!QLIST_EMPTY(&blkdev->freelist)) {
|
||||
@ -1360,6 +1398,7 @@ static int blk_free(struct XenDevice *xendev)
|
||||
g_free(blkdev->dev);
|
||||
g_free(blkdev->devtype);
|
||||
qemu_bh_delete(blkdev->bh);
|
||||
iothread_destroy(blkdev->iothread);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include "hw/hw.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/i386/pc.h"
|
||||
|
||||
#define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon"
|
||||
#define ISA_DEBUGCON_DEVICE(obj) \
|
||||
|
@ -27,7 +27,6 @@
|
||||
#include "hw/hw.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "hw/xen/xen_backend.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#include <xen/io/console.h>
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "sysemu/numa.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/numa.h"
|
||||
#include "sysemu/qtest.h"
|
||||
|
||||
static char *machine_get_accel(Object *obj, Error **errp)
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "qapi/visitor.h"
|
||||
#include "chardev/char-fe.h"
|
||||
#include "sysemu/iothread.h"
|
||||
#include "sysemu/tpm_backend.h"
|
||||
|
||||
static void get_pointer(Object *obj, Visitor *v, Property *prop,
|
||||
char *(*print)(void *ptr),
|
||||
|
@ -6,6 +6,7 @@
|
||||
* 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 "hw/cpu/core.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/error.h"
|
||||
|
@ -31,7 +31,6 @@
|
||||
#include "trace.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "ui/console.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "vga_int.h"
|
||||
#include "hw/loader.h"
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
#include "ui/console.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "vga_int.h"
|
||||
|
@ -795,6 +795,8 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr,
|
||||
case SM501_ARBTRTN_CONTROL:
|
||||
ret = s->arbitration_control;
|
||||
break;
|
||||
case SM501_COMMAND_LIST_STATUS:
|
||||
ret = 0x00180002; /* FIFOs are empty, everything idle */
|
||||
case SM501_IRQ_MASK:
|
||||
ret = s->irq_mask;
|
||||
break;
|
||||
@ -812,6 +814,9 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr,
|
||||
case SM501_POWER_MODE_CONTROL:
|
||||
ret = s->power_mode_control;
|
||||
break;
|
||||
case SM501_ENDIAN_CONTROL:
|
||||
ret = 0; /* Only default little endian mode is supported */
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("sm501 system config : not implemented register read."
|
||||
@ -865,6 +870,12 @@ static void sm501_system_config_write(void *opaque, hwaddr addr,
|
||||
case SM501_POWER_MODE_CONTROL:
|
||||
s->power_mode_control = value & 0x00000003;
|
||||
break;
|
||||
case SM501_ENDIAN_CONTROL:
|
||||
if (value & 0x00000001) {
|
||||
printf("sm501 system config : big endian mode not implemented.\n");
|
||||
abort();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("sm501 system config : not implemented register write."
|
||||
@ -924,6 +935,9 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr,
|
||||
case SM501_DC_PANEL_PANNING_CONTROL:
|
||||
ret = s->dc_panel_panning_control;
|
||||
break;
|
||||
case SM501_DC_PANEL_COLOR_KEY:
|
||||
/* Not implemented yet */
|
||||
break;
|
||||
case SM501_DC_PANEL_FB_ADDR:
|
||||
ret = s->dc_panel_fb_addr;
|
||||
break;
|
||||
@ -956,6 +970,19 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr,
|
||||
ret = s->dc_panel_v_sync;
|
||||
break;
|
||||
|
||||
case SM501_DC_PANEL_HWC_ADDR:
|
||||
ret = s->dc_panel_hwc_addr;
|
||||
break;
|
||||
case SM501_DC_PANEL_HWC_LOC:
|
||||
ret = s->dc_panel_hwc_location;
|
||||
break;
|
||||
case SM501_DC_PANEL_HWC_COLOR_1_2:
|
||||
ret = s->dc_panel_hwc_color_1_2;
|
||||
break;
|
||||
case SM501_DC_PANEL_HWC_COLOR_3:
|
||||
ret = s->dc_panel_hwc_color_3;
|
||||
break;
|
||||
|
||||
case SM501_DC_VIDEO_CONTROL:
|
||||
ret = s->dc_video_control;
|
||||
break;
|
||||
@ -1022,6 +1049,9 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr,
|
||||
case SM501_DC_PANEL_PANNING_CONTROL:
|
||||
s->dc_panel_panning_control = value & 0xFF3FFF3F;
|
||||
break;
|
||||
case SM501_DC_PANEL_COLOR_KEY:
|
||||
/* Not implemented yet */
|
||||
break;
|
||||
case SM501_DC_PANEL_FB_ADDR:
|
||||
s->dc_panel_fb_addr = value & 0x8FFFFFF0;
|
||||
break;
|
||||
|
@ -172,6 +172,7 @@ static void tc6393xb_gpio_handler_update(TC6393xbState *s)
|
||||
int bit;
|
||||
|
||||
level = s->gpio_level & s->gpio_dir;
|
||||
level &= MAKE_64BIT_MASK(0, TC6393XB_GPIOS);
|
||||
|
||||
for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
|
||||
bit = ctz32(diff);
|
||||
|
@ -23,11 +23,9 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/display/vga.h"
|
||||
#include "vga_int.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "qemu/timer.h"
|
||||
|
||||
#define VGA_RAM_SIZE (8192 * 1024)
|
||||
|
||||
|
@ -25,8 +25,7 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "vga_int.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "qemu/timer.h"
|
||||
|
@ -25,7 +25,6 @@
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "hw/hw.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "vga_int.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
|
@ -24,11 +24,10 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "hw/hw.h"
|
||||
#include "vga.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/display/vga.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "vga_int.h"
|
||||
#include "vga_regs.h"
|
||||
#include "ui/pixel_ops.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "hw/xen/xen.h"
|
||||
|
@ -25,8 +25,9 @@
|
||||
#ifndef HW_VGA_INT_H
|
||||
#define HW_VGA_INT_H
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "exec/ioport.h"
|
||||
#include "exec/memory.h"
|
||||
#include "ui/console.h"
|
||||
|
||||
#define ST01_V_RETRACE 0x08
|
||||
#define ST01_DISP_ENABLE 0x01
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user