Merge upstream QEMU v3.0.0

This updates XQEMU's QEMU code base from v2.12.0 to v3.0.0. A few minor fixups were necessary due to core changes. Basic testing done on Ubuntu 18.04, macOS, and Windows.
This commit is contained in:
Matt 2018-09-05 22:47:03 -07:00 committed by GitHub
commit 337e1fa114
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1855 changed files with 101315 additions and 46284 deletions

6
.gitignore vendored
View File

@ -36,6 +36,7 @@
/qapi/qapi-commands-common.[ch] /qapi/qapi-commands-common.[ch]
/qapi/qapi-commands-crypto.[ch] /qapi/qapi-commands-crypto.[ch]
/qapi/qapi-commands-introspect.[ch] /qapi/qapi-commands-introspect.[ch]
/qapi/qapi-commands-job.[ch]
/qapi/qapi-commands-migration.[ch] /qapi/qapi-commands-migration.[ch]
/qapi/qapi-commands-misc.[ch] /qapi/qapi-commands-misc.[ch]
/qapi/qapi-commands-net.[ch] /qapi/qapi-commands-net.[ch]
@ -53,6 +54,7 @@
/qapi/qapi-events-common.[ch] /qapi/qapi-events-common.[ch]
/qapi/qapi-events-crypto.[ch] /qapi/qapi-events-crypto.[ch]
/qapi/qapi-events-introspect.[ch] /qapi/qapi-events-introspect.[ch]
/qapi/qapi-events-job.[ch]
/qapi/qapi-events-migration.[ch] /qapi/qapi-events-migration.[ch]
/qapi/qapi-events-misc.[ch] /qapi/qapi-events-misc.[ch]
/qapi/qapi-events-net.[ch] /qapi/qapi-events-net.[ch]
@ -71,6 +73,7 @@
/qapi/qapi-types-common.[ch] /qapi/qapi-types-common.[ch]
/qapi/qapi-types-crypto.[ch] /qapi/qapi-types-crypto.[ch]
/qapi/qapi-types-introspect.[ch] /qapi/qapi-types-introspect.[ch]
/qapi/qapi-types-job.[ch]
/qapi/qapi-types-migration.[ch] /qapi/qapi-types-migration.[ch]
/qapi/qapi-types-misc.[ch] /qapi/qapi-types-misc.[ch]
/qapi/qapi-types-net.[ch] /qapi/qapi-types-net.[ch]
@ -88,6 +91,7 @@
/qapi/qapi-visit-common.[ch] /qapi/qapi-visit-common.[ch]
/qapi/qapi-visit-crypto.[ch] /qapi/qapi-visit-crypto.[ch]
/qapi/qapi-visit-introspect.[ch] /qapi/qapi-visit-introspect.[ch]
/qapi/qapi-visit-job.[ch]
/qapi/qapi-visit-migration.[ch] /qapi/qapi-visit-migration.[ch]
/qapi/qapi-visit-misc.[ch] /qapi/qapi-visit-misc.[ch]
/qapi/qapi-visit-net.[ch] /qapi/qapi-visit-net.[ch]
@ -151,6 +155,7 @@
.sdk .sdk
*.gcda *.gcda
*.gcno *.gcno
*.gcov
/pc-bios/bios-pq/status /pc-bios/bios-pq/status
/pc-bios/vgabios-pq/status /pc-bios/vgabios-pq/status
/pc-bios/optionrom/linuxboot.asm /pc-bios/optionrom/linuxboot.asm
@ -206,3 +211,4 @@ trace-dtrace-root.h
trace-dtrace-root.dtrace trace-dtrace-root.dtrace
trace-ust-all.h trace-ust-all.h
trace-ust-all.c trace-ust-all.c
/target/arm/decode-sve.inc.c

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "roms/vgabios"]
path = roms/vgabios
url = git://git.qemu-project.org/vgabios.git/
[submodule "roms/seabios"] [submodule "roms/seabios"]
path = roms/seabios path = roms/seabios
url = git://git.qemu-project.org/seabios.git/ url = git://git.qemu-project.org/seabios.git/

View File

@ -35,13 +35,5 @@ build:
options: "-e HOME=/root" options: "-e HOME=/root"
ci: ci:
- unset CC - unset CC
# some targets require newer up to date packages, for example TARGET_LIST matching
# aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu)
# see the configure script:
# error_exit "DTC (libfdt) version >= 1.4.2 not present. Your options:"
# " (1) Preferred: Install the DTC (libfdt) devel package"
# " (2) Fetch the DTC submodule, using:"
# " git submodule update --init dtc"
- dpkg --compare-versions `dpkg-query --showformat='${Version}' --show libfdt-dev` ge 1.4.2 || git submodule update --init dtc
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST} - ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1)) - make -j$(($(getconf _NPROCESSORS_ONLN) + 1))

View File

@ -124,6 +124,23 @@ We use traditional C-style /* */ comments and avoid // comments.
Rationale: The // form is valid in C99, so this is purely a matter of Rationale: The // form is valid in C99, so this is purely a matter of
consistency of style. The checkpatch script will warn you about this. consistency of style. The checkpatch script will warn you about this.
Multiline comment blocks should have a row of stars on the left,
and the initial /* and terminating */ both on their own lines:
/*
* like
* this
*/
This is the same format required by the Linux kernel coding style.
(Some of the existing comments in the codebase use the GNU Coding
Standards form which does not have stars on the left, or other
variations; avoid these when writing new comments, but don't worry
about converting to the preferred form unless you're editing that
comment anyway.)
Rationale: Consistency, and ease of visually picking out a multiline
comment from the surrounding code.
8. trace-events style 8. trace-events style
8.1 0x prefix 8.1 0x prefix

View File

@ -1,270 +0,0 @@
A. HISTORY OF THE SOFTWARE
==========================
Python was created in the early 1990s by Guido van Rossum at Stichting
Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
as a successor of a language called ABC. Guido remains Python's
principal author, although it includes many contributions from others.
In 1995, Guido continued his work on Python at the Corporation for
National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
in Reston, Virginia where he released several versions of the
software.
In May 2000, Guido and the Python core development team moved to
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
year, the PythonLabs team moved to Digital Creations (now Zope
Corporation, see http://www.zope.com). In 2001, the Python Software
Foundation (PSF, see http://www.python.org/psf/) was formed, a
non-profit organization created specifically to own Python-related
Intellectual Property. Zope Corporation is a sponsoring member of
the PSF.
All Python releases are Open Source (see http://www.opensource.org for
the Open Source Definition). Historically, most, but not all, Python
releases have also been GPL-compatible; the table below summarizes
the various releases.
Release Derived Year Owner GPL-
from compatible? (1)
0.9.0 thru 1.2 1991-1995 CWI yes
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
1.6 1.5.2 2000 CNRI no
2.0 1.6 2000 BeOpen.com no
1.6.1 1.6 2001 CNRI yes (2)
2.1 2.0+1.6.1 2001 PSF no
2.0.1 2.0+1.6.1 2001 PSF yes
2.1.1 2.1+2.0.1 2001 PSF yes
2.2 2.1.1 2001 PSF yes
2.1.2 2.1.1 2002 PSF yes
2.1.3 2.1.2 2002 PSF yes
2.2.1 2.2 2002 PSF yes
2.2.2 2.2.1 2002 PSF yes
2.2.3 2.2.2 2003 PSF yes
2.3 2.2.2 2002-2003 PSF yes
2.3.1 2.3 2002-2003 PSF yes
2.3.2 2.3.1 2002-2003 PSF yes
2.3.3 2.3.2 2002-2003 PSF yes
2.3.4 2.3.3 2004 PSF yes
2.3.5 2.3.4 2005 PSF yes
2.4 2.3 2004 PSF yes
2.4.1 2.4 2005 PSF yes
2.4.2 2.4.1 2005 PSF yes
2.4.3 2.4.2 2006 PSF yes
2.5 2.4 2006 PSF yes
2.7 2.6 2010 PSF yes
Footnotes:
(1) GPL-compatible doesn't mean that we're distributing Python under
the GPL. All Python licenses, unlike the GPL, let you distribute
a modified version without making your changes open source. The
GPL-compatible licenses make it possible to combine Python with
other software that is released under the GPL; the others don't.
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
because its license has a choice of law clause. According to
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
is "not incompatible" with the GPL.
Thanks to the many outside volunteers who have worked under Guido's
direction to make these releases possible.
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
===============================================================
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python
alone or in any derivative version, provided, however, that PSF's
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights
Reserved" are retained in Python alone or in any derivative version
prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.
4. PSF is making Python available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-------------------------------------------
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
Individual or Organization ("Licensee") accessing and otherwise using
this software in source or binary form and its associated
documentation ("the Software").
2. Subject to the terms and conditions of this BeOpen Python License
Agreement, BeOpen hereby grants Licensee a non-exclusive,
royalty-free, world-wide license to reproduce, analyze, test, perform
and/or display publicly, prepare derivative works, distribute, and
otherwise use the Software alone or in any derivative version,
provided, however, that the BeOpen Python License is retained in the
Software, alone or in any derivative version prepared by Licensee.
3. BeOpen is making the Software available to Licensee on an "AS IS"
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all
respects by the law of the State of California, excluding conflict of
law provisions. Nothing in this License Agreement shall be deemed to
create any relationship of agency, partnership, or joint venture
between BeOpen and Licensee. This License Agreement does not grant
permission to use BeOpen trademarks or trade names in a trademark
sense to endorse or promote products or services of Licensee, or any
third party. As an exception, the "BeOpen Python" logos available at
http://www.pythonlabs.com/logos.html may be used according to the
permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
---------------------------------------
1. This LICENSE AGREEMENT is between the Corporation for National
Research Initiatives, having an office at 1895 Preston White Drive,
Reston, VA 20191 ("CNRI"), and the Individual or Organization
("Licensee") accessing and otherwise using Python 1.6.1 software in
source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, CNRI
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 1.6.1
alone or in any derivative version, provided, however, that CNRI's
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
1995-2001 Corporation for National Research Initiatives; All Rights
Reserved" are retained in Python 1.6.1 alone or in any derivative
version prepared by Licensee. Alternately, in lieu of CNRI's License
Agreement, Licensee may substitute the following text (omitting the
quotes): "Python 1.6.1 is made available subject to the terms and
conditions in CNRI's License Agreement. This Agreement together with
Python 1.6.1 may be located on the Internet using the following
unique, persistent identifier (known as a handle): 1895.22/1013. This
Agreement may also be obtained from a proxy server on the Internet
using the following URL: http://hdl.handle.net/1895.22/1013".
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 1.6.1 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 1.6.1.
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. This License Agreement shall be governed by the federal
intellectual property law of the United States, including without
limitation the federal copyright law, and, to the extent such
U.S. federal law does not apply, by the law of the Commonwealth of
Virginia, excluding Virginia's conflict of law provisions.
Notwithstanding the foregoing, with regard to derivative works based
on Python 1.6.1 that incorporate non-separable material that was
previously distributed under the GNU General Public License (GPL), the
law of the Commonwealth of Virginia shall govern this License
Agreement only as to issues arising under or with respect to
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
License Agreement shall be deemed to create any relationship of
agency, partnership, or joint venture between CNRI and Licensee. This
License Agreement does not grant permission to use CNRI trademarks or
trade name in a trademark sense to endorse or promote products or
services of Licensee, or any third party.
8. By clicking on the "ACCEPT" button where indicated, or by copying,
installing or otherwise using Python 1.6.1, Licensee agrees to be
bound by the terms and conditions of this License Agreement.
ACCEPT
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
--------------------------------------------------
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -118,6 +118,15 @@ Please note that g_malloc will exit on allocation failure, so there
is no need to test for failure (as you would have to with malloc). is no need to test for failure (as you would have to with malloc).
Calling g_malloc with a zero size is valid and will return NULL. Calling g_malloc with a zero size is valid and will return NULL.
Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following
reasons:
a. It catches multiplication overflowing size_t;
b. It returns T * instead of void *, letting compiler catch more type
errors.
Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though.
Memory allocated by qemu_memalign or qemu_blockalign must be freed with Memory allocated by qemu_memalign or qemu_blockalign must be freed with
qemu_vfree, since breaking this will cause problems on Win32. qemu_vfree, since breaking this will cause problems on Win32.

View File

@ -135,6 +135,8 @@ M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org L: qemu-arm@nongnu.org
S: Maintained S: Maintained
F: target/arm/ F: target/arm/
F: tests/tcg/arm/
F: tests/tcg/aarch64/
F: hw/arm/ F: hw/arm/
F: hw/cpu/a*mpcore.c F: hw/cpu/a*mpcore.c
F: include/hw/cpu/a*mpcore.h F: include/hw/cpu/a*mpcore.h
@ -185,7 +187,7 @@ F: disas/microblaze.c
MIPS MIPS
M: Aurelien Jarno <aurelien@aurel32.net> M: Aurelien Jarno <aurelien@aurel32.net>
M: Yongbok Kim <yongbok.kim@mips.com> M: Aleksandar Markovic <aleksandar.markovic@mips.com>
S: Maintained S: Maintained
F: target/mips/ F: target/mips/
F: hw/mips/ F: hw/mips/
@ -283,6 +285,8 @@ M: Richard Henderson <rth@twiddle.net>
M: Eduardo Habkost <ehabkost@redhat.com> M: Eduardo Habkost <ehabkost@redhat.com>
S: Maintained S: Maintained
F: target/i386/ F: target/i386/
F: tests/tcg/i386/
F: tests/tcg/x86_64/
F: hw/i386/ F: hw/i386/
F: disas/i386.c F: disas/i386.c
T: git git://github.com/ehabkost/qemu.git x86-next T: git git://github.com/ehabkost/qemu.git x86-next
@ -303,6 +307,10 @@ F: target/tricore/
F: hw/tricore/ F: hw/tricore/
F: include/hw/tricore/ F: include/hw/tricore/
Multiarch Linux User Tests
M: Alex Bennée <alex.bennee@linaro.org>
F: tests/tcg/multiarch/
Guest CPU Cores (KVM): Guest CPU Cores (KVM):
---------------------- ----------------------
@ -447,6 +455,10 @@ F: hw/timer/cmsdk-apb-timer.c
F: include/hw/timer/cmsdk-apb-timer.h F: include/hw/timer/cmsdk-apb-timer.h
F: hw/char/cmsdk-apb-uart.c F: hw/char/cmsdk-apb-uart.c
F: include/hw/char/cmsdk-apb-uart.h F: include/hw/char/cmsdk-apb-uart.h
F: hw/misc/tz-ppc.c
F: include/hw/misc/tz-ppc.h
F: hw/misc/tz-mpc.c
F: include/hw/misc/tz-mpc.h
ARM cores ARM cores
M: Peter Maydell <peter.maydell@linaro.org> M: Peter Maydell <peter.maydell@linaro.org>
@ -489,9 +501,10 @@ F: include/hw/arm/digic.h
F: hw/*/digic* F: hw/*/digic*
Gumstix Gumstix
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
L: qemu-devel@nongnu.org L: qemu-devel@nongnu.org
L: qemu-arm@nongnu.org L: qemu-arm@nongnu.org
S: Orphan S: Odd Fixes
F: hw/arm/gumstix.c F: hw/arm/gumstix.c
i.MX31 i.MX31
@ -515,8 +528,11 @@ M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org L: qemu-arm@nongnu.org
S: Maintained S: Maintained
F: hw/arm/mps2.c F: hw/arm/mps2.c
F: hw/misc/mps2-scc.c F: hw/arm/mps2-tz.c
F: include/hw/misc/mps2-scc.h F: hw/misc/mps2-*.c
F: include/hw/misc/mps2-*.h
F: hw/arm/iotkit.c
F: include/hw/arm/iotkit.h
Musicpal Musicpal
M: Jan Kiszka <jan.kiszka@web.de> M: Jan Kiszka <jan.kiszka@web.de>
@ -629,6 +645,17 @@ M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
S: Maintained S: Maintained
F: hw/arm/msf2-som.c F: hw/arm/msf2-som.c
ASPEED BMCs
M: Cédric Le Goater <clg@kaod.org>
R: Andrew Jeffery <andrew@aj.id.au>
R: Joel Stanley <joel@jms.id.au>
L: qemu-arm@nongnu.org
S: Maintained
F: hw/*/*aspeed*
F: include/hw/*/*aspeed*
F: hw/net/ftgmac100.c
F: include/hw/net/ftgmac100.h
CRIS Machines CRIS Machines
------------- -------------
Axis Dev88 Axis Dev88
@ -691,7 +718,7 @@ S: Maintained
F: hw/mips/mips_malta.c F: hw/mips/mips_malta.c
Mipssim Mipssim
M: Yongbok Kim <yongbok.kim@mips.com> M: Aleksandar Markovic <aleksandar.markovic@mips.com>
S: Odd Fixes S: Odd Fixes
F: hw/mips/mips_mipssim.c F: hw/mips/mips_mipssim.c
F: hw/net/mipsnet.c F: hw/net/mipsnet.c
@ -702,7 +729,7 @@ S: Maintained
F: hw/mips/mips_r4k.c F: hw/mips/mips_r4k.c
Fulong 2E Fulong 2E
M: Yongbok Kim <yongbok.kim@mips.com> M: Aleksandar Markovic <aleksandar.markovic@mips.com>
S: Odd Fixes S: Odd Fixes
F: hw/mips/mips_fulong2e.c F: hw/mips/mips_fulong2e.c
F: hw/isa/vt82c686.c F: hw/isa/vt82c686.c
@ -761,8 +788,11 @@ F: hw/ppc/mac_newworld.c
F: hw/pci-host/uninorth.c F: hw/pci-host/uninorth.c
F: hw/pci-bridge/dec.[hc] F: hw/pci-bridge/dec.[hc]
F: hw/misc/macio/ F: hw/misc/macio/
F: include/hw/ppc/mac_dbdma.h F: hw/misc/mos6522.c
F: hw/nvram/mac_nvram.c F: hw/nvram/mac_nvram.c
F: include/hw/misc/macio/
F: include/hw/misc/mos6522.h
F: include/hw/ppc/mac_dbdma.h
Old World Old World
M: Alexander Graf <agraf@suse.de> M: Alexander Graf <agraf@suse.de>
@ -821,6 +851,7 @@ M: BALATON Zoltan <balaton@eik.bme.hu>
L: qemu-ppc@nongnu.org L: qemu-ppc@nongnu.org
S: Maintained S: Maintained
F: hw/ide/sii3112.c F: hw/ide/sii3112.c
F: hw/timer/m41t80.c
SH4 Machines SH4 Machines
------------ ------------
@ -909,7 +940,7 @@ X86 Machines
------------ ------------
PC PC
M: Michael S. Tsirkin <mst@redhat.com> M: Michael S. Tsirkin <mst@redhat.com>
M: Marcel Apfelbaum <marcel@redhat.com> M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
S: Supported S: Supported
F: include/hw/i386/ F: include/hw/i386/
F: hw/i386/ F: hw/i386/
@ -959,7 +990,7 @@ F: include/hw/timer/mc146818rtc*
Machine core Machine core
M: Eduardo Habkost <ehabkost@redhat.com> M: Eduardo Habkost <ehabkost@redhat.com>
M: Marcel Apfelbaum <marcel@redhat.com> M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
S: Supported S: Supported
F: hw/core/machine.c F: hw/core/machine.c
F: hw/core/null-machine.c F: hw/core/null-machine.c
@ -998,6 +1029,7 @@ F: hw/block/cdrom.c
F: hw/block/hd-geometry.c F: hw/block/hd-geometry.c
F: tests/ide-test.c F: tests/ide-test.c
F: tests/ahci-test.c F: tests/ahci-test.c
F: tests/cdrom-test.c
F: tests/libqos/ahci* F: tests/libqos/ahci*
T: git git://github.com/jnsnow/qemu.git ide T: git git://github.com/jnsnow/qemu.git ide
@ -1033,7 +1065,7 @@ F: hw/ipack/
PCI PCI
M: Michael S. Tsirkin <mst@redhat.com> M: Michael S. Tsirkin <mst@redhat.com>
M: Marcel Apfelbaum <marcel@redhat.com> M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
S: Supported S: Supported
F: include/hw/pci/* F: include/hw/pci/*
F: hw/misc/pci-testdev.c F: hw/misc/pci-testdev.c
@ -1314,6 +1346,33 @@ S: Maintained
F: include/hw/misc/unimp.h F: include/hw/misc/unimp.h
F: hw/misc/unimp.c F: hw/misc/unimp.c
Standard VGA
M: Gerd Hoffmann <kraxel@redhat.com>
S: Maintained
F: hw/display/vga*
F: hw/display/bochs-display.c
F: include/hw/display/vga.h
F: include/hw/display/bochs-vbe.h
ramfb
M: Gerd Hoffmann <kraxel@redhat.com>
S: Maintained
F: hw/display/ramfb*.c
F: include/hw/display/ramfb.h
virtio-gpu
M: Gerd Hoffmann <kraxel@redhat.com>
S: Maintained
F: hw/display/virtio-gpu*
F: hw/display/virtio-vga.c
F: include/hw/virtio/virtio-gpu.h
Cirrus VGA
M: Gerd Hoffmann <kraxel@redhat.com>
S: Odd Fixes
W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/
F: hw/display/cirrus*
Subsystems Subsystems
---------- ----------
Audio Audio
@ -1339,6 +1398,8 @@ F: qemu-img*
F: qemu-io* F: qemu-io*
F: tests/qemu-iotests/ F: tests/qemu-iotests/
F: util/qemu-progress.c F: util/qemu-progress.c
F: qobject/block-qdict.c
F: test/check-block-qdict.c
T: git git://repo.or.cz/qemu/kevin.git block T: git git://repo.or.cz/qemu/kevin.git block
Block I/O path Block I/O path
@ -1369,10 +1430,14 @@ L: qemu-block@nongnu.org
S: Supported S: Supported
F: blockjob.c F: blockjob.c
F: include/block/blockjob.h F: include/block/blockjob.h
F: job.c
F: job-qmp.c
F: include/block/job.h
F: block/backup.c F: block/backup.c
F: block/commit.c F: block/commit.c
F: block/stream.c F: block/stream.c
F: block/mirror.c F: block/mirror.c
F: qapi/job.json
T: git git://github.com/codyprime/qemu-kvm-jtc.git block T: git git://github.com/codyprime/qemu-kvm-jtc.git block
Block QAPI, monitor, command line Block QAPI, monitor, command line
@ -1573,7 +1638,8 @@ F: tests/test-*-visitor.c
F: tests/test-qapi-*.c F: tests/test-qapi-*.c
F: tests/test-qmp-*.c F: tests/test-qmp-*.c
F: tests/test-visitor-serialization.c F: tests/test-visitor-serialization.c
F: scripts/qapi* F: scripts/qapi-gen.py
F: scripts/qapi/*
F: docs/devel/qapi* F: docs/devel/qapi*
T: git git://repo.or.cz/qemu/armbru.git qapi-next T: git git://repo.or.cz/qemu/armbru.git qapi-next
@ -1645,6 +1711,7 @@ S: Maintained
F: slirp/ F: slirp/
F: net/slirp.c F: net/slirp.c
F: include/net/slirp.h F: include/net/slirp.h
T: git https://people.debian.org/~sthibault/qemu.git slirp
T: git git://git.kiszka.org/qemu.git queues/slirp T: git git://git.kiszka.org/qemu.git queues/slirp
Stubs Stubs
@ -1656,6 +1723,8 @@ Tracing
M: Stefan Hajnoczi <stefanha@redhat.com> M: Stefan Hajnoczi <stefanha@redhat.com>
S: Maintained S: Maintained
F: trace/ F: trace/
F: trace-events
F: qemu-option-trace.texi
F: scripts/tracetool.py F: scripts/tracetool.py
F: scripts/tracetool/ F: scripts/tracetool/
F: docs/devel/tracing.txt F: docs/devel/tracing.txt
@ -1781,6 +1850,12 @@ F: include/sysemu/replay.h
F: docs/replay.txt F: docs/replay.txt
F: stubs/replay.c F: stubs/replay.c
IOVA Tree
M: Peter Xu <peterx@redhat.com>
S: Maintained
F: include/qemu/iova-tree.h
F: util/iova-tree.c
Usermode Emulation Usermode Emulation
------------------ ------------------
Overall Overall
@ -1921,6 +1996,7 @@ F: nbd/
F: include/block/nbd* F: include/block/nbd*
F: qemu-nbd.* F: qemu-nbd.*
F: blockdev-nbd.c F: blockdev-nbd.c
F: docs/interop/nbd.txt
T: git git://repo.or.cz/qemu/ericb.git nbd T: git git://repo.or.cz/qemu/ericb.git nbd
NFS NFS
@ -1976,6 +2052,12 @@ S: Supported
F: block/quorum.c F: block/quorum.c
L: qemu-block@nongnu.org L: qemu-block@nongnu.org
blklogwrites
M: Ari Sundholm <ari@tuxera.com>
L: qemu-block@nongnu.org
S: Supported
F: block/blklogwrites.c
blkverify blkverify
M: Stefan Hajnoczi <stefanha@redhat.com> M: Stefan Hajnoczi <stefanha@redhat.com>
L: qemu-block@nongnu.org L: qemu-block@nongnu.org
@ -2075,7 +2157,7 @@ F: docs/block-replication.txt
PVRDMA PVRDMA
M: Yuval Shaia <yuval.shaia@oracle.com> M: Yuval Shaia <yuval.shaia@oracle.com>
M: Marcel Apfelbaum <marcel@redhat.com> M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
S: Maintained S: Maintained
F: hw/rdma/* F: hw/rdma/*
F: hw/rdma/vmw/* F: hw/rdma/vmw/*
@ -2090,6 +2172,7 @@ R: Philippe Mathieu-Daudé <f4bug@amsat.org>
L: qemu-devel@nongnu.org L: qemu-devel@nongnu.org
S: Maintained S: Maintained
F: .travis.yml F: .travis.yml
F: scripts/travis/
F: .shippable.yml F: .shippable.yml
F: tests/docker/ F: tests/docker/
F: tests/vm/ F: tests/vm/
@ -2097,6 +2180,13 @@ W: https://travis-ci.org/qemu/qemu
W: https://app.shippable.com/github/qemu/qemu W: https://app.shippable.com/github/qemu/qemu
W: http://patchew.org/QEMU/ W: http://patchew.org/QEMU/
Guest Test Compilation Support
M: Alex Bennée <alex.bennee@linaro.org>
R: Philippe Mathieu-Daudé <f4bug@amsat.org>
F: tests/tcg/Makefile
F: tests/tcg/Makefile.include
L: qemu-devel@nongnu.org
Documentation Documentation
------------- -------------
Build system architecture Build system architecture
@ -2104,6 +2194,10 @@ M: Daniel P. Berrange <berrange@redhat.com>
S: Odd Fixes S: Odd Fixes
F: docs/devel/build-system.txt F: docs/devel/build-system.txt
Incompatible changes
R: libvir-list@redhat.com
F: qemu-deprecated.texi
Build System Build System
------------ ------------
GIT submodules GIT submodules

View File

@ -20,8 +20,6 @@ ifneq ($(wildcard config-host.mak),)
all: all:
include config-host.mak include config-host.mak
PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON)
git-submodule-update: git-submodule-update:
.PHONY: git-submodule-update .PHONY: git-submodule-update
@ -98,6 +96,7 @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c
GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c
GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c
GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c
GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c
GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c
GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
@ -116,6 +115,7 @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c
GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c
GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c
GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c
GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c
GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c
GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c
GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
@ -133,6 +133,7 @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c
GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c
GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c
GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c
GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c
GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c
GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c
GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
@ -150,6 +151,7 @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c
GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c
GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c
GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c
GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c
GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c
GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c
GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
@ -318,6 +320,7 @@ KEYCODEMAP_FILES = \
ui/input-keymap-xorgkbd-to-qcode.c \ ui/input-keymap-xorgkbd-to-qcode.c \
ui/input-keymap-xorgxquartz-to-qcode.c \ ui/input-keymap-xorgxquartz-to-qcode.c \
ui/input-keymap-xorgxwin-to-qcode.c \ ui/input-keymap-xorgxwin-to-qcode.c \
ui/input-keymap-osx-to-qcode.c \
$(NULL) $(NULL)
GENERATED_FILES += $(KEYCODEMAP_FILES) GENERATED_FILES += $(KEYCODEMAP_FILES)
@ -347,7 +350,7 @@ $(call set-vpath, $(SRC_PATH))
LIBS+=-lz $(LIBS_TOOLS) LIBS+=-lz $(LIBS_TOOLS)
HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF)
ifdef BUILD_DOCS ifdef BUILD_DOCS
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
@ -485,7 +488,7 @@ subdir-dtc: .git-submodule-status dtc/libfdt dtc/tests
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,) $(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
dtc/%: .git-submodule-status dtc/%: .git-submodule-status
mkdir -p $@ @mkdir -p $@
# Overriding CFLAGS causes us to lose defines added in the sub-makefile. # Overriding CFLAGS causes us to lose defines added in the sub-makefile.
# Not overriding CFLAGS leads to mis-matches between compilation modes. # Not overriding CFLAGS leads to mis-matches between compilation modes.
@ -563,7 +566,6 @@ $(SRC_PATH)/scripts/qapi/types.py \
$(SRC_PATH)/scripts/qapi/visit.py \ $(SRC_PATH)/scripts/qapi/visit.py \
$(SRC_PATH)/scripts/qapi/common.py \ $(SRC_PATH)/scripts/qapi/common.py \
$(SRC_PATH)/scripts/qapi/doc.py \ $(SRC_PATH)/scripts/qapi/doc.py \
$(SRC_PATH)/scripts/ordereddict.py \
$(SRC_PATH)/scripts/qapi-gen.py $(SRC_PATH)/scripts/qapi-gen.py
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \ qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
@ -572,7 +574,7 @@ qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \
qga/qapi-generated/qga-qapi-doc.texi: \ qga/qapi-generated/qga-qapi-doc.texi: \
qga/qapi-generated/qapi-gen-timestamp ; qga/qapi-generated/qapi-gen-timestamp ;
qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py) qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
-o qga/qapi-generated -p "qga-" $<, \ -o qga/qapi-generated -p "qga-" $<, \
"GEN","$(@:%-timestamp=%)") "GEN","$(@:%-timestamp=%)")
@>$@ @>$@
@ -582,6 +584,7 @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \
$(SRC_PATH)/qapi/char.json \ $(SRC_PATH)/qapi/char.json \
$(SRC_PATH)/qapi/crypto.json \ $(SRC_PATH)/qapi/crypto.json \
$(SRC_PATH)/qapi/introspect.json \ $(SRC_PATH)/qapi/introspect.json \
$(SRC_PATH)/qapi/job.json \
$(SRC_PATH)/qapi/migration.json \ $(SRC_PATH)/qapi/migration.json \
$(SRC_PATH)/qapi/misc.json \ $(SRC_PATH)/qapi/misc.json \
$(SRC_PATH)/qapi/net.json \ $(SRC_PATH)/qapi/net.json \
@ -601,6 +604,7 @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \
qapi/qapi-types-common.c qapi/qapi-types-common.h \ qapi/qapi-types-common.c qapi/qapi-types-common.h \
qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \
qapi/qapi-types-job.c qapi/qapi-types-job.h \
qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ qapi/qapi-types-migration.c qapi/qapi-types-migration.h \
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
qapi/qapi-types-net.c qapi/qapi-types-net.h \ qapi/qapi-types-net.c qapi/qapi-types-net.h \
@ -619,6 +623,7 @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \
qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ qapi/qapi-visit-common.c qapi/qapi-visit-common.h \
qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \
qapi/qapi-visit-job.c qapi/qapi-visit-job.h \
qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
@ -636,6 +641,7 @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \
qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ qapi/qapi-commands-common.c qapi/qapi-commands-common.h \
qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \
qapi/qapi-commands-job.c qapi/qapi-commands-job.h \
qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
@ -653,6 +659,7 @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \
qapi/qapi-events-common.c qapi/qapi-events-common.h \ qapi/qapi-events-common.c qapi/qapi-events-common.h \
qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \
qapi/qapi-events-job.c qapi/qapi-events-job.h \
qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ qapi/qapi-events-migration.c qapi/qapi-events-migration.h \
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
qapi/qapi-events-net.c qapi/qapi-events-net.h \ qapi/qapi-events-net.c qapi/qapi-events-net.h \
@ -667,7 +674,7 @@ qapi/qapi-introspect.h qapi/qapi-introspect.c \
qapi/qapi-doc.texi: \ qapi/qapi-doc.texi: \
qapi-gen-timestamp ; qapi-gen-timestamp ;
qapi-gen-timestamp: $(qapi-modules) $(qapi-py) qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
$(call quiet-command,$(PYTHON_UTF8) $(SRC_PATH)/scripts/qapi-gen.py \ $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
-o "qapi" -b $<, \ -o "qapi" -b $<, \
"GEN","$(@:%-timestamp=%)") "GEN","$(@:%-timestamp=%)")
@>$@ @>$@
@ -716,6 +723,14 @@ module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak
$(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ $(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \
"GEN","$@") "GEN","$@")
ifdef CONFIG_GCOV
.PHONY: clean-coverage
clean-coverage:
$(call quiet-command, \
find . \( -name '*.gcda' -o -name '*.gcov' \) -type f -exec rm {} +, \
"CLEAN", "coverage files")
endif
clean: clean:
# avoid old build problems by removing potentially incorrect old files # avoid old build problems by removing potentially incorrect old files
rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
@ -971,6 +986,16 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \
docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \
docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi
# Reports/Analysis
%/coverage-report.html:
@mkdir -p $*
$(call quiet-command,\
gcovr -p --html --html-details -o $@, \
"GEN", "coverage-report.html")
.PHONY: coverage-report
coverage-report: $(CURDIR)/reports/coverage/coverage-report.html
ifdef CONFIG_WIN32 ifdef CONFIG_WIN32
@ -1047,9 +1072,6 @@ endif
include $(SRC_PATH)/tests/docker/Makefile.include include $(SRC_PATH)/tests/docker/Makefile.include
include $(SRC_PATH)/tests/vm/Makefile.include include $(SRC_PATH)/tests/vm/Makefile.include
printgen:
@echo $(GENERATED_FILES)
.PHONY: help .PHONY: help
help: help:
@echo 'Generic targets:' @echo 'Generic targets:'
@ -1069,6 +1091,9 @@ endif
echo '') echo '')
@echo 'Cleaning targets:' @echo 'Cleaning targets:'
@echo ' clean - Remove most generated files but keep the config' @echo ' clean - Remove most generated files but keep the config'
ifdef CONFIG_GCOV
@echo ' clean-coverage - Remove coverage files'
endif
@echo ' distclean - Remove all generated files' @echo ' distclean - Remove all generated files'
@echo ' dist - Build a distributable tarball' @echo ' dist - Build a distributable tarball'
@echo '' @echo ''
@ -1080,6 +1105,9 @@ endif
@echo 'Documentation targets:' @echo 'Documentation targets:'
@echo ' html info pdf txt' @echo ' html info pdf txt'
@echo ' - Build documentation in specified format' @echo ' - Build documentation in specified format'
ifdef CONFIG_GCOV
@echo ' coverage-report - Create code coverage report'
endif
@echo '' @echo ''
ifdef CONFIG_WIN32 ifdef CONFIG_WIN32
@echo 'Windows targets:' @echo 'Windows targets:'

View File

@ -10,6 +10,7 @@ util-obj-y += qapi/qapi-types-char.o
util-obj-y += qapi/qapi-types-common.o util-obj-y += qapi/qapi-types-common.o
util-obj-y += qapi/qapi-types-crypto.o util-obj-y += qapi/qapi-types-crypto.o
util-obj-y += qapi/qapi-types-introspect.o util-obj-y += qapi/qapi-types-introspect.o
util-obj-y += qapi/qapi-types-job.o
util-obj-y += qapi/qapi-types-migration.o util-obj-y += qapi/qapi-types-migration.o
util-obj-y += qapi/qapi-types-misc.o util-obj-y += qapi/qapi-types-misc.o
util-obj-y += qapi/qapi-types-net.o util-obj-y += qapi/qapi-types-net.o
@ -28,6 +29,7 @@ util-obj-y += qapi/qapi-visit-char.o
util-obj-y += qapi/qapi-visit-common.o util-obj-y += qapi/qapi-visit-common.o
util-obj-y += qapi/qapi-visit-crypto.o util-obj-y += qapi/qapi-visit-crypto.o
util-obj-y += qapi/qapi-visit-introspect.o util-obj-y += qapi/qapi-visit-introspect.o
util-obj-y += qapi/qapi-visit-job.o
util-obj-y += qapi/qapi-visit-migration.o util-obj-y += qapi/qapi-visit-migration.o
util-obj-y += qapi/qapi-visit-misc.o util-obj-y += qapi/qapi-visit-misc.o
util-obj-y += qapi/qapi-visit-net.o util-obj-y += qapi/qapi-visit-net.o
@ -45,6 +47,7 @@ util-obj-y += qapi/qapi-events-char.o
util-obj-y += qapi/qapi-events-common.o util-obj-y += qapi/qapi-events-common.o
util-obj-y += qapi/qapi-events-crypto.o util-obj-y += qapi/qapi-events-crypto.o
util-obj-y += qapi/qapi-events-introspect.o util-obj-y += qapi/qapi-events-introspect.o
util-obj-y += qapi/qapi-events-job.o
util-obj-y += qapi/qapi-events-migration.o util-obj-y += qapi/qapi-events-migration.o
util-obj-y += qapi/qapi-events-misc.o util-obj-y += qapi/qapi-events-misc.o
util-obj-y += qapi/qapi-events-net.o util-obj-y += qapi/qapi-events-net.o
@ -63,7 +66,7 @@ chardev-obj-y = chardev/
# block-obj-y is code used by both qemu system emulation and qemu-img # block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y += nbd/ block-obj-y += nbd/
block-obj-y += block.o blockjob.o block-obj-y += block.o blockjob.o job.o
block-obj-y += block/ scsi/ block-obj-y += block/ scsi/
block-obj-y += qemu-io-cmds.o block-obj-y += qemu-io-cmds.o
block-obj-$(CONFIG_REPLICATION) += replication.o block-obj-$(CONFIG_REPLICATION) += replication.o
@ -94,6 +97,7 @@ io-obj-y = io/
ifeq ($(CONFIG_SOFTMMU),y) ifeq ($(CONFIG_SOFTMMU),y)
common-obj-y = blockdev.o blockdev-nbd.o block/ common-obj-y = blockdev.o blockdev-nbd.o block/
common-obj-y += bootdevice.o iothread.o common-obj-y += bootdevice.o iothread.o
common-obj-y += job-qmp.o
common-obj-y += net/ common-obj-y += net/
common-obj-y += qdev-monitor.o device-hotplug.o common-obj-y += qdev-monitor.o device-hotplug.o
common-obj-$(CONFIG_WIN32) += os-win32.o common-obj-$(CONFIG_WIN32) += os-win32.o
@ -140,6 +144,7 @@ common-obj-y += qapi/qapi-commands-char.o
common-obj-y += qapi/qapi-commands-common.o common-obj-y += qapi/qapi-commands-common.o
common-obj-y += qapi/qapi-commands-crypto.o common-obj-y += qapi/qapi-commands-crypto.o
common-obj-y += qapi/qapi-commands-introspect.o common-obj-y += qapi/qapi-commands-introspect.o
common-obj-y += qapi/qapi-commands-job.o
common-obj-y += qapi/qapi-commands-migration.o common-obj-y += qapi/qapi-commands-migration.o
common-obj-y += qapi/qapi-commands-misc.o common-obj-y += qapi/qapi-commands-misc.o
common-obj-y += qapi/qapi-commands-net.o common-obj-y += qapi/qapi-commands-net.o
@ -191,66 +196,67 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/
###################################################################### ######################################################################
trace-events-subdirs = trace-events-subdirs =
trace-events-subdirs += util trace-events-subdirs += accel/kvm
trace-events-subdirs += crypto trace-events-subdirs += accel/tcg
trace-events-subdirs += io trace-events-subdirs += audio
trace-events-subdirs += migration
trace-events-subdirs += block trace-events-subdirs += block
trace-events-subdirs += chardev trace-events-subdirs += chardev
trace-events-subdirs += crypto
trace-events-subdirs += hw/9pfs
trace-events-subdirs += hw/acpi
trace-events-subdirs += hw/alpha
trace-events-subdirs += hw/arm
trace-events-subdirs += hw/audio
trace-events-subdirs += hw/block trace-events-subdirs += hw/block
trace-events-subdirs += hw/block/dataplane trace-events-subdirs += hw/block/dataplane
trace-events-subdirs += hw/char trace-events-subdirs += hw/char
trace-events-subdirs += hw/intc
trace-events-subdirs += hw/net
trace-events-subdirs += hw/rdma
trace-events-subdirs += hw/rdma/vmw
trace-events-subdirs += hw/virtio
trace-events-subdirs += hw/audio
trace-events-subdirs += hw/misc
trace-events-subdirs += hw/misc/macio
trace-events-subdirs += hw/usb
trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/nvram
trace-events-subdirs += hw/display trace-events-subdirs += hw/display
trace-events-subdirs += hw/input
trace-events-subdirs += hw/timer
trace-events-subdirs += hw/dma trace-events-subdirs += hw/dma
trace-events-subdirs += hw/sparc trace-events-subdirs += hw/hppa
trace-events-subdirs += hw/sparc64 trace-events-subdirs += hw/i2c
trace-events-subdirs += hw/sd
trace-events-subdirs += hw/isa
trace-events-subdirs += hw/mem
trace-events-subdirs += hw/i386 trace-events-subdirs += hw/i386
trace-events-subdirs += hw/i386/xen trace-events-subdirs += hw/i386/xen
trace-events-subdirs += hw/9pfs trace-events-subdirs += hw/ide
trace-events-subdirs += hw/ppc trace-events-subdirs += hw/input
trace-events-subdirs += hw/intc
trace-events-subdirs += hw/isa
trace-events-subdirs += hw/mem
trace-events-subdirs += hw/misc
trace-events-subdirs += hw/misc/macio
trace-events-subdirs += hw/net
trace-events-subdirs += hw/nvram
trace-events-subdirs += hw/pci trace-events-subdirs += hw/pci
trace-events-subdirs += hw/pci-host trace-events-subdirs += hw/pci-host
trace-events-subdirs += hw/ppc
trace-events-subdirs += hw/rdma
trace-events-subdirs += hw/rdma/vmw
trace-events-subdirs += hw/s390x trace-events-subdirs += hw/s390x
trace-events-subdirs += hw/vfio trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/acpi trace-events-subdirs += hw/sd
trace-events-subdirs += hw/arm trace-events-subdirs += hw/sparc
trace-events-subdirs += hw/alpha trace-events-subdirs += hw/sparc64
trace-events-subdirs += hw/hppa trace-events-subdirs += hw/timer
trace-events-subdirs += hw/xen
trace-events-subdirs += hw/ide
trace-events-subdirs += hw/tpm trace-events-subdirs += hw/tpm
trace-events-subdirs += ui trace-events-subdirs += hw/usb
trace-events-subdirs += audio trace-events-subdirs += hw/vfio
trace-events-subdirs += hw/virtio
trace-events-subdirs += hw/xen
trace-events-subdirs += io
trace-events-subdirs += linux-user
trace-events-subdirs += migration
trace-events-subdirs += nbd
trace-events-subdirs += net trace-events-subdirs += net
trace-events-subdirs += qapi
trace-events-subdirs += qom
trace-events-subdirs += scsi
trace-events-subdirs += target/arm trace-events-subdirs += target/arm
trace-events-subdirs += target/i386 trace-events-subdirs += target/i386
trace-events-subdirs += target/mips trace-events-subdirs += target/mips
trace-events-subdirs += target/sparc
trace-events-subdirs += target/s390x
trace-events-subdirs += target/ppc trace-events-subdirs += target/ppc
trace-events-subdirs += qom trace-events-subdirs += target/s390x
trace-events-subdirs += linux-user trace-events-subdirs += target/sparc
trace-events-subdirs += qapi trace-events-subdirs += ui
trace-events-subdirs += accel/tcg trace-events-subdirs += util
trace-events-subdirs += accel/kvm
trace-events-subdirs += nbd
trace-events-subdirs += scsi
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events) trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)

View File

@ -36,6 +36,11 @@ endif
PROGS=$(QEMU_PROG) $(QEMU_PROGW) PROGS=$(QEMU_PROG) $(QEMU_PROGW)
STPFILES= STPFILES=
# Makefile Tests
ifdef CONFIG_USER_ONLY
include $(SRC_PATH)/tests/tcg/Makefile.include
endif
config-target.h: config-target.h-timestamp config-target.h: config-target.h-timestamp
config-target.h-timestamp: config-target.mak config-target.h-timestamp: config-target.mak
@ -97,7 +102,7 @@ obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o obj-$(CONFIG_TCG_INTERPRETER) += tcg/tci.o
obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o obj-$(CONFIG_TCG_INTERPRETER) += disas/tci.o
obj-y += fpu/softfloat.o obj-$(CONFIG_TCG) += fpu/softfloat.o
obj-y += target/$(TARGET_BASE_ARCH)/ obj-y += target/$(TARGET_BASE_ARCH)/
obj-y += disas.o obj-y += disas.o
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
@ -138,6 +143,7 @@ obj-y += hw/
obj-y += memory.o obj-y += memory.o
obj-y += memory_mapping.o obj-y += memory_mapping.o
obj-y += dump.o obj-y += dump.o
obj-$(TARGET_X86_64) += win_dump.o
obj-y += migration/ram.o obj-y += migration/ram.o
LIBS := $(libs_softmmu) $(LIBS) LIBS := $(libs_softmmu) $(LIBS)

View File

@ -1 +1 @@
2.12.0 3.0.0

View File

@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
void configure_accelerator(MachineState *ms) void configure_accelerator(MachineState *ms)
{ {
const char *accel, *p; const char *accel;
char buf[10]; char **accel_list, **tmp;
int ret; int ret;
bool accel_initialised = false; bool accel_initialised = false;
bool init_failed = false; bool init_failed = false;
@ -83,13 +83,10 @@ void configure_accelerator(MachineState *ms)
accel = "tcg"; accel = "tcg";
} }
p = accel; accel_list = g_strsplit(accel, ":", 0);
while (!accel_initialised && *p != '\0') {
if (*p == ':') { for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
p++; acc = accel_find(*tmp);
}
p = get_opt_name(buf, sizeof(buf), p, ':');
acc = accel_find(buf);
if (!acc) { if (!acc) {
continue; continue;
} }
@ -107,6 +104,7 @@ void configure_accelerator(MachineState *ms)
accel_initialised = true; accel_initialised = true;
} }
} }
g_strfreev(accel_list);
if (!accel_initialised) { if (!accel_initialised) {
if (!init_failed) { if (!init_failed) {
@ -126,6 +124,15 @@ void accel_register_compat_props(AccelState *accel)
register_compat_props_array(class->global_props); register_compat_props_array(class->global_props);
} }
void accel_setup_post(MachineState *ms)
{
AccelState *accel = ms->accelerator;
AccelClass *acc = ACCEL_GET_CLASS(accel);
if (acc->setup_post) {
acc->setup_post(ms, accel);
}
}
static void register_accel_types(void) static void register_accel_types(void)
{ {
type_register_static(&accel_type); type_register_static(&accel_type);

View File

@ -256,7 +256,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
return 0; return 0;
} }
static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot) static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, bool new)
{ {
KVMState *s = kvm_state; KVMState *s = kvm_state;
struct kvm_userspace_memory_region mem; struct kvm_userspace_memory_region mem;
@ -267,7 +267,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot)
mem.userspace_addr = (unsigned long)slot->ram; mem.userspace_addr = (unsigned long)slot->ram;
mem.flags = slot->flags; mem.flags = slot->flags;
if (slot->memory_size && mem.flags & KVM_MEM_READONLY) { if (slot->memory_size && !new && (mem.flags ^ slot->old_flags) & KVM_MEM_READONLY) {
/* Set the slot size to 0 before setting the slot to the desired /* Set the slot size to 0 before setting the slot to the desired
* value. This is needed based on KVM commit 75d61fbc. */ * value. This is needed based on KVM commit 75d61fbc. */
mem.memory_size = 0; mem.memory_size = 0;
@ -275,6 +275,7 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot)
} }
mem.memory_size = slot->memory_size; mem.memory_size = slot->memory_size;
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem); ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
slot->old_flags = mem.flags;
trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr, trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
mem.memory_size, mem.userspace_addr, ret); mem.memory_size, mem.userspace_addr, ret);
return ret; return ret;
@ -391,17 +392,14 @@ static int kvm_mem_flags(MemoryRegion *mr)
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem, static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
MemoryRegion *mr) MemoryRegion *mr)
{ {
int old_flags;
old_flags = mem->flags;
mem->flags = kvm_mem_flags(mr); mem->flags = kvm_mem_flags(mr);
/* If nothing changed effectively, no need to issue ioctl */ /* If nothing changed effectively, no need to issue ioctl */
if (mem->flags == old_flags) { if (mem->flags == mem->old_flags) {
return 0; return 0;
} }
return kvm_set_user_memory_region(kml, mem); return kvm_set_user_memory_region(kml, mem, false);
} }
static int kvm_section_update_flags(KVMMemoryListener *kml, static int kvm_section_update_flags(KVMMemoryListener *kml,
@ -755,7 +753,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
/* unregister the slot */ /* unregister the slot */
mem->memory_size = 0; mem->memory_size = 0;
err = kvm_set_user_memory_region(kml, mem); mem->flags = 0;
err = kvm_set_user_memory_region(kml, mem, false);
if (err) { if (err) {
fprintf(stderr, "%s: error unregistering slot: %s\n", fprintf(stderr, "%s: error unregistering slot: %s\n",
__func__, strerror(-err)); __func__, strerror(-err));
@ -771,7 +770,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
mem->ram = ram; mem->ram = ram;
mem->flags = kvm_mem_flags(mr); mem->flags = kvm_mem_flags(mr);
err = kvm_set_user_memory_region(kml, mem); err = kvm_set_user_memory_region(kml, mem, true);
if (err) { if (err) {
fprintf(stderr, "%s: error registering slot: %s\n", __func__, fprintf(stderr, "%s: error registering slot: %s\n", __func__,
strerror(-err)); strerror(-err));

View File

@ -21,10 +21,6 @@ void tb_flush(CPUState *cpu)
{ {
} }
void tb_unlock(void)
{
}
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
{ {
} }

View File

@ -18,26 +18,37 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>. * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "trace/mem.h"
#if DATA_SIZE == 16 #if DATA_SIZE == 16
# define SUFFIX o # define SUFFIX o
# define DATA_TYPE Int128 # define DATA_TYPE Int128
# define BSWAP bswap128 # define BSWAP bswap128
# define SHIFT 4
#elif DATA_SIZE == 8 #elif DATA_SIZE == 8
# define SUFFIX q # define SUFFIX q
# define DATA_TYPE uint64_t # define DATA_TYPE uint64_t
# define SDATA_TYPE int64_t
# define BSWAP bswap64 # define BSWAP bswap64
# define SHIFT 3
#elif DATA_SIZE == 4 #elif DATA_SIZE == 4
# define SUFFIX l # define SUFFIX l
# define DATA_TYPE uint32_t # define DATA_TYPE uint32_t
# define SDATA_TYPE int32_t
# define BSWAP bswap32 # define BSWAP bswap32
# define SHIFT 2
#elif DATA_SIZE == 2 #elif DATA_SIZE == 2
# define SUFFIX w # define SUFFIX w
# define DATA_TYPE uint16_t # define DATA_TYPE uint16_t
# define SDATA_TYPE int16_t
# define BSWAP bswap16 # define BSWAP bswap16
# define SHIFT 1
#elif DATA_SIZE == 1 #elif DATA_SIZE == 1
# define SUFFIX b # define SUFFIX b
# define DATA_TYPE uint8_t # define DATA_TYPE uint8_t
# define SDATA_TYPE int8_t
# define BSWAP # define BSWAP
# define SHIFT 0
#else #else
# error unsupported data size # error unsupported data size
#endif #endif
@ -48,14 +59,37 @@
# define ABI_TYPE uint32_t # define ABI_TYPE uint32_t
#endif #endif
#define ATOMIC_TRACE_RMW do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
\
trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \
trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, \
info | TRACE_MEM_ST); \
} while (0)
#define ATOMIC_TRACE_LD do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
\
trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \
} while (0)
# define ATOMIC_TRACE_ST do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
\
trace_guest_mem_before_exec(ENV_GET_CPU(env), addr, info); \
} while (0)
/* Define host-endian atomic operations. Note that END is used within /* Define host-endian atomic operations. Note that END is used within
the ATOMIC_NAME macro, and redefined below. */ the ATOMIC_NAME macro, and redefined below. */
#if DATA_SIZE == 1 #if DATA_SIZE == 1
# define END # define END
# define MEND _be /* either le or be would be fine */
#elif defined(HOST_WORDS_BIGENDIAN) #elif defined(HOST_WORDS_BIGENDIAN)
# define END _be # define END _be
# define MEND _be
#else #else
# define END _le # define END _le
# define MEND _le
#endif #endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
@ -63,7 +97,10 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); DATA_TYPE ret;
ATOMIC_TRACE_RMW;
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return ret; return ret;
} }
@ -73,6 +110,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_LD;
__atomic_load(haddr, &val, __ATOMIC_RELAXED); __atomic_load(haddr, &val, __ATOMIC_RELAXED);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return val; return val;
@ -83,6 +122,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_ST;
__atomic_store(haddr, &val, __ATOMIC_RELAXED); __atomic_store(haddr, &val, __ATOMIC_RELAXED);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
} }
@ -92,7 +133,10 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret = atomic_xchg__nocheck(haddr, val); DATA_TYPE ret;
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, val);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return ret; return ret;
} }
@ -103,7 +147,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
{ \ { \
ATOMIC_MMU_DECLS; \ ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret = atomic_##X(haddr, val); \ DATA_TYPE ret; \
\
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, val); \
ATOMIC_MMU_CLEANUP; \ ATOMIC_MMU_CLEANUP; \
return ret; \ return ret; \
} }
@ -118,9 +165,48 @@ GEN_ATOMIC_HELPER(or_fetch)
GEN_ATOMIC_HELPER(xor_fetch) GEN_ATOMIC_HELPER(xor_fetch)
#undef GEN_ATOMIC_HELPER #undef GEN_ATOMIC_HELPER
/* These helpers are, as a whole, full barriers. Within the helper,
* the leading barrier is explicit and the trailing barrier is within
* cmpxchg primitive.
*
* Trace this load + RMW loop as a single RMW op. This way, regardless
* of CF_PARALLEL's value, we'll trace just a read and a write.
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE xval EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE cmp, old, new, val = xval; \
\
ATOMIC_TRACE_RMW; \
smp_mb(); \
cmp = atomic_read__nocheck(haddr); \
do { \
old = cmp; new = FN(old, val); \
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
} while (cmp != old); \
ATOMIC_MMU_CLEANUP; \
return RET; \
}
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA SIZE >= 16 */ #endif /* DATA SIZE >= 16 */
#undef END #undef END
#undef MEND
#if DATA_SIZE > 1 #if DATA_SIZE > 1
@ -128,8 +214,10 @@ GEN_ATOMIC_HELPER(xor_fetch)
within the ATOMIC_NAME macro. */ within the ATOMIC_NAME macro. */
#ifdef HOST_WORDS_BIGENDIAN #ifdef HOST_WORDS_BIGENDIAN
# define END _le # define END _le
# define MEND _le
#else #else
# define END _be # define END _be
# define MEND _be
#endif #endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
@ -137,7 +225,10 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); DATA_TYPE ret;
ATOMIC_TRACE_RMW;
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return BSWAP(ret); return BSWAP(ret);
} }
@ -147,6 +238,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_LD;
__atomic_load(haddr, &val, __ATOMIC_RELAXED); __atomic_load(haddr, &val, __ATOMIC_RELAXED);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return BSWAP(val); return BSWAP(val);
@ -157,6 +250,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ATOMIC_TRACE_ST;
val = BSWAP(val); val = BSWAP(val);
__atomic_store(haddr, &val, __ATOMIC_RELAXED); __atomic_store(haddr, &val, __ATOMIC_RELAXED);
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
@ -167,7 +262,10 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
{ {
ATOMIC_MMU_DECLS; ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ABI_TYPE ret = atomic_xchg__nocheck(haddr, BSWAP(val)); ABI_TYPE ret;
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP; ATOMIC_MMU_CLEANUP;
return BSWAP(ret); return BSWAP(ret);
} }
@ -178,7 +276,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
{ \ { \
ATOMIC_MMU_DECLS; \ ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \ DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret = atomic_##X(haddr, BSWAP(val)); \ DATA_TYPE ret; \
\
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, BSWAP(val)); \
ATOMIC_MMU_CLEANUP; \ ATOMIC_MMU_CLEANUP; \
return BSWAP(ret); \ return BSWAP(ret); \
} }
@ -192,54 +293,64 @@ GEN_ATOMIC_HELPER(xor_fetch)
#undef GEN_ATOMIC_HELPER #undef GEN_ATOMIC_HELPER
/* These helpers are, as a whole, full barriers. Within the helper,
* the leading barrier is explicit and the trailing barrier is within
* cmpxchg primitive.
*
* Trace this load + RMW loop as a single RMW op. This way, regardless
* of CF_PARALLEL's value, we'll trace just a read and a write.
*/
#define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE xval EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE ldo, ldn, old, new, val = xval; \
\
ATOMIC_TRACE_RMW; \
smp_mb(); \
ldn = atomic_read__nocheck(haddr); \
do { \
ldo = ldn; old = BSWAP(ldo); new = FN(old, val); \
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
} while (ldo != ldn); \
ATOMIC_MMU_CLEANUP; \
return RET; \
}
GEN_ATOMIC_HELPER_FN(fetch_smin, MIN, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umin, MIN, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_smax, MAX, SDATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(fetch_umax, MAX, DATA_TYPE, old)
GEN_ATOMIC_HELPER_FN(smin_fetch, MIN, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umin_fetch, MIN, DATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
/* Note that for addition, we need to use a separate cmpxchg loop instead /* Note that for addition, we need to use a separate cmpxchg loop instead
of bswaps for the reverse-host-endian helpers. */ of bswaps for the reverse-host-endian helpers. */
ABI_TYPE ATOMIC_NAME(fetch_add)(CPUArchState *env, target_ulong addr, #define ADD(X, Y) (X + Y)
ABI_TYPE val EXTRA_ARGS) GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
{ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
ATOMIC_MMU_DECLS; #undef ADD
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ldo, ldn, ret, sto;
ldo = atomic_read__nocheck(haddr); #undef GEN_ATOMIC_HELPER_FN
while (1) {
ret = BSWAP(ldo);
sto = BSWAP(ret + val);
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
if (ldn == ldo) {
ATOMIC_MMU_CLEANUP;
return ret;
}
ldo = ldn;
}
}
ABI_TYPE ATOMIC_NAME(add_fetch)(CPUArchState *env, target_ulong addr,
ABI_TYPE val EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ldo, ldn, ret, sto;
ldo = atomic_read__nocheck(haddr);
while (1) {
ret = BSWAP(ldo) + val;
sto = BSWAP(ret);
ldn = atomic_cmpxchg__nocheck(haddr, ldo, sto);
if (ldn == ldo) {
ATOMIC_MMU_CLEANUP;
return ret;
}
ldo = ldn;
}
}
#endif /* DATA_SIZE >= 16 */ #endif /* DATA_SIZE >= 16 */
#undef END #undef END
#undef MEND
#endif /* DATA_SIZE > 1 */ #endif /* DATA_SIZE > 1 */
#undef ATOMIC_TRACE_ST
#undef ATOMIC_TRACE_LD
#undef ATOMIC_TRACE_RMW
#undef BSWAP #undef BSWAP
#undef ABI_TYPE #undef ABI_TYPE
#undef DATA_TYPE #undef DATA_TYPE
#undef SDATA_TYPE
#undef SUFFIX #undef SUFFIX
#undef DATA_SIZE #undef DATA_SIZE
#undef SHIFT

View File

@ -25,7 +25,6 @@
#include "qemu/atomic.h" #include "qemu/atomic.h"
#include "sysemu/qtest.h" #include "sysemu/qtest.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "exec/address-spaces.h"
#include "qemu/rcu.h" #include "qemu/rcu.h"
#include "exec/tb-hash.h" #include "exec/tb-hash.h"
#include "exec/tb-lookup.h" #include "exec/tb-lookup.h"
@ -156,11 +155,14 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
if (qemu_loglevel_mask(CPU_LOG_TB_CPU) if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
&& qemu_log_in_addr_range(itb->pc)) { && qemu_log_in_addr_range(itb->pc)) {
qemu_log_lock(); qemu_log_lock();
int flags = 0;
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
flags |= CPU_DUMP_FPU;
}
#if defined(TARGET_I386) #if defined(TARGET_I386)
log_cpu_state(cpu, CPU_DUMP_CCOP); flags |= CPU_DUMP_CCOP;
#else
log_cpu_state(cpu, 0);
#endif #endif
log_cpu_state(cpu, flags);
qemu_log_unlock(); qemu_log_unlock();
} }
#endif /* DEBUG_DISAS */ #endif /* DEBUG_DISAS */
@ -210,20 +212,20 @@ static void cpu_exec_nocache(CPUState *cpu, int max_cycles,
We only end up here when an existing TB is too long. */ We only end up here when an existing TB is too long. */
cflags |= MIN(max_cycles, CF_COUNT_MASK); cflags |= MIN(max_cycles, CF_COUNT_MASK);
tb_lock(); mmap_lock();
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base, tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
orig_tb->flags, cflags); orig_tb->flags, cflags);
tb->orig_tb = orig_tb; tb->orig_tb = orig_tb;
tb_unlock(); mmap_unlock();
/* execute the generated code */ /* execute the generated code */
trace_exec_tb_nocache(tb, tb->pc); trace_exec_tb_nocache(tb, tb->pc);
cpu_tb_exec(cpu, tb); cpu_tb_exec(cpu, tb);
tb_lock(); mmap_lock();
tb_phys_invalidate(tb, -1); tb_phys_invalidate(tb, -1);
tb_remove(tb); mmap_unlock();
tb_unlock(); tcg_tb_remove(tb);
} }
#endif #endif
@ -242,12 +244,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) { if (tb == NULL) {
mmap_lock(); mmap_lock();
tb_lock(); tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
if (likely(tb == NULL)) {
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
}
tb_unlock();
mmap_unlock(); mmap_unlock();
} }
@ -262,15 +259,14 @@ void cpu_exec_step_atomic(CPUState *cpu)
cpu_tb_exec(cpu, tb); cpu_tb_exec(cpu, tb);
cc->cpu_exec_exit(cpu); cc->cpu_exec_exit(cpu);
} else { } else {
/* We may have exited due to another problem here, so we need /*
* to reset any tb_locks we may have taken but didn't release.
* The mmap_lock is dropped by tb_gen_code if it runs out of * The mmap_lock is dropped by tb_gen_code if it runs out of
* memory. * memory.
*/ */
#ifndef CONFIG_SOFTMMU #ifndef CONFIG_SOFTMMU
tcg_debug_assert(!have_mmap_lock()); tcg_debug_assert(!have_mmap_lock());
#endif #endif
tb_lock_reset(); assert_no_pages_locked();
} }
if (in_exclusive_region) { if (in_exclusive_region) {
@ -293,7 +289,7 @@ struct tb_desc {
uint32_t trace_vcpu_dstate; uint32_t trace_vcpu_dstate;
}; };
static bool tb_cmp(const void *p, const void *d) static bool tb_lookup_cmp(const void *p, const void *d)
{ {
const TranslationBlock *tb = p; const TranslationBlock *tb = p;
const struct tb_desc *desc = d; const struct tb_desc *desc = d;
@ -338,7 +334,7 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc,
phys_pc = get_page_addr_code(desc.env, pc); phys_pc = get_page_addr_code(desc.env, pc);
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate);
return qht_lookup(&tb_ctx.htable, tb_cmp, &desc, h); return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp);
} }
void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr) void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
@ -352,28 +348,43 @@ void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr)
} }
} }
/* Called with tb_lock held. */
static inline void tb_add_jump(TranslationBlock *tb, int n, static inline void tb_add_jump(TranslationBlock *tb, int n,
TranslationBlock *tb_next) TranslationBlock *tb_next)
{ {
uintptr_t old;
assert(n < ARRAY_SIZE(tb->jmp_list_next)); assert(n < ARRAY_SIZE(tb->jmp_list_next));
if (tb->jmp_list_next[n]) { qemu_spin_lock(&tb_next->jmp_lock);
/* Another thread has already done this while we were
* outside of the lock; nothing to do in this case */ /* make sure the destination TB is valid */
return; if (tb_next->cflags & CF_INVALID) {
goto out_unlock_next;
} }
/* Atomically claim the jump destination slot only if it was NULL */
old = atomic_cmpxchg(&tb->jmp_dest[n], (uintptr_t)NULL, (uintptr_t)tb_next);
if (old) {
goto out_unlock_next;
}
/* patch the native jump address */
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
/* add in TB jmp list */
tb->jmp_list_next[n] = tb_next->jmp_list_head;
tb_next->jmp_list_head = (uintptr_t)tb | n;
qemu_spin_unlock(&tb_next->jmp_lock);
qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc, qemu_log_mask_and_addr(CPU_LOG_EXEC, tb->pc,
"Linking TBs %p [" TARGET_FMT_lx "Linking TBs %p [" TARGET_FMT_lx
"] index %d -> %p [" TARGET_FMT_lx "]\n", "] index %d -> %p [" TARGET_FMT_lx "]\n",
tb->tc.ptr, tb->pc, n, tb->tc.ptr, tb->pc, n,
tb_next->tc.ptr, tb_next->pc); tb_next->tc.ptr, tb_next->pc);
return;
/* patch the native jump address */ out_unlock_next:
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr); qemu_spin_unlock(&tb_next->jmp_lock);
return;
/* add in TB jmp circular list */
tb->jmp_list_next[n] = tb_next->jmp_list_first;
tb_next->jmp_list_first = (uintptr_t)tb | n;
} }
static inline TranslationBlock *tb_find(CPUState *cpu, static inline TranslationBlock *tb_find(CPUState *cpu,
@ -383,27 +394,11 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
TranslationBlock *tb; TranslationBlock *tb;
target_ulong cs_base, pc; target_ulong cs_base, pc;
uint32_t flags; uint32_t flags;
bool acquired_tb_lock = false;
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask); tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) { if (tb == NULL) {
/* mmap_lock is needed by tb_gen_code, and mmap_lock must be
* taken outside tb_lock. As system emulation is currently
* single threaded the locks are NOPs.
*/
mmap_lock(); mmap_lock();
tb_lock(); tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
acquired_tb_lock = true;
/* There's a chance that our desired tb has been translated while
* taking the locks so we check again inside the lock.
*/
tb = tb_htable_lookup(cpu, pc, cs_base, flags, cf_mask);
if (likely(tb == NULL)) {
/* if no translated code available, then translate it now */
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
}
mmap_unlock(); mmap_unlock();
/* We add the TB in the virtual pc hash table for the fast lookup */ /* We add the TB in the virtual pc hash table for the fast lookup */
atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb); atomic_set(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)], tb);
@ -419,16 +414,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
#endif #endif
/* See if we can patch the calling TB. */ /* See if we can patch the calling TB. */
if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
if (!acquired_tb_lock) { tb_add_jump(last_tb, tb_exit, tb);
tb_lock();
acquired_tb_lock = true;
}
if (!(tb->cflags & CF_INVALID)) {
tb_add_jump(last_tb, tb_exit, tb);
}
}
if (acquired_tb_lock) {
tb_unlock();
} }
return tb; return tb;
} }
@ -704,7 +690,9 @@ int cpu_exec(CPUState *cpu)
g_assert(cpu == current_cpu); g_assert(cpu == current_cpu);
g_assert(cc == CPU_GET_CLASS(cpu)); g_assert(cc == CPU_GET_CLASS(cpu));
#endif /* buggy compiler */ #endif /* buggy compiler */
tb_lock_reset(); #ifndef CONFIG_SOFTMMU
tcg_debug_assert(!have_mmap_lock());
#endif
if (qemu_mutex_iothread_locked()) { if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread(); qemu_mutex_unlock_iothread();
} }

View File

@ -125,8 +125,6 @@ static void tlb_flush_nocheck(CPUState *cpu)
atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1); atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1);
tlb_debug("(count: %zu)\n", tlb_flush_count()); tlb_debug("(count: %zu)\n", tlb_flush_count());
tb_lock();
memset(env->tlb_table, -1, sizeof(env->tlb_table)); memset(env->tlb_table, -1, sizeof(env->tlb_table));
memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table));
cpu_tb_jmp_cache_clear(cpu); cpu_tb_jmp_cache_clear(cpu);
@ -135,8 +133,6 @@ static void tlb_flush_nocheck(CPUState *cpu)
env->tlb_flush_addr = -1; env->tlb_flush_addr = -1;
env->tlb_flush_mask = 0; env->tlb_flush_mask = 0;
tb_unlock();
atomic_mb_set(&cpu->pending_tlb_flush, 0); atomic_mb_set(&cpu->pending_tlb_flush, 0);
} }
@ -180,8 +176,6 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
assert_cpu_is_self(cpu); assert_cpu_is_self(cpu);
tb_lock();
tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask); tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
@ -197,8 +191,6 @@ static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data)
cpu_tb_jmp_cache_clear(cpu); cpu_tb_jmp_cache_clear(cpu);
tlb_debug("done\n"); tlb_debug("done\n");
tb_unlock();
} }
void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap)
@ -243,20 +235,30 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu,
async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap));
} }
static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry,
target_ulong page)
static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
{ {
if (addr == (tlb_entry->addr_read & return tlb_hit_page(tlb_entry->addr_read, page) ||
(TARGET_PAGE_MASK | TLB_INVALID_MASK)) || tlb_hit_page(tlb_entry->addr_write, page) ||
addr == (tlb_entry->addr_write & tlb_hit_page(tlb_entry->addr_code, page);
(TARGET_PAGE_MASK | TLB_INVALID_MASK)) || }
addr == (tlb_entry->addr_code &
(TARGET_PAGE_MASK | TLB_INVALID_MASK))) { 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)); memset(tlb_entry, -1, sizeof(*tlb_entry));
} }
} }
static inline void tlb_flush_vtlb_page(CPUArchState *env, int mmu_idx,
target_ulong page)
{
int k;
for (k = 0; k < CPU_VTLB_SIZE; k++) {
tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], page);
}
}
static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data)
{ {
CPUArchState *env = cpu->env_ptr; CPUArchState *env = cpu->env_ptr;
@ -282,14 +284,7 @@ static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data)
i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);
} tlb_flush_vtlb_page(env, mmu_idx, addr);
/* check whether there are entries that need to be flushed in the vtlb */
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
int k;
for (k = 0; k < CPU_VTLB_SIZE; k++) {
tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr);
}
} }
tb_flush_jmp_cache(cpu, addr); tb_flush_jmp_cache(cpu, addr);
@ -321,7 +316,6 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu,
unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS;
int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
int mmu_idx; int mmu_idx;
int i;
assert_cpu_is_self(cpu); assert_cpu_is_self(cpu);
@ -331,11 +325,7 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu,
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
if (test_bit(mmu_idx, &mmu_idx_bitmap)) { if (test_bit(mmu_idx, &mmu_idx_bitmap)) {
tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr); tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr);
tlb_flush_vtlb_page(env, mmu_idx, addr);
/* check whether there are vltb entries that need to be flushed */
for (i = 0; i < CPU_VTLB_SIZE; i++) {
tlb_flush_entry(&env->tlb_v_table[mmu_idx][i], addr);
}
} }
} }
@ -620,27 +610,42 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
target_ulong address; target_ulong address;
target_ulong code_address; target_ulong code_address;
uintptr_t addend; uintptr_t addend;
CPUTLBEntry *te, *tv, tn; CPUTLBEntry *te, tn;
hwaddr iotlb, xlat, sz; hwaddr iotlb, xlat, sz, paddr_page;
unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; target_ulong vaddr_page;
int asidx = cpu_asidx_from_attrs(cpu, attrs); int asidx = cpu_asidx_from_attrs(cpu, attrs);
assert_cpu_is_self(cpu); assert_cpu_is_self(cpu);
assert(size >= TARGET_PAGE_SIZE);
if (size != TARGET_PAGE_SIZE) {
tlb_add_large_page(env, vaddr, size);
}
sz = size; if (size < TARGET_PAGE_SIZE) {
section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz); sz = TARGET_PAGE_SIZE;
} else {
if (size > TARGET_PAGE_SIZE) {
tlb_add_large_page(env, vaddr, size);
}
sz = size;
}
vaddr_page = vaddr & TARGET_PAGE_MASK;
paddr_page = paddr & TARGET_PAGE_MASK;
section = address_space_translate_for_iotlb(cpu, asidx, paddr_page,
&xlat, &sz, attrs, &prot);
assert(sz >= TARGET_PAGE_SIZE); assert(sz >= TARGET_PAGE_SIZE);
tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx
" prot=%x idx=%d\n", " prot=%x idx=%d\n",
vaddr, paddr, prot, mmu_idx); vaddr, paddr, prot, mmu_idx);
address = vaddr; address = vaddr_page;
if (!memory_region_is_ram(section->mr) && !memory_region_is_romd(section->mr)) { if (size < TARGET_PAGE_SIZE) {
/*
* Slow-path the TLB entries; we will repeat the MMU check and TLB
* fill on every access.
*/
address |= TLB_RECHECK;
}
if (!memory_region_is_ram(section->mr) &&
!memory_region_is_romd(section->mr)) {
/* IO memory case */ /* IO memory case */
address |= TLB_MMIO; address |= TLB_MMIO;
addend = 0; addend = 0;
@ -649,26 +654,47 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat; 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; code_address = address;
iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat, iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page,
prot, &address); paddr_page, xlat, prot, &address);
index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
te = &env->tlb_table[mmu_idx][index]; te = &env->tlb_table[mmu_idx][index];
/* do not discard the translation in te, evict it into a victim tlb */
tv = &env->tlb_v_table[mmu_idx][vidx];
/* addr_write can race with tlb_reset_dirty_range */ /*
copy_tlb_helper(tv, te, true); * 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->vtlb_index++ % CPU_VTLB_SIZE;
CPUTLBEntry *tv = &env->tlb_v_table[mmu_idx][vidx];
env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; /* Evict the old entry into the victim tlb. */
copy_tlb_helper(tv, te, true);
env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index];
}
/* refill the tlb */ /* refill the tlb */
env->iotlb[mmu_idx][index].addr = iotlb - vaddr; /*
* At this point iotlb contains a physical section number in the lower
* TARGET_PAGE_BITS, and either
* + the ram_addr_t of the page base of the target RAM (if NOTDIRTY or ROM)
* + the offset within section->mr of the page base (otherwise)
* We subtract the vaddr_page (which is page aligned and thus won't
* disturb the low bits) to give an offset which can be added to the
* (non-page-aligned) vaddr of the eventual memory access to get
* the MemoryRegion offset for the access. Note that the vaddr we
* subtract here is that of the page base, and not the same as the
* vaddr we add back in io_readx()/io_writex()/get_page_addr_code().
*/
env->iotlb[mmu_idx][index].addr = iotlb - vaddr_page;
env->iotlb[mmu_idx][index].attrs = attrs; env->iotlb[mmu_idx][index].attrs = attrs;
/* Now calculate the new entry */ /* Now calculate the new entry */
tn.addend = addend - vaddr; tn.addend = addend - vaddr_page;
if (prot & PAGE_READ) { if (prot & PAGE_READ) {
tn.addr_read = address; tn.addr_read = address;
} else { } else {
@ -689,7 +715,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
tn.addr_write = address | TLB_MMIO; tn.addr_write = address | TLB_MMIO;
} else if (memory_region_is_ram(section->mr) } else if (memory_region_is_ram(section->mr)
&& cpu_physical_memory_is_clean( && cpu_physical_memory_is_clean(
memory_region_get_ram_addr(section->mr) + xlat)) { memory_region_get_ram_addr(section->mr) + xlat)) {
tn.addr_write = address | TLB_NOTDIRTY; tn.addr_write = address | TLB_NOTDIRTY;
} else { } else {
tn.addr_write = address; tn.addr_write = address;
@ -762,16 +788,43 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr)
static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
int mmu_idx, int mmu_idx,
target_ulong addr, uintptr_t retaddr, int size) target_ulong addr, uintptr_t retaddr,
bool recheck, int size)
{ {
CPUState *cpu = ENV_GET_CPU(env); CPUState *cpu = ENV_GET_CPU(env);
hwaddr physaddr = iotlbentry->addr; hwaddr mr_offset;
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); MemoryRegionSection *section;
MemoryRegion *mr;
uint64_t val; uint64_t val;
bool locked = false; bool locked = false;
MemTxResult r; MemTxResult r;
physaddr = (physaddr & TARGET_PAGE_MASK) + addr; if (recheck) {
/*
* 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, size, MMU_DATA_LOAD, mmu_idx, retaddr);
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 + env->tlb_table[mmu_idx][index].addend;
return ldn_p((void *)haddr, size);
}
/* Fall through for handling IO accesses */
}
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
mr = section->mr;
mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
cpu->mem_io_pc = retaddr; cpu->mem_io_pc = retaddr;
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
cpu_io_recompile(cpu, retaddr); cpu_io_recompile(cpu, retaddr);
@ -783,9 +836,13 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
qemu_mutex_lock_iothread(); qemu_mutex_lock_iothread();
locked = true; locked = true;
} }
r = memory_region_dispatch_read(mr, physaddr, r = memory_region_dispatch_read(mr, mr_offset,
&val, size, iotlbentry->attrs); &val, size, iotlbentry->attrs);
if (r != MEMTX_OK) { if (r != MEMTX_OK) {
hwaddr physaddr = mr_offset +
section->offset_within_address_space -
section->offset_within_region;
cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_LOAD, cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_LOAD,
mmu_idx, iotlbentry->attrs, r, retaddr); mmu_idx, iotlbentry->attrs, r, retaddr);
} }
@ -799,15 +856,42 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
int mmu_idx, int mmu_idx,
uint64_t val, target_ulong addr, uint64_t val, target_ulong addr,
uintptr_t retaddr, int size) uintptr_t retaddr, bool recheck, int size)
{ {
CPUState *cpu = ENV_GET_CPU(env); CPUState *cpu = ENV_GET_CPU(env);
hwaddr physaddr = iotlbentry->addr; hwaddr mr_offset;
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs); MemoryRegionSection *section;
MemoryRegion *mr;
bool locked = false; bool locked = false;
MemTxResult r; MemTxResult r;
physaddr = (physaddr & TARGET_PAGE_MASK) + addr; if (recheck) {
/*
* 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, size, MMU_DATA_STORE, mmu_idx, retaddr);
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 + env->tlb_table[mmu_idx][index].addend;
stn_p((void *)haddr, size, val);
return;
}
/* Fall through for handling IO accesses */
}
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
mr = section->mr;
mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr;
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) { if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
cpu_io_recompile(cpu, retaddr); cpu_io_recompile(cpu, retaddr);
} }
@ -818,9 +902,13 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
qemu_mutex_lock_iothread(); qemu_mutex_lock_iothread();
locked = true; locked = true;
} }
r = memory_region_dispatch_write(mr, physaddr, r = memory_region_dispatch_write(mr, mr_offset,
val, size, iotlbentry->attrs); val, size, iotlbentry->attrs);
if (r != MEMTX_OK) { if (r != MEMTX_OK) {
hwaddr physaddr = mr_offset +
section->offset_within_address_space -
section->offset_within_region;
cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_STORE, cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_STORE,
mmu_idx, iotlbentry->attrs, r, retaddr); mmu_idx, iotlbentry->attrs, r, retaddr);
} }
@ -868,24 +956,51 @@ 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) tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
{ {
int mmu_idx, index, pd; int mmu_idx, index;
void *p; void *p;
MemoryRegion *mr; MemoryRegion *mr;
MemoryRegionSection *section;
CPUState *cpu = ENV_GET_CPU(env); CPUState *cpu = ENV_GET_CPU(env);
CPUIOTLBEntry *iotlbentry; CPUIOTLBEntry *iotlbentry;
hwaddr physaddr; hwaddr physaddr, mr_offset;
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
mmu_idx = cpu_mmu_index(env, true); mmu_idx = cpu_mmu_index(env, true);
if (unlikely(env->tlb_table[mmu_idx][index].addr_code != if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) {
(addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)))) { if (!VICTIM_TLB_HIT(addr_code, addr)) {
if (!VICTIM_TLB_HIT(addr_read, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0);
} }
assert(tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr));
} }
if (unlikely(env->tlb_table[mmu_idx][index].addr_code & TLB_RECHECK)) {
/*
* 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)
*/
}
iotlbentry = &env->iotlb[mmu_idx][index]; iotlbentry = &env->iotlb[mmu_idx][index];
pd = iotlbentry->addr & ~TARGET_PAGE_MASK; section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
mr = iotlb_to_region(cpu, pd, iotlbentry->attrs); mr = section->mr;
if (memory_region_is_unassigned(mr)) { if (memory_region_is_unassigned(mr)) {
qemu_mutex_lock_iothread(); qemu_mutex_lock_iothread();
if (memory_region_request_mmio_ptr(mr, addr)) { if (memory_region_request_mmio_ptr(mr, addr)) {
@ -906,7 +1021,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr)
* and use the MemTXResult it produced). However it is the * and use the MemTXResult it produced). However it is the
* simplest place we have currently available for the check. * simplest place we have currently available for the check.
*/ */
physaddr = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; 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, cpu_transaction_failed(cpu, physaddr, addr, 0, MMU_INST_FETCH, mmu_idx,
iotlbentry->attrs, MEMTX_DECODE_ERROR, 0); iotlbentry->attrs, MEMTX_DECODE_ERROR, 0);
@ -934,8 +1052,7 @@ void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx,
int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
if ((addr & TARGET_PAGE_MASK) if (!tlb_hit(tlb_addr, addr)) {
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
/* TLB entry is for a different page */ /* TLB entry is for a different page */
if (!VICTIM_TLB_HIT(addr_write, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE, tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE,
@ -979,8 +1096,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
} }
/* Check TLB entry and enforce page permissions. */ /* Check TLB entry and enforce page permissions. */
if ((addr & TARGET_PAGE_MASK) if (!tlb_hit(tlb_addr, addr)) {
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
if (!VICTIM_TLB_HIT(addr_write, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE, tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE,
mmu_idx, retaddr); mmu_idx, retaddr);
@ -988,8 +1104,8 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK; tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK;
} }
/* Notice an IO access */ /* Notice an IO access or a needs-MMU-lookup access */
if (unlikely(tlb_addr & TLB_MMIO)) { if (unlikely(tlb_addr & (TLB_MMIO | TLB_RECHECK))) {
/* There's really nothing that can be done to /* There's really nothing that can be done to
support this apart from stop-the-world. */ support this apart from stop-the-world. */
goto stop_the_world; goto stop_the_world;

View File

@ -98,10 +98,12 @@
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
size_t mmu_idx, size_t index, size_t mmu_idx, size_t index,
target_ulong addr, target_ulong addr,
uintptr_t retaddr) uintptr_t retaddr,
bool recheck)
{ {
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, DATA_SIZE); return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck,
DATA_SIZE);
} }
#endif #endif
@ -121,8 +123,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
} }
/* If the TLB entry is for a different page, reload and try again. */ /* If the TLB entry is for a different page, reload and try again. */
if ((addr & TARGET_PAGE_MASK) if (!tlb_hit(tlb_addr, addr)) {
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
mmu_idx, retaddr); mmu_idx, retaddr);
@ -138,7 +139,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr,
/* ??? Note that the io helpers always read data in the target /* ??? Note that the io helpers always read data in the target
byte ordering. We should push the LE/BE request down into io. */ byte ordering. We should push the LE/BE request down into io. */
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
tlb_addr & TLB_RECHECK);
res = TGT_LE(res); res = TGT_LE(res);
return res; return res;
} }
@ -188,8 +190,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
} }
/* If the TLB entry is for a different page, reload and try again. */ /* If the TLB entry is for a different page, reload and try again. */
if ((addr & TARGET_PAGE_MASK) if (!tlb_hit(tlb_addr, addr)) {
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
mmu_idx, retaddr); mmu_idx, retaddr);
@ -205,7 +206,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr,
/* ??? Note that the io helpers always read data in the target /* ??? Note that the io helpers always read data in the target
byte ordering. We should push the LE/BE request down into io. */ byte ordering. We should push the LE/BE request down into io. */
res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr); res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr,
tlb_addr & TLB_RECHECK);
res = TGT_BE(res); res = TGT_BE(res);
return res; return res;
} }
@ -259,10 +261,12 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
size_t mmu_idx, size_t index, size_t mmu_idx, size_t index,
DATA_TYPE val, DATA_TYPE val,
target_ulong addr, target_ulong addr,
uintptr_t retaddr) uintptr_t retaddr,
bool recheck)
{ {
CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index];
return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr, DATA_SIZE); return io_writex(env, iotlbentry, mmu_idx, val, addr, retaddr,
recheck, DATA_SIZE);
} }
void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
@ -280,8 +284,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
} }
/* If the TLB entry is for a different page, reload and try again. */ /* If the TLB entry is for a different page, reload and try again. */
if ((addr & TARGET_PAGE_MASK) if (!tlb_hit(tlb_addr, addr)) {
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
if (!VICTIM_TLB_HIT(addr_write, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr); mmu_idx, retaddr);
@ -298,7 +301,8 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
/* ??? Note that the io helpers always read data in the target /* ??? Note that the io helpers always read data in the target
byte ordering. We should push the LE/BE request down into io. */ byte ordering. We should push the LE/BE request down into io. */
val = TGT_LE(val); val = TGT_LE(val);
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr,
retaddr, tlb_addr & TLB_RECHECK);
return; return;
} }
@ -315,7 +319,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) if (!tlb_hit_page(tlb_addr2, page2)
&& !VICTIM_TLB_HIT(addr_write, page2)) { && !VICTIM_TLB_HIT(addr_write, page2)) {
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr); mmu_idx, retaddr);
@ -357,8 +361,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
} }
/* If the TLB entry is for a different page, reload and try again. */ /* If the TLB entry is for a different page, reload and try again. */
if ((addr & TARGET_PAGE_MASK) if (!tlb_hit(tlb_addr, addr)) {
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
if (!VICTIM_TLB_HIT(addr_write, addr)) { if (!VICTIM_TLB_HIT(addr_write, addr)) {
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr); mmu_idx, retaddr);
@ -375,7 +378,8 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
/* ??? Note that the io helpers always read data in the target /* ??? Note that the io helpers always read data in the target
byte ordering. We should push the LE/BE request down into io. */ byte ordering. We should push the LE/BE request down into io. */
val = TGT_BE(val); val = TGT_BE(val);
glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr); glue(io_write, SUFFIX)(env, mmu_idx, index, val, addr, retaddr,
tlb_addr & TLB_RECHECK);
return; return;
} }
@ -392,7 +396,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write;
if (page2 != (tlb_addr2 & (TARGET_PAGE_MASK | TLB_INVALID_MASK)) if (!tlb_hit_page(tlb_addr2, page2)
&& !VICTIM_TLB_HIT(addr_write, page2)) { && !VICTIM_TLB_HIT(addr_write, page2)) {
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
mmu_idx, retaddr); mmu_idx, retaddr);

View File

@ -125,11 +125,19 @@ GEN_ATOMIC_HELPERS(fetch_add)
GEN_ATOMIC_HELPERS(fetch_and) GEN_ATOMIC_HELPERS(fetch_and)
GEN_ATOMIC_HELPERS(fetch_or) GEN_ATOMIC_HELPERS(fetch_or)
GEN_ATOMIC_HELPERS(fetch_xor) GEN_ATOMIC_HELPERS(fetch_xor)
GEN_ATOMIC_HELPERS(fetch_smin)
GEN_ATOMIC_HELPERS(fetch_umin)
GEN_ATOMIC_HELPERS(fetch_smax)
GEN_ATOMIC_HELPERS(fetch_umax)
GEN_ATOMIC_HELPERS(add_fetch) GEN_ATOMIC_HELPERS(add_fetch)
GEN_ATOMIC_HELPERS(and_fetch) GEN_ATOMIC_HELPERS(and_fetch)
GEN_ATOMIC_HELPERS(or_fetch) GEN_ATOMIC_HELPERS(or_fetch)
GEN_ATOMIC_HELPERS(xor_fetch) GEN_ATOMIC_HELPERS(xor_fetch)
GEN_ATOMIC_HELPERS(smin_fetch)
GEN_ATOMIC_HELPERS(umin_fetch)
GEN_ATOMIC_HELPERS(smax_fetch)
GEN_ATOMIC_HELPERS(umax_fetch)
GEN_ATOMIC_HELPERS(xchg) GEN_ATOMIC_HELPERS(xchg)

File diff suppressed because it is too large Load Diff

View File

@ -23,10 +23,13 @@
/* translate-all.c */ /* translate-all.c */
void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); struct page_collection *page_collection_lock(tb_page_addr_t start,
tb_page_addr_t end);
void page_collection_unlock(struct page_collection *set);
void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len);
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end, void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
int is_cpu_write_access); int is_cpu_write_access);
void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_check_watchpoint(CPUState *cpu); void tb_check_watchpoint(CPUState *cpu);
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY

View File

@ -34,8 +34,6 @@ void translator_loop_temp_check(DisasContextBase *db)
void translator_loop(const TranslatorOps *ops, DisasContextBase *db, void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
CPUState *cpu, TranslationBlock *tb) CPUState *cpu, TranslationBlock *tb)
{ {
int max_insns;
/* Initialize DisasContext */ /* Initialize DisasContext */
db->tb = tb; db->tb = tb;
db->pc_first = tb->pc; db->pc_first = tb->pc;
@ -45,18 +43,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
db->singlestep_enabled = cpu->singlestep_enabled; db->singlestep_enabled = cpu->singlestep_enabled;
/* Instruction counting */ /* Instruction counting */
max_insns = tb_cflags(db->tb) & CF_COUNT_MASK; db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
if (max_insns == 0) { if (db->max_insns == 0) {
max_insns = CF_COUNT_MASK; db->max_insns = CF_COUNT_MASK;
} }
if (max_insns > TCG_MAX_INSNS) { if (db->max_insns > TCG_MAX_INSNS) {
max_insns = TCG_MAX_INSNS; db->max_insns = TCG_MAX_INSNS;
} }
if (db->singlestep_enabled || singlestep) { if (db->singlestep_enabled || singlestep) {
max_insns = 1; db->max_insns = 1;
} }
max_insns = ops->init_disas_context(db, cpu, max_insns); ops->init_disas_context(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
/* Reset the temp count so that we can identify leaks */ /* Reset the temp count so that we can identify leaks */
@ -95,7 +93,8 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
update db->pc_next and db->is_jmp to indicate what should be update db->pc_next and db->is_jmp to indicate what should be
done next -- either exiting this loop or locate the start of done next -- either exiting this loop or locate the start of
the next instruction. */ the next instruction. */
if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) { if (db->num_insns == db->max_insns
&& (tb_cflags(db->tb) & CF_LAST_IO)) {
/* Accept I/O on the last instruction. */ /* Accept I/O on the last instruction. */
gen_io_start(); gen_io_start();
ops->translate_insn(db, cpu); ops->translate_insn(db, cpu);
@ -111,7 +110,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
/* Stop translation if the output buffer is full, /* Stop translation if the output buffer is full,
or we have executed all of the allowed instructions. */ or we have executed all of the allowed instructions. */
if (tcg_op_buf_full() || db->num_insns >= max_insns) { if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
db->is_jmp = DISAS_TOO_MANY; db->is_jmp = DISAS_TOO_MANY;
break; break;
} }

View File

@ -2,6 +2,9 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "qom/cpu.h" #include "qom/cpu.h"
#include "sysemu/replay.h" #include "sysemu/replay.h"
#include "sysemu/sysemu.h"
bool enable_cpu_pm = false;
void cpu_resume(CPUState *cpu) void cpu_resume(CPUState *cpu)
{ {

View File

@ -29,6 +29,7 @@
#include "hw/pci/pci.h" #include "hw/pci/pci.h"
#include "hw/audio/soundhw.h" #include "hw/audio/soundhw.h"
#include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-misc.h"
#include "qapi/error.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/acpi/acpi.h" #include "hw/acpi/acpi.h"
@ -51,14 +52,14 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_ARM #define QEMU_ARCH QEMU_ARCH_ARM
#elif defined(TARGET_CRIS) #elif defined(TARGET_CRIS)
#define QEMU_ARCH QEMU_ARCH_CRIS #define QEMU_ARCH QEMU_ARCH_CRIS
#elif defined(TARGET_I386)
#define QEMU_ARCH QEMU_ARCH_I386
#elif defined(TARGET_HPPA) #elif defined(TARGET_HPPA)
#define QEMU_ARCH QEMU_ARCH_HPPA #define QEMU_ARCH QEMU_ARCH_HPPA
#elif defined(TARGET_M68K) #elif defined(TARGET_I386)
#define QEMU_ARCH QEMU_ARCH_M68K #define QEMU_ARCH QEMU_ARCH_I386
#elif defined(TARGET_LM32) #elif defined(TARGET_LM32)
#define QEMU_ARCH QEMU_ARCH_LM32 #define QEMU_ARCH QEMU_ARCH_LM32
#elif defined(TARGET_M68K)
#define QEMU_ARCH QEMU_ARCH_M68K
#elif defined(TARGET_MICROBLAZE) #elif defined(TARGET_MICROBLAZE)
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE #define QEMU_ARCH QEMU_ARCH_MICROBLAZE
#elif defined(TARGET_MIPS) #elif defined(TARGET_MIPS)
@ -79,12 +80,12 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_SH4 #define QEMU_ARCH QEMU_ARCH_SH4
#elif defined(TARGET_SPARC) #elif defined(TARGET_SPARC)
#define QEMU_ARCH QEMU_ARCH_SPARC #define QEMU_ARCH QEMU_ARCH_SPARC
#elif defined(TARGET_XTENSA)
#define QEMU_ARCH QEMU_ARCH_XTENSA
#elif defined(TARGET_UNICORE32)
#define QEMU_ARCH QEMU_ARCH_UNICORE32
#elif defined(TARGET_TRICORE) #elif defined(TARGET_TRICORE)
#define QEMU_ARCH QEMU_ARCH_TRICORE #define QEMU_ARCH QEMU_ARCH_TRICORE
#elif defined(TARGET_UNICORE32)
#define QEMU_ARCH QEMU_ARCH_UNICORE32
#elif defined(TARGET_XTENSA)
#define QEMU_ARCH QEMU_ARCH_XTENSA
#endif #endif
const uint32_t arch_type = QEMU_ARCH; const uint32_t arch_type = QEMU_ARCH;
@ -112,7 +113,8 @@ TargetInfo *qmp_query_target(Error **errp)
{ {
TargetInfo *info = g_malloc0(sizeof(*info)); TargetInfo *info = g_malloc0(sizeof(*info));
info->arch = g_strdup(TARGET_NAME); info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
&error_abort);
return info; return info;
} }

View File

@ -29,6 +29,7 @@
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "sysemu/replay.h" #include "sysemu/replay.h"
#include "trace.h"
#define AUDIO_CAP "audio" #define AUDIO_CAP "audio"
#include "audio_int.h" #include "audio_int.h"
@ -335,9 +336,8 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp)
char *strval; char *strval;
strval = getenv (key); strval = getenv (key);
if (strval) { if (strval && !qemu_strtoi(strval, NULL, 10, &val)) {
*defaultp = 0; *defaultp = 0;
val = atoi (strval);
return val; return val;
} }
else { else {
@ -1130,6 +1130,10 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
/* /*
* Timer * Timer
*/ */
static bool audio_timer_running;
static uint64_t audio_timer_last;
static int audio_is_timer_needed (void) static int audio_is_timer_needed (void)
{ {
HWVoiceIn *hwi = NULL; HWVoiceIn *hwi = NULL;
@ -1149,14 +1153,31 @@ static void audio_reset_timer (AudioState *s)
if (audio_is_timer_needed ()) { if (audio_is_timer_needed ()) {
timer_mod_anticipate_ns(s->ts, timer_mod_anticipate_ns(s->ts,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
} if (!audio_timer_running) {
else { audio_timer_running = true;
timer_del (s->ts); audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
trace_audio_timer_start(conf.period.ticks / SCALE_MS);
}
} else {
timer_del(s->ts);
if (audio_timer_running) {
audio_timer_running = false;
trace_audio_timer_stop();
}
} }
} }
static void audio_timer (void *opaque) static void audio_timer (void *opaque)
{ {
int64_t now, diff;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
diff = now - audio_timer_last;
if (diff > conf.period.ticks * 3 / 2) {
trace_audio_timer_delayed(diff / SCALE_MS);
}
audio_timer_last = now;
audio_run ("timer"); audio_run ("timer");
audio_reset_timer (opaque); audio_reset_timer (opaque);
} }

View File

@ -15,3 +15,8 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d"
# audio/ossaudio.c # audio/ossaudio.c
oss_version(int version) "OSS version = 0x%x" oss_version(int version) "OSS version = 0x%x"
oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d"
# audio/audio.c
audio_timer_start(int interval) "interval %d ms"
audio_timer_stop(void) ""
audio_timer_delayed(int interval) "interval %d ms"

View File

@ -26,6 +26,7 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/virtio/vhost-user.h"
#include "standard-headers/linux/virtio_crypto.h" #include "standard-headers/linux/virtio_crypto.h"
#include "sysemu/cryptodev-vhost.h" #include "sysemu/cryptodev-vhost.h"
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
@ -46,6 +47,7 @@
typedef struct CryptoDevBackendVhostUser { typedef struct CryptoDevBackendVhostUser {
CryptoDevBackend parent_obj; CryptoDevBackend parent_obj;
VhostUserState *vhost_user;
CharBackend chr; CharBackend chr;
char *chr_name; char *chr_name;
bool opened; bool opened;
@ -102,7 +104,7 @@ cryptodev_vhost_user_start(int queues,
continue; continue;
} }
options.opaque = &s->chr; options.opaque = s->vhost_user;
options.backend_type = VHOST_BACKEND_TYPE_USER; options.backend_type = VHOST_BACKEND_TYPE_USER;
options.cc = b->conf.peers.ccs[i]; options.cc = b->conf.peers.ccs[i];
s->vhost_crypto[i] = cryptodev_vhost_init(&options); s->vhost_crypto[i] = cryptodev_vhost_init(&options);
@ -155,7 +157,6 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
{ {
CryptoDevBackendVhostUser *s = opaque; CryptoDevBackendVhostUser *s = opaque;
CryptoDevBackend *b = CRYPTODEV_BACKEND(s); CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
Error *err = NULL;
int queues = b->conf.peers.queues; int queues = b->conf.peers.queues;
assert(queues < MAX_CRYPTO_QUEUE_NUM); assert(queues < MAX_CRYPTO_QUEUE_NUM);
@ -172,10 +173,6 @@ static void cryptodev_vhost_user_event(void *opaque, int event)
cryptodev_vhost_user_stop(queues, s); cryptodev_vhost_user_stop(queues, s);
break; break;
} }
if (err) {
error_report_err(err);
}
} }
static void cryptodev_vhost_user_init( static void cryptodev_vhost_user_init(
@ -185,6 +182,7 @@ static void cryptodev_vhost_user_init(
size_t i; size_t i;
Error *local_err = NULL; Error *local_err = NULL;
Chardev *chr; Chardev *chr;
VhostUserState *user;
CryptoDevBackendClient *cc; CryptoDevBackendClient *cc;
CryptoDevBackendVhostUser *s = CryptoDevBackendVhostUser *s =
CRYPTODEV_BACKEND_VHOST_USER(backend); CRYPTODEV_BACKEND_VHOST_USER(backend);
@ -215,6 +213,15 @@ static void cryptodev_vhost_user_init(
} }
} }
user = vhost_user_init();
if (!user) {
error_setg(errp, "Failed to init vhost_user");
return;
}
user->chr = &s->chr;
s->vhost_user = user;
qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, qemu_chr_fe_set_handlers(&s->chr, NULL, NULL,
cryptodev_vhost_user_event, NULL, s, NULL, true); cryptodev_vhost_user_event, NULL, s, NULL, true);
@ -299,6 +306,12 @@ static void cryptodev_vhost_user_cleanup(
backend->conf.peers.ccs[i] = NULL; backend->conf.peers.ccs[i] = NULL;
} }
} }
if (s->vhost_user) {
vhost_user_cleanup(s->vhost_user);
g_free(s->vhost_user);
s->vhost_user = NULL;
}
} }
static void cryptodev_vhost_user_set_chardev(Object *obj, static void cryptodev_vhost_user_set_chardev(Object *obj,

View File

@ -18,6 +18,7 @@
#include "qapi/visitor.h" #include "qapi/visitor.h"
#include "qemu/config-file.h" #include "qemu/config-file.h"
#include "qom/object_interfaces.h" #include "qom/object_interfaces.h"
#include "qemu/mmap-alloc.h"
#ifdef CONFIG_NUMA #ifdef CONFIG_NUMA
#include <numaif.h> #include <numaif.h>
@ -246,8 +247,7 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
return memory_region_size(&backend->mr) != 0; return memory_region_size(&backend->mr) != 0;
} }
MemoryRegion * MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend)
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
{ {
return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL; return host_memory_backend_mr_inited(backend) ? &backend->mr : NULL;
} }
@ -262,6 +262,23 @@ bool host_memory_backend_is_mapped(HostMemoryBackend *backend)
return backend->is_mapped; return backend->is_mapped;
} }
#ifdef __linux__
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
{
Object *obj = OBJECT(memdev);
char *path = object_property_get_str(obj, "mem-path", NULL);
size_t pagesize = qemu_mempath_getpagesize(path);
g_free(path);
return pagesize;
}
#else
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
{
return getpagesize();
}
#endif
static void static void
host_memory_backend_memory_complete(UserCreatable *uc, Error **errp) host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
{ {
@ -351,24 +368,6 @@ host_memory_backend_can_be_deleted(UserCreatable *uc)
} }
} }
static char *get_id(Object *o, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
return g_strdup(backend->id);
}
static void set_id(Object *o, const char *str, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
if (backend->id) {
error_setg(errp, "cannot change property value");
return;
}
backend->id = g_strdup(str);
}
static bool host_memory_backend_get_share(Object *o, Error **errp) static bool host_memory_backend_get_share(Object *o, Error **errp)
{ {
HostMemoryBackend *backend = MEMORY_BACKEND(o); HostMemoryBackend *backend = MEMORY_BACKEND(o);
@ -416,18 +415,11 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
&HostMemPolicy_lookup, &HostMemPolicy_lookup,
host_memory_backend_get_policy, host_memory_backend_get_policy,
host_memory_backend_set_policy, &error_abort); host_memory_backend_set_policy, &error_abort);
object_class_property_add_str(oc, "id", get_id, set_id, &error_abort);
object_class_property_add_bool(oc, "share", object_class_property_add_bool(oc, "share",
host_memory_backend_get_share, host_memory_backend_set_share, host_memory_backend_get_share, host_memory_backend_set_share,
&error_abort); &error_abort);
} }
static void host_memory_backend_finalize(Object *o)
{
HostMemoryBackend *backend = MEMORY_BACKEND(o);
g_free(backend->id);
}
static const TypeInfo host_memory_backend_info = { static const TypeInfo host_memory_backend_info = {
.name = TYPE_MEMORY_BACKEND, .name = TYPE_MEMORY_BACKEND,
.parent = TYPE_OBJECT, .parent = TYPE_OBJECT,
@ -436,7 +428,6 @@ static const TypeInfo host_memory_backend_info = {
.class_init = host_memory_backend_class_init, .class_init = host_memory_backend_class_init,
.instance_size = sizeof(HostMemoryBackend), .instance_size = sizeof(HostMemoryBackend),
.instance_init = host_memory_backend_init, .instance_init = host_memory_backend_init,
.instance_finalize = host_memory_backend_finalize,
.interfaces = (InterfaceInfo[]) { .interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE }, { TYPE_USER_CREATABLE },
{ } { }

286
block.c
View File

@ -27,6 +27,7 @@
#include "block/block_int.h" #include "block/block_int.h"
#include "block/blockjob.h" #include "block/blockjob.h"
#include "block/nbd.h" #include "block/nbd.h"
#include "block/qdict.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "module_block.h" #include "module_block.h"
#include "qemu/module.h" #include "qemu/module.h"
@ -332,6 +333,10 @@ BlockDriverState *bdrv_new(void)
qemu_co_queue_init(&bs->flush_queue); qemu_co_queue_init(&bs->flush_queue);
for (i = 0; i < bdrv_drain_all_count; i++) {
bdrv_drained_begin(bs);
}
QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list);
return bs; return bs;
@ -720,7 +725,7 @@ static int find_image_format(BlockBackend *file, const char *filename,
* Set the current 'total_sectors' value * Set the current 'total_sectors' value
* Return 0 on success, -errno on error. * Return 0 on success, -errno on error.
*/ */
static int refresh_total_sectors(BlockDriverState *bs, int64_t hint) int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
{ {
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
@ -817,7 +822,13 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c)
static void bdrv_child_cb_drained_begin(BdrvChild *child) static void bdrv_child_cb_drained_begin(BdrvChild *child)
{ {
BlockDriverState *bs = child->opaque; BlockDriverState *bs = child->opaque;
bdrv_drained_begin(bs); bdrv_do_drained_begin_quiesce(bs, NULL, false);
}
static bool bdrv_child_cb_drained_poll(BdrvChild *child)
{
BlockDriverState *bs = child->opaque;
return bdrv_drain_poll(bs, false, NULL, false);
} }
static void bdrv_child_cb_drained_end(BdrvChild *child) static void bdrv_child_cb_drained_end(BdrvChild *child)
@ -901,9 +912,11 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options,
} }
const BdrvChildRole child_file = { const BdrvChildRole child_file = {
.parent_is_bds = true,
.get_parent_desc = bdrv_child_get_parent_desc, .get_parent_desc = bdrv_child_get_parent_desc,
.inherit_options = bdrv_inherited_options, .inherit_options = bdrv_inherited_options,
.drained_begin = bdrv_child_cb_drained_begin, .drained_begin = bdrv_child_cb_drained_begin,
.drained_poll = bdrv_child_cb_drained_poll,
.drained_end = bdrv_child_cb_drained_end, .drained_end = bdrv_child_cb_drained_end,
.attach = bdrv_child_cb_attach, .attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach, .detach = bdrv_child_cb_detach,
@ -925,9 +938,11 @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options,
} }
const BdrvChildRole child_format = { const BdrvChildRole child_format = {
.parent_is_bds = true,
.get_parent_desc = bdrv_child_get_parent_desc, .get_parent_desc = bdrv_child_get_parent_desc,
.inherit_options = bdrv_inherited_fmt_options, .inherit_options = bdrv_inherited_fmt_options,
.drained_begin = bdrv_child_cb_drained_begin, .drained_begin = bdrv_child_cb_drained_begin,
.drained_poll = bdrv_child_cb_drained_poll,
.drained_end = bdrv_child_cb_drained_end, .drained_end = bdrv_child_cb_drained_end,
.attach = bdrv_child_cb_attach, .attach = bdrv_child_cb_attach,
.detach = bdrv_child_cb_detach, .detach = bdrv_child_cb_detach,
@ -1042,11 +1057,13 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base,
} }
const BdrvChildRole child_backing = { const BdrvChildRole child_backing = {
.parent_is_bds = true,
.get_parent_desc = bdrv_child_get_parent_desc, .get_parent_desc = bdrv_child_get_parent_desc,
.attach = bdrv_backing_attach, .attach = bdrv_backing_attach,
.detach = bdrv_backing_detach, .detach = bdrv_backing_detach,
.inherit_options = bdrv_backing_options, .inherit_options = bdrv_backing_options,
.drained_begin = bdrv_child_cb_drained_begin, .drained_begin = bdrv_child_cb_drained_begin,
.drained_poll = bdrv_child_cb_drained_poll,
.drained_end = bdrv_child_cb_drained_end, .drained_end = bdrv_child_cb_drained_end,
.inactivate = bdrv_child_cb_inactivate, .inactivate = bdrv_child_cb_inactivate,
.update_filename = bdrv_backing_update_filename, .update_filename = bdrv_backing_update_filename,
@ -1139,6 +1156,12 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
goto out; goto out;
} }
/* Make sure that the node name isn't truncated */
if (strlen(node_name) >= sizeof(bs->node_name)) {
error_setg(errp, "Node name too long");
goto out;
}
/* copy node name into the bs and insert it into the graph list */ /* copy node name into the bs and insert it into the graph list */
pstrcpy(bs->node_name, sizeof(bs->node_name), node_name); pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list); QTAILQ_INSERT_TAIL(&graph_bdrv_states, bs, node_list);
@ -1151,7 +1174,7 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
int open_flags, Error **errp) int open_flags, Error **errp)
{ {
Error *local_err = NULL; Error *local_err = NULL;
int ret; int i, ret;
bdrv_assign_node_name(bs, node_name, &local_err); bdrv_assign_node_name(bs, node_name, &local_err);
if (local_err) { if (local_err) {
@ -1199,6 +1222,12 @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
assert(bdrv_min_mem_align(bs) != 0); assert(bdrv_min_mem_align(bs) != 0);
assert(is_power_of_2(bs->bl.request_alignment)); assert(is_power_of_2(bs->bl.request_alignment));
for (i = 0; i < bs->quiesce_counter; i++) {
if (drv->bdrv_co_drain_begin) {
drv->bdrv_co_drain_begin(bs);
}
}
return 0; return 0;
open_failed: open_failed:
bs->drv = NULL; bs->drv = NULL;
@ -1227,9 +1256,9 @@ BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name,
ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp); ret = bdrv_open_driver(bs, drv, node_name, bs->options, flags, errp);
if (ret < 0) { if (ret < 0) {
QDECREF(bs->explicit_options); qobject_unref(bs->explicit_options);
bs->explicit_options = NULL; bs->explicit_options = NULL;
QDECREF(bs->options); qobject_unref(bs->options);
bs->options = NULL; bs->options = NULL;
bdrv_unref(bs); bdrv_unref(bs);
return NULL; return NULL;
@ -1460,7 +1489,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
options = qobject_to(QDict, options_obj); options = qobject_to(QDict, options_obj);
if (!options) { if (!options) {
qobject_decref(options_obj); qobject_unref(options_obj);
error_setg(errp, "Invalid JSON object given"); error_setg(errp, "Invalid JSON object given");
return NULL; return NULL;
} }
@ -1490,7 +1519,7 @@ static void parse_json_protocol(QDict *options, const char **pfilename,
/* Options given in the filename have lower priority than options /* Options given in the filename have lower priority than options
* specified directly */ * specified directly */
qdict_join(options, json_options, false); qdict_join(options, json_options, false);
QDECREF(json_options); qobject_unref(json_options);
*pfilename = NULL; *pfilename = NULL;
} }
@ -1620,13 +1649,24 @@ static int bdrv_reopen_get_flags(BlockReopenQueue *q, BlockDriverState *bs)
/* Returns whether the image file can be written to after the reopen queue @q /* Returns whether the image file can be written to after the reopen queue @q
* has been successfully applied, or right now if @q is NULL. */ * has been successfully applied, or right now if @q is NULL. */
static bool bdrv_is_writable(BlockDriverState *bs, BlockReopenQueue *q) static bool bdrv_is_writable_after_reopen(BlockDriverState *bs,
BlockReopenQueue *q)
{ {
int flags = bdrv_reopen_get_flags(q, bs); int flags = bdrv_reopen_get_flags(q, bs);
return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR; return (flags & (BDRV_O_RDWR | BDRV_O_INACTIVE)) == BDRV_O_RDWR;
} }
/*
* Return whether the BDS can be written to. This is not necessarily
* the same as !bdrv_is_read_only(bs), as inactivated images may not
* be written to but do not count as read-only images.
*/
bool bdrv_is_writable(BlockDriverState *bs)
{
return bdrv_is_writable_after_reopen(bs, NULL);
}
static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs, static void bdrv_child_perm(BlockDriverState *bs, BlockDriverState *child_bs,
BdrvChild *c, const BdrvChildRole *role, BdrvChild *c, const BdrvChildRole *role,
BlockReopenQueue *reopen_queue, BlockReopenQueue *reopen_queue,
@ -1664,7 +1704,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
/* Write permissions never work with read-only images */ /* Write permissions never work with read-only images */
if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) && if ((cumulative_perms & (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED)) &&
!bdrv_is_writable(bs, q)) !bdrv_is_writable_after_reopen(bs, q))
{ {
error_setg(errp, "Block node is read-only"); error_setg(errp, "Block node is read-only");
return -EPERM; return -EPERM;
@ -1914,12 +1954,6 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
return 0; return 0;
} }
#define DEFAULT_PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
| BLK_PERM_WRITE \
| BLK_PERM_WRITE_UNCHANGED \
| BLK_PERM_RESIZE)
#define DEFAULT_PERM_UNCHANGED (BLK_PERM_ALL & ~DEFAULT_PERM_PASSTHROUGH)
void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c, void bdrv_filter_default_perms(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role, const BdrvChildRole *role,
BlockReopenQueue *reopen_queue, BlockReopenQueue *reopen_queue,
@ -1956,7 +1990,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
&perm, &shared); &perm, &shared);
/* Format drivers may touch metadata even if the guest doesn't write */ /* Format drivers may touch metadata even if the guest doesn't write */
if (bdrv_is_writable(bs, reopen_queue)) { if (bdrv_is_writable_after_reopen(bs, reopen_queue)) {
perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE; perm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
} }
@ -2009,7 +2043,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
child->role->detach(child); child->role->detach(child);
} }
if (old_bs->quiesce_counter && child->role->drained_end) { if (old_bs->quiesce_counter && child->role->drained_end) {
for (i = 0; i < old_bs->quiesce_counter; i++) { int num = old_bs->quiesce_counter;
if (child->role->parent_is_bds) {
num -= bdrv_drain_all_count;
}
assert(num >= 0);
for (i = 0; i < num; i++) {
child->role->drained_end(child); child->role->drained_end(child);
} }
} }
@ -2021,8 +2060,13 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
if (new_bs) { if (new_bs) {
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent); QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
if (new_bs->quiesce_counter && child->role->drained_begin) { if (new_bs->quiesce_counter && child->role->drained_begin) {
for (i = 0; i < new_bs->quiesce_counter; i++) { int num = new_bs->quiesce_counter;
child->role->drained_begin(child); if (child->role->parent_is_bds) {
num -= bdrv_drain_all_count;
}
assert(num >= 0);
for (i = 0; i < num; i++) {
bdrv_parent_drained_begin_single(child, true);
} }
} }
@ -2182,16 +2226,6 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load)
} }
} }
static void bdrv_parent_cb_resize(BlockDriverState *bs)
{
BdrvChild *c;
QLIST_FOREACH(c, &bs->parents, next_parent) {
if (c->role->resize) {
c->role->resize(c);
}
}
}
/* /*
* Sets the backing file link of a BDS. A new reference is created; callers * 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(). * which don't need their own reference any more must call bdrv_unref().
@ -2273,7 +2307,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
if (reference || qdict_haskey(options, "file.filename")) { if (reference || qdict_haskey(options, "file.filename")) {
backing_filename[0] = '\0'; backing_filename[0] = '\0';
} else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
QDECREF(options); qobject_unref(options);
goto free_exit; goto free_exit;
} else { } else {
bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX, bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX,
@ -2281,7 +2315,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
if (local_err) { if (local_err) {
ret = -EINVAL; ret = -EINVAL;
error_propagate(errp, local_err); error_propagate(errp, local_err);
QDECREF(options); qobject_unref(options);
goto free_exit; goto free_exit;
} }
} }
@ -2289,7 +2323,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
if (!bs->drv || !bs->drv->supports_backing) { if (!bs->drv || !bs->drv->supports_backing) {
ret = -EINVAL; ret = -EINVAL;
error_setg(errp, "Driver doesn't support backing files"); error_setg(errp, "Driver doesn't support backing files");
QDECREF(options); qobject_unref(options);
goto free_exit; goto free_exit;
} }
@ -2323,7 +2357,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
free_exit: free_exit:
g_free(backing_filename); g_free(backing_filename);
QDECREF(tmp_parent_options); qobject_unref(tmp_parent_options);
return ret; return ret;
} }
@ -2356,7 +2390,7 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key,
error_setg(errp, "A block device must be specified for \"%s\"", error_setg(errp, "A block device must be specified for \"%s\"",
bdref_key); bdref_key);
} }
QDECREF(image_options); qobject_unref(image_options);
goto done; goto done;
} }
@ -2449,7 +2483,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
obj = NULL; obj = NULL;
fail: fail:
qobject_decref(obj); qobject_unref(obj);
visit_free(v); visit_free(v);
return bs; return bs;
} }
@ -2519,7 +2553,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
} }
out: out:
QDECREF(snapshot_options); qobject_unref(snapshot_options);
g_free(tmp_filename); g_free(tmp_filename);
return bs_snapshot; return bs_snapshot;
} }
@ -2530,7 +2564,7 @@ out:
* options is a QDict of options to pass to the block drivers, or NULL for an * options is a QDict of options to pass to the block drivers, or NULL for an
* empty set of options. The reference to the QDict belongs to the block layer * empty set of options. The reference to the QDict belongs to the block layer
* after the call (even on failure), so if the caller intends to reuse the * after the call (even on failure), so if the caller intends to reuse the
* dictionary, it needs to use QINCREF() before calling bdrv_open. * dictionary, it needs to use qobject_ref() before calling bdrv_open.
* *
* If *pbs is NULL, a new BDS will be created with a pointer to it stored there. * If *pbs is NULL, a new BDS will be created with a pointer to it stored there.
* If it is not NULL, the referenced BDS will be reused. * If it is not NULL, the referenced BDS will be reused.
@ -2561,7 +2595,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
if (reference) { if (reference) {
bool options_non_empty = options ? qdict_size(options) : false; bool options_non_empty = options ? qdict_size(options) : false;
QDECREF(options); qobject_unref(options);
if (filename || options_non_empty) { if (filename || options_non_empty) {
error_setg(errp, "Cannot reference an existing block device with " error_setg(errp, "Cannot reference an existing block device with "
@ -2752,7 +2786,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
bdrv_parent_cb_change_media(bs, true); bdrv_parent_cb_change_media(bs, true);
QDECREF(options); qobject_unref(options);
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the /* For snapshot=on, create a temporary qcow2 overlay. bs points to the
* temporary snapshot afterwards. */ * temporary snapshot afterwards. */
@ -2776,10 +2810,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
fail: fail:
blk_unref(file); blk_unref(file);
QDECREF(snapshot_options); qobject_unref(snapshot_options);
QDECREF(bs->explicit_options); qobject_unref(bs->explicit_options);
QDECREF(bs->options); qobject_unref(bs->options);
QDECREF(options); qobject_unref(options);
bs->options = NULL; bs->options = NULL;
bs->explicit_options = NULL; bs->explicit_options = NULL;
bdrv_unref(bs); bdrv_unref(bs);
@ -2788,8 +2822,8 @@ fail:
close_and_fail: close_and_fail:
bdrv_unref(bs); bdrv_unref(bs);
QDECREF(snapshot_options); qobject_unref(snapshot_options);
QDECREF(options); qobject_unref(options);
error_propagate(errp, local_err); error_propagate(errp, local_err);
return NULL; return NULL;
} }
@ -2884,7 +2918,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
old_options = qdict_clone_shallow(bs->explicit_options); old_options = qdict_clone_shallow(bs->explicit_options);
} }
bdrv_join_options(bs, options, old_options); bdrv_join_options(bs, options, old_options);
QDECREF(old_options); qobject_unref(old_options);
explicit_options = qdict_clone_shallow(options); explicit_options = qdict_clone_shallow(options);
@ -2899,13 +2933,13 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
qemu_opts_absorb_qdict(opts, options_copy, NULL); qemu_opts_absorb_qdict(opts, options_copy, NULL);
update_flags_from_options(&flags, opts); update_flags_from_options(&flags, opts);
qemu_opts_del(opts); qemu_opts_del(opts);
QDECREF(options_copy); qobject_unref(options_copy);
} }
/* Old values are used for options that aren't set yet */ /* Old values are used for options that aren't set yet */
old_options = qdict_clone_shallow(bs->options); old_options = qdict_clone_shallow(bs->options);
bdrv_join_options(bs, options, old_options); bdrv_join_options(bs, options, old_options);
QDECREF(old_options); qobject_unref(old_options);
/* bdrv_open_inherit() sets and clears some additional flags internally */ /* bdrv_open_inherit() sets and clears some additional flags internally */
flags &= ~BDRV_O_PROTOCOL; flags &= ~BDRV_O_PROTOCOL;
@ -2917,8 +2951,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
bs_entry = g_new0(BlockReopenQueueEntry, 1); bs_entry = g_new0(BlockReopenQueueEntry, 1);
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry); QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
} else { } else {
QDECREF(bs_entry->state.options); qobject_unref(bs_entry->state.options);
QDECREF(bs_entry->state.explicit_options); qobject_unref(bs_entry->state.explicit_options);
} }
bs_entry->state.bs = bs; bs_entry->state.bs = bs;
@ -2968,7 +3002,7 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
* *
* Reopens all BDS specified in the queue, with the appropriate * Reopens all BDS specified in the queue, with the appropriate
* flags. All devices are prepared for reopen, and failure of any * flags. All devices are prepared for reopen, and failure of any
* device will cause all device changes to be abandonded, and intermediate * device will cause all device changes to be abandoned, and intermediate
* data cleaned up. * data cleaned up.
* *
* If all devices prepare successfully, then the changes are committed * If all devices prepare successfully, then the changes are committed
@ -3008,9 +3042,9 @@ cleanup:
if (ret && bs_entry->prepared) { if (ret && bs_entry->prepared) {
bdrv_reopen_abort(&bs_entry->state); bdrv_reopen_abort(&bs_entry->state);
} else if (ret) { } else if (ret) {
QDECREF(bs_entry->state.explicit_options); qobject_unref(bs_entry->state.explicit_options);
} }
QDECREF(bs_entry->state.options); qobject_unref(bs_entry->state.options);
g_free(bs_entry); g_free(bs_entry);
} }
g_free(bs_queue); g_free(bs_queue);
@ -3253,7 +3287,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
} }
/* set BDS specific flags now */ /* set BDS specific flags now */
QDECREF(bs->explicit_options); qobject_unref(bs->explicit_options);
bs->explicit_options = reopen_state->explicit_options; bs->explicit_options = reopen_state->explicit_options;
bs->open_flags = reopen_state->flags; bs->open_flags = reopen_state->flags;
@ -3296,7 +3330,7 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state)
drv->bdrv_reopen_abort(reopen_state); drv->bdrv_reopen_abort(reopen_state);
} }
QDECREF(reopen_state->explicit_options); qobject_unref(reopen_state->explicit_options);
bdrv_abort_perm_update(reopen_state->bs); bdrv_abort_perm_update(reopen_state->bs);
} }
@ -3343,11 +3377,11 @@ static void bdrv_close(BlockDriverState *bs)
bs->total_sectors = 0; bs->total_sectors = 0;
bs->encrypted = false; bs->encrypted = false;
bs->sg = false; bs->sg = false;
QDECREF(bs->options); qobject_unref(bs->options);
QDECREF(bs->explicit_options); qobject_unref(bs->explicit_options);
bs->options = NULL; bs->options = NULL;
bs->explicit_options = NULL; bs->explicit_options = NULL;
QDECREF(bs->full_open_options); qobject_unref(bs->full_open_options);
bs->full_open_options = NULL; bs->full_open_options = NULL;
bdrv_release_named_dirty_bitmaps(bs); bdrv_release_named_dirty_bitmaps(bs);
@ -3362,7 +3396,7 @@ static void bdrv_close(BlockDriverState *bs)
void bdrv_close_all(void) void bdrv_close_all(void)
{ {
block_job_cancel_sync_all(); assert(job_next(NULL) == NULL);
nbd_export_close_all(); nbd_export_close_all();
/* Drop references from requests still in flight, such as canceled block /* Drop references from requests still in flight, such as canceled block
@ -3383,16 +3417,39 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to)
return false; return false;
} }
if (c->role == &child_backing) { /* If the child @c belongs to the BDS @to, replacing the current
/* If @from is a backing file of @to, ignore the child to avoid * c->bs by @to would mean to create a loop.
* creating a loop. We only want to change the pointer of other *
* parents. */ * Such a case occurs when appending a BDS to a backing chain.
QLIST_FOREACH(to_c, &to->children, next) { * For instance, imagine the following chain:
if (to_c == c) { *
break; * guest device -> node A -> further backing chain...
} *
} * Now we create a new BDS B which we want to put on top of this
if (to_c) { * chain, so we first attach A as its backing node:
*
* node B
* |
* v
* guest device -> node A -> further backing chain...
*
* Finally we want to replace A by B. When doing that, we want to
* replace all pointers to A by pointers to B -- except for the
* pointer from B because (1) that would create a loop, and (2)
* that pointer should simply stay intact:
*
* guest device -> node B
* |
* v
* node A -> further backing chain...
*
* In general, when replacing a node A (c->bs) by a node B (@to),
* if A is a child of B, that means we cannot replace A by B there
* because that would create a loop. Silently detaching A from B
* is also not really an option. So overall just leaving A in
* place there is the most sensible choice. */
QLIST_FOREACH(to_c, &to->children, next) {
if (to_c == c) {
return false; return false;
} }
} }
@ -3418,6 +3475,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
/* Put all parents into @list and calculate their cumulative permissions */ /* Put all parents into @list and calculate their cumulative permissions */
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
assert(c->bs == from);
if (!should_update_child(c, to)) { if (!should_update_child(c, to)) {
continue; continue;
} }
@ -3717,58 +3775,6 @@ exit:
return ret; return ret;
} }
/**
* Truncate file to 'offset' bytes (needed only for file protocols)
*/
int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
Error **errp)
{
BlockDriverState *bs = child->bs;
BlockDriver *drv = bs->drv;
int ret;
assert(child->perm & BLK_PERM_RESIZE);
/* if bs->drv == NULL, bs is closed, so there's nothing to do here */
if (!drv) {
error_setg(errp, "No medium inserted");
return -ENOMEDIUM;
}
if (offset < 0) {
error_setg(errp, "Image size cannot be negative");
return -EINVAL;
}
if (!drv->bdrv_truncate) {
if (bs->file && drv->is_filter) {
return bdrv_truncate(bs->file, offset, prealloc, errp);
}
error_setg(errp, "Image format driver does not support resize");
return -ENOTSUP;
}
if (bs->read_only) {
error_setg(errp, "Image is read-only");
return -EACCES;
}
assert(!(bs->open_flags & BDRV_O_INACTIVE));
ret = drv->bdrv_truncate(bs, offset, prealloc, errp);
if (ret < 0) {
return ret;
}
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not refresh total sector count");
} else {
offset = bs->total_sectors * BDRV_SECTOR_SIZE;
}
bdrv_dirty_bitmap_truncate(bs, offset);
bdrv_parent_cb_resize(bs);
atomic_inc(&bs->write_gen);
return ret;
}
/** /**
* Length of a allocated file in bytes. Sparse files are counted by actual * Length of a allocated file in bytes. Sparse files are counted by actual
* allocated space. Return < 0 if error or unknown. * allocated space. Return < 0 if error or unknown.
@ -4025,6 +4031,14 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs)
return QTAILQ_NEXT(bs, node_list); return QTAILQ_NEXT(bs, node_list);
} }
BlockDriverState *bdrv_next_all_states(BlockDriverState *bs)
{
if (!bs) {
return QTAILQ_FIRST(&all_bdrv_states);
}
return QTAILQ_NEXT(bs, bs_list);
}
const char *bdrv_get_node_name(const BlockDriverState *bs) const char *bdrv_get_node_name(const BlockDriverState *bs)
{ {
return bs->node_name; return bs->node_name;
@ -4936,7 +4950,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
AioContext *ctx = bdrv_get_aio_context(bs); AioContext *ctx = bdrv_get_aio_context(bs);
aio_disable_external(ctx); aio_disable_external(ctx);
bdrv_parent_drained_begin(bs, NULL); bdrv_parent_drained_begin(bs, NULL, false);
bdrv_drain(bs); /* ensure there are no in-flight requests */ bdrv_drain(bs); /* ensure there are no in-flight requests */
while (aio_poll(ctx, false)) { while (aio_poll(ctx, false)) {
@ -4950,7 +4964,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
*/ */
aio_context_acquire(new_context); aio_context_acquire(new_context);
bdrv_attach_aio_context(bs, new_context); bdrv_attach_aio_context(bs, new_context);
bdrv_parent_drained_end(bs, NULL); bdrv_parent_drained_end(bs, NULL, false);
aio_enable_external(ctx); aio_enable_external(ctx);
aio_context_release(new_context); aio_context_release(new_context);
} }
@ -4996,15 +5010,19 @@ void bdrv_remove_aio_context_notifier(BlockDriverState *bs,
} }
int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts, int bdrv_amend_options(BlockDriverState *bs, QemuOpts *opts,
BlockDriverAmendStatusCB *status_cb, void *cb_opaque) BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
Error **errp)
{ {
if (!bs->drv) { if (!bs->drv) {
error_setg(errp, "Node is ejected");
return -ENOMEDIUM; return -ENOMEDIUM;
} }
if (!bs->drv->bdrv_amend_options) { if (!bs->drv->bdrv_amend_options) {
error_setg(errp, "Block driver '%s' does not support option amendment",
bs->drv->format_name);
return -ENOTSUP; return -ENOTSUP;
} }
return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque); return bs->drv->bdrv_amend_options(bs, opts, status_cb, cb_opaque, errp);
} }
/* This function will be called by the bdrv_recurse_is_first_non_filter method /* This function will be called by the bdrv_recurse_is_first_non_filter method
@ -5134,8 +5152,8 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
continue; continue;
} }
qobject_incref(qdict_entry_value(entry)); qdict_put_obj(d, qdict_entry_key(entry),
qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry)); qobject_ref(qdict_entry_value(entry)));
found_any = true; found_any = true;
} }
@ -5174,21 +5192,21 @@ void bdrv_refresh_filename(BlockDriverState *bs)
* information before refreshing it */ * information before refreshing it */
bs->exact_filename[0] = '\0'; bs->exact_filename[0] = '\0';
if (bs->full_open_options) { if (bs->full_open_options) {
QDECREF(bs->full_open_options); qobject_unref(bs->full_open_options);
bs->full_open_options = NULL; bs->full_open_options = NULL;
} }
opts = qdict_new(); opts = qdict_new();
append_open_options(opts, bs); append_open_options(opts, bs);
drv->bdrv_refresh_filename(bs, opts); drv->bdrv_refresh_filename(bs, opts);
QDECREF(opts); qobject_unref(opts);
} else if (bs->file) { } else if (bs->file) {
/* Try to reconstruct valid information from the underlying file */ /* Try to reconstruct valid information from the underlying file */
bool has_open_options; bool has_open_options;
bs->exact_filename[0] = '\0'; bs->exact_filename[0] = '\0';
if (bs->full_open_options) { if (bs->full_open_options) {
QDECREF(bs->full_open_options); qobject_unref(bs->full_open_options);
bs->full_open_options = NULL; bs->full_open_options = NULL;
} }
@ -5207,12 +5225,12 @@ void bdrv_refresh_filename(BlockDriverState *bs)
* suffices without querying the (exact_)filename of this BDS. */ * suffices without querying the (exact_)filename of this BDS. */
if (bs->file->bs->full_open_options) { if (bs->file->bs->full_open_options) {
qdict_put_str(opts, "driver", drv->format_name); qdict_put_str(opts, "driver", drv->format_name);
QINCREF(bs->file->bs->full_open_options); qdict_put(opts, "file",
qdict_put(opts, "file", bs->file->bs->full_open_options); qobject_ref(bs->file->bs->full_open_options));
bs->full_open_options = opts; bs->full_open_options = opts;
} else { } else {
QDECREF(opts); qobject_unref(opts);
} }
} else if (!bs->full_open_options && qdict_size(bs->options)) { } else if (!bs->full_open_options && qdict_size(bs->options)) {
/* There is no underlying file BDS (at least referenced by BDS.file), /* There is no underlying file BDS (at least referenced by BDS.file),
@ -5246,7 +5264,7 @@ void bdrv_refresh_filename(BlockDriverState *bs)
QString *json = qobject_to_json(QOBJECT(bs->full_open_options)); QString *json = qobject_to_json(QOBJECT(bs->full_open_options));
snprintf(bs->filename, sizeof(bs->filename), "json:%s", snprintf(bs->filename, sizeof(bs->filename), "json:%s",
qstring_get_str(json)); qstring_get_str(json));
QDECREF(json); qobject_unref(json);
} }
} }

View File

@ -1,10 +1,11 @@
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 += 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-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o
#block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.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 += qed-check.o
#block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
#block-obj-y += quorum.o block-obj-y += quorum.o
#block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.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-y += block-backend.o snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
block-obj-$(CONFIG_POSIX) += file-posix.o block-obj-$(CONFIG_POSIX) += file-posix.o
@ -13,20 +14,20 @@ block-obj-y += null.o mirror.o commit.o io.o create.o
block-obj-y += throttle-groups.o block-obj-y += throttle-groups.o
block-obj-$(CONFIG_LINUX) += nvme.o block-obj-$(CONFIG_LINUX) += nvme.o
#block-obj-y += nbd.o nbd-client.o sheepdog.o block-obj-y += nbd.o nbd-client.o sheepdog.o
#block-obj-$(CONFIG_LIBISCSI) += iscsi.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o
#block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o
#block-obj-$(CONFIG_LIBNFS) += nfs.o block-obj-$(CONFIG_LIBNFS) += nfs.o
#block-obj-$(CONFIG_CURL) += curl.o block-obj-$(CONFIG_CURL) += curl.o
#block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_RBD) += rbd.o
#block-obj-$(CONFIG_GLUSTERFS) += gluster.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o
#block-obj-$(CONFIG_VXHS) += vxhs.o block-obj-$(CONFIG_VXHS) += vxhs.o
#block-obj-$(CONFIG_LIBSSH2) += ssh.o block-obj-$(CONFIG_LIBSSH2) += ssh.o
block-obj-y += accounting.o dirty-bitmap.o block-obj-y += accounting.o dirty-bitmap.o
block-obj-y += write-threshold.o block-obj-y += write-threshold.o
block-obj-y += backup.o block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.o block-obj-y += throttle.o copy-on-read.o
block-obj-y += crypto.o block-obj-y += crypto.o

View File

@ -27,7 +27,6 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
#define SLICE_TIME 100000000ULL /* ns */
typedef struct BackupBlockJob { typedef struct BackupBlockJob {
BlockJob common; BlockJob common;
@ -35,10 +34,10 @@ typedef struct BackupBlockJob {
/* bitmap for sync=incremental */ /* bitmap for sync=incremental */
BdrvDirtyBitmap *sync_bitmap; BdrvDirtyBitmap *sync_bitmap;
MirrorSyncMode sync_mode; MirrorSyncMode sync_mode;
RateLimit limit;
BlockdevOnError on_source_error; BlockdevOnError on_source_error;
BlockdevOnError on_target_error; BlockdevOnError on_target_error;
CoRwlock flush_rwlock; CoRwlock flush_rwlock;
uint64_t len;
uint64_t bytes_read; uint64_t bytes_read;
int64_t cluster_size; int64_t cluster_size;
bool compress; bool compress;
@ -46,8 +45,14 @@ typedef struct BackupBlockJob {
QLIST_HEAD(, CowRequest) inflight_reqs; QLIST_HEAD(, CowRequest) inflight_reqs;
HBitmap *copy_bitmap; HBitmap *copy_bitmap;
bool use_copy_range;
int64_t copy_range_size;
bool serialize_target_writes;
} BackupBlockJob; } BackupBlockJob;
static const BlockJobDriver backup_job_driver;
/* See if in-flight requests overlap and wait for them to complete */ /* See if in-flight requests overlap and wait for them to complete */
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job, static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
int64_t start, int64_t start,
@ -85,19 +90,104 @@ static void cow_request_end(CowRequest *req)
qemu_co_queue_restart_all(&req->wait_queue); qemu_co_queue_restart_all(&req->wait_queue);
} }
/* Copy range to target with a bounce buffer and return the bytes copied. If
* error occurred, return a negative error number */
static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
int64_t start,
int64_t end,
bool is_write_notifier,
bool *error_is_read,
void **bounce_buffer)
{
int ret;
struct iovec iov;
QEMUIOVector qiov;
BlockBackend *blk = job->common.blk;
int nbytes;
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
nbytes = MIN(job->cluster_size, job->len - start);
if (!*bounce_buffer) {
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
}
iov.iov_base = *bounce_buffer;
iov.iov_len = nbytes;
qemu_iovec_init_external(&qiov, &iov, 1);
ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags);
if (ret < 0) {
trace_backup_do_cow_read_fail(job, start, ret);
if (error_is_read) {
*error_is_read = true;
}
goto fail;
}
if (qemu_iovec_is_zero(&qiov)) {
ret = blk_co_pwrite_zeroes(job->target, start,
qiov.size, write_flags | BDRV_REQ_MAY_UNMAP);
} else {
ret = blk_co_pwritev(job->target, start,
qiov.size, &qiov, write_flags |
(job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0));
}
if (ret < 0) {
trace_backup_do_cow_write_fail(job, start, ret);
if (error_is_read) {
*error_is_read = false;
}
goto fail;
}
return nbytes;
fail:
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
return ret;
}
/* Copy range to target and return the bytes copied. If error occurred, return a
* negative error number. */
static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
int64_t start,
int64_t end,
bool is_write_notifier)
{
int ret;
int nr_clusters;
BlockBackend *blk = job->common.blk;
int nbytes;
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
nbytes = MIN(job->copy_range_size, end - start);
nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
hbitmap_reset(job->copy_bitmap, start / job->cluster_size,
nr_clusters);
ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
read_flags, write_flags);
if (ret < 0) {
trace_backup_do_cow_copy_range_fail(job, start, ret);
hbitmap_set(job->copy_bitmap, start / job->cluster_size,
nr_clusters);
return ret;
}
return nbytes;
}
static int coroutine_fn backup_do_cow(BackupBlockJob *job, static int coroutine_fn backup_do_cow(BackupBlockJob *job,
int64_t offset, uint64_t bytes, int64_t offset, uint64_t bytes,
bool *error_is_read, bool *error_is_read,
bool is_write_notifier) bool is_write_notifier)
{ {
BlockBackend *blk = job->common.blk;
CowRequest cow_request; CowRequest cow_request;
struct iovec iov;
QEMUIOVector bounce_qiov;
void *bounce_buffer = NULL;
int ret = 0; int ret = 0;
int64_t start, end; /* bytes */ int64_t start, end; /* bytes */
int n; /* bytes */ void *bounce_buffer = NULL;
qemu_co_rwlock_rdlock(&job->flush_rwlock); qemu_co_rwlock_rdlock(&job->flush_rwlock);
@ -109,60 +199,38 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
wait_for_overlapping_requests(job, start, end); wait_for_overlapping_requests(job, start, end);
cow_request_begin(&cow_request, job, start, end); cow_request_begin(&cow_request, job, start, end);
for (; start < end; start += job->cluster_size) { while (start < end) {
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) { if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
trace_backup_do_cow_skip(job, start); trace_backup_do_cow_skip(job, start);
start += job->cluster_size;
continue; /* already copied */ continue; /* already copied */
} }
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
trace_backup_do_cow_process(job, start); trace_backup_do_cow_process(job, start);
n = MIN(job->cluster_size, job->common.len - start); if (job->use_copy_range) {
ret = backup_cow_with_offload(job, start, end, is_write_notifier);
if (!bounce_buffer) { if (ret < 0) {
bounce_buffer = blk_blockalign(blk, job->cluster_size); job->use_copy_range = false;
}
iov.iov_base = bounce_buffer;
iov.iov_len = n;
qemu_iovec_init_external(&bounce_qiov, &iov, 1);
ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
if (ret < 0) {
trace_backup_do_cow_read_fail(job, start, ret);
if (error_is_read) {
*error_is_read = true;
} }
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
goto out;
} }
if (!job->use_copy_range) {
if (buffer_is_zero(iov.iov_base, iov.iov_len)) { ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier,
ret = blk_co_pwrite_zeroes(job->target, start, error_is_read, &bounce_buffer);
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
} else {
ret = blk_co_pwritev(job->target, start,
bounce_qiov.size, &bounce_qiov,
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
} }
if (ret < 0) { if (ret < 0) {
trace_backup_do_cow_write_fail(job, start, ret); break;
if (error_is_read) {
*error_is_read = false;
}
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
goto out;
} }
/* Publish progress, guest I/O counts as progress too. Note that the /* Publish progress, guest I/O counts as progress too. Note that the
* offset field is an opaque progress value, it is not a disk offset. * offset field is an opaque progress value, it is not a disk offset.
*/ */
job->bytes_read += n; start += ret;
job->common.offset += n; job->bytes_read += ret;
job_progress_update(&job->common.job, ret);
ret = 0;
} }
out:
if (bounce_buffer) { if (bounce_buffer) {
qemu_vfree(bounce_buffer); qemu_vfree(bounce_buffer);
} }
@ -190,17 +258,6 @@ static int coroutine_fn backup_before_write_notify(
return backup_do_cow(job, req->offset, req->bytes, NULL, true); return backup_do_cow(job, req->offset, req->bytes, NULL, true);
} }
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
if (speed < 0) {
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return;
}
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
}
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret) static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
{ {
BdrvDirtyBitmap *bm; BdrvDirtyBitmap *bm;
@ -217,25 +274,25 @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
} }
} }
static void backup_commit(BlockJob *job) static void backup_commit(Job *job)
{ {
BackupBlockJob *s = container_of(job, BackupBlockJob, common); BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
if (s->sync_bitmap) { if (s->sync_bitmap) {
backup_cleanup_sync_bitmap(s, 0); backup_cleanup_sync_bitmap(s, 0);
} }
} }
static void backup_abort(BlockJob *job) static void backup_abort(Job *job)
{ {
BackupBlockJob *s = container_of(job, BackupBlockJob, common); BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
if (s->sync_bitmap) { if (s->sync_bitmap) {
backup_cleanup_sync_bitmap(s, -1); backup_cleanup_sync_bitmap(s, -1);
} }
} }
static void backup_clean(BlockJob *job) static void backup_clean(Job *job)
{ {
BackupBlockJob *s = container_of(job, BackupBlockJob, common); BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
assert(s->target); assert(s->target);
blk_unref(s->target); blk_unref(s->target);
s->target = NULL; s->target = NULL;
@ -253,7 +310,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t len; int64_t len;
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); assert(block_job_driver(job) == &backup_job_driver);
if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) { if (backup_job->sync_mode != MIRROR_SYNC_MODE_NONE) {
error_setg(errp, "The backup job only supports block checkpoint in" error_setg(errp, "The backup job only supports block checkpoint in"
@ -261,7 +318,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
return; return;
} }
len = DIV_ROUND_UP(backup_job->common.len, backup_job->cluster_size); len = DIV_ROUND_UP(backup_job->len, backup_job->cluster_size);
hbitmap_set(backup_job->copy_bitmap, 0, len); hbitmap_set(backup_job->copy_bitmap, 0, len);
} }
@ -271,7 +328,7 @@ void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t start, end; int64_t start, end;
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); assert(block_job_driver(job) == &backup_job_driver);
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
@ -284,7 +341,7 @@ void backup_cow_request_begin(CowRequest *req, BlockJob *job,
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t start, end; int64_t start, end;
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); assert(block_job_driver(job) == &backup_job_driver);
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
@ -327,33 +384,29 @@ typedef struct {
int ret; int ret;
} BackupCompleteData; } BackupCompleteData;
static void backup_complete(BlockJob *job, void *opaque) static void backup_complete(Job *job, void *opaque)
{ {
BackupCompleteData *data = opaque; BackupCompleteData *data = opaque;
block_job_completed(job, data->ret); job_completed(job, data->ret, NULL);
g_free(data); g_free(data);
} }
static bool coroutine_fn yield_and_check(BackupBlockJob *job) static bool coroutine_fn yield_and_check(BackupBlockJob *job)
{ {
if (block_job_is_cancelled(&job->common)) { uint64_t delay_ns;
if (job_is_cancelled(&job->common.job)) {
return true; return true;
} }
/* we need to yield so that bdrv_drain_all() returns. /* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
* (without, VM does not reboot) * return. Without a yield, the VM would not reboot. */
*/ delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
if (job->common.speed) { job->bytes_read = 0;
uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, job_sleep_ns(&job->common.job, delay_ns);
job->bytes_read);
job->bytes_read = 0;
block_job_sleep_ns(&job->common, delay_ns);
} else {
block_job_sleep_ns(&job->common, 0);
}
if (block_job_is_cancelled(&job->common)) { if (job_is_cancelled(&job->common.job)) {
return true; return true;
} }
@ -368,7 +421,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
HBitmapIter hbi; HBitmapIter hbi;
hbitmap_iter_init(&hbi, job->copy_bitmap, 0); hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
while ((cluster = hbitmap_iter_next(&hbi)) != -1) { while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) {
do { do {
if (yield_and_check(job)) { if (yield_and_check(job)) {
return 0; return 0;
@ -420,8 +473,9 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size); bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
} }
job->common.offset = job->common.len - /* TODO job_progress_set_remaining() would make more sense */
hbitmap_count(job->copy_bitmap) * job->cluster_size; job_progress_update(&job->common.job,
job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size);
bdrv_dirty_iter_free(dbi); bdrv_dirty_iter_free(dbi);
} }
@ -437,7 +491,9 @@ static void coroutine_fn backup_run(void *opaque)
QLIST_INIT(&job->inflight_reqs); QLIST_INIT(&job->inflight_reqs);
qemu_co_rwlock_init(&job->flush_rwlock); qemu_co_rwlock_init(&job->flush_rwlock);
nb_clusters = DIV_ROUND_UP(job->common.len, job->cluster_size); nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size);
job_progress_set_remaining(&job->common.job, job->len);
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
backup_incremental_init_copy_bitmap(job); backup_incremental_init_copy_bitmap(job);
@ -452,16 +508,16 @@ static void coroutine_fn backup_run(void *opaque)
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
/* All bits are set in copy_bitmap to allow any cluster to be copied. /* All bits are set in copy_bitmap to allow any cluster to be copied.
* This does not actually require them to be copied. */ * This does not actually require them to be copied. */
while (!block_job_is_cancelled(&job->common)) { while (!job_is_cancelled(&job->common.job)) {
/* Yield until the job is cancelled. We just let our before_write /* Yield until the job is cancelled. We just let our before_write
* notify callback service CoW requests. */ * notify callback service CoW requests. */
block_job_yield(&job->common); job_yield(&job->common.job);
} }
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
ret = backup_run_incremental(job); ret = backup_run_incremental(job);
} else { } else {
/* Both FULL and TOP SYNC_MODE's require copying.. */ /* Both FULL and TOP SYNC_MODE's require copying.. */
for (offset = 0; offset < job->common.len; for (offset = 0; offset < job->len;
offset += job->cluster_size) { offset += job->cluster_size) {
bool error_is_read; bool error_is_read;
int alloced = 0; int alloced = 0;
@ -530,17 +586,21 @@ static void coroutine_fn backup_run(void *opaque)
data = g_malloc(sizeof(*data)); data = g_malloc(sizeof(*data));
data->ret = ret; data->ret = ret;
block_job_defer_to_main_loop(&job->common, backup_complete, data); job_defer_to_main_loop(&job->common.job, backup_complete, data);
} }
static const BlockJobDriver backup_job_driver = { static const BlockJobDriver backup_job_driver = {
.instance_size = sizeof(BackupBlockJob), .job_driver = {
.job_type = BLOCK_JOB_TYPE_BACKUP, .instance_size = sizeof(BackupBlockJob),
.start = backup_run, .job_type = JOB_TYPE_BACKUP,
.set_speed = backup_set_speed, .free = block_job_free,
.commit = backup_commit, .user_resume = block_job_user_resume,
.abort = backup_abort, .drain = block_job_drain,
.clean = backup_clean, .start = backup_run,
.commit = backup_commit,
.abort = backup_abort,
.clean = backup_clean,
},
.attached_aio_context = backup_attached_aio_context, .attached_aio_context = backup_attached_aio_context,
.drain = backup_drain, .drain = backup_drain,
}; };
@ -553,7 +613,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
int creation_flags, int creation_flags,
BlockCompletionFunc *cb, void *opaque, BlockCompletionFunc *cb, void *opaque,
BlockJobTxn *txn, Error **errp) JobTxn *txn, Error **errp)
{ {
int64_t len; int64_t len;
BlockDriverInfo bdi; BlockDriverInfo bdi;
@ -620,7 +680,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
goto error; goto error;
} }
/* job->common.len is fixed, so we can't allow resize */ /* job->len is fixed, so we can't allow resize */
job = block_job_create(job_id, &backup_job_driver, txn, bs, job = block_job_create(job_id, &backup_job_driver, txn, bs,
BLK_PERM_CONSISTENT_READ, BLK_PERM_CONSISTENT_READ,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
@ -646,6 +706,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
sync_bitmap : NULL; sync_bitmap : NULL;
job->compress = compress; job->compress = compress;
/* Detect image-fleecing (and similar) schemes */
job->serialize_target_writes = bdrv_chain_contains(target, bs);
/* If there is no backing file on the target, we cannot rely on COW if our /* If there is no backing file on the target, we cannot rely on COW if our
* backup cluster size is smaller than the target cluster size. Even for * backup cluster size is smaller than the target cluster size. Even for
* targets with a backing file, try to avoid COW if possible. */ * targets with a backing file, try to avoid COW if possible. */
@ -672,11 +735,17 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
} else { } else {
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size); job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
} }
job->use_copy_range = true;
job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
blk_get_max_transfer(job->target));
job->copy_range_size = MAX(job->cluster_size,
QEMU_ALIGN_UP(job->copy_range_size,
job->cluster_size));
/* Required permissions are already taken with target's blk_new() */ /* Required permissions are already taken with target's blk_new() */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort); &error_abort);
job->common.len = len; job->len = len;
return &job->common; return &job->common;
@ -685,8 +754,8 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL); bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
} }
if (job) { if (job) {
backup_clean(&job->common); backup_clean(&job->common.job);
block_job_early_fail(&job->common); job_early_fail(&job->common.job);
} }
return NULL; return NULL;

View File

@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
if (c != filename) { if (c != filename) {
QString *config_path; QString *config_path;
config_path = qstring_from_substr(filename, 0, c - filename - 1); config_path = qstring_from_substr(filename, 0, c - filename);
qdict_put(options, "config", config_path); qdict_put(options, "config", config_path);
} }
@ -398,10 +398,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
goto out; goto out;
} }
bs->supported_write_flags = BDRV_REQ_FUA & bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
bs->file->bs->supported_write_flags; (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
bs->file->bs->supported_zero_flags; ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
bs->file->bs->supported_zero_flags);
ret = -EINVAL; ret = -EINVAL;
/* Set alignment overrides */ /* Set alignment overrides */
@ -624,7 +625,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
return err; return err;
} }
return bdrv_co_pdiscard(bs->file->bs, offset, bytes); return bdrv_co_pdiscard(bs->file, offset, bytes);
} }
static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs, static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
@ -845,13 +846,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
opts = qdict_new(); opts = qdict_new();
qdict_put_str(opts, "driver", "blkdebug"); qdict_put_str(opts, "driver", "blkdebug");
QINCREF(bs->file->bs->full_open_options); qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options));
qdict_put(opts, "image", bs->file->bs->full_open_options);
for (e = qdict_first(options); e; e = qdict_next(options, e)) { for (e = qdict_first(options); e; e = qdict_next(options, e)) {
if (strcmp(qdict_entry_key(e), "x-image")) { if (strcmp(qdict_entry_key(e), "x-image")) {
qobject_incref(qdict_entry_value(e)); qdict_put_obj(opts, qdict_entry_key(e),
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e)); qobject_ref(qdict_entry_value(e)));
} }
} }

550
block/blklogwrites.c Normal file
View File

@ -0,0 +1,550 @@
/*
* Write logging blk driver based on blkverify and blkdebug.
*
* Copyright (c) 2017 Tuomas Tynkkynen <tuomas@tuxera.com>
* Copyright (c) 2018 Aapo Vienamo <aapo@tuxera.com>
* Copyright (c) 2018 Ari Sundholm <ari@tuxera.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qemu/sockets.h" /* for EINPROGRESS on Windows */
#include "block/block_int.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "qemu/cutils.h"
#include "qemu/option.h"
/* Disk format stuff - taken from Linux drivers/md/dm-log-writes.c */
#define LOG_FLUSH_FLAG (1 << 0)
#define LOG_FUA_FLAG (1 << 1)
#define LOG_DISCARD_FLAG (1 << 2)
#define LOG_MARK_FLAG (1 << 3)
#define LOG_FLAG_MASK (LOG_FLUSH_FLAG \
| LOG_FUA_FLAG \
| LOG_DISCARD_FLAG \
| LOG_MARK_FLAG)
#define WRITE_LOG_VERSION 1ULL
#define WRITE_LOG_MAGIC 0x6a736677736872ULL
/* All fields are little-endian. */
struct log_write_super {
uint64_t magic;
uint64_t version;
uint64_t nr_entries;
uint32_t sectorsize;
} QEMU_PACKED;
struct log_write_entry {
uint64_t sector;
uint64_t nr_sectors;
uint64_t flags;
uint64_t data_len;
} QEMU_PACKED;
/* End of disk format structures. */
typedef struct {
BdrvChild *log_file;
uint32_t sectorsize;
uint32_t sectorbits;
uint64_t cur_log_sector;
uint64_t nr_entries;
uint64_t update_interval;
} BDRVBlkLogWritesState;
static QemuOptsList runtime_opts = {
.name = "blklogwrites",
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
.desc = {
{
.name = "log-append",
.type = QEMU_OPT_BOOL,
.help = "Append to an existing log",
},
{
.name = "log-sector-size",
.type = QEMU_OPT_SIZE,
.help = "Log sector size",
},
{
.name = "log-super-update-interval",
.type = QEMU_OPT_NUMBER,
.help = "Log superblock update interval (# of write requests)",
},
{ /* end of list */ }
},
};
static inline uint32_t blk_log_writes_log2(uint32_t value)
{
assert(value > 0);
return 31 - clz32(value);
}
static inline bool blk_log_writes_sector_size_valid(uint32_t sector_size)
{
return is_power_of_2(sector_size) &&
sector_size >= sizeof(struct log_write_super) &&
sector_size >= sizeof(struct log_write_entry) &&
sector_size < (1ull << 24);
}
static uint64_t blk_log_writes_find_cur_log_sector(BdrvChild *log,
uint32_t sector_size,
uint64_t nr_entries,
Error **errp)
{
uint64_t cur_sector = 1;
uint64_t cur_idx = 0;
uint32_t sector_bits = blk_log_writes_log2(sector_size);
struct log_write_entry cur_entry;
while (cur_idx < nr_entries) {
int read_ret = bdrv_pread(log, cur_sector << sector_bits, &cur_entry,
sizeof(cur_entry));
if (read_ret < 0) {
error_setg_errno(errp, -read_ret,
"Failed to read log entry %"PRIu64, cur_idx);
return (uint64_t)-1ull;
}
if (cur_entry.flags & ~cpu_to_le64(LOG_FLAG_MASK)) {
error_setg(errp, "Invalid flags 0x%"PRIx64" in log entry %"PRIu64,
le64_to_cpu(cur_entry.flags), cur_idx);
return (uint64_t)-1ull;
}
/* Account for the sector of the entry itself */
++cur_sector;
/*
* Account for the data of the write.
* For discards, this data is not present.
*/
if (!(cur_entry.flags & cpu_to_le64(LOG_DISCARD_FLAG))) {
cur_sector += le64_to_cpu(cur_entry.nr_sectors);
}
++cur_idx;
}
return cur_sector;
}
static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
BDRVBlkLogWritesState *s = bs->opaque;
QemuOpts *opts;
Error *local_err = NULL;
int ret;
uint64_t log_sector_size;
bool log_append;
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto fail;
}
/* Open the file */
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
&local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto fail;
}
/* Open the log file */
s->log_file = bdrv_open_child(NULL, options, "log", bs, &child_file, false,
&local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto fail;
}
log_append = qemu_opt_get_bool(opts, "log-append", false);
if (log_append) {
struct log_write_super log_sb = { 0, 0, 0, 0 };
if (qemu_opt_find(opts, "log-sector-size")) {
ret = -EINVAL;
error_setg(errp, "log-append and log-sector-size are mutually "
"exclusive");
goto fail_log;
}
/* Read log superblock or fake one for an empty log */
if (!bdrv_getlength(s->log_file->bs)) {
log_sb.magic = cpu_to_le64(WRITE_LOG_MAGIC);
log_sb.version = cpu_to_le64(WRITE_LOG_VERSION);
log_sb.nr_entries = cpu_to_le64(0);
log_sb.sectorsize = cpu_to_le32(BDRV_SECTOR_SIZE);
} else {
ret = bdrv_pread(s->log_file, 0, &log_sb, sizeof(log_sb));
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not read log superblock");
goto fail_log;
}
}
if (log_sb.magic != cpu_to_le64(WRITE_LOG_MAGIC)) {
ret = -EINVAL;
error_setg(errp, "Invalid log superblock magic");
goto fail_log;
}
if (log_sb.version != cpu_to_le64(WRITE_LOG_VERSION)) {
ret = -EINVAL;
error_setg(errp, "Unsupported log version %"PRIu64,
le64_to_cpu(log_sb.version));
goto fail_log;
}
log_sector_size = le32_to_cpu(log_sb.sectorsize);
s->cur_log_sector = 1;
s->nr_entries = 0;
if (blk_log_writes_sector_size_valid(log_sector_size)) {
s->cur_log_sector =
blk_log_writes_find_cur_log_sector(s->log_file, log_sector_size,
le64_to_cpu(log_sb.nr_entries), &local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto fail_log;
}
s->nr_entries = le64_to_cpu(log_sb.nr_entries);
}
} else {
log_sector_size = qemu_opt_get_size(opts, "log-sector-size",
BDRV_SECTOR_SIZE);
s->cur_log_sector = 1;
s->nr_entries = 0;
}
if (!blk_log_writes_sector_size_valid(log_sector_size)) {
ret = -EINVAL;
error_setg(errp, "Invalid log sector size %"PRIu64, log_sector_size);
goto fail_log;
}
s->sectorsize = log_sector_size;
s->sectorbits = blk_log_writes_log2(log_sector_size);
s->update_interval = qemu_opt_get_number(opts, "log-super-update-interval",
4096);
if (!s->update_interval) {
ret = -EINVAL;
error_setg(errp, "Invalid log superblock update interval %"PRIu64,
s->update_interval);
goto fail_log;
}
ret = 0;
fail_log:
if (ret < 0) {
bdrv_unref_child(bs, s->log_file);
s->log_file = NULL;
}
fail:
if (ret < 0) {
bdrv_unref_child(bs, bs->file);
bs->file = NULL;
}
qemu_opts_del(opts);
return ret;
}
static void blk_log_writes_close(BlockDriverState *bs)
{
BDRVBlkLogWritesState *s = bs->opaque;
bdrv_unref_child(bs, s->log_file);
s->log_file = NULL;
}
static int64_t blk_log_writes_getlength(BlockDriverState *bs)
{
return bdrv_getlength(bs->file->bs);
}
static void blk_log_writes_refresh_filename(BlockDriverState *bs,
QDict *options)
{
BDRVBlkLogWritesState *s = bs->opaque;
/* bs->file->bs has already been refreshed */
bdrv_refresh_filename(s->log_file->bs);
if (bs->file->bs->full_open_options
&& s->log_file->bs->full_open_options)
{
QDict *opts = qdict_new();
qdict_put_str(opts, "driver", "blklogwrites");
qobject_ref(bs->file->bs->full_open_options);
qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options));
qobject_ref(s->log_file->bs->full_open_options);
qdict_put_obj(opts, "log",
QOBJECT(s->log_file->bs->full_open_options));
qdict_put_int(opts, "log-sector-size", s->sectorsize);
bs->full_open_options = opts;
}
}
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *ro_q,
uint64_t perm, uint64_t shrd,
uint64_t *nperm, uint64_t *nshrd)
{
if (!c) {
*nperm = perm & DEFAULT_PERM_PASSTHROUGH;
*nshrd = (shrd & DEFAULT_PERM_PASSTHROUGH) | DEFAULT_PERM_UNCHANGED;
return;
}
if (!strcmp(c->name, "log")) {
bdrv_format_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
} else {
bdrv_filter_default_perms(bs, c, role, ro_q, perm, shrd, nperm, nshrd);
}
}
static void blk_log_writes_refresh_limits(BlockDriverState *bs, Error **errp)
{
BDRVBlkLogWritesState *s = bs->opaque;
bs->bl.request_alignment = s->sectorsize;
}
static int coroutine_fn
blk_log_writes_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
}
typedef struct BlkLogWritesFileReq {
BlockDriverState *bs;
uint64_t offset;
uint64_t bytes;
int file_flags;
QEMUIOVector *qiov;
int (*func)(struct BlkLogWritesFileReq *r);
int file_ret;
} BlkLogWritesFileReq;
typedef struct {
BlockDriverState *bs;
QEMUIOVector *qiov;
struct log_write_entry entry;
uint64_t zero_size;
int log_ret;
} BlkLogWritesLogReq;
static void coroutine_fn blk_log_writes_co_do_log(BlkLogWritesLogReq *lr)
{
BDRVBlkLogWritesState *s = lr->bs->opaque;
uint64_t cur_log_offset = s->cur_log_sector << s->sectorbits;
s->nr_entries++;
s->cur_log_sector +=
ROUND_UP(lr->qiov->size, s->sectorsize) >> s->sectorbits;
lr->log_ret = bdrv_co_pwritev(s->log_file, cur_log_offset, lr->qiov->size,
lr->qiov, 0);
/* Logging for the "write zeroes" operation */
if (lr->log_ret == 0 && lr->zero_size) {
cur_log_offset = s->cur_log_sector << s->sectorbits;
s->cur_log_sector +=
ROUND_UP(lr->zero_size, s->sectorsize) >> s->sectorbits;
lr->log_ret = bdrv_co_pwrite_zeroes(s->log_file, cur_log_offset,
lr->zero_size, 0);
}
/* Update super block on flush or every update interval */
if (lr->log_ret == 0 && ((lr->entry.flags & LOG_FLUSH_FLAG)
|| (s->nr_entries % s->update_interval == 0)))
{
struct log_write_super super = {
.magic = cpu_to_le64(WRITE_LOG_MAGIC),
.version = cpu_to_le64(WRITE_LOG_VERSION),
.nr_entries = cpu_to_le64(s->nr_entries),
.sectorsize = cpu_to_le32(s->sectorsize),
};
void *zeroes = g_malloc0(s->sectorsize - sizeof(super));
QEMUIOVector qiov;
qemu_iovec_init(&qiov, 2);
qemu_iovec_add(&qiov, &super, sizeof(super));
qemu_iovec_add(&qiov, zeroes, s->sectorsize - sizeof(super));
lr->log_ret =
bdrv_co_pwritev(s->log_file, 0, s->sectorsize, &qiov, 0);
if (lr->log_ret == 0) {
lr->log_ret = bdrv_co_flush(s->log_file->bs);
}
qemu_iovec_destroy(&qiov);
g_free(zeroes);
}
}
static void coroutine_fn blk_log_writes_co_do_file(BlkLogWritesFileReq *fr)
{
fr->file_ret = fr->func(fr);
}
static int coroutine_fn
blk_log_writes_co_log(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags,
int (*file_func)(BlkLogWritesFileReq *r),
uint64_t entry_flags, bool is_zero_write)
{
QEMUIOVector log_qiov;
size_t niov = qiov ? qiov->niov : 0;
BDRVBlkLogWritesState *s = bs->opaque;
BlkLogWritesFileReq fr = {
.bs = bs,
.offset = offset,
.bytes = bytes,
.file_flags = flags,
.qiov = qiov,
.func = file_func,
};
BlkLogWritesLogReq lr = {
.bs = bs,
.qiov = &log_qiov,
.entry = {
.sector = cpu_to_le64(offset >> s->sectorbits),
.nr_sectors = cpu_to_le64(bytes >> s->sectorbits),
.flags = cpu_to_le64(entry_flags),
.data_len = 0,
},
.zero_size = is_zero_write ? bytes : 0,
};
void *zeroes = g_malloc0(s->sectorsize - sizeof(lr.entry));
assert((1 << s->sectorbits) == s->sectorsize);
assert(bs->bl.request_alignment == s->sectorsize);
assert(QEMU_IS_ALIGNED(offset, bs->bl.request_alignment));
assert(QEMU_IS_ALIGNED(bytes, bs->bl.request_alignment));
qemu_iovec_init(&log_qiov, niov + 2);
qemu_iovec_add(&log_qiov, &lr.entry, sizeof(lr.entry));
qemu_iovec_add(&log_qiov, zeroes, s->sectorsize - sizeof(lr.entry));
if (qiov) {
qemu_iovec_concat(&log_qiov, qiov, 0, qiov->size);
}
blk_log_writes_co_do_file(&fr);
blk_log_writes_co_do_log(&lr);
qemu_iovec_destroy(&log_qiov);
g_free(zeroes);
if (lr.log_ret < 0) {
return lr.log_ret;
}
return fr.file_ret;
}
static int coroutine_fn
blk_log_writes_co_do_file_pwritev(BlkLogWritesFileReq *fr)
{
return bdrv_co_pwritev(fr->bs->file, fr->offset, fr->bytes,
fr->qiov, fr->file_flags);
}
static int coroutine_fn
blk_log_writes_co_do_file_pwrite_zeroes(BlkLogWritesFileReq *fr)
{
return bdrv_co_pwrite_zeroes(fr->bs->file, fr->offset, fr->bytes,
fr->file_flags);
}
static int coroutine_fn blk_log_writes_co_do_file_flush(BlkLogWritesFileReq *fr)
{
return bdrv_co_flush(fr->bs->file->bs);
}
static int coroutine_fn
blk_log_writes_co_do_file_pdiscard(BlkLogWritesFileReq *fr)
{
return bdrv_co_pdiscard(fr->bs->file, fr->offset, fr->bytes);
}
static int coroutine_fn
blk_log_writes_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return blk_log_writes_co_log(bs, offset, bytes, qiov, flags,
blk_log_writes_co_do_file_pwritev, 0, false);
}
static int coroutine_fn
blk_log_writes_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
BdrvRequestFlags flags)
{
return blk_log_writes_co_log(bs, offset, bytes, NULL, flags,
blk_log_writes_co_do_file_pwrite_zeroes, 0,
true);
}
static int coroutine_fn blk_log_writes_co_flush_to_disk(BlockDriverState *bs)
{
return blk_log_writes_co_log(bs, 0, 0, NULL, 0,
blk_log_writes_co_do_file_flush,
LOG_FLUSH_FLAG, false);
}
static int coroutine_fn
blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
{
return blk_log_writes_co_log(bs, offset, count, NULL, 0,
blk_log_writes_co_do_file_pdiscard,
LOG_DISCARD_FLAG, false);
}
static BlockDriver bdrv_blk_log_writes = {
.format_name = "blklogwrites",
.instance_size = sizeof(BDRVBlkLogWritesState),
.bdrv_open = blk_log_writes_open,
.bdrv_close = blk_log_writes_close,
.bdrv_getlength = blk_log_writes_getlength,
.bdrv_refresh_filename = blk_log_writes_refresh_filename,
.bdrv_child_perm = blk_log_writes_child_perm,
.bdrv_refresh_limits = blk_log_writes_refresh_limits,
.bdrv_co_preadv = blk_log_writes_co_preadv,
.bdrv_co_pwritev = blk_log_writes_co_pwritev,
.bdrv_co_pwrite_zeroes = blk_log_writes_co_pwrite_zeroes,
.bdrv_co_flush_to_disk = blk_log_writes_co_flush_to_disk,
.bdrv_co_pdiscard = blk_log_writes_co_pdiscard,
.bdrv_co_block_status = bdrv_co_block_status_from_file,
.is_filter = true,
};
static void bdrv_blk_log_writes_init(void)
{
bdrv_register(&bdrv_blk_log_writes);
}
block_init(bdrv_blk_log_writes_init);

View File

@ -35,6 +35,9 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} }
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
ret = 0; ret = 0;
fail: fail:
return ret; return ret;
@ -110,7 +113,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes) int64_t offset, int bytes)
{ {
uint64_t reqid = blkreplay_next_id(); uint64_t reqid = blkreplay_next_id();
int ret = bdrv_co_pdiscard(bs->file->bs, offset, bytes); int ret = bdrv_co_pdiscard(bs->file, offset, bytes);
block_request_create(reqid, bs, qemu_coroutine_self()); block_request_create(reqid, bs, qemu_coroutine_self());
qemu_coroutine_yield(); qemu_coroutine_yield();

View File

@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
} }
/* TODO Implement option pass-through and set raw.filename here */ /* TODO Implement option pass-through and set raw.filename here */
raw_path = qstring_from_substr(filename, 0, c - filename - 1); raw_path = qstring_from_substr(filename, 0, c - filename);
qdict_put(options, "x-raw", raw_path); qdict_put(options, "x-raw", raw_path);
/* TODO Allow multi-level nesting and set file.filename here */ /* TODO Allow multi-level nesting and set file.filename here */
@ -141,6 +141,9 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} }
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
ret = 0; ret = 0;
fail: fail:
qemu_opts_del(opts); qemu_opts_del(opts);
@ -291,10 +294,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
QDict *opts = qdict_new(); QDict *opts = qdict_new();
qdict_put_str(opts, "driver", "blkverify"); qdict_put_str(opts, "driver", "blkverify");
QINCREF(bs->file->bs->full_open_options); qdict_put(opts, "raw",
qdict_put(opts, "raw", bs->file->bs->full_open_options); qobject_ref(bs->file->bs->full_open_options));
QINCREF(s->test_file->bs->full_open_options); qdict_put(opts, "test",
qdict_put(opts, "test", s->test_file->bs->full_open_options); qobject_ref(s->test_file->bs->full_open_options));
bs->full_open_options = opts; bs->full_open_options = opts;
} }

View File

@ -768,6 +768,11 @@ void blk_remove_bs(BlockBackend *blk)
blk_update_root_state(blk); blk_update_root_state(blk);
/* bdrv_root_unref_child() will cause blk->root to become stale and may
* switch to a completion coroutine later on. Let's drain all I/O here
* to avoid that and a potential QEMU crash.
*/
blk_drain(blk);
bdrv_root_unref_child(blk->root); bdrv_root_unref_child(blk->root);
blk->root = NULL; blk->root = NULL;
} }
@ -1555,7 +1560,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
return ret; return ret;
} }
return bdrv_co_pdiscard(blk_bs(blk), offset, bytes); return bdrv_co_pdiscard(blk->root, offset, bytes);
} }
int blk_co_flush(BlockBackend *blk) int blk_co_flush(BlockBackend *blk)
@ -1865,13 +1870,7 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason)
AioContext *blk_get_aio_context(BlockBackend *blk) AioContext *blk_get_aio_context(BlockBackend *blk)
{ {
BlockDriverState *bs = blk_bs(blk); return bdrv_get_aio_context(blk_bs(blk));
if (bs) {
return bdrv_get_aio_context(bs);
} else {
return qemu_get_aio_context();
}
} }
static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb) static AioContext *blk_aiocb_get_aio_context(BlockAIOCB *acb)
@ -2217,3 +2216,22 @@ void blk_unregister_buf(BlockBackend *blk, void *host)
{ {
bdrv_unregister_buf(blk_bs(blk), host); bdrv_unregister_buf(blk_bs(blk), host);
} }
int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
BlockBackend *blk_out, int64_t off_out,
int bytes, BdrvRequestFlags read_flags,
BdrvRequestFlags write_flags)
{
int r;
r = blk_check_byte_request(blk_in, off_in, bytes);
if (r) {
return r;
}
r = blk_check_byte_request(blk_out, off_out, bytes);
if (r) {
return r;
}
return bdrv_co_copy_range(blk_in->root, off_in,
blk_out->root, off_out,
bytes, read_flags, write_flags);
}

View File

@ -31,11 +31,8 @@ enum {
COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
}; };
#define SLICE_TIME 100000000ULL /* ns */
typedef struct CommitBlockJob { typedef struct CommitBlockJob {
BlockJob common; BlockJob common;
RateLimit limit;
BlockDriverState *commit_top_bs; BlockDriverState *commit_top_bs;
BlockBackend *top; BlockBackend *top;
BlockBackend *base; BlockBackend *base;
@ -75,9 +72,10 @@ typedef struct {
int ret; int ret;
} CommitCompleteData; } CommitCompleteData;
static void commit_complete(BlockJob *job, void *opaque) static void commit_complete(Job *job, void *opaque)
{ {
CommitBlockJob *s = container_of(job, CommitBlockJob, common); CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
BlockJob *bjob = &s->common;
CommitCompleteData *data = opaque; CommitCompleteData *data = opaque;
BlockDriverState *top = blk_bs(s->top); BlockDriverState *top = blk_bs(s->top);
BlockDriverState *base = blk_bs(s->base); BlockDriverState *base = blk_bs(s->base);
@ -93,7 +91,7 @@ static void commit_complete(BlockJob *job, void *opaque)
* the normal backing chain can be restored. */ * the normal backing chain can be restored. */
blk_unref(s->base); blk_unref(s->base);
if (!block_job_is_cancelled(&s->common) && ret == 0) { if (!job_is_cancelled(job) && ret == 0) {
/* success */ /* success */
ret = bdrv_drop_intermediate(s->commit_top_bs, base, ret = bdrv_drop_intermediate(s->commit_top_bs, base,
s->backing_file_str); s->backing_file_str);
@ -114,12 +112,12 @@ static void commit_complete(BlockJob *job, void *opaque)
blk_unref(s->top); blk_unref(s->top);
/* If there is more than one reference to the job (e.g. if called from /* If there is more than one reference to the job (e.g. if called from
* block_job_finish_sync()), block_job_completed() won't free it and * job_finish_sync()), job_completed() won't free it and therefore the
* therefore the blockers on the intermediate nodes remain. This would * blockers on the intermediate nodes remain. This would cause
* cause bdrv_set_backing_hd() to fail. */ * bdrv_set_backing_hd() to fail. */
block_job_remove_all_bdrv(job); block_job_remove_all_bdrv(bjob);
block_job_completed(&s->common, ret); job_completed(job, ret, NULL);
g_free(data); g_free(data);
/* If bdrv_drop_intermediate() didn't already do that, remove the commit /* If bdrv_drop_intermediate() didn't already do that, remove the commit
@ -146,21 +144,21 @@ static void coroutine_fn commit_run(void *opaque)
int64_t n = 0; /* bytes */ int64_t n = 0; /* bytes */
void *buf = NULL; void *buf = NULL;
int bytes_written = 0; int bytes_written = 0;
int64_t base_len; int64_t len, base_len;
ret = s->common.len = blk_getlength(s->top); ret = len = blk_getlength(s->top);
if (len < 0) {
if (s->common.len < 0) {
goto out; goto out;
} }
job_progress_set_remaining(&s->common.job, len);
ret = base_len = blk_getlength(s->base); ret = base_len = blk_getlength(s->base);
if (base_len < 0) { if (base_len < 0) {
goto out; goto out;
} }
if (base_len < s->common.len) { if (base_len < len) {
ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL); ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL);
if (ret) { if (ret) {
goto out; goto out;
} }
@ -168,14 +166,14 @@ static void coroutine_fn commit_run(void *opaque)
buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
for (offset = 0; offset < s->common.len; offset += n) { for (offset = 0; offset < len; offset += n) {
bool copy; bool copy;
/* Note that even when no rate limit is applied we need to yield /* Note that even when no rate limit is applied we need to yield
* with no pending I/O here so that bdrv_drain_all() returns. * with no pending I/O here so that bdrv_drain_all() returns.
*/ */
block_job_sleep_ns(&s->common, delay_ns); job_sleep_ns(&s->common.job, delay_ns);
if (block_job_is_cancelled(&s->common)) { if (job_is_cancelled(&s->common.job)) {
break; break;
} }
/* Copy if allocated above the base */ /* Copy if allocated above the base */
@ -198,10 +196,10 @@ static void coroutine_fn commit_run(void *opaque)
} }
} }
/* Publish progress */ /* Publish progress */
s->common.offset += n; job_progress_update(&s->common.job, n);
if (copy && s->common.speed) { if (copy) {
delay_ns = ratelimit_calculate_delay(&s->limit, n); delay_ns = block_job_ratelimit_get_delay(&s->common, n);
} else { } else {
delay_ns = 0; delay_ns = 0;
} }
@ -214,25 +212,18 @@ out:
data = g_malloc(sizeof(*data)); data = g_malloc(sizeof(*data));
data->ret = ret; data->ret = ret;
block_job_defer_to_main_loop(&s->common, commit_complete, data); job_defer_to_main_loop(&s->common.job, commit_complete, data);
}
static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
CommitBlockJob *s = container_of(job, CommitBlockJob, common);
if (speed < 0) {
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return;
}
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
} }
static const BlockJobDriver commit_job_driver = { static const BlockJobDriver commit_job_driver = {
.instance_size = sizeof(CommitBlockJob), .job_driver = {
.job_type = BLOCK_JOB_TYPE_COMMIT, .instance_size = sizeof(CommitBlockJob),
.set_speed = commit_set_speed, .job_type = JOB_TYPE_COMMIT,
.start = commit_run, .free = block_job_free,
.user_resume = block_job_user_resume,
.drain = block_job_drain,
.start = commit_run,
},
}; };
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
@ -292,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, s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); speed, JOB_DEFAULT, NULL, NULL, errp);
if (!s) { if (!s) {
return; return;
} }
@ -382,7 +373,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
s->on_error = on_error; s->on_error = on_error;
trace_commit_start(bs, base, top, s); trace_commit_start(bs, base, top, s);
block_job_start(&s->common); job_start(&s->common.job);
return; return;
fail: fail:
@ -395,7 +386,7 @@ fail:
if (commit_top_bs) { if (commit_top_bs) {
bdrv_replace_node(commit_top_bs, top, &error_abort); bdrv_replace_node(commit_top_bs, top, &error_abort);
} }
block_job_early_fail(&s->common); job_early_fail(&s->common.job);
} }

173
block/copy-on-read.c Normal file
View File

@ -0,0 +1,173 @@
/*
* Copy-on-read filter block driver
*
* Copyright (c) 2018 Red Hat, Inc.
*
* Author:
* Max Reitz <mreitz@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "block/block_int.h"
static int cor_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp)
{
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false,
errp);
if (!bs->file) {
return -EINVAL;
}
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
(BDRV_REQ_FUA &
bs->file->bs->supported_write_flags);
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
bs->file->bs->supported_zero_flags);
return 0;
}
static void cor_close(BlockDriverState *bs)
{
}
#define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \
| BLK_PERM_WRITE \
| BLK_PERM_RESIZE)
#define PERM_UNCHANGED (BLK_PERM_ALL & ~PERM_PASSTHROUGH)
static void cor_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *reopen_queue,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
if (c == NULL) {
*nperm = (perm & PERM_PASSTHROUGH) | BLK_PERM_WRITE_UNCHANGED;
*nshared = (shared & PERM_PASSTHROUGH) | PERM_UNCHANGED;
return;
}
*nperm = (perm & PERM_PASSTHROUGH) |
(c->perm & PERM_UNCHANGED);
*nshared = (shared & PERM_PASSTHROUGH) |
(c->shared_perm & PERM_UNCHANGED);
}
static int64_t cor_getlength(BlockDriverState *bs)
{
return bdrv_getlength(bs->file->bs);
}
static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{
return bdrv_co_truncate(bs->file, offset, prealloc, errp);
}
static int coroutine_fn cor_co_preadv(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return bdrv_co_preadv(bs->file, offset, bytes, qiov,
flags | BDRV_REQ_COPY_ON_READ);
}
static int coroutine_fn cor_co_pwritev(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
}
static int coroutine_fn cor_co_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int bytes,
BdrvRequestFlags flags)
{
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
}
static int coroutine_fn cor_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes)
{
return bdrv_co_pdiscard(bs->file, offset, bytes);
}
static void cor_eject(BlockDriverState *bs, bool eject_flag)
{
bdrv_eject(bs->file->bs, eject_flag);
}
static void cor_lock_medium(BlockDriverState *bs, bool locked)
{
bdrv_lock_medium(bs->file->bs, locked);
}
static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
BlockDriverState *candidate)
{
return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
}
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,
.bdrv_co_truncate = cor_co_truncate,
.bdrv_co_preadv = cor_co_preadv,
.bdrv_co_pwritev = cor_co_pwritev,
.bdrv_co_pwrite_zeroes = cor_co_pwrite_zeroes,
.bdrv_co_pdiscard = cor_co_pdiscard,
.bdrv_eject = cor_eject,
.bdrv_lock_medium = cor_lock_medium,
.bdrv_co_block_status = bdrv_co_block_status_from_file,
.bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter,
.has_variable_length = true,
.is_filter = true,
};
static void bdrv_copy_on_read_init(void)
{
bdrv_register(&bdrv_copy_on_read);
}
block_init(bdrv_copy_on_read_init);

View File

@ -24,28 +24,51 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/job.h"
#include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-commands-block-core.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/clone-visitor.h"
#include "qapi/error.h" #include "qapi/error.h"
typedef struct BlockdevCreateCo { typedef struct BlockdevCreateJob {
Job common;
BlockDriver *drv; BlockDriver *drv;
BlockdevCreateOptions *opts; BlockdevCreateOptions *opts;
int ret; int ret;
Error **errp; Error *err;
} BlockdevCreateCo; } BlockdevCreateJob;
static void coroutine_fn bdrv_co_create_co_entry(void *opaque) static void blockdev_create_complete(Job *job, void *opaque)
{ {
BlockdevCreateCo *cco = opaque; BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
job_completed(job, s->ret, s->err);
} }
void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp) static void coroutine_fn blockdev_create_run(void *opaque)
{ {
BlockdevCreateJob *s = opaque;
job_progress_set_remaining(&s->common, 1);
s->ret = s->drv->bdrv_co_create(s->opts, &s->err);
job_progress_update(&s->common, 1);
qapi_free_BlockdevCreateOptions(s->opts);
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,
.start = blockdev_create_run,
};
void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
Error **errp)
{
BlockdevCreateJob *s;
const char *fmt = BlockdevDriver_str(options->driver); const char *fmt = BlockdevDriver_str(options->driver);
BlockDriver *drv = bdrv_find_format(fmt); BlockDriver *drv = bdrv_find_format(fmt);
Coroutine *co;
BlockdevCreateCo cco;
/* If the driver is in the schema, we know that it exists. But it may not /* If the driver is in the schema, we know that it exists. But it may not
* be whitelisted. */ * be whitelisted. */
@ -55,22 +78,24 @@ void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
return; return;
} }
/* Call callback if it exists */ /* Error out if the driver doesn't support .bdrv_co_create */
if (!drv->bdrv_co_create) { if (!drv->bdrv_co_create) {
error_setg(errp, "Driver does not support blockdev-create"); error_setg(errp, "Driver does not support blockdev-create");
return; return;
} }
cco = (BlockdevCreateCo) { /* Create the block job */
.drv = drv, /* TODO Running in the main context. Block drivers need to error out or add
.opts = options, * locking when they use a BDS in a different AioContext. */
.ret = -EINPROGRESS, s = job_create(job_id, &blockdev_create_job_driver, NULL,
.errp = errp, qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS,
}; NULL, NULL, errp);
if (!s) {
co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco); return;
qemu_coroutine_enter(co);
while (cco.ret == -EINPROGRESS) {
aio_poll(qemu_get_aio_context(), true);
} }
s->drv = drv,
s->opts = QAPI_CLONE(BlockdevCreateOptions, options),
job_start(&s->common);
} }

View File

@ -21,15 +21,15 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "crypto/block.h" #include "crypto/block.h"
#include "qapi/opts-visitor.h" #include "qapi/opts-visitor.h"
#include "qapi/qapi-visit-crypto.h" #include "qapi/qapi-visit-crypto.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qobject-input-visitor.h" #include "qapi/qobject-input-visitor.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "block/crypto.h" #include "crypto.h"
typedef struct BlockCrypto BlockCrypto; typedef struct BlockCrypto BlockCrypto;
@ -148,102 +148,36 @@ static QemuOptsList block_crypto_create_opts_luks = {
QCryptoBlockOpenOptions * QCryptoBlockOpenOptions *
block_crypto_open_opts_init(QCryptoBlockFormat format, block_crypto_open_opts_init(QDict *opts, Error **errp)
QDict *opts,
Error **errp)
{ {
Visitor *v; Visitor *v;
QCryptoBlockOpenOptions *ret = NULL; QCryptoBlockOpenOptions *ret;
Error *local_err = NULL;
ret = g_new0(QCryptoBlockOpenOptions, 1); v = qobject_input_visitor_new_flat_confused(opts, errp);
ret->format = format; if (!v) {
return NULL;
v = qobject_input_visitor_new_keyval(QOBJECT(opts));
visit_start_struct(v, NULL, NULL, 0, &local_err);
if (local_err) {
goto out;
} }
switch (format) { visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp);
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
visit_type_QCryptoBlockOptionsLUKS_members(
v, &ret->u.luks, &local_err);
break;
case Q_CRYPTO_BLOCK_FORMAT_QCOW:
visit_type_QCryptoBlockOptionsQCow_members(
v, &ret->u.qcow, &local_err);
break;
default:
error_setg(&local_err, "Unsupported block format %d", format);
break;
}
if (!local_err) {
visit_check_struct(v, &local_err);
}
visit_end_struct(v, NULL);
out:
if (local_err) {
error_propagate(errp, local_err);
qapi_free_QCryptoBlockOpenOptions(ret);
ret = NULL;
}
visit_free(v); visit_free(v);
return ret; return ret;
} }
QCryptoBlockCreateOptions * QCryptoBlockCreateOptions *
block_crypto_create_opts_init(QCryptoBlockFormat format, block_crypto_create_opts_init(QDict *opts, Error **errp)
QDict *opts,
Error **errp)
{ {
Visitor *v; Visitor *v;
QCryptoBlockCreateOptions *ret = NULL; QCryptoBlockCreateOptions *ret;
Error *local_err = NULL;
ret = g_new0(QCryptoBlockCreateOptions, 1); v = qobject_input_visitor_new_flat_confused(opts, errp);
ret->format = format; if (!v) {
return NULL;
v = qobject_input_visitor_new_keyval(QOBJECT(opts));
visit_start_struct(v, NULL, NULL, 0, &local_err);
if (local_err) {
goto out;
} }
switch (format) { visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp);
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
visit_type_QCryptoBlockCreateOptionsLUKS_members(
v, &ret->u.luks, &local_err);
break;
case Q_CRYPTO_BLOCK_FORMAT_QCOW:
visit_type_QCryptoBlockOptionsQCow_members(
v, &ret->u.qcow, &local_err);
break;
default:
error_setg(&local_err, "Unsupported block format %d", format);
break;
}
if (!local_err) {
visit_check_struct(v, &local_err);
}
visit_end_struct(v, NULL);
out:
if (local_err) {
error_propagate(errp, local_err);
qapi_free_QCryptoBlockCreateOptions(ret);
ret = NULL;
}
visit_free(v); visit_free(v);
return ret; return ret;
} }
@ -281,8 +215,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
} }
cryptoopts = qemu_opts_to_qdict(opts, NULL); cryptoopts = qemu_opts_to_qdict(opts, NULL);
qdict_put_str(cryptoopts, "format", QCryptoBlockFormat_str(format));
open_opts = block_crypto_open_opts_init(format, cryptoopts, errp); open_opts = block_crypto_open_opts_init(cryptoopts, errp);
if (!open_opts) { if (!open_opts) {
goto cleanup; goto cleanup;
} }
@ -305,7 +240,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
ret = 0; ret = 0;
cleanup: cleanup:
QDECREF(cryptoopts); qobject_unref(cryptoopts);
qapi_free_QCryptoBlockOpenOptions(open_opts); qapi_free_QCryptoBlockOpenOptions(open_opts);
return ret; return ret;
} }
@ -351,8 +286,9 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
return ret; return ret;
} }
static int block_crypto_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn
PreallocMode prealloc, Error **errp) block_crypto_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{ {
BlockCrypto *crypto = bs->opaque; BlockCrypto *crypto = bs->opaque;
uint64_t payload_offset = uint64_t payload_offset =
@ -365,7 +301,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
offset += payload_offset; offset += payload_offset;
return bdrv_truncate(bs->file, offset, prealloc, errp); return bdrv_co_truncate(bs->file, offset, prealloc, errp);
} }
static void block_crypto_close(BlockDriverState *bs) static void block_crypto_close(BlockDriverState *bs)
@ -605,8 +541,8 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
&block_crypto_create_opts_luks, &block_crypto_create_opts_luks,
true); true);
create_opts = block_crypto_create_opts_init(Q_CRYPTO_BLOCK_FORMAT_LUKS, qdict_put_str(cryptoopts, "format", "luks");
cryptoopts, errp); create_opts = block_crypto_create_opts_init(cryptoopts, errp);
if (!create_opts) { if (!create_opts) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
@ -615,7 +551,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
/* Create protocol layer */ /* Create protocol layer */
ret = bdrv_create_file(filename, opts, errp); ret = bdrv_create_file(filename, opts, errp);
if (ret < 0) { if (ret < 0) {
return ret; goto fail;
} }
bs = bdrv_open(filename, NULL, NULL, bs = bdrv_open(filename, NULL, NULL,
@ -635,7 +571,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
fail: fail:
bdrv_unref(bs); bdrv_unref(bs);
qapi_free_QCryptoBlockCreateOptions(create_opts); qapi_free_QCryptoBlockCreateOptions(create_opts);
QDECREF(cryptoopts); qobject_unref(cryptoopts);
return ret; return ret;
} }
@ -694,7 +630,7 @@ BlockDriver bdrv_crypto_luks = {
.bdrv_child_perm = bdrv_format_default_perms, .bdrv_child_perm = bdrv_format_default_perms,
.bdrv_co_create = block_crypto_co_create_luks, .bdrv_co_create = block_crypto_co_create_luks,
.bdrv_co_create_opts = block_crypto_co_create_opts_luks, .bdrv_co_create_opts = block_crypto_co_create_opts_luks,
.bdrv_truncate = block_crypto_truncate, .bdrv_co_truncate = block_crypto_co_truncate,
.create_opts = &block_crypto_create_opts_luks, .create_opts = &block_crypto_create_opts_luks,
.bdrv_reopen_prepare = block_crypto_reopen_prepare, .bdrv_reopen_prepare = block_crypto_reopen_prepare,

View File

@ -89,13 +89,9 @@
} }
QCryptoBlockCreateOptions * QCryptoBlockCreateOptions *
block_crypto_create_opts_init(QCryptoBlockFormat format, block_crypto_create_opts_init(QDict *opts, Error **errp);
QDict *opts,
Error **errp);
QCryptoBlockOpenOptions * QCryptoBlockOpenOptions *
block_crypto_open_opts_init(QCryptoBlockFormat format, block_crypto_open_opts_init(QDict *opts, Error **errp);
QDict *opts,
Error **errp);
#endif /* BLOCK_CRYPTO_H__ */ #endif /* BLOCK_CRYPTO_H__ */

View File

@ -804,7 +804,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
} }
/* Prior CURL 7.19.4 return value of 0 could mean that the file size is not /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
* know or the size is zero. From 7.19.4 CURL returns -1 if size is not * know or the size is zero. From 7.19.4 CURL returns -1 if size is not
* known and zero if it is realy zero-length file. */ * known and zero if it is really zero-length file. */
#if LIBCURL_VERSION_NUM >= 0x071304 #if LIBCURL_VERSION_NUM >= 0x071304
if (d < 0) { if (d < 0) {
pstrcpy(state->errmsg, CURL_ERROR_SIZE, pstrcpy(state->errmsg, CURL_ERROR_SIZE,

View File

@ -97,15 +97,6 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
return NULL; return NULL;
} }
/* Called with BQL taken. */
void bdrv_dirty_bitmap_make_anon(BdrvDirtyBitmap *bitmap)
{
assert(!bdrv_dirty_bitmap_frozen(bitmap));
g_free(bitmap->name);
bitmap->name = NULL;
bitmap->persistent = false;
}
/* Called with BQL taken. */ /* Called with BQL taken. */
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
uint32_t granularity, uint32_t granularity,
@ -250,57 +241,31 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
return 0; return 0;
} }
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
{
assert(!bdrv_dirty_bitmap_frozen(bitmap));
bitmap->disabled = false;
}
/* Called with BQL taken. */ /* Called with BQL taken. */
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
{ {
assert(bitmap->mutex == bitmap->successor->mutex);
qemu_mutex_lock(bitmap->mutex); qemu_mutex_lock(bitmap->mutex);
bdrv_enable_dirty_bitmap(bitmap->successor); bdrv_enable_dirty_bitmap_locked(bitmap->successor);
qemu_mutex_unlock(bitmap->mutex); qemu_mutex_unlock(bitmap->mutex);
} }
/* Called within bdrv_dirty_bitmap_lock..unlock */ /* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */
static void bdrv_do_release_matching_dirty_bitmap_locked( static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
bool (*cond)(BdrvDirtyBitmap *bitmap))
{ {
BdrvDirtyBitmap *bm, *next; assert(!bitmap->active_iterators);
assert(!bdrv_dirty_bitmap_frozen(bitmap));
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { assert(!bitmap->meta);
if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) { QLIST_REMOVE(bitmap, list);
assert(!bm->active_iterators); hbitmap_free(bitmap->bitmap);
assert(!bdrv_dirty_bitmap_frozen(bm)); g_free(bitmap->name);
assert(!bm->meta); g_free(bitmap);
QLIST_REMOVE(bm, list);
hbitmap_free(bm->bitmap);
g_free(bm->name);
g_free(bm);
if (bitmap) {
return;
}
}
}
if (bitmap) {
abort();
}
}
/* Called with BQL taken. */
static void bdrv_do_release_matching_dirty_bitmap(
BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
bool (*cond)(BdrvDirtyBitmap *bitmap))
{
bdrv_dirty_bitmaps_lock(bs);
bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, cond);
bdrv_dirty_bitmaps_unlock(bs);
}
/* Called within bdrv_dirty_bitmap_lock..unlock */
static void bdrv_release_dirty_bitmap_locked(BlockDriverState *bs,
BdrvDirtyBitmap *bitmap)
{
bdrv_do_release_matching_dirty_bitmap_locked(bs, bitmap, NULL);
} }
/** /**
@ -353,7 +318,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
error_setg(errp, "Merging of parent and successor bitmap failed"); error_setg(errp, "Merging of parent and successor bitmap failed");
return NULL; return NULL;
} }
bdrv_release_dirty_bitmap_locked(bs, successor); bdrv_release_dirty_bitmap_locked(successor);
parent->successor = NULL; parent->successor = NULL;
return parent; return parent;
@ -391,15 +356,12 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
bdrv_dirty_bitmaps_unlock(bs); bdrv_dirty_bitmaps_unlock(bs);
} }
static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap)
{
return !!bdrv_dirty_bitmap_name(bitmap);
}
/* Called with BQL taken. */ /* Called with BQL taken. */
void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
{ {
bdrv_do_release_matching_dirty_bitmap(bs, bitmap, NULL); bdrv_dirty_bitmaps_lock(bs);
bdrv_release_dirty_bitmap_locked(bitmap);
bdrv_dirty_bitmaps_unlock(bs);
} }
/** /**
@ -410,7 +372,15 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap)
*/ */
void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
{ {
bdrv_do_release_matching_dirty_bitmap(bs, NULL, bdrv_dirty_bitmap_has_name); BdrvDirtyBitmap *bm, *next;
bdrv_dirty_bitmaps_lock(bs);
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
if (bdrv_dirty_bitmap_name(bm)) {
bdrv_release_dirty_bitmap_locked(bm);
}
}
bdrv_dirty_bitmaps_unlock(bs);
} }
/** /**
@ -418,11 +388,19 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs)
* bdrv_inactivate_recurse()). * bdrv_inactivate_recurse()).
* There must not be any frozen bitmaps attached. * There must not be any frozen bitmaps attached.
* This function does not remove persistent bitmaps from the storage. * This function does not remove persistent bitmaps from the storage.
* Called with BQL taken.
*/ */
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs)
{ {
bdrv_do_release_matching_dirty_bitmap(bs, NULL, BdrvDirtyBitmap *bm, *next;
bdrv_dirty_bitmap_get_persistance);
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);
} }
/** /**
@ -442,18 +420,19 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
} }
} }
/* Called with BQL taken. */
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
{ {
bdrv_dirty_bitmap_lock(bitmap);
assert(!bdrv_dirty_bitmap_frozen(bitmap)); assert(!bdrv_dirty_bitmap_frozen(bitmap));
bitmap->disabled = true; bitmap->disabled = true;
bdrv_dirty_bitmap_unlock(bitmap);
} }
/* Called with BQL taken. */
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap) void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
{ {
assert(!bdrv_dirty_bitmap_frozen(bitmap)); bdrv_dirty_bitmap_lock(bitmap);
bitmap->disabled = false; bdrv_enable_dirty_bitmap_locked(bitmap);
bdrv_dirty_bitmap_unlock(bitmap);
} }
BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs)
@ -546,7 +525,62 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter)
int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter)
{ {
return hbitmap_iter_next(&iter->hbi); return hbitmap_iter_next(&iter->hbi, true);
}
/**
* Return the next consecutively dirty area in the dirty bitmap
* belonging to the given iterator @iter.
*
* @max_offset: Maximum value that may be returned for
* *offset + *bytes
* @offset: Will contain the start offset of the next dirty area
* @bytes: Will contain the length of the next dirty area
*
* Returns: True if a dirty area could be found before max_offset
* (which means that *offset and *bytes then contain valid
* values), false otherwise.
*
* Note that @iter is never advanced if false is returned. If an area
* is found (which means that true is returned), it will be advanced
* past that area.
*/
bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset,
uint64_t *offset, int *bytes)
{
uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap);
uint64_t gran_max_offset;
int64_t ret;
int size;
if (max_offset == iter->bitmap->size) {
/* If max_offset points to the image end, round it up by the
* bitmap granularity */
gran_max_offset = ROUND_UP(max_offset, granularity);
} else {
gran_max_offset = max_offset;
}
ret = hbitmap_iter_next(&iter->hbi, false);
if (ret < 0 || ret + granularity > gran_max_offset) {
return false;
}
*offset = ret;
size = 0;
assert(granularity <= INT_MAX);
do {
/* Advance iterator */
ret = hbitmap_iter_next(&iter->hbi, true);
size += granularity;
} while (ret + granularity <= gran_max_offset &&
hbitmap_iter_next(&iter->hbi, false) == ret + granularity &&
size <= INT_MAX - granularity);
*bytes = MIN(size, max_offset - *offset);
return true;
} }
/* Called within bdrv_dirty_bitmap_lock..unlock */ /* Called within bdrv_dirty_bitmap_lock..unlock */
@ -755,3 +789,21 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset)
{ {
return hbitmap_next_zero(bitmap->bitmap, offset); return hbitmap_next_zero(bitmap->bitmap, offset);
} }
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
Error **errp)
{
/* only bitmaps from one bds are supported */
assert(dest->mutex == src->mutex);
qemu_mutex_lock(dest->mutex);
assert(bdrv_dirty_bitmap_enabled(dest));
assert(!bdrv_dirty_bitmap_readonly(dest));
if (!hbitmap_merge(dest->bitmap, src->bitmap)) {
error_setg(errp, "Bitmaps are incompatible and can't be merged");
}
qemu_mutex_unlock(dest->mutex);
}

File diff suppressed because it is too large Load Diff

View File

@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
acb->aio_nbytes = count; acb->aio_nbytes = count;
acb->aio_offset = offset; acb->aio_offset = offset;
trace_paio_submit(acb, opaque, offset, count, type); trace_file_paio_submit(acb, opaque, offset, count, type);
pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); pool = aio_get_thread_pool(bdrv_get_aio_context(bs));
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
} }
@ -251,7 +251,11 @@ static void raw_probe_alignment(BlockDriverState *bs, Error **errp)
&dg.Geometry.BytesPerSector, &dg.Geometry.BytesPerSector,
&freeClusters, &totalClusters); &freeClusters, &totalClusters);
bs->bl.request_alignment = dg.Geometry.BytesPerSector; bs->bl.request_alignment = dg.Geometry.BytesPerSector;
return;
} }
/* XXX Does Windows support AIO on less than 512-byte alignment? */
bs->bl.request_alignment = 512;
} }
static void raw_parse_flags(int flags, bool use_aio, int *access_flags, static void raw_parse_flags(int flags, bool use_aio, int *access_flags,
@ -410,32 +414,32 @@ fail:
return ret; return ret;
} }
static BlockAIOCB *raw_aio_readv(BlockDriverState *bs, static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, uint64_t offset, uint64_t bytes,
BlockCompletionFunc *cb, void *opaque) QEMUIOVector *qiov, int flags,
BlockCompletionFunc *cb, void *opaque)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
if (s->aio) { if (s->aio) {
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
nb_sectors, cb, opaque, QEMU_AIO_READ); cb, opaque, QEMU_AIO_READ);
} else { } else {
return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, return paio_submit(bs, s->hfile, offset, qiov, bytes,
nb_sectors << BDRV_SECTOR_BITS,
cb, opaque, QEMU_AIO_READ); cb, opaque, QEMU_AIO_READ);
} }
} }
static BlockAIOCB *raw_aio_writev(BlockDriverState *bs, static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, uint64_t offset, uint64_t bytes,
BlockCompletionFunc *cb, void *opaque) QEMUIOVector *qiov, int flags,
BlockCompletionFunc *cb, void *opaque)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
if (s->aio) { if (s->aio) {
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov, return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
nb_sectors, cb, opaque, QEMU_AIO_WRITE); cb, opaque, QEMU_AIO_WRITE);
} else { } else {
return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov, return paio_submit(bs, s->hfile, offset, qiov, bytes,
nb_sectors << BDRV_SECTOR_BITS,
cb, opaque, QEMU_AIO_WRITE); cb, opaque, QEMU_AIO_WRITE);
} }
} }
@ -463,8 +467,8 @@ static void raw_close(BlockDriverState *bs)
} }
} }
static int raw_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp) PreallocMode prealloc, Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
LONG low, high; LONG low, high;
@ -632,11 +636,11 @@ BlockDriver bdrv_file = {
.bdrv_co_create_opts = raw_co_create_opts, .bdrv_co_create_opts = raw_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_aio_readv = raw_aio_readv, .bdrv_aio_preadv = raw_aio_preadv,
.bdrv_aio_writev = raw_aio_writev, .bdrv_aio_pwritev = raw_aio_pwritev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_truncate = raw_truncate, .bdrv_co_truncate = raw_co_truncate,
.bdrv_getlength = raw_getlength, .bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size .bdrv_get_allocated_file_size
= raw_get_allocated_file_size, = raw_get_allocated_file_size,
@ -708,6 +712,12 @@ static void hdev_parse_filename(const char *filename, QDict *options,
bdrv_parse_filename_strip_prefix(filename, "host_device:", options); bdrv_parse_filename_strip_prefix(filename, "host_device:", options);
} }
static void hdev_refresh_limits(BlockDriverState *bs, Error **errp)
{
/* XXX Does Windows support AIO on less than 512-byte alignment? */
bs->bl.request_alignment = 512;
}
static int hdev_open(BlockDriverState *bs, QDict *options, int flags, static int hdev_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp) Error **errp)
{ {
@ -793,9 +803,10 @@ static BlockDriver bdrv_host_device = {
.bdrv_probe_device = hdev_probe_device, .bdrv_probe_device = hdev_probe_device,
.bdrv_file_open = hdev_open, .bdrv_file_open = hdev_open,
.bdrv_close = raw_close, .bdrv_close = raw_close,
.bdrv_refresh_limits = hdev_refresh_limits,
.bdrv_aio_readv = raw_aio_readv, .bdrv_aio_preadv = raw_aio_preadv,
.bdrv_aio_writev = raw_aio_writev, .bdrv_aio_pwritev = raw_aio_pwritev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_detach_aio_context = raw_detach_aio_context, .bdrv_detach_aio_context = raw_detach_aio_context,

View File

@ -11,6 +11,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include <glusterfs/api/glfs.h> #include <glusterfs/api/glfs.h>
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
@ -650,7 +651,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
} }
gsconf = NULL; gsconf = NULL;
QDECREF(backing_options); qobject_unref(backing_options);
backing_options = NULL; backing_options = NULL;
g_free(str); g_free(str);
str = NULL; str = NULL;
@ -663,7 +664,7 @@ out:
qapi_free_SocketAddress(gsconf); qapi_free_SocketAddress(gsconf);
qemu_opts_del(opts); qemu_opts_del(opts);
g_free(str); g_free(str);
QDECREF(backing_options); qobject_unref(backing_options);
errno = EINVAL; errno = EINVAL;
return -errno; return -errno;
} }
@ -1176,8 +1177,10 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
return acb.ret; return acb.ret;
} }
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset, static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs,
PreallocMode prealloc, Error **errp) int64_t offset,
PreallocMode prealloc,
Error **errp)
{ {
BDRVGlusterState *s = bs->opaque; BDRVGlusterState *s = bs->opaque;
return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp); return qemu_gluster_do_truncate(s->fd, offset, prealloc, errp);
@ -1194,8 +1197,10 @@ static coroutine_fn int qemu_gluster_co_readv(BlockDriverState *bs,
static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs, static coroutine_fn int qemu_gluster_co_writev(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int nb_sectors, int nb_sectors,
QEMUIOVector *qiov) QEMUIOVector *qiov,
int flags)
{ {
assert(!flags);
return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1); return qemu_gluster_co_rw(bs, sector_num, nb_sectors, qiov, 1);
} }
@ -1321,7 +1326,7 @@ static int qemu_gluster_has_zero_init(BlockDriverState *bs)
* If @start is in a trailing hole or beyond EOF, return -ENXIO. * If @start is in a trailing hole or beyond EOF, return -ENXIO.
* If we can't find out, return a negative errno other than -ENXIO. * If we can't find out, return a negative errno other than -ENXIO.
* *
* (Shamefully copied from file-posix.c, only miniscule adaptions.) * (Shamefully copied from file-posix.c, only minuscule adaptions.)
*/ */
static int find_allocation(BlockDriverState *bs, off_t start, static int find_allocation(BlockDriverState *bs, off_t start,
off_t *data, off_t *hole) off_t *data, off_t *hole)
@ -1496,7 +1501,7 @@ static BlockDriver bdrv_gluster = {
.bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_co_create_opts = qemu_gluster_co_create_opts,
.bdrv_getlength = qemu_gluster_getlength, .bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
.bdrv_truncate = qemu_gluster_truncate, .bdrv_co_truncate = qemu_gluster_co_truncate,
.bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_readv = qemu_gluster_co_readv,
.bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_writev = qemu_gluster_co_writev,
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
@ -1525,7 +1530,7 @@ static BlockDriver bdrv_gluster_tcp = {
.bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_co_create_opts = qemu_gluster_co_create_opts,
.bdrv_getlength = qemu_gluster_getlength, .bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
.bdrv_truncate = qemu_gluster_truncate, .bdrv_co_truncate = qemu_gluster_co_truncate,
.bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_readv = qemu_gluster_co_readv,
.bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_writev = qemu_gluster_co_writev,
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
@ -1554,7 +1559,7 @@ static BlockDriver bdrv_gluster_unix = {
.bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_co_create_opts = qemu_gluster_co_create_opts,
.bdrv_getlength = qemu_gluster_getlength, .bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
.bdrv_truncate = qemu_gluster_truncate, .bdrv_co_truncate = qemu_gluster_co_truncate,
.bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_readv = qemu_gluster_co_readv,
.bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_writev = qemu_gluster_co_writev,
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
@ -1589,7 +1594,7 @@ static BlockDriver bdrv_gluster_rdma = {
.bdrv_co_create_opts = qemu_gluster_co_create_opts, .bdrv_co_create_opts = qemu_gluster_co_create_opts,
.bdrv_getlength = qemu_gluster_getlength, .bdrv_getlength = qemu_gluster_getlength,
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size, .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
.bdrv_truncate = qemu_gluster_truncate, .bdrv_co_truncate = qemu_gluster_co_truncate,
.bdrv_co_readv = qemu_gluster_co_readv, .bdrv_co_readv = qemu_gluster_co_readv,
.bdrv_co_writev = qemu_gluster_co_writev, .bdrv_co_writev = qemu_gluster_co_writev,
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk, .bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@
#include "qemu/bitops.h" #include "qemu/bitops.h"
#include "qemu/bitmap.h" #include "qemu/bitmap.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "scsi/constants.h" #include "scsi/constants.h"
#include "qemu/iov.h" #include "qemu/iov.h"
#include "qemu/option.h" #include "qemu/option.h"
@ -43,6 +44,7 @@
#include "qapi/qmp/qstring.h" #include "qapi/qmp/qstring.h"
#include "crypto/secret.h" #include "crypto/secret.h"
#include "scsi/utils.h" #include "scsi/utils.h"
#include "trace.h"
/* Conflict between scsi/utils.h and libiscsi! :( */ /* Conflict between scsi/utils.h and libiscsi! :( */
#define SCSI_XFER_NONE ISCSI_XFER_NONE #define SCSI_XFER_NONE ISCSI_XFER_NONE
@ -68,6 +70,7 @@ typedef struct IscsiLun {
QemuMutex mutex; QemuMutex mutex;
struct scsi_inquiry_logical_block_provisioning lbp; struct scsi_inquiry_logical_block_provisioning lbp;
struct scsi_inquiry_block_limits bl; struct scsi_inquiry_block_limits bl;
struct scsi_inquiry_device_designator *dd;
unsigned char *zeroblock; unsigned char *zeroblock;
/* The allocmap tracks which clusters (pages) on the iSCSI target are /* The allocmap tracks which clusters (pages) on the iSCSI target are
* allocated and which are not. In case a target returns zeros for * allocated and which are not. In case a target returns zeros for
@ -555,9 +558,20 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun,
offset / iscsilun->cluster_size) == size); offset / iscsilun->cluster_size) == size);
} }
static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask,
IscsiLun *iscsilun)
{
while (!iTask->complete) {
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
}
static int coroutine_fn static int coroutine_fn
iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors, iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
QEMUIOVector *iov, int flags) QEMUIOVector *iov, int flags)
{ {
IscsiLun *iscsilun = bs->opaque; IscsiLun *iscsilun = bs->opaque;
struct IscsiTask iTask; struct IscsiTask iTask;
@ -616,12 +630,7 @@ retry:
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov, scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
iov->niov); iov->niov);
#endif #endif
while (!iTask.complete) { iscsi_co_wait_for_task(&iTask, iscsilun);
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
if (iTask.task != NULL) { if (iTask.task != NULL) {
scsi_free_scsi_task(iTask.task); scsi_free_scsi_task(iTask.task);
@ -692,13 +701,7 @@ retry:
ret = -ENOMEM; ret = -ENOMEM;
goto out_unlock; goto out_unlock;
} }
iscsi_co_wait_for_task(&iTask, iscsilun);
while (!iTask.complete) {
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
if (iTask.do_retry) { if (iTask.do_retry) {
if (iTask.task != NULL) { if (iTask.task != NULL) {
@ -732,7 +735,7 @@ retry:
goto out_unlock; goto out_unlock;
} }
*pnum = lbasd->num_blocks * iscsilun->block_size; *pnum = (int64_t) lbasd->num_blocks * iscsilun->block_size;
if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED || if (lbasd->provisioning == SCSI_PROVISIONING_TYPE_DEALLOCATED ||
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) { lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
@ -862,13 +865,8 @@ retry:
#if LIBISCSI_API_VERSION < (20160603) #if LIBISCSI_API_VERSION < (20160603)
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov); scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
#endif #endif
while (!iTask.complete) {
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
iscsi_co_wait_for_task(&iTask, iscsilun);
if (iTask.task != NULL) { if (iTask.task != NULL) {
scsi_free_scsi_task(iTask.task); scsi_free_scsi_task(iTask.task);
iTask.task = NULL; iTask.task = NULL;
@ -905,12 +903,7 @@ retry:
return -ENOMEM; return -ENOMEM;
} }
while (!iTask.complete) { iscsi_co_wait_for_task(&iTask, iscsilun);
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
if (iTask.task != NULL) { if (iTask.task != NULL) {
scsi_free_scsi_task(iTask.task); scsi_free_scsi_task(iTask.task);
@ -1142,12 +1135,7 @@ retry:
goto out_unlock; goto out_unlock;
} }
while (!iTask.complete) { iscsi_co_wait_for_task(&iTask, iscsilun);
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
if (iTask.task != NULL) { if (iTask.task != NULL) {
scsi_free_scsi_task(iTask.task); scsi_free_scsi_task(iTask.task);
@ -1243,12 +1231,7 @@ retry:
return -ENOMEM; return -ENOMEM;
} }
while (!iTask.complete) { iscsi_co_wait_for_task(&iTask, iscsilun);
iscsi_set_events(iscsilun);
qemu_mutex_unlock(&iscsilun->mutex);
qemu_coroutine_yield();
qemu_mutex_lock(&iscsilun->mutex);
}
if (iTask.status == SCSI_STATUS_CHECK_CONDITION && if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST && iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
@ -1732,14 +1715,34 @@ static QemuOptsList runtime_opts = {
.name = "timeout", .name = "timeout",
.type = QEMU_OPT_NUMBER, .type = QEMU_OPT_NUMBER,
}, },
{
.name = "filename",
.type = QEMU_OPT_STRING,
},
{ /* end of list */ } { /* end of list */ }
}, },
}; };
static void iscsi_save_designator(IscsiLun *lun,
struct scsi_inquiry_device_identification *inq_di)
{
struct scsi_inquiry_device_designator *desig, *copy = NULL;
for (desig = inq_di->designators; desig; desig = desig->next) {
if (desig->association ||
desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) {
continue;
}
/* NAA works better than T10 vendor ID based designator. */
if (!copy || copy->designator_type < desig->designator_type) {
copy = desig;
}
}
if (copy) {
lun->dd = g_new(struct scsi_inquiry_device_designator, 1);
*lun->dd = *copy;
lun->dd->next = NULL;
lun->dd->designator = g_malloc(copy->designator_length);
memcpy(lun->dd->designator, copy->designator, copy->designator_length);
}
}
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
Error **errp) Error **errp)
{ {
@ -1751,27 +1754,12 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
char *initiator_name = NULL; char *initiator_name = NULL;
QemuOpts *opts; QemuOpts *opts;
Error *local_err = NULL; Error *local_err = NULL;
const char *transport_name, *portal, *target, *filename; const char *transport_name, *portal, *target;
#if LIBISCSI_API_VERSION >= (20160603) #if LIBISCSI_API_VERSION >= (20160603)
enum iscsi_transport_type transport; enum iscsi_transport_type transport;
#endif #endif
int i, ret = 0, timeout = 0, lun; int i, ret = 0, timeout = 0, lun;
/* If we are given a filename, parse the filename, with precedence given to
* filename encoded options */
filename = qdict_get_try_str(options, "filename");
if (filename) {
warn_report("'filename' option specified. "
"This is an unsupported option, and may be deprecated "
"in the future");
iscsi_parse_filename(filename, options, &local_err);
if (local_err) {
ret = -EINVAL;
error_propagate(errp, local_err);
goto exit;
}
}
opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
qemu_opts_absorb_qdict(opts, options, &local_err); qemu_opts_absorb_qdict(opts, options, &local_err);
if (local_err) { if (local_err) {
@ -1922,6 +1910,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
struct scsi_task *inq_task; struct scsi_task *inq_task;
struct scsi_inquiry_logical_block_provisioning *inq_lbp; struct scsi_inquiry_logical_block_provisioning *inq_lbp;
struct scsi_inquiry_block_limits *inq_bl; struct scsi_inquiry_block_limits *inq_bl;
struct scsi_inquiry_device_identification *inq_di;
switch (inq_vpd->pages[i]) { switch (inq_vpd->pages[i]) {
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING: case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1, inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
@ -1947,6 +1936,17 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
sizeof(struct scsi_inquiry_block_limits)); sizeof(struct scsi_inquiry_block_limits));
scsi_free_scsi_task(inq_task); scsi_free_scsi_task(inq_task);
break; break;
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION,
(void **) &inq_di, errp);
if (inq_task == NULL) {
ret = -EINVAL;
goto out;
}
iscsi_save_designator(iscsilun, inq_di);
scsi_free_scsi_task(inq_task);
break;
default: default:
break; break;
} }
@ -1989,7 +1989,7 @@ out:
} }
memset(iscsilun, 0, sizeof(IscsiLun)); memset(iscsilun, 0, sizeof(IscsiLun));
} }
exit:
return ret; return ret;
} }
@ -2003,6 +2003,10 @@ static void iscsi_close(BlockDriverState *bs)
iscsi_logout_sync(iscsi); iscsi_logout_sync(iscsi);
} }
iscsi_destroy_context(iscsi); iscsi_destroy_context(iscsi);
if (iscsilun->dd) {
g_free(iscsilun->dd->designator);
g_free(iscsilun->dd);
}
g_free(iscsilun->zeroblock); g_free(iscsilun->zeroblock);
iscsi_allocmap_free(iscsilun); iscsi_allocmap_free(iscsilun);
qemu_mutex_destroy(&iscsilun->mutex); qemu_mutex_destroy(&iscsilun->mutex);
@ -2082,8 +2086,8 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state)
} }
} }
static int iscsi_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp) PreallocMode prealloc, Error **errp)
{ {
IscsiLun *iscsilun = bs->opaque; IscsiLun *iscsilun = bs->opaque;
Error *local_err = NULL; Error *local_err = NULL;
@ -2143,7 +2147,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt
} else { } else {
ret = iscsi_open(bs, bs_options, 0, NULL); ret = iscsi_open(bs, bs_options, 0, NULL);
} }
QDECREF(bs_options); qobject_unref(bs_options);
if (ret != 0) { if (ret != 0) {
goto out; goto out;
@ -2184,6 +2188,226 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
iscsi_allocmap_invalidate(iscsilun); iscsi_allocmap_invalidate(iscsilun);
} }
static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs,
BdrvChild *src,
uint64_t src_offset,
BdrvChild *dst,
uint64_t dst_offset,
uint64_t bytes,
BdrvRequestFlags read_flags,
BdrvRequestFlags write_flags)
{
return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes,
read_flags, write_flags);
}
static struct scsi_task *iscsi_xcopy_task(int param_len)
{
struct scsi_task *task;
task = g_new0(struct scsi_task, 1);
task->cdb[0] = EXTENDED_COPY;
task->cdb[10] = (param_len >> 24) & 0xFF;
task->cdb[11] = (param_len >> 16) & 0xFF;
task->cdb[12] = (param_len >> 8) & 0xFF;
task->cdb[13] = param_len & 0xFF;
task->cdb_size = 16;
task->xfer_dir = SCSI_XFER_WRITE;
task->expxferlen = param_len;
return task;
}
static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun)
{
struct scsi_inquiry_device_designator *dd = lun->dd;
memset(desc, 0, 32);
desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */
desc[4] = dd->code_set;
desc[5] = (dd->designator_type & 0xF)
| ((dd->association & 3) << 4);
desc[7] = dd->designator_length;
memcpy(desc + 8, dd->designator, MIN(dd->designator_length, 20));
desc[28] = 0;
desc[29] = (lun->block_size >> 16) & 0xFF;
desc[30] = (lun->block_size >> 8) & 0xFF;
desc[31] = lun->block_size & 0xFF;
}
static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index,
int dst_index)
{
hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */
hdr[1] = ((dc << 1) | cat) & 0xFF;
hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF;
/* don't account for the first 4 bytes in descriptor header*/
hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF;
hdr[4] = (src_index >> 8) & 0xFF;
hdr[5] = src_index & 0xFF;
hdr[6] = (dst_index >> 8) & 0xFF;
hdr[7] = dst_index & 0xFF;
}
static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat,
int src_index, int dst_index, int num_blks,
uint64_t src_lba, uint64_t dst_lba)
{
iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index);
/* The caller should verify the request size */
assert(num_blks < 65536);
desc[10] = (num_blks >> 8) & 0xFF;
desc[11] = num_blks & 0xFF;
desc[12] = (src_lba >> 56) & 0xFF;
desc[13] = (src_lba >> 48) & 0xFF;
desc[14] = (src_lba >> 40) & 0xFF;
desc[15] = (src_lba >> 32) & 0xFF;
desc[16] = (src_lba >> 24) & 0xFF;
desc[17] = (src_lba >> 16) & 0xFF;
desc[18] = (src_lba >> 8) & 0xFF;
desc[19] = src_lba & 0xFF;
desc[20] = (dst_lba >> 56) & 0xFF;
desc[21] = (dst_lba >> 48) & 0xFF;
desc[22] = (dst_lba >> 40) & 0xFF;
desc[23] = (dst_lba >> 32) & 0xFF;
desc[24] = (dst_lba >> 24) & 0xFF;
desc[25] = (dst_lba >> 16) & 0xFF;
desc[26] = (dst_lba >> 8) & 0xFF;
desc[27] = dst_lba & 0xFF;
}
static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str,
int list_id_usage, int prio,
int tgt_desc_len,
int seg_desc_len, int inline_data_len)
{
buf[0] = list_id;
buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7);
buf[2] = (tgt_desc_len >> 8) & 0xFF;
buf[3] = tgt_desc_len & 0xFF;
buf[8] = (seg_desc_len >> 24) & 0xFF;
buf[9] = (seg_desc_len >> 16) & 0xFF;
buf[10] = (seg_desc_len >> 8) & 0xFF;
buf[11] = seg_desc_len & 0xFF;
buf[12] = (inline_data_len >> 24) & 0xFF;
buf[13] = (inline_data_len >> 16) & 0xFF;
buf[14] = (inline_data_len >> 8) & 0xFF;
buf[15] = inline_data_len & 0xFF;
}
static void iscsi_xcopy_data(struct iscsi_data *data,
IscsiLun *src, int64_t src_lba,
IscsiLun *dst, int64_t dst_lba,
uint16_t num_blocks)
{
uint8_t *buf;
const int src_offset = XCOPY_DESC_OFFSET;
const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE;
const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE;
data->size = XCOPY_DESC_OFFSET +
IDENT_DESCR_TGT_DESCR_SIZE * 2 +
XCOPY_BLK2BLK_SEG_DESC_SIZE;
data->data = g_malloc0(data->size);
buf = data->data;
/* Initialise the parameter list header */
iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */,
0, 2 * IDENT_DESCR_TGT_DESCR_SIZE,
XCOPY_BLK2BLK_SEG_DESC_SIZE,
0);
/* Initialise CSCD list with one src + one dst descriptor */
iscsi_populate_target_desc(&buf[src_offset], src);
iscsi_populate_target_desc(&buf[dst_offset], dst);
/* Initialise one segment descriptor */
iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks,
src_lba, dst_lba);
}
static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs,
BdrvChild *src,
uint64_t src_offset,
BdrvChild *dst,
uint64_t dst_offset,
uint64_t bytes,
BdrvRequestFlags read_flags,
BdrvRequestFlags write_flags)
{
IscsiLun *dst_lun = dst->bs->opaque;
IscsiLun *src_lun;
struct IscsiTask iscsi_task;
struct iscsi_data data;
int r = 0;
int block_size;
if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) {
return -ENOTSUP;
}
src_lun = src->bs->opaque;
if (!src_lun->dd || !dst_lun->dd) {
return -ENOTSUP;
}
if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) {
return -ENOTSUP;
}
if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) {
return -ENOTSUP;
}
if (dst_lun->block_size != src_lun->block_size ||
!dst_lun->block_size) {
return -ENOTSUP;
}
block_size = dst_lun->block_size;
if (bytes / block_size > 65535) {
return -ENOTSUP;
}
iscsi_xcopy_data(&data,
src_lun, src_offset / block_size,
dst_lun, dst_offset / block_size,
bytes / block_size);
iscsi_co_init_iscsitask(dst_lun, &iscsi_task);
qemu_mutex_lock(&dst_lun->mutex);
iscsi_task.task = iscsi_xcopy_task(data.size);
retry:
if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun,
iscsi_task.task, iscsi_co_generic_cb,
&data,
&iscsi_task) != 0) {
r = -EIO;
goto out_unlock;
}
iscsi_co_wait_for_task(&iscsi_task, dst_lun);
if (iscsi_task.do_retry) {
iscsi_task.complete = 0;
goto retry;
}
if (iscsi_task.status != SCSI_STATUS_GOOD) {
r = iscsi_task.err_code;
goto out_unlock;
}
out_unlock:
trace_iscsi_xcopy(src_lun, src_offset, dst_lun, dst_offset, bytes, r);
g_free(iscsi_task.task);
qemu_mutex_unlock(&dst_lun->mutex);
g_free(iscsi_task.err_str);
return r;
}
static QemuOptsList iscsi_create_opts = { static QemuOptsList iscsi_create_opts = {
.name = "iscsi-create-opts", .name = "iscsi-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head), .head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
@ -2213,14 +2437,16 @@ static BlockDriver bdrv_iscsi = {
.bdrv_getlength = iscsi_getlength, .bdrv_getlength = iscsi_getlength,
.bdrv_get_info = iscsi_get_info, .bdrv_get_info = iscsi_get_info,
.bdrv_truncate = iscsi_truncate, .bdrv_co_truncate = iscsi_co_truncate,
.bdrv_refresh_limits = iscsi_refresh_limits, .bdrv_refresh_limits = iscsi_refresh_limits,
.bdrv_co_block_status = iscsi_co_block_status, .bdrv_co_block_status = iscsi_co_block_status,
.bdrv_co_pdiscard = iscsi_co_pdiscard, .bdrv_co_pdiscard = iscsi_co_pdiscard,
.bdrv_co_copy_range_from = iscsi_co_copy_range_from,
.bdrv_co_copy_range_to = iscsi_co_copy_range_to,
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
.bdrv_co_readv = iscsi_co_readv, .bdrv_co_readv = iscsi_co_readv,
.bdrv_co_writev_flags = iscsi_co_writev_flags, .bdrv_co_writev = iscsi_co_writev,
.bdrv_co_flush_to_disk = iscsi_co_flush, .bdrv_co_flush_to_disk = iscsi_co_flush,
#ifdef __linux__ #ifdef __linux__
@ -2248,14 +2474,16 @@ static BlockDriver bdrv_iser = {
.bdrv_getlength = iscsi_getlength, .bdrv_getlength = iscsi_getlength,
.bdrv_get_info = iscsi_get_info, .bdrv_get_info = iscsi_get_info,
.bdrv_truncate = iscsi_truncate, .bdrv_co_truncate = iscsi_co_truncate,
.bdrv_refresh_limits = iscsi_refresh_limits, .bdrv_refresh_limits = iscsi_refresh_limits,
.bdrv_co_block_status = iscsi_co_block_status, .bdrv_co_block_status = iscsi_co_block_status,
.bdrv_co_pdiscard = iscsi_co_pdiscard, .bdrv_co_pdiscard = iscsi_co_pdiscard,
.bdrv_co_copy_range_from = iscsi_co_copy_range_from,
.bdrv_co_copy_range_to = iscsi_co_copy_range_to,
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
.bdrv_co_readv = iscsi_co_readv, .bdrv_co_readv = iscsi_co_readv,
.bdrv_co_writev_flags = iscsi_co_writev_flags, .bdrv_co_writev = iscsi_co_writev,
.bdrv_co_flush_to_disk = iscsi_co_flush, .bdrv_co_flush_to_disk = iscsi_co_flush,
#ifdef __linux__ #ifdef __linux__

View File

@ -15,6 +15,7 @@
#include "block/raw-aio.h" #include "block/raw-aio.h"
#include "qemu/event_notifier.h" #include "qemu/event_notifier.h"
#include "qemu/coroutine.h" #include "qemu/coroutine.h"
#include "qapi/error.h"
#include <libaio.h> #include <libaio.h>
@ -470,16 +471,21 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
qemu_laio_poll_cb); qemu_laio_poll_cb);
} }
LinuxAioState *laio_init(void) LinuxAioState *laio_init(Error **errp)
{ {
int rc;
LinuxAioState *s; LinuxAioState *s;
s = g_malloc0(sizeof(*s)); s = g_malloc0(sizeof(*s));
if (event_notifier_init(&s->e, false) < 0) { rc = event_notifier_init(&s->e, false);
if (rc < 0) {
error_setg_errno(errp, -rc, "failed to to initialize event notifier");
goto out_free_state; goto out_free_state;
} }
if (io_setup(MAX_EVENTS, &s->ctx) != 0) { rc = io_setup(MAX_EVENTS, &s->ctx);
if (rc < 0) {
error_setg_errno(errp, -rc, "failed to create linux AIO context");
goto out_close_efd; goto out_close_efd;
} }

File diff suppressed because it is too large Load Diff

View File

@ -259,14 +259,18 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
if (extent->length == 0 || if (extent->length == 0 ||
(client->info.min_block && !QEMU_IS_ALIGNED(extent->length, (client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
client->info.min_block)) || client->info.min_block))) {
extent->length > orig_length)
{
error_setg(errp, "Protocol error: server sent status chunk with " error_setg(errp, "Protocol error: server sent status chunk with "
"invalid length"); "invalid length");
return -EINVAL; return -EINVAL;
} }
/* The server is allowed to send us extra information on the final
* extent; just clamp it to the length we requested. */
if (extent->length > orig_length) {
extent->length = orig_length;
}
return 0; return 0;
} }
@ -966,6 +970,7 @@ int nbd_client_init(BlockDriverState *bs,
const char *export, const char *export,
QCryptoTLSCreds *tlscreds, QCryptoTLSCreds *tlscreds,
const char *hostname, const char *hostname,
const char *x_dirty_bitmap,
Error **errp) Error **errp)
{ {
NBDClientSession *client = nbd_get_client_session(bs); NBDClientSession *client = nbd_get_client_session(bs);
@ -978,9 +983,11 @@ int nbd_client_init(BlockDriverState *bs,
client->info.request_sizes = true; client->info.request_sizes = true;
client->info.structured_reply = true; client->info.structured_reply = true;
client->info.base_allocation = true; client->info.base_allocation = true;
client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
tlscreds, hostname, tlscreds, hostname,
&client->ioc, &client->info, errp); &client->ioc, &client->info, errp);
g_free(client->info.x_dirty_bitmap);
if (ret < 0) { if (ret < 0) {
logout("Failed to negotiate with the NBD server\n"); logout("Failed to negotiate with the NBD server\n");
return ret; return ret;

View File

@ -45,6 +45,7 @@ int nbd_client_init(BlockDriverState *bs,
const char *export_name, const char *export_name,
QCryptoTLSCreds *tlscreds, QCryptoTLSCreds *tlscreds,
const char *hostname, const char *hostname,
const char *x_dirty_bitmap,
Error **errp); Error **errp);
void nbd_client_close(BlockDriverState *bs); void nbd_client_close(BlockDriverState *bs);

View File

@ -27,7 +27,8 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "block/nbd-client.h" #include "nbd-client.h"
#include "block/qdict.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/uri.h" #include "qemu/uri.h"
#include "block/block_int.h" #include "block/block_int.h"
@ -108,7 +109,7 @@ static int nbd_parse_uri(const char *filename, QDict *options)
/* strip braces from literal IPv6 address */ /* strip braces from literal IPv6 address */
if (uri->server[0] == '[') { if (uri->server[0] == '[') {
host = qstring_from_substr(uri->server, 1, host = qstring_from_substr(uri->server, 1,
strlen(uri->server) - 2); strlen(uri->server) - 1);
} else { } else {
host = qstring_from_str(uri->server); host = qstring_from_str(uri->server);
} }
@ -262,7 +263,6 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
{ {
SocketAddress *saddr = NULL; SocketAddress *saddr = NULL;
QDict *addr = NULL; QDict *addr = NULL;
QObject *crumpled_addr = NULL;
Visitor *iv = NULL; Visitor *iv = NULL;
Error *local_err = NULL; Error *local_err = NULL;
@ -272,20 +272,11 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
goto done; goto done;
} }
crumpled_addr = qdict_crumple(addr, errp); iv = qobject_input_visitor_new_flat_confused(addr, errp);
if (!crumpled_addr) { if (!iv) {
goto done; goto done;
} }
/*
* FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive
* server.type=inet. .to doesn't matter, it's ignored anyway.
* That's because when @options come from -blockdev or
* blockdev_add, members are typed according to the QAPI schema,
* but when they come from -drive, they're all QString. The
* visitor expects the former.
*/
iv = qobject_input_visitor_new(crumpled_addr);
visit_type_SocketAddress(iv, NULL, &saddr, &local_err); visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -293,8 +284,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
} }
done: done:
QDECREF(addr); qobject_unref(addr);
qobject_decref(crumpled_addr);
visit_free(iv); visit_free(iv);
return saddr; return saddr;
} }
@ -388,6 +378,12 @@ static QemuOptsList nbd_runtime_opts = {
.type = QEMU_OPT_STRING, .type = QEMU_OPT_STRING,
.help = "ID of the TLS credentials to use", .help = "ID of the TLS credentials to use",
}, },
{
.name = "x-dirty-bitmap",
.type = QEMU_OPT_STRING,
.help = "experimental: expose named dirty bitmap in place of "
"block status",
},
{ /* end of list */ } { /* end of list */ }
}, },
}; };
@ -448,8 +444,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
} }
/* NBD handshake */ /* NBD handshake */
ret = nbd_client_init(bs, sioc, s->export, ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname,
tlscreds, hostname, errp); qemu_opt_get(opts, "x-dirty-bitmap"), errp);
error: error:
if (sioc) { if (sioc) {
object_unref(OBJECT(sioc)); object_unref(OBJECT(sioc));

View File

@ -29,6 +29,7 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "trace.h" #include "trace.h"
#include "qemu/iov.h" #include "qemu/iov.h"
#include "qemu/option.h" #include "qemu/option.h"
@ -555,24 +556,29 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
Error **errp) Error **errp)
{ {
BlockdevOptionsNfs *opts = NULL; BlockdevOptionsNfs *opts = NULL;
QObject *crumpled = NULL;
Visitor *v; Visitor *v;
const QDictEntry *e;
Error *local_err = NULL; Error *local_err = NULL;
crumpled = qdict_crumple(options, errp); v = qobject_input_visitor_new_flat_confused(options, errp);
if (crumpled == NULL) { if (!v) {
return NULL; return NULL;
} }
v = qobject_input_visitor_new_keyval(crumpled);
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err); visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
visit_free(v); visit_free(v);
qobject_decref(crumpled);
if (local_err) { if (local_err) {
error_propagate(errp, local_err);
return NULL; return NULL;
} }
/* Remove the processed options from the QDict (the visitor processes
* _all_ options in the QDict) */
while ((e = qdict_first(options))) {
qdict_del(options, e->key);
}
return opts; return opts;
} }
@ -683,7 +689,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
ret = 0; ret = 0;
out: out:
QDECREF(options); qobject_unref(options);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;
} }
@ -737,8 +743,9 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
return (task.ret < 0 ? task.ret : st.st_blocks * 512); return (task.ret < 0 ? task.ret : st.st_blocks * 512);
} }
static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn
PreallocMode prealloc, Error **errp) nfs_file_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{ {
NFSClient *client = bs->opaque; NFSClient *client = bs->opaque;
int ret; int ret;
@ -867,7 +874,7 @@ static BlockDriver bdrv_nfs = {
.bdrv_has_zero_init = nfs_has_zero_init, .bdrv_has_zero_init = nfs_has_zero_init,
.bdrv_get_allocated_file_size = nfs_get_allocated_file_size, .bdrv_get_allocated_file_size = nfs_get_allocated_file_size,
.bdrv_truncate = nfs_file_truncate, .bdrv_co_truncate = nfs_file_co_truncate,
.bdrv_file_open = nfs_file_open, .bdrv_file_open = nfs_file_open,
.bdrv_close = nfs_file_close, .bdrv_close = nfs_file_close,

View File

@ -93,6 +93,7 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
} }
s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false); s->read_zeroes = qemu_opt_get_bool(opts, NULL_OPT_ZEROES, false);
qemu_opts_del(opts); qemu_opts_del(opts);
bs->supported_write_flags = BDRV_REQ_FUA;
return ret; return ret;
} }
@ -116,22 +117,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
return 0; return 0;
} }
static coroutine_fn int null_co_readv(BlockDriverState *bs, static coroutine_fn int null_co_preadv(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov) QEMUIOVector *qiov, int flags)
{ {
BDRVNullState *s = bs->opaque; BDRVNullState *s = bs->opaque;
if (s->read_zeroes) { if (s->read_zeroes) {
qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); qemu_iovec_memset(qiov, 0, 0, bytes);
} }
return null_co_common(bs); return null_co_common(bs);
} }
static coroutine_fn int null_co_writev(BlockDriverState *bs, static coroutine_fn int null_co_pwritev(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov) QEMUIOVector *qiov, int flags)
{ {
return null_co_common(bs); return null_co_common(bs);
} }
@ -186,26 +187,26 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
return &acb->common; return &acb->common;
} }
static BlockAIOCB *null_aio_readv(BlockDriverState *bs, static BlockAIOCB *null_aio_preadv(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, uint64_t offset, uint64_t bytes,
int nb_sectors, QEMUIOVector *qiov, int flags,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,
void *opaque) void *opaque)
{ {
BDRVNullState *s = bs->opaque; BDRVNullState *s = bs->opaque;
if (s->read_zeroes) { if (s->read_zeroes) {
qemu_iovec_memset(qiov, 0, 0, nb_sectors * BDRV_SECTOR_SIZE); qemu_iovec_memset(qiov, 0, 0, bytes);
} }
return null_aio_common(bs, cb, opaque); return null_aio_common(bs, cb, opaque);
} }
static BlockAIOCB *null_aio_writev(BlockDriverState *bs, static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, uint64_t offset, uint64_t bytes,
int nb_sectors, QEMUIOVector *qiov, int flags,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,
void *opaque) void *opaque)
{ {
return null_aio_common(bs, cb, opaque); return null_aio_common(bs, cb, opaque);
} }
@ -244,7 +245,6 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs,
static void null_refresh_filename(BlockDriverState *bs, QDict *opts) static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
{ {
QINCREF(opts);
qdict_del(opts, "filename"); qdict_del(opts, "filename");
if (!qdict_size(opts)) { if (!qdict_size(opts)) {
@ -253,7 +253,7 @@ static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
} }
qdict_put_str(opts, "driver", bs->drv->format_name); qdict_put_str(opts, "driver", bs->drv->format_name);
bs->full_open_options = opts; bs->full_open_options = qobject_ref(opts);
} }
static BlockDriver bdrv_null_co = { static BlockDriver bdrv_null_co = {
@ -266,8 +266,8 @@ static BlockDriver bdrv_null_co = {
.bdrv_close = null_close, .bdrv_close = null_close,
.bdrv_getlength = null_getlength, .bdrv_getlength = null_getlength,
.bdrv_co_readv = null_co_readv, .bdrv_co_preadv = null_co_preadv,
.bdrv_co_writev = null_co_writev, .bdrv_co_pwritev = null_co_pwritev,
.bdrv_co_flush_to_disk = null_co_flush, .bdrv_co_flush_to_disk = null_co_flush,
.bdrv_reopen_prepare = null_reopen_prepare, .bdrv_reopen_prepare = null_reopen_prepare,
@ -286,8 +286,8 @@ static BlockDriver bdrv_null_aio = {
.bdrv_close = null_close, .bdrv_close = null_close,
.bdrv_getlength = null_getlength, .bdrv_getlength = null_getlength,
.bdrv_aio_readv = null_aio_readv, .bdrv_aio_preadv = null_aio_preadv,
.bdrv_aio_writev = null_aio_writev, .bdrv_aio_pwritev = null_aio_pwritev,
.bdrv_aio_flush = null_aio_flush, .bdrv_aio_flush = null_aio_flush,
.bdrv_reopen_prepare = null_reopen_prepare, .bdrv_reopen_prepare = null_reopen_prepare,

View File

@ -1073,7 +1073,6 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
{ {
QINCREF(opts);
qdict_del(opts, "filename"); qdict_del(opts, "filename");
if (!qdict_size(opts)) { if (!qdict_size(opts)) {
@ -1082,7 +1081,7 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
} }
qdict_put_str(opts, "driver", bs->drv->format_name); qdict_put_str(opts, "driver", bs->drv->format_name);
bs->full_open_options = opts; bs->full_open_options = qobject_ref(opts);
} }
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)

View File

@ -31,6 +31,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
@ -226,14 +227,15 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
}; };
qemu_iovec_init_external(&qiov, &iov, 1); qemu_iovec_init_external(&qiov, &iov, 1);
ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors, ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
&qiov); nb_cow_bytes, &qiov, 0);
if (ret < 0) { if (ret < 0) {
qemu_vfree(iov.iov_base); qemu_vfree(iov.iov_base);
return ret; return ret;
} }
ret = bdrv_co_writev(bs->file, s->data_end, nb_cow_sectors, &qiov); ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE,
nb_cow_bytes, &qiov, 0);
qemu_vfree(iov.iov_base); qemu_vfree(iov.iov_base);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@ -311,13 +313,15 @@ static int coroutine_fn parallels_co_block_status(BlockDriverState *bs,
} }
static coroutine_fn int parallels_co_writev(BlockDriverState *bs, static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) int64_t sector_num, int nb_sectors,
QEMUIOVector *qiov, int flags)
{ {
BDRVParallelsState *s = bs->opaque; BDRVParallelsState *s = bs->opaque;
uint64_t bytes_done = 0; uint64_t bytes_done = 0;
QEMUIOVector hd_qiov; QEMUIOVector hd_qiov;
int ret = 0; int ret = 0;
assert(!flags);
qemu_iovec_init(&hd_qiov, qiov->niov); qemu_iovec_init(&hd_qiov, qiov->niov);
while (nb_sectors > 0) { while (nb_sectors > 0) {
@ -337,7 +341,8 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
qemu_iovec_reset(&hd_qiov); qemu_iovec_reset(&hd_qiov);
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes);
ret = bdrv_co_writev(bs->file, position, n, &hd_qiov); ret = bdrv_co_pwritev(bs->file, position * BDRV_SECTOR_SIZE, nbytes,
&hd_qiov, 0);
if (ret < 0) { if (ret < 0) {
break; break;
} }
@ -376,7 +381,8 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
if (position < 0) { if (position < 0) {
if (bs->backing) { if (bs->backing) {
ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); ret = bdrv_co_preadv(bs->backing, sector_num * BDRV_SECTOR_SIZE,
nbytes, &hd_qiov, 0);
if (ret < 0) { if (ret < 0) {
break; break;
} }
@ -384,7 +390,8 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
qemu_iovec_memset(&hd_qiov, 0, 0, nbytes); qemu_iovec_memset(&hd_qiov, 0, 0, nbytes);
} }
} else { } else {
ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); ret = bdrv_co_preadv(bs->file, position * BDRV_SECTOR_SIZE, nbytes,
&hd_qiov, 0);
if (ret < 0) { if (ret < 0) {
break; break;
} }
@ -613,8 +620,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
BlockdevCreateOptions *create_options = NULL; BlockdevCreateOptions *create_options = NULL;
Error *local_err = NULL; Error *local_err = NULL;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
QDict *qdict = NULL; QDict *qdict;
QObject *qobj;
Visitor *v; Visitor *v;
int ret; int ret;
@ -650,15 +656,12 @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
qdict_put_str(qdict, "driver", "parallels"); qdict_put_str(qdict, "driver", "parallels");
qdict_put_str(qdict, "file", bs->node_name); qdict_put_str(qdict, "file", bs->node_name);
qobj = qdict_crumple(qdict, errp); v = qobject_input_visitor_new_flat_confused(qdict, errp);
QDECREF(qdict); if (!v) {
qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL; ret = -EINVAL;
goto done; goto done;
} }
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
visit_free(v); visit_free(v);
@ -682,7 +685,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
ret = 0; ret = 0;
done: done:
QDECREF(qdict); qobject_unref(qdict);
bdrv_unref(bs); bdrv_unref(bs);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;

View File

@ -593,15 +593,29 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
p_next = &info->next; p_next = &info->next;
} }
} else { } else {
for (blk = blk_next(NULL); blk; blk = blk_next(blk)) { for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) {
BlockStatsList *info = g_malloc0(sizeof(*info)); BlockStatsList *info = g_malloc0(sizeof(*info));
AioContext *ctx = blk_get_aio_context(blk); AioContext *ctx = blk_get_aio_context(blk);
BlockStats *s; BlockStats *s;
char *qdev;
if (!*blk_name(blk) && !blk_get_attached_dev(blk)) {
continue;
}
aio_context_acquire(ctx); aio_context_acquire(ctx);
s = bdrv_query_bds_stats(blk_bs(blk), true); s = bdrv_query_bds_stats(blk_bs(blk), true);
s->has_device = true; s->has_device = true;
s->device = g_strdup(blk_name(blk)); s->device = g_strdup(blk_name(blk));
qdev = blk_get_attached_dev_id(blk);
if (qdev && *qdev) {
s->has_qdev = true;
s->qdev = qdev;
} else {
g_free(qdev);
}
bdrv_query_blk_stats(s->stats, blk); bdrv_query_blk_stats(s->stats, blk);
aio_context_release(ctx); aio_context_release(ctx);
@ -773,7 +787,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
visit_complete(v, &obj); visit_complete(v, &obj);
data = qdict_get(qobject_to(QDict, obj), "data"); data = qdict_get(qobject_to(QDict, obj), "data");
dump_qobject(func_fprintf, f, 1, data); dump_qobject(func_fprintf, f, 1, data);
qobject_decref(obj); qobject_unref(obj);
visit_free(v); visit_free(v);
} }

View File

@ -26,6 +26,7 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
@ -37,7 +38,7 @@
#include "qapi/qapi-visit-block-core.h" #include "qapi/qapi-visit-block-core.h"
#include "crypto/block.h" #include "crypto/block.h"
#include "migration/blocker.h" #include "migration/blocker.h"
#include "block/crypto.h" #include "crypto.h"
/**************************************************************/ /**************************************************************/
/* QEMU COW block driver with compression and encryption support */ /* QEMU COW block driver with compression and encryption support */
@ -69,7 +70,6 @@ typedef struct QCowHeader {
typedef struct BDRVQcowState { typedef struct BDRVQcowState {
int cluster_bits; int cluster_bits;
int cluster_size; int cluster_size;
int cluster_sectors;
int l2_bits; int l2_bits;
int l2_size; int l2_size;
unsigned int l1_size; unsigned int l1_size;
@ -202,9 +202,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
qdict_del(encryptopts, "format"); qdict_put_str(encryptopts, "format", "qcow");
crypto_opts = block_crypto_open_opts_init( crypto_opts = block_crypto_open_opts_init(encryptopts, errp);
Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
if (!crypto_opts) { if (!crypto_opts) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
@ -235,7 +234,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
} }
s->cluster_bits = header.cluster_bits; s->cluster_bits = header.cluster_bits;
s->cluster_size = 1 << s->cluster_bits; s->cluster_size = 1 << s->cluster_bits;
s->cluster_sectors = 1 << (s->cluster_bits - 9);
s->l2_bits = header.l2_bits; s->l2_bits = header.l2_bits;
s->l2_size = 1 << s->l2_bits; s->l2_size = 1 << s->l2_bits;
bs->total_sectors = header.size / 512; bs->total_sectors = header.size / 512;
@ -315,7 +313,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} }
QDECREF(encryptopts); qobject_unref(encryptopts);
qapi_free_QCryptoBlockOpenOptions(crypto_opts); qapi_free_QCryptoBlockOpenOptions(crypto_opts);
qemu_co_mutex_init(&s->lock); qemu_co_mutex_init(&s->lock);
return 0; return 0;
@ -326,7 +324,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
g_free(s->cluster_cache); g_free(s->cluster_cache);
g_free(s->cluster_data); g_free(s->cluster_data);
qcrypto_block_free(s->crypto); qcrypto_block_free(s->crypto);
QDECREF(encryptopts); qobject_unref(encryptopts);
qapi_free_QCryptoBlockOpenOptions(crypto_opts); qapi_free_QCryptoBlockOpenOptions(crypto_opts);
return ret; return ret;
} }
@ -345,8 +343,8 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
* *
* 0 to not allocate. * 0 to not allocate.
* *
* 1 to allocate a normal cluster (for sector indexes 'n_start' to * 1 to allocate a normal cluster (for sector-aligned byte offsets 'n_start'
* 'n_end') * to 'n_end' within the cluster)
* *
* 2 to allocate a compressed cluster of size * 2 to allocate a compressed cluster of size
* 'compressed_size'. 'compressed_size' must be > 0 and < * 'compressed_size'. 'compressed_size' must be > 0 and <
@ -440,9 +438,10 @@ static int get_cluster_offset(BlockDriverState *bs,
if (!allocate) if (!allocate)
return 0; return 0;
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE));
/* allocate a new cluster */ /* allocate a new cluster */
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) && if ((cluster_offset & QCOW_OFLAG_COMPRESSED) &&
(n_end - n_start) < s->cluster_sectors) { (n_end - n_start) < s->cluster_size) {
/* if the cluster is already compressed, we must /* if the cluster is already compressed, we must
decompress it in the case it is not completely decompress it in the case it is not completely
overwritten */ overwritten */
@ -480,16 +479,15 @@ static int get_cluster_offset(BlockDriverState *bs,
/* if encrypted, we must initialize the cluster /* if encrypted, we must initialize the cluster
content which won't be written */ content which won't be written */
if (bs->encrypted && if (bs->encrypted &&
(n_end - n_start) < s->cluster_sectors) { (n_end - n_start) < s->cluster_size) {
uint64_t start_sect; uint64_t start_offset;
assert(s->crypto); assert(s->crypto);
start_sect = (offset & ~(s->cluster_size - 1)) >> 9; start_offset = offset & ~(s->cluster_size - 1);
for(i = 0; i < s->cluster_sectors; i++) { for (i = 0; i < s->cluster_size; i += BDRV_SECTOR_SIZE) {
if (i < n_start || i >= n_end) { if (i < n_start || i >= n_end) {
memset(s->cluster_data, 0x00, 512); memset(s->cluster_data, 0x00, BDRV_SECTOR_SIZE);
if (qcrypto_block_encrypt(s->crypto, if (qcrypto_block_encrypt(s->crypto,
(start_sect + i) * start_offset + i,
BDRV_SECTOR_SIZE,
s->cluster_data, s->cluster_data,
BDRV_SECTOR_SIZE, BDRV_SECTOR_SIZE,
NULL) < 0) { NULL) < 0) {
@ -497,8 +495,9 @@ static int get_cluster_offset(BlockDriverState *bs,
} }
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
ret = bdrv_pwrite(bs->file, ret = bdrv_pwrite(bs->file,
cluster_offset + i * 512, cluster_offset + i,
s->cluster_data, 512); s->cluster_data,
BDRV_SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -612,11 +611,21 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
return 0; return 0;
} }
static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, static void qcow_refresh_limits(BlockDriverState *bs, Error **errp)
int nb_sectors, QEMUIOVector *qiov) {
/* At least encrypted images require 512-byte alignment. Apply the
* limit universally, rather than just on encrypted images, as
* it's easier to let the block layer handle rounding than to
* audit this code further. */
bs->bl.request_alignment = BDRV_SECTOR_SIZE;
}
static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov,
int flags)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int index_in_cluster; int offset_in_cluster;
int ret = 0, n; int ret = 0, n;
uint64_t cluster_offset; uint64_t cluster_offset;
struct iovec hd_iov; struct iovec hd_iov;
@ -624,6 +633,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf; uint8_t *buf;
void *orig_buf; void *orig_buf;
assert(!flags);
if (qiov->niov > 1) { if (qiov->niov > 1) {
buf = orig_buf = qemu_try_blockalign(bs, qiov->size); buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
if (buf == NULL) { if (buf == NULL) {
@ -636,36 +646,35 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
while (nb_sectors != 0) { while (bytes != 0) {
/* prepare next request */ /* prepare next request */
ret = get_cluster_offset(bs, sector_num << 9, ret = get_cluster_offset(bs, offset, 0, 0, 0, 0, &cluster_offset);
0, 0, 0, 0, &cluster_offset);
if (ret < 0) { if (ret < 0) {
break; break;
} }
index_in_cluster = sector_num & (s->cluster_sectors - 1); offset_in_cluster = offset & (s->cluster_size - 1);
n = s->cluster_sectors - index_in_cluster; n = s->cluster_size - offset_in_cluster;
if (n > nb_sectors) { if (n > bytes) {
n = nb_sectors; n = bytes;
} }
if (!cluster_offset) { if (!cluster_offset) {
if (bs->backing) { if (bs->backing) {
/* read from the base image */ /* read from the base image */
hd_iov.iov_base = (void *)buf; hd_iov.iov_base = (void *)buf;
hd_iov.iov_len = n * 512; hd_iov.iov_len = n;
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
/* qcow2 emits this on bs->file instead of bs->backing */ /* qcow2 emits this on bs->file instead of bs->backing */
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); ret = bdrv_co_preadv(bs->backing, offset, n, &hd_qiov, 0);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
break; break;
} }
} else { } else {
/* Note: in this case, no need to wait */ /* Note: in this case, no need to wait */
memset(buf, 0, 512 * n); memset(buf, 0, n);
} }
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
/* add AIO support for compressed blocks ? */ /* add AIO support for compressed blocks ? */
@ -673,21 +682,19 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
ret = -EIO; ret = -EIO;
break; break;
} }
memcpy(buf, memcpy(buf, s->cluster_cache + offset_in_cluster, n);
s->cluster_cache + index_in_cluster * 512, 512 * n);
} else { } else {
if ((cluster_offset & 511) != 0) { if ((cluster_offset & 511) != 0) {
ret = -EIO; ret = -EIO;
break; break;
} }
hd_iov.iov_base = (void *)buf; hd_iov.iov_base = (void *)buf;
hd_iov.iov_len = n * 512; hd_iov.iov_len = n;
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
ret = bdrv_co_readv(bs->file, ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
(cluster_offset >> 9) + index_in_cluster, n, &hd_qiov, 0);
n, &hd_qiov);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
break; break;
@ -695,8 +702,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
if (bs->encrypted) { if (bs->encrypted) {
assert(s->crypto); assert(s->crypto);
if (qcrypto_block_decrypt(s->crypto, if (qcrypto_block_decrypt(s->crypto,
sector_num * BDRV_SECTOR_SIZE, buf, offset, buf, n, NULL) < 0) {
n * BDRV_SECTOR_SIZE, NULL) < 0) {
ret = -EIO; ret = -EIO;
break; break;
} }
@ -704,9 +710,9 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
} }
ret = 0; ret = 0;
nb_sectors -= n; bytes -= n;
sector_num += n; offset += n;
buf += n * 512; buf += n;
} }
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
@ -719,11 +725,12 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
return ret; return ret;
} }
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
int nb_sectors, QEMUIOVector *qiov) uint64_t bytes, QEMUIOVector *qiov,
int flags)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int index_in_cluster; int offset_in_cluster;
uint64_t cluster_offset; uint64_t cluster_offset;
int ret = 0, n; int ret = 0, n;
struct iovec hd_iov; struct iovec hd_iov;
@ -731,6 +738,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf; uint8_t *buf;
void *orig_buf; void *orig_buf;
assert(!flags);
s->cluster_cache_offset = -1; /* disable compressed cache */ s->cluster_cache_offset = -1; /* disable compressed cache */
/* We must always copy the iov when encrypting, so we /* We must always copy the iov when encrypting, so we
@ -748,16 +756,14 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
while (nb_sectors != 0) { while (bytes != 0) {
offset_in_cluster = offset & (s->cluster_size - 1);
index_in_cluster = sector_num & (s->cluster_sectors - 1); n = s->cluster_size - offset_in_cluster;
n = s->cluster_sectors - index_in_cluster; if (n > bytes) {
if (n > nb_sectors) { n = bytes;
n = nb_sectors;
} }
ret = get_cluster_offset(bs, sector_num << 9, 1, 0, ret = get_cluster_offset(bs, offset, 1, 0, offset_in_cluster,
index_in_cluster, offset_in_cluster + n, &cluster_offset);
index_in_cluster + n, &cluster_offset);
if (ret < 0) { if (ret < 0) {
break; break;
} }
@ -767,30 +773,28 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
} }
if (bs->encrypted) { if (bs->encrypted) {
assert(s->crypto); assert(s->crypto);
if (qcrypto_block_encrypt(s->crypto, sector_num * BDRV_SECTOR_SIZE, if (qcrypto_block_encrypt(s->crypto, offset, buf, n, NULL) < 0) {
buf, n * BDRV_SECTOR_SIZE, NULL) < 0) {
ret = -EIO; ret = -EIO;
break; break;
} }
} }
hd_iov.iov_base = (void *)buf; hd_iov.iov_base = (void *)buf;
hd_iov.iov_len = n * 512; hd_iov.iov_len = n;
qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); qemu_iovec_init_external(&hd_qiov, &hd_iov, 1);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
ret = bdrv_co_writev(bs->file, ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
(cluster_offset >> 9) + index_in_cluster, n, &hd_qiov, 0);
n, &hd_qiov);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
break; break;
} }
ret = 0; ret = 0;
nb_sectors -= n; bytes -= n;
sector_num += n; offset += n;
buf += n * 512; buf += n;
} }
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
@ -934,6 +938,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
ret = 0; ret = 0;
exit: exit:
blk_unref(qcow_blk); blk_unref(qcow_blk);
bdrv_unref(bs);
qcrypto_block_free(crypto); qcrypto_block_free(crypto);
return ret; return ret;
} }
@ -943,8 +948,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
{ {
BlockdevCreateOptions *create_options = NULL; BlockdevCreateOptions *create_options = NULL;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
QDict *qdict = NULL; QDict *qdict;
QObject *qobj;
Visitor *v; Visitor *v;
const char *val; const char *val;
Error *local_err = NULL; Error *local_err = NULL;
@ -994,15 +998,12 @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
qdict_put_str(qdict, "driver", "qcow"); qdict_put_str(qdict, "driver", "qcow");
qdict_put_str(qdict, "file", bs->node_name); qdict_put_str(qdict, "file", bs->node_name);
qobj = qdict_crumple(qdict, errp); v = qobject_input_visitor_new_flat_confused(qdict, errp);
QDECREF(qdict); if (!v) {
qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
visit_free(v); visit_free(v);
@ -1025,7 +1026,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
ret = 0; ret = 0;
fail: fail:
QDECREF(qdict); qobject_unref(qdict);
bdrv_unref(bs); bdrv_unref(bs);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;
@ -1109,8 +1110,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
if (ret != Z_STREAM_END || out_len >= s->cluster_size) { if (ret != Z_STREAM_END || out_len >= s->cluster_size) {
/* could not compress: write normal cluster */ /* could not compress: write normal cluster */
ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS, ret = qcow_co_pwritev(bs, offset, bytes, qiov, 0);
bytes >> BDRV_SECTOR_BITS, qiov);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -1195,9 +1195,10 @@ static BlockDriver bdrv_qcow = {
.bdrv_co_create_opts = qcow_co_create_opts, .bdrv_co_create_opts = qcow_co_create_opts,
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.supports_backing = true, .supports_backing = true,
.bdrv_refresh_limits = qcow_refresh_limits,
.bdrv_co_readv = qcow_co_readv, .bdrv_co_preadv = qcow_co_preadv,
.bdrv_co_writev = qcow_co_writev, .bdrv_co_pwritev = qcow_co_pwritev,
.bdrv_co_block_status = qcow_co_block_status, .bdrv_co_block_status = qcow_co_block_status,
.bdrv_make_empty = qcow_make_empty, .bdrv_make_empty = qcow_make_empty,

View File

@ -30,7 +30,7 @@
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qcow2.h" #include "qcow2.h"
/* NOTICE: BME here means Bitmaps Extension and used as a namespace for /* NOTICE: BME here means Bitmaps Extension and used as a namespace for
* _internal_ constants. Please do not use this _internal_ abbreviation for * _internal_ constants. Please do not use this _internal_ abbreviation for
@ -254,7 +254,6 @@ static int free_bitmap_clusters(BlockDriverState *bs, Qcow2BitmapTable *tb)
ret = bitmap_table_load(bs, tb, &bitmap_table); ret = bitmap_table_load(bs, tb, &bitmap_table);
if (ret < 0) { if (ret < 0) {
assert(bitmap_table == NULL);
return ret; return ret;
} }
@ -776,7 +775,12 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list,
} }
} }
ret = qcow2_pre_write_overlap_check(bs, 0, dir_offset, dir_size); /* Actually, even in in-place case ignoring QCOW2_OL_BITMAP_DIRECTORY is not
* necessary, because we drop QCOW2_AUTOCLEAR_BITMAPS when updating bitmap
* directory in-place (actually, turn-off the extension), which is checked
* in qcow2_check_metadata_overlap() */
ret = qcow2_pre_write_overlap_check(
bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }

View File

@ -28,7 +28,7 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qcow2.h" #include "qcow2.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "trace.h" #include "trace.h"
@ -994,6 +994,17 @@ err:
return ret; return ret;
} }
/**
* Frees the allocated clusters because the request failed and they won't
* actually be linked.
*/
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m)
{
BDRVQcow2State *s = bs->opaque;
qcow2_free_clusters(bs, m->alloc_offset, m->nb_clusters << s->cluster_bits,
QCOW2_DISCARD_NEVER);
}
/* /*
* Returns the number of contiguous clusters that can be used for an allocating * Returns the number of contiguous clusters that can be used for an allocating
* write, but require COW to be performed (this includes yet unallocated space, * write, but require COW to be performed (this includes yet unallocated space,

View File

@ -26,7 +26,7 @@
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qcow2.h" #include "qcow2.h"
#include "qemu/range.h" #include "qemu/range.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
@ -734,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
/* Discard is optional, ignore the return value */ /* Discard is optional, ignore the return value */
if (ret >= 0) { if (ret >= 0) {
bdrv_pdiscard(bs->file->bs, d->offset, d->bytes); bdrv_pdiscard(bs->file, d->offset, d->bytes);
} }
g_free(d); g_free(d);
@ -1577,9 +1577,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
case QCOW2_CLUSTER_COMPRESSED: case QCOW2_CLUSTER_COMPRESSED:
/* Compressed clusters don't have QCOW_OFLAG_COPIED */ /* Compressed clusters don't have QCOW_OFLAG_COPIED */
if (l2_entry & QCOW_OFLAG_COPIED) { if (l2_entry & QCOW_OFLAG_COPIED) {
fprintf(stderr, "ERROR: cluster %" PRId64 ": " fprintf(stderr, "ERROR: coffset=0x%" PRIx64 ": "
"copied flag must never be set for compressed " "copied flag must never be set for compressed "
"clusters\n", l2_entry >> s->cluster_bits); "clusters\n", l2_entry & s->cluster_offset_mask);
l2_entry &= ~QCOW_OFLAG_COPIED; l2_entry &= ~QCOW_OFLAG_COPIED;
res->corruptions++; res->corruptions++;
} }
@ -1799,6 +1799,19 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
int ret; int ret;
uint64_t refcount; uint64_t refcount;
int i, j; int i, j;
bool repair;
if (fix & BDRV_FIX_ERRORS) {
/* Always repair */
repair = true;
} else if (fix & BDRV_FIX_LEAKS) {
/* Repair only if that seems safe: This function is always
* called after the refcounts have been fixed, so the refcount
* is accurate if that repair was successful */
repair = !res->check_errors && !res->corruptions && !res->leaks;
} else {
repair = false;
}
for (i = 0; i < s->l1_size; i++) { for (i = 0; i < s->l1_size; i++) {
uint64_t l1_entry = s->l1_table[i]; uint64_t l1_entry = s->l1_table[i];
@ -1818,10 +1831,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) { if ((refcount == 1) != ((l1_entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d " fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
"l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n", "l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
fix & BDRV_FIX_ERRORS ? "Repairing" : repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
"ERROR", if (repair) {
i, l1_entry, refcount);
if (fix & BDRV_FIX_ERRORS) {
s->l1_table[i] = refcount == 1 s->l1_table[i] = refcount == 1
? l1_entry | QCOW_OFLAG_COPIED ? l1_entry | QCOW_OFLAG_COPIED
: l1_entry & ~QCOW_OFLAG_COPIED; : l1_entry & ~QCOW_OFLAG_COPIED;
@ -1862,10 +1873,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "%s OFLAG_COPIED data cluster: " fprintf(stderr, "%s OFLAG_COPIED data cluster: "
"l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n", "l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
fix & BDRV_FIX_ERRORS ? "Repairing" : repair ? "Repairing" : "ERROR", l2_entry, refcount);
"ERROR", if (repair) {
l2_entry, refcount);
if (fix & BDRV_FIX_ERRORS) {
l2_table[j] = cpu_to_be64(refcount == 1 l2_table[j] = cpu_to_be64(refcount == 1
? l2_entry | QCOW_OFLAG_COPIED ? l2_entry | QCOW_OFLAG_COPIED
: l2_entry & ~QCOW_OFLAG_COPIED); : l2_entry & ~QCOW_OFLAG_COPIED);
@ -2696,6 +2705,16 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
} }
} }
if ((chk & QCOW2_OL_BITMAP_DIRECTORY) &&
(s->autoclear_features & QCOW2_AUTOCLEAR_BITMAPS))
{
if (overlaps_with(s->bitmap_directory_offset,
s->bitmap_directory_size))
{
return QCOW2_OL_BITMAP_DIRECTORY;
}
}
return 0; return 0;
} }

View File

@ -25,7 +25,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qcow2.h" #include "qcow2.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"

File diff suppressed because it is too large Load Diff

View File

@ -77,10 +77,6 @@
#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ #define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ #define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */
/* The refblock cache needs only a fourth of the L2 cache size to cover as many
* clusters */
#define DEFAULT_L2_REFCOUNT_SIZE_RATIO 4
#define DEFAULT_CLUSTER_SIZE 65536 #define DEFAULT_CLUSTER_SIZE 65536
@ -98,6 +94,7 @@
#define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table" #define QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE "overlap-check.snapshot-table"
#define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1" #define QCOW2_OPT_OVERLAP_INACTIVE_L1 "overlap-check.inactive-l1"
#define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2" #define QCOW2_OPT_OVERLAP_INACTIVE_L2 "overlap-check.inactive-l2"
#define QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY "overlap-check.bitmap-directory"
#define QCOW2_OPT_CACHE_SIZE "cache-size" #define QCOW2_OPT_CACHE_SIZE "cache-size"
#define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size" #define QCOW2_OPT_L2_CACHE_SIZE "l2-cache-size"
#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size" #define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size"
@ -330,6 +327,9 @@ typedef struct BDRVQcow2State {
* override) */ * override) */
char *image_backing_file; char *image_backing_file;
char *image_backing_format; char *image_backing_format;
CoQueue compress_wait_queue;
int nb_compress_threads;
} BDRVQcow2State; } BDRVQcow2State;
typedef struct Qcow2COWRegion { typedef struct Qcow2COWRegion {
@ -401,34 +401,36 @@ typedef enum QCow2ClusterType {
} QCow2ClusterType; } QCow2ClusterType;
typedef enum QCow2MetadataOverlap { typedef enum QCow2MetadataOverlap {
QCOW2_OL_MAIN_HEADER_BITNR = 0, QCOW2_OL_MAIN_HEADER_BITNR = 0,
QCOW2_OL_ACTIVE_L1_BITNR = 1, QCOW2_OL_ACTIVE_L1_BITNR = 1,
QCOW2_OL_ACTIVE_L2_BITNR = 2, QCOW2_OL_ACTIVE_L2_BITNR = 2,
QCOW2_OL_REFCOUNT_TABLE_BITNR = 3, QCOW2_OL_REFCOUNT_TABLE_BITNR = 3,
QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4, QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4,
QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5, QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
QCOW2_OL_INACTIVE_L1_BITNR = 6, QCOW2_OL_INACTIVE_L1_BITNR = 6,
QCOW2_OL_INACTIVE_L2_BITNR = 7, QCOW2_OL_INACTIVE_L2_BITNR = 7,
QCOW2_OL_BITMAP_DIRECTORY_BITNR = 8,
QCOW2_OL_MAX_BITNR = 8, QCOW2_OL_MAX_BITNR = 9,
QCOW2_OL_NONE = 0, QCOW2_OL_NONE = 0,
QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR), QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR), QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR),
QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR), QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR),
QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR), QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR),
QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR), QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR),
QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR), QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR),
QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR), QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR),
/* NOTE: Checking overlaps with inactive L2 tables will result in bdrv /* NOTE: Checking overlaps with inactive L2 tables will result in bdrv
* reads. */ * reads. */
QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR), QCOW2_OL_INACTIVE_L2 = (1 << QCOW2_OL_INACTIVE_L2_BITNR),
QCOW2_OL_BITMAP_DIRECTORY = (1 << QCOW2_OL_BITMAP_DIRECTORY_BITNR),
} QCow2MetadataOverlap; } QCow2MetadataOverlap;
/* Perform all overlap checks which can be done in constant time */ /* Perform all overlap checks which can be done in constant time */
#define QCOW2_OL_CONSTANT \ #define QCOW2_OL_CONSTANT \
(QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \ (QCOW2_OL_MAIN_HEADER | QCOW2_OL_ACTIVE_L1 | QCOW2_OL_REFCOUNT_TABLE | \
QCOW2_OL_SNAPSHOT_TABLE) QCOW2_OL_SNAPSHOT_TABLE | QCOW2_OL_BITMAP_DIRECTORY)
/* Perform all overlap checks which don't require disk access */ /* Perform all overlap checks which don't require disk access */
#define QCOW2_OL_CACHED \ #define QCOW2_OL_CACHED \
@ -618,6 +620,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
int compressed_size); int compressed_size);
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m);
int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset, int qcow2_cluster_discard(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, enum qcow2_discard_type type, uint64_t bytes, enum qcow2_discard_type type,
bool full_discard); bool full_discard);

View File

@ -13,6 +13,7 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "block/qdict.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
@ -721,8 +722,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
Error **errp) Error **errp)
{ {
BlockdevCreateOptions *create_options = NULL; BlockdevCreateOptions *create_options = NULL;
QDict *qdict = NULL; QDict *qdict;
QObject *qobj;
Visitor *v; Visitor *v;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
Error *local_err = NULL; Error *local_err = NULL;
@ -762,15 +762,12 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
qdict_put_str(qdict, "driver", "qed"); qdict_put_str(qdict, "driver", "qed");
qdict_put_str(qdict, "file", bs->node_name); qdict_put_str(qdict, "file", bs->node_name);
qobj = qdict_crumple(qdict, errp); v = qobject_input_visitor_new_flat_confused(qdict, errp);
QDECREF(qdict); if (!v) {
qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
visit_free(v); visit_free(v);
@ -789,7 +786,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
ret = bdrv_qed_co_create(create_options, errp); ret = bdrv_qed_co_create(create_options, errp);
fail: fail:
QDECREF(qdict); qobject_unref(qdict);
bdrv_unref(bs); bdrv_unref(bs);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;
@ -1437,8 +1434,9 @@ static int coroutine_fn bdrv_qed_co_readv(BlockDriverState *bs,
static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs, static int coroutine_fn bdrv_qed_co_writev(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int64_t sector_num, int nb_sectors,
QEMUIOVector *qiov) QEMUIOVector *qiov, int flags)
{ {
assert(!flags);
return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE); return qed_co_request(bs, sector_num, qiov, nb_sectors, QED_AIOCB_WRITE);
} }
@ -1469,8 +1467,10 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
QED_AIOCB_WRITE | QED_AIOCB_ZERO); QED_AIOCB_WRITE | QED_AIOCB_ZERO);
} }
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs,
PreallocMode prealloc, Error **errp) int64_t offset,
PreallocMode prealloc,
Error **errp)
{ {
BDRVQEDState *s = bs->opaque; BDRVQEDState *s = bs->opaque;
uint64_t old_image_size; uint64_t old_image_size;
@ -1680,7 +1680,7 @@ static BlockDriver bdrv_qed = {
.bdrv_co_readv = bdrv_qed_co_readv, .bdrv_co_readv = bdrv_qed_co_readv,
.bdrv_co_writev = bdrv_qed_co_writev, .bdrv_co_writev = bdrv_qed_co_writev,
.bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes,
.bdrv_truncate = bdrv_qed_truncate, .bdrv_co_truncate = bdrv_qed_co_truncate,
.bdrv_getlength = bdrv_qed_getlength, .bdrv_getlength = bdrv_qed_getlength,
.bdrv_get_info = bdrv_qed_get_info, .bdrv_get_info = bdrv_qed_get_info,
.bdrv_refresh_limits = bdrv_qed_refresh_limits, .bdrv_refresh_limits = bdrv_qed_refresh_limits,

View File

@ -17,6 +17,7 @@
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qapi-events-block.h" #include "qapi/qapi-events-block.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
@ -115,6 +116,7 @@ struct QuorumAIOCB {
/* Request metadata */ /* Request metadata */
uint64_t offset; uint64_t offset;
uint64_t bytes; uint64_t bytes;
int flags;
QEMUIOVector *qiov; /* calling IOV */ QEMUIOVector *qiov; /* calling IOV */
@ -157,7 +159,8 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs, static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
QEMUIOVector *qiov, QEMUIOVector *qiov,
uint64_t offset, uint64_t offset,
uint64_t bytes) uint64_t bytes,
int flags)
{ {
BDRVQuorumState *s = bs->opaque; BDRVQuorumState *s = bs->opaque;
QuorumAIOCB *acb = g_new(QuorumAIOCB, 1); QuorumAIOCB *acb = g_new(QuorumAIOCB, 1);
@ -168,6 +171,7 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
.bs = bs, .bs = bs,
.offset = offset, .offset = offset,
.bytes = bytes, .bytes = bytes,
.flags = flags,
.qiov = qiov, .qiov = qiov,
.votes.compare = quorum_sha256_compare, .votes.compare = quorum_sha256_compare,
.votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list), .votes.vote_list = QLIST_HEAD_INITIALIZER(acb.votes.vote_list),
@ -271,9 +275,11 @@ static void quorum_rewrite_entry(void *opaque)
BDRVQuorumState *s = acb->bs->opaque; BDRVQuorumState *s = acb->bs->opaque;
/* Ignore any errors, it's just a correction attempt for already /* Ignore any errors, it's just a correction attempt for already
* corrupted data. */ * corrupted data.
* Mask out BDRV_REQ_WRITE_UNCHANGED because this overwrites the
* area with different data from the other children. */
bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes, bdrv_co_pwritev(s->children[co->idx], acb->offset, acb->bytes,
acb->qiov, 0); acb->qiov, acb->flags & ~BDRV_REQ_WRITE_UNCHANGED);
/* Wake up the caller after the last rewrite */ /* Wake up the caller after the last rewrite */
acb->rewrite_count--; acb->rewrite_count--;
@ -608,7 +614,7 @@ static void read_quorum_children_entry(void *opaque)
static int read_quorum_children(QuorumAIOCB *acb) static int read_quorum_children(QuorumAIOCB *acb)
{ {
BDRVQuorumState *s = acb->bs->opaque; BDRVQuorumState *s = acb->bs->opaque;
int i, ret; int i;
acb->children_read = s->num_children; acb->children_read = s->num_children;
for (i = 0; i < s->num_children; i++) { for (i = 0; i < s->num_children; i++) {
@ -643,9 +649,7 @@ static int read_quorum_children(QuorumAIOCB *acb)
qemu_coroutine_yield(); qemu_coroutine_yield();
} }
ret = acb->vote_ret; return acb->vote_ret;
return ret;
} }
static int read_fifo_child(QuorumAIOCB *acb) static int read_fifo_child(QuorumAIOCB *acb)
@ -673,7 +677,7 @@ static int quorum_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov, int flags) uint64_t bytes, QEMUIOVector *qiov, int flags)
{ {
BDRVQuorumState *s = bs->opaque; BDRVQuorumState *s = bs->opaque;
QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags);
int ret; int ret;
acb->is_read = true; acb->is_read = true;
@ -699,7 +703,7 @@ static void write_quorum_entry(void *opaque)
sacb->bs = s->children[i]->bs; sacb->bs = s->children[i]->bs;
sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes, sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes,
acb->qiov, 0); acb->qiov, acb->flags);
if (sacb->ret == 0) { if (sacb->ret == 0) {
acb->success_count++; acb->success_count++;
} else { } else {
@ -719,7 +723,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov, int flags) uint64_t bytes, QEMUIOVector *qiov, int flags)
{ {
BDRVQuorumState *s = bs->opaque; BDRVQuorumState *s = bs->opaque;
QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes); QuorumAIOCB *acb = quorum_aio_get(bs, qiov, offset, bytes, flags);
int i, ret; int i, ret;
for (i = 0; i < s->num_children; i++) { for (i = 0; i < s->num_children; i++) {
@ -961,6 +965,8 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
} }
s->next_child_index = s->num_children; s->next_child_index = s->num_children;
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
g_free(opened); g_free(opened);
goto exit; goto exit;
@ -1082,8 +1088,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
children = qlist_new(); children = qlist_new();
for (i = 0; i < s->num_children; i++) { for (i = 0; i < s->num_children; i++) {
QINCREF(s->children[i]->bs->full_open_options); qlist_append(children,
qlist_append(children, s->children[i]->bs->full_open_options); qobject_ref(s->children[i]->bs->full_open_options));
} }
opts = qdict_new(); opts = qdict_new();

View File

@ -167,16 +167,37 @@ static void raw_reopen_abort(BDRVReopenState *state)
state->opaque = NULL; state->opaque = NULL;
} }
/* Check and adjust the offset, against 'offset' and 'size' options. */
static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset,
uint64_t bytes, bool is_write)
{
BDRVRawState *s = bs->opaque;
if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) {
/* There's not enough space for the write, or the read request is
* out-of-range. Don't read/write anything to prevent leaking out of
* the size specified in options. */
return is_write ? -ENOSPC : -EINVAL;
}
if (*offset > INT64_MAX - s->offset) {
return -EINVAL;
}
*offset += s->offset;
return 0;
}
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset, static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov, uint64_t bytes, QEMUIOVector *qiov,
int flags) int flags)
{ {
BDRVRawState *s = bs->opaque; int ret;
if (offset > UINT64_MAX - s->offset) { ret = raw_adjust_offset(bs, &offset, bytes, false);
return -EINVAL; if (ret) {
return ret;
} }
offset += s->offset;
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
@ -186,23 +207,11 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov, uint64_t bytes, QEMUIOVector *qiov,
int flags) int flags)
{ {
BDRVRawState *s = bs->opaque;
void *buf = NULL; void *buf = NULL;
BlockDriver *drv; BlockDriver *drv;
QEMUIOVector local_qiov; QEMUIOVector local_qiov;
int ret; int ret;
if (s->has_size && (offset > s->size || bytes > (s->size - offset))) {
/* There's not enough space for the data. Don't write anything and just
* fail to prevent leaking out of the size specified in options. */
return -ENOSPC;
}
if (offset > UINT64_MAX - s->offset) {
ret = -EINVAL;
goto fail;
}
if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) { if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) {
/* Handling partial writes would be a pain - so we just /* Handling partial writes would be a pain - so we just
* require that guests have 512-byte request alignment if * require that guests have 512-byte request alignment if
@ -237,7 +246,10 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
qiov = &local_qiov; qiov = &local_qiov;
} }
offset += s->offset; ret = raw_adjust_offset(bs, &offset, bytes, true);
if (ret) {
goto fail;
}
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
@ -267,23 +279,25 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int bytes, int64_t offset, int bytes,
BdrvRequestFlags flags) BdrvRequestFlags flags)
{ {
BDRVRawState *s = bs->opaque; int ret;
if (offset > UINT64_MAX - s->offset) {
return -EINVAL; ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
if (ret) {
return ret;
} }
offset += s->offset;
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags); return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
} }
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs, static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes) int64_t offset, int bytes)
{ {
BDRVRawState *s = bs->opaque; int ret;
if (offset > UINT64_MAX - s->offset) {
return -EINVAL; ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
if (ret) {
return ret;
} }
offset += s->offset; return bdrv_co_pdiscard(bs->file, offset, bytes);
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
} }
static int64_t raw_getlength(BlockDriverState *bs) static int64_t raw_getlength(BlockDriverState *bs)
@ -352,8 +366,8 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
} }
} }
static int raw_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp) PreallocMode prealloc, Error **errp)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
@ -369,7 +383,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
s->size = offset; s->size = offset;
offset += s->offset; offset += s->offset;
return bdrv_truncate(bs->file, offset, prealloc, errp); return bdrv_co_truncate(bs->file, offset, prealloc, errp);
} }
static void raw_eject(BlockDriverState *bs, bool eject_flag) static void raw_eject(BlockDriverState *bs, bool eject_flag)
@ -415,10 +429,11 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
} }
bs->sg = bs->file->bs->sg; bs->sg = bs->file->bs->sg;
bs->supported_write_flags = BDRV_REQ_FUA & bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
bs->file->bs->supported_write_flags; (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
bs->supported_zero_flags = (BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
bs->file->bs->supported_zero_flags; ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) &
bs->file->bs->supported_zero_flags);
if (bs->probed && !bdrv_is_read_only(bs)) { if (bs->probed && !bdrv_is_read_only(bs)) {
fprintf(stderr, fprintf(stderr,
@ -482,6 +497,44 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
return bdrv_probe_geometry(bs->file->bs, geo); return bdrv_probe_geometry(bs->file->bs, geo);
} }
static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs,
BdrvChild *src,
uint64_t src_offset,
BdrvChild *dst,
uint64_t dst_offset,
uint64_t bytes,
BdrvRequestFlags read_flags,
BdrvRequestFlags write_flags)
{
int ret;
ret = raw_adjust_offset(bs, &src_offset, bytes, false);
if (ret) {
return ret;
}
return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset,
bytes, read_flags, write_flags);
}
static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
BdrvChild *src,
uint64_t src_offset,
BdrvChild *dst,
uint64_t dst_offset,
uint64_t bytes,
BdrvRequestFlags read_flags,
BdrvRequestFlags write_flags)
{
int ret;
ret = raw_adjust_offset(bs, &dst_offset, bytes, true);
if (ret) {
return ret;
}
return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes,
read_flags, write_flags);
}
BlockDriver bdrv_raw = { BlockDriver bdrv_raw = {
.format_name = "raw", .format_name = "raw",
.instance_size = sizeof(BDRVRawState), .instance_size = sizeof(BDRVRawState),
@ -498,7 +551,9 @@ BlockDriver bdrv_raw = {
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
.bdrv_co_pdiscard = &raw_co_pdiscard, .bdrv_co_pdiscard = &raw_co_pdiscard,
.bdrv_co_block_status = &raw_co_block_status, .bdrv_co_block_status = &raw_co_block_status,
.bdrv_truncate = &raw_truncate, .bdrv_co_copy_range_from = &raw_co_copy_range_from,
.bdrv_co_copy_range_to = &raw_co_copy_range_to,
.bdrv_co_truncate = &raw_co_truncate,
.bdrv_getlength = &raw_getlength, .bdrv_getlength = &raw_getlength,
.has_variable_length = true, .has_variable_length = true,
.bdrv_measure = &raw_measure, .bdrv_measure = &raw_measure,

View File

@ -18,6 +18,7 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "crypto/secret.h" #include "crypto/secret.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qapi/qmp/qstring.h" #include "qapi/qmp/qstring.h"
@ -226,27 +227,57 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
done: done:
g_free(buf); g_free(buf);
QDECREF(keypairs); qobject_unref(keypairs);
return; return;
} }
static int qemu_rbd_set_auth(rados_t cluster, const char *secretid, static void qemu_rbd_refresh_limits(BlockDriverState *bs, Error **errp)
{
/* XXX Does RBD support AIO on less than 512-byte alignment? */
bs->bl.request_alignment = 512;
}
static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts,
Error **errp) Error **errp)
{ {
if (secretid == 0) { char *key, *acr;
return 0; int r;
GString *accu;
RbdAuthModeList *auth;
if (opts->key_secret) {
key = qcrypto_secret_lookup_as_base64(opts->key_secret, errp);
if (!key) {
return -EIO;
}
r = rados_conf_set(cluster, "key", key);
g_free(key);
if (r < 0) {
error_setg_errno(errp, -r, "Could not set 'key'");
return r;
}
} }
gchar *secret = qcrypto_secret_lookup_as_base64(secretid, if (opts->has_auth_client_required) {
errp); accu = g_string_new("");
if (!secret) { for (auth = opts->auth_client_required; auth; auth = auth->next) {
return -1; if (accu->str[0]) {
g_string_append_c(accu, ';');
}
g_string_append(accu, RbdAuthMode_str(auth->value));
}
acr = g_string_free(accu, FALSE);
r = rados_conf_set(cluster, "auth_client_required", acr);
g_free(acr);
if (r < 0) {
error_setg_errno(errp, -r,
"Could not set 'auth_client_required'");
return r;
}
} }
rados_conf_set(cluster, "key", secret);
g_free(secret);
return 0; return 0;
} }
@ -275,17 +306,17 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
key = qstring_get_str(name); key = qstring_get_str(name);
ret = rados_conf_set(cluster, key, qstring_get_str(value)); ret = rados_conf_set(cluster, key, qstring_get_str(value));
QDECREF(value); qobject_unref(value);
if (ret < 0) { if (ret < 0) {
error_setg_errno(errp, -ret, "invalid conf option %s", key); error_setg_errno(errp, -ret, "invalid conf option %s", key);
QDECREF(name); qobject_unref(name);
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
QDECREF(name); qobject_unref(name);
} }
QDECREF(keypairs); qobject_unref(keypairs);
return ret; return ret;
} }
@ -337,9 +368,7 @@ static QemuOptsList runtime_opts = {
}, },
}; };
/* FIXME Deprecate and remove keypairs or make it available in QMP. /* FIXME Deprecate and remove keypairs or make it available in QMP. */
* password_secret should eventually be configurable in opts->location. Support
* for it in .bdrv_open will make it work here as well. */
static int qemu_rbd_do_create(BlockdevCreateOptions *options, static int qemu_rbd_do_create(BlockdevCreateOptions *options,
const char *keypairs, const char *password_secret, const char *keypairs, const char *password_secret,
Error **errp) Error **errp)
@ -449,7 +478,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
} }
exit: exit:
QDECREF(options); qobject_unref(options);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;
} }
@ -545,6 +574,16 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
Error *local_err = NULL; Error *local_err = NULL;
int r; int r;
if (secretid) {
if (opts->key_secret) {
error_setg(errp,
"Legacy 'password-secret' clashes with 'key-secret'");
return -EINVAL;
}
opts->key_secret = g_strdup(secretid);
opts->has_key_secret = true;
}
mon_host = qemu_rbd_mon_host(opts, &local_err); mon_host = qemu_rbd_mon_host(opts, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -577,8 +616,8 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
} }
} }
if (qemu_rbd_set_auth(*cluster, secretid, errp) < 0) { r = qemu_rbd_set_auth(*cluster, opts, errp);
r = -EIO; if (r < 0) {
goto failed_shutdown; goto failed_shutdown;
} }
@ -622,28 +661,11 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
BDRVRBDState *s = bs->opaque; BDRVRBDState *s = bs->opaque;
BlockdevOptionsRbd *opts = NULL; BlockdevOptionsRbd *opts = NULL;
Visitor *v; Visitor *v;
QObject *crumpled = NULL;
const QDictEntry *e; const QDictEntry *e;
Error *local_err = NULL; Error *local_err = NULL;
const char *filename;
char *keypairs, *secretid; char *keypairs, *secretid;
int r; int r;
/* If we are given a filename, parse the filename, with precedence given to
* filename encoded options */
filename = qdict_get_try_str(options, "filename");
if (filename) {
warn_report("'filename' option specified. "
"This is an unsupported option, and may be deprecated "
"in the future");
qemu_rbd_parse_filename(filename, options, &local_err);
qdict_del(options, "filename");
if (local_err) {
error_propagate(errp, local_err);
return -EINVAL;
}
}
keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs"));
if (keypairs) { if (keypairs) {
qdict_del(options, "=keyvalue-pairs"); qdict_del(options, "=keyvalue-pairs");
@ -655,16 +677,14 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
} }
/* Convert the remaining options into a QAPI object */ /* Convert the remaining options into a QAPI object */
crumpled = qdict_crumple(options, errp); v = qobject_input_visitor_new_flat_confused(options, errp);
if (crumpled == NULL) { if (!v) {
r = -EINVAL; r = -EINVAL;
goto out; goto out;
} }
v = qobject_input_visitor_new_keyval(crumpled);
visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err);
visit_free(v); visit_free(v);
qobject_decref(crumpled);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -899,27 +919,23 @@ failed:
return NULL; return NULL;
} }
static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs, static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs,
int64_t sector_num, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, QEMUIOVector *qiov, int flags,
int nb_sectors,
BlockCompletionFunc *cb,
void *opaque)
{
return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov,
(int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque,
RBD_AIO_READ);
}
static BlockAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
int64_t sector_num,
QEMUIOVector *qiov,
int nb_sectors,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,
void *opaque) void *opaque)
{ {
return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov, return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque,
(int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque, RBD_AIO_READ);
}
static BlockAIOCB *qemu_rbd_aio_pwritev(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags,
BlockCompletionFunc *cb,
void *opaque)
{
return rbd_start_aio(bs, offset, qiov, bytes, cb, opaque,
RBD_AIO_WRITE); RBD_AIO_WRITE);
} }
@ -974,8 +990,10 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs)
return info.size; return info.size;
} }
static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
PreallocMode prealloc, Error **errp) int64_t offset,
PreallocMode prealloc,
Error **errp)
{ {
BDRVRBDState *s = bs->opaque; BDRVRBDState *s = bs->opaque;
int r; int r;
@ -1158,6 +1176,7 @@ static BlockDriver bdrv_rbd = {
.format_name = "rbd", .format_name = "rbd",
.instance_size = sizeof(BDRVRBDState), .instance_size = sizeof(BDRVRBDState),
.bdrv_parse_filename = qemu_rbd_parse_filename, .bdrv_parse_filename = qemu_rbd_parse_filename,
.bdrv_refresh_limits = qemu_rbd_refresh_limits,
.bdrv_file_open = qemu_rbd_open, .bdrv_file_open = qemu_rbd_open,
.bdrv_close = qemu_rbd_close, .bdrv_close = qemu_rbd_close,
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare, .bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
@ -1167,11 +1186,11 @@ static BlockDriver bdrv_rbd = {
.bdrv_get_info = qemu_rbd_getinfo, .bdrv_get_info = qemu_rbd_getinfo,
.create_opts = &qemu_rbd_create_opts, .create_opts = &qemu_rbd_create_opts,
.bdrv_getlength = qemu_rbd_getlength, .bdrv_getlength = qemu_rbd_getlength,
.bdrv_truncate = qemu_rbd_truncate, .bdrv_co_truncate = qemu_rbd_co_truncate,
.protocol_name = "rbd", .protocol_name = "rbd",
.bdrv_aio_readv = qemu_rbd_aio_readv, .bdrv_aio_preadv = qemu_rbd_aio_preadv,
.bdrv_aio_writev = qemu_rbd_aio_writev, .bdrv_aio_pwritev = qemu_rbd_aio_pwritev,
#ifdef LIBRBD_SUPPORTS_AIO_FLUSH #ifdef LIBRBD_SUPPORTS_AIO_FLUSH
.bdrv_aio_flush = qemu_rbd_aio_flush, .bdrv_aio_flush = qemu_rbd_aio_flush,

View File

@ -145,7 +145,7 @@ static void replication_close(BlockDriverState *bs)
replication_stop(s->rs, false, NULL); replication_stop(s->rs, false, NULL);
} }
if (s->stage == BLOCK_REPLICATION_FAILOVER) { if (s->stage == BLOCK_REPLICATION_FAILOVER) {
block_job_cancel_sync(s->active_disk->bs->job); job_cancel_sync(&s->active_disk->bs->job->job);
} }
if (s->mode == REPLICATION_MODE_SECONDARY) { if (s->mode == REPLICATION_MODE_SECONDARY) {
@ -246,13 +246,14 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs,
backup_cow_request_begin(&req, child->bs->job, backup_cow_request_begin(&req, child->bs->job,
sector_num * BDRV_SECTOR_SIZE, sector_num * BDRV_SECTOR_SIZE,
remaining_bytes); remaining_bytes);
ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE,
qiov); remaining_bytes, qiov, 0);
backup_cow_request_end(&req); backup_cow_request_end(&req);
goto out; goto out;
} }
ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors, qiov); ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE,
remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
out: out:
return replication_return_value(s, ret); return replication_return_value(s, ret);
} }
@ -260,7 +261,8 @@ out:
static coroutine_fn int replication_co_writev(BlockDriverState *bs, static coroutine_fn int replication_co_writev(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int remaining_sectors, int remaining_sectors,
QEMUIOVector *qiov) QEMUIOVector *qiov,
int flags)
{ {
BDRVReplicationState *s = bs->opaque; BDRVReplicationState *s = bs->opaque;
QEMUIOVector hd_qiov; QEMUIOVector hd_qiov;
@ -271,14 +273,15 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
int ret; int ret;
int64_t n; int64_t n;
assert(!flags);
ret = replication_get_io_status(s); ret = replication_get_io_status(s);
if (ret < 0) { if (ret < 0) {
goto out; goto out;
} }
if (ret == 0) { if (ret == 0) {
ret = bdrv_co_writev(top, sector_num, ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE,
remaining_sectors, qiov); remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
return replication_return_value(s, ret); return replication_return_value(s, ret);
} }
@ -304,7 +307,8 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count); qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count);
target = ret ? top : base; target = ret ? top : base;
ret = bdrv_co_writev(target, sector_num, n, &hd_qiov); ret = bdrv_co_pwritev(target, sector_num * BDRV_SECTOR_SIZE,
n * BDRV_SECTOR_SIZE, &hd_qiov, 0);
if (ret < 0) { if (ret < 0) {
goto out1; goto out1;
} }
@ -566,7 +570,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs, job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs,
0, MIRROR_SYNC_MODE_NONE, NULL, false, 0, MIRROR_SYNC_MODE_NONE, NULL, false,
BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT,
BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
backup_job_completed, bs, NULL, &local_err); backup_job_completed, bs, NULL, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -574,7 +578,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
aio_context_release(aio_context); aio_context_release(aio_context);
return; return;
} }
block_job_start(job); job_start(&job->job);
break; break;
default: default:
aio_context_release(aio_context); aio_context_release(aio_context);
@ -679,7 +683,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
* disk, secondary disk in backup_job_completed(). * disk, secondary disk in backup_job_completed().
*/ */
if (s->secondary_disk->bs->job) { if (s->secondary_disk->bs->job) {
block_job_cancel_sync(s->secondary_disk->bs->job); job_cancel_sync(&s->secondary_disk->bs->job->job);
} }
if (!failover) { if (!failover) {
@ -691,7 +695,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
s->stage = BLOCK_REPLICATION_FAILOVER; s->stage = BLOCK_REPLICATION_FAILOVER;
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs, commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
NULL, replication_done, bs, true, errp); NULL, replication_done, bs, true, errp);
break; break;
default: default:

View File

@ -24,6 +24,7 @@
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/sockets.h" #include "qemu/sockets.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qemu/bitops.h" #include "qemu/bitops.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
@ -538,27 +539,17 @@ static void sd_aio_setup(SheepdogAIOCB *acb, BDRVSheepdogState *s,
static SocketAddress *sd_server_config(QDict *options, Error **errp) static SocketAddress *sd_server_config(QDict *options, Error **errp)
{ {
QDict *server = NULL; QDict *server = NULL;
QObject *crumpled_server = NULL;
Visitor *iv = NULL; Visitor *iv = NULL;
SocketAddress *saddr = NULL; SocketAddress *saddr = NULL;
Error *local_err = NULL; Error *local_err = NULL;
qdict_extract_subqdict(options, &server, "server."); qdict_extract_subqdict(options, &server, "server.");
crumpled_server = qdict_crumple(server, errp); iv = qobject_input_visitor_new_flat_confused(server, errp);
if (!crumpled_server) { if (!iv) {
goto done; goto done;
} }
/*
* FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive
* server.type=inet. .to doesn't matter, it's ignored anyway.
* That's because when @options come from -blockdev or
* blockdev_add, members are typed according to the QAPI schema,
* but when they come from -drive, they're all QString. The
* visitor expects the former.
*/
iv = qobject_input_visitor_new(crumpled_server);
visit_type_SocketAddress(iv, NULL, &saddr, &local_err); visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -567,8 +558,7 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp)
done: done:
visit_free(iv); visit_free(iv);
qobject_decref(crumpled_server); qobject_unref(server);
QDECREF(server);
return saddr; return saddr;
} }
@ -1859,9 +1849,7 @@ out:
error_setg_errno(errp, -ret, "Can't pre-allocate"); error_setg_errno(errp, -ret, "Can't pre-allocate");
} }
out_with_err_set: out_with_err_set:
if (blk) { blk_unref(blk);
blk_unref(blk);
}
g_free(buf); g_free(buf);
return ret; return ret;
@ -1883,7 +1871,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
qobject_decref(obj); qobject_unref(obj);
return -EINVAL; return -EINVAL;
} }
@ -1901,7 +1889,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
ret = sd_prealloc(bs, 0, size, errp); ret = sd_prealloc(bs, 0, size, errp);
fail: fail:
bdrv_unref(bs); bdrv_unref(bs);
QDECREF(qdict); qobject_unref(qdict);
return ret; return ret;
} }
@ -1987,6 +1975,7 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt)
} else { } else {
ret = qemu_strtol(n2, NULL, 10, &parity); ret = qemu_strtol(n2, NULL, 10, &parity);
if (ret < 0) { if (ret < 0) {
g_free(redundancy);
return NULL; return NULL;
} }
@ -2181,9 +2170,8 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
{ {
BlockdevCreateOptions *create_options = NULL; BlockdevCreateOptions *create_options = NULL;
QDict *qdict, *location_qdict; QDict *qdict, *location_qdict;
QObject *crumpled;
Visitor *v; Visitor *v;
const char *redundancy; char *redundancy;
Error *local_err = NULL; Error *local_err = NULL;
int ret; int ret;
@ -2217,16 +2205,14 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
} }
/* Get the QAPI object */ /* Get the QAPI object */
crumpled = qdict_crumple(qdict, errp); v = qobject_input_visitor_new_flat_confused(qdict, errp);
if (crumpled == NULL) { if (!v) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
v = qobject_input_visitor_new_keyval(crumpled);
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
visit_free(v); visit_free(v);
qobject_decref(crumpled);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -2252,7 +2238,8 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
ret = sd_co_create(create_options, errp); ret = sd_co_create(create_options, errp);
fail: fail:
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
QDECREF(qdict); qobject_unref(qdict);
g_free(redundancy);
return ret; return ret;
} }
@ -2305,8 +2292,8 @@ static int64_t sd_getlength(BlockDriverState *bs)
return s->inode.vdi_size; return s->inode.vdi_size;
} }
static int sd_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp) PreallocMode prealloc, Error **errp)
{ {
BDRVSheepdogState *s = bs->opaque; BDRVSheepdogState *s = bs->opaque;
int ret, fd; int ret, fd;
@ -2335,7 +2322,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset,
} }
/* we don't need to update entire object */ /* we don't need to update entire object */
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); datalen = SD_INODE_HEADER_SIZE;
s->inode.vdi_size = offset; s->inode.vdi_size = offset;
ret = write_object(fd, s->bs, (char *)&s->inode, ret = write_object(fd, s->bs, (char *)&s->inode,
vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies, vid_to_vdi_oid(s->inode.vdi_id), s->inode.nr_copies,
@ -2612,15 +2599,17 @@ static void sd_aio_complete(SheepdogAIOCB *acb)
} }
static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov,
int flags)
{ {
SheepdogAIOCB acb; SheepdogAIOCB acb;
int ret; int ret;
int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE; int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
BDRVSheepdogState *s = bs->opaque; BDRVSheepdogState *s = bs->opaque;
assert(!flags);
if (offset > s->inode.vdi_size) { if (offset > s->inode.vdi_size) {
ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL); ret = sd_co_truncate(bs, offset, PREALLOC_MODE_OFF, NULL);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -2701,7 +2690,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
*/ */
strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag)); strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag));
/* we don't need to update entire object */ /* we don't need to update entire object */
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); datalen = SD_INODE_HEADER_SIZE;
inode = g_malloc(datalen); inode = g_malloc(datalen);
/* refresh inode. */ /* refresh inode. */
@ -2936,13 +2925,14 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
QEMUSnapshotInfo *sn_tab = NULL; QEMUSnapshotInfo *sn_tab = NULL;
unsigned wlen, rlen; unsigned wlen, rlen;
int found = 0; int found = 0;
static SheepdogInode inode; SheepdogInode *inode;
unsigned long *vdi_inuse; unsigned long *vdi_inuse;
unsigned int start_nr; unsigned int start_nr;
uint64_t hval; uint64_t hval;
uint32_t vid; uint32_t vid;
vdi_inuse = g_malloc(max); vdi_inuse = g_malloc(max);
inode = g_malloc(SD_INODE_HEADER_SIZE);
fd = connect_to_sdog(s, &local_err); fd = connect_to_sdog(s, &local_err);
if (fd < 0) { if (fd < 0) {
@ -2985,26 +2975,26 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
} }
/* we don't need to read entire object */ /* we don't need to read entire object */
ret = read_object(fd, s->bs, (char *)&inode, ret = read_object(fd, s->bs, (char *)inode,
vid_to_vdi_oid(vid), vid_to_vdi_oid(vid),
0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0, 0, SD_INODE_HEADER_SIZE, 0,
s->cache_flags); s->cache_flags);
if (ret) { if (ret) {
continue; continue;
} }
if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) { if (!strcmp(inode->name, s->name) && is_snapshot(inode)) {
sn_tab[found].date_sec = inode.snap_ctime >> 32; sn_tab[found].date_sec = inode->snap_ctime >> 32;
sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff; sn_tab[found].date_nsec = inode->snap_ctime & 0xffffffff;
sn_tab[found].vm_state_size = inode.vm_state_size; sn_tab[found].vm_state_size = inode->vm_state_size;
sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec; sn_tab[found].vm_clock_nsec = inode->vm_clock_nsec;
snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str), snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str),
"%" PRIu32, inode.snap_id); "%" PRIu32, inode->snap_id);
pstrcpy(sn_tab[found].name, pstrcpy(sn_tab[found].name,
MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)), MIN(sizeof(sn_tab[found].name), sizeof(inode->tag)),
inode.tag); inode->tag);
found++; found++;
} }
} }
@ -3014,6 +3004,7 @@ out:
*psn_tab = sn_tab; *psn_tab = sn_tab;
g_free(vdi_inuse); g_free(vdi_inuse);
g_free(inode);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@ -3240,7 +3231,7 @@ static BlockDriver bdrv_sheepdog = {
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength, .bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size, .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
.bdrv_truncate = sd_truncate, .bdrv_co_truncate = sd_co_truncate,
.bdrv_co_readv = sd_co_readv, .bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev, .bdrv_co_writev = sd_co_writev,
@ -3277,7 +3268,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength, .bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size, .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
.bdrv_truncate = sd_truncate, .bdrv_co_truncate = sd_co_truncate,
.bdrv_co_readv = sd_co_readv, .bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev, .bdrv_co_writev = sd_co_writev,
@ -3314,7 +3305,7 @@ static BlockDriver bdrv_sheepdog_unix = {
.bdrv_has_zero_init = bdrv_has_zero_init_1, .bdrv_has_zero_init = bdrv_has_zero_init_1,
.bdrv_getlength = sd_getlength, .bdrv_getlength = sd_getlength,
.bdrv_get_allocated_file_size = sd_get_allocated_file_size, .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
.bdrv_truncate = sd_truncate, .bdrv_co_truncate = sd_co_truncate,
.bdrv_co_readv = sd_co_readv, .bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev, .bdrv_co_writev = sd_co_writev,

View File

@ -25,6 +25,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "block/snapshot.h" #include "block/snapshot.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
@ -214,7 +215,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
bdrv_ref(file); bdrv_ref(file);
qdict_extract_subqdict(options, &file_options, "file."); qdict_extract_subqdict(options, &file_options, "file.");
QDECREF(file_options); qobject_unref(file_options);
qdict_put_str(options, "file", bdrv_get_node_name(file)); qdict_put_str(options, "file", bdrv_get_node_name(file));
drv->bdrv_close(bs); drv->bdrv_close(bs);
@ -223,7 +224,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
ret = bdrv_snapshot_goto(file, snapshot_id, errp); ret = bdrv_snapshot_goto(file, snapshot_id, errp);
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err); open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
QDECREF(options); qobject_unref(options);
if (open_ret < 0) { if (open_ret < 0) {
bdrv_unref(file); bdrv_unref(file);
bs->drv = NULL; bs->drv = NULL;

View File

@ -28,6 +28,7 @@
#include <libssh2_sftp.h> #include <libssh2_sftp.h>
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/option.h" #include "qemu/option.h"
@ -605,7 +606,6 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
BlockdevOptionsSsh *result = NULL; BlockdevOptionsSsh *result = NULL;
QemuOpts *opts = NULL; QemuOpts *opts = NULL;
Error *local_err = NULL; Error *local_err = NULL;
QObject *crumpled;
const QDictEntry *e; const QDictEntry *e;
Visitor *v; Visitor *v;
@ -622,23 +622,13 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
} }
/* Create the QAPI object */ /* Create the QAPI object */
crumpled = qdict_crumple(options, errp); v = qobject_input_visitor_new_flat_confused(options, errp);
if (crumpled == NULL) { if (!v) {
goto fail; goto fail;
} }
/*
* FIXME .numeric, .to, .ipv4 or .ipv6 don't work with -drive.
* .to doesn't matter, it's ignored anyway.
* That's because when @options come from -blockdev or
* blockdev_add, members are typed according to the QAPI schema,
* but when they come from -drive, they're all QString. The
* visitor expects the former.
*/
v = qobject_input_visitor_new(crumpled);
visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err); visit_type_BlockdevOptionsSsh(v, NULL, &result, &local_err);
visit_free(v); visit_free(v);
qobject_decref(crumpled);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -917,7 +907,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts,
ret = ssh_co_create(create_options, errp); ret = ssh_co_create(create_options, errp);
out: out:
QDECREF(uri_options); qobject_unref(uri_options);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;
} }
@ -1164,11 +1154,13 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
static coroutine_fn int ssh_co_writev(BlockDriverState *bs, static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov,
int flags)
{ {
BDRVSSHState *s = bs->opaque; BDRVSSHState *s = bs->opaque;
int ret; int ret;
assert(!flags);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE, ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
nb_sectors * BDRV_SECTOR_SIZE, qiov); nb_sectors * BDRV_SECTOR_SIZE, qiov);
@ -1251,8 +1243,8 @@ static int64_t ssh_getlength(BlockDriverState *bs)
return length; return length;
} }
static int ssh_truncate(BlockDriverState *bs, int64_t offset, static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp) PreallocMode prealloc, Error **errp)
{ {
BDRVSSHState *s = bs->opaque; BDRVSSHState *s = bs->opaque;
@ -1287,7 +1279,7 @@ static BlockDriver bdrv_ssh = {
.bdrv_co_readv = ssh_co_readv, .bdrv_co_readv = ssh_co_readv,
.bdrv_co_writev = ssh_co_writev, .bdrv_co_writev = ssh_co_writev,
.bdrv_getlength = ssh_getlength, .bdrv_getlength = ssh_getlength,
.bdrv_truncate = ssh_truncate, .bdrv_co_truncate = ssh_co_truncate,
.bdrv_co_flush_to_disk = ssh_co_flush, .bdrv_co_flush_to_disk = ssh_co_flush,
.create_opts = &ssh_create_opts, .create_opts = &ssh_create_opts,
}; };

View File

@ -29,11 +29,8 @@ enum {
STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */ STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */
}; };
#define SLICE_TIME 100000000ULL /* ns */
typedef struct StreamBlockJob { typedef struct StreamBlockJob {
BlockJob common; BlockJob common;
RateLimit limit;
BlockDriverState *base; BlockDriverState *base;
BlockdevOnError on_error; BlockdevOnError on_error;
char *backing_file_str; char *backing_file_str;
@ -61,16 +58,16 @@ typedef struct {
int ret; int ret;
} StreamCompleteData; } StreamCompleteData;
static void stream_complete(BlockJob *job, void *opaque) static void stream_complete(Job *job, void *opaque)
{ {
StreamBlockJob *s = container_of(job, StreamBlockJob, common); StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
BlockJob *bjob = &s->common;
StreamCompleteData *data = opaque; StreamCompleteData *data = opaque;
BlockDriverState *bs = blk_bs(job->blk); BlockDriverState *bs = blk_bs(bjob->blk);
BlockDriverState *base = s->base; BlockDriverState *base = s->base;
Error *local_err = NULL; Error *local_err = NULL;
if (!block_job_is_cancelled(&s->common) && bs->backing && if (!job_is_cancelled(job) && bs->backing && data->ret == 0) {
data->ret == 0) {
const char *base_id = NULL, *base_fmt = NULL; const char *base_id = NULL, *base_fmt = NULL;
if (base) { if (base) {
base_id = s->backing_file_str; base_id = s->backing_file_str;
@ -91,12 +88,12 @@ out:
/* Reopen the image back in read-only mode if necessary */ /* Reopen the image back in read-only mode if necessary */
if (s->bs_flags != bdrv_get_flags(bs)) { if (s->bs_flags != bdrv_get_flags(bs)) {
/* Give up write permissions before making it read-only */ /* Give up write permissions before making it read-only */
blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort); blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
bdrv_reopen(bs, s->bs_flags, NULL); bdrv_reopen(bs, s->bs_flags, NULL);
} }
g_free(s->backing_file_str); g_free(s->backing_file_str);
block_job_completed(&s->common, data->ret); job_completed(job, data->ret, NULL);
g_free(data); g_free(data);
} }
@ -107,6 +104,7 @@ static void coroutine_fn stream_run(void *opaque)
BlockBackend *blk = s->common.blk; BlockBackend *blk = s->common.blk;
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
BlockDriverState *base = s->base; BlockDriverState *base = s->base;
int64_t len;
int64_t offset = 0; int64_t offset = 0;
uint64_t delay_ns = 0; uint64_t delay_ns = 0;
int error = 0; int error = 0;
@ -118,11 +116,12 @@ static void coroutine_fn stream_run(void *opaque)
goto out; goto out;
} }
s->common.len = bdrv_getlength(bs); len = bdrv_getlength(bs);
if (s->common.len < 0) { if (len < 0) {
ret = s->common.len; ret = len;
goto out; goto out;
} }
job_progress_set_remaining(&s->common.job, len);
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
@ -135,14 +134,14 @@ static void coroutine_fn stream_run(void *opaque)
bdrv_enable_copy_on_read(bs); bdrv_enable_copy_on_read(bs);
} }
for ( ; offset < s->common.len; offset += n) { for ( ; offset < len; offset += n) {
bool copy; bool copy;
/* Note that even when no rate limit is applied we need to yield /* Note that even when no rate limit is applied we need to yield
* with no pending I/O here so that bdrv_drain_all() returns. * with no pending I/O here so that bdrv_drain_all() returns.
*/ */
block_job_sleep_ns(&s->common, delay_ns); job_sleep_ns(&s->common.job, delay_ns);
if (block_job_is_cancelled(&s->common)) { if (job_is_cancelled(&s->common.job)) {
break; break;
} }
@ -159,7 +158,7 @@ static void coroutine_fn stream_run(void *opaque)
/* Finish early if end of backing file has been reached */ /* Finish early if end of backing file has been reached */
if (ret == 0 && n == 0) { if (ret == 0 && n == 0) {
n = s->common.len - offset; n = len - offset;
} }
copy = (ret == 1); copy = (ret == 1);
@ -185,9 +184,9 @@ static void coroutine_fn stream_run(void *opaque)
ret = 0; ret = 0;
/* Publish progress */ /* Publish progress */
s->common.offset += n; job_progress_update(&s->common.job, n);
if (copy && s->common.speed) { if (copy) {
delay_ns = ratelimit_calculate_delay(&s->limit, n); delay_ns = block_job_ratelimit_get_delay(&s->common, n);
} else { } else {
delay_ns = 0; delay_ns = 0;
} }
@ -206,25 +205,18 @@ out:
/* Modify backing chain and close BDSes in main loop */ /* Modify backing chain and close BDSes in main loop */
data = g_malloc(sizeof(*data)); data = g_malloc(sizeof(*data));
data->ret = ret; data->ret = ret;
block_job_defer_to_main_loop(&s->common, stream_complete, data); job_defer_to_main_loop(&s->common.job, stream_complete, data);
}
static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
StreamBlockJob *s = container_of(job, StreamBlockJob, common);
if (speed < 0) {
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return;
}
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
} }
static const BlockJobDriver stream_job_driver = { static const BlockJobDriver stream_job_driver = {
.instance_size = sizeof(StreamBlockJob), .job_driver = {
.job_type = BLOCK_JOB_TYPE_STREAM, .instance_size = sizeof(StreamBlockJob),
.set_speed = stream_set_speed, .job_type = JOB_TYPE_STREAM,
.start = stream_run, .free = block_job_free,
.start = stream_run,
.user_resume = block_job_user_resume,
.drain = block_job_drain,
},
}; };
void stream_start(const char *job_id, BlockDriverState *bs, void stream_start(const char *job_id, BlockDriverState *bs,
@ -251,7 +243,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
BLK_PERM_GRAPH_MOD, BLK_PERM_GRAPH_MOD,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
BLK_PERM_WRITE, BLK_PERM_WRITE,
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp); speed, JOB_DEFAULT, NULL, NULL, errp);
if (!s) { if (!s) {
goto fail; goto fail;
} }
@ -272,7 +264,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
s->on_error = on_error; s->on_error = on_error;
trace_stream_start(bs, base, s); trace_stream_start(bs, base, s);
block_job_start(&s->common); job_start(&s->common.job);
return; return;
fail: fail:

View File

@ -564,6 +564,10 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
qemu_mutex_lock(&tg->lock); qemu_mutex_lock(&tg->lock);
for (i = 0; i < 2; i++) { for (i = 0; i < 2; 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) { if (tg->tokens[i] == tgm) {
token = throttle_group_next_tgm(tgm); token = throttle_group_next_tgm(tgm);
/* Take care of the case where this is the last tgm in the group */ /* Take care of the case where this is the last tgm in the group */

View File

@ -36,9 +36,12 @@ static QemuOptsList throttle_opts = {
}, },
}; };
static int throttle_configure_tgm(BlockDriverState *bs, /*
ThrottleGroupMember *tgm, * If this function succeeds then the throttle group name is stored in
QDict *options, Error **errp) * @group and must be freed by the caller.
* If there's an error then @group remains unmodified.
*/
static int throttle_parse_options(QDict *options, char **group, Error **errp)
{ {
int ret; int ret;
const char *group_name; const char *group_name;
@ -63,8 +66,7 @@ static int throttle_configure_tgm(BlockDriverState *bs,
goto fin; goto fin;
} }
/* Register membership to group with name group_name */ *group = g_strdup(group_name);
throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
ret = 0; ret = 0;
fin: fin:
qemu_opts_del(opts); qemu_opts_del(opts);
@ -75,16 +77,27 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
int flags, Error **errp) int flags, Error **errp)
{ {
ThrottleGroupMember *tgm = bs->opaque; ThrottleGroupMember *tgm = bs->opaque;
char *group;
int ret;
bs->file = bdrv_open_child(NULL, options, "file", bs, bs->file = bdrv_open_child(NULL, options, "file", bs,
&child_file, false, errp); &child_file, false, errp);
if (!bs->file) { if (!bs->file) {
return -EINVAL; return -EINVAL;
} }
bs->supported_write_flags = bs->file->bs->supported_write_flags; bs->supported_write_flags = bs->file->bs->supported_write_flags |
bs->supported_zero_flags = bs->file->bs->supported_zero_flags; BDRV_REQ_WRITE_UNCHANGED;
bs->supported_zero_flags = bs->file->bs->supported_zero_flags |
BDRV_REQ_WRITE_UNCHANGED;
return throttle_configure_tgm(bs, tgm, options, errp); ret = throttle_parse_options(options, &group, errp);
if (ret == 0) {
/* Register membership to group with name group_name */
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
g_free(group);
}
return ret;
} }
static void throttle_close(BlockDriverState *bs) static void throttle_close(BlockDriverState *bs)
@ -136,7 +149,7 @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs,
ThrottleGroupMember *tgm = bs->opaque; ThrottleGroupMember *tgm = bs->opaque;
throttle_group_co_io_limits_intercept(tgm, bytes, true); throttle_group_co_io_limits_intercept(tgm, bytes, true);
return bdrv_co_pdiscard(bs->file->bs, offset, bytes); return bdrv_co_pdiscard(bs->file, offset, bytes);
} }
static int throttle_co_flush(BlockDriverState *bs) static int throttle_co_flush(BlockDriverState *bs)
@ -160,35 +173,36 @@ static void throttle_attach_aio_context(BlockDriverState *bs,
static int throttle_reopen_prepare(BDRVReopenState *reopen_state, static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
BlockReopenQueue *queue, Error **errp) BlockReopenQueue *queue, Error **errp)
{ {
ThrottleGroupMember *tgm; int ret;
char *group = NULL;
assert(reopen_state != NULL); assert(reopen_state != NULL);
assert(reopen_state->bs != NULL); assert(reopen_state->bs != NULL);
reopen_state->opaque = g_new0(ThrottleGroupMember, 1); ret = throttle_parse_options(reopen_state->options, &group, errp);
tgm = reopen_state->opaque; reopen_state->opaque = group;
return ret;
return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
errp);
} }
static void throttle_reopen_commit(BDRVReopenState *reopen_state) static void throttle_reopen_commit(BDRVReopenState *reopen_state)
{ {
ThrottleGroupMember *old_tgm = reopen_state->bs->opaque; BlockDriverState *bs = reopen_state->bs;
ThrottleGroupMember *new_tgm = reopen_state->opaque; ThrottleGroupMember *tgm = bs->opaque;
char *group = reopen_state->opaque;
throttle_group_unregister_tgm(old_tgm); assert(group);
g_free(old_tgm);
reopen_state->bs->opaque = new_tgm; if (strcmp(group, throttle_group_get_name(tgm))) {
throttle_group_unregister_tgm(tgm);
throttle_group_register_tgm(tgm, group, bdrv_get_aio_context(bs));
}
g_free(reopen_state->opaque);
reopen_state->opaque = NULL; reopen_state->opaque = NULL;
} }
static void throttle_reopen_abort(BDRVReopenState *reopen_state) static void throttle_reopen_abort(BDRVReopenState *reopen_state)
{ {
ThrottleGroupMember *tgm = reopen_state->opaque; g_free(reopen_state->opaque);
throttle_group_unregister_tgm(tgm);
g_free(tgm);
reopen_state->opaque = NULL; reopen_state->opaque = NULL;
} }

View File

@ -4,11 +4,6 @@
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\"" bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\""
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
# blockjob.c
block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
# block/block-backend.c # block/block-backend.c
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
@ -20,6 +15,8 @@ bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs
bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x"
bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x" bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x"
bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64 bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, int64_t cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %"PRId64
bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x"
bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x"
# block/stream.c # block/stream.c
stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d"
@ -47,6 +44,7 @@ backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64
backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64 backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
# blockdev.c # blockdev.c
qmp_block_job_cancel(void *job) "job %p" qmp_block_job_cancel(void *job) "job %p"
@ -59,8 +57,9 @@ qmp_block_stream(void *bs, void *job) "bs %p job %p"
# block/file-win32.c # block/file-win32.c
# block/file-posix.c # block/file-posix.c
paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d"
paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d"
file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64
# block/qcow2.c # block/qcow2.c
qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d"
@ -154,3 +153,6 @@ nvme_free_req_queue_wait(void *q) "q %p"
nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d" nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p cmd %p req %p qiov %p entries %d"
nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64
nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d"
# block/iscsi.c
iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d"

View File

@ -50,11 +50,12 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/units.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qobject-input-visitor.h" #include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-block-core.h" #include "qapi/qapi-visit-block-core.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
@ -83,9 +84,6 @@
/* Command line option for static images. */ /* Command line option for static images. */
#define BLOCK_OPT_STATIC "static" #define BLOCK_OPT_STATIC "static"
#define KiB 1024
#define MiB (KiB * KiB)
#define SECTOR_SIZE 512 #define SECTOR_SIZE 512
#define DEFAULT_CLUSTER_SIZE (1 * MiB) #define DEFAULT_CLUSTER_SIZE (1 * MiB)
@ -434,7 +432,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} else if (header.block_size != DEFAULT_CLUSTER_SIZE) { } else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
error_setg(errp, "unsupported VDI image (block size %" PRIu32 error_setg(errp, "unsupported VDI image (block size %" PRIu32
" is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE); " is not %" PRIu64 ")",
header.block_size, DEFAULT_CLUSTER_SIZE);
ret = -ENOTSUP; ret = -ENOTSUP;
goto fail; goto fail;
} else if (header.disk_size > } else if (header.disk_size >
@ -865,6 +864,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
} }
} }
ret = 0;
exit: exit:
blk_unref(blk); blk_unref(blk);
bdrv_unref(bs_file); bdrv_unref(bs_file);
@ -933,7 +933,11 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
} }
/* Get the QAPI object */ /* Get the QAPI object */
v = qobject_input_visitor_new_keyval(QOBJECT(qdict)); v = qobject_input_visitor_new_flat_confused(qdict, errp);
if (!v) {
ret = -EINVAL;
goto done;
}
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
visit_free(v); visit_free(v);
@ -951,7 +955,7 @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
/* Create the vdi image (format layer) */ /* Create the vdi image (format layer) */
ret = vdi_co_do_create(create_options, block_size, errp); ret = vdi_co_do_create(create_options, block_size, errp);
done: done:
QDECREF(qdict); qobject_unref(qdict);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
bdrv_unref(bs_file); bdrv_unref(bs_file);
return ret; return ret;

View File

@ -19,7 +19,7 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "block/vhdx.h" #include "vhdx.h"
/* /*
* All the VHDX formats on disk are little endian - the following * All the VHDX formats on disk are little endian - the following

View File

@ -24,7 +24,7 @@
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "block/vhdx.h" #include "vhdx.h"
typedef struct VHDXLogSequence { typedef struct VHDXLogSequence {

View File

@ -18,12 +18,13 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/crc32c.h" #include "qemu/crc32c.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
#include "block/vhdx.h" #include "vhdx.h"
#include "migration/blocker.h" #include "migration/blocker.h"
#include "qemu/uuid.h" #include "qemu/uuid.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
@ -184,7 +185,7 @@ uint32_t vhdx_checksum_calc(uint32_t crc, uint8_t *buf, size_t size,
/* Validates the checksum of the buffer, with an in-place CRC. /* Validates the checksum of the buffer, with an in-place CRC.
* *
* Zero is substituted during crc calculation for the original crc field, * Zero is substituted during crc calculation for the original crc field,
* and the crc field is restored afterwards. But the buffer will be modifed * and the crc field is restored afterwards. But the buffer will be modified
* during the calculation, so this may not be not suitable for multi-threaded * during the calculation, so this may not be not suitable for multi-threaded
* use. * use.
* *
@ -1126,9 +1127,9 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num,
break; break;
case PAYLOAD_BLOCK_FULLY_PRESENT: case PAYLOAD_BLOCK_FULLY_PRESENT:
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
ret = bdrv_co_readv(bs->file, ret = bdrv_co_preadv(bs->file, sinfo.file_offset,
sinfo.file_offset >> BDRV_SECTOR_BITS, sinfo.sectors_avail * BDRV_SECTOR_SIZE,
sinfo.sectors_avail, &hd_qiov); &hd_qiov, 0);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
goto exit; goto exit;
@ -1226,7 +1227,8 @@ int vhdx_user_visible_write(BlockDriverState *bs, BDRVVHDXState *s)
} }
static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num, static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov) int nb_sectors, QEMUIOVector *qiov,
int flags)
{ {
int ret = -ENOTSUP; int ret = -ENOTSUP;
BDRVVHDXState *s = bs->opaque; BDRVVHDXState *s = bs->opaque;
@ -1242,6 +1244,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
uint64_t bat_prior_offset = 0; uint64_t bat_prior_offset = 0;
bool bat_update = false; bool bat_update = false;
assert(!flags);
qemu_iovec_init(&hd_qiov, qiov->niov); qemu_iovec_init(&hd_qiov, qiov->niov);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
@ -1346,9 +1349,9 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
} }
/* block exists, so we can just overwrite it */ /* block exists, so we can just overwrite it */
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
ret = bdrv_co_writev(bs->file, ret = bdrv_co_pwritev(bs->file, sinfo.file_offset,
sinfo.file_offset >> BDRV_SECTOR_BITS, sectors_to_write * BDRV_SECTOR_SIZE,
sectors_to_write, &hd_qiov); &hd_qiov, 0);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
goto error_bat_restore; goto error_bat_restore;
@ -1949,7 +1952,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
goto delete_and_exit; goto delete_and_exit;
} }
ret = 0;
delete_and_exit: delete_and_exit:
blk_unref(blk); blk_unref(blk);
bdrv_unref(bs); bdrv_unref(bs);
@ -1962,8 +1965,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename,
Error **errp) Error **errp)
{ {
BlockdevCreateOptions *create_options = NULL; BlockdevCreateOptions *create_options = NULL;
QDict *qdict = NULL; QDict *qdict;
QObject *qobj;
Visitor *v; Visitor *v;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
Error *local_err = NULL; Error *local_err = NULL;
@ -2002,15 +2004,12 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename,
qdict_put_str(qdict, "driver", "vhdx"); qdict_put_str(qdict, "driver", "vhdx");
qdict_put_str(qdict, "file", bs->node_name); qdict_put_str(qdict, "file", bs->node_name);
qobj = qdict_crumple(qdict, errp); v = qobject_input_visitor_new_flat_confused(qdict, errp);
QDECREF(qdict); if (!v) {
qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
visit_free(v); visit_free(v);
@ -2049,7 +2048,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename,
ret = vhdx_co_create(create_options, errp); ret = vhdx_co_create(create_options, errp);
fail: fail:
QDECREF(qdict); qobject_unref(qdict);
bdrv_unref(bs); bdrv_unref(bs);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;

View File

@ -333,6 +333,12 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
if (!s->cid_checked && bs->backing) { if (!s->cid_checked && bs->backing) {
BlockDriverState *p_bs = bs->backing->bs; BlockDriverState *p_bs = bs->backing->bs;
if (strcmp(p_bs->drv->format_name, "vmdk")) {
/* Backing file is not in vmdk format, so it does not have
* a CID, which makes the overlay's parent CID invalid */
return 0;
}
if (vmdk_read_cid(p_bs, 0, &cur_pcid) != 0) { if (vmdk_read_cid(p_bs, 0, &cur_pcid) != 0) {
/* read failure: report as not valid */ /* read failure: report as not valid */
return 0; return 0;

View File

@ -26,6 +26,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "sysemu/block-backend.h" #include "sysemu/block-backend.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
@ -1080,8 +1081,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
QemuOpts *opts, Error **errp) QemuOpts *opts, Error **errp)
{ {
BlockdevCreateOptions *create_options = NULL; BlockdevCreateOptions *create_options = NULL;
QDict *qdict = NULL; QDict *qdict;
QObject *qobj;
Visitor *v; Visitor *v;
BlockDriverState *bs = NULL; BlockDriverState *bs = NULL;
Error *local_err = NULL; Error *local_err = NULL;
@ -1118,15 +1118,12 @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
qdict_put_str(qdict, "driver", "vpc"); qdict_put_str(qdict, "driver", "vpc");
qdict_put_str(qdict, "file", bs->node_name); qdict_put_str(qdict, "file", bs->node_name);
qobj = qdict_crumple(qdict, errp); v = qobject_input_visitor_new_flat_confused(qdict, errp);
QDECREF(qdict); if (!v) {
qdict = qobject_to(QDict, qobj);
if (qdict == NULL) {
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err); visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
visit_free(v); visit_free(v);
@ -1157,7 +1154,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
ret = vpc_co_create(create_options, errp); ret = vpc_co_create(create_options, errp);
fail: fail:
QDECREF(qdict); qobject_unref(qdict);
bdrv_unref(bs); bdrv_unref(bs);
qapi_free_BlockdevCreateOptions(create_options); qapi_free_BlockdevCreateOptions(create_options);
return ret; return ret;

View File

@ -27,6 +27,7 @@
#include <dirent.h> #include <dirent.h>
#include "qapi/error.h" #include "qapi/error.h"
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu/option.h" #include "qemu/option.h"
#include "qemu/bswap.h" #include "qemu/bswap.h"
@ -1244,8 +1245,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
s->fat2 = NULL; s->fat2 = NULL;
s->downcase_short_names = 1; s->downcase_short_names = 1;
fprintf(stderr, "vvfat %s chs %d,%d,%d\n", DLOG(fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
dirname, cyls, heads, secs); dirname, cyls, heads, secs));
s->sector_count = cyls * heads * secs - s->offset_to_bootsector; s->sector_count = cyls * heads * secs - s->offset_to_bootsector;
@ -3133,6 +3134,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options,
} }
static const BdrvChildRole child_vvfat_qcow = { static const BdrvChildRole child_vvfat_qcow = {
.parent_is_bds = true,
.inherit_options = vvfat_qcow_options, .inherit_options = vvfat_qcow_options,
}; };
@ -3179,7 +3181,7 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
qdict_put_str(options, "write-target.driver", "qcow"); qdict_put_str(options, "write-target.driver", "qcow");
s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs, s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
&child_vvfat_qcow, false, errp); &child_vvfat_qcow, false, errp);
QDECREF(options); qobject_unref(options);
if (!s->qcow) { if (!s->qcow) {
ret = -EINVAL; ret = -EINVAL;
goto err; goto err;

View File

@ -12,6 +12,7 @@
#include <qnio/qnio_api.h> #include <qnio/qnio_api.h>
#include <sys/param.h> #include <sys/param.h>
#include "block/block_int.h" #include "block/block_int.h"
#include "block/qdict.h"
#include "qapi/qmp/qerror.h" #include "qapi/qmp/qerror.h"
#include "qapi/qmp/qdict.h" #include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h" #include "qapi/qmp/qstring.h"
@ -216,6 +217,12 @@ static void vxhs_parse_filename(const char *filename, QDict *options,
} }
} }
static void vxhs_refresh_limits(BlockDriverState *bs, Error **errp)
{
/* XXX Does VXHS support AIO on less than 512-byte alignment? */
bs->bl.request_alignment = 512;
}
static int vxhs_init_and_ref(void) static int vxhs_init_and_ref(void)
{ {
if (vxhs_ref++ == 0) { if (vxhs_ref++ == 0) {
@ -396,7 +403,7 @@ static int vxhs_open(BlockDriverState *bs, QDict *options,
out: out:
g_free(of_vsa_addr); g_free(of_vsa_addr);
QDECREF(backing_options); qobject_unref(backing_options);
qemu_opts_del(tcp_opts); qemu_opts_del(tcp_opts);
qemu_opts_del(opts); qemu_opts_del(opts);
g_free(cacert); g_free(cacert);
@ -424,21 +431,17 @@ static const AIOCBInfo vxhs_aiocb_info = {
* and is passed to QNIO. When QNIO completes the work, * and is passed to QNIO. When QNIO completes the work,
* it will be passed back through the callback. * it will be passed back through the callback.
*/ */
static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num, static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset,
QEMUIOVector *qiov, int nb_sectors, QEMUIOVector *qiov, uint64_t size,
BlockCompletionFunc *cb, void *opaque, BlockCompletionFunc *cb, void *opaque,
VDISKAIOCmd iodir) VDISKAIOCmd iodir)
{ {
VXHSAIOCB *acb = NULL; VXHSAIOCB *acb = NULL;
BDRVVXHSState *s = bs->opaque; BDRVVXHSState *s = bs->opaque;
size_t size;
uint64_t offset;
int iio_flags = 0; int iio_flags = 0;
int ret = 0; int ret = 0;
void *dev_handle = s->vdisk_hostinfo.dev_handle; void *dev_handle = s->vdisk_hostinfo.dev_handle;
offset = sector_num * BDRV_SECTOR_SIZE;
size = nb_sectors * BDRV_SECTOR_SIZE;
acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque); acb = qemu_aio_get(&vxhs_aiocb_info, bs, cb, opaque);
/* /*
@ -451,11 +454,11 @@ static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num,
switch (iodir) { switch (iodir) {
case VDISK_AIO_WRITE: case VDISK_AIO_WRITE:
ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov, ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov,
offset, (uint64_t)size, iio_flags); offset, size, iio_flags);
break; break;
case VDISK_AIO_READ: case VDISK_AIO_READ:
ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov, ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov,
offset, (uint64_t)size, iio_flags); offset, size, iio_flags);
break; break;
default: default:
trace_vxhs_aio_rw_invalid(iodir); trace_vxhs_aio_rw_invalid(iodir);
@ -474,22 +477,20 @@ errout:
return NULL; return NULL;
} }
static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs, static BlockAIOCB *vxhs_aio_preadv(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, uint64_t offset, uint64_t bytes,
int nb_sectors, QEMUIOVector *qiov, int flags,
BlockCompletionFunc *cb, void *opaque) BlockCompletionFunc *cb, void *opaque)
{ {
return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb, return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_READ);
opaque, VDISK_AIO_READ);
} }
static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs, static BlockAIOCB *vxhs_aio_pwritev(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, uint64_t offset, uint64_t bytes,
int nb_sectors, QEMUIOVector *qiov, int flags,
BlockCompletionFunc *cb, void *opaque) BlockCompletionFunc *cb, void *opaque)
{ {
return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_WRITE);
cb, opaque, VDISK_AIO_WRITE);
} }
static void vxhs_close(BlockDriverState *bs) static void vxhs_close(BlockDriverState *bs)
@ -561,10 +562,11 @@ static BlockDriver bdrv_vxhs = {
.instance_size = sizeof(BDRVVXHSState), .instance_size = sizeof(BDRVVXHSState),
.bdrv_file_open = vxhs_open, .bdrv_file_open = vxhs_open,
.bdrv_parse_filename = vxhs_parse_filename, .bdrv_parse_filename = vxhs_parse_filename,
.bdrv_refresh_limits = vxhs_refresh_limits,
.bdrv_close = vxhs_close, .bdrv_close = vxhs_close,
.bdrv_getlength = vxhs_getlength, .bdrv_getlength = vxhs_getlength,
.bdrv_aio_readv = vxhs_aio_readv, .bdrv_aio_preadv = vxhs_aio_preadv,
.bdrv_aio_writev = vxhs_aio_writev, .bdrv_aio_pwritev = vxhs_aio_pwritev,
}; };
static void bdrv_vxhs_init(void) static void bdrv_vxhs_init(void)

View File

@ -112,15 +112,14 @@ static const AIOCBInfo win32_aiocb_info = {
BlockAIOCB *win32_aio_submit(BlockDriverState *bs, BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
QEMUWin32AIOState *aio, HANDLE hfile, QEMUWin32AIOState *aio, HANDLE hfile,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov,
BlockCompletionFunc *cb, void *opaque, int type) BlockCompletionFunc *cb, void *opaque, int type)
{ {
struct QEMUWin32AIOCB *waiocb; struct QEMUWin32AIOCB *waiocb;
uint64_t offset = sector_num * 512;
DWORD rc; DWORD rc;
waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque); waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque);
waiocb->nbytes = nb_sectors * 512; waiocb->nbytes = bytes;
waiocb->qiov = qiov; waiocb->qiov = qiov;
waiocb->is_read = (type == QEMU_AIO_READ); waiocb->is_read = (type == QEMU_AIO_READ);

View File

@ -220,3 +220,26 @@ void qmp_nbd_server_stop(Error **errp)
nbd_server_free(nbd_server); nbd_server_free(nbd_server);
nbd_server = NULL; nbd_server = NULL;
} }
void qmp_x_nbd_server_add_bitmap(const char *name, const char *bitmap,
bool has_bitmap_export_name,
const char *bitmap_export_name,
Error **errp)
{
NBDExport *exp;
if (!nbd_server) {
error_setg(errp, "NBD server not running");
return;
}
exp = nbd_export_find(name);
if (exp == NULL) {
error_setg(errp, "Export '%s' is not found", name);
return;
}
nbd_export_bitmap(exp, bitmap,
has_bitmap_export_name ? bitmap_export_name : bitmap,
errp);
}

View File

@ -35,6 +35,7 @@
#include "sysemu/blockdev.h" #include "sysemu/blockdev.h"
#include "hw/block/block.h" #include "hw/block/block.h"
#include "block/blockjob.h" #include "block/blockjob.h"
#include "block/qdict.h"
#include "block/throttle-groups.h" #include "block/throttle-groups.h"
#include "monitor/monitor.h" #include "monitor/monitor.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
@ -150,7 +151,7 @@ void blockdev_mark_auto_del(BlockBackend *blk)
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
if (bs->job) { if (bs->job) {
block_job_cancel(bs->job, false); job_cancel(&bs->job->job, false);
} }
aio_context_release(aio_context); aio_context_release(aio_context);
@ -576,7 +577,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
blk_rs->read_only = read_only; blk_rs->read_only = read_only;
blk_rs->detect_zeroes = detect_zeroes; blk_rs->detect_zeroes = detect_zeroes;
QDECREF(bs_opts); qobject_unref(bs_opts);
} else { } else {
if (file && !*file) { if (file && !*file) {
file = NULL; file = NULL;
@ -632,16 +633,16 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
err_no_bs_opts: err_no_bs_opts:
qemu_opts_del(opts); qemu_opts_del(opts);
QDECREF(interval_dict); qobject_unref(interval_dict);
QDECREF(interval_list); qobject_unref(interval_list);
return blk; return blk;
early_err: early_err:
qemu_opts_del(opts); qemu_opts_del(opts);
QDECREF(interval_dict); qobject_unref(interval_dict);
QDECREF(interval_list); qobject_unref(interval_list);
err_no_opts: err_no_opts:
QDECREF(bs_opts); qobject_unref(bs_opts);
return NULL; return NULL;
} }
@ -1139,7 +1140,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
fail: fail:
qemu_opts_del(legacy_opts); qemu_opts_del(legacy_opts);
QDECREF(bs_opts); qobject_unref(bs_opts);
return dinfo; return dinfo;
} }
@ -1455,7 +1456,7 @@ typedef struct BlkActionOps {
struct BlkActionState { struct BlkActionState {
TransactionAction *action; TransactionAction *action;
const BlkActionOps *ops; const BlkActionOps *ops;
BlockJobTxn *block_job_txn; JobTxn *block_job_txn;
TransactionProperties *txn_props; TransactionProperties *txn_props;
QSIMPLEQ_ENTRY(BlkActionState) entry; QSIMPLEQ_ENTRY(BlkActionState) entry;
}; };
@ -1873,7 +1874,7 @@ typedef struct DriveBackupState {
BlockJob *job; BlockJob *job;
} DriveBackupState; } DriveBackupState;
static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
Error **errp); Error **errp);
static void drive_backup_prepare(BlkActionState *common, Error **errp) static void drive_backup_prepare(BlkActionState *common, Error **errp)
@ -1919,7 +1920,7 @@ static void drive_backup_commit(BlkActionState *common)
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
assert(state->job); assert(state->job);
block_job_start(state->job); job_start(&state->job->job);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -1934,7 +1935,7 @@ static void drive_backup_abort(BlkActionState *common)
aio_context = bdrv_get_aio_context(state->bs); aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
block_job_cancel_sync(state->job); job_cancel_sync(&state->job->job);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -1963,7 +1964,7 @@ typedef struct BlockdevBackupState {
BlockJob *job; BlockJob *job;
} BlockdevBackupState; } BlockdevBackupState;
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
Error **errp); Error **errp);
static void blockdev_backup_prepare(BlkActionState *common, Error **errp) static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
@ -1977,7 +1978,7 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP); assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
backup = common->action->u.blockdev_backup.data; backup = common->action->u.blockdev_backup.data;
bs = qmp_get_root_bs(backup->device, errp); bs = bdrv_lookup_bs(backup->device, backup->device, errp);
if (!bs) { if (!bs) {
return; return;
} }
@ -2017,7 +2018,7 @@ static void blockdev_backup_commit(BlkActionState *common)
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
assert(state->job); assert(state->job);
block_job_start(state->job); job_start(&state->job->job);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -2032,7 +2033,7 @@ static void blockdev_backup_abort(BlkActionState *common)
aio_context = bdrv_get_aio_context(state->bs); aio_context = bdrv_get_aio_context(state->bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
block_job_cancel_sync(state->job); job_cancel_sync(&state->job->job);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -2061,6 +2062,7 @@ typedef struct BlockDirtyBitmapState {
BlockDriverState *bs; BlockDriverState *bs;
HBitmap *backup; HBitmap *backup;
bool prepared; bool prepared;
bool was_enabled;
} BlockDirtyBitmapState; } BlockDirtyBitmapState;
static void block_dirty_bitmap_add_prepare(BlkActionState *common, static void block_dirty_bitmap_add_prepare(BlkActionState *common,
@ -2081,6 +2083,7 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common,
action->has_granularity, action->granularity, action->has_granularity, action->granularity,
action->has_persistent, action->persistent, action->has_persistent, action->persistent,
action->has_autoload, action->autoload, action->has_autoload, action->autoload,
action->has_x_disabled, action->x_disabled,
&local_err); &local_err);
if (!local_err) { if (!local_err) {
@ -2160,6 +2163,74 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common)
hbitmap_free(state->backup); hbitmap_free(state->backup);
} }
static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
Error **errp)
{
BlockDirtyBitmap *action;
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common);
if (action_check_completion_mode(common, errp) < 0) {
return;
}
action = common->action->u.x_block_dirty_bitmap_enable.data;
state->bitmap = block_dirty_bitmap_lookup(action->node,
action->name,
NULL,
errp);
if (!state->bitmap) {
return;
}
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
bdrv_enable_dirty_bitmap(state->bitmap);
}
static void block_dirty_bitmap_enable_abort(BlkActionState *common)
{
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common);
if (!state->was_enabled) {
bdrv_disable_dirty_bitmap(state->bitmap);
}
}
static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
Error **errp)
{
BlockDirtyBitmap *action;
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common);
if (action_check_completion_mode(common, errp) < 0) {
return;
}
action = common->action->u.x_block_dirty_bitmap_disable.data;
state->bitmap = block_dirty_bitmap_lookup(action->node,
action->name,
NULL,
errp);
if (!state->bitmap) {
return;
}
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
bdrv_disable_dirty_bitmap(state->bitmap);
}
static void block_dirty_bitmap_disable_abort(BlkActionState *common)
{
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common);
if (state->was_enabled) {
bdrv_enable_dirty_bitmap(state->bitmap);
}
}
static void abort_prepare(BlkActionState *common, Error **errp) static void abort_prepare(BlkActionState *common, Error **errp)
{ {
error_setg(errp, "Transaction aborted using Abort action"); error_setg(errp, "Transaction aborted using Abort action");
@ -2220,7 +2291,17 @@ static const BlkActionOps actions[] = {
.prepare = block_dirty_bitmap_clear_prepare, .prepare = block_dirty_bitmap_clear_prepare,
.commit = block_dirty_bitmap_clear_commit, .commit = block_dirty_bitmap_clear_commit,
.abort = block_dirty_bitmap_clear_abort, .abort = block_dirty_bitmap_clear_abort,
} },
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = {
.instance_size = sizeof(BlockDirtyBitmapState),
.prepare = block_dirty_bitmap_enable_prepare,
.abort = block_dirty_bitmap_enable_abort,
},
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = {
.instance_size = sizeof(BlockDirtyBitmapState),
.prepare = block_dirty_bitmap_disable_prepare,
.abort = block_dirty_bitmap_disable_abort,
}
}; };
/** /**
@ -2252,7 +2333,7 @@ void qmp_transaction(TransactionActionList *dev_list,
Error **errp) Error **errp)
{ {
TransactionActionList *dev_entry = dev_list; TransactionActionList *dev_entry = dev_list;
BlockJobTxn *block_job_txn = NULL; JobTxn *block_job_txn = NULL;
BlkActionState *state, *next; BlkActionState *state, *next;
Error *local_err = NULL; Error *local_err = NULL;
@ -2260,11 +2341,11 @@ void qmp_transaction(TransactionActionList *dev_list,
QSIMPLEQ_INIT(&snap_bdrv_states); QSIMPLEQ_INIT(&snap_bdrv_states);
/* Does this transaction get canceled as a group on failure? /* Does this transaction get canceled as a group on failure?
* If not, we don't really need to make a BlockJobTxn. * If not, we don't really need to make a JobTxn.
*/ */
props = get_transaction_properties(props); props = get_transaction_properties(props);
if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) { if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
block_job_txn = block_job_txn_new(); block_job_txn = job_txn_new();
} }
/* drain all i/o before any operations */ /* drain all i/o before any operations */
@ -2323,7 +2404,7 @@ exit:
if (!has_props) { if (!has_props) {
qapi_free_TransactionProperties(props); qapi_free_TransactionProperties(props);
} }
block_job_txn_unref(block_job_txn); job_txn_unref(block_job_txn);
} }
void qmp_eject(bool has_device, const char *device, void qmp_eject(bool has_device, const char *device,
@ -2810,6 +2891,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
bool has_granularity, uint32_t granularity, bool has_granularity, uint32_t granularity,
bool has_persistent, bool persistent, bool has_persistent, bool persistent,
bool has_autoload, bool autoload, bool has_autoload, bool autoload,
bool has_disabled, bool disabled,
Error **errp) Error **errp)
{ {
BlockDriverState *bs; BlockDriverState *bs;
@ -2844,6 +2926,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
warn_report("Autoload option is deprecated and its value is ignored"); warn_report("Autoload option is deprecated and its value is ignored");
} }
if (!has_disabled) {
disabled = false;
}
if (persistent && if (persistent &&
!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp))
{ {
@ -2855,6 +2941,10 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
return; return;
} }
if (disabled) {
bdrv_disable_dirty_bitmap(bitmap);
}
bdrv_dirty_bitmap_set_persistance(bitmap, persistent); bdrv_dirty_bitmap_set_persistance(bitmap, persistent);
} }
@ -2890,7 +2980,6 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name,
} }
} }
bdrv_dirty_bitmap_make_anon(bitmap);
bdrv_release_dirty_bitmap(bs, bitmap); bdrv_release_dirty_bitmap(bs, bitmap);
} }
@ -2932,6 +3021,78 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
bdrv_clear_dirty_bitmap(bitmap, NULL); bdrv_clear_dirty_bitmap(bitmap, NULL);
} }
void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name,
Error **errp)
{
BlockDriverState *bs;
BdrvDirtyBitmap *bitmap;
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
if (!bitmap) {
return;
}
if (bdrv_dirty_bitmap_frozen(bitmap)) {
error_setg(errp,
"Bitmap '%s' is currently frozen and cannot be enabled",
name);
return;
}
bdrv_enable_dirty_bitmap(bitmap);
}
void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name,
Error **errp)
{
BlockDriverState *bs;
BdrvDirtyBitmap *bitmap;
bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp);
if (!bitmap) {
return;
}
if (bdrv_dirty_bitmap_frozen(bitmap)) {
error_setg(errp,
"Bitmap '%s' is currently frozen and cannot be disabled",
name);
return;
}
bdrv_disable_dirty_bitmap(bitmap);
}
void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name,
const char *src_name, Error **errp)
{
BlockDriverState *bs;
BdrvDirtyBitmap *dst, *src;
dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp);
if (!dst) {
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, errp);
}
BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
const char *name, const char *name,
Error **errp) Error **errp)
@ -3253,7 +3414,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
goto out; goto out;
} }
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
BLOCK_JOB_DEFAULT, speed, on_error, JOB_DEFAULT, speed, on_error,
filter_node_name, NULL, NULL, false, &local_err); filter_node_name, NULL, NULL, false, &local_err);
} else { } else {
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
@ -3273,7 +3434,7 @@ out:
aio_context_release(aio_context); aio_context_release(aio_context);
} }
static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
Error **errp) Error **errp)
{ {
BlockDriverState *bs; BlockDriverState *bs;
@ -3284,7 +3445,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
AioContext *aio_context; AioContext *aio_context;
QDict *options = NULL; QDict *options = NULL;
Error *local_err = NULL; Error *local_err = NULL;
int flags, job_flags = BLOCK_JOB_DEFAULT; int flags, job_flags = JOB_DEFAULT;
int64_t size; int64_t size;
bool set_backing_hd = false; bool set_backing_hd = false;
@ -3407,10 +3568,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
} }
} }
if (!backup->auto_finalize) { if (!backup->auto_finalize) {
job_flags |= BLOCK_JOB_MANUAL_FINALIZE; job_flags |= JOB_MANUAL_FINALIZE;
} }
if (!backup->auto_dismiss) { if (!backup->auto_dismiss) {
job_flags |= BLOCK_JOB_MANUAL_DISMISS; job_flags |= JOB_MANUAL_DISMISS;
} }
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
@ -3434,7 +3595,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp)
BlockJob *job; BlockJob *job;
job = do_drive_backup(arg, NULL, errp); job = do_drive_backup(arg, NULL, errp);
if (job) { if (job) {
block_job_start(job); job_start(&job->job);
} }
} }
@ -3443,7 +3604,7 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
return bdrv_named_nodes_list(errp); return bdrv_named_nodes_list(errp);
} }
BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
Error **errp) Error **errp)
{ {
BlockDriverState *bs; BlockDriverState *bs;
@ -3451,7 +3612,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
Error *local_err = NULL; Error *local_err = NULL;
AioContext *aio_context; AioContext *aio_context;
BlockJob *job = NULL; BlockJob *job = NULL;
int job_flags = BLOCK_JOB_DEFAULT; int job_flags = JOB_DEFAULT;
if (!backup->has_speed) { if (!backup->has_speed) {
backup->speed = 0; backup->speed = 0;
@ -3475,7 +3636,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
backup->compress = false; backup->compress = false;
} }
bs = qmp_get_root_bs(backup->device, errp); bs = bdrv_lookup_bs(backup->device, backup->device, errp);
if (!bs) { if (!bs) {
return NULL; return NULL;
} }
@ -3500,10 +3661,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
} }
} }
if (!backup->auto_finalize) { if (!backup->auto_finalize) {
job_flags |= BLOCK_JOB_MANUAL_FINALIZE; job_flags |= JOB_MANUAL_FINALIZE;
} }
if (!backup->auto_dismiss) { if (!backup->auto_dismiss) {
job_flags |= BLOCK_JOB_MANUAL_DISMISS; job_flags |= JOB_MANUAL_DISMISS;
} }
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
backup->sync, NULL, backup->compress, backup->sync, NULL, backup->compress,
@ -3522,7 +3683,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
BlockJob *job; BlockJob *job;
job = do_blockdev_backup(arg, NULL, errp); job = do_blockdev_backup(arg, NULL, errp);
if (job) { if (job) {
block_job_start(job); job_start(&job->job);
} }
} }
@ -3544,6 +3705,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
bool has_unmap, bool unmap, bool has_unmap, bool unmap,
bool has_filter_node_name, bool has_filter_node_name,
const char *filter_node_name, const char *filter_node_name,
bool has_copy_mode, MirrorCopyMode copy_mode,
Error **errp) Error **errp)
{ {
@ -3568,6 +3730,9 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
if (!has_filter_node_name) { if (!has_filter_node_name) {
filter_node_name = NULL; filter_node_name = NULL;
} }
if (!has_copy_mode) {
copy_mode = MIRROR_COPY_MODE_BACKGROUND;
}
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
@ -3598,7 +3763,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
has_replaces ? replaces : NULL, has_replaces ? replaces : NULL,
speed, granularity, buf_size, sync, backing_mode, speed, granularity, buf_size, sync, backing_mode,
on_source_error, on_target_error, unmap, filter_node_name, on_source_error, on_target_error, unmap, filter_node_name,
errp); copy_mode, errp);
} }
void qmp_drive_mirror(DriveMirror *arg, Error **errp) void qmp_drive_mirror(DriveMirror *arg, Error **errp)
@ -3744,6 +3909,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)
arg->has_on_target_error, arg->on_target_error, arg->has_on_target_error, arg->on_target_error,
arg->has_unmap, arg->unmap, arg->has_unmap, arg->unmap,
false, NULL, false, NULL,
arg->has_copy_mode, arg->copy_mode,
&local_err); &local_err);
bdrv_unref(target_bs); bdrv_unref(target_bs);
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -3764,6 +3930,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool has_filter_node_name, bool has_filter_node_name,
const char *filter_node_name, const char *filter_node_name,
bool has_copy_mode, MirrorCopyMode copy_mode,
Error **errp) Error **errp)
{ {
BlockDriverState *bs; BlockDriverState *bs;
@ -3796,6 +3963,7 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
has_on_target_error, on_target_error, has_on_target_error, on_target_error,
true, true, true, true,
has_filter_node_name, filter_node_name, has_filter_node_name, filter_node_name,
has_copy_mode, copy_mode,
&local_err); &local_err);
error_propagate(errp, local_err); error_propagate(errp, local_err);
@ -3853,14 +4021,14 @@ void qmp_block_job_cancel(const char *device,
force = false; force = false;
} }
if (block_job_user_paused(job) && !force) { if (job_user_paused(&job->job) && !force) {
error_setg(errp, "The block job for device '%s' is currently paused", error_setg(errp, "The block job for device '%s' is currently paused",
device); device);
goto out; goto out;
} }
trace_qmp_block_job_cancel(job); trace_qmp_block_job_cancel(job);
block_job_user_cancel(job, force, errp); job_user_cancel(&job->job, force, errp);
out: out:
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -3875,7 +4043,7 @@ void qmp_block_job_pause(const char *device, Error **errp)
} }
trace_qmp_block_job_pause(job); trace_qmp_block_job_pause(job);
block_job_user_pause(job, errp); job_user_pause(&job->job, errp);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -3889,7 +4057,7 @@ void qmp_block_job_resume(const char *device, Error **errp)
} }
trace_qmp_block_job_resume(job); trace_qmp_block_job_resume(job);
block_job_user_resume(job, errp); job_user_resume(&job->job, errp);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -3903,7 +4071,7 @@ void qmp_block_job_complete(const char *device, Error **errp)
} }
trace_qmp_block_job_complete(job); trace_qmp_block_job_complete(job);
block_job_complete(job, errp); job_complete(&job->job, errp);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -3917,21 +4085,23 @@ void qmp_block_job_finalize(const char *id, Error **errp)
} }
trace_qmp_block_job_finalize(job); trace_qmp_block_job_finalize(job);
block_job_finalize(job, errp); job_finalize(&job->job, errp);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
void qmp_block_job_dismiss(const char *id, Error **errp) void qmp_block_job_dismiss(const char *id, Error **errp)
{ {
AioContext *aio_context; AioContext *aio_context;
BlockJob *job = find_block_job(id, &aio_context, errp); BlockJob *bjob = find_block_job(id, &aio_context, errp);
Job *job;
if (!job) { if (!bjob) {
return; return;
} }
trace_qmp_block_job_dismiss(job); trace_qmp_block_job_dismiss(bjob);
block_job_dismiss(&job, errp); job = &bjob->job;
job_dismiss(&job, errp);
aio_context_release(aio_context); aio_context_release(aio_context);
} }
@ -4031,7 +4201,7 @@ void hmp_drive_add_node(Monitor *mon, const char *optstr)
qdict = qemu_opts_to_qdict(opts, NULL); qdict = qemu_opts_to_qdict(opts, NULL);
if (!qdict_get_try_str(qdict, "node-name")) { if (!qdict_get_try_str(qdict, "node-name")) {
QDECREF(qdict); qobject_unref(qdict);
error_report("'node-name' needs to be specified"); error_report("'node-name' needs to be specified");
goto out; goto out;
} }

1138
blockjob.c

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>. * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu-version.h" #include "qemu-version.h"
#include <machine/trap.h> #include <machine/trap.h>
@ -795,9 +796,9 @@ int main(int argc, char **argv)
if (x86_stack_size <= 0) if (x86_stack_size <= 0)
usage(); usage();
if (*r == 'M') if (*r == 'M')
x86_stack_size *= 1024 * 1024; x86_stack_size *= MiB;
else if (*r == 'k' || *r == 'K') else if (*r == 'k' || *r == 'K')
x86_stack_size *= 1024; x86_stack_size *= KiB;
} else if (!strcmp(r, "L")) { } else if (!strcmp(r, "L")) {
interp_prefix = argv[optind++]; interp_prefix = argv[optind++];
} else if (!strcmp(r, "p")) { } else if (!strcmp(r, "p")) {
@ -898,9 +899,10 @@ int main(int argc, char **argv)
cpu_model = "any"; cpu_model = "any";
#endif #endif
} }
/* init tcg before creating CPUs and to get qemu_host_page_size */
tcg_exec_init(0); tcg_exec_init(0);
/* NOTE: we need to init the CPU at this stage to get
qemu_host_page_size */
cpu_type = parse_cpu_model(cpu_model); cpu_type = parse_cpu_model(cpu_model);
cpu = cpu_create(cpu_type); cpu = cpu_create(cpu_type);
env = cpu->env_ptr; env = cpu->env_ptr;
@ -917,7 +919,7 @@ int main(int argc, char **argv)
envlist_free(envlist); envlist_free(envlist);
/* /*
* Now that page sizes are configured in cpu_init() we can do * Now that page sizes are configured in tcg_exec_init() we can do
* proper page alignment for guest_base. * proper page alignment for guest_base.
*/ */
guest_base = HOST_PAGE_ALIGN(guest_base); guest_base = HOST_PAGE_ALIGN(guest_base);

View File

@ -21,6 +21,7 @@
#include "qemu.h" #include "qemu.h"
#include "qemu-common.h" #include "qemu-common.h"
#include "bsd-mman.h" #include "bsd-mman.h"
#include "exec/exec-all.h"
//#define DEBUG_MMAP //#define DEBUG_MMAP

View File

@ -19,7 +19,6 @@
#include "cpu.h" #include "cpu.h"
#include "exec/exec-all.h"
#include "exec/cpu_ldst.h" #include "exec/cpu_ldst.h"
#undef DEBUG_REMAP #undef DEBUG_REMAP

View File

@ -304,6 +304,7 @@ void mux_set_focus(Chardev *chr, int focus)
} }
d->focus = focus; d->focus = focus;
chr->be = d->backends[focus];
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN); mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
} }

View File

@ -139,7 +139,7 @@ static void tty_serial_init(int fd, int speed,
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON); | INLCR | IGNCR | ICRNL | IXON);
tty.c_oflag |= OPOST; tty.c_oflag &= ~OPOST;
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG); tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB); tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB);
switch (data_bits) { switch (data_bits) {
@ -265,7 +265,8 @@ static void qmp_chardev_open_serial(Chardev *chr,
ChardevHostdev *serial = backend->u.serial.data; ChardevHostdev *serial = backend->u.serial.data;
int fd; int fd;
fd = qmp_chardev_open_file_source(serial->device, O_RDWR, errp); fd = qmp_chardev_open_file_source(serial->device, O_RDWR | O_NONBLOCK,
errp);
if (fd < 0) { if (fd < 0) {
return; return;
} }

View File

@ -134,8 +134,11 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
s->write_msgfds, s->write_msgfds,
s->write_msgfds_num); s->write_msgfds_num);
/* free the written msgfds, no matter what */ /* free the written msgfds in any cases
if (s->write_msgfds_num) { * other than ret < 0 && errno == EAGAIN
*/
if (!(ret < 0 && EAGAIN == errno)
&& s->write_msgfds_num) {
g_free(s->write_msgfds); g_free(s->write_msgfds);
s->write_msgfds = 0; s->write_msgfds = 0;
s->write_msgfds_num = 0; s->write_msgfds_num = 0;

View File

@ -46,8 +46,10 @@ static bool stdio_echo_state;
static void term_exit(void) static void term_exit(void)
{ {
tcsetattr(0, TCSANOW, &oldtty); if (stdio_in_use) {
fcntl(0, F_SETFL, old_fd0_flags); tcsetattr(0, TCSANOW, &oldtty);
fcntl(0, F_SETFL, old_fd0_flags);
}
} }
static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo) static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo)

314
configure vendored
View File

@ -60,6 +60,11 @@ do_compiler() {
# is compiler binary to execute. # is compiler binary to execute.
local compiler="$1" local compiler="$1"
shift shift
if test -n "$BASH_VERSION"; then eval '
echo >>config.log "
funcs: ${FUNCNAME[*]}
lines: ${BASH_LINENO[*]}"
'; fi
echo $compiler "$@" >> config.log echo $compiler "$@" >> config.log
$compiler "$@" >> config.log 2>&1 || return $? $compiler "$@" >> config.log 2>&1 || return $?
# Test passed. If this is an --enable-werror build, rerun # Test passed. If this is an --enable-werror build, rerun
@ -284,7 +289,6 @@ libs_softmmu=""
libs_tools="" libs_tools=""
audio_pt_int="" audio_pt_int=""
audio_win_int="" audio_win_int=""
cc_i386=i386-pc-linux-gnu-gcc
libs_qga="" libs_qga=""
debug_info="yes" debug_info="yes"
stack_protector="" stack_protector=""
@ -296,6 +300,24 @@ then
else else
git_update=no git_update=no
git_submodules="" git_submodules=""
if ! test -f "$source_path/ui/keycodemapdb/README"
then
echo
echo "ERROR: missing file $source_path/ui/keycodemapdb/README"
echo
echo "This is not a GIT checkout but module content appears to"
echo "be missing. Do not use 'git archive' or GitHub download links"
echo "to acquire QEMU source archives. Non-GIT builds are only"
echo "supported with source archives linked from:"
echo
echo " https://www.qemu.org/download/"
echo
echo "Developers working with GIT can use scripts/archive-source.sh"
echo "if they need to create valid source archives."
echo
exit 1
fi
fi fi
git="git" git="git"
@ -451,6 +473,21 @@ jemalloc="no"
replication="yes" replication="yes"
vxhs="" vxhs=""
libxml2="" libxml2=""
docker="no"
debug_mutex="no"
# cross compilers defaults, can be overridden with --cross-cc-ARCH
cross_cc_aarch64="aarch64-linux-gnu-gcc"
cross_cc_aarch64_be="$cross_cc_aarch64"
cross_cc_cflags_aarch64_be="-mbig-endian"
cross_cc_arm="arm-linux-gnueabihf-gcc"
cross_cc_cflags_armeb="-mbig-endian"
cross_cc_i386="i386-pc-linux-gnu-gcc"
cross_cc_cflags_i386=""
cross_cc_powerpc="powerpc-linux-gnu-gcc"
cross_cc_powerpc="powerpc-linux-gnu-gcc"
enabled_cross_compilers=""
supported_cpu="no" supported_cpu="no"
supported_os="no" supported_os="no"
@ -482,6 +519,14 @@ for opt do
;; ;;
--disable-debug-info) debug_info="no" --disable-debug-info) debug_info="no"
;; ;;
--cross-cc-*[!a-zA-Z0-9_-]*=*) error_exit "Passed bad --cross-cc-FOO option"
;;
--cross-cc-cflags-*) cc_arch=${opt#--cross-cc-flags-}; cc_arch=${cc_arch%%=*}
eval "cross_cc_cflags_${cc_arch}=\$optarg"
;;
--cross-cc-*) cc_arch=${opt#--cross-cc-}; cc_arch=${cc_arch%%=*}
eval "cross_cc_${cc_arch}=\$optarg"
;;
esac esac
done done
# OS specific # OS specific
@ -670,30 +715,37 @@ case "$cpu" in
ppc|ppc64|s390|s390x|sparc64|x32) ppc|ppc64|s390|s390x|sparc64|x32)
cpu="$cpu" cpu="$cpu"
supported_cpu="yes" supported_cpu="yes"
eval "cross_cc_${cpu}=\$host_cc"
;; ;;
i386|i486|i586|i686|i86pc|BePC) i386|i486|i586|i686|i86pc|BePC)
cpu="i386" cpu="i386"
supported_cpu="yes" supported_cpu="yes"
cross_cc_i386=$host_cc
;; ;;
x86_64|amd64) x86_64|amd64)
cpu="x86_64" cpu="x86_64"
supported_cpu="yes" supported_cpu="yes"
cross_cc_x86_64=$host_cc
;; ;;
armv*b|armv*l|arm) armv*b|armv*l|arm)
cpu="arm" cpu="arm"
supported_cpu="yes" supported_cpu="yes"
cross_cc_arm=$host_cc
;; ;;
aarch64) aarch64)
cpu="aarch64" cpu="aarch64"
supported_cpu="yes" supported_cpu="yes"
cross_cc_aarch64=$host_cc
;; ;;
mips*) mips*)
cpu="mips" cpu="mips"
supported_cpu="yes" supported_cpu="yes"
cross_cc_mips=$host_cc
;; ;;
sparc|sun4[cdmuv]) sparc|sun4[cdmuv])
cpu="sparc" cpu="sparc"
supported_cpu="yes" supported_cpu="yes"
cross_cc_sparc=$host_cc
;; ;;
*) *)
# This will result in either an error or falling back to TCI later # This will result in either an error or falling back to TCI later
@ -911,6 +963,8 @@ for opt do
;; ;;
--disable-debug-info) --disable-debug-info)
;; ;;
--cross-cc-*)
;;
--enable-modules) --enable-modules)
modules="yes" modules="yes"
;; ;;
@ -959,6 +1013,8 @@ for opt do
;; ;;
--firmwarepath=*) firmwarepath="$optarg" --firmwarepath=*) firmwarepath="$optarg"
;; ;;
--host=*|--build=*|\
--disable-dependency-tracking|\
--sbindir=*|--sharedstatedir=*|\ --sbindir=*|--sharedstatedir=*|\
--oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\ --oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\
--htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*) --htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*)
@ -1004,6 +1060,7 @@ for opt do
--enable-debug) --enable-debug)
# Enable debugging options that aren't excessively noisy # Enable debugging options that aren't excessively noisy
debug_tcg="yes" debug_tcg="yes"
debug_mutex="yes"
debug="yes" debug="yes"
strip_opt="no" strip_opt="no"
fortify_source="no" fortify_source="no"
@ -1374,6 +1431,10 @@ for opt do
;; ;;
--disable-git-update) git_update=no --disable-git-update) git_update=no
;; ;;
--enable-debug-mutex) debug_mutex=yes
;;
--disable-debug-mutex) debug_mutex=no
;;
*) *)
echo "ERROR: unknown option $opt" echo "ERROR: unknown option $opt"
echo "Try '$0 --help' for more information" echo "Try '$0 --help' for more information"
@ -1394,31 +1455,44 @@ case "$cpu" in
ppc) ppc)
CPU_CFLAGS="-m32" CPU_CFLAGS="-m32"
LDFLAGS="-m32 $LDFLAGS" LDFLAGS="-m32 $LDFLAGS"
cross_cc_powerpc=$cc
cross_cc_cflags_powerpc=$CPU_CFLAGS
;; ;;
ppc64) ppc64)
CPU_CFLAGS="-m64" CPU_CFLAGS="-m64"
LDFLAGS="-m64 $LDFLAGS" LDFLAGS="-m64 $LDFLAGS"
cross_cc_ppc64=$cc
cross_cc_cflags_ppc64=$CPU_CFLAGS
;; ;;
sparc) sparc)
CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc" CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc"
LDFLAGS="-m32 -mv8plus $LDFLAGS" LDFLAGS="-m32 -mv8plus $LDFLAGS"
cross_cc_sparc=$cc
cross_cc_cflags_sparc=$CPU_CFLAGS
;; ;;
sparc64) sparc64)
CPU_CFLAGS="-m64 -mcpu=ultrasparc" CPU_CFLAGS="-m64 -mcpu=ultrasparc"
LDFLAGS="-m64 $LDFLAGS" LDFLAGS="-m64 $LDFLAGS"
cross_cc_sparc64=$cc
cross_cc_cflags_sparc64=$CPU_CFLAGS
;; ;;
s390) s390)
CPU_CFLAGS="-m31" CPU_CFLAGS="-m31"
LDFLAGS="-m31 $LDFLAGS" LDFLAGS="-m31 $LDFLAGS"
cross_cc_s390=$cc
cross_cc_cflags_s390=$CPU_CFLAGS
;; ;;
s390x) s390x)
CPU_CFLAGS="-m64" CPU_CFLAGS="-m64"
LDFLAGS="-m64 $LDFLAGS" LDFLAGS="-m64 $LDFLAGS"
cross_cc_s390x=$cc
cross_cc_cflags_s390x=$CPU_CFLAGS
;; ;;
i386) i386)
CPU_CFLAGS="-m32" CPU_CFLAGS="-m32"
LDFLAGS="-m32 $LDFLAGS" LDFLAGS="-m32 $LDFLAGS"
cc_i386='$(CC) -m32' cross_cc_i386=$cc
cross_cc_cflags_i386=$CPU_CFLAGS
;; ;;
x86_64) x86_64)
# ??? Only extremely old AMD cpus do not have cmpxchg16b. # ??? Only extremely old AMD cpus do not have cmpxchg16b.
@ -1426,12 +1500,14 @@ case "$cpu" in
# runtime and generate the fallback to serial emulation. # runtime and generate the fallback to serial emulation.
CPU_CFLAGS="-m64 -mcx16" CPU_CFLAGS="-m64 -mcx16"
LDFLAGS="-m64 $LDFLAGS" LDFLAGS="-m64 $LDFLAGS"
cc_i386='$(CC) -m32' cross_cc_x86_64=$cc
cross_cc_cflags_x86_64=$CPU_CFLAGS
;; ;;
x32) x32)
CPU_CFLAGS="-mx32" CPU_CFLAGS="-mx32"
LDFLAGS="-mx32 $LDFLAGS" LDFLAGS="-mx32 $LDFLAGS"
cc_i386='$(CC) -m32' cross_cc_i386=$cc
cross_cc_cflags_i386=$CPU_CFLAGS
;; ;;
# No special flags required for other host CPUs # No special flags required for other host CPUs
esac esac
@ -1493,6 +1569,8 @@ Advanced options (experts only):
--extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS --extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS
--extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS --extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS
--extra-ldflags=LDFLAGS append extra linker flags LDFLAGS --extra-ldflags=LDFLAGS append extra linker flags LDFLAGS
--cross-cc-ARCH=CC use compiler when building ARCH guest test cases
--cross-cc-flags-ARCH= use compiler flags when building ARCH guest tests
--make=MAKE use specified make [$make] --make=MAKE use specified make [$make]
--install=INSTALL use specified install [$install] --install=INSTALL use specified install [$install]
--python=PYTHON use specified python [$python] --python=PYTHON use specified python [$python]
@ -1581,7 +1659,7 @@ disabled with --disable-FEATURE, default is enabled if available:
virtfs VirtFS virtfs VirtFS
mpath Multipath persistent reservation passthrough mpath Multipath persistent reservation passthrough
xen xen backend driver support xen xen backend driver support
xen-pci-passthrough xen-pci-passthrough PCI passthrough support for Xen
brlapi BrlAPI (Braile) brlapi BrlAPI (Braile)
curl curl connectivity curl curl connectivity
membarrier membarrier system call (for Linux 4.14+ or Windows) membarrier membarrier system call (for Linux 4.14+ or Windows)
@ -1631,6 +1709,7 @@ disabled with --disable-FEATURE, default is enabled if available:
crypto-afalg Linux AF_ALG crypto backend driver crypto-afalg Linux AF_ALG crypto backend driver
vhost-user vhost-user support vhost-user vhost-user support
capstone capstone disassembler support capstone capstone disassembler support
debug-mutex mutex debugging support
NOTE: The object files are built at the place where configure is launched NOTE: The object files are built at the place where configure is launched
EOF EOF
@ -1643,8 +1722,8 @@ fi
# Note that if the Python conditional here evaluates True we will exit # Note that if the Python conditional here evaluates True we will exit
# with status 1 which is a shell 'false' value. # with status 1 which is a shell 'false' value.
if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6))'; then if ! $python -c 'import sys; sys.exit(sys.version_info < (2,7))'; then
error_exit "Cannot use '$python', Python 2 >= 2.6 or Python 3 is required." \ error_exit "Cannot use '$python', Python 2 >= 2.7 or Python 3 is required." \
"Use --python=/path/to/python to specify a supported Python." "Use --python=/path/to/python to specify a supported Python."
fi fi
@ -2189,6 +2268,9 @@ if test "$xen" != "no" ; then
xen=yes xen=yes
xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab" xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab"
xen_pc="$xen_pc xenevtchn xendevicemodel" xen_pc="$xen_pc xenevtchn xendevicemodel"
if $pkg_config --exists xentoolcore; then
xen_pc="$xen_pc xentoolcore"
fi
QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)" QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)"
libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu" libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu"
LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS" LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS"
@ -2218,20 +2300,46 @@ EOF
# Xen unstable # Xen unstable
elif elif
cat > $TMPC <<EOF && cat > $TMPC <<EOF &&
#undef XC_WANT_COMPAT_DEVICEMODEL_API
#define __XEN_TOOLS__
#include <xendevicemodel.h>
#include <xenforeignmemory.h>
int main(void) {
xendevicemodel_handle *xd;
xenforeignmemory_handle *xfmem;
xd = xendevicemodel_open(0, 0);
xendevicemodel_pin_memory_cacheattr(xd, 0, 0, 0, 0);
xfmem = xenforeignmemory_open(0, 0);
xenforeignmemory_map_resource(xfmem, 0, 0, 0, 0, 0, NULL, 0, 0);
return 0;
}
EOF
compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore"
then
xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore"
xen_ctrl_version=41100
xen=yes
elif
cat > $TMPC <<EOF &&
#undef XC_WANT_COMPAT_MAP_FOREIGN_API #undef XC_WANT_COMPAT_MAP_FOREIGN_API
#include <xenforeignmemory.h> #include <xenforeignmemory.h>
#include <xentoolcore.h>
int main(void) { int main(void) {
xenforeignmemory_handle *xfmem; xenforeignmemory_handle *xfmem;
xfmem = xenforeignmemory_open(0, 0); xfmem = xenforeignmemory_open(0, 0);
xenforeignmemory_map2(xfmem, 0, 0, 0, 0, 0, 0, 0); xenforeignmemory_map2(xfmem, 0, 0, 0, 0, 0, 0, 0);
xentoolcore_restrict_all(0);
return 0; return 0;
} }
EOF EOF
compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs" compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore"
then then
xen_stable_libs="-lxendevicemodel $xen_stable_libs" xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore"
xen_ctrl_version=41000 xen_ctrl_version=41000
xen=yes xen=yes
elif elif
@ -2493,20 +2601,7 @@ fi
########################################## ##########################################
# Windows Hypervisor Platform accelerator (WHPX) check # Windows Hypervisor Platform accelerator (WHPX) check
if test "$whpx" != "no" ; then if test "$whpx" != "no" ; then
cat > $TMPC << EOF if check_include "WinHvPlatform.h" && check_include "WinHvEmulation.h"; then
#include <windows.h>
#include <WinHvPlatform.h>
#include <WinHvEmulation.h>
int main(void) {
WHV_CAPABILITY whpx_cap;
UINT32 writtenSize;
WHvGetCapability(WHvCapabilityCodeFeatures, &whpx_cap, sizeof(whpx_cap),
&writtenSize);
return 0;
}
EOF
if compile_prog "" "-lWinHvPlatform -lWinHvEmulation" ; then
libs_softmmu="$libs_softmmu -lWinHvPlatform -lWinHvEmulation"
whpx="yes" whpx="yes"
else else
if test "$whpx" = "yes"; then if test "$whpx" = "yes"; then
@ -3392,11 +3487,7 @@ fi
########################################## ##########################################
# glib support probe # glib support probe
if test "$mingw32" = yes; then glib_req_ver=2.40
glib_req_ver=2.30
else
glib_req_ver=2.22
fi
glib_modules="gthread-2.0 gobject-2.0" glib_modules="gthread-2.0 gobject-2.0"
if test "$modules" = yes; then if test "$modules" = yes; then
glib_modules="$glib_modules gmodule-export-2.0" glib_modules="$glib_modules gmodule-export-2.0"
@ -3761,7 +3852,7 @@ fi
fdt_required=no fdt_required=no
for target in $target_list; do for target in $target_list; do
case $target in case $target in
aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu) aarch64*-softmmu|arm*-softmmu|ppc*-softmmu|microblaze*-softmmu|mips64el-softmmu|riscv*-softmmu)
fdt_required=yes fdt_required=yes
;; ;;
esac esac
@ -3787,22 +3878,22 @@ int main(void) { fdt_first_subnode(0, 0); return 0; }
EOF EOF
if compile_prog "" "$fdt_libs" ; then if compile_prog "" "$fdt_libs" ; then
# system DTC is good - use it # system DTC is good - use it
fdt=yes fdt=system
else else
# have GIT checkout, so activate dtc submodule # have GIT checkout, so activate dtc submodule
if test -e "${source_path}/.git" ; then if test -e "${source_path}/.git" ; then
git_submodules="${git_submodules} dtc" git_submodules="${git_submodules} dtc"
fi fi
if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then
fdt=yes fdt=git
dtc_internal="yes"
mkdir -p dtc mkdir -p dtc
if [ "$pwd_is_source_path" != "y" ] ; then if [ "$pwd_is_source_path" != "y" ] ; then
symlink "$source_path/dtc/Makefile" "dtc/Makefile" symlink "$source_path/dtc/Makefile" "dtc/Makefile"
symlink "$source_path/dtc/scripts" "dtc/scripts" symlink "$source_path/dtc/scripts" "dtc/scripts"
fi fi
fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt" fdt_cflags="-I\$(SRC_PATH)/dtc/libfdt"
fdt_libs="-L\$(BUILD_DIR)/dtc/libfdt $fdt_libs" fdt_ldflags="-L\$(BUILD_DIR)/dtc/libfdt"
fdt_libs="$fdt_libs"
elif test "$fdt" = "yes" ; then elif test "$fdt" = "yes" ; then
# Not a git build & no libfdt found, prompt for system install # Not a git build & no libfdt found, prompt for system install
error_exit "DTC (libfdt) version >= 1.4.2 not present." \ error_exit "DTC (libfdt) version >= 1.4.2 not present." \
@ -3821,7 +3912,7 @@ libs_softmmu="$libs_softmmu $fdt_libs"
# opengl probe (for sdl2, gtk, milkymist-tmu2) # opengl probe (for sdl2, gtk, milkymist-tmu2)
if test "$opengl" != "no" ; then if test "$opengl" != "no" ; then
opengl_pkgs="epoxy libdrm gbm" opengl_pkgs="epoxy gbm"
if $pkg_config $opengl_pkgs; then if $pkg_config $opengl_pkgs; then
opengl_cflags="$($pkg_config --cflags $opengl_pkgs)" opengl_cflags="$($pkg_config --cflags $opengl_pkgs)"
opengl_libs="$($pkg_config --libs $opengl_pkgs)" opengl_libs="$($pkg_config --libs $opengl_pkgs)"
@ -4478,7 +4569,7 @@ fi
# check for smartcard support # check for smartcard support
if test "$smartcard" != "no"; then if test "$smartcard" != "no"; then
if $pkg_config libcacard; then if $pkg_config --atleast-version=2.5.1 libcacard; then
libcacard_cflags=$($pkg_config --cflags libcacard) libcacard_cflags=$($pkg_config --cflags libcacard)
libcacard_libs=$($pkg_config --libs libcacard) libcacard_libs=$($pkg_config --libs libcacard)
smartcard="yes" smartcard="yes"
@ -4604,6 +4695,7 @@ int main(void) { virgl_renderer_poll(); return 0; }
EOF EOF
virgl_cflags=$($pkg_config --cflags virglrenderer 2>/dev/null) virgl_cflags=$($pkg_config --cflags virglrenderer 2>/dev/null)
virgl_libs=$($pkg_config --libs virglrenderer 2>/dev/null) virgl_libs=$($pkg_config --libs virglrenderer 2>/dev/null)
virgl_version=$($pkg_config --modversion virglrenderer 2>/dev/null)
if $pkg_config virglrenderer >/dev/null 2>&1 && \ if $pkg_config virglrenderer >/dev/null 2>&1 && \
compile_prog "$virgl_cflags" "$virgl_libs" ; then compile_prog "$virgl_cflags" "$virgl_libs" ; then
virglrenderer="yes" virglrenderer="yes"
@ -4751,6 +4843,21 @@ if compile_prog "" "" ; then
sem_timedwait=yes sem_timedwait=yes
fi fi
##########################################
# check if we have strchrnul
strchrnul=no
cat > $TMPC << EOF
#include <string.h>
int main(void);
// Use a haystack that the compiler shouldn't be able to constant fold
char *haystack = (char*)&main;
int main(void) { return strchrnul(haystack, 'x') != &haystack[6]; }
EOF
if compile_prog "" "" ; then
strchrnul=yes
fi
########################################## ##########################################
# check if trace backend exists # check if trace backend exists
@ -5176,6 +5283,20 @@ if test "$fortify_source" != "no"; then
fi fi
fi fi
###############################################
# Check if copy_file_range is provided by glibc
have_copy_file_range=no
cat > $TMPC << EOF
#include <unistd.h>
int main(void) {
copy_file_range(0, NULL, 0, NULL, 0, 0);
return 0;
}
EOF
if compile_prog "" "" ; then
have_copy_file_range=yes
fi
########################################## ##########################################
# check if struct fsxattr is available via linux/fs.h # check if struct fsxattr is available via linux/fs.h
@ -5442,6 +5563,17 @@ EOF
fi fi
fi fi
##########################################
# Docker and cross-compiler support
#
# This is specifically for building test
# cases for foreign architectures, not
# cross-compiling QEMU itself.
if has "docker"; then
docker=$($python $source_path/tests/docker/docker.py probe)
fi
########################################## ##########################################
# End of CC checks # End of CC checks
# After here, no more $cc or $ld runs # After here, no more $cc or $ld runs
@ -5744,6 +5876,7 @@ echo_version() {
# prepend pixman and ftd flags after all config tests are done # prepend pixman and ftd flags after all config tests are done
QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS" QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS"
QEMU_LDFLAGS="$fdt_ldflags $QEMU_LDFLAGS"
libs_softmmu="$pixman_libs $libs_softmmu" libs_softmmu="$pixman_libs $libs_softmmu"
echo "Install prefix $prefix" echo "Install prefix $prefix"
@ -5774,6 +5907,7 @@ echo "ARFLAGS $ARFLAGS"
echo "CFLAGS $CFLAGS" echo "CFLAGS $CFLAGS"
echo "QEMU_CFLAGS $QEMU_CFLAGS" echo "QEMU_CFLAGS $QEMU_CFLAGS"
echo "LDFLAGS $LDFLAGS" echo "LDFLAGS $LDFLAGS"
echo "QEMU_LDFLAGS $QEMU_LDFLAGS"
echo "make $make" echo "make $make"
echo "install $install" echo "install $install"
echo "python $python" echo "python $python"
@ -5805,7 +5939,7 @@ echo "nettle $nettle $(echo_version $nettle $nettle_version)"
echo "nettle kdf $nettle_kdf" echo "nettle kdf $nettle_kdf"
echo "libtasn1 $tasn1" echo "libtasn1 $tasn1"
echo "curses support $curses" echo "curses support $curses"
echo "virgl support $virglrenderer" echo "virgl support $virglrenderer $(echo_version $virglrenderer $virgl_version)"
echo "curl support $curl" echo "curl support $curl"
echo "mingw32 support $mingw32" echo "mingw32 support $mingw32"
echo "Audio drivers $audio_drv_list" echo "Audio drivers $audio_drv_list"
@ -5882,6 +6016,7 @@ echo "seccomp support $seccomp"
echo "coroutine backend $coroutine" echo "coroutine backend $coroutine"
echo "coroutine pool $coroutine_pool" echo "coroutine pool $coroutine_pool"
echo "debug stack usage $debug_stack_usage" echo "debug stack usage $debug_stack_usage"
echo "mutex debugging $debug_mutex"
echo "crypto afalg $crypto_afalg" echo "crypto afalg $crypto_afalg"
echo "GlusterFS support $glusterfs" echo "GlusterFS support $glusterfs"
echo "gcov $gcov_tool" echo "gcov $gcov_tool"
@ -5903,6 +6038,7 @@ echo "avx2 optimization $avx2_opt"
echo "replication support $replication" echo "replication support $replication"
echo "VxHS block device $vxhs" echo "VxHS block device $vxhs"
echo "capstone $capstone" echo "capstone $capstone"
echo "docker $docker"
if test "$sdl_too_old" = "yes"; then if test "$sdl_too_old" = "yes"; then
echo "-> Your SDL version is too old - please upgrade to have SDL support" echo "-> Your SDL version is too old - please upgrade to have SDL support"
@ -6206,6 +6342,9 @@ fi
if test "$sem_timedwait" = "yes" ; then if test "$sem_timedwait" = "yes" ; then
echo "CONFIG_SEM_TIMEDWAIT=y" >> $config_host_mak echo "CONFIG_SEM_TIMEDWAIT=y" >> $config_host_mak
fi fi
if test "$strchrnul" = "yes" ; then
echo "HAVE_STRCHRNUL=y" >> $config_host_mak
fi
if test "$byteswap_h" = "yes" ; then if test "$byteswap_h" = "yes" ; then
echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak
fi fi
@ -6277,6 +6416,9 @@ fi
if test "$have_fsxattr" = "yes" ; then if test "$have_fsxattr" = "yes" ; then
echo "HAVE_FSXATTR=y" >> $config_host_mak echo "HAVE_FSXATTR=y" >> $config_host_mak
fi fi
if test "$have_copy_file_range" = "yes" ; then
echo "HAVE_COPY_FILE_RANGE=y" >> $config_host_mak
fi
if test "$vte" = "yes" ; then if test "$vte" = "yes" ; then
echo "CONFIG_VTE=y" >> $config_host_mak echo "CONFIG_VTE=y" >> $config_host_mak
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
@ -6333,7 +6475,7 @@ fi
if test "$preadv" = "yes" ; then if test "$preadv" = "yes" ; then
echo "CONFIG_PREADV=y" >> $config_host_mak echo "CONFIG_PREADV=y" >> $config_host_mak
fi fi
if test "$fdt" = "yes" ; then if test "$fdt" != "no" ; then
echo "CONFIG_FDT=y" >> $config_host_mak echo "CONFIG_FDT=y" >> $config_host_mak
fi fi
if test "$membarrier" = "yes" ; then if test "$membarrier" = "yes" ; then
@ -6631,6 +6773,9 @@ fi
if test "$capstone" != "no" ; then if test "$capstone" != "no" ; then
echo "CONFIG_CAPSTONE=y" >> $config_host_mak echo "CONFIG_CAPSTONE=y" >> $config_host_mak
fi fi
if test "$debug_mutex" = "yes" ; then
echo "CONFIG_DEBUG_MUTEX=y" >> $config_host_mak
fi
# Hold two types of flag: # Hold two types of flag:
# CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on # CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on
@ -6675,7 +6820,6 @@ echo "CC=$cc" >> $config_host_mak
if $iasl -h > /dev/null 2>&1; then if $iasl -h > /dev/null 2>&1; then
echo "IASL=$iasl" >> $config_host_mak echo "IASL=$iasl" >> $config_host_mak
fi fi
echo "CC_I386=$cc_i386" >> $config_host_mak
echo "HOST_CC=$host_cc" >> $config_host_mak echo "HOST_CC=$host_cc" >> $config_host_mak
echo "CXX=$cxx" >> $config_host_mak echo "CXX=$cxx" >> $config_host_mak
echo "OBJCC=$objcc" >> $config_host_mak echo "OBJCC=$objcc" >> $config_host_mak
@ -6708,6 +6852,7 @@ else
fi fi
echo "LDFLAGS=$LDFLAGS" >> $config_host_mak echo "LDFLAGS=$LDFLAGS" >> $config_host_mak
echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak
echo "QEMU_LDFLAGS=$QEMU_LDFLAGS" >> $config_host_mak
echo "LD_REL_FLAGS=$LD_REL_FLAGS" >> $config_host_mak echo "LD_REL_FLAGS=$LD_REL_FLAGS" >> $config_host_mak
echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak echo "LD_I386_EMULATION=$ld_i386_emulation" >> $config_host_mak
echo "LIBS+=$LIBS" >> $config_host_mak echo "LIBS+=$LIBS" >> $config_host_mak
@ -6726,6 +6871,10 @@ if test "$gcov" = "yes" ; then
echo "GCOV=$gcov_tool" >> $config_host_mak echo "GCOV=$gcov_tool" >> $config_host_mak
fi fi
if test "$docker" != "no"; then
echo "HAVE_USER_DOCKER=y" >> $config_host_mak
fi
# use included Linux headers # use included Linux headers
if test "$linux" = "yes" ; then if test "$linux" = "yes" ; then
mkdir -p linux-headers mkdir -p linux-headers
@ -6789,6 +6938,10 @@ case "$target" in
;; ;;
esac esac
target_compiler=""
target_compiler_static=""
target_compiler_cflags=""
mkdir -p $target_dir mkdir -p $target_dir
echo "# Automatically generated by configure - do not modify" > $config_target_mak echo "# Automatically generated by configure - do not modify" > $config_target_mak
@ -6804,19 +6957,25 @@ TARGET_ABI_DIR=""
case "$target_name" in case "$target_name" in
i386) i386)
gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml" gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml"
target_compiler=$cross_cc_i386
target_compiler_cflags=$cross_cc_ccflags_i386
;; ;;
x86_64) x86_64)
TARGET_BASE_ARCH=i386 TARGET_BASE_ARCH=i386
gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml" gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml"
target_compiler=$cross_cc_x86_64
;; ;;
alpha) alpha)
mttcg="yes" mttcg="yes"
target_compiler=$cross_cc_alpha
;; ;;
arm|armeb) arm|armeb)
TARGET_ARCH=arm TARGET_ARCH=arm
bflt="yes" bflt="yes"
mttcg="yes" mttcg="yes"
gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" gdb_xml_files="arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
target_compiler=$cross_cc_arm
eval "target_compiler_cflags=\$cross_cc_cflags_${target_name}"
;; ;;
aarch64|aarch64_be) aarch64|aarch64_be)
TARGET_ARCH=aarch64 TARGET_ARCH=aarch64
@ -6824,58 +6983,75 @@ case "$target_name" in
bflt="yes" bflt="yes"
mttcg="yes" mttcg="yes"
gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml" gdb_xml_files="aarch64-core.xml aarch64-fpu.xml arm-core.xml arm-vfp.xml arm-vfp3.xml arm-neon.xml"
target_compiler=$cross_cc_aarch64
eval "target_compiler_cflags=\$cross_cc_cflags_${target_name}"
;; ;;
cris) cris)
target_compiler=$cross_cc_cris
;; ;;
hppa) hppa)
mttcg="yes" mttcg="yes"
target_compiler=$cross_cc_hppa
;; ;;
lm32) lm32)
target_compiler=$cross_cc_lm32
;; ;;
m68k) m68k)
bflt="yes" bflt="yes"
gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml" gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml"
target_compiler=$cross_cc_m68k
;; ;;
microblaze|microblazeel) microblaze|microblazeel)
TARGET_ARCH=microblaze TARGET_ARCH=microblaze
bflt="yes" bflt="yes"
echo "TARGET_ABI32=y" >> $config_target_mak
target_compiler=$cross_cc_microblaze
;; ;;
mips|mipsel) mips|mipsel)
TARGET_ARCH=mips TARGET_ARCH=mips
target_compiler=$cross_cc_mips
echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak
;; ;;
mipsn32|mipsn32el) mipsn32|mipsn32el)
TARGET_ARCH=mips64 TARGET_ARCH=mips64
TARGET_BASE_ARCH=mips TARGET_BASE_ARCH=mips
target_compiler=$cross_cc_mipsn32
echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak
echo "TARGET_ABI32=y" >> $config_target_mak echo "TARGET_ABI32=y" >> $config_target_mak
;; ;;
mips64|mips64el) mips64|mips64el)
TARGET_ARCH=mips64 TARGET_ARCH=mips64
TARGET_BASE_ARCH=mips TARGET_BASE_ARCH=mips
target_compiler=$cross_cc_mips64
echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak
;; ;;
moxie) moxie)
target_compiler=$cross_cc_moxie
;; ;;
nios2) nios2)
target_compiler=$cross_cc_nios2
;; ;;
or1k) or1k)
target_compiler=$cross_cc_or1k
TARGET_ARCH=openrisc TARGET_ARCH=openrisc
TARGET_BASE_ARCH=openrisc TARGET_BASE_ARCH=openrisc
;; ;;
ppc) ppc)
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
target_compiler=$cross_cc_powerpc
;; ;;
ppcemb) ppcemb)
TARGET_BASE_ARCH=ppc TARGET_BASE_ARCH=ppc
TARGET_ABI_DIR=ppc TARGET_ABI_DIR=ppc
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
target_compiler=$cross_cc_ppcemb
;; ;;
ppc64) ppc64)
TARGET_BASE_ARCH=ppc TARGET_BASE_ARCH=ppc
TARGET_ABI_DIR=ppc TARGET_ABI_DIR=ppc
mttcg=yes mttcg=yes
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
target_compiler=$cross_cc_ppc64
;; ;;
ppc64le) ppc64le)
TARGET_ARCH=ppc64 TARGET_ARCH=ppc64
@ -6883,6 +7059,7 @@ case "$target_name" in
TARGET_ABI_DIR=ppc TARGET_ABI_DIR=ppc
mttcg=yes mttcg=yes
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
target_compiler=$cross_cc_ppc64le
;; ;;
ppc64abi32) ppc64abi32)
TARGET_ARCH=ppc64 TARGET_ARCH=ppc64
@ -6890,45 +7067,57 @@ case "$target_name" in
TARGET_ABI_DIR=ppc TARGET_ABI_DIR=ppc
echo "TARGET_ABI32=y" >> $config_target_mak echo "TARGET_ABI32=y" >> $config_target_mak
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml" gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
target_compiler=$cross_cc_ppc64abi32
;; ;;
riscv32) riscv32)
TARGET_BASE_ARCH=riscv TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv TARGET_ABI_DIR=riscv
mttcg=yes mttcg=yes
target_compiler=$cross_cc_riscv32
;; ;;
riscv64) riscv64)
TARGET_BASE_ARCH=riscv TARGET_BASE_ARCH=riscv
TARGET_ABI_DIR=riscv TARGET_ABI_DIR=riscv
mttcg=yes mttcg=yes
target_compiler=$cross_cc_riscv64
;; ;;
sh4|sh4eb) sh4|sh4eb)
TARGET_ARCH=sh4 TARGET_ARCH=sh4
bflt="yes" bflt="yes"
target_compiler=$cross_cc_sh4
;; ;;
sparc) sparc)
target_compiler=$cross_cc_sparc
;; ;;
sparc64) sparc64)
TARGET_BASE_ARCH=sparc TARGET_BASE_ARCH=sparc
target_compiler=$cross_cc_sparc64
;; ;;
sparc32plus) sparc32plus)
TARGET_ARCH=sparc64 TARGET_ARCH=sparc64
TARGET_BASE_ARCH=sparc TARGET_BASE_ARCH=sparc
TARGET_ABI_DIR=sparc TARGET_ABI_DIR=sparc
target_compiler=$cross_cc_sparc32plus
echo "TARGET_ABI32=y" >> $config_target_mak echo "TARGET_ABI32=y" >> $config_target_mak
;; ;;
s390x) s390x)
mttcg=yes mttcg=yes
gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml s390-gs.xml" gdb_xml_files="s390x-core64.xml s390-acr.xml s390-fpr.xml s390-vx.xml s390-cr.xml s390-virt.xml s390-gs.xml"
target_compiler=$cross_cc_s390x
;; ;;
tilegx) tilegx)
target_compiler=$cross_cc_tilegx
;; ;;
tricore) tricore)
target_compiler=$cross_cc_tricore
;; ;;
unicore32) unicore32)
target_compiler=$cross_cc_unicore32
;; ;;
xtensa|xtensaeb) xtensa|xtensaeb)
TARGET_ARCH=xtensa TARGET_ARCH=xtensa
mttcg="yes" mttcg="yes"
target_compiler=$cross_cc_xtensa
;; ;;
*) *)
error_exit "Unsupported target CPU" error_exit "Unsupported target CPU"
@ -6939,6 +7128,27 @@ if [ "$TARGET_BASE_ARCH" = "" ]; then
TARGET_BASE_ARCH=$TARGET_ARCH TARGET_BASE_ARCH=$TARGET_ARCH
fi fi
# Do we have a cross compiler for this target?
if has $target_compiler; then
write_c_skeleton
if ! do_compiler "$target_compiler" $target_compiler_cflags -o $TMPE $TMPC -static ; then
# For host systems we might get away with building without -static
if ! do_compiler "$target_compiler" $target_compiler_cflags -o $TMPE $TMPC ; then
target_compiler=""
else
enabled_cross_compilers="${enabled_cross_compilers} '${target_compiler}'"
target_compiler_static="n"
fi
else
enabled_cross_compilers="${enabled_cross_compilers} '${target_compiler}'"
target_compiler_static="y"
fi
else
target_compiler=""
fi
symlink "$source_path/Makefile.target" "$target_dir/Makefile" symlink "$source_path/Makefile.target" "$target_dir/Makefile"
upper() { upper() {
@ -7012,6 +7222,19 @@ if test "$target_bsd_user" = "yes" ; then
echo "CONFIG_BSD_USER=y" >> $config_target_mak echo "CONFIG_BSD_USER=y" >> $config_target_mak
fi fi
if test -n "$target_compiler"; then
echo "CROSS_CC_GUEST=\"$target_compiler\"" >> $config_target_mak
if test -n "$target_compiler_static"; then
echo "CROSS_CC_GUEST_STATIC=$target_compiler_static" >> $config_target_mak
fi
if test -n "$target_compiler_cflags"; then
echo "CROSS_CC_GUEST_CFLAGS=$target_compiler_cflags" >> $config_target_mak
fi
fi
# generate QEMU_CFLAGS/LDFLAGS for targets # generate QEMU_CFLAGS/LDFLAGS for targets
cflags="" cflags=""
@ -7134,7 +7357,12 @@ echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak
done # for target in $targets done # for target in $targets
if [ "$dtc_internal" = "yes" ]; then if test -n "$enabled_cross_compilers"; then
echo
echo "NOTE: cross-compilers enabled: $enabled_cross_compilers"
fi
if [ "$fdt" = "git" ]; then
echo "config-host.h: subdir-dtc" >> $config_host_mak echo "config-host.h: subdir-dtc" >> $config_host_mak
fi fi
if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then
@ -7207,9 +7435,11 @@ for rom in seabios vgabios ; do
done done
# set up tests data directory # set up tests data directory
if [ ! -e tests/data ]; then for tests_subdir in acceptance data; do
symlink "$source_path/tests/data" tests/data if [ ! -e tests/$tests_subdir ]; then
fi symlink "$source_path/tests/$tests_subdir" tests/$tests_subdir
fi
done
# set up qemu-iotests in this build directory # set up qemu-iotests in this build directory
iotests_common_env="tests/qemu-iotests/common.env" iotests_common_env="tests/qemu-iotests/common.env"

View File

@ -314,22 +314,19 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
msg.msg_controllen = 0; msg.msg_controllen = 0;
} }
/* Set the version in the flags when sending the reply */
vmsg->flags &= ~VHOST_USER_VERSION_MASK;
vmsg->flags |= VHOST_USER_VERSION;
vmsg->flags |= VHOST_USER_REPLY_MASK;
do { do {
rc = sendmsg(conn_fd, &msg, 0); rc = sendmsg(conn_fd, &msg, 0);
} while (rc < 0 && (errno == EINTR || errno == EAGAIN)); } while (rc < 0 && (errno == EINTR || errno == EAGAIN));
do { if (vmsg->size) {
if (vmsg->data) { do {
rc = write(conn_fd, vmsg->data, vmsg->size); if (vmsg->data) {
} else { rc = write(conn_fd, vmsg->data, vmsg->size);
rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size); } else {
} rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size);
} while (rc < 0 && (errno == EINTR || errno == EAGAIN)); }
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
}
if (rc <= 0) { if (rc <= 0) {
vu_panic(dev, "Error while writing: %s", strerror(errno)); vu_panic(dev, "Error while writing: %s", strerror(errno));
@ -339,6 +336,39 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
return true; return true;
} }
static bool
vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
{
/* Set the version in the flags when sending the reply */
vmsg->flags &= ~VHOST_USER_VERSION_MASK;
vmsg->flags |= VHOST_USER_VERSION;
vmsg->flags |= VHOST_USER_REPLY_MASK;
return vu_message_write(dev, conn_fd, vmsg);
}
static bool
vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg)
{
VhostUserMsg msg_reply;
if ((vmsg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) {
return true;
}
if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) {
return false;
}
if (msg_reply.request != vmsg->request) {
DPRINT("Received unexpected msg type. Expected %d received %d",
vmsg->request, msg_reply.request);
return false;
}
return msg_reply.payload.u64 == 0;
}
/* Kick the log_call_fd if required. */ /* Kick the log_call_fd if required. */
static void static void
vu_log_kick(VuDev *dev) vu_log_kick(VuDev *dev)
@ -534,7 +564,7 @@ vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg)
/* Send the message back to qemu with the addresses filled in */ /* Send the message back to qemu with the addresses filled in */
vmsg->fd_num = 0; vmsg->fd_num = 0;
if (!vu_message_write(dev, dev->sock, vmsg)) { if (!vu_send_reply(dev, dev->sock, vmsg)) {
vu_panic(dev, "failed to respond to set-mem-table for postcopy"); vu_panic(dev, "failed to respond to set-mem-table for postcopy");
return false; return false;
} }
@ -914,6 +944,41 @@ void vu_set_queue_handler(VuDev *dev, VuVirtq *vq,
} }
} }
bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd,
int size, int offset)
{
int qidx = vq - dev->vq;
int fd_num = 0;
VhostUserMsg vmsg = {
.request = VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG,
.flags = VHOST_USER_VERSION | VHOST_USER_NEED_REPLY_MASK,
.size = sizeof(vmsg.payload.area),
.payload.area = {
.u64 = qidx & VHOST_USER_VRING_IDX_MASK,
.size = size,
.offset = offset,
},
};
if (fd == -1) {
vmsg.payload.area.u64 |= VHOST_USER_VRING_NOFD_MASK;
} else {
vmsg.fds[fd_num++] = fd;
}
vmsg.fd_num = fd_num;
if ((dev->protocol_features & VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD) == 0) {
return false;
}
if (!vu_message_write(dev, dev->slave_fd, &vmsg)) {
return false;
}
return vu_process_message_reply(dev, &vmsg);
}
static bool static bool
vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg) vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg)
{ {
@ -966,7 +1031,9 @@ static bool
vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
{ {
uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD | uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD |
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ; 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ |
1ULL << VHOST_USER_PROTOCOL_F_HOST_NOTIFIER |
1ULL << VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD;
if (have_userfault()) { if (have_userfault()) {
features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT; features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT;
@ -1250,7 +1317,7 @@ vu_dispatch(VuDev *dev)
goto end; goto end;
} }
if (!vu_message_write(dev, dev->sock, &vmsg)) { if (!vu_send_reply(dev, dev->sock, &vmsg)) {
goto end; goto end;
} }

View File

@ -51,6 +51,8 @@ enum VhostUserProtocolFeature {
VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7, VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7,
VHOST_USER_PROTOCOL_F_PAGEFAULT = 8, VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
VHOST_USER_PROTOCOL_F_CONFIG = 9, VHOST_USER_PROTOCOL_F_CONFIG = 9,
VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10,
VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11,
VHOST_USER_PROTOCOL_F_MAX VHOST_USER_PROTOCOL_F_MAX
}; };
@ -92,6 +94,14 @@ typedef enum VhostUserRequest {
VHOST_USER_MAX VHOST_USER_MAX
} VhostUserRequest; } VhostUserRequest;
typedef enum VhostUserSlaveRequest {
VHOST_USER_SLAVE_NONE = 0,
VHOST_USER_SLAVE_IOTLB_MSG = 1,
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
VHOST_USER_SLAVE_MAX
} VhostUserSlaveRequest;
typedef struct VhostUserMemoryRegion { typedef struct VhostUserMemoryRegion {
uint64_t guest_phys_addr; uint64_t guest_phys_addr;
uint64_t memory_size; uint64_t memory_size;
@ -122,6 +132,12 @@ static VhostUserConfig c __attribute__ ((unused));
+ sizeof(c.size) \ + sizeof(c.size) \
+ sizeof(c.flags)) + sizeof(c.flags))
typedef struct VhostUserVringArea {
uint64_t u64;
uint64_t size;
uint64_t offset;
} VhostUserVringArea;
#if defined(_WIN32) #if defined(_WIN32)
# define VU_PACKED __attribute__((gcc_struct, packed)) # define VU_PACKED __attribute__((gcc_struct, packed))
#else #else
@ -133,6 +149,7 @@ typedef struct VhostUserMsg {
#define VHOST_USER_VERSION_MASK (0x3) #define VHOST_USER_VERSION_MASK (0x3)
#define VHOST_USER_REPLY_MASK (0x1 << 2) #define VHOST_USER_REPLY_MASK (0x1 << 2)
#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3)
uint32_t flags; uint32_t flags;
uint32_t size; /* the following payload size */ uint32_t size; /* the following payload size */
@ -145,6 +162,7 @@ typedef struct VhostUserMsg {
VhostUserMemory memory; VhostUserMemory memory;
VhostUserLog log; VhostUserLog log;
VhostUserConfig config; VhostUserConfig config;
VhostUserVringArea area;
} payload; } payload;
int fds[VHOST_MEMORY_MAX_NREGIONS]; int fds[VHOST_MEMORY_MAX_NREGIONS];
@ -368,6 +386,20 @@ VuVirtq *vu_get_queue(VuDev *dev, int qidx);
void vu_set_queue_handler(VuDev *dev, VuVirtq *vq, void vu_set_queue_handler(VuDev *dev, VuVirtq *vq,
vu_queue_handler_cb handler); vu_queue_handler_cb handler);
/**
* vu_set_queue_host_notifier:
* @dev: a VuDev context
* @vq: a VuVirtq queue
* @fd: a file descriptor
* @size: host page size
* @offset: notifier offset in @fd file
*
* Set queue's host notifier. This function may be called several
* times for the same queue. If called with -1 @fd, the notifier
* is removed.
*/
bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd,
int size, int offset);
/** /**
* vu_queue_set_notification: * vu_queue_set_notification:

View File

@ -31,6 +31,7 @@ typedef struct VubDev {
VugDev parent; VugDev parent;
int blk_fd; int blk_fd;
struct virtio_blk_config blkcfg; struct virtio_blk_config blkcfg;
bool enable_ro;
char *blk_name; char *blk_name;
GMainLoop *loop; GMainLoop *loop;
} VubDev; } VubDev;
@ -301,14 +302,33 @@ static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started)
static uint64_t static uint64_t
vub_get_features(VuDev *dev) vub_get_features(VuDev *dev)
{ {
return 1ull << VIRTIO_BLK_F_SIZE_MAX | uint64_t features;
1ull << VIRTIO_BLK_F_SEG_MAX | VugDev *gdev;
1ull << VIRTIO_BLK_F_TOPOLOGY | VubDev *vdev_blk;
1ull << VIRTIO_BLK_F_BLK_SIZE |
1ull << VIRTIO_BLK_F_FLUSH | gdev = container_of(dev, VugDev, parent);
1ull << VIRTIO_BLK_F_CONFIG_WCE | vdev_blk = container_of(gdev, VubDev, parent);
1ull << VIRTIO_F_VERSION_1 |
1ull << VHOST_USER_F_PROTOCOL_FEATURES; features = 1ull << VIRTIO_BLK_F_SIZE_MAX |
1ull << VIRTIO_BLK_F_SEG_MAX |
1ull << VIRTIO_BLK_F_TOPOLOGY |
1ull << VIRTIO_BLK_F_BLK_SIZE |
1ull << VIRTIO_BLK_F_FLUSH |
1ull << VIRTIO_BLK_F_CONFIG_WCE |
1ull << VIRTIO_F_VERSION_1 |
1ull << VHOST_USER_F_PROTOCOL_FEATURES;
if (vdev_blk->enable_ro) {
features |= 1ull << VIRTIO_BLK_F_RO;
}
return features;
}
static uint64_t
vub_get_protocol_features(VuDev *dev)
{
return 1ull << VHOST_USER_PROTOCOL_F_CONFIG;
} }
static int static int
@ -373,6 +393,7 @@ vub_set_config(VuDev *vu_dev, const uint8_t *data,
static const VuDevIface vub_iface = { static const VuDevIface vub_iface = {
.get_features = vub_get_features, .get_features = vub_get_features,
.queue_set_started = vub_queue_set_started, .queue_set_started = vub_queue_set_started,
.get_protocol_features = vub_get_protocol_features,
.get_config = vub_get_config, .get_config = vub_get_config,
.set_config = vub_set_config, .set_config = vub_set_config,
}; };
@ -469,6 +490,7 @@ vub_new(char *blk_file)
vub_free(vdev_blk); vub_free(vdev_blk);
return NULL; return NULL;
} }
vdev_blk->enable_ro = false;
vdev_blk->blkcfg.wce = 0; vdev_blk->blkcfg.wce = 0;
vdev_blk->blk_name = blk_file; vdev_blk->blk_name = blk_file;
@ -483,10 +505,11 @@ int main(int argc, char **argv)
int opt; int opt;
char *unix_socket = NULL; char *unix_socket = NULL;
char *blk_file = NULL; char *blk_file = NULL;
bool enable_ro = false;
int lsock = -1, csock = -1; int lsock = -1, csock = -1;
VubDev *vdev_blk = NULL; VubDev *vdev_blk = NULL;
while ((opt = getopt(argc, argv, "b:s:h")) != -1) { while ((opt = getopt(argc, argv, "b:rs:h")) != -1) {
switch (opt) { switch (opt) {
case 'b': case 'b':
blk_file = g_strdup(optarg); blk_file = g_strdup(optarg);
@ -494,17 +517,20 @@ int main(int argc, char **argv)
case 's': case 's':
unix_socket = g_strdup(optarg); unix_socket = g_strdup(optarg);
break; break;
case 'r':
enable_ro = true;
break;
case 'h': case 'h':
default: default:
printf("Usage: %s [-b block device or file, -s UNIX domain socket]" printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
" | [ -h ]\n", argv[0]); " | -r Enable read-only ] | [ -h ]\n", argv[0]);
return 0; return 0;
} }
} }
if (!unix_socket || !blk_file) { if (!unix_socket || !blk_file) {
printf("Usage: %s [-b block device or file, -s UNIX domain socket] |" printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
" [ -h ]\n", argv[0]); " | -r Enable read-only ] | [ -h ]\n", argv[0]);
return -1; return -1;
} }
@ -523,6 +549,9 @@ int main(int argc, char **argv)
if (!vdev_blk) { if (!vdev_blk) {
goto err; goto err;
} }
if (enable_ro) {
vdev_blk->enable_ro = true;
}
vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface); vug_init(&vdev_blk->parent, csock, vub_panic_cb, &vub_iface);

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