Compare commits
106
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dfcf652e6 | ||
|
|
a4a5021413 | ||
|
|
a0380bdae9 | ||
|
|
8d25276172 | ||
|
|
345fab6ffe | ||
|
|
bd6dd4eaa6 | ||
|
|
f43a6b314a | ||
|
|
9d01327689 | ||
|
|
9ad3314fa3 | ||
|
|
8eba63e1d2 | ||
|
|
7347a04da3 | ||
|
|
61f87388af | ||
|
|
d086829e5b | ||
|
|
e49f868dc0 | ||
|
|
2c4f6cabf5 | ||
|
|
5d64550dc5 | ||
|
|
7b8e8ff009 | ||
|
|
690bb48e40 | ||
|
|
f3200c4c20 | ||
|
|
2251d74275 | ||
|
|
9f9a3548d3 | ||
|
|
4afb0370b9 | ||
|
|
66160b7aeb | ||
|
|
3fd92155fd | ||
|
|
69ec5ee5ac | ||
|
|
16d1300e1a | ||
|
|
1c9b86daec | ||
|
|
422bf25df1 | ||
|
|
89d2237683 | ||
|
|
1f79226d22 | ||
|
|
02db60391f | ||
|
|
9d61beac23 | ||
|
|
2ce4635d9b | ||
|
|
fc50e1fa79 | ||
|
|
e47e19ed21 | ||
|
|
d02d6dd710 | ||
|
|
32e0caf8ff | ||
|
|
38490efee0 | ||
|
|
e13f56a154 | ||
|
|
a69e9049d6 | ||
|
|
22acdb9937 | ||
|
|
93ef995b2c | ||
|
|
e31de8e7bb | ||
|
|
c192d40267 | ||
|
|
a36defa3c8 | ||
|
|
81e38ad8d0 | ||
|
|
b528bb2840 | ||
|
|
53a69e7ee4 | ||
|
|
ca1b4263d9 | ||
|
|
ef862b9e8e | ||
|
|
bbe21c6d30 | ||
|
|
b7fce72779 | ||
|
|
0a818f87f7 | ||
|
|
92c7eec109 | ||
|
|
6abeb6d85e | ||
|
|
399814e1a1 | ||
|
|
ce2c15fdaf | ||
|
|
15f40e2e63 | ||
|
|
901d84e675 | ||
|
|
9d9430b75f | ||
|
|
b3f1083edb | ||
|
|
9971a3fb90 | ||
|
|
b05db67c98 | ||
|
|
e9d87aa766 | ||
|
|
5b2f6925e0 | ||
|
|
0bc2dc7357 | ||
|
|
22e766bcf3 | ||
|
|
481be11cba | ||
|
|
a8b436c88d | ||
|
|
ccdd67c8d9 | ||
|
|
ef6de9e720 | ||
|
|
9e25063bb0 | ||
|
|
84cabd6c6c | ||
|
|
f8d4616e62 | ||
|
|
bda7545118 | ||
|
|
70b8acfa0d | ||
|
|
2f5c767a82 | ||
|
|
32a51d0704 | ||
|
|
a40e71884d | ||
|
|
457b0e4761 | ||
|
|
def983b923 | ||
|
|
3dceceec72 | ||
|
|
9431827af9 | ||
|
|
d1c384fabc | ||
|
|
8f0e1478d0 | ||
|
|
98f6f21261 | ||
|
|
ac141891ee | ||
|
|
4b4201206c | ||
|
|
1662d4d490 | ||
|
|
c89985bad9 | ||
|
|
fb661dac05 | ||
|
|
aff596dcf4 | ||
|
|
a606615e2f | ||
|
|
fbec38531c | ||
|
|
b7f525bf89 | ||
|
|
5a49083b1e | ||
|
|
192686a800 | ||
|
|
965307a713 | ||
|
|
3d95093fa7 | ||
|
|
e97358cae4 | ||
|
|
ca276173c9 | ||
|
|
471b5ae011 | ||
|
|
d1d2721876 | ||
|
|
bfc23fb2f0 | ||
|
|
f7ddde210a | ||
|
|
58c4b085ee |
No files matched your search
+1
-20
@@ -1,10 +1,4 @@
|
||||
# EditorConfig is a file format and collection of text editor plugins
|
||||
# for maintaining consistent coding styles between different editors
|
||||
# and IDEs. Most popular editors support this either natively or via
|
||||
# plugin.
|
||||
#
|
||||
# Check https://editorconfig.org for details.
|
||||
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
@@ -12,23 +6,10 @@ end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
[*.mak]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
file_type_emacs = makefile
|
||||
|
||||
[Makefile*]
|
||||
indent_style = tab
|
||||
indent_size = 8
|
||||
file_type_emacs = makefile
|
||||
|
||||
[*.{c,h}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{vert,frag}]
|
||||
file_type_emacs = glsl
|
||||
|
||||
[*.json]
|
||||
indent_style = space
|
||||
file_type_emacs = python
|
||||
@@ -107,7 +107,6 @@
|
||||
/qemu-doc.html
|
||||
/qemu-doc.info
|
||||
/qemu-doc.txt
|
||||
/qemu-edid
|
||||
/qemu-img
|
||||
/qemu-nbd
|
||||
/qemu-options.def
|
||||
|
||||
+15
-21
@@ -1,51 +1,45 @@
|
||||
[submodule "roms/seabios"]
|
||||
path = roms/seabios
|
||||
url = https://git.qemu.org/git/seabios.git/
|
||||
url = git://git.qemu-project.org/seabios.git/
|
||||
[submodule "roms/SLOF"]
|
||||
path = roms/SLOF
|
||||
url = https://git.qemu.org/git/SLOF.git
|
||||
url = git://git.qemu-project.org/SLOF.git
|
||||
[submodule "roms/ipxe"]
|
||||
path = roms/ipxe
|
||||
url = https://git.qemu.org/git/ipxe.git
|
||||
url = git://git.qemu-project.org/ipxe.git
|
||||
[submodule "roms/openbios"]
|
||||
path = roms/openbios
|
||||
url = https://git.qemu.org/git/openbios.git
|
||||
url = git://git.qemu-project.org/openbios.git
|
||||
[submodule "roms/openhackware"]
|
||||
path = roms/openhackware
|
||||
url = https://git.qemu.org/git/openhackware.git
|
||||
url = git://git.qemu-project.org/openhackware.git
|
||||
[submodule "roms/qemu-palcode"]
|
||||
path = roms/qemu-palcode
|
||||
url = https://git.qemu.org/git/qemu-palcode.git
|
||||
url = git://git.qemu.org/qemu-palcode.git
|
||||
[submodule "roms/sgabios"]
|
||||
path = roms/sgabios
|
||||
url = https://git.qemu.org/git/sgabios.git
|
||||
url = git://git.qemu-project.org/sgabios.git
|
||||
[submodule "dtc"]
|
||||
path = dtc
|
||||
url = https://git.qemu.org/git/dtc.git
|
||||
url = git://git.qemu-project.org/dtc.git
|
||||
[submodule "roms/u-boot"]
|
||||
path = roms/u-boot
|
||||
url = https://git.qemu.org/git/u-boot.git
|
||||
url = git://git.qemu-project.org/u-boot.git
|
||||
[submodule "roms/skiboot"]
|
||||
path = roms/skiboot
|
||||
url = https://git.qemu.org/git/skiboot.git
|
||||
url = git://git.qemu.org/skiboot.git
|
||||
[submodule "roms/QemuMacDrivers"]
|
||||
path = roms/QemuMacDrivers
|
||||
url = https://git.qemu.org/git/QemuMacDrivers.git
|
||||
url = git://git.qemu.org/QemuMacDrivers.git
|
||||
[submodule "ui/keycodemapdb"]
|
||||
path = ui/keycodemapdb
|
||||
url = https://git.qemu.org/git/keycodemapdb.git
|
||||
url = git://git.qemu.org/keycodemapdb.git
|
||||
[submodule "capstone"]
|
||||
path = capstone
|
||||
url = https://git.qemu.org/git/capstone.git
|
||||
url = git://git.qemu.org/capstone.git
|
||||
[submodule "roms/seabios-hppa"]
|
||||
path = roms/seabios-hppa
|
||||
url = https://github.com/hdeller/seabios-hppa.git
|
||||
url = git://github.com/hdeller/seabios-hppa.git
|
||||
[submodule "roms/u-boot-sam460ex"]
|
||||
path = roms/u-boot-sam460ex
|
||||
url = https://git.qemu.org/git/u-boot-sam460ex.git
|
||||
[submodule "tests/fp/berkeley-testfloat-3"]
|
||||
path = tests/fp/berkeley-testfloat-3
|
||||
url = https://github.com/cota/berkeley-testfloat-3
|
||||
[submodule "tests/fp/berkeley-softfloat-3"]
|
||||
path = tests/fp/berkeley-softfloat-3
|
||||
url = https://github.com/cota/berkeley-softfloat-3
|
||||
url = git://git.qemu.org/u-boot-sam460ex.git
|
||||
@@ -12,12 +12,8 @@ Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-7
|
||||
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
|
||||
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
|
||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
|
||||
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
|
||||
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
|
||||
Paul Burton <paul.burton@mips.com> <paul.burton@imgtec.com>
|
||||
Paul Burton <paul.burton@mips.com> <paul@archlinuxmips.org>
|
||||
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
|
||||
|
||||
@@ -34,6 +30,5 @@ Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu
|
||||
|
||||
|
||||
# Also list preferred name forms where people have changed their
|
||||
# git author config, or having utf8/latin1 encoding issues.
|
||||
# git author config
|
||||
Daniel P. Berrangé <berrange@redhat.com>
|
||||
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
|
||||
+1
-15
@@ -107,14 +107,8 @@ matrix:
|
||||
- env: CONFIG="--disable-tcg"
|
||||
TEST_CMD=""
|
||||
compiler: gcc
|
||||
# MacOSX builds
|
||||
- env: CONFIG="--target-list=aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
|
||||
- env: CONFIG=""
|
||||
os: osx
|
||||
osx_image: xcode9.4
|
||||
compiler: clang
|
||||
- env: CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
|
||||
os: osx
|
||||
osx_image: xcode10
|
||||
compiler: clang
|
||||
# Python builds
|
||||
- env: CONFIG="--target-list=x86_64-softmmu"
|
||||
@@ -123,14 +117,6 @@ matrix:
|
||||
- env: CONFIG="--target-list=x86_64-softmmu"
|
||||
python:
|
||||
- "3.6"
|
||||
# Acceptance (Functional) tests
|
||||
- env: CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu"
|
||||
TEST_CMD="make AVOCADO_SHOW=app check-acceptance"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python3-pip
|
||||
- python3.4-venv
|
||||
# Using newer GCC with sanitizers
|
||||
- addons:
|
||||
apt:
|
||||
|
||||
+102
-317
File diff suppressed because it is too large.
Load diff
@@ -357,7 +357,6 @@ DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
|
||||
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
|
||||
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
|
||||
DOCS+=docs/qemu-block-drivers.7
|
||||
DOCS+=docs/qemu-cpu-models.7
|
||||
ifdef CONFIG_VIRTFS
|
||||
DOCS+=fsdev/virtfs-proxy-helper.1
|
||||
endif
|
||||
@@ -415,7 +414,6 @@ dummy := $(call unnest-vars,, \
|
||||
chardev-obj-y \
|
||||
util-obj-y \
|
||||
qga-obj-y \
|
||||
elf2dmp-obj-y \
|
||||
ivshmem-client-obj-y \
|
||||
ivshmem-server-obj-y \
|
||||
libvhost-user-obj-y \
|
||||
@@ -544,8 +542,6 @@ qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
|
||||
|
||||
qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
|
||||
|
||||
qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
|
||||
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
|
||||
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
|
||||
|
||||
@@ -711,10 +707,6 @@ ifneq ($(EXESUF),)
|
||||
qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI)
|
||||
endif
|
||||
|
||||
elf2dmp: LIBS = $(CURL_LIBS)
|
||||
elf2dmp: $(elf2dmp-obj-y)
|
||||
$(call LINK, $^)
|
||||
|
||||
ifdef CONFIG_IVSHMEM
|
||||
ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS)
|
||||
$(call LINK, $^)
|
||||
@@ -759,7 +751,7 @@ clean:
|
||||
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
|
||||
rm -f $$d/qemu-options.def; \
|
||||
done
|
||||
rm -f config-all-devices.mak
|
||||
rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak
|
||||
|
||||
VERSION ?= $(shell cat VERSION)
|
||||
|
||||
@@ -771,7 +763,6 @@ qemu-%.tar.bz2:
|
||||
distclean: clean
|
||||
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
|
||||
rm -f config-all-devices.mak config-all-disas.mak config.status
|
||||
rm -f $(SUBDIR_DEVICES_MAK)
|
||||
rm -f po/*.mo tests/qemu-iotests/common.env
|
||||
rm -f roms/seabios/config.mak roms/vgabios/config.mak
|
||||
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
|
||||
@@ -787,7 +778,6 @@ distclean: clean
|
||||
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
|
||||
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||
rm -f docs/qemu-block-drivers.7
|
||||
rm -f docs/qemu-cpu-models.7
|
||||
for d in $(TARGET_DIRS); do \
|
||||
rm -rf $$d || exit 1 ; \
|
||||
done
|
||||
@@ -802,7 +792,6 @@ bepo cz
|
||||
ifdef INSTALL_BLOBS
|
||||
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
|
||||
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
|
||||
vgabios-ramfb.bin vgabios-bochs-display.bin \
|
||||
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
|
||||
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
|
||||
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
|
||||
@@ -834,7 +823,6 @@ ifdef CONFIG_POSIX
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
|
||||
$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
|
||||
ifneq ($(TOOLS),)
|
||||
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
|
||||
@@ -977,7 +965,6 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
|
||||
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
|
||||
qemu-ga.8: qemu-ga.texi
|
||||
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
|
||||
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
|
||||
|
||||
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
|
||||
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
|
||||
@@ -986,9 +973,8 @@ txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
|
||||
|
||||
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
|
||||
qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \
|
||||
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||
qemu-monitor-info.texi docs/qemu-block-drivers.texi \
|
||||
docs/qemu-cpu-models.texi
|
||||
qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
|
||||
qemu-monitor-info.texi docs/qemu-block-drivers.texi
|
||||
|
||||
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
|
||||
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
|
||||
|
||||
@@ -186,7 +186,6 @@ qga-vss-dll-obj-y = qga/
|
||||
|
||||
######################################################################
|
||||
# contrib
|
||||
elf2dmp-obj-y = contrib/elf2dmp/
|
||||
ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/
|
||||
ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/
|
||||
libvhost-user-obj-y = contrib/libvhost-user/
|
||||
@@ -241,7 +240,6 @@ trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += hw/usb
|
||||
trace-events-subdirs += hw/vfio
|
||||
trace-events-subdirs += hw/virtio
|
||||
trace-events-subdirs += hw/watchdog
|
||||
trace-events-subdirs += hw/xen
|
||||
trace-events-subdirs += io
|
||||
trace-events-subdirs += linux-user
|
||||
|
||||
@@ -54,7 +54,7 @@ Submitting patches
|
||||
|
||||
The QEMU source code is maintained under the GIT version control system.
|
||||
|
||||
git clone https://git.qemu.org/git/qemu.git
|
||||
git clone git://git.qemu.org/qemu.git
|
||||
|
||||
When submitting patches, one common approach is to use 'git
|
||||
format-patch' and/or 'git send-email' to format & send the mail to the
|
||||
@@ -70,7 +70,7 @@ the QEMU website
|
||||
|
||||
The QEMU website is also maintained under source control.
|
||||
|
||||
git clone https://git.qemu.org/git/qemu-web.git
|
||||
git clone git://git.qemu.org/qemu-web.git
|
||||
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
|
||||
|
||||
A 'git-publish' utility was created to make above process less
|
||||
|
||||
+5
-57
@@ -39,7 +39,6 @@
|
||||
#include "trace.h"
|
||||
#include "hw/irq.h"
|
||||
#include "sysemu/sev.h"
|
||||
#include "sysemu/balloon.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
|
||||
@@ -79,7 +78,6 @@ struct KVMState
|
||||
int fd;
|
||||
int vmfd;
|
||||
int coalesced_mmio;
|
||||
int coalesced_pio;
|
||||
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
|
||||
bool coalesced_flush_in_progress;
|
||||
int vcpu_events;
|
||||
@@ -561,45 +559,6 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener,
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_coalesce_pio_add(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
hwaddr start, hwaddr size)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
|
||||
if (s->coalesced_pio) {
|
||||
struct kvm_coalesced_mmio_zone zone;
|
||||
|
||||
zone.addr = start;
|
||||
zone.size = size;
|
||||
zone.pio = 1;
|
||||
|
||||
(void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_coalesce_pio_del(MemoryListener *listener,
|
||||
MemoryRegionSection *section,
|
||||
hwaddr start, hwaddr size)
|
||||
{
|
||||
KVMState *s = kvm_state;
|
||||
|
||||
if (s->coalesced_pio) {
|
||||
struct kvm_coalesced_mmio_zone zone;
|
||||
|
||||
zone.addr = start;
|
||||
zone.size = size;
|
||||
zone.pio = 1;
|
||||
|
||||
(void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone);
|
||||
}
|
||||
}
|
||||
|
||||
static MemoryListener kvm_coalesced_pio_listener = {
|
||||
.coalesced_io_add = kvm_coalesce_pio_add,
|
||||
.coalesced_io_del = kvm_coalesce_pio_del,
|
||||
};
|
||||
|
||||
int kvm_check_extension(KVMState *s, unsigned int extension)
|
||||
{
|
||||
int ret;
|
||||
@@ -1656,8 +1615,6 @@ static int kvm_init(MachineState *ms)
|
||||
}
|
||||
|
||||
s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO);
|
||||
s->coalesced_pio = s->coalesced_mmio &&
|
||||
kvm_check_extension(s, KVM_CAP_COALESCED_PIO);
|
||||
|
||||
#ifdef KVM_CAP_VCPU_EVENTS
|
||||
s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS);
|
||||
@@ -1681,8 +1638,10 @@ static int kvm_init(MachineState *ms)
|
||||
s->irq_set_ioctl = KVM_IRQ_LINE_STATUS;
|
||||
}
|
||||
|
||||
#ifdef KVM_CAP_READONLY_MEM
|
||||
kvm_readonly_mem_allowed =
|
||||
(kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0);
|
||||
#endif
|
||||
|
||||
kvm_eventfds_allowed =
|
||||
(kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0);
|
||||
@@ -1728,22 +1687,17 @@ static int kvm_init(MachineState *ms)
|
||||
s->memory_listener.listener.eventfd_add = kvm_mem_ioeventfd_add;
|
||||
s->memory_listener.listener.eventfd_del = kvm_mem_ioeventfd_del;
|
||||
}
|
||||
s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region;
|
||||
s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region;
|
||||
s->memory_listener.listener.coalesced_mmio_add = kvm_coalesce_mmio_region;
|
||||
s->memory_listener.listener.coalesced_mmio_del = kvm_uncoalesce_mmio_region;
|
||||
|
||||
kvm_memory_listener_register(s, &s->memory_listener,
|
||||
&address_space_memory, 0);
|
||||
memory_listener_register(&kvm_io_listener,
|
||||
&address_space_io);
|
||||
memory_listener_register(&kvm_coalesced_pio_listener,
|
||||
&address_space_io);
|
||||
|
||||
s->many_ioeventfds = kvm_check_many_ioeventfds();
|
||||
|
||||
s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
||||
if (!s->sync_mmu) {
|
||||
qemu_balloon_inhibit(true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1822,13 +1776,7 @@ void kvm_flush_coalesced_mmio_buffer(void)
|
||||
|
||||
ent = &ring->coalesced_mmio[ring->first];
|
||||
|
||||
if (ent->pio == 1) {
|
||||
address_space_rw(&address_space_io, ent->phys_addr,
|
||||
MEMTXATTRS_UNSPECIFIED, ent->data,
|
||||
ent->len, true);
|
||||
} else {
|
||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
||||
}
|
||||
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
|
||||
smp_wmb();
|
||||
ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX;
|
||||
}
|
||||
|
||||
@@ -100,24 +100,19 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
DATA_TYPE ret;
|
||||
|
||||
ATOMIC_TRACE_RMW;
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, cmpv, newv);
|
||||
#else
|
||||
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
|
||||
#endif
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_LD;
|
||||
val = atomic16_read(haddr);
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return val;
|
||||
}
|
||||
@@ -129,10 +124,9 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_ST;
|
||||
atomic16_set(haddr, val);
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
@@ -234,24 +228,19 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
|
||||
DATA_TYPE ret;
|
||||
|
||||
ATOMIC_TRACE_RMW;
|
||||
#if DATA_SIZE == 16
|
||||
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#else
|
||||
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
|
||||
#endif
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return BSWAP(ret);
|
||||
}
|
||||
|
||||
#if DATA_SIZE >= 16
|
||||
#if HAVE_ATOMIC128
|
||||
ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
||||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_LD;
|
||||
val = atomic16_read(haddr);
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return BSWAP(val);
|
||||
}
|
||||
@@ -264,10 +253,9 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
||||
|
||||
ATOMIC_TRACE_ST;
|
||||
val = BSWAP(val);
|
||||
atomic16_set(haddr, val);
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
||||
ABI_TYPE val EXTRA_ARGS)
|
||||
|
||||
@@ -332,9 +332,6 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
|
||||
desc.trace_vcpu_dstate = *cpu->trace_dstate;
|
||||
desc.pc = pc;
|
||||
phys_pc = get_page_addr_code(desc.env, pc);
|
||||
if (phys_pc == -1) {
|
||||
return NULL;
|
||||
}
|
||||
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
|
||||
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
|
||||
return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
|
||||
@@ -416,7 +413,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||
}
|
||||
#endif
|
||||
/* See if we can patch the calling TB. */
|
||||
if (last_tb) {
|
||||
if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
return tb;
|
||||
|
||||
+391
-236
@@ -32,7 +32,6 @@
|
||||
#include "exec/log.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "qemu/atomic128.h"
|
||||
|
||||
/* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */
|
||||
/* #define DEBUG_TLB */
|
||||
@@ -59,9 +58,9 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define assert_cpu_is_self(cpu) do { \
|
||||
#define assert_cpu_is_self(this_cpu) do { \
|
||||
if (DEBUG_TLB_GATE) { \
|
||||
g_assert(!(cpu)->created || qemu_cpu_is_self(cpu)); \
|
||||
g_assert(!cpu->created || qemu_cpu_is_self(cpu)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@@ -74,16 +73,6 @@ QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data));
|
||||
QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16);
|
||||
#define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1)
|
||||
|
||||
void tlb_init(CPUState *cpu)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
|
||||
qemu_spin_init(&env->tlb_c.lock);
|
||||
|
||||
/* Ensure that cpu_reset performs a full flush. */
|
||||
env->tlb_c.dirty = ALL_MMUIDX_BITS;
|
||||
}
|
||||
|
||||
/* flush_all_helper: run fn across all cpus
|
||||
*
|
||||
* If the wait flag is set then the src cpu's helper will be queued as
|
||||
@@ -103,87 +92,126 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn,
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide)
|
||||
size_t tlb_flush_count(void)
|
||||
{
|
||||
CPUState *cpu;
|
||||
size_t full = 0, part = 0, elide = 0;
|
||||
size_t count = 0;
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
|
||||
full += atomic_read(&env->tlb_c.full_flush_count);
|
||||
part += atomic_read(&env->tlb_c.part_flush_count);
|
||||
elide += atomic_read(&env->tlb_c.elide_flush_count);
|
||||
count += atomic_read(&env->tlb_flush_count);
|
||||
}
|
||||
*pfull = full;
|
||||
*ppart = part;
|
||||
*pelide = elide;
|
||||
return count;
|
||||
}
|
||||
|
||||
static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx)
|
||||
/* This is OK because CPU architectures generally permit an
|
||||
* implementation to drop entries from the TLB at any time, so
|
||||
* flushing more entries than required is only an efficiency issue,
|
||||
* not a correctness issue.
|
||||
*/
|
||||
static void tlb_flush_nocheck(CPUState *cpu)
|
||||
{
|
||||
memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0]));
|
||||
memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0]));
|
||||
env->tlb_d[mmu_idx].large_page_addr = -1;
|
||||
env->tlb_d[mmu_idx].large_page_mask = -1;
|
||||
env->tlb_d[mmu_idx].vindex = 0;
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
|
||||
/* The QOM tests will trigger tlb_flushes without setting up TCG
|
||||
* so we bug out here in that case.
|
||||
*/
|
||||
if (!tcg_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1);
|
||||
tlb_debug("(count: %zu)\n", tlb_flush_count());
|
||||
|
||||
memset(env->tlb_table, -1, sizeof(env->tlb_table));
|
||||
memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table));
|
||||
cpu_tb_jmp_cache_clear(cpu);
|
||||
|
||||
env->vtlb_index = 0;
|
||||
env->tlb_flush_addr = -1;
|
||||
env->tlb_flush_mask = 0;
|
||||
|
||||
atomic_mb_set(&cpu->pending_tlb_flush, 0);
|
||||
}
|
||||
|
||||
static void tlb_flush_global_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
tlb_flush_nocheck(cpu);
|
||||
}
|
||||
|
||||
void tlb_flush(CPUState *cpu)
|
||||
{
|
||||
if (cpu->created && !qemu_cpu_is_self(cpu)) {
|
||||
if (atomic_mb_read(&cpu->pending_tlb_flush) != ALL_MMUIDX_BITS) {
|
||||
atomic_mb_set(&cpu->pending_tlb_flush, ALL_MMUIDX_BITS);
|
||||
async_run_on_cpu(cpu, tlb_flush_global_async_work,
|
||||
RUN_ON_CPU_NULL);
|
||||
}
|
||||
} else {
|
||||
tlb_flush_nocheck(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_all_cpus(CPUState *src_cpu)
|
||||
{
|
||||
const run_on_cpu_func fn = tlb_flush_global_async_work;
|
||||
flush_all_helper(src_cpu, fn, RUN_ON_CPU_NULL);
|
||||
fn(src_cpu, RUN_ON_CPU_NULL);
|
||||
}
|
||||
|
||||
void tlb_flush_all_cpus_synced(CPUState *src_cpu)
|
||||
{
|
||||
const run_on_cpu_func fn = tlb_flush_global_async_work;
|
||||
flush_all_helper(src_cpu, fn, RUN_ON_CPU_NULL);
|
||||
async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_NULL);
|
||||
}
|
||||
|
||||
static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
uint16_t asked = data.host_int;
|
||||
uint16_t all_dirty, work, to_clean;
|
||||
unsigned long mmu_idx_bitmask = data.host_int;
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
tlb_debug("mmu_idx:0x%04" PRIx16 "\n", asked);
|
||||
tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask);
|
||||
|
||||
qemu_spin_lock(&env->tlb_c.lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
|
||||
all_dirty = env->tlb_c.dirty;
|
||||
to_clean = asked & all_dirty;
|
||||
all_dirty &= ~to_clean;
|
||||
env->tlb_c.dirty = all_dirty;
|
||||
if (test_bit(mmu_idx, &mmu_idx_bitmask)) {
|
||||
tlb_debug("%d\n", mmu_idx);
|
||||
|
||||
for (work = to_clean; work != 0; work &= work - 1) {
|
||||
int mmu_idx = ctz32(work);
|
||||
tlb_flush_one_mmuidx_locked(env, mmu_idx);
|
||||
memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0]));
|
||||
memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0]));
|
||||
}
|
||||
}
|
||||
|
||||
qemu_spin_unlock(&env->tlb_c.lock);
|
||||
|
||||
cpu_tb_jmp_cache_clear(cpu);
|
||||
|
||||
if (to_clean == ALL_MMUIDX_BITS) {
|
||||
atomic_set(&env->tlb_c.full_flush_count,
|
||||
env->tlb_c.full_flush_count + 1);
|
||||
} else {
|
||||
atomic_set(&env->tlb_c.part_flush_count,
|
||||
env->tlb_c.part_flush_count + ctpop16(to_clean));
|
||||
if (to_clean != asked) {
|
||||
atomic_set(&env->tlb_c.elide_flush_count,
|
||||
env->tlb_c.elide_flush_count +
|
||||
ctpop16(asked & ~to_clean));
|
||||
}
|
||||
}
|
||||
tlb_debug("done\n");
|
||||
}
|
||||
|
||||
void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
|
||||
{
|
||||
tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap);
|
||||
|
||||
if (cpu->created && !qemu_cpu_is_self(cpu)) {
|
||||
async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work,
|
||||
RUN_ON_CPU_HOST_INT(idxmap));
|
||||
} else {
|
||||
tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap));
|
||||
}
|
||||
}
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
uint16_t pending_flushes = idxmap;
|
||||
pending_flushes &= ~atomic_mb_read(&cpu->pending_tlb_flush);
|
||||
|
||||
void tlb_flush(CPUState *cpu)
|
||||
{
|
||||
tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS);
|
||||
if (pending_flushes) {
|
||||
tlb_debug("reduced mmu_idx: 0x%" PRIx16 "\n", pending_flushes);
|
||||
|
||||
atomic_or(&cpu->pending_tlb_flush, pending_flushes);
|
||||
async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work,
|
||||
RUN_ON_CPU_HOST_INT(pending_flushes));
|
||||
}
|
||||
} else {
|
||||
tlb_flush_by_mmuidx_async_work(cpu,
|
||||
RUN_ON_CPU_HOST_INT(idxmap));
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap)
|
||||
@@ -196,12 +224,8 @@ void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap)
|
||||
fn(src_cpu, RUN_ON_CPU_HOST_INT(idxmap));
|
||||
}
|
||||
|
||||
void tlb_flush_all_cpus(CPUState *src_cpu)
|
||||
{
|
||||
tlb_flush_by_mmuidx_all_cpus(src_cpu, ALL_MMUIDX_BITS);
|
||||
}
|
||||
|
||||
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap)
|
||||
void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work;
|
||||
|
||||
@@ -211,55 +235,70 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap)
|
||||
async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap));
|
||||
}
|
||||
|
||||
void tlb_flush_all_cpus_synced(CPUState *src_cpu)
|
||||
{
|
||||
tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, ALL_MMUIDX_BITS);
|
||||
}
|
||||
|
||||
static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry,
|
||||
target_ulong page)
|
||||
{
|
||||
return tlb_hit_page(tlb_entry->addr_read, page) ||
|
||||
tlb_hit_page(tlb_addr_write(tlb_entry), page) ||
|
||||
tlb_hit_page(tlb_entry->addr_write, page) ||
|
||||
tlb_hit_page(tlb_entry->addr_code, page);
|
||||
}
|
||||
|
||||
/* Called with tlb_c.lock held */
|
||||
static inline void tlb_flush_entry_locked(CPUTLBEntry *tlb_entry,
|
||||
target_ulong page)
|
||||
static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong page)
|
||||
{
|
||||
if (tlb_hit_page_anyprot(tlb_entry, page)) {
|
||||
memset(tlb_entry, -1, sizeof(*tlb_entry));
|
||||
}
|
||||
}
|
||||
|
||||
/* Called with tlb_c.lock held */
|
||||
static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx,
|
||||
target_ulong page)
|
||||
static inline void tlb_flush_vtlb_page(CPUArchState *env, int mmu_idx,
|
||||
target_ulong page)
|
||||
{
|
||||
int k;
|
||||
|
||||
assert_cpu_is_self(ENV_GET_CPU(env));
|
||||
for (k = 0; k < CPU_VTLB_SIZE; k++) {
|
||||
tlb_flush_entry_locked(&env->tlb_v_table[mmu_idx][k], page);
|
||||
tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], page);
|
||||
}
|
||||
}
|
||||
|
||||
static void tlb_flush_page_locked(CPUArchState *env, int midx,
|
||||
target_ulong page)
|
||||
static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data)
|
||||
{
|
||||
target_ulong lp_addr = env->tlb_d[midx].large_page_addr;
|
||||
target_ulong lp_mask = env->tlb_d[midx].large_page_mask;
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
target_ulong addr = (target_ulong) data.target_ptr;
|
||||
int i;
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
tlb_debug("page :" TARGET_FMT_lx "\n", addr);
|
||||
|
||||
/* Check if we need to flush due to large pages. */
|
||||
if ((page & lp_mask) == lp_addr) {
|
||||
tlb_debug("forcing full flush midx %d ("
|
||||
if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) {
|
||||
tlb_debug("forcing full flush ("
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
|
||||
midx, lp_addr, lp_mask);
|
||||
tlb_flush_one_mmuidx_locked(env, midx);
|
||||
env->tlb_flush_addr, env->tlb_flush_mask);
|
||||
|
||||
tlb_flush(cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
|
||||
tlb_flush_vtlb_page(env, mmu_idx, addr);
|
||||
}
|
||||
|
||||
tb_flush_jmp_cache(cpu, addr);
|
||||
}
|
||||
|
||||
void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
{
|
||||
tlb_debug("page :" TARGET_FMT_lx "\n", addr);
|
||||
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
async_run_on_cpu(cpu, tlb_flush_page_async_work,
|
||||
RUN_ON_CPU_TARGET_PTR(addr));
|
||||
} else {
|
||||
tlb_flush_entry_locked(tlb_entry(env, midx, page), page);
|
||||
tlb_flush_vtlb_page_locked(env, midx, page);
|
||||
tlb_flush_page_async_work(cpu, RUN_ON_CPU_TARGET_PTR(addr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,24 +314,47 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu,
|
||||
target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr;
|
||||
target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK;
|
||||
unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS;
|
||||
int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%lx\n",
|
||||
addr, mmu_idx_bitmap);
|
||||
tlb_debug("page:%d addr:"TARGET_FMT_lx" mmu_idx:0x%lx\n",
|
||||
page, addr, mmu_idx_bitmap);
|
||||
|
||||
qemu_spin_lock(&env->tlb_c.lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
if (test_bit(mmu_idx, &mmu_idx_bitmap)) {
|
||||
tlb_flush_page_locked(env, mmu_idx, addr);
|
||||
tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr);
|
||||
tlb_flush_vtlb_page(env, mmu_idx, addr);
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&env->tlb_c.lock);
|
||||
|
||||
tb_flush_jmp_cache(cpu, addr);
|
||||
}
|
||||
|
||||
static void tlb_check_page_and_flush_by_mmuidx_async_work(CPUState *cpu,
|
||||
run_on_cpu_data data)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr;
|
||||
target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK;
|
||||
unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS;
|
||||
|
||||
tlb_debug("addr:"TARGET_FMT_lx" mmu_idx: %04lx\n", addr, mmu_idx_bitmap);
|
||||
|
||||
/* Check if we need to flush due to large pages. */
|
||||
if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) {
|
||||
tlb_debug("forced full flush ("
|
||||
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
|
||||
env->tlb_flush_addr, env->tlb_flush_mask);
|
||||
|
||||
tlb_flush_by_mmuidx_async_work(cpu,
|
||||
RUN_ON_CPU_HOST_INT(mmu_idx_bitmap));
|
||||
} else {
|
||||
tlb_flush_page_by_mmuidx_async_work(cpu, data);
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap)
|
||||
{
|
||||
target_ulong addr_and_mmu_idx;
|
||||
@@ -304,23 +366,18 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap)
|
||||
addr_and_mmu_idx |= idxmap;
|
||||
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_work,
|
||||
async_run_on_cpu(cpu, tlb_check_page_and_flush_by_mmuidx_async_work,
|
||||
RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
} else {
|
||||
tlb_flush_page_by_mmuidx_async_work(
|
||||
tlb_check_page_and_flush_by_mmuidx_async_work(
|
||||
cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
}
|
||||
}
|
||||
|
||||
void tlb_flush_page(CPUState *cpu, target_ulong addr)
|
||||
{
|
||||
tlb_flush_page_by_mmuidx(cpu, addr, ALL_MMUIDX_BITS);
|
||||
}
|
||||
|
||||
void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work;
|
||||
const run_on_cpu_func fn = tlb_check_page_and_flush_by_mmuidx_async_work;
|
||||
target_ulong addr_and_mmu_idx;
|
||||
|
||||
tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap);
|
||||
@@ -333,16 +390,11 @@ void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr,
|
||||
fn(src_cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
}
|
||||
|
||||
void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr)
|
||||
{
|
||||
tlb_flush_page_by_mmuidx_all_cpus(src, addr, ALL_MMUIDX_BITS);
|
||||
}
|
||||
|
||||
void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||
target_ulong addr,
|
||||
uint16_t idxmap)
|
||||
target_ulong addr,
|
||||
uint16_t idxmap)
|
||||
{
|
||||
const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work;
|
||||
const run_on_cpu_func fn = tlb_check_page_and_flush_by_mmuidx_async_work;
|
||||
target_ulong addr_and_mmu_idx;
|
||||
|
||||
tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap);
|
||||
@@ -355,9 +407,21 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
|
||||
async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx));
|
||||
}
|
||||
|
||||
void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr)
|
||||
void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr)
|
||||
{
|
||||
tlb_flush_page_by_mmuidx_all_cpus_synced(src, addr, ALL_MMUIDX_BITS);
|
||||
const run_on_cpu_func fn = tlb_flush_page_async_work;
|
||||
|
||||
flush_all_helper(src, fn, RUN_ON_CPU_TARGET_PTR(addr));
|
||||
fn(src, RUN_ON_CPU_TARGET_PTR(addr));
|
||||
}
|
||||
|
||||
void tlb_flush_page_all_cpus_synced(CPUState *src,
|
||||
target_ulong addr)
|
||||
{
|
||||
const run_on_cpu_func fn = tlb_flush_page_async_work;
|
||||
|
||||
flush_all_helper(src, fn, RUN_ON_CPU_TARGET_PTR(addr));
|
||||
async_safe_run_on_cpu(src, fn, RUN_ON_CPU_TARGET_PTR(addr));
|
||||
}
|
||||
|
||||
/* update the TLBs so that writes to code in the virtual page 'addr'
|
||||
@@ -386,44 +450,72 @@ void tlb_unprotect_code(ram_addr_t ram_addr)
|
||||
* most usual is detecting writes to code regions which may invalidate
|
||||
* generated code.
|
||||
*
|
||||
* Other vCPUs might be reading their TLBs during guest execution, so we update
|
||||
* te->addr_write with atomic_set. We don't need to worry about this for
|
||||
* oversized guests as MTTCG is disabled for them.
|
||||
* Because we want other vCPUs to respond to changes straight away we
|
||||
* update the te->addr_write field atomically. If the TLB entry has
|
||||
* been changed by the vCPU in the mean time we skip the update.
|
||||
*
|
||||
* Called with tlb_c.lock held.
|
||||
* As this function uses atomic accesses we also need to ensure
|
||||
* updates to tlb_entries follow the same access rules. We don't need
|
||||
* to worry about this for oversized guests as MTTCG is disabled for
|
||||
* them.
|
||||
*/
|
||||
static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry,
|
||||
uintptr_t start, uintptr_t length)
|
||||
|
||||
static void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start,
|
||||
uintptr_t length)
|
||||
{
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
uintptr_t addr = tlb_entry->addr_write;
|
||||
|
||||
if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) {
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
addr += tlb_entry->addend;
|
||||
if ((addr - start) < length) {
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
tlb_entry->addr_write |= TLB_NOTDIRTY;
|
||||
#else
|
||||
atomic_set(&tlb_entry->addr_write,
|
||||
tlb_entry->addr_write | TLB_NOTDIRTY);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* paired with atomic_mb_set in tlb_set_page_with_attrs */
|
||||
uintptr_t orig_addr = atomic_mb_read(&tlb_entry->addr_write);
|
||||
uintptr_t addr = orig_addr;
|
||||
|
||||
if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) {
|
||||
addr &= TARGET_PAGE_MASK;
|
||||
addr += atomic_read(&tlb_entry->addend);
|
||||
if ((addr - start) < length) {
|
||||
uintptr_t notdirty_addr = orig_addr | TLB_NOTDIRTY;
|
||||
atomic_cmpxchg(&tlb_entry->addr_write, orig_addr, notdirty_addr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Called with tlb_c.lock held.
|
||||
* Called only from the vCPU context, i.e. the TLB's owner thread.
|
||||
*/
|
||||
static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s)
|
||||
/* For atomic correctness when running MTTCG we need to use the right
|
||||
* primitives when copying entries */
|
||||
static inline void copy_tlb_helper(CPUTLBEntry *d, CPUTLBEntry *s,
|
||||
bool atomic_set)
|
||||
{
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
*d = *s;
|
||||
#else
|
||||
if (atomic_set) {
|
||||
d->addr_read = s->addr_read;
|
||||
d->addr_code = s->addr_code;
|
||||
atomic_set(&d->addend, atomic_read(&s->addend));
|
||||
/* Pairs with flag setting in tlb_reset_dirty_range */
|
||||
atomic_mb_set(&d->addr_write, atomic_read(&s->addr_write));
|
||||
} else {
|
||||
d->addr_read = s->addr_read;
|
||||
d->addr_write = atomic_read(&s->addr_write);
|
||||
d->addr_code = s->addr_code;
|
||||
d->addend = atomic_read(&s->addend);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This is a cross vCPU call (i.e. another vCPU resetting the flags of
|
||||
* the target vCPU).
|
||||
* We must take tlb_c.lock to avoid racing with another vCPU update. The only
|
||||
* thing actually updated is the target TLB entry ->addr_write flags.
|
||||
* the target vCPU). As such care needs to be taken that we don't
|
||||
* dangerously race with another vCPU update. The only thing actually
|
||||
* updated is the target TLB entry ->addr_write flags.
|
||||
*/
|
||||
void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
|
||||
{
|
||||
@@ -432,26 +524,22 @@ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length)
|
||||
int mmu_idx;
|
||||
|
||||
env = cpu->env_ptr;
|
||||
qemu_spin_lock(&env->tlb_c.lock);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < CPU_TLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range_locked(&env->tlb_table[mmu_idx][i], start1,
|
||||
length);
|
||||
tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i],
|
||||
start1, length);
|
||||
}
|
||||
|
||||
for (i = 0; i < CPU_VTLB_SIZE; i++) {
|
||||
tlb_reset_dirty_range_locked(&env->tlb_v_table[mmu_idx][i], start1,
|
||||
length);
|
||||
tlb_reset_dirty_range(&env->tlb_v_table[mmu_idx][i],
|
||||
start1, length);
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&env->tlb_c.lock);
|
||||
}
|
||||
|
||||
/* Called with tlb_c.lock held */
|
||||
static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry,
|
||||
target_ulong vaddr)
|
||||
static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr)
|
||||
{
|
||||
if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) {
|
||||
tlb_entry->addr_write = vaddr;
|
||||
@@ -463,47 +551,46 @@ static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry,
|
||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
||||
{
|
||||
CPUArchState *env = cpu->env_ptr;
|
||||
int i;
|
||||
int mmu_idx;
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
vaddr &= TARGET_PAGE_MASK;
|
||||
qemu_spin_lock(&env->tlb_c.lock);
|
||||
i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, vaddr), vaddr);
|
||||
tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr);
|
||||
}
|
||||
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
int k;
|
||||
for (k = 0; k < CPU_VTLB_SIZE; k++) {
|
||||
tlb_set_dirty1_locked(&env->tlb_v_table[mmu_idx][k], vaddr);
|
||||
tlb_set_dirty1(&env->tlb_v_table[mmu_idx][k], vaddr);
|
||||
}
|
||||
}
|
||||
qemu_spin_unlock(&env->tlb_c.lock);
|
||||
}
|
||||
|
||||
/* Our TLB does not support large pages, so remember the area covered by
|
||||
large pages and trigger a full TLB flush if these are invalidated. */
|
||||
static void tlb_add_large_page(CPUArchState *env, int mmu_idx,
|
||||
target_ulong vaddr, target_ulong size)
|
||||
static void tlb_add_large_page(CPUArchState *env, target_ulong vaddr,
|
||||
target_ulong size)
|
||||
{
|
||||
target_ulong lp_addr = env->tlb_d[mmu_idx].large_page_addr;
|
||||
target_ulong lp_mask = ~(size - 1);
|
||||
target_ulong mask = ~(size - 1);
|
||||
|
||||
if (lp_addr == (target_ulong)-1) {
|
||||
/* No previous large page. */
|
||||
lp_addr = vaddr;
|
||||
} else {
|
||||
/* Extend the existing region to include the new page.
|
||||
This is a compromise between unnecessary flushes and
|
||||
the cost of maintaining a full variable size TLB. */
|
||||
lp_mask &= env->tlb_d[mmu_idx].large_page_mask;
|
||||
while (((lp_addr ^ vaddr) & lp_mask) != 0) {
|
||||
lp_mask <<= 1;
|
||||
}
|
||||
if (env->tlb_flush_addr == (target_ulong)-1) {
|
||||
env->tlb_flush_addr = vaddr & mask;
|
||||
env->tlb_flush_mask = mask;
|
||||
return;
|
||||
}
|
||||
env->tlb_d[mmu_idx].large_page_addr = lp_addr & lp_mask;
|
||||
env->tlb_d[mmu_idx].large_page_mask = lp_mask;
|
||||
/* Extend the existing region to include the new page.
|
||||
This is a compromise between unnecessary flushes and the cost
|
||||
of maintaining a full variable size TLB. */
|
||||
mask &= env->tlb_flush_mask;
|
||||
while (((env->tlb_flush_addr ^ vaddr) & mask) != 0) {
|
||||
mask <<= 1;
|
||||
}
|
||||
env->tlb_flush_addr &= mask;
|
||||
env->tlb_flush_mask = mask;
|
||||
}
|
||||
|
||||
/* Add a new TLB entry. At most one entry for a given virtual address
|
||||
@@ -530,10 +617,12 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
|
||||
if (size <= TARGET_PAGE_SIZE) {
|
||||
if (size < TARGET_PAGE_SIZE) {
|
||||
sz = TARGET_PAGE_SIZE;
|
||||
} else {
|
||||
tlb_add_large_page(env, mmu_idx, vaddr, size);
|
||||
if (size > TARGET_PAGE_SIZE) {
|
||||
tlb_add_large_page(env, vaddr, size);
|
||||
}
|
||||
sz = size;
|
||||
}
|
||||
vaddr_page = vaddr & TARGET_PAGE_MASK;
|
||||
@@ -565,38 +654,26 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat;
|
||||
}
|
||||
|
||||
/* Make sure there's no cached translation for the new page. */
|
||||
tlb_flush_vtlb_page(env, mmu_idx, vaddr_page);
|
||||
|
||||
code_address = address;
|
||||
iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page,
|
||||
paddr_page, xlat, prot, &address);
|
||||
|
||||
index = tlb_index(env, mmu_idx, vaddr_page);
|
||||
te = tlb_entry(env, mmu_idx, vaddr_page);
|
||||
|
||||
/*
|
||||
* Hold the TLB lock for the rest of the function. We could acquire/release
|
||||
* the lock several times in the function, but it is faster to amortize the
|
||||
* acquisition cost by acquiring it just once. Note that this leads to
|
||||
* a longer critical section, but this is not a concern since the TLB lock
|
||||
* is unlikely to be contended.
|
||||
*/
|
||||
qemu_spin_lock(&env->tlb_c.lock);
|
||||
|
||||
/* Note that the tlb is no longer clean. */
|
||||
env->tlb_c.dirty |= 1 << mmu_idx;
|
||||
|
||||
/* Make sure there's no cached translation for the new page. */
|
||||
tlb_flush_vtlb_page_locked(env, mmu_idx, vaddr_page);
|
||||
index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
te = &env->tlb_table[mmu_idx][index];
|
||||
|
||||
/*
|
||||
* Only evict the old entry to the victim tlb if it's for a
|
||||
* different page; otherwise just overwrite the stale data.
|
||||
*/
|
||||
if (!tlb_hit_page_anyprot(te, vaddr_page)) {
|
||||
unsigned vidx = env->tlb_d[mmu_idx].vindex++ % CPU_VTLB_SIZE;
|
||||
unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE;
|
||||
CPUTLBEntry *tv = &env->tlb_v_table[mmu_idx][vidx];
|
||||
|
||||
/* Evict the old entry into the victim tlb. */
|
||||
copy_tlb_helper_locked(tv, te);
|
||||
copy_tlb_helper(tv, te, true);
|
||||
env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index];
|
||||
}
|
||||
|
||||
@@ -648,8 +725,9 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
||||
}
|
||||
}
|
||||
|
||||
copy_tlb_helper_locked(te, &tn);
|
||||
qemu_spin_unlock(&env->tlb_c.lock);
|
||||
/* Pairs with flag setting in tlb_reset_dirty_range */
|
||||
copy_tlb_helper(te, &tn, true);
|
||||
/* atomic_mb_set(&te->addr_write, write_address); */
|
||||
}
|
||||
|
||||
/* Add a new TLB entry, but without specifying the memory
|
||||
@@ -663,6 +741,39 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr,
|
||||
prot, mmu_idx, size);
|
||||
}
|
||||
|
||||
static void report_bad_exec(CPUState *cpu, target_ulong addr)
|
||||
{
|
||||
/* Accidentally executing outside RAM or ROM is quite common for
|
||||
* several user-error situations, so report it in a way that
|
||||
* makes it clear that this isn't a QEMU bug and provide suggestions
|
||||
* about what a user could do to fix things.
|
||||
*/
|
||||
error_report("Trying to execute code outside RAM or ROM at 0x"
|
||||
TARGET_FMT_lx, addr);
|
||||
error_printf("This usually means one of the following happened:\n\n"
|
||||
"(1) You told QEMU to execute a kernel for the wrong machine "
|
||||
"type, and it crashed on startup (eg trying to run a "
|
||||
"raspberry pi kernel on a versatilepb QEMU machine)\n"
|
||||
"(2) You didn't give QEMU a kernel or BIOS filename at all, "
|
||||
"and QEMU executed a ROM full of no-op instructions until "
|
||||
"it fell off the end\n"
|
||||
"(3) Your guest kernel has a bug and crashed by jumping "
|
||||
"off into nowhere\n\n"
|
||||
"This is almost always one of the first two, so check your "
|
||||
"command line and that you are using the right type of kernel "
|
||||
"for this machine.\n"
|
||||
"If you think option (3) is likely then you can try debugging "
|
||||
"your guest with the -d debug options; in particular "
|
||||
"-d guest_errors will cause the log to include a dump of the "
|
||||
"guest register state at this point.\n\n"
|
||||
"Execution cannot continue; stopping here.\n\n");
|
||||
|
||||
/* Report also to the logs, with more detail including register dump */
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "qemu: fatal: Trying to execute code "
|
||||
"outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr);
|
||||
log_cpu_state_mask(LOG_GUEST_ERROR, cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP);
|
||||
}
|
||||
|
||||
static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
|
||||
{
|
||||
ram_addr_t ram_addr;
|
||||
@@ -678,7 +789,7 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
|
||||
static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
int mmu_idx,
|
||||
target_ulong addr, uintptr_t retaddr,
|
||||
bool recheck, MMUAccessType access_type, int size)
|
||||
bool recheck, int size)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr mr_offset;
|
||||
@@ -695,16 +806,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
CPUTLBEntry *entry;
|
||||
int index;
|
||||
target_ulong tlb_addr;
|
||||
|
||||
tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr);
|
||||
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
tlb_addr = entry->addr_read;
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_read;
|
||||
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
|
||||
/* RAM access */
|
||||
uintptr_t haddr = addr + entry->addend;
|
||||
uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
|
||||
return ldn_p((void *)haddr, size);
|
||||
}
|
||||
@@ -720,7 +831,6 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
}
|
||||
|
||||
cpu->mem_io_vaddr = addr;
|
||||
cpu->mem_io_access_type = access_type;
|
||||
|
||||
if (mr->global_locking && !qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_lock_iothread();
|
||||
@@ -733,7 +843,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
section->offset_within_address_space -
|
||||
section->offset_within_region;
|
||||
|
||||
cpu_transaction_failed(cpu, physaddr, addr, size, access_type,
|
||||
cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_LOAD,
|
||||
mmu_idx, iotlbentry->attrs, r, retaddr);
|
||||
}
|
||||
if (locked) {
|
||||
@@ -762,16 +872,16 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
CPUTLBEntry *entry;
|
||||
int index;
|
||||
target_ulong tlb_addr;
|
||||
|
||||
tlb_fill(cpu, addr, size, MMU_DATA_STORE, mmu_idx, retaddr);
|
||||
|
||||
entry = tlb_entry(env, mmu_idx, addr);
|
||||
tlb_addr = tlb_addr_write(entry);
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
|
||||
/* RAM access */
|
||||
uintptr_t haddr = addr + entry->addend;
|
||||
uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
|
||||
stn_p((void *)haddr, size, val);
|
||||
return;
|
||||
@@ -813,28 +923,17 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
size_t elt_ofs, target_ulong page)
|
||||
{
|
||||
size_t vidx;
|
||||
|
||||
assert_cpu_is_self(ENV_GET_CPU(env));
|
||||
for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) {
|
||||
CPUTLBEntry *vtlb = &env->tlb_v_table[mmu_idx][vidx];
|
||||
target_ulong cmp;
|
||||
|
||||
/* elt_ofs might correspond to .addr_write, so use atomic_read */
|
||||
#if TCG_OVERSIZED_GUEST
|
||||
cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs);
|
||||
#else
|
||||
cmp = atomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs));
|
||||
#endif
|
||||
target_ulong cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs);
|
||||
|
||||
if (cmp == page) {
|
||||
/* Found entry in victim tlb, swap tlb and iotlb. */
|
||||
CPUTLBEntry tmptlb, *tlb = &env->tlb_table[mmu_idx][index];
|
||||
|
||||
qemu_spin_lock(&env->tlb_c.lock);
|
||||
copy_tlb_helper_locked(&tmptlb, tlb);
|
||||
copy_tlb_helper_locked(tlb, vtlb);
|
||||
copy_tlb_helper_locked(vtlb, &tmptlb);
|
||||
qemu_spin_unlock(&env->tlb_c.lock);
|
||||
copy_tlb_helper(&tmptlb, tlb, false);
|
||||
copy_tlb_helper(tlb, vtlb, true);
|
||||
copy_tlb_helper(vtlb, &tmptlb, true);
|
||||
|
||||
CPUIOTLBEntry tmpio, *io = &env->iotlb[mmu_idx][index];
|
||||
CPUIOTLBEntry *vio = &env->iotlb_v[mmu_idx][vidx];
|
||||
@@ -857,31 +956,87 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index,
|
||||
*/
|
||||
tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
|
||||
{
|
||||
uintptr_t mmu_idx = cpu_mmu_index(env, true);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
int mmu_idx, index;
|
||||
void *p;
|
||||
MemoryRegion *mr;
|
||||
MemoryRegionSection *section;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
CPUIOTLBEntry *iotlbentry;
|
||||
hwaddr physaddr, mr_offset;
|
||||
|
||||
if (unlikely(!tlb_hit(entry->addr_code, addr))) {
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = cpu_mmu_index(env, true);
|
||||
if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) {
|
||||
if (!VICTIM_TLB_HIT(addr_code, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0);
|
||||
}
|
||||
assert(tlb_hit(entry->addr_code, addr));
|
||||
assert(tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr));
|
||||
}
|
||||
|
||||
if (unlikely(entry->addr_code & (TLB_RECHECK | TLB_MMIO))) {
|
||||
if (unlikely(env->tlb_table[mmu_idx][index].addr_code & TLB_RECHECK)) {
|
||||
/*
|
||||
* Return -1 if we can't translate and execute from an entire
|
||||
* page of RAM here, which will cause us to execute by loading
|
||||
* and translating one insn at a time, without caching:
|
||||
* - TLB_RECHECK: means the MMU protection covers a smaller range
|
||||
* than a target page, so we must redo the MMU check every insn
|
||||
* - TLB_MMIO: region is not backed by RAM
|
||||
* This is a TLB_RECHECK access, where the MMU protection
|
||||
* covers a smaller range than a target page, and we must
|
||||
* repeat the MMU check here. This tlb_fill() call might
|
||||
* longjump out if this access should cause a guest exception.
|
||||
*/
|
||||
int index;
|
||||
target_ulong tlb_addr;
|
||||
|
||||
tlb_fill(cpu, addr, 0, MMU_INST_FETCH, mmu_idx, 0);
|
||||
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_code;
|
||||
if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) {
|
||||
/* RAM access. We can't handle this, so for now just stop */
|
||||
cpu_abort(cpu, "Unable to handle guest executing from RAM within "
|
||||
"a small MPU region at 0x" TARGET_FMT_lx, addr);
|
||||
}
|
||||
/*
|
||||
* Fall through to handle IO accesses (which will almost certainly
|
||||
* also result in failure)
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
|
||||
p = (void *)((uintptr_t)addr + entry->addend);
|
||||
iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
|
||||
mr = section->mr;
|
||||
if (memory_region_is_unassigned(mr)) {
|
||||
qemu_mutex_lock_iothread();
|
||||
if (memory_region_request_mmio_ptr(mr, addr)) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
/* A MemoryRegion is potentially added so re-run the
|
||||
* get_page_addr_code.
|
||||
*/
|
||||
return get_page_addr_code(env, addr);
|
||||
}
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
||||
/* Give the new-style cpu_transaction_failed() hook first chance
|
||||
* to handle this.
|
||||
* This is not the ideal place to detect and generate CPU
|
||||
* exceptions for instruction fetch failure (for instance
|
||||
* we don't know the length of the access that the CPU would
|
||||
* use, and it would be better to go ahead and try the access
|
||||
* and use the MemTXResult it produced). However it is the
|
||||
* simplest place we have currently available for the check.
|
||||
*/
|
||||
mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
|
||||
physaddr = mr_offset +
|
||||
section->offset_within_address_space -
|
||||
section->offset_within_region;
|
||||
cpu_transaction_failed(cpu, physaddr, addr, 0, MMU_INST_FETCH, mmu_idx,
|
||||
iotlbentry->attrs, MEMTX_DECODE_ERROR, 0);
|
||||
|
||||
cpu_unassigned_access(cpu, addr, false, true, 0, 4);
|
||||
/* The CPU's unassigned access hook might have longjumped out
|
||||
* with an exception. If it didn't (or there was no hook) then
|
||||
* we can't proceed further.
|
||||
*/
|
||||
report_bad_exec(cpu, addr);
|
||||
exit(1);
|
||||
}
|
||||
p = (void *)((uintptr_t)addr + env->tlb_table[mmu_idx][index].addend);
|
||||
return qemu_ram_addr_from_host_nofail(p);
|
||||
}
|
||||
|
||||
@@ -894,10 +1049,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
|
||||
void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx,
|
||||
uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
|
||||
if (!tlb_hit(tlb_addr_write(entry), addr)) {
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
/* TLB entry is for a different page */
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE,
|
||||
@@ -913,9 +1068,9 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
NotDirtyInfo *ndi)
|
||||
{
|
||||
size_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *tlbe = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(tlbe);
|
||||
size_t index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
CPUTLBEntry *tlbe = &env->tlb_table[mmu_idx][index];
|
||||
target_ulong tlb_addr = tlbe->addr_write;
|
||||
TCGMemOp mop = get_memop(oi);
|
||||
int a_bits = get_alignment_bits(mop);
|
||||
int s_bits = mop & MO_SIZE;
|
||||
@@ -946,7 +1101,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK;
|
||||
tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Notice an IO access or a needs-MMU-lookup access */
|
||||
@@ -1033,7 +1188,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
#if HAVE_CMPXCHG128 || HAVE_ATOMIC128
|
||||
#ifdef CONFIG_ATOMIC128
|
||||
#define DATA_SIZE 16
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
|
||||
@@ -99,22 +99,20 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||
size_t mmu_idx, size_t index,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr,
|
||||
bool recheck,
|
||||
MMUAccessType access_type)
|
||||
bool recheck)
|
||||
{
|
||||
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
|
||||
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
|
||||
access_type, DATA_SIZE);
|
||||
DATA_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = entry->ADDR_READ;
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
DATA_TYPE res;
|
||||
@@ -130,7 +128,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = entry->ADDR_READ;
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
@@ -142,8 +140,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
||||
tlb_addr & TLB_RECHECK,
|
||||
READ_ACCESS_TYPE);
|
||||
tlb_addr & TLB_RECHECK);
|
||||
res = TGT_LE(res);
|
||||
return res;
|
||||
}
|
||||
@@ -167,7 +164,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
return res;
|
||||
}
|
||||
|
||||
haddr = addr + entry->addend;
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
#if DATA_SIZE == 1
|
||||
res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr);
|
||||
#else
|
||||
@@ -180,10 +177,9 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
|
||||
WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = entry->ADDR_READ;
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
DATA_TYPE res;
|
||||
@@ -199,7 +195,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = entry->ADDR_READ;
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
@@ -211,8 +207,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
/* ??? Note that the io helpers always read data in the target
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
|
||||
tlb_addr & TLB_RECHECK,
|
||||
READ_ACCESS_TYPE);
|
||||
tlb_addr & TLB_RECHECK);
|
||||
res = TGT_BE(res);
|
||||
return res;
|
||||
}
|
||||
@@ -236,7 +231,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
|
||||
return res;
|
||||
}
|
||||
|
||||
haddr = addr + entry->addend;
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr);
|
||||
return res;
|
||||
}
|
||||
@@ -277,10 +272,9 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
||||
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
|
||||
@@ -295,7 +289,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
@@ -316,16 +310,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i;
|
||||
target_ulong page2;
|
||||
CPUTLBEntry *entry2;
|
||||
int i, index2;
|
||||
target_ulong page2, tlb_addr2;
|
||||
do_unaligned_access:
|
||||
/* Ensure the second page is in the TLB. Note that the first page
|
||||
is already guaranteed to be filled, and that the second page
|
||||
cannot evict the first. */
|
||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
||||
if (!tlb_hit_page(tlb_addr2, page2)
|
||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
@@ -343,7 +337,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
return;
|
||||
}
|
||||
|
||||
haddr = addr + entry->addend;
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
#if DATA_SIZE == 1
|
||||
glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val);
|
||||
#else
|
||||
@@ -355,10 +349,9 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
TCGMemOpIdx oi, uintptr_t retaddr)
|
||||
{
|
||||
uintptr_t mmu_idx = get_mmuidx(oi);
|
||||
uintptr_t index = tlb_index(env, mmu_idx, addr);
|
||||
CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr);
|
||||
target_ulong tlb_addr = tlb_addr_write(entry);
|
||||
unsigned mmu_idx = get_mmuidx(oi);
|
||||
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
unsigned a_bits = get_alignment_bits(get_memop(oi));
|
||||
uintptr_t haddr;
|
||||
|
||||
@@ -373,7 +366,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
}
|
||||
tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK;
|
||||
tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK;
|
||||
}
|
||||
|
||||
/* Handle an IO access. */
|
||||
@@ -394,16 +387,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
if (DATA_SIZE > 1
|
||||
&& unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1
|
||||
>= TARGET_PAGE_SIZE)) {
|
||||
int i;
|
||||
target_ulong page2;
|
||||
CPUTLBEntry *entry2;
|
||||
int i, index2;
|
||||
target_ulong page2, tlb_addr2;
|
||||
do_unaligned_access:
|
||||
/* Ensure the second page is in the TLB. Note that the first page
|
||||
is already guaranteed to be filled, and that the second page
|
||||
cannot evict the first. */
|
||||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
entry2 = tlb_entry(env, mmu_idx, page2);
|
||||
if (!tlb_hit_page(tlb_addr_write(entry2), page2)
|
||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
|
||||
if (!tlb_hit_page(tlb_addr2, page2)
|
||||
&& !VICTIM_TLB_HIT(addr_write, page2)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
@@ -421,7 +414,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
||||
return;
|
||||
}
|
||||
|
||||
haddr = addr + entry->addend;
|
||||
haddr = addr + env->tlb_table[mmu_idx][index].addend;
|
||||
glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val);
|
||||
}
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
|
||||
if (!qemu_cpu_is_self(cpu)) {
|
||||
qemu_cpu_kick(cpu);
|
||||
} else {
|
||||
atomic_set(&cpu->icount_decr.u16.high, -1);
|
||||
cpu->icount_decr.u16.high = -1;
|
||||
if (use_icount &&
|
||||
!cpu->can_do_io
|
||||
&& (mask & ~old_mask) != 0) {
|
||||
|
||||
+18
-30
@@ -1282,7 +1282,8 @@ void tb_flush(CPUState *cpu)
|
||||
*/
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
|
||||
static void do_tb_invalidate_check(void *p, uint32_t hash, void *userp)
|
||||
static void
|
||||
do_tb_invalidate_check(struct qht *ht, void *p, uint32_t hash, void *userp)
|
||||
{
|
||||
TranslationBlock *tb = p;
|
||||
target_ulong addr = *(target_ulong *)userp;
|
||||
@@ -1303,7 +1304,8 @@ static void tb_invalidate_check(target_ulong address)
|
||||
qht_iter(&tb_ctx.htable, do_tb_invalidate_check, &address);
|
||||
}
|
||||
|
||||
static void do_tb_page_check(void *p, uint32_t hash, void *userp)
|
||||
static void
|
||||
do_tb_page_check(struct qht *ht, void *p, uint32_t hash, void *userp)
|
||||
{
|
||||
TranslationBlock *tb = p;
|
||||
int flags1, flags2;
|
||||
@@ -1491,7 +1493,7 @@ static void tb_phys_invalidate__locked(TranslationBlock *tb)
|
||||
*/
|
||||
void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr)
|
||||
{
|
||||
if (page_addr == -1 && tb->page_addr[0] != -1) {
|
||||
if (page_addr == -1) {
|
||||
page_lock_tb(tb);
|
||||
do_tb_phys_invalidate(tb, true);
|
||||
page_unlock_tb(tb);
|
||||
@@ -1606,17 +1608,6 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc,
|
||||
|
||||
assert_memory_lock();
|
||||
|
||||
if (phys_pc == -1) {
|
||||
/*
|
||||
* If the TB is not associated with a physical RAM page then
|
||||
* it must be a temporary one-insn TB, and we have nothing to do
|
||||
* except fill in the page_addr[] fields.
|
||||
*/
|
||||
assert(tb->cflags & CF_NOCACHE);
|
||||
tb->page_addr[0] = tb->page_addr[1] = -1;
|
||||
return tb;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the TB to the page list, acquiring first the pages's locks.
|
||||
* We keep the locks held until after inserting the TB in the hash table,
|
||||
@@ -1686,12 +1677,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
|
||||
|
||||
phys_pc = get_page_addr_code(env, pc);
|
||||
|
||||
if (phys_pc == -1) {
|
||||
/* Generate a temporary TB with 1 insn in it */
|
||||
cflags &= ~CF_COUNT_MASK;
|
||||
cflags |= CF_NOCACHE | 1;
|
||||
}
|
||||
|
||||
buffer_overflow:
|
||||
tb = tb_alloc(pc);
|
||||
if (unlikely(!tb)) {
|
||||
@@ -2009,6 +1994,15 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
|
||||
{
|
||||
PageDesc *p;
|
||||
|
||||
#if 0
|
||||
if (1) {
|
||||
qemu_log("modifying code at 0x%x size=%d EIP=%x PC=%08x\n",
|
||||
cpu_single_env->mem_io_vaddr, len,
|
||||
cpu_single_env->eip,
|
||||
cpu_single_env->eip +
|
||||
(intptr_t)cpu_single_env->segs[R_CS].base);
|
||||
}
|
||||
#endif
|
||||
assert_memory_lock();
|
||||
|
||||
p = page_find(start >> TARGET_PAGE_BITS);
|
||||
@@ -2127,9 +2121,7 @@ void tb_check_watchpoint(CPUState *cpu)
|
||||
|
||||
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
|
||||
addr = get_page_addr_code(env, pc);
|
||||
if (addr != -1) {
|
||||
tb_invalidate_phys_range(addr, addr + 1);
|
||||
}
|
||||
tb_invalidate_phys_range(addr, addr + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2290,7 +2282,7 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
|
||||
{
|
||||
struct tb_tree_stats tst = {};
|
||||
struct qht_stats hst;
|
||||
size_t nb_tbs, flush_full, flush_part, flush_elide;
|
||||
size_t nb_tbs;
|
||||
|
||||
tcg_tb_foreach(tb_tree_stats_iter, &tst);
|
||||
nb_tbs = tst.nb_tbs;
|
||||
@@ -2326,11 +2318,7 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf)
|
||||
cpu_fprintf(f, "TB flush count %u\n",
|
||||
atomic_read(&tb_ctx.tb_flush_count));
|
||||
cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count());
|
||||
|
||||
tlb_flush_counts(&flush_full, &flush_part, &flush_elide);
|
||||
cpu_fprintf(f, "TLB full flushes %zu\n", flush_full);
|
||||
cpu_fprintf(f, "TLB partial flushes %zu\n", flush_part);
|
||||
cpu_fprintf(f, "TLB elided flushes %zu\n", flush_elide);
|
||||
cpu_fprintf(f, "TLB flush count %zu\n", tlb_flush_count());
|
||||
tcg_dump_info(f, cpu_fprintf);
|
||||
}
|
||||
|
||||
@@ -2345,7 +2333,7 @@ void cpu_interrupt(CPUState *cpu, int mask)
|
||||
{
|
||||
g_assert(qemu_mutex_iothread_locked());
|
||||
cpu->interrupt_request |= mask;
|
||||
atomic_set(&cpu->icount_decr.u16.high, -1);
|
||||
cpu->icount_decr.u16.high = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -34,8 +34,6 @@ void translator_loop_temp_check(DisasContextBase *db)
|
||||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb)
|
||||
{
|
||||
int bp_insn = 0;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
db->tb = tb;
|
||||
db->pc_first = tb->pc;
|
||||
@@ -73,13 +71,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
|
||||
|
||||
/* Pass breakpoint hits to target for further processing */
|
||||
if (!db->singlestep_enabled
|
||||
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
|
||||
CPUBreakpoint *bp;
|
||||
QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
|
||||
if (bp->pc == db->pc_next) {
|
||||
if (ops->breakpoint_check(db, cpu, bp)) {
|
||||
bp_insn = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -122,7 +118,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
|
||||
/* Emit code to exit the TB, as indicated by db->is_jmp. */
|
||||
ops->tb_stop(db, cpu);
|
||||
gen_tb_end(db->tb, db->num_insns - bp_insn);
|
||||
gen_tb_end(db->tb, db->num_insns);
|
||||
|
||||
/* The disas_log hook may use these values rather than recompute. */
|
||||
db->tb->size = db->pc_next - db->pc_first;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "exec/cpu_ldst.h"
|
||||
#include "translate-all.h"
|
||||
#include "exec/helper-proto.h"
|
||||
#include "qemu/atomic128.h"
|
||||
|
||||
#undef EAX
|
||||
#undef ECX
|
||||
@@ -616,7 +615,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
/* The following is only callable from other helpers, and matches up
|
||||
with the softmmu version. */
|
||||
|
||||
#if HAVE_ATOMIC128 || HAVE_CMPXCHG128
|
||||
#ifdef CONFIG_ATOMIC128
|
||||
|
||||
#undef EXTRA_ARGS
|
||||
#undef ATOMIC_NAME
|
||||
@@ -629,4 +628,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
||||
|
||||
#define DATA_SIZE 16
|
||||
#include "atomic_template.h"
|
||||
#endif
|
||||
#endif /* CONFIG_ATOMIC128 */
|
||||
+2
-2
@@ -227,7 +227,7 @@ static void *qpa_thread_out (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
decr = to_mix = audio_MIN(pa->live, pa->g->conf.samples >> 5);
|
||||
decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
|
||||
rpos = pa->rpos;
|
||||
|
||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||
@@ -319,7 +319,7 @@ static void *qpa_thread_in (void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
incr = to_grab = audio_MIN(pa->dead, pa->g->conf.samples >> 5);
|
||||
incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
|
||||
wpos = pa->wpos;
|
||||
|
||||
if (audio_pt_unlock(&pa->pt, __func__)) {
|
||||
|
||||
@@ -4,7 +4,7 @@ common-obj-$(CONFIG_POSIX) += rng-random.o
|
||||
common-obj-$(CONFIG_TPM) += tpm.o
|
||||
|
||||
common-obj-y += hostmem.o hostmem-ram.o
|
||||
common-obj-$(CONFIG_POSIX) += hostmem-file.o
|
||||
common-obj-$(CONFIG_LINUX) += hostmem-file.o
|
||||
|
||||
common-obj-y += cryptodev.o
|
||||
common-obj-y += cryptodev-builtin.o
|
||||
|
||||
+12
-58
@@ -12,7 +12,6 @@
|
||||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/hostmem.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
@@ -32,19 +31,15 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile;
|
||||
struct HostMemoryBackendFile {
|
||||
HostMemoryBackend parent_obj;
|
||||
|
||||
bool discard_data;
|
||||
char *mem_path;
|
||||
uint64_t align;
|
||||
bool discard_data;
|
||||
bool is_pmem;
|
||||
};
|
||||
|
||||
static void
|
||||
file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
{
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend);
|
||||
#ifdef CONFIG_POSIX
|
||||
gchar *path;
|
||||
#endif
|
||||
|
||||
if (!backend->size) {
|
||||
error_setg(errp, "can't create backend with size 0");
|
||||
@@ -54,18 +49,19 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
error_setg(errp, "mem-path property not set");
|
||||
return;
|
||||
}
|
||||
#ifndef CONFIG_POSIX
|
||||
#ifndef CONFIG_LINUX
|
||||
error_setg(errp, "-mem-path not supported on this host");
|
||||
#else
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
path = object_get_canonical_path(OBJECT(backend));
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||
path,
|
||||
backend->size, fb->align,
|
||||
(backend->share ? RAM_SHARED : 0) |
|
||||
(fb->is_pmem ? RAM_PMEM : 0),
|
||||
fb->mem_path, errp);
|
||||
g_free(path);
|
||||
if (!host_memory_backend_mr_inited(backend)) {
|
||||
gchar *path;
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
path = object_get_canonical_path(OBJECT(backend));
|
||||
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
|
||||
path,
|
||||
backend->size, fb->align, backend->share,
|
||||
fb->mem_path, errp);
|
||||
g_free(path);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -134,45 +130,6 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
|
||||
error_propagate(errp, local_err);
|
||||
}
|
||||
|
||||
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
|
||||
{
|
||||
return MEMORY_BACKEND_FILE(o)->is_pmem;
|
||||
}
|
||||
|
||||
static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
char *path = object_get_canonical_path_component(o);
|
||||
|
||||
error_setg(errp, "cannot change property 'pmem' of %s '%s'",
|
||||
object_get_typename(o),
|
||||
path);
|
||||
g_free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_LIBPMEM
|
||||
if (value) {
|
||||
Error *local_err = NULL;
|
||||
char *path = object_get_canonical_path_component(o);
|
||||
|
||||
error_setg(&local_err,
|
||||
"Lack of libpmem support while setting the 'pmem=on'"
|
||||
" of %s '%s'. We can't ensure data persistence.",
|
||||
object_get_typename(o),
|
||||
path);
|
||||
g_free(path);
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
fb->is_pmem = value;
|
||||
}
|
||||
|
||||
static void file_backend_unparent(Object *obj)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
|
||||
@@ -204,9 +161,6 @@ file_backend_class_init(ObjectClass *oc, void *data)
|
||||
file_memory_backend_get_align,
|
||||
file_memory_backend_set_align,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_add_bool(oc, "pmem",
|
||||
file_memory_backend_get_pmem, file_memory_backend_set_pmem,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void file_backend_instance_finalize(Object *o)
|
||||
|
||||
+18
-31
@@ -44,6 +44,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (host_memory_backend_mr_inited(backend)) {
|
||||
return;
|
||||
}
|
||||
|
||||
backend->force_prealloc = mem_prealloc;
|
||||
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
|
||||
m->hugetlb, m->hugetlbsize, m->seal ?
|
||||
@@ -55,8 +59,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
|
||||
|
||||
name = object_get_canonical_path(OBJECT(backend));
|
||||
memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend),
|
||||
name, backend->size,
|
||||
backend->share, fd, errp);
|
||||
name, backend->size, true, fd, errp);
|
||||
g_free(name);
|
||||
}
|
||||
|
||||
@@ -128,7 +131,6 @@ memfd_backend_instance_init(Object *obj)
|
||||
|
||||
/* default to sealed file */
|
||||
m->seal = true;
|
||||
MEMORY_BACKEND(m)->share = true;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -138,31 +140,18 @@ memfd_backend_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
bc->alloc = memfd_backend_memory_alloc;
|
||||
|
||||
if (qemu_memfd_check(MFD_HUGETLB)) {
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "hugetlb",
|
||||
"Use huge pages",
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "hugetlbsize",
|
||||
"Huge pages size (ex: 2M, 1G)",
|
||||
&error_abort);
|
||||
}
|
||||
if (qemu_memfd_check(MFD_ALLOW_SEALING)) {
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "seal",
|
||||
"Seal growing & shrinking",
|
||||
&error_abort);
|
||||
}
|
||||
object_class_property_add_bool(oc, "hugetlb",
|
||||
memfd_backend_get_hugetlb,
|
||||
memfd_backend_set_hugetlb,
|
||||
&error_abort);
|
||||
object_class_property_add(oc, "hugetlbsize", "int",
|
||||
memfd_backend_get_hugetlbsize,
|
||||
memfd_backend_set_hugetlbsize,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_add_bool(oc, "seal",
|
||||
memfd_backend_get_seal,
|
||||
memfd_backend_set_seal,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static const TypeInfo memfd_backend_info = {
|
||||
@@ -175,9 +164,7 @@ static const TypeInfo memfd_backend_info = {
|
||||
|
||||
static void register_types(void)
|
||||
{
|
||||
if (qemu_memfd_check(0)) {
|
||||
type_register_static(&memfd_backend_info);
|
||||
}
|
||||
type_register_static(&memfd_backend_info);
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
@@ -397,41 +397,27 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
||||
object_class_property_add_bool(oc, "merge",
|
||||
host_memory_backend_get_merge,
|
||||
host_memory_backend_set_merge, &error_abort);
|
||||
object_class_property_set_description(oc, "merge",
|
||||
"Mark memory as mergeable", &error_abort);
|
||||
object_class_property_add_bool(oc, "dump",
|
||||
host_memory_backend_get_dump,
|
||||
host_memory_backend_set_dump, &error_abort);
|
||||
object_class_property_set_description(oc, "dump",
|
||||
"Set to 'off' to exclude from core dump", &error_abort);
|
||||
object_class_property_add_bool(oc, "prealloc",
|
||||
host_memory_backend_get_prealloc,
|
||||
host_memory_backend_set_prealloc, &error_abort);
|
||||
object_class_property_set_description(oc, "prealloc",
|
||||
"Preallocate memory", &error_abort);
|
||||
object_class_property_add(oc, "size", "int",
|
||||
host_memory_backend_get_size,
|
||||
host_memory_backend_set_size,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "size",
|
||||
"Size of the memory region (ex: 500M)", &error_abort);
|
||||
object_class_property_add(oc, "host-nodes", "int",
|
||||
host_memory_backend_get_host_nodes,
|
||||
host_memory_backend_set_host_nodes,
|
||||
NULL, NULL, &error_abort);
|
||||
object_class_property_set_description(oc, "host-nodes",
|
||||
"Binds memory to the list of NUMA host nodes", &error_abort);
|
||||
object_class_property_add_enum(oc, "policy", "HostMemPolicy",
|
||||
&HostMemPolicy_lookup,
|
||||
host_memory_backend_get_policy,
|
||||
host_memory_backend_set_policy, &error_abort);
|
||||
object_class_property_set_description(oc, "policy",
|
||||
"Set the NUMA policy", &error_abort);
|
||||
object_class_property_add_bool(oc, "share",
|
||||
host_memory_backend_get_share, host_memory_backend_set_share,
|
||||
&error_abort);
|
||||
object_class_property_set_description(oc, "share",
|
||||
"Mark the memory as private to QEMU or shared", &error_abort);
|
||||
}
|
||||
|
||||
static const TypeInfo host_memory_backend_info = {
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu-common.h"
|
||||
#include "qemu/atomic.h"
|
||||
#include "exec/cpu-common.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/balloon.h"
|
||||
@@ -38,22 +37,16 @@
|
||||
static QEMUBalloonEvent *balloon_event_fn;
|
||||
static QEMUBalloonStatus *balloon_stat_fn;
|
||||
static void *balloon_opaque;
|
||||
static int balloon_inhibit_count;
|
||||
static bool balloon_inhibited;
|
||||
|
||||
bool qemu_balloon_is_inhibited(void)
|
||||
{
|
||||
return atomic_read(&balloon_inhibit_count) > 0;
|
||||
return balloon_inhibited;
|
||||
}
|
||||
|
||||
void qemu_balloon_inhibit(bool state)
|
||||
{
|
||||
if (state) {
|
||||
atomic_inc(&balloon_inhibit_count);
|
||||
} else {
|
||||
atomic_dec(&balloon_inhibit_count);
|
||||
}
|
||||
|
||||
assert(atomic_read(&balloon_inhibit_count) >= 0);
|
||||
balloon_inhibited = state;
|
||||
}
|
||||
|
||||
static bool have_balloon(Error **errp)
|
||||
|
||||
@@ -266,41 +266,22 @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by a driver that can only provide a read-only image.
|
||||
*
|
||||
* Returns 0 if the node is already read-only or it could switch the node to
|
||||
* read-only because BDRV_O_AUTO_RDONLY is set.
|
||||
*
|
||||
* Returns -EACCES if the node is read-write and BDRV_O_AUTO_RDONLY is not set
|
||||
* or bdrv_can_set_read_only() forbids making the node read-only. If @errmsg
|
||||
* is not NULL, it is used as the error message for the Error object.
|
||||
*/
|
||||
int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg,
|
||||
Error **errp)
|
||||
/* TODO Remove (deprecated since 2.11)
|
||||
* Block drivers are not supposed to automatically change bs->read_only.
|
||||
* Instead, they should just check whether they can provide what the user
|
||||
* explicitly requested and error out if read-write is requested, but they can
|
||||
* only provide read-only access. */
|
||||
int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!(bs->open_flags & BDRV_O_RDWR)) {
|
||||
return 0;
|
||||
}
|
||||
if (!(bs->open_flags & BDRV_O_AUTO_RDONLY)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = bdrv_can_set_read_only(bs, true, false, NULL);
|
||||
ret = bdrv_can_set_read_only(bs, read_only, false, errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs->read_only = true;
|
||||
bs->open_flags &= ~BDRV_O_RDWR;
|
||||
|
||||
bs->read_only = read_only;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
error_setg(errp, "%s", errmsg ?: "Image is read-only");
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
void bdrv_get_full_backing_filename_from_filename(const char *backed,
|
||||
@@ -783,31 +764,6 @@ static void bdrv_join_options(BlockDriverState *bs, QDict *options,
|
||||
}
|
||||
}
|
||||
|
||||
static BlockdevDetectZeroesOptions bdrv_parse_detect_zeroes(QemuOpts *opts,
|
||||
int open_flags,
|
||||
Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
char *value = qemu_opt_get_del(opts, "detect-zeroes");
|
||||
BlockdevDetectZeroesOptions detect_zeroes =
|
||||
qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, value,
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err);
|
||||
g_free(value);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return detect_zeroes;
|
||||
}
|
||||
|
||||
if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
|
||||
!(open_flags & BDRV_O_UNMAP))
|
||||
{
|
||||
error_setg(errp, "setting detect-zeroes to unmap is not allowed "
|
||||
"without setting discard operation to unmap");
|
||||
}
|
||||
|
||||
return detect_zeroes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set open flags for a given discard mode
|
||||
*
|
||||
@@ -942,7 +898,6 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options,
|
||||
|
||||
/* Inherit the read-only option from the parent if it's not set */
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
|
||||
qdict_copy_default(child_options, parent_options, BDRV_OPT_AUTO_READ_ONLY);
|
||||
|
||||
/* Our block drivers take care to send flushes and respect unmap policy,
|
||||
* so we can default to enable both on lower layers regardless of the
|
||||
@@ -1066,7 +1021,6 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
|
||||
|
||||
/* backing files always opened read-only */
|
||||
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
|
||||
qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off");
|
||||
flags &= ~BDRV_O_COPY_ON_READ;
|
||||
|
||||
/* snapshot=on is handled on the top layer */
|
||||
@@ -1137,27 +1091,25 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
|
||||
static void update_flags_from_options(int *flags, QemuOpts *opts)
|
||||
{
|
||||
*flags &= ~(BDRV_O_CACHE_MASK | BDRV_O_RDWR | BDRV_O_AUTO_RDONLY);
|
||||
*flags &= ~BDRV_O_CACHE_MASK;
|
||||
|
||||
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH));
|
||||
if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) {
|
||||
*flags |= BDRV_O_NO_FLUSH;
|
||||
}
|
||||
|
||||
assert(qemu_opt_find(opts, BDRV_OPT_CACHE_DIRECT));
|
||||
if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_DIRECT, false)) {
|
||||
if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_DIRECT, false)) {
|
||||
*flags |= BDRV_O_NOCACHE;
|
||||
}
|
||||
|
||||
*flags &= ~BDRV_O_RDWR;
|
||||
|
||||
assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY));
|
||||
if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) {
|
||||
if (!qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false)) {
|
||||
*flags |= BDRV_O_RDWR;
|
||||
}
|
||||
|
||||
assert(qemu_opt_find(opts, BDRV_OPT_AUTO_READ_ONLY));
|
||||
if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) {
|
||||
*flags |= BDRV_O_AUTO_RDONLY;
|
||||
}
|
||||
}
|
||||
|
||||
static void update_options_from_flags(QDict *options, int flags)
|
||||
@@ -1172,10 +1124,6 @@ static void update_options_from_flags(QDict *options, int flags)
|
||||
if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) {
|
||||
qdict_put_bool(options, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR));
|
||||
}
|
||||
if (!qdict_haskey(options, BDRV_OPT_AUTO_READ_ONLY)) {
|
||||
qdict_put_bool(options, BDRV_OPT_AUTO_READ_ONLY,
|
||||
flags & BDRV_O_AUTO_RDONLY);
|
||||
}
|
||||
}
|
||||
|
||||
static void bdrv_assign_node_name(BlockDriverState *bs,
|
||||
@@ -1348,18 +1296,13 @@ QemuOptsList bdrv_runtime_opts = {
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Node is opened in read-only mode",
|
||||
},
|
||||
{
|
||||
.name = BDRV_OPT_AUTO_READ_ONLY,
|
||||
.type = QEMU_OPT_BOOL,
|
||||
.help = "Node can become read-only if opening read-write fails",
|
||||
},
|
||||
{
|
||||
.name = "detect-zeroes",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "try to optimize zero writes (off, on, unmap)",
|
||||
},
|
||||
{
|
||||
.name = BDRV_OPT_DISCARD,
|
||||
.name = "discard",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "discard operation (ignore/off, unmap/on)",
|
||||
},
|
||||
@@ -1385,6 +1328,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
||||
const char *driver_name = NULL;
|
||||
const char *node_name = NULL;
|
||||
const char *discard;
|
||||
const char *detect_zeroes;
|
||||
QemuOpts *opts;
|
||||
BlockDriver *drv;
|
||||
Error *local_err = NULL;
|
||||
@@ -1464,7 +1408,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
||||
}
|
||||
}
|
||||
|
||||
discard = qemu_opt_get(opts, BDRV_OPT_DISCARD);
|
||||
discard = qemu_opt_get(opts, "discard");
|
||||
if (discard != NULL) {
|
||||
if (bdrv_parse_discard_flags(discard, &bs->open_flags) != 0) {
|
||||
error_setg(errp, "Invalid discard option");
|
||||
@@ -1473,12 +1417,29 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
|
||||
}
|
||||
}
|
||||
|
||||
bs->detect_zeroes =
|
||||
bdrv_parse_detect_zeroes(opts, bs->open_flags, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
detect_zeroes = qemu_opt_get(opts, "detect-zeroes");
|
||||
if (detect_zeroes) {
|
||||
BlockdevDetectZeroesOptions value =
|
||||
qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup,
|
||||
detect_zeroes,
|
||||
BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
if (value == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP &&
|
||||
!(bs->open_flags & BDRV_O_UNMAP))
|
||||
{
|
||||
error_setg(errp, "setting detect-zeroes to unmap is not allowed "
|
||||
"without setting discard operation to unmap");
|
||||
ret = -EINVAL;
|
||||
goto fail_opts;
|
||||
}
|
||||
|
||||
bs->detect_zeroes = value;
|
||||
}
|
||||
|
||||
if (filename != NULL) {
|
||||
@@ -1517,6 +1478,11 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
||||
|
||||
options_obj = qobject_from_json(filename, errp);
|
||||
if (!options_obj) {
|
||||
/* Work around qobject_from_json() lossage TODO fix that */
|
||||
if (errp && !*errp) {
|
||||
error_setg(errp, "Could not parse the JSON options");
|
||||
return NULL;
|
||||
}
|
||||
error_prepend(errp, "Could not parse the JSON options: ");
|
||||
return NULL;
|
||||
}
|
||||
@@ -2260,18 +2226,6 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load)
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if you can reach parent going through child->inherits_from
|
||||
* recursively. If parent or child are NULL, return false */
|
||||
static bool bdrv_inherits_from_recursive(BlockDriverState *child,
|
||||
BlockDriverState *parent)
|
||||
{
|
||||
while (child && child != parent) {
|
||||
child = child->inherits_from;
|
||||
}
|
||||
|
||||
return child != NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the backing file link of a BDS. A new reference is created; callers
|
||||
* which don't need their own reference any more must call bdrv_unref().
|
||||
@@ -2279,9 +2233,6 @@ static bool bdrv_inherits_from_recursive(BlockDriverState *child,
|
||||
void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
|
||||
Error **errp)
|
||||
{
|
||||
bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) &&
|
||||
bdrv_inherits_from_recursive(backing_hd, bs);
|
||||
|
||||
if (backing_hd) {
|
||||
bdrv_ref(backing_hd);
|
||||
}
|
||||
@@ -2297,12 +2248,6 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd,
|
||||
|
||||
bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing,
|
||||
errp);
|
||||
/* If backing_hd was already part of bs's backing chain, and
|
||||
* inherits_from pointed recursively to bs then let's update it to
|
||||
* point directly to bs (else it will become NULL). */
|
||||
if (update_inherits_from) {
|
||||
backing_hd->inherits_from = bs;
|
||||
}
|
||||
if (!bs->backing) {
|
||||
bdrv_unref(backing_hd);
|
||||
}
|
||||
@@ -2532,8 +2477,6 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
||||
qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
|
||||
qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||
qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off");
|
||||
qdict_set_default_str(qdict, BDRV_OPT_AUTO_READ_ONLY, "off");
|
||||
|
||||
}
|
||||
|
||||
bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
|
||||
@@ -2641,7 +2584,6 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
BlockBackend *file = NULL;
|
||||
BlockDriverState *bs;
|
||||
BlockDriver *drv = NULL;
|
||||
BdrvChild *child;
|
||||
const char *drvname;
|
||||
const char *backing;
|
||||
Error *local_err = NULL;
|
||||
@@ -2825,18 +2767,6 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove all children options and references
|
||||
* from bs->options and bs->explicit_options */
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
char *child_key_dot;
|
||||
child_key_dot = g_strdup_printf("%s.", child->name);
|
||||
qdict_extract_subqdict(bs->explicit_options, NULL, child_key_dot);
|
||||
qdict_extract_subqdict(bs->options, NULL, child_key_dot);
|
||||
qdict_del(bs->explicit_options, child->name);
|
||||
qdict_del(bs->options, child->name);
|
||||
g_free(child_key_dot);
|
||||
}
|
||||
|
||||
bdrv_refresh_filename(bs);
|
||||
|
||||
/* Check if any unknown options were used */
|
||||
@@ -3047,7 +2977,6 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
||||
}
|
||||
|
||||
child_key_dot = g_strdup_printf("%s.", child->name);
|
||||
qdict_extract_subqdict(explicit_options, NULL, child_key_dot);
|
||||
qdict_extract_subqdict(options, &new_child_options, child_key_dot);
|
||||
g_free(child_key_dot);
|
||||
|
||||
@@ -3111,13 +3040,12 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
|
||||
|
||||
cleanup:
|
||||
QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) {
|
||||
if (ret) {
|
||||
if (bs_entry->prepared) {
|
||||
bdrv_reopen_abort(&bs_entry->state);
|
||||
}
|
||||
if (ret && bs_entry->prepared) {
|
||||
bdrv_reopen_abort(&bs_entry->state);
|
||||
} else if (ret) {
|
||||
qobject_unref(bs_entry->state.explicit_options);
|
||||
qobject_unref(bs_entry->state.options);
|
||||
}
|
||||
qobject_unref(bs_entry->state.options);
|
||||
g_free(bs_entry);
|
||||
}
|
||||
g_free(bs_queue);
|
||||
@@ -3217,20 +3145,13 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
||||
Error *local_err = NULL;
|
||||
BlockDriver *drv;
|
||||
QemuOpts *opts;
|
||||
QDict *orig_reopen_opts;
|
||||
char *discard = NULL;
|
||||
const char *value;
|
||||
bool read_only;
|
||||
bool drv_prepared = false;
|
||||
|
||||
assert(reopen_state != NULL);
|
||||
assert(reopen_state->bs->drv != NULL);
|
||||
drv = reopen_state->bs->drv;
|
||||
|
||||
/* This function and each driver's bdrv_reopen_prepare() remove
|
||||
* entries from reopen_state->options as they are processed, so
|
||||
* we need to make a copy of the original QDict. */
|
||||
orig_reopen_opts = qdict_clone_shallow(reopen_state->options);
|
||||
|
||||
/* Process generic block layer options */
|
||||
opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
|
||||
qemu_opts_absorb_qdict(opts, reopen_state->options, &local_err);
|
||||
@@ -3242,28 +3163,18 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
||||
|
||||
update_flags_from_options(&reopen_state->flags, opts);
|
||||
|
||||
discard = qemu_opt_get_del(opts, BDRV_OPT_DISCARD);
|
||||
if (discard != NULL) {
|
||||
if (bdrv_parse_discard_flags(discard, &reopen_state->flags) != 0) {
|
||||
error_setg(errp, "Invalid discard option");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
/* node-name and driver must be unchanged. Put them back into the QDict, so
|
||||
* that they are checked at the end of this function. */
|
||||
value = qemu_opt_get(opts, "node-name");
|
||||
if (value) {
|
||||
qdict_put_str(reopen_state->options, "node-name", value);
|
||||
}
|
||||
|
||||
reopen_state->detect_zeroes =
|
||||
bdrv_parse_detect_zeroes(opts, reopen_state->flags, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
value = qemu_opt_get(opts, "driver");
|
||||
if (value) {
|
||||
qdict_put_str(reopen_state->options, "driver", value);
|
||||
}
|
||||
|
||||
/* All other options (including node-name and driver) must be unchanged.
|
||||
* Put them back into the QDict, so that they are checked at the end
|
||||
* of this function. */
|
||||
qemu_opts_to_qdict(opts, reopen_state->options);
|
||||
|
||||
/* If we are to stay read-only, do not allow permission change
|
||||
* to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is
|
||||
* not set, or if the BDS still has copy_on_read enabled */
|
||||
@@ -3305,8 +3216,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
||||
goto error;
|
||||
}
|
||||
|
||||
drv_prepared = true;
|
||||
|
||||
/* Options that are not handled are only okay if they are unchanged
|
||||
* compared to the old state. It is expected that some options are only
|
||||
* used for the initial open, but not reopen (e.g. filename) */
|
||||
@@ -3317,24 +3226,6 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
||||
QObject *new = entry->value;
|
||||
QObject *old = qdict_get(reopen_state->bs->options, entry->key);
|
||||
|
||||
/* Allow child references (child_name=node_name) as long as they
|
||||
* point to the current child (i.e. everything stays the same). */
|
||||
if (qobject_type(new) == QTYPE_QSTRING) {
|
||||
BdrvChild *child;
|
||||
QLIST_FOREACH(child, &reopen_state->bs->children, next) {
|
||||
if (!strcmp(child->name, entry->key)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (child) {
|
||||
const char *str = qobject_get_try_str(new);
|
||||
if (!strcmp(child->bs->node_name, str)) {
|
||||
continue; /* Found child with this name, skip option */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: When using -drive to specify blockdev options, all values
|
||||
* will be strings; however, when using -blockdev, blockdev-add or
|
||||
@@ -3367,23 +3258,8 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
|
||||
|
||||
ret = 0;
|
||||
|
||||
/* Restore the original reopen_state->options QDict */
|
||||
qobject_unref(reopen_state->options);
|
||||
reopen_state->options = qobject_ref(orig_reopen_opts);
|
||||
|
||||
error:
|
||||
if (ret < 0 && drv_prepared) {
|
||||
/* drv->bdrv_reopen_prepare() has succeeded, so we need to
|
||||
* call drv->bdrv_reopen_abort() before signaling an error
|
||||
* (bdrv_reopen_multiple() will not call bdrv_reopen_abort()
|
||||
* when the respective bdrv_reopen_prepare() has failed) */
|
||||
if (drv->bdrv_reopen_abort) {
|
||||
drv->bdrv_reopen_abort(reopen_state);
|
||||
}
|
||||
}
|
||||
qemu_opts_del(opts);
|
||||
qobject_unref(orig_reopen_opts);
|
||||
g_free(discard);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3396,7 +3272,6 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||
{
|
||||
BlockDriver *drv;
|
||||
BlockDriverState *bs;
|
||||
BdrvChild *child;
|
||||
bool old_can_write, new_can_write;
|
||||
|
||||
assert(reopen_state != NULL);
|
||||
@@ -3414,20 +3289,10 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
||||
|
||||
/* set BDS specific flags now */
|
||||
qobject_unref(bs->explicit_options);
|
||||
qobject_unref(bs->options);
|
||||
|
||||
bs->explicit_options = reopen_state->explicit_options;
|
||||
bs->options = reopen_state->options;
|
||||
bs->open_flags = reopen_state->flags;
|
||||
bs->read_only = !(reopen_state->flags & BDRV_O_RDWR);
|
||||
bs->detect_zeroes = reopen_state->detect_zeroes;
|
||||
|
||||
/* Remove child references from bs->options and bs->explicit_options.
|
||||
* Child options were already removed in bdrv_reopen_queue_child() */
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
qdict_del(bs->explicit_options, child->name);
|
||||
qdict_del(bs->options, child->name);
|
||||
}
|
||||
|
||||
bdrv_refresh_limits(bs, NULL);
|
||||
|
||||
@@ -3466,6 +3331,8 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
|
||||
drv->bdrv_reopen_abort(reopen_state);
|
||||
}
|
||||
|
||||
qobject_unref(reopen_state->explicit_options);
|
||||
|
||||
bdrv_abort_perm_update(reopen_state->bs);
|
||||
}
|
||||
|
||||
@@ -3483,9 +3350,7 @@ static void bdrv_close(BlockDriverState *bs)
|
||||
bdrv_drain(bs); /* in case flush left pending I/O */
|
||||
|
||||
if (bs->drv) {
|
||||
if (bs->drv->bdrv_close) {
|
||||
bs->drv->bdrv_close(bs);
|
||||
}
|
||||
bs->drv->bdrv_close(bs);
|
||||
bs->drv = NULL;
|
||||
}
|
||||
|
||||
@@ -3736,7 +3601,6 @@ static void bdrv_check_co_entry(void *opaque)
|
||||
{
|
||||
CheckCo *cco = opaque;
|
||||
cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int bdrv_check(BlockDriverState *bs,
|
||||
@@ -3755,7 +3619,7 @@ int bdrv_check(BlockDriverState *bs,
|
||||
bdrv_check_co_entry(&cco);
|
||||
} else {
|
||||
co = qemu_coroutine_create(bdrv_check_co_entry, &cco);
|
||||
bdrv_coroutine_enter(bs, co);
|
||||
qemu_coroutine_enter(co);
|
||||
BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS);
|
||||
}
|
||||
|
||||
@@ -3856,8 +3720,6 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs)
|
||||
int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
|
||||
const char *backing_file_str)
|
||||
{
|
||||
BlockDriverState *explicit_top = top;
|
||||
bool update_inherits_from;
|
||||
BdrvChild *c, *next;
|
||||
Error *local_err = NULL;
|
||||
int ret = -EIO;
|
||||
@@ -3873,16 +3735,6 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* If 'base' recursively inherits from 'top' then we should set
|
||||
* base->inherits_from to top->inherits_from after 'top' and all
|
||||
* other intermediate nodes have been dropped.
|
||||
* If 'top' is an implicit node (e.g. "commit_top") we should skip
|
||||
* it because no one inherits from it. We use explicit_top for that. */
|
||||
while (explicit_top && explicit_top->implicit) {
|
||||
explicit_top = backing_bs(explicit_top);
|
||||
}
|
||||
update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top);
|
||||
|
||||
/* success - we can delete the intermediate states, and link top->base */
|
||||
/* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once
|
||||
* we've figured out how they should work. */
|
||||
@@ -3918,10 +3770,6 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base,
|
||||
bdrv_unref(top);
|
||||
}
|
||||
|
||||
if (update_inherits_from) {
|
||||
base->inherits_from = explicit_top->inherits_from;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
bdrv_unref(top);
|
||||
@@ -4487,7 +4335,6 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
|
||||
uint64_t perm, shared_perm;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
BdrvDirtyBitmap *bm;
|
||||
|
||||
if (!bs->drv) {
|
||||
return;
|
||||
@@ -4537,12 +4384,6 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs,
|
||||
}
|
||||
}
|
||||
|
||||
for (bm = bdrv_dirty_bitmap_next(bs, NULL); bm;
|
||||
bm = bdrv_dirty_bitmap_next(bs, bm))
|
||||
{
|
||||
bdrv_dirty_bitmap_set_migration(bm, false);
|
||||
}
|
||||
|
||||
ret = refresh_total_sectors(bs, bs->total_sectors);
|
||||
if (ret < 0) {
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
@@ -4573,7 +4414,6 @@ static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque)
|
||||
InvalidateCacheCo *ico = opaque;
|
||||
bdrv_co_invalidate_cache(ico->bs, ico->errp);
|
||||
ico->done = true;
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
@@ -4590,7 +4430,7 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp)
|
||||
bdrv_invalidate_cache_co_entry(&ico);
|
||||
} else {
|
||||
co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico);
|
||||
bdrv_coroutine_enter(bs, co);
|
||||
qemu_coroutine_enter(co);
|
||||
BDRV_POLL_WHILE(bs, !ico.done);
|
||||
}
|
||||
}
|
||||
@@ -4615,73 +4455,54 @@ void bdrv_invalidate_cache_all(Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active)
|
||||
{
|
||||
BdrvChild *parent;
|
||||
|
||||
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||
if (parent->role->parent_is_bds) {
|
||||
BlockDriverState *parent_bs = parent->opaque;
|
||||
if (!only_active || !(parent_bs->open_flags & BDRV_O_INACTIVE)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int bdrv_inactivate_recurse(BlockDriverState *bs)
|
||||
static int bdrv_inactivate_recurse(BlockDriverState *bs,
|
||||
bool setting_flag)
|
||||
{
|
||||
BdrvChild *child, *parent;
|
||||
uint64_t perm, shared_perm;
|
||||
int ret;
|
||||
|
||||
if (!bs->drv) {
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
|
||||
/* Make sure that we don't inactivate a child before its parent.
|
||||
* It will be covered by recursion from the yet active parent. */
|
||||
if (bdrv_has_bds_parent(bs, true)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(!(bs->open_flags & BDRV_O_INACTIVE));
|
||||
|
||||
/* Inactivate this node */
|
||||
if (bs->drv->bdrv_inactivate) {
|
||||
if (!setting_flag && bs->drv->bdrv_inactivate) {
|
||||
ret = bs->drv->bdrv_inactivate(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||
if (parent->role->inactivate) {
|
||||
ret = parent->role->inactivate(parent);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
if (setting_flag && !(bs->open_flags & BDRV_O_INACTIVE)) {
|
||||
uint64_t perm, shared_perm;
|
||||
|
||||
QLIST_FOREACH(parent, &bs->parents, next_parent) {
|
||||
if (parent->role->inactivate) {
|
||||
ret = parent->role->inactivate(parent);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
|
||||
/* Update permissions, they may differ for inactive nodes */
|
||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||
bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort);
|
||||
bdrv_set_perm(bs, perm, shared_perm);
|
||||
}
|
||||
|
||||
bs->open_flags |= BDRV_O_INACTIVE;
|
||||
|
||||
/* Update permissions, they may differ for inactive nodes */
|
||||
bdrv_get_cumulative_perm(bs, &perm, &shared_perm);
|
||||
bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort);
|
||||
bdrv_set_perm(bs, perm, shared_perm);
|
||||
|
||||
|
||||
/* Recursively inactivate children */
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
ret = bdrv_inactivate_recurse(child->bs);
|
||||
ret = bdrv_inactivate_recurse(child->bs, setting_flag);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point persistent bitmaps should be already stored by the format
|
||||
* driver */
|
||||
bdrv_release_persistent_dirty_bitmaps(bs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -4690,6 +4511,7 @@ int bdrv_inactivate_all(void)
|
||||
BlockDriverState *bs = NULL;
|
||||
BdrvNextIterator it;
|
||||
int ret = 0;
|
||||
int pass;
|
||||
GSList *aio_ctxs = NULL, *ctx;
|
||||
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
@@ -4701,17 +4523,17 @@ int bdrv_inactivate_all(void)
|
||||
}
|
||||
}
|
||||
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
/* Nodes with BDS parents are covered by recursion from the last
|
||||
* parent that gets inactivated. Don't inactivate them a second
|
||||
* time if that has already happened. */
|
||||
if (bdrv_has_bds_parent(bs, false)) {
|
||||
continue;
|
||||
}
|
||||
ret = bdrv_inactivate_recurse(bs);
|
||||
if (ret < 0) {
|
||||
bdrv_next_cleanup(&it);
|
||||
goto out;
|
||||
/* We do two passes of inactivation. The first pass calls to drivers'
|
||||
* .bdrv_inactivate callbacks recursively so all cache is flushed to disk;
|
||||
* the second pass sets the BDRV_O_INACTIVE flag so that no further write
|
||||
* is allowed. */
|
||||
for (pass = 0; pass < 2; pass++) {
|
||||
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
|
||||
ret = bdrv_inactivate_recurse(bs, pass);
|
||||
if (ret < 0) {
|
||||
bdrv_next_cleanup(&it);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4808,9 +4630,9 @@ bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp)
|
||||
assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX);
|
||||
if (!QLIST_EMPTY(&bs->op_blockers[op])) {
|
||||
blocker = QLIST_FIRST(&bs->op_blockers[op]);
|
||||
error_propagate_prepend(errp, error_copy(blocker->reason),
|
||||
"Node '%s' is busy: ",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
error_propagate(errp, error_copy(blocker->reason));
|
||||
error_prepend(errp, "Node '%s' is busy: ",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -4914,6 +4736,9 @@ void bdrv_img_create(const char *filename, const char *fmt,
|
||||
if (options) {
|
||||
qemu_opts_do_parse(opts, options, NULL, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
local_err = NULL;
|
||||
error_setg(errp, "Invalid options for file format '%s'", fmt);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -5042,6 +4867,11 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs)
|
||||
return bs ? bs->aio_context : qemu_get_aio_context();
|
||||
}
|
||||
|
||||
AioWait *bdrv_get_aio_wait(BlockDriverState *bs)
|
||||
{
|
||||
return bs ? &bs->wait : NULL;
|
||||
}
|
||||
|
||||
void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co)
|
||||
{
|
||||
aio_co_enter(bdrv_get_aio_context(bs), co);
|
||||
@@ -5058,6 +4888,10 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
|
||||
BdrvAioNotifier *baf, *baf_tmp;
|
||||
BdrvChild *child;
|
||||
|
||||
if (!bs->drv) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!bs->walking_aio_notifiers);
|
||||
bs->walking_aio_notifiers = true;
|
||||
QLIST_FOREACH_SAFE(baf, &bs->aio_notifiers, list, baf_tmp) {
|
||||
@@ -5072,7 +4906,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs)
|
||||
*/
|
||||
bs->walking_aio_notifiers = false;
|
||||
|
||||
if (bs->drv && bs->drv->bdrv_detach_aio_context) {
|
||||
if (bs->drv->bdrv_detach_aio_context) {
|
||||
bs->drv->bdrv_detach_aio_context(bs);
|
||||
}
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
@@ -5088,12 +4922,16 @@ void bdrv_attach_aio_context(BlockDriverState *bs,
|
||||
BdrvAioNotifier *ban, *ban_tmp;
|
||||
BdrvChild *child;
|
||||
|
||||
if (!bs->drv) {
|
||||
return;
|
||||
}
|
||||
|
||||
bs->aio_context = new_context;
|
||||
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
bdrv_attach_aio_context(child->bs, new_context);
|
||||
}
|
||||
if (bs->drv && bs->drv->bdrv_attach_aio_context) {
|
||||
if (bs->drv->bdrv_attach_aio_context) {
|
||||
bs->drv->bdrv_attach_aio_context(bs, new_context);
|
||||
}
|
||||
|
||||
@@ -5287,12 +5125,26 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
|
||||
{
|
||||
const QDictEntry *entry;
|
||||
QemuOptDesc *desc;
|
||||
BdrvChild *child;
|
||||
bool found_any = false;
|
||||
const char *p;
|
||||
|
||||
for (entry = qdict_first(bs->options); entry;
|
||||
entry = qdict_next(bs->options, entry))
|
||||
{
|
||||
/* Exclude all non-driver-specific options */
|
||||
/* Exclude options for children */
|
||||
QLIST_FOREACH(child, &bs->children, next) {
|
||||
if (strstart(qdict_entry_key(entry), child->name, &p)
|
||||
&& (!*p || *p == '.'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (child) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* And exclude all non-driver-specific options */
|
||||
for (desc = bdrv_runtime_opts.desc; desc->name; desc++) {
|
||||
if (!strcmp(qdict_entry_key(entry), desc->name)) {
|
||||
break;
|
||||
|
||||
+6
-16
@@ -1,18 +1,10 @@
|
||||
block-obj-y += raw-format.o vmdk.o vpc.o
|
||||
block-obj-$(CONFIG_QCOW1) += qcow.o
|
||||
block-obj-$(CONFIG_VDI) += vdi.o
|
||||
block-obj-$(CONFIG_CLOOP) += cloop.o
|
||||
block-obj-$(CONFIG_BOCHS) += bochs.o
|
||||
block-obj-$(CONFIG_VVFAT) += vvfat.o
|
||||
block-obj-$(CONFIG_DMG) += dmg.o
|
||||
|
||||
block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o
|
||||
block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
|
||||
block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-$(CONFIG_QED) += qed-check.o
|
||||
block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o
|
||||
block-obj-y += qed-check.o
|
||||
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
|
||||
block-obj-y += quorum.o
|
||||
block-obj-y += blkdebug.o blkverify.o blkreplay.o
|
||||
block-obj-$(CONFIG_PARALLELS) += parallels.o
|
||||
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
|
||||
block-obj-y += blklogwrites.o
|
||||
block-obj-y += block-backend.o snapshot.o qapi.o
|
||||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||
@@ -22,8 +14,7 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
|
||||
block-obj-y += throttle-groups.o
|
||||
block-obj-$(CONFIG_LINUX) += nvme.o
|
||||
|
||||
block-obj-y += nbd.o nbd-client.o
|
||||
block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o
|
||||
block-obj-y += nbd.o nbd-client.o sheepdog.o
|
||||
block-obj-$(CONFIG_LIBISCSI) += iscsi.o
|
||||
block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
|
||||
block-obj-$(CONFIG_LIBNFS) += nfs.o
|
||||
@@ -54,8 +45,7 @@ gluster.o-libs := $(GLUSTERFS_LIBS)
|
||||
vxhs.o-libs := $(VXHS_LIBS)
|
||||
ssh.o-cflags := $(LIBSSH2_CFLAGS)
|
||||
ssh.o-libs := $(LIBSSH2_LIBS)
|
||||
block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o
|
||||
block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y)
|
||||
block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o
|
||||
dmg-bz2.o-libs := $(BZIP2_LIBS)
|
||||
qcow.o-libs := -lz
|
||||
linux-aio.o-libs := -laio
|
||||
|
||||
+48
-33
@@ -380,6 +380,18 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int ret;
|
||||
} BackupCompleteData;
|
||||
|
||||
static void backup_complete(Job *job, void *opaque)
|
||||
{
|
||||
BackupCompleteData *data = opaque;
|
||||
|
||||
job_completed(job, data->ret, NULL);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
||||
{
|
||||
uint64_t delay_ns;
|
||||
@@ -468,59 +480,60 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
|
||||
bdrv_dirty_iter_free(dbi);
|
||||
}
|
||||
|
||||
static int coroutine_fn backup_run(Job *job, Error **errp)
|
||||
static void coroutine_fn backup_run(void *opaque)
|
||||
{
|
||||
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
|
||||
BlockDriverState *bs = blk_bs(s->common.blk);
|
||||
BackupBlockJob *job = opaque;
|
||||
BackupCompleteData *data;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
int64_t offset, nb_clusters;
|
||||
int ret = 0;
|
||||
|
||||
QLIST_INIT(&s->inflight_reqs);
|
||||
qemu_co_rwlock_init(&s->flush_rwlock);
|
||||
QLIST_INIT(&job->inflight_reqs);
|
||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||
|
||||
nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size);
|
||||
job_progress_set_remaining(job, s->len);
|
||||
nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size);
|
||||
job_progress_set_remaining(&job->common.job, job->len);
|
||||
|
||||
s->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
|
||||
if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
backup_incremental_init_copy_bitmap(s);
|
||||
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(s->copy_bitmap, 0, nb_clusters);
|
||||
hbitmap_set(job->copy_bitmap, 0, nb_clusters);
|
||||
}
|
||||
|
||||
|
||||
s->before_write.notify = backup_before_write_notify;
|
||||
bdrv_add_before_write_notifier(bs, &s->before_write);
|
||||
job->before_write.notify = backup_before_write_notify;
|
||||
bdrv_add_before_write_notifier(bs, &job->before_write);
|
||||
|
||||
if (s->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 (!job_is_cancelled(job)) {
|
||||
while (!job_is_cancelled(&job->common.job)) {
|
||||
/* Yield until the job is cancelled. We just let our before_write
|
||||
* notify callback service CoW requests. */
|
||||
job_yield(job);
|
||||
job_yield(&job->common.job);
|
||||
}
|
||||
} else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
ret = backup_run_incremental(s);
|
||||
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
ret = backup_run_incremental(job);
|
||||
} else {
|
||||
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
||||
for (offset = 0; offset < s->len;
|
||||
offset += s->cluster_size) {
|
||||
for (offset = 0; offset < job->len;
|
||||
offset += job->cluster_size) {
|
||||
bool error_is_read;
|
||||
int alloced = 0;
|
||||
|
||||
if (yield_and_check(s)) {
|
||||
if (yield_and_check(job)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
int i;
|
||||
int64_t n;
|
||||
|
||||
/* Check to see if these blocks are already in the
|
||||
* backing file. */
|
||||
|
||||
for (i = 0; i < s->cluster_size;) {
|
||||
for (i = 0; i < job->cluster_size;) {
|
||||
/* bdrv_is_allocated() only returns true/false based
|
||||
* on the first set of sectors it comes across that
|
||||
* are are all in the same state.
|
||||
@@ -529,7 +542,7 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
|
||||
* needed but at some point that is always the case. */
|
||||
alloced =
|
||||
bdrv_is_allocated(bs, offset + i,
|
||||
s->cluster_size - i, &n);
|
||||
job->cluster_size - i, &n);
|
||||
i += n;
|
||||
|
||||
if (alloced || n == 0) {
|
||||
@@ -547,31 +560,33 @@ static int coroutine_fn backup_run(Job *job, Error **errp)
|
||||
if (alloced < 0) {
|
||||
ret = alloced;
|
||||
} else {
|
||||
ret = backup_do_cow(s, offset, s->cluster_size,
|
||||
ret = backup_do_cow(job, offset, job->cluster_size,
|
||||
&error_is_read, false);
|
||||
}
|
||||
if (ret < 0) {
|
||||
/* Depending on error action, fail now or retry cluster */
|
||||
BlockErrorAction action =
|
||||
backup_error_action(s, error_is_read, -ret);
|
||||
backup_error_action(job, error_is_read, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
break;
|
||||
} else {
|
||||
offset -= s->cluster_size;
|
||||
offset -= job->cluster_size;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifier_with_return_remove(&s->before_write);
|
||||
notifier_with_return_remove(&job->before_write);
|
||||
|
||||
/* wait until pending backup_do_cow() calls have completed */
|
||||
qemu_co_rwlock_wrlock(&s->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&s->flush_rwlock);
|
||||
hbitmap_free(s->copy_bitmap);
|
||||
qemu_co_rwlock_wrlock(&job->flush_rwlock);
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
hbitmap_free(job->copy_bitmap);
|
||||
|
||||
return ret;
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
job_defer_to_main_loop(&job->common.job, backup_complete, data);
|
||||
}
|
||||
|
||||
static const BlockJobDriver backup_job_driver = {
|
||||
@@ -581,7 +596,7 @@ static const BlockJobDriver backup_job_driver = {
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.run = backup_run,
|
||||
.start = backup_run,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
.clean = backup_clean,
|
||||
|
||||
Regular → Executable
+5
@@ -43,6 +43,10 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void blkreplay_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int64_t blkreplay_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file->bs);
|
||||
@@ -131,6 +135,7 @@ static BlockDriver bdrv_blkreplay = {
|
||||
.instance_size = 0,
|
||||
|
||||
.bdrv_open = blkreplay_open,
|
||||
.bdrv_close = blkreplay_close,
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_getlength = blkreplay_getlength,
|
||||
|
||||
|
||||
+20
-36
@@ -88,6 +88,7 @@ struct BlockBackend {
|
||||
* Accessed with atomic ops.
|
||||
*/
|
||||
unsigned int in_flight;
|
||||
AioWait wait;
|
||||
};
|
||||
|
||||
typedef struct BlockBackendAIOCB {
|
||||
@@ -120,7 +121,6 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options,
|
||||
abort();
|
||||
}
|
||||
static void blk_root_drained_begin(BdrvChild *child);
|
||||
static bool blk_root_drained_poll(BdrvChild *child);
|
||||
static void blk_root_drained_end(BdrvChild *child);
|
||||
|
||||
static void blk_root_change_media(BdrvChild *child, bool load);
|
||||
@@ -294,7 +294,6 @@ static const BdrvChildRole child_root = {
|
||||
.get_parent_desc = blk_root_get_parent_desc,
|
||||
|
||||
.drained_begin = blk_root_drained_begin,
|
||||
.drained_poll = blk_root_drained_poll,
|
||||
.drained_end = blk_root_drained_end,
|
||||
|
||||
.activate = blk_root_activate,
|
||||
@@ -423,6 +422,7 @@ static void drive_info_del(DriveInfo *dinfo)
|
||||
return;
|
||||
}
|
||||
qemu_opts_del(dinfo->opts);
|
||||
g_free(dinfo->serial);
|
||||
g_free(dinfo);
|
||||
}
|
||||
|
||||
@@ -437,7 +437,6 @@ int blk_get_refcnt(BlockBackend *blk)
|
||||
*/
|
||||
void blk_ref(BlockBackend *blk)
|
||||
{
|
||||
assert(blk->refcnt > 0);
|
||||
blk->refcnt++;
|
||||
}
|
||||
|
||||
@@ -450,13 +449,7 @@ void blk_unref(BlockBackend *blk)
|
||||
{
|
||||
if (blk) {
|
||||
assert(blk->refcnt > 0);
|
||||
if (blk->refcnt > 1) {
|
||||
blk->refcnt--;
|
||||
} else {
|
||||
blk_drain(blk);
|
||||
/* blk_drain() cannot resurrect blk, nobody held a reference */
|
||||
assert(blk->refcnt == 1);
|
||||
blk->refcnt = 0;
|
||||
if (!--blk->refcnt) {
|
||||
blk_delete(blk);
|
||||
}
|
||||
}
|
||||
@@ -918,8 +911,7 @@ char *blk_get_attached_dev_id(BlockBackend *blk)
|
||||
} else if (dev->id) {
|
||||
return g_strdup(dev->id);
|
||||
}
|
||||
|
||||
return object_get_canonical_path(OBJECT(dev)) ?: g_strdup("");
|
||||
return object_get_canonical_path(OBJECT(dev));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -992,7 +984,8 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp)
|
||||
|
||||
if (tray_was_open != tray_is_open) {
|
||||
char *id = blk_get_attached_dev_id(blk);
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open);
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open,
|
||||
&error_abort);
|
||||
g_free(id);
|
||||
}
|
||||
}
|
||||
@@ -1220,7 +1213,6 @@ static void blk_read_entry(void *opaque)
|
||||
|
||||
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, rwco->flags);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
static void blk_write_entry(void *opaque)
|
||||
@@ -1230,7 +1222,6 @@ static void blk_write_entry(void *opaque)
|
||||
|
||||
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
|
||||
qiov, rwco->flags);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||
@@ -1303,7 +1294,7 @@ static void blk_inc_in_flight(BlockBackend *blk)
|
||||
static void blk_dec_in_flight(BlockBackend *blk)
|
||||
{
|
||||
atomic_dec(&blk->in_flight);
|
||||
aio_wait_kick();
|
||||
aio_wait_kick(&blk->wait);
|
||||
}
|
||||
|
||||
static void error_callback_bh(void *opaque)
|
||||
@@ -1344,8 +1335,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = {
|
||||
static void blk_aio_complete(BlkAioEmAIOCB *acb)
|
||||
{
|
||||
if (acb->has_returned) {
|
||||
acb->common.cb(acb->common.opaque, acb->rwco.ret);
|
||||
blk_dec_in_flight(acb->rwco.blk);
|
||||
acb->common.cb(acb->common.opaque, acb->rwco.ret);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
}
|
||||
@@ -1542,7 +1533,6 @@ static void blk_ioctl_entry(void *opaque)
|
||||
|
||||
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
|
||||
qiov->iov[0].iov_base);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
|
||||
@@ -1589,7 +1579,6 @@ static void blk_flush_entry(void *opaque)
|
||||
{
|
||||
BlkRwCo *rwco = opaque;
|
||||
rwco->ret = blk_co_flush(rwco->blk);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int blk_flush(BlockBackend *blk)
|
||||
@@ -1606,8 +1595,9 @@ void blk_drain(BlockBackend *blk)
|
||||
}
|
||||
|
||||
/* We may have -ENOMEDIUM completions in flight */
|
||||
AIO_WAIT_WHILE(blk_get_aio_context(blk),
|
||||
atomic_mb_read(&blk->in_flight) > 0);
|
||||
AIO_WAIT_WHILE(&blk->wait,
|
||||
blk_get_aio_context(blk),
|
||||
atomic_mb_read(&blk->in_flight) > 0);
|
||||
|
||||
if (bs) {
|
||||
bdrv_drained_end(bs);
|
||||
@@ -1626,7 +1616,8 @@ void blk_drain_all(void)
|
||||
aio_context_acquire(ctx);
|
||||
|
||||
/* We may have -ENOMEDIUM completions in flight */
|
||||
AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0);
|
||||
AIO_WAIT_WHILE(&blk->wait, ctx,
|
||||
atomic_mb_read(&blk->in_flight) > 0);
|
||||
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
@@ -1678,7 +1669,8 @@ static void send_qmp_error_event(BlockBackend *blk,
|
||||
qapi_event_send_block_io_error(blk_name(blk), !!bs,
|
||||
bs ? bdrv_get_node_name(bs) : NULL, optype,
|
||||
action, blk_iostatus_is_enabled(blk),
|
||||
error == ENOSPC, strerror(error));
|
||||
error == ENOSPC, strerror(error),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
/* This is done by device models because, while the block layer knows
|
||||
@@ -1713,7 +1705,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action,
|
||||
}
|
||||
}
|
||||
|
||||
bool blk_is_read_only(BlockBackend *blk)
|
||||
int blk_is_read_only(BlockBackend *blk)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
@@ -1724,18 +1716,18 @@ bool blk_is_read_only(BlockBackend *blk)
|
||||
}
|
||||
}
|
||||
|
||||
bool blk_is_sg(BlockBackend *blk)
|
||||
int blk_is_sg(BlockBackend *blk)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (!bs) {
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bdrv_is_sg(bs);
|
||||
}
|
||||
|
||||
bool blk_enable_write_cache(BlockBackend *blk)
|
||||
int blk_enable_write_cache(BlockBackend *blk)
|
||||
{
|
||||
return blk->enable_write_cache;
|
||||
}
|
||||
@@ -1794,7 +1786,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
|
||||
* the frontend experienced a tray event. */
|
||||
id = blk_get_attached_dev_id(blk);
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), id,
|
||||
eject_flag);
|
||||
eject_flag, &error_abort);
|
||||
g_free(id);
|
||||
}
|
||||
|
||||
@@ -2022,7 +2014,6 @@ static void blk_pdiscard_entry(void *opaque)
|
||||
QEMUIOVector *qiov = rwco->iobuf;
|
||||
|
||||
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
||||
@@ -2204,13 +2195,6 @@ static void blk_root_drained_begin(BdrvChild *child)
|
||||
}
|
||||
}
|
||||
|
||||
static bool blk_root_drained_poll(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
assert(blk->quiesce_counter);
|
||||
return !!blk->in_flight;
|
||||
}
|
||||
|
||||
static void blk_root_drained_end(BdrvChild *child)
|
||||
{
|
||||
BlockBackend *blk = child->opaque;
|
||||
|
||||
+11
-6
@@ -105,18 +105,23 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
struct bochs_header bochs;
|
||||
int ret;
|
||||
|
||||
/* No write support yet */
|
||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
error_report("Opening bochs images without an explicit read-only=on "
|
||||
"option is deprecated. Future versions will refuse to "
|
||||
"open the image instead of automatically marking the "
|
||||
"image read-only.");
|
||||
ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
||||
+11
-5
@@ -67,17 +67,23 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
uint32_t offsets_size, max_compressed_block_size = 1, i;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
error_report("Opening cloop images without an explicit read-only=on "
|
||||
"option is deprecated. Future versions will refuse to "
|
||||
"open the image instead of automatically marking the "
|
||||
"image read-only.");
|
||||
ret = bdrv_set_read_only(bs, true, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* read header */
|
||||
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
|
||||
if (ret < 0) {
|
||||
|
||||
+63
-58
@@ -36,7 +36,6 @@ typedef struct CommitBlockJob {
|
||||
BlockDriverState *commit_top_bs;
|
||||
BlockBackend *top;
|
||||
BlockBackend *base;
|
||||
BlockDriverState *base_bs;
|
||||
BlockdevOnError on_error;
|
||||
int base_flags;
|
||||
char *backing_file_str;
|
||||
@@ -69,72 +68,76 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int commit_prepare(Job *job)
|
||||
typedef struct {
|
||||
int ret;
|
||||
} CommitCompleteData;
|
||||
|
||||
static void commit_complete(Job *job, void *opaque)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
BlockJob *bjob = &s->common;
|
||||
CommitCompleteData *data = opaque;
|
||||
BlockDriverState *top = blk_bs(s->top);
|
||||
BlockDriverState *base = blk_bs(s->base);
|
||||
BlockDriverState *commit_top_bs = s->commit_top_bs;
|
||||
int ret = data->ret;
|
||||
bool remove_commit_top_bs = false;
|
||||
|
||||
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
|
||||
bdrv_ref(top);
|
||||
bdrv_ref(commit_top_bs);
|
||||
|
||||
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
|
||||
* the normal backing chain can be restored. */
|
||||
blk_unref(s->base);
|
||||
s->base = NULL;
|
||||
|
||||
/* FIXME: bdrv_drop_intermediate treats total failures and partial failures
|
||||
* identically. Further work is needed to disambiguate these cases. */
|
||||
return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs,
|
||||
s->backing_file_str);
|
||||
}
|
||||
|
||||
static void commit_abort(Job *job)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
BlockDriverState *top_bs = blk_bs(s->top);
|
||||
|
||||
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
|
||||
bdrv_ref(top_bs);
|
||||
bdrv_ref(s->commit_top_bs);
|
||||
|
||||
if (s->base) {
|
||||
blk_unref(s->base);
|
||||
if (!job_is_cancelled(job) && ret == 0) {
|
||||
/* success */
|
||||
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
|
||||
s->backing_file_str);
|
||||
} else {
|
||||
/* XXX Can (or should) we somehow keep 'consistent read' blocked even
|
||||
* after the failed/cancelled commit job is gone? If we already wrote
|
||||
* something to base, the intermediate images aren't valid any more. */
|
||||
remove_commit_top_bs = true;
|
||||
}
|
||||
|
||||
/* free the blockers on the intermediate nodes so that bdrv_replace_nodes
|
||||
* can succeed */
|
||||
block_job_remove_all_bdrv(&s->common);
|
||||
|
||||
/* If bdrv_drop_intermediate() failed (or was not invoked), remove the
|
||||
* commit filter driver from the backing chain now. Do this as the final
|
||||
* step so that the 'consistent read' permission can be granted.
|
||||
*
|
||||
* XXX Can (or should) we somehow keep 'consistent read' blocked even
|
||||
* after the failed/cancelled commit job is gone? If we already wrote
|
||||
* something to base, the intermediate images aren't valid any more. */
|
||||
bdrv_child_try_set_perm(s->commit_top_bs->backing, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs),
|
||||
&error_abort);
|
||||
|
||||
bdrv_unref(s->commit_top_bs);
|
||||
bdrv_unref(top_bs);
|
||||
}
|
||||
|
||||
static void commit_clean(Job *job)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
|
||||
/* restore base open flags here if appropriate (e.g., change the base back
|
||||
* to r/o). These reopens do not need to be atomic, since we won't abort
|
||||
* even on failure here */
|
||||
if (s->base_flags != bdrv_get_flags(s->base_bs)) {
|
||||
bdrv_reopen(s->base_bs, s->base_flags, NULL);
|
||||
if (s->base_flags != bdrv_get_flags(base)) {
|
||||
bdrv_reopen(base, s->base_flags, NULL);
|
||||
}
|
||||
|
||||
g_free(s->backing_file_str);
|
||||
blk_unref(s->top);
|
||||
|
||||
/* If there is more than one reference to the job (e.g. if called from
|
||||
* job_finish_sync()), job_completed() won't free it and therefore the
|
||||
* blockers on the intermediate nodes remain. This would cause
|
||||
* bdrv_set_backing_hd() to fail. */
|
||||
block_job_remove_all_bdrv(bjob);
|
||||
|
||||
job_completed(job, ret, NULL);
|
||||
g_free(data);
|
||||
|
||||
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
|
||||
* filter driver from the backing chain. Do this as the final step so that
|
||||
* the 'consistent read' permission can be granted. */
|
||||
if (remove_commit_top_bs) {
|
||||
bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs),
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
bdrv_unref(commit_top_bs);
|
||||
bdrv_unref(top);
|
||||
}
|
||||
|
||||
static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
static void coroutine_fn commit_run(void *opaque)
|
||||
{
|
||||
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
|
||||
CommitBlockJob *s = opaque;
|
||||
CommitCompleteData *data;
|
||||
int64_t offset;
|
||||
uint64_t delay_ns = 0;
|
||||
int ret = 0;
|
||||
@@ -207,7 +210,9 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
|
||||
out:
|
||||
qemu_vfree(buf);
|
||||
|
||||
return ret;
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
job_defer_to_main_loop(&s->common.job, commit_complete, data);
|
||||
}
|
||||
|
||||
static const BlockJobDriver commit_job_driver = {
|
||||
@@ -217,10 +222,7 @@ static const BlockJobDriver commit_job_driver = {
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.run = commit_run,
|
||||
.prepare = commit_prepare,
|
||||
.abort = commit_abort,
|
||||
.clean = commit_clean
|
||||
.start = commit_run,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -237,6 +239,10 @@ static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||
bs->backing->bs->filename);
|
||||
}
|
||||
|
||||
static void bdrv_commit_top_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
@@ -254,12 +260,12 @@ static BlockDriver bdrv_commit_top = {
|
||||
.bdrv_co_preadv = bdrv_commit_top_preadv,
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
||||
.bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
|
||||
.bdrv_close = bdrv_commit_top_close,
|
||||
.bdrv_child_perm = bdrv_commit_top_child_perm,
|
||||
};
|
||||
|
||||
void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, BlockDriverState *top,
|
||||
int creation_flags, int64_t speed,
|
||||
BlockDriverState *base, BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, const char *backing_file_str,
|
||||
const char *filter_node_name, Error **errp)
|
||||
{
|
||||
@@ -277,7 +283,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
|
||||
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
|
||||
speed, creation_flags, NULL, NULL, errp);
|
||||
speed, JOB_DEFAULT, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
@@ -354,7 +360,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
s->base_bs = base;
|
||||
|
||||
/* Required permissions are already taken with block_job_add_bdrv() */
|
||||
s->top = blk_new(0, BLK_PERM_ALL);
|
||||
|
||||
@@ -45,6 +45,11 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
|
||||
static void cor_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
|
||||
| BLK_PERM_WRITE \
|
||||
| BLK_PERM_RESIZE)
|
||||
@@ -138,6 +143,7 @@ BlockDriver bdrv_copy_on_read = {
|
||||
.format_name = "copy-on-read",
|
||||
|
||||
.bdrv_open = cor_open,
|
||||
.bdrv_close = cor_close,
|
||||
.bdrv_child_perm = cor_child_perm,
|
||||
|
||||
.bdrv_getlength = cor_getlength,
|
||||
|
||||
+13
-6
@@ -34,26 +34,33 @@ typedef struct BlockdevCreateJob {
|
||||
Job common;
|
||||
BlockDriver *drv;
|
||||
BlockdevCreateOptions *opts;
|
||||
int ret;
|
||||
Error *err;
|
||||
} BlockdevCreateJob;
|
||||
|
||||
static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
|
||||
static void blockdev_create_complete(Job *job, void *opaque)
|
||||
{
|
||||
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
|
||||
int ret;
|
||||
|
||||
job_completed(job, s->ret, s->err);
|
||||
}
|
||||
|
||||
static void coroutine_fn blockdev_create_run(void *opaque)
|
||||
{
|
||||
BlockdevCreateJob *s = opaque;
|
||||
|
||||
job_progress_set_remaining(&s->common, 1);
|
||||
ret = s->drv->bdrv_co_create(s->opts, errp);
|
||||
s->ret = s->drv->bdrv_co_create(s->opts, &s->err);
|
||||
job_progress_update(&s->common, 1);
|
||||
|
||||
qapi_free_BlockdevCreateOptions(s->opts);
|
||||
|
||||
return ret;
|
||||
job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL);
|
||||
}
|
||||
|
||||
static const JobDriver blockdev_create_job_driver = {
|
||||
.instance_size = sizeof(BlockdevCreateJob),
|
||||
.job_type = JOB_TYPE_CREATE,
|
||||
.run = blockdev_create_run,
|
||||
.start = blockdev_create_run,
|
||||
};
|
||||
|
||||
void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
|
||||
|
||||
+1
-3
@@ -627,9 +627,7 @@ BlockDriver bdrv_crypto_luks = {
|
||||
.bdrv_probe = block_crypto_probe_luks,
|
||||
.bdrv_open = block_crypto_open_luks,
|
||||
.bdrv_close = block_crypto_close,
|
||||
/* This driver doesn't modify LUKS metadata except when creating image.
|
||||
* Allow share-rw=on as a special case. */
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = block_crypto_co_create_luks,
|
||||
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
||||
.bdrv_co_truncate = block_crypto_co_truncate,
|
||||
|
||||
+4
-6
@@ -483,8 +483,6 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
||||
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
||||
(long) s->sslverify);
|
||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST,
|
||||
s->sslverify ? 2L : 0L);
|
||||
if (s->cookie) {
|
||||
curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
|
||||
}
|
||||
@@ -684,10 +682,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
const char *protocol_delimiter;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes",
|
||||
errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
error_setg(errp, "curl block device does not support writes");
|
||||
return -EROFS;
|
||||
}
|
||||
|
||||
if (!libcurl_initialized) {
|
||||
|
||||
+30
-49
@@ -55,10 +55,6 @@ struct BdrvDirtyBitmap {
|
||||
and this bitmap must remain unchanged while
|
||||
this flag is set. */
|
||||
bool persistent; /* bitmap must be saved to owner disk image */
|
||||
bool migration; /* Bitmap is selected for migration, it should
|
||||
not be stored on the next inactivation
|
||||
(persistent flag doesn't matter until next
|
||||
invalidation).*/
|
||||
QLIST_ENTRY(BdrvDirtyBitmap) list;
|
||||
};
|
||||
|
||||
@@ -180,12 +176,6 @@ bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap)
|
||||
return bitmap->successor;
|
||||
}
|
||||
|
||||
/* Both conditions disallow user-modification via QMP. */
|
||||
bool bdrv_dirty_bitmap_user_locked(BdrvDirtyBitmap *bitmap) {
|
||||
return bdrv_dirty_bitmap_frozen(bitmap) ||
|
||||
bdrv_dirty_bitmap_qmp_locked(bitmap);
|
||||
}
|
||||
|
||||
void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
@@ -324,7 +314,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) {
|
||||
if (!hbitmap_merge(parent->bitmap, successor->bitmap)) {
|
||||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||
return NULL;
|
||||
}
|
||||
@@ -393,6 +383,26 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Release all persistent dirty bitmaps attached to a BDS (for use in
|
||||
* bdrv_inactivate_recurse()).
|
||||
* There must not be any frozen bitmaps attached.
|
||||
* This function does not remove persistent bitmaps from the storage.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if (bdrv_dirty_bitmap_get_persistance(bm)) {
|
||||
bdrv_release_dirty_bitmap_locked(bm);
|
||||
}
|
||||
}
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove persistent dirty bitmap from the storage if it exists.
|
||||
* Absence of bitmap is not an error, because we have the following scenario:
|
||||
@@ -609,6 +619,7 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap,
|
||||
|
||||
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||
{
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||
bdrv_dirty_bitmap_lock(bitmap);
|
||||
if (!out) {
|
||||
@@ -622,12 +633,12 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out)
|
||||
bdrv_dirty_bitmap_unlock(bitmap);
|
||||
}
|
||||
|
||||
void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup)
|
||||
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in)
|
||||
{
|
||||
HBitmap *tmp = bitmap->bitmap;
|
||||
assert(bdrv_dirty_bitmap_enabled(bitmap));
|
||||
assert(!bdrv_dirty_bitmap_readonly(bitmap));
|
||||
bitmap->bitmap = backup;
|
||||
bitmap->bitmap = in;
|
||||
hbitmap_free(tmp);
|
||||
}
|
||||
|
||||
@@ -745,24 +756,16 @@ void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent)
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration)
|
||||
{
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
bitmap->migration = migration;
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return bitmap->persistent && !bitmap->migration;
|
||||
return bitmap->persistent;
|
||||
}
|
||||
|
||||
bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
BdrvDirtyBitmap *bm;
|
||||
QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) {
|
||||
if (bm->persistent && !bm->readonly && !bm->migration) {
|
||||
if (bm->persistent && !bm->readonly) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -788,41 +791,19 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
|
||||
}
|
||||
|
||||
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||
HBitmap **backup, Error **errp)
|
||||
Error **errp)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
/* only bitmaps from one bds are supported */
|
||||
assert(dest->mutex == src->mutex);
|
||||
|
||||
qemu_mutex_lock(dest->mutex);
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(dest)) {
|
||||
error_setg(errp, "Bitmap '%s' is currently in use by another"
|
||||
" operation and cannot be modified", dest->name);
|
||||
goto out;
|
||||
}
|
||||
assert(bdrv_dirty_bitmap_enabled(dest));
|
||||
assert(!bdrv_dirty_bitmap_readonly(dest));
|
||||
|
||||
if (bdrv_dirty_bitmap_readonly(dest)) {
|
||||
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
||||
dest->name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) {
|
||||
if (!hbitmap_merge(dest->bitmap, src->bitmap)) {
|
||||
error_setg(errp, "Bitmaps are incompatible and can't be merged");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (backup) {
|
||||
*backup = dest->bitmap;
|
||||
dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup));
|
||||
ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap);
|
||||
} else {
|
||||
ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap);
|
||||
}
|
||||
assert(ret);
|
||||
|
||||
out:
|
||||
qemu_mutex_unlock(dest->mutex);
|
||||
}
|
||||
+11
-5
@@ -413,17 +413,23 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
int64_t offset;
|
||||
int ret;
|
||||
|
||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
|
||||
false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
error_report("Opening dmg images without an explicit read-only=on "
|
||||
"option is deprecated. Future versions will refuse to "
|
||||
"open the image instead of automatically marking the "
|
||||
"image read-only.");
|
||||
ret = bdrv_set_read_only(bs, true, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
block_module_load_one("dmg-bz2");
|
||||
|
||||
s->n_chunks = 0;
|
||||
|
||||
+62
-114
@@ -142,6 +142,7 @@ do { \
|
||||
|
||||
typedef struct BDRVRawState {
|
||||
int fd;
|
||||
int lock_fd;
|
||||
bool use_lock;
|
||||
int type;
|
||||
int open_flags;
|
||||
@@ -151,11 +152,6 @@ typedef struct BDRVRawState {
|
||||
uint64_t perm;
|
||||
uint64_t shared_perm;
|
||||
|
||||
/* The perms bits whose corresponding bytes are already locked in
|
||||
* s->fd. */
|
||||
uint64_t locked_perm;
|
||||
uint64_t locked_shared_perm;
|
||||
|
||||
#ifdef CONFIG_XFS
|
||||
bool is_xfs:1;
|
||||
#endif
|
||||
@@ -209,7 +205,7 @@ static int cdrom_reopen(BlockDriverState *bs);
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
static int raw_normalize_devicepath(const char **filename, Error **errp)
|
||||
static int raw_normalize_devicepath(const char **filename)
|
||||
{
|
||||
static char namebuf[PATH_MAX];
|
||||
const char *dp, *fname;
|
||||
@@ -218,7 +214,8 @@ static int raw_normalize_devicepath(const char **filename, Error **errp)
|
||||
fname = *filename;
|
||||
dp = strrchr(fname, '/');
|
||||
if (lstat(fname, &sb) < 0) {
|
||||
error_setg_errno(errp, errno, "%s: stat failed", fname);
|
||||
fprintf(stderr, "%s: stat failed: %s\n",
|
||||
fname, strerror(errno));
|
||||
return -errno;
|
||||
}
|
||||
|
||||
@@ -232,13 +229,14 @@ static int raw_normalize_devicepath(const char **filename, Error **errp)
|
||||
snprintf(namebuf, PATH_MAX, "%.*s/r%s",
|
||||
(int)(dp - fname), fname, dp + 1);
|
||||
}
|
||||
fprintf(stderr, "%s is a block device", fname);
|
||||
*filename = namebuf;
|
||||
warn_report("%s is a block device, using %s", fname, *filename);
|
||||
fprintf(stderr, ", using %s\n", *filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int raw_normalize_devicepath(const char **filename, Error **errp)
|
||||
static int raw_normalize_devicepath(const char **filename)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -463,8 +461,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
|
||||
filename = qemu_opt_get(opts, "filename");
|
||||
|
||||
ret = raw_normalize_devicepath(&filename, errp);
|
||||
ret = raw_normalize_devicepath(&filename);
|
||||
if (ret != 0) {
|
||||
error_setg_errno(errp, -ret, "Could not normalize device path");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -493,10 +492,11 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
case ON_OFF_AUTO_ON:
|
||||
s->use_lock = true;
|
||||
if (!qemu_has_ofd_lock()) {
|
||||
warn_report("File lock requested but OFD locking syscall is "
|
||||
"unavailable, falling back to POSIX file locks");
|
||||
error_printf("Due to the implementation, locks can be lost "
|
||||
"unexpectedly.\n");
|
||||
fprintf(stderr,
|
||||
"File lock requested but OFD locking syscall is "
|
||||
"unavailable, falling back to POSIX file locks.\n"
|
||||
"Due to the implementation, locks can be lost "
|
||||
"unexpectedly.\n");
|
||||
}
|
||||
break;
|
||||
case ON_OFF_AUTO_OFF:
|
||||
@@ -527,22 +527,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
|
||||
s->fd = -1;
|
||||
fd = qemu_open(filename, s->open_flags, 0644);
|
||||
ret = fd < 0 ? -errno : 0;
|
||||
|
||||
if (ret == -EACCES || ret == -EROFS) {
|
||||
/* Try to degrade to read-only, but if it doesn't work, still use the
|
||||
* normal error message. */
|
||||
if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
|
||||
bdrv_flags &= ~BDRV_O_RDWR;
|
||||
raw_parse_flags(bdrv_flags, &s->open_flags);
|
||||
assert(!(s->open_flags & O_CREAT));
|
||||
fd = qemu_open(filename, s->open_flags);
|
||||
ret = fd < 0 ? -errno : 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not open '%s'", filename);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, errno, "Could not open '%s'", filename);
|
||||
if (ret == -EROFS) {
|
||||
ret = -EACCES;
|
||||
}
|
||||
@@ -550,6 +537,18 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
|
||||
}
|
||||
s->fd = fd;
|
||||
|
||||
s->lock_fd = -1;
|
||||
if (s->use_lock) {
|
||||
fd = qemu_open(filename, s->open_flags);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
error_setg_errno(errp, errno, "Could not open '%s' for locking",
|
||||
filename);
|
||||
qemu_close(s->fd);
|
||||
goto fail;
|
||||
}
|
||||
s->lock_fd = fd;
|
||||
}
|
||||
s->perm = 0;
|
||||
s->shared_perm = BLK_PERM_ALL;
|
||||
|
||||
@@ -681,72 +680,43 @@ typedef enum {
|
||||
* file; if @unlock == true, also unlock the unneeded bytes.
|
||||
* @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
|
||||
*/
|
||||
static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
|
||||
static int raw_apply_lock_bytes(int fd,
|
||||
uint64_t perm_lock_bits,
|
||||
uint64_t shared_perm_lock_bits,
|
||||
bool unlock, Error **errp)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
uint64_t locked_perm, locked_shared_perm;
|
||||
|
||||
if (s) {
|
||||
locked_perm = s->locked_perm;
|
||||
locked_shared_perm = s->locked_shared_perm;
|
||||
} else {
|
||||
/*
|
||||
* We don't have the previous bits, just lock/unlock for each of the
|
||||
* requested bits.
|
||||
*/
|
||||
if (unlock) {
|
||||
locked_perm = BLK_PERM_ALL;
|
||||
locked_shared_perm = BLK_PERM_ALL;
|
||||
} else {
|
||||
locked_perm = 0;
|
||||
locked_shared_perm = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PERM_FOREACH(i) {
|
||||
int off = RAW_LOCK_PERM_BASE + i;
|
||||
uint64_t bit = (1ULL << i);
|
||||
if ((perm_lock_bits & bit) && !(locked_perm & bit)) {
|
||||
if (perm_lock_bits & (1ULL << i)) {
|
||||
ret = qemu_lock_fd(fd, off, 1, false);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to lock byte %d", off);
|
||||
return ret;
|
||||
} else if (s) {
|
||||
s->locked_perm |= bit;
|
||||
}
|
||||
} else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) {
|
||||
} else if (unlock) {
|
||||
ret = qemu_unlock_fd(fd, off, 1);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to unlock byte %d", off);
|
||||
return ret;
|
||||
} else if (s) {
|
||||
s->locked_perm &= ~bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
PERM_FOREACH(i) {
|
||||
int off = RAW_LOCK_SHARED_BASE + i;
|
||||
uint64_t bit = (1ULL << i);
|
||||
if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) {
|
||||
if (shared_perm_lock_bits & (1ULL << i)) {
|
||||
ret = qemu_lock_fd(fd, off, 1, false);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to lock byte %d", off);
|
||||
return ret;
|
||||
} else if (s) {
|
||||
s->locked_shared_perm |= bit;
|
||||
}
|
||||
} else if (unlock && (locked_shared_perm & bit) &&
|
||||
!(shared_perm_lock_bits & bit)) {
|
||||
} else if (unlock) {
|
||||
ret = qemu_unlock_fd(fd, off, 1);
|
||||
if (ret) {
|
||||
error_setg(errp, "Failed to unlock byte %d", off);
|
||||
return ret;
|
||||
} else if (s) {
|
||||
s->locked_shared_perm &= ~bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -771,6 +741,8 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
|
||||
"Failed to get \"%s\" lock",
|
||||
perm_name);
|
||||
g_free(perm_name);
|
||||
error_append_hint(errp,
|
||||
"Is another process using the image?\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -786,6 +758,8 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm,
|
||||
"Failed to get shared \"%s\" lock",
|
||||
perm_name);
|
||||
g_free(perm_name);
|
||||
error_append_hint(errp,
|
||||
"Is another process using the image?\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -810,40 +784,39 @@ static int raw_handle_perm_lock(BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(s->lock_fd > 0);
|
||||
|
||||
switch (op) {
|
||||
case RAW_PL_PREPARE:
|
||||
ret = raw_apply_lock_bytes(s, s->fd, s->perm | new_perm,
|
||||
ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm,
|
||||
~s->shared_perm | ~new_shared,
|
||||
false, errp);
|
||||
if (!ret) {
|
||||
ret = raw_check_lock_bytes(s->fd, new_perm, new_shared, errp);
|
||||
ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp);
|
||||
if (!ret) {
|
||||
return 0;
|
||||
}
|
||||
error_append_hint(errp,
|
||||
"Is another process using the image [%s]?\n",
|
||||
bs->filename);
|
||||
}
|
||||
op = RAW_PL_ABORT;
|
||||
/* fall through to unlock bytes. */
|
||||
case RAW_PL_ABORT:
|
||||
raw_apply_lock_bytes(s, s->fd, s->perm, ~s->shared_perm,
|
||||
raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm,
|
||||
true, &local_err);
|
||||
if (local_err) {
|
||||
/* Theoretically the above call only unlocks bytes and it cannot
|
||||
* fail. Something weird happened, report it.
|
||||
*/
|
||||
warn_report_err(local_err);
|
||||
error_report_err(local_err);
|
||||
}
|
||||
break;
|
||||
case RAW_PL_COMMIT:
|
||||
raw_apply_lock_bytes(s, s->fd, new_perm, ~new_shared,
|
||||
raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared,
|
||||
true, &local_err);
|
||||
if (local_err) {
|
||||
/* Theoretically the above call only unlocks bytes and it cannot
|
||||
* fail. Something weird happened, report it.
|
||||
*/
|
||||
warn_report_err(local_err);
|
||||
error_report_err(local_err);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -877,13 +850,8 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
goto out;
|
||||
}
|
||||
|
||||
rs->check_cache_dropped =
|
||||
qemu_opt_get_bool_del(opts, "x-check-cache-dropped", false);
|
||||
|
||||
/* This driver's reopen function doesn't currently allow changing
|
||||
* other options, so let's put them back in the original QDict and
|
||||
* bdrv_reopen_prepare() will detect changes and complain. */
|
||||
qemu_opts_to_qdict(opts, state->options);
|
||||
rs->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped",
|
||||
s->check_cache_dropped);
|
||||
|
||||
if (s->type == FTYPE_CD) {
|
||||
rs->open_flags |= O_NONBLOCK;
|
||||
@@ -920,8 +888,10 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
/* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */
|
||||
if (rs->fd == -1) {
|
||||
const char *normalized_filename = state->bs->filename;
|
||||
ret = raw_normalize_devicepath(&normalized_filename, errp);
|
||||
if (ret >= 0) {
|
||||
ret = raw_normalize_devicepath(&normalized_filename);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not normalize device path");
|
||||
} else {
|
||||
assert(!(rs->open_flags & O_CREAT));
|
||||
rs->fd = qemu_open(normalized_filename, rs->open_flags);
|
||||
if (rs->fd == -1) {
|
||||
@@ -952,18 +922,10 @@ static void raw_reopen_commit(BDRVReopenState *state)
|
||||
{
|
||||
BDRVRawReopenState *rs = state->opaque;
|
||||
BDRVRawState *s = state->bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
|
||||
s->check_cache_dropped = rs->check_cache_dropped;
|
||||
s->open_flags = rs->open_flags;
|
||||
|
||||
/* Copy locks to the new fd before closing the old one. */
|
||||
raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
|
||||
s->locked_shared_perm, false, &local_err);
|
||||
if (local_err) {
|
||||
/* shouldn't fail in a sane host, but report it just in case. */
|
||||
error_report_err(local_err);
|
||||
}
|
||||
qemu_close(s->fd);
|
||||
s->fd = rs->fd;
|
||||
|
||||
@@ -1809,7 +1771,7 @@ static int aio_worker(void *arg)
|
||||
ret = handle_aiocb_truncate(aiocb);
|
||||
break;
|
||||
default:
|
||||
error_report("invalid aio request (0x%x)", aiocb->aio_type);
|
||||
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
@@ -1956,6 +1918,10 @@ static void raw_close(BlockDriverState *bs)
|
||||
qemu_close(s->fd);
|
||||
s->fd = -1;
|
||||
}
|
||||
if (s->lock_fd >= 0) {
|
||||
qemu_close(s->lock_fd);
|
||||
s->lock_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2243,7 +2209,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
|
||||
|
||||
/* Step one: Take locks */
|
||||
result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp);
|
||||
result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp);
|
||||
if (result < 0) {
|
||||
goto out_close;
|
||||
}
|
||||
@@ -2251,9 +2217,6 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
/* Step two: Check that nobody else has taken conflicting locks */
|
||||
result = raw_check_lock_bytes(fd, perm, shared, errp);
|
||||
if (result < 0) {
|
||||
error_append_hint(errp,
|
||||
"Is another process using the image [%s]?\n",
|
||||
file_opts->filename);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
@@ -2287,13 +2250,13 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err);
|
||||
raw_apply_lock_bytes(fd, 0, 0, true, &local_err);
|
||||
if (local_err) {
|
||||
/* The above call should not fail, and if it does, that does
|
||||
* not mean the whole creation operation has failed. So
|
||||
* report it the user for their convenience, but do not report
|
||||
* it to the caller. */
|
||||
warn_report_err(local_err);
|
||||
error_report_err(local_err);
|
||||
}
|
||||
|
||||
out_close:
|
||||
@@ -2461,8 +2424,6 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
|
||||
off_t data = 0, hole = 0;
|
||||
int ret;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
|
||||
|
||||
ret = fd_open(bs);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
@@ -2488,20 +2449,6 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
|
||||
/* On a data extent, compute bytes to the end of the extent,
|
||||
* possibly including a partial sector at EOF. */
|
||||
*pnum = MIN(bytes, hole - offset);
|
||||
|
||||
/*
|
||||
* We are not allowed to return partial sectors, though, so
|
||||
* round up if necessary.
|
||||
*/
|
||||
if (!QEMU_IS_ALIGNED(*pnum, bs->bl.request_alignment)) {
|
||||
int64_t file_length = raw_getlength(bs);
|
||||
if (file_length > 0) {
|
||||
/* Ignore errors, this is just a safeguard */
|
||||
assert(hole == file_length);
|
||||
}
|
||||
*pnum = ROUND_UP(*pnum, bs->bl.request_alignment);
|
||||
}
|
||||
|
||||
ret = BDRV_BLOCK_DATA;
|
||||
} else {
|
||||
/* On a hole, compute bytes to the beginning of the next extent. */
|
||||
@@ -3174,8 +3121,9 @@ static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts
|
||||
|
||||
(void)has_prefix;
|
||||
|
||||
ret = raw_normalize_devicepath(&filename, errp);
|
||||
ret = raw_normalize_devicepath(&filename);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not normalize device path");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+3
-19
@@ -20,10 +20,6 @@
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
#ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT
|
||||
# define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL)
|
||||
#endif
|
||||
|
||||
#define GLUSTER_OPT_FILENAME "filename"
|
||||
#define GLUSTER_OPT_VOLUME "volume"
|
||||
#define GLUSTER_OPT_PATH "path"
|
||||
@@ -729,11 +725,7 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
|
||||
/*
|
||||
* AIO callback routine called from GlusterFS thread.
|
||||
*/
|
||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret,
|
||||
#ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT
|
||||
struct glfs_stat *pre, struct glfs_stat *post,
|
||||
#endif
|
||||
void *arg)
|
||||
static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
|
||||
{
|
||||
GlusterAIOCB *acb = (GlusterAIOCB *)arg;
|
||||
|
||||
@@ -857,16 +849,8 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
|
||||
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
|
||||
|
||||
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
|
||||
ret = s->fd ? 0 : -errno;
|
||||
|
||||
if (ret == -EACCES || ret == -EROFS) {
|
||||
/* Try to degrade to read-only, but if it doesn't work, still use the
|
||||
* normal error message. */
|
||||
if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
|
||||
open_flags = (open_flags & ~O_RDWR) | O_RDONLY;
|
||||
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
|
||||
ret = s->fd ? 0 : -errno;
|
||||
}
|
||||
if (!s->fd) {
|
||||
ret = -errno;
|
||||
}
|
||||
|
||||
s->supports_seek_data = qemu_gluster_test_seek(s->fd);
|
||||
|
||||
+14
-24
@@ -38,6 +38,8 @@
|
||||
/* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */
|
||||
#define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS)
|
||||
|
||||
static AioWait drain_all_aio_wait;
|
||||
|
||||
static void bdrv_parent_cb_resize(BlockDriverState *bs);
|
||||
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
|
||||
int64_t offset, int bytes, BdrvRequestFlags flags);
|
||||
@@ -266,6 +268,10 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive,
|
||||
static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive,
|
||||
BdrvChild *ignore_parent)
|
||||
{
|
||||
/* Execute pending BHs first and check everything else only after the BHs
|
||||
* have executed. */
|
||||
while (aio_poll(bs->aio_context, false));
|
||||
|
||||
return bdrv_drain_poll(bs, recursive, ignore_parent, false);
|
||||
}
|
||||
|
||||
@@ -282,18 +288,6 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
BlockDriverState *bs = data->bs;
|
||||
|
||||
if (bs) {
|
||||
AioContext *ctx = bdrv_get_aio_context(bs);
|
||||
AioContext *co_ctx = qemu_coroutine_get_aio_context(co);
|
||||
|
||||
/*
|
||||
* When the coroutine yielded, the lock for its home context was
|
||||
* released, so we need to re-acquire it here. If it explicitly
|
||||
* acquired a different context, the lock is still held and we don't
|
||||
* want to lock it a second time (or AIO_WAIT_WHILE() would hang).
|
||||
*/
|
||||
if (ctx == co_ctx) {
|
||||
aio_context_acquire(ctx);
|
||||
}
|
||||
bdrv_dec_in_flight(bs);
|
||||
if (data->begin) {
|
||||
bdrv_do_drained_begin(bs, data->recursive, data->parent,
|
||||
@@ -302,9 +296,6 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
bdrv_do_drained_end(bs, data->recursive, data->parent,
|
||||
data->ignore_bds_parents);
|
||||
}
|
||||
if (ctx == co_ctx) {
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
} else {
|
||||
assert(data->begin);
|
||||
bdrv_drain_all_begin();
|
||||
@@ -505,6 +496,10 @@ static bool bdrv_drain_all_poll(void)
|
||||
BlockDriverState *bs = NULL;
|
||||
bool result = false;
|
||||
|
||||
/* Execute pending BHs first (may modify the graph) and check everything
|
||||
* else only after the BHs have executed. */
|
||||
while (aio_poll(qemu_get_aio_context(), false));
|
||||
|
||||
/* bdrv_drain_poll() can't make changes to the graph and we are holding the
|
||||
* main AioContext lock, so iterating bdrv_next_all_states() is safe. */
|
||||
while ((bs = bdrv_next_all_states(bs))) {
|
||||
@@ -555,7 +550,7 @@ void bdrv_drain_all_begin(void)
|
||||
}
|
||||
|
||||
/* Now poll the in-flight requests */
|
||||
AIO_WAIT_WHILE(NULL, bdrv_drain_all_poll());
|
||||
AIO_WAIT_WHILE(&drain_all_aio_wait, NULL, bdrv_drain_all_poll());
|
||||
|
||||
while ((bs = bdrv_next_all_states(bs))) {
|
||||
bdrv_drain_assert_idle(bs);
|
||||
@@ -711,7 +706,8 @@ void bdrv_inc_in_flight(BlockDriverState *bs)
|
||||
|
||||
void bdrv_wakeup(BlockDriverState *bs)
|
||||
{
|
||||
aio_wait_kick();
|
||||
aio_wait_kick(bdrv_get_aio_wait(bs));
|
||||
aio_wait_kick(&drain_all_aio_wait);
|
||||
}
|
||||
|
||||
void bdrv_dec_in_flight(BlockDriverState *bs)
|
||||
@@ -806,7 +802,6 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque)
|
||||
rwco->qiov->size, rwco->qiov,
|
||||
rwco->flags);
|
||||
}
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2280,7 +2275,6 @@ static void coroutine_fn bdrv_block_status_above_co_entry(void *opaque)
|
||||
data->offset, data->bytes,
|
||||
data->pnum, data->map, data->file);
|
||||
data->done = true;
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2440,7 +2434,6 @@ static void coroutine_fn bdrv_co_rw_vmstate_entry(void *opaque)
|
||||
{
|
||||
BdrvVmstateCo *co = opaque;
|
||||
co->ret = bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
static inline int
|
||||
@@ -2562,7 +2555,6 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque)
|
||||
FlushCo *rwco = opaque;
|
||||
|
||||
rwco->ret = bdrv_co_flush(rwco->bs);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
|
||||
@@ -2708,7 +2700,6 @@ static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque)
|
||||
DiscardCo *rwco = opaque;
|
||||
|
||||
rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes)
|
||||
@@ -3222,7 +3213,6 @@ static void coroutine_fn bdrv_truncate_co_entry(void *opaque)
|
||||
TruncateCo *tco = opaque;
|
||||
tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc,
|
||||
tco->errp);
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
|
||||
@@ -3242,7 +3232,7 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
|
||||
bdrv_truncate_co_entry(&tco);
|
||||
} else {
|
||||
co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco);
|
||||
bdrv_coroutine_enter(child->bs, co);
|
||||
qemu_coroutine_enter(co);
|
||||
BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE);
|
||||
}
|
||||
|
||||
|
||||
+4
-6
@@ -1844,7 +1844,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
iscsi_set_timeout(iscsi, timeout);
|
||||
#else
|
||||
if (timeout) {
|
||||
warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
|
||||
error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1878,11 +1878,9 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Check the write protect flag of the LUN if we want to write */
|
||||
if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
|
||||
iscsilun->write_protected) {
|
||||
ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
flags &= ~BDRV_O_RDWR;
|
||||
error_setg(errp, "Cannot open a write protected LUN as read-write");
|
||||
ret = -EACCES;
|
||||
goto out;
|
||||
}
|
||||
|
||||
iscsi_readcapacity_sync(iscsilun, &local_err);
|
||||
|
||||
+1
-1
@@ -234,9 +234,9 @@ static void qemu_laio_process_completions(LinuxAioState *s)
|
||||
|
||||
static void qemu_laio_process_completions_and_submit(LinuxAioState *s)
|
||||
{
|
||||
aio_context_acquire(s->aio_context);
|
||||
qemu_laio_process_completions(s);
|
||||
|
||||
aio_context_acquire(s->aio_context);
|
||||
if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
|
||||
ioq_submit(s);
|
||||
}
|
||||
|
||||
+34
-47
@@ -79,7 +79,6 @@ typedef struct MirrorBlockJob {
|
||||
int max_iov;
|
||||
bool initial_zeroing_ongoing;
|
||||
int in_active_write_counter;
|
||||
bool prepared;
|
||||
} MirrorBlockJob;
|
||||
|
||||
typedef struct MirrorBDSOpaque {
|
||||
@@ -605,33 +604,26 @@ static void mirror_wait_for_all_io(MirrorBlockJob *s)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mirror_exit_common: handle both abort() and prepare() cases.
|
||||
* for .prepare, returns 0 on success and -errno on failure.
|
||||
* for .abort cases, denoted by abort = true, MUST return 0.
|
||||
*/
|
||||
static int mirror_exit_common(Job *job)
|
||||
typedef struct {
|
||||
int ret;
|
||||
} MirrorExitData;
|
||||
|
||||
static void mirror_exit(Job *job, void *opaque)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||
BlockJob *bjob = &s->common;
|
||||
MirrorExitData *data = opaque;
|
||||
MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque;
|
||||
AioContext *replace_aio_context = NULL;
|
||||
BlockDriverState *src = s->mirror_top_bs->backing->bs;
|
||||
BlockDriverState *target_bs = blk_bs(s->target);
|
||||
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
|
||||
Error *local_err = NULL;
|
||||
bool abort = job->ret < 0;
|
||||
int ret = 0;
|
||||
|
||||
if (s->prepared) {
|
||||
return 0;
|
||||
}
|
||||
s->prepared = true;
|
||||
|
||||
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
|
||||
|
||||
/* Make sure that the source BDS doesn't go away during bdrv_replace_node,
|
||||
* before we can call bdrv_drained_end */
|
||||
/* Make sure that the source BDS doesn't go away before we called
|
||||
* job_completed(). */
|
||||
bdrv_ref(src);
|
||||
bdrv_ref(mirror_top_bs);
|
||||
bdrv_ref(target_bs);
|
||||
@@ -651,13 +643,13 @@ static int mirror_exit_common(Job *job)
|
||||
* required before it could become a backing file of target_bs. */
|
||||
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
|
||||
if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
|
||||
BlockDriverState *backing = s->is_none_mode ? src : s->base;
|
||||
if (backing_bs(target_bs) != backing) {
|
||||
bdrv_set_backing_hd(target_bs, backing, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
ret = -EPERM;
|
||||
data->ret = -EPERM;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -667,8 +659,11 @@ static int mirror_exit_common(Job *job)
|
||||
aio_context_acquire(replace_aio_context);
|
||||
}
|
||||
|
||||
if (s->should_complete && !abort) {
|
||||
BlockDriverState *to_replace = s->to_replace ?: src;
|
||||
if (s->should_complete && data->ret == 0) {
|
||||
BlockDriverState *to_replace = src;
|
||||
if (s->to_replace) {
|
||||
to_replace = s->to_replace;
|
||||
}
|
||||
|
||||
if (bdrv_get_flags(target_bs) != bdrv_get_flags(to_replace)) {
|
||||
bdrv_reopen(target_bs, bdrv_get_flags(to_replace), NULL);
|
||||
@@ -681,7 +676,7 @@ static int mirror_exit_common(Job *job)
|
||||
bdrv_drained_end(target_bs);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
ret = -EPERM;
|
||||
data->ret = -EPERM;
|
||||
}
|
||||
}
|
||||
if (s->to_replace) {
|
||||
@@ -712,23 +707,12 @@ static int mirror_exit_common(Job *job)
|
||||
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
|
||||
|
||||
bs_opaque->job = NULL;
|
||||
job_completed(job, data->ret, NULL);
|
||||
|
||||
g_free(data);
|
||||
bdrv_drained_end(src);
|
||||
bdrv_unref(mirror_top_bs);
|
||||
bdrv_unref(src);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mirror_prepare(Job *job)
|
||||
{
|
||||
return mirror_exit_common(job);
|
||||
}
|
||||
|
||||
static void mirror_abort(Job *job)
|
||||
{
|
||||
int ret = mirror_exit_common(job);
|
||||
assert(ret == 0);
|
||||
}
|
||||
|
||||
static void mirror_throttle(MirrorBlockJob *s)
|
||||
@@ -825,9 +809,10 @@ static int mirror_flush(MirrorBlockJob *s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int coroutine_fn mirror_run(Job *job, Error **errp)
|
||||
static void coroutine_fn mirror_run(void *opaque)
|
||||
{
|
||||
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
|
||||
MirrorBlockJob *s = opaque;
|
||||
MirrorExitData *data;
|
||||
BlockDriverState *bs = s->mirror_top_bs->backing->bs;
|
||||
BlockDriverState *target_bs = blk_bs(s->target);
|
||||
bool need_drain = true;
|
||||
@@ -1047,11 +1032,13 @@ immediate_exit:
|
||||
g_free(s->in_flight_bitmap);
|
||||
bdrv_dirty_iter_free(s->dbi);
|
||||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
|
||||
if (need_drain) {
|
||||
bdrv_drained_begin(bs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
job_defer_to_main_loop(&s->common.job, mirror_exit, data);
|
||||
}
|
||||
|
||||
static void mirror_complete(Job *job, Error **errp)
|
||||
@@ -1148,9 +1135,7 @@ static const BlockJobDriver mirror_job_driver = {
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.run = mirror_run,
|
||||
.prepare = mirror_prepare,
|
||||
.abort = mirror_abort,
|
||||
.start = mirror_run,
|
||||
.pause = mirror_pause,
|
||||
.complete = mirror_complete,
|
||||
},
|
||||
@@ -1166,9 +1151,7 @@ static const BlockJobDriver commit_active_job_driver = {
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.run = mirror_run,
|
||||
.prepare = mirror_prepare,
|
||||
.abort = mirror_abort,
|
||||
.start = mirror_run,
|
||||
.pause = mirror_pause,
|
||||
.complete = mirror_complete,
|
||||
},
|
||||
@@ -1440,6 +1423,10 @@ static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||
bs->backing->bs->filename);
|
||||
}
|
||||
|
||||
static void bdrv_mirror_top_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
@@ -1466,6 +1453,7 @@ static BlockDriver bdrv_mirror_top = {
|
||||
.bdrv_co_flush = bdrv_mirror_top_flush,
|
||||
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
|
||||
.bdrv_refresh_filename = bdrv_mirror_top_refresh_filename,
|
||||
.bdrv_close = bdrv_mirror_top_close,
|
||||
.bdrv_child_perm = bdrv_mirror_top_child_perm,
|
||||
};
|
||||
|
||||
@@ -1658,8 +1646,7 @@ fail:
|
||||
|
||||
void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target, const char *replaces,
|
||||
int creation_flags, int64_t speed,
|
||||
uint32_t granularity, int64_t buf_size,
|
||||
int64_t speed, uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
@@ -1675,7 +1662,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
}
|
||||
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
|
||||
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
|
||||
mirror_start_job(job_id, bs, creation_flags, target, replaces,
|
||||
mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces,
|
||||
speed, granularity, buf_size, backing_mode,
|
||||
on_source_error, on_target_error, unmap, NULL, NULL,
|
||||
&mirror_job_driver, is_none_mode, base, false,
|
||||
|
||||
+6
-6
@@ -116,7 +116,6 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque)
|
||||
s->quit = true;
|
||||
nbd_recv_coroutines_wake_all(s);
|
||||
s->read_reply_co = NULL;
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
static int nbd_co_send_request(BlockDriverState *bs,
|
||||
@@ -999,11 +998,12 @@ int nbd_client_init(BlockDriverState *bs,
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (client->info.flags & NBD_FLAG_READ_ONLY) {
|
||||
ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (client->info.flags & NBD_FLAG_READ_ONLY &&
|
||||
!bdrv_is_read_only(bs)) {
|
||||
error_setg(errp,
|
||||
"request for write access conflicts with read-only export");
|
||||
ret = -EACCES;
|
||||
goto fail;
|
||||
}
|
||||
if (client->info.flags & NBD_FLAG_SEND_FUA) {
|
||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
||||
|
||||
@@ -97,6 +97,10 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void null_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int64_t null_getlength(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNullState *s = bs->opaque;
|
||||
@@ -259,6 +263,7 @@ static BlockDriver bdrv_null_co = {
|
||||
|
||||
.bdrv_file_open = null_file_open,
|
||||
.bdrv_parse_filename = null_co_parse_filename,
|
||||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_co_preadv = null_co_preadv,
|
||||
@@ -278,6 +283,7 @@ static BlockDriver bdrv_null_aio = {
|
||||
|
||||
.bdrv_file_open = null_file_open,
|
||||
.bdrv_parse_filename = null_aio_parse_filename,
|
||||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_aio_preadv = null_aio_preadv,
|
||||
|
||||
+12
-11
@@ -104,7 +104,7 @@ typedef struct {
|
||||
uint64_t nsze; /* Namespace size reported by identify command */
|
||||
int nsid; /* The namespace id to read/write data. */
|
||||
uint64_t max_transfer;
|
||||
bool plugged;
|
||||
int plugged;
|
||||
|
||||
CoMutex dma_map_lock;
|
||||
CoQueue dma_flush_queue;
|
||||
@@ -390,7 +390,6 @@ static void nvme_cmd_sync_cb(void *opaque, int ret)
|
||||
{
|
||||
int *pret = opaque;
|
||||
*pret = ret;
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q,
|
||||
@@ -490,8 +489,10 @@ static void nvme_handle_event(EventNotifier *n)
|
||||
BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier);
|
||||
|
||||
trace_nvme_handle_event(s);
|
||||
aio_context_acquire(s->aio_context);
|
||||
event_notifier_test_and_clear(n);
|
||||
nvme_poll_queues(s);
|
||||
aio_context_release(s->aio_context);
|
||||
}
|
||||
|
||||
static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp)
|
||||
@@ -1100,8 +1101,7 @@ static void nvme_attach_aio_context(BlockDriverState *bs,
|
||||
static void nvme_aio_plug(BlockDriverState *bs)
|
||||
{
|
||||
BDRVNVMeState *s = bs->opaque;
|
||||
assert(!s->plugged);
|
||||
s->plugged = true;
|
||||
s->plugged++;
|
||||
}
|
||||
|
||||
static void nvme_aio_unplug(BlockDriverState *bs)
|
||||
@@ -1109,13 +1109,14 @@ static void nvme_aio_unplug(BlockDriverState *bs)
|
||||
int i;
|
||||
BDRVNVMeState *s = bs->opaque;
|
||||
assert(s->plugged);
|
||||
s->plugged = false;
|
||||
for (i = 1; i < s->nr_queues; i++) {
|
||||
NVMeQueuePair *q = s->queues[i];
|
||||
qemu_mutex_lock(&q->lock);
|
||||
nvme_kick(s, q);
|
||||
nvme_process_completion(s, q);
|
||||
qemu_mutex_unlock(&q->lock);
|
||||
if (!--s->plugged) {
|
||||
for (i = 1; i < s->nr_queues; i++) {
|
||||
NVMeQueuePair *q = s->queues[i];
|
||||
qemu_mutex_lock(&q->lock);
|
||||
nvme_kick(s, q);
|
||||
nvme_process_completion(s, q);
|
||||
qemu_mutex_unlock(&q->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -140,14 +140,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
header.magic = be32_to_cpu(header.magic);
|
||||
header.version = be32_to_cpu(header.version);
|
||||
header.backing_file_offset = be64_to_cpu(header.backing_file_offset);
|
||||
header.backing_file_size = be32_to_cpu(header.backing_file_size);
|
||||
header.mtime = be32_to_cpu(header.mtime);
|
||||
header.size = be64_to_cpu(header.size);
|
||||
header.crypt_method = be32_to_cpu(header.crypt_method);
|
||||
header.l1_table_offset = be64_to_cpu(header.l1_table_offset);
|
||||
be32_to_cpus(&header.magic);
|
||||
be32_to_cpus(&header.version);
|
||||
be64_to_cpus(&header.backing_file_offset);
|
||||
be32_to_cpus(&header.backing_file_size);
|
||||
be32_to_cpus(&header.mtime);
|
||||
be64_to_cpus(&header.size);
|
||||
be32_to_cpus(&header.crypt_method);
|
||||
be64_to_cpus(&header.l1_table_offset);
|
||||
|
||||
if (header.magic != QCOW_MAGIC) {
|
||||
error_setg(errp, "Image not in qcow format");
|
||||
@@ -270,7 +270,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
s->l1_table[i] = be64_to_cpu(s->l1_table[i]);
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
|
||||
/* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
|
||||
|
||||
+12
-28
@@ -118,7 +118,7 @@ static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
bitmap_table[i] = cpu_to_be64(bitmap_table[i]);
|
||||
cpu_to_be64s(&bitmap_table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb,
|
||||
}
|
||||
|
||||
for (i = 0; i < tb->size; ++i) {
|
||||
table[i] = be64_to_cpu(table[i]);
|
||||
be64_to_cpus(&table[i]);
|
||||
ret = check_table_entry(table[i], s->cluster_size);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
@@ -394,20 +394,20 @@ fail:
|
||||
|
||||
static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry)
|
||||
{
|
||||
entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset);
|
||||
entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size);
|
||||
entry->flags = be32_to_cpu(entry->flags);
|
||||
entry->name_size = be16_to_cpu(entry->name_size);
|
||||
entry->extra_data_size = be32_to_cpu(entry->extra_data_size);
|
||||
be64_to_cpus(&entry->bitmap_table_offset);
|
||||
be32_to_cpus(&entry->bitmap_table_size);
|
||||
be32_to_cpus(&entry->flags);
|
||||
be16_to_cpus(&entry->name_size);
|
||||
be32_to_cpus(&entry->extra_data_size);
|
||||
}
|
||||
|
||||
static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
|
||||
{
|
||||
entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset);
|
||||
entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size);
|
||||
entry->flags = cpu_to_be32(entry->flags);
|
||||
entry->name_size = cpu_to_be16(entry->name_size);
|
||||
entry->extra_data_size = cpu_to_be32(entry->extra_data_size);
|
||||
cpu_to_be64s(&entry->bitmap_table_offset);
|
||||
cpu_to_be32s(&entry->bitmap_table_size);
|
||||
cpu_to_be32s(&entry->flags);
|
||||
cpu_to_be16s(&entry->name_size);
|
||||
cpu_to_be32s(&entry->extra_data_size);
|
||||
}
|
||||
|
||||
static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
|
||||
@@ -1418,22 +1418,6 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp)
|
||||
g_free(tb);
|
||||
}
|
||||
|
||||
QSIMPLEQ_FOREACH(bm, bm_list, entry) {
|
||||
/* For safety, we remove bitmap after storing.
|
||||
* We may be here in two cases:
|
||||
* 1. bdrv_close. It's ok to drop bitmap.
|
||||
* 2. inactivation. It means migration without 'dirty-bitmaps'
|
||||
* capability, so bitmaps are not marked with
|
||||
* BdrvDirtyBitmap.migration flags. It's not bad to drop them too,
|
||||
* and reload on invalidation.
|
||||
*/
|
||||
if (bm->dirty_bitmap == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap);
|
||||
}
|
||||
|
||||
bitmap_list_free(bm_list);
|
||||
return;
|
||||
|
||||
|
||||
+15
-23
@@ -31,8 +31,7 @@
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
|
||||
uint64_t max);
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
|
||||
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||
int64_t offset, int64_t length, uint64_t addend,
|
||||
bool decrease, enum qcow2_discard_type type);
|
||||
@@ -363,7 +362,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
/* Allocate the refcount block itself and mark it as used */
|
||||
int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX);
|
||||
int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
|
||||
if (new_block < 0) {
|
||||
return new_block;
|
||||
}
|
||||
@@ -955,8 +954,7 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
|
||||
|
||||
|
||||
/* return < 0 if error */
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
|
||||
uint64_t max)
|
||||
static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t i, nb_clusters, refcount;
|
||||
@@ -981,9 +979,9 @@ retry:
|
||||
}
|
||||
|
||||
/* Make sure that all offsets in the "allocated" range are representable
|
||||
* in the requested max */
|
||||
* in an int64_t */
|
||||
if (s->free_cluster_index > 0 &&
|
||||
s->free_cluster_index - 1 > (max >> s->cluster_bits))
|
||||
s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits))
|
||||
{
|
||||
return -EFBIG;
|
||||
}
|
||||
@@ -1003,7 +1001,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size)
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
|
||||
do {
|
||||
offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET);
|
||||
offset = alloc_clusters_noref(bs, size);
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
}
|
||||
@@ -1085,11 +1083,7 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
|
||||
free_in_cluster = s->cluster_size - offset_into_cluster(s, offset);
|
||||
do {
|
||||
if (!offset || free_in_cluster < size) {
|
||||
int64_t new_cluster;
|
||||
|
||||
new_cluster = alloc_clusters_noref(bs, s->cluster_size,
|
||||
MIN(s->cluster_offset_mask,
|
||||
QCOW_MAX_CLUSTER_OFFSET));
|
||||
int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size);
|
||||
if (new_cluster < 0) {
|
||||
return new_cluster;
|
||||
}
|
||||
@@ -2725,17 +2719,15 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
|
||||
}
|
||||
|
||||
static const char *metadata_ol_names[] = {
|
||||
[QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header",
|
||||
[QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table",
|
||||
[QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table",
|
||||
[QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table",
|
||||
[QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block",
|
||||
[QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
|
||||
[QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
|
||||
[QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
|
||||
[QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory",
|
||||
[QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header",
|
||||
[QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table",
|
||||
[QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table",
|
||||
[QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table",
|
||||
[QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block",
|
||||
[QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table",
|
||||
[QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table",
|
||||
[QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table",
|
||||
};
|
||||
QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names));
|
||||
|
||||
/*
|
||||
* First performs a check for metadata overlaps (through
|
||||
|
||||
+73
-132
@@ -210,8 +210,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
"pread fail from offset %" PRIu64, offset);
|
||||
return 1;
|
||||
}
|
||||
ext.magic = be32_to_cpu(ext.magic);
|
||||
ext.len = be32_to_cpu(ext.len);
|
||||
be32_to_cpus(&ext.magic);
|
||||
be32_to_cpus(&ext.len);
|
||||
offset += sizeof(ext);
|
||||
#ifdef DEBUG_EXT
|
||||
printf("ext.magic = 0x%x\n", ext.magic);
|
||||
@@ -279,8 +279,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
"Unable to read CRYPTO header extension");
|
||||
return ret;
|
||||
}
|
||||
s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset);
|
||||
s->crypto_header.length = be64_to_cpu(s->crypto_header.length);
|
||||
be64_to_cpus(&s->crypto_header.offset);
|
||||
be64_to_cpus(&s->crypto_header.length);
|
||||
|
||||
if ((s->crypto_header.offset % s->cluster_size) != 0) {
|
||||
error_setg(errp, "Encryption header offset '%" PRIu64 "' is "
|
||||
@@ -342,11 +342,9 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bitmaps_ext.nb_bitmaps = be32_to_cpu(bitmaps_ext.nb_bitmaps);
|
||||
bitmaps_ext.bitmap_directory_size =
|
||||
be64_to_cpu(bitmaps_ext.bitmap_directory_size);
|
||||
bitmaps_ext.bitmap_directory_offset =
|
||||
be64_to_cpu(bitmaps_ext.bitmap_directory_offset);
|
||||
be32_to_cpus(&bitmaps_ext.nb_bitmaps);
|
||||
be64_to_cpus(&bitmaps_ext.bitmap_directory_size);
|
||||
be64_to_cpus(&bitmaps_ext.bitmap_directory_offset);
|
||||
|
||||
if (bitmaps_ext.nb_bitmaps > QCOW2_MAX_BITMAPS) {
|
||||
error_setg(errp,
|
||||
@@ -779,35 +777,29 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
uint64_t *refcount_cache_size, Error **errp)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t combined_cache_size, l2_cache_max_setting;
|
||||
uint64_t combined_cache_size;
|
||||
bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set;
|
||||
int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size;
|
||||
uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8);
|
||||
|
||||
combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE);
|
||||
l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE);
|
||||
refcount_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE);
|
||||
|
||||
combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0);
|
||||
l2_cache_max_setting = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE,
|
||||
DEFAULT_L2_CACHE_MAX_SIZE);
|
||||
*l2_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE, 0);
|
||||
*refcount_cache_size = qemu_opt_get_size(opts,
|
||||
QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0);
|
||||
|
||||
*l2_cache_entry_size = qemu_opt_get_size(
|
||||
opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size);
|
||||
|
||||
*l2_cache_size = MIN(max_l2_cache, l2_cache_max_setting);
|
||||
|
||||
if (combined_cache_size_set) {
|
||||
if (l2_cache_size_set && refcount_cache_size_set) {
|
||||
error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE
|
||||
" and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set "
|
||||
"at the same time");
|
||||
return;
|
||||
} else if (l2_cache_size_set &&
|
||||
(l2_cache_max_setting > combined_cache_size)) {
|
||||
} else if (*l2_cache_size > combined_cache_size) {
|
||||
error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed "
|
||||
QCOW2_OPT_CACHE_SIZE);
|
||||
return;
|
||||
@@ -822,6 +814,9 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
} else if (refcount_cache_size_set) {
|
||||
*l2_cache_size = combined_cache_size - *refcount_cache_size;
|
||||
} else {
|
||||
uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8);
|
||||
|
||||
/* Assign as much memory as possible to the L2 cache, and
|
||||
* use the remainder for the refcount cache */
|
||||
if (combined_cache_size >= max_l2_cache + min_refcount_cache) {
|
||||
@@ -833,9 +828,16 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts,
|
||||
*l2_cache_size = combined_cache_size - *refcount_cache_size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!l2_cache_size_set) {
|
||||
*l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE,
|
||||
(uint64_t)DEFAULT_L2_CACHE_CLUSTERS
|
||||
* s->cluster_size);
|
||||
}
|
||||
if (!refcount_cache_size_set) {
|
||||
*refcount_cache_size = min_refcount_cache;
|
||||
}
|
||||
}
|
||||
/* l2_cache_size and refcount_cache_size are ensured to have at least
|
||||
* their minimum values in qcow2_update_options_prepare() */
|
||||
|
||||
if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) ||
|
||||
*l2_cache_entry_size > s->cluster_size ||
|
||||
@@ -946,7 +948,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs,
|
||||
/* New interval for cache cleanup timer */
|
||||
r->cache_clean_interval =
|
||||
qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL,
|
||||
DEFAULT_CACHE_CLEAN_INTERVAL);
|
||||
s->cache_clean_interval);
|
||||
#ifndef CONFIG_LINUX
|
||||
if (r->cache_clean_interval != 0) {
|
||||
error_setg(errp, QCOW2_OPT_CACHE_CLEAN_INTERVAL
|
||||
@@ -1155,26 +1157,26 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
uint64_t ext_end;
|
||||
uint64_t l1_vm_state_index;
|
||||
bool update_header = false;
|
||||
bool header_updated = false;
|
||||
|
||||
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not read qcow2 header");
|
||||
goto fail;
|
||||
}
|
||||
header.magic = be32_to_cpu(header.magic);
|
||||
header.version = be32_to_cpu(header.version);
|
||||
header.backing_file_offset = be64_to_cpu(header.backing_file_offset);
|
||||
header.backing_file_size = be32_to_cpu(header.backing_file_size);
|
||||
header.size = be64_to_cpu(header.size);
|
||||
header.cluster_bits = be32_to_cpu(header.cluster_bits);
|
||||
header.crypt_method = be32_to_cpu(header.crypt_method);
|
||||
header.l1_table_offset = be64_to_cpu(header.l1_table_offset);
|
||||
header.l1_size = be32_to_cpu(header.l1_size);
|
||||
header.refcount_table_offset = be64_to_cpu(header.refcount_table_offset);
|
||||
header.refcount_table_clusters =
|
||||
be32_to_cpu(header.refcount_table_clusters);
|
||||
header.snapshots_offset = be64_to_cpu(header.snapshots_offset);
|
||||
header.nb_snapshots = be32_to_cpu(header.nb_snapshots);
|
||||
be32_to_cpus(&header.magic);
|
||||
be32_to_cpus(&header.version);
|
||||
be64_to_cpus(&header.backing_file_offset);
|
||||
be32_to_cpus(&header.backing_file_size);
|
||||
be64_to_cpus(&header.size);
|
||||
be32_to_cpus(&header.cluster_bits);
|
||||
be32_to_cpus(&header.crypt_method);
|
||||
be64_to_cpus(&header.l1_table_offset);
|
||||
be32_to_cpus(&header.l1_size);
|
||||
be64_to_cpus(&header.refcount_table_offset);
|
||||
be32_to_cpus(&header.refcount_table_clusters);
|
||||
be64_to_cpus(&header.snapshots_offset);
|
||||
be32_to_cpus(&header.nb_snapshots);
|
||||
|
||||
if (header.magic != QCOW_MAGIC) {
|
||||
error_setg(errp, "Image is not in qcow2 format");
|
||||
@@ -1210,12 +1212,11 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
header.refcount_order = 4;
|
||||
header.header_length = 72;
|
||||
} else {
|
||||
header.incompatible_features =
|
||||
be64_to_cpu(header.incompatible_features);
|
||||
header.compatible_features = be64_to_cpu(header.compatible_features);
|
||||
header.autoclear_features = be64_to_cpu(header.autoclear_features);
|
||||
header.refcount_order = be32_to_cpu(header.refcount_order);
|
||||
header.header_length = be32_to_cpu(header.header_length);
|
||||
be64_to_cpus(&header.incompatible_features);
|
||||
be64_to_cpus(&header.compatible_features);
|
||||
be64_to_cpus(&header.autoclear_features);
|
||||
be32_to_cpus(&header.refcount_order);
|
||||
be32_to_cpus(&header.header_length);
|
||||
|
||||
if (header.header_length < 104) {
|
||||
error_setg(errp, "qcow2 header too short");
|
||||
@@ -1327,7 +1328,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
/* 2^(s->refcount_order - 3) is the refcount width in bytes */
|
||||
s->refcount_block_bits = s->cluster_bits - (s->refcount_order - 3);
|
||||
s->refcount_block_size = 1 << s->refcount_block_bits;
|
||||
bs->total_sectors = header.size / BDRV_SECTOR_SIZE;
|
||||
bs->total_sectors = header.size / 512;
|
||||
s->csize_shift = (62 - (s->cluster_bits - 8));
|
||||
s->csize_mask = (1 << (s->cluster_bits - 8)) - 1;
|
||||
s->cluster_offset_mask = (1LL << s->csize_shift) - 1;
|
||||
@@ -1404,7 +1405,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
goto fail;
|
||||
}
|
||||
for(i = 0;i < s->l1_size; i++) {
|
||||
s->l1_table[i] = be64_to_cpu(s->l1_table[i]);
|
||||
be64_to_cpus(&s->l1_table[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1495,70 +1496,23 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
|
||||
s->autoclear_features &= QCOW2_AUTOCLEAR_MASK;
|
||||
}
|
||||
|
||||
/* == Handle persistent dirty bitmaps ==
|
||||
*
|
||||
* We want load dirty bitmaps in three cases:
|
||||
*
|
||||
* 1. Normal open of the disk in active mode, not related to invalidation
|
||||
* after migration.
|
||||
*
|
||||
* 2. Invalidation of the target vm after pre-copy phase of migration, if
|
||||
* bitmaps are _not_ migrating through migration channel, i.e.
|
||||
* 'dirty-bitmaps' capability is disabled.
|
||||
*
|
||||
* 3. Invalidation of source vm after failed or canceled migration.
|
||||
* This is a very interesting case. There are two possible types of
|
||||
* bitmaps:
|
||||
*
|
||||
* A. Stored on inactivation and removed. They should be loaded from the
|
||||
* image.
|
||||
*
|
||||
* B. Not stored: not-persistent bitmaps and bitmaps, migrated through
|
||||
* the migration channel (with dirty-bitmaps capability).
|
||||
*
|
||||
* On the other hand, there are two possible sub-cases:
|
||||
*
|
||||
* 3.1 disk was changed by somebody else while were inactive. In this
|
||||
* case all in-RAM dirty bitmaps (both persistent and not) are
|
||||
* definitely invalid. And we don't have any method to determine
|
||||
* this.
|
||||
*
|
||||
* Simple and safe thing is to just drop all the bitmaps of type B on
|
||||
* inactivation. But in this case we lose bitmaps in valid 4.2 case.
|
||||
*
|
||||
* On the other hand, resuming source vm, if disk was already changed
|
||||
* is a bad thing anyway: not only bitmaps, the whole vm state is
|
||||
* out of sync with disk.
|
||||
*
|
||||
* This means, that user or management tool, who for some reason
|
||||
* decided to resume source vm, after disk was already changed by
|
||||
* target vm, should at least drop all dirty bitmaps by hand.
|
||||
*
|
||||
* So, we can ignore this case for now, but TODO: "generation"
|
||||
* extension for qcow2, to determine, that image was changed after
|
||||
* last inactivation. And if it is changed, we will drop (or at least
|
||||
* mark as 'invalid' all the bitmaps of type B, both persistent
|
||||
* and not).
|
||||
*
|
||||
* 3.2 disk was _not_ changed while were inactive. Bitmaps may be saved
|
||||
* to disk ('dirty-bitmaps' capability disabled), or not saved
|
||||
* ('dirty-bitmaps' capability enabled), but we don't need to care
|
||||
* of: let's load bitmaps as always: stored bitmaps will be loaded,
|
||||
* and not stored has flag IN_USE=1 in the image and will be skipped
|
||||
* on loading.
|
||||
*
|
||||
* One remaining possible case when we don't want load bitmaps:
|
||||
*
|
||||
* 4. Open disk in inactive mode in target vm (bitmaps are migrating or
|
||||
* will be loaded on invalidation, no needs try loading them before)
|
||||
*/
|
||||
|
||||
if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) {
|
||||
/* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */
|
||||
bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err);
|
||||
|
||||
update_header = update_header && !header_updated;
|
||||
if (s->dirty_bitmaps_loaded) {
|
||||
/* It's some kind of reopen. There are no known cases where we need to
|
||||
* reload bitmaps in such a situation, so it's safer to skip them.
|
||||
*
|
||||
* Moreover, if we have some readonly bitmaps and we are reopening for
|
||||
* rw we should reopen bitmaps correspondingly.
|
||||
*/
|
||||
if (bdrv_has_readonly_bitmaps(bs) &&
|
||||
!bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE))
|
||||
{
|
||||
qcow2_reopen_bitmaps_rw_hint(bs, &header_updated, &local_err);
|
||||
}
|
||||
} else {
|
||||
header_updated = qcow2_load_dirty_bitmaps(bs, &local_err);
|
||||
s->dirty_bitmaps_loaded = true;
|
||||
}
|
||||
update_header = update_header && !header_updated;
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
ret = -EINVAL;
|
||||
@@ -1665,7 +1619,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* From bdrv_co_create. */
|
||||
qcow2_open_entry(&qoc);
|
||||
} else {
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc));
|
||||
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
|
||||
}
|
||||
@@ -1678,7 +1631,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
|
||||
if (bs->encrypted) {
|
||||
/* Encryption works on a sector granularity */
|
||||
bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto);
|
||||
bs->bl.request_alignment = BDRV_SECTOR_SIZE;
|
||||
}
|
||||
bs->bl.pwrite_zeroes_alignment = s->cluster_size;
|
||||
bs->bl.pdiscard_alignment = s->cluster_size;
|
||||
@@ -2174,9 +2127,9 @@ static int qcow2_inactivate(BlockDriverState *bs)
|
||||
qcow2_store_persistent_dirty_bitmaps(bs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
result = -EINVAL;
|
||||
error_reportf_err(local_err, "Lost persistent bitmaps during "
|
||||
"inactivation of node '%s': ",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
error_report_err(local_err);
|
||||
error_report("Persistent bitmaps are lost for node '%s'",
|
||||
bdrv_get_device_or_node_name(bs));
|
||||
}
|
||||
|
||||
ret = qcow2_cache_flush(bs, s->l2_table_cache);
|
||||
@@ -2259,8 +2212,8 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs,
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qobject_unref(options);
|
||||
if (local_err) {
|
||||
error_propagate_prepend(errp, local_err,
|
||||
"Could not reopen qcow2 layer: ");
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "Could not reopen qcow2 layer: ");
|
||||
bs->drv = NULL;
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
@@ -2397,13 +2350,13 @@ int qcow2_update_header(BlockDriverState *bs)
|
||||
|
||||
/* Full disk encryption header pointer extension */
|
||||
if (s->crypto_header.offset != 0) {
|
||||
s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset);
|
||||
s->crypto_header.length = cpu_to_be64(s->crypto_header.length);
|
||||
cpu_to_be64s(&s->crypto_header.offset);
|
||||
cpu_to_be64s(&s->crypto_header.length);
|
||||
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_CRYPTO_HEADER,
|
||||
&s->crypto_header, sizeof(s->crypto_header),
|
||||
buflen);
|
||||
s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset);
|
||||
s->crypto_header.length = be64_to_cpu(s->crypto_header.length);
|
||||
be64_to_cpus(&s->crypto_header.offset);
|
||||
be64_to_cpus(&s->crypto_header.length);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
@@ -2591,7 +2544,6 @@ static int qcow2_set_up_encryption(BlockDriverState *bs,
|
||||
static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t new_length)
|
||||
{
|
||||
BDRVQcow2State *s = bs->opaque;
|
||||
uint64_t bytes;
|
||||
uint64_t host_offset = 0;
|
||||
unsigned int cur_bytes;
|
||||
@@ -2602,7 +2554,7 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset,
|
||||
bytes = new_length - offset;
|
||||
|
||||
while (bytes) {
|
||||
cur_bytes = MIN(bytes, QEMU_ALIGN_DOWN(INT_MAX, s->cluster_size));
|
||||
cur_bytes = MIN(bytes, INT_MAX);
|
||||
ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes,
|
||||
&host_offset, &meta);
|
||||
if (ret < 0) {
|
||||
@@ -3470,7 +3422,6 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
uint64_t old_length;
|
||||
int64_t new_l1_size;
|
||||
int ret;
|
||||
QDict *options;
|
||||
|
||||
if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA &&
|
||||
prealloc != PREALLOC_MODE_FALLOC && prealloc != PREALLOC_MODE_FULL)
|
||||
@@ -3502,7 +3453,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
old_length = bs->total_sectors * BDRV_SECTOR_SIZE;
|
||||
old_length = bs->total_sectors * 512;
|
||||
new_l1_size = size_to_l1(s, offset);
|
||||
|
||||
if (offset < old_length) {
|
||||
@@ -3695,8 +3646,6 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
}
|
||||
}
|
||||
|
||||
bs->total_sectors = offset / BDRV_SECTOR_SIZE;
|
||||
|
||||
/* write updated header.size */
|
||||
offset = cpu_to_be64(offset);
|
||||
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size),
|
||||
@@ -3707,14 +3656,6 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
}
|
||||
|
||||
s->l1_vm_state_index = new_l1_size;
|
||||
|
||||
/* Update cache sizes */
|
||||
options = qdict_clone_shallow(bs->options);
|
||||
ret = qcow2_update_options(bs, options, s->flags, errp);
|
||||
qobject_unref(options);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
fail:
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
@@ -4718,7 +4659,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset,
|
||||
*node_name != '\0', node_name,
|
||||
message, offset >= 0, offset,
|
||||
size >= 0, size,
|
||||
fatal);
|
||||
fatal, &error_abort);
|
||||
g_free(message);
|
||||
|
||||
if (fatal) {
|
||||
|
||||
+8
-18
@@ -27,7 +27,6 @@
|
||||
|
||||
#include "crypto/block.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "qemu/units.h"
|
||||
|
||||
//#define DEBUG_ALLOC
|
||||
//#define DEBUG_ALLOC2
|
||||
@@ -42,19 +41,13 @@
|
||||
#define QCOW_MAX_CRYPT_CLUSTERS 32
|
||||
#define QCOW_MAX_SNAPSHOTS 65536
|
||||
|
||||
/* Field widths in qcow2 mean normal cluster offsets cannot reach
|
||||
* 64PB; depending on cluster size, compressed clusters can have a
|
||||
* smaller limit (64PB for up to 16k clusters, then ramps down to
|
||||
* 512TB for 2M clusters). */
|
||||
#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1)
|
||||
|
||||
/* 8 MB refcount table is enough for 2 PB images at 64k cluster size
|
||||
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||
#define QCOW_MAX_REFTABLE_SIZE S_8MiB
|
||||
#define QCOW_MAX_REFTABLE_SIZE 0x800000
|
||||
|
||||
/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
|
||||
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||
#define QCOW_MAX_L1_SIZE S_32MiB
|
||||
#define QCOW_MAX_L1_SIZE 0x2000000
|
||||
|
||||
/* Allow for an average of 1k per snapshot table entry, should be plenty of
|
||||
* space for snapshot names and IDs */
|
||||
@@ -80,16 +73,12 @@
|
||||
/* Must be at least 4 to cover all cases of refcount table growth */
|
||||
#define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */
|
||||
|
||||
#ifdef CONFIG_LINUX
|
||||
#define DEFAULT_L2_CACHE_MAX_SIZE S_32MiB
|
||||
#define DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */
|
||||
#else
|
||||
#define DEFAULT_L2_CACHE_MAX_SIZE S_8MiB
|
||||
/* Cache clean interval is currently available only on Linux, so must be 0 */
|
||||
#define DEFAULT_CACHE_CLEAN_INTERVAL 0
|
||||
#endif
|
||||
/* Whichever is more */
|
||||
#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
|
||||
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
|
||||
|
||||
#define DEFAULT_CLUSTER_SIZE 65536
|
||||
|
||||
#define DEFAULT_CLUSTER_SIZE S_64KiB
|
||||
|
||||
#define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts"
|
||||
#define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request"
|
||||
@@ -306,6 +295,7 @@ typedef struct BDRVQcow2State {
|
||||
uint32_t nb_bitmaps;
|
||||
uint64_t bitmap_directory_size;
|
||||
uint64_t bitmap_directory_offset;
|
||||
bool dirty_bitmaps_loaded;
|
||||
|
||||
int flags;
|
||||
int qcow_version;
|
||||
|
||||
+2
-3
@@ -559,7 +559,6 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
if (qemu_in_coroutine()) {
|
||||
bdrv_qed_open_entry(&qoc);
|
||||
} else {
|
||||
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
|
||||
qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc));
|
||||
BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS);
|
||||
}
|
||||
@@ -1607,8 +1606,8 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs,
|
||||
ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err);
|
||||
qemu_co_mutex_unlock(&s->table_lock);
|
||||
if (local_err) {
|
||||
error_propagate_prepend(errp, local_err,
|
||||
"Could not reopen qed layer: ");
|
||||
error_propagate(errp, local_err);
|
||||
error_prepend(errp, "Could not reopen qed layer: ");
|
||||
return;
|
||||
} else if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "Could not reopen qed layer");
|
||||
|
||||
+28
-21
@@ -199,7 +199,7 @@ static void quorum_report_bad(QuorumOpType type, uint64_t offset,
|
||||
}
|
||||
|
||||
qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector,
|
||||
end_sector - start_sector);
|
||||
end_sector - start_sector, &error_abort);
|
||||
}
|
||||
|
||||
static void quorum_report_failure(QuorumAIOCB *acb)
|
||||
@@ -210,7 +210,7 @@ static void quorum_report_failure(QuorumAIOCB *acb)
|
||||
BDRV_SECTOR_SIZE);
|
||||
|
||||
qapi_event_send_quorum_failure(reference, start_sector,
|
||||
end_sector - start_sector);
|
||||
end_sector - start_sector, &error_abort);
|
||||
}
|
||||
|
||||
static int quorum_vote_error(QuorumAIOCB *acb);
|
||||
@@ -437,7 +437,23 @@ static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b)
|
||||
static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 " ",
|
||||
acb->offset, acb->bytes);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static bool quorum_compare(QuorumAIOCB *acb,
|
||||
QEMUIOVector *a,
|
||||
QEMUIOVector *b)
|
||||
{
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
ssize_t offset;
|
||||
@@ -446,10 +462,8 @@ static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b)
|
||||
if (s->is_blkverify) {
|
||||
offset = qemu_iovec_compare(a, b);
|
||||
if (offset != -1) {
|
||||
fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64
|
||||
" contents mismatch at offset %" PRIu64 "\n",
|
||||
acb->offset, acb->bytes, acb->offset + offset);
|
||||
exit(1);
|
||||
quorum_err(acb, "contents mismatch at offset %" PRIu64,
|
||||
acb->offset + offset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -912,12 +926,13 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
s->read_pattern = ret;
|
||||
|
||||
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
|
||||
s->is_blkverify = qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false);
|
||||
if (s->is_blkverify && (s->num_children != 2 || s->threshold != 2)) {
|
||||
error_setg(&local_err, "blkverify=on can only be set if there are "
|
||||
"exactly two files and vote-threshold is 2");
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
/* is the driver in blkverify mode */
|
||||
if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
|
||||
s->num_children == 2 && s->threshold == 2) {
|
||||
s->is_blkverify = true;
|
||||
} else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
|
||||
fprintf(stderr, "blkverify mode is set by setting blkverify=on "
|
||||
"and using two files with vote_threshold=2\n");
|
||||
}
|
||||
|
||||
s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE,
|
||||
@@ -992,11 +1007,6 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs,
|
||||
char indexstr[32];
|
||||
int ret;
|
||||
|
||||
if (s->is_blkverify) {
|
||||
error_setg(errp, "Cannot add a child to a quorum in blkverify mode");
|
||||
return;
|
||||
}
|
||||
|
||||
assert(s->num_children <= INT_MAX / sizeof(BdrvChild *));
|
||||
if (s->num_children == INT_MAX / sizeof(BdrvChild *) ||
|
||||
s->next_child_index == UINT_MAX) {
|
||||
@@ -1051,9 +1061,6 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child,
|
||||
return;
|
||||
}
|
||||
|
||||
/* We know now that num_children > threshold, so blkverify must be false */
|
||||
assert(!s->is_blkverify);
|
||||
|
||||
bdrv_drained_begin(bs);
|
||||
|
||||
/* We can safely remove this child now */
|
||||
|
||||
@@ -459,6 +459,10 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void raw_close(BlockDriverState *bs)
|
||||
{
|
||||
}
|
||||
|
||||
static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||
{
|
||||
/* smallest possible positive score so that raw is used if and only if no
|
||||
@@ -539,6 +543,7 @@ BlockDriver bdrv_raw = {
|
||||
.bdrv_reopen_commit = &raw_reopen_commit,
|
||||
.bdrv_reopen_abort = &raw_reopen_abort,
|
||||
.bdrv_open = &raw_open,
|
||||
.bdrv_close = &raw_close,
|
||||
.bdrv_child_perm = bdrv_filter_default_perms,
|
||||
.bdrv_co_create_opts = &raw_co_create_opts,
|
||||
.bdrv_co_preadv = &raw_co_preadv,
|
||||
|
||||
+12
-6
@@ -750,8 +750,8 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* Take care whenever deciding to actually deprecate; once this ability
|
||||
* is removed, we will not be able to open any images with legacy-styled
|
||||
* backing image strings. */
|
||||
warn_report("RBD options encoded in the filename as keyvalue pairs "
|
||||
"is deprecated");
|
||||
error_report("RBD options encoded in the filename as keyvalue pairs "
|
||||
"is deprecated");
|
||||
}
|
||||
|
||||
/* Remove the processed options from the QDict (the visitor processes
|
||||
@@ -780,10 +780,16 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* If we are using an rbd snapshot, we must be r/o, otherwise
|
||||
* leave as-is */
|
||||
if (s->snap != NULL) {
|
||||
r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp);
|
||||
if (r < 0) {
|
||||
rbd_close(s->image);
|
||||
goto failed_open;
|
||||
if (!bdrv_is_read_only(bs)) {
|
||||
error_report("Opening rbd snapshots without an explicit "
|
||||
"read-only=on option is deprecated. Future versions "
|
||||
"will refuse to open the image instead of "
|
||||
"automatically marking the image read-only.");
|
||||
r = bdrv_set_read_only(bs, true, &local_err);
|
||||
if (r < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto failed_open;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -572,7 +572,7 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp)
|
||||
if (s->addr->type == SOCKET_ADDRESS_TYPE_INET && fd >= 0) {
|
||||
int ret = socket_set_nodelay(fd);
|
||||
if (ret < 0) {
|
||||
warn_report("can't set TCP_NODELAY: %s", strerror(errno));
|
||||
error_report("%s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -218,9 +218,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
||||
qobject_unref(file_options);
|
||||
qdict_put_str(options, "file", bdrv_get_node_name(file));
|
||||
|
||||
if (drv->bdrv_close) {
|
||||
drv->bdrv_close(bs);
|
||||
}
|
||||
drv->bdrv_close(bs);
|
||||
bdrv_unref_child(bs, bs->file);
|
||||
bs->file = NULL;
|
||||
|
||||
|
||||
+22
-23
@@ -54,16 +54,20 @@ static int coroutine_fn stream_populate(BlockBackend *blk,
|
||||
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
|
||||
}
|
||||
|
||||
static int stream_prepare(Job *job)
|
||||
typedef struct {
|
||||
int ret;
|
||||
} StreamCompleteData;
|
||||
|
||||
static void stream_complete(Job *job, void *opaque)
|
||||
{
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
||||
BlockJob *bjob = &s->common;
|
||||
StreamCompleteData *data = opaque;
|
||||
BlockDriverState *bs = blk_bs(bjob->blk);
|
||||
BlockDriverState *base = s->base;
|
||||
Error *local_err = NULL;
|
||||
int ret = 0;
|
||||
|
||||
if (bs->backing) {
|
||||
if (!job_is_cancelled(job) && bs->backing && data->ret == 0) {
|
||||
const char *base_id = NULL, *base_fmt = NULL;
|
||||
if (base) {
|
||||
base_id = s->backing_file_str;
|
||||
@@ -71,23 +75,16 @@ static int stream_prepare(Job *job)
|
||||
base_fmt = base->drv->format_name;
|
||||
}
|
||||
}
|
||||
ret = bdrv_change_backing_file(bs, base_id, base_fmt);
|
||||
data->ret = bdrv_change_backing_file(bs, base_id, base_fmt);
|
||||
bdrv_set_backing_hd(bs, base, &local_err);
|
||||
if (local_err) {
|
||||
error_report_err(local_err);
|
||||
return -EPERM;
|
||||
data->ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stream_clean(Job *job)
|
||||
{
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
||||
BlockJob *bjob = &s->common;
|
||||
BlockDriverState *bs = blk_bs(bjob->blk);
|
||||
|
||||
out:
|
||||
/* Reopen the image back in read-only mode if necessary */
|
||||
if (s->bs_flags != bdrv_get_flags(bs)) {
|
||||
/* Give up write permissions before making it read-only */
|
||||
@@ -96,11 +93,14 @@ static void stream_clean(Job *job)
|
||||
}
|
||||
|
||||
g_free(s->backing_file_str);
|
||||
job_completed(job, data->ret, NULL);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static int coroutine_fn stream_run(Job *job, Error **errp)
|
||||
static void coroutine_fn stream_run(void *opaque)
|
||||
{
|
||||
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
|
||||
StreamBlockJob *s = opaque;
|
||||
StreamCompleteData *data;
|
||||
BlockBackend *blk = s->common.blk;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
BlockDriverState *base = s->base;
|
||||
@@ -203,7 +203,9 @@ static int coroutine_fn stream_run(Job *job, Error **errp)
|
||||
|
||||
out:
|
||||
/* Modify backing chain and close BDSes in main loop */
|
||||
return ret;
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
job_defer_to_main_loop(&s->common.job, stream_complete, data);
|
||||
}
|
||||
|
||||
static const BlockJobDriver stream_job_driver = {
|
||||
@@ -211,9 +213,7 @@ static const BlockJobDriver stream_job_driver = {
|
||||
.instance_size = sizeof(StreamBlockJob),
|
||||
.job_type = JOB_TYPE_STREAM,
|
||||
.free = block_job_free,
|
||||
.run = stream_run,
|
||||
.prepare = stream_prepare,
|
||||
.clean = stream_clean,
|
||||
.start = stream_run,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
},
|
||||
@@ -221,8 +221,7 @@ static const BlockJobDriver stream_job_driver = {
|
||||
|
||||
void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, const char *backing_file_str,
|
||||
int creation_flags, int64_t speed,
|
||||
BlockdevOnError on_error, Error **errp)
|
||||
int64_t speed, BlockdevOnError on_error, Error **errp)
|
||||
{
|
||||
StreamBlockJob *s;
|
||||
BlockDriverState *iter;
|
||||
@@ -244,7 +243,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
BLK_PERM_GRAPH_MOD,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_WRITE,
|
||||
speed, creation_flags, NULL, NULL, errp);
|
||||
speed, JOB_DEFAULT, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
+10
-31
@@ -36,7 +36,6 @@
|
||||
|
||||
static void throttle_group_obj_init(Object *obj);
|
||||
static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
|
||||
static void timer_cb(ThrottleGroupMember *tgm, bool is_write);
|
||||
|
||||
/* The ThrottleGroup structure (with its ThrottleState) is shared
|
||||
* among different ThrottleGroupMembers and it's independent from
|
||||
@@ -222,15 +221,6 @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm,
|
||||
ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
|
||||
ThrottleGroupMember *token, *start;
|
||||
|
||||
/* If this member has its I/O limits disabled then it means that
|
||||
* it's being drained. Skip the round-robin search and return tgm
|
||||
* immediately if it has pending requests. Otherwise we could be
|
||||
* forcing it to wait for other member's throttled requests. */
|
||||
if (tgm_has_pending_reqs(tgm, is_write) &&
|
||||
atomic_read(&tgm->io_limits_disabled)) {
|
||||
return tgm;
|
||||
}
|
||||
|
||||
start = token = tg->tokens[is_write];
|
||||
|
||||
/* get next bs round in round robin style */
|
||||
@@ -425,31 +415,15 @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write
|
||||
rd->tgm = tgm;
|
||||
rd->is_write = is_write;
|
||||
|
||||
/* This function is called when a timer is fired or when
|
||||
* throttle_group_restart_tgm() is called. Either way, there can
|
||||
* be no timer pending on this tgm at this point */
|
||||
assert(!timer_pending(tgm->throttle_timers.timers[is_write]));
|
||||
|
||||
co = qemu_coroutine_create(throttle_group_restart_queue_entry, rd);
|
||||
aio_co_enter(tgm->aio_context, co);
|
||||
}
|
||||
|
||||
void throttle_group_restart_tgm(ThrottleGroupMember *tgm)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (tgm->throttle_state) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
QEMUTimer *t = tgm->throttle_timers.timers[i];
|
||||
if (timer_pending(t)) {
|
||||
/* If there's a pending timer on this tgm, fire it now */
|
||||
timer_del(t);
|
||||
timer_cb(tgm, i);
|
||||
} else {
|
||||
/* Else run the next request from the queue manually */
|
||||
throttle_group_restart_queue(tgm, i);
|
||||
}
|
||||
}
|
||||
throttle_group_restart_queue(tgm, 0);
|
||||
throttle_group_restart_queue(tgm, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,11 +558,16 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
|
||||
return;
|
||||
}
|
||||
|
||||
assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0);
|
||||
assert(qemu_co_queue_empty(&tgm->throttled_reqs[0]));
|
||||
assert(qemu_co_queue_empty(&tgm->throttled_reqs[1]));
|
||||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
for (i = 0; i < 2; i++) {
|
||||
assert(tgm->pending_reqs[i] == 0);
|
||||
assert(qemu_co_queue_empty(&tgm->throttled_reqs[i]));
|
||||
assert(!timer_pending(tgm->throttle_timers.timers[i]));
|
||||
if (timer_pending(tgm->throttle_timers.timers[i])) {
|
||||
tg->any_timer_armed[i] = false;
|
||||
schedule_next_request(tgm, i);
|
||||
}
|
||||
if (tg->tokens[i] == tgm) {
|
||||
token = throttle_group_next_tgm(tgm);
|
||||
/* Take care of the case where this is the last tgm in the group */
|
||||
|
||||
+34
-34
@@ -85,7 +85,7 @@
|
||||
#define BLOCK_OPT_STATIC "static"
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define DEFAULT_CLUSTER_SIZE S_1MiB
|
||||
#define DEFAULT_CLUSTER_SIZE (1 * MiB)
|
||||
|
||||
#if defined(CONFIG_VDI_DEBUG)
|
||||
#define VDI_DEBUG 1
|
||||
@@ -187,22 +187,22 @@ typedef struct {
|
||||
|
||||
static void vdi_header_to_cpu(VdiHeader *header)
|
||||
{
|
||||
header->signature = le32_to_cpu(header->signature);
|
||||
header->version = le32_to_cpu(header->version);
|
||||
header->header_size = le32_to_cpu(header->header_size);
|
||||
header->image_type = le32_to_cpu(header->image_type);
|
||||
header->image_flags = le32_to_cpu(header->image_flags);
|
||||
header->offset_bmap = le32_to_cpu(header->offset_bmap);
|
||||
header->offset_data = le32_to_cpu(header->offset_data);
|
||||
header->cylinders = le32_to_cpu(header->cylinders);
|
||||
header->heads = le32_to_cpu(header->heads);
|
||||
header->sectors = le32_to_cpu(header->sectors);
|
||||
header->sector_size = le32_to_cpu(header->sector_size);
|
||||
header->disk_size = le64_to_cpu(header->disk_size);
|
||||
header->block_size = le32_to_cpu(header->block_size);
|
||||
header->block_extra = le32_to_cpu(header->block_extra);
|
||||
header->blocks_in_image = le32_to_cpu(header->blocks_in_image);
|
||||
header->blocks_allocated = le32_to_cpu(header->blocks_allocated);
|
||||
le32_to_cpus(&header->signature);
|
||||
le32_to_cpus(&header->version);
|
||||
le32_to_cpus(&header->header_size);
|
||||
le32_to_cpus(&header->image_type);
|
||||
le32_to_cpus(&header->image_flags);
|
||||
le32_to_cpus(&header->offset_bmap);
|
||||
le32_to_cpus(&header->offset_data);
|
||||
le32_to_cpus(&header->cylinders);
|
||||
le32_to_cpus(&header->heads);
|
||||
le32_to_cpus(&header->sectors);
|
||||
le32_to_cpus(&header->sector_size);
|
||||
le64_to_cpus(&header->disk_size);
|
||||
le32_to_cpus(&header->block_size);
|
||||
le32_to_cpus(&header->block_extra);
|
||||
le32_to_cpus(&header->blocks_in_image);
|
||||
le32_to_cpus(&header->blocks_allocated);
|
||||
qemu_uuid_bswap(&header->uuid_image);
|
||||
qemu_uuid_bswap(&header->uuid_last_snap);
|
||||
qemu_uuid_bswap(&header->uuid_link);
|
||||
@@ -211,22 +211,22 @@ static void vdi_header_to_cpu(VdiHeader *header)
|
||||
|
||||
static void vdi_header_to_le(VdiHeader *header)
|
||||
{
|
||||
header->signature = cpu_to_le32(header->signature);
|
||||
header->version = cpu_to_le32(header->version);
|
||||
header->header_size = cpu_to_le32(header->header_size);
|
||||
header->image_type = cpu_to_le32(header->image_type);
|
||||
header->image_flags = cpu_to_le32(header->image_flags);
|
||||
header->offset_bmap = cpu_to_le32(header->offset_bmap);
|
||||
header->offset_data = cpu_to_le32(header->offset_data);
|
||||
header->cylinders = cpu_to_le32(header->cylinders);
|
||||
header->heads = cpu_to_le32(header->heads);
|
||||
header->sectors = cpu_to_le32(header->sectors);
|
||||
header->sector_size = cpu_to_le32(header->sector_size);
|
||||
header->disk_size = cpu_to_le64(header->disk_size);
|
||||
header->block_size = cpu_to_le32(header->block_size);
|
||||
header->block_extra = cpu_to_le32(header->block_extra);
|
||||
header->blocks_in_image = cpu_to_le32(header->blocks_in_image);
|
||||
header->blocks_allocated = cpu_to_le32(header->blocks_allocated);
|
||||
cpu_to_le32s(&header->signature);
|
||||
cpu_to_le32s(&header->version);
|
||||
cpu_to_le32s(&header->header_size);
|
||||
cpu_to_le32s(&header->image_type);
|
||||
cpu_to_le32s(&header->image_flags);
|
||||
cpu_to_le32s(&header->offset_bmap);
|
||||
cpu_to_le32s(&header->offset_data);
|
||||
cpu_to_le32s(&header->cylinders);
|
||||
cpu_to_le32s(&header->heads);
|
||||
cpu_to_le32s(&header->sectors);
|
||||
cpu_to_le32s(&header->sector_size);
|
||||
cpu_to_le64s(&header->disk_size);
|
||||
cpu_to_le32s(&header->block_size);
|
||||
cpu_to_le32s(&header->block_extra);
|
||||
cpu_to_le32s(&header->blocks_in_image);
|
||||
cpu_to_le32s(&header->blocks_allocated);
|
||||
qemu_uuid_bswap(&header->uuid_image);
|
||||
qemu_uuid_bswap(&header->uuid_last_snap);
|
||||
qemu_uuid_bswap(&header->uuid_link);
|
||||
@@ -432,7 +432,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
goto fail;
|
||||
} else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
|
||||
error_setg(errp, "unsupported VDI image (block size %" PRIu32
|
||||
" is not %" PRIu32 ")",
|
||||
" is not %" PRIu64 ")",
|
||||
header.block_size, DEFAULT_CLUSTER_SIZE);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
|
||||
+59
-59
@@ -35,18 +35,18 @@ void vhdx_header_le_import(VHDXHeader *h)
|
||||
{
|
||||
assert(h != NULL);
|
||||
|
||||
h->signature = le32_to_cpu(h->signature);
|
||||
h->checksum = le32_to_cpu(h->checksum);
|
||||
h->sequence_number = le64_to_cpu(h->sequence_number);
|
||||
le32_to_cpus(&h->signature);
|
||||
le32_to_cpus(&h->checksum);
|
||||
le64_to_cpus(&h->sequence_number);
|
||||
|
||||
leguid_to_cpus(&h->file_write_guid);
|
||||
leguid_to_cpus(&h->data_write_guid);
|
||||
leguid_to_cpus(&h->log_guid);
|
||||
|
||||
h->log_version = le16_to_cpu(h->log_version);
|
||||
h->version = le16_to_cpu(h->version);
|
||||
h->log_length = le32_to_cpu(h->log_length);
|
||||
h->log_offset = le64_to_cpu(h->log_offset);
|
||||
le16_to_cpus(&h->log_version);
|
||||
le16_to_cpus(&h->version);
|
||||
le32_to_cpus(&h->log_length);
|
||||
le64_to_cpus(&h->log_offset);
|
||||
}
|
||||
|
||||
void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h)
|
||||
@@ -80,68 +80,68 @@ void vhdx_log_desc_le_import(VHDXLogDescriptor *d)
|
||||
{
|
||||
assert(d != NULL);
|
||||
|
||||
d->signature = le32_to_cpu(d->signature);
|
||||
d->file_offset = le64_to_cpu(d->file_offset);
|
||||
d->sequence_number = le64_to_cpu(d->sequence_number);
|
||||
le32_to_cpus(&d->signature);
|
||||
le64_to_cpus(&d->file_offset);
|
||||
le64_to_cpus(&d->sequence_number);
|
||||
}
|
||||
|
||||
void vhdx_log_desc_le_export(VHDXLogDescriptor *d)
|
||||
{
|
||||
assert(d != NULL);
|
||||
|
||||
d->signature = cpu_to_le32(d->signature);
|
||||
d->trailing_bytes = cpu_to_le32(d->trailing_bytes);
|
||||
d->leading_bytes = cpu_to_le64(d->leading_bytes);
|
||||
d->file_offset = cpu_to_le64(d->file_offset);
|
||||
d->sequence_number = cpu_to_le64(d->sequence_number);
|
||||
cpu_to_le32s(&d->signature);
|
||||
cpu_to_le32s(&d->trailing_bytes);
|
||||
cpu_to_le64s(&d->leading_bytes);
|
||||
cpu_to_le64s(&d->file_offset);
|
||||
cpu_to_le64s(&d->sequence_number);
|
||||
}
|
||||
|
||||
void vhdx_log_data_le_import(VHDXLogDataSector *d)
|
||||
{
|
||||
assert(d != NULL);
|
||||
|
||||
d->data_signature = le32_to_cpu(d->data_signature);
|
||||
d->sequence_high = le32_to_cpu(d->sequence_high);
|
||||
d->sequence_low = le32_to_cpu(d->sequence_low);
|
||||
le32_to_cpus(&d->data_signature);
|
||||
le32_to_cpus(&d->sequence_high);
|
||||
le32_to_cpus(&d->sequence_low);
|
||||
}
|
||||
|
||||
void vhdx_log_data_le_export(VHDXLogDataSector *d)
|
||||
{
|
||||
assert(d != NULL);
|
||||
|
||||
d->data_signature = cpu_to_le32(d->data_signature);
|
||||
d->sequence_high = cpu_to_le32(d->sequence_high);
|
||||
d->sequence_low = cpu_to_le32(d->sequence_low);
|
||||
cpu_to_le32s(&d->data_signature);
|
||||
cpu_to_le32s(&d->sequence_high);
|
||||
cpu_to_le32s(&d->sequence_low);
|
||||
}
|
||||
|
||||
void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr)
|
||||
{
|
||||
assert(hdr != NULL);
|
||||
|
||||
hdr->signature = le32_to_cpu(hdr->signature);
|
||||
hdr->checksum = le32_to_cpu(hdr->checksum);
|
||||
hdr->entry_length = le32_to_cpu(hdr->entry_length);
|
||||
hdr->tail = le32_to_cpu(hdr->tail);
|
||||
hdr->sequence_number = le64_to_cpu(hdr->sequence_number);
|
||||
hdr->descriptor_count = le32_to_cpu(hdr->descriptor_count);
|
||||
le32_to_cpus(&hdr->signature);
|
||||
le32_to_cpus(&hdr->checksum);
|
||||
le32_to_cpus(&hdr->entry_length);
|
||||
le32_to_cpus(&hdr->tail);
|
||||
le64_to_cpus(&hdr->sequence_number);
|
||||
le32_to_cpus(&hdr->descriptor_count);
|
||||
leguid_to_cpus(&hdr->log_guid);
|
||||
hdr->flushed_file_offset = le64_to_cpu(hdr->flushed_file_offset);
|
||||
hdr->last_file_offset = le64_to_cpu(hdr->last_file_offset);
|
||||
le64_to_cpus(&hdr->flushed_file_offset);
|
||||
le64_to_cpus(&hdr->last_file_offset);
|
||||
}
|
||||
|
||||
void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr)
|
||||
{
|
||||
assert(hdr != NULL);
|
||||
|
||||
hdr->signature = cpu_to_le32(hdr->signature);
|
||||
hdr->checksum = cpu_to_le32(hdr->checksum);
|
||||
hdr->entry_length = cpu_to_le32(hdr->entry_length);
|
||||
hdr->tail = cpu_to_le32(hdr->tail);
|
||||
hdr->sequence_number = cpu_to_le64(hdr->sequence_number);
|
||||
hdr->descriptor_count = cpu_to_le32(hdr->descriptor_count);
|
||||
cpu_to_le32s(&hdr->signature);
|
||||
cpu_to_le32s(&hdr->checksum);
|
||||
cpu_to_le32s(&hdr->entry_length);
|
||||
cpu_to_le32s(&hdr->tail);
|
||||
cpu_to_le64s(&hdr->sequence_number);
|
||||
cpu_to_le32s(&hdr->descriptor_count);
|
||||
cpu_to_leguids(&hdr->log_guid);
|
||||
hdr->flushed_file_offset = cpu_to_le64(hdr->flushed_file_offset);
|
||||
hdr->last_file_offset = cpu_to_le64(hdr->last_file_offset);
|
||||
cpu_to_le64s(&hdr->flushed_file_offset);
|
||||
cpu_to_le64s(&hdr->last_file_offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -150,18 +150,18 @@ void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr)
|
||||
{
|
||||
assert(hdr != NULL);
|
||||
|
||||
hdr->signature = le32_to_cpu(hdr->signature);
|
||||
hdr->checksum = le32_to_cpu(hdr->checksum);
|
||||
hdr->entry_count = le32_to_cpu(hdr->entry_count);
|
||||
le32_to_cpus(&hdr->signature);
|
||||
le32_to_cpus(&hdr->checksum);
|
||||
le32_to_cpus(&hdr->entry_count);
|
||||
}
|
||||
|
||||
void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr)
|
||||
{
|
||||
assert(hdr != NULL);
|
||||
|
||||
hdr->signature = cpu_to_le32(hdr->signature);
|
||||
hdr->checksum = cpu_to_le32(hdr->checksum);
|
||||
hdr->entry_count = cpu_to_le32(hdr->entry_count);
|
||||
cpu_to_le32s(&hdr->signature);
|
||||
cpu_to_le32s(&hdr->checksum);
|
||||
cpu_to_le32s(&hdr->entry_count);
|
||||
}
|
||||
|
||||
void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
|
||||
@@ -169,9 +169,9 @@ void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
|
||||
assert(e != NULL);
|
||||
|
||||
leguid_to_cpus(&e->guid);
|
||||
e->file_offset = le64_to_cpu(e->file_offset);
|
||||
e->length = le32_to_cpu(e->length);
|
||||
e->data_bits = le32_to_cpu(e->data_bits);
|
||||
le64_to_cpus(&e->file_offset);
|
||||
le32_to_cpus(&e->length);
|
||||
le32_to_cpus(&e->data_bits);
|
||||
}
|
||||
|
||||
void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
|
||||
@@ -179,9 +179,9 @@ void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
|
||||
assert(e != NULL);
|
||||
|
||||
cpu_to_leguids(&e->guid);
|
||||
e->file_offset = cpu_to_le64(e->file_offset);
|
||||
e->length = cpu_to_le32(e->length);
|
||||
e->data_bits = cpu_to_le32(e->data_bits);
|
||||
cpu_to_le64s(&e->file_offset);
|
||||
cpu_to_le32s(&e->length);
|
||||
cpu_to_le32s(&e->data_bits);
|
||||
}
|
||||
|
||||
|
||||
@@ -190,16 +190,16 @@ void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr)
|
||||
{
|
||||
assert(hdr != NULL);
|
||||
|
||||
hdr->signature = le64_to_cpu(hdr->signature);
|
||||
hdr->entry_count = le16_to_cpu(hdr->entry_count);
|
||||
le64_to_cpus(&hdr->signature);
|
||||
le16_to_cpus(&hdr->entry_count);
|
||||
}
|
||||
|
||||
void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr)
|
||||
{
|
||||
assert(hdr != NULL);
|
||||
|
||||
hdr->signature = cpu_to_le64(hdr->signature);
|
||||
hdr->entry_count = cpu_to_le16(hdr->entry_count);
|
||||
cpu_to_le64s(&hdr->signature);
|
||||
cpu_to_le16s(&hdr->entry_count);
|
||||
}
|
||||
|
||||
void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
|
||||
@@ -207,16 +207,16 @@ void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
|
||||
assert(e != NULL);
|
||||
|
||||
leguid_to_cpus(&e->item_id);
|
||||
e->offset = le32_to_cpu(e->offset);
|
||||
e->length = le32_to_cpu(e->length);
|
||||
e->data_bits = le32_to_cpu(e->data_bits);
|
||||
le32_to_cpus(&e->offset);
|
||||
le32_to_cpus(&e->length);
|
||||
le32_to_cpus(&e->data_bits);
|
||||
}
|
||||
void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e)
|
||||
{
|
||||
assert(e != NULL);
|
||||
|
||||
cpu_to_leguids(&e->item_id);
|
||||
e->offset = cpu_to_le32(e->offset);
|
||||
e->length = cpu_to_le32(e->length);
|
||||
e->data_bits = cpu_to_le32(e->data_bits);
|
||||
cpu_to_le32s(&e->offset);
|
||||
cpu_to_le32s(&e->length);
|
||||
cpu_to_le32s(&e->data_bits);
|
||||
}
|
||||
+2
-2
@@ -835,11 +835,11 @@ static void vhdx_log_raw_to_le_sector(VHDXLogDescriptor *desc,
|
||||
/* 8 + 4084 + 4 = 4096, 1 log sector */
|
||||
memcpy(&desc->leading_bytes, data, 8);
|
||||
data += 8;
|
||||
desc->leading_bytes = cpu_to_le64(desc->leading_bytes);
|
||||
cpu_to_le64s(&desc->leading_bytes);
|
||||
memcpy(sector->data, data, 4084);
|
||||
data += 4084;
|
||||
memcpy(&desc->trailing_bytes, data, 4);
|
||||
desc->trailing_bytes = cpu_to_le32(desc->trailing_bytes);
|
||||
cpu_to_le32s(&desc->trailing_bytes);
|
||||
data += 4;
|
||||
|
||||
sector->sequence_high = (uint32_t) (seq >> 32);
|
||||
|
||||
+9
-9
@@ -156,7 +156,7 @@ uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset)
|
||||
|
||||
memset(buf + crc_offset, 0, sizeof(crc));
|
||||
crc = crc32c(0xffffffff, buf, size);
|
||||
crc = cpu_to_le32(crc);
|
||||
cpu_to_le32s(&crc);
|
||||
memcpy(buf + crc_offset, &crc, sizeof(crc));
|
||||
|
||||
return crc;
|
||||
@@ -753,8 +753,8 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s->params.block_size = le32_to_cpu(s->params.block_size);
|
||||
s->params.data_bits = le32_to_cpu(s->params.data_bits);
|
||||
le32_to_cpus(&s->params.block_size);
|
||||
le32_to_cpus(&s->params.data_bits);
|
||||
|
||||
|
||||
/* We now have the file parameters, so we can tell if this is a
|
||||
@@ -803,9 +803,9 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
s->virtual_disk_size = le64_to_cpu(s->virtual_disk_size);
|
||||
s->logical_sector_size = le32_to_cpu(s->logical_sector_size);
|
||||
s->physical_sector_size = le32_to_cpu(s->physical_sector_size);
|
||||
le64_to_cpus(&s->virtual_disk_size);
|
||||
le32_to_cpus(&s->logical_sector_size);
|
||||
le32_to_cpus(&s->physical_sector_size);
|
||||
|
||||
if (s->params.block_size < VHDX_BLOCK_SIZE_MIN ||
|
||||
s->params.block_size > VHDX_BLOCK_SIZE_MAX) {
|
||||
@@ -985,7 +985,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
/* endian convert, and verify populated BAT field file offsets against
|
||||
* region table and log entries */
|
||||
for (i = 0; i < s->bat_entries; i++) {
|
||||
s->bat[i] = le64_to_cpu(s->bat[i]);
|
||||
le64_to_cpus(&s->bat[i]);
|
||||
if (payblocks--) {
|
||||
/* payload bat entries */
|
||||
if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
|
||||
@@ -1509,7 +1509,7 @@ static int vhdx_create_new_metadata(BlockBackend *blk,
|
||||
mt_file_params->block_size = cpu_to_le32(block_size);
|
||||
if (type == VHDX_TYPE_FIXED) {
|
||||
mt_file_params->data_bits |= VHDX_PARAMS_LEAVE_BLOCKS_ALLOCED;
|
||||
mt_file_params->data_bits = cpu_to_le32(mt_file_params->data_bits);
|
||||
cpu_to_le32s(&mt_file_params->data_bits);
|
||||
}
|
||||
|
||||
vhdx_guid_generate(&mt_page83->page_83_data);
|
||||
@@ -1656,7 +1656,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
|
||||
sinfo.file_offset = ROUND_UP(sinfo.file_offset, MiB);
|
||||
vhdx_update_bat_table_entry(blk_bs(blk), s, &sinfo, &unused, &unused,
|
||||
block_state);
|
||||
s->bat[sinfo.bat_idx] = cpu_to_le64(s->bat[sinfo.bat_idx]);
|
||||
cpu_to_le64s(&s->bat[sinfo.bat_idx]);
|
||||
sector_num += s->sectors_per_block;
|
||||
}
|
||||
ret = blk_pwrite(blk, file_offset, s->bat, length, 0);
|
||||
|
||||
+6
-6
@@ -420,16 +420,16 @@ int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s,
|
||||
|
||||
static inline void leguid_to_cpus(MSGUID *guid)
|
||||
{
|
||||
guid->data1 = le32_to_cpu(guid->data1);
|
||||
guid->data2 = le16_to_cpu(guid->data2);
|
||||
guid->data3 = le16_to_cpu(guid->data3);
|
||||
le32_to_cpus(&guid->data1);
|
||||
le16_to_cpus(&guid->data2);
|
||||
le16_to_cpus(&guid->data3);
|
||||
}
|
||||
|
||||
static inline void cpu_to_leguids(MSGUID *guid)
|
||||
{
|
||||
guid->data1 = cpu_to_le32(guid->data1);
|
||||
guid->data2 = cpu_to_le16(guid->data2);
|
||||
guid->data3 = cpu_to_le16(guid->data3);
|
||||
cpu_to_le32s(&guid->data1);
|
||||
cpu_to_le16s(&guid->data2);
|
||||
cpu_to_le16s(&guid->data3);
|
||||
}
|
||||
|
||||
void vhdx_header_le_import(VHDXHeader *h);
|
||||
|
||||
@@ -1698,27 +1698,6 @@ static int coroutine_fn
|
||||
vmdk_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov)
|
||||
{
|
||||
if (bytes == 0) {
|
||||
/* The caller will write bytes 0 to signal EOF.
|
||||
* When receive it, we align EOF to a sector boundary. */
|
||||
BDRVVmdkState *s = bs->opaque;
|
||||
int i, ret;
|
||||
int64_t length;
|
||||
|
||||
for (i = 0; i < s->num_extents; i++) {
|
||||
length = bdrv_getlength(s->extents[i].file->bs);
|
||||
if (length < 0) {
|
||||
return length;
|
||||
}
|
||||
length = QEMU_ALIGN_UP(length, BDRV_SECTOR_SIZE);
|
||||
ret = bdrv_truncate(s->extents[i].file, length,
|
||||
PREALLOC_MODE_OFF, NULL);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return vmdk_co_pwritev(bs, offset, bytes, qiov, 0);
|
||||
}
|
||||
|
||||
|
||||
+3
-7
@@ -284,11 +284,9 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
|
||||
checksum = be32_to_cpu(footer->checksum);
|
||||
footer->checksum = 0;
|
||||
if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) {
|
||||
error_setg(errp, "Incorrect header checksum");
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum)
|
||||
fprintf(stderr, "block-vpc: The header checksum of '%s' is "
|
||||
"incorrect.\n", bs->filename);
|
||||
|
||||
/* Write 'checksum' back to footer, or else will leave it with zero. */
|
||||
footer->checksum = cpu_to_be32(checksum);
|
||||
@@ -456,12 +454,10 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
}
|
||||
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
qemu_opts_del(opts);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
qemu_vfree(s->pagetable);
|
||||
#ifdef CACHE
|
||||
g_free(s->pageentry_u8);
|
||||
|
||||
+29
-38
@@ -100,26 +100,30 @@ static inline void array_free(array_t* array)
|
||||
/* does not automatically grow */
|
||||
static inline void* array_get(array_t* array,unsigned int index) {
|
||||
assert(index < array->next);
|
||||
assert(array->pointer);
|
||||
return array->pointer + index * array->item_size;
|
||||
}
|
||||
|
||||
static inline void array_ensure_allocated(array_t *array, int index)
|
||||
static inline int array_ensure_allocated(array_t* array, int index)
|
||||
{
|
||||
if((index + 1) * array->item_size > array->size) {
|
||||
int new_size = (index + 32) * array->item_size;
|
||||
array->pointer = g_realloc(array->pointer, new_size);
|
||||
assert(array->pointer);
|
||||
if (!array->pointer)
|
||||
return -1;
|
||||
memset(array->pointer + array->size, 0, new_size - array->size);
|
||||
array->size = new_size;
|
||||
array->next = index + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void* array_get_next(array_t* array) {
|
||||
unsigned int next = array->next;
|
||||
|
||||
array_ensure_allocated(array, next);
|
||||
if (array_ensure_allocated(array, next) < 0)
|
||||
return NULL;
|
||||
|
||||
array->next = next + 1;
|
||||
return array_get(array, next);
|
||||
}
|
||||
@@ -969,10 +973,10 @@ static int init_directories(BDRVVVFATState* s,
|
||||
mapping = array_get(&(s->mapping), i);
|
||||
|
||||
if (mapping->mode & MODE_DIRECTORY) {
|
||||
char *path = mapping->path;
|
||||
mapping->begin = cluster;
|
||||
if(read_directory(s, i)) {
|
||||
error_setg(errp, "Could not read directory %s", path);
|
||||
error_setg(errp, "Could not read directory %s",
|
||||
mapping->path);
|
||||
return -1;
|
||||
}
|
||||
mapping = array_get(&(s->mapping), i);
|
||||
@@ -1258,9 +1262,15 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
||||
"Unable to set VVFAT to 'rw' when drive is read-only");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
ret = bdrv_apply_auto_read_only(bs, NULL, errp);
|
||||
} else if (!bdrv_is_read_only(bs)) {
|
||||
error_report("Opening non-rw vvfat images without an explicit "
|
||||
"read-only=on option is deprecated. Future versions "
|
||||
"will refuse to open the image instead of "
|
||||
"automatically marking the image read-only.");
|
||||
/* read only is the default for safety */
|
||||
ret = bdrv_set_read_only(bs, true, &local_err);
|
||||
if (ret < 0) {
|
||||
error_propagate(errp, local_err);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -2418,13 +2428,16 @@ static int commit_direntries(BDRVVVFATState* s,
|
||||
direntry_t* direntry = array_get(&(s->directory), dir_index);
|
||||
uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry);
|
||||
mapping_t* mapping = find_mapping_for_cluster(s, first_cluster);
|
||||
|
||||
int factor = 0x10 * s->sectors_per_cluster;
|
||||
int old_cluster_count, new_cluster_count;
|
||||
int current_dir_index;
|
||||
int first_dir_index;
|
||||
int current_dir_index = mapping->info.dir.first_dir_index;
|
||||
int first_dir_index = current_dir_index;
|
||||
int ret, i;
|
||||
uint32_t c;
|
||||
|
||||
DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index));
|
||||
|
||||
assert(direntry);
|
||||
assert(mapping);
|
||||
assert(mapping->begin == first_cluster);
|
||||
@@ -2432,11 +2445,6 @@ static int commit_direntries(BDRVVVFATState* s,
|
||||
assert(mapping->mode & MODE_DIRECTORY);
|
||||
assert(dir_index == 0 || is_directory(direntry));
|
||||
|
||||
DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n",
|
||||
mapping->path, parent_mapping_index));
|
||||
|
||||
current_dir_index = mapping->info.dir.first_dir_index;
|
||||
first_dir_index = current_dir_index;
|
||||
mapping->info.dir.parent_mapping_index = parent_mapping_index;
|
||||
|
||||
if (first_cluster == 0) {
|
||||
@@ -2486,9 +2494,6 @@ static int commit_direntries(BDRVVVFATState* s,
|
||||
direntry = array_get(&(s->directory), first_dir_index + i);
|
||||
if (is_directory(direntry) && !is_dot(direntry)) {
|
||||
mapping = find_mapping_for_cluster(s, first_cluster);
|
||||
if (mapping == NULL) {
|
||||
return -1;
|
||||
}
|
||||
assert(mapping->mode & MODE_DIRECTORY);
|
||||
ret = commit_direntries(s, first_dir_index + i,
|
||||
array_index(&(s->mapping), mapping));
|
||||
@@ -2510,17 +2515,13 @@ static int commit_one_file(BDRVVVFATState* s,
|
||||
uint32_t first_cluster = c;
|
||||
mapping_t* mapping = find_mapping_for_cluster(s, c);
|
||||
uint32_t size = filesize_of_direntry(direntry);
|
||||
char *cluster;
|
||||
char* cluster = g_malloc(s->cluster_size);
|
||||
uint32_t i;
|
||||
int fd = 0;
|
||||
|
||||
assert(offset < size);
|
||||
assert((offset % s->cluster_size) == 0);
|
||||
|
||||
if (mapping == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = s->cluster_size; i < offset; i += s->cluster_size)
|
||||
c = modified_fat_get(s, c);
|
||||
|
||||
@@ -2528,17 +2529,17 @@ static int commit_one_file(BDRVVVFATState* s,
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
|
||||
strerror(errno), errno);
|
||||
g_free(cluster);
|
||||
return fd;
|
||||
}
|
||||
if (offset > 0) {
|
||||
if (lseek(fd, offset, SEEK_SET) != offset) {
|
||||
qemu_close(fd);
|
||||
g_free(cluster);
|
||||
return -3;
|
||||
}
|
||||
}
|
||||
|
||||
cluster = g_malloc(s->cluster_size);
|
||||
|
||||
while (offset < size) {
|
||||
uint32_t c1;
|
||||
int rest_size = (size - offset > s->cluster_size ?
|
||||
@@ -2667,12 +2668,8 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
|
||||
if (commit->action == ACTION_RENAME) {
|
||||
mapping_t* mapping = find_mapping_for_cluster(s,
|
||||
commit->param.rename.cluster);
|
||||
char *old_path;
|
||||
char* old_path = mapping->path;
|
||||
|
||||
if (mapping == NULL) {
|
||||
return -1;
|
||||
}
|
||||
old_path = mapping->path;
|
||||
assert(commit->path);
|
||||
mapping->path = commit->path;
|
||||
if (rename(old_path, mapping->path))
|
||||
@@ -2693,15 +2690,10 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s)
|
||||
direntry_t* d = direntry + i;
|
||||
|
||||
if (is_file(d) || (is_directory(d) && !is_dot(d))) {
|
||||
int l;
|
||||
char *new_path;
|
||||
mapping_t* m = find_mapping_for_cluster(s,
|
||||
begin_of_direntry(d));
|
||||
if (m == NULL) {
|
||||
return -1;
|
||||
}
|
||||
l = strlen(m->path);
|
||||
new_path = g_malloc(l + diff + 1);
|
||||
int l = strlen(m->path);
|
||||
char* new_path = g_malloc(l + diff + 1);
|
||||
|
||||
assert(!strncmp(m->path, mapping->path, l2));
|
||||
|
||||
@@ -3138,7 +3130,6 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options,
|
||||
int parent_flags, QDict *parent_options)
|
||||
{
|
||||
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
|
||||
qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off");
|
||||
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ static int coroutine_fn before_write_notify(NotifierWithReturn *notifier,
|
||||
qapi_event_send_block_write_threshold(
|
||||
bs->node_name,
|
||||
amount,
|
||||
bs->write_threshold_offset);
|
||||
bs->write_threshold_offset,
|
||||
&error_abort);
|
||||
|
||||
/* autodisable to avoid flooding the monitor */
|
||||
write_threshold_disable(bs);
|
||||
|
||||
+2
-1
@@ -36,7 +36,8 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
|
||||
gpointer opaque)
|
||||
{
|
||||
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
|
||||
nbd_client_new(cioc, nbd_server->tlscreds, NULL,
|
||||
nbd_client_new(NULL, cioc,
|
||||
nbd_server->tlscreds, NULL,
|
||||
nbd_blockdev_client_closed);
|
||||
}
|
||||
|
||||
|
||||
+191
-198
@@ -590,7 +590,6 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY,
|
||||
read_only ? "on" : "off");
|
||||
qdict_set_default_str(bs_opts, BDRV_OPT_AUTO_READ_ONLY, "on");
|
||||
assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0);
|
||||
|
||||
if (runstate_check(RUN_STATE_INMIGRATE)) {
|
||||
@@ -731,6 +730,30 @@ QemuOptsList qemu_legacy_drive_opts = {
|
||||
.name = "if",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
||||
},{
|
||||
.name = "cyls",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of cylinders (ide disk geometry)",
|
||||
},{
|
||||
.name = "heads",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of heads (ide disk geometry)",
|
||||
},{
|
||||
.name = "secs",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
.help = "number of sectors (ide disk geometry)",
|
||||
},{
|
||||
.name = "trans",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "chs translation (auto, lba, none)",
|
||||
},{
|
||||
.name = "addr",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "pci address (virtio only)",
|
||||
},{
|
||||
.name = "serial",
|
||||
.type = QEMU_OPT_STRING,
|
||||
.help = "disk serial number",
|
||||
},{
|
||||
.name = "file",
|
||||
.type = QEMU_OPT_STRING,
|
||||
@@ -760,8 +783,7 @@ QemuOptsList qemu_legacy_drive_opts = {
|
||||
},
|
||||
};
|
||||
|
||||
DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
Error **errp)
|
||||
DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
||||
{
|
||||
const char *value;
|
||||
BlockBackend *blk;
|
||||
@@ -770,13 +792,19 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
QemuOpts *legacy_opts;
|
||||
DriveMediaType media = MEDIA_DISK;
|
||||
BlockInterfaceType type;
|
||||
int cyls, heads, secs, translation;
|
||||
int max_devs, bus_id, unit_id, index;
|
||||
const char *devaddr;
|
||||
const char *werror, *rerror;
|
||||
bool read_only = false;
|
||||
bool copy_on_read;
|
||||
const char *serial;
|
||||
const char *filename;
|
||||
Error *local_err = NULL;
|
||||
int i;
|
||||
const char *deprecated[] = {
|
||||
"serial", "trans", "secs", "heads", "cyls", "addr"
|
||||
};
|
||||
|
||||
/* Change legacy command line options into QMP ones */
|
||||
static const struct {
|
||||
@@ -810,7 +838,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
qemu_opt_rename(all_opts, opt_renames[i].from, opt_renames[i].to,
|
||||
&local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_report_err(local_err);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
@@ -821,7 +849,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
bool writethrough;
|
||||
|
||||
if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) {
|
||||
error_setg(errp, "invalid cache option");
|
||||
error_report("invalid cache option");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -849,10 +877,20 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
&error_abort);
|
||||
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_report_err(local_err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Other deprecated options */
|
||||
if (!qtest_enabled()) {
|
||||
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
|
||||
if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) {
|
||||
error_report("'%s' is deprecated, please use the corresponding "
|
||||
"option of '-device' instead", deprecated[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Media type */
|
||||
value = qemu_opt_get(legacy_opts, "media");
|
||||
if (value) {
|
||||
@@ -862,7 +900,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
media = MEDIA_CDROM;
|
||||
read_only = true;
|
||||
} else {
|
||||
error_setg(errp, "'%s' invalid media", value);
|
||||
error_report("'%s' invalid media", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -887,13 +925,64 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
type++) {
|
||||
}
|
||||
if (type == IF_COUNT) {
|
||||
error_setg(errp, "unsupported bus type '%s'", value);
|
||||
error_report("unsupported bus type '%s'", value);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
type = block_default_type;
|
||||
}
|
||||
|
||||
/* Geometry */
|
||||
cyls = qemu_opt_get_number(legacy_opts, "cyls", 0);
|
||||
heads = qemu_opt_get_number(legacy_opts, "heads", 0);
|
||||
secs = qemu_opt_get_number(legacy_opts, "secs", 0);
|
||||
|
||||
if (cyls || heads || secs) {
|
||||
if (cyls < 1) {
|
||||
error_report("invalid physical cyls number");
|
||||
goto fail;
|
||||
}
|
||||
if (heads < 1) {
|
||||
error_report("invalid physical heads number");
|
||||
goto fail;
|
||||
}
|
||||
if (secs < 1) {
|
||||
error_report("invalid physical secs number");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
value = qemu_opt_get(legacy_opts, "trans");
|
||||
if (value != NULL) {
|
||||
if (!cyls) {
|
||||
error_report("'%s' trans must be used with cyls, heads and secs",
|
||||
value);
|
||||
goto fail;
|
||||
}
|
||||
if (!strcmp(value, "none")) {
|
||||
translation = BIOS_ATA_TRANSLATION_NONE;
|
||||
} else if (!strcmp(value, "lba")) {
|
||||
translation = BIOS_ATA_TRANSLATION_LBA;
|
||||
} else if (!strcmp(value, "large")) {
|
||||
translation = BIOS_ATA_TRANSLATION_LARGE;
|
||||
} else if (!strcmp(value, "rechs")) {
|
||||
translation = BIOS_ATA_TRANSLATION_RECHS;
|
||||
} else if (!strcmp(value, "auto")) {
|
||||
translation = BIOS_ATA_TRANSLATION_AUTO;
|
||||
} else {
|
||||
error_report("'%s' invalid translation type", value);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (media == MEDIA_CDROM) {
|
||||
if (cyls || secs || heads) {
|
||||
error_report("CHS can't be set with media=cdrom");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Device address specified by bus/unit or index.
|
||||
* If none was specified, try to find the first free one. */
|
||||
bus_id = qemu_opt_get_number(legacy_opts, "bus", 0);
|
||||
@@ -904,7 +993,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
|
||||
if (index != -1) {
|
||||
if (bus_id != 0 || unit_id != -1) {
|
||||
error_setg(errp, "index cannot be used with bus and unit");
|
||||
error_report("index cannot be used with bus and unit");
|
||||
goto fail;
|
||||
}
|
||||
bus_id = drive_index_to_bus_id(type, index);
|
||||
@@ -923,16 +1012,19 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
}
|
||||
|
||||
if (max_devs && unit_id >= max_devs) {
|
||||
error_setg(errp, "unit %d too big (max is %d)", unit_id, max_devs - 1);
|
||||
error_report("unit %d too big (max is %d)", unit_id, max_devs - 1);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (drive_get(type, bus_id, unit_id) != NULL) {
|
||||
error_setg(errp, "drive with bus=%d, unit=%d (index=%d) exists",
|
||||
bus_id, unit_id, index);
|
||||
error_report("drive with bus=%d, unit=%d (index=%d) exists",
|
||||
bus_id, unit_id, index);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Serial number */
|
||||
serial = qemu_opt_get(legacy_opts, "serial");
|
||||
|
||||
/* no id supplied -> create one */
|
||||
if (qemu_opts_id(all_opts) == NULL) {
|
||||
char *new_id;
|
||||
@@ -952,6 +1044,12 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
}
|
||||
|
||||
/* Add virtio block device */
|
||||
devaddr = qemu_opt_get(legacy_opts, "addr");
|
||||
if (devaddr && type != IF_VIRTIO) {
|
||||
error_report("addr is not supported by this bus type");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (type == IF_VIRTIO) {
|
||||
QemuOpts *devopts;
|
||||
devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
|
||||
@@ -963,6 +1061,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
}
|
||||
qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"),
|
||||
&error_abort);
|
||||
if (devaddr) {
|
||||
qemu_opt_set(devopts, "addr", devaddr, &error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
filename = qemu_opt_get(legacy_opts, "file");
|
||||
@@ -972,7 +1073,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
if (werror != NULL) {
|
||||
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO &&
|
||||
type != IF_NONE) {
|
||||
error_setg(errp, "werror is not supported by this bus type");
|
||||
error_report("werror is not supported by this bus type");
|
||||
goto fail;
|
||||
}
|
||||
qdict_put_str(bs_opts, "werror", werror);
|
||||
@@ -982,7 +1083,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
if (rerror != NULL) {
|
||||
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI &&
|
||||
type != IF_NONE) {
|
||||
error_setg(errp, "rerror is not supported by this bus type");
|
||||
error_report("rerror is not supported by this bus type");
|
||||
goto fail;
|
||||
}
|
||||
qdict_put_str(bs_opts, "rerror", rerror);
|
||||
@@ -993,7 +1094,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
bs_opts = NULL;
|
||||
if (!blk) {
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
error_report_err(local_err);
|
||||
}
|
||||
goto fail;
|
||||
} else {
|
||||
@@ -1004,9 +1105,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
|
||||
dinfo = g_malloc0(sizeof(*dinfo));
|
||||
dinfo->opts = all_opts;
|
||||
|
||||
dinfo->cyls = cyls;
|
||||
dinfo->heads = heads;
|
||||
dinfo->secs = secs;
|
||||
dinfo->trans = translation;
|
||||
|
||||
dinfo->type = type;
|
||||
dinfo->bus = bus_id;
|
||||
dinfo->unit = unit_id;
|
||||
dinfo->devaddr = devaddr;
|
||||
dinfo->serial = g_strdup(serial);
|
||||
|
||||
blk_set_legacy_dinfo(blk, dinfo);
|
||||
|
||||
@@ -1640,7 +1748,7 @@ static void external_snapshot_prepare(BlkActionState *common,
|
||||
}
|
||||
|
||||
options = qdict_new();
|
||||
if (snapshot_node_name) {
|
||||
if (s->has_snapshot_node_name) {
|
||||
qdict_put_str(options, "node-name", snapshot_node_name);
|
||||
}
|
||||
qdict_put_str(options, "driver", format);
|
||||
@@ -2011,8 +2119,14 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
|
||||
error_setg(errp, "Cannot modify a bitmap in use by another operation");
|
||||
if (bdrv_dirty_bitmap_frozen(state->bitmap)) {
|
||||
error_setg(errp, "Cannot modify a frozen bitmap");
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) {
|
||||
error_setg(errp, "Cannot modify a locked bitmap");
|
||||
return;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) {
|
||||
error_setg(errp, "Cannot clear a disabled bitmap");
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_readonly(state->bitmap)) {
|
||||
error_setg(errp, "Cannot clear a readonly bitmap");
|
||||
@@ -2022,17 +2136,17 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
|
||||
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_restore(BlkActionState *common)
|
||||
static void block_dirty_bitmap_clear_abort(BlkActionState *common)
|
||||
{
|
||||
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
||||
common, common);
|
||||
|
||||
if (state->backup) {
|
||||
bdrv_restore_dirty_bitmap(state->bitmap, state->backup);
|
||||
bdrv_undo_clear_dirty_bitmap(state->bitmap, state->backup);
|
||||
}
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_free_backup(BlkActionState *common)
|
||||
static void block_dirty_bitmap_clear_commit(BlkActionState *common)
|
||||
{
|
||||
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
||||
common, common);
|
||||
@@ -2060,13 +2174,6 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be enabled", action->name);
|
||||
return;
|
||||
}
|
||||
|
||||
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
|
||||
bdrv_enable_dirty_bitmap(state->bitmap);
|
||||
}
|
||||
@@ -2101,13 +2208,6 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be disabled", action->name);
|
||||
return;
|
||||
}
|
||||
|
||||
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
|
||||
bdrv_disable_dirty_bitmap(state->bitmap);
|
||||
}
|
||||
@@ -2122,35 +2222,6 @@ static void block_dirty_bitmap_disable_abort(BlkActionState *common)
|
||||
}
|
||||
}
|
||||
|
||||
static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDirtyBitmapMerge *action;
|
||||
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
|
||||
common, common);
|
||||
BdrvDirtyBitmap *merge_source;
|
||||
|
||||
if (action_check_completion_mode(common, errp) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
action = common->action->u.x_block_dirty_bitmap_merge.data;
|
||||
state->bitmap = block_dirty_bitmap_lookup(action->node,
|
||||
action->dst_name,
|
||||
&state->bs,
|
||||
errp);
|
||||
if (!state->bitmap) {
|
||||
return;
|
||||
}
|
||||
|
||||
merge_source = bdrv_find_dirty_bitmap(state->bs, action->src_name);
|
||||
if (!merge_source) {
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_merge_dirty_bitmap(state->bitmap, merge_source, &state->backup, errp);
|
||||
}
|
||||
|
||||
static void abort_prepare(BlkActionState *common, Error **errp)
|
||||
{
|
||||
error_setg(errp, "Transaction aborted using Abort action");
|
||||
@@ -2209,8 +2280,8 @@ static const BlkActionOps actions[] = {
|
||||
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = {
|
||||
.instance_size = sizeof(BlockDirtyBitmapState),
|
||||
.prepare = block_dirty_bitmap_clear_prepare,
|
||||
.commit = block_dirty_bitmap_free_backup,
|
||||
.abort = block_dirty_bitmap_restore,
|
||||
.commit = block_dirty_bitmap_clear_commit,
|
||||
.abort = block_dirty_bitmap_clear_abort,
|
||||
},
|
||||
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = {
|
||||
.instance_size = sizeof(BlockDirtyBitmapState),
|
||||
@@ -2221,19 +2292,7 @@ static const BlkActionOps actions[] = {
|
||||
.instance_size = sizeof(BlockDirtyBitmapState),
|
||||
.prepare = block_dirty_bitmap_disable_prepare,
|
||||
.abort = block_dirty_bitmap_disable_abort,
|
||||
},
|
||||
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_MERGE] = {
|
||||
.instance_size = sizeof(BlockDirtyBitmapState),
|
||||
.prepare = block_dirty_bitmap_merge_prepare,
|
||||
.commit = block_dirty_bitmap_free_backup,
|
||||
.abort = block_dirty_bitmap_restore,
|
||||
},
|
||||
/* Where are transactions for MIRROR, COMMIT and STREAM?
|
||||
* Although these blockjobs use transaction callbacks like the backup job,
|
||||
* these jobs do not necessarily adhere to transaction semantics.
|
||||
* These jobs may not fully undo all of their actions on abort, nor do they
|
||||
* necessarily work in transactions with more than one job in them.
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2652,7 +2711,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device,
|
||||
|
||||
bdrv_flags = blk_get_open_flags_from_root_state(blk);
|
||||
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
|
||||
BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
|
||||
BDRV_O_PROTOCOL);
|
||||
|
||||
if (!has_read_only) {
|
||||
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
|
||||
@@ -2892,10 +2951,15 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation and"
|
||||
" cannot be removed", name);
|
||||
"Bitmap '%s' is currently frozen and cannot be removed",
|
||||
name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently locked and cannot be removed",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2925,10 +2989,20 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be cleared", name);
|
||||
"Bitmap '%s' is currently frozen and cannot be modified",
|
||||
name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently locked and cannot be modified",
|
||||
name);
|
||||
return;
|
||||
} else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently disabled and cannot be cleared",
|
||||
name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_readonly(bitmap)) {
|
||||
error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name);
|
||||
@@ -2949,10 +3023,10 @@ void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be enabled", name);
|
||||
"Bitmap '%s' is currently frozen and cannot be enabled",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2970,10 +3044,10 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_user_locked(bitmap)) {
|
||||
if (bdrv_dirty_bitmap_frozen(bitmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be disabled", name);
|
||||
"Bitmap '%s' is currently frozen and cannot be disabled",
|
||||
name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2991,13 +3065,23 @@ void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name,
|
||||
return;
|
||||
}
|
||||
|
||||
if (bdrv_dirty_bitmap_frozen(dst)) {
|
||||
error_setg(errp, "Bitmap '%s' is frozen and cannot be modified",
|
||||
dst_name);
|
||||
return;
|
||||
} else if (bdrv_dirty_bitmap_readonly(dst)) {
|
||||
error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
|
||||
dst_name);
|
||||
return;
|
||||
}
|
||||
|
||||
src = bdrv_find_dirty_bitmap(bs, src_name);
|
||||
if (!src) {
|
||||
error_setg(errp, "Dirty bitmap '%s' not found", src_name);
|
||||
return;
|
||||
}
|
||||
|
||||
bdrv_merge_dirty_bitmap(dst, src, NULL, errp);
|
||||
bdrv_merge_dirty_bitmap(dst, src, errp);
|
||||
}
|
||||
|
||||
BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
|
||||
@@ -3142,8 +3226,6 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
|
||||
bool has_backing_file, const char *backing_file,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_on_error, BlockdevOnError on_error,
|
||||
bool has_auto_finalize, bool auto_finalize,
|
||||
bool has_auto_dismiss, bool auto_dismiss,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs, *iter;
|
||||
@@ -3151,7 +3233,6 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
|
||||
AioContext *aio_context;
|
||||
Error *local_err = NULL;
|
||||
const char *base_name = NULL;
|
||||
int job_flags = JOB_DEFAULT;
|
||||
|
||||
if (!has_on_error) {
|
||||
on_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
@@ -3213,15 +3294,8 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
|
||||
/* backing_file string overrides base bs filename */
|
||||
base_name = has_backing_file ? backing_file : base_name;
|
||||
|
||||
if (has_auto_finalize && !auto_finalize) {
|
||||
job_flags |= JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
if (has_auto_dismiss && !auto_dismiss) {
|
||||
job_flags |= JOB_MANUAL_DISMISS;
|
||||
}
|
||||
|
||||
stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
|
||||
job_flags, has_speed ? speed : 0, on_error, &local_err);
|
||||
has_speed ? speed : 0, on_error, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
@@ -3234,15 +3308,11 @@ out:
|
||||
}
|
||||
|
||||
void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
||||
bool has_base_node, const char *base_node,
|
||||
bool has_base, const char *base,
|
||||
bool has_top_node, const char *top_node,
|
||||
bool has_top, const char *top,
|
||||
bool has_backing_file, const char *backing_file,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_filter_node_name, const char *filter_node_name,
|
||||
bool has_auto_finalize, bool auto_finalize,
|
||||
bool has_auto_dismiss, bool auto_dismiss,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
@@ -3254,7 +3324,6 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
||||
* BlockdevOnError change for blkmirror makes it in
|
||||
*/
|
||||
BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT;
|
||||
int job_flags = JOB_DEFAULT;
|
||||
|
||||
if (!has_speed) {
|
||||
speed = 0;
|
||||
@@ -3262,12 +3331,6 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
||||
if (!has_filter_node_name) {
|
||||
filter_node_name = NULL;
|
||||
}
|
||||
if (has_auto_finalize && !auto_finalize) {
|
||||
job_flags |= JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
if (has_auto_dismiss && !auto_dismiss) {
|
||||
job_flags |= JOB_MANUAL_DISMISS;
|
||||
}
|
||||
|
||||
/* Important Note:
|
||||
* libvirt relies on the DeviceNotFound error class in order to probe for
|
||||
@@ -3297,20 +3360,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
||||
/* default top_bs is the active layer */
|
||||
top_bs = bs;
|
||||
|
||||
if (has_top_node && has_top) {
|
||||
error_setg(errp, "'top-node' and 'top' are mutually exclusive");
|
||||
goto out;
|
||||
} else if (has_top_node) {
|
||||
top_bs = bdrv_lookup_bs(NULL, top_node, errp);
|
||||
if (top_bs == NULL) {
|
||||
goto out;
|
||||
}
|
||||
if (!bdrv_chain_contains(bs, top_bs)) {
|
||||
error_setg(errp, "'%s' is not in this backing file chain",
|
||||
top_node);
|
||||
goto out;
|
||||
}
|
||||
} else if (has_top && top) {
|
||||
if (has_top && top) {
|
||||
if (strcmp(bs->filename, top) != 0) {
|
||||
top_bs = bdrv_find_backing_image(bs, top);
|
||||
}
|
||||
@@ -3323,20 +3373,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
||||
|
||||
assert(bdrv_get_aio_context(top_bs) == aio_context);
|
||||
|
||||
if (has_base_node && has_base) {
|
||||
error_setg(errp, "'base-node' and 'base' are mutually exclusive");
|
||||
goto out;
|
||||
} else if (has_base_node) {
|
||||
base_bs = bdrv_lookup_bs(NULL, base_node, errp);
|
||||
if (base_bs == NULL) {
|
||||
goto out;
|
||||
}
|
||||
if (!bdrv_chain_contains(top_bs, base_bs)) {
|
||||
error_setg(errp, "'%s' is not in this backing file chain",
|
||||
base_node);
|
||||
goto out;
|
||||
}
|
||||
} else if (has_base && base) {
|
||||
if (has_base && base) {
|
||||
base_bs = bdrv_find_backing_image(top_bs, base);
|
||||
} else {
|
||||
base_bs = bdrv_find_base(top_bs);
|
||||
@@ -3368,15 +3405,15 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
||||
goto out;
|
||||
}
|
||||
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
|
||||
job_flags, speed, on_error,
|
||||
JOB_DEFAULT, speed, on_error,
|
||||
filter_node_name, NULL, NULL, false, &local_err);
|
||||
} else {
|
||||
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
|
||||
if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
|
||||
goto out;
|
||||
}
|
||||
commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, job_flags,
|
||||
speed, on_error, has_backing_file ? backing_file : NULL,
|
||||
commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed,
|
||||
on_error, has_backing_file ? backing_file : NULL,
|
||||
filter_node_name, &local_err);
|
||||
}
|
||||
if (local_err != NULL) {
|
||||
@@ -3514,10 +3551,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
||||
bdrv_unref(target_bs);
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_user_locked(bmap)) {
|
||||
if (bdrv_dirty_bitmap_qmp_locked(bmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be used for backup", backup->bitmap);
|
||||
"Bitmap '%s' is currently locked and cannot be used for "
|
||||
"backup", backup->bitmap);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -3564,7 +3601,6 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
BlockDriverState *bs;
|
||||
BlockDriverState *target_bs;
|
||||
Error *local_err = NULL;
|
||||
BdrvDirtyBitmap *bmap = NULL;
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = NULL;
|
||||
int job_flags = JOB_DEFAULT;
|
||||
@@ -3615,21 +3651,6 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (backup->has_bitmap) {
|
||||
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
|
||||
if (!bmap) {
|
||||
error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
|
||||
goto out;
|
||||
}
|
||||
if (bdrv_dirty_bitmap_user_locked(bmap)) {
|
||||
error_setg(errp,
|
||||
"Bitmap '%s' is currently in use by another operation"
|
||||
" and cannot be used for backup", backup->bitmap);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!backup->auto_finalize) {
|
||||
job_flags |= JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
@@ -3637,7 +3658,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
job_flags |= JOB_MANUAL_DISMISS;
|
||||
}
|
||||
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
|
||||
backup->sync, bmap, backup->compress,
|
||||
backup->sync, NULL, backup->compress,
|
||||
backup->on_source_error, backup->on_target_error,
|
||||
job_flags, NULL, NULL, txn, &local_err);
|
||||
if (local_err != NULL) {
|
||||
@@ -3676,11 +3697,8 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
bool has_filter_node_name,
|
||||
const char *filter_node_name,
|
||||
bool has_copy_mode, MirrorCopyMode copy_mode,
|
||||
bool has_auto_finalize, bool auto_finalize,
|
||||
bool has_auto_dismiss, bool auto_dismiss,
|
||||
Error **errp)
|
||||
{
|
||||
int job_flags = JOB_DEFAULT;
|
||||
|
||||
if (!has_speed) {
|
||||
speed = 0;
|
||||
@@ -3706,12 +3724,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
if (!has_copy_mode) {
|
||||
copy_mode = MIRROR_COPY_MODE_BACKGROUND;
|
||||
}
|
||||
if (has_auto_finalize && !auto_finalize) {
|
||||
job_flags |= JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
if (has_auto_dismiss && !auto_dismiss) {
|
||||
job_flags |= JOB_MANUAL_DISMISS;
|
||||
}
|
||||
|
||||
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
|
||||
@@ -3739,7 +3751,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
* and will allow to check whether the node still exist at mirror completion
|
||||
*/
|
||||
mirror_start(job_id, bs, target,
|
||||
has_replaces ? replaces : NULL, job_flags,
|
||||
has_replaces ? replaces : NULL,
|
||||
speed, granularity, buf_size, sync, backing_mode,
|
||||
on_source_error, on_target_error, unmap, filter_node_name,
|
||||
copy_mode, errp);
|
||||
@@ -3889,8 +3901,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
|
||||
arg->has_unmap, arg->unmap,
|
||||
false, NULL,
|
||||
arg->has_copy_mode, arg->copy_mode,
|
||||
arg->has_auto_finalize, arg->auto_finalize,
|
||||
arg->has_auto_dismiss, arg->auto_dismiss,
|
||||
&local_err);
|
||||
bdrv_unref(target_bs);
|
||||
error_propagate(errp, local_err);
|
||||
@@ -3912,8 +3922,6 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
|
||||
bool has_filter_node_name,
|
||||
const char *filter_node_name,
|
||||
bool has_copy_mode, MirrorCopyMode copy_mode,
|
||||
bool has_auto_finalize, bool auto_finalize,
|
||||
bool has_auto_dismiss, bool auto_dismiss,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
@@ -3947,8 +3955,6 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
|
||||
true, true,
|
||||
has_filter_node_name, filter_node_name,
|
||||
has_copy_mode, copy_mode,
|
||||
has_auto_finalize, auto_finalize,
|
||||
has_auto_dismiss, auto_dismiss,
|
||||
&local_err);
|
||||
error_propagate(errp, local_err);
|
||||
|
||||
@@ -4413,7 +4419,6 @@ void qmp_x_block_latency_histogram_set(
|
||||
{
|
||||
BlockBackend *blk = blk_by_name(device);
|
||||
BlockAcctStats *stats;
|
||||
int ret;
|
||||
|
||||
if (!blk) {
|
||||
error_setg(errp, "Device '%s' not found", device);
|
||||
@@ -4429,33 +4434,21 @@ void qmp_x_block_latency_histogram_set(
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_read) {
|
||||
ret = block_latency_histogram_set(
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_READ,
|
||||
has_boundaries_read ? boundaries_read : boundaries);
|
||||
if (ret) {
|
||||
error_setg(errp, "Device '%s' set read boundaries fail", device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_write) {
|
||||
ret = block_latency_histogram_set(
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_WRITE,
|
||||
has_boundaries_write ? boundaries_write : boundaries);
|
||||
if (ret) {
|
||||
error_setg(errp, "Device '%s' set write boundaries fail", device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_boundaries || has_boundaries_flush) {
|
||||
ret = block_latency_histogram_set(
|
||||
block_latency_histogram_set(
|
||||
stats, BLOCK_ACCT_FLUSH,
|
||||
has_boundaries_flush ? boundaries_flush : boundaries);
|
||||
if (ret) {
|
||||
error_setg(errp, "Device '%s' set flush boundaries fail", device);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-13
@@ -164,7 +164,7 @@ static bool child_job_drained_poll(BdrvChild *c)
|
||||
/* An inactive or completed job doesn't have any pending requests. Jobs
|
||||
* with !job->busy are either already paused or have a pause point after
|
||||
* being reentered, so no job driver code will run before they pause. */
|
||||
if (!job->busy || job_is_completed(job)) {
|
||||
if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -221,11 +221,6 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void block_job_on_idle(Notifier *n, void *opaque)
|
||||
{
|
||||
aio_wait_kick();
|
||||
}
|
||||
|
||||
bool block_job_is_internal(BlockJob *job)
|
||||
{
|
||||
return (job->job.id == NULL);
|
||||
@@ -320,7 +315,8 @@ static void block_job_event_cancelled(Notifier *n, void *opaque)
|
||||
job->job.id,
|
||||
job->job.progress_total,
|
||||
job->job.progress_current,
|
||||
job->speed);
|
||||
job->speed,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void block_job_event_completed(Notifier *n, void *opaque)
|
||||
@@ -342,7 +338,8 @@ static void block_job_event_completed(Notifier *n, void *opaque)
|
||||
job->job.progress_current,
|
||||
job->speed,
|
||||
!!msg,
|
||||
msg);
|
||||
msg,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void block_job_event_pending(Notifier *n, void *opaque)
|
||||
@@ -354,7 +351,8 @@ static void block_job_event_pending(Notifier *n, void *opaque)
|
||||
}
|
||||
|
||||
qapi_event_send_block_job_pending(job_type(&job->job),
|
||||
job->job.id);
|
||||
job->job.id,
|
||||
&error_abort);
|
||||
}
|
||||
|
||||
static void block_job_event_ready(Notifier *n, void *opaque)
|
||||
@@ -369,7 +367,7 @@ static void block_job_event_ready(Notifier *n, void *opaque)
|
||||
job->job.id,
|
||||
job->job.progress_total,
|
||||
job->job.progress_current,
|
||||
job->speed);
|
||||
job->speed, &error_abort);
|
||||
}
|
||||
|
||||
|
||||
@@ -421,7 +419,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
job->finalize_completed_notifier.notify = block_job_event_completed;
|
||||
job->pending_notifier.notify = block_job_event_pending;
|
||||
job->ready_notifier.notify = block_job_event_ready;
|
||||
job->idle_notifier.notify = block_job_on_idle;
|
||||
|
||||
notifier_list_add(&job->job.on_finalize_cancelled,
|
||||
&job->finalize_cancelled_notifier);
|
||||
@@ -429,7 +426,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
&job->finalize_completed_notifier);
|
||||
notifier_list_add(&job->job.on_pending, &job->pending_notifier);
|
||||
notifier_list_add(&job->job.on_ready, &job->ready_notifier);
|
||||
notifier_list_add(&job->job.on_idle, &job->idle_notifier);
|
||||
|
||||
error_setg(&job->blocker, "block device is in use by block job: %s",
|
||||
job_type_str(&job->job));
|
||||
@@ -498,7 +494,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
|
||||
qapi_event_send_block_job_error(job->job.id,
|
||||
is_read ? IO_OPERATION_TYPE_READ :
|
||||
IO_OPERATION_TYPE_WRITE,
|
||||
action);
|
||||
action, &error_abort);
|
||||
}
|
||||
if (action == BLOCK_ERROR_ACTION_STOP) {
|
||||
job_pause(&job->job);
|
||||
|
||||
+1
-4
@@ -29,7 +29,6 @@
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/reset.h"
|
||||
#include "hw/qdev-core.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
typedef struct FWBootEntry FWBootEntry;
|
||||
|
||||
@@ -209,13 +208,11 @@ DeviceState *get_boot_device(uint32_t position)
|
||||
* memory pointed by "size" is assigned total length of the array in bytes
|
||||
*
|
||||
*/
|
||||
char *get_boot_devices_list(size_t *size)
|
||||
char *get_boot_devices_list(size_t *size, bool ignore_suffixes)
|
||||
{
|
||||
FWBootEntry *i;
|
||||
size_t total = 0;
|
||||
char *list = NULL;
|
||||
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
|
||||
bool ignore_suffixes = mc->ignore_boot_device_suffixes;
|
||||
|
||||
QTAILQ_FOREACH(i, &fw_boot_order, link) {
|
||||
char *devpath = NULL, *suffix = NULL;
|
||||
|
||||
+6
-7
@@ -56,7 +56,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len)
|
||||
int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
|
||||
{
|
||||
Chardev *s = be->chr;
|
||||
int offset = 0;
|
||||
int offset = 0, counter = 10;
|
||||
int res;
|
||||
|
||||
if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) {
|
||||
@@ -88,6 +88,10 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len)
|
||||
}
|
||||
|
||||
offset += res;
|
||||
|
||||
if (!counter--) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) {
|
||||
@@ -235,12 +239,7 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del)
|
||||
d->backends[b->tag] = NULL;
|
||||
}
|
||||
if (del) {
|
||||
Object *obj = OBJECT(b->chr);
|
||||
if (obj->parent) {
|
||||
object_unparent(obj);
|
||||
} else {
|
||||
object_unref(obj);
|
||||
}
|
||||
object_unparent(OBJECT(b->chr));
|
||||
}
|
||||
b->chr = NULL;
|
||||
}
|
||||
|
||||
+43
-5
@@ -33,6 +33,7 @@ typedef struct IOWatchPoll {
|
||||
IOCanReadHandler *fd_can_read;
|
||||
GSourceFunc fd_read;
|
||||
void *opaque;
|
||||
GMainContext *context;
|
||||
} IOWatchPoll;
|
||||
|
||||
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
|
||||
@@ -54,24 +55,47 @@ static gboolean io_watch_poll_prepare(GSource *source,
|
||||
iwp->src = qio_channel_create_watch(
|
||||
iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
|
||||
g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
|
||||
g_source_add_child_source(source, iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
g_source_attach(iwp->src, iwp->context);
|
||||
} else {
|
||||
g_source_remove_child_source(source, iwp->src);
|
||||
g_source_destroy(iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
iwp->src = NULL;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_check(GSource *source)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
return G_SOURCE_CONTINUE;
|
||||
abort();
|
||||
}
|
||||
|
||||
static void io_watch_poll_finalize(GSource *source)
|
||||
{
|
||||
/* Due to a glib bug, removing the last reference to a source
|
||||
* inside a finalize callback causes recursive locking (and a
|
||||
* deadlock). This is not a problem inside other callbacks,
|
||||
* including dispatch callbacks, so we call io_remove_watch_poll
|
||||
* to remove this source. At this point, iwp->src must
|
||||
* be NULL, or we would leak it.
|
||||
*
|
||||
* This would be solved much more elegantly by child sources,
|
||||
* but we support older glib versions that do not have them.
|
||||
*/
|
||||
IOWatchPoll *iwp = io_watch_poll_from_source(source);
|
||||
assert(iwp->src == NULL);
|
||||
}
|
||||
|
||||
static GSourceFuncs io_watch_poll_funcs = {
|
||||
.prepare = io_watch_poll_prepare,
|
||||
.check = io_watch_poll_check,
|
||||
.dispatch = io_watch_poll_dispatch,
|
||||
.finalize = io_watch_poll_finalize,
|
||||
};
|
||||
|
||||
GSource *io_add_watch_poll(Chardev *chr,
|
||||
@@ -91,6 +115,7 @@ GSource *io_add_watch_poll(Chardev *chr,
|
||||
iwp->ioc = ioc;
|
||||
iwp->fd_read = (GSourceFunc) fd_read;
|
||||
iwp->src = NULL;
|
||||
iwp->context = context;
|
||||
|
||||
name = g_strdup_printf("chardev-iowatch-%s", chr->label);
|
||||
g_source_set_name((GSource *)iwp, name);
|
||||
@@ -101,10 +126,23 @@ GSource *io_add_watch_poll(Chardev *chr,
|
||||
return (GSource *)iwp;
|
||||
}
|
||||
|
||||
static void io_remove_watch_poll(GSource *source)
|
||||
{
|
||||
IOWatchPoll *iwp;
|
||||
|
||||
iwp = io_watch_poll_from_source(source);
|
||||
if (iwp->src) {
|
||||
g_source_destroy(iwp->src);
|
||||
g_source_unref(iwp->src);
|
||||
iwp->src = NULL;
|
||||
}
|
||||
g_source_destroy(&iwp->parent);
|
||||
}
|
||||
|
||||
void remove_fd_in_watch(Chardev *chr)
|
||||
{
|
||||
if (chr->gsource) {
|
||||
g_source_destroy(chr->gsource);
|
||||
io_remove_watch_poll(chr->gsource);
|
||||
chr->gsource = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -31,6 +31,10 @@
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
|
||||
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|
||||
|| defined(__GLIBC__)
|
||||
|
||||
typedef struct {
|
||||
Chardev parent;
|
||||
QIOChannel *ioc;
|
||||
@@ -259,7 +263,7 @@ static void char_pty_open(Chardev *chr,
|
||||
qemu_set_nonblock(master_fd);
|
||||
|
||||
chr->filename = g_strdup_printf("pty:%s", pty_name);
|
||||
error_printf("char device redirected to %s (label %s)\n",
|
||||
error_report("char device redirected to %s (label %s)",
|
||||
pty_name, chr->label);
|
||||
|
||||
s = PTY_CHARDEV(chr);
|
||||
@@ -295,3 +299,5 @@ static void register_types(void)
|
||||
}
|
||||
|
||||
type_init(register_types);
|
||||
|
||||
#endif
|
||||
+83
-132
@@ -26,13 +26,13 @@
|
||||
#include "chardev/char.h"
|
||||
#include "io/channel-socket.h"
|
||||
#include "io/channel-tls.h"
|
||||
#include "io/channel-websock.h"
|
||||
#include "io/net-listener.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/qapi-visit-sockets.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
#include "chardev/char-io.h"
|
||||
|
||||
@@ -69,8 +69,6 @@ typedef struct {
|
||||
GSource *telnet_source;
|
||||
TCPChardevTelnetInit *telnet_init;
|
||||
|
||||
bool is_websock;
|
||||
|
||||
GSource *reconnect_timer;
|
||||
int64_t reconnect_time;
|
||||
bool connect_err_reported;
|
||||
@@ -356,15 +354,6 @@ static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond)
|
||||
return qio_channel_create_watch(s->ioc, cond);
|
||||
}
|
||||
|
||||
static void remove_hup_source(SocketChardev *s)
|
||||
{
|
||||
if (s->hup_source != NULL) {
|
||||
g_source_destroy(s->hup_source);
|
||||
g_source_unref(s->hup_source);
|
||||
s->hup_source = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void tcp_chr_free_connection(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
@@ -379,7 +368,11 @@ static void tcp_chr_free_connection(Chardev *chr)
|
||||
s->read_msgfds_num = 0;
|
||||
}
|
||||
|
||||
remove_hup_source(s);
|
||||
if (s->hup_source != NULL) {
|
||||
g_source_destroy(s->hup_source);
|
||||
g_source_unref(s->hup_source);
|
||||
s->hup_source = NULL;
|
||||
}
|
||||
|
||||
tcp_set_msgfds(chr, NULL, 0);
|
||||
remove_fd_in_watch(chr);
|
||||
@@ -392,37 +385,30 @@ static void tcp_chr_free_connection(Chardev *chr)
|
||||
s->connected = 0;
|
||||
}
|
||||
|
||||
static const char *qemu_chr_socket_protocol(SocketChardev *s)
|
||||
static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
|
||||
bool is_listen, bool is_telnet)
|
||||
{
|
||||
if (s->is_telnet) {
|
||||
return "telnet";
|
||||
}
|
||||
return s->is_websock ? "websocket" : "tcp";
|
||||
}
|
||||
|
||||
static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix)
|
||||
{
|
||||
switch (s->addr->type) {
|
||||
switch (addr->type) {
|
||||
case SOCKET_ADDRESS_TYPE_INET:
|
||||
return g_strdup_printf("%s%s:%s:%s%s", prefix,
|
||||
qemu_chr_socket_protocol(s),
|
||||
s->addr->u.inet.host,
|
||||
s->addr->u.inet.port,
|
||||
s->is_listen ? ",server" : "");
|
||||
is_telnet ? "telnet" : "tcp",
|
||||
addr->u.inet.host,
|
||||
addr->u.inet.port,
|
||||
is_listen ? ",server" : "");
|
||||
break;
|
||||
case SOCKET_ADDRESS_TYPE_UNIX:
|
||||
return g_strdup_printf("%sunix:%s%s", prefix,
|
||||
s->addr->u.q_unix.path,
|
||||
s->is_listen ? ",server" : "");
|
||||
addr->u.q_unix.path,
|
||||
is_listen ? ",server" : "");
|
||||
break;
|
||||
case SOCKET_ADDRESS_TYPE_FD:
|
||||
return g_strdup_printf("%sfd:%s%s", prefix, s->addr->u.fd.str,
|
||||
s->is_listen ? ",server" : "");
|
||||
return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.str,
|
||||
is_listen ? ",server" : "");
|
||||
break;
|
||||
case SOCKET_ADDRESS_TYPE_VSOCK:
|
||||
return g_strdup_printf("%svsock:%s:%s", prefix,
|
||||
s->addr->u.vsock.cid,
|
||||
s->addr->u.vsock.port);
|
||||
addr->u.vsock.cid,
|
||||
addr->u.vsock.port);
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
@@ -433,11 +419,8 @@ static void update_disconnected_filename(SocketChardev *s)
|
||||
Chardev *chr = CHARDEV(s);
|
||||
|
||||
g_free(chr->filename);
|
||||
if (s->addr) {
|
||||
chr->filename = qemu_chr_socket_address(s, "disconnected:");
|
||||
} else {
|
||||
chr->filename = g_strdup("disconnected:socket");
|
||||
}
|
||||
chr->filename = SocketAddress_to_str("disconnected:", s->addr,
|
||||
s->is_listen, s->is_telnet);
|
||||
}
|
||||
|
||||
/* NB may be called even if tcp_chr_connect has not been
|
||||
@@ -523,12 +506,10 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len)
|
||||
return size;
|
||||
}
|
||||
|
||||
static char *qemu_chr_compute_filename(SocketChardev *s)
|
||||
static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len,
|
||||
struct sockaddr_storage *ps, socklen_t ps_len,
|
||||
bool is_listen, bool is_telnet)
|
||||
{
|
||||
struct sockaddr_storage *ss = &s->sioc->localAddr;
|
||||
struct sockaddr_storage *ps = &s->sioc->remoteAddr;
|
||||
socklen_t ss_len = s->sioc->localAddrLen;
|
||||
socklen_t ps_len = s->sioc->remoteAddrLen;
|
||||
char shost[NI_MAXHOST], sserv[NI_MAXSERV];
|
||||
char phost[NI_MAXHOST], pserv[NI_MAXSERV];
|
||||
const char *left = "", *right = "";
|
||||
@@ -538,7 +519,7 @@ static char *qemu_chr_compute_filename(SocketChardev *s)
|
||||
case AF_UNIX:
|
||||
return g_strdup_printf("unix:%s%s",
|
||||
((struct sockaddr_un *)(ss))->sun_path,
|
||||
s->is_listen ? ",server" : "");
|
||||
is_listen ? ",server" : "");
|
||||
#endif
|
||||
case AF_INET6:
|
||||
left = "[";
|
||||
@@ -550,9 +531,9 @@ static char *qemu_chr_compute_filename(SocketChardev *s)
|
||||
getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost),
|
||||
pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s",
|
||||
qemu_chr_socket_protocol(s),
|
||||
is_telnet ? "telnet" : "tcp",
|
||||
left, shost, right, sserv,
|
||||
s->is_listen ? ",server" : "",
|
||||
is_listen ? ",server" : "",
|
||||
left, phost, right, pserv);
|
||||
|
||||
default:
|
||||
@@ -560,37 +541,28 @@ static char *qemu_chr_compute_filename(SocketChardev *s)
|
||||
}
|
||||
}
|
||||
|
||||
static void update_ioc_handlers(SocketChardev *s)
|
||||
{
|
||||
Chardev *chr = CHARDEV(s);
|
||||
|
||||
if (!s->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read, chr,
|
||||
chr->gcontext);
|
||||
|
||||
remove_hup_source(s);
|
||||
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
|
||||
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
|
||||
chr, NULL);
|
||||
g_source_attach(s->hup_source, chr->gcontext);
|
||||
}
|
||||
|
||||
static void tcp_chr_connect(void *opaque)
|
||||
{
|
||||
Chardev *chr = CHARDEV(opaque);
|
||||
SocketChardev *s = SOCKET_CHARDEV(opaque);
|
||||
|
||||
g_free(chr->filename);
|
||||
chr->filename = qemu_chr_compute_filename(s);
|
||||
chr->filename = sockaddr_to_str(
|
||||
&s->sioc->localAddr, s->sioc->localAddrLen,
|
||||
&s->sioc->remoteAddr, s->sioc->remoteAddrLen,
|
||||
s->is_listen, s->is_telnet);
|
||||
|
||||
s->connected = 1;
|
||||
update_ioc_handlers(s);
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read,
|
||||
chr, chr->gcontext);
|
||||
|
||||
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
|
||||
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
|
||||
chr, NULL);
|
||||
g_source_attach(s->hup_source, chr->gcontext);
|
||||
|
||||
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
|
||||
}
|
||||
|
||||
@@ -621,7 +593,17 @@ static void tcp_chr_update_read_handler(Chardev *chr)
|
||||
tcp_chr_telnet_init(CHARDEV(s));
|
||||
}
|
||||
|
||||
update_ioc_handlers(s);
|
||||
if (!s->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
remove_fd_in_watch(chr);
|
||||
if (s->ioc) {
|
||||
chr->gsource = io_add_watch_poll(chr, s->ioc,
|
||||
tcp_chr_read_poll,
|
||||
tcp_chr_read, chr,
|
||||
chr->gcontext);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
|
||||
@@ -717,7 +699,8 @@ cont:
|
||||
}
|
||||
|
||||
|
||||
static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data)
|
||||
static void tcp_chr_tls_handshake(QIOTask *task,
|
||||
gpointer user_data)
|
||||
{
|
||||
Chardev *chr = user_data;
|
||||
SocketChardev *s = user_data;
|
||||
@@ -734,44 +717,6 @@ static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data)
|
||||
}
|
||||
|
||||
|
||||
static void tcp_chr_websock_init(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
QIOChannelWebsock *wioc = NULL;
|
||||
gchar *name;
|
||||
|
||||
wioc = qio_channel_websock_new_server(s->ioc);
|
||||
|
||||
name = g_strdup_printf("chardev-websocket-server-%s", chr->label);
|
||||
qio_channel_set_name(QIO_CHANNEL(wioc), name);
|
||||
g_free(name);
|
||||
object_unref(OBJECT(s->ioc));
|
||||
s->ioc = QIO_CHANNEL(wioc);
|
||||
|
||||
qio_channel_websock_handshake(wioc, tcp_chr_websock_handshake, chr, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void tcp_chr_tls_handshake(QIOTask *task,
|
||||
gpointer user_data)
|
||||
{
|
||||
Chardev *chr = user_data;
|
||||
SocketChardev *s = user_data;
|
||||
|
||||
if (qio_task_propagate_error(task, NULL)) {
|
||||
tcp_chr_disconnect(chr);
|
||||
} else {
|
||||
if (s->is_websock) {
|
||||
tcp_chr_websock_init(chr);
|
||||
} else if (s->do_telnetopt) {
|
||||
tcp_chr_telnet_init(chr);
|
||||
} else {
|
||||
tcp_chr_connect(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void tcp_chr_tls_init(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
@@ -779,6 +724,11 @@ static void tcp_chr_tls_init(Chardev *chr)
|
||||
Error *err = NULL;
|
||||
gchar *name;
|
||||
|
||||
if (!machine_init_done) {
|
||||
/* This will be postponed to machine_done notifier */
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->is_listen) {
|
||||
tioc = qio_channel_tls_new_server(
|
||||
s->ioc, s->tls_creds,
|
||||
@@ -849,12 +799,12 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
|
||||
|
||||
if (s->tls_creds) {
|
||||
tcp_chr_tls_init(chr);
|
||||
} else if (s->is_websock) {
|
||||
tcp_chr_websock_init(chr);
|
||||
} else if (s->do_telnetopt) {
|
||||
tcp_chr_telnet_init(chr);
|
||||
} else {
|
||||
tcp_chr_connect(chr);
|
||||
if (s->do_telnetopt) {
|
||||
tcp_chr_telnet_init(chr);
|
||||
} else {
|
||||
tcp_chr_connect(chr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -999,20 +949,13 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
bool is_telnet = sock->has_telnet ? sock->telnet : false;
|
||||
bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false;
|
||||
bool is_waitconnect = sock->has_wait ? sock->wait : false;
|
||||
bool is_websock = sock->has_websocket ? sock->websocket : false;
|
||||
int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0;
|
||||
QIOChannelSocket *sioc = NULL;
|
||||
SocketAddress *addr;
|
||||
|
||||
if (!is_listen && is_websock) {
|
||||
error_setg(errp, "%s", "Websocket client is not implemented");
|
||||
goto error;
|
||||
}
|
||||
|
||||
s->is_listen = is_listen;
|
||||
s->is_telnet = is_telnet;
|
||||
s->is_tn3270 = is_tn3270;
|
||||
s->is_websock = is_websock;
|
||||
s->do_nodelay = do_nodelay;
|
||||
if (sock->tls_creds) {
|
||||
Object *creds;
|
||||
@@ -1049,10 +992,6 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
|
||||
s->addr = addr = socket_address_flatten(sock->addr);
|
||||
|
||||
if (sock->has_reconnect && addr->type == SOCKET_ADDRESS_TYPE_FD) {
|
||||
error_setg(errp, "'reconnect' option is incompatible with 'fd'");
|
||||
goto error;
|
||||
}
|
||||
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE);
|
||||
/* TODO SOCKET_ADDRESS_FD where fd has AF_UNIX */
|
||||
if (addr->type == SOCKET_ADDRESS_TYPE_UNIX) {
|
||||
@@ -1072,9 +1011,8 @@ static void qmp_chardev_open_socket(Chardev *chr,
|
||||
s->reconnect_time = reconnect;
|
||||
}
|
||||
|
||||
if (s->reconnect_time) {
|
||||
tcp_chr_connect_async(chr);
|
||||
} else {
|
||||
/* If reconnect_time is set, will do that in chr_machine_done. */
|
||||
if (!s->reconnect_time) {
|
||||
if (s->is_listen) {
|
||||
char *name;
|
||||
s->listener = qio_net_listener_new();
|
||||
@@ -1123,7 +1061,6 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true);
|
||||
bool is_telnet = qemu_opt_get_bool(opts, "telnet", false);
|
||||
bool is_tn3270 = qemu_opt_get_bool(opts, "tn3270", false);
|
||||
bool is_websock = qemu_opt_get_bool(opts, "websocket", false);
|
||||
bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true);
|
||||
int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0);
|
||||
const char *path = qemu_opt_get(opts, "path");
|
||||
@@ -1172,11 +1109,9 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
|
||||
sock->telnet = is_telnet;
|
||||
sock->has_tn3270 = true;
|
||||
sock->tn3270 = is_tn3270;
|
||||
sock->has_websocket = true;
|
||||
sock->websocket = is_websock;
|
||||
sock->has_wait = true;
|
||||
sock->wait = is_waitconnect;
|
||||
sock->has_reconnect = qemu_opt_find(opts, "reconnect");
|
||||
sock->has_reconnect = true;
|
||||
sock->reconnect = reconnect;
|
||||
sock->tls_creds = g_strdup(tls_creds);
|
||||
|
||||
@@ -1226,6 +1161,21 @@ char_socket_get_connected(Object *obj, Error **errp)
|
||||
return s->connected;
|
||||
}
|
||||
|
||||
static int tcp_chr_machine_done_hook(Chardev *chr)
|
||||
{
|
||||
SocketChardev *s = SOCKET_CHARDEV(chr);
|
||||
|
||||
if (s->reconnect_time) {
|
||||
tcp_chr_connect_async(chr);
|
||||
}
|
||||
|
||||
if (s->ioc && s->tls_creds) {
|
||||
tcp_chr_tls_init(chr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
ChardevClass *cc = CHARDEV_CLASS(oc);
|
||||
@@ -1241,6 +1191,7 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
|
||||
cc->chr_add_client = tcp_chr_add_client;
|
||||
cc->chr_add_watch = tcp_chr_add_watch;
|
||||
cc->chr_update_read_handler = tcp_chr_update_read_handler;
|
||||
cc->chr_machine_done = tcp_chr_machine_done_hook;
|
||||
|
||||
object_class_property_add(oc, "addr", "SocketAddress",
|
||||
char_socket_get_addr, NULL,
|
||||
|
||||
+10
-39
@@ -329,8 +329,7 @@ int qemu_chr_wait_connected(Chardev *chr, Error **errp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
|
||||
bool permit_mux_mon)
|
||||
QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename)
|
||||
{
|
||||
char host[65], port[33], width[8], height[8];
|
||||
int pos;
|
||||
@@ -345,10 +344,6 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
|
||||
}
|
||||
|
||||
if (strstart(filename, "mon:", &p)) {
|
||||
if (!permit_mux_mon) {
|
||||
error_report("mon: isn't supported in this context");
|
||||
return NULL;
|
||||
}
|
||||
filename = p;
|
||||
qemu_opt_set(opts, "mux", "on", &error_abort);
|
||||
if (strcmp(filename, "stdio") == 0) {
|
||||
@@ -409,8 +404,7 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
|
||||
}
|
||||
if (strstart(filename, "tcp:", &p) ||
|
||||
strstart(filename, "telnet:", &p) ||
|
||||
strstart(filename, "tn3270:", &p) ||
|
||||
strstart(filename, "websocket:", &p)) {
|
||||
strstart(filename, "tn3270:", &p)) {
|
||||
if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) {
|
||||
host[0] = 0;
|
||||
if (sscanf(p, ":%32[^,]%n", port, &pos) < 1)
|
||||
@@ -430,8 +424,6 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename,
|
||||
qemu_opt_set(opts, "telnet", "on", &error_abort);
|
||||
} else if (strstart(filename, "tn3270:", &p)) {
|
||||
qemu_opt_set(opts, "tn3270", "on", &error_abort);
|
||||
} else if (strstart(filename, "websocket:", &p)) {
|
||||
qemu_opt_set(opts, "websocket", "on", &error_abort);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
@@ -572,7 +564,7 @@ help_string_append(const char *name, void *opaque)
|
||||
{
|
||||
GString *str = opaque;
|
||||
|
||||
g_string_append_printf(str, "\n %s", name);
|
||||
g_string_append_printf(str, "\n%s", name);
|
||||
}
|
||||
|
||||
static const char *chardev_alias_translate(const char *name)
|
||||
@@ -637,7 +629,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp)
|
||||
|
||||
chardev_name_foreach(help_string_append, str);
|
||||
|
||||
error_printf("Available chardev backend types: %s\n", str->str);
|
||||
error_report("Available chardev backend types: %s", str->str);
|
||||
g_string_free(str, true);
|
||||
return NULL;
|
||||
}
|
||||
@@ -691,8 +683,7 @@ out:
|
||||
return chr;
|
||||
}
|
||||
|
||||
Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
|
||||
bool permit_mux_mon)
|
||||
Chardev *qemu_chr_new_noreplay(const char *label, const char *filename)
|
||||
{
|
||||
const char *p;
|
||||
Chardev *chr;
|
||||
@@ -703,32 +694,25 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename,
|
||||
return qemu_chr_find(p);
|
||||
}
|
||||
|
||||
opts = qemu_chr_parse_compat(label, filename, permit_mux_mon);
|
||||
opts = qemu_chr_parse_compat(label, filename);
|
||||
if (!opts)
|
||||
return NULL;
|
||||
|
||||
chr = qemu_chr_new_from_opts(opts, &err);
|
||||
if (!chr) {
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qemu_opt_get_bool(opts, "mux", 0)) {
|
||||
assert(permit_mux_mon);
|
||||
if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
|
||||
monitor_init(chr, MONITOR_USE_READLINE);
|
||||
}
|
||||
|
||||
out:
|
||||
qemu_opts_del(opts);
|
||||
return chr;
|
||||
}
|
||||
|
||||
static Chardev *qemu_chr_new_permit_mux_mon(const char *label,
|
||||
const char *filename,
|
||||
bool permit_mux_mon)
|
||||
Chardev *qemu_chr_new(const char *label, const char *filename)
|
||||
{
|
||||
Chardev *chr;
|
||||
chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon);
|
||||
chr = qemu_chr_new_noreplay(label, filename);
|
||||
if (chr) {
|
||||
if (replay_mode != REPLAY_MODE_NONE) {
|
||||
qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY);
|
||||
@@ -742,16 +726,6 @@ static Chardev *qemu_chr_new_permit_mux_mon(const char *label,
|
||||
return chr;
|
||||
}
|
||||
|
||||
Chardev *qemu_chr_new(const char *label, const char *filename)
|
||||
{
|
||||
return qemu_chr_new_permit_mux_mon(label, filename, false);
|
||||
}
|
||||
|
||||
Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename)
|
||||
{
|
||||
return qemu_chr_new_permit_mux_mon(label, filename, true);
|
||||
}
|
||||
|
||||
static int qmp_query_chardev_foreach(Object *obj, void *data)
|
||||
{
|
||||
Chardev *chr = CHARDEV(obj);
|
||||
@@ -863,9 +837,6 @@ QemuOptsList qemu_chardev_opts = {
|
||||
},{
|
||||
.name = "tls-creds",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},{
|
||||
.name = "websocket",
|
||||
.type = QEMU_OPT_BOOL,
|
||||
},{
|
||||
.name = "width",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
elf2dmp-obj-y = main.o addrspace.o download.o pdb.o qemu_elf.o
|
||||
@@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "addrspace.h"
|
||||
|
||||
static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < ps->block_nr; i++) {
|
||||
if (ps->block[i].paddr <= pa &&
|
||||
pa <= ps->block[i].paddr + ps->block[i].size) {
|
||||
return ps->block + i;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa)
|
||||
{
|
||||
struct pa_block *block = pa_space_find_block(ps, pa);
|
||||
|
||||
if (!block) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return block->addr + (pa - block->paddr);
|
||||
}
|
||||
|
||||
int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf)
|
||||
{
|
||||
Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map);
|
||||
Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map);
|
||||
size_t block_i = 0;
|
||||
size_t i;
|
||||
|
||||
ps->block_nr = 0;
|
||||
|
||||
for (i = 0; i < phdr_nr; i++) {
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
ps->block_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
ps->block = malloc(sizeof(*ps->block) * ps->block_nr);
|
||||
if (!ps->block) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < phdr_nr; i++) {
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
ps->block[block_i] = (struct pa_block) {
|
||||
.addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset,
|
||||
.paddr = phdr[i].p_paddr,
|
||||
.size = phdr[i].p_filesz,
|
||||
};
|
||||
block_i++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pa_space_destroy(struct pa_space *ps)
|
||||
{
|
||||
ps->block_nr = 0;
|
||||
free(ps->block);
|
||||
}
|
||||
|
||||
void va_space_set_dtb(struct va_space *vs, uint64_t dtb)
|
||||
{
|
||||
vs->dtb = dtb & 0x00ffffffffff000;
|
||||
}
|
||||
|
||||
void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb)
|
||||
{
|
||||
vs->ps = ps;
|
||||
va_space_set_dtb(vs, dtb);
|
||||
}
|
||||
|
||||
static uint64_t get_pml4e(struct va_space *vs, uint64_t va)
|
||||
{
|
||||
uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36);
|
||||
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pa);
|
||||
}
|
||||
|
||||
static uint64_t get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e)
|
||||
{
|
||||
uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) |
|
||||
((va & 0x7FC0000000) >> 27);
|
||||
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pdpte_paddr);
|
||||
}
|
||||
|
||||
static uint64_t pde_index(uint64_t va)
|
||||
{
|
||||
return (va >> 21) & 0x1FF;
|
||||
}
|
||||
|
||||
static uint64_t pdba_base(uint64_t pdpe)
|
||||
{
|
||||
return pdpe & 0xFFFFFFFFFF000;
|
||||
}
|
||||
|
||||
static uint64_t get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe)
|
||||
{
|
||||
uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8;
|
||||
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pgd_entry);
|
||||
}
|
||||
|
||||
static uint64_t pte_index(uint64_t va)
|
||||
{
|
||||
return (va >> 12) & 0x1FF;
|
||||
}
|
||||
|
||||
static uint64_t ptba_base(uint64_t pde)
|
||||
{
|
||||
return pde & 0xFFFFFFFFFF000;
|
||||
}
|
||||
|
||||
static uint64_t get_pte(struct va_space *vs, uint64_t va, uint64_t pgd)
|
||||
{
|
||||
uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8;
|
||||
|
||||
return *(uint64_t *)pa_space_resolve(vs->ps, pgd_val);
|
||||
}
|
||||
|
||||
static uint64_t get_paddr(uint64_t va, uint64_t pte)
|
||||
{
|
||||
return (pte & 0xFFFFFFFFFF000) | (va & 0xFFF);
|
||||
}
|
||||
|
||||
static bool is_present(uint64_t entry)
|
||||
{
|
||||
return entry & 0x1;
|
||||
}
|
||||
|
||||
static bool page_size_flag(uint64_t entry)
|
||||
{
|
||||
return entry & (1 << 7);
|
||||
}
|
||||
|
||||
static uint64_t get_1GB_paddr(uint64_t va, uint64_t pdpte)
|
||||
{
|
||||
return (pdpte & 0xfffffc0000000) | (va & 0x3fffffff);
|
||||
}
|
||||
|
||||
static uint64_t get_2MB_paddr(uint64_t va, uint64_t pgd_entry)
|
||||
{
|
||||
return (pgd_entry & 0xfffffffe00000) | (va & 0x00000001fffff);
|
||||
}
|
||||
|
||||
static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va)
|
||||
{
|
||||
uint64_t pml4e, pdpe, pgd, pte;
|
||||
|
||||
pml4e = get_pml4e(vs, va);
|
||||
if (!is_present(pml4e)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
pdpe = get_pdpi(vs, va, pml4e);
|
||||
if (!is_present(pdpe)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
if (page_size_flag(pdpe)) {
|
||||
return get_1GB_paddr(va, pdpe);
|
||||
}
|
||||
|
||||
pgd = get_pgd(vs, va, pdpe);
|
||||
if (!is_present(pgd)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
if (page_size_flag(pgd)) {
|
||||
return get_2MB_paddr(va, pgd);
|
||||
}
|
||||
|
||||
pte = get_pte(vs, va, pgd);
|
||||
if (!is_present(pte)) {
|
||||
return INVALID_PA;
|
||||
}
|
||||
|
||||
return get_paddr(va, pte);
|
||||
}
|
||||
|
||||
void *va_space_resolve(struct va_space *vs, uint64_t va)
|
||||
{
|
||||
uint64_t pa = va_space_va2pa(vs, va);
|
||||
|
||||
if (pa == INVALID_PA) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pa_space_resolve(vs->ps, pa);
|
||||
}
|
||||
|
||||
int va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
void *buf, size_t size, int is_write)
|
||||
{
|
||||
while (size) {
|
||||
uint64_t page = addr & PFN_MASK;
|
||||
size_t s = (page + PAGE_SIZE) - addr;
|
||||
void *ptr;
|
||||
|
||||
s = (s > size) ? size : s;
|
||||
|
||||
ptr = va_space_resolve(vs, addr);
|
||||
if (!ptr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (is_write) {
|
||||
memcpy(ptr, buf, s);
|
||||
} else {
|
||||
memcpy(buf, ptr, s);
|
||||
}
|
||||
|
||||
size -= s;
|
||||
buf = (uint8_t *)buf + s;
|
||||
addr += s;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ADDRSPACE_H
|
||||
#define ADDRSPACE_H
|
||||
|
||||
#include "qemu_elf.h"
|
||||
|
||||
#define PAGE_BITS 12
|
||||
#define PAGE_SIZE (1ULL << PAGE_BITS)
|
||||
#define PFN_MASK (~(PAGE_SIZE - 1))
|
||||
|
||||
#define INVALID_PA UINT64_MAX
|
||||
|
||||
struct pa_block {
|
||||
uint8_t *addr;
|
||||
uint64_t paddr;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct pa_space {
|
||||
size_t block_nr;
|
||||
struct pa_block *block;
|
||||
};
|
||||
|
||||
struct va_space {
|
||||
uint64_t dtb;
|
||||
struct pa_space *ps;
|
||||
};
|
||||
|
||||
int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf);
|
||||
void pa_space_destroy(struct pa_space *ps);
|
||||
|
||||
void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb);
|
||||
void va_space_set_dtb(struct va_space *vs, uint64_t dtb);
|
||||
void *va_space_resolve(struct va_space *vs, uint64_t va);
|
||||
int va_space_rw(struct va_space *vs, uint64_t addr,
|
||||
void *buf, size_t size, int is_write);
|
||||
|
||||
#endif /* ADDRSPACE_H */
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <curl/curl.h>
|
||||
#include "download.h"
|
||||
|
||||
int download_url(const char *name, const char *url)
|
||||
{
|
||||
int err = 0;
|
||||
FILE *file;
|
||||
CURL *curl = curl_easy_init();
|
||||
|
||||
if (!curl) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
file = fopen(name, "wb");
|
||||
if (!file) {
|
||||
err = 1;
|
||||
goto out_curl;
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
|
||||
|
||||
if (curl_easy_perform(curl) != CURLE_OK) {
|
||||
err = 1;
|
||||
fclose(file);
|
||||
unlink(name);
|
||||
goto out_curl;
|
||||
}
|
||||
|
||||
err = fclose(file);
|
||||
|
||||
out_curl:
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef DOWNLOAD_H
|
||||
#define DOWNLOAD_H
|
||||
|
||||
int download_url(const char *name, const char *url);
|
||||
|
||||
#endif /* DOWNLOAD_H */
|
||||
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ERR_H
|
||||
#define ERR_H
|
||||
|
||||
#define eprintf(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
#endif /* ERR_H */
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef KDBG_H
|
||||
#define KDBG_H
|
||||
|
||||
typedef struct DBGKD_GET_VERSION64 {
|
||||
uint16_t MajorVersion;
|
||||
uint16_t MinorVersion;
|
||||
uint8_t ProtocolVersion;
|
||||
uint8_t KdSecondaryVersion;
|
||||
uint16_t Flags;
|
||||
uint16_t MachineType;
|
||||
uint8_t MaxPacketType;
|
||||
uint8_t MaxStateChange;
|
||||
uint8_t MaxManipulate;
|
||||
uint8_t Simulation;
|
||||
uint16_t Unused[1];
|
||||
uint64_t KernBase;
|
||||
uint64_t PsLoadedModuleList;
|
||||
uint64_t DebuggerDataList;
|
||||
} DBGKD_GET_VERSION64;
|
||||
|
||||
typedef struct DBGKD_DEBUG_DATA_HEADER64 {
|
||||
struct LIST_ENTRY64 {
|
||||
struct LIST_ENTRY64 *Flink;
|
||||
struct LIST_ENTRY64 *Blink;
|
||||
} List;
|
||||
uint32_t OwnerTag;
|
||||
uint32_t Size;
|
||||
} DBGKD_DEBUG_DATA_HEADER64;
|
||||
|
||||
typedef struct KDDEBUGGER_DATA64 {
|
||||
DBGKD_DEBUG_DATA_HEADER64 Header;
|
||||
|
||||
uint64_t KernBase;
|
||||
uint64_t BreakpointWithStatus;
|
||||
uint64_t SavedContext;
|
||||
uint16_t ThCallbackStack;
|
||||
uint16_t NextCallback;
|
||||
uint16_t FramePointer;
|
||||
uint16_t PaeEnabled:1;
|
||||
uint64_t KiCallUserMode;
|
||||
uint64_t KeUserCallbackDispatcher;
|
||||
uint64_t PsLoadedModuleList;
|
||||
uint64_t PsActiveProcessHead;
|
||||
uint64_t PspCidTable;
|
||||
uint64_t ExpSystemResourcesList;
|
||||
uint64_t ExpPagedPoolDescriptor;
|
||||
uint64_t ExpNumberOfPagedPools;
|
||||
uint64_t KeTimeIncrement;
|
||||
uint64_t KeBugCheckCallbackListHead;
|
||||
uint64_t KiBugcheckData;
|
||||
uint64_t IopErrorLogListHead;
|
||||
uint64_t ObpRootDirectoryObject;
|
||||
uint64_t ObpTypeObjectType;
|
||||
uint64_t MmSystemCacheStart;
|
||||
uint64_t MmSystemCacheEnd;
|
||||
uint64_t MmSystemCacheWs;
|
||||
uint64_t MmPfnDatabase;
|
||||
uint64_t MmSystemPtesStart;
|
||||
uint64_t MmSystemPtesEnd;
|
||||
uint64_t MmSubsectionBase;
|
||||
uint64_t MmNumberOfPagingFiles;
|
||||
uint64_t MmLowestPhysicalPage;
|
||||
uint64_t MmHighestPhysicalPage;
|
||||
uint64_t MmNumberOfPhysicalPages;
|
||||
uint64_t MmMaximumNonPagedPoolInBytes;
|
||||
uint64_t MmNonPagedSystemStart;
|
||||
uint64_t MmNonPagedPoolStart;
|
||||
uint64_t MmNonPagedPoolEnd;
|
||||
uint64_t MmPagedPoolStart;
|
||||
uint64_t MmPagedPoolEnd;
|
||||
uint64_t MmPagedPoolInformation;
|
||||
uint64_t MmPageSize;
|
||||
uint64_t MmSizeOfPagedPoolInBytes;
|
||||
uint64_t MmTotalCommitLimit;
|
||||
uint64_t MmTotalCommittedPages;
|
||||
uint64_t MmSharedCommit;
|
||||
uint64_t MmDriverCommit;
|
||||
uint64_t MmProcessCommit;
|
||||
uint64_t MmPagedPoolCommit;
|
||||
uint64_t MmExtendedCommit;
|
||||
uint64_t MmZeroedPageListHead;
|
||||
uint64_t MmFreePageListHead;
|
||||
uint64_t MmStandbyPageListHead;
|
||||
uint64_t MmModifiedPageListHead;
|
||||
uint64_t MmModifiedNoWritePageListHead;
|
||||
uint64_t MmAvailablePages;
|
||||
uint64_t MmResidentAvailablePages;
|
||||
uint64_t PoolTrackTable;
|
||||
uint64_t NonPagedPoolDescriptor;
|
||||
uint64_t MmHighestUserAddress;
|
||||
uint64_t MmSystemRangeStart;
|
||||
uint64_t MmUserProbeAddress;
|
||||
uint64_t KdPrintCircularBuffer;
|
||||
uint64_t KdPrintCircularBufferEnd;
|
||||
uint64_t KdPrintWritePointer;
|
||||
uint64_t KdPrintRolloverCount;
|
||||
uint64_t MmLoadedUserImageList;
|
||||
|
||||
/* NT 5.1 Addition */
|
||||
|
||||
uint64_t NtBuildLab;
|
||||
uint64_t KiNormalSystemCall;
|
||||
|
||||
/* NT 5.0 hotfix addition */
|
||||
|
||||
uint64_t KiProcessorBlock;
|
||||
uint64_t MmUnloadedDrivers;
|
||||
uint64_t MmLastUnloadedDriver;
|
||||
uint64_t MmTriageActionTaken;
|
||||
uint64_t MmSpecialPoolTag;
|
||||
uint64_t KernelVerifier;
|
||||
uint64_t MmVerifierData;
|
||||
uint64_t MmAllocatedNonPagedPool;
|
||||
uint64_t MmPeakCommitment;
|
||||
uint64_t MmTotalCommitLimitMaximum;
|
||||
uint64_t CmNtCSDVersion;
|
||||
|
||||
/* NT 5.1 Addition */
|
||||
|
||||
uint64_t MmPhysicalMemoryBlock;
|
||||
uint64_t MmSessionBase;
|
||||
uint64_t MmSessionSize;
|
||||
uint64_t MmSystemParentTablePage;
|
||||
|
||||
/* Server 2003 addition */
|
||||
|
||||
uint64_t MmVirtualTranslationBase;
|
||||
uint16_t OffsetKThreadNextProcessor;
|
||||
uint16_t OffsetKThreadTeb;
|
||||
uint16_t OffsetKThreadKernelStack;
|
||||
uint16_t OffsetKThreadInitialStack;
|
||||
uint16_t OffsetKThreadApcProcess;
|
||||
uint16_t OffsetKThreadState;
|
||||
uint16_t OffsetKThreadBStore;
|
||||
uint16_t OffsetKThreadBStoreLimit;
|
||||
uint16_t SizeEProcess;
|
||||
uint16_t OffsetEprocessPeb;
|
||||
uint16_t OffsetEprocessParentCID;
|
||||
uint16_t OffsetEprocessDirectoryTableBase;
|
||||
uint16_t SizePrcb;
|
||||
uint16_t OffsetPrcbDpcRoutine;
|
||||
uint16_t OffsetPrcbCurrentThread;
|
||||
uint16_t OffsetPrcbMhz;
|
||||
uint16_t OffsetPrcbCpuType;
|
||||
uint16_t OffsetPrcbVendorString;
|
||||
uint16_t OffsetPrcbProcStateContext;
|
||||
uint16_t OffsetPrcbNumber;
|
||||
uint16_t SizeEThread;
|
||||
uint64_t KdPrintCircularBufferPtr;
|
||||
uint64_t KdPrintBufferSize;
|
||||
uint64_t KeLoaderBlock;
|
||||
uint16_t SizePcr;
|
||||
uint16_t OffsetPcrSelfPcr;
|
||||
uint16_t OffsetPcrCurrentPrcb;
|
||||
uint16_t OffsetPcrContainedPrcb;
|
||||
uint16_t OffsetPcrInitialBStore;
|
||||
uint16_t OffsetPcrBStoreLimit;
|
||||
uint16_t OffsetPcrInitialStack;
|
||||
uint16_t OffsetPcrStackLimit;
|
||||
uint16_t OffsetPrcbPcrPage;
|
||||
uint16_t OffsetPrcbProcStateSpecialReg;
|
||||
uint16_t GdtR0Code;
|
||||
uint16_t GdtR0Data;
|
||||
uint16_t GdtR0Pcr;
|
||||
uint16_t GdtR3Code;
|
||||
uint16_t GdtR3Data;
|
||||
uint16_t GdtR3Teb;
|
||||
uint16_t GdtLdt;
|
||||
uint16_t GdtTss;
|
||||
uint16_t Gdt64R3CmCode;
|
||||
uint16_t Gdt64R3CmTeb;
|
||||
uint64_t IopNumTriageDumpDataBlocks;
|
||||
uint64_t IopTriageDumpDataBlocks;
|
||||
|
||||
/* Longhorn addition */
|
||||
|
||||
uint64_t VfCrashDataBlock;
|
||||
uint64_t MmBadPagesDetected;
|
||||
uint64_t MmZeroedPageSingleBitErrorsDetected;
|
||||
|
||||
/* Windows 7 addition */
|
||||
|
||||
uint64_t EtwpDebuggerData;
|
||||
uint16_t OffsetPrcbContext;
|
||||
} KDDEBUGGER_DATA64;
|
||||
|
||||
#endif /* KDBG_H */
|
||||
@@ -1,589 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "err.h"
|
||||
#include "addrspace.h"
|
||||
#include "pe.h"
|
||||
#include "pdb.h"
|
||||
#include "kdbg.h"
|
||||
#include "download.h"
|
||||
#include "qemu/win_dump_defs.h"
|
||||
|
||||
#define SYM_URL_BASE "https://msdl.microsoft.com/download/symbols/"
|
||||
#define PDB_NAME "ntkrnlmp.pdb"
|
||||
|
||||
#define INITIAL_MXCSR 0x1f80
|
||||
|
||||
typedef struct idt_desc {
|
||||
uint16_t offset1; /* offset bits 0..15 */
|
||||
uint16_t selector;
|
||||
uint8_t ist;
|
||||
uint8_t type_attr;
|
||||
uint16_t offset2; /* offset bits 16..31 */
|
||||
uint32_t offset3; /* offset bits 32..63 */
|
||||
uint32_t rsrvd;
|
||||
} __attribute__ ((packed)) idt_desc_t;
|
||||
|
||||
static uint64_t idt_desc_addr(idt_desc_t desc)
|
||||
{
|
||||
return (uint64_t)desc.offset1 | ((uint64_t)desc.offset2 << 16) |
|
||||
((uint64_t)desc.offset3 << 32);
|
||||
}
|
||||
|
||||
static const uint64_t SharedUserData = 0xfffff78000000000;
|
||||
|
||||
#define KUSD_OFFSET_SUITE_MASK 0x2d0
|
||||
#define KUSD_OFFSET_PRODUCT_TYPE 0x264
|
||||
|
||||
#define SYM_RESOLVE(base, r, s) ((s = pdb_resolve(base, r, #s)),\
|
||||
s ? printf(#s" = 0x%016lx\n", s) : eprintf("Failed to resolve "#s"\n"), s)
|
||||
|
||||
static uint64_t rol(uint64_t x, uint64_t y)
|
||||
{
|
||||
return (x << y) | (x >> (64 - y));
|
||||
}
|
||||
|
||||
/*
|
||||
* Decoding algorithm can be found in Volatility project
|
||||
*/
|
||||
static void kdbg_decode(uint64_t *dst, uint64_t *src, size_t size,
|
||||
uint64_t kwn, uint64_t kwa, uint64_t kdbe)
|
||||
{
|
||||
size_t i;
|
||||
assert(size % sizeof(uint64_t) == 0);
|
||||
for (i = 0; i < size / sizeof(uint64_t); i++) {
|
||||
uint64_t block;
|
||||
|
||||
block = src[i];
|
||||
block = rol(block ^ kwn, (uint8_t)kwn);
|
||||
block = __builtin_bswap64(block ^ kdbe) ^ kwa;
|
||||
dst[i] = block;
|
||||
}
|
||||
}
|
||||
|
||||
static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb,
|
||||
struct va_space *vs, uint64_t KdDebuggerDataBlock)
|
||||
{
|
||||
const char OwnerTag[4] = "KDBG";
|
||||
KDDEBUGGER_DATA64 *kdbg = NULL;
|
||||
DBGKD_DEBUG_DATA_HEADER64 kdbg_hdr;
|
||||
bool decode = false;
|
||||
uint64_t kwn, kwa, KdpDataBlockEncoded;
|
||||
|
||||
if (va_space_rw(vs,
|
||||
KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header),
|
||||
&kdbg_hdr, sizeof(kdbg_hdr), 0)) {
|
||||
eprintf("Failed to extract KDBG header\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) {
|
||||
uint64_t KiWaitNever, KiWaitAlways;
|
||||
|
||||
decode = true;
|
||||
|
||||
if (!SYM_RESOLVE(KernBase, pdb, KiWaitNever) ||
|
||||
!SYM_RESOLVE(KernBase, pdb, KiWaitAlways) ||
|
||||
!SYM_RESOLVE(KernBase, pdb, KdpDataBlockEncoded)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) ||
|
||||
va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
printf("[KiWaitNever] = 0x%016lx\n", kwn);
|
||||
printf("[KiWaitAlways] = 0x%016lx\n", kwa);
|
||||
|
||||
/*
|
||||
* If KDBG header can be decoded, KDBG size is available
|
||||
* and entire KDBG can be decoded.
|
||||
*/
|
||||
printf("Decoding KDBG header...\n");
|
||||
kdbg_decode((uint64_t *)&kdbg_hdr, (uint64_t *)&kdbg_hdr,
|
||||
sizeof(kdbg_hdr), kwn, kwa, KdpDataBlockEncoded);
|
||||
|
||||
printf("Owner tag is \'%.4s\'\n", (char *)&kdbg_hdr.OwnerTag);
|
||||
if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) {
|
||||
eprintf("Failed to decode KDBG header\n");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
kdbg = malloc(kdbg_hdr.Size);
|
||||
if (!kdbg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) {
|
||||
eprintf("Failed to extract entire KDBG\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!decode) {
|
||||
return kdbg;
|
||||
}
|
||||
|
||||
printf("Decoding KdDebuggerDataBlock...\n");
|
||||
kdbg_decode((uint64_t *)kdbg, (uint64_t *)kdbg, kdbg_hdr.Size,
|
||||
kwn, kwa, KdpDataBlockEncoded);
|
||||
|
||||
va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 1);
|
||||
|
||||
return kdbg;
|
||||
}
|
||||
|
||||
static void win_context_init_from_qemu_cpu_state(WinContext *ctx,
|
||||
QEMUCPUState *s)
|
||||
{
|
||||
WinContext win_ctx = (WinContext){
|
||||
.ContextFlags = WIN_CTX_X64 | WIN_CTX_INT | WIN_CTX_SEG | WIN_CTX_CTL,
|
||||
.MxCsr = INITIAL_MXCSR,
|
||||
|
||||
.SegCs = s->cs.selector,
|
||||
.SegSs = s->ss.selector,
|
||||
.SegDs = s->ds.selector,
|
||||
.SegEs = s->es.selector,
|
||||
.SegFs = s->fs.selector,
|
||||
.SegGs = s->gs.selector,
|
||||
.EFlags = (uint32_t)s->rflags,
|
||||
|
||||
.Rax = s->rax,
|
||||
.Rbx = s->rbx,
|
||||
.Rcx = s->rcx,
|
||||
.Rdx = s->rdx,
|
||||
.Rsp = s->rsp,
|
||||
.Rbp = s->rbp,
|
||||
.Rsi = s->rsi,
|
||||
.Rdi = s->rdi,
|
||||
.R8 = s->r8,
|
||||
.R9 = s->r9,
|
||||
.R10 = s->r10,
|
||||
.R11 = s->r11,
|
||||
.R12 = s->r12,
|
||||
.R13 = s->r13,
|
||||
.R14 = s->r14,
|
||||
.R15 = s->r15,
|
||||
|
||||
.Rip = s->rip,
|
||||
.FltSave = {
|
||||
.MxCsr = INITIAL_MXCSR,
|
||||
},
|
||||
};
|
||||
|
||||
*ctx = win_ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds paging-structure hierarchy base,
|
||||
* if previously set doesn't give access to kernel structures
|
||||
*/
|
||||
static int fix_dtb(struct va_space *vs, QEMU_Elf *qe)
|
||||
{
|
||||
/*
|
||||
* Firstly, test previously set DTB.
|
||||
*/
|
||||
if (va_space_resolve(vs, SharedUserData)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Secondly, find CPU which run system task.
|
||||
*/
|
||||
size_t i;
|
||||
for (i = 0; i < qe->state_nr; i++) {
|
||||
QEMUCPUState *s = qe->state[i];
|
||||
|
||||
if (is_system(s)) {
|
||||
va_space_set_dtb(vs, s->cr[3]);
|
||||
printf("DTB 0x%016lx has been found from CPU #%zu"
|
||||
" as system task CR3\n", vs->dtb, i);
|
||||
return !(va_space_resolve(vs, SharedUserData));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Thirdly, use KERNEL_GS_BASE from CPU #0 as PRCB address and
|
||||
* CR3 as [Prcb+0x7000]
|
||||
*/
|
||||
if (qe->has_kernel_gs_base) {
|
||||
QEMUCPUState *s = qe->state[0];
|
||||
uint64_t Prcb = s->kernel_gs_base;
|
||||
uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000);
|
||||
|
||||
if (!cr3) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
va_space_set_dtb(vs, *cr3);
|
||||
printf("DirectoryTableBase = 0x%016lx has been found from CPU #0"
|
||||
" as interrupt handling CR3\n", vs->dtb);
|
||||
return !(va_space_resolve(vs, SharedUserData));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps,
|
||||
struct va_space *vs, uint64_t KdDebuggerDataBlock,
|
||||
KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus)
|
||||
{
|
||||
uint32_t *suite_mask = va_space_resolve(vs, SharedUserData +
|
||||
KUSD_OFFSET_SUITE_MASK);
|
||||
int32_t *product_type = va_space_resolve(vs, SharedUserData +
|
||||
KUSD_OFFSET_PRODUCT_TYPE);
|
||||
DBGKD_GET_VERSION64 kvb;
|
||||
WinDumpHeader64 h;
|
||||
size_t i;
|
||||
|
||||
QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= PAGE_SIZE);
|
||||
QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= PAGE_SIZE);
|
||||
|
||||
if (!suite_mask || !product_type) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) {
|
||||
eprintf("Failed to extract KdVersionBlock\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
h = (WinDumpHeader64) {
|
||||
.Signature = "PAGE",
|
||||
.ValidDump = "DU64",
|
||||
.MajorVersion = kvb.MajorVersion,
|
||||
.MinorVersion = kvb.MinorVersion,
|
||||
.DirectoryTableBase = vs->dtb,
|
||||
.PfnDatabase = kdbg->MmPfnDatabase,
|
||||
.PsLoadedModuleList = kdbg->PsLoadedModuleList,
|
||||
.PsActiveProcessHead = kdbg->PsActiveProcessHead,
|
||||
.MachineImageType = kvb.MachineType,
|
||||
.NumberProcessors = nr_cpus,
|
||||
.BugcheckCode = LIVE_SYSTEM_DUMP,
|
||||
.KdDebuggerDataBlock = KdDebuggerDataBlock,
|
||||
.DumpType = 1,
|
||||
.Comment = "Hello from elf2dmp!",
|
||||
.SuiteMask = *suite_mask,
|
||||
.ProductType = *product_type,
|
||||
.SecondaryDataState = kvb.KdSecondaryVersion,
|
||||
.PhysicalMemoryBlock = (WinDumpPhyMemDesc64) {
|
||||
.NumberOfRuns = ps->block_nr,
|
||||
},
|
||||
.RequiredDumpSpace = sizeof(h),
|
||||
};
|
||||
|
||||
for (i = 0; i < ps->block_nr; i++) {
|
||||
h.PhysicalMemoryBlock.NumberOfPages += ps->block[i].size / PAGE_SIZE;
|
||||
h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) {
|
||||
.BasePage = ps->block[i].paddr / PAGE_SIZE,
|
||||
.PageCount = ps->block[i].size / PAGE_SIZE,
|
||||
};
|
||||
}
|
||||
|
||||
h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << PAGE_BITS;
|
||||
|
||||
*hdr = h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_context(KDDEBUGGER_DATA64 *kdbg,
|
||||
struct va_space *vs, QEMU_Elf *qe)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < qe->state_nr; i++) {
|
||||
uint64_t Prcb;
|
||||
uint64_t Context;
|
||||
WinContext ctx;
|
||||
QEMUCPUState *s = qe->state[i];
|
||||
|
||||
if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i,
|
||||
&Prcb, sizeof(Prcb), 0)) {
|
||||
eprintf("Failed to read CPU #%d PRCB location\n", i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext,
|
||||
&Context, sizeof(Context), 0)) {
|
||||
eprintf("Failed to read CPU #%d ContextFrame location\n", i);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Filling context for CPU #%d...\n", i);
|
||||
win_context_init_from_qemu_cpu_state(&ctx, s);
|
||||
|
||||
if (va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) {
|
||||
eprintf("Failed to fill CPU #%d context\n", i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_dump(struct pa_space *ps,
|
||||
WinDumpHeader64 *hdr, const char *name)
|
||||
{
|
||||
FILE *dmp_file = fopen(name, "wb");
|
||||
size_t i;
|
||||
|
||||
if (!dmp_file) {
|
||||
eprintf("Failed to open output file \'%s\'\n", name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Writing header to file...\n");
|
||||
|
||||
if (fwrite(hdr, sizeof(*hdr), 1, dmp_file) != 1) {
|
||||
eprintf("Failed to write dump header\n");
|
||||
fclose(dmp_file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < ps->block_nr; i++) {
|
||||
struct pa_block *b = &ps->block[i];
|
||||
|
||||
printf("Writing block #%zu/%zu to file...\n", i, ps->block_nr);
|
||||
if (fwrite(b->addr, b->size, 1, dmp_file) != 1) {
|
||||
eprintf("Failed to write dump header\n");
|
||||
fclose(dmp_file);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return fclose(dmp_file);
|
||||
}
|
||||
|
||||
static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr,
|
||||
char *hash, struct va_space *vs)
|
||||
{
|
||||
const char e_magic[2] = "MZ";
|
||||
const char Signature[4] = "PE\0\0";
|
||||
const char sign_rsds[4] = "RSDS";
|
||||
IMAGE_DOS_HEADER *dos_hdr = start_addr;
|
||||
IMAGE_NT_HEADERS64 nt_hdrs;
|
||||
IMAGE_FILE_HEADER *file_hdr = &nt_hdrs.FileHeader;
|
||||
IMAGE_OPTIONAL_HEADER64 *opt_hdr = &nt_hdrs.OptionalHeader;
|
||||
IMAGE_DATA_DIRECTORY *data_dir = nt_hdrs.OptionalHeader.DataDirectory;
|
||||
IMAGE_DEBUG_DIRECTORY debug_dir;
|
||||
OMFSignatureRSDS rsds;
|
||||
char *pdb_name;
|
||||
size_t pdb_name_sz;
|
||||
size_t i;
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= PAGE_SIZE);
|
||||
|
||||
if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, base + dos_hdr->e_lfanew,
|
||||
&nt_hdrs, sizeof(nt_hdrs), 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) ||
|
||||
file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("Debug Directory RVA = 0x%016x\n",
|
||||
data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress);
|
||||
|
||||
if (va_space_rw(vs,
|
||||
base + data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress,
|
||||
&debug_dir, sizeof(debug_dir), 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (debug_dir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs,
|
||||
base + debug_dir.AddressOfRawData,
|
||||
&rsds, sizeof(rsds), 0)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("CodeView signature is \'%.4s\'\n", rsds.Signature);
|
||||
|
||||
if (memcmp(&rsds.Signature, sign_rsds, sizeof(sign_rsds))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
pdb_name_sz = debug_dir.SizeOfData - sizeof(rsds);
|
||||
pdb_name = malloc(pdb_name_sz);
|
||||
if (!pdb_name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (va_space_rw(vs, base + debug_dir.AddressOfRawData +
|
||||
offsetof(OMFSignatureRSDS, name), pdb_name, pdb_name_sz, 0)) {
|
||||
free(pdb_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("PDB name is \'%s\', \'%s\' expected\n", pdb_name, PDB_NAME);
|
||||
|
||||
if (strcmp(pdb_name, PDB_NAME)) {
|
||||
eprintf("Unexpected PDB name, it seems the kernel isn't found\n");
|
||||
free(pdb_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
free(pdb_name);
|
||||
|
||||
sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds.guid.a, rsds.guid.b,
|
||||
rsds.guid.c, rsds.guid.d[0], rsds.guid.d[1]);
|
||||
hash += 20;
|
||||
for (i = 0; i < 6; i++, hash += 2) {
|
||||
sprintf(hash, "%.02x", rsds.guid.e[i]);
|
||||
}
|
||||
|
||||
sprintf(hash, "%.01x", rsds.age);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int err = 0;
|
||||
QEMU_Elf qemu_elf;
|
||||
struct pa_space ps;
|
||||
struct va_space vs;
|
||||
QEMUCPUState *state;
|
||||
idt_desc_t first_idt_desc;
|
||||
uint64_t KernBase;
|
||||
void *nt_start_addr = NULL;
|
||||
WinDumpHeader64 header;
|
||||
char pdb_hash[34];
|
||||
char pdb_url[] = SYM_URL_BASE PDB_NAME
|
||||
"/0123456789ABCDEF0123456789ABCDEFx/" PDB_NAME;
|
||||
struct pdb_reader pdb;
|
||||
uint64_t KdDebuggerDataBlock;
|
||||
KDDEBUGGER_DATA64 *kdbg;
|
||||
uint64_t KdVersionBlock;
|
||||
|
||||
if (argc != 3) {
|
||||
eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (QEMU_Elf_init(&qemu_elf, argv[1])) {
|
||||
eprintf("Failed to initialize QEMU ELF dump\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pa_space_create(&ps, &qemu_elf)) {
|
||||
eprintf("Failed to initialize physical address space\n");
|
||||
err = 1;
|
||||
goto out_elf;
|
||||
}
|
||||
|
||||
state = qemu_elf.state[0];
|
||||
printf("CPU #0 CR3 is 0x%016lx\n", state->cr[3]);
|
||||
|
||||
va_space_create(&vs, &ps, state->cr[3]);
|
||||
if (fix_dtb(&vs, &qemu_elf)) {
|
||||
eprintf("Failed to find paging base\n");
|
||||
err = 1;
|
||||
goto out_elf;
|
||||
}
|
||||
|
||||
printf("CPU #0 IDT is at 0x%016lx\n", state->idt.base);
|
||||
|
||||
if (va_space_rw(&vs, state->idt.base,
|
||||
&first_idt_desc, sizeof(first_idt_desc), 0)) {
|
||||
eprintf("Failed to get CPU #0 IDT[0]\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
printf("CPU #0 IDT[0] -> 0x%016lx\n", idt_desc_addr(first_idt_desc));
|
||||
|
||||
KernBase = idt_desc_addr(first_idt_desc) & ~(PAGE_SIZE - 1);
|
||||
printf("Searching kernel downwards from 0x%16lx...\n", KernBase);
|
||||
|
||||
for (; KernBase >= 0xfffff78000000000; KernBase -= PAGE_SIZE) {
|
||||
nt_start_addr = va_space_resolve(&vs, KernBase);
|
||||
if (!nt_start_addr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("KernBase = 0x%16lx, signature is \'%.2s\'\n", KernBase,
|
||||
(char *)nt_start_addr);
|
||||
|
||||
if (pe_get_pdb_symstore_hash(KernBase, nt_start_addr, pdb_hash, &vs)) {
|
||||
eprintf("Failed to get PDB symbol store hash\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
|
||||
sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME);
|
||||
printf("PDB URL is %s\n", pdb_url);
|
||||
|
||||
if (download_url(PDB_NAME, pdb_url)) {
|
||||
eprintf("Failed to download PDB file\n");
|
||||
err = 1;
|
||||
goto out_ps;
|
||||
}
|
||||
|
||||
if (pdb_init_from_file(PDB_NAME, &pdb)) {
|
||||
eprintf("Failed to initialize PDB reader\n");
|
||||
err = 1;
|
||||
goto out_pdb_file;
|
||||
}
|
||||
|
||||
if (!SYM_RESOLVE(KernBase, &pdb, KdDebuggerDataBlock) ||
|
||||
!SYM_RESOLVE(KernBase, &pdb, KdVersionBlock)) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
kdbg = get_kdbg(KernBase, &pdb, &vs, KdDebuggerDataBlock);
|
||||
if (!kdbg) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg,
|
||||
KdVersionBlock, qemu_elf.state_nr)) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
if (fill_context(kdbg, &vs, &qemu_elf)) {
|
||||
err = 1;
|
||||
goto out_pdb;
|
||||
}
|
||||
|
||||
if (write_dump(&ps, &header, argv[2])) {
|
||||
eprintf("Failed to save dump\n");
|
||||
err = 1;
|
||||
goto out_kdbg;
|
||||
}
|
||||
|
||||
out_kdbg:
|
||||
free(kdbg);
|
||||
out_pdb:
|
||||
pdb_exit(&pdb);
|
||||
out_pdb_file:
|
||||
unlink(PDB_NAME);
|
||||
out_ps:
|
||||
pa_space_destroy(&ps);
|
||||
out_elf:
|
||||
QEMU_Elf_exit(&qemu_elf);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* Based on source of Wine project
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "pdb.h"
|
||||
#include "err.h"
|
||||
|
||||
static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx)
|
||||
{
|
||||
return r->ds.toc->file_size[idx];
|
||||
}
|
||||
|
||||
static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *ptr;
|
||||
|
||||
for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) {
|
||||
i++;
|
||||
ptr += 8;
|
||||
if (i == n) {
|
||||
break;
|
||||
}
|
||||
ptr += sizeof(pdb_seg);
|
||||
}
|
||||
|
||||
return (pdb_seg *)ptr;
|
||||
}
|
||||
|
||||
uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name)
|
||||
{
|
||||
size_t size = pdb_get_file_size(r, r->symbols->gsym_file);
|
||||
int length;
|
||||
const union codeview_symbol *sym;
|
||||
const uint8_t *root = r->modimage;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i += length) {
|
||||
sym = (const void *)(root + i);
|
||||
length = sym->generic.len + 2;
|
||||
|
||||
if (!sym->generic.id || length < 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (sym->generic.id == S_PUB_V3 &&
|
||||
!strcmp(name, sym->public_v3.name)) {
|
||||
pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment);
|
||||
uint32_t sect_rva = segment->dword[1];
|
||||
uint64_t rva = sect_rva + sym->public_v3.offset;
|
||||
|
||||
printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09lx\n", name,
|
||||
sect_rva, sym->public_v3.segment,
|
||||
((char *)segment - 8), sym->public_v3.offset, rva);
|
||||
return rva;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name)
|
||||
{
|
||||
uint64_t rva = pdb_find_public_v3_symbol(r, name);
|
||||
|
||||
if (!rva) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return img_base + rva;
|
||||
}
|
||||
|
||||
static void pdb_reader_ds_exit(struct pdb_reader *r)
|
||||
{
|
||||
free(r->ds.toc);
|
||||
}
|
||||
|
||||
static void pdb_exit_symbols(struct pdb_reader *r)
|
||||
{
|
||||
free(r->modimage);
|
||||
free(r->symbols);
|
||||
}
|
||||
|
||||
static void pdb_exit_segments(struct pdb_reader *r)
|
||||
{
|
||||
free(r->segs);
|
||||
}
|
||||
|
||||
static void *pdb_ds_read(const PDB_DS_HEADER *header,
|
||||
const uint32_t *block_list, int size)
|
||||
{
|
||||
int i, nBlocks;
|
||||
uint8_t *buffer;
|
||||
|
||||
if (!size) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nBlocks = (size + header->block_size - 1) / header->block_size;
|
||||
|
||||
buffer = malloc(nBlocks * header->block_size);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < nBlocks; i++) {
|
||||
memcpy(buffer + i * header->block_size, (const char *)header +
|
||||
block_list[i] * header->block_size, header->block_size);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number)
|
||||
{
|
||||
const uint32_t *block_list;
|
||||
uint32_t block_size;
|
||||
const uint32_t *file_size;
|
||||
size_t i;
|
||||
|
||||
if (!r->ds.toc || file_number >= r->ds.toc->num_files) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file_size = r->ds.toc->file_size;
|
||||
r->file_used[file_number / 32] |= 1 << (file_number % 32);
|
||||
|
||||
if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
block_list = file_size + r->ds.toc->num_files;
|
||||
block_size = r->ds.header->block_size;
|
||||
|
||||
for (i = 0; i < file_number; i++) {
|
||||
block_list += (file_size[i] + block_size - 1) / block_size;
|
||||
}
|
||||
|
||||
return pdb_ds_read(r->ds.header, block_list, file_size[file_number]);
|
||||
}
|
||||
|
||||
static int pdb_init_segments(struct pdb_reader *r)
|
||||
{
|
||||
char *segs;
|
||||
unsigned stream_idx = r->sidx.segments;
|
||||
|
||||
segs = pdb_ds_read_file(r, stream_idx);
|
||||
if (!segs) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->segs = segs;
|
||||
r->segs_size = pdb_get_file_size(r, stream_idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pdb_init_symbols(struct pdb_reader *r)
|
||||
{
|
||||
int err = 0;
|
||||
PDB_SYMBOLS *symbols;
|
||||
PDB_STREAM_INDEXES *sidx = &r->sidx;
|
||||
|
||||
memset(sidx, -1, sizeof(*sidx));
|
||||
|
||||
symbols = pdb_ds_read_file(r, 3);
|
||||
if (!symbols) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->symbols = symbols;
|
||||
|
||||
if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) {
|
||||
err = 1;
|
||||
goto out_symbols;
|
||||
}
|
||||
|
||||
memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) +
|
||||
symbols->module_size + symbols->offset_size +
|
||||
symbols->hash_size + symbols->srcmodule_size +
|
||||
symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx));
|
||||
|
||||
/* Read global symbol table */
|
||||
r->modimage = pdb_ds_read_file(r, symbols->gsym_file);
|
||||
if (!r->modimage) {
|
||||
err = 1;
|
||||
goto out_symbols;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_symbols:
|
||||
free(symbols);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr)
|
||||
{
|
||||
memset(r->file_used, 0, sizeof(r->file_used));
|
||||
r->ds.header = hdr;
|
||||
r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr +
|
||||
hdr->toc_page * hdr->block_size), hdr->toc_size);
|
||||
|
||||
if (!r->ds.toc) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pdb_reader_init(struct pdb_reader *r, void *data)
|
||||
{
|
||||
int err = 0;
|
||||
const char pdb7[] = "Microsoft C/C++ MSF 7.00";
|
||||
|
||||
if (memcmp(data, pdb7, sizeof(pdb7) - 1)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pdb_reader_ds_init(r, data)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->ds.root = pdb_ds_read_file(r, 1);
|
||||
if (!r->ds.root) {
|
||||
err = 1;
|
||||
goto out_ds;
|
||||
}
|
||||
|
||||
if (pdb_init_symbols(r)) {
|
||||
err = 1;
|
||||
goto out_root;
|
||||
}
|
||||
|
||||
if (pdb_init_segments(r)) {
|
||||
err = 1;
|
||||
goto out_sym;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_sym:
|
||||
pdb_exit_symbols(r);
|
||||
out_root:
|
||||
free(r->ds.root);
|
||||
out_ds:
|
||||
pdb_reader_ds_exit(r);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void pdb_reader_exit(struct pdb_reader *r)
|
||||
{
|
||||
pdb_exit_segments(r);
|
||||
pdb_exit_symbols(r);
|
||||
free(r->ds.root);
|
||||
pdb_reader_ds_exit(r);
|
||||
}
|
||||
|
||||
int pdb_init_from_file(const char *name, struct pdb_reader *reader)
|
||||
{
|
||||
int err = 0;
|
||||
int fd;
|
||||
void *map;
|
||||
struct stat st;
|
||||
|
||||
fd = open(name, O_RDONLY, 0);
|
||||
if (fd == -1) {
|
||||
eprintf("Failed to open PDB file \'%s\'\n", name);
|
||||
return 1;
|
||||
}
|
||||
reader->fd = fd;
|
||||
|
||||
fstat(fd, &st);
|
||||
reader->file_size = st.st_size;
|
||||
|
||||
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (map == MAP_FAILED) {
|
||||
eprintf("Failed to map PDB file\n");
|
||||
err = 1;
|
||||
goto out_fd;
|
||||
}
|
||||
|
||||
if (pdb_reader_init(reader, map)) {
|
||||
err = 1;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
munmap(map, st.st_size);
|
||||
out_fd:
|
||||
close(fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void pdb_exit(struct pdb_reader *reader)
|
||||
{
|
||||
munmap(reader->ds.header, reader->file_size);
|
||||
close(reader->fd);
|
||||
pdb_reader_exit(reader);
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PDB_H
|
||||
#define PDB_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct GUID {
|
||||
unsigned int Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
unsigned char Data4[8];
|
||||
} GUID;
|
||||
|
||||
struct PDB_FILE {
|
||||
uint32_t size;
|
||||
uint32_t unknown;
|
||||
};
|
||||
|
||||
typedef struct PDB_DS_HEADER {
|
||||
char signature[32];
|
||||
uint32_t block_size;
|
||||
uint32_t unknown1;
|
||||
uint32_t num_pages;
|
||||
uint32_t toc_size;
|
||||
uint32_t unknown2;
|
||||
uint32_t toc_page;
|
||||
} PDB_DS_HEADER;
|
||||
|
||||
typedef struct PDB_DS_TOC {
|
||||
uint32_t num_files;
|
||||
uint32_t file_size[1];
|
||||
} PDB_DS_TOC;
|
||||
|
||||
typedef struct PDB_DS_ROOT {
|
||||
uint32_t Version;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t Age;
|
||||
GUID guid;
|
||||
uint32_t cbNames;
|
||||
char names[1];
|
||||
} PDB_DS_ROOT;
|
||||
|
||||
typedef struct PDB_TYPES_OLD {
|
||||
uint32_t version;
|
||||
uint16_t first_index;
|
||||
uint16_t last_index;
|
||||
uint32_t type_size;
|
||||
uint16_t file;
|
||||
uint16_t pad;
|
||||
} PDB_TYPES_OLD;
|
||||
|
||||
typedef struct PDB_TYPES {
|
||||
uint32_t version;
|
||||
uint32_t type_offset;
|
||||
uint32_t first_index;
|
||||
uint32_t last_index;
|
||||
uint32_t type_size;
|
||||
uint16_t file;
|
||||
uint16_t pad;
|
||||
uint32_t hash_size;
|
||||
uint32_t hash_base;
|
||||
uint32_t hash_offset;
|
||||
uint32_t hash_len;
|
||||
uint32_t search_offset;
|
||||
uint32_t search_len;
|
||||
uint32_t unknown_offset;
|
||||
uint32_t unknown_len;
|
||||
} PDB_TYPES;
|
||||
|
||||
typedef struct PDB_SYMBOL_RANGE {
|
||||
uint16_t segment;
|
||||
uint16_t pad1;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t characteristics;
|
||||
uint16_t index;
|
||||
uint16_t pad2;
|
||||
} PDB_SYMBOL_RANGE;
|
||||
|
||||
typedef struct PDB_SYMBOL_RANGE_EX {
|
||||
uint16_t segment;
|
||||
uint16_t pad1;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t characteristics;
|
||||
uint16_t index;
|
||||
uint16_t pad2;
|
||||
uint32_t timestamp;
|
||||
uint32_t unknown;
|
||||
} PDB_SYMBOL_RANGE_EX;
|
||||
|
||||
typedef struct PDB_SYMBOL_FILE {
|
||||
uint32_t unknown1;
|
||||
PDB_SYMBOL_RANGE range;
|
||||
uint16_t flag;
|
||||
uint16_t file;
|
||||
uint32_t symbol_size;
|
||||
uint32_t lineno_size;
|
||||
uint32_t unknown2;
|
||||
uint32_t nSrcFiles;
|
||||
uint32_t attribute;
|
||||
char filename[1];
|
||||
} PDB_SYMBOL_FILE;
|
||||
|
||||
typedef struct PDB_SYMBOL_FILE_EX {
|
||||
uint32_t unknown1;
|
||||
PDB_SYMBOL_RANGE_EX range;
|
||||
uint16_t flag;
|
||||
uint16_t file;
|
||||
uint32_t symbol_size;
|
||||
uint32_t lineno_size;
|
||||
uint32_t unknown2;
|
||||
uint32_t nSrcFiles;
|
||||
uint32_t attribute;
|
||||
uint32_t reserved[2];
|
||||
char filename[1];
|
||||
} PDB_SYMBOL_FILE_EX;
|
||||
|
||||
typedef struct PDB_SYMBOL_SOURCE {
|
||||
uint16_t nModules;
|
||||
uint16_t nSrcFiles;
|
||||
uint16_t table[1];
|
||||
} PDB_SYMBOL_SOURCE;
|
||||
|
||||
typedef struct PDB_SYMBOL_IMPORT {
|
||||
uint32_t unknown1;
|
||||
uint32_t unknown2;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t Age;
|
||||
char filename[1];
|
||||
} PDB_SYMBOL_IMPORT;
|
||||
|
||||
typedef struct PDB_SYMBOLS_OLD {
|
||||
uint16_t hash1_file;
|
||||
uint16_t hash2_file;
|
||||
uint16_t gsym_file;
|
||||
uint16_t pad;
|
||||
uint32_t module_size;
|
||||
uint32_t offset_size;
|
||||
uint32_t hash_size;
|
||||
uint32_t srcmodule_size;
|
||||
} PDB_SYMBOLS_OLD;
|
||||
|
||||
typedef struct PDB_SYMBOLS {
|
||||
uint32_t signature;
|
||||
uint32_t version;
|
||||
uint32_t unknown;
|
||||
uint32_t hash1_file;
|
||||
uint32_t hash2_file;
|
||||
uint16_t gsym_file;
|
||||
uint16_t unknown1;
|
||||
uint32_t module_size;
|
||||
uint32_t offset_size;
|
||||
uint32_t hash_size;
|
||||
uint32_t srcmodule_size;
|
||||
uint32_t pdbimport_size;
|
||||
uint32_t resvd0;
|
||||
uint32_t stream_index_size;
|
||||
uint32_t unknown2_size;
|
||||
uint16_t resvd3;
|
||||
uint16_t machine;
|
||||
uint32_t resvd4;
|
||||
} PDB_SYMBOLS;
|
||||
|
||||
typedef struct {
|
||||
uint16_t FPO;
|
||||
uint16_t unk0;
|
||||
uint16_t unk1;
|
||||
uint16_t unk2;
|
||||
uint16_t unk3;
|
||||
uint16_t segments;
|
||||
} PDB_STREAM_INDEXES_OLD;
|
||||
|
||||
typedef struct {
|
||||
uint16_t FPO;
|
||||
uint16_t unk0;
|
||||
uint16_t unk1;
|
||||
uint16_t unk2;
|
||||
uint16_t unk3;
|
||||
uint16_t segments;
|
||||
uint16_t unk4;
|
||||
uint16_t unk5;
|
||||
uint16_t unk6;
|
||||
uint16_t FPO_EXT;
|
||||
uint16_t unk7;
|
||||
} PDB_STREAM_INDEXES;
|
||||
|
||||
union codeview_symbol {
|
||||
struct {
|
||||
int16_t len;
|
||||
int16_t id;
|
||||
} generic;
|
||||
|
||||
struct {
|
||||
int16_t len;
|
||||
int16_t id;
|
||||
uint32_t symtype;
|
||||
uint32_t offset;
|
||||
uint16_t segment;
|
||||
char name[1];
|
||||
} public_v3;
|
||||
};
|
||||
|
||||
#define S_PUB_V3 0x110E
|
||||
|
||||
typedef struct pdb_seg {
|
||||
uint32_t dword[8];
|
||||
} __attribute__ ((packed)) pdb_seg;
|
||||
|
||||
#define IMAGE_FILE_MACHINE_I386 0x014c
|
||||
#define IMAGE_FILE_MACHINE_AMD64 0x8664
|
||||
|
||||
struct pdb_reader {
|
||||
int fd;
|
||||
size_t file_size;
|
||||
struct {
|
||||
PDB_DS_HEADER *header;
|
||||
PDB_DS_TOC *toc;
|
||||
PDB_DS_ROOT *root;
|
||||
} ds;
|
||||
uint32_t file_used[1024];
|
||||
PDB_SYMBOLS *symbols;
|
||||
PDB_STREAM_INDEXES sidx;
|
||||
uint8_t *modimage;
|
||||
char *segs;
|
||||
size_t segs_size;
|
||||
};
|
||||
|
||||
int pdb_init_from_file(const char *name, struct pdb_reader *reader);
|
||||
void pdb_exit(struct pdb_reader *reader);
|
||||
uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name);
|
||||
uint64_t pdb_find_public_v3_symbol(struct pdb_reader *reader, const char *name);
|
||||
|
||||
#endif /* PDB_H */
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef PE_H
|
||||
#define PE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct IMAGE_DOS_HEADER {
|
||||
uint16_t e_magic; /* 0x00: MZ Header signature */
|
||||
uint16_t e_cblp; /* 0x02: Bytes on last page of file */
|
||||
uint16_t e_cp; /* 0x04: Pages in file */
|
||||
uint16_t e_crlc; /* 0x06: Relocations */
|
||||
uint16_t e_cparhdr; /* 0x08: Size of header in paragraphs */
|
||||
uint16_t e_minalloc; /* 0x0a: Minimum extra paragraphs needed */
|
||||
uint16_t e_maxalloc; /* 0x0c: Maximum extra paragraphs needed */
|
||||
uint16_t e_ss; /* 0x0e: Initial (relative) SS value */
|
||||
uint16_t e_sp; /* 0x10: Initial SP value */
|
||||
uint16_t e_csum; /* 0x12: Checksum */
|
||||
uint16_t e_ip; /* 0x14: Initial IP value */
|
||||
uint16_t e_cs; /* 0x16: Initial (relative) CS value */
|
||||
uint16_t e_lfarlc; /* 0x18: File address of relocation table */
|
||||
uint16_t e_ovno; /* 0x1a: Overlay number */
|
||||
uint16_t e_res[4]; /* 0x1c: Reserved words */
|
||||
uint16_t e_oemid; /* 0x24: OEM identifier (for e_oeminfo) */
|
||||
uint16_t e_oeminfo; /* 0x26: OEM information; e_oemid specific */
|
||||
uint16_t e_res2[10]; /* 0x28: Reserved words */
|
||||
uint32_t e_lfanew; /* 0x3c: Offset to extended header */
|
||||
} __attribute__ ((packed)) IMAGE_DOS_HEADER;
|
||||
|
||||
typedef struct IMAGE_FILE_HEADER {
|
||||
uint16_t Machine;
|
||||
uint16_t NumberOfSections;
|
||||
uint32_t TimeDateStamp;
|
||||
uint32_t PointerToSymbolTable;
|
||||
uint32_t NumberOfSymbols;
|
||||
uint16_t SizeOfOptionalHeader;
|
||||
uint16_t Characteristics;
|
||||
} __attribute__ ((packed)) IMAGE_FILE_HEADER;
|
||||
|
||||
typedef struct IMAGE_DATA_DIRECTORY {
|
||||
uint32_t VirtualAddress;
|
||||
uint32_t Size;
|
||||
} __attribute__ ((packed)) IMAGE_DATA_DIRECTORY;
|
||||
|
||||
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
|
||||
|
||||
typedef struct IMAGE_OPTIONAL_HEADER64 {
|
||||
uint16_t Magic; /* 0x20b */
|
||||
uint8_t MajorLinkerVersion;
|
||||
uint8_t MinorLinkerVersion;
|
||||
uint32_t SizeOfCode;
|
||||
uint32_t SizeOfInitializedData;
|
||||
uint32_t SizeOfUninitializedData;
|
||||
uint32_t AddressOfEntryPoint;
|
||||
uint32_t BaseOfCode;
|
||||
uint64_t ImageBase;
|
||||
uint32_t SectionAlignment;
|
||||
uint32_t FileAlignment;
|
||||
uint16_t MajorOperatingSystemVersion;
|
||||
uint16_t MinorOperatingSystemVersion;
|
||||
uint16_t MajorImageVersion;
|
||||
uint16_t MinorImageVersion;
|
||||
uint16_t MajorSubsystemVersion;
|
||||
uint16_t MinorSubsystemVersion;
|
||||
uint32_t Win32VersionValue;
|
||||
uint32_t SizeOfImage;
|
||||
uint32_t SizeOfHeaders;
|
||||
uint32_t CheckSum;
|
||||
uint16_t Subsystem;
|
||||
uint16_t DllCharacteristics;
|
||||
uint64_t SizeOfStackReserve;
|
||||
uint64_t SizeOfStackCommit;
|
||||
uint64_t SizeOfHeapReserve;
|
||||
uint64_t SizeOfHeapCommit;
|
||||
uint32_t LoaderFlags;
|
||||
uint32_t NumberOfRvaAndSizes;
|
||||
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
|
||||
} __attribute__ ((packed)) IMAGE_OPTIONAL_HEADER64;
|
||||
|
||||
typedef struct IMAGE_NT_HEADERS64 {
|
||||
uint32_t Signature;
|
||||
IMAGE_FILE_HEADER FileHeader;
|
||||
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
|
||||
} __attribute__ ((packed)) IMAGE_NT_HEADERS64;
|
||||
|
||||
#define IMAGE_FILE_DEBUG_DIRECTORY 6
|
||||
|
||||
typedef struct IMAGE_DEBUG_DIRECTORY {
|
||||
uint32_t Characteristics;
|
||||
uint32_t TimeDateStamp;
|
||||
uint16_t MajorVersion;
|
||||
uint16_t MinorVersion;
|
||||
uint32_t Type;
|
||||
uint32_t SizeOfData;
|
||||
uint32_t AddressOfRawData;
|
||||
uint32_t PointerToRawData;
|
||||
} __attribute__ ((packed)) IMAGE_DEBUG_DIRECTORY;
|
||||
|
||||
#define IMAGE_DEBUG_TYPE_CODEVIEW 2
|
||||
|
||||
typedef struct guid_t {
|
||||
uint32_t a;
|
||||
uint16_t b;
|
||||
uint16_t c;
|
||||
uint8_t d[2];
|
||||
uint8_t e[6];
|
||||
} __attribute__ ((packed)) guid_t;
|
||||
|
||||
typedef struct OMFSignatureRSDS {
|
||||
char Signature[4];
|
||||
guid_t guid;
|
||||
uint32_t age;
|
||||
char name[];
|
||||
} __attribute__ ((packed)) OMFSignatureRSDS;
|
||||
|
||||
#endif /* PE_H */
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "err.h"
|
||||
#include "qemu_elf.h"
|
||||
|
||||
#define QEMU_NOTE_NAME "QEMU"
|
||||
|
||||
#ifndef ROUND_UP
|
||||
#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
|
||||
#endif
|
||||
|
||||
#ifndef DIV_ROUND_UP
|
||||
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
|
||||
#endif
|
||||
|
||||
#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \
|
||||
((DIV_ROUND_UP((hdr_size), 4) + \
|
||||
DIV_ROUND_UP((name_size), 4) + \
|
||||
DIV_ROUND_UP((desc_size), 4)) * 4)
|
||||
|
||||
int is_system(QEMUCPUState *s)
|
||||
{
|
||||
return s->gs.base >> 63;
|
||||
}
|
||||
|
||||
static char *nhdr_get_name(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4);
|
||||
}
|
||||
|
||||
static void *nhdr_get_desc(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4);
|
||||
}
|
||||
|
||||
static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr)
|
||||
{
|
||||
return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr),
|
||||
nhdr->n_namesz, nhdr->n_descsz));
|
||||
}
|
||||
|
||||
Elf64_Phdr *elf64_getphdr(void *map)
|
||||
{
|
||||
Elf64_Ehdr *ehdr = map;
|
||||
Elf64_Phdr *phdr = (void *)((uint8_t *)map + ehdr->e_phoff);
|
||||
|
||||
return phdr;
|
||||
}
|
||||
|
||||
Elf64_Half elf_getphdrnum(void *map)
|
||||
{
|
||||
Elf64_Ehdr *ehdr = map;
|
||||
|
||||
return ehdr->e_phnum;
|
||||
}
|
||||
|
||||
static int init_states(QEMU_Elf *qe)
|
||||
{
|
||||
Elf64_Phdr *phdr = elf64_getphdr(qe->map);
|
||||
Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset);
|
||||
Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz);
|
||||
Elf64_Nhdr *nhdr;
|
||||
size_t cpu_nr = 0;
|
||||
|
||||
if (phdr[0].p_type != PT_NOTE) {
|
||||
eprintf("Failed to find PT_NOTE\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
qe->has_kernel_gs_base = 1;
|
||||
|
||||
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
||||
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
||||
QEMUCPUState *state = nhdr_get_desc(nhdr);
|
||||
|
||||
if (state->size < sizeof(*state)) {
|
||||
eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n",
|
||||
cpu_nr, state->size);
|
||||
/*
|
||||
* We assume either every QEMU CPU state has KERNEL_GS_BASE or
|
||||
* no one has.
|
||||
*/
|
||||
qe->has_kernel_gs_base = 0;
|
||||
}
|
||||
cpu_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%zu CPU states has been found\n", cpu_nr);
|
||||
|
||||
qe->state = malloc(sizeof(*qe->state) * cpu_nr);
|
||||
if (!qe->state) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
cpu_nr = 0;
|
||||
|
||||
for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) {
|
||||
if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) {
|
||||
qe->state[cpu_nr] = nhdr_get_desc(nhdr);
|
||||
cpu_nr++;
|
||||
}
|
||||
}
|
||||
|
||||
qe->state_nr = cpu_nr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exit_states(QEMU_Elf *qe)
|
||||
{
|
||||
free(qe->state);
|
||||
}
|
||||
|
||||
int QEMU_Elf_init(QEMU_Elf *qe, const char *filename)
|
||||
{
|
||||
int err = 0;
|
||||
struct stat st;
|
||||
|
||||
qe->fd = open(filename, O_RDONLY, 0);
|
||||
if (qe->fd == -1) {
|
||||
eprintf("Failed to open ELF dump file \'%s\'\n", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fstat(qe->fd, &st);
|
||||
qe->size = st.st_size;
|
||||
|
||||
qe->map = mmap(NULL, qe->size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE, qe->fd, 0);
|
||||
if (qe->map == MAP_FAILED) {
|
||||
eprintf("Failed to map ELF file\n");
|
||||
err = 1;
|
||||
goto out_fd;
|
||||
}
|
||||
|
||||
if (init_states(qe)) {
|
||||
eprintf("Failed to extract QEMU CPU states\n");
|
||||
err = 1;
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_unmap:
|
||||
munmap(qe->map, qe->size);
|
||||
out_fd:
|
||||
close(qe->fd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void QEMU_Elf_exit(QEMU_Elf *qe)
|
||||
{
|
||||
exit_states(qe);
|
||||
munmap(qe->map, qe->size);
|
||||
close(qe->fd);
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Virtuozzo International GmbH
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef QEMU_ELF_H
|
||||
#define QEMU_ELF_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <elf.h>
|
||||
|
||||
typedef struct QEMUCPUSegment {
|
||||
uint32_t selector;
|
||||
uint32_t limit;
|
||||
uint32_t flags;
|
||||
uint32_t pad;
|
||||
uint64_t base;
|
||||
} QEMUCPUSegment;
|
||||
|
||||
typedef struct QEMUCPUState {
|
||||
uint32_t version;
|
||||
uint32_t size;
|
||||
uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp;
|
||||
uint64_t r8, r9, r10, r11, r12, r13, r14, r15;
|
||||
uint64_t rip, rflags;
|
||||
QEMUCPUSegment cs, ds, es, fs, gs, ss;
|
||||
QEMUCPUSegment ldt, tr, gdt, idt;
|
||||
uint64_t cr[5];
|
||||
uint64_t kernel_gs_base;
|
||||
} QEMUCPUState;
|
||||
|
||||
int is_system(QEMUCPUState *s);
|
||||
|
||||
typedef struct QEMU_Elf {
|
||||
int fd;
|
||||
size_t size;
|
||||
void *map;
|
||||
QEMUCPUState **state;
|
||||
size_t state_nr;
|
||||
int has_kernel_gs_base;
|
||||
} QEMU_Elf;
|
||||
|
||||
int QEMU_Elf_init(QEMU_Elf *qe, const char *filename);
|
||||
void QEMU_Elf_exit(QEMU_Elf *qe);
|
||||
|
||||
Elf64_Phdr *elf64_getphdr(void *map);
|
||||
Elf64_Half elf_getphdrnum(void *map);
|
||||
|
||||
#endif /* QEMU_ELF_H */
|
||||
+2
-2
@@ -84,7 +84,7 @@ void cpu_list_add(CPUState *cpu)
|
||||
} else {
|
||||
assert(!cpu_index_auto_assigned);
|
||||
}
|
||||
QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node);
|
||||
QTAILQ_INSERT_TAIL(&cpus, cpu, node);
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
|
||||
finish_safe_work(cpu);
|
||||
@@ -101,7 +101,7 @@ void cpu_list_remove(CPUState *cpu)
|
||||
|
||||
assert(!(cpu_index_auto_assigned && cpu != QTAILQ_LAST(&cpus, CPUTailQ)));
|
||||
|
||||
QTAILQ_REMOVE_RCU(&cpus, cpu, node);
|
||||
QTAILQ_REMOVE(&cpus, cpu, node);
|
||||
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
|
||||
qemu_mutex_unlock(&qemu_cpu_list_lock);
|
||||
}
|
||||
|
||||
@@ -121,6 +121,8 @@ static bool all_cpu_threads_idle(void)
|
||||
/* Protected by TimersState seqlock */
|
||||
|
||||
static bool icount_sleep = true;
|
||||
/* Conversion factor from emulated instructions to virtual clock ticks. */
|
||||
static int icount_time_shift;
|
||||
/* Arbitrarily pick 1MIPS as the minimum allowable speed. */
|
||||
#define MAX_ICOUNT_SHIFT 10
|
||||
|
||||
@@ -129,27 +131,20 @@ typedef struct TimersState {
|
||||
int64_t cpu_ticks_prev;
|
||||
int64_t cpu_ticks_offset;
|
||||
|
||||
/* Protect fields that can be respectively read outside the
|
||||
* BQL, and written from multiple threads.
|
||||
/* cpu_clock_offset can be read out of BQL, so protect it with
|
||||
* this lock.
|
||||
*/
|
||||
QemuSeqLock vm_clock_seqlock;
|
||||
QemuSpin vm_clock_lock;
|
||||
|
||||
int16_t cpu_ticks_enabled;
|
||||
|
||||
/* Conversion factor from emulated instructions to virtual clock ticks. */
|
||||
int16_t icount_time_shift;
|
||||
int64_t cpu_clock_offset;
|
||||
int32_t cpu_ticks_enabled;
|
||||
int64_t dummy;
|
||||
|
||||
/* Compensate for varying guest execution speed. */
|
||||
int64_t qemu_icount_bias;
|
||||
|
||||
int64_t vm_clock_warp_start;
|
||||
int64_t cpu_clock_offset;
|
||||
|
||||
/* Only written by TCG thread */
|
||||
int64_t qemu_icount;
|
||||
|
||||
/* for adjusting icount */
|
||||
int64_t vm_clock_warp_start;
|
||||
QEMUTimer *icount_rt_timer;
|
||||
QEMUTimer *icount_vm_timer;
|
||||
QEMUTimer *icount_warp_timer;
|
||||
@@ -211,12 +206,12 @@ void qemu_tcg_configure(QemuOpts *opts, Error **errp)
|
||||
error_setg(errp, "No MTTCG when icount is enabled");
|
||||
} else {
|
||||
#ifndef TARGET_SUPPORTS_MTTCG
|
||||
warn_report("Guest not yet converted to MTTCG - "
|
||||
"you may get unexpected results");
|
||||
error_report("Guest not yet converted to MTTCG - "
|
||||
"you may get unexpected results");
|
||||
#endif
|
||||
if (!check_tcg_memory_orders_compatible()) {
|
||||
warn_report("Guest expects a stronger memory ordering "
|
||||
"than the host provides");
|
||||
error_report("Guest expects a stronger memory ordering "
|
||||
"than the host provides");
|
||||
error_printf("This may cause strange/hard to debug errors\n");
|
||||
}
|
||||
mttcg_enabled = true;
|
||||
@@ -245,30 +240,21 @@ static int64_t cpu_get_icount_executed(CPUState *cpu)
|
||||
* account executed instructions. This is done by the TCG vCPU
|
||||
* thread so the main-loop can see time has moved forward.
|
||||
*/
|
||||
static void cpu_update_icount_locked(CPUState *cpu)
|
||||
void cpu_update_icount(CPUState *cpu)
|
||||
{
|
||||
int64_t executed = cpu_get_icount_executed(cpu);
|
||||
cpu->icount_budget -= executed;
|
||||
|
||||
atomic_set_i64(&timers_state.qemu_icount,
|
||||
timers_state.qemu_icount + executed);
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
atomic_set__nocheck(&timers_state.qemu_icount,
|
||||
atomic_read__nocheck(&timers_state.qemu_icount) +
|
||||
executed);
|
||||
#else /* FIXME: we need 64bit atomics to do this safely */
|
||||
timers_state.qemu_icount += executed;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the global shared timer_state.qemu_icount to take into
|
||||
* account executed instructions. This is done by the TCG vCPU
|
||||
* thread so the main-loop can see time has moved forward.
|
||||
*/
|
||||
void cpu_update_icount(CPUState *cpu)
|
||||
{
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
cpu_update_icount_locked(cpu);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
}
|
||||
|
||||
static int64_t cpu_get_icount_raw_locked(void)
|
||||
int64_t cpu_get_icount_raw(void)
|
||||
{
|
||||
CPUState *cpu = current_cpu;
|
||||
|
||||
@@ -278,33 +264,22 @@ static int64_t cpu_get_icount_raw_locked(void)
|
||||
exit(1);
|
||||
}
|
||||
/* Take into account what has run */
|
||||
cpu_update_icount_locked(cpu);
|
||||
cpu_update_icount(cpu);
|
||||
}
|
||||
/* The read is protected by the seqlock, but needs atomic64 to avoid UB */
|
||||
return atomic_read_i64(&timers_state.qemu_icount);
|
||||
}
|
||||
|
||||
static int64_t cpu_get_icount_locked(void)
|
||||
{
|
||||
int64_t icount = cpu_get_icount_raw_locked();
|
||||
return atomic_read_i64(&timers_state.qemu_icount_bias) +
|
||||
cpu_icount_to_ns(icount);
|
||||
}
|
||||
|
||||
int64_t cpu_get_icount_raw(void)
|
||||
{
|
||||
int64_t icount;
|
||||
unsigned start;
|
||||
|
||||
do {
|
||||
start = seqlock_read_begin(&timers_state.vm_clock_seqlock);
|
||||
icount = cpu_get_icount_raw_locked();
|
||||
} while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start));
|
||||
|
||||
return icount;
|
||||
#ifdef CONFIG_ATOMIC64
|
||||
return atomic_read__nocheck(&timers_state.qemu_icount);
|
||||
#else /* FIXME: we need 64bit atomics to do this safely */
|
||||
return timers_state.qemu_icount;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return the virtual CPU time, based on the instruction counter. */
|
||||
static int64_t cpu_get_icount_locked(void)
|
||||
{
|
||||
int64_t icount = cpu_get_icount_raw();
|
||||
return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount);
|
||||
}
|
||||
|
||||
int64_t cpu_get_icount(void)
|
||||
{
|
||||
int64_t icount;
|
||||
@@ -320,29 +295,14 @@ int64_t cpu_get_icount(void)
|
||||
|
||||
int64_t cpu_icount_to_ns(int64_t icount)
|
||||
{
|
||||
return icount << atomic_read(&timers_state.icount_time_shift);
|
||||
}
|
||||
|
||||
static int64_t cpu_get_ticks_locked(void)
|
||||
{
|
||||
int64_t ticks = timers_state.cpu_ticks_offset;
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
ticks += cpu_get_host_ticks();
|
||||
}
|
||||
|
||||
if (timers_state.cpu_ticks_prev > ticks) {
|
||||
/* Non increasing ticks may happen if the host uses software suspend. */
|
||||
timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
|
||||
ticks = timers_state.cpu_ticks_prev;
|
||||
}
|
||||
|
||||
timers_state.cpu_ticks_prev = ticks;
|
||||
return ticks;
|
||||
return icount << icount_time_shift;
|
||||
}
|
||||
|
||||
/* return the time elapsed in VM between vm_start and vm_stop. Unless
|
||||
* icount is active, cpu_get_ticks() uses units of the host CPU cycle
|
||||
* counter.
|
||||
*
|
||||
* Caller must hold the BQL
|
||||
*/
|
||||
int64_t cpu_get_ticks(void)
|
||||
{
|
||||
@@ -352,9 +312,19 @@ int64_t cpu_get_ticks(void)
|
||||
return cpu_get_icount();
|
||||
}
|
||||
|
||||
qemu_spin_lock(&timers_state.vm_clock_lock);
|
||||
ticks = cpu_get_ticks_locked();
|
||||
qemu_spin_unlock(&timers_state.vm_clock_lock);
|
||||
ticks = timers_state.cpu_ticks_offset;
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
ticks += cpu_get_host_ticks();
|
||||
}
|
||||
|
||||
if (timers_state.cpu_ticks_prev > ticks) {
|
||||
/* Note: non increasing ticks may happen if the host uses
|
||||
software suspend */
|
||||
timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks;
|
||||
ticks = timers_state.cpu_ticks_prev;
|
||||
}
|
||||
|
||||
timers_state.cpu_ticks_prev = ticks;
|
||||
return ticks;
|
||||
}
|
||||
|
||||
@@ -391,15 +361,14 @@ int64_t cpu_get_clock(void)
|
||||
*/
|
||||
void cpu_enable_ticks(void)
|
||||
{
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
/* Here, the really thing protected by seqlock is cpu_clock_offset. */
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
if (!timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset -= cpu_get_host_ticks();
|
||||
timers_state.cpu_clock_offset -= get_clock();
|
||||
timers_state.cpu_ticks_enabled = 1;
|
||||
}
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
}
|
||||
|
||||
/* disable cpu_get_ticks() : the clock is stopped. You must not call
|
||||
@@ -408,15 +377,14 @@ void cpu_enable_ticks(void)
|
||||
*/
|
||||
void cpu_disable_ticks(void)
|
||||
{
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
/* Here, the really thing protected by seqlock is cpu_clock_offset. */
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
if (timers_state.cpu_ticks_enabled) {
|
||||
timers_state.cpu_ticks_offset += cpu_get_host_ticks();
|
||||
timers_state.cpu_clock_offset = cpu_get_clock_locked();
|
||||
timers_state.cpu_ticks_enabled = 0;
|
||||
}
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
}
|
||||
|
||||
/* Correlation between real and virtual time is always going to be
|
||||
@@ -439,8 +407,7 @@ static void icount_adjust(void)
|
||||
return;
|
||||
}
|
||||
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
cur_time = cpu_get_clock_locked();
|
||||
cur_icount = cpu_get_icount_locked();
|
||||
|
||||
@@ -448,24 +415,20 @@ static void icount_adjust(void)
|
||||
/* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */
|
||||
if (delta > 0
|
||||
&& last_delta + ICOUNT_WOBBLE < delta * 2
|
||||
&& timers_state.icount_time_shift > 0) {
|
||||
&& icount_time_shift > 0) {
|
||||
/* The guest is getting too far ahead. Slow time down. */
|
||||
atomic_set(&timers_state.icount_time_shift,
|
||||
timers_state.icount_time_shift - 1);
|
||||
icount_time_shift--;
|
||||
}
|
||||
if (delta < 0
|
||||
&& last_delta - ICOUNT_WOBBLE > delta * 2
|
||||
&& timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) {
|
||||
&& icount_time_shift < MAX_ICOUNT_SHIFT) {
|
||||
/* The guest is getting too far behind. Speed time up. */
|
||||
atomic_set(&timers_state.icount_time_shift,
|
||||
timers_state.icount_time_shift + 1);
|
||||
icount_time_shift++;
|
||||
}
|
||||
last_delta = delta;
|
||||
atomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
cur_icount - (timers_state.qemu_icount
|
||||
<< timers_state.icount_time_shift));
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
timers_state.qemu_icount_bias = cur_icount
|
||||
- (timers_state.qemu_icount << icount_time_shift);
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
}
|
||||
|
||||
static void icount_adjust_rt(void *opaque)
|
||||
@@ -485,8 +448,7 @@ static void icount_adjust_vm(void *opaque)
|
||||
|
||||
static int64_t qemu_icount_round(int64_t count)
|
||||
{
|
||||
int shift = atomic_read(&timers_state.icount_time_shift);
|
||||
return (count + (1 << shift) - 1) >> shift;
|
||||
return (count + (1 << icount_time_shift) - 1) >> icount_time_shift;
|
||||
}
|
||||
|
||||
static void icount_warp_rt(void)
|
||||
@@ -506,11 +468,10 @@ static void icount_warp_rt(void)
|
||||
return;
|
||||
}
|
||||
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
if (runstate_is_running()) {
|
||||
int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT,
|
||||
cpu_get_clock_locked());
|
||||
int64_t clock = REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT,
|
||||
cpu_get_clock_locked());
|
||||
int64_t warp_delta;
|
||||
|
||||
warp_delta = clock - timers_state.vm_clock_warp_start;
|
||||
@@ -523,12 +484,10 @@ static void icount_warp_rt(void)
|
||||
int64_t delta = clock - cur_icount;
|
||||
warp_delta = MIN(warp_delta, delta);
|
||||
}
|
||||
atomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp_delta);
|
||||
timers_state.qemu_icount_bias += warp_delta;
|
||||
}
|
||||
timers_state.vm_clock_warp_start = -1;
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
|
||||
if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
@@ -553,12 +512,9 @@ void qtest_clock_warp(int64_t dest)
|
||||
int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL);
|
||||
int64_t warp = qemu_soonest_timeout(dest - clock, deadline);
|
||||
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
atomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + warp);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
timers_state.qemu_icount_bias += warp;
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
|
||||
qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL);
|
||||
timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]);
|
||||
@@ -583,29 +539,18 @@ void qemu_start_warp_timer(void)
|
||||
return;
|
||||
}
|
||||
|
||||
if (replay_mode != REPLAY_MODE_PLAY) {
|
||||
if (!all_cpu_threads_idle()) {
|
||||
return;
|
||||
}
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (qtest_enabled()) {
|
||||
/* When testing, qtest commands advance icount. */
|
||||
return;
|
||||
}
|
||||
if (!all_cpu_threads_idle()) {
|
||||
return;
|
||||
}
|
||||
|
||||
replay_checkpoint(CHECKPOINT_CLOCK_WARP_START);
|
||||
} else {
|
||||
/* warp clock deterministically in record/replay mode */
|
||||
if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) {
|
||||
/* vCPU is sleeping and warp can't be started.
|
||||
It is probably a race condition: notification sent
|
||||
to vCPU was processed in advance and vCPU went to sleep.
|
||||
Therefore we have to wake it up for doing someting. */
|
||||
if (replay_has_checkpoint()) {
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (qtest_enabled()) {
|
||||
/* When testing, qtest commands advance icount. */
|
||||
return;
|
||||
}
|
||||
|
||||
/* We want to use the earliest deadline from ALL vm_clocks */
|
||||
@@ -636,12 +581,9 @@ void qemu_start_warp_timer(void)
|
||||
* It is useful when we want a deterministic execution time,
|
||||
* isolated from host latencies.
|
||||
*/
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
atomic_set_i64(&timers_state.qemu_icount_bias,
|
||||
timers_state.qemu_icount_bias + deadline);
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
timers_state.qemu_icount_bias += deadline;
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
qemu_clock_notify(QEMU_CLOCK_VIRTUAL);
|
||||
} else {
|
||||
/*
|
||||
@@ -652,14 +594,12 @@ void qemu_start_warp_timer(void)
|
||||
* you will not be sending network packets continuously instead of
|
||||
* every 100ms.
|
||||
*/
|
||||
seqlock_write_lock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_begin(&timers_state.vm_clock_seqlock);
|
||||
if (timers_state.vm_clock_warp_start == -1
|
||||
|| timers_state.vm_clock_warp_start > clock) {
|
||||
timers_state.vm_clock_warp_start = clock;
|
||||
}
|
||||
seqlock_write_unlock(&timers_state.vm_clock_seqlock,
|
||||
&timers_state.vm_clock_lock);
|
||||
seqlock_write_end(&timers_state.vm_clock_seqlock);
|
||||
timer_mod_anticipate(timers_state.icount_warp_timer,
|
||||
clock + deadline);
|
||||
}
|
||||
@@ -760,7 +700,7 @@ static const VMStateDescription vmstate_timers = {
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_INT64(cpu_ticks_offset, TimersState),
|
||||
VMSTATE_UNUSED(8),
|
||||
VMSTATE_INT64(dummy, TimersState),
|
||||
VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2),
|
||||
VMSTATE_END_OF_LIST()
|
||||
},
|
||||
@@ -841,7 +781,6 @@ int cpu_throttle_get_percentage(void)
|
||||
void cpu_ticks_init(void)
|
||||
{
|
||||
seqlock_init(&timers_state.vm_clock_seqlock);
|
||||
qemu_spin_init(&timers_state.vm_clock_lock);
|
||||
vmstate_register(NULL, 0, &vmstate_timers, &timers_state);
|
||||
throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT,
|
||||
cpu_throttle_timer_tick, NULL);
|
||||
@@ -873,7 +812,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
||||
}
|
||||
if (strcmp(option, "auto") != 0) {
|
||||
errno = 0;
|
||||
timers_state.icount_time_shift = strtol(option, &rem_str, 0);
|
||||
icount_time_shift = strtol(option, &rem_str, 0);
|
||||
if (errno != 0 || *rem_str != '\0' || !strlen(option)) {
|
||||
error_setg(errp, "icount: Invalid shift value");
|
||||
}
|
||||
@@ -889,7 +828,7 @@ void configure_icount(QemuOpts *opts, Error **errp)
|
||||
|
||||
/* 125MIPS seems a reasonable initial guess at the guest speed.
|
||||
It will be corrected fairly quickly anyway. */
|
||||
timers_state.icount_time_shift = 3;
|
||||
icount_time_shift = 3;
|
||||
|
||||
/* Have both realtime and virtual time triggers for speed adjustment.
|
||||
The realtime trigger catches emulated time passing too slowly,
|
||||
@@ -983,8 +922,6 @@ static void start_tcg_kick_timer(void)
|
||||
if (!tcg_kick_vcpu_timer && CPU_NEXT(first_cpu)) {
|
||||
tcg_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
kick_tcg_thread, NULL);
|
||||
}
|
||||
if (tcg_kick_vcpu_timer && !timer_pending(tcg_kick_vcpu_timer)) {
|
||||
timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick());
|
||||
}
|
||||
}
|
||||
@@ -992,8 +929,9 @@ static void start_tcg_kick_timer(void)
|
||||
static void stop_tcg_kick_timer(void)
|
||||
{
|
||||
assert(!mttcg_enabled);
|
||||
if (tcg_kick_vcpu_timer && timer_pending(tcg_kick_vcpu_timer)) {
|
||||
if (tcg_kick_vcpu_timer) {
|
||||
timer_del(tcg_kick_vcpu_timer);
|
||||
tcg_kick_vcpu_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1073,7 +1011,7 @@ static int do_vm_stop(RunState state, bool send_stop)
|
||||
runstate_set(state);
|
||||
vm_state_notify(0, state);
|
||||
if (send_stop) {
|
||||
qapi_event_send_stop();
|
||||
qapi_event_send_stop(&error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1220,20 +1158,16 @@ static void qemu_wait_io_event_common(CPUState *cpu)
|
||||
process_queued_cpu_work(cpu);
|
||||
}
|
||||
|
||||
static void qemu_tcg_rr_wait_io_event(void)
|
||||
static void qemu_tcg_rr_wait_io_event(CPUState *cpu)
|
||||
{
|
||||
CPUState *cpu;
|
||||
|
||||
while (all_cpu_threads_idle()) {
|
||||
stop_tcg_kick_timer();
|
||||
qemu_cond_wait(first_cpu->halt_cond, &qemu_global_mutex);
|
||||
qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex);
|
||||
}
|
||||
|
||||
start_tcg_kick_timer();
|
||||
|
||||
CPU_FOREACH(cpu) {
|
||||
qemu_wait_io_event_common(cpu);
|
||||
}
|
||||
qemu_wait_io_event_common(cpu);
|
||||
}
|
||||
|
||||
static void qemu_wait_io_event(CPUState *cpu)
|
||||
@@ -1429,8 +1363,7 @@ static int tcg_cpu_exec(CPUState *cpu)
|
||||
ret = cpu_exec(cpu);
|
||||
cpu_exec_end(cpu);
|
||||
#ifdef CONFIG_PROFILER
|
||||
atomic_set(&tcg_ctx->prof.cpu_exec_time,
|
||||
tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti);
|
||||
tcg_time += profile_getclock() - ti;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
@@ -1558,15 +1491,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg)
|
||||
atomic_mb_set(&cpu->exit_request, 0);
|
||||
}
|
||||
|
||||
if (use_icount && all_cpu_threads_idle()) {
|
||||
/*
|
||||
* When all cpus are sleeping (e.g in WFI), to avoid a deadlock
|
||||
* in the main_loop, wake it up in order to start the warp timer.
|
||||
*/
|
||||
qemu_notify_event();
|
||||
}
|
||||
|
||||
qemu_tcg_rr_wait_io_event();
|
||||
qemu_tcg_rr_wait_io_event(cpu ? cpu : QTAILQ_FIRST(&cpus));
|
||||
deal_with_unplugged_cpus();
|
||||
}
|
||||
|
||||
@@ -1837,16 +1762,10 @@ bool qemu_mutex_iothread_locked(void)
|
||||
return iothread_locked;
|
||||
}
|
||||
|
||||
/*
|
||||
* The BQL is taken from so many places that it is worth profiling the
|
||||
* callers directly, instead of funneling them all through a single function.
|
||||
*/
|
||||
void qemu_mutex_lock_iothread_impl(const char *file, int line)
|
||||
void qemu_mutex_lock_iothread(void)
|
||||
{
|
||||
QemuMutexLockFunc bql_lock = atomic_read(&qemu_bql_mutex_lock_func);
|
||||
|
||||
g_assert(!qemu_mutex_iothread_locked());
|
||||
bql_lock(&qemu_global_mutex, file, line);
|
||||
qemu_mutex_lock(&qemu_global_mutex);
|
||||
iothread_locked = true;
|
||||
}
|
||||
|
||||
@@ -2140,13 +2059,13 @@ int vm_prepare_start(void)
|
||||
* the STOP event.
|
||||
*/
|
||||
if (runstate_is_running()) {
|
||||
qapi_event_send_stop();
|
||||
qapi_event_send_resume();
|
||||
qapi_event_send_stop(&error_abort);
|
||||
qapi_event_send_resume(&error_abort);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We are sending this now, but the CPUs will be resumed shortly later */
|
||||
qapi_event_send_resume();
|
||||
qapi_event_send_resume(&error_abort);
|
||||
|
||||
replay_enable_events();
|
||||
cpu_enable_ticks();
|
||||
@@ -2284,6 +2203,7 @@ static CpuInfoArch sysemu_target_to_cpuinfo_arch(SysEmuTarget target)
|
||||
return CPU_INFO_ARCH_X86;
|
||||
|
||||
case SYS_EMU_TARGET_PPC:
|
||||
case SYS_EMU_TARGET_PPCEMB:
|
||||
case SYS_EMU_TARGET_PPC64:
|
||||
return CPU_INFO_ARCH_PPC;
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ crypto-obj-y += tlscredsx509.o
|
||||
crypto-obj-y += tlssession.o
|
||||
crypto-obj-y += secret.o
|
||||
crypto-obj-$(CONFIG_GCRYPT) += random-gcrypt.o
|
||||
crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS)) += random-gnutls.o
|
||||
crypto-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS),n,y)) += random-platform.o
|
||||
crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS_RND)) += random-gnutls.o
|
||||
crypto-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS_RND),n,y)) += random-platform.o
|
||||
crypto-obj-y += pbkdf.o
|
||||
crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
|
||||
crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += pbkdf-gcrypt.o
|
||||
crypto-obj-$(CONFIG_NETTLE_KDF) += pbkdf-nettle.o
|
||||
crypto-obj-$(if $(CONFIG_NETTLE_KDF),n,$(CONFIG_GCRYPT_KDF)) += pbkdf-gcrypt.o
|
||||
crypto-obj-y += ivgen.o
|
||||
crypto-obj-y += ivgen-essiv.o
|
||||
crypto-obj-y += ivgen-plain.o
|
||||
|
||||
@@ -102,8 +102,6 @@ qcrypto_block_qcow_open(QCryptoBlock *block,
|
||||
Error **errp)
|
||||
{
|
||||
if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) {
|
||||
block->sector_size = QCRYPTO_BLOCK_QCOW_SECTOR_SIZE;
|
||||
block->payload_offset = 0;
|
||||
return 0;
|
||||
} else {
|
||||
if (!options->u.qcow.key_secret) {
|
||||
|
||||
+21
-2
@@ -37,14 +37,33 @@
|
||||
/* #define DEBUG_GNUTLS */
|
||||
|
||||
/*
|
||||
* We need to init gcrypt threading if
|
||||
* If GNUTLS is built against GCrypt then
|
||||
*
|
||||
* - When GNUTLS >= 2.12, we must not initialize gcrypt threading
|
||||
* because GNUTLS will do that itself
|
||||
* - When GNUTLS < 2.12 we must always initialize gcrypt threading
|
||||
* - When GNUTLS is disabled we must always initialize gcrypt threading
|
||||
*
|
||||
* But....
|
||||
*
|
||||
* When gcrypt >= 1.6.0 we must not initialize gcrypt threading
|
||||
* because gcrypt will do that itself.
|
||||
*
|
||||
* So we need to init gcrypt threading if
|
||||
*
|
||||
* - gcrypt < 1.6.0
|
||||
* AND
|
||||
* - gnutls < 2.12
|
||||
* OR
|
||||
* - gnutls is disabled
|
||||
*
|
||||
*/
|
||||
|
||||
#if (defined(CONFIG_GCRYPT) && \
|
||||
(GCRYPT_VERSION_NUMBER < 0x010600))
|
||||
(!defined(CONFIG_GNUTLS) || \
|
||||
(LIBGNUTLS_VERSION_NUMBER < 0x020c00)) && \
|
||||
(!defined(GCRYPT_VERSION_NUMBER) || \
|
||||
(GCRYPT_VERSION_NUMBER < 0x010600)))
|
||||
#define QCRYPTO_INIT_GCRYPT_THREADS
|
||||
#else
|
||||
#undef QCRYPTO_INIT_GCRYPT_THREADS
|
||||
|
||||
@@ -72,6 +72,14 @@ qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert,
|
||||
}
|
||||
|
||||
|
||||
#if LIBGNUTLS_VERSION_NUMBER >= 2
|
||||
/*
|
||||
* The gnutls_x509_crt_get_basic_constraints function isn't
|
||||
* available in GNUTLS 1.0.x branches. This isn't critical
|
||||
* though, since gnutls_certificate_verify_peers2 will do
|
||||
* pretty much the same check at runtime, so we can just
|
||||
* disable this code
|
||||
*/
|
||||
static int
|
||||
qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds,
|
||||
gnutls_x509_crt_t cert,
|
||||
@@ -122,6 +130,7 @@ qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds,
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
@@ -290,12 +299,14 @@ qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds,
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if LIBGNUTLS_VERSION_NUMBER >= 2
|
||||
if (qcrypto_tls_creds_check_cert_basic_constraints(creds,
|
||||
cert, certFile,
|
||||
isServer, isCA,
|
||||
errp) < 0) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (qcrypto_tls_creds_check_cert_key_usage(creds,
|
||||
cert, certFile,
|
||||
@@ -604,6 +615,7 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
|
||||
}
|
||||
|
||||
if (cert != NULL && key != NULL) {
|
||||
#if LIBGNUTLS_VERSION_NUMBER >= 0x030111
|
||||
char *password = NULL;
|
||||
if (creds->passwordid) {
|
||||
password = qcrypto_secret_lookup_as_utf8(creds->passwordid,
|
||||
@@ -618,6 +630,15 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds,
|
||||
password,
|
||||
0);
|
||||
g_free(password);
|
||||
#else /* LIBGNUTLS_VERSION_NUMBER < 0x030111 */
|
||||
if (creds->passwordid) {
|
||||
error_setg(errp, "PKCS8 decryption requires GNUTLS >= 3.1.11");
|
||||
goto cleanup;
|
||||
}
|
||||
ret = gnutls_certificate_set_x509_key_file(creds->data,
|
||||
cert, key,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
#endif
|
||||
if (ret < 0) {
|
||||
error_setg(errp, "Cannot load certificate '%s' & key '%s': %s",
|
||||
cert, key, gnutls_strerror(ret));
|
||||
|
||||
+7
-4
@@ -90,7 +90,13 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len)
|
||||
}
|
||||
|
||||
#define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH"
|
||||
#define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK"
|
||||
|
||||
#if GNUTLS_VERSION_MAJOR >= 3
|
||||
#define TLS_ECDHE_PSK "+ECDHE-PSK:"
|
||||
#else
|
||||
#define TLS_ECDHE_PSK ""
|
||||
#endif
|
||||
#define TLS_PRIORITY_ADDITIONAL_PSK TLS_ECDHE_PSK "+DHE-PSK:+PSK"
|
||||
|
||||
QCryptoTLSSession *
|
||||
qcrypto_tls_session_new(QCryptoTLSCreds *creds,
|
||||
@@ -473,9 +479,6 @@ qcrypto_tls_session_read(QCryptoTLSSession *session,
|
||||
case GNUTLS_E_INTERRUPTED:
|
||||
errno = EINTR;
|
||||
break;
|
||||
case GNUTLS_E_PREMATURE_TERMINATION:
|
||||
errno = ECONNABORTED;
|
||||
break;
|
||||
default:
|
||||
errno = EIO;
|
||||
break;
|
||||
|
||||
Loaded 100 of 1521 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user