mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'origin/master' into HEAD
Resolve conflicts around apb. Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
@@ -53,7 +53,6 @@
|
||||
/qemu-version.h.tmp
|
||||
/module_block.h
|
||||
/scsi/qemu-pr-helper
|
||||
/vscclient
|
||||
/vhost-user-scsi
|
||||
/fsdev/virtfs-proxy-helper
|
||||
*.tmp
|
||||
|
||||
+23
-4
@@ -259,6 +259,7 @@ S: Maintained
|
||||
F: target/xtensa/
|
||||
F: hw/xtensa/
|
||||
F: tests/tcg/xtensa/
|
||||
F: disas/xtensa.c
|
||||
|
||||
TriCore
|
||||
M: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
|
||||
@@ -543,7 +544,7 @@ F: include/hw/*/xlnx*.h
|
||||
|
||||
ARM ACPI Subsystem
|
||||
M: Shannon Zhao <zhaoshenglong@huawei.com>
|
||||
M: Shannon Zhao <shannon.zhao@linaro.org>
|
||||
M: Shannon Zhao <shannon.zhaosl@gmail.com>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/virt-acpi-build.c
|
||||
@@ -732,7 +733,11 @@ F: hw/ppc/prep.c
|
||||
F: hw/ppc/prep_systemio.c
|
||||
F: hw/ppc/rs6000_mc.c
|
||||
F: hw/pci-host/prep.[hc]
|
||||
F: hw/isa/i82378.c
|
||||
F: hw/isa/pc87312.[hc]
|
||||
F: hw/dma/i82374.c
|
||||
F: hw/timer/m48t59-isa.c
|
||||
F: include/hw/timer/m48t59.h
|
||||
F: pc-bios/ppc_rom.bin
|
||||
|
||||
sPAPR
|
||||
@@ -761,6 +766,12 @@ L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/ppc/virtex_ml507.c
|
||||
|
||||
sam460ex
|
||||
M: BALATON Zoltan <balaton@eik.bme.hu>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ide/sii3112.c
|
||||
|
||||
SH4 Machines
|
||||
------------
|
||||
R2D
|
||||
@@ -861,12 +872,13 @@ F: hw/misc/sga.c
|
||||
PC Chipset
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
S: Support
|
||||
S: Supported
|
||||
F: hw/char/debugcon.c
|
||||
F: hw/char/parallel.c
|
||||
F: hw/char/serial*
|
||||
F: hw/dma/i8257*
|
||||
F: hw/i2c/pm_smbus.c
|
||||
F: hw/input/pckbd.c
|
||||
F: hw/intc/apic*
|
||||
F: hw/intc/ioapic*
|
||||
F: hw/intc/i8259*
|
||||
@@ -875,7 +887,10 @@ F: hw/misc/pc-testdev.c
|
||||
F: hw/timer/hpet*
|
||||
F: hw/timer/i8254*
|
||||
F: hw/timer/mc146818rtc*
|
||||
F: hw/watchdog/wdt_ib700.c
|
||||
F: include/hw/display/vga.h
|
||||
F: include/hw/i2c/pm_smbus.h
|
||||
F: include/hw/isa/i8257.h
|
||||
F: include/hw/timer/hpet.h
|
||||
F: include/hw/timer/i8254*
|
||||
F: include/hw/timer/mc146818rtc*
|
||||
@@ -976,7 +991,9 @@ M: Alexander Graf <agraf@suse.de>
|
||||
L: qemu-ppc@nongnu.org
|
||||
S: Odd Fixes
|
||||
F: hw/ppc/ppc4*.c
|
||||
F: hw/i2c/ppc4xx_i2c.c
|
||||
F: include/hw/ppc/ppc4xx.h
|
||||
F: include/hw/i2c/ppc4xx_i2c.h
|
||||
|
||||
ppce500
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
@@ -995,11 +1012,13 @@ Network devices
|
||||
M: Jason Wang <jasowang@redhat.com>
|
||||
S: Odd Fixes
|
||||
F: hw/net/
|
||||
F: include/hw/net/
|
||||
F: tests/virtio-net-test.c
|
||||
T: git git://github.com/jasowang/qemu.git net
|
||||
|
||||
SCSI
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Fam Zheng <famz@redhat.com>
|
||||
S: Supported
|
||||
F: include/hw/scsi/*
|
||||
F: hw/scsi/*
|
||||
@@ -1071,13 +1090,11 @@ F: include/hw/virtio/
|
||||
F: tests/virtio-balloon-test.c
|
||||
|
||||
virtio-9p
|
||||
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
||||
M: Greg Kurz <groug@kaod.org>
|
||||
S: Supported
|
||||
F: hw/9pfs/
|
||||
F: fsdev/
|
||||
F: tests/virtio-9p-test.c
|
||||
T: git git://github.com/kvaneesh/QEMU.git
|
||||
T: git git://github.com/gkurz/qemu.git 9p-next
|
||||
|
||||
virtio-blk
|
||||
@@ -1260,6 +1277,7 @@ T: git git://github.com/stefanha/qemu.git block
|
||||
|
||||
Block SCSI subsystem
|
||||
M: Paolo Bonzini <pbonzini@redhat.com>
|
||||
R: Fam Zheng <famz@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: include/scsi/*
|
||||
@@ -1538,6 +1556,7 @@ M: Alistair Francis <alistair.francis@xilinx.com>
|
||||
S: Maintained
|
||||
F: hw/core/register.c
|
||||
F: include/hw/register.h
|
||||
F: include/hw/registerfields.h
|
||||
|
||||
SLIRP
|
||||
M: Samuel Thibault <samuel.thibault@ens-lyon.org>
|
||||
|
||||
@@ -6,7 +6,10 @@ BUILD_DIR=$(CURDIR)
|
||||
# Before including a proper config-host.mak, assume we are in the source tree
|
||||
SRC_PATH=.
|
||||
|
||||
UNCHECKED_GOALS := %clean TAGS cscope ctags docker docker-% help
|
||||
UNCHECKED_GOALS := %clean TAGS cscope ctags dist \
|
||||
html info pdf txt \
|
||||
help check-help \
|
||||
docker docker-% vm-test vm-build-%
|
||||
|
||||
# All following code might depend on configuration variables
|
||||
ifneq ($(wildcard config-host.mak),)
|
||||
@@ -50,7 +53,7 @@ ifneq ($(realpath $(SRC_PATH)),$(realpath .))
|
||||
ifneq ($(wildcard $(SRC_PATH)/config-host.mak),)
|
||||
$(error This is an out of tree build but your source tree ($(SRC_PATH)) \
|
||||
seems to have been used for an in-tree build. You can fix this by running \
|
||||
"make distclean && rm -rf *-linux-user *-softmmu" in your source tree)
|
||||
"$(MAKE) distclean && rm -rf *-linux-user *-softmmu" in your source tree)
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -229,6 +232,7 @@ KEYCODEMAP_FILES = \
|
||||
ui/input-keymap-linux-to-qcode.c \
|
||||
ui/input-keymap-qcode-to-qnum.c \
|
||||
ui/input-keymap-qnum-to-qcode.c \
|
||||
ui/input-keymap-qcode-to-linux.c \
|
||||
$(NULL)
|
||||
|
||||
GENERATED_FILES += $(KEYCODEMAP_FILES)
|
||||
@@ -303,7 +307,7 @@ endif
|
||||
else \
|
||||
echo "WARNING: $@ out of date.";\
|
||||
fi; \
|
||||
echo "Run \"make defconfig\" to regenerate."; \
|
||||
echo "Run \"$(MAKE) defconfig\" to regenerate."; \
|
||||
rm $@.tmp; \
|
||||
fi; \
|
||||
else \
|
||||
@@ -933,4 +937,4 @@ ifdef QEMU_GA_MSI_ENABLED
|
||||
endif
|
||||
@echo ''
|
||||
endif
|
||||
@echo ' make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
|
||||
@echo ' $(MAKE) V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
|
||||
|
||||
@@ -140,6 +140,7 @@ trace-events-subdirs += hw/input
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sparc64
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/isa
|
||||
trace-events-subdirs += hw/mem
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
obj-$(call lnot,$(CONFIG_HAX)) += hax-stub.o
|
||||
obj-$(call lnot,$(CONFIG_HVF)) += hvf-stub.o
|
||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o
|
||||
obj-$(call lnot,$(CONFIG_TCG)) += tcg-stub.o
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* QEMU HVF support
|
||||
*
|
||||
* Copyright 2017 Red Hat, Inc.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2 or later, as published by the Free Software Foundation,
|
||||
* and may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* See the COPYING file in the top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "sysemu/hvf.h"
|
||||
|
||||
int hvf_init_vcpu(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int hvf_vcpu_exec(CPUState *cpu)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void hvf_vcpu_destroy(CPUState *cpu)
|
||||
{
|
||||
}
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "cpu.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/memory-internal.h"
|
||||
|
||||
bool tcg_allowed;
|
||||
|
||||
|
||||
+7
-11
@@ -146,8 +146,10 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
|
||||
uint8_t *tb_ptr = itb->tc.ptr;
|
||||
|
||||
qemu_log_mask_and_addr(CPU_LOG_EXEC, itb->pc,
|
||||
"Trace %p [%d: " TARGET_FMT_lx "] %s\n",
|
||||
itb->tc.ptr, cpu->cpu_index, itb->pc,
|
||||
"Trace %d: %p ["
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n",
|
||||
cpu->cpu_index, itb->tc.ptr,
|
||||
itb->cs_base, itb->pc, itb->flags,
|
||||
lookup_symbol(itb->pc));
|
||||
|
||||
#if defined(DEBUG_DISAS)
|
||||
@@ -525,19 +527,13 @@ static inline bool cpu_handle_interrupt(CPUState *cpu,
|
||||
TranslationBlock **last_tb)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
int32_t insns_left;
|
||||
|
||||
/* Clear the interrupt flag now since we're processing
|
||||
* cpu->interrupt_request and cpu->exit_request.
|
||||
* Ensure zeroing happens before reading cpu->exit_request or
|
||||
* cpu->interrupt_request (see also smp_wmb in cpu_exit())
|
||||
*/
|
||||
insns_left = atomic_read(&cpu->icount_decr.u32);
|
||||
atomic_set(&cpu->icount_decr.u16.high, 0);
|
||||
if (unlikely(insns_left < 0)) {
|
||||
/* Ensure the zeroing of icount_decr comes before the next read
|
||||
* of cpu->exit_request or cpu->interrupt_request.
|
||||
*/
|
||||
smp_mb();
|
||||
}
|
||||
atomic_mb_set(&cpu->icount_decr.u16.high, 0);
|
||||
|
||||
if (unlikely(atomic_read(&cpu->interrupt_request))) {
|
||||
int interrupt_request;
|
||||
|
||||
@@ -156,8 +156,9 @@ void *HELPER(lookup_tb_ptr)(CPUArchState *env)
|
||||
return tcg_ctx->code_gen_epilogue;
|
||||
}
|
||||
qemu_log_mask_and_addr(CPU_LOG_EXEC, pc,
|
||||
"Chain %p [%d: " TARGET_FMT_lx "] %s\n",
|
||||
tb->tc.ptr, cpu->cpu_index, pc,
|
||||
"Chain %d: %p ["
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx "/%#x] %s\n",
|
||||
cpu->cpu_index, tb->tc.ptr, cs_base, pc, flags,
|
||||
lookup_symbol(pc));
|
||||
return tb->tc.ptr;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#include "tcg.h"
|
||||
#if defined(CONFIG_USER_ONLY)
|
||||
#include "qemu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
#include <sys/param.h>
|
||||
#if __FreeBSD_version >= 700104
|
||||
@@ -257,7 +256,7 @@ static target_long decode_sleb128(uint8_t **pp)
|
||||
/* Encode the data collected about the instructions while compiling TB.
|
||||
Place the data at BLOCK, and return the number of bytes consumed.
|
||||
|
||||
The logical table consisits of TARGET_INSN_START_WORDS target_ulong's,
|
||||
The logical table consists of TARGET_INSN_START_WORDS target_ulong's,
|
||||
which come from the target's insn_start data, followed by a uintptr_t
|
||||
which comes from the host pc of the end of the code implementing the insn.
|
||||
|
||||
|
||||
+36
-58
@@ -17,16 +17,25 @@
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "sysemu/tpm.h"
|
||||
#include "hw/tpm/tpm_int.h"
|
||||
#include "qemu/thread.h"
|
||||
#include "qemu/main-loop.h"
|
||||
|
||||
static void tpm_backend_request_completed_bh(void *opaque)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(opaque);
|
||||
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
|
||||
|
||||
tic->request_completed(s->tpmif);
|
||||
}
|
||||
|
||||
static void tpm_backend_worker_thread(gpointer data, gpointer user_data)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(user_data);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
|
||||
assert(k->handle_request != NULL);
|
||||
k->handle_request(s, (TPMBackendCmd *)data);
|
||||
|
||||
qemu_bh_schedule(s->bh);
|
||||
}
|
||||
|
||||
static void tpm_backend_thread_end(TPMBackend *s)
|
||||
@@ -44,15 +53,22 @@ enum TpmType tpm_backend_get_type(TPMBackend *s)
|
||||
return k->type;
|
||||
}
|
||||
|
||||
int tpm_backend_init(TPMBackend *s, TPMState *state)
|
||||
int tpm_backend_init(TPMBackend *s, TPMIf *tpmif, Error **errp)
|
||||
{
|
||||
s->tpm_state = state;
|
||||
if (s->tpmif) {
|
||||
error_setg(errp, "TPM backend '%s' is already initialized", s->id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->tpmif = tpmif;
|
||||
object_ref(OBJECT(tpmif));
|
||||
|
||||
s->had_startup_error = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm_backend_startup_tpm(TPMBackend *s)
|
||||
int tpm_backend_startup_tpm(TPMBackend *s, size_t buffersize)
|
||||
{
|
||||
int res = 0;
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
@@ -63,7 +79,7 @@ int tpm_backend_startup_tpm(TPMBackend *s)
|
||||
s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE,
|
||||
NULL);
|
||||
|
||||
res = k->startup_tpm ? k->startup_tpm(s) : 0;
|
||||
res = k->startup_tpm ? k->startup_tpm(s, buffersize) : 0;
|
||||
|
||||
s->had_startup_error = (res != 0);
|
||||
|
||||
@@ -97,8 +113,6 @@ void tpm_backend_cancel_cmd(TPMBackend *s)
|
||||
{
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
|
||||
assert(k->cancel_cmd);
|
||||
|
||||
k->cancel_cmd(s);
|
||||
}
|
||||
|
||||
@@ -122,80 +136,44 @@ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s)
|
||||
{
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
|
||||
assert(k->get_tpm_version);
|
||||
|
||||
return k->get_tpm_version(s);
|
||||
}
|
||||
|
||||
size_t tpm_backend_get_buffer_size(TPMBackend *s)
|
||||
{
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
|
||||
return k->get_buffer_size(s);
|
||||
}
|
||||
|
||||
TPMInfo *tpm_backend_query_tpm(TPMBackend *s)
|
||||
{
|
||||
TPMInfo *info = g_new0(TPMInfo, 1);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
TPMIfClass *tic = TPM_IF_GET_CLASS(s->tpmif);
|
||||
|
||||
info->id = g_strdup(s->id);
|
||||
info->model = s->fe_model;
|
||||
if (k->get_tpm_options) {
|
||||
info->options = k->get_tpm_options(s);
|
||||
}
|
||||
info->model = tic->model;
|
||||
info->options = k->get_tpm_options(s);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool tpm_backend_prop_get_opened(Object *obj, Error **errp)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
|
||||
return s->opened;
|
||||
}
|
||||
|
||||
void tpm_backend_open(TPMBackend *s, Error **errp)
|
||||
{
|
||||
object_property_set_bool(OBJECT(s), true, "opened", errp);
|
||||
}
|
||||
|
||||
static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s);
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (value == s->opened) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value && s->opened) {
|
||||
error_setg(errp, QERR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (k->opened) {
|
||||
k->opened(s, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
s->opened = true;
|
||||
}
|
||||
|
||||
static void tpm_backend_instance_init(Object *obj)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
|
||||
object_property_add_bool(obj, "opened",
|
||||
tpm_backend_prop_get_opened,
|
||||
tpm_backend_prop_set_opened,
|
||||
NULL);
|
||||
s->fe_model = -1;
|
||||
s->bh = qemu_bh_new(tpm_backend_request_completed_bh, s);
|
||||
}
|
||||
|
||||
static void tpm_backend_instance_finalize(Object *obj)
|
||||
{
|
||||
TPMBackend *s = TPM_BACKEND(obj);
|
||||
|
||||
object_unref(OBJECT(s->tpmif));
|
||||
g_free(s->id);
|
||||
tpm_backend_thread_end(s);
|
||||
qemu_bh_delete(s->bh);
|
||||
}
|
||||
|
||||
static const TypeInfo tpm_backend_info = {
|
||||
|
||||
@@ -822,6 +822,18 @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
|
||||
bdrv_drained_end(bs);
|
||||
}
|
||||
|
||||
static void bdrv_child_cb_attach(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
bdrv_apply_subtree_drain(child, bs);
|
||||
}
|
||||
|
||||
static void bdrv_child_cb_detach(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
bdrv_unapply_subtree_drain(child, bs);
|
||||
}
|
||||
|
||||
static int bdrv_child_cb_inactivate(BdrvChild *child)
|
||||
{
|
||||
BlockDriverState *bs = child->opaque;
|
||||
@@ -889,6 +901,8 @@ const BdrvChildRole child_file = {
|
||||
.inherit_options = bdrv_inherited_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.attach = bdrv_child_cb_attach,
|
||||
.detach = bdrv_child_cb_detach,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
};
|
||||
|
||||
@@ -911,6 +925,8 @@ const BdrvChildRole child_format = {
|
||||
.inherit_options = bdrv_inherited_fmt_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.attach = bdrv_child_cb_attach,
|
||||
.detach = bdrv_child_cb_detach,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
};
|
||||
|
||||
@@ -953,6 +969,8 @@ static void bdrv_backing_attach(BdrvChild *c)
|
||||
parent->backing_blocker);
|
||||
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
|
||||
parent->backing_blocker);
|
||||
|
||||
bdrv_child_cb_attach(c);
|
||||
}
|
||||
|
||||
static void bdrv_backing_detach(BdrvChild *c)
|
||||
@@ -963,6 +981,8 @@ static void bdrv_backing_detach(BdrvChild *c)
|
||||
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
|
||||
error_free(parent->backing_blocker);
|
||||
parent->backing_blocker = NULL;
|
||||
|
||||
bdrv_child_cb_detach(c);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1924,6 +1944,8 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
assert(role == &child_backing || role == &child_file);
|
||||
|
||||
if (!backing) {
|
||||
int flags = bdrv_reopen_get_flags(reopen_queue, bs);
|
||||
|
||||
/* Apart from the modifications below, the same permissions are
|
||||
* forwarded and left alone as for filters */
|
||||
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
|
||||
@@ -1936,7 +1958,9 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
||||
|
||||
/* bs->file always needs to be consistent because of the metadata. We
|
||||
* can never allow other users to resize or write to it. */
|
||||
perm |= BLK_PERM_CONSISTENT_READ;
|
||||
if (!(flags & BDRV_O_NO_IO)) {
|
||||
perm |= BLK_PERM_CONSISTENT_READ;
|
||||
}
|
||||
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
|
||||
} else {
|
||||
/* We want consistent read from backing files if the parent needs it.
|
||||
@@ -1968,17 +1992,23 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
BlockDriverState *new_bs)
|
||||
{
|
||||
BlockDriverState *old_bs = child->bs;
|
||||
int i;
|
||||
|
||||
if (old_bs && new_bs) {
|
||||
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
|
||||
}
|
||||
if (old_bs) {
|
||||
if (old_bs->quiesce_counter && child->role->drained_end) {
|
||||
child->role->drained_end(child);
|
||||
}
|
||||
/* Detach first so that the recursive drain sections coming from @child
|
||||
* are already gone and we only end the drain sections that came from
|
||||
* elsewhere. */
|
||||
if (child->role->detach) {
|
||||
child->role->detach(child);
|
||||
}
|
||||
if (old_bs->quiesce_counter && child->role->drained_end) {
|
||||
for (i = 0; i < old_bs->quiesce_counter; i++) {
|
||||
child->role->drained_end(child);
|
||||
}
|
||||
}
|
||||
QLIST_REMOVE(child, next_parent);
|
||||
}
|
||||
|
||||
@@ -1987,9 +2017,14 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
||||
if (new_bs) {
|
||||
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
|
||||
if (new_bs->quiesce_counter && child->role->drained_begin) {
|
||||
child->role->drained_begin(child);
|
||||
for (i = 0; i < new_bs->quiesce_counter; i++) {
|
||||
child->role->drained_begin(child);
|
||||
}
|
||||
}
|
||||
|
||||
/* Attach only after starting new drained sections, so that recursive
|
||||
* drain sections coming from @child don't get an extra .drained_begin
|
||||
* callback. */
|
||||
if (child->role->attach) {
|
||||
child->role->attach(child);
|
||||
}
|
||||
@@ -2731,6 +2766,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
|
||||
* returns a pointer to bs_queue, which is either the newly allocated
|
||||
* bs_queue, or the existing bs_queue being used.
|
||||
*
|
||||
* bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
|
||||
*/
|
||||
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
BlockDriverState *bs,
|
||||
@@ -2746,6 +2782,11 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
BdrvChild *child;
|
||||
QDict *old_options, *explicit_options;
|
||||
|
||||
/* Make sure that the caller remembered to use a drained section. This is
|
||||
* important to avoid graph changes between the recursive queuing here and
|
||||
* bdrv_reopen_multiple(). */
|
||||
assert(bs->quiesce_counter > 0);
|
||||
|
||||
if (bs_queue == NULL) {
|
||||
bs_queue = g_new0(BlockReopenQueue, 1);
|
||||
QSIMPLEQ_INIT(bs_queue);
|
||||
@@ -2870,6 +2911,8 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
|
||||
* If all devices prepare successfully, then the changes are committed
|
||||
* to all devices.
|
||||
*
|
||||
* All affected nodes must be drained between bdrv_reopen_queue() and
|
||||
* bdrv_reopen_multiple().
|
||||
*/
|
||||
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
|
||||
{
|
||||
@@ -2879,11 +2922,8 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
|
||||
|
||||
assert(bs_queue != NULL);
|
||||
|
||||
aio_context_release(ctx);
|
||||
bdrv_drain_all_begin();
|
||||
aio_context_acquire(ctx);
|
||||
|
||||
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
|
||||
assert(bs_entry->state.bs->quiesce_counter > 0);
|
||||
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto cleanup;
|
||||
@@ -2912,8 +2952,6 @@ cleanup:
|
||||
}
|
||||
g_free(bs_queue);
|
||||
|
||||
bdrv_drain_all_end();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2923,12 +2961,18 @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
|
||||
{
|
||||
int ret = -1;
|
||||
Error *local_err = NULL;
|
||||
BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
|
||||
BlockReopenQueue *queue;
|
||||
|
||||
bdrv_subtree_drained_begin(bs);
|
||||
|
||||
queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
|
||||
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
bdrv_subtree_drained_end(bs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -4320,9 +4364,15 @@ int bdrv_inactivate_all(void)
|
||||
BdrvNextIterator it;
|
||||
int ret = 0;
|
||||
int pass;
|
||||
GSList *aio_ctxs = NULL, *ctx;
|
||||
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
aio_context_acquire(bdrv_get_aio_context(bs));
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
if (!g_slist_find(aio_ctxs, aio_context)) {
|
||||
aio_ctxs = g_slist_prepend(aio_ctxs, aio_context);
|
||||
aio_context_acquire(aio_context);
|
||||
}
|
||||
}
|
||||
|
||||
/* We do two passes of inactivation. The first pass calls to drivers'
|
||||
@@ -4340,9 +4390,11 @@ int bdrv_inactivate_all(void)
|
||||
}
|
||||
|
||||
out:
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
aio_context_release(bdrv_get_aio_context(bs));
|
||||
for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
|
||||
AioContext *aio_context = ctx->data;
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
g_slist_free(aio_ctxs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -4593,10 +4645,11 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
back_flags = flags;
|
||||
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||
|
||||
backing_options = qdict_new();
|
||||
if (backing_fmt) {
|
||||
backing_options = qdict_new();
|
||||
qdict_put_str(backing_options, "driver", backing_fmt);
|
||||
}
|
||||
qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
|
||||
|
||||
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
|
||||
&local_err);
|
||||
@@ -4746,7 +4799,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
|
||||
aio_disable_external(ctx);
|
||||
bdrv_parent_drained_begin(bs);
|
||||
bdrv_parent_drained_begin(bs, NULL);
|
||||
bdrv_drain(bs); /* ensure there are no in-flight requests */
|
||||
|
||||
while (aio_poll(ctx, false)) {
|
||||
@@ -4760,7 +4813,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
||||
*/
|
||||
aio_context_acquire(new_context);
|
||||
bdrv_attach_aio_context(bs, new_context);
|
||||
bdrv_parent_drained_end(bs);
|
||||
bdrv_parent_drained_end(bs, NULL);
|
||||
aio_enable_external(ctx);
|
||||
aio_context_release(new_context);
|
||||
}
|
||||
|
||||
+65
-51
@@ -40,11 +40,12 @@ typedef struct BackupBlockJob {
|
||||
BlockdevOnError on_target_error;
|
||||
CoRwlock flush_rwlock;
|
||||
uint64_t bytes_read;
|
||||
unsigned long *done_bitmap;
|
||||
int64_t cluster_size;
|
||||
bool compress;
|
||||
NotifierWithReturn before_write;
|
||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||
|
||||
HBitmap *copy_bitmap;
|
||||
} BackupBlockJob;
|
||||
|
||||
/* See if in-flight requests overlap and wait for them to complete */
|
||||
@@ -109,10 +110,11 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
cow_request_begin(&cow_request, job, start, end);
|
||||
|
||||
for (; start < end; start += job->cluster_size) {
|
||||
if (test_bit(start / job->cluster_size, job->done_bitmap)) {
|
||||
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
|
||||
trace_backup_do_cow_skip(job, start);
|
||||
continue; /* already copied */
|
||||
}
|
||||
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
|
||||
trace_backup_do_cow_process(job, start);
|
||||
|
||||
@@ -132,6 +134,7 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
if (error_is_read) {
|
||||
*error_is_read = true;
|
||||
}
|
||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -148,11 +151,10 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
if (error_is_read) {
|
||||
*error_is_read = false;
|
||||
}
|
||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_bit(start / job->cluster_size, job->done_bitmap);
|
||||
|
||||
/* Publish progress, guest I/O counts as progress too. Note that the
|
||||
* offset field is an opaque progress value, it is not a disk offset.
|
||||
*/
|
||||
@@ -260,7 +262,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
||||
}
|
||||
|
||||
len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size);
|
||||
bitmap_zero(backup_job->done_bitmap, len);
|
||||
hbitmap_set(backup_job->copy_bitmap, 0, len);
|
||||
}
|
||||
|
||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
||||
@@ -360,64 +362,68 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
||||
|
||||
static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
||||
{
|
||||
int ret;
|
||||
bool error_is_read;
|
||||
int ret = 0;
|
||||
int clusters_per_iter;
|
||||
uint32_t granularity;
|
||||
int64_t offset;
|
||||
int64_t cluster;
|
||||
int64_t end;
|
||||
int64_t last_cluster = -1;
|
||||
HBitmapIter hbi;
|
||||
|
||||
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
||||
while ((cluster = hbitmap_iter_next(&hbi)) != -1) {
|
||||
do {
|
||||
if (yield_and_check(job)) {
|
||||
return 0;
|
||||
}
|
||||
ret = backup_do_cow(job, cluster * job->cluster_size,
|
||||
job->cluster_size, &error_is_read, false);
|
||||
if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
|
||||
BLOCK_ERROR_ACTION_REPORT)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
} while (ret < 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* init copy_bitmap from sync_bitmap */
|
||||
static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
|
||||
{
|
||||
BdrvDirtyBitmapIter *dbi;
|
||||
int64_t offset;
|
||||
int64_t end = DIV_ROUND_UP(bdrv_dirty_bitmap_size(job->sync_bitmap),
|
||||
job->cluster_size);
|
||||
|
||||
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||
clusters_per_iter = MAX((granularity / job->cluster_size), 1);
|
||||
dbi = bdrv_dirty_iter_new(job->sync_bitmap);
|
||||
while ((offset = bdrv_dirty_iter_next(dbi)) != -1) {
|
||||
int64_t cluster = offset / job->cluster_size;
|
||||
int64_t next_cluster;
|
||||
|
||||
/* Find the next dirty sector(s) */
|
||||
while ((offset = bdrv_dirty_iter_next(dbi)) >= 0) {
|
||||
cluster = offset / job->cluster_size;
|
||||
|
||||
/* Fake progress updates for any clusters we skipped */
|
||||
if (cluster != last_cluster + 1) {
|
||||
job->common.offset += ((cluster - last_cluster - 1) *
|
||||
job->cluster_size);
|
||||
offset += bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||
if (offset >= bdrv_dirty_bitmap_size(job->sync_bitmap)) {
|
||||
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
|
||||
break;
|
||||
}
|
||||
|
||||
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
||||
do {
|
||||
if (yield_and_check(job)) {
|
||||
goto out;
|
||||
}
|
||||
ret = backup_do_cow(job, cluster * job->cluster_size,
|
||||
job->cluster_size, &error_is_read,
|
||||
false);
|
||||
if ((ret < 0) &&
|
||||
backup_error_action(job, error_is_read, -ret) ==
|
||||
BLOCK_ERROR_ACTION_REPORT) {
|
||||
goto out;
|
||||
}
|
||||
} while (ret < 0);
|
||||
offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset);
|
||||
if (offset == -1) {
|
||||
hbitmap_set(job->copy_bitmap, cluster, end - cluster);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the bitmap granularity is smaller than the backup granularity,
|
||||
* we need to advance the iterator pointer to the next cluster. */
|
||||
if (granularity < job->cluster_size) {
|
||||
bdrv_set_dirty_iter(dbi, cluster * job->cluster_size);
|
||||
next_cluster = DIV_ROUND_UP(offset, job->cluster_size);
|
||||
hbitmap_set(job->copy_bitmap, cluster, next_cluster - cluster);
|
||||
if (next_cluster >= end) {
|
||||
break;
|
||||
}
|
||||
|
||||
last_cluster = cluster - 1;
|
||||
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
|
||||
}
|
||||
|
||||
/* Play some final catchup with the progress meter */
|
||||
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
if (last_cluster + 1 < end) {
|
||||
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
|
||||
}
|
||||
job->common.offset = job->common.len -
|
||||
hbitmap_count(job->copy_bitmap) * job->cluster_size;
|
||||
|
||||
out:
|
||||
bdrv_dirty_iter_free(dbi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void coroutine_fn backup_run(void *opaque)
|
||||
@@ -425,19 +431,27 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
BackupBlockJob *job = opaque;
|
||||
BackupCompleteData *data;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
int64_t offset;
|
||||
int64_t offset, nb_clusters;
|
||||
int ret = 0;
|
||||
|
||||
QLIST_INIT(&job->inflight_reqs);
|
||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||
|
||||
job->done_bitmap = bitmap_new(DIV_ROUND_UP(job->common.len,
|
||||
job->cluster_size));
|
||||
nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
backup_incremental_init_copy_bitmap(job);
|
||||
} else {
|
||||
hbitmap_set(job->copy_bitmap, 0, nb_clusters);
|
||||
}
|
||||
|
||||
|
||||
job->before_write.notify = backup_before_write_notify;
|
||||
bdrv_add_before_write_notifier(bs, &job->before_write);
|
||||
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
|
||||
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
||||
* This does not actually require them to be copied. */
|
||||
while (!block_job_is_cancelled(&job->common)) {
|
||||
/* Yield until the job is cancelled. We just let our before_write
|
||||
* notify callback service CoW requests. */
|
||||
@@ -512,7 +526,7 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
/* wait until pending backup_do_cow() calls have completed */
|
||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
g_free(job->done_bitmap);
|
||||
hbitmap_free(job->copy_bitmap);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
|
||||
+1
-7
@@ -277,7 +277,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
const char *filter_node_name, Error **errp)
|
||||
{
|
||||
CommitBlockJob *s;
|
||||
BlockReopenQueue *reopen_queue = NULL;
|
||||
int orig_base_flags;
|
||||
BlockDriverState *iter;
|
||||
BlockDriverState *commit_top_bs = NULL;
|
||||
@@ -299,12 +298,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
/* convert base to r/w, if necessary */
|
||||
orig_base_flags = bdrv_get_flags(base);
|
||||
if (!(orig_base_flags & BDRV_O_RDWR)) {
|
||||
reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
|
||||
orig_base_flags | BDRV_O_RDWR);
|
||||
}
|
||||
|
||||
if (reopen_queue) {
|
||||
bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
|
||||
bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
|
||||
+18
-6
@@ -89,6 +89,8 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle,
|
||||
|
||||
struct BDRVCURLState;
|
||||
|
||||
static bool libcurl_initialized;
|
||||
|
||||
typedef struct CURLAIOCB {
|
||||
Coroutine *co;
|
||||
QEMUIOVector *qiov;
|
||||
@@ -686,14 +688,23 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
double d;
|
||||
const char *secretid;
|
||||
const char *protocol_delimiter;
|
||||
int ret;
|
||||
|
||||
static int inited = 0;
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
error_setg(errp, "curl block device does not support writes");
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
if (!libcurl_initialized) {
|
||||
ret = curl_global_init(CURL_GLOBAL_ALL);
|
||||
if (ret) {
|
||||
error_setg(errp, "libcurl initialization failed with %d", ret);
|
||||
return -EIO;
|
||||
}
|
||||
libcurl_initialized = true;
|
||||
}
|
||||
|
||||
qemu_mutex_init(&s->mutex);
|
||||
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||
@@ -772,11 +783,6 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
}
|
||||
|
||||
if (!inited) {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
inited = 1;
|
||||
}
|
||||
|
||||
DPRINTF("CURL: Opening %s\n", file);
|
||||
QSIMPLEQ_INIT(&s->free_state_waitq);
|
||||
s->aio_context = bdrv_get_aio_context(bs);
|
||||
@@ -851,6 +857,9 @@ out_noclean:
|
||||
qemu_mutex_destroy(&s->mutex);
|
||||
g_free(s->cookie);
|
||||
g_free(s->url);
|
||||
g_free(s->username);
|
||||
g_free(s->proxyusername);
|
||||
g_free(s->proxypassword);
|
||||
qemu_opts_del(opts);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -949,6 +958,9 @@ static void curl_close(BlockDriverState *bs)
|
||||
|
||||
g_free(s->cookie);
|
||||
g_free(s->url);
|
||||
g_free(s->username);
|
||||
g_free(s->proxyusername);
|
||||
g_free(s->proxypassword);
|
||||
}
|
||||
|
||||
static int64_t curl_getlength(BlockDriverState *bs)
|
||||
|
||||
@@ -715,3 +715,8 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp)
|
||||
{
|
||||
return hbitmap_sha256(bitmap->bitmap, errp);
|
||||
}
|
||||
|
||||
int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
|
||||
{
|
||||
return hbitmap_next_zero(bitmap->bitmap, offset);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#ifndef BLOCK_DMG_H
|
||||
#define BLOCK_DMG_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include <zlib.h>
|
||||
|
||||
+121
-51
@@ -40,22 +40,28 @@
|
||||
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes, BdrvRequestFlags flags);
|
||||
|
||||
void bdrv_parent_drained_begin(BlockDriverState *bs)
|
||||
void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
|
||||
{
|
||||
BdrvChild *c, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
|
||||
if (c == ignore) {
|
||||
continue;
|
||||
}
|
||||
if (c->role->drained_begin) {
|
||||
c->role->drained_begin(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_parent_drained_end(BlockDriverState *bs)
|
||||
void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
|
||||
{
|
||||
BdrvChild *c, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
|
||||
if (c == ignore) {
|
||||
continue;
|
||||
}
|
||||
if (c->role->drained_end) {
|
||||
c->role->drained_end(c);
|
||||
}
|
||||
@@ -134,29 +140,13 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
||||
assert(old >= 1);
|
||||
}
|
||||
|
||||
/* Check if any requests are in-flight (including throttled requests) */
|
||||
bool bdrv_requests_pending(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child;
|
||||
|
||||
if (atomic_read(&bs->in_flight)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
if (bdrv_requests_pending(child->bs)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Coroutine *co;
|
||||
BlockDriverState *bs;
|
||||
bool done;
|
||||
bool begin;
|
||||
bool recursive;
|
||||
BdrvChild *parent;
|
||||
} BdrvCoDrainData;
|
||||
|
||||
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
|
||||
@@ -175,8 +165,10 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
|
||||
bdrv_wakeup(bs);
|
||||
}
|
||||
|
||||
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
|
||||
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
|
||||
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
|
||||
{
|
||||
BdrvChild *child, *tmp;
|
||||
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
|
||||
|
||||
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
|
||||
@@ -187,16 +179,19 @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
|
||||
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
|
||||
bdrv_coroutine_enter(bs, data.co);
|
||||
BDRV_POLL_WHILE(bs, !data.done);
|
||||
|
||||
if (recursive) {
|
||||
QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
|
||||
bdrv_drain_invoke(child->bs, begin, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
|
||||
static bool bdrv_drain_recurse(BlockDriverState *bs)
|
||||
{
|
||||
BdrvChild *child, *tmp;
|
||||
bool waited;
|
||||
|
||||
/* Ensure any pending metadata writes are submitted to bs->file. */
|
||||
bdrv_drain_invoke(bs, begin);
|
||||
|
||||
/* Wait for drained requests to finish */
|
||||
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
|
||||
|
||||
@@ -215,7 +210,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
|
||||
*/
|
||||
bdrv_ref(bs);
|
||||
}
|
||||
waited |= bdrv_drain_recurse(bs, begin);
|
||||
waited |= bdrv_drain_recurse(bs);
|
||||
if (in_main_loop) {
|
||||
bdrv_unref(bs);
|
||||
}
|
||||
@@ -224,6 +219,11 @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
|
||||
return waited;
|
||||
}
|
||||
|
||||
static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *parent);
|
||||
static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *parent);
|
||||
|
||||
static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
{
|
||||
BdrvCoDrainData *data = opaque;
|
||||
@@ -232,9 +232,9 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
|
||||
bdrv_dec_in_flight(bs);
|
||||
if (data->begin) {
|
||||
bdrv_drained_begin(bs);
|
||||
bdrv_do_drained_begin(bs, data->recursive, data->parent);
|
||||
} else {
|
||||
bdrv_drained_end(bs);
|
||||
bdrv_do_drained_end(bs, data->recursive, data->parent);
|
||||
}
|
||||
|
||||
data->done = true;
|
||||
@@ -242,7 +242,8 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
}
|
||||
|
||||
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
bool begin)
|
||||
bool begin, bool recursive,
|
||||
BdrvChild *parent)
|
||||
{
|
||||
BdrvCoDrainData data;
|
||||
|
||||
@@ -256,6 +257,8 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
.bs = bs,
|
||||
.done = false,
|
||||
.begin = begin,
|
||||
.recursive = recursive,
|
||||
.parent = parent,
|
||||
};
|
||||
bdrv_inc_in_flight(bs);
|
||||
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
|
||||
@@ -267,35 +270,97 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
|
||||
assert(data.done);
|
||||
}
|
||||
|
||||
void bdrv_drained_begin(BlockDriverState *bs)
|
||||
void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *parent)
|
||||
{
|
||||
BdrvChild *child, *next;
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
bdrv_co_yield_to_drain(bs, true);
|
||||
bdrv_co_yield_to_drain(bs, true, recursive, parent);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Stop things in parent-to-child order */
|
||||
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
|
||||
aio_disable_external(bdrv_get_aio_context(bs));
|
||||
bdrv_parent_drained_begin(bs);
|
||||
}
|
||||
|
||||
bdrv_drain_recurse(bs, true);
|
||||
bdrv_parent_drained_begin(bs, parent);
|
||||
bdrv_drain_invoke(bs, true, false);
|
||||
bdrv_drain_recurse(bs);
|
||||
|
||||
if (recursive) {
|
||||
bs->recursive_quiesce_counter++;
|
||||
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
|
||||
bdrv_do_drained_begin(child->bs, true, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_drained_begin(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_drained_begin(bs, false, NULL);
|
||||
}
|
||||
|
||||
void bdrv_subtree_drained_begin(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_drained_begin(bs, true, NULL);
|
||||
}
|
||||
|
||||
void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *parent)
|
||||
{
|
||||
BdrvChild *child, *next;
|
||||
int old_quiesce_counter;
|
||||
|
||||
if (qemu_in_coroutine()) {
|
||||
bdrv_co_yield_to_drain(bs, false, recursive, parent);
|
||||
return;
|
||||
}
|
||||
assert(bs->quiesce_counter > 0);
|
||||
old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
|
||||
|
||||
/* Re-enable things in child-to-parent order */
|
||||
bdrv_drain_invoke(bs, false, false);
|
||||
bdrv_parent_drained_end(bs, parent);
|
||||
if (old_quiesce_counter == 1) {
|
||||
aio_enable_external(bdrv_get_aio_context(bs));
|
||||
}
|
||||
|
||||
if (recursive) {
|
||||
bs->recursive_quiesce_counter--;
|
||||
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
|
||||
bdrv_do_drained_end(child->bs, true, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_drained_end(BlockDriverState *bs)
|
||||
{
|
||||
if (qemu_in_coroutine()) {
|
||||
bdrv_co_yield_to_drain(bs, false);
|
||||
return;
|
||||
}
|
||||
assert(bs->quiesce_counter > 0);
|
||||
if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
|
||||
return;
|
||||
}
|
||||
bdrv_do_drained_end(bs, false, NULL);
|
||||
}
|
||||
|
||||
bdrv_parent_drained_end(bs);
|
||||
bdrv_drain_recurse(bs, false);
|
||||
aio_enable_external(bdrv_get_aio_context(bs));
|
||||
void bdrv_subtree_drained_end(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_drained_end(bs, true, NULL);
|
||||
}
|
||||
|
||||
void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
|
||||
bdrv_do_drained_begin(child->bs, true, child);
|
||||
}
|
||||
}
|
||||
|
||||
void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
|
||||
bdrv_do_drained_end(child->bs, true, child);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -342,14 +407,20 @@ void bdrv_drain_all_begin(void)
|
||||
BdrvNextIterator it;
|
||||
GSList *aio_ctxs = NULL, *ctx;
|
||||
|
||||
block_job_pause_all();
|
||||
/* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
|
||||
* or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
|
||||
* nodes in several different AioContexts, so make sure we're in the main
|
||||
* context. */
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
/* Stop things in parent-to-child order */
|
||||
aio_context_acquire(aio_context);
|
||||
bdrv_parent_drained_begin(bs);
|
||||
aio_disable_external(aio_context);
|
||||
bdrv_parent_drained_begin(bs, NULL);
|
||||
bdrv_drain_invoke(bs, true, true);
|
||||
aio_context_release(aio_context);
|
||||
|
||||
if (!g_slist_find(aio_ctxs, aio_context)) {
|
||||
@@ -372,7 +443,7 @@ void bdrv_drain_all_begin(void)
|
||||
aio_context_acquire(aio_context);
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
if (aio_context == bdrv_get_aio_context(bs)) {
|
||||
waited |= bdrv_drain_recurse(bs, true);
|
||||
waited |= bdrv_drain_recurse(bs);
|
||||
}
|
||||
}
|
||||
aio_context_release(aio_context);
|
||||
@@ -390,14 +461,13 @@ void bdrv_drain_all_end(void)
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
/* Re-enable things in child-to-parent order */
|
||||
aio_context_acquire(aio_context);
|
||||
bdrv_drain_invoke(bs, false, true);
|
||||
bdrv_parent_drained_end(bs, NULL);
|
||||
aio_enable_external(aio_context);
|
||||
bdrv_parent_drained_end(bs);
|
||||
bdrv_drain_recurse(bs, false);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
block_job_resume_all();
|
||||
}
|
||||
|
||||
void bdrv_drain_all(void)
|
||||
|
||||
+36
-15
@@ -2,7 +2,7 @@
|
||||
* QEMU Block driver for iSCSI images
|
||||
*
|
||||
* Copyright (c) 2010-2011 Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
* Copyright (c) 2012-2016 Peter Lieven <pl@kamp.de>
|
||||
* Copyright (c) 2012-2017 Peter Lieven <pl@kamp.de>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -104,6 +104,7 @@ typedef struct IscsiTask {
|
||||
IscsiLun *iscsilun;
|
||||
QEMUTimer retry_timer;
|
||||
int err_code;
|
||||
char *err_str;
|
||||
} IscsiTask;
|
||||
|
||||
typedef struct IscsiAIOCB {
|
||||
@@ -265,7 +266,7 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status,
|
||||
}
|
||||
}
|
||||
iTask->err_code = iscsi_translate_sense(&task->sense);
|
||||
error_report("iSCSI Failure: %s", iscsi_get_error(iscsi));
|
||||
iTask->err_str = g_strdup(iscsi_get_error(iscsi));
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -629,6 +630,8 @@ retry:
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
iscsi_allocmap_set_invalid(iscsilun, sector_num, nb_sectors);
|
||||
error_report("iSCSI WRITE10/16 failed at lba %" PRIu64 ": %s", lba,
|
||||
iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
goto out_unlock;
|
||||
}
|
||||
@@ -637,6 +640,7 @@ retry:
|
||||
|
||||
out_unlock:
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -651,10 +655,9 @@ static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
||||
struct scsi_get_lba_status *lbas = NULL;
|
||||
struct scsi_lba_status_descriptor *lbasd = NULL;
|
||||
struct IscsiTask iTask;
|
||||
uint64_t lba;
|
||||
int64_t ret;
|
||||
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
|
||||
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@@ -670,11 +673,13 @@ static int64_t coroutine_fn iscsi_co_get_block_status(BlockDriverState *bs,
|
||||
goto out;
|
||||
}
|
||||
|
||||
lba = sector_qemu2lun(sector_num, iscsilun);
|
||||
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
retry:
|
||||
if (iscsi_get_lba_status_task(iscsilun->iscsi, iscsilun->lun,
|
||||
sector_qemu2lun(sector_num, iscsilun),
|
||||
8 + 16, iscsi_co_generic_cb,
|
||||
lba, 8 + 16, iscsi_co_generic_cb,
|
||||
&iTask) == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto out_unlock;
|
||||
@@ -701,6 +706,8 @@ retry:
|
||||
* because the device is busy or the cmd is not
|
||||
* supported) we pretend all blocks are allocated
|
||||
* for backwards compatibility */
|
||||
error_report("iSCSI GET_LBA_STATUS failed at lba %" PRIu64 ": %s",
|
||||
lba, iTask.err_str);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
@@ -738,6 +745,7 @@ retry:
|
||||
}
|
||||
out_unlock:
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
out:
|
||||
if (iTask.task != NULL) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
@@ -756,6 +764,7 @@ static int coroutine_fn iscsi_co_readv(BlockDriverState *bs,
|
||||
struct IscsiTask iTask;
|
||||
uint64_t lba;
|
||||
uint32_t num_sectors;
|
||||
int r = 0;
|
||||
|
||||
if (!is_sector_request_lun_aligned(sector_num, nb_sectors, iscsilun)) {
|
||||
return -EINVAL;
|
||||
@@ -853,19 +862,23 @@ retry:
|
||||
iTask.complete = 0;
|
||||
goto retry;
|
||||
}
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
error_report("iSCSI READ10/16 failed at lba %" PRIu64 ": %s",
|
||||
lba, iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
}
|
||||
|
||||
return 0;
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int coroutine_fn iscsi_co_flush(BlockDriverState *bs)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct IscsiTask iTask;
|
||||
int r = 0;
|
||||
|
||||
iscsi_co_init_iscsitask(iscsilun, &iTask);
|
||||
qemu_mutex_lock(&iscsilun->mutex);
|
||||
@@ -892,13 +905,15 @@ retry:
|
||||
iTask.complete = 0;
|
||||
goto retry;
|
||||
}
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
return iTask.err_code;
|
||||
error_report("iSCSI SYNCHRONIZECACHE10 failed: %s", iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
}
|
||||
|
||||
return 0;
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
@@ -1128,6 +1143,9 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
|
||||
bytes >> BDRV_SECTOR_BITS);
|
||||
|
||||
if (iTask.status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
/* the target might fail with a check condition if it
|
||||
is not happy with the alignment of the UNMAP request
|
||||
@@ -1136,15 +1154,15 @@ retry:
|
||||
}
|
||||
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
error_report("iSCSI UNMAP failed at lba %" PRIu64 ": %s",
|
||||
list.lba, iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
|
||||
bytes >> BDRV_SECTOR_BITS);
|
||||
|
||||
out_unlock:
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1241,6 +1259,8 @@ retry:
|
||||
if (iTask.status != SCSI_STATUS_GOOD) {
|
||||
iscsi_allocmap_set_invalid(iscsilun, offset >> BDRV_SECTOR_BITS,
|
||||
bytes >> BDRV_SECTOR_BITS);
|
||||
error_report("iSCSI WRITESAME10/16 failed at lba %" PRIu64 ": %s",
|
||||
lba, iTask.err_str);
|
||||
r = iTask.err_code;
|
||||
goto out_unlock;
|
||||
}
|
||||
@@ -1255,6 +1275,7 @@ retry:
|
||||
|
||||
out_unlock:
|
||||
qemu_mutex_unlock(&iscsilun->mutex);
|
||||
g_free(iTask.err_str);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user