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:
Michael S. Tsirkin 2018-01-11 22:01:17 +02:00
commit acc95bc850
579 changed files with 67167 additions and 9215 deletions

1
.gitignore vendored
View File

@ -53,7 +53,6 @@
/qemu-version.h.tmp /qemu-version.h.tmp
/module_block.h /module_block.h
/scsi/qemu-pr-helper /scsi/qemu-pr-helper
/vscclient
/vhost-user-scsi /vhost-user-scsi
/fsdev/virtfs-proxy-helper /fsdev/virtfs-proxy-helper
*.tmp *.tmp

View File

@ -259,6 +259,7 @@ S: Maintained
F: target/xtensa/ F: target/xtensa/
F: hw/xtensa/ F: hw/xtensa/
F: tests/tcg/xtensa/ F: tests/tcg/xtensa/
F: disas/xtensa.c
TriCore TriCore
M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de> M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
@ -543,7 +544,7 @@ F: include/hw/*/xlnx*.h
ARM ACPI Subsystem ARM ACPI Subsystem
M: Shannon Zhao <zhaoshenglong@huawei.com> 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 L: qemu-arm@nongnu.org
S: Maintained S: Maintained
F: hw/arm/virt-acpi-build.c 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/prep_systemio.c
F: hw/ppc/rs6000_mc.c F: hw/ppc/rs6000_mc.c
F: hw/pci-host/prep.[hc] F: hw/pci-host/prep.[hc]
F: hw/isa/i82378.c
F: hw/isa/pc87312.[hc] 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 F: pc-bios/ppc_rom.bin
sPAPR sPAPR
@ -761,6 +766,12 @@ L: qemu-ppc@nongnu.org
S: Odd Fixes S: Odd Fixes
F: hw/ppc/virtex_ml507.c 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 SH4 Machines
------------ ------------
R2D R2D
@ -861,12 +872,13 @@ F: hw/misc/sga.c
PC Chipset PC Chipset
M: Michael S. Tsirkin <mst@redhat.com> M: Michael S. Tsirkin <mst@redhat.com>
M: Paolo Bonzini <pbonzini@redhat.com> M: Paolo Bonzini <pbonzini@redhat.com>
S: Support S: Supported
F: hw/char/debugcon.c F: hw/char/debugcon.c
F: hw/char/parallel.c F: hw/char/parallel.c
F: hw/char/serial* F: hw/char/serial*
F: hw/dma/i8257* F: hw/dma/i8257*
F: hw/i2c/pm_smbus.c F: hw/i2c/pm_smbus.c
F: hw/input/pckbd.c
F: hw/intc/apic* F: hw/intc/apic*
F: hw/intc/ioapic* F: hw/intc/ioapic*
F: hw/intc/i8259* F: hw/intc/i8259*
@ -875,7 +887,10 @@ F: hw/misc/pc-testdev.c
F: hw/timer/hpet* F: hw/timer/hpet*
F: hw/timer/i8254* F: hw/timer/i8254*
F: hw/timer/mc146818rtc* 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/i2c/pm_smbus.h
F: include/hw/isa/i8257.h
F: include/hw/timer/hpet.h F: include/hw/timer/hpet.h
F: include/hw/timer/i8254* F: include/hw/timer/i8254*
F: include/hw/timer/mc146818rtc* F: include/hw/timer/mc146818rtc*
@ -976,7 +991,9 @@ M: Alexander Graf <agraf@suse.de>
L: qemu-ppc@nongnu.org L: qemu-ppc@nongnu.org
S: Odd Fixes S: Odd Fixes
F: hw/ppc/ppc4*.c F: hw/ppc/ppc4*.c
F: hw/i2c/ppc4xx_i2c.c
F: include/hw/ppc/ppc4xx.h F: include/hw/ppc/ppc4xx.h
F: include/hw/i2c/ppc4xx_i2c.h
ppce500 ppce500
M: Alexander Graf <agraf@suse.de> M: Alexander Graf <agraf@suse.de>
@ -995,11 +1012,13 @@ Network devices
M: Jason Wang <jasowang@redhat.com> M: Jason Wang <jasowang@redhat.com>
S: Odd Fixes S: Odd Fixes
F: hw/net/ F: hw/net/
F: include/hw/net/
F: tests/virtio-net-test.c F: tests/virtio-net-test.c
T: git git://github.com/jasowang/qemu.git net T: git git://github.com/jasowang/qemu.git net
SCSI SCSI
M: Paolo Bonzini <pbonzini@redhat.com> M: Paolo Bonzini <pbonzini@redhat.com>
R: Fam Zheng <famz@redhat.com>
S: Supported S: Supported
F: include/hw/scsi/* F: include/hw/scsi/*
F: hw/scsi/* F: hw/scsi/*
@ -1071,13 +1090,11 @@ F: include/hw/virtio/
F: tests/virtio-balloon-test.c F: tests/virtio-balloon-test.c
virtio-9p virtio-9p
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
M: Greg Kurz <groug@kaod.org> M: Greg Kurz <groug@kaod.org>
S: Supported S: Supported
F: hw/9pfs/ F: hw/9pfs/
F: fsdev/ F: fsdev/
F: tests/virtio-9p-test.c F: tests/virtio-9p-test.c
T: git git://github.com/kvaneesh/QEMU.git
T: git git://github.com/gkurz/qemu.git 9p-next T: git git://github.com/gkurz/qemu.git 9p-next
virtio-blk virtio-blk
@ -1260,6 +1277,7 @@ T: git git://github.com/stefanha/qemu.git block
Block SCSI subsystem Block SCSI subsystem
M: Paolo Bonzini <pbonzini@redhat.com> M: Paolo Bonzini <pbonzini@redhat.com>
R: Fam Zheng <famz@redhat.com>
L: qemu-block@nongnu.org L: qemu-block@nongnu.org
S: Supported S: Supported
F: include/scsi/* F: include/scsi/*
@ -1538,6 +1556,7 @@ M: Alistair Francis <alistair.francis@xilinx.com>
S: Maintained S: Maintained
F: hw/core/register.c F: hw/core/register.c
F: include/hw/register.h F: include/hw/register.h
F: include/hw/registerfields.h
SLIRP SLIRP
M: Samuel Thibault <samuel.thibault@ens-lyon.org> M: Samuel Thibault <samuel.thibault@ens-lyon.org>

View File

@ -6,7 +6,10 @@ BUILD_DIR=$(CURDIR)
# Before including a proper config-host.mak, assume we are in the source tree # Before including a proper config-host.mak, assume we are in the source tree
SRC_PATH=. 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 # All following code might depend on configuration variables
ifneq ($(wildcard config-host.mak),) ifneq ($(wildcard config-host.mak),)
@ -50,7 +53,7 @@ ifneq ($(realpath $(SRC_PATH)),$(realpath .))
ifneq ($(wildcard $(SRC_PATH)/config-host.mak),) ifneq ($(wildcard $(SRC_PATH)/config-host.mak),)
$(error This is an out of tree build but your source tree ($(SRC_PATH)) \ $(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 \ 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
endif endif
@ -229,6 +232,7 @@ KEYCODEMAP_FILES = \
ui/input-keymap-linux-to-qcode.c \ ui/input-keymap-linux-to-qcode.c \
ui/input-keymap-qcode-to-qnum.c \ ui/input-keymap-qcode-to-qnum.c \
ui/input-keymap-qnum-to-qcode.c \ ui/input-keymap-qnum-to-qcode.c \
ui/input-keymap-qcode-to-linux.c \
$(NULL) $(NULL)
GENERATED_FILES += $(KEYCODEMAP_FILES) GENERATED_FILES += $(KEYCODEMAP_FILES)
@ -303,7 +307,7 @@ endif
else \ else \
echo "WARNING: $@ out of date.";\ echo "WARNING: $@ out of date.";\
fi; \ fi; \
echo "Run \"make defconfig\" to regenerate."; \ echo "Run \"$(MAKE) defconfig\" to regenerate."; \
rm $@.tmp; \ rm $@.tmp; \
fi; \ fi; \
else \ else \
@ -933,4 +937,4 @@ ifdef QEMU_GA_MSI_ENABLED
endif endif
@echo '' @echo ''
endif 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'

View File

@ -140,6 +140,7 @@ trace-events-subdirs += hw/input
trace-events-subdirs += hw/timer trace-events-subdirs += hw/timer
trace-events-subdirs += hw/dma trace-events-subdirs += hw/dma
trace-events-subdirs += hw/sparc trace-events-subdirs += hw/sparc
trace-events-subdirs += hw/sparc64
trace-events-subdirs += hw/sd trace-events-subdirs += hw/sd
trace-events-subdirs += hw/isa trace-events-subdirs += hw/isa
trace-events-subdirs += hw/mem trace-events-subdirs += hw/mem

View File

@ -1 +1 @@
2.10.94 2.11.50

View File

@ -1,3 +1,4 @@
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o 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_KVM)) += kvm-stub.o
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o

31
accel/stubs/hvf-stub.c Normal file
View 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)
{
}

View File

@ -21,7 +21,6 @@
#include "cpu.h" #include "cpu.h"
#include "sysemu/cpus.h" #include "sysemu/cpus.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "exec/memory-internal.h"
bool tcg_allowed; bool tcg_allowed;

View File

@ -146,8 +146,10 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
uint8_t *tb_ptr = itb->tc.ptr; uint8_t *tb_ptr = itb->tc.ptr;
qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc, qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc,
"Trace %p [%d: " TARGET_FMT_lx "] %s\n", "Trace %d: %p ["
itb->tc.ptr, cpu->cpu_index, itb->pc, 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)); lookup_symbol(itb->pc));
#if defined(DEBUG_DISAS) #if defined(DEBUG_DISAS)
@ -525,19 +527,13 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
TranslationBlock **last_tb) TranslationBlock **last_tb)
{ {
CPUClass *cc = CPU_GET_CLASS(cpu); CPUClass *cc = CPU_GET_CLASS(cpu);
int32_t insns_left;
/* Clear the interrupt flag now since we're processing /* Clear the interrupt flag now since we're processing
* cpu->interrupt_request and cpu->exit_request. * 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_mb_set(&cpu->icount_decr.u16.high, 0);
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();
}
if (unlikely(atomic_read(&cpu->interrupt_request))) { if (unlikely(atomic_read(&cpu->interrupt_request))) {
int interrupt_request; int interrupt_request;

View File

@ -156,8 +156,9 @@ void *HELPER(lookup_tb_ptr)(CPUArchState *env)
return tcg_ctx->code_gen_epilogue; return tcg_ctx->code_gen_epilogue;
} }
qemu_log_mask_and_addr(CPU_LOG_EXEC, pc, qemu_log_mask_and_addr(CPU_LOG_EXEC, pc,
"Chain %p [%d: " TARGET_FMT_lx "] %s\n", "Chain %d: %p ["
tb->tc.ptr, cpu->cpu_index, pc, TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n",
cpu->cpu_index, tb->tc.ptr, cs_base, pc, flags,
lookup_symbol(pc)); lookup_symbol(pc));
return tb->tc.ptr; return tb->tc.ptr;
} }

View File

@ -31,7 +31,6 @@
#include "tcg.h" #include "tcg.h"
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
#include "qemu.h" #include "qemu.h"
#include "exec/exec-all.h"
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <sys/param.h> #include <sys/param.h>
#if __FreeBSD_version >= 700104 #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. /* Encode the data collected about the instructions while compiling TB.
Place the data at BLOCK, and return the number of bytes consumed. 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 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. which comes from the host pc of the end of the code implementing the insn.

View File

@ -17,16 +17,25 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "sysemu/tpm.h" #include "sysemu/tpm.h"
#include "hw/tpm/tpm_int.h"
#include "qemu/thread.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) static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
{ {
TPMBackend *s = TPM_BACKEND(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); k->handle_request(s, (TPMBackendCmd *)data);
qemu_bh_schedule(s->bh);
} }
static void tpm_backend_thread_end(TPMBackend *s) static void tpm_backend_thread_end(TPMBackend *s)
@ -44,15 +53,22 @@ enum TpmType tpm_backend_get_type(TPMBackend *s)
return k->type; 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; s->had_startup_error = false;
return 0; return 0;
} }
int tpm_backend_startup_tpm(TPMBackend *s) int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
{ {
int res = 0; int res = 0;
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); 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, s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
NULL); 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); s->had_startup_error = (res != 0);
@ -97,8 +113,6 @@ void tpm_backend_cancel_cmd(TPMBackend *s)
{ {
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
assert(k->cancel_cmd);
k->cancel_cmd(s); k->cancel_cmd(s);
} }
@ -122,80 +136,44 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
{ {
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
assert(k->get_tpm_version);
return k->get_tpm_version(s); 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 *tpm_backend_query_tpm(TPMBackend *s)
{ {
TPMInfo *info = g_new0(TPMInfo, 1); TPMInfo *info = g_new0(TPMInfo, 1);
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
info->id = g_strdup(s->id); info->id = g_strdup(s->id);
info->model = s->fe_model; info->model = tic->model;
if (k->get_tpm_options) { info->options = k->get_tpm_options(s);
info->options = k->get_tpm_options(s);
}
return info; 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) static void tpm_backend_instance_init(Object *obj)
{ {
TPMBackend *s = TPM_BACKEND(obj); TPMBackend *s = TPM_BACKEND(obj);
object_property_add_bool(obj, "opened", s->bh = qemu_bh_new(tpm_backend_request_completed_bh, s);
tpm_backend_prop_get_opened,
tpm_backend_prop_set_opened,
NULL);
s->fe_model = -1;
} }
static void tpm_backend_instance_finalize(Object *obj) static void tpm_backend_instance_finalize(Object *obj)
{ {
TPMBackend *s = TPM_BACKEND(obj); TPMBackend *s = TPM_BACKEND(obj);
object_unref(OBJECT(s->tpmif));
g_free(s->id); g_free(s->id);
tpm_backend_thread_end(s); tpm_backend_thread_end(s);
qemu_bh_delete(s->bh);
} }
static const TypeInfo tpm_backend_info = { static const TypeInfo tpm_backend_info = {

89
block.c
View File

@ -822,6 +822,18 @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
bdrv_drained_end(bs); 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) static int bdrv_child_cb_inactivate(BdrvChild *child)
{ {
BlockDriverState *bs = child->opaque; BlockDriverState *bs = child->opaque;
@ -889,6 +901,8 @@ const BdrvChildRole child_file = {
.inherit_options = bdrv_inherited_options, .inherit_options = bdrv_inherited_options,
.drained_begin = bdrv_child_cb_drained_begin, .drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end, .drained_end = bdrv_child_cb_drained_end,
.attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate, .inactivate = bdrv_child_cb_inactivate,
}; };
@ -911,6 +925,8 @@ const BdrvChildRole child_format = {
.inherit_options = bdrv_inherited_fmt_options, .inherit_options = bdrv_inherited_fmt_options,
.drained_begin = bdrv_child_cb_drained_begin, .drained_begin = bdrv_child_cb_drained_begin,
.drained_end = bdrv_child_cb_drained_end, .drained_end = bdrv_child_cb_drained_end,
.attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach,
.inactivate = bdrv_child_cb_inactivate, .inactivate = bdrv_child_cb_inactivate,
}; };
@ -953,6 +969,8 @@ static void bdrv_backing_attach(BdrvChild *c)
parent->backing_blocker); parent->backing_blocker);
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET, bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
parent->backing_blocker); parent->backing_blocker);
bdrv_child_cb_attach(c);
} }
static void bdrv_backing_detach(BdrvChild *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); bdrv_op_unblock_all(c->bs, parent->backing_blocker);
error_free(parent->backing_blocker); error_free(parent->backing_blocker);
parent->backing_blocker = NULL; 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); assert(role == &child_backing || role == &child_file);
if (!backing) { if (!backing) {
int flags = bdrv_reopen_get_flags(reopen_queue, bs);
/* Apart from the modifications below, the same permissions are /* Apart from the modifications below, the same permissions are
* forwarded and left alone as for filters */ * forwarded and left alone as for filters */
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared, 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 /* bs->file always needs to be consistent because of the metadata. We
* can never allow other users to resize or write to it. */ * 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); shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
} else { } else {
/* We want consistent read from backing files if the parent needs it. /* 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 *new_bs)
{ {
BlockDriverState *old_bs = child->bs; BlockDriverState *old_bs = child->bs;
int i;
if (old_bs && new_bs) { if (old_bs && new_bs) {
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
} }
if (old_bs) { if (old_bs) {
if (old_bs->quiesce_counter && child->role->drained_end) { /* Detach first so that the recursive drain sections coming from @child
child->role->drained_end(child); * are already gone and we only end the drain sections that came from
} * elsewhere. */
if (child->role->detach) { if (child->role->detach) {
child->role->detach(child); 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); QLIST_REMOVE(child, next_parent);
} }
@ -1987,9 +2017,14 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
if (new_bs) { if (new_bs) {
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
if (new_bs->quiesce_counter && child->role->drained_begin) { 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) { if (child->role->attach) {
child->role->attach(child); 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 * returns a pointer to bs_queue, which is either the newly allocated
* bs_queue, or the existing bs_queue being used. * 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, static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
BlockDriverState *bs, BlockDriverState *bs,
@ -2746,6 +2782,11 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
BdrvChild *child; BdrvChild *child;
QDict *old_options, *explicit_options; 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) { if (bs_queue == NULL) {
bs_queue = g_new0(BlockReopenQueue, 1); bs_queue = g_new0(BlockReopenQueue, 1);
QSIMPLEQ_INIT(bs_queue); 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 * If all devices prepare successfully, then the changes are committed
* to all devices. * 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) 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); assert(bs_queue != NULL);
aio_context_release(ctx);
bdrv_drain_all_begin();
aio_context_acquire(ctx);
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { 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)) { if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
goto cleanup; goto cleanup;
@ -2912,8 +2952,6 @@ cleanup:
} }
g_free(bs_queue); g_free(bs_queue);
bdrv_drain_all_end();
return ret; return ret;
} }
@ -2923,12 +2961,18 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
{ {
int ret = -1; int ret = -1;
Error *local_err = NULL; 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); ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
} }
bdrv_subtree_drained_end(bs);
return ret; return ret;
} }
@ -4320,9 +4364,15 @@ int bdrv_inactivate_all(void)
BdrvNextIterator it; BdrvNextIterator it;
int ret = 0; int ret = 0;
int pass; int pass;
GSList *aio_ctxs = NULL, *ctx;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { 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' /* We do two passes of inactivation. The first pass calls to drivers'
@ -4340,9 +4390,11 @@ int bdrv_inactivate_all(void)
} }
out: out:
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
aio_context_release(bdrv_get_aio_context(bs)); AioContext *aio_context = ctx->data;
aio_context_release(aio_context);
} }
g_slist_free(aio_ctxs);
return ret; return ret;
} }
@ -4593,10 +4645,11 @@ void bdrv_img_create(const char *filename, const char *fmt,
back_flags = flags; back_flags = flags;
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING); back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
backing_options = qdict_new();
if (backing_fmt) { if (backing_fmt) {
backing_options = qdict_new();
qdict_put_str(backing_options, "driver", backing_fmt); 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, bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
&local_err); &local_err);
@ -4746,7 +4799,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
AioContext *ctx = bdrv_get_aio_context(bs); AioContext *ctx = bdrv_get_aio_context(bs);
aio_disable_external(ctx); 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 */ bdrv_drain(bs); /* ensure there are no in-flight requests */
while (aio_poll(ctx, false)) { while (aio_poll(ctx, false)) {
@ -4760,7 +4813,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
*/ */
aio_context_acquire(new_context); aio_context_acquire(new_context);
bdrv_attach_aio_context(bs, 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_enable_external(ctx);
aio_context_release(new_context); aio_context_release(new_context);
} }

View File

@ -40,11 +40,12 @@ typedef struct BackupBlockJob {
BlockdevOnError on_target_error; BlockdevOnError on_target_error;
CoRwlock flush_rwlock; CoRwlock flush_rwlock;
uint64_t bytes_read; uint64_t bytes_read;
unsigned long *done_bitmap;
int64_t cluster_size; int64_t cluster_size;
bool compress; bool compress;
NotifierWithReturn before_write; NotifierWithReturn before_write;
QLIST_HEAD(, CowRequest) inflight_reqs; QLIST_HEAD(, CowRequest) inflight_reqs;
HBitmap *copy_bitmap;
} BackupBlockJob; } BackupBlockJob;
/* See if in-flight requests overlap and wait for them to complete */ /* 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); cow_request_begin(&cow_request, job, start, end);
for (; start < end; start += job->cluster_size) { 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); trace_backup_do_cow_skip(job, start);
continue; /* already copied */ continue; /* already copied */
} }
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
trace_backup_do_cow_process(job, start); trace_backup_do_cow_process(job, start);
@ -132,6 +134,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
if (error_is_read) { if (error_is_read) {
*error_is_read = true; *error_is_read = true;
} }
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
goto out; goto out;
} }
@ -148,11 +151,10 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
if (error_is_read) { if (error_is_read) {
*error_is_read = false; *error_is_read = false;
} }
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
goto out; goto out;
} }
set_bit(start / job->cluster_size, job->done_bitmap);
/* Publish progress, guest I/O counts as progress too. Note that the /* 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. * 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); 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, 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) static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
{ {
int ret;
bool error_is_read; bool error_is_read;
int ret = 0;
int clusters_per_iter;
uint32_t granularity;
int64_t offset;
int64_t cluster; int64_t cluster;
int64_t end; HBitmapIter hbi;
int64_t last_cluster = -1;
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; 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); 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) */ offset += bdrv_dirty_bitmap_granularity(job->sync_bitmap);
while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) { if (offset >= bdrv_dirty_bitmap_size(job->sync_bitmap)) {
cluster = offset / job->cluster_size; hbitmap_set(job->copy_bitmap, cluster, end - cluster);
break;
/* Fake progress updates for any clusters we skipped */
if (cluster != last_cluster + 1) {
job->common.offset += ((cluster - last_cluster - 1) *
job->cluster_size);
} }
for (end = cluster + clusters_per_iter; cluster < end; cluster++) { offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
do { if (offset == -1) {
if (yield_and_check(job)) { hbitmap_set(job->copy_bitmap, cluster, end - cluster);
goto out; break;
}
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);
} }
/* If the bitmap granularity is smaller than the backup granularity, next_cluster = DIV_ROUND_UP(offset, job->cluster_size);
* we need to advance the iterator pointer to the next cluster. */ hbitmap_set(job->copy_bitmap, cluster, next_cluster - cluster);
if (granularity < job->cluster_size) { if (next_cluster >= end) {
bdrv_set_dirty_iter(dbi, cluster * job->cluster_size); break;
} }
last_cluster = cluster - 1; bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
} }
/* Play some final catchup with the progress meter */ job->common.offset = job->common.len -
end = DIV_ROUND_UP(job->common.len, job->cluster_size); hbitmap_count(job->copy_bitmap) * job->cluster_size;
if (last_cluster + 1 < end) {
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
}
out:
bdrv_dirty_iter_free(dbi); bdrv_dirty_iter_free(dbi);
return ret;
} }
static void coroutine_fn backup_run(void *opaque) static void coroutine_fn backup_run(void *opaque)
@ -425,19 +431,27 @@ static void coroutine_fn backup_run(void *opaque)
BackupBlockJob *job = opaque; BackupBlockJob *job = opaque;
BackupCompleteData *data; BackupCompleteData *data;
BlockDriverState *bs = blk_bs(job->common.blk); BlockDriverState *bs = blk_bs(job->common.blk);
int64_t offset; int64_t offset, nb_clusters;
int ret = 0; int ret = 0;
QLIST_INIT(&job->inflight_reqs); QLIST_INIT(&job->inflight_reqs);
qemu_co_rwlock_init(&job->flush_rwlock); qemu_co_rwlock_init(&job->flush_rwlock);
job->done_bitmap = bitmap_new(DIV_ROUND_UP(job->common.len, nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
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; job->before_write.notify = backup_before_write_notify;
bdrv_add_before_write_notifier(bs, &job->before_write); bdrv_add_before_write_notifier(bs, &job->before_write);
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { 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)) { while (!block_job_is_cancelled(&job->common)) {
/* Yield until the job is cancelled. We just let our before_write /* Yield until the job is cancelled. We just let our before_write
* notify callback service CoW requests. */ * 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 */ /* wait until pending backup_do_cow() calls have completed */
qemu_co_rwlock_wrlock(&job->flush_rwlock); qemu_co_rwlock_wrlock(&job->flush_rwlock);
qemu_co_rwlock_unlock(&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 = g_malloc(sizeof(*data));
data->ret = ret; data->ret = ret;

View File

@ -277,7 +277,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
const char *filter_node_name, Error **errp) const char *filter_node_name, Error **errp)
{ {
CommitBlockJob *s; CommitBlockJob *s;
BlockReopenQueue *reopen_queue = NULL;
int orig_base_flags; int orig_base_flags;
BlockDriverState *iter; BlockDriverState *iter;
BlockDriverState *commit_top_bs = NULL; 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 */ /* convert base to r/w, if necessary */
orig_base_flags = bdrv_get_flags(base); orig_base_flags = bdrv_get_flags(base);
if (!(orig_base_flags & BDRV_O_RDWR)) { if (!(orig_base_flags & BDRV_O_RDWR)) {
reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL, bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
orig_base_flags | BDRV_O_RDWR);
}
if (reopen_queue) {
bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
goto fail; goto fail;

View File

@ -89,6 +89,8 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
struct BDRVCURLState; struct BDRVCURLState;
static bool libcurl_initialized;
typedef struct CURLAIOCB { typedef struct CURLAIOCB {
Coroutine *co; Coroutine *co;
QEMUIOVector *qiov; QEMUIOVector *qiov;
@ -686,14 +688,23 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
double d; double d;
const char *secretid; const char *secretid;
const char *protocol_delimiter; const char *protocol_delimiter;
int ret;
static int inited = 0;
if (flags & BDRV_O_RDWR) { if (flags & BDRV_O_RDWR) {
error_setg(errp, "curl block device does not support writes"); error_setg(errp, "curl block device does not support writes");
return -EROFS; 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); qemu_mutex_init(&s->mutex);
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err); 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); DPRINTF("CURL: Opening %s\n", file);
QSIMPLEQ_INIT(&s->free_state_waitq); QSIMPLEQ_INIT(&s->free_state_waitq);
s->aio_context = bdrv_get_aio_context(bs); s->aio_context = bdrv_get_aio_context(bs);
@ -851,6 +857,9 @@ out_noclean:
qemu_mutex_destroy(&s->mutex); qemu_mutex_destroy(&s->mutex);
g_free(s->cookie); g_free(s->cookie);
g_free(s->url); g_free(s->url);
g_free(s->username);
g_free(s->proxyusername);
g_free(s->proxypassword);
qemu_opts_del(opts); qemu_opts_del(opts);
return -EINVAL; return -EINVAL;
} }
@ -949,6 +958,9 @@ static void curl_close(BlockDriverState *bs)
g_free(s->cookie); g_free(s->cookie);
g_free(s->url); g_free(s->url);
g_free(s->username);
g_free(s->proxyusername);
g_free(s->proxypassword);
} }
static int64_t curl_getlength(BlockDriverState *bs) static int64_t curl_getlength(BlockDriverState *bs)

View File

@ -715,3 +715,8 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
{ {
return hbitmap_sha256(bitmap->bitmap, 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);
}

View File

@ -26,7 +26,6 @@
#ifndef BLOCK_DMG_H #ifndef BLOCK_DMG_H
#define BLOCK_DMG_H #define BLOCK_DMG_H
#include "qemu/osdep.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "block/block_int.h" #include "block/block_int.h"
#include <zlib.h> #include <zlib.h>

View File

@ -40,22 +40,28 @@
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int bytes, BdrvRequestFlags flags); 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; BdrvChild *c, *next;
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
if (c == ignore) {
continue;
}
if (c->role->drained_begin) { if (c->role->drained_begin) {
c->role->drained_begin(c); c->role->drained_begin(c);
} }
} }
} }
void bdrv_parent_drained_end(BlockDriverState *bs) void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
{ {
BdrvChild *c, *next; BdrvChild *c, *next;
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) { QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
if (c == ignore) {
continue;
}
if (c->role->drained_end) { if (c->role->drained_end) {
c->role->drained_end(c); c->role->drained_end(c);
} }
@ -134,29 +140,13 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
assert(old >= 1); 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 { typedef struct {
Coroutine *co; Coroutine *co;
BlockDriverState *bs; BlockDriverState *bs;
bool done; bool done;
bool begin; bool begin;
bool recursive;
BdrvChild *parent;
} BdrvCoDrainData; } BdrvCoDrainData;
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) 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); 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}; BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_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); data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
bdrv_coroutine_enter(bs, data.co); bdrv_coroutine_enter(bs, data.co);
BDRV_POLL_WHILE(bs, !data.done); 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; BdrvChild *child, *tmp;
bool waited; bool waited;
/* Ensure any pending metadata writes are submitted to bs->file. */
bdrv_drain_invoke(bs, begin);
/* Wait for drained requests to finish */ /* Wait for drained requests to finish */
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); 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); bdrv_ref(bs);
} }
waited |= bdrv_drain_recurse(bs, begin); waited |= bdrv_drain_recurse(bs);
if (in_main_loop) { if (in_main_loop) {
bdrv_unref(bs); bdrv_unref(bs);
} }
@ -224,6 +219,11 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
return waited; 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) static void bdrv_co_drain_bh_cb(void *opaque)
{ {
BdrvCoDrainData *data = opaque; BdrvCoDrainData *data = opaque;
@ -232,9 +232,9 @@ static void bdrv_co_drain_bh_cb(void *opaque)
bdrv_dec_in_flight(bs); bdrv_dec_in_flight(bs);
if (data->begin) { if (data->begin) {
bdrv_drained_begin(bs); bdrv_do_drained_begin(bs, data->recursive, data->parent);
} else { } else {
bdrv_drained_end(bs); bdrv_do_drained_end(bs, data->recursive, data->parent);
} }
data->done = true; 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, static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
bool begin) bool begin, bool recursive,
BdrvChild *parent)
{ {
BdrvCoDrainData data; BdrvCoDrainData data;
@ -256,6 +257,8 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
.bs = bs, .bs = bs,
.done = false, .done = false,
.begin = begin, .begin = begin,
.recursive = recursive,
.parent = parent,
}; };
bdrv_inc_in_flight(bs); bdrv_inc_in_flight(bs);
aio_bh_schedule_oneshot(bdrv_get_aio_context(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); 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()) { if (qemu_in_coroutine()) {
bdrv_co_yield_to_drain(bs, true); bdrv_co_yield_to_drain(bs, true, recursive, parent);
return; return;
} }
/* Stop things in parent-to-child order */
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) { if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
aio_disable_external(bdrv_get_aio_context(bs)); 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) void bdrv_drained_end(BlockDriverState *bs)
{ {
if (qemu_in_coroutine()) { bdrv_do_drained_end(bs, false, NULL);
bdrv_co_yield_to_drain(bs, false); }
return;
}
assert(bs->quiesce_counter > 0);
if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
return;
}
bdrv_parent_drained_end(bs); void bdrv_subtree_drained_end(BlockDriverState *bs)
bdrv_drain_recurse(bs, false); {
aio_enable_external(bdrv_get_aio_context(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; BdrvNextIterator it;
GSList *aio_ctxs = NULL, *ctx; 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)) { for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs); AioContext *aio_context = bdrv_get_aio_context(bs);
/* Stop things in parent-to-child order */
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
bdrv_parent_drained_begin(bs);
aio_disable_external(aio_context); aio_disable_external(aio_context);
bdrv_parent_drained_begin(bs, NULL);
bdrv_drain_invoke(bs, true, true);
aio_context_release(aio_context); aio_context_release(aio_context);
if (!g_slist_find(aio_ctxs, 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); aio_context_acquire(aio_context);
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (aio_context == bdrv_get_aio_context(bs)) { if (aio_context == bdrv_get_aio_context(bs)) {
waited |= bdrv_drain_recurse(bs, true); waited |= bdrv_drain_recurse(bs);
} }
} }
aio_context_release(aio_context); 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)) { for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs); AioContext *aio_context = bdrv_get_aio_context(bs);
/* Re-enable things in child-to-parent order */
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
bdrv_drain_invoke(bs, false, true);
bdrv_parent_drained_end(bs, NULL);
aio_enable_external(aio_context); aio_enable_external(aio_context);
bdrv_parent_drained_end(bs);
bdrv_drain_recurse(bs, false);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
block_job_resume_all();
} }
void bdrv_drain_all(void) void bdrv_drain_all(void)

View File

@ -2,7 +2,7 @@
* QEMU Block driver for iSCSI images * QEMU Block driver for iSCSI images
* *
* Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com> * 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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -104,6 +104,7 @@ typedef struct IscsiTask {
IscsiLun *iscsilun; IscsiLun *iscsilun;
QEMUTimer retry_timer; QEMUTimer retry_timer;
int err_code; int err_code;
char *err_str;
} IscsiTask; } IscsiTask;
typedef struct IscsiAIOCB { 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); 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: out:
@ -629,6 +630,8 @@ retry:
if (iTask.status != SCSI_STATUS_GOOD) { if (iTask.status != SCSI_STATUS_GOOD) {
iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors); 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; r = iTask.err_code;
goto out_unlock; goto out_unlock;
} }
@ -637,6 +640,7 @@ retry:
out_unlock: out_unlock:
qemu_mutex_unlock(&iscsilun->mutex); qemu_mutex_unlock(&iscsilun->mutex);
g_free(iTask.err_str);
return r; 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_get_lba_status *lbas = NULL;
struct scsi_lba_status_descriptor *lbasd = NULL; struct scsi_lba_status_descriptor *lbasd = NULL;
struct IscsiTask iTask; struct IscsiTask iTask;
uint64_t lba;
int64_t ret; int64_t ret;
iscsi_co_init_iscsitask(iscsilun, &iTask);
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
@ -670,11 +673,13 @@ static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
goto out; goto out;
} }
lba = sector_qemu2lun(sector_num, iscsilun);
iscsi_co_init_iscsitask(iscsilun, &iTask);
qemu_mutex_lock(&iscsilun->mutex); qemu_mutex_lock(&iscsilun->mutex);
retry: retry:
if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun, if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
sector_qemu2lun(sector_num, iscsilun), lba, 8 + 16, iscsi_co_generic_cb,
8 + 16, iscsi_co_generic_cb,
&iTask) == NULL) { &iTask) == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto out_unlock; goto out_unlock;
@ -701,6 +706,8 @@ retry:
* because the device is busy or the cmd is not * because the device is busy or the cmd is not
* supported) we pretend all blocks are allocated * supported) we pretend all blocks are allocated
* for backwards compatibility */ * for backwards compatibility */
error_report("iSCSI GET_LBA_STATUS failed at lba %" PRIu64 ": %s",
lba, iTask.err_str);
goto out_unlock; goto out_unlock;
} }
@ -738,6 +745,7 @@ retry:
} }
out_unlock: out_unlock:
qemu_mutex_unlock(&iscsilun->mutex); qemu_mutex_unlock(&iscsilun->mutex);
g_free(iTask.err_str);
out: out:
if (iTask.task != NULL) { if (iTask.task != NULL) {
scsi_free_scsi_task(iTask.task); scsi_free_scsi_task(iTask.task);
@ -756,6 +764,7 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
struct IscsiTask iTask; struct IscsiTask iTask;
uint64_t lba; uint64_t lba;
uint32_t num_sectors; uint32_t num_sectors;
int r = 0;
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) { if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
return -EINVAL; return -EINVAL;
@ -853,19 +862,23 @@ retry:
iTask.complete = 0; iTask.complete = 0;
goto retry; goto retry;
} }
qemu_mutex_unlock(&iscsilun->mutex);
if (iTask.status != SCSI_STATUS_GOOD) { 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) static int coroutine_fn iscsi_co_flush(BlockDriverState *bs)
{ {
IscsiLun *iscsilun = bs->opaque; IscsiLun *iscsilun = bs->opaque;
struct IscsiTask iTask; struct IscsiTask iTask;
int r = 0;
iscsi_co_init_iscsitask(iscsilun, &iTask); iscsi_co_init_iscsitask(iscsilun, &iTask);
qemu_mutex_lock(&iscsilun->mutex); qemu_mutex_lock(&iscsilun->mutex);
@ -892,13 +905,15 @@ retry:
iTask.complete = 0; iTask.complete = 0;
goto retry; goto retry;
} }
qemu_mutex_unlock(&iscsilun->mutex);
if (iTask.status != SCSI_STATUS_GOOD) { 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__ #ifdef __linux__
@ -1128,6 +1143,9 @@ retry:
goto retry; goto retry;
} }
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
bytes >> BDRV_SECTOR_BITS);
if (iTask.status == SCSI_STATUS_CHECK_CONDITION) { if (iTask.status == SCSI_STATUS_CHECK_CONDITION) {
/* the target might fail with a check condition if it /* the target might fail with a check condition if it
is not happy with the alignment of the UNMAP request is not happy with the alignment of the UNMAP request
@ -1136,15 +1154,15 @@ retry:
} }
if (iTask.status != SCSI_STATUS_GOOD) { if (iTask.status != SCSI_STATUS_GOOD) {
error_report("iSCSI UNMAP failed at lba %" PRIu64 ": %s",
list.lba, iTask.err_str);
r = iTask.err_code; r = iTask.err_code;
goto out_unlock; goto out_unlock;
} }
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
bytes >> BDRV_SECTOR_BITS);
out_unlock: out_unlock:
qemu_mutex_unlock(&iscsilun->mutex); qemu_mutex_unlock(&iscsilun->mutex);
g_free(iTask.err_str);
return r; return r;
} }
@ -1241,6 +1259,8 @@ retry:
if (iTask.status != SCSI_STATUS_GOOD) { if (iTask.status != SCSI_STATUS_GOOD) {
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS, iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
bytes >> 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; r = iTask.err_code;
goto out_unlock; goto out_unlock;
} }
@ -1255,6 +1275,7 @@ retry:
out_unlock: out_unlock:
qemu_mutex_unlock(&iscsilun->mutex); qemu_mutex_unlock(&iscsilun->mutex);
g_free(iTask.err_str);
return r; return r;
} }

View File

@ -388,6 +388,7 @@ static QemuOptsList nbd_runtime_opts = {
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "ID of the TLS credentials to use", .help = "ID of the TLS credentials to use",
}, },
{ /* end of list */ }
}, },
}; };

View File

@ -110,8 +110,7 @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
BDRVNullState *s = bs->opaque; BDRVNullState *s = bs->opaque;
if (s->latency_ns) { if (s->latency_ns) {
co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME, qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, s->latency_ns);
s->latency_ns);
} }
return 0; return 0;
} }

View File

@ -1672,34 +1672,12 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
return status; 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, static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov, uint64_t bytes, QEMUIOVector *qiov,
int flags) int flags)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int offset_in_cluster, n1; int offset_in_cluster;
int ret; int ret;
unsigned int cur_bytes; /* number of bytes in current iteration */ unsigned int cur_bytes; /* number of bytes in current iteration */
uint64_t cluster_offset = 0; 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: case QCOW2_CLUSTER_UNALLOCATED:
if (bs->backing) { if (bs->backing) {
/* read from the base image */ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov, qemu_co_mutex_unlock(&s->lock);
offset, cur_bytes); ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
if (n1 > 0) { &hd_qiov, 0);
QEMUIOVector local_qiov; qemu_co_mutex_lock(&s->lock);
if (ret < 0) {
qemu_iovec_init(&local_qiov, hd_qiov.niov); goto fail;
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;
}
} }
} else { } else {
/* Note: in this case, no need to wait */ /* Note: in this case, no need to wait */

View File

@ -528,9 +528,6 @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
} }
/* qcow2.c functions */ /* 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, int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
int refcount_order, bool generous_increase, int refcount_order, bool generous_increase,
uint64_t *refblock_count); uint64_t *refblock_count);

View File

@ -394,6 +394,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
new_secondary_flags = s->orig_secondary_flags; 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) { if (orig_hidden_flags != new_hidden_flags) {
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL, reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
new_hidden_flags); new_hidden_flags);
@ -409,6 +412,9 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
reopen_queue, &local_err); reopen_queue, &local_err);
error_propagate(errp, 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) static void backup_job_cleanup(BlockDriverState *bs)

View File

@ -400,7 +400,7 @@ typedef struct BDRVSheepdogReopenState {
int cache_flags; int cache_flags;
} BDRVSheepdogReopenState; } BDRVSheepdogReopenState;
static const char * sd_strerror(int err) static const char *sd_strerror(int err)
{ {
int i; int i;
@ -776,8 +776,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
if (s->fd < 0) { if (s->fd < 0) {
DPRINTF("Wait for connection to be established\n"); DPRINTF("Wait for connection to be established\n");
error_report_err(local_err); error_report_err(local_err);
co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME, qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL);
1000000000ULL);
} }
}; };
@ -1632,7 +1631,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags,
if (!tag) { if (!tag) {
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"); error_setg(errp, "value of parameter 'tag' is too long");
ret = -EINVAL; ret = -EINVAL;
goto err_no_fd; goto err_no_fd;
@ -3078,111 +3077,111 @@ static QemuOptsList sd_create_opts = {
}; };
static BlockDriver bdrv_sheepdog = { static BlockDriver bdrv_sheepdog = {
.format_name = "sheepdog", .format_name = "sheepdog",
.protocol_name = "sheepdog", .protocol_name = "sheepdog",
.instance_size = sizeof(BDRVSheepdogState), .instance_size = sizeof(BDRVSheepdogState),
.bdrv_parse_filename = sd_parse_filename, .bdrv_parse_filename = sd_parse_filename,
.bdrv_file_open = sd_open, .bdrv_file_open = sd_open,
.bdrv_reopen_prepare = sd_reopen_prepare, .bdrv_reopen_prepare = sd_reopen_prepare,
.bdrv_reopen_commit = sd_reopen_commit, .bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort, .bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close, .bdrv_close = sd_close,
.bdrv_create = sd_create, .bdrv_create = sd_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength, .bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size, .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_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev, .bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk, .bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_pdiscard = sd_co_pdiscard, .bdrv_co_pdiscard = sd_co_pdiscard,
.bdrv_co_get_block_status = sd_co_get_block_status, .bdrv_co_get_block_status = sd_co_get_block_status,
.bdrv_snapshot_create = sd_snapshot_create, .bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto, .bdrv_snapshot_goto = sd_snapshot_goto,
.bdrv_snapshot_delete = sd_snapshot_delete, .bdrv_snapshot_delete = sd_snapshot_delete,
.bdrv_snapshot_list = sd_snapshot_list, .bdrv_snapshot_list = sd_snapshot_list,
.bdrv_save_vmstate = sd_save_vmstate, .bdrv_save_vmstate = sd_save_vmstate,
.bdrv_load_vmstate = sd_load_vmstate, .bdrv_load_vmstate = sd_load_vmstate,
.bdrv_detach_aio_context = sd_detach_aio_context, .bdrv_detach_aio_context = sd_detach_aio_context,
.bdrv_attach_aio_context = sd_attach_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 = { static BlockDriver bdrv_sheepdog_tcp = {
.format_name = "sheepdog", .format_name = "sheepdog",
.protocol_name = "sheepdog+tcp", .protocol_name = "sheepdog+tcp",
.instance_size = sizeof(BDRVSheepdogState), .instance_size = sizeof(BDRVSheepdogState),
.bdrv_parse_filename = sd_parse_filename, .bdrv_parse_filename = sd_parse_filename,
.bdrv_file_open = sd_open, .bdrv_file_open = sd_open,
.bdrv_reopen_prepare = sd_reopen_prepare, .bdrv_reopen_prepare = sd_reopen_prepare,
.bdrv_reopen_commit = sd_reopen_commit, .bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort, .bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close, .bdrv_close = sd_close,
.bdrv_create = sd_create, .bdrv_create = sd_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength, .bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size, .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_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev, .bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk, .bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_pdiscard = sd_co_pdiscard, .bdrv_co_pdiscard = sd_co_pdiscard,
.bdrv_co_get_block_status = sd_co_get_block_status, .bdrv_co_get_block_status = sd_co_get_block_status,
.bdrv_snapshot_create = sd_snapshot_create, .bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto, .bdrv_snapshot_goto = sd_snapshot_goto,
.bdrv_snapshot_delete = sd_snapshot_delete, .bdrv_snapshot_delete = sd_snapshot_delete,
.bdrv_snapshot_list = sd_snapshot_list, .bdrv_snapshot_list = sd_snapshot_list,
.bdrv_save_vmstate = sd_save_vmstate, .bdrv_save_vmstate = sd_save_vmstate,
.bdrv_load_vmstate = sd_load_vmstate, .bdrv_load_vmstate = sd_load_vmstate,
.bdrv_detach_aio_context = sd_detach_aio_context, .bdrv_detach_aio_context = sd_detach_aio_context,
.bdrv_attach_aio_context = sd_attach_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 = { static BlockDriver bdrv_sheepdog_unix = {
.format_name = "sheepdog", .format_name = "sheepdog",
.protocol_name = "sheepdog+unix", .protocol_name = "sheepdog+unix",
.instance_size = sizeof(BDRVSheepdogState), .instance_size = sizeof(BDRVSheepdogState),
.bdrv_parse_filename = sd_parse_filename, .bdrv_parse_filename = sd_parse_filename,
.bdrv_file_open = sd_open, .bdrv_file_open = sd_open,
.bdrv_reopen_prepare = sd_reopen_prepare, .bdrv_reopen_prepare = sd_reopen_prepare,
.bdrv_reopen_commit = sd_reopen_commit, .bdrv_reopen_commit = sd_reopen_commit,
.bdrv_reopen_abort = sd_reopen_abort, .bdrv_reopen_abort = sd_reopen_abort,
.bdrv_close = sd_close, .bdrv_close = sd_close,
.bdrv_create = sd_create, .bdrv_create = sd_create,
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength, .bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size, .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_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev, .bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk, .bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_co_pdiscard = sd_co_pdiscard, .bdrv_co_pdiscard = sd_co_pdiscard,
.bdrv_co_get_block_status = sd_co_get_block_status, .bdrv_co_get_block_status = sd_co_get_block_status,
.bdrv_snapshot_create = sd_snapshot_create, .bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto, .bdrv_snapshot_goto = sd_snapshot_goto,
.bdrv_snapshot_delete = sd_snapshot_delete, .bdrv_snapshot_delete = sd_snapshot_delete,
.bdrv_snapshot_list = sd_snapshot_list, .bdrv_snapshot_list = sd_snapshot_list,
.bdrv_save_vmstate = sd_save_vmstate, .bdrv_save_vmstate = sd_save_vmstate,
.bdrv_load_vmstate = sd_load_vmstate, .bdrv_load_vmstate = sd_load_vmstate,
.bdrv_detach_aio_context = sd_detach_aio_context, .bdrv_detach_aio_context = sd_detach_aio_context,
.bdrv_attach_aio_context = sd_attach_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) static void bdrv_sheepdog_init(void)

View File

@ -18,10 +18,10 @@
#include "qmp-commands.h" #include "qmp-commands.h"
#include "block/nbd.h" #include "block/nbd.h"
#include "io/channel-socket.h" #include "io/channel-socket.h"
#include "io/net-listener.h"
typedef struct NBDServerData { typedef struct NBDServerData {
QIOChannelSocket *listen_ioc; QIONetListener *listener;
int watch;
QCryptoTLSCreds *tlscreds; QCryptoTLSCreds *tlscreds;
} NBDServerData; } NBDServerData;
@ -32,27 +32,13 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
nbd_client_put(client); nbd_client_put(client);
} }
static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition, static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
gpointer opaque) 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"); qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
nbd_client_new(NULL, cioc, nbd_client_new(NULL, cioc,
nbd_server->tlscreds, NULL, nbd_server->tlscreds, NULL,
nbd_blockdev_client_closed); nbd_blockdev_client_closed);
object_unref(OBJECT(cioc));
return TRUE;
} }
@ -62,10 +48,8 @@ static void nbd_server_free(NBDServerData *server)
return; return;
} }
if (server->watch != -1) { qio_net_listener_disconnect(server->listener);
g_source_remove(server->watch); object_unref(OBJECT(server->listener));
}
object_unref(OBJECT(server->listen_ioc));
if (server->tlscreds) { if (server->tlscreds) {
object_unref(OBJECT(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 = g_new0(NBDServerData, 1);
nbd_server->watch = -1; nbd_server->listener = qio_net_listener_new();
nbd_server->listen_ioc = qio_channel_socket_new();
qio_channel_set_name(QIO_CHANNEL(nbd_server->listen_ioc), qio_net_listener_set_name(nbd_server->listener,
"nbd-listener"); "nbd-listener");
if (qio_channel_socket_listen_sync(
nbd_server->listen_ioc, addr, errp) < 0) { if (qio_net_listener_open_sync(nbd_server->listener, addr, errp) < 0) {
goto error; goto error;
} }
@ -134,12 +118,10 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
} }
} }
nbd_server->watch = qio_channel_add_watch( qio_net_listener_set_client_func(nbd_server->listener,
QIO_CHANNEL(nbd_server->listen_ioc), nbd_accept,
G_IO_IN, NULL,
nbd_accept, NULL);
NULL,
NULL);
return; return;

View File

@ -45,6 +45,7 @@
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qapi/qobject-output-visitor.h" #include "qapi/qobject-output-visitor.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "sysemu/iothread.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qmp-commands.h" #include "qmp-commands.h"
#include "block/trace.h" #include "block/trace.h"
@ -733,10 +734,6 @@ QemuOptsList qemu_legacy_drive_opts = {
.name = "trans", .name = "trans",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "chs translation (auto, lba, none)", .help = "chs translation (auto, lba, none)",
},{
.name = "boot",
.type = QEMU_OPT_BOOL,
.help = "(deprecated, ignored)",
},{ },{
.name = "addr", .name = "addr",
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
@ -872,13 +869,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
goto fail; 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 */ /* Other deprecated options */
if (!qtest_enabled()) { if (!qtest_enabled()) {
for (i = 0; i < ARRAY_SIZE(deprecated); i++) { for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
@ -1454,7 +1444,6 @@ struct BlkActionState {
typedef struct InternalSnapshotState { typedef struct InternalSnapshotState {
BlkActionState common; BlkActionState common;
BlockDriverState *bs; BlockDriverState *bs;
AioContext *aio_context;
QEMUSnapshotInfo sn; QEMUSnapshotInfo sn;
bool created; bool created;
} InternalSnapshotState; } InternalSnapshotState;
@ -1485,6 +1474,7 @@ static void internal_snapshot_prepare(BlkActionState *common,
qemu_timeval tv; qemu_timeval tv;
BlockdevSnapshotInternal *internal; BlockdevSnapshotInternal *internal;
InternalSnapshotState *state; InternalSnapshotState *state;
AioContext *aio_context;
int ret1; int ret1;
g_assert(common->action->type == g_assert(common->action->type ==
@ -1506,32 +1496,33 @@ static void internal_snapshot_prepare(BlkActionState *common,
return; return;
} }
/* AioContext is released in .clean() */ aio_context = bdrv_get_aio_context(bs);
state->aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context);
aio_context_acquire(state->aio_context);
state->bs = bs; state->bs = bs;
/* Paired with .clean() */
bdrv_drained_begin(bs); bdrv_drained_begin(bs);
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) {
return; goto out;
} }
if (bdrv_is_read_only(bs)) { if (bdrv_is_read_only(bs)) {
error_setg(errp, "Device '%s' is read only", device); error_setg(errp, "Device '%s' is read only", device);
return; goto out;
} }
if (!bdrv_can_snapshot(bs)) { if (!bdrv_can_snapshot(bs)) {
error_setg(errp, "Block format '%s' used by device '%s' " error_setg(errp, "Block format '%s' used by device '%s' "
"does not support internal snapshots", "does not support internal snapshots",
bs->drv->format_name, device); bs->drv->format_name, device);
return; goto out;
} }
if (!strlen(name)) { if (!strlen(name)) {
error_setg(errp, "Name is empty"); error_setg(errp, "Name is empty");
return; goto out;
} }
/* check whether a snapshot with name exist */ /* check whether a snapshot with name exist */
@ -1539,12 +1530,12 @@ static void internal_snapshot_prepare(BlkActionState *common,
&local_err); &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} else if (ret) { } else if (ret) {
error_setg(errp, error_setg(errp,
"Snapshot with name '%s' already exists on device '%s'", "Snapshot with name '%s' already exists on device '%s'",
name, device); name, device);
return; goto out;
} }
/* 3. take the snapshot */ /* 3. take the snapshot */
@ -1560,11 +1551,14 @@ static void internal_snapshot_prepare(BlkActionState *common,
error_setg_errno(errp, -ret1, error_setg_errno(errp, -ret1,
"Failed to create snapshot '%s' on device '%s'", "Failed to create snapshot '%s' on device '%s'",
name, device); name, device);
return; goto out;
} }
/* 4. succeed, mark a snapshot is created */ /* 4. succeed, mark a snapshot is created */
state->created = true; state->created = true;
out:
aio_context_release(aio_context);
} }
static void internal_snapshot_abort(BlkActionState *common) static void internal_snapshot_abort(BlkActionState *common)
@ -1573,12 +1567,16 @@ static void internal_snapshot_abort(BlkActionState *common)
DO_UPCAST(InternalSnapshotState, common, common); DO_UPCAST(InternalSnapshotState, common, common);
BlockDriverState *bs = state->bs; BlockDriverState *bs = state->bs;
QEMUSnapshotInfo *sn = &state->sn; QEMUSnapshotInfo *sn = &state->sn;
AioContext *aio_context;
Error *local_error = NULL; Error *local_error = NULL;
if (!state->created) { if (!state->created) {
return; 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) { if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
error_reportf_err(local_error, error_reportf_err(local_error,
"Failed to delete snapshot with id '%s' and " "Failed to delete snapshot with id '%s' and "
@ -1586,19 +1584,26 @@ static void internal_snapshot_abort(BlkActionState *common)
sn->id_str, sn->name, sn->id_str, sn->name,
bdrv_get_device_name(bs)); bdrv_get_device_name(bs));
} }
aio_context_release(aio_context);
} }
static void internal_snapshot_clean(BlkActionState *common) static void internal_snapshot_clean(BlkActionState *common)
{ {
InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState, InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
common, common); common, common);
AioContext *aio_context;
if (state->aio_context) { if (!state->bs) {
if (state->bs) { return;
bdrv_drained_end(state->bs);
}
aio_context_release(state->aio_context);
} }
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 */ /* external snapshot private data */
@ -1606,7 +1611,6 @@ typedef struct ExternalSnapshotState {
BlkActionState common; BlkActionState common;
BlockDriverState *old_bs; BlockDriverState *old_bs;
BlockDriverState *new_bs; BlockDriverState *new_bs;
AioContext *aio_context;
bool overlay_appended; bool overlay_appended;
} ExternalSnapshotState; } ExternalSnapshotState;
@ -1626,6 +1630,7 @@ static void external_snapshot_prepare(BlkActionState *common,
ExternalSnapshotState *state = ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common); DO_UPCAST(ExternalSnapshotState, common, common);
TransactionAction *action = common->action; TransactionAction *action = common->action;
AioContext *aio_context;
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar /* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
* purpose but a different set of parameters */ * purpose but a different set of parameters */
@ -1662,31 +1667,32 @@ static void external_snapshot_prepare(BlkActionState *common,
return; return;
} }
/* Acquire AioContext now so any threads operating on old_bs stop */ aio_context = bdrv_get_aio_context(state->old_bs);
state->aio_context = bdrv_get_aio_context(state->old_bs); aio_context_acquire(aio_context);
aio_context_acquire(state->aio_context);
/* Paired with .clean() */
bdrv_drained_begin(state->old_bs); bdrv_drained_begin(state->old_bs);
if (!bdrv_is_inserted(state->old_bs)) { if (!bdrv_is_inserted(state->old_bs)) {
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
return; goto out;
} }
if (bdrv_op_is_blocked(state->old_bs, if (bdrv_op_is_blocked(state->old_bs,
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) { BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
return; goto out;
} }
if (!bdrv_is_read_only(state->old_bs)) { if (!bdrv_is_read_only(state->old_bs)) {
if (bdrv_flush(state->old_bs)) { if (bdrv_flush(state->old_bs)) {
error_setg(errp, QERR_IO_ERROR); error_setg(errp, QERR_IO_ERROR);
return; goto out;
} }
} }
if (!bdrv_is_first_non_filter(state->old_bs)) { if (!bdrv_is_first_non_filter(state->old_bs)) {
error_setg(errp, QERR_FEATURE_DISABLED, "snapshot"); error_setg(errp, QERR_FEATURE_DISABLED, "snapshot");
return; goto out;
} }
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) { 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) { if (node_name && !snapshot_node_name) {
error_setg(errp, "New snapshot node name missing"); error_setg(errp, "New snapshot node name missing");
return; goto out;
} }
if (snapshot_node_name && if (snapshot_node_name &&
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
error_setg(errp, "New snapshot node name already in use"); error_setg(errp, "New snapshot node name already in use");
return; goto out;
} }
flags = state->old_bs->open_flags; 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); int64_t size = bdrv_getlength(state->old_bs);
if (size < 0) { if (size < 0) {
error_setg_errno(errp, -size, "bdrv_getlength failed"); error_setg_errno(errp, -size, "bdrv_getlength failed");
return; goto out;
} }
bdrv_img_create(new_image_file, format, bdrv_img_create(new_image_file, format,
state->old_bs->filename, state->old_bs->filename,
@ -1725,7 +1731,7 @@ static void external_snapshot_prepare(BlkActionState *common,
NULL, size, flags, false, &local_err); NULL, size, flags, false, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} }
} }
@ -1740,30 +1746,30 @@ static void external_snapshot_prepare(BlkActionState *common,
errp); errp);
/* We will manually add the backing_hd field to the bs later */ /* We will manually add the backing_hd field to the bs later */
if (!state->new_bs) { if (!state->new_bs) {
return; goto out;
} }
if (bdrv_has_blk(state->new_bs)) { if (bdrv_has_blk(state->new_bs)) {
error_setg(errp, "The snapshot is already in use"); 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, if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
errp)) { errp)) {
return; goto out;
} }
if (state->new_bs->backing != NULL) { if (state->new_bs->backing != NULL) {
error_setg(errp, "The snapshot already has a backing image"); error_setg(errp, "The snapshot already has a backing image");
return; goto out;
} }
if (!state->new_bs->drv->supports_backing) { if (!state->new_bs->drv->supports_backing) {
error_setg(errp, "The snapshot does not support backing images"); 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 /* 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 * 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); bdrv_append(state->new_bs, state->old_bs, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} }
state->overlay_appended = true; state->overlay_appended = true;
out:
aio_context_release(aio_context);
} }
static void external_snapshot_commit(BlkActionState *common) static void external_snapshot_commit(BlkActionState *common)
{ {
ExternalSnapshotState *state = ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common); 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 /* We don't need (or want) to use the transactional
* bdrv_reopen_multiple() across all the entries at once, because we * 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, bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
NULL); NULL);
} }
aio_context_release(aio_context);
} }
static void external_snapshot_abort(BlkActionState *common) static void external_snapshot_abort(BlkActionState *common)
@ -1797,11 +1812,18 @@ static void external_snapshot_abort(BlkActionState *common)
DO_UPCAST(ExternalSnapshotState, common, common); DO_UPCAST(ExternalSnapshotState, common, common);
if (state->new_bs) { if (state->new_bs) {
if (state->overlay_appended) { 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() bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd()
close state->old_bs; we need it */ close state->old_bs; we need it */
bdrv_set_backing_hd(state->new_bs, NULL, &error_abort); bdrv_set_backing_hd(state->new_bs, NULL, &error_abort);
bdrv_replace_node(state->new_bs, state->old_bs, &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 */ 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 = ExternalSnapshotState *state =
DO_UPCAST(ExternalSnapshotState, common, common); DO_UPCAST(ExternalSnapshotState, common, common);
if (state->aio_context) { AioContext *aio_context;
bdrv_drained_end(state->old_bs);
aio_context_release(state->aio_context); if (!state->old_bs) {
bdrv_unref(state->new_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 { typedef struct DriveBackupState {
BlkActionState common; BlkActionState common;
BlockDriverState *bs; BlockDriverState *bs;
AioContext *aio_context;
BlockJob *job; BlockJob *job;
} DriveBackupState; } DriveBackupState;
@ -1832,6 +1861,7 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
BlockDriverState *bs; BlockDriverState *bs;
DriveBackup *backup; DriveBackup *backup;
AioContext *aio_context;
Error *local_err = NULL; Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP); assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
@ -1842,24 +1872,36 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
return; return;
} }
/* AioContext is released in .clean() */ aio_context = bdrv_get_aio_context(bs);
state->aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context);
aio_context_acquire(state->aio_context);
/* Paired with .clean() */
bdrv_drained_begin(bs); bdrv_drained_begin(bs);
state->bs = bs; state->bs = bs;
state->job = do_drive_backup(backup, common->block_job_txn, &local_err); state->job = do_drive_backup(backup, common->block_job_txn, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} }
out:
aio_context_release(aio_context);
} }
static void drive_backup_commit(BlkActionState *common) static void drive_backup_commit(BlkActionState *common)
{ {
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, 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); assert(state->job);
block_job_start(state->job); block_job_start(state->job);
aio_context_release(aio_context);
} }
static void drive_backup_abort(BlkActionState *common) 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); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
if (state->job) { 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); block_job_cancel_sync(state->job);
aio_context_release(aio_context);
} }
} }
static void drive_backup_clean(BlkActionState *common) static void drive_backup_clean(BlkActionState *common)
{ {
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common); DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
AioContext *aio_context;
if (state->aio_context) { if (!state->bs) {
bdrv_drained_end(state->bs); return;
aio_context_release(state->aio_context);
} }
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 { typedef struct BlockdevBackupState {
BlkActionState common; BlkActionState common;
BlockDriverState *bs; BlockDriverState *bs;
BlockJob *job; BlockJob *job;
AioContext *aio_context;
} BlockdevBackupState; } BlockdevBackupState;
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, 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); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
BlockdevBackup *backup; BlockdevBackup *backup;
BlockDriverState *bs, *target; BlockDriverState *bs, *target;
AioContext *aio_context;
Error *local_err = NULL; Error *local_err = NULL;
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
@ -1911,29 +1967,39 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
return; return;
} }
/* AioContext is released in .clean() */ aio_context = bdrv_get_aio_context(bs);
state->aio_context = bdrv_get_aio_context(bs); if (aio_context != bdrv_get_aio_context(target)) {
if (state->aio_context != bdrv_get_aio_context(target)) {
state->aio_context = NULL;
error_setg(errp, "Backup between two IO threads is not implemented"); error_setg(errp, "Backup between two IO threads is not implemented");
return; return;
} }
aio_context_acquire(state->aio_context); aio_context_acquire(aio_context);
state->bs = bs; state->bs = bs;
/* Paired with .clean() */
bdrv_drained_begin(state->bs); bdrv_drained_begin(state->bs);
state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err); state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; goto out;
} }
out:
aio_context_release(aio_context);
} }
static void blockdev_backup_commit(BlkActionState *common) static void blockdev_backup_commit(BlkActionState *common)
{ {
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, 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); assert(state->job);
block_job_start(state->job); block_job_start(state->job);
aio_context_release(aio_context);
} }
static void blockdev_backup_abort(BlkActionState *common) 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); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
if (state->job) { 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); block_job_cancel_sync(state->job);
aio_context_release(aio_context);
} }
} }
static void blockdev_backup_clean(BlkActionState *common) static void blockdev_backup_clean(BlkActionState *common)
{ {
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common); BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
AioContext *aio_context;
if (state->aio_context) { if (!state->bs) {
bdrv_drained_end(state->bs); return;
aio_context_release(state->aio_context);
} }
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 { typedef struct BlockDirtyBitmapState {
BlkActionState common; BlkActionState common;
BdrvDirtyBitmap *bitmap; BdrvDirtyBitmap *bitmap;
BlockDriverState *bs; BlockDriverState *bs;
AioContext *aio_context;
HBitmap *backup; HBitmap *backup;
bool prepared; bool prepared;
} BlockDirtyBitmapState; } BlockDirtyBitmapState;
@ -2038,7 +2117,6 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
} }
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
/* AioContext is released in .clean() */
} }
static void block_dirty_bitmap_clear_abort(BlkActionState *common) 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); 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) static void abort_prepare(BlkActionState *common, Error **errp)
{ {
error_setg(errp, "Transaction aborted using Abort action"); error_setg(errp, "Transaction aborted using Abort action");
@ -2129,7 +2197,6 @@ static const BlkActionOps actions[] = {
.prepare = block_dirty_bitmap_clear_prepare, .prepare = block_dirty_bitmap_clear_prepare,
.commit = block_dirty_bitmap_clear_commit, .commit = block_dirty_bitmap_clear_commit,
.abort = block_dirty_bitmap_clear_abort, .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; 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 = { QemuOptsList qemu_common_drive_opts = {
.name = "drive", .name = "drive",
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head), .head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),

View File

@ -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_cancelled(BlockJob *job);
static void block_job_event_completed(BlockJob *job, const char *msg); 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 */ /* Transactional group of block jobs */
struct BlockJobTxn { struct BlockJobTxn {
@ -233,26 +234,23 @@ static char *child_job_get_parent_desc(BdrvChild *c)
job->id); job->id);
} }
static const BdrvChildRole child_job = { static void child_job_drained_begin(BdrvChild *c)
.get_parent_desc = child_job_get_parent_desc,
.stay_at_node = true,
};
static void block_job_drained_begin(void *opaque)
{ {
BlockJob *job = opaque; BlockJob *job = c->opaque;
block_job_pause(job); 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); block_job_resume(job);
} }
static const BlockDevOps block_job_dev_ops = { static const BdrvChildRole child_job = {
.drained_begin = block_job_drained_begin, .get_parent_desc = child_job_get_parent_desc,
.drained_end = block_job_drained_end, .drained_begin = child_job_drained_begin,
.drained_end = child_job_drained_end,
.stay_at_node = true,
}; };
void block_job_remove_all_bdrv(BlockJob *job) 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) void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
{ {
Error *local_err = NULL; Error *local_err = NULL;
int64_t old_speed = job->speed;
if (!job->driver->set_speed) { if (!job->driver->set_speed) {
error_setg(errp, QERR_UNSUPPORTED); error_setg(errp, QERR_UNSUPPORTED);
@ -495,6 +500,12 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
} }
job->speed = speed; 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) 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); block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
bs->job = job; bs->job = job;
blk_set_dev_ops(blk, &block_job_dev_ops, job);
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker); bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
QLIST_INSERT_HEAD(&block_jobs, job, job_list); 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)) { if (!block_job_started(job)) {
return; return;
@ -836,6 +850,11 @@ void block_job_enter(BlockJob *job)
return; return;
} }
if (fn && !fn(job)) {
block_job_unlock();
return;
}
assert(!job->deferred_to_main_loop); assert(!job->deferred_to_main_loop);
timer_del(&job->sleep_timer); timer_del(&job->sleep_timer);
job->busy = true; job->busy = true;
@ -843,6 +862,11 @@ void block_job_enter(BlockJob *job)
aio_co_wake(job->co); aio_co_wake(job->co);
} }
void block_job_enter(BlockJob *job)
{
block_job_enter_cond(job, NULL);
}
bool block_job_is_cancelled(BlockJob *job) bool block_job_is_cancelled(BlockJob *job)
{ {
return job->cancelled; return job->cancelled;

View File

@ -32,7 +32,6 @@
#include "qemu/envlist.h" #include "qemu/envlist.h"
#include "exec/log.h" #include "exec/log.h"
#include "trace/control.h" #include "trace/control.h"
#include "glib-compat.h"
int singlestep; int singlestep;
unsigned long mmap_min_addr; unsigned long mmap_min_addr;

View File

@ -1,7 +1,7 @@
/* /*
* QEMU Baum Braille Device * 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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * 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"); brlapi_perror("baum: brlapi__getDisplaySize");
return 0; return 0;
} }
if (baum->y > 1) {
baum->y = 1;
}
if (baum->x > 84) {
baum->x = 84;
}
con = qemu_console_lookup_by_index(0); con = qemu_console_lookup_by_index(0);
if (con && qemu_console_is_graphic(con)) { if (con && qemu_console_is_graphic(con)) {

View File

@ -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) static int mux_proc_byte(Chardev *chr, MuxChardev *d, int ch)
{ {
if (d->term_got_escape) { 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_write = mux_chr_write;
cc->chr_accept_input = mux_chr_accept_input; cc->chr_accept_input = mux_chr_accept_input;
cc->chr_add_watch = mux_chr_add_watch; cc->chr_add_watch = mux_chr_add_watch;
cc->chr_be_event = mux_chr_be_event;
} }
static const TypeInfo char_mux_type_info = { static const TypeInfo char_mux_type_info = {

View File

@ -25,6 +25,7 @@
#include "chardev/char.h" #include "chardev/char.h"
#include "io/channel-socket.h" #include "io/channel-socket.h"
#include "io/channel-tls.h" #include "io/channel-tls.h"
#include "io/net-listener.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/clone-visitor.h" #include "qapi/clone-visitor.h"
@ -40,8 +41,7 @@ typedef struct {
Chardev parent; Chardev parent;
QIOChannel *ioc; /* Client I/O channel */ QIOChannel *ioc; /* Client I/O channel */
QIOChannelSocket *sioc; /* Client master channel */ QIOChannelSocket *sioc; /* Client master channel */
QIOChannelSocket *listen_ioc; QIONetListener *listener;
guint listen_tag;
QCryptoTLSCreds *tls_creds; QCryptoTLSCreds *tls_creds;
int connected; int connected;
int max_size; int max_size;
@ -93,9 +93,9 @@ static void check_report_connect_error(Chardev *chr,
qemu_chr_socket_restart_timer(chr); qemu_chr_socket_restart_timer(chr);
} }
static gboolean tcp_chr_accept(QIOChannel *chan, static void tcp_chr_accept(QIONetListener *listener,
GIOCondition cond, QIOChannelSocket *cioc,
void *opaque); void *opaque);
static int tcp_chr_read_poll(void *opaque); static int tcp_chr_read_poll(void *opaque);
static void tcp_chr_disconnect(Chardev *chr); static void tcp_chr_disconnect(Chardev *chr);
@ -401,9 +401,9 @@ static void tcp_chr_disconnect(Chardev *chr)
tcp_chr_free_connection(chr); tcp_chr_free_connection(chr);
if (s->listen_ioc && s->listen_tag == 0) { if (s->listener) {
s->listen_tag = qio_channel_add_watch( qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
QIO_CHANNEL(s->listen_ioc), G_IO_IN, tcp_chr_accept, chr, NULL); chr, NULL);
} }
update_disconnected_filename(s); update_disconnected_filename(s);
if (emit_close) { if (emit_close) {
@ -702,9 +702,8 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
if (s->do_nodelay) { if (s->do_nodelay) {
qio_channel_set_delay(s->ioc, false); qio_channel_set_delay(s->ioc, false);
} }
if (s->listen_tag) { if (s->listener) {
g_source_remove(s->listen_tag); qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
s->listen_tag = 0;
} }
if (s->tls_creds) { if (s->tls_creds) {
@ -736,24 +735,14 @@ static int tcp_chr_add_client(Chardev *chr, int fd)
return ret; return ret;
} }
static gboolean tcp_chr_accept(QIOChannel *channel, static void tcp_chr_accept(QIONetListener *listener,
GIOCondition cond, QIOChannelSocket *cioc,
void *opaque) void *opaque)
{ {
Chardev *chr = CHARDEV(opaque); Chardev *chr = CHARDEV(opaque);
QIOChannelSocket *sioc;
sioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(channel), tcp_chr_set_client_ioc_name(chr, cioc);
NULL); tcp_chr_new_client(chr, cioc);
if (!sioc) {
return TRUE;
}
tcp_chr_new_client(chr, sioc);
object_unref(OBJECT(sioc));
return TRUE;
} }
static int tcp_chr_wait_connected(Chardev *chr, Error **errp) 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) { if (s->is_listen) {
info_report("QEMU waiting for connection on: %s", info_report("QEMU waiting for connection on: %s",
chr->filename); chr->filename);
qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), true, NULL); sioc = qio_net_listener_wait_client(s->listener);
tcp_chr_accept(QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr); tcp_chr_set_client_ioc_name(chr, sioc);
qio_channel_set_blocking(QIO_CHANNEL(s->listen_ioc), false, NULL); tcp_chr_new_client(chr, sioc);
object_unref(OBJECT(sioc));
} else { } else {
sioc = qio_channel_socket_new(); sioc = qio_channel_socket_new();
tcp_chr_set_client_ioc_name(chr, sioc); tcp_chr_set_client_ioc_name(chr, sioc);
@ -797,12 +787,9 @@ static void char_socket_finalize(Object *obj)
s->reconnect_timer = 0; s->reconnect_timer = 0;
} }
qapi_free_SocketAddress(s->addr); qapi_free_SocketAddress(s->addr);
if (s->listen_tag) { if (s->listener) {
g_source_remove(s->listen_tag); qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
s->listen_tag = 0; object_unref(OBJECT(s->listener));
}
if (s->listen_ioc) {
object_unref(OBJECT(s->listen_ioc));
} }
if (s->tls_creds) { if (s->tls_creds) {
object_unref(OBJECT(s->tls_creds)); object_unref(OBJECT(s->tls_creds));
@ -935,29 +922,29 @@ static void qmp_chardev_open_socket(Chardev *chr,
} else { } else {
if (s->is_listen) { if (s->is_listen) {
char *name; char *name;
sioc = qio_channel_socket_new(); s->listener = qio_net_listener_new();
name = g_strdup_printf("chardev-tcp-listener-%s", chr->label); 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); 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; goto error;
} }
qapi_free_SocketAddress(s->addr); 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); update_disconnected_filename(s);
s->listen_ioc = sioc;
if (is_waitconnect && if (is_waitconnect &&
qemu_chr_wait_connected(chr, errp) < 0) { qemu_chr_wait_connected(chr, errp) < 0) {
return; return;
} }
if (!s->ioc) { if (!s->ioc) {
s->listen_tag = qio_channel_add_watch( qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
QIO_CHANNEL(s->listen_ioc), G_IO_IN, chr, NULL);
tcp_chr_accept, chr, NULL);
} }
} else if (qemu_chr_wait_connected(chr, errp) < 0) { } else if (qemu_chr_wait_connected(chr, errp) < 0) {
goto error; goto error;

View File

@ -43,10 +43,19 @@ static Object *get_chardevs_root(void)
return container_get(object_get_root(), "/chardevs"); 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; 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 */ /* Keep track if the char device is open */
switch (event) { switch (event) {
case CHR_EVENT_OPENED: case CHR_EVENT_OPENED:
@ -57,11 +66,7 @@ void qemu_chr_be_event(Chardev *s, int event)
break; break;
} }
if (!be || !be->chr_event) { CHARDEV_GET_CLASS(s)->chr_be_event(s, event);
return;
}
be->chr_event(be->opaque, event);
} }
/* Not reporting errors from writing to logfile, as logs are /* 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); ChardevClass *cc = CHARDEV_CLASS(oc);
cc->chr_write = null_chr_write; cc->chr_write = null_chr_write;
cc->chr_be_event = chr_be_event;
} }
static void char_finalize(Object *obj) static void char_finalize(Object *obj)

View File

@ -25,10 +25,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu-common.h" #include "qemu-common.h"

105
configure vendored
View File

@ -211,6 +211,17 @@ supported_xen_target() {
return 1 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() { supported_target() {
case "$1" in case "$1" in
*-softmmu) *-softmmu)
@ -236,6 +247,7 @@ supported_target() {
supported_kvm_target "$1" && return 0 supported_kvm_target "$1" && return 0
supported_xen_target "$1" && return 0 supported_xen_target "$1" && return 0
supported_hax_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'" print_error "TCG disabled, but hardware accelerator not available for '$target'"
return 1 return 1
} }
@ -325,6 +337,7 @@ vhost_vsock="no"
vhost_user="" vhost_user=""
kvm="no" kvm="no"
hax="no" hax="no"
hvf="no"
rdma="" rdma=""
gprof="no" gprof="no"
debug_tcg="no" debug_tcg="no"
@ -426,6 +439,7 @@ vxhs=""
supported_cpu="no" supported_cpu="no"
supported_os="no" supported_os="no"
bogus_os="no" bogus_os="no"
malloc_trim=""
# parse CC options first # parse CC options first
for opt do for opt do
@ -740,6 +754,7 @@ Darwin)
bsd="yes" bsd="yes"
darwin="yes" darwin="yes"
hax="yes" hax="yes"
hvf="yes"
LDFLAGS_SHARED="-bundle -undefined dynamic_lookup" LDFLAGS_SHARED="-bundle -undefined dynamic_lookup"
if [ "$cpu" = "x86_64" ] ; then if [ "$cpu" = "x86_64" ] ; then
QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS" QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
@ -1035,6 +1050,10 @@ for opt do
;; ;;
--enable-hax) hax="yes" --enable-hax) hax="yes"
;; ;;
--disable-hvf) hvf="no"
;;
--enable-hvf) hvf="yes"
;;
--disable-tcg-interpreter) tcg_interpreter="no" --disable-tcg-interpreter) tcg_interpreter="no"
;; ;;
--enable-tcg-interpreter) tcg_interpreter="yes" --enable-tcg-interpreter) tcg_interpreter="yes"
@ -1047,6 +1066,10 @@ for opt do
;; ;;
--enable-tcg) tcg="yes" --enable-tcg) tcg="yes"
;; ;;
--disable-malloc-trim) malloc_trim="no"
;;
--enable-malloc-trim) malloc_trim="yes"
;;
--disable-spice) spice="no" --disable-spice) spice="no"
;; ;;
--enable-spice) spice="yes" --enable-spice) spice="yes"
@ -1466,6 +1489,7 @@ Advanced options (experts only):
Default:trace-<pid> Default:trace-<pid>
--disable-slirp disable SLIRP userspace network connectivity --disable-slirp disable SLIRP userspace network connectivity
--enable-tcg-interpreter enable TCG with bytecode interpreter (TCI) --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 --oss-lib path to OSS library
--cpu=CPU Build for host CPU [$cpu] --cpu=CPU Build for host CPU [$cpu]
--with-coroutine=BACKEND coroutine backend. Supported options: --with-coroutine=BACKEND coroutine backend. Supported options:
@ -1523,6 +1547,7 @@ disabled with --disable-FEATURE, default is enabled if available:
bluez bluez stack connectivity bluez bluez stack connectivity
kvm KVM acceleration support kvm KVM acceleration support
hax HAX acceleration support hax HAX acceleration support
hvf Hypervisor.framework acceleration support
rdma RDMA-based migration support rdma RDMA-based migration support
vde support for vde network vde support for vde network
netmap support for netmap network netmap support for netmap network
@ -1582,6 +1607,20 @@ fi
# Suppress writing compiled files # Suppress writing compiled files
python="$python -B" 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 # 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. # printing the help message, bail out if the host CPU isn't supported.
if test "$ARCH" = "unknown"; then if test "$ARCH" = "unknown"; then
@ -1603,17 +1642,6 @@ if test -z "$werror" ; then
fi fi
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 if test "$bogus_os" = "yes"; then
# Now that we know that we're not printing the help and that # 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 # 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 exit 1
fi 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 # tcmalloc probe
@ -3920,7 +3972,7 @@ fi
# check if memfd is supported # check if memfd is supported
memfd=no memfd=no
cat > $TMPC << EOF cat > $TMPC << EOF
#include <sys/memfd.h> #include <sys/mman.h>
int main(void) int main(void)
{ {
@ -5022,6 +5074,21 @@ then
fi 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 # Sparc implicitly links with --relax, which is
# incompatible with -r, so --no-relax should be # incompatible with -r, so --no-relax should be
@ -5497,11 +5564,13 @@ echo "ATTR/XATTR support $attr"
echo "Install blobs $blobs" echo "Install blobs $blobs"
echo "KVM support $kvm" echo "KVM support $kvm"
echo "HAX support $hax" echo "HAX support $hax"
echo "HVF support $hvf"
echo "TCG support $tcg" echo "TCG support $tcg"
if test "$tcg" = "yes" ; then if test "$tcg" = "yes" ; then
echo "TCG debug enabled $debug_tcg" echo "TCG debug enabled $debug_tcg"
echo "TCG interpreter $tcg_interpreter" echo "TCG interpreter $tcg_interpreter"
fi fi
echo "malloc trim support $malloc_trim"
echo "RDMA support $rdma" echo "RDMA support $rdma"
echo "fdt support $fdt" echo "fdt support $fdt"
echo "preadv support $preadv" echo "preadv support $preadv"
@ -6012,6 +6081,10 @@ if test "$opengl" = "yes" ; then
fi fi
fi fi
if test "$malloc_trim" = "yes" ; then
echo "CONFIG_MALLOC_TRIM=y" >> $config_host_mak
fi
if test "$avx2_opt" = "yes" ; then if test "$avx2_opt" = "yes" ; then
echo "CONFIG_AVX2_OPT=y" >> $config_host_mak echo "CONFIG_AVX2_OPT=y" >> $config_host_mak
fi fi
@ -6366,7 +6439,7 @@ target_name=$(echo $target | cut -d '-' -f 1)
target_bigendian="no" target_bigendian="no"
case "$target_name" in 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 target_bigendian=yes
;; ;;
esac esac
@ -6421,7 +6494,8 @@ case "$target_name" in
mttcg="yes" mttcg="yes"
gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" 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 TARGET_BASE_ARCH=arm
bflt="yes" bflt="yes"
mttcg="yes" mttcg="yes"
@ -6564,6 +6638,9 @@ fi
if supported_hax_target $target; then if supported_hax_target $target; then
echo "CONFIG_HAX=y" >> $config_target_mak echo "CONFIG_HAX=y" >> $config_target_mak
fi fi
if supported_hvf_target $target; then
echo "CONFIG_HVF=y" >> $config_target_mak
fi
if test "$target_bigendian" = "yes" ; then if test "$target_bigendian" = "yes" ; then
echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak echo "TARGET_WORDS_BIGENDIAN=y" >> $config_target_mak
fi fi

View 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]

View 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]

View 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
View File

@ -37,6 +37,7 @@
#include "sysemu/hw_accel.h" #include "sysemu/hw_accel.h"
#include "sysemu/kvm.h" #include "sysemu/kvm.h"
#include "sysemu/hax.h" #include "sysemu/hax.h"
#include "sysemu/hvf.h"
#include "qmp-commands.h" #include "qmp-commands.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
@ -900,6 +901,10 @@ void cpu_synchronize_all_states(void)
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
cpu_synchronize_state(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_FOREACH(cpu) {
cpu_synchronize_post_reset(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_FOREACH(cpu) {
cpu_synchronize_post_init(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) static void qemu_wait_io_event_common(CPUState *cpu)
{ {
atomic_mb_set(&cpu->thread_kicked, false); atomic_mb_set(&cpu->thread_kicked, false);
if (cpu->stop) { if (cpu->stop) {
cpu->stop = false; qemu_cpu_stop(cpu, false);
cpu->stopped = true;
qemu_cond_broadcast(&qemu_pause_cond);
} }
process_queued_cpu_work(cpu); process_queued_cpu_work(cpu);
} }
@ -1098,6 +1120,14 @@ static void qemu_kvm_wait_io_event(CPUState *cpu)
qemu_wait_io_event_common(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) static void *qemu_kvm_cpu_thread_fn(void *arg)
{ {
CPUState *cpu = arg; CPUState *cpu = arg;
@ -1435,6 +1465,48 @@ static void *qemu_hax_cpu_thread_fn(void *arg)
return NULL; 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 #ifdef _WIN32
static void CALLBACK dummy_apc_func(ULONG_PTR unused) 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); qemu_clock_enable(QEMU_CLOCK_VIRTUAL, false);
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
cpu->stop = true; if (qemu_cpu_is_self(cpu)) {
qemu_cpu_kick(cpu); qemu_cpu_stop(cpu, true);
} } else {
cpu->stop = true;
if (qemu_in_vcpu_thread()) { qemu_cpu_kick(cpu);
cpu_stop_current(); }
} }
while (!all_vcpus_paused()) { 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) static void qemu_dummy_start_vcpu(CPUState *cpu)
{ {
char thread_name[VCPU_THREAD_NAME_SIZE]; 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, /* If the target cpu hasn't set up any address spaces itself,
* give it the default one. * give it the default one.
*/ */
AddressSpace *as = g_new0(AddressSpace, 1);
address_space_init(as, cpu->memory, "cpu-memory");
cpu->num_ases = 1; cpu->num_ases = 1;
cpu_address_space_init(cpu, as, 0); cpu_address_space_init(cpu, 0, "cpu-memory", cpu->memory);
} }
if (kvm_enabled()) { if (kvm_enabled()) {
qemu_kvm_start_vcpu(cpu); qemu_kvm_start_vcpu(cpu);
} else if (hax_enabled()) { } else if (hax_enabled()) {
qemu_hax_start_vcpu(cpu); qemu_hax_start_vcpu(cpu);
} else if (hvf_enabled()) {
qemu_hvf_start_vcpu(cpu);
} else if (tcg_enabled()) { } else if (tcg_enabled()) {
qemu_tcg_init_vcpu(cpu); qemu_tcg_init_vcpu(cpu);
} else { } else {
@ -1799,10 +1891,7 @@ void qemu_init_vcpu(CPUState *cpu)
void cpu_stop_current(void) void cpu_stop_current(void)
{ {
if (current_cpu) { if (current_cpu) {
current_cpu->stop = false; qemu_cpu_stop(current_cpu, true);
current_cpu->stopped = true;
cpu_exit(current_cpu);
qemu_cond_broadcast(&qemu_pause_cond);
} }
} }

View File

@ -0,0 +1 @@
# Default configuration for aarch64_be-linux-user

View File

@ -130,5 +130,5 @@ CONFIG_SMBIOS=y
CONFIG_ASPEED_SOC=y CONFIG_ASPEED_SOC=y
CONFIG_GPIO_KEY=y CONFIG_GPIO_KEY=y
CONFIG_MSF2=y CONFIG_MSF2=y
CONFIG_FW_CFG_DMA=y CONFIG_FW_CFG_DMA=y
CONFIG_XILINX_AXI=y

View File

@ -16,3 +16,4 @@ CONFIG_I8259=y
CONFIG_XILINX=y CONFIG_XILINX=y
CONFIG_XILINX_ETHLITE=y CONFIG_XILINX_ETHLITE=y
CONFIG_SM501=y CONFIG_SM501=y
CONFIG_IDE_SII3112=y

View File

@ -21,6 +21,7 @@ common-obj-$(CONFIG_S390_DIS) += s390.o
common-obj-$(CONFIG_SH4_DIS) += sh4.o common-obj-$(CONFIG_SH4_DIS) += sh4.o
common-obj-$(CONFIG_SPARC_DIS) += sparc.o common-obj-$(CONFIG_SPARC_DIS) += sparc.o
common-obj-$(CONFIG_LM32_DIS) += lm32.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 # TODO: As long as the TCG interpreter and its generated code depend
# on the QEMU target, we cannot compile the disassembler here. # on the QEMU target, we cannot compile the disassembler here.

View File

@ -1662,7 +1662,7 @@ print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given,
} }
else else
{ {
/* Only match unconditional instuctions against unconditional /* Only match unconditional instructions against unconditional
patterns. */ patterns. */
if ((given & 0xf0000000) == 0xf0000000) if ((given & 0xf0000000) == 0xf0000000)
{ {

View File

@ -1756,7 +1756,6 @@ extern const int nios2_num_r2_reg_range_mappings;
#endif /* _NIOS2_H */ #endif /* _NIOS2_H */
/*#include "sysdep.h" /*#include "sysdep.h"
#include <stdio.h>
#include "opcode/nios2.h" #include "opcode/nios2.h"
*/ */
/* Register string table */ /* Register string table */
@ -2521,8 +2520,6 @@ const int nios2_num_r2_reg_range_mappings = 8;
#include "dis-asm.h" #include "dis-asm.h"
#include "opcode/nios2.h" #include "opcode/nios2.h"
#include "libiberty.h" #include "libiberty.h"
#include <string.h>
#include <assert.h>
*/ */
/* No symbol table is available when this code runs out in an embedded /* 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. */ system as when it is used for disassembler support in a monitor. */

133
disas/xtensa.c Normal file
View 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;
}

View File

@ -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 This work is licensed under the terms of the GNU GPL, version 2 or later. See
the COPYING file in the top-level directory. 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 context is acquired no other thread can access it or run event loop iterations
in this AioContext. in this AioContext.
aio_context_acquire()/aio_context_release() calls may be nested. This Legacy code sometimes nests aio_context_acquire()/aio_context_release() calls.
means you can call them if you're not sure whether #2 applies. 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 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 AioContexts simultaneously. Therefore, it is only safe for code holding the

View File

@ -63,7 +63,7 @@ Comment text starting with '=' is a section title:
Double the '=' for a subsection title: Double the '=' for a subsection title:
# == Subection title # == Subsection title
'|' denotes examples: '|' denotes examples:

2
dtc

@ -1 +1 @@
Subproject commit 558cd81bdd432769b59bff01240c44f82cfb1a9d Subproject commit e54388015af1fb4bf04d0bca99caba1074d9cc42

7
dump.c
View File

@ -788,12 +788,7 @@ static bool note_name_equal(DumpState *s,
get_note_sizes(s, note, &head_size, &name_size, NULL); get_note_sizes(s, note, &head_size, &name_size, NULL);
head_size = ROUND_UP(head_size, 4); head_size = ROUND_UP(head_size, 4);
if (name_size != len || return name_size == len && memcmp(note + head_size, name, len) == 0;
memcmp(note + head_size, "VMCOREINFO", len)) {
return false;
}
return true;
} }
/* write common header, sub header and elf note to vmcore */ /* write common header, sub header and elf note to vmcore */

47
exec.c
View File

@ -18,8 +18,6 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#ifndef _WIN32
#endif
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "cpu.h" #include "cpu.h"
@ -51,7 +49,6 @@
#include "trace-root.h" #include "trace-root.h"
#ifdef CONFIG_FALLOCATE_PUNCH_HOLE #ifdef CONFIG_FALLOCATE_PUNCH_HOLE
#include <fcntl.h>
#include <linux/falloc.h> #include <linux/falloc.h>
#endif #endif
@ -708,9 +705,17 @@ CPUState *qemu_get_cpu(int index)
} }
#if !defined(CONFIG_USER_ONLY) #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; 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 */ /* Target code should have set num_ases before calling us */
assert(asidx < cpu->num_ases); 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, &section); return phys_section_add(map, &section);
} }
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) MemoryRegion *iotlb_to_region(CPUState *cpu, hwaddr index, MemTxAttrs attrs)
{ {
int asidx = cpu_asidx_from_attrs(cpu, 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) 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, memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL,
NULL, UINT64_MAX); NULL, UINT64_MAX);

View File

@ -22,22 +22,19 @@
#define SM_LOCAL_MODE_BITS 0600 #define SM_LOCAL_MODE_BITS 0600
#define SM_LOCAL_DIR_MODE_BITS 0700 #define SM_LOCAL_DIR_MODE_BITS 0700
typedef struct FsCred typedef struct FsCred {
{
uid_t fc_uid; uid_t fc_uid;
gid_t fc_gid; gid_t fc_gid;
mode_t fc_mode; mode_t fc_mode;
dev_t fc_rdev; dev_t fc_rdev;
} FsCred; } FsCred;
struct xattr_operations; typedef struct FsContext FsContext;
struct FsContext; typedef struct V9fsPath V9fsPath;
struct V9fsPath;
typedef struct extended_ops { typedef struct ExtendedOps {
int (*get_st_gen)(struct FsContext *, struct V9fsPath *, int (*get_st_gen)(FsContext *, V9fsPath *, mode_t, uint64_t *);
mode_t, uint64_t *); } ExtendedOps;
} extended_ops;
/* export flags */ /* export flags */
#define V9FS_IMMEDIATE_WRITEOUT 0x00000001 #define V9FS_IMMEDIATE_WRITEOUT 0x00000001
@ -67,6 +64,8 @@ typedef struct extended_ops {
typedef struct FileOperations FileOperations; typedef struct FileOperations FileOperations;
typedef struct XattrOperations XattrOperations;
/* /*
* Structure to store the various fsdev's passed through command line. * Structure to store the various fsdev's passed through command line.
*/ */
@ -80,24 +79,23 @@ typedef struct FsDriverEntry {
mode_t dmode; mode_t dmode;
} FsDriverEntry; } FsDriverEntry;
typedef struct FsContext struct FsContext {
{
uid_t uid; uid_t uid;
char *fs_root; char *fs_root;
int export_flags; int export_flags;
struct xattr_operations **xops; XattrOperations **xops;
struct extended_ops exops; ExtendedOps exops;
FsThrottle *fst; FsThrottle *fst;
/* fs driver specific data */ /* fs driver specific data */
void *private; void *private;
mode_t fmode; mode_t fmode;
mode_t dmode; mode_t dmode;
} FsContext; };
typedef struct V9fsPath { struct V9fsPath {
uint16_t size; uint16_t size;
char *data; char *data;
} V9fsPath; };
typedef union V9fsFidOpenState V9fsFidOpenState; typedef union V9fsFidOpenState V9fsFidOpenState;
@ -105,9 +103,9 @@ void cred_init(FsCred *);
struct FileOperations struct FileOperations
{ {
int (*parse_opts)(QemuOpts *, struct FsDriverEntry *); int (*parse_opts)(QemuOpts *, FsDriverEntry *, Error **errp);
int (*init)(struct FsContext *); int (*init)(FsContext *, Error **errp);
void (*cleanup)(struct FsContext *); void (*cleanup)(FsContext *);
int (*lstat)(FsContext *, V9fsPath *, struct stat *); int (*lstat)(FsContext *, V9fsPath *, struct stat *);
ssize_t (*readlink)(FsContext *, V9fsPath *, char *, size_t); ssize_t (*readlink)(FsContext *, V9fsPath *, char *, size_t);
int (*chmod)(FsContext *, V9fsPath *, FsCred *); int (*chmod)(FsContext *, V9fsPath *, FsCred *);

View File

@ -37,6 +37,7 @@ int qemu_fsdev_add(QemuOpts *opts)
const char *fsdriver = qemu_opt_get(opts, "fsdriver"); const char *fsdriver = qemu_opt_get(opts, "fsdriver");
const char *writeout = qemu_opt_get(opts, "writeout"); const char *writeout = qemu_opt_get(opts, "writeout");
bool ro = qemu_opt_get_bool(opts, "readonly", 0); bool ro = qemu_opt_get_bool(opts, "readonly", 0);
Error *local_err = NULL;
if (!fsdev_id) { if (!fsdev_id) {
error_report("fsdev: No id specified"); 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) {
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->fse.fsdev_id);
g_free(fsle); g_free(fsle);
return -1; return -1;

113
gdbstub.c
View File

@ -21,6 +21,7 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "cpu.h" #include "cpu.h"
#include "trace-root.h"
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
#include "qemu.h" #include "qemu.h"
#else #else
@ -287,21 +288,6 @@ static int gdb_signal_to_target (int sig)
return -1; 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 { typedef struct GDBRegisterState {
int base_reg; int base_reg;
int num_regs; int num_regs;
@ -410,10 +396,13 @@ int use_gdb_syscalls(void)
/* Resume execution. */ /* Resume execution. */
static inline void gdb_continue(GDBState *s) static inline void gdb_continue(GDBState *s)
{ {
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
s->running_state = 1; s->running_state = 1;
trace_gdbstub_op_continue();
#else #else
if (!runstate_needs_reset()) { if (!runstate_needs_reset()) {
trace_gdbstub_op_continue();
vm_start(); vm_start();
} }
#endif #endif
@ -434,6 +423,7 @@ static int gdb_continue_partial(GDBState *s, char *newstates)
*/ */
CPU_FOREACH(cpu) { CPU_FOREACH(cpu) {
if (newstates[cpu->cpu_index] == 's') { if (newstates[cpu->cpu_index] == 's') {
trace_gdbstub_op_stepping(cpu->cpu_index);
cpu_single_step(cpu, sstep_flags); cpu_single_step(cpu, sstep_flags);
} }
} }
@ -452,11 +442,13 @@ static int gdb_continue_partial(GDBState *s, char *newstates)
case 1: case 1:
break; /* nothing to do here */ break; /* nothing to do here */
case 's': case 's':
trace_gdbstub_op_stepping(cpu->cpu_index);
cpu_single_step(cpu, sstep_flags); cpu_single_step(cpu, sstep_flags);
cpu_resume(cpu); cpu_resume(cpu);
flag = 1; flag = 1;
break; break;
case 'c': case 'c':
trace_gdbstub_op_continue_cpu(cpu->cpu_index);
cpu_resume(cpu); cpu_resume(cpu);
flag = 1; flag = 1;
break; 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 */ /* 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; int csum, i;
uint8_t *p; uint8_t *p;
if (dump && trace_event_get_state_backends(TRACE_GDBSTUB_IO_BINARYREPLY)) {
hexdump(buf, len, trace_gdbstub_io_binaryreply);
}
for(;;) { for(;;) {
p = s->last_packet; p = s->last_packet;
*(p++) = '$'; *(p++) = '$';
@ -576,9 +605,9 @@ static int put_packet_binary(GDBState *s, const char *buf, int len)
/* return -1 if error, 0 if OK */ /* return -1 if error, 0 if OK */
static int put_packet(GDBState *s, const char *buf) 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. */ /* 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; uint8_t *registers;
target_ulong addr, len; target_ulong addr, len;
trace_gdbstub_io_command(line_buf);
gdb_debug("command='%s'\n", line_buf);
p = line_buf; p = line_buf;
ch = *p++; ch = *p++;
@ -999,7 +1027,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
} }
s->signal = 0; s->signal = 0;
gdb_continue(s); gdb_continue(s);
return RS_IDLE; return RS_IDLE;
case 'C': case 'C':
s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16)); s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16));
if (s->signal == -1) 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); cpu_single_step(s->c_cpu, sstep_flags);
gdb_continue(s); gdb_continue(s);
return RS_IDLE; return RS_IDLE;
case 'F': case 'F':
{ {
target_ulong ret; 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, len = snprintf((char *)mem_buf, sizeof(buf) / 2,
"CPU#%d [%s]", cpu->cpu_index, "CPU#%d [%s]", cpu->cpu_index,
cpu->halted ? "halted " : "running"); cpu->halted ? "halted " : "running");
trace_gdbstub_op_extra_info((char *)mem_buf);
memtohex(buf, mem_buf, len); memtohex(buf, mem_buf, len);
put_packet(s, buf); put_packet(s, buf);
} }
@ -1350,7 +1379,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf)
buf[0] = 'l'; buf[0] = 'l';
len = memtox(buf + 1, xml + addr, total_len - addr); 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; break;
} }
if (is_query_packet(p, "Attached", ':')) { if (is_query_packet(p, "Attached", ':')) {
@ -1407,29 +1436,38 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state)
type = ""; type = "";
break; break;
} }
trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu),
(target_ulong)cpu->watchpoint_hit->vaddr);
snprintf(buf, sizeof(buf), snprintf(buf, sizeof(buf),
"T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";",
GDB_SIGNAL_TRAP, cpu_gdb_index(cpu), type, GDB_SIGNAL_TRAP, cpu_gdb_index(cpu), type,
(target_ulong)cpu->watchpoint_hit->vaddr); (target_ulong)cpu->watchpoint_hit->vaddr);
cpu->watchpoint_hit = NULL; cpu->watchpoint_hit = NULL;
goto send_packet; goto send_packet;
} else {
trace_gdbstub_hit_break();
} }
tb_flush(cpu); tb_flush(cpu);
ret = GDB_SIGNAL_TRAP; ret = GDB_SIGNAL_TRAP;
break; break;
case RUN_STATE_PAUSED: case RUN_STATE_PAUSED:
trace_gdbstub_hit_paused();
ret = GDB_SIGNAL_INT; ret = GDB_SIGNAL_INT;
break; break;
case RUN_STATE_SHUTDOWN: case RUN_STATE_SHUTDOWN:
trace_gdbstub_hit_shutdown();
ret = GDB_SIGNAL_QUIT; ret = GDB_SIGNAL_QUIT;
break; break;
case RUN_STATE_IO_ERROR: case RUN_STATE_IO_ERROR:
trace_gdbstub_hit_io_error();
ret = GDB_SIGNAL_IO; ret = GDB_SIGNAL_IO;
break; break;
case RUN_STATE_WATCHDOG: case RUN_STATE_WATCHDOG:
trace_gdbstub_hit_watchdog();
ret = GDB_SIGNAL_ALRM; ret = GDB_SIGNAL_ALRM;
break; break;
case RUN_STATE_INTERNAL_ERROR: case RUN_STATE_INTERNAL_ERROR:
trace_gdbstub_hit_internal_error();
ret = GDB_SIGNAL_ABRT; ret = GDB_SIGNAL_ABRT;
break; break;
case RUN_STATE_SAVE_VM: 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; ret = GDB_SIGNAL_XCPU;
break; break;
default: default:
trace_gdbstub_hit_unknown(state);
ret = GDB_SIGNAL_UNKNOWN; ret = GDB_SIGNAL_UNKNOWN;
break; 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 /* Waiting for a response to the last packet. If we see the start
of a new command then abandon the previous response. */ of a new command then abandon the previous response. */
if (ch == '-') { 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); put_buffer(s, (uint8_t *)s->last_packet, s->last_packet_len);
} else if (ch == '+') { } else if (ch == '+') {
gdb_debug("Got ACK\n"); trace_gdbstub_io_got_ack();
} else { } else {
gdb_debug("Got '%c' when expecting ACK/NACK\n", ch); trace_gdbstub_io_got_unexpected((uint8_t)ch);
} }
if (ch == '+' || ch == '$') if (ch == '+' || ch == '$')
@ -1566,7 +1605,7 @@ static void gdb_read_byte(GDBState *s, int ch)
s->line_sum = 0; s->line_sum = 0;
s->state = RS_GETLINE; s->state = RS_GETLINE;
} else { } else {
gdb_debug("received garbage between packets: 0x%x\n", ch); trace_gdbstub_err_garbage((uint8_t)ch);
} }
break; break;
case RS_GETLINE: case RS_GETLINE:
@ -1582,7 +1621,7 @@ static void gdb_read_byte(GDBState *s, int ch)
/* end of command, start of checksum*/ /* end of command, start of checksum*/
s->state = RS_CHKSUM1; s->state = RS_CHKSUM1;
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { } 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; s->state = RS_IDLE;
} else { } else {
/* unescaped command character */ /* unescaped command character */
@ -1596,7 +1635,7 @@ static void gdb_read_byte(GDBState *s, int ch)
s->state = RS_CHKSUM1; s->state = RS_CHKSUM1;
} else if (s->line_buf_index >= sizeof(s->line_buf) - 1) { } else if (s->line_buf_index >= sizeof(s->line_buf) - 1) {
/* command buffer overrun */ /* command buffer overrun */
gdb_debug("command buffer overrun, dropping command\n"); trace_gdbstub_err_overrun();
s->state = RS_IDLE; s->state = RS_IDLE;
} else { } else {
/* parse escaped character and leave escape state */ /* parse escaped character and leave escape state */
@ -1608,18 +1647,18 @@ static void gdb_read_byte(GDBState *s, int ch)
case RS_GETLINE_RLE: case RS_GETLINE_RLE:
if (ch < ' ') { if (ch < ' ') {
/* invalid RLE count encoding */ /* 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; s->state = RS_GETLINE;
} else { } else {
/* decode repeat length */ /* decode repeat length */
int repeat = (unsigned char)ch - ' ' + 3; int repeat = (unsigned char)ch - ' ' + 3;
if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) { if (s->line_buf_index + repeat >= sizeof(s->line_buf) - 1) {
/* that many repeats would overrun the command buffer */ /* that many repeats would overrun the command buffer */
gdb_debug("command buffer overrun, dropping command\n"); trace_gdbstub_err_overrun();
s->state = RS_IDLE; s->state = RS_IDLE;
} else if (s->line_buf_index < 1) { } else if (s->line_buf_index < 1) {
/* got a repeat but we have nothing to repeat */ /* 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; s->state = RS_GETLINE;
} else { } else {
/* repeat the last character */ /* repeat the last character */
@ -1634,7 +1673,7 @@ static void gdb_read_byte(GDBState *s, int ch)
case RS_CHKSUM1: case RS_CHKSUM1:
/* get high hex digit of checksum */ /* get high hex digit of checksum */
if (!isxdigit(ch)) { if (!isxdigit(ch)) {
gdb_debug("got invalid command checksum digit\n"); trace_gdbstub_err_checksum_invalid((uint8_t)ch);
s->state = RS_GETLINE; s->state = RS_GETLINE;
break; break;
} }
@ -1645,14 +1684,14 @@ static void gdb_read_byte(GDBState *s, int ch)
case RS_CHKSUM2: case RS_CHKSUM2:
/* get low hex digit of checksum */ /* get low hex digit of checksum */
if (!isxdigit(ch)) { if (!isxdigit(ch)) {
gdb_debug("got invalid command checksum digit\n"); trace_gdbstub_err_checksum_invalid((uint8_t)ch);
s->state = RS_GETLINE; s->state = RS_GETLINE;
break; break;
} }
s->line_csum |= fromhex(ch); s->line_csum |= fromhex(ch);
if (s->line_csum != (s->line_sum & 0xff)) { 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 */ /* send NAK reply */
reply = '-'; reply = '-';
put_buffer(s, &reply, 1); put_buffer(s, &reply, 1);
@ -1686,6 +1725,8 @@ void gdb_exit(CPUArchState *env, int code)
} }
#endif #endif
trace_gdbstub_op_exiting((uint8_t)code);
snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code); snprintf(buf, sizeof(buf), "W%02x", (uint8_t)code);
put_packet(s, buf); put_packet(s, buf);
@ -1944,6 +1985,8 @@ static const TypeInfo char_gdb_type_info = {
int gdbserver_start(const char *device) int gdbserver_start(const char *device)
{ {
trace_gdbstub_op_start(device);
GDBState *s; GDBState *s;
char gdbstub_device_name[128]; char gdbstub_device_name[128];
Chardev *chr = NULL; Chardev *chr = NULL;

View File

@ -663,39 +663,6 @@ STEXI
@item sum @var{addr} @var{size} @item sum @var{addr} @var{size}
@findex sum @findex sum
Compute the checksum of a memory region. 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 ETEXI
{ {

6
hmp.c
View File

@ -2318,7 +2318,6 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
{ {
BlockBackend *blk; BlockBackend *blk;
BlockBackend *local_blk = NULL; BlockBackend *local_blk = NULL;
AioContext *aio_context;
const char* device = qdict_get_str(qdict, "device"); const char* device = qdict_get_str(qdict, "device");
const char* command = qdict_get_str(qdict, "command"); const char* command = qdict_get_str(qdict, "command");
Error *err = NULL; 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 * Notably absent: Proper permission management. This is sad, but it seems
* almost impossible to achieve without changing the semantics and thereby * 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); qemuio_command(blk, command);
aio_context_release(aio_context);
fail: fail:
blk_unref(local_blk); blk_unref(local_blk);
hmp_handle_error(mon, &err); hmp_handle_error(mon, &err);

View File

@ -41,10 +41,10 @@
#define BTRFS_SUPER_MAGIC 0x9123683E #define BTRFS_SUPER_MAGIC 0x9123683E
#endif #endif
struct handle_data { typedef struct HandleData {
int mountfd; int mountfd;
int handle_bytes; int handle_bytes;
}; } HandleData;
static inline int name_to_handle(int dirfd, const char *name, static inline int name_to_handle(int dirfd, const char *name,
struct file_handle *fh, int *mnt_id, int flags) 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) struct stat *stbuf)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
if (fd < 0) { if (fd < 0) {
@ -94,7 +94,7 @@ static ssize_t handle_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
char *buf, size_t bufsz) char *buf, size_t bufsz)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
if (fd < 0) { if (fd < 0) {
@ -118,7 +118,7 @@ static int handle_closedir(FsContext *ctx, V9fsFidOpenState *fs)
static int handle_open(FsContext *ctx, V9fsPath *fs_path, static int handle_open(FsContext *ctx, V9fsPath *fs_path,
int flags, V9fsFidOpenState *fs) 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); fs->fd = open_by_handle(data->mountfd, fs_path->data, flags);
return fs->fd; 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) static int handle_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
if (fd < 0) { if (fd < 0) {
@ -222,7 +222,7 @@ static int handle_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
const char *name, FsCred *credp) const char *name, FsCred *credp)
{ {
int dirfd, ret; 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); dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
if (dirfd < 0) { if (dirfd < 0) {
@ -240,7 +240,7 @@ static int handle_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
const char *name, FsCred *credp) const char *name, FsCred *credp)
{ {
int dirfd, ret; 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); dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
if (dirfd < 0) { if (dirfd < 0) {
@ -272,7 +272,7 @@ static int handle_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
{ {
int ret; int ret;
int dirfd, fd; 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); dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
if (dirfd < 0) { 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) V9fsPath *dir_path, const char *name, FsCred *credp)
{ {
int fd, dirfd, ret; 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); dirfd = open_by_handle(data->mountfd, dir_path->data, O_PATH);
if (dirfd < 0) { if (dirfd < 0) {
@ -322,7 +322,7 @@ static int handle_link(FsContext *ctx, V9fsPath *oldpath,
V9fsPath *dirpath, const char *name) V9fsPath *dirpath, const char *name)
{ {
int oldfd, newdirfd, ret; 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); oldfd = open_by_handle(data->mountfd, oldpath->data, O_PATH);
if (oldfd < 0) { 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) static int handle_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK | O_WRONLY);
if (fd < 0) { 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) static int handle_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_PATH);
if (fd < 0) { if (fd < 0) {
@ -379,7 +379,7 @@ static int handle_utimensat(FsContext *ctx, V9fsPath *fs_path,
{ {
int ret; int ret;
int fd; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
if (fd < 0) { if (fd < 0) {
@ -418,7 +418,7 @@ static int handle_statfs(FsContext *ctx, V9fsPath *fs_path,
struct statfs *stbuf) struct statfs *stbuf)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
if (fd < 0) { 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) const char *name, void *value, size_t size)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
if (fd < 0) { if (fd < 0) {
@ -448,7 +448,7 @@ static ssize_t handle_llistxattr(FsContext *ctx, V9fsPath *fs_path,
void *value, size_t size) void *value, size_t size)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
if (fd < 0) { 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) void *value, size_t size, int flags)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
if (fd < 0) { if (fd < 0) {
@ -478,7 +478,7 @@ static int handle_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
const char *name) const char *name)
{ {
int fd, ret; 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); fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK);
if (fd < 0) { if (fd < 0) {
@ -495,7 +495,7 @@ static int handle_name_to_path(FsContext *ctx, V9fsPath *dir_path,
char *buffer; char *buffer;
struct file_handle *fh; struct file_handle *fh;
int dirfd, ret, mnt_id; int dirfd, ret, mnt_id;
struct handle_data *data = (struct handle_data *)ctx->private; HandleData *data = (HandleData *) ctx->private;
/* "." and ".." are not allowed */ /* "." and ".." are not allowed */
if (!strcmp(name, ".") || !strcmp(name, "..")) { if (!strcmp(name, ".") || !strcmp(name, "..")) {
@ -536,7 +536,7 @@ static int handle_renameat(FsContext *ctx, V9fsPath *olddir,
const char *new_name) const char *new_name)
{ {
int olddirfd, newdirfd, ret; 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); olddirfd = open_by_handle(data->mountfd, olddir->data, O_PATH);
if (olddirfd < 0) { if (olddirfd < 0) {
@ -557,7 +557,7 @@ static int handle_unlinkat(FsContext *ctx, V9fsPath *dir,
const char *name, int flags) const char *name, int flags)
{ {
int dirfd, ret; int dirfd, ret;
struct handle_data *data = (struct handle_data *)ctx->private; HandleData *data = (HandleData *) ctx->private;
int rflags; int rflags;
dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); dirfd = open_by_handle(data->mountfd, dir->data, O_PATH);
@ -604,12 +604,12 @@ static int handle_ioc_getversion(FsContext *ctx, V9fsPath *path,
#endif #endif
} }
static int handle_init(FsContext *ctx) static int handle_init(FsContext *ctx, Error **errp)
{ {
int ret, mnt_id; int ret, mnt_id;
struct statfs stbuf; struct statfs stbuf;
struct file_handle fh; 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); data->mountfd = open(ctx->fs_root, O_DIRECTORY);
if (data->mountfd < 0) { if (data->mountfd < 0) {
@ -646,17 +646,19 @@ out:
static void handle_cleanup(FsContext *ctx) static void handle_cleanup(FsContext *ctx)
{ {
struct handle_data *data = ctx->private; HandleData *data = ctx->private;
close(data->mountfd); close(data->mountfd);
g_free(data); 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 *sec_model = qemu_opt_get(opts, "security_model");
const char *path = qemu_opt_get(opts, "path"); const char *path = qemu_opt_get(opts, "path");
warn_report("handle backend is deprecated");
if (sec_model) { if (sec_model) {
error_report("Invalid argument security_model specified with handle fsdriver"); error_report("Invalid argument security_model specified with handle fsdriver");
return -1; return -1;

View File

@ -1400,13 +1400,14 @@ static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
#endif #endif
} }
static int local_init(FsContext *ctx) static int local_init(FsContext *ctx, Error **errp)
{ {
struct statfs stbuf; struct statfs stbuf;
LocalData *data = g_malloc(sizeof(*data)); LocalData *data = g_malloc(sizeof(*data));
data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY); data->mountfd = open(ctx->fs_root, O_DIRECTORY | O_RDONLY);
if (data->mountfd == -1) { if (data->mountfd == -1) {
error_setg_errno(errp, errno, "failed to open '%s'", ctx->fs_root);
goto err; goto err;
} }
@ -1459,16 +1460,21 @@ static void local_cleanup(FsContext *ctx)
g_free(data); 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 *sec_model = qemu_opt_get(opts, "security_model");
const char *path = qemu_opt_get(opts, "path"); const char *path = qemu_opt_get(opts, "path");
Error *err = NULL; Error *local_err = NULL;
if (!sec_model) { if (!sec_model) {
error_report("Security model not specified, local fs needs security model"); error_setg(errp, "security_model property not set");
error_printf("valid options are:" error_append_security_model_hint(errp);
"\tsecurity_model=[passthrough|mapped-xattr|mapped-file|none]\n");
return -1; return -1;
} }
@ -1482,20 +1488,20 @@ static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
} else if (!strcmp(sec_model, "mapped-file")) { } else if (!strcmp(sec_model, "mapped-file")) {
fse->export_flags |= V9FS_SM_MAPPED_FILE; fse->export_flags |= V9FS_SM_MAPPED_FILE;
} else { } else {
error_report("Invalid security model %s specified", sec_model); error_setg(errp, "invalid security_model property '%s'", sec_model);
error_printf("valid options are:" error_append_security_model_hint(errp);
"\t[passthrough|mapped-xattr|mapped-file|none]\n");
return -1; return -1;
} }
if (!path) { if (!path) {
error_report("fsdev: No path specified"); error_setg(errp, "path property not set");
return -1; return -1;
} }
fsdev_throttle_parse_opts(opts, &fse->fst, &err); fsdev_throttle_parse_opts(opts, &fse->fst, &local_err);
if (err) { if (local_err) {
error_reportf_err(err, "Throttle configuration is not valid: "); error_propagate(errp, local_err);
error_prepend(errp, "invalid throttle configuration: ");
return -1; 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; qemu_opt_get_number(opts, "dmode", SM_LOCAL_DIR_MODE_BITS) & 0777;
} else { } else {
if (qemu_opt_find(opts, "fmode")) { 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; return -1;
} }
if (qemu_opt_find(opts, "dmode")) { 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; return -1;
} }
} }

View File

@ -1083,25 +1083,25 @@ static int proxy_ioc_getversion(FsContext *fs_ctx, V9fsPath *path,
return err; return err;
} }
static int connect_namedsocket(const char *path) static int connect_namedsocket(const char *path, Error **errp)
{ {
int sockfd, size; int sockfd, size;
struct sockaddr_un helper; struct sockaddr_un helper;
if (strlen(path) >= sizeof(helper.sun_path)) { if (strlen(path) >= sizeof(helper.sun_path)) {
error_report("Socket name too long"); error_setg(errp, "socket name too long");
return -1; return -1;
} }
sockfd = socket(AF_UNIX, SOCK_STREAM, 0); sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sockfd < 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; return -1;
} }
strcpy(helper.sun_path, path); strcpy(helper.sun_path, path);
helper.sun_family = AF_UNIX; helper.sun_family = AF_UNIX;
size = strlen(helper.sun_path) + sizeof(helper.sun_family); size = strlen(helper.sun_path) + sizeof(helper.sun_family);
if (connect(sockfd, (struct sockaddr *)&helper, size) < 0) { 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); close(sockfd);
return -1; return -1;
} }
@ -1111,17 +1111,27 @@ static int connect_namedsocket(const char *path)
return sockfd; 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 *socket = qemu_opt_get(opts, "socket");
const char *sock_fd = qemu_opt_get(opts, "sock_fd"); const char *sock_fd = qemu_opt_get(opts, "sock_fd");
if (!socket && !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; return -1;
} }
if (socket && sock_fd) { 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; return -1;
} }
if (socket) { if (socket) {
@ -1134,17 +1144,17 @@ static int proxy_parse_opts(QemuOpts *opts, struct FsDriverEntry *fs)
return 0; return 0;
} }
static int proxy_init(FsContext *ctx) static int proxy_init(FsContext *ctx, Error **errp)
{ {
V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy)); V9fsProxy *proxy = g_malloc(sizeof(V9fsProxy));
int sock_id; int sock_id;
if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) { if (ctx->export_flags & V9FS_PROXY_SOCK_NAME) {
sock_id = connect_namedsocket(ctx->fs_root); sock_id = connect_namedsocket(ctx->fs_root, errp);
} else { } else {
sock_id = atoi(ctx->fs_root); sock_id = atoi(ctx->fs_root);
if (sock_id < 0) { if (sock_id < 0) {
error_report("Socket descriptor not initialized"); error_setg(errp, "socket descriptor not initialized");
} }
} }
if (sock_id < 0) { if (sock_id < 0) {

View File

@ -514,7 +514,7 @@ static int synth_unlinkat(FsContext *ctx, V9fsPath *dir,
return -1; return -1;
} }
static int synth_init(FsContext *ctx) static int synth_init(FsContext *ctx, Error **errp)
{ {
QLIST_INIT(&synth_root.child); QLIST_INIT(&synth_root.child);
qemu_mutex_init(&synth_mutex); qemu_mutex_init(&synth_mutex);

View File

@ -16,8 +16,7 @@
#include "qemu/xattr.h" #include "qemu/xattr.h"
typedef struct xattr_operations struct XattrOperations {
{
const char *name; const char *name;
ssize_t (*getxattr)(FsContext *ctx, const char *path, ssize_t (*getxattr)(FsContext *ctx, const char *path,
const char *name, void *value, size_t size); const char *name, void *value, size_t size);
@ -27,7 +26,7 @@ typedef struct xattr_operations
void *value, size_t size, int flags); void *value, size_t size, int flags);
int (*removexattr)(FsContext *ctx, int (*removexattr)(FsContext *ctx,
const char *path, const char *name); const char *path, const char *name);
} XattrOperations; };
ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path, ssize_t local_getxattr_nofollow(FsContext *ctx, const char *path,
const char *name, void *value, size_t size); const char *name, void *value, size_t size);

View File

@ -41,7 +41,7 @@ enum {
Oappend = 0x80, 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; ssize_t ret;
va_list ap; va_list ap;
@ -53,7 +53,7 @@ ssize_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
return ret; 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; ssize_t ret;
va_list ap; va_list ap;
@ -99,10 +99,10 @@ static int omode_to_uflags(int8_t mode)
return ret; return ret;
} }
struct dotl_openflag_map { typedef struct DotlOpenflagMap {
int dotl_flag; int dotl_flag;
int open_flag; int open_flag;
}; } DotlOpenflagMap;
static int dotl_to_open_flags(int flags) 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; int oflags = flags & O_ACCMODE;
struct dotl_openflag_map dotl_oflag_map[] = { DotlOpenflagMap dotl_oflag_map[] = {
{ P9_DOTL_CREATE, O_CREAT }, { P9_DOTL_CREATE, O_CREAT },
{ P9_DOTL_EXCL, O_EXCL }, { P9_DOTL_EXCL, O_EXCL },
{ P9_DOTL_NOCTTY , O_NOCTTY }, { P9_DOTL_NOCTTY , O_NOCTTY },
@ -3473,14 +3473,12 @@ void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr)
if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) || if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
(pdu_co_handlers[pdu->id] == NULL)) { (pdu_co_handlers[pdu->id] == NULL)) {
handler = v9fs_op_not_supp; handler = v9fs_op_not_supp;
} else if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
handler = v9fs_fs_ro;
} else { } else {
handler = pdu_co_handlers[pdu->id]; 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); qemu_co_queue_init(&pdu->complete);
co = qemu_coroutine_create(handler, pdu); co = qemu_coroutine_create(handler, pdu);
qemu_coroutine_enter(co); qemu_coroutine_enter(co);
@ -3544,9 +3542,9 @@ int v9fs_device_realize_common(V9fsState *s, Error **errp)
s->fid_list = NULL; s->fid_list = NULL;
qemu_co_rwlock_init(&s->rename_lock); qemu_co_rwlock_init(&s->rename_lock);
if (s->ops->init(&s->ctx) < 0) { if (s->ops->init(&s->ctx, errp) < 0) {
error_setg(errp, "9pfs Failed to initialize fs-driver with id:%s" error_prepend(errp, "cannot initialize fsdev '%s': ",
" and export path:%s", s->fsconf.fsdev_id, s->ctx.fs_root); s->fsconf.fsdev_id);
goto out; goto out;
} }

View File

@ -94,10 +94,10 @@ enum {
P9_QTFILE = 0x00, P9_QTFILE = 0x00,
}; };
enum p9_proto_version { typedef enum P9ProtoVersion {
V9FS_PROTO_2000U = 0x01, V9FS_PROTO_2000U = 0x01,
V9FS_PROTO_2000L = 0x02, V9FS_PROTO_2000L = 0x02,
}; } P9ProtoVersion;
#define P9_NOTAG UINT16_MAX #define P9_NOTAG UINT16_MAX
#define P9_NOFID UINT32_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 V9fsPDU V9fsPDU;
typedef struct V9fsState V9fsState; typedef struct V9fsState V9fsState;
typedef struct V9fsTransport V9fsTransport;
typedef struct { typedef struct {
uint32_t size_le; uint32_t size_le;
@ -238,10 +239,10 @@ struct V9fsState
FileOperations *ops; FileOperations *ops;
FsContext ctx; FsContext ctx;
char *tag; char *tag;
enum p9_proto_version proto_version; P9ProtoVersion proto_version;
int32_t msize; int32_t msize;
V9fsPDU pdus[MAX_REQ]; V9fsPDU pdus[MAX_REQ];
const struct V9fsTransport *transport; const V9fsTransport *transport;
/* /*
* lock ensuring atomic path update * lock ensuring atomic path update
* on rename. * on rename.
@ -348,8 +349,6 @@ int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath,
int v9fs_device_realize_common(V9fsState *s, Error **errp); int v9fs_device_realize_common(V9fsState *s, Error **errp);
void v9fs_device_unrealize_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); V9fsPDU *pdu_alloc(V9fsState *s);
void pdu_free(V9fsPDU *pdu); void pdu_free(V9fsPDU *pdu);
void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr); void pdu_submit(V9fsPDU *pdu, P9MsgHeader *hdr);
@ -367,8 +366,7 @@ struct V9fsTransport {
void (*push_and_notify)(V9fsPDU *pdu); void (*push_and_notify)(V9fsPDU *pdu);
}; };
static inline int v9fs_register_transport(V9fsState *s, static inline int v9fs_register_transport(V9fsState *s, const V9fsTransport *t)
const struct V9fsTransport *t)
{ {
assert(!s->transport); assert(!s->transport);
s->transport = t; s->transport = t;

View File

@ -20,8 +20,6 @@
#include "hw/virtio/virtio-access.h" #include "hw/virtio/virtio-access.h"
#include "qemu/iov.h" #include "qemu/iov.h"
static const struct V9fsTransport virtio_9p_transport;
static void virtio_9p_push_and_notify(V9fsPDU *pdu) static void virtio_9p_push_and_notify(V9fsPDU *pdu)
{ {
V9fsState *s = pdu->s; V9fsState *s = pdu->s;
@ -104,35 +102,6 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config)
g_free(cfg); 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) static void virtio_9p_reset(VirtIODevice *vdev)
{ {
V9fsVirtioState *v = (V9fsVirtioState *)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; *pniov = elem->out_num;
} }
static const struct V9fsTransport virtio_9p_transport = { static const V9fsTransport virtio_9p_transport = {
.pdu_vmarshal = virtio_pdu_vmarshal, .pdu_vmarshal = virtio_pdu_vmarshal,
.pdu_vunmarshal = virtio_pdu_vunmarshal, .pdu_vunmarshal = virtio_pdu_vunmarshal,
.init_in_iov_from_pdu = virtio_init_in_iov_from_pdu, .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, .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 */ /* virtio-9p device */
static const VMStateDescription vmstate_virtio_9p = { static const VMStateDescription vmstate_virtio_9p = {

View File

@ -233,7 +233,7 @@ static void xen_9pfs_push_and_notify(V9fsPDU *pdu)
qemu_bh_schedule(ring->bh); 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_vmarshal = xen_9pfs_pdu_vmarshal,
.pdu_vunmarshal = xen_9pfs_pdu_vunmarshal, .pdu_vunmarshal = xen_9pfs_pdu_vunmarshal,
.init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu, .init_in_iov_from_pdu = xen_9pfs_init_in_iov_from_pdu,

View File

@ -21,7 +21,6 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/acpi/acpi.h" #include "hw/acpi/acpi.h"
#include "hw/nvram/fw_cfg.h" #include "hw/nvram/fw_cfg.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"

View File

@ -7,6 +7,7 @@
* See the COPYING file in the top-level directory. * See the COPYING file in the top-level directory.
*/ */
#include "qemu/osdep.h"
#include "hw/acpi/ipmi.h" #include "hw/acpi/ipmi.h"
void build_acpi_ipmi_devices(Aml *table, BusState *bus) void build_acpi_ipmi_devices(Aml *table, BusState *bus)

View File

@ -78,9 +78,9 @@ static void clipper_init(MachineState *machine)
clipper_pci_map_irq); clipper_pci_map_irq);
/* Since we have an SRM-compatible PALcode, use the SRM epoch. */ /* 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"); isa_create_simple(isa_bus, "i8042");
/* VGA setup. Don't bother loading the bios. */ /* VGA setup. Don't bother loading the bios. */

View File

@ -385,6 +385,7 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp)
spi_table[i].irq)); spi_table[i].irq));
} }
qdev_set_nic_properties(DEVICE(&s->eth), &nd_table[0]);
object_property_set_bool(OBJECT(&s->eth), true, "realized", &err); object_property_set_bool(OBJECT(&s->eth), true, "realized", &err);
if (err) { if (err) {
error_propagate(errp, err); error_propagate(errp, err);

View File

@ -29,7 +29,6 @@
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "hw/sysbus.h" #include "hw/sysbus.h"
#include "exec/address-spaces.h" #include "exec/address-spaces.h"
#include "sysemu/sysemu.h"
#include "cpu.h" #include "cpu.h"
#undef REG_FMT #undef REG_FMT

View File

@ -453,6 +453,7 @@ build_spcr(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
AcpiSerialPortConsoleRedirection *spcr; AcpiSerialPortConsoleRedirection *spcr;
const MemMapEntry *uart_memmap = &vms->memmap[VIRT_UART]; const MemMapEntry *uart_memmap = &vms->memmap[VIRT_UART];
int irq = vms->irqmap[VIRT_UART] + ARM_SPI_BASE; int irq = vms->irqmap[VIRT_UART] + ARM_SPI_BASE;
int spcr_start = table_data->len;
spcr = acpi_data_push(table_data, sizeof(*spcr)); 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_device_id = 0xffff; /* PCI Device ID: not a PCI device */
spcr->pci_vendor_id = 0xffff; /* PCI Vendor 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, build_header(linker, table_data, (void *)(table_data->data + spcr_start),
NULL, NULL); "SPCR", table_data->len - spcr_start, 2, NULL, NULL);
} }
static void static void
@ -512,8 +513,8 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
mem_base += numa_info[i].node_mem; mem_base += numa_info[i].node_mem;
} }
build_header(linker, table_data, (void *)srat, "SRAT", build_header(linker, table_data, (void *)(table_data->data + srat_start),
table_data->len - srat_start, 3, NULL, NULL); "SRAT", table_data->len - srat_start, 3, NULL, NULL);
} }
static void static void
@ -522,6 +523,7 @@ build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
AcpiTableMcfg *mcfg; AcpiTableMcfg *mcfg;
const MemMapEntry *memmap = vms->memmap; const MemMapEntry *memmap = vms->memmap;
int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]); int len = sizeof(*mcfg) + sizeof(mcfg->allocation[0]);
int mcfg_start = table_data->len;
mcfg = acpi_data_push(table_data, len); mcfg = acpi_data_push(table_data, len);
mcfg->allocation[0].address = cpu_to_le64(memmap[VIRT_PCIE_ECAM].base); 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 mcfg->allocation[0].end_bus_number = (memmap[VIRT_PCIE_ECAM].size
/ PCIE_MMCFG_SIZE_MIN) - 1; / 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 */ /* GTDT */
@ -651,6 +654,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
static void build_fadt(GArray *table_data, BIOSLinker *linker, static void build_fadt(GArray *table_data, BIOSLinker *linker,
VirtMachineState *vms, unsigned dsdt_tbl_offset) VirtMachineState *vms, unsigned dsdt_tbl_offset)
{ {
int fadt_start = table_data->len;
AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); AcpiFadtDescriptorRev5_1 *fadt = acpi_data_push(table_data, sizeof(*fadt));
unsigned xdsdt_entry_offset = (char *)&fadt->x_dsdt - table_data->data; unsigned xdsdt_entry_offset = (char *)&fadt->x_dsdt - table_data->data;
uint16_t bootflags; 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, xdsdt_entry_offset, sizeof(fadt->x_dsdt),
ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset); ACPI_BUILD_TABLE_FILE, dsdt_tbl_offset);
build_header(linker, table_data, build_header(linker, table_data, (void *)(table_data->data + fadt_start),
(void *)fadt, "FACP", sizeof(*fadt), 5, NULL, NULL); "FACP", table_data->len - fadt_start, 5, NULL, NULL);
} }
/* DSDT */ /* DSDT */

View File

@ -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); 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() */ /* TODO create and connect IDE devices for ide_drive_get() */
xlnx_zcu102_binfo.ram_size = ram_size; xlnx_zcu102_binfo.ram_size = ram_size;

View File

@ -40,6 +40,10 @@
#define SATA_ADDR 0xFD0C0000 #define SATA_ADDR 0xFD0C0000
#define SATA_NUM_PORTS 2 #define SATA_NUM_PORTS 2
#define QSPI_ADDR 0xff0f0000
#define LQSPI_ADDR 0xc0000000
#define QSPI_IRQ 15
#define DP_ADDR 0xfd4a0000 #define DP_ADDR 0xfd4a0000
#define DP_IRQ 113 #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()); 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); object_initialize(&s->dp, sizeof(s->dp), TYPE_XLNX_DP);
qdev_set_parent_bus(DEVICE(&s->dp), sysbus_get_default()); 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); 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); object_property_set_bool(OBJECT(&s->dp), true, "realized", &err);
if (err) { if (err) {
error_propagate(errp, err); error_propagate(errp, err);

View File

@ -34,7 +34,6 @@
#include <math.h> #include <math.h>
//#include "driver.h" /* use M.A.M.E. */ //#include "driver.h" /* use M.A.M.E. */
#include "fmopl.h" #include "fmopl.h"
#include "qemu/osdep.h"
#ifndef PI #ifndef PI
#define PI 3.14159265358979323846 #define PI 3.14159265358979323846
#endif #endif

View File

@ -1,7 +1,6 @@
#ifndef FMOPL_H #ifndef FMOPL_H
#define FMOPL_H #define FMOPL_H
#include <stdint.h>
typedef void (*OPL_TIMERHANDLER)(void *param, int channel, double interval_Sec); typedef void (*OPL_TIMERHANDLER)(void *param, int channel, double interval_Sec);

View File

@ -24,7 +24,6 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "hw/i386/pc.h"
#include "hw/isa/isa.h" #include "hw/isa/isa.h"
#include "hw/audio/soundhw.h" #include "hw/audio/soundhw.h"
#include "audio/audio.h" #include "audio/audio.h"

View File

@ -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) bool resizable, Error **errp)
{ {
BlockBackend *blk = conf->blk; 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); ret = blk_set_perm(blk, perm, shared_perm, errp);
if (ret < 0) { if (ret < 0) {
return; return false;
} }
switch (conf->wce) { 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_enable_write_cache(blk, wce);
blk_set_on_error(blk, rerror, werror); 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, unsigned cyls_max, unsigned heads_max, unsigned secs_max,
Error **errp) Error **errp)
{ {
@ -129,15 +131,16 @@ void blkconf_geometry(BlockConf *conf, int *ptrans,
if (conf->cyls || conf->heads || conf->secs) { if (conf->cyls || conf->heads || conf->secs) {
if (conf->cyls < 1 || conf->cyls > cyls_max) { if (conf->cyls < 1 || conf->cyls > cyls_max) {
error_setg(errp, "cyls must be between 1 and %u", 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) { if (conf->heads < 1 || conf->heads > heads_max) {
error_setg(errp, "heads must be between 1 and %u", 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) { if (conf->secs < 1 || conf->secs > secs_max) {
error_setg(errp, "secs must be between 1 and %u", secs_max); error_setg(errp, "secs must be between 1 and %u", secs_max);
return; return false;
} }
} }
return true;
} }

View File

@ -76,7 +76,7 @@ static void notify_guest_bh(void *opaque)
} }
/* Context: QEMU global mutex held */ /* 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, VirtIOBlockDataPlane **dataplane,
Error **errp) Error **errp)
{ {
@ -91,11 +91,11 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
error_setg(errp, error_setg(errp,
"device is incompatible with iothread " "device is incompatible with iothread "
"(transport does not support notifiers)"); "(transport does not support notifiers)");
return; return false;
} }
if (!virtio_device_ioeventfd_enabled(vdev)) { if (!virtio_device_ioeventfd_enabled(vdev)) {
error_setg(errp, "ioeventfd is required for iothread"); error_setg(errp, "ioeventfd is required for iothread");
return; return false;
} }
/* If dataplane is (re-)enabled while the guest is running there could /* 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)) { if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
error_prepend(errp, "cannot start virtio-blk dataplane: "); error_prepend(errp, "cannot start virtio-blk dataplane: ");
return; return false;
} }
} }
/* Don't try if transport does not support notifiers. */ /* Don't try if transport does not support notifiers. */
if (!virtio_device_ioeventfd_enabled(vdev)) { if (!virtio_device_ioeventfd_enabled(vdev)) {
return; return false;
} }
s = g_new0(VirtIOBlockDataPlane, 1); 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); s->batch_notify_vqs = bitmap_new(conf->num_queues);
*dataplane = s; *dataplane = s;
return true;
} }
/* Context: QEMU global mutex held */ /* Context: QEMU global mutex held */

View File

@ -19,7 +19,7 @@
typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane; 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, VirtIOBlockDataPlane **dataplane,
Error **errp); Error **errp);
void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s); void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);

View File

@ -473,16 +473,13 @@ static void fd_revalidate(FDrive *drv)
static void fd_change_cb(void *opaque, bool load, Error **errp) static void fd_change_cb(void *opaque, bool load, Error **errp)
{ {
FDrive *drive = opaque; FDrive *drive = opaque;
Error *local_err = NULL;
if (!load) { if (!load) {
blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort); blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
} else { } else {
blkconf_apply_backend_options(drive->conf, if (!blkconf_apply_backend_options(drive->conf,
blk_is_read_only(drive->blk), false, blk_is_read_only(drive->blk), false,
&local_err); errp)) {
if (local_err) {
error_propagate(errp, local_err);
return; return;
} }
} }
@ -522,7 +519,6 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
FloppyDrive *dev = FLOPPY_DRIVE(qdev); FloppyDrive *dev = FLOPPY_DRIVE(qdev);
FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus); FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus);
FDrive *drive; FDrive *drive;
Error *local_err = NULL;
int ret; int ret;
if (dev->unit == -1) { 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.rerror = BLOCKDEV_ON_ERROR_AUTO;
dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO; dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
blkconf_apply_backend_options(&dev->conf, blk_is_read_only(dev->conf.blk), if (!blkconf_apply_backend_options(&dev->conf,
false, &local_err); blk_is_read_only(dev->conf.blk),
if (local_err) { false, errp)) {
error_propagate(errp, local_err);
return; return;
} }

View File

@ -240,6 +240,8 @@ static const FlashPartInfo known_devices[] = {
{ INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) }, { INFO("n25q128a13", 0x20ba18, 0, 64 << 10, 256, ER_4K) },
{ INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) }, { INFO("n25q256a11", 0x20bb19, 0, 64 << 10, 512, ER_4K) },
{ INFO("n25q256a13", 0x20ba19, 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("n25q128", 0x20ba18, 0, 64 << 10, 256, 0) },
{ INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) }, { INFO("n25q256a", 0x20ba19, 0, 64 << 10, 512, ER_4K) },
{ INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) }, { INFO("n25q512a", 0x20ba20, 0, 64 << 10, 1024, ER_4K) },
@ -331,7 +333,10 @@ typedef enum {
WRDI = 0x4, WRDI = 0x4,
RDSR = 0x5, RDSR = 0x5,
WREN = 0x6, WREN = 0x6,
BRRD = 0x16,
BRWR = 0x17,
JEDEC_READ = 0x9f, JEDEC_READ = 0x9f,
BULK_ERASE_60 = 0x60,
BULK_ERASE = 0xc7, BULK_ERASE = 0xc7,
READ_FSR = 0x70, READ_FSR = 0x70,
RDCR = 0x15, RDCR = 0x15,
@ -355,6 +360,8 @@ typedef enum {
DPP = 0xa2, DPP = 0xa2,
QPP = 0x32, QPP = 0x32,
QPP_4 = 0x34, QPP_4 = 0x34,
RDID_90 = 0x90,
RDID_AB = 0xab,
ERASE_4K = 0x20, ERASE_4K = 0x20,
ERASE4_4K = 0x21, ERASE4_4K = 0x21,
@ -405,6 +412,7 @@ typedef enum {
MAN_MACRONIX, MAN_MACRONIX,
MAN_NUMONYX, MAN_NUMONYX,
MAN_WINBOND, MAN_WINBOND,
MAN_SST,
MAN_GENERIC, MAN_GENERIC,
} Manufacturer; } Manufacturer;
@ -423,6 +431,7 @@ typedef struct Flash {
uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ]; uint8_t data[M25P80_INTERNAL_DATA_BUFFER_SZ];
uint32_t len; uint32_t len;
uint32_t pos; uint32_t pos;
bool data_read_loop;
uint8_t needed_bytes; uint8_t needed_bytes;
uint8_t cmd_in_progress; uint8_t cmd_in_progress;
uint32_t cur_addr; uint32_t cur_addr;
@ -475,6 +484,8 @@ static inline Manufacturer get_man(Flash *s)
return MAN_SPANSION; return MAN_SPANSION;
case 0xC2: case 0xC2:
return MAN_MACRONIX; return MAN_MACRONIX;
case 0xBF:
return MAN_SST;
default: default:
return MAN_GENERIC; return MAN_GENERIC;
} }
@ -698,6 +709,7 @@ static void complete_collecting_data(Flash *s)
s->write_enable = false; s->write_enable = false;
} }
break; break;
case BRWR:
case EXTEND_ADDR_WRITE: case EXTEND_ADDR_WRITE:
s->ear = s->data[0]; s->ear = s->data[0];
break; break;
@ -710,6 +722,31 @@ static void complete_collecting_data(Flash *s)
case WEVCR: case WEVCR:
s->enh_volatile_cfg = s->data[0]; s->enh_volatile_cfg = s->data[0];
break; 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: default:
break; break;
} }
@ -925,6 +962,8 @@ static void decode_new_cmd(Flash *s, uint32_t value)
case PP4: case PP4:
case PP4_4: case PP4_4:
case DIE_ERASE: case DIE_ERASE:
case RDID_90:
case RDID_AB:
s->needed_bytes = get_addr_length(s); s->needed_bytes = get_addr_length(s);
s->pos = 0; s->pos = 0;
s->len = 0; s->len = 0;
@ -983,6 +1022,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
} }
s->pos = 0; s->pos = 0;
s->len = 1; s->len = 1;
s->data_read_loop = true;
s->state = STATE_READING_DATA; s->state = STATE_READING_DATA;
break; break;
@ -993,6 +1033,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
} }
s->pos = 0; s->pos = 0;
s->len = 1; s->len = 1;
s->data_read_loop = true;
s->state = STATE_READING_DATA; s->state = STATE_READING_DATA;
break; break;
@ -1015,6 +1056,7 @@ static void decode_new_cmd(Flash *s, uint32_t value)
s->state = STATE_READING_DATA; s->state = STATE_READING_DATA;
break; break;
case BULK_ERASE_60:
case BULK_ERASE: case BULK_ERASE:
if (s->write_enable) { if (s->write_enable) {
DB_PRINT_L(0, "chip erase\n"); 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: case EX_4BYTE_ADDR:
s->four_bytes_address_mode = false; s->four_bytes_address_mode = false;
break; break;
case BRRD:
case EXTEND_ADDR_READ: case EXTEND_ADDR_READ:
s->data[0] = s->ear; s->data[0] = s->ear;
s->pos = 0; s->pos = 0;
s->len = 1; s->len = 1;
s->state = STATE_READING_DATA; s->state = STATE_READING_DATA;
break; break;
case BRWR:
case EXTEND_ADDR_WRITE: case EXTEND_ADDR_WRITE:
if (s->write_enable) { if (s->write_enable) {
s->needed_bytes = 1; s->needed_bytes = 1;
@ -1133,6 +1177,7 @@ static int m25p80_cs(SSISlave *ss, bool select)
s->pos = 0; s->pos = 0;
s->state = STATE_IDLE; s->state = STATE_IDLE;
flash_sync_dirty(s, -1); flash_sync_dirty(s, -1);
s->data_read_loop = false;
} }
DB_PRINT_L(0, "%sselect\n", select ? "de" : ""); DB_PRINT_L(0, "%sselect\n", select ? "de" : "");
@ -1198,7 +1243,9 @@ static uint32_t m25p80_transfer8(SSISlave *ss, uint32_t tx)
s->pos++; s->pos++;
if (s->pos == s->len) { if (s->pos == s->len) {
s->pos = 0; s->pos = 0;
s->state = STATE_IDLE; if (!s->data_read_loop) {
s->state = STATE_IDLE;
}
} }
break; break;
@ -1269,11 +1316,38 @@ static Property m25p80_properties[] = {
DEFINE_PROP_END_OF_LIST(), 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 = { static const VMStateDescription vmstate_m25p80 = {
.name = "m25p80", .name = "m25p80",
.version_id = 0, .version_id = 0,
.minimum_version_id = 0, .minimum_version_id = 0,
.pre_save = m25p80_pre_save, .pre_save = m25p80_pre_save,
.pre_load = m25p80_pre_load,
.fields = (VMStateField[]) { .fields = (VMStateField[]) {
VMSTATE_UINT8(state, Flash), VMSTATE_UINT8(state, Flash),
VMSTATE_UINT8_ARRAY(data, Flash, M25P80_INTERNAL_DATA_BUFFER_SZ), 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_cr3nv, Flash),
VMSTATE_UINT8(spansion_cr4nv, Flash), VMSTATE_UINT8(spansion_cr4nv, Flash),
VMSTATE_END_OF_LIST() VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * []) {
&vmstate_m25p80_data_read_loop,
NULL
} }
}; };

View File

@ -34,8 +34,17 @@
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qemu/log.h"
#include "trace.h"
#include "nvme.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_process_sq(void *opaque);
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size) 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 (cq->irq_enabled) {
if (msix_enabled(&(n->parent_obj))) { if (msix_enabled(&(n->parent_obj))) {
trace_nvme_irq_msix(cq->vector);
msix_notify(&(n->parent_obj), cq->vector); msix_notify(&(n->parent_obj), cq->vector);
} else { } else {
trace_nvme_irq_pin();
pci_irq_pulse(&n->parent_obj); 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); trans_len = MIN(len, trans_len);
int num_prps = (len >> n->page_bits) + 1; int num_prps = (len >> n->page_bits) + 1;
if (!prp1) { if (unlikely(!prp1)) {
trace_nvme_err_invalid_prp();
return NVME_INVALID_FIELD | NVME_DNR; return NVME_INVALID_FIELD | NVME_DNR;
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr && } else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) { 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; len -= trans_len;
if (len) { if (len) {
if (!prp2) { if (unlikely(!prp2)) {
trace_nvme_err_invalid_prp2_missing();
goto unmap; goto unmap;
} }
if (len > n->page_size) { 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]); uint64_t prp_ent = le64_to_cpu(prp_list[i]);
if (i == n->max_prp_ents - 1 && len > n->page_size) { 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; 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]); 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; goto unmap;
} }
@ -154,7 +171,8 @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
i++; i++;
} }
} else { } else {
if (prp2 & (n->page_size - 1)) { if (unlikely(prp2 & (n->page_size - 1))) {
trace_nvme_err_invalid_prp2_align(prp2);
goto unmap; goto unmap;
} }
if (qsg->nsg) { if (qsg->nsg) {
@ -178,16 +196,20 @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
QEMUIOVector iov; QEMUIOVector iov;
uint16_t status = NVME_SUCCESS; uint16_t status = NVME_SUCCESS;
trace_nvme_dma_read(prp1, prp2);
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) { if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
return NVME_INVALID_FIELD | NVME_DNR; return NVME_INVALID_FIELD | NVME_DNR;
} }
if (qsg.nsg > 0) { 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; status = NVME_INVALID_FIELD | NVME_DNR;
} }
qemu_sglist_destroy(&qsg); qemu_sglist_destroy(&qsg);
} else { } 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; status = NVME_INVALID_FIELD | NVME_DNR;
} }
qemu_iovec_destroy(&iov); 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); uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
uint32_t aio_nlb = nlb << (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; 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; int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ; 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); 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; return NVME_LBA_RANGE | NVME_DNR;
} }
@ -336,7 +362,8 @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
NvmeNamespace *ns; NvmeNamespace *ns;
uint32_t nsid = le32_to_cpu(cmd->nsid); 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; 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: case NVME_CMD_READ:
return nvme_rw(n, ns, cmd, req); return nvme_rw(n, ns, cmd, req);
default: default:
trace_nvme_err_invalid_opc(cmd->opcode);
return NVME_INVALID_OPCODE | NVME_DNR; return NVME_INVALID_OPCODE | NVME_DNR;
} }
} }
@ -373,10 +401,13 @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
NvmeCQueue *cq; NvmeCQueue *cq;
uint16_t qid = le16_to_cpu(c->qid); 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; return NVME_INVALID_QID | NVME_DNR;
} }
trace_nvme_del_sq(qid);
sq = n->sq[qid]; sq = n->sq[qid];
while (!QTAILQ_EMPTY(&sq->out_req_list)) { while (!QTAILQ_EMPTY(&sq->out_req_list)) {
req = QTAILQ_FIRST(&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); uint16_t qflags = le16_to_cpu(c->sq_flags);
uint64_t prp1 = le64_to_cpu(c->prp1); 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; 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; 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; 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; 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; return NVME_INVALID_FIELD | NVME_DNR;
} }
sq = g_malloc0(sizeof(*sq)); sq = g_malloc0(sizeof(*sq));
@ -476,14 +514,17 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
NvmeCQueue *cq; NvmeCQueue *cq;
uint16_t qid = le16_to_cpu(c->qid); 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; return NVME_INVALID_CQID | NVME_DNR;
} }
cq = n->cq[qid]; 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; return NVME_INVALID_QUEUE_DEL;
} }
trace_nvme_del_cq(qid);
nvme_free_cq(cq, n); nvme_free_cq(cq, n);
return NVME_SUCCESS; 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); uint16_t qflags = le16_to_cpu(c->cq_flags);
uint64_t prp1 = le64_to_cpu(c->prp1); 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; 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; 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; 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; 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; 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 prp1 = le64_to_cpu(c->prp1);
uint64_t prp2 = le64_to_cpu(c->prp2); 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), return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
prp1, prp2); 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 prp1 = le64_to_cpu(c->prp1);
uint64_t prp2 = le64_to_cpu(c->prp2); 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; return NVME_INVALID_NSID | NVME_DNR;
} }
ns = &n->namespaces[nsid - 1]; ns = &n->namespaces[nsid - 1];
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns), return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
prp1, prp2); prp1, prp2);
} }
@ -573,6 +628,8 @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
uint16_t ret; uint16_t ret;
int i, j = 0; int i, j = 0;
trace_nvme_identify_nslist(min_nsid);
list = g_malloc0(data_len); list = g_malloc0(data_len);
for (i = 0; i < n->num_namespaces; i++) { for (i = 0; i < n->num_namespaces; i++) {
if (i < min_nsid) { if (i < min_nsid) {
@ -601,6 +658,7 @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
case 0x02: case 0x02:
return nvme_identify_nslist(n, c); return nvme_identify_nslist(n, c);
default: default:
trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
return NVME_INVALID_FIELD | NVME_DNR; return NVME_INVALID_FIELD | NVME_DNR;
} }
} }
@ -613,11 +671,14 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
switch (dw10) { switch (dw10) {
case NVME_VOLATILE_WRITE_CACHE: case NVME_VOLATILE_WRITE_CACHE:
result = blk_enable_write_cache(n->conf.blk); result = blk_enable_write_cache(n->conf.blk);
trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
break; break;
case NVME_NUMBER_OF_QUEUES: case NVME_NUMBER_OF_QUEUES:
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16)); result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
trace_nvme_getfeat_numq(result);
break; break;
default: default:
trace_nvme_err_invalid_getfeat(dw10);
return NVME_INVALID_FIELD | NVME_DNR; 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); blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
break; break;
case NVME_NUMBER_OF_QUEUES: 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 = req->cqe.result =
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16)); cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
break; break;
default: default:
trace_nvme_err_invalid_setfeat(dw10);
return NVME_INVALID_FIELD | NVME_DNR; return NVME_INVALID_FIELD | NVME_DNR;
} }
return NVME_SUCCESS; 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: case NVME_ADM_CMD_GET_FEATURES:
return nvme_get_feature(n, cmd, req); return nvme_get_feature(n, cmd, req);
default: default:
trace_nvme_err_invalid_admin_opc(cmd->opcode);
return NVME_INVALID_OPCODE | NVME_DNR; 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_bits = NVME_CC_MPS(n->bar.cc) + 12;
uint32_t page_size = 1 << page_bits; uint32_t page_size = 1 << page_bits;
if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq || if (unlikely(n->cq[0])) {
n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) || trace_nvme_err_startfail_cq();
NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) || return -1;
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) || if (unlikely(n->sq[0])) {
NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) || trace_nvme_err_startfail_sq();
NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) || return -1;
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->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; 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, static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
unsigned size) 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) { 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.intms |= data & 0xffffffff;
n->bar.intmc = n->bar.intms; n->bar.intmc = n->bar.intms;
trace_nvme_mmio_intm_set(data & 0xffffffff,
n->bar.intmc);
break; 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.intms &= ~(data & 0xffffffff);
n->bar.intmc = n->bar.intms; n->bar.intmc = n->bar.intms;
trace_nvme_mmio_intm_clr(data & 0xffffffff,
n->bar.intmc);
break; break;
case 0x14: case 0x14: /* CC */
trace_nvme_mmio_cfg(data & 0xffffffff);
/* Windows first sends data, then sends enable bit */ /* Windows first sends data, then sends enable bit */
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) && if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
!NVME_CC_SHN(data) && !NVME_CC_SHN(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)) { if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
n->bar.cc = data; 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; n->bar.csts = NVME_CSTS_FAILED;
} else { } else {
trace_nvme_mmio_start_success();
n->bar.csts = NVME_CSTS_READY; n->bar.csts = NVME_CSTS_READY;
} }
} else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) { } else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
trace_nvme_mmio_stopped();
nvme_clear_ctrl(n); nvme_clear_ctrl(n);
n->bar.csts &= ~NVME_CSTS_READY; n->bar.csts &= ~NVME_CSTS_READY;
} }
if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) { if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
nvme_clear_ctrl(n); trace_nvme_mmio_shutdown_set();
n->bar.cc = data; nvme_clear_ctrl(n);
n->bar.csts |= NVME_CSTS_SHST_COMPLETE; n->bar.cc = data;
n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
} else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) { } else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE; trace_nvme_mmio_shutdown_cleared();
n->bar.cc = data; n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
n->bar.cc = data;
} }
break; 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; n->bar.aqa = data & 0xffffffff;
trace_nvme_mmio_aqattr(data & 0xffffffff);
break; break;
case 0x28: case 0x28: /* ASQ */
n->bar.asq = data; n->bar.asq = data;
trace_nvme_mmio_asqaddr(data);
break; break;
case 0x2c: case 0x2c: /* ASQ hi */
n->bar.asq |= data << 32; n->bar.asq |= data << 32;
trace_nvme_mmio_asqaddr_hi(data, n->bar.asq);
break; break;
case 0x30: case 0x30: /* ACQ */
trace_nvme_mmio_acqaddr(data);
n->bar.acq = data; n->bar.acq = data;
break; break;
case 0x34: case 0x34: /* ACQ hi */
n->bar.acq |= data << 32; n->bar.acq |= data << 32;
trace_nvme_mmio_acqaddr_hi(data, n->bar.acq);
break; 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: default:
NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
"invalid MMIO write,"
" offset=0x%"PRIx64", data=%"PRIx64"",
offset, data);
break; break;
} }
} }
@ -812,9 +1015,26 @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
uint8_t *ptr = (uint8_t *)&n->bar; uint8_t *ptr = (uint8_t *)&n->bar;
uint64_t val = 0; 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)) { if (addr < sizeof(n->bar)) {
memcpy(&val, ptr + addr, size); 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; return val;
} }
@ -822,22 +1042,36 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
{ {
uint32_t qid; 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; return;
} }
if (((addr - 0x1000) >> 2) & 1) { if (((addr - 0x1000) >> 2) & 1) {
/* Completion queue doorbell write */
uint16_t new_head = val & 0xffff; uint16_t new_head = val & 0xffff;
int start_sqs; int start_sqs;
NvmeCQueue *cq; NvmeCQueue *cq;
qid = (addr - (0x1000 + (1 << 2))) >> 3; 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; return;
} }
cq = n->cq[qid]; 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; return;
} }
@ -855,16 +1089,27 @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
nvme_isr_notify(n, cq); nvme_isr_notify(n, cq);
} }
} else { } else {
/* Submission queue doorbell write */
uint16_t new_tail = val & 0xffff; uint16_t new_tail = val & 0xffff;
NvmeSQueue *sq; NvmeSQueue *sq;
qid = (addr - 0x1000) >> 3; 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; return;
} }
sq = n->sq[qid]; 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; 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); NvmeCtrl *n = NVME(pci_dev);
NvmeIdCtrl *id = &n->id_ctrl; NvmeIdCtrl *id = &n->id_ctrl;
@ -928,27 +1173,27 @@ static int nvme_init(PCIDevice *pci_dev)
int i; int i;
int64_t bs_size; int64_t bs_size;
uint8_t *pci_conf; uint8_t *pci_conf;
Error *local_err = NULL;
if (!n->conf.blk) { if (!n->conf.blk) {
return -1; error_setg(errp, "drive property not set");
return;
} }
bs_size = blk_getlength(n->conf.blk); bs_size = blk_getlength(n->conf.blk);
if (bs_size < 0) { if (bs_size < 0) {
return -1; error_setg(errp, "could not get backing file size");
return;
} }
blkconf_serial(&n->conf, &n->serial); blkconf_serial(&n->conf, &n->serial);
if (!n->serial) { if (!n->serial) {
return -1; error_setg(errp, "serial property not set");
return;
} }
blkconf_blocksizes(&n->conf); blkconf_blocksizes(&n->conf);
blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk), if (!blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
false, &local_err); false, errp)) {
if (local_err) { return;
error_report_err(local_err);
return -1;
} }
pci_conf = pci_dev->config; pci_conf = pci_dev->config;
@ -1046,7 +1291,6 @@ static int nvme_init(PCIDevice *pci_dev)
cpu_to_le64(n->ns_size >> cpu_to_le64(n->ns_size >>
id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds); id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds);
} }
return 0;
} }
static void nvme_exit(PCIDevice *pci_dev) 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); DeviceClass *dc = DEVICE_CLASS(oc);
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
pc->init = nvme_init; pc->realize = nvme_realize;
pc->exit = nvme_exit; pc->exit = nvme_exit;
pc->class_id = PCI_CLASS_STORAGE_EXPRESS; pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
pc->vendor_id = PCI_VENDOR_ID_INTEL; pc->vendor_id = PCI_VENDOR_ID_INTEL;

View File

@ -10,3 +10,103 @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
# hw/block/hd-geometry.c # 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_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" 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"

View File

@ -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"); error_setg(errp, "num-queues property must be larger than 0");
return; 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_serial(&conf->conf, &conf->serial);
blkconf_apply_backend_options(&conf->conf, if (!blkconf_apply_backend_options(&conf->conf,
blk_is_read_only(conf->conf.blk), true, blk_is_read_only(conf->conf.blk), true,
&err); errp)) {
if (err) {
error_propagate(errp, err);
return; return;
} }
s->original_wce = blk_enable_write_cache(conf->conf.blk); s->original_wce = blk_enable_write_cache(conf->conf.blk);
blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err); if (!blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, errp)) {
if (err) {
error_propagate(errp, err);
return; return;
} }
blkconf_blocksizes(&conf->conf); 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, virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
sizeof(struct virtio_blk_config)); 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; s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
for (i = 0; i < conf->num_queues; i++) { 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); virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
if (err != NULL) { if (err != NULL) {
@ -1012,6 +1023,7 @@ static Property virtio_blk_properties[] = {
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
true), true),
DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1), 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, DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD,
IOThread *), IOThread *),
DEFINE_PROP_END_OF_LIST(), DEFINE_PROP_END_OF_LIST(),

View File

@ -27,10 +27,12 @@
#include "hw/xen/xen_backend.h" #include "hw/xen/xen_backend.h"
#include "xen_blkif.h" #include "xen_blkif.h"
#include "sysemu/blockdev.h" #include "sysemu/blockdev.h"
#include "sysemu/iothread.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h" #include "qapi/qmp/qstring.h"
#include "trace.h"
/* ------------------------------------------------------------- */ /* ------------------------------------------------------------- */
@ -125,6 +127,9 @@ struct XenBlkDev {
DriveInfo *dinfo; DriveInfo *dinfo;
BlockBackend *blk; BlockBackend *blk;
QEMUBH *bh; 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) static void qemu_aio_complete(void *opaque, int ret)
{ {
struct ioreq *ioreq = opaque; struct ioreq *ioreq = opaque;
struct XenBlkDev *blkdev = ioreq->blkdev;
aio_context_acquire(blkdev->ctx);
if (ret != 0) { 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->req.operation == BLKIF_OP_READ ? "read" : "write");
ioreq->aio_errors++; ioreq->aio_errors++;
} }
@ -607,10 +615,10 @@ static void qemu_aio_complete(void *opaque, int ret)
if (ioreq->presync) { if (ioreq->presync) {
ioreq->presync = 0; ioreq->presync = 0;
ioreq_runio_qemu_aio(ioreq); ioreq_runio_qemu_aio(ioreq);
return; goto done;
} }
if (ioreq->aio_inflight > 0) { if (ioreq->aio_inflight > 0) {
return; goto done;
} }
if (xen_feature_grant_copy) { if (xen_feature_grant_copy) {
@ -647,16 +655,19 @@ static void qemu_aio_complete(void *opaque, int ret)
} }
case BLKIF_OP_READ: case BLKIF_OP_READ:
if (ioreq->status == BLKIF_RSP_OKAY) { 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 { } else {
block_acct_failed(blk_get_stats(ioreq->blkdev->blk), &ioreq->acct); block_acct_failed(blk_get_stats(blkdev->blk), &ioreq->acct);
} }
break; break;
case BLKIF_OP_DISCARD: case BLKIF_OP_DISCARD:
default: default:
break; 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, 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) static void blk_bh(void *opaque)
{ {
struct XenBlkDev *blkdev = opaque; struct XenBlkDev *blkdev = opaque;
aio_context_acquire(blkdev->ctx);
blk_handle_requests(blkdev); blk_handle_requests(blkdev);
aio_context_release(blkdev->ctx);
} }
static void blk_alloc(struct XenDevice *xendev) static void blk_alloc(struct XenDevice *xendev)
{ {
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, 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->inflight);
QLIST_INIT(&blkdev->finished); QLIST_INIT(&blkdev->finished);
QLIST_INIT(&blkdev->freelist); 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) { if (xen_mode != XEN_EMULATE) {
batch_maps = 1; batch_maps = 1;
} }
@ -950,6 +973,8 @@ static int blk_init(struct XenDevice *xendev)
int info = 0; int info = 0;
char *directiosafe = NULL; char *directiosafe = NULL;
trace_xen_disk_init(xendev->name);
/* read xenstore entries */ /* read xenstore entries */
if (blkdev->params == NULL) { if (blkdev->params == NULL) {
char *h = NULL; char *h = NULL;
@ -1062,6 +1087,8 @@ static int blk_connect(struct XenDevice *xendev)
unsigned int i; unsigned int i;
uint32_t *domids; uint32_t *domids;
trace_xen_disk_connect(xendev->name);
/* read-only ? */ /* read-only ? */
if (blkdev->directiosafe) { if (blkdev->directiosafe) {
qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO; qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO;
@ -1287,6 +1314,8 @@ static int blk_connect(struct XenDevice *xendev)
blkdev->persistent_gnt_count = 0; blkdev->persistent_gnt_count = 0;
} }
blk_set_aio_context(blkdev->blk, blkdev->ctx);
xen_be_bind_evtchn(&blkdev->xendev); xen_be_bind_evtchn(&blkdev->xendev);
xen_pv_printf(&blkdev->xendev, 1, "ok: proto %s, nr-ring-ref %u, " 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); struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
trace_xen_disk_disconnect(xendev->name);
aio_context_acquire(blkdev->ctx);
if (blkdev->blk) { if (blkdev->blk) {
blk_set_aio_context(blkdev->blk, qemu_get_aio_context());
blk_detach_dev(blkdev->blk, blkdev); blk_detach_dev(blkdev->blk, blkdev);
blk_unref(blkdev->blk); blk_unref(blkdev->blk);
blkdev->blk = NULL; blkdev->blk = NULL;
} }
xen_pv_unbind_evtchn(&blkdev->xendev); xen_pv_unbind_evtchn(&blkdev->xendev);
aio_context_release(blkdev->ctx);
if (blkdev->sring) { if (blkdev->sring) {
xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring, xengnttab_unmap(blkdev->xendev.gnttabdev, blkdev->sring,
blkdev->nr_ring_ref); 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 XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
struct ioreq *ioreq; struct ioreq *ioreq;
trace_xen_disk_free(xendev->name);
blk_disconnect(xendev); blk_disconnect(xendev);
while (!QLIST_EMPTY(&blkdev->freelist)) { while (!QLIST_EMPTY(&blkdev->freelist)) {
@ -1360,6 +1398,7 @@ static int blk_free(struct XenDevice *xendev)
g_free(blkdev->dev); g_free(blkdev->dev);
g_free(blkdev->devtype); g_free(blkdev->devtype);
qemu_bh_delete(blkdev->bh); qemu_bh_delete(blkdev->bh);
iothread_destroy(blkdev->iothread);
return 0; return 0;
} }

View File

@ -29,7 +29,6 @@
#include "hw/hw.h" #include "hw/hw.h"
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
#include "hw/isa/isa.h" #include "hw/isa/isa.h"
#include "hw/i386/pc.h"
#define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon" #define TYPE_ISA_DEBUGCON_DEVICE "isa-debugcon"
#define ISA_DEBUGCON_DEVICE(obj) \ #define ISA_DEBUGCON_DEVICE(obj) \

View File

@ -27,7 +27,6 @@
#include "hw/hw.h" #include "hw/hw.h"
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
#include "hw/xen/xen_backend.h" #include "hw/xen/xen_backend.h"
#include "qapi/error.h"
#include <xen/io/console.h> #include <xen/io/console.h>

View File

@ -20,7 +20,6 @@
#include "sysemu/numa.h" #include "sysemu/numa.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "sysemu/numa.h"
#include "sysemu/qtest.h" #include "sysemu/qtest.h"
static char *machine_get_accel(Object *obj, Error **errp) static char *machine_get_accel(Object *obj, Error **errp)

View File

@ -22,6 +22,7 @@
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
#include "sysemu/iothread.h" #include "sysemu/iothread.h"
#include "sysemu/tpm_backend.h"
static void get_pointer(Object *obj, Visitor *v, Property *prop, static void get_pointer(Object *obj, Visitor *v, Property *prop,
char *(*print)(void *ptr), char *(*print)(void *ptr),

View File

@ -6,6 +6,7 @@
* This work is licensed under the terms of the GNU GPL, version 2 or later. * This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory. * See the COPYING file in the top-level directory.
*/ */
#include "qemu/osdep.h"
#include "hw/cpu/core.h" #include "hw/cpu/core.h"
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "qapi/error.h" #include "qapi/error.h"

View File

@ -31,7 +31,6 @@
#include "trace.h" #include "trace.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "ui/console.h"
#include "ui/pixel_ops.h" #include "ui/pixel_ops.h"
#include "vga_int.h" #include "vga_int.h"
#include "hw/loader.h" #include "hw/loader.h"

View File

@ -3,7 +3,6 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "ui/console.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "vga_int.h" #include "vga_int.h"

View File

@ -795,6 +795,8 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr,
case SM501_ARBTRTN_CONTROL: case SM501_ARBTRTN_CONTROL:
ret = s->arbitration_control; ret = s->arbitration_control;
break; break;
case SM501_COMMAND_LIST_STATUS:
ret = 0x00180002; /* FIFOs are empty, everything idle */
case SM501_IRQ_MASK: case SM501_IRQ_MASK:
ret = s->irq_mask; ret = s->irq_mask;
break; break;
@ -812,6 +814,9 @@ static uint64_t sm501_system_config_read(void *opaque, hwaddr addr,
case SM501_POWER_MODE_CONTROL: case SM501_POWER_MODE_CONTROL:
ret = s->power_mode_control; ret = s->power_mode_control;
break; break;
case SM501_ENDIAN_CONTROL:
ret = 0; /* Only default little endian mode is supported */
break;
default: default:
printf("sm501 system config : not implemented register read." 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: case SM501_POWER_MODE_CONTROL:
s->power_mode_control = value & 0x00000003; s->power_mode_control = value & 0x00000003;
break; break;
case SM501_ENDIAN_CONTROL:
if (value & 0x00000001) {
printf("sm501 system config : big endian mode not implemented.\n");
abort();
}
break;
default: default:
printf("sm501 system config : not implemented register write." 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: case SM501_DC_PANEL_PANNING_CONTROL:
ret = s->dc_panel_panning_control; ret = s->dc_panel_panning_control;
break; break;
case SM501_DC_PANEL_COLOR_KEY:
/* Not implemented yet */
break;
case SM501_DC_PANEL_FB_ADDR: case SM501_DC_PANEL_FB_ADDR:
ret = s->dc_panel_fb_addr; ret = s->dc_panel_fb_addr;
break; break;
@ -956,6 +970,19 @@ static uint64_t sm501_disp_ctrl_read(void *opaque, hwaddr addr,
ret = s->dc_panel_v_sync; ret = s->dc_panel_v_sync;
break; 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: case SM501_DC_VIDEO_CONTROL:
ret = s->dc_video_control; ret = s->dc_video_control;
break; break;
@ -1022,6 +1049,9 @@ static void sm501_disp_ctrl_write(void *opaque, hwaddr addr,
case SM501_DC_PANEL_PANNING_CONTROL: case SM501_DC_PANEL_PANNING_CONTROL:
s->dc_panel_panning_control = value & 0xFF3FFF3F; s->dc_panel_panning_control = value & 0xFF3FFF3F;
break; break;
case SM501_DC_PANEL_COLOR_KEY:
/* Not implemented yet */
break;
case SM501_DC_PANEL_FB_ADDR: case SM501_DC_PANEL_FB_ADDR:
s->dc_panel_fb_addr = value & 0x8FFFFFF0; s->dc_panel_fb_addr = value & 0x8FFFFFF0;
break; break;

View File

@ -172,6 +172,7 @@ static void tc6393xb_gpio_handler_update(TC6393xbState *s)
int bit; int bit;
level = s->gpio_level & s->gpio_dir; level = s->gpio_level & s->gpio_dir;
level &= MAKE_64BIT_MASK(0, TC6393XB_GPIOS);
for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) { for (diff = s->prev_level ^ level; diff; diff ^= 1 << bit) {
bit = ctz32(diff); bit = ctz32(diff);

View File

@ -23,11 +23,9 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "ui/console.h" #include "hw/display/vga.h"
#include "hw/i386/pc.h"
#include "vga_int.h" #include "vga_int.h"
#include "ui/pixel_ops.h" #include "ui/pixel_ops.h"
#include "qemu/timer.h"
#define VGA_RAM_SIZE (8192 * 1024) #define VGA_RAM_SIZE (8192 * 1024)

View File

@ -25,8 +25,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "ui/console.h" #include "hw/isa/isa.h"
#include "hw/i386/pc.h"
#include "vga_int.h" #include "vga_int.h"
#include "ui/pixel_ops.h" #include "ui/pixel_ops.h"
#include "qemu/timer.h" #include "qemu/timer.h"

View File

@ -25,7 +25,6 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "ui/console.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "vga_int.h" #include "vga_int.h"
#include "ui/pixel_ops.h" #include "ui/pixel_ops.h"

View File

@ -24,11 +24,10 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "hw/hw.h" #include "hw/hw.h"
#include "vga.h" #include "hw/display/vga.h"
#include "ui/console.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "vga_int.h" #include "vga_int.h"
#include "vga_regs.h"
#include "ui/pixel_ops.h" #include "ui/pixel_ops.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "hw/xen/xen.h" #include "hw/xen/xen.h"

View File

@ -25,8 +25,9 @@
#ifndef HW_VGA_INT_H #ifndef HW_VGA_INT_H
#define HW_VGA_INT_H #define HW_VGA_INT_H
#include "hw/hw.h" #include "exec/ioport.h"
#include "exec/memory.h" #include "exec/memory.h"
#include "ui/console.h"
#define ST01_V_RETRACE 0x08 #define ST01_V_RETRACE 0x08
#define ST01_DISP_ENABLE 0x01 #define ST01_DISP_ENABLE 0x01

Some files were not shown because too many files have changed in this diff Show More