mirror of https://github.com/xemu-project/xemu.git
v3.0.0 release
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJbcveDAAoJEDwlJe0UNgzen9QP/jEa9I1aN6ylYJHF7R9NI8Yh 06k+dXCIK51ETRVXbWDnNa5KHxa1pYdBOVlBt/TtHq1sqJL0B3eAaNyeoRkuehvk LE+72CxEm4M+D+cw8Mnq3NEND1dCu3PVXykHUWaoLck3hLwS4gvD/J0veht72GId Ifv+wlAObtCijnJ9z5/K54bliPMt/c3HWXQegJ5Z6/aBiha+fYTairu9x3qBht56 yHHc0cPnqMOR710vzB93u8HGP5jbtbqYmQZGyFXheUFLpEdQhzXfGlNeZsE3FRPb 4CCRhSDp0a7sfhNQ29eYI4kTmpadnXLFtrDYeoKnzcrfe6VXwhAZotU1iAmAUCVA 86Z3UI96PeGNwmyuFRAxomtKD5fdilG+IBSrv9WPFHbUzT4OsdldobjAaabGjtvk Lt+MG758/GWgRRldNjozi7YfGukctryMYoJcqv6v8o3sXlahId2CffDCGOxUQgWJ C4u4zgWDMrZVR7Iqcnn044Q4DCb7pSLnaGd7mpG1sW5MDCEhJKbDBBKzwjsuzGRZ YFjAzZ3pCaYrJ2foFteobc43Y0wdugR1AyDNoRY+aaK2qn0EG4gv+NCtlF9ZJlm3 UCYdAFQuevXFJQ5ZO2nHYNkWUMw0xGUG7CXYMXAxRePk8urLY/tC3g0CySJYnBe1 BBEbaX+pR3eT5xnEWPL0 =rchV -----END PGP SIGNATURE----- Merge tag 'v3.0.0' into merge_3_0_0 v3.0.0 release
This commit is contained in:
commit
95503c6608
|
@ -36,6 +36,7 @@
|
|||
/qapi/qapi-commands-common.[ch]
|
||||
/qapi/qapi-commands-crypto.[ch]
|
||||
/qapi/qapi-commands-introspect.[ch]
|
||||
/qapi/qapi-commands-job.[ch]
|
||||
/qapi/qapi-commands-migration.[ch]
|
||||
/qapi/qapi-commands-misc.[ch]
|
||||
/qapi/qapi-commands-net.[ch]
|
||||
|
@ -53,6 +54,7 @@
|
|||
/qapi/qapi-events-common.[ch]
|
||||
/qapi/qapi-events-crypto.[ch]
|
||||
/qapi/qapi-events-introspect.[ch]
|
||||
/qapi/qapi-events-job.[ch]
|
||||
/qapi/qapi-events-migration.[ch]
|
||||
/qapi/qapi-events-misc.[ch]
|
||||
/qapi/qapi-events-net.[ch]
|
||||
|
@ -71,6 +73,7 @@
|
|||
/qapi/qapi-types-common.[ch]
|
||||
/qapi/qapi-types-crypto.[ch]
|
||||
/qapi/qapi-types-introspect.[ch]
|
||||
/qapi/qapi-types-job.[ch]
|
||||
/qapi/qapi-types-migration.[ch]
|
||||
/qapi/qapi-types-misc.[ch]
|
||||
/qapi/qapi-types-net.[ch]
|
||||
|
@ -88,6 +91,7 @@
|
|||
/qapi/qapi-visit-common.[ch]
|
||||
/qapi/qapi-visit-crypto.[ch]
|
||||
/qapi/qapi-visit-introspect.[ch]
|
||||
/qapi/qapi-visit-job.[ch]
|
||||
/qapi/qapi-visit-migration.[ch]
|
||||
/qapi/qapi-visit-misc.[ch]
|
||||
/qapi/qapi-visit-net.[ch]
|
||||
|
@ -151,6 +155,7 @@
|
|||
.sdk
|
||||
*.gcda
|
||||
*.gcno
|
||||
*.gcov
|
||||
/pc-bios/bios-pq/status
|
||||
/pc-bios/vgabios-pq/status
|
||||
/pc-bios/optionrom/linuxboot.asm
|
||||
|
@ -206,3 +211,4 @@ trace-dtrace-root.h
|
|||
trace-dtrace-root.dtrace
|
||||
trace-ust-all.h
|
||||
trace-ust-all.c
|
||||
/target/arm/decode-sve.inc.c
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[submodule "roms/vgabios"]
|
||||
path = roms/vgabios
|
||||
url = git://git.qemu-project.org/vgabios.git/
|
||||
[submodule "roms/seabios"]
|
||||
path = roms/seabios
|
||||
url = git://git.qemu-project.org/seabios.git/
|
||||
|
|
|
@ -35,13 +35,5 @@ build:
|
|||
options: "-e HOME=/root"
|
||||
ci:
|
||||
- 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}
|
||||
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
|
||||
|
|
17
CODING_STYLE
17
CODING_STYLE
|
@ -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
|
||||
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.1 0x prefix
|
||||
|
|
270
COPYING.PYTHON
270
COPYING.PYTHON
|
@ -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.
|
9
HACKING
9
HACKING
|
@ -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).
|
||||
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
|
||||
qemu_vfree, since breaking this will cause problems on Win32.
|
||||
|
||||
|
|
118
MAINTAINERS
118
MAINTAINERS
|
@ -135,6 +135,8 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
|||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: target/arm/
|
||||
F: tests/tcg/arm/
|
||||
F: tests/tcg/aarch64/
|
||||
F: hw/arm/
|
||||
F: hw/cpu/a*mpcore.c
|
||||
F: include/hw/cpu/a*mpcore.h
|
||||
|
@ -185,7 +187,7 @@ F: disas/microblaze.c
|
|||
|
||||
MIPS
|
||||
M: Aurelien Jarno <aurelien@aurel32.net>
|
||||
M: Yongbok Kim <yongbok.kim@mips.com>
|
||||
M: Aleksandar Markovic <aleksandar.markovic@mips.com>
|
||||
S: Maintained
|
||||
F: target/mips/
|
||||
F: hw/mips/
|
||||
|
@ -283,6 +285,8 @@ M: Richard Henderson <rth@twiddle.net>
|
|||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
S: Maintained
|
||||
F: target/i386/
|
||||
F: tests/tcg/i386/
|
||||
F: tests/tcg/x86_64/
|
||||
F: hw/i386/
|
||||
F: disas/i386.c
|
||||
T: git git://github.com/ehabkost/qemu.git x86-next
|
||||
|
@ -303,6 +307,10 @@ F: target/tricore/
|
|||
F: 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):
|
||||
----------------------
|
||||
|
||||
|
@ -447,6 +455,10 @@ F: hw/timer/cmsdk-apb-timer.c
|
|||
F: include/hw/timer/cmsdk-apb-timer.h
|
||||
F: hw/char/cmsdk-apb-uart.c
|
||||
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
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
|
@ -489,9 +501,10 @@ F: include/hw/arm/digic.h
|
|||
F: hw/*/digic*
|
||||
|
||||
Gumstix
|
||||
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
||||
L: qemu-devel@nongnu.org
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Orphan
|
||||
S: Odd Fixes
|
||||
F: hw/arm/gumstix.c
|
||||
|
||||
i.MX31
|
||||
|
@ -515,8 +528,11 @@ M: Peter Maydell <peter.maydell@linaro.org>
|
|||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/mps2.c
|
||||
F: hw/misc/mps2-scc.c
|
||||
F: include/hw/misc/mps2-scc.h
|
||||
F: hw/arm/mps2-tz.c
|
||||
F: hw/misc/mps2-*.c
|
||||
F: include/hw/misc/mps2-*.h
|
||||
F: hw/arm/iotkit.c
|
||||
F: include/hw/arm/iotkit.h
|
||||
|
||||
Musicpal
|
||||
M: Jan Kiszka <jan.kiszka@web.de>
|
||||
|
@ -629,6 +645,17 @@ M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
|
|||
S: Maintained
|
||||
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
|
||||
-------------
|
||||
Axis Dev88
|
||||
|
@ -691,7 +718,7 @@ S: Maintained
|
|||
F: hw/mips/mips_malta.c
|
||||
|
||||
Mipssim
|
||||
M: Yongbok Kim <yongbok.kim@mips.com>
|
||||
M: Aleksandar Markovic <aleksandar.markovic@mips.com>
|
||||
S: Odd Fixes
|
||||
F: hw/mips/mips_mipssim.c
|
||||
F: hw/net/mipsnet.c
|
||||
|
@ -702,7 +729,7 @@ S: Maintained
|
|||
F: hw/mips/mips_r4k.c
|
||||
|
||||
Fulong 2E
|
||||
M: Yongbok Kim <yongbok.kim@mips.com>
|
||||
M: Aleksandar Markovic <aleksandar.markovic@mips.com>
|
||||
S: Odd Fixes
|
||||
F: hw/mips/mips_fulong2e.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-bridge/dec.[hc]
|
||||
F: hw/misc/macio/
|
||||
F: include/hw/ppc/mac_dbdma.h
|
||||
F: hw/misc/mos6522.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
|
||||
M: Alexander Graf <agraf@suse.de>
|
||||
|
@ -821,6 +851,7 @@ M: BALATON Zoltan <balaton@eik.bme.hu>
|
|||
L: qemu-ppc@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/ide/sii3112.c
|
||||
F: hw/timer/m41t80.c
|
||||
|
||||
SH4 Machines
|
||||
------------
|
||||
|
@ -909,7 +940,7 @@ X86 Machines
|
|||
------------
|
||||
PC
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
S: Supported
|
||||
F: include/hw/i386/
|
||||
F: hw/i386/
|
||||
|
@ -959,7 +990,7 @@ F: include/hw/timer/mc146818rtc*
|
|||
|
||||
Machine core
|
||||
M: Eduardo Habkost <ehabkost@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
S: Supported
|
||||
F: hw/core/machine.c
|
||||
F: hw/core/null-machine.c
|
||||
|
@ -998,6 +1029,7 @@ F: hw/block/cdrom.c
|
|||
F: hw/block/hd-geometry.c
|
||||
F: tests/ide-test.c
|
||||
F: tests/ahci-test.c
|
||||
F: tests/cdrom-test.c
|
||||
F: tests/libqos/ahci*
|
||||
T: git git://github.com/jnsnow/qemu.git ide
|
||||
|
||||
|
@ -1033,7 +1065,7 @@ F: hw/ipack/
|
|||
|
||||
PCI
|
||||
M: Michael S. Tsirkin <mst@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
S: Supported
|
||||
F: include/hw/pci/*
|
||||
F: hw/misc/pci-testdev.c
|
||||
|
@ -1314,6 +1346,33 @@ S: Maintained
|
|||
F: include/hw/misc/unimp.h
|
||||
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
|
||||
----------
|
||||
Audio
|
||||
|
@ -1339,6 +1398,8 @@ F: qemu-img*
|
|||
F: qemu-io*
|
||||
F: tests/qemu-iotests/
|
||||
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
|
||||
|
||||
Block I/O path
|
||||
|
@ -1369,10 +1430,14 @@ L: qemu-block@nongnu.org
|
|||
S: Supported
|
||||
F: blockjob.c
|
||||
F: include/block/blockjob.h
|
||||
F: job.c
|
||||
F: job-qmp.c
|
||||
F: include/block/job.h
|
||||
F: block/backup.c
|
||||
F: block/commit.c
|
||||
F: block/stream.c
|
||||
F: block/mirror.c
|
||||
F: qapi/job.json
|
||||
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
|
||||
|
||||
Block QAPI, monitor, command line
|
||||
|
@ -1573,7 +1638,8 @@ F: tests/test-*-visitor.c
|
|||
F: tests/test-qapi-*.c
|
||||
F: tests/test-qmp-*.c
|
||||
F: tests/test-visitor-serialization.c
|
||||
F: scripts/qapi*
|
||||
F: scripts/qapi-gen.py
|
||||
F: scripts/qapi/*
|
||||
F: docs/devel/qapi*
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
|
@ -1645,6 +1711,7 @@ S: Maintained
|
|||
F: slirp/
|
||||
F: net/slirp.c
|
||||
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
|
||||
|
||||
Stubs
|
||||
|
@ -1656,6 +1723,8 @@ Tracing
|
|||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
S: Maintained
|
||||
F: trace/
|
||||
F: trace-events
|
||||
F: qemu-option-trace.texi
|
||||
F: scripts/tracetool.py
|
||||
F: scripts/tracetool/
|
||||
F: docs/devel/tracing.txt
|
||||
|
@ -1781,6 +1850,12 @@ F: include/sysemu/replay.h
|
|||
F: docs/replay.txt
|
||||
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
|
||||
------------------
|
||||
Overall
|
||||
|
@ -1921,6 +1996,7 @@ F: nbd/
|
|||
F: include/block/nbd*
|
||||
F: qemu-nbd.*
|
||||
F: blockdev-nbd.c
|
||||
F: docs/interop/nbd.txt
|
||||
T: git git://repo.or.cz/qemu/ericb.git nbd
|
||||
|
||||
NFS
|
||||
|
@ -1976,6 +2052,12 @@ S: Supported
|
|||
F: block/quorum.c
|
||||
L: qemu-block@nongnu.org
|
||||
|
||||
blklogwrites
|
||||
M: Ari Sundholm <ari@tuxera.com>
|
||||
L: qemu-block@nongnu.org
|
||||
S: Supported
|
||||
F: block/blklogwrites.c
|
||||
|
||||
blkverify
|
||||
M: Stefan Hajnoczi <stefanha@redhat.com>
|
||||
L: qemu-block@nongnu.org
|
||||
|
@ -2075,7 +2157,7 @@ F: docs/block-replication.txt
|
|||
|
||||
PVRDMA
|
||||
M: Yuval Shaia <yuval.shaia@oracle.com>
|
||||
M: Marcel Apfelbaum <marcel@redhat.com>
|
||||
M: Marcel Apfelbaum <marcel.apfelbaum@gmail.com>
|
||||
S: Maintained
|
||||
F: hw/rdma/*
|
||||
F: hw/rdma/vmw/*
|
||||
|
@ -2090,6 +2172,7 @@ R: Philippe Mathieu-Daudé <f4bug@amsat.org>
|
|||
L: qemu-devel@nongnu.org
|
||||
S: Maintained
|
||||
F: .travis.yml
|
||||
F: scripts/travis/
|
||||
F: .shippable.yml
|
||||
F: tests/docker/
|
||||
F: tests/vm/
|
||||
|
@ -2097,6 +2180,13 @@ W: https://travis-ci.org/qemu/qemu
|
|||
W: https://app.shippable.com/github/qemu/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
|
||||
-------------
|
||||
Build system architecture
|
||||
|
@ -2104,6 +2194,10 @@ M: Daniel P. Berrange <berrange@redhat.com>
|
|||
S: Odd Fixes
|
||||
F: docs/devel/build-system.txt
|
||||
|
||||
Incompatible changes
|
||||
R: libvir-list@redhat.com
|
||||
F: qemu-deprecated.texi
|
||||
|
||||
Build System
|
||||
------------
|
||||
GIT submodules
|
||||
|
|
48
Makefile
48
Makefile
|
@ -20,8 +20,6 @@ ifneq ($(wildcard config-host.mak),)
|
|||
all:
|
||||
include config-host.mak
|
||||
|
||||
PYTHON_UTF8 = LC_ALL= LANG=C LC_CTYPE=en_US.UTF-8 $(PYTHON)
|
||||
|
||||
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-crypto.h qapi/qapi-types-crypto.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-misc.h qapi/qapi-types-misc.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-crypto.h qapi/qapi-visit-crypto.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-misc.h qapi/qapi-visit-misc.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-crypto.h qapi/qapi-commands-crypto.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-misc.h qapi/qapi-commands-misc.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-crypto.h qapi/qapi-events-crypto.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-misc.h qapi/qapi-events-misc.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-xorgxquartz-to-qcode.c \
|
||||
ui/input-keymap-xorgxwin-to-qcode.c \
|
||||
ui/input-keymap-osx-to-qcode.c \
|
||||
$(NULL)
|
||||
|
||||
GENERATED_FILES += $(KEYCODEMAP_FILES)
|
||||
|
@ -347,7 +350,7 @@ $(call set-vpath, $(SRC_PATH))
|
|||
|
||||
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
|
||||
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,)
|
||||
|
||||
dtc/%: .git-submodule-status
|
||||
mkdir -p $@
|
||||
@mkdir -p $@
|
||||
|
||||
# Overriding CFLAGS causes us to lose defines added in the sub-makefile.
|
||||
# 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/common.py \
|
||||
$(SRC_PATH)/scripts/qapi/doc.py \
|
||||
$(SRC_PATH)/scripts/ordereddict.py \
|
||||
$(SRC_PATH)/scripts/qapi-gen.py
|
||||
|
||||
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/qapi-gen-timestamp ;
|
||||
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-" $<, \
|
||||
"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/crypto.json \
|
||||
$(SRC_PATH)/qapi/introspect.json \
|
||||
$(SRC_PATH)/qapi/job.json \
|
||||
$(SRC_PATH)/qapi/migration.json \
|
||||
$(SRC_PATH)/qapi/misc.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-crypto.c qapi/qapi-types-crypto.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-misc.c qapi/qapi-types-misc.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-crypto.c qapi/qapi-visit-crypto.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-misc.c qapi/qapi-visit-misc.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-crypto.c qapi/qapi-commands-crypto.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-misc.c qapi/qapi-commands-misc.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-crypto.c qapi/qapi-events-crypto.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-misc.c qapi/qapi-events-misc.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-gen-timestamp ;
|
||||
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 $<, \
|
||||
"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))), \
|
||||
"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:
|
||||
# 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
|
||||
|
@ -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.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
|
||||
|
||||
|
@ -1047,9 +1072,6 @@ endif
|
|||
include $(SRC_PATH)/tests/docker/Makefile.include
|
||||
include $(SRC_PATH)/tests/vm/Makefile.include
|
||||
|
||||
printgen:
|
||||
@echo $(GENERATED_FILES)
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo 'Generic targets:'
|
||||
|
@ -1069,6 +1091,9 @@ endif
|
|||
echo '')
|
||||
@echo 'Cleaning targets:'
|
||||
@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 ' dist - Build a distributable tarball'
|
||||
@echo ''
|
||||
|
@ -1080,6 +1105,9 @@ endif
|
|||
@echo 'Documentation targets:'
|
||||
@echo ' html info pdf txt'
|
||||
@echo ' - Build documentation in specified format'
|
||||
ifdef CONFIG_GCOV
|
||||
@echo ' coverage-report - Create code coverage report'
|
||||
endif
|
||||
@echo ''
|
||||
ifdef CONFIG_WIN32
|
||||
@echo 'Windows targets:'
|
||||
|
|
|
@ -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-crypto.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-misc.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-crypto.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-misc.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-crypto.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-misc.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 += 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 += qemu-io-cmds.o
|
||||
block-obj-$(CONFIG_REPLICATION) += replication.o
|
||||
|
@ -94,6 +97,7 @@ io-obj-y = io/
|
|||
ifeq ($(CONFIG_SOFTMMU),y)
|
||||
common-obj-y = blockdev.o blockdev-nbd.o block/
|
||||
common-obj-y += bootdevice.o iothread.o
|
||||
common-obj-y += job-qmp.o
|
||||
common-obj-y += net/
|
||||
common-obj-y += qdev-monitor.o device-hotplug.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-crypto.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-misc.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 += util
|
||||
trace-events-subdirs += crypto
|
||||
trace-events-subdirs += io
|
||||
trace-events-subdirs += migration
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += block
|
||||
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/dataplane
|
||||
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/input
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/dma
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sparc64
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/isa
|
||||
trace-events-subdirs += hw/mem
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/i2c
|
||||
trace-events-subdirs += hw/i386
|
||||
trace-events-subdirs += hw/i386/xen
|
||||
trace-events-subdirs += hw/9pfs
|
||||
trace-events-subdirs += hw/ppc
|
||||
trace-events-subdirs += hw/ide
|
||||
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-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/vfio
|
||||
trace-events-subdirs += hw/acpi
|
||||
trace-events-subdirs += hw/arm
|
||||
trace-events-subdirs += hw/alpha
|
||||
trace-events-subdirs += hw/hppa
|
||||
trace-events-subdirs += hw/xen
|
||||
trace-events-subdirs += hw/ide
|
||||
trace-events-subdirs += hw/scsi
|
||||
trace-events-subdirs += hw/sd
|
||||
trace-events-subdirs += hw/sparc
|
||||
trace-events-subdirs += hw/sparc64
|
||||
trace-events-subdirs += hw/timer
|
||||
trace-events-subdirs += hw/tpm
|
||||
trace-events-subdirs += ui
|
||||
trace-events-subdirs += audio
|
||||
trace-events-subdirs += hw/usb
|
||||
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 += qapi
|
||||
trace-events-subdirs += qom
|
||||
trace-events-subdirs += scsi
|
||||
trace-events-subdirs += target/arm
|
||||
trace-events-subdirs += target/i386
|
||||
trace-events-subdirs += target/mips
|
||||
trace-events-subdirs += target/sparc
|
||||
trace-events-subdirs += target/s390x
|
||||
trace-events-subdirs += target/ppc
|
||||
trace-events-subdirs += qom
|
||||
trace-events-subdirs += linux-user
|
||||
trace-events-subdirs += qapi
|
||||
trace-events-subdirs += accel/tcg
|
||||
trace-events-subdirs += accel/kvm
|
||||
trace-events-subdirs += nbd
|
||||
trace-events-subdirs += scsi
|
||||
trace-events-subdirs += target/s390x
|
||||
trace-events-subdirs += target/sparc
|
||||
trace-events-subdirs += ui
|
||||
trace-events-subdirs += util
|
||||
|
||||
trace-events-files = $(SRC_PATH)/trace-events $(trace-events-subdirs:%=$(SRC_PATH)/%/trace-events)
|
||||
|
||||
|
|
|
@ -36,6 +36,11 @@ endif
|
|||
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
|
||||
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-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_INTERPRETER) += tcg/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 += disas.o
|
||||
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
|
||||
|
@ -138,6 +143,7 @@ obj-y += hw/
|
|||
obj-y += memory.o
|
||||
obj-y += memory_mapping.o
|
||||
obj-y += dump.o
|
||||
obj-$(TARGET_X86_64) += win_dump.o
|
||||
obj-y += migration/ram.o
|
||||
LIBS := $(libs_softmmu) $(LIBS)
|
||||
|
||||
|
|
|
@ -70,8 +70,8 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms)
|
|||
|
||||
void configure_accelerator(MachineState *ms)
|
||||
{
|
||||
const char *accel, *p;
|
||||
char buf[10];
|
||||
const char *accel;
|
||||
char **accel_list, **tmp;
|
||||
int ret;
|
||||
bool accel_initialised = false;
|
||||
bool init_failed = false;
|
||||
|
@ -83,13 +83,10 @@ void configure_accelerator(MachineState *ms)
|
|||
accel = "tcg";
|
||||
}
|
||||
|
||||
p = accel;
|
||||
while (!accel_initialised && *p != '\0') {
|
||||
if (*p == ':') {
|
||||
p++;
|
||||
}
|
||||
p = get_opt_name(buf, sizeof(buf), p, ':');
|
||||
acc = accel_find(buf);
|
||||
accel_list = g_strsplit(accel, ":", 0);
|
||||
|
||||
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
|
||||
acc = accel_find(*tmp);
|
||||
if (!acc) {
|
||||
continue;
|
||||
}
|
||||
|
@ -107,6 +104,7 @@ void configure_accelerator(MachineState *ms)
|
|||
accel_initialised = true;
|
||||
}
|
||||
}
|
||||
g_strfreev(accel_list);
|
||||
|
||||
if (!accel_initialised) {
|
||||
if (!init_failed) {
|
||||
|
@ -126,6 +124,15 @@ void accel_register_compat_props(AccelState *accel)
|
|||
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)
|
||||
{
|
||||
type_register_static(&accel_type);
|
||||
|
|
|
@ -256,7 +256,7 @@ int kvm_physical_memory_addr_from_host(KVMState *s, void *ram,
|
|||
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;
|
||||
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.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
|
||||
* value. This is needed based on KVM commit 75d61fbc. */
|
||||
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;
|
||||
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,
|
||||
mem.memory_size, mem.userspace_addr, ret);
|
||||
return ret;
|
||||
|
@ -391,17 +392,14 @@ static int kvm_mem_flags(MemoryRegion *mr)
|
|||
static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
|
||||
MemoryRegion *mr)
|
||||
{
|
||||
int old_flags;
|
||||
|
||||
old_flags = mem->flags;
|
||||
mem->flags = kvm_mem_flags(mr);
|
||||
|
||||
/* If nothing changed effectively, no need to issue ioctl */
|
||||
if (mem->flags == old_flags) {
|
||||
if (mem->flags == mem->old_flags) {
|
||||
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,
|
||||
|
@ -755,7 +753,8 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
|||
|
||||
/* unregister the slot */
|
||||
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) {
|
||||
fprintf(stderr, "%s: error unregistering slot: %s\n",
|
||||
__func__, strerror(-err));
|
||||
|
@ -771,7 +770,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
|
|||
mem->ram = ram;
|
||||
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) {
|
||||
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
|
||||
strerror(-err));
|
||||
|
|
|
@ -21,10 +21,6 @@ void tb_flush(CPUState *cpu)
|
|||
{
|
||||
}
|
||||
|
||||
void tb_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void tlb_set_dirty(CPUState *cpu, target_ulong vaddr)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -18,26 +18,37 @@
|
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "trace/mem.h"
|
||||
|
||||
#if DATA_SIZE == 16
|
||||
# define SUFFIX o
|
||||
# define DATA_TYPE Int128
|
||||
# define BSWAP bswap128
|
||||
# define SHIFT 4
|
||||
#elif DATA_SIZE == 8
|
||||
# define SUFFIX q
|
||||
# define DATA_TYPE uint64_t
|
||||
# define SDATA_TYPE int64_t
|
||||
# define BSWAP bswap64
|
||||
# define SHIFT 3
|
||||
#elif DATA_SIZE == 4
|
||||
# define SUFFIX l
|
||||
# define DATA_TYPE uint32_t
|
||||
# define SDATA_TYPE int32_t
|
||||
# define BSWAP bswap32
|
||||
# define SHIFT 2
|
||||
#elif DATA_SIZE == 2
|
||||
# define SUFFIX w
|
||||
# define DATA_TYPE uint16_t
|
||||
# define SDATA_TYPE int16_t
|
||||
# define BSWAP bswap16
|
||||
# define SHIFT 1
|
||||
#elif DATA_SIZE == 1
|
||||
# define SUFFIX b
|
||||
# define DATA_TYPE uint8_t
|
||||
# define SDATA_TYPE int8_t
|
||||
# define BSWAP
|
||||
# define SHIFT 0
|
||||
#else
|
||||
# error unsupported data size
|
||||
#endif
|
||||
|
@ -48,14 +59,37 @@
|
|||
# define ABI_TYPE uint32_t
|
||||
#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
|
||||
the ATOMIC_NAME macro, and redefined below. */
|
||||
#if DATA_SIZE == 1
|
||||
# define END
|
||||
# define MEND _be /* either le or be would be fine */
|
||||
#elif defined(HOST_WORDS_BIGENDIAN)
|
||||
# define END _be
|
||||
# define MEND _be
|
||||
#else
|
||||
# define END _le
|
||||
# define MEND _le
|
||||
#endif
|
||||
|
||||
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;
|
||||
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;
|
||||
return ret;
|
||||
}
|
||||
|
@ -73,6 +110,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_LD;
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return val;
|
||||
|
@ -83,6 +122,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_ST;
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
}
|
||||
|
@ -92,7 +133,10 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
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;
|
||||
return ret;
|
||||
}
|
||||
|
@ -103,7 +147,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
|||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
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; \
|
||||
return ret; \
|
||||
}
|
||||
|
@ -118,9 +165,48 @@ GEN_ATOMIC_HELPER(or_fetch)
|
|||
GEN_ATOMIC_HELPER(xor_fetch)
|
||||
|
||||
#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 */
|
||||
|
||||
#undef END
|
||||
#undef MEND
|
||||
|
||||
#if DATA_SIZE > 1
|
||||
|
||||
|
@ -128,8 +214,10 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
|||
within the ATOMIC_NAME macro. */
|
||||
#ifdef HOST_WORDS_BIGENDIAN
|
||||
# define END _le
|
||||
# define MEND _le
|
||||
#else
|
||||
# define END _be
|
||||
# define MEND _be
|
||||
#endif
|
||||
|
||||
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;
|
||||
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;
|
||||
return BSWAP(ret);
|
||||
}
|
||||
|
@ -147,6 +238,8 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_LD;
|
||||
__atomic_load(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
return BSWAP(val);
|
||||
|
@ -157,6 +250,8 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
|
||||
|
||||
ATOMIC_TRACE_ST;
|
||||
val = BSWAP(val);
|
||||
__atomic_store(haddr, &val, __ATOMIC_RELAXED);
|
||||
ATOMIC_MMU_CLEANUP;
|
||||
|
@ -167,7 +262,10 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
|
|||
{
|
||||
ATOMIC_MMU_DECLS;
|
||||
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;
|
||||
return BSWAP(ret);
|
||||
}
|
||||
|
@ -178,7 +276,10 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
|
|||
{ \
|
||||
ATOMIC_MMU_DECLS; \
|
||||
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; \
|
||||
return BSWAP(ret); \
|
||||
}
|
||||
|
@ -192,54 +293,64 @@ GEN_ATOMIC_HELPER(xor_fetch)
|
|||
|
||||
#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
|
||||
of bswaps for the reverse-host-endian helpers. */
|
||||
ABI_TYPE ATOMIC_NAME(fetch_add)(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;
|
||||
#define ADD(X, Y) (X + Y)
|
||||
GEN_ATOMIC_HELPER_FN(fetch_add, ADD, DATA_TYPE, old)
|
||||
GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
|
||||
#undef ADD
|
||||
|
||||
ldo = atomic_read__nocheck(haddr);
|
||||
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;
|
||||
}
|
||||
}
|
||||
#undef GEN_ATOMIC_HELPER_FN
|
||||
#endif /* DATA_SIZE >= 16 */
|
||||
|
||||
#undef END
|
||||
#undef MEND
|
||||
#endif /* DATA_SIZE > 1 */
|
||||
|
||||
#undef ATOMIC_TRACE_ST
|
||||
#undef ATOMIC_TRACE_LD
|
||||
#undef ATOMIC_TRACE_RMW
|
||||
|
||||
#undef BSWAP
|
||||
#undef ABI_TYPE
|
||||
#undef DATA_TYPE
|
||||
#undef SDATA_TYPE
|
||||
#undef SUFFIX
|
||||
#undef DATA_SIZE
|
||||
#undef SHIFT
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "qemu/atomic.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "qemu/rcu.h"
|
||||
#include "exec/tb-hash.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)
|
||||
&& qemu_log_in_addr_range(itb->pc)) {
|
||||
qemu_log_lock();
|
||||
int flags = 0;
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
|
||||
flags |= CPU_DUMP_FPU;
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
log_cpu_state(cpu, CPU_DUMP_CCOP);
|
||||
#else
|
||||
log_cpu_state(cpu, 0);
|
||||
flags |= CPU_DUMP_CCOP;
|
||||
#endif
|
||||
log_cpu_state(cpu, flags);
|
||||
qemu_log_unlock();
|
||||
}
|
||||
#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. */
|
||||
cflags |= MIN(max_cycles, CF_COUNT_MASK);
|
||||
|
||||
tb_lock();
|
||||
mmap_lock();
|
||||
tb = tb_gen_code(cpu, orig_tb->pc, orig_tb->cs_base,
|
||||
orig_tb->flags, cflags);
|
||||
tb->orig_tb = orig_tb;
|
||||
tb_unlock();
|
||||
mmap_unlock();
|
||||
|
||||
/* execute the generated code */
|
||||
trace_exec_tb_nocache(tb, tb->pc);
|
||||
cpu_tb_exec(cpu, tb);
|
||||
|
||||
tb_lock();
|
||||
mmap_lock();
|
||||
tb_phys_invalidate(tb, -1);
|
||||
tb_remove(tb);
|
||||
tb_unlock();
|
||||
mmap_unlock();
|
||||
tcg_tb_remove(tb);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -242,12 +244,7 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
tb_lock();
|
||||
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();
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cflags);
|
||||
mmap_unlock();
|
||||
}
|
||||
|
||||
|
@ -262,15 +259,14 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
|||
cpu_tb_exec(cpu, tb);
|
||||
cc->cpu_exec_exit(cpu);
|
||||
} 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
|
||||
* memory.
|
||||
*/
|
||||
#ifndef CONFIG_SOFTMMU
|
||||
tcg_debug_assert(!have_mmap_lock());
|
||||
#endif
|
||||
tb_lock_reset();
|
||||
assert_no_pages_locked();
|
||||
}
|
||||
|
||||
if (in_exclusive_region) {
|
||||
|
@ -293,7 +289,7 @@ struct tb_desc {
|
|||
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 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);
|
||||
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
|
||||
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)
|
||||
|
@ -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,
|
||||
TranslationBlock *tb_next)
|
||||
{
|
||||
uintptr_t old;
|
||||
|
||||
assert(n < ARRAY_SIZE(tb->jmp_list_next));
|
||||
if (tb->jmp_list_next[n]) {
|
||||
/* Another thread has already done this while we were
|
||||
* outside of the lock; nothing to do in this case */
|
||||
return;
|
||||
qemu_spin_lock(&tb_next->jmp_lock);
|
||||
|
||||
/* make sure the destination TB is valid */
|
||||
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,
|
||||
"Linking TBs %p [" TARGET_FMT_lx
|
||||
"] index %d -> %p [" TARGET_FMT_lx "]\n",
|
||||
tb->tc.ptr, tb->pc, n,
|
||||
tb_next->tc.ptr, tb_next->pc);
|
||||
return;
|
||||
|
||||
/* patch the native jump address */
|
||||
tb_set_jmp_target(tb, n, (uintptr_t)tb_next->tc.ptr);
|
||||
|
||||
/* 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;
|
||||
out_unlock_next:
|
||||
qemu_spin_unlock(&tb_next->jmp_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
static inline TranslationBlock *tb_find(CPUState *cpu,
|
||||
|
@ -383,27 +394,11 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
|||
TranslationBlock *tb;
|
||||
target_ulong cs_base, pc;
|
||||
uint32_t flags;
|
||||
bool acquired_tb_lock = false;
|
||||
|
||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||
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();
|
||||
tb_lock();
|
||||
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);
|
||||
}
|
||||
|
||||
tb = tb_gen_code(cpu, pc, cs_base, flags, cf_mask);
|
||||
mmap_unlock();
|
||||
/* 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);
|
||||
|
@ -419,16 +414,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu,
|
|||
#endif
|
||||
/* See if we can patch the calling TB. */
|
||||
if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) {
|
||||
if (!acquired_tb_lock) {
|
||||
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();
|
||||
tb_add_jump(last_tb, tb_exit, tb);
|
||||
}
|
||||
return tb;
|
||||
}
|
||||
|
@ -704,7 +690,9 @@ int cpu_exec(CPUState *cpu)
|
|||
g_assert(cpu == current_cpu);
|
||||
g_assert(cc == CPU_GET_CLASS(cpu));
|
||||
#endif /* buggy compiler */
|
||||
tb_lock_reset();
|
||||
#ifndef CONFIG_SOFTMMU
|
||||
tcg_debug_assert(!have_mmap_lock());
|
||||
#endif
|
||||
if (qemu_mutex_iothread_locked()) {
|
||||
qemu_mutex_unlock_iothread();
|
||||
}
|
||||
|
|
|
@ -125,8 +125,6 @@ static void tlb_flush_nocheck(CPUState *cpu)
|
|||
atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1);
|
||||
tlb_debug("(count: %zu)\n", tlb_flush_count());
|
||||
|
||||
tb_lock();
|
||||
|
||||
memset(env->tlb_table, -1, sizeof(env->tlb_table));
|
||||
memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table));
|
||||
cpu_tb_jmp_cache_clear(cpu);
|
||||
|
@ -135,8 +133,6 @@ static void tlb_flush_nocheck(CPUState *cpu)
|
|||
env->tlb_flush_addr = -1;
|
||||
env->tlb_flush_mask = 0;
|
||||
|
||||
tb_unlock();
|
||||
|
||||
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);
|
||||
|
||||
tb_lock();
|
||||
|
||||
tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask);
|
||||
|
||||
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);
|
||||
|
||||
tlb_debug("done\n");
|
||||
|
||||
tb_unlock();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
|
||||
static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry,
|
||||
target_ulong page)
|
||||
{
|
||||
if (addr == (tlb_entry->addr_read &
|
||||
(TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
|
||||
addr == (tlb_entry->addr_write &
|
||||
(TARGET_PAGE_MASK | TLB_INVALID_MASK)) ||
|
||||
addr == (tlb_entry->addr_code &
|
||||
(TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
return tlb_hit_page(tlb_entry->addr_read, page) ||
|
||||
tlb_hit_page(tlb_entry->addr_write, page) ||
|
||||
tlb_hit_page(tlb_entry->addr_code, page);
|
||||
}
|
||||
|
||||
static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong page)
|
||||
{
|
||||
if (tlb_hit_page_anyprot(tlb_entry, page)) {
|
||||
memset(tlb_entry, -1, sizeof(*tlb_entry));
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) {
|
||||
tlb_flush_entry(&env->tlb_table[mmu_idx][i], 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);
|
||||
}
|
||||
tlb_flush_vtlb_page(env, mmu_idx, 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;
|
||||
int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
int mmu_idx;
|
||||
int i;
|
||||
|
||||
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++) {
|
||||
if (test_bit(mmu_idx, &mmu_idx_bitmap)) {
|
||||
tlb_flush_entry(&env->tlb_table[mmu_idx][page], 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);
|
||||
}
|
||||
tlb_flush_vtlb_page(env, mmu_idx, addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -620,27 +610,42 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
|||
target_ulong address;
|
||||
target_ulong code_address;
|
||||
uintptr_t addend;
|
||||
CPUTLBEntry *te, *tv, tn;
|
||||
hwaddr iotlb, xlat, sz;
|
||||
unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE;
|
||||
CPUTLBEntry *te, tn;
|
||||
hwaddr iotlb, xlat, sz, paddr_page;
|
||||
target_ulong vaddr_page;
|
||||
int asidx = cpu_asidx_from_attrs(cpu, attrs);
|
||||
|
||||
assert_cpu_is_self(cpu);
|
||||
assert(size >= TARGET_PAGE_SIZE);
|
||||
if (size != TARGET_PAGE_SIZE) {
|
||||
tlb_add_large_page(env, vaddr, size);
|
||||
}
|
||||
|
||||
sz = size;
|
||||
section = address_space_translate_for_iotlb(cpu, asidx, paddr, &xlat, &sz);
|
||||
if (size < TARGET_PAGE_SIZE) {
|
||||
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);
|
||||
|
||||
tlb_debug("vaddr=" TARGET_FMT_lx " paddr=0x" TARGET_FMT_plx
|
||||
" prot=%x idx=%d\n",
|
||||
vaddr, paddr, prot, mmu_idx);
|
||||
|
||||
address = vaddr;
|
||||
if (!memory_region_is_ram(section->mr) && !memory_region_is_romd(section->mr)) {
|
||||
address = vaddr_page;
|
||||
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 */
|
||||
address |= TLB_MMIO;
|
||||
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;
|
||||
}
|
||||
|
||||
/* Make sure there's no cached translation for the new page. */
|
||||
tlb_flush_vtlb_page(env, mmu_idx, vaddr_page);
|
||||
|
||||
code_address = address;
|
||||
iotlb = memory_region_section_get_iotlb(cpu, section, vaddr, paddr, xlat,
|
||||
prot, &address);
|
||||
iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page,
|
||||
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];
|
||||
/* 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 */
|
||||
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;
|
||||
|
||||
/* Now calculate the new entry */
|
||||
tn.addend = addend - vaddr;
|
||||
tn.addend = addend - vaddr_page;
|
||||
if (prot & PAGE_READ) {
|
||||
tn.addr_read = address;
|
||||
} else {
|
||||
|
@ -689,7 +715,7 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr,
|
|||
tn.addr_write = address | TLB_MMIO;
|
||||
} else if (memory_region_is_ram(section->mr)
|
||||
&& 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;
|
||||
} else {
|
||||
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,
|
||||
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);
|
||||
hwaddr physaddr = iotlbentry->addr;
|
||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||
hwaddr mr_offset;
|
||||
MemoryRegionSection *section;
|
||||
MemoryRegion *mr;
|
||||
uint64_t val;
|
||||
bool locked = false;
|
||||
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;
|
||||
if (mr != &io_mem_rom && mr != &io_mem_notdirty && !cpu->can_do_io) {
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
|
@ -783,9 +836,13 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
|||
qemu_mutex_lock_iothread();
|
||||
locked = true;
|
||||
}
|
||||
r = memory_region_dispatch_read(mr, physaddr,
|
||||
r = memory_region_dispatch_read(mr, mr_offset,
|
||||
&val, size, iotlbentry->attrs);
|
||||
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,
|
||||
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,
|
||||
int mmu_idx,
|
||||
uint64_t val, target_ulong addr,
|
||||
uintptr_t retaddr, int size)
|
||||
uintptr_t retaddr, bool recheck, int size)
|
||||
{
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
hwaddr physaddr = iotlbentry->addr;
|
||||
MemoryRegion *mr = iotlb_to_region(cpu, physaddr, iotlbentry->attrs);
|
||||
hwaddr mr_offset;
|
||||
MemoryRegionSection *section;
|
||||
MemoryRegion *mr;
|
||||
bool locked = false;
|
||||
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) {
|
||||
cpu_io_recompile(cpu, retaddr);
|
||||
}
|
||||
|
@ -818,9 +902,13 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry,
|
|||
qemu_mutex_lock_iothread();
|
||||
locked = true;
|
||||
}
|
||||
r = memory_region_dispatch_write(mr, physaddr,
|
||||
r = memory_region_dispatch_write(mr, mr_offset,
|
||||
val, size, iotlbentry->attrs);
|
||||
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,
|
||||
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)
|
||||
{
|
||||
int mmu_idx, index, pd;
|
||||
int mmu_idx, index;
|
||||
void *p;
|
||||
MemoryRegion *mr;
|
||||
MemoryRegionSection *section;
|
||||
CPUState *cpu = ENV_GET_CPU(env);
|
||||
CPUIOTLBEntry *iotlbentry;
|
||||
hwaddr physaddr;
|
||||
hwaddr physaddr, mr_offset;
|
||||
|
||||
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
mmu_idx = cpu_mmu_index(env, true);
|
||||
if (unlikely(env->tlb_table[mmu_idx][index].addr_code !=
|
||||
(addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)))) {
|
||||
if (!VICTIM_TLB_HIT(addr_read, addr)) {
|
||||
if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) {
|
||||
if (!VICTIM_TLB_HIT(addr_code, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0);
|
||||
}
|
||||
assert(tlb_hit(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];
|
||||
pd = iotlbentry->addr & ~TARGET_PAGE_MASK;
|
||||
mr = iotlb_to_region(cpu, pd, iotlbentry->attrs);
|
||||
section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs);
|
||||
mr = section->mr;
|
||||
if (memory_region_is_unassigned(mr)) {
|
||||
qemu_mutex_lock_iothread();
|
||||
if (memory_region_request_mmio_ptr(mr, addr)) {
|
||||
|
@ -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
|
||||
* 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,
|
||||
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);
|
||||
target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
|
||||
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
/* TLB entry is for a different page */
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE,
|
||||
|
@ -979,8 +1096,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
|
|||
}
|
||||
|
||||
/* Check TLB entry and enforce page permissions. */
|
||||
if ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE,
|
||||
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;
|
||||
}
|
||||
|
||||
/* Notice an IO access */
|
||||
if (unlikely(tlb_addr & TLB_MMIO)) {
|
||||
/* Notice an IO access or a needs-MMU-lookup access */
|
||||
if (unlikely(tlb_addr & (TLB_MMIO | TLB_RECHECK))) {
|
||||
/* There's really nothing that can be done to
|
||||
support this apart from stop-the-world. */
|
||||
goto stop_the_world;
|
||||
|
|
|
@ -98,10 +98,12 @@
|
|||
static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env,
|
||||
size_t mmu_idx, size_t index,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr)
|
||||
uintptr_t retaddr,
|
||||
bool recheck)
|
||||
{
|
||||
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
|
||||
|
||||
|
@ -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 ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
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
|
||||
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);
|
||||
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 ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(ADDR_READ, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE,
|
||||
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
|
||||
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);
|
||||
return res;
|
||||
}
|
||||
|
@ -259,10 +261,12 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env,
|
|||
size_t mmu_idx, size_t index,
|
||||
DATA_TYPE val,
|
||||
target_ulong addr,
|
||||
uintptr_t retaddr)
|
||||
uintptr_t retaddr,
|
||||
bool recheck)
|
||||
{
|
||||
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,
|
||||
|
@ -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 ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
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
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -315,7 +319,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
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)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
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 ((addr & TARGET_PAGE_MASK)
|
||||
!= (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
|
||||
if (!tlb_hit(tlb_addr, addr)) {
|
||||
if (!VICTIM_TLB_HIT(addr_write, addr)) {
|
||||
tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE,
|
||||
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
|
||||
byte ordering. We should push the LE/BE request down into io. */
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -392,7 +396,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val,
|
|||
page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK;
|
||||
index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
|
||||
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)) {
|
||||
tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE,
|
||||
mmu_idx, retaddr);
|
||||
|
|
|
@ -125,11 +125,19 @@ GEN_ATOMIC_HELPERS(fetch_add)
|
|||
GEN_ATOMIC_HELPERS(fetch_and)
|
||||
GEN_ATOMIC_HELPERS(fetch_or)
|
||||
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(and_fetch)
|
||||
GEN_ATOMIC_HELPERS(or_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)
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -23,10 +23,13 @@
|
|||
|
||||
|
||||
/* 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,
|
||||
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);
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
|
|
|
@ -34,8 +34,6 @@ void translator_loop_temp_check(DisasContextBase *db)
|
|||
void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
||||
CPUState *cpu, TranslationBlock *tb)
|
||||
{
|
||||
int max_insns;
|
||||
|
||||
/* Initialize DisasContext */
|
||||
db->tb = tb;
|
||||
db->pc_first = tb->pc;
|
||||
|
@ -45,18 +43,18 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
|
|||
db->singlestep_enabled = cpu->singlestep_enabled;
|
||||
|
||||
/* Instruction counting */
|
||||
max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
|
||||
if (max_insns == 0) {
|
||||
max_insns = CF_COUNT_MASK;
|
||||
db->max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
|
||||
if (db->max_insns == 0) {
|
||||
db->max_insns = CF_COUNT_MASK;
|
||||
}
|
||||
if (max_insns > TCG_MAX_INSNS) {
|
||||
max_insns = TCG_MAX_INSNS;
|
||||
if (db->max_insns > TCG_MAX_INSNS) {
|
||||
db->max_insns = TCG_MAX_INSNS;
|
||||
}
|
||||
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 */
|
||||
|
||||
/* 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
|
||||
done next -- either exiting this loop or locate the start of
|
||||
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. */
|
||||
gen_io_start();
|
||||
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,
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
#include "qemu-common.h"
|
||||
#include "qom/cpu.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
bool enable_cpu_pm = false;
|
||||
|
||||
void cpu_resume(CPUState *cpu)
|
||||
{
|
||||
|
|
20
arch_init.c
20
arch_init.c
|
@ -29,6 +29,7 @@
|
|||
#include "hw/pci/pci.h"
|
||||
#include "hw/audio/soundhw.h"
|
||||
#include "qapi/qapi-commands-misc.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
|
@ -51,14 +52,14 @@ int graphic_depth = 32;
|
|||
#define QEMU_ARCH QEMU_ARCH_ARM
|
||||
#elif defined(TARGET_CRIS)
|
||||
#define QEMU_ARCH QEMU_ARCH_CRIS
|
||||
#elif defined(TARGET_I386)
|
||||
#define QEMU_ARCH QEMU_ARCH_I386
|
||||
#elif defined(TARGET_HPPA)
|
||||
#define QEMU_ARCH QEMU_ARCH_HPPA
|
||||
#elif defined(TARGET_M68K)
|
||||
#define QEMU_ARCH QEMU_ARCH_M68K
|
||||
#elif defined(TARGET_I386)
|
||||
#define QEMU_ARCH QEMU_ARCH_I386
|
||||
#elif defined(TARGET_LM32)
|
||||
#define QEMU_ARCH QEMU_ARCH_LM32
|
||||
#elif defined(TARGET_M68K)
|
||||
#define QEMU_ARCH QEMU_ARCH_M68K
|
||||
#elif defined(TARGET_MICROBLAZE)
|
||||
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
|
||||
#elif defined(TARGET_MIPS)
|
||||
|
@ -79,12 +80,12 @@ int graphic_depth = 32;
|
|||
#define QEMU_ARCH QEMU_ARCH_SH4
|
||||
#elif defined(TARGET_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)
|
||||
#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
|
||||
|
||||
const uint32_t arch_type = QEMU_ARCH;
|
||||
|
@ -112,7 +113,8 @@ TargetInfo *qmp_query_target(Error **errp)
|
|||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "sysemu/sysemu.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "sysemu/replay.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define AUDIO_CAP "audio"
|
||||
#include "audio_int.h"
|
||||
|
@ -335,9 +336,8 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp)
|
|||
char *strval;
|
||||
|
||||
strval = getenv (key);
|
||||
if (strval) {
|
||||
if (strval && !qemu_strtoi(strval, NULL, 10, &val)) {
|
||||
*defaultp = 0;
|
||||
val = atoi (strval);
|
||||
return val;
|
||||
}
|
||||
else {
|
||||
|
@ -1130,6 +1130,10 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
|
|||
/*
|
||||
* Timer
|
||||
*/
|
||||
|
||||
static bool audio_timer_running;
|
||||
static uint64_t audio_timer_last;
|
||||
|
||||
static int audio_is_timer_needed (void)
|
||||
{
|
||||
HWVoiceIn *hwi = NULL;
|
||||
|
@ -1149,14 +1153,31 @@ static void audio_reset_timer (AudioState *s)
|
|||
if (audio_is_timer_needed ()) {
|
||||
timer_mod_anticipate_ns(s->ts,
|
||||
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
|
||||
}
|
||||
else {
|
||||
timer_del (s->ts);
|
||||
if (!audio_timer_running) {
|
||||
audio_timer_running = true;
|
||||
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)
|
||||
{
|
||||
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_reset_timer (opaque);
|
||||
}
|
||||
|
|
|
@ -15,3 +15,8 @@ alsa_no_frames(int state) "No frames available and ALSA state is %d"
|
|||
# audio/ossaudio.c
|
||||
oss_version(int version) "OSS version = 0x%x"
|
||||
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"
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/virtio/vhost-user.h"
|
||||
#include "standard-headers/linux/virtio_crypto.h"
|
||||
#include "sysemu/cryptodev-vhost.h"
|
||||
#include "chardev/char-fe.h"
|
||||
|
@ -46,6 +47,7 @@
|
|||
typedef struct CryptoDevBackendVhostUser {
|
||||
CryptoDevBackend parent_obj;
|
||||
|
||||
VhostUserState *vhost_user;
|
||||
CharBackend chr;
|
||||
char *chr_name;
|
||||
bool opened;
|
||||
|
@ -102,7 +104,7 @@ cryptodev_vhost_user_start(int queues,
|
|||
continue;
|
||||
}
|
||||
|
||||
options.opaque = &s->chr;
|
||||
options.opaque = s->vhost_user;
|
||||
options.backend_type = VHOST_BACKEND_TYPE_USER;
|
||||
options.cc = b->conf.peers.ccs[i];
|
||||
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;
|
||||
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
|
||||
Error *err = NULL;
|
||||
int queues = b->conf.peers.queues;
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
}
|
||||
}
|
||||
|
||||
static void cryptodev_vhost_user_init(
|
||||
|
@ -185,6 +182,7 @@ static void cryptodev_vhost_user_init(
|
|||
size_t i;
|
||||
Error *local_err = NULL;
|
||||
Chardev *chr;
|
||||
VhostUserState *user;
|
||||
CryptoDevBackendClient *cc;
|
||||
CryptoDevBackendVhostUser *s =
|
||||
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,
|
||||
cryptodev_vhost_user_event, NULL, s, NULL, true);
|
||||
|
||||
|
@ -299,6 +306,12 @@ static void cryptodev_vhost_user_cleanup(
|
|||
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,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "qapi/visitor.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "qom/object_interfaces.h"
|
||||
#include "qemu/mmap-alloc.h"
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
#include <numaif.h>
|
||||
|
@ -246,8 +247,7 @@ bool host_memory_backend_mr_inited(HostMemoryBackend *backend)
|
|||
return memory_region_size(&backend->mr) != 0;
|
||||
}
|
||||
|
||||
MemoryRegion *
|
||||
host_memory_backend_get_memory(HostMemoryBackend *backend, Error **errp)
|
||||
MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
#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
|
||||
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)
|
||||
{
|
||||
HostMemoryBackend *backend = MEMORY_BACKEND(o);
|
||||
|
@ -416,18 +415,11 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
|
|||
&HostMemPolicy_lookup,
|
||||
host_memory_backend_get_policy,
|
||||
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",
|
||||
host_memory_backend_get_share, host_memory_backend_set_share,
|
||||
&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 = {
|
||||
.name = TYPE_MEMORY_BACKEND,
|
||||
.parent = TYPE_OBJECT,
|
||||
|
@ -436,7 +428,6 @@ static const TypeInfo host_memory_backend_info = {
|
|||
.class_init = host_memory_backend_class_init,
|
||||
.instance_size = sizeof(HostMemoryBackend),
|
||||
.instance_init = host_memory_backend_init,
|
||||
.instance_finalize = host_memory_backend_finalize,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_USER_CREATABLE },
|
||||
{ }
|
||||
|
|
286
block.c
286
block.c
|
@ -27,6 +27,7 @@
|
|||
#include "block/block_int.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/nbd.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "module_block.h"
|
||||
#include "qemu/module.h"
|
||||
|
@ -332,6 +333,10 @@ BlockDriverState *bdrv_new(void)
|
|||
|
||||
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);
|
||||
|
||||
return bs;
|
||||
|
@ -720,7 +725,7 @@ static int find_image_format(BlockBackend *file, const char *filename,
|
|||
* Set the current 'total_sectors' value
|
||||
* 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;
|
||||
|
||||
|
@ -817,7 +822,13 @@ static char *bdrv_child_get_parent_desc(BdrvChild *c)
|
|||
static void bdrv_child_cb_drained_begin(BdrvChild *child)
|
||||
{
|
||||
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)
|
||||
|
@ -901,9 +912,11 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options,
|
|||
}
|
||||
|
||||
const BdrvChildRole child_file = {
|
||||
.parent_is_bds = true,
|
||||
.get_parent_desc = bdrv_child_get_parent_desc,
|
||||
.inherit_options = bdrv_inherited_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_poll = bdrv_child_cb_drained_poll,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.attach = bdrv_child_cb_attach,
|
||||
.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 = {
|
||||
.parent_is_bds = true,
|
||||
.get_parent_desc = bdrv_child_get_parent_desc,
|
||||
.inherit_options = bdrv_inherited_fmt_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_poll = bdrv_child_cb_drained_poll,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.attach = bdrv_child_cb_attach,
|
||||
.detach = bdrv_child_cb_detach,
|
||||
|
@ -1042,11 +1057,13 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base,
|
|||
}
|
||||
|
||||
const BdrvChildRole child_backing = {
|
||||
.parent_is_bds = true,
|
||||
.get_parent_desc = bdrv_child_get_parent_desc,
|
||||
.attach = bdrv_backing_attach,
|
||||
.detach = bdrv_backing_detach,
|
||||
.inherit_options = bdrv_backing_options,
|
||||
.drained_begin = bdrv_child_cb_drained_begin,
|
||||
.drained_poll = bdrv_child_cb_drained_poll,
|
||||
.drained_end = bdrv_child_cb_drained_end,
|
||||
.inactivate = bdrv_child_cb_inactivate,
|
||||
.update_filename = bdrv_backing_update_filename,
|
||||
|
@ -1139,6 +1156,12 @@ static void bdrv_assign_node_name(BlockDriverState *bs,
|
|||
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 */
|
||||
pstrcpy(bs->node_name, sizeof(bs->node_name), node_name);
|
||||
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)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
int i, ret;
|
||||
|
||||
bdrv_assign_node_name(bs, node_name, &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(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;
|
||||
open_failed:
|
||||
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);
|
||||
if (ret < 0) {
|
||||
QDECREF(bs->explicit_options);
|
||||
qobject_unref(bs->explicit_options);
|
||||
bs->explicit_options = NULL;
|
||||
QDECREF(bs->options);
|
||||
qobject_unref(bs->options);
|
||||
bs->options = NULL;
|
||||
bdrv_unref(bs);
|
||||
return NULL;
|
||||
|
@ -1460,7 +1489,7 @@ static QDict *parse_json_filename(const char *filename, Error **errp)
|
|||
|
||||
options = qobject_to(QDict, options_obj);
|
||||
if (!options) {
|
||||
qobject_decref(options_obj);
|
||||
qobject_unref(options_obj);
|
||||
error_setg(errp, "Invalid JSON object given");
|
||||
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
|
||||
* specified directly */
|
||||
qdict_join(options, json_options, false);
|
||||
QDECREF(json_options);
|
||||
qobject_unref(json_options);
|
||||
*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
|
||||
* 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);
|
||||
|
||||
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,
|
||||
BdrvChild *c, const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
|
@ -1664,7 +1704,7 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
|
|||
|
||||
/* Write permissions never work with read-only images */
|
||||
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");
|
||||
return -EPERM;
|
||||
|
@ -1914,12 +1954,6 @@ int bdrv_child_try_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared,
|
|||
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,
|
||||
const BdrvChildRole *role,
|
||||
BlockReopenQueue *reopen_queue,
|
||||
|
@ -1956,7 +1990,7 @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
|
|||
&perm, &shared);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -2009,7 +2043,12 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
|||
child->role->detach(child);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -2021,8 +2060,13 @@ static void bdrv_replace_child_noperm(BdrvChild *child,
|
|||
if (new_bs) {
|
||||
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
|
||||
if (new_bs->quiesce_counter && child->role->drained_begin) {
|
||||
for (i = 0; i < new_bs->quiesce_counter; i++) {
|
||||
child->role->drained_begin(child);
|
||||
int num = new_bs->quiesce_counter;
|
||||
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
|
||||
* 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")) {
|
||||
backing_filename[0] = '\0';
|
||||
} else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) {
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
goto free_exit;
|
||||
} else {
|
||||
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) {
|
||||
ret = -EINVAL;
|
||||
error_propagate(errp, local_err);
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
goto free_exit;
|
||||
}
|
||||
}
|
||||
|
@ -2289,7 +2323,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
|||
if (!bs->drv || !bs->drv->supports_backing) {
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Driver doesn't support backing files");
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
goto free_exit;
|
||||
}
|
||||
|
||||
|
@ -2323,7 +2357,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
|
|||
|
||||
free_exit:
|
||||
g_free(backing_filename);
|
||||
QDECREF(tmp_parent_options);
|
||||
qobject_unref(tmp_parent_options);
|
||||
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\"",
|
||||
bdref_key);
|
||||
}
|
||||
QDECREF(image_options);
|
||||
qobject_unref(image_options);
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
@ -2449,7 +2483,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
|
|||
obj = NULL;
|
||||
|
||||
fail:
|
||||
qobject_decref(obj);
|
||||
qobject_unref(obj);
|
||||
visit_free(v);
|
||||
return bs;
|
||||
}
|
||||
|
@ -2519,7 +2553,7 @@ static BlockDriverState *bdrv_append_temp_snapshot(BlockDriverState *bs,
|
|||
}
|
||||
|
||||
out:
|
||||
QDECREF(snapshot_options);
|
||||
qobject_unref(snapshot_options);
|
||||
g_free(tmp_filename);
|
||||
return bs_snapshot;
|
||||
}
|
||||
|
@ -2530,7 +2564,7 @@ out:
|
|||
* 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
|
||||
* 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 it is not NULL, the referenced BDS will be reused.
|
||||
|
@ -2561,7 +2595,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
|||
|
||||
if (reference) {
|
||||
bool options_non_empty = options ? qdict_size(options) : false;
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
|
||||
if (filename || options_non_empty) {
|
||||
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);
|
||||
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
|
||||
/* For snapshot=on, create a temporary qcow2 overlay. bs points to the
|
||||
* temporary snapshot afterwards. */
|
||||
|
@ -2776,10 +2810,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
|
|||
|
||||
fail:
|
||||
blk_unref(file);
|
||||
QDECREF(snapshot_options);
|
||||
QDECREF(bs->explicit_options);
|
||||
QDECREF(bs->options);
|
||||
QDECREF(options);
|
||||
qobject_unref(snapshot_options);
|
||||
qobject_unref(bs->explicit_options);
|
||||
qobject_unref(bs->options);
|
||||
qobject_unref(options);
|
||||
bs->options = NULL;
|
||||
bs->explicit_options = NULL;
|
||||
bdrv_unref(bs);
|
||||
|
@ -2788,8 +2822,8 @@ fail:
|
|||
|
||||
close_and_fail:
|
||||
bdrv_unref(bs);
|
||||
QDECREF(snapshot_options);
|
||||
QDECREF(options);
|
||||
qobject_unref(snapshot_options);
|
||||
qobject_unref(options);
|
||||
error_propagate(errp, local_err);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -2884,7 +2918,7 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
|||
old_options = qdict_clone_shallow(bs->explicit_options);
|
||||
}
|
||||
bdrv_join_options(bs, options, old_options);
|
||||
QDECREF(old_options);
|
||||
qobject_unref(old_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);
|
||||
update_flags_from_options(&flags, opts);
|
||||
qemu_opts_del(opts);
|
||||
QDECREF(options_copy);
|
||||
qobject_unref(options_copy);
|
||||
}
|
||||
|
||||
/* Old values are used for options that aren't set yet */
|
||||
old_options = qdict_clone_shallow(bs->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 */
|
||||
flags &= ~BDRV_O_PROTOCOL;
|
||||
|
@ -2917,8 +2951,8 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
|
|||
bs_entry = g_new0(BlockReopenQueueEntry, 1);
|
||||
QSIMPLEQ_INSERT_TAIL(bs_queue, bs_entry, entry);
|
||||
} else {
|
||||
QDECREF(bs_entry->state.options);
|
||||
QDECREF(bs_entry->state.explicit_options);
|
||||
qobject_unref(bs_entry->state.options);
|
||||
qobject_unref(bs_entry->state.explicit_options);
|
||||
}
|
||||
|
||||
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
|
||||
* 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.
|
||||
*
|
||||
* If all devices prepare successfully, then the changes are committed
|
||||
|
@ -3008,9 +3042,9 @@ cleanup:
|
|||
if (ret && bs_entry->prepared) {
|
||||
bdrv_reopen_abort(&bs_entry->state);
|
||||
} 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_queue);
|
||||
|
@ -3253,7 +3287,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state)
|
|||
}
|
||||
|
||||
/* set BDS specific flags now */
|
||||
QDECREF(bs->explicit_options);
|
||||
qobject_unref(bs->explicit_options);
|
||||
|
||||
bs->explicit_options = reopen_state->explicit_options;
|
||||
bs->open_flags = reopen_state->flags;
|
||||
|
@ -3296,7 +3330,7 @@ void bdrv_reopen_abort(BDRVReopenState *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);
|
||||
}
|
||||
|
@ -3343,11 +3377,11 @@ static void bdrv_close(BlockDriverState *bs)
|
|||
bs->total_sectors = 0;
|
||||
bs->encrypted = false;
|
||||
bs->sg = false;
|
||||
QDECREF(bs->options);
|
||||
QDECREF(bs->explicit_options);
|
||||
qobject_unref(bs->options);
|
||||
qobject_unref(bs->explicit_options);
|
||||
bs->options = NULL;
|
||||
bs->explicit_options = NULL;
|
||||
QDECREF(bs->full_open_options);
|
||||
qobject_unref(bs->full_open_options);
|
||||
bs->full_open_options = NULL;
|
||||
|
||||
bdrv_release_named_dirty_bitmaps(bs);
|
||||
|
@ -3362,7 +3396,7 @@ static void bdrv_close(BlockDriverState *bs)
|
|||
|
||||
void bdrv_close_all(void)
|
||||
{
|
||||
block_job_cancel_sync_all();
|
||||
assert(job_next(NULL) == NULL);
|
||||
nbd_export_close_all();
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (c->role == &child_backing) {
|
||||
/* If @from is a backing file of @to, ignore the child to avoid
|
||||
* creating a loop. We only want to change the pointer of other
|
||||
* parents. */
|
||||
QLIST_FOREACH(to_c, &to->children, next) {
|
||||
if (to_c == c) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (to_c) {
|
||||
/* If the child @c belongs to the BDS @to, replacing the current
|
||||
* c->bs by @to would mean to create a loop.
|
||||
*
|
||||
* Such a case occurs when appending a BDS to a backing chain.
|
||||
* For instance, imagine the following chain:
|
||||
*
|
||||
* guest device -> node A -> further backing chain...
|
||||
*
|
||||
* Now we create a new BDS B which we want to put on top of this
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
@ -3418,6 +3475,7 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to,
|
|||
|
||||
/* Put all parents into @list and calculate their cumulative permissions */
|
||||
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
|
||||
assert(c->bs == from);
|
||||
if (!should_update_child(c, to)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -3717,58 +3775,6 @@ exit:
|
|||
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
|
||||
* allocated space. Return < 0 if error or unknown.
|
||||
|
@ -4025,6 +4031,14 @@ BlockDriverState *bdrv_next_node(BlockDriverState *bs)
|
|||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
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 */
|
||||
|
||||
while (aio_poll(ctx, false)) {
|
||||
|
@ -4950,7 +4964,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
|
|||
*/
|
||||
aio_context_acquire(new_context);
|
||||
bdrv_attach_aio_context(bs, new_context);
|
||||
bdrv_parent_drained_end(bs, NULL);
|
||||
bdrv_parent_drained_end(bs, NULL, false);
|
||||
aio_enable_external(ctx);
|
||||
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,
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque)
|
||||
BlockDriverAmendStatusCB *status_cb, void *cb_opaque,
|
||||
Error **errp)
|
||||
{
|
||||
if (!bs->drv) {
|
||||
error_setg(errp, "Node is ejected");
|
||||
return -ENOMEDIUM;
|
||||
}
|
||||
if (!bs->drv->bdrv_amend_options) {
|
||||
error_setg(errp, "Block driver '%s' does not support option amendment",
|
||||
bs->drv->format_name);
|
||||
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
|
||||
|
@ -5134,8 +5152,8 @@ static bool append_open_options(QDict *d, BlockDriverState *bs)
|
|||
continue;
|
||||
}
|
||||
|
||||
qobject_incref(qdict_entry_value(entry));
|
||||
qdict_put_obj(d, qdict_entry_key(entry), qdict_entry_value(entry));
|
||||
qdict_put_obj(d, qdict_entry_key(entry),
|
||||
qobject_ref(qdict_entry_value(entry)));
|
||||
found_any = true;
|
||||
}
|
||||
|
||||
|
@ -5174,21 +5192,21 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
|||
* information before refreshing it */
|
||||
bs->exact_filename[0] = '\0';
|
||||
if (bs->full_open_options) {
|
||||
QDECREF(bs->full_open_options);
|
||||
qobject_unref(bs->full_open_options);
|
||||
bs->full_open_options = NULL;
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
append_open_options(opts, bs);
|
||||
drv->bdrv_refresh_filename(bs, opts);
|
||||
QDECREF(opts);
|
||||
qobject_unref(opts);
|
||||
} else if (bs->file) {
|
||||
/* Try to reconstruct valid information from the underlying file */
|
||||
bool has_open_options;
|
||||
|
||||
bs->exact_filename[0] = '\0';
|
||||
if (bs->full_open_options) {
|
||||
QDECREF(bs->full_open_options);
|
||||
qobject_unref(bs->full_open_options);
|
||||
bs->full_open_options = NULL;
|
||||
}
|
||||
|
||||
|
@ -5207,12 +5225,12 @@ void bdrv_refresh_filename(BlockDriverState *bs)
|
|||
* suffices without querying the (exact_)filename of this BDS. */
|
||||
if (bs->file->bs->full_open_options) {
|
||||
qdict_put_str(opts, "driver", drv->format_name);
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "file", bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "file",
|
||||
qobject_ref(bs->file->bs->full_open_options));
|
||||
|
||||
bs->full_open_options = opts;
|
||||
} else {
|
||||
QDECREF(opts);
|
||||
qobject_unref(opts);
|
||||
}
|
||||
} else if (!bs->full_open_options && qdict_size(bs->options)) {
|
||||
/* 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));
|
||||
snprintf(bs->filename, sizeof(bs->filename), "json:%s",
|
||||
qstring_get_str(json));
|
||||
QDECREF(json);
|
||||
qobject_unref(json);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ block-obj-y += qed-check.o
|
|||
block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o
|
||||
block-obj-y += quorum.o
|
||||
block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o
|
||||
block-obj-y += blklogwrites.o
|
||||
block-obj-y += block-backend.o snapshot.o qapi.o
|
||||
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
|
||||
block-obj-$(CONFIG_POSIX) += file-posix.o
|
||||
|
@ -26,7 +27,7 @@ block-obj-y += accounting.o dirty-bitmap.o
|
|||
block-obj-y += write-threshold.o
|
||||
block-obj-y += backup.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
|
||||
|
||||
|
|
267
block/backup.c
267
block/backup.c
|
@ -27,7 +27,6 @@
|
|||
#include "qemu/error-report.h"
|
||||
|
||||
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct BackupBlockJob {
|
||||
BlockJob common;
|
||||
|
@ -35,10 +34,10 @@ typedef struct BackupBlockJob {
|
|||
/* bitmap for sync=incremental */
|
||||
BdrvDirtyBitmap *sync_bitmap;
|
||||
MirrorSyncMode sync_mode;
|
||||
RateLimit limit;
|
||||
BlockdevOnError on_source_error;
|
||||
BlockdevOnError on_target_error;
|
||||
CoRwlock flush_rwlock;
|
||||
uint64_t len;
|
||||
uint64_t bytes_read;
|
||||
int64_t cluster_size;
|
||||
bool compress;
|
||||
|
@ -46,8 +45,14 @@ typedef struct BackupBlockJob {
|
|||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||
|
||||
HBitmap *copy_bitmap;
|
||||
bool use_copy_range;
|
||||
int64_t copy_range_size;
|
||||
|
||||
bool serialize_target_writes;
|
||||
} BackupBlockJob;
|
||||
|
||||
static const BlockJobDriver backup_job_driver;
|
||||
|
||||
/* See if in-flight requests overlap and wait for them to complete */
|
||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||
int64_t start,
|
||||
|
@ -85,19 +90,104 @@ static void cow_request_end(CowRequest *req)
|
|||
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,
|
||||
int64_t offset, uint64_t bytes,
|
||||
bool *error_is_read,
|
||||
bool is_write_notifier)
|
||||
{
|
||||
BlockBackend *blk = job->common.blk;
|
||||
CowRequest cow_request;
|
||||
struct iovec iov;
|
||||
QEMUIOVector bounce_qiov;
|
||||
void *bounce_buffer = NULL;
|
||||
int ret = 0;
|
||||
int64_t start, end; /* bytes */
|
||||
int n; /* bytes */
|
||||
void *bounce_buffer = NULL;
|
||||
|
||||
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);
|
||||
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)) {
|
||||
trace_backup_do_cow_skip(job, start);
|
||||
start += job->cluster_size;
|
||||
continue; /* already copied */
|
||||
}
|
||||
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
|
||||
trace_backup_do_cow_process(job, start);
|
||||
|
||||
n = MIN(job->cluster_size, job->common.len - start);
|
||||
|
||||
if (!bounce_buffer) {
|
||||
bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||
}
|
||||
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;
|
||||
if (job->use_copy_range) {
|
||||
ret = backup_cow_with_offload(job, start, end, is_write_notifier);
|
||||
if (ret < 0) {
|
||||
job->use_copy_range = false;
|
||||
}
|
||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||
ret = blk_co_pwrite_zeroes(job->target, start,
|
||||
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 (!job->use_copy_range) {
|
||||
ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier,
|
||||
error_is_read, &bounce_buffer);
|
||||
}
|
||||
if (ret < 0) {
|
||||
trace_backup_do_cow_write_fail(job, start, ret);
|
||||
if (error_is_read) {
|
||||
*error_is_read = false;
|
||||
}
|
||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||
goto out;
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
job->bytes_read += n;
|
||||
job->common.offset += n;
|
||||
start += ret;
|
||||
job->bytes_read += ret;
|
||||
job_progress_update(&job->common.job, ret);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
if (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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
blk_unref(s->target);
|
||||
s->target = NULL;
|
||||
|
@ -253,7 +310,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
|||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
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) {
|
||||
error_setg(errp, "The backup job only supports block checkpoint in"
|
||||
|
@ -261,7 +318,7 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -271,7 +328,7 @@ void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
|||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||
|
@ -327,33 +384,29 @@ typedef struct {
|
|||
int ret;
|
||||
} BackupCompleteData;
|
||||
|
||||
static void backup_complete(BlockJob *job, void *opaque)
|
||||
static void backup_complete(Job *job, void *opaque)
|
||||
{
|
||||
BackupCompleteData *data = opaque;
|
||||
|
||||
block_job_completed(job, data->ret);
|
||||
job_completed(job, data->ret, NULL);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* we need to yield so that bdrv_drain_all() returns.
|
||||
* (without, VM does not reboot)
|
||||
*/
|
||||
if (job->common.speed) {
|
||||
uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
|
||||
job->bytes_read);
|
||||
job->bytes_read = 0;
|
||||
block_job_sleep_ns(&job->common, delay_ns);
|
||||
} else {
|
||||
block_job_sleep_ns(&job->common, 0);
|
||||
}
|
||||
/* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
|
||||
* return. Without a yield, the VM would not reboot. */
|
||||
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
|
||||
job->bytes_read = 0;
|
||||
job_sleep_ns(&job->common.job, delay_ns);
|
||||
|
||||
if (block_job_is_cancelled(&job->common)) {
|
||||
if (job_is_cancelled(&job->common.job)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -368,7 +421,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||
HBitmapIter hbi;
|
||||
|
||||
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
|
||||
while ((cluster = hbitmap_iter_next(&hbi)) != -1) {
|
||||
while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) {
|
||||
do {
|
||||
if (yield_and_check(job)) {
|
||||
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);
|
||||
}
|
||||
|
||||
job->common.offset = job->common.len -
|
||||
hbitmap_count(job->copy_bitmap) * job->cluster_size;
|
||||
/* TODO job_progress_set_remaining() would make more sense */
|
||||
job_progress_update(&job->common.job,
|
||||
job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size);
|
||||
|
||||
bdrv_dirty_iter_free(dbi);
|
||||
}
|
||||
|
@ -437,7 +491,9 @@ static void coroutine_fn backup_run(void *opaque)
|
|||
QLIST_INIT(&job->inflight_reqs);
|
||||
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);
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
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) {
|
||||
/* All bits are set in copy_bitmap to allow any cluster to be copied.
|
||||
* This does not actually require them to be copied. */
|
||||
while (!block_job_is_cancelled(&job->common)) {
|
||||
while (!job_is_cancelled(&job->common.job)) {
|
||||
/* Yield until the job is cancelled. We just let our before_write
|
||||
* notify callback service CoW requests. */
|
||||
block_job_yield(&job->common);
|
||||
job_yield(&job->common.job);
|
||||
}
|
||||
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
|
||||
ret = backup_run_incremental(job);
|
||||
} else {
|
||||
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
||||
for (offset = 0; offset < job->common.len;
|
||||
for (offset = 0; offset < job->len;
|
||||
offset += job->cluster_size) {
|
||||
bool error_is_read;
|
||||
int alloced = 0;
|
||||
|
@ -530,17 +586,21 @@ static void coroutine_fn backup_run(void *opaque)
|
|||
|
||||
data = g_malloc(sizeof(*data));
|
||||
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 = {
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_BACKUP,
|
||||
.start = backup_run,
|
||||
.set_speed = backup_set_speed,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
.clean = backup_clean,
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(BackupBlockJob),
|
||||
.job_type = JOB_TYPE_BACKUP,
|
||||
.free = block_job_free,
|
||||
.user_resume = block_job_user_resume,
|
||||
.drain = block_job_drain,
|
||||
.start = backup_run,
|
||||
.commit = backup_commit,
|
||||
.abort = backup_abort,
|
||||
.clean = backup_clean,
|
||||
},
|
||||
.attached_aio_context = backup_attached_aio_context,
|
||||
.drain = backup_drain,
|
||||
};
|
||||
|
@ -553,7 +613,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||
BlockdevOnError on_target_error,
|
||||
int creation_flags,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
BlockJobTxn *txn, Error **errp)
|
||||
JobTxn *txn, Error **errp)
|
||||
{
|
||||
int64_t len;
|
||||
BlockDriverInfo bdi;
|
||||
|
@ -620,7 +680,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||
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,
|
||||
BLK_PERM_CONSISTENT_READ,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
|
||||
|
@ -646,6 +706,9 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
|||
sync_bitmap : NULL;
|
||||
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
|
||||
* backup cluster size is smaller than the target cluster size. Even for
|
||||
* 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 {
|
||||
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() */
|
||||
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
||||
&error_abort);
|
||||
job->common.len = len;
|
||||
job->len = len;
|
||||
|
||||
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);
|
||||
}
|
||||
if (job) {
|
||||
backup_clean(&job->common);
|
||||
block_job_early_fail(&job->common);
|
||||
backup_clean(&job->common.job);
|
||||
job_early_fail(&job->common.job);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
|
@ -305,7 +305,7 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
|
|||
|
||||
if (c != filename) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -398,10 +398,11 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
goto out;
|
||||
}
|
||||
|
||||
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->file->bs->supported_zero_flags;
|
||||
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);
|
||||
ret = -EINVAL;
|
||||
|
||||
/* Set alignment overrides */
|
||||
|
@ -624,7 +625,7 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
|
|||
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,
|
||||
|
@ -845,13 +846,12 @@ static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||
opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", "blkdebug");
|
||||
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "image", bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options));
|
||||
|
||||
for (e = qdict_first(options); e; e = qdict_next(options, e)) {
|
||||
if (strcmp(qdict_entry_key(e), "x-image")) {
|
||||
qobject_incref(qdict_entry_value(e));
|
||||
qdict_put_obj(opts, qdict_entry_key(e), qdict_entry_value(e));
|
||||
qdict_put_obj(opts, qdict_entry_key(e),
|
||||
qobject_ref(qdict_entry_value(e)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
|
@ -35,6 +35,9 @@ static int blkreplay_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
return ret;
|
||||
|
@ -110,7 +113,7 @@ static int coroutine_fn blkreplay_co_pdiscard(BlockDriverState *bs,
|
|||
int64_t offset, int bytes)
|
||||
{
|
||||
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());
|
||||
qemu_coroutine_yield();
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ static void blkverify_parse_filename(const char *filename, QDict *options,
|
|||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
qemu_opts_del(opts);
|
||||
|
@ -291,10 +294,10 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||
QDict *opts = qdict_new();
|
||||
qdict_put_str(opts, "driver", "blkverify");
|
||||
|
||||
QINCREF(bs->file->bs->full_open_options);
|
||||
qdict_put(opts, "raw", bs->file->bs->full_open_options);
|
||||
QINCREF(s->test_file->bs->full_open_options);
|
||||
qdict_put(opts, "test", s->test_file->bs->full_open_options);
|
||||
qdict_put(opts, "raw",
|
||||
qobject_ref(bs->file->bs->full_open_options));
|
||||
qdict_put(opts, "test",
|
||||
qobject_ref(s->test_file->bs->full_open_options));
|
||||
|
||||
bs->full_open_options = opts;
|
||||
}
|
||||
|
|
|
@ -768,6 +768,11 @@ void blk_remove_bs(BlockBackend *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);
|
||||
blk->root = NULL;
|
||||
}
|
||||
|
@ -1555,7 +1560,7 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
|
|||
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)
|
||||
|
@ -1865,13 +1870,7 @@ void blk_op_unblock_all(BlockBackend *blk, Error *reason)
|
|||
|
||||
AioContext *blk_get_aio_context(BlockBackend *blk)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
|
||||
if (bs) {
|
||||
return bdrv_get_aio_context(bs);
|
||||
} else {
|
||||
return qemu_get_aio_context();
|
||||
}
|
||||
return bdrv_get_aio_context(blk_bs(blk));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -31,11 +31,8 @@ enum {
|
|||
COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||
};
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct CommitBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockDriverState *commit_top_bs;
|
||||
BlockBackend *top;
|
||||
BlockBackend *base;
|
||||
|
@ -75,9 +72,10 @@ typedef struct {
|
|||
int ret;
|
||||
} 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;
|
||||
BlockDriverState *top = blk_bs(s->top);
|
||||
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. */
|
||||
blk_unref(s->base);
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && ret == 0) {
|
||||
if (!job_is_cancelled(job) && ret == 0) {
|
||||
/* success */
|
||||
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
|
||||
s->backing_file_str);
|
||||
|
@ -114,12 +112,12 @@ static void commit_complete(BlockJob *job, void *opaque)
|
|||
blk_unref(s->top);
|
||||
|
||||
/* 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
|
||||
* therefore the blockers on the intermediate nodes remain. This would
|
||||
* cause bdrv_set_backing_hd() to fail. */
|
||||
block_job_remove_all_bdrv(job);
|
||||
* job_finish_sync()), job_completed() won't free it and therefore the
|
||||
* blockers on the intermediate nodes remain. This would cause
|
||||
* bdrv_set_backing_hd() to fail. */
|
||||
block_job_remove_all_bdrv(bjob);
|
||||
|
||||
block_job_completed(&s->common, ret);
|
||||
job_completed(job, ret, NULL);
|
||||
g_free(data);
|
||||
|
||||
/* 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 */
|
||||
void *buf = NULL;
|
||||
int bytes_written = 0;
|
||||
int64_t base_len;
|
||||
int64_t len, base_len;
|
||||
|
||||
ret = s->common.len = blk_getlength(s->top);
|
||||
|
||||
if (s->common.len < 0) {
|
||||
ret = len = blk_getlength(s->top);
|
||||
if (len < 0) {
|
||||
goto out;
|
||||
}
|
||||
job_progress_set_remaining(&s->common.job, len);
|
||||
|
||||
ret = base_len = blk_getlength(s->base);
|
||||
if (base_len < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (base_len < s->common.len) {
|
||||
ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL);
|
||||
if (base_len < len) {
|
||||
ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL);
|
||||
if (ret) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -168,14 +166,14 @@ static void coroutine_fn commit_run(void *opaque)
|
|||
|
||||
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;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
job_sleep_ns(&s->common.job, delay_ns);
|
||||
if (job_is_cancelled(&s->common.job)) {
|
||||
break;
|
||||
}
|
||||
/* Copy if allocated above the base */
|
||||
|
@ -198,10 +196,10 @@ static void coroutine_fn commit_run(void *opaque)
|
|||
}
|
||||
}
|
||||
/* Publish progress */
|
||||
s->common.offset += n;
|
||||
job_progress_update(&s->common.job, n);
|
||||
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
if (copy) {
|
||||
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
|
||||
} else {
|
||||
delay_ns = 0;
|
||||
}
|
||||
|
@ -214,25 +212,18 @@ out:
|
|||
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
block_job_defer_to_main_loop(&s->common, 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);
|
||||
job_defer_to_main_loop(&s->common.job, commit_complete, data);
|
||||
}
|
||||
|
||||
static const BlockJobDriver commit_job_driver = {
|
||||
.instance_size = sizeof(CommitBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_COMMIT,
|
||||
.set_speed = commit_set_speed,
|
||||
.start = commit_run,
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(CommitBlockJob),
|
||||
.job_type = JOB_TYPE_COMMIT,
|
||||
.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,
|
||||
|
@ -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,
|
||||
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
|
||||
speed, JOB_DEFAULT, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
@ -382,7 +373,7 @@ void commit_start(const char *job_id, BlockDriverState *bs,
|
|||
s->on_error = on_error;
|
||||
|
||||
trace_commit_start(bs, base, top, s);
|
||||
block_job_start(&s->common);
|
||||
job_start(&s->common.job);
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
@ -395,7 +386,7 @@ fail:
|
|||
if (commit_top_bs) {
|
||||
bdrv_replace_node(commit_top_bs, top, &error_abort);
|
||||
}
|
||||
block_job_early_fail(&s->common);
|
||||
job_early_fail(&s->common.job);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
|
@ -24,28 +24,51 @@
|
|||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/job.h"
|
||||
#include "qapi/qapi-commands-block-core.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "qapi/clone-visitor.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
typedef struct BlockdevCreateCo {
|
||||
typedef struct BlockdevCreateJob {
|
||||
Job common;
|
||||
BlockDriver *drv;
|
||||
BlockdevCreateOptions *opts;
|
||||
int ret;
|
||||
Error **errp;
|
||||
} BlockdevCreateCo;
|
||||
Error *err;
|
||||
} BlockdevCreateJob;
|
||||
|
||||
static void coroutine_fn bdrv_co_create_co_entry(void *opaque)
|
||||
static void blockdev_create_complete(Job *job, void *opaque)
|
||||
{
|
||||
BlockdevCreateCo *cco = opaque;
|
||||
cco->ret = cco->drv->bdrv_co_create(cco->opts, cco->errp);
|
||||
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
|
||||
|
||||
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);
|
||||
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
|
||||
* be whitelisted. */
|
||||
|
@ -55,22 +78,24 @@ void qmp_x_blockdev_create(BlockdevCreateOptions *options, Error **errp)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Call callback if it exists */
|
||||
/* Error out if the driver doesn't support .bdrv_co_create */
|
||||
if (!drv->bdrv_co_create) {
|
||||
error_setg(errp, "Driver does not support blockdev-create");
|
||||
return;
|
||||
}
|
||||
|
||||
cco = (BlockdevCreateCo) {
|
||||
.drv = drv,
|
||||
.opts = options,
|
||||
.ret = -EINPROGRESS,
|
||||
.errp = errp,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_create_co_entry, &cco);
|
||||
qemu_coroutine_enter(co);
|
||||
while (cco.ret == -EINPROGRESS) {
|
||||
aio_poll(qemu_get_aio_context(), true);
|
||||
/* Create the block job */
|
||||
/* TODO Running in the main context. Block drivers need to error out or add
|
||||
* locking when they use a BDS in a different AioContext. */
|
||||
s = job_create(job_id, &blockdev_create_job_driver, NULL,
|
||||
qemu_get_aio_context(), JOB_DEFAULT | JOB_MANUAL_DISMISS,
|
||||
NULL, NULL, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
|
||||
s->drv = drv,
|
||||
s->opts = QAPI_CLONE(BlockdevCreateOptions, options),
|
||||
|
||||
job_start(&s->common);
|
||||
}
|
||||
|
|
116
block/crypto.c
116
block/crypto.c
|
@ -21,15 +21,15 @@
|
|||
#include "qemu/osdep.h"
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "crypto/block.h"
|
||||
#include "qapi/opts-visitor.h"
|
||||
#include "qapi/qapi-visit-crypto.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/option.h"
|
||||
#include "block/crypto.h"
|
||||
#include "crypto.h"
|
||||
|
||||
typedef struct BlockCrypto BlockCrypto;
|
||||
|
||||
|
@ -148,102 +148,36 @@ static QemuOptsList block_crypto_create_opts_luks = {
|
|||
|
||||
|
||||
QCryptoBlockOpenOptions *
|
||||
block_crypto_open_opts_init(QCryptoBlockFormat format,
|
||||
QDict *opts,
|
||||
Error **errp)
|
||||
block_crypto_open_opts_init(QDict *opts, Error **errp)
|
||||
{
|
||||
Visitor *v;
|
||||
QCryptoBlockOpenOptions *ret = NULL;
|
||||
Error *local_err = NULL;
|
||||
QCryptoBlockOpenOptions *ret;
|
||||
|
||||
ret = g_new0(QCryptoBlockOpenOptions, 1);
|
||||
ret->format = format;
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(opts));
|
||||
|
||||
visit_start_struct(v, NULL, NULL, 0, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
v = qobject_input_visitor_new_flat_confused(opts, errp);
|
||||
if (!v) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
|
||||
visit_type_QCryptoBlockOptionsLUKS_members(
|
||||
v, &ret->u.luks, &local_err);
|
||||
break;
|
||||
visit_type_QCryptoBlockOpenOptions(v, NULL, &ret, errp);
|
||||
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QCryptoBlockCreateOptions *
|
||||
block_crypto_create_opts_init(QCryptoBlockFormat format,
|
||||
QDict *opts,
|
||||
Error **errp)
|
||||
block_crypto_create_opts_init(QDict *opts, Error **errp)
|
||||
{
|
||||
Visitor *v;
|
||||
QCryptoBlockCreateOptions *ret = NULL;
|
||||
Error *local_err = NULL;
|
||||
QCryptoBlockCreateOptions *ret;
|
||||
|
||||
ret = g_new0(QCryptoBlockCreateOptions, 1);
|
||||
ret->format = format;
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(opts));
|
||||
|
||||
visit_start_struct(v, NULL, NULL, 0, &local_err);
|
||||
if (local_err) {
|
||||
goto out;
|
||||
v = qobject_input_visitor_new_flat_confused(opts, errp);
|
||||
if (!v) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case Q_CRYPTO_BLOCK_FORMAT_LUKS:
|
||||
visit_type_QCryptoBlockCreateOptionsLUKS_members(
|
||||
v, &ret->u.luks, &local_err);
|
||||
break;
|
||||
visit_type_QCryptoBlockCreateOptions(v, NULL, &ret, errp);
|
||||
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
@ -281,8 +215,9 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
|||
}
|
||||
|
||||
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) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -305,7 +240,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
|
|||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
QDECREF(cryptoopts);
|
||||
qobject_unref(cryptoopts);
|
||||
qapi_free_QCryptoBlockOpenOptions(open_opts);
|
||||
return ret;
|
||||
}
|
||||
|
@ -351,8 +286,9 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn
|
||||
block_crypto_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
BlockCrypto *crypto = bs->opaque;
|
||||
uint64_t payload_offset =
|
||||
|
@ -365,7 +301,7 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t 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)
|
||||
|
@ -605,8 +541,8 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
|
|||
&block_crypto_create_opts_luks,
|
||||
true);
|
||||
|
||||
create_opts = block_crypto_create_opts_init(Q_CRYPTO_BLOCK_FORMAT_LUKS,
|
||||
cryptoopts, errp);
|
||||
qdict_put_str(cryptoopts, "format", "luks");
|
||||
create_opts = block_crypto_create_opts_init(cryptoopts, errp);
|
||||
if (!create_opts) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
|
@ -615,7 +551,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
|
|||
/* Create protocol layer */
|
||||
ret = bdrv_create_file(filename, opts, errp);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
bs = bdrv_open(filename, NULL, NULL,
|
||||
|
@ -635,7 +571,7 @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
|
|||
fail:
|
||||
bdrv_unref(bs);
|
||||
qapi_free_QCryptoBlockCreateOptions(create_opts);
|
||||
QDECREF(cryptoopts);
|
||||
qobject_unref(cryptoopts);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -694,7 +630,7 @@ BlockDriver bdrv_crypto_luks = {
|
|||
.bdrv_child_perm = bdrv_format_default_perms,
|
||||
.bdrv_co_create = block_crypto_co_create_luks,
|
||||
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
|
||||
.bdrv_truncate = block_crypto_truncate,
|
||||
.bdrv_co_truncate = block_crypto_co_truncate,
|
||||
.create_opts = &block_crypto_create_opts_luks,
|
||||
|
||||
.bdrv_reopen_prepare = block_crypto_reopen_prepare,
|
||||
|
|
|
@ -89,13 +89,9 @@
|
|||
}
|
||||
|
||||
QCryptoBlockCreateOptions *
|
||||
block_crypto_create_opts_init(QCryptoBlockFormat format,
|
||||
QDict *opts,
|
||||
Error **errp);
|
||||
block_crypto_create_opts_init(QDict *opts, Error **errp);
|
||||
|
||||
QCryptoBlockOpenOptions *
|
||||
block_crypto_open_opts_init(QCryptoBlockFormat format,
|
||||
QDict *opts,
|
||||
Error **errp);
|
||||
block_crypto_open_opts_init(QDict *opts, Error **errp);
|
||||
|
||||
#endif /* BLOCK_CRYPTO_H__ */
|
||||
|
|
|
@ -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
|
||||
* 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 (d < 0) {
|
||||
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
||||
|
|
|
@ -97,15 +97,6 @@ BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name)
|
|||
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. */
|
||||
BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs,
|
||||
uint32_t granularity,
|
||||
|
@ -250,57 +241,31 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = false;
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(bitmap->mutex == bitmap->successor->mutex);
|
||||
qemu_mutex_lock(bitmap->mutex);
|
||||
bdrv_enable_dirty_bitmap(bitmap->successor);
|
||||
bdrv_enable_dirty_bitmap_locked(bitmap->successor);
|
||||
qemu_mutex_unlock(bitmap->mutex);
|
||||
}
|
||||
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock */
|
||||
static void bdrv_do_release_matching_dirty_bitmap_locked(
|
||||
BlockDriverState *bs, BdrvDirtyBitmap *bitmap,
|
||||
bool (*cond)(BdrvDirtyBitmap *bitmap))
|
||||
/* Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */
|
||||
static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if ((!bitmap || bm == bitmap) && (!cond || cond(bm))) {
|
||||
assert(!bm->active_iterators);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bm));
|
||||
assert(!bm->meta);
|
||||
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);
|
||||
assert(!bitmap->active_iterators);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
assert(!bitmap->meta);
|
||||
QLIST_REMOVE(bitmap, list);
|
||||
hbitmap_free(bitmap->bitmap);
|
||||
g_free(bitmap->name);
|
||||
g_free(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -353,7 +318,7 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs,
|
|||
error_setg(errp, "Merging of parent and successor bitmap failed");
|
||||
return NULL;
|
||||
}
|
||||
bdrv_release_dirty_bitmap_locked(bs, successor);
|
||||
bdrv_release_dirty_bitmap_locked(successor);
|
||||
parent->successor = NULL;
|
||||
|
||||
return parent;
|
||||
|
@ -391,15 +356,12 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes)
|
|||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
static bool bdrv_dirty_bitmap_has_name(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
return !!bdrv_dirty_bitmap_name(bitmap);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
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)
|
||||
{
|
||||
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()).
|
||||
* There must not be any frozen bitmaps attached.
|
||||
* This function does not remove persistent bitmaps from the storage.
|
||||
* Called with BQL taken.
|
||||
*/
|
||||
void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs)
|
||||
{
|
||||
bdrv_do_release_matching_dirty_bitmap(bs, NULL,
|
||||
bdrv_dirty_bitmap_get_persistance);
|
||||
BdrvDirtyBitmap *bm, *next;
|
||||
|
||||
bdrv_dirty_bitmaps_lock(bs);
|
||||
QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) {
|
||||
if (bdrv_dirty_bitmap_get_persistance(bm)) {
|
||||
bdrv_release_dirty_bitmap_locked(bm);
|
||||
}
|
||||
}
|
||||
bdrv_dirty_bitmaps_unlock(bs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -442,18 +420,19 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs,
|
|||
}
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
bdrv_dirty_bitmap_lock(bitmap);
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = true;
|
||||
bdrv_dirty_bitmap_unlock(bitmap);
|
||||
}
|
||||
|
||||
/* Called with BQL taken. */
|
||||
void bdrv_enable_dirty_bitmap(BdrvDirtyBitmap *bitmap)
|
||||
{
|
||||
assert(!bdrv_dirty_bitmap_frozen(bitmap));
|
||||
bitmap->disabled = false;
|
||||
bdrv_dirty_bitmap_lock(bitmap);
|
||||
bdrv_enable_dirty_bitmap_locked(bitmap);
|
||||
bdrv_dirty_bitmap_unlock(bitmap);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
|
@ -755,3 +789,21 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t 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
|
@ -162,7 +162,7 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, HANDLE hfile,
|
|||
acb->aio_nbytes = count;
|
||||
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));
|
||||
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,
|
||||
&freeClusters, &totalClusters);
|
||||
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,
|
||||
|
@ -410,32 +414,32 @@ fail:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
static BlockAIOCB *raw_aio_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->aio) {
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
|
||||
nb_sectors, cb, opaque, QEMU_AIO_READ);
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
|
||||
cb, opaque, QEMU_AIO_READ);
|
||||
} else {
|
||||
return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov,
|
||||
nb_sectors << BDRV_SECTOR_BITS,
|
||||
return paio_submit(bs, s->hfile, offset, qiov, bytes,
|
||||
cb, opaque, QEMU_AIO_READ);
|
||||
}
|
||||
}
|
||||
|
||||
static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
static BlockAIOCB *raw_aio_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (s->aio) {
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, sector_num, qiov,
|
||||
nb_sectors, cb, opaque, QEMU_AIO_WRITE);
|
||||
return win32_aio_submit(bs, s->aio, s->hfile, offset, bytes, qiov,
|
||||
cb, opaque, QEMU_AIO_WRITE);
|
||||
} else {
|
||||
return paio_submit(bs, s->hfile, sector_num << BDRV_SECTOR_BITS, qiov,
|
||||
nb_sectors << BDRV_SECTOR_BITS,
|
||||
return paio_submit(bs, s->hfile, offset, qiov, bytes,
|
||||
cb, opaque, QEMU_AIO_WRITE);
|
||||
}
|
||||
}
|
||||
|
@ -463,8 +467,8 @@ static void raw_close(BlockDriverState *bs)
|
|||
}
|
||||
}
|
||||
|
||||
static int raw_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
LONG low, high;
|
||||
|
@ -632,11 +636,11 @@ BlockDriver bdrv_file = {
|
|||
.bdrv_co_create_opts = raw_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_preadv = raw_aio_preadv,
|
||||
.bdrv_aio_pwritev = raw_aio_pwritev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
|
||||
.bdrv_truncate = raw_truncate,
|
||||
.bdrv_co_truncate = raw_co_truncate,
|
||||
.bdrv_getlength = raw_getlength,
|
||||
.bdrv_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);
|
||||
}
|
||||
|
||||
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,
|
||||
Error **errp)
|
||||
{
|
||||
|
@ -793,9 +803,10 @@ static BlockDriver bdrv_host_device = {
|
|||
.bdrv_probe_device = hdev_probe_device,
|
||||
.bdrv_file_open = hdev_open,
|
||||
.bdrv_close = raw_close,
|
||||
.bdrv_refresh_limits = hdev_refresh_limits,
|
||||
|
||||
.bdrv_aio_readv = raw_aio_readv,
|
||||
.bdrv_aio_writev = raw_aio_writev,
|
||||
.bdrv_aio_preadv = raw_aio_preadv,
|
||||
.bdrv_aio_pwritev = raw_aio_pwritev,
|
||||
.bdrv_aio_flush = raw_aio_flush,
|
||||
|
||||
.bdrv_detach_aio_context = raw_detach_aio_context,
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include <glusterfs/api/glfs.h>
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
@ -650,7 +651,7 @@ static int qemu_gluster_parse_json(BlockdevOptionsGluster *gconf,
|
|||
}
|
||||
gsconf = NULL;
|
||||
|
||||
QDECREF(backing_options);
|
||||
qobject_unref(backing_options);
|
||||
backing_options = NULL;
|
||||
g_free(str);
|
||||
str = NULL;
|
||||
|
@ -663,7 +664,7 @@ out:
|
|||
qapi_free_SocketAddress(gsconf);
|
||||
qemu_opts_del(opts);
|
||||
g_free(str);
|
||||
QDECREF(backing_options);
|
||||
qobject_unref(backing_options);
|
||||
errno = EINVAL;
|
||||
return -errno;
|
||||
}
|
||||
|
@ -1176,8 +1177,10 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
|
|||
return acb.ret;
|
||||
}
|
||||
|
||||
static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static coroutine_fn int qemu_gluster_co_truncate(BlockDriverState *bs,
|
||||
int64_t offset,
|
||||
PreallocMode prealloc,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVGlusterState *s = bs->opaque;
|
||||
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,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
assert(!flags);
|
||||
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 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,
|
||||
off_t *data, off_t *hole)
|
||||
|
@ -1496,7 +1501,7 @@ static BlockDriver bdrv_gluster = {
|
|||
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
|
||||
.bdrv_getlength = qemu_gluster_getlength,
|
||||
.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_writev = qemu_gluster_co_writev,
|
||||
.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_getlength = qemu_gluster_getlength,
|
||||
.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_writev = qemu_gluster_co_writev,
|
||||
.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_getlength = qemu_gluster_getlength,
|
||||
.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_writev = qemu_gluster_co_writev,
|
||||
.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_getlength = qemu_gluster_getlength,
|
||||
.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_writev = qemu_gluster_co_writev,
|
||||
.bdrv_co_flush_to_disk = qemu_gluster_co_flush_to_disk,
|
||||
|
|
886
block/io.c
886
block/io.c
File diff suppressed because it is too large
Load Diff
364
block/iscsi.c
364
block/iscsi.c
|
@ -33,6 +33,7 @@
|
|||
#include "qemu/bitops.h"
|
||||
#include "qemu/bitmap.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "scsi/constants.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "qemu/option.h"
|
||||
|
@ -43,6 +44,7 @@
|
|||
#include "qapi/qmp/qstring.h"
|
||||
#include "crypto/secret.h"
|
||||
#include "scsi/utils.h"
|
||||
#include "trace.h"
|
||||
|
||||
/* Conflict between scsi/utils.h and libiscsi! :( */
|
||||
#define SCSI_XFER_NONE ISCSI_XFER_NONE
|
||||
|
@ -68,6 +70,7 @@ typedef struct IscsiLun {
|
|||
QemuMutex mutex;
|
||||
struct scsi_inquiry_logical_block_provisioning lbp;
|
||||
struct scsi_inquiry_block_limits bl;
|
||||
struct scsi_inquiry_device_designator *dd;
|
||||
unsigned char *zeroblock;
|
||||
/* The allocmap tracks which clusters (pages) on the iSCSI target are
|
||||
* 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);
|
||||
}
|
||||
|
||||
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
|
||||
iscsi_co_writev_flags(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *iov, int flags)
|
||||
iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *iov, int flags)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
struct IscsiTask iTask;
|
||||
|
@ -616,12 +630,7 @@ retry:
|
|||
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
||||
iov->niov);
|
||||
#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) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
|
@ -692,13 +701,7 @@ retry:
|
|||
ret = -ENOMEM;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
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.do_retry) {
|
||||
if (iTask.task != NULL) {
|
||||
|
@ -732,7 +735,7 @@ retry:
|
|||
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 ||
|
||||
lbasd->provisioning == SCSI_PROVISIONING_TYPE_ANCHORED) {
|
||||
|
@ -862,13 +865,8 @@ retry:
|
|||
#if LIBISCSI_API_VERSION < (20160603)
|
||||
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
||||
#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) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
iTask.task = NULL;
|
||||
|
@ -905,12 +903,7 @@ retry:
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
|
@ -1142,12 +1135,7 @@ retry:
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
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) {
|
||||
scsi_free_scsi_task(iTask.task);
|
||||
|
@ -1243,12 +1231,7 @@ retry:
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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.status == SCSI_STATUS_CHECK_CONDITION &&
|
||||
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
||||
|
@ -1732,14 +1715,34 @@ static QemuOptsList runtime_opts = {
|
|||
.name = "timeout",
|
||||
.type = QEMU_OPT_NUMBER,
|
||||
},
|
||||
{
|
||||
.name = "filename",
|
||||
.type = QEMU_OPT_STRING,
|
||||
},
|
||||
{ /* 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,
|
||||
Error **errp)
|
||||
{
|
||||
|
@ -1751,27 +1754,12 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
char *initiator_name = NULL;
|
||||
QemuOpts *opts;
|
||||
Error *local_err = NULL;
|
||||
const char *transport_name, *portal, *target, *filename;
|
||||
const char *transport_name, *portal, *target;
|
||||
#if LIBISCSI_API_VERSION >= (20160603)
|
||||
enum iscsi_transport_type transport;
|
||||
#endif
|
||||
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);
|
||||
qemu_opts_absorb_qdict(opts, options, &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_inquiry_logical_block_provisioning *inq_lbp;
|
||||
struct scsi_inquiry_block_limits *inq_bl;
|
||||
struct scsi_inquiry_device_identification *inq_di;
|
||||
switch (inq_vpd->pages[i]) {
|
||||
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
||||
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));
|
||||
scsi_free_scsi_task(inq_task);
|
||||
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:
|
||||
break;
|
||||
}
|
||||
|
@ -1989,7 +1989,7 @@ out:
|
|||
}
|
||||
memset(iscsilun, 0, sizeof(IscsiLun));
|
||||
}
|
||||
exit:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2003,6 +2003,10 @@ static void iscsi_close(BlockDriverState *bs)
|
|||
iscsi_logout_sync(iscsi);
|
||||
}
|
||||
iscsi_destroy_context(iscsi);
|
||||
if (iscsilun->dd) {
|
||||
g_free(iscsilun->dd->designator);
|
||||
g_free(iscsilun->dd);
|
||||
}
|
||||
g_free(iscsilun->zeroblock);
|
||||
iscsi_allocmap_free(iscsilun);
|
||||
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,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
IscsiLun *iscsilun = bs->opaque;
|
||||
Error *local_err = NULL;
|
||||
|
@ -2143,7 +2147,7 @@ static int coroutine_fn iscsi_co_create_opts(const char *filename, QemuOpts *opt
|
|||
} else {
|
||||
ret = iscsi_open(bs, bs_options, 0, NULL);
|
||||
}
|
||||
QDECREF(bs_options);
|
||||
qobject_unref(bs_options);
|
||||
|
||||
if (ret != 0) {
|
||||
goto out;
|
||||
|
@ -2184,6 +2188,226 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
|
|||
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 = {
|
||||
.name = "iscsi-create-opts",
|
||||
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
|
||||
|
@ -2213,14 +2437,16 @@ static BlockDriver bdrv_iscsi = {
|
|||
|
||||
.bdrv_getlength = iscsi_getlength,
|
||||
.bdrv_get_info = iscsi_get_info,
|
||||
.bdrv_truncate = iscsi_truncate,
|
||||
.bdrv_co_truncate = iscsi_co_truncate,
|
||||
.bdrv_refresh_limits = iscsi_refresh_limits,
|
||||
|
||||
.bdrv_co_block_status = iscsi_co_block_status,
|
||||
.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_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,
|
||||
|
||||
#ifdef __linux__
|
||||
|
@ -2248,14 +2474,16 @@ static BlockDriver bdrv_iser = {
|
|||
|
||||
.bdrv_getlength = iscsi_getlength,
|
||||
.bdrv_get_info = iscsi_get_info,
|
||||
.bdrv_truncate = iscsi_truncate,
|
||||
.bdrv_co_truncate = iscsi_co_truncate,
|
||||
.bdrv_refresh_limits = iscsi_refresh_limits,
|
||||
|
||||
.bdrv_co_block_status = iscsi_co_block_status,
|
||||
.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_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,
|
||||
|
||||
#ifdef __linux__
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "block/raw-aio.h"
|
||||
#include "qemu/event_notifier.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "qapi/error.h"
|
||||
|
||||
#include <libaio.h>
|
||||
|
||||
|
@ -470,16 +471,21 @@ void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
|
|||
qemu_laio_poll_cb);
|
||||
}
|
||||
|
||||
LinuxAioState *laio_init(void)
|
||||
LinuxAioState *laio_init(Error **errp)
|
||||
{
|
||||
int rc;
|
||||
LinuxAioState *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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
768
block/mirror.c
768
block/mirror.c
File diff suppressed because it is too large
Load Diff
|
@ -259,14 +259,18 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client,
|
|||
|
||||
if (extent->length == 0 ||
|
||||
(client->info.min_block && !QEMU_IS_ALIGNED(extent->length,
|
||||
client->info.min_block)) ||
|
||||
extent->length > orig_length)
|
||||
{
|
||||
client->info.min_block))) {
|
||||
error_setg(errp, "Protocol error: server sent status chunk with "
|
||||
"invalid length");
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -966,6 +970,7 @@ int nbd_client_init(BlockDriverState *bs,
|
|||
const char *export,
|
||||
QCryptoTLSCreds *tlscreds,
|
||||
const char *hostname,
|
||||
const char *x_dirty_bitmap,
|
||||
Error **errp)
|
||||
{
|
||||
NBDClientSession *client = nbd_get_client_session(bs);
|
||||
|
@ -978,9 +983,11 @@ int nbd_client_init(BlockDriverState *bs,
|
|||
client->info.request_sizes = true;
|
||||
client->info.structured_reply = true;
|
||||
client->info.base_allocation = true;
|
||||
client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap);
|
||||
ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export,
|
||||
tlscreds, hostname,
|
||||
&client->ioc, &client->info, errp);
|
||||
g_free(client->info.x_dirty_bitmap);
|
||||
if (ret < 0) {
|
||||
logout("Failed to negotiate with the NBD server\n");
|
||||
return ret;
|
||||
|
|
|
@ -45,6 +45,7 @@ int nbd_client_init(BlockDriverState *bs,
|
|||
const char *export_name,
|
||||
QCryptoTLSCreds *tlscreds,
|
||||
const char *hostname,
|
||||
const char *x_dirty_bitmap,
|
||||
Error **errp);
|
||||
void nbd_client_close(BlockDriverState *bs);
|
||||
|
||||
|
|
32
block/nbd.c
32
block/nbd.c
|
@ -27,7 +27,8 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/nbd-client.h"
|
||||
#include "nbd-client.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/uri.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 */
|
||||
if (uri->server[0] == '[') {
|
||||
host = qstring_from_substr(uri->server, 1,
|
||||
strlen(uri->server) - 2);
|
||||
strlen(uri->server) - 1);
|
||||
} else {
|
||||
host = qstring_from_str(uri->server);
|
||||
}
|
||||
|
@ -262,7 +263,6 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
|||
{
|
||||
SocketAddress *saddr = NULL;
|
||||
QDict *addr = NULL;
|
||||
QObject *crumpled_addr = NULL;
|
||||
Visitor *iv = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
|
@ -272,20 +272,11 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
|||
goto done;
|
||||
}
|
||||
|
||||
crumpled_addr = qdict_crumple(addr, errp);
|
||||
if (!crumpled_addr) {
|
||||
iv = qobject_input_visitor_new_flat_confused(addr, errp);
|
||||
if (!iv) {
|
||||
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);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
|
@ -293,8 +284,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options,
|
|||
}
|
||||
|
||||
done:
|
||||
QDECREF(addr);
|
||||
qobject_decref(crumpled_addr);
|
||||
qobject_unref(addr);
|
||||
visit_free(iv);
|
||||
return saddr;
|
||||
}
|
||||
|
@ -388,6 +378,12 @@ static QemuOptsList nbd_runtime_opts = {
|
|||
.type = QEMU_OPT_STRING,
|
||||
.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 */ }
|
||||
},
|
||||
};
|
||||
|
@ -448,8 +444,8 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
}
|
||||
|
||||
/* NBD handshake */
|
||||
ret = nbd_client_init(bs, sioc, s->export,
|
||||
tlscreds, hostname, errp);
|
||||
ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname,
|
||||
qemu_opt_get(opts, "x-dirty-bitmap"), errp);
|
||||
error:
|
||||
if (sioc) {
|
||||
object_unref(OBJECT(sioc));
|
||||
|
|
25
block/nfs.c
25
block/nfs.c
|
@ -29,6 +29,7 @@
|
|||
#include "qemu/error-report.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "trace.h"
|
||||
#include "qemu/iov.h"
|
||||
#include "qemu/option.h"
|
||||
|
@ -555,24 +556,29 @@ static BlockdevOptionsNfs *nfs_options_qdict_to_qapi(QDict *options,
|
|||
Error **errp)
|
||||
{
|
||||
BlockdevOptionsNfs *opts = NULL;
|
||||
QObject *crumpled = NULL;
|
||||
Visitor *v;
|
||||
const QDictEntry *e;
|
||||
Error *local_err = NULL;
|
||||
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
||||
if (!v) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevOptionsNfs(v, NULL, &opts, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -683,7 +689,7 @@ static int coroutine_fn nfs_file_co_create_opts(const char *url, QemuOpts *opts,
|
|||
|
||||
ret = 0;
|
||||
out:
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
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);
|
||||
}
|
||||
|
||||
static int nfs_file_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn
|
||||
nfs_file_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
NFSClient *client = bs->opaque;
|
||||
int ret;
|
||||
|
@ -867,7 +874,7 @@ static BlockDriver bdrv_nfs = {
|
|||
|
||||
.bdrv_has_zero_init = nfs_has_zero_init,
|
||||
.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_close = nfs_file_close,
|
||||
|
|
48
block/null.c
48
block/null.c
|
@ -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);
|
||||
qemu_opts_del(opts);
|
||||
bs->supported_write_flags = BDRV_REQ_FUA;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -116,22 +117,22 @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
static coroutine_fn int null_co_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
BDRVNullState *s = bs->opaque;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static coroutine_fn int null_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
static coroutine_fn int null_co_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
return null_co_common(bs);
|
||||
}
|
||||
|
@ -186,26 +187,26 @@ static inline BlockAIOCB *null_aio_common(BlockDriverState *bs,
|
|||
return &acb->common;
|
||||
}
|
||||
|
||||
static BlockAIOCB *null_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockAIOCB *null_aio_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
BDRVNullState *s = bs->opaque;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static BlockAIOCB *null_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
static BlockAIOCB *null_aio_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb,
|
||||
void *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)
|
||||
{
|
||||
QINCREF(opts);
|
||||
qdict_del(opts, "filename");
|
||||
|
||||
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);
|
||||
bs->full_open_options = opts;
|
||||
bs->full_open_options = qobject_ref(opts);
|
||||
}
|
||||
|
||||
static BlockDriver bdrv_null_co = {
|
||||
|
@ -266,8 +266,8 @@ static BlockDriver bdrv_null_co = {
|
|||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_co_readv = null_co_readv,
|
||||
.bdrv_co_writev = null_co_writev,
|
||||
.bdrv_co_preadv = null_co_preadv,
|
||||
.bdrv_co_pwritev = null_co_pwritev,
|
||||
.bdrv_co_flush_to_disk = null_co_flush,
|
||||
.bdrv_reopen_prepare = null_reopen_prepare,
|
||||
|
||||
|
@ -286,8 +286,8 @@ static BlockDriver bdrv_null_aio = {
|
|||
.bdrv_close = null_close,
|
||||
.bdrv_getlength = null_getlength,
|
||||
|
||||
.bdrv_aio_readv = null_aio_readv,
|
||||
.bdrv_aio_writev = null_aio_writev,
|
||||
.bdrv_aio_preadv = null_aio_preadv,
|
||||
.bdrv_aio_pwritev = null_aio_pwritev,
|
||||
.bdrv_aio_flush = null_aio_flush,
|
||||
.bdrv_reopen_prepare = null_reopen_prepare,
|
||||
|
||||
|
|
|
@ -1073,7 +1073,6 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state,
|
|||
|
||||
static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
|
||||
{
|
||||
QINCREF(opts);
|
||||
qdict_del(opts, "filename");
|
||||
|
||||
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);
|
||||
bs->full_open_options = opts;
|
||||
bs->full_open_options = qobject_ref(opts);
|
||||
}
|
||||
|
||||
static void nvme_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.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);
|
||||
|
||||
ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors,
|
||||
&qiov);
|
||||
ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE,
|
||||
nb_cow_bytes, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
qemu_vfree(iov.iov_base);
|
||||
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);
|
||||
if (ret < 0) {
|
||||
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,
|
||||
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov)
|
||||
int64_t sector_num, int nb_sectors,
|
||||
QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
BDRVParallelsState *s = bs->opaque;
|
||||
uint64_t bytes_done = 0;
|
||||
QEMUIOVector hd_qiov;
|
||||
int ret = 0;
|
||||
|
||||
assert(!flags);
|
||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||
|
||||
while (nb_sectors > 0) {
|
||||
|
@ -337,7 +341,8 @@ static coroutine_fn int parallels_co_writev(BlockDriverState *bs,
|
|||
qemu_iovec_reset(&hd_qiov);
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
|
@ -376,7 +381,8 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
|
|||
|
||||
if (position < 0) {
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
|
@ -384,7 +390,8 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs,
|
|||
qemu_iovec_memset(&hd_qiov, 0, 0, nbytes);
|
||||
}
|
||||
} 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) {
|
||||
break;
|
||||
}
|
||||
|
@ -613,8 +620,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
|
|||
BlockdevCreateOptions *create_options = NULL;
|
||||
Error *local_err = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
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, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
|
@ -682,7 +685,7 @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
|
|||
ret = 0;
|
||||
|
||||
done:
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
|
|
18
block/qapi.c
18
block/qapi.c
|
@ -593,15 +593,29 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes,
|
|||
p_next = &info->next;
|
||||
}
|
||||
} 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));
|
||||
AioContext *ctx = blk_get_aio_context(blk);
|
||||
BlockStats *s;
|
||||
char *qdev;
|
||||
|
||||
if (!*blk_name(blk) && !blk_get_attached_dev(blk)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
s = bdrv_query_bds_stats(blk_bs(blk), true);
|
||||
s->has_device = true;
|
||||
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);
|
||||
aio_context_release(ctx);
|
||||
|
||||
|
@ -773,7 +787,7 @@ void bdrv_image_info_specific_dump(fprintf_function func_fprintf, void *f,
|
|||
visit_complete(v, &obj);
|
||||
data = qdict_get(qobject_to(QDict, obj), "data");
|
||||
dump_qobject(func_fprintf, f, 1, data);
|
||||
qobject_decref(obj);
|
||||
qobject_unref(obj);
|
||||
visit_free(v);
|
||||
}
|
||||
|
||||
|
|
155
block/qcow.c
155
block/qcow.c
|
@ -26,6 +26,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
|
@ -37,7 +38,7 @@
|
|||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "crypto/block.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "block/crypto.h"
|
||||
#include "crypto.h"
|
||||
|
||||
/**************************************************************/
|
||||
/* QEMU COW block driver with compression and encryption support */
|
||||
|
@ -69,7 +70,6 @@ typedef struct QCowHeader {
|
|||
typedef struct BDRVQcowState {
|
||||
int cluster_bits;
|
||||
int cluster_size;
|
||||
int cluster_sectors;
|
||||
int l2_bits;
|
||||
int l2_size;
|
||||
unsigned int l1_size;
|
||||
|
@ -202,9 +202,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
qdict_del(encryptopts, "format");
|
||||
crypto_opts = block_crypto_open_opts_init(
|
||||
Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
|
||||
qdict_put_str(encryptopts, "format", "qcow");
|
||||
crypto_opts = block_crypto_open_opts_init(encryptopts, errp);
|
||||
if (!crypto_opts) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
|
@ -235,7 +234,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
}
|
||||
s->cluster_bits = header.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_size = 1 << s->l2_bits;
|
||||
bs->total_sectors = header.size / 512;
|
||||
|
@ -315,7 +313,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
QDECREF(encryptopts);
|
||||
qobject_unref(encryptopts);
|
||||
qapi_free_QCryptoBlockOpenOptions(crypto_opts);
|
||||
qemu_co_mutex_init(&s->lock);
|
||||
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_data);
|
||||
qcrypto_block_free(s->crypto);
|
||||
QDECREF(encryptopts);
|
||||
qobject_unref(encryptopts);
|
||||
qapi_free_QCryptoBlockOpenOptions(crypto_opts);
|
||||
return ret;
|
||||
}
|
||||
|
@ -345,8 +343,8 @@ static int qcow_reopen_prepare(BDRVReopenState *state,
|
|||
*
|
||||
* 0 to not allocate.
|
||||
*
|
||||
* 1 to allocate a normal cluster (for sector indexes 'n_start' to
|
||||
* 'n_end')
|
||||
* 1 to allocate a normal cluster (for sector-aligned byte offsets 'n_start'
|
||||
* to 'n_end' within the cluster)
|
||||
*
|
||||
* 2 to allocate a compressed cluster of size
|
||||
* 'compressed_size'. 'compressed_size' must be > 0 and <
|
||||
|
@ -440,9 +438,10 @@ static int get_cluster_offset(BlockDriverState *bs,
|
|||
if (!allocate)
|
||||
return 0;
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
|
||||
assert(QEMU_IS_ALIGNED(n_start | n_end, BDRV_SECTOR_SIZE));
|
||||
/* allocate a new cluster */
|
||||
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
|
||||
decompress it in the case it is not completely
|
||||
overwritten */
|
||||
|
@ -480,16 +479,15 @@ static int get_cluster_offset(BlockDriverState *bs,
|
|||
/* if encrypted, we must initialize the cluster
|
||||
content which won't be written */
|
||||
if (bs->encrypted &&
|
||||
(n_end - n_start) < s->cluster_sectors) {
|
||||
uint64_t start_sect;
|
||||
(n_end - n_start) < s->cluster_size) {
|
||||
uint64_t start_offset;
|
||||
assert(s->crypto);
|
||||
start_sect = (offset & ~(s->cluster_size - 1)) >> 9;
|
||||
for(i = 0; i < s->cluster_sectors; i++) {
|
||||
start_offset = offset & ~(s->cluster_size - 1);
|
||||
for (i = 0; i < s->cluster_size; i += BDRV_SECTOR_SIZE) {
|
||||
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,
|
||||
(start_sect + i) *
|
||||
BDRV_SECTOR_SIZE,
|
||||
start_offset + i,
|
||||
s->cluster_data,
|
||||
BDRV_SECTOR_SIZE,
|
||||
NULL) < 0) {
|
||||
|
@ -497,8 +495,9 @@ static int get_cluster_offset(BlockDriverState *bs,
|
|||
}
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
ret = bdrv_pwrite(bs->file,
|
||||
cluster_offset + i * 512,
|
||||
s->cluster_data, 512);
|
||||
cluster_offset + i,
|
||||
s->cluster_data,
|
||||
BDRV_SECTOR_SIZE);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -612,11 +611,21 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
static void qcow_refresh_limits(BlockDriverState *bs, Error **errp)
|
||||
{
|
||||
/* 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;
|
||||
int index_in_cluster;
|
||||
int offset_in_cluster;
|
||||
int ret = 0, n;
|
||||
uint64_t cluster_offset;
|
||||
struct iovec hd_iov;
|
||||
|
@ -624,6 +633,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
uint8_t *buf;
|
||||
void *orig_buf;
|
||||
|
||||
assert(!flags);
|
||||
if (qiov->niov > 1) {
|
||||
buf = orig_buf = qemu_try_blockalign(bs, qiov->size);
|
||||
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);
|
||||
|
||||
while (nb_sectors != 0) {
|
||||
while (bytes != 0) {
|
||||
/* prepare next request */
|
||||
ret = get_cluster_offset(bs, sector_num << 9,
|
||||
0, 0, 0, 0, &cluster_offset);
|
||||
ret = get_cluster_offset(bs, offset, 0, 0, 0, 0, &cluster_offset);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
offset_in_cluster = offset & (s->cluster_size - 1);
|
||||
n = s->cluster_size - offset_in_cluster;
|
||||
if (n > bytes) {
|
||||
n = bytes;
|
||||
}
|
||||
|
||||
if (!cluster_offset) {
|
||||
if (bs->backing) {
|
||||
/* read from the base image */
|
||||
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_co_mutex_unlock(&s->lock);
|
||||
/* qcow2 emits this on bs->file instead of bs->backing */
|
||||
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);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
/* Note: in this case, no need to wait */
|
||||
memset(buf, 0, 512 * n);
|
||||
memset(buf, 0, n);
|
||||
}
|
||||
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
|
||||
/* 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;
|
||||
break;
|
||||
}
|
||||
memcpy(buf,
|
||||
s->cluster_cache + index_in_cluster * 512, 512 * n);
|
||||
memcpy(buf, s->cluster_cache + offset_in_cluster, n);
|
||||
} else {
|
||||
if ((cluster_offset & 511) != 0) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
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_co_mutex_unlock(&s->lock);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
ret = bdrv_co_readv(bs->file,
|
||||
(cluster_offset >> 9) + index_in_cluster,
|
||||
n, &hd_qiov);
|
||||
ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster,
|
||||
n, &hd_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
|
@ -695,8 +702,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
if (bs->encrypted) {
|
||||
assert(s->crypto);
|
||||
if (qcrypto_block_decrypt(s->crypto,
|
||||
sector_num * BDRV_SECTOR_SIZE, buf,
|
||||
n * BDRV_SECTOR_SIZE, NULL) < 0) {
|
||||
offset, buf, n, NULL) < 0) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
|
@ -704,9 +710,9 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
}
|
||||
ret = 0;
|
||||
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
bytes -= n;
|
||||
offset += n;
|
||||
buf += n;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||
uint64_t bytes, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int index_in_cluster;
|
||||
int offset_in_cluster;
|
||||
uint64_t cluster_offset;
|
||||
int ret = 0, n;
|
||||
struct iovec hd_iov;
|
||||
|
@ -731,6 +738,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
|||
uint8_t *buf;
|
||||
void *orig_buf;
|
||||
|
||||
assert(!flags);
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
|
||||
/* 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);
|
||||
|
||||
while (nb_sectors != 0) {
|
||||
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n = s->cluster_sectors - index_in_cluster;
|
||||
if (n > nb_sectors) {
|
||||
n = nb_sectors;
|
||||
while (bytes != 0) {
|
||||
offset_in_cluster = offset & (s->cluster_size - 1);
|
||||
n = s->cluster_size - offset_in_cluster;
|
||||
if (n > bytes) {
|
||||
n = bytes;
|
||||
}
|
||||
ret = get_cluster_offset(bs, sector_num << 9, 1, 0,
|
||||
index_in_cluster,
|
||||
index_in_cluster + n, &cluster_offset);
|
||||
ret = get_cluster_offset(bs, offset, 1, 0, offset_in_cluster,
|
||||
offset_in_cluster + n, &cluster_offset);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
|
@ -767,30 +773,28 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
|
|||
}
|
||||
if (bs->encrypted) {
|
||||
assert(s->crypto);
|
||||
if (qcrypto_block_encrypt(s->crypto, sector_num * BDRV_SECTOR_SIZE,
|
||||
buf, n * BDRV_SECTOR_SIZE, NULL) < 0) {
|
||||
if (qcrypto_block_encrypt(s->crypto, offset, buf, n, NULL) < 0) {
|
||||
ret = -EIO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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_co_mutex_unlock(&s->lock);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
ret = bdrv_co_writev(bs->file,
|
||||
(cluster_offset >> 9) + index_in_cluster,
|
||||
n, &hd_qiov);
|
||||
ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster,
|
||||
n, &hd_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
nb_sectors -= n;
|
||||
sector_num += n;
|
||||
buf += n * 512;
|
||||
bytes -= n;
|
||||
offset += n;
|
||||
buf += n;
|
||||
}
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
|
@ -934,6 +938,7 @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
|
|||
ret = 0;
|
||||
exit:
|
||||
blk_unref(qcow_blk);
|
||||
bdrv_unref(bs);
|
||||
qcrypto_block_free(crypto);
|
||||
return ret;
|
||||
}
|
||||
|
@ -943,8 +948,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
|
|||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
BlockDriverState *bs = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
const char *val;
|
||||
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, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
|
||||
|
@ -1025,7 +1026,7 @@ static int coroutine_fn qcow_co_create_opts(const char *filename,
|
|||
|
||||
ret = 0;
|
||||
fail:
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
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) {
|
||||
/* could not compress: write normal cluster */
|
||||
ret = qcow_co_writev(bs, offset >> BDRV_SECTOR_BITS,
|
||||
bytes >> BDRV_SECTOR_BITS, qiov);
|
||||
ret = qcow_co_pwritev(bs, offset, bytes, qiov, 0);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -1195,9 +1195,10 @@ static BlockDriver bdrv_qcow = {
|
|||
.bdrv_co_create_opts = qcow_co_create_opts,
|
||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.supports_backing = true,
|
||||
.bdrv_refresh_limits = qcow_refresh_limits,
|
||||
|
||||
.bdrv_co_readv = qcow_co_readv,
|
||||
.bdrv_co_writev = qcow_co_writev,
|
||||
.bdrv_co_preadv = qcow_co_preadv,
|
||||
.bdrv_co_pwritev = qcow_co_pwritev,
|
||||
.bdrv_co_block_status = qcow_co_block_status,
|
||||
|
||||
.bdrv_make_empty = qcow_make_empty,
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include "qemu/cutils.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
|
||||
* _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);
|
||||
if (ret < 0) {
|
||||
assert(bitmap_table == NULL);
|
||||
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) {
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qcow2.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "trace.h"
|
||||
|
||||
|
@ -994,6 +994,17 @@ err:
|
|||
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
|
||||
* write, but require COW to be performed (this includes yet unallocated space,
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "qapi/error.h"
|
||||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qcow2.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
@ -734,7 +734,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret)
|
|||
|
||||
/* Discard is optional, ignore the return value */
|
||||
if (ret >= 0) {
|
||||
bdrv_pdiscard(bs->file->bs, d->offset, d->bytes);
|
||||
bdrv_pdiscard(bs->file, d->offset, d->bytes);
|
||||
}
|
||||
|
||||
g_free(d);
|
||||
|
@ -1577,9 +1577,9 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
case QCOW2_CLUSTER_COMPRESSED:
|
||||
/* Compressed clusters don't have 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 "
|
||||
"clusters\n", l2_entry >> s->cluster_bits);
|
||||
"clusters\n", l2_entry & s->cluster_offset_mask);
|
||||
l2_entry &= ~QCOW_OFLAG_COPIED;
|
||||
res->corruptions++;
|
||||
}
|
||||
|
@ -1799,6 +1799,19 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res,
|
|||
int ret;
|
||||
uint64_t refcount;
|
||||
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++) {
|
||||
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)) {
|
||||
fprintf(stderr, "%s OFLAG_COPIED L2 cluster: l1_index=%d "
|
||||
"l1_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
||||
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||
"ERROR",
|
||||
i, l1_entry, refcount);
|
||||
if (fix & BDRV_FIX_ERRORS) {
|
||||
repair ? "Repairing" : "ERROR", i, l1_entry, refcount);
|
||||
if (repair) {
|
||||
s->l1_table[i] = refcount == 1
|
||||
? 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)) {
|
||||
fprintf(stderr, "%s OFLAG_COPIED data cluster: "
|
||||
"l2_entry=%" PRIx64 " refcount=%" PRIu64 "\n",
|
||||
fix & BDRV_FIX_ERRORS ? "Repairing" :
|
||||
"ERROR",
|
||||
l2_entry, refcount);
|
||||
if (fix & BDRV_FIX_ERRORS) {
|
||||
repair ? "Repairing" : "ERROR", l2_entry, refcount);
|
||||
if (repair) {
|
||||
l2_table[j] = cpu_to_be64(refcount == 1
|
||||
? 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qcow2.h"
|
||||
#include "qcow2.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
|
652
block/qcow2.c
652
block/qcow2.c
File diff suppressed because it is too large
Load Diff
|
@ -77,10 +77,6 @@
|
|||
#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */
|
||||
#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
|
||||
|
||||
|
||||
|
@ -98,6 +94,7 @@
|
|||
#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_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_L2_CACHE_SIZE "l2-cache-size"
|
||||
#define QCOW2_OPT_L2_CACHE_ENTRY_SIZE "l2-cache-entry-size"
|
||||
|
@ -330,6 +327,9 @@ typedef struct BDRVQcow2State {
|
|||
* override) */
|
||||
char *image_backing_file;
|
||||
char *image_backing_format;
|
||||
|
||||
CoQueue compress_wait_queue;
|
||||
int nb_compress_threads;
|
||||
} BDRVQcow2State;
|
||||
|
||||
typedef struct Qcow2COWRegion {
|
||||
|
@ -401,34 +401,36 @@ typedef enum QCow2ClusterType {
|
|||
} QCow2ClusterType;
|
||||
|
||||
typedef enum QCow2MetadataOverlap {
|
||||
QCOW2_OL_MAIN_HEADER_BITNR = 0,
|
||||
QCOW2_OL_ACTIVE_L1_BITNR = 1,
|
||||
QCOW2_OL_ACTIVE_L2_BITNR = 2,
|
||||
QCOW2_OL_REFCOUNT_TABLE_BITNR = 3,
|
||||
QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4,
|
||||
QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
|
||||
QCOW2_OL_INACTIVE_L1_BITNR = 6,
|
||||
QCOW2_OL_INACTIVE_L2_BITNR = 7,
|
||||
QCOW2_OL_MAIN_HEADER_BITNR = 0,
|
||||
QCOW2_OL_ACTIVE_L1_BITNR = 1,
|
||||
QCOW2_OL_ACTIVE_L2_BITNR = 2,
|
||||
QCOW2_OL_REFCOUNT_TABLE_BITNR = 3,
|
||||
QCOW2_OL_REFCOUNT_BLOCK_BITNR = 4,
|
||||
QCOW2_OL_SNAPSHOT_TABLE_BITNR = 5,
|
||||
QCOW2_OL_INACTIVE_L1_BITNR = 6,
|
||||
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_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
|
||||
QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR),
|
||||
QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR),
|
||||
QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR),
|
||||
QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR),
|
||||
QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR),
|
||||
QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR),
|
||||
QCOW2_OL_NONE = 0,
|
||||
QCOW2_OL_MAIN_HEADER = (1 << QCOW2_OL_MAIN_HEADER_BITNR),
|
||||
QCOW2_OL_ACTIVE_L1 = (1 << QCOW2_OL_ACTIVE_L1_BITNR),
|
||||
QCOW2_OL_ACTIVE_L2 = (1 << QCOW2_OL_ACTIVE_L2_BITNR),
|
||||
QCOW2_OL_REFCOUNT_TABLE = (1 << QCOW2_OL_REFCOUNT_TABLE_BITNR),
|
||||
QCOW2_OL_REFCOUNT_BLOCK = (1 << QCOW2_OL_REFCOUNT_BLOCK_BITNR),
|
||||
QCOW2_OL_SNAPSHOT_TABLE = (1 << QCOW2_OL_SNAPSHOT_TABLE_BITNR),
|
||||
QCOW2_OL_INACTIVE_L1 = (1 << QCOW2_OL_INACTIVE_L1_BITNR),
|
||||
/* NOTE: Checking overlaps with inactive L2 tables will result in bdrv
|
||||
* 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;
|
||||
|
||||
/* Perform all overlap checks which can be done in constant time */
|
||||
#define QCOW2_OL_CONSTANT \
|
||||
(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 */
|
||||
#define QCOW2_OL_CACHED \
|
||||
|
@ -618,6 +620,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
|||
int compressed_size);
|
||||
|
||||
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,
|
||||
uint64_t bytes, enum qcow2_discard_type type,
|
||||
bool full_discard);
|
||||
|
|
24
block/qed.c
24
block/qed.c
|
@ -13,6 +13,7 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/bswap.h"
|
||||
|
@ -721,8 +722,7 @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
|
|||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = 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, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
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);
|
||||
|
||||
fail:
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1469,8 +1467,10 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
|
|||
QED_AIOCB_WRITE | QED_AIOCB_ZERO);
|
||||
}
|
||||
|
||||
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs,
|
||||
int64_t offset,
|
||||
PreallocMode prealloc,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVQEDState *s = bs->opaque;
|
||||
uint64_t old_image_size;
|
||||
|
@ -1680,7 +1680,7 @@ static BlockDriver bdrv_qed = {
|
|||
.bdrv_co_readv = bdrv_qed_co_readv,
|
||||
.bdrv_co_writev = bdrv_qed_co_writev,
|
||||
.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_get_info = bdrv_qed_get_info,
|
||||
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "qemu/cutils.h"
|
||||
#include "qemu/option.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qapi-events-block.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
|
@ -115,6 +116,7 @@ struct QuorumAIOCB {
|
|||
/* Request metadata */
|
||||
uint64_t offset;
|
||||
uint64_t bytes;
|
||||
int flags;
|
||||
|
||||
QEMUIOVector *qiov; /* calling IOV */
|
||||
|
||||
|
@ -157,7 +159,8 @@ static bool quorum_64bits_compare(QuorumVoteValue *a, QuorumVoteValue *b)
|
|||
static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
||||
QEMUIOVector *qiov,
|
||||
uint64_t offset,
|
||||
uint64_t bytes)
|
||||
uint64_t bytes,
|
||||
int flags)
|
||||
{
|
||||
BDRVQuorumState *s = bs->opaque;
|
||||
QuorumAIOCB *acb = g_new(QuorumAIOCB, 1);
|
||||
|
@ -168,6 +171,7 @@ static QuorumAIOCB *quorum_aio_get(BlockDriverState *bs,
|
|||
.bs = bs,
|
||||
.offset = offset,
|
||||
.bytes = bytes,
|
||||
.flags = flags,
|
||||
.qiov = qiov,
|
||||
.votes.compare = quorum_sha256_compare,
|
||||
.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;
|
||||
|
||||
/* 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,
|
||||
acb->qiov, 0);
|
||||
acb->qiov, acb->flags & ~BDRV_REQ_WRITE_UNCHANGED);
|
||||
|
||||
/* Wake up the caller after the last rewrite */
|
||||
acb->rewrite_count--;
|
||||
|
@ -608,7 +614,7 @@ static void read_quorum_children_entry(void *opaque)
|
|||
static int read_quorum_children(QuorumAIOCB *acb)
|
||||
{
|
||||
BDRVQuorumState *s = acb->bs->opaque;
|
||||
int i, ret;
|
||||
int i;
|
||||
|
||||
acb->children_read = s->num_children;
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
|
@ -643,9 +649,7 @@ static int read_quorum_children(QuorumAIOCB *acb)
|
|||
qemu_coroutine_yield();
|
||||
}
|
||||
|
||||
ret = acb->vote_ret;
|
||||
|
||||
return ret;
|
||||
return acb->vote_ret;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
acb->is_read = true;
|
||||
|
@ -699,7 +703,7 @@ static void write_quorum_entry(void *opaque)
|
|||
|
||||
sacb->bs = s->children[i]->bs;
|
||||
sacb->ret = bdrv_co_pwritev(s->children[i], acb->offset, acb->bytes,
|
||||
acb->qiov, 0);
|
||||
acb->qiov, acb->flags);
|
||||
if (sacb->ret == 0) {
|
||||
acb->success_count++;
|
||||
} else {
|
||||
|
@ -719,7 +723,7 @@ static int quorum_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
|||
uint64_t bytes, QEMUIOVector *qiov, int flags)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED;
|
||||
|
||||
g_free(opened);
|
||||
goto exit;
|
||||
|
||||
|
@ -1082,8 +1088,8 @@ static void quorum_refresh_filename(BlockDriverState *bs, QDict *options)
|
|||
|
||||
children = qlist_new();
|
||||
for (i = 0; i < s->num_children; i++) {
|
||||
QINCREF(s->children[i]->bs->full_open_options);
|
||||
qlist_append(children, s->children[i]->bs->full_open_options);
|
||||
qlist_append(children,
|
||||
qobject_ref(s->children[i]->bs->full_open_options));
|
||||
}
|
||||
|
||||
opts = qdict_new();
|
||||
|
|
|
@ -167,16 +167,37 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
|||
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,
|
||||
uint64_t bytes, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
ret = raw_adjust_offset(bs, &offset, bytes, false);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
offset += s->offset;
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||
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,
|
||||
int flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
void *buf = NULL;
|
||||
BlockDriver *drv;
|
||||
QEMUIOVector local_qiov;
|
||||
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) {
|
||||
/* Handling partial writes would be a pain - so we just
|
||||
* 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;
|
||||
}
|
||||
|
||||
offset += s->offset;
|
||||
ret = raw_adjust_offset(bs, &offset, bytes, true);
|
||||
if (ret) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
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,
|
||||
BdrvRequestFlags flags)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
int ret;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
|
||||
int64_t offset, int bytes)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
if (offset > UINT64_MAX - s->offset) {
|
||||
return -EINVAL;
|
||||
int ret;
|
||||
|
||||
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
offset += s->offset;
|
||||
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||
return bdrv_co_pdiscard(bs->file, offset, bytes);
|
||||
}
|
||||
|
||||
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,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
|
||||
|
@ -369,7 +383,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
|
|||
|
||||
s->size = 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)
|
||||
|
@ -415,10 +429,11 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
}
|
||||
|
||||
bs->sg = bs->file->bs->sg;
|
||||
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->file->bs->supported_zero_flags;
|
||||
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);
|
||||
|
||||
if (bs->probed && !bdrv_is_read_only(bs)) {
|
||||
fprintf(stderr,
|
||||
|
@ -482,6 +497,44 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *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 = {
|
||||
.format_name = "raw",
|
||||
.instance_size = sizeof(BDRVRawState),
|
||||
|
@ -498,7 +551,9 @@ BlockDriver bdrv_raw = {
|
|||
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = &raw_co_pdiscard,
|
||||
.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,
|
||||
.has_variable_length = true,
|
||||
.bdrv_measure = &raw_measure,
|
||||
|
|
149
block/rbd.c
149
block/rbd.c
|
@ -18,6 +18,7 @@
|
|||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "crypto/secret.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qapi/qmp/qstring.h"
|
||||
|
@ -226,27 +227,57 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options,
|
|||
|
||||
done:
|
||||
g_free(buf);
|
||||
QDECREF(keypairs);
|
||||
qobject_unref(keypairs);
|
||||
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)
|
||||
{
|
||||
if (secretid == 0) {
|
||||
return 0;
|
||||
char *key, *acr;
|
||||
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,
|
||||
errp);
|
||||
if (!secret) {
|
||||
return -1;
|
||||
if (opts->has_auth_client_required) {
|
||||
accu = g_string_new("");
|
||||
for (auth = opts->auth_client_required; auth; auth = auth->next) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -275,17 +306,17 @@ static int qemu_rbd_set_keypairs(rados_t cluster, const char *keypairs_json,
|
|||
key = qstring_get_str(name);
|
||||
|
||||
ret = rados_conf_set(cluster, key, qstring_get_str(value));
|
||||
QDECREF(value);
|
||||
qobject_unref(value);
|
||||
if (ret < 0) {
|
||||
error_setg_errno(errp, -ret, "invalid conf option %s", key);
|
||||
QDECREF(name);
|
||||
qobject_unref(name);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
QDECREF(name);
|
||||
qobject_unref(name);
|
||||
}
|
||||
|
||||
QDECREF(keypairs);
|
||||
qobject_unref(keypairs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -337,9 +368,7 @@ static QemuOptsList runtime_opts = {
|
|||
},
|
||||
};
|
||||
|
||||
/* 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. */
|
||||
/* FIXME Deprecate and remove keypairs or make it available in QMP. */
|
||||
static int qemu_rbd_do_create(BlockdevCreateOptions *options,
|
||||
const char *keypairs, const char *password_secret,
|
||||
Error **errp)
|
||||
|
@ -449,7 +478,7 @@ static int coroutine_fn qemu_rbd_co_create_opts(const char *filename,
|
|||
}
|
||||
|
||||
exit:
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
@ -545,6 +574,16 @@ static int qemu_rbd_connect(rados_t *cluster, rados_ioctx_t *io_ctx,
|
|||
Error *local_err = NULL;
|
||||
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);
|
||||
if (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 = -EIO;
|
||||
r = qemu_rbd_set_auth(*cluster, opts, errp);
|
||||
if (r < 0) {
|
||||
goto failed_shutdown;
|
||||
}
|
||||
|
||||
|
@ -622,28 +661,11 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
BDRVRBDState *s = bs->opaque;
|
||||
BlockdevOptionsRbd *opts = NULL;
|
||||
Visitor *v;
|
||||
QObject *crumpled = NULL;
|
||||
const QDictEntry *e;
|
||||
Error *local_err = NULL;
|
||||
const char *filename;
|
||||
char *keypairs, *secretid;
|
||||
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"));
|
||||
if (keypairs) {
|
||||
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 */
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
||||
if (!v) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
|
@ -899,27 +919,23 @@ failed:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static BlockAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
QEMUIOVector *qiov,
|
||||
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,
|
||||
static BlockAIOCB *qemu_rbd_aio_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque)
|
||||
{
|
||||
return rbd_start_aio(bs, sector_num << BDRV_SECTOR_BITS, qiov,
|
||||
(int64_t) nb_sectors << BDRV_SECTOR_BITS, cb, opaque,
|
||||
return rbd_start_aio(bs, offset, qiov, bytes, 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);
|
||||
}
|
||||
|
||||
|
@ -974,8 +990,10 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs)
|
|||
return info.size;
|
||||
}
|
||||
|
||||
static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn qemu_rbd_co_truncate(BlockDriverState *bs,
|
||||
int64_t offset,
|
||||
PreallocMode prealloc,
|
||||
Error **errp)
|
||||
{
|
||||
BDRVRBDState *s = bs->opaque;
|
||||
int r;
|
||||
|
@ -1158,6 +1176,7 @@ static BlockDriver bdrv_rbd = {
|
|||
.format_name = "rbd",
|
||||
.instance_size = sizeof(BDRVRBDState),
|
||||
.bdrv_parse_filename = qemu_rbd_parse_filename,
|
||||
.bdrv_refresh_limits = qemu_rbd_refresh_limits,
|
||||
.bdrv_file_open = qemu_rbd_open,
|
||||
.bdrv_close = qemu_rbd_close,
|
||||
.bdrv_reopen_prepare = qemu_rbd_reopen_prepare,
|
||||
|
@ -1167,11 +1186,11 @@ static BlockDriver bdrv_rbd = {
|
|||
.bdrv_get_info = qemu_rbd_getinfo,
|
||||
.create_opts = &qemu_rbd_create_opts,
|
||||
.bdrv_getlength = qemu_rbd_getlength,
|
||||
.bdrv_truncate = qemu_rbd_truncate,
|
||||
.bdrv_co_truncate = qemu_rbd_co_truncate,
|
||||
.protocol_name = "rbd",
|
||||
|
||||
.bdrv_aio_readv = qemu_rbd_aio_readv,
|
||||
.bdrv_aio_writev = qemu_rbd_aio_writev,
|
||||
.bdrv_aio_preadv = qemu_rbd_aio_preadv,
|
||||
.bdrv_aio_pwritev = qemu_rbd_aio_pwritev,
|
||||
|
||||
#ifdef LIBRBD_SUPPORTS_AIO_FLUSH
|
||||
.bdrv_aio_flush = qemu_rbd_aio_flush,
|
||||
|
|
|
@ -145,7 +145,7 @@ static void replication_close(BlockDriverState *bs)
|
|||
replication_stop(s->rs, false, NULL);
|
||||
}
|
||||
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) {
|
||||
|
@ -246,13 +246,14 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs,
|
|||
backup_cow_request_begin(&req, child->bs->job,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
remaining_bytes);
|
||||
ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors,
|
||||
qiov);
|
||||
ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE,
|
||||
remaining_bytes, qiov, 0);
|
||||
backup_cow_request_end(&req);
|
||||
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:
|
||||
return replication_return_value(s, ret);
|
||||
}
|
||||
|
@ -260,7 +261,8 @@ out:
|
|||
static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int remaining_sectors,
|
||||
QEMUIOVector *qiov)
|
||||
QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
BDRVReplicationState *s = bs->opaque;
|
||||
QEMUIOVector hd_qiov;
|
||||
|
@ -271,14 +273,15 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
|||
int ret;
|
||||
int64_t n;
|
||||
|
||||
assert(!flags);
|
||||
ret = replication_get_io_status(s);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = bdrv_co_writev(top, sector_num,
|
||||
remaining_sectors, qiov);
|
||||
ret = bdrv_co_pwritev(top, sector_num * BDRV_SECTOR_SIZE,
|
||||
remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0);
|
||||
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);
|
||||
|
||||
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) {
|
||||
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,
|
||||
0, MIRROR_SYNC_MODE_NONE, NULL, false,
|
||||
BLOCKDEV_ON_ERROR_REPORT,
|
||||
BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
|
||||
BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
|
||||
backup_job_completed, bs, NULL, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
|
@ -574,7 +578,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
|
|||
aio_context_release(aio_context);
|
||||
return;
|
||||
}
|
||||
block_job_start(job);
|
||||
job_start(&job->job);
|
||||
break;
|
||||
default:
|
||||
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().
|
||||
*/
|
||||
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) {
|
||||
|
@ -691,7 +695,7 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
|
|||
|
||||
s->stage = BLOCK_REPLICATION_FAILOVER;
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "qemu/option.h"
|
||||
#include "qemu/sockets.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/bitops.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)
|
||||
{
|
||||
QDict *server = NULL;
|
||||
QObject *crumpled_server = NULL;
|
||||
Visitor *iv = NULL;
|
||||
SocketAddress *saddr = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
qdict_extract_subqdict(options, &server, "server.");
|
||||
|
||||
crumpled_server = qdict_crumple(server, errp);
|
||||
if (!crumpled_server) {
|
||||
iv = qobject_input_visitor_new_flat_confused(server, errp);
|
||||
if (!iv) {
|
||||
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);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
|
@ -567,8 +558,7 @@ static SocketAddress *sd_server_config(QDict *options, Error **errp)
|
|||
|
||||
done:
|
||||
visit_free(iv);
|
||||
qobject_decref(crumpled_server);
|
||||
QDECREF(server);
|
||||
qobject_unref(server);
|
||||
return saddr;
|
||||
}
|
||||
|
||||
|
@ -1859,9 +1849,7 @@ out:
|
|||
error_setg_errno(errp, -ret, "Can't pre-allocate");
|
||||
}
|
||||
out_with_err_set:
|
||||
if (blk) {
|
||||
blk_unref(blk);
|
||||
}
|
||||
blk_unref(blk);
|
||||
g_free(buf);
|
||||
|
||||
return ret;
|
||||
|
@ -1883,7 +1871,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
|
|||
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
qobject_decref(obj);
|
||||
qobject_unref(obj);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1901,7 +1889,7 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
|
|||
ret = sd_prealloc(bs, 0, size, errp);
|
||||
fail:
|
||||
bdrv_unref(bs);
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1987,6 +1975,7 @@ static SheepdogRedundancy *parse_redundancy_str(const char *opt)
|
|||
} else {
|
||||
ret = qemu_strtol(n2, NULL, 10, &parity);
|
||||
if (ret < 0) {
|
||||
g_free(redundancy);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -2181,9 +2170,8 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
|||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict, *location_qdict;
|
||||
QObject *crumpled;
|
||||
Visitor *v;
|
||||
const char *redundancy;
|
||||
char *redundancy;
|
||||
Error *local_err = NULL;
|
||||
int ret;
|
||||
|
||||
|
@ -2217,16 +2205,14 @@ static int coroutine_fn sd_co_create_opts(const char *filename, QemuOpts *opts,
|
|||
}
|
||||
|
||||
/* Get the QAPI object */
|
||||
crumpled = qdict_crumple(qdict, errp);
|
||||
if (crumpled == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(crumpled);
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
visit_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (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);
|
||||
fail:
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
g_free(redundancy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2305,8 +2292,8 @@ static int64_t sd_getlength(BlockDriverState *bs)
|
|||
return s->inode.vdi_size;
|
||||
}
|
||||
|
||||
static int sd_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn sd_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
int ret, fd;
|
||||
|
@ -2335,7 +2322,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset,
|
|||
}
|
||||
|
||||
/* 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;
|
||||
ret = write_object(fd, s->bs, (char *)&s->inode,
|
||||
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,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
SheepdogAIOCB acb;
|
||||
int ret;
|
||||
int64_t offset = (sector_num + nb_sectors) * BDRV_SECTOR_SIZE;
|
||||
BDRVSheepdogState *s = bs->opaque;
|
||||
|
||||
assert(!flags);
|
||||
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) {
|
||||
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));
|
||||
/* 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);
|
||||
|
||||
/* refresh inode. */
|
||||
|
@ -2936,13 +2925,14 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
|
|||
QEMUSnapshotInfo *sn_tab = NULL;
|
||||
unsigned wlen, rlen;
|
||||
int found = 0;
|
||||
static SheepdogInode inode;
|
||||
SheepdogInode *inode;
|
||||
unsigned long *vdi_inuse;
|
||||
unsigned int start_nr;
|
||||
uint64_t hval;
|
||||
uint32_t vid;
|
||||
|
||||
vdi_inuse = g_malloc(max);
|
||||
inode = g_malloc(SD_INODE_HEADER_SIZE);
|
||||
|
||||
fd = connect_to_sdog(s, &local_err);
|
||||
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 */
|
||||
ret = read_object(fd, s->bs, (char *)&inode,
|
||||
ret = read_object(fd, s->bs, (char *)inode,
|
||||
vid_to_vdi_oid(vid),
|
||||
0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0,
|
||||
0, SD_INODE_HEADER_SIZE, 0,
|
||||
s->cache_flags);
|
||||
|
||||
if (ret) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strcmp(inode.name, s->name) && is_snapshot(&inode)) {
|
||||
sn_tab[found].date_sec = inode.snap_ctime >> 32;
|
||||
sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff;
|
||||
sn_tab[found].vm_state_size = inode.vm_state_size;
|
||||
sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec;
|
||||
if (!strcmp(inode->name, s->name) && is_snapshot(inode)) {
|
||||
sn_tab[found].date_sec = inode->snap_ctime >> 32;
|
||||
sn_tab[found].date_nsec = inode->snap_ctime & 0xffffffff;
|
||||
sn_tab[found].vm_state_size = inode->vm_state_size;
|
||||
sn_tab[found].vm_clock_nsec = inode->vm_clock_nsec;
|
||||
|
||||
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,
|
||||
MIN(sizeof(sn_tab[found].name), sizeof(inode.tag)),
|
||||
inode.tag);
|
||||
MIN(sizeof(sn_tab[found].name), sizeof(inode->tag)),
|
||||
inode->tag);
|
||||
found++;
|
||||
}
|
||||
}
|
||||
|
@ -3014,6 +3004,7 @@ out:
|
|||
*psn_tab = sn_tab;
|
||||
|
||||
g_free(vdi_inuse);
|
||||
g_free(inode);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
|
@ -3240,7 +3231,7 @@ static BlockDriver bdrv_sheepdog = {
|
|||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
.bdrv_co_truncate = sd_co_truncate,
|
||||
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.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_getlength = sd_getlength,
|
||||
.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_writev = sd_co_writev,
|
||||
|
@ -3314,7 +3305,7 @@ static BlockDriver bdrv_sheepdog_unix = {
|
|||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||
.bdrv_getlength = sd_getlength,
|
||||
.bdrv_get_allocated_file_size = sd_get_allocated_file_size,
|
||||
.bdrv_truncate = sd_truncate,
|
||||
.bdrv_co_truncate = sd_co_truncate,
|
||||
|
||||
.bdrv_co_readv = sd_co_readv,
|
||||
.bdrv_co_writev = sd_co_writev,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "block/snapshot.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
@ -214,7 +215,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||
bdrv_ref(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));
|
||||
|
||||
drv->bdrv_close(bs);
|
||||
|
@ -223,7 +224,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||
|
||||
ret = bdrv_snapshot_goto(file, snapshot_id, errp);
|
||||
open_ret = drv->bdrv_open(bs, options, bs->open_flags, &local_err);
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
if (open_ret < 0) {
|
||||
bdrv_unref(file);
|
||||
bs->drv = NULL;
|
||||
|
|
28
block/ssh.c
28
block/ssh.c
|
@ -28,6 +28,7 @@
|
|||
#include <libssh2_sftp.h>
|
||||
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/option.h"
|
||||
|
@ -605,7 +606,6 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
|
|||
BlockdevOptionsSsh *result = NULL;
|
||||
QemuOpts *opts = NULL;
|
||||
Error *local_err = NULL;
|
||||
QObject *crumpled;
|
||||
const QDictEntry *e;
|
||||
Visitor *v;
|
||||
|
||||
|
@ -622,23 +622,13 @@ static BlockdevOptionsSsh *ssh_parse_options(QDict *options, Error **errp)
|
|||
}
|
||||
|
||||
/* Create the QAPI object */
|
||||
crumpled = qdict_crumple(options, errp);
|
||||
if (crumpled == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(options, errp);
|
||||
if (!v) {
|
||||
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_free(v);
|
||||
qobject_decref(crumpled);
|
||||
|
||||
if (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);
|
||||
|
||||
out:
|
||||
QDECREF(uri_options);
|
||||
qobject_unref(uri_options);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1164,11 +1154,13 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs,
|
|||
|
||||
static coroutine_fn int ssh_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
BDRVSSHState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
assert(!flags);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
ret = ssh_write(s, bs, sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE, qiov);
|
||||
|
@ -1251,8 +1243,8 @@ static int64_t ssh_getlength(BlockDriverState *bs)
|
|||
return length;
|
||||
}
|
||||
|
||||
static int ssh_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset,
|
||||
PreallocMode prealloc, Error **errp)
|
||||
{
|
||||
BDRVSSHState *s = bs->opaque;
|
||||
|
||||
|
@ -1287,7 +1279,7 @@ static BlockDriver bdrv_ssh = {
|
|||
.bdrv_co_readv = ssh_co_readv,
|
||||
.bdrv_co_writev = ssh_co_writev,
|
||||
.bdrv_getlength = ssh_getlength,
|
||||
.bdrv_truncate = ssh_truncate,
|
||||
.bdrv_co_truncate = ssh_co_truncate,
|
||||
.bdrv_co_flush_to_disk = ssh_co_flush,
|
||||
.create_opts = &ssh_create_opts,
|
||||
};
|
||||
|
|
|
@ -29,11 +29,8 @@ enum {
|
|||
STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */
|
||||
};
|
||||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
|
||||
typedef struct StreamBlockJob {
|
||||
BlockJob common;
|
||||
RateLimit limit;
|
||||
BlockDriverState *base;
|
||||
BlockdevOnError on_error;
|
||||
char *backing_file_str;
|
||||
|
@ -61,16 +58,16 @@ typedef struct {
|
|||
int ret;
|
||||
} 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;
|
||||
BlockDriverState *bs = blk_bs(job->blk);
|
||||
BlockDriverState *bs = blk_bs(bjob->blk);
|
||||
BlockDriverState *base = s->base;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && bs->backing &&
|
||||
data->ret == 0) {
|
||||
if (!job_is_cancelled(job) && bs->backing && data->ret == 0) {
|
||||
const char *base_id = NULL, *base_fmt = NULL;
|
||||
if (base) {
|
||||
base_id = s->backing_file_str;
|
||||
|
@ -91,12 +88,12 @@ out:
|
|||
/* Reopen the image back in read-only mode if necessary */
|
||||
if (s->bs_flags != bdrv_get_flags(bs)) {
|
||||
/* Give up write permissions before making it read-only */
|
||||
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);
|
||||
}
|
||||
|
||||
g_free(s->backing_file_str);
|
||||
block_job_completed(&s->common, data->ret);
|
||||
job_completed(job, data->ret, NULL);
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
|
@ -107,6 +104,7 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
BlockBackend *blk = s->common.blk;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
BlockDriverState *base = s->base;
|
||||
int64_t len;
|
||||
int64_t offset = 0;
|
||||
uint64_t delay_ns = 0;
|
||||
int error = 0;
|
||||
|
@ -118,11 +116,12 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
goto out;
|
||||
}
|
||||
|
||||
s->common.len = bdrv_getlength(bs);
|
||||
if (s->common.len < 0) {
|
||||
ret = s->common.len;
|
||||
len = bdrv_getlength(bs);
|
||||
if (len < 0) {
|
||||
ret = len;
|
||||
goto out;
|
||||
}
|
||||
job_progress_set_remaining(&s->common.job, len);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
for ( ; offset < s->common.len; offset += n) {
|
||||
for ( ; offset < len; offset += n) {
|
||||
bool copy;
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
job_sleep_ns(&s->common.job, delay_ns);
|
||||
if (job_is_cancelled(&s->common.job)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -159,7 +158,7 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
|
||||
/* Finish early if end of backing file has been reached */
|
||||
if (ret == 0 && n == 0) {
|
||||
n = s->common.len - offset;
|
||||
n = len - offset;
|
||||
}
|
||||
|
||||
copy = (ret == 1);
|
||||
|
@ -185,9 +184,9 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
ret = 0;
|
||||
|
||||
/* Publish progress */
|
||||
s->common.offset += n;
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
job_progress_update(&s->common.job, n);
|
||||
if (copy) {
|
||||
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
|
||||
} else {
|
||||
delay_ns = 0;
|
||||
}
|
||||
|
@ -206,25 +205,18 @@ out:
|
|||
/* Modify backing chain and close BDSes in main loop */
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
block_job_defer_to_main_loop(&s->common, 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);
|
||||
job_defer_to_main_loop(&s->common.job, stream_complete, data);
|
||||
}
|
||||
|
||||
static const BlockJobDriver stream_job_driver = {
|
||||
.instance_size = sizeof(StreamBlockJob),
|
||||
.job_type = BLOCK_JOB_TYPE_STREAM,
|
||||
.set_speed = stream_set_speed,
|
||||
.start = stream_run,
|
||||
.job_driver = {
|
||||
.instance_size = sizeof(StreamBlockJob),
|
||||
.job_type = JOB_TYPE_STREAM,
|
||||
.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,
|
||||
|
@ -251,7 +243,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|||
BLK_PERM_GRAPH_MOD,
|
||||
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
|
||||
BLK_PERM_WRITE,
|
||||
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
|
||||
speed, JOB_DEFAULT, NULL, NULL, errp);
|
||||
if (!s) {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -272,7 +264,7 @@ void stream_start(const char *job_id, BlockDriverState *bs,
|
|||
|
||||
s->on_error = on_error;
|
||||
trace_stream_start(bs, base, s);
|
||||
block_job_start(&s->common);
|
||||
job_start(&s->common.job);
|
||||
return;
|
||||
|
||||
fail:
|
||||
|
|
|
@ -564,6 +564,10 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm)
|
|||
|
||||
qemu_mutex_lock(&tg->lock);
|
||||
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) {
|
||||
token = throttle_group_next_tgm(tgm);
|
||||
/* Take care of the case where this is the last tgm in the group */
|
||||
|
|
|
@ -36,9 +36,12 @@ static QemuOptsList throttle_opts = {
|
|||
},
|
||||
};
|
||||
|
||||
static int throttle_configure_tgm(BlockDriverState *bs,
|
||||
ThrottleGroupMember *tgm,
|
||||
QDict *options, Error **errp)
|
||||
/*
|
||||
* If this function succeeds then the throttle group name is stored in
|
||||
* @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;
|
||||
const char *group_name;
|
||||
|
@ -63,8 +66,7 @@ static int throttle_configure_tgm(BlockDriverState *bs,
|
|||
goto fin;
|
||||
}
|
||||
|
||||
/* Register membership to group with name group_name */
|
||||
throttle_group_register_tgm(tgm, group_name, bdrv_get_aio_context(bs));
|
||||
*group = g_strdup(group_name);
|
||||
ret = 0;
|
||||
fin:
|
||||
qemu_opts_del(opts);
|
||||
|
@ -75,16 +77,27 @@ static int throttle_open(BlockDriverState *bs, QDict *options,
|
|||
int flags, Error **errp)
|
||||
{
|
||||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
char *group;
|
||||
int ret;
|
||||
|
||||
bs->file = bdrv_open_child(NULL, options, "file", bs,
|
||||
&child_file, false, errp);
|
||||
if (!bs->file) {
|
||||
return -EINVAL;
|
||||
}
|
||||
bs->supported_write_flags = bs->file->bs->supported_write_flags;
|
||||
bs->supported_zero_flags = bs->file->bs->supported_zero_flags;
|
||||
bs->supported_write_flags = bs->file->bs->supported_write_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)
|
||||
|
@ -136,7 +149,7 @@ static int coroutine_fn throttle_co_pdiscard(BlockDriverState *bs,
|
|||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
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)
|
||||
|
@ -160,35 +173,36 @@ static void throttle_attach_aio_context(BlockDriverState *bs,
|
|||
static int throttle_reopen_prepare(BDRVReopenState *reopen_state,
|
||||
BlockReopenQueue *queue, Error **errp)
|
||||
{
|
||||
ThrottleGroupMember *tgm;
|
||||
int ret;
|
||||
char *group = NULL;
|
||||
|
||||
assert(reopen_state != NULL);
|
||||
assert(reopen_state->bs != NULL);
|
||||
|
||||
reopen_state->opaque = g_new0(ThrottleGroupMember, 1);
|
||||
tgm = reopen_state->opaque;
|
||||
|
||||
return throttle_configure_tgm(reopen_state->bs, tgm, reopen_state->options,
|
||||
errp);
|
||||
ret = throttle_parse_options(reopen_state->options, &group, errp);
|
||||
reopen_state->opaque = group;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void throttle_reopen_commit(BDRVReopenState *reopen_state)
|
||||
{
|
||||
ThrottleGroupMember *old_tgm = reopen_state->bs->opaque;
|
||||
ThrottleGroupMember *new_tgm = reopen_state->opaque;
|
||||
BlockDriverState *bs = reopen_state->bs;
|
||||
ThrottleGroupMember *tgm = bs->opaque;
|
||||
char *group = reopen_state->opaque;
|
||||
|
||||
throttle_group_unregister_tgm(old_tgm);
|
||||
g_free(old_tgm);
|
||||
reopen_state->bs->opaque = new_tgm;
|
||||
assert(group);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void throttle_reopen_abort(BDRVReopenState *reopen_state)
|
||||
{
|
||||
ThrottleGroupMember *tgm = reopen_state->opaque;
|
||||
|
||||
throttle_group_unregister_tgm(tgm);
|
||||
g_free(tgm);
|
||||
g_free(reopen_state->opaque);
|
||||
reopen_state->opaque = NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -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_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
|
||||
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"
|
||||
|
@ -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_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_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
|
||||
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_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_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||
|
||||
# blockdev.c
|
||||
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-posix.c
|
||||
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_co(int64_t offset, int count, int type) "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
|
||||
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_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"
|
||||
|
||||
# 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"
|
||||
|
|
18
block/vdi.c
18
block/vdi.c
|
@ -50,11 +50,12 @@
|
|||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/qmp/qdict.h"
|
||||
#include "qapi/qobject-input-visitor.h"
|
||||
#include "qapi/qapi-visit-block-core.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
|
@ -83,9 +84,6 @@
|
|||
/* Command line option for static images. */
|
||||
#define BLOCK_OPT_STATIC "static"
|
||||
|
||||
#define KiB 1024
|
||||
#define MiB (KiB * KiB)
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define DEFAULT_CLUSTER_SIZE (1 * MiB)
|
||||
|
||||
|
@ -434,7 +432,8 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
goto fail;
|
||||
} else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
|
||||
error_setg(errp, "unsupported VDI image (block size %" PRIu32
|
||||
" is not %u)", header.block_size, DEFAULT_CLUSTER_SIZE);
|
||||
" is not %" PRIu64 ")",
|
||||
header.block_size, DEFAULT_CLUSTER_SIZE);
|
||||
ret = -ENOTSUP;
|
||||
goto fail;
|
||||
} else if (header.disk_size >
|
||||
|
@ -865,6 +864,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
|
|||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
exit:
|
||||
blk_unref(blk);
|
||||
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 */
|
||||
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_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) */
|
||||
ret = vdi_co_do_create(create_options, block_size, errp);
|
||||
done:
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
bdrv_unref(bs_file);
|
||||
return ret;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "block/block_int.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "block/vhdx.h"
|
||||
#include "vhdx.h"
|
||||
|
||||
/*
|
||||
* All the VHDX formats on disk are little endian - the following
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "qemu/error-report.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "block/vhdx.h"
|
||||
#include "vhdx.h"
|
||||
|
||||
|
||||
typedef struct VHDXLogSequence {
|
||||
|
|
35
block/vhdx.c
35
block/vhdx.c
|
@ -18,12 +18,13 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/crc32c.h"
|
||||
#include "qemu/bswap.h"
|
||||
#include "block/vhdx.h"
|
||||
#include "vhdx.h"
|
||||
#include "migration/blocker.h"
|
||||
#include "qemu/uuid.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.
|
||||
*
|
||||
* 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
|
||||
* use.
|
||||
*
|
||||
|
@ -1126,9 +1127,9 @@ static coroutine_fn int vhdx_co_readv(BlockDriverState *bs, int64_t sector_num,
|
|||
break;
|
||||
case PAYLOAD_BLOCK_FULLY_PRESENT:
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_readv(bs->file,
|
||||
sinfo.file_offset >> BDRV_SECTOR_BITS,
|
||||
sinfo.sectors_avail, &hd_qiov);
|
||||
ret = bdrv_co_preadv(bs->file, sinfo.file_offset,
|
||||
sinfo.sectors_avail * BDRV_SECTOR_SIZE,
|
||||
&hd_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
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,
|
||||
int nb_sectors, QEMUIOVector *qiov)
|
||||
int nb_sectors, QEMUIOVector *qiov,
|
||||
int flags)
|
||||
{
|
||||
int ret = -ENOTSUP;
|
||||
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;
|
||||
bool bat_update = false;
|
||||
|
||||
assert(!flags);
|
||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||
|
||||
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 */
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = bdrv_co_writev(bs->file,
|
||||
sinfo.file_offset >> BDRV_SECTOR_BITS,
|
||||
sectors_to_write, &hd_qiov);
|
||||
ret = bdrv_co_pwritev(bs->file, sinfo.file_offset,
|
||||
sectors_to_write * BDRV_SECTOR_SIZE,
|
||||
&hd_qiov, 0);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0) {
|
||||
goto error_bat_restore;
|
||||
|
@ -1949,7 +1952,7 @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
|
|||
goto delete_and_exit;
|
||||
}
|
||||
|
||||
|
||||
ret = 0;
|
||||
delete_and_exit:
|
||||
blk_unref(blk);
|
||||
bdrv_unref(bs);
|
||||
|
@ -1962,8 +1965,7 @@ static int coroutine_fn vhdx_co_create_opts(const char *filename,
|
|||
Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = 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, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
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);
|
||||
|
||||
fail:
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
|
|
|
@ -333,6 +333,12 @@ static int vmdk_is_cid_valid(BlockDriverState *bs)
|
|||
if (!s->cid_checked && bs->backing) {
|
||||
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) {
|
||||
/* read failure: report as not valid */
|
||||
return 0;
|
||||
|
|
13
block/vpc.c
13
block/vpc.c
|
@ -26,6 +26,7 @@
|
|||
#include "qemu/osdep.h"
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "sysemu/block-backend.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
|
@ -1080,8 +1081,7 @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
|
|||
QemuOpts *opts, Error **errp)
|
||||
{
|
||||
BlockdevCreateOptions *create_options = NULL;
|
||||
QDict *qdict = NULL;
|
||||
QObject *qobj;
|
||||
QDict *qdict;
|
||||
Visitor *v;
|
||||
BlockDriverState *bs = 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, "file", bs->node_name);
|
||||
|
||||
qobj = qdict_crumple(qdict, errp);
|
||||
QDECREF(qdict);
|
||||
qdict = qobject_to(QDict, qobj);
|
||||
if (qdict == NULL) {
|
||||
v = qobject_input_visitor_new_flat_confused(qdict, errp);
|
||||
if (!v) {
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
|
||||
visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
|
||||
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);
|
||||
|
||||
fail:
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
bdrv_unref(bs);
|
||||
qapi_free_BlockdevCreateOptions(create_options);
|
||||
return ret;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <dirent.h>
|
||||
#include "qapi/error.h"
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qemu/module.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/bswap.h"
|
||||
|
@ -1244,8 +1245,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
|
|||
s->fat2 = NULL;
|
||||
s->downcase_short_names = 1;
|
||||
|
||||
fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
|
||||
dirname, cyls, heads, secs);
|
||||
DLOG(fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
|
||||
dirname, cyls, heads, secs));
|
||||
|
||||
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 = {
|
||||
.parent_is_bds = true,
|
||||
.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");
|
||||
s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
|
||||
&child_vvfat_qcow, false, errp);
|
||||
QDECREF(options);
|
||||
qobject_unref(options);
|
||||
if (!s->qcow) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
|
|
46
block/vxhs.c
46
block/vxhs.c
|
@ -12,6 +12,7 @@
|
|||
#include <qnio/qnio_api.h>
|
||||
#include <sys/param.h>
|
||||
#include "block/block_int.h"
|
||||
#include "block/qdict.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qdict.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)
|
||||
{
|
||||
if (vxhs_ref++ == 0) {
|
||||
|
@ -396,7 +403,7 @@ static int vxhs_open(BlockDriverState *bs, QDict *options,
|
|||
|
||||
out:
|
||||
g_free(of_vsa_addr);
|
||||
QDECREF(backing_options);
|
||||
qobject_unref(backing_options);
|
||||
qemu_opts_del(tcp_opts);
|
||||
qemu_opts_del(opts);
|
||||
g_free(cacert);
|
||||
|
@ -424,21 +431,17 @@ static const AIOCBInfo vxhs_aiocb_info = {
|
|||
* and is passed to QNIO. When QNIO completes the work,
|
||||
* it will be passed back through the callback.
|
||||
*/
|
||||
static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num,
|
||||
QEMUIOVector *qiov, int nb_sectors,
|
||||
static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, uint64_t offset,
|
||||
QEMUIOVector *qiov, uint64_t size,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
VDISKAIOCmd iodir)
|
||||
{
|
||||
VXHSAIOCB *acb = NULL;
|
||||
BDRVVXHSState *s = bs->opaque;
|
||||
size_t size;
|
||||
uint64_t offset;
|
||||
int iio_flags = 0;
|
||||
int ret = 0;
|
||||
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);
|
||||
|
||||
/*
|
||||
|
@ -451,11 +454,11 @@ static BlockAIOCB *vxhs_aio_rw(BlockDriverState *bs, int64_t sector_num,
|
|||
switch (iodir) {
|
||||
case VDISK_AIO_WRITE:
|
||||
ret = iio_writev(dev_handle, acb, qiov->iov, qiov->niov,
|
||||
offset, (uint64_t)size, iio_flags);
|
||||
offset, size, iio_flags);
|
||||
break;
|
||||
case VDISK_AIO_READ:
|
||||
ret = iio_readv(dev_handle, acb, qiov->iov, qiov->niov,
|
||||
offset, (uint64_t)size, iio_flags);
|
||||
offset, size, iio_flags);
|
||||
break;
|
||||
default:
|
||||
trace_vxhs_aio_rw_invalid(iodir);
|
||||
|
@ -474,22 +477,20 @@ errout:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static BlockAIOCB *vxhs_aio_readv(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
static BlockAIOCB *vxhs_aio_preadv(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors, cb,
|
||||
opaque, VDISK_AIO_READ);
|
||||
return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_READ);
|
||||
}
|
||||
|
||||
static BlockAIOCB *vxhs_aio_writev(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov,
|
||||
int nb_sectors,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
static BlockAIOCB *vxhs_aio_pwritev(BlockDriverState *bs,
|
||||
uint64_t offset, uint64_t bytes,
|
||||
QEMUIOVector *qiov, int flags,
|
||||
BlockCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
return vxhs_aio_rw(bs, sector_num, qiov, nb_sectors,
|
||||
cb, opaque, VDISK_AIO_WRITE);
|
||||
return vxhs_aio_rw(bs, offset, qiov, bytes, cb, opaque, VDISK_AIO_WRITE);
|
||||
}
|
||||
|
||||
static void vxhs_close(BlockDriverState *bs)
|
||||
|
@ -561,10 +562,11 @@ static BlockDriver bdrv_vxhs = {
|
|||
.instance_size = sizeof(BDRVVXHSState),
|
||||
.bdrv_file_open = vxhs_open,
|
||||
.bdrv_parse_filename = vxhs_parse_filename,
|
||||
.bdrv_refresh_limits = vxhs_refresh_limits,
|
||||
.bdrv_close = vxhs_close,
|
||||
.bdrv_getlength = vxhs_getlength,
|
||||
.bdrv_aio_readv = vxhs_aio_readv,
|
||||
.bdrv_aio_writev = vxhs_aio_writev,
|
||||
.bdrv_aio_preadv = vxhs_aio_preadv,
|
||||
.bdrv_aio_pwritev = vxhs_aio_pwritev,
|
||||
};
|
||||
|
||||
static void bdrv_vxhs_init(void)
|
||||
|
|
|
@ -112,15 +112,14 @@ static const AIOCBInfo win32_aiocb_info = {
|
|||
|
||||
BlockAIOCB *win32_aio_submit(BlockDriverState *bs,
|
||||
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)
|
||||
{
|
||||
struct QEMUWin32AIOCB *waiocb;
|
||||
uint64_t offset = sector_num * 512;
|
||||
DWORD rc;
|
||||
|
||||
waiocb = qemu_aio_get(&win32_aiocb_info, bs, cb, opaque);
|
||||
waiocb->nbytes = nb_sectors * 512;
|
||||
waiocb->nbytes = bytes;
|
||||
waiocb->qiov = qiov;
|
||||
waiocb->is_read = (type == QEMU_AIO_READ);
|
||||
|
||||
|
|
|
@ -220,3 +220,26 @@ void qmp_nbd_server_stop(Error **errp)
|
|||
nbd_server_free(nbd_server);
|
||||
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);
|
||||
}
|
||||
|
|
262
blockdev.c
262
blockdev.c
|
@ -35,6 +35,7 @@
|
|||
#include "sysemu/blockdev.h"
|
||||
#include "hw/block/block.h"
|
||||
#include "block/blockjob.h"
|
||||
#include "block/qdict.h"
|
||||
#include "block/throttle-groups.h"
|
||||
#include "monitor/monitor.h"
|
||||
#include "qemu/error-report.h"
|
||||
|
@ -150,7 +151,7 @@ void blockdev_mark_auto_del(BlockBackend *blk)
|
|||
aio_context_acquire(aio_context);
|
||||
|
||||
if (bs->job) {
|
||||
block_job_cancel(bs->job, false);
|
||||
job_cancel(&bs->job->job, false);
|
||||
}
|
||||
|
||||
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->detect_zeroes = detect_zeroes;
|
||||
|
||||
QDECREF(bs_opts);
|
||||
qobject_unref(bs_opts);
|
||||
} else {
|
||||
if (file && !*file) {
|
||||
file = NULL;
|
||||
|
@ -632,16 +633,16 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
|||
|
||||
err_no_bs_opts:
|
||||
qemu_opts_del(opts);
|
||||
QDECREF(interval_dict);
|
||||
QDECREF(interval_list);
|
||||
qobject_unref(interval_dict);
|
||||
qobject_unref(interval_list);
|
||||
return blk;
|
||||
|
||||
early_err:
|
||||
qemu_opts_del(opts);
|
||||
QDECREF(interval_dict);
|
||||
QDECREF(interval_list);
|
||||
qobject_unref(interval_dict);
|
||||
qobject_unref(interval_list);
|
||||
err_no_opts:
|
||||
QDECREF(bs_opts);
|
||||
qobject_unref(bs_opts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1139,7 +1140,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||
|
||||
fail:
|
||||
qemu_opts_del(legacy_opts);
|
||||
QDECREF(bs_opts);
|
||||
qobject_unref(bs_opts);
|
||||
return dinfo;
|
||||
}
|
||||
|
||||
|
@ -1455,7 +1456,7 @@ typedef struct BlkActionOps {
|
|||
struct BlkActionState {
|
||||
TransactionAction *action;
|
||||
const BlkActionOps *ops;
|
||||
BlockJobTxn *block_job_txn;
|
||||
JobTxn *block_job_txn;
|
||||
TransactionProperties *txn_props;
|
||||
QSIMPLEQ_ENTRY(BlkActionState) entry;
|
||||
};
|
||||
|
@ -1873,7 +1874,7 @@ typedef struct DriveBackupState {
|
|||
BlockJob *job;
|
||||
} DriveBackupState;
|
||||
|
||||
static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
||||
static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
|
||||
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);
|
||||
|
||||
assert(state->job);
|
||||
block_job_start(state->job);
|
||||
job_start(&state->job->job);
|
||||
|
||||
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_acquire(aio_context);
|
||||
|
||||
block_job_cancel_sync(state->job);
|
||||
job_cancel_sync(&state->job->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
@ -1963,7 +1964,7 @@ typedef struct BlockdevBackupState {
|
|||
BlockJob *job;
|
||||
} BlockdevBackupState;
|
||||
|
||||
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
||||
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
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);
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
@ -2017,7 +2018,7 @@ static void blockdev_backup_commit(BlkActionState *common)
|
|||
aio_context_acquire(aio_context);
|
||||
|
||||
assert(state->job);
|
||||
block_job_start(state->job);
|
||||
job_start(&state->job->job);
|
||||
|
||||
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_acquire(aio_context);
|
||||
|
||||
block_job_cancel_sync(state->job);
|
||||
job_cancel_sync(&state->job->job);
|
||||
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
@ -2061,6 +2062,7 @@ typedef struct BlockDirtyBitmapState {
|
|||
BlockDriverState *bs;
|
||||
HBitmap *backup;
|
||||
bool prepared;
|
||||
bool was_enabled;
|
||||
} BlockDirtyBitmapState;
|
||||
|
||||
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_persistent, action->persistent,
|
||||
action->has_autoload, action->autoload,
|
||||
action->has_x_disabled, action->x_disabled,
|
||||
&local_err);
|
||||
|
||||
if (!local_err) {
|
||||
|
@ -2160,6 +2163,74 @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common)
|
|||
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)
|
||||
{
|
||||
error_setg(errp, "Transaction aborted using Abort action");
|
||||
|
@ -2220,7 +2291,17 @@ static const BlkActionOps actions[] = {
|
|||
.prepare = block_dirty_bitmap_clear_prepare,
|
||||
.commit = block_dirty_bitmap_clear_commit,
|
||||
.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)
|
||||
{
|
||||
TransactionActionList *dev_entry = dev_list;
|
||||
BlockJobTxn *block_job_txn = NULL;
|
||||
JobTxn *block_job_txn = NULL;
|
||||
BlkActionState *state, *next;
|
||||
Error *local_err = NULL;
|
||||
|
||||
|
@ -2260,11 +2341,11 @@ void qmp_transaction(TransactionActionList *dev_list,
|
|||
QSIMPLEQ_INIT(&snap_bdrv_states);
|
||||
|
||||
/* 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);
|
||||
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 */
|
||||
|
@ -2323,7 +2404,7 @@ exit:
|
|||
if (!has_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,
|
||||
|
@ -2810,6 +2891,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name,
|
|||
bool has_granularity, uint32_t granularity,
|
||||
bool has_persistent, bool persistent,
|
||||
bool has_autoload, bool autoload,
|
||||
bool has_disabled, bool disabled,
|
||||
Error **errp)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
if (!has_disabled) {
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
if (persistent &&
|
||||
!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;
|
||||
}
|
||||
|
||||
if (disabled) {
|
||||
bdrv_disable_dirty_bitmap(bitmap);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -2932,6 +3021,78 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
|
|||
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,
|
||||
const char *name,
|
||||
Error **errp)
|
||||
|
@ -3253,7 +3414,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
|||
goto out;
|
||||
}
|
||||
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
|
||||
BLOCK_JOB_DEFAULT, speed, on_error,
|
||||
JOB_DEFAULT, speed, on_error,
|
||||
filter_node_name, NULL, NULL, false, &local_err);
|
||||
} else {
|
||||
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
|
||||
|
@ -3273,7 +3434,7 @@ out:
|
|||
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)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
@ -3284,7 +3445,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
|||
AioContext *aio_context;
|
||||
QDict *options = NULL;
|
||||
Error *local_err = NULL;
|
||||
int flags, job_flags = BLOCK_JOB_DEFAULT;
|
||||
int flags, job_flags = JOB_DEFAULT;
|
||||
int64_t size;
|
||||
bool set_backing_hd = false;
|
||||
|
||||
|
@ -3407,10 +3568,10 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
|
|||
}
|
||||
}
|
||||
if (!backup->auto_finalize) {
|
||||
job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
|
||||
job_flags |= JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
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,
|
||||
|
@ -3434,7 +3595,7 @@ void qmp_drive_backup(DriveBackup *arg, Error **errp)
|
|||
BlockJob *job;
|
||||
job = do_drive_backup(arg, NULL, errp);
|
||||
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);
|
||||
}
|
||||
|
||||
BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
||||
BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
|
||||
Error **errp)
|
||||
{
|
||||
BlockDriverState *bs;
|
||||
|
@ -3451,7 +3612,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
|||
Error *local_err = NULL;
|
||||
AioContext *aio_context;
|
||||
BlockJob *job = NULL;
|
||||
int job_flags = BLOCK_JOB_DEFAULT;
|
||||
int job_flags = JOB_DEFAULT;
|
||||
|
||||
if (!backup->has_speed) {
|
||||
backup->speed = 0;
|
||||
|
@ -3475,7 +3636,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
|||
backup->compress = false;
|
||||
}
|
||||
|
||||
bs = qmp_get_root_bs(backup->device, errp);
|
||||
bs = bdrv_lookup_bs(backup->device, backup->device, errp);
|
||||
if (!bs) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -3500,10 +3661,10 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
|
|||
}
|
||||
}
|
||||
if (!backup->auto_finalize) {
|
||||
job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
|
||||
job_flags |= JOB_MANUAL_FINALIZE;
|
||||
}
|
||||
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,
|
||||
backup->sync, NULL, backup->compress,
|
||||
|
@ -3522,7 +3683,7 @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
|
|||
BlockJob *job;
|
||||
job = do_blockdev_backup(arg, NULL, errp);
|
||||
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_filter_node_name,
|
||||
const char *filter_node_name,
|
||||
bool has_copy_mode, MirrorCopyMode copy_mode,
|
||||
Error **errp)
|
||||
{
|
||||
|
||||
|
@ -3568,6 +3730,9 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
|||
if (!has_filter_node_name) {
|
||||
filter_node_name = NULL;
|
||||
}
|
||||
if (!has_copy_mode) {
|
||||
copy_mode = MIRROR_COPY_MODE_BACKGROUND;
|
||||
}
|
||||
|
||||
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
|
||||
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,
|
||||
speed, granularity, buf_size, sync, backing_mode,
|
||||
on_source_error, on_target_error, unmap, filter_node_name,
|
||||
errp);
|
||||
copy_mode, 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_unmap, arg->unmap,
|
||||
false, NULL,
|
||||
arg->has_copy_mode, arg->copy_mode,
|
||||
&local_err);
|
||||
bdrv_unref(target_bs);
|
||||
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,
|
||||
bool has_filter_node_name,
|
||||
const char *filter_node_name,
|
||||
bool has_copy_mode, MirrorCopyMode copy_mode,
|
||||
Error **errp)
|
||||
{
|
||||
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,
|
||||
true, true,
|
||||
has_filter_node_name, filter_node_name,
|
||||
has_copy_mode, copy_mode,
|
||||
&local_err);
|
||||
error_propagate(errp, local_err);
|
||||
|
||||
|
@ -3853,14 +4021,14 @@ void qmp_block_job_cancel(const char *device,
|
|||
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",
|
||||
device);
|
||||
goto out;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_cancel(job);
|
||||
block_job_user_cancel(job, force, errp);
|
||||
job_user_cancel(&job->job, force, errp);
|
||||
out:
|
||||
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);
|
||||
block_job_user_pause(job, errp);
|
||||
job_user_pause(&job->job, errp);
|
||||
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);
|
||||
block_job_user_resume(job, errp);
|
||||
job_user_resume(&job->job, errp);
|
||||
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);
|
||||
block_job_complete(job, errp);
|
||||
job_complete(&job->job, errp);
|
||||
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);
|
||||
block_job_finalize(job, errp);
|
||||
job_finalize(&job->job, errp);
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_block_job_dismiss(const char *id, Error **errp)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
trace_qmp_block_job_dismiss(job);
|
||||
block_job_dismiss(&job, errp);
|
||||
trace_qmp_block_job_dismiss(bjob);
|
||||
job = &bjob->job;
|
||||
job_dismiss(&job, errp);
|
||||
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);
|
||||
|
||||
if (!qdict_get_try_str(qdict, "node-name")) {
|
||||
QDECREF(qdict);
|
||||
qobject_unref(qdict);
|
||||
error_report("'node-name' needs to be specified");
|
||||
goto out;
|
||||
}
|
||||
|
|
1138
blockjob.c
1138
blockjob.c
File diff suppressed because it is too large
Load Diff
|
@ -17,6 +17,7 @@
|
|||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/units.h"
|
||||
#include "qemu-version.h"
|
||||
#include <machine/trap.h>
|
||||
|
||||
|
@ -795,9 +796,9 @@ int main(int argc, char **argv)
|
|||
if (x86_stack_size <= 0)
|
||||
usage();
|
||||
if (*r == 'M')
|
||||
x86_stack_size *= 1024 * 1024;
|
||||
x86_stack_size *= MiB;
|
||||
else if (*r == 'k' || *r == 'K')
|
||||
x86_stack_size *= 1024;
|
||||
x86_stack_size *= KiB;
|
||||
} else if (!strcmp(r, "L")) {
|
||||
interp_prefix = argv[optind++];
|
||||
} else if (!strcmp(r, "p")) {
|
||||
|
@ -898,9 +899,10 @@ int main(int argc, char **argv)
|
|||
cpu_model = "any";
|
||||
#endif
|
||||
}
|
||||
|
||||
/* init tcg before creating CPUs and to get qemu_host_page_size */
|
||||
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 = cpu_create(cpu_type);
|
||||
env = cpu->env_ptr;
|
||||
|
@ -917,7 +919,7 @@ int main(int argc, char **argv)
|
|||
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.
|
||||
*/
|
||||
guest_base = HOST_PAGE_ALIGN(guest_base);
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "qemu.h"
|
||||
#include "qemu-common.h"
|
||||
#include "bsd-mman.h"
|
||||
#include "exec/exec-all.h"
|
||||
|
||||
//#define DEBUG_MMAP
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
|
||||
#include "cpu.h"
|
||||
#include "exec/exec-all.h"
|
||||
#include "exec/cpu_ldst.h"
|
||||
|
||||
#undef DEBUG_REMAP
|
||||
|
|
|
@ -304,6 +304,7 @@ void mux_set_focus(Chardev *chr, int focus)
|
|||
}
|
||||
|
||||
d->focus = focus;
|
||||
chr->be = d->backends[focus];
|
||||
mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
|
||||
}
|
||||
|
||||
|
|
|
@ -139,7 +139,7 @@ static void tty_serial_init(int fd, int speed,
|
|||
|
||||
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
||||
| INLCR | IGNCR | ICRNL | IXON);
|
||||
tty.c_oflag |= OPOST;
|
||||
tty.c_oflag &= ~OPOST;
|
||||
tty.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
|
||||
tty.c_cflag &= ~(CSIZE | PARENB | PARODD | CRTSCTS | CSTOPB);
|
||||
switch (data_bits) {
|
||||
|
@ -265,7 +265,8 @@ static void qmp_chardev_open_serial(Chardev *chr,
|
|||
ChardevHostdev *serial = backend->u.serial.data;
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -134,8 +134,11 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len)
|
|||
s->write_msgfds,
|
||||
s->write_msgfds_num);
|
||||
|
||||
/* free the written msgfds, no matter what */
|
||||
if (s->write_msgfds_num) {
|
||||
/* free the written msgfds in any cases
|
||||
* other than ret < 0 && errno == EAGAIN
|
||||
*/
|
||||
if (!(ret < 0 && EAGAIN == errno)
|
||||
&& s->write_msgfds_num) {
|
||||
g_free(s->write_msgfds);
|
||||
s->write_msgfds = 0;
|
||||
s->write_msgfds_num = 0;
|
||||
|
|
|
@ -46,8 +46,10 @@ static bool stdio_echo_state;
|
|||
|
||||
static void term_exit(void)
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &oldtty);
|
||||
fcntl(0, F_SETFL, old_fd0_flags);
|
||||
if (stdio_in_use) {
|
||||
tcsetattr(0, TCSANOW, &oldtty);
|
||||
fcntl(0, F_SETFL, old_fd0_flags);
|
||||
}
|
||||
}
|
||||
|
||||
static void qemu_chr_set_echo_stdio(Chardev *chr, bool echo)
|
||||
|
|
|
@ -60,6 +60,11 @@ do_compiler() {
|
|||
# is compiler binary to execute.
|
||||
local compiler="$1"
|
||||
shift
|
||||
if test -n "$BASH_VERSION"; then eval '
|
||||
echo >>config.log "
|
||||
funcs: ${FUNCNAME[*]}
|
||||
lines: ${BASH_LINENO[*]}"
|
||||
'; fi
|
||||
echo $compiler "$@" >> config.log
|
||||
$compiler "$@" >> config.log 2>&1 || return $?
|
||||
# Test passed. If this is an --enable-werror build, rerun
|
||||
|
@ -284,7 +289,6 @@ libs_softmmu=""
|
|||
libs_tools=""
|
||||
audio_pt_int=""
|
||||
audio_win_int=""
|
||||
cc_i386=i386-pc-linux-gnu-gcc
|
||||
libs_qga=""
|
||||
debug_info="yes"
|
||||
stack_protector=""
|
||||
|
@ -296,6 +300,24 @@ then
|
|||
else
|
||||
git_update=no
|
||||
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
|
||||
git="git"
|
||||
|
||||
|
@ -451,6 +473,21 @@ jemalloc="no"
|
|||
replication="yes"
|
||||
vxhs=""
|
||||
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_os="no"
|
||||
|
@ -482,6 +519,14 @@ for opt do
|
|||
;;
|
||||
--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
|
||||
done
|
||||
# OS specific
|
||||
|
@ -670,30 +715,37 @@ case "$cpu" in
|
|||
ppc|ppc64|s390|s390x|sparc64|x32)
|
||||
cpu="$cpu"
|
||||
supported_cpu="yes"
|
||||
eval "cross_cc_${cpu}=\$host_cc"
|
||||
;;
|
||||
i386|i486|i586|i686|i86pc|BePC)
|
||||
cpu="i386"
|
||||
supported_cpu="yes"
|
||||
cross_cc_i386=$host_cc
|
||||
;;
|
||||
x86_64|amd64)
|
||||
cpu="x86_64"
|
||||
supported_cpu="yes"
|
||||
cross_cc_x86_64=$host_cc
|
||||
;;
|
||||
armv*b|armv*l|arm)
|
||||
cpu="arm"
|
||||
supported_cpu="yes"
|
||||
cross_cc_arm=$host_cc
|
||||
;;
|
||||
aarch64)
|
||||
cpu="aarch64"
|
||||
supported_cpu="yes"
|
||||
cross_cc_aarch64=$host_cc
|
||||
;;
|
||||
mips*)
|
||||
cpu="mips"
|
||||
supported_cpu="yes"
|
||||
cross_cc_mips=$host_cc
|
||||
;;
|
||||
sparc|sun4[cdmuv])
|
||||
cpu="sparc"
|
||||
supported_cpu="yes"
|
||||
cross_cc_sparc=$host_cc
|
||||
;;
|
||||
*)
|
||||
# This will result in either an error or falling back to TCI later
|
||||
|
@ -911,6 +963,8 @@ for opt do
|
|||
;;
|
||||
--disable-debug-info)
|
||||
;;
|
||||
--cross-cc-*)
|
||||
;;
|
||||
--enable-modules)
|
||||
modules="yes"
|
||||
;;
|
||||
|
@ -959,6 +1013,8 @@ for opt do
|
|||
;;
|
||||
--firmwarepath=*) firmwarepath="$optarg"
|
||||
;;
|
||||
--host=*|--build=*|\
|
||||
--disable-dependency-tracking|\
|
||||
--sbindir=*|--sharedstatedir=*|\
|
||||
--oldincludedir=*|--datarootdir=*|--infodir=*|--localedir=*|\
|
||||
--htmldir=*|--dvidir=*|--pdfdir=*|--psdir=*)
|
||||
|
@ -1004,6 +1060,7 @@ for opt do
|
|||
--enable-debug)
|
||||
# Enable debugging options that aren't excessively noisy
|
||||
debug_tcg="yes"
|
||||
debug_mutex="yes"
|
||||
debug="yes"
|
||||
strip_opt="no"
|
||||
fortify_source="no"
|
||||
|
@ -1374,6 +1431,10 @@ for opt do
|
|||
;;
|
||||
--disable-git-update) git_update=no
|
||||
;;
|
||||
--enable-debug-mutex) debug_mutex=yes
|
||||
;;
|
||||
--disable-debug-mutex) debug_mutex=no
|
||||
;;
|
||||
*)
|
||||
echo "ERROR: unknown option $opt"
|
||||
echo "Try '$0 --help' for more information"
|
||||
|
@ -1394,31 +1455,44 @@ case "$cpu" in
|
|||
ppc)
|
||||
CPU_CFLAGS="-m32"
|
||||
LDFLAGS="-m32 $LDFLAGS"
|
||||
cross_cc_powerpc=$cc
|
||||
cross_cc_cflags_powerpc=$CPU_CFLAGS
|
||||
;;
|
||||
ppc64)
|
||||
CPU_CFLAGS="-m64"
|
||||
LDFLAGS="-m64 $LDFLAGS"
|
||||
cross_cc_ppc64=$cc
|
||||
cross_cc_cflags_ppc64=$CPU_CFLAGS
|
||||
;;
|
||||
sparc)
|
||||
CPU_CFLAGS="-m32 -mv8plus -mcpu=ultrasparc"
|
||||
LDFLAGS="-m32 -mv8plus $LDFLAGS"
|
||||
cross_cc_sparc=$cc
|
||||
cross_cc_cflags_sparc=$CPU_CFLAGS
|
||||
;;
|
||||
sparc64)
|
||||
CPU_CFLAGS="-m64 -mcpu=ultrasparc"
|
||||
LDFLAGS="-m64 $LDFLAGS"
|
||||
cross_cc_sparc64=$cc
|
||||
cross_cc_cflags_sparc64=$CPU_CFLAGS
|
||||
;;
|
||||
s390)
|
||||
CPU_CFLAGS="-m31"
|
||||
LDFLAGS="-m31 $LDFLAGS"
|
||||
cross_cc_s390=$cc
|
||||
cross_cc_cflags_s390=$CPU_CFLAGS
|
||||
;;
|
||||
s390x)
|
||||
CPU_CFLAGS="-m64"
|
||||
LDFLAGS="-m64 $LDFLAGS"
|
||||
cross_cc_s390x=$cc
|
||||
cross_cc_cflags_s390x=$CPU_CFLAGS
|
||||
;;
|
||||
i386)
|
||||
CPU_CFLAGS="-m32"
|
||||
LDFLAGS="-m32 $LDFLAGS"
|
||||
cc_i386='$(CC) -m32'
|
||||
cross_cc_i386=$cc
|
||||
cross_cc_cflags_i386=$CPU_CFLAGS
|
||||
;;
|
||||
x86_64)
|
||||
# ??? Only extremely old AMD cpus do not have cmpxchg16b.
|
||||
|
@ -1426,12 +1500,14 @@ case "$cpu" in
|
|||
# runtime and generate the fallback to serial emulation.
|
||||
CPU_CFLAGS="-m64 -mcx16"
|
||||
LDFLAGS="-m64 $LDFLAGS"
|
||||
cc_i386='$(CC) -m32'
|
||||
cross_cc_x86_64=$cc
|
||||
cross_cc_cflags_x86_64=$CPU_CFLAGS
|
||||
;;
|
||||
x32)
|
||||
CPU_CFLAGS="-mx32"
|
||||
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
|
||||
esac
|
||||
|
@ -1493,6 +1569,8 @@ Advanced options (experts only):
|
|||
--extra-cflags=CFLAGS append extra C compiler flags QEMU_CFLAGS
|
||||
--extra-cxxflags=CXXFLAGS append extra C++ compiler flags QEMU_CXXFLAGS
|
||||
--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]
|
||||
--install=INSTALL use specified install [$install]
|
||||
--python=PYTHON use specified python [$python]
|
||||
|
@ -1581,7 +1659,7 @@ disabled with --disable-FEATURE, default is enabled if available:
|
|||
virtfs VirtFS
|
||||
mpath Multipath persistent reservation passthrough
|
||||
xen xen backend driver support
|
||||
xen-pci-passthrough
|
||||
xen-pci-passthrough PCI passthrough support for Xen
|
||||
brlapi BrlAPI (Braile)
|
||||
curl curl connectivity
|
||||
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
|
||||
vhost-user vhost-user support
|
||||
capstone capstone disassembler support
|
||||
debug-mutex mutex debugging support
|
||||
|
||||
NOTE: The object files are built at the place where configure is launched
|
||||
EOF
|
||||
|
@ -1643,8 +1722,8 @@ fi
|
|||
|
||||
# Note that if the Python conditional here evaluates True we will exit
|
||||
# with status 1 which is a shell 'false' value.
|
||||
if ! $python -c 'import sys; sys.exit(sys.version_info < (2,6))'; then
|
||||
error_exit "Cannot use '$python', Python 2 >= 2.6 or Python 3 is required." \
|
||||
if ! $python -c 'import sys; sys.exit(sys.version_info < (2,7))'; then
|
||||
error_exit "Cannot use '$python', Python 2 >= 2.7 or Python 3 is required." \
|
||||
"Use --python=/path/to/python to specify a supported Python."
|
||||
fi
|
||||
|
||||
|
@ -2189,6 +2268,9 @@ if test "$xen" != "no" ; then
|
|||
xen=yes
|
||||
xen_pc="xencontrol xenstore xenguest xenforeignmemory xengnttab"
|
||||
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)"
|
||||
libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu"
|
||||
LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS"
|
||||
|
@ -2218,20 +2300,46 @@ EOF
|
|||
# Xen unstable
|
||||
elif
|
||||
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
|
||||
#include <xenforeignmemory.h>
|
||||
#include <xentoolcore.h>
|
||||
int main(void) {
|
||||
xenforeignmemory_handle *xfmem;
|
||||
|
||||
xfmem = xenforeignmemory_open(0, 0);
|
||||
xenforeignmemory_map2(xfmem, 0, 0, 0, 0, 0, 0, 0);
|
||||
xentoolcore_restrict_all(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs"
|
||||
compile_prog "" "$xen_libs -lxendevicemodel $xen_stable_libs -lxentoolcore"
|
||||
then
|
||||
xen_stable_libs="-lxendevicemodel $xen_stable_libs"
|
||||
xen_stable_libs="-lxendevicemodel $xen_stable_libs -lxentoolcore"
|
||||
xen_ctrl_version=41000
|
||||
xen=yes
|
||||
elif
|
||||
|
@ -2493,20 +2601,7 @@ fi
|
|||
##########################################
|
||||
# Windows Hypervisor Platform accelerator (WHPX) check
|
||||
if test "$whpx" != "no" ; then
|
||||
cat > $TMPC << EOF
|
||||
#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"
|
||||
if check_include "WinHvPlatform.h" && check_include "WinHvEmulation.h"; then
|
||||
whpx="yes"
|
||||
else
|
||||
if test "$whpx" = "yes"; then
|
||||
|
@ -3392,11 +3487,7 @@ fi
|
|||
##########################################
|
||||
# glib support probe
|
||||
|
||||
if test "$mingw32" = yes; then
|
||||
glib_req_ver=2.30
|
||||
else
|
||||
glib_req_ver=2.22
|
||||
fi
|
||||
glib_req_ver=2.40
|
||||
glib_modules="gthread-2.0 gobject-2.0"
|
||||
if test "$modules" = yes; then
|
||||
glib_modules="$glib_modules gmodule-export-2.0"
|
||||
|
@ -3761,7 +3852,7 @@ fi
|
|||
fdt_required=no
|
||||
for target in $target_list; do
|
||||
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
|
||||
;;
|
||||
esac
|
||||
|
@ -3787,22 +3878,22 @@ int main(void) { fdt_first_subnode(0, 0); return 0; }
|
|||
EOF
|
||||
if compile_prog "" "$fdt_libs" ; then
|
||||
# system DTC is good - use it
|
||||
fdt=yes
|
||||
fdt=system
|
||||
else
|
||||
# have GIT checkout, so activate dtc submodule
|
||||
if test -e "${source_path}/.git" ; then
|
||||
git_submodules="${git_submodules} dtc"
|
||||
fi
|
||||
if test -d "${source_path}/dtc/libfdt" || test -e "${source_path}/.git" ; then
|
||||
fdt=yes
|
||||
dtc_internal="yes"
|
||||
fdt=git
|
||||
mkdir -p dtc
|
||||
if [ "$pwd_is_source_path" != "y" ] ; then
|
||||
symlink "$source_path/dtc/Makefile" "dtc/Makefile"
|
||||
symlink "$source_path/dtc/scripts" "dtc/scripts"
|
||||
fi
|
||||
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
|
||||
# Not a git build & no libfdt found, prompt for system install
|
||||
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)
|
||||
|
||||
if test "$opengl" != "no" ; then
|
||||
opengl_pkgs="epoxy libdrm gbm"
|
||||
opengl_pkgs="epoxy gbm"
|
||||
if $pkg_config $opengl_pkgs; then
|
||||
opengl_cflags="$($pkg_config --cflags $opengl_pkgs)"
|
||||
opengl_libs="$($pkg_config --libs $opengl_pkgs)"
|
||||
|
@ -4478,7 +4569,7 @@ fi
|
|||
|
||||
# check for smartcard support
|
||||
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_libs=$($pkg_config --libs libcacard)
|
||||
smartcard="yes"
|
||||
|
@ -4604,6 +4695,7 @@ int main(void) { virgl_renderer_poll(); return 0; }
|
|||
EOF
|
||||
virgl_cflags=$($pkg_config --cflags 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 && \
|
||||
compile_prog "$virgl_cflags" "$virgl_libs" ; then
|
||||
virglrenderer="yes"
|
||||
|
@ -4751,6 +4843,21 @@ if compile_prog "" "" ; then
|
|||
sem_timedwait=yes
|
||||
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
|
||||
|
||||
|
@ -5176,6 +5283,20 @@ if test "$fortify_source" != "no"; then
|
|||
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
|
||||
|
||||
|
@ -5442,6 +5563,17 @@ EOF
|
|||
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
|
||||
# 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
|
||||
QEMU_CFLAGS="$pixman_cflags $fdt_cflags $QEMU_CFLAGS"
|
||||
QEMU_LDFLAGS="$fdt_ldflags $QEMU_LDFLAGS"
|
||||
libs_softmmu="$pixman_libs $libs_softmmu"
|
||||
|
||||
echo "Install prefix $prefix"
|
||||
|
@ -5774,6 +5907,7 @@ echo "ARFLAGS $ARFLAGS"
|
|||
echo "CFLAGS $CFLAGS"
|
||||
echo "QEMU_CFLAGS $QEMU_CFLAGS"
|
||||
echo "LDFLAGS $LDFLAGS"
|
||||
echo "QEMU_LDFLAGS $QEMU_LDFLAGS"
|
||||
echo "make $make"
|
||||
echo "install $install"
|
||||
echo "python $python"
|
||||
|
@ -5805,7 +5939,7 @@ echo "nettle $nettle $(echo_version $nettle $nettle_version)"
|
|||
echo "nettle kdf $nettle_kdf"
|
||||
echo "libtasn1 $tasn1"
|
||||
echo "curses support $curses"
|
||||
echo "virgl support $virglrenderer"
|
||||
echo "virgl support $virglrenderer $(echo_version $virglrenderer $virgl_version)"
|
||||
echo "curl support $curl"
|
||||
echo "mingw32 support $mingw32"
|
||||
echo "Audio drivers $audio_drv_list"
|
||||
|
@ -5882,6 +6016,7 @@ echo "seccomp support $seccomp"
|
|||
echo "coroutine backend $coroutine"
|
||||
echo "coroutine pool $coroutine_pool"
|
||||
echo "debug stack usage $debug_stack_usage"
|
||||
echo "mutex debugging $debug_mutex"
|
||||
echo "crypto afalg $crypto_afalg"
|
||||
echo "GlusterFS support $glusterfs"
|
||||
echo "gcov $gcov_tool"
|
||||
|
@ -5903,6 +6038,7 @@ echo "avx2 optimization $avx2_opt"
|
|||
echo "replication support $replication"
|
||||
echo "VxHS block device $vxhs"
|
||||
echo "capstone $capstone"
|
||||
echo "docker $docker"
|
||||
|
||||
if test "$sdl_too_old" = "yes"; then
|
||||
echo "-> Your SDL version is too old - please upgrade to have SDL support"
|
||||
|
@ -6206,6 +6342,9 @@ fi
|
|||
if test "$sem_timedwait" = "yes" ; then
|
||||
echo "CONFIG_SEM_TIMEDWAIT=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$strchrnul" = "yes" ; then
|
||||
echo "HAVE_STRCHRNUL=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$byteswap_h" = "yes" ; then
|
||||
echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak
|
||||
fi
|
||||
|
@ -6277,6 +6416,9 @@ fi
|
|||
if test "$have_fsxattr" = "yes" ; then
|
||||
echo "HAVE_FSXATTR=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$have_copy_file_range" = "yes" ; then
|
||||
echo "HAVE_COPY_FILE_RANGE=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$vte" = "yes" ; then
|
||||
echo "CONFIG_VTE=y" >> $config_host_mak
|
||||
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
|
||||
|
@ -6333,7 +6475,7 @@ fi
|
|||
if test "$preadv" = "yes" ; then
|
||||
echo "CONFIG_PREADV=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$fdt" = "yes" ; then
|
||||
if test "$fdt" != "no" ; then
|
||||
echo "CONFIG_FDT=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$membarrier" = "yes" ; then
|
||||
|
@ -6631,6 +6773,9 @@ fi
|
|||
if test "$capstone" != "no" ; then
|
||||
echo "CONFIG_CAPSTONE=y" >> $config_host_mak
|
||||
fi
|
||||
if test "$debug_mutex" = "yes" ; then
|
||||
echo "CONFIG_DEBUG_MUTEX=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
# Hold two types of flag:
|
||||
# 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
|
||||
echo "IASL=$iasl" >> $config_host_mak
|
||||
fi
|
||||
echo "CC_I386=$cc_i386" >> $config_host_mak
|
||||
echo "HOST_CC=$host_cc" >> $config_host_mak
|
||||
echo "CXX=$cxx" >> $config_host_mak
|
||||
echo "OBJCC=$objcc" >> $config_host_mak
|
||||
|
@ -6708,6 +6852,7 @@ else
|
|||
fi
|
||||
echo "LDFLAGS=$LDFLAGS" >> $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_I386_EMULATION=$ld_i386_emulation" >> $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
|
||||
fi
|
||||
|
||||
if test "$docker" != "no"; then
|
||||
echo "HAVE_USER_DOCKER=y" >> $config_host_mak
|
||||
fi
|
||||
|
||||
# use included Linux headers
|
||||
if test "$linux" = "yes" ; then
|
||||
mkdir -p linux-headers
|
||||
|
@ -6789,6 +6938,10 @@ case "$target" in
|
|||
;;
|
||||
esac
|
||||
|
||||
target_compiler=""
|
||||
target_compiler_static=""
|
||||
target_compiler_cflags=""
|
||||
|
||||
mkdir -p $target_dir
|
||||
echo "# Automatically generated by configure - do not modify" > $config_target_mak
|
||||
|
||||
|
@ -6804,19 +6957,25 @@ TARGET_ABI_DIR=""
|
|||
case "$target_name" in
|
||||
i386)
|
||||
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)
|
||||
TARGET_BASE_ARCH=i386
|
||||
gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml"
|
||||
target_compiler=$cross_cc_x86_64
|
||||
;;
|
||||
alpha)
|
||||
mttcg="yes"
|
||||
target_compiler=$cross_cc_alpha
|
||||
;;
|
||||
arm|armeb)
|
||||
TARGET_ARCH=arm
|
||||
bflt="yes"
|
||||
mttcg="yes"
|
||||
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)
|
||||
TARGET_ARCH=aarch64
|
||||
|
@ -6824,58 +6983,75 @@ case "$target_name" in
|
|||
bflt="yes"
|
||||
mttcg="yes"
|
||||
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)
|
||||
target_compiler=$cross_cc_cris
|
||||
;;
|
||||
hppa)
|
||||
mttcg="yes"
|
||||
target_compiler=$cross_cc_hppa
|
||||
;;
|
||||
lm32)
|
||||
target_compiler=$cross_cc_lm32
|
||||
;;
|
||||
m68k)
|
||||
bflt="yes"
|
||||
gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml"
|
||||
target_compiler=$cross_cc_m68k
|
||||
;;
|
||||
microblaze|microblazeel)
|
||||
TARGET_ARCH=microblaze
|
||||
bflt="yes"
|
||||
echo "TARGET_ABI32=y" >> $config_target_mak
|
||||
target_compiler=$cross_cc_microblaze
|
||||
;;
|
||||
mips|mipsel)
|
||||
TARGET_ARCH=mips
|
||||
target_compiler=$cross_cc_mips
|
||||
echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak
|
||||
;;
|
||||
mipsn32|mipsn32el)
|
||||
TARGET_ARCH=mips64
|
||||
TARGET_BASE_ARCH=mips
|
||||
target_compiler=$cross_cc_mipsn32
|
||||
echo "TARGET_ABI_MIPSN32=y" >> $config_target_mak
|
||||
echo "TARGET_ABI32=y" >> $config_target_mak
|
||||
;;
|
||||
mips64|mips64el)
|
||||
TARGET_ARCH=mips64
|
||||
TARGET_BASE_ARCH=mips
|
||||
target_compiler=$cross_cc_mips64
|
||||
echo "TARGET_ABI_MIPSN64=y" >> $config_target_mak
|
||||
;;
|
||||
moxie)
|
||||
target_compiler=$cross_cc_moxie
|
||||
;;
|
||||
nios2)
|
||||
target_compiler=$cross_cc_nios2
|
||||
;;
|
||||
or1k)
|
||||
target_compiler=$cross_cc_or1k
|
||||
TARGET_ARCH=openrisc
|
||||
TARGET_BASE_ARCH=openrisc
|
||||
;;
|
||||
ppc)
|
||||
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
|
||||
target_compiler=$cross_cc_powerpc
|
||||
;;
|
||||
ppcemb)
|
||||
TARGET_BASE_ARCH=ppc
|
||||
TARGET_ABI_DIR=ppc
|
||||
gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml"
|
||||
target_compiler=$cross_cc_ppcemb
|
||||
;;
|
||||
ppc64)
|
||||
TARGET_BASE_ARCH=ppc
|
||||
TARGET_ABI_DIR=ppc
|
||||
mttcg=yes
|
||||
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
||||
target_compiler=$cross_cc_ppc64
|
||||
;;
|
||||
ppc64le)
|
||||
TARGET_ARCH=ppc64
|
||||
|
@ -6883,6 +7059,7 @@ case "$target_name" in
|
|||
TARGET_ABI_DIR=ppc
|
||||
mttcg=yes
|
||||
gdb_xml_files="power64-core.xml power-fpu.xml power-altivec.xml power-spe.xml power-vsx.xml"
|
||||
target_compiler=$cross_cc_ppc64le
|
||||
;;
|
||||
ppc64abi32)
|
||||
TARGET_ARCH=ppc64
|
||||
|
@ -6890,45 +7067,57 @@ case "$target_name" in
|
|||
TARGET_ABI_DIR=ppc
|
||||
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"
|
||||
target_compiler=$cross_cc_ppc64abi32
|
||||
;;
|
||||
riscv32)
|
||||
TARGET_BASE_ARCH=riscv
|
||||
TARGET_ABI_DIR=riscv
|
||||
mttcg=yes
|
||||
target_compiler=$cross_cc_riscv32
|
||||
;;
|
||||
riscv64)
|
||||
TARGET_BASE_ARCH=riscv
|
||||
TARGET_ABI_DIR=riscv
|
||||
mttcg=yes
|
||||
target_compiler=$cross_cc_riscv64
|
||||
;;
|
||||
sh4|sh4eb)
|
||||
TARGET_ARCH=sh4
|
||||
bflt="yes"
|
||||
target_compiler=$cross_cc_sh4
|
||||
;;
|
||||
sparc)
|
||||
target_compiler=$cross_cc_sparc
|
||||
;;
|
||||
sparc64)
|
||||
TARGET_BASE_ARCH=sparc
|
||||
target_compiler=$cross_cc_sparc64
|
||||
;;
|
||||
sparc32plus)
|
||||
TARGET_ARCH=sparc64
|
||||
TARGET_BASE_ARCH=sparc
|
||||
TARGET_ABI_DIR=sparc
|
||||
target_compiler=$cross_cc_sparc32plus
|
||||
echo "TARGET_ABI32=y" >> $config_target_mak
|
||||
;;
|
||||
s390x)
|
||||
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"
|
||||
target_compiler=$cross_cc_s390x
|
||||
;;
|
||||
tilegx)
|
||||
target_compiler=$cross_cc_tilegx
|
||||
;;
|
||||
tricore)
|
||||
target_compiler=$cross_cc_tricore
|
||||
;;
|
||||
unicore32)
|
||||
target_compiler=$cross_cc_unicore32
|
||||
;;
|
||||
xtensa|xtensaeb)
|
||||
TARGET_ARCH=xtensa
|
||||
mttcg="yes"
|
||||
target_compiler=$cross_cc_xtensa
|
||||
;;
|
||||
*)
|
||||
error_exit "Unsupported target CPU"
|
||||
|
@ -6939,6 +7128,27 @@ if [ "$TARGET_BASE_ARCH" = "" ]; then
|
|||
TARGET_BASE_ARCH=$TARGET_ARCH
|
||||
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"
|
||||
|
||||
upper() {
|
||||
|
@ -7012,6 +7222,19 @@ if test "$target_bsd_user" = "yes" ; then
|
|||
echo "CONFIG_BSD_USER=y" >> $config_target_mak
|
||||
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
|
||||
|
||||
cflags=""
|
||||
|
@ -7134,7 +7357,12 @@ echo "QEMU_CFLAGS+=$cflags" >> $config_target_mak
|
|||
|
||||
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
|
||||
fi
|
||||
if [ "$capstone" = "git" -o "$capstone" = "internal" ]; then
|
||||
|
@ -7207,9 +7435,11 @@ for rom in seabios vgabios ; do
|
|||
done
|
||||
|
||||
# set up tests data directory
|
||||
if [ ! -e tests/data ]; then
|
||||
symlink "$source_path/tests/data" tests/data
|
||||
fi
|
||||
for tests_subdir in acceptance data; do
|
||||
if [ ! -e tests/$tests_subdir ]; then
|
||||
symlink "$source_path/tests/$tests_subdir" tests/$tests_subdir
|
||||
fi
|
||||
done
|
||||
|
||||
# set up qemu-iotests in this build directory
|
||||
iotests_common_env="tests/qemu-iotests/common.env"
|
||||
|
|
|
@ -314,22 +314,19 @@ vu_message_write(VuDev *dev, int conn_fd, VhostUserMsg *vmsg)
|
|||
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 {
|
||||
rc = sendmsg(conn_fd, &msg, 0);
|
||||
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
do {
|
||||
if (vmsg->data) {
|
||||
rc = write(conn_fd, vmsg->data, vmsg->size);
|
||||
} else {
|
||||
rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size);
|
||||
}
|
||||
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
if (vmsg->size) {
|
||||
do {
|
||||
if (vmsg->data) {
|
||||
rc = write(conn_fd, vmsg->data, vmsg->size);
|
||||
} else {
|
||||
rc = write(conn_fd, p + VHOST_USER_HDR_SIZE, vmsg->size);
|
||||
}
|
||||
} while (rc < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
}
|
||||
|
||||
if (rc <= 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
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. */
|
||||
static void
|
||||
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 */
|
||||
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");
|
||||
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
|
||||
vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
|
@ -966,7 +1031,9 @@ static bool
|
|||
vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg)
|
||||
{
|
||||
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()) {
|
||||
features |= 1ULL << VHOST_USER_PROTOCOL_F_PAGEFAULT;
|
||||
|
@ -1250,7 +1317,7 @@ vu_dispatch(VuDev *dev)
|
|||
goto end;
|
||||
}
|
||||
|
||||
if (!vu_message_write(dev, dev->sock, &vmsg)) {
|
||||
if (!vu_send_reply(dev, dev->sock, &vmsg)) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ enum VhostUserProtocolFeature {
|
|||
VHOST_USER_PROTOCOL_F_CRYPTO_SESSION = 7,
|
||||
VHOST_USER_PROTOCOL_F_PAGEFAULT = 8,
|
||||
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
|
||||
};
|
||||
|
@ -92,6 +94,14 @@ typedef enum VhostUserRequest {
|
|||
VHOST_USER_MAX
|
||||
} 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 {
|
||||
uint64_t guest_phys_addr;
|
||||
uint64_t memory_size;
|
||||
|
@ -122,6 +132,12 @@ static VhostUserConfig c __attribute__ ((unused));
|
|||
+ sizeof(c.size) \
|
||||
+ sizeof(c.flags))
|
||||
|
||||
typedef struct VhostUserVringArea {
|
||||
uint64_t u64;
|
||||
uint64_t size;
|
||||
uint64_t offset;
|
||||
} VhostUserVringArea;
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define VU_PACKED __attribute__((gcc_struct, packed))
|
||||
#else
|
||||
|
@ -133,6 +149,7 @@ typedef struct VhostUserMsg {
|
|||
|
||||
#define VHOST_USER_VERSION_MASK (0x3)
|
||||
#define VHOST_USER_REPLY_MASK (0x1 << 2)
|
||||
#define VHOST_USER_NEED_REPLY_MASK (0x1 << 3)
|
||||
uint32_t flags;
|
||||
uint32_t size; /* the following payload size */
|
||||
|
||||
|
@ -145,6 +162,7 @@ typedef struct VhostUserMsg {
|
|||
VhostUserMemory memory;
|
||||
VhostUserLog log;
|
||||
VhostUserConfig config;
|
||||
VhostUserVringArea area;
|
||||
} payload;
|
||||
|
||||
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,
|
||||
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:
|
||||
|
|
|
@ -31,6 +31,7 @@ typedef struct VubDev {
|
|||
VugDev parent;
|
||||
int blk_fd;
|
||||
struct virtio_blk_config blkcfg;
|
||||
bool enable_ro;
|
||||
char *blk_name;
|
||||
GMainLoop *loop;
|
||||
} VubDev;
|
||||
|
@ -301,14 +302,33 @@ static void vub_queue_set_started(VuDev *vu_dev, int idx, bool started)
|
|||
static uint64_t
|
||||
vub_get_features(VuDev *dev)
|
||||
{
|
||||
return 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;
|
||||
uint64_t features;
|
||||
VugDev *gdev;
|
||||
VubDev *vdev_blk;
|
||||
|
||||
gdev = container_of(dev, VugDev, parent);
|
||||
vdev_blk = container_of(gdev, VubDev, parent);
|
||||
|
||||
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
|
||||
|
@ -373,6 +393,7 @@ vub_set_config(VuDev *vu_dev, const uint8_t *data,
|
|||
static const VuDevIface vub_iface = {
|
||||
.get_features = vub_get_features,
|
||||
.queue_set_started = vub_queue_set_started,
|
||||
.get_protocol_features = vub_get_protocol_features,
|
||||
.get_config = vub_get_config,
|
||||
.set_config = vub_set_config,
|
||||
};
|
||||
|
@ -469,6 +490,7 @@ vub_new(char *blk_file)
|
|||
vub_free(vdev_blk);
|
||||
return NULL;
|
||||
}
|
||||
vdev_blk->enable_ro = false;
|
||||
vdev_blk->blkcfg.wce = 0;
|
||||
vdev_blk->blk_name = blk_file;
|
||||
|
||||
|
@ -483,10 +505,11 @@ int main(int argc, char **argv)
|
|||
int opt;
|
||||
char *unix_socket = NULL;
|
||||
char *blk_file = NULL;
|
||||
bool enable_ro = false;
|
||||
int lsock = -1, csock = -1;
|
||||
VubDev *vdev_blk = NULL;
|
||||
|
||||
while ((opt = getopt(argc, argv, "b:s:h")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "b:rs:h")) != -1) {
|
||||
switch (opt) {
|
||||
case 'b':
|
||||
blk_file = g_strdup(optarg);
|
||||
|
@ -494,17 +517,20 @@ int main(int argc, char **argv)
|
|||
case 's':
|
||||
unix_socket = g_strdup(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
enable_ro = true;
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
printf("Usage: %s [-b block device or file, -s UNIX domain socket]"
|
||||
" | [ -h ]\n", argv[0]);
|
||||
printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
|
||||
" | -r Enable read-only ] | [ -h ]\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!unix_socket || !blk_file) {
|
||||
printf("Usage: %s [-b block device or file, -s UNIX domain socket] |"
|
||||
" [ -h ]\n", argv[0]);
|
||||
printf("Usage: %s [ -b block device or file, -s UNIX domain socket"
|
||||
" | -r Enable read-only ] | [ -h ]\n", argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -523,6 +549,9 @@ int main(int argc, char **argv)
|
|||
if (!vdev_blk) {
|
||||
goto err;
|
||||
}
|
||||
if (enable_ro) {
|
||||
vdev_blk->enable_ro = true;
|
||||
}
|
||||
|
||||
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
Loading…
Reference in New Issue