Compare commits

..

39 Commits

Author SHA1 Message Date
Justin M. Forbes
3ffb4001c2 Version 1.0.1
Signed-off-by: Justin M. Forbes <jforbes@redhat.com>
2012-02-02 16:44:08 -06:00
Justin M. Forbes
86a8d63bc1 Merge branch 's390-1.0' of git://repo.or.cz/qemu/agraf 2012-02-01 11:25:23 -06:00
Justin M. Forbes
102dd9167c Merge branch 'ppc-1.0' of git://repo.or.cz/qemu/agraf 2012-02-01 11:24:47 -06:00
Anthony Liguori
d0ed2d2e8e e1000: bounds packet size against buffer size
Otherwise we can write beyond the buffer and corrupt memory.  This is tracked
as CVE-2012-0029.

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-01-23 15:07:00 -06:00
Christian Borntraeger
d194ba1cdb s390: fix cpu hotplug / cpu activity on interrupts
The add_del/running_cpu code and env->halted are tracking stopped cpus.
Sleeping cpus (idle and enabled for interrupts) are waiting inside the
kernel.
No interrupt besides the restart can move a cpu from stopped to
operational. This is already handled over there. So lets just remove
the bogus wakup from the common interrupt delivery, otherwise any
interrupt will wake up a cpu, even if this cpu is stopped (Thus leading
to strange hangs on sigp restart)

This fixes
echo 0 > /sys/devices/system/cpu/cpu0/online
echo 1 > /sys/devices/system/cpu/cpu0/online
in the guest

Signed-off-by: Christian Borntraeger<borntraeger@de.ibm.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
(cherry picked from commit 93116ac0cf)
2012-01-12 18:41:08 +01:00
Alexander Graf
8a0a9cf35b s390x: add TR function for EXECUTE
Newer gcc versions (or glibc?) also generate code that tries to EXECUTE
the TR opcode. Implement it so that we don't break valid guests.

Reported-by: Andreas Faerber <afaerber@suse.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
2012-01-12 18:41:08 +01:00
David Gibson
d928541b51 pseries: Don't try to munmap() a malloc()ed TCE table
For the pseries machine, TCE (IOMMU) tables can either be directly
malloc()ed in qemu or, when running on a KVM which supports it, mmap()ed
from a KVM ioctl.  The latter option is used when available, because it
allows the (frequent bottlenext) H_PUT_TCE hypercall to be KVM accelerated.
However, even when KVM is persent, TCE acceleration is not always possible.
Only KVM HV supports this ioctl(), not KVM PR, or the kernel could run out
of contiguous memory to allocate the new table.  In this case we need to
fall back on the malloc()ed table.

When a device is removed, and we need to remove the TCE table, we need to
either munmap() or free() the table as appropriate for how it was
allocated.  The code is supposed to do that, but we buggily fail to
initialize the tcet->fd variable in the malloc() case, which is used as a
flag to determine which is the right choice.

This patch fixes the bug, and cleans up error messages relating to this
path while we're at it.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
2012-01-12 18:30:51 +01:00
David Gibson
7a6d80e93c pseries: Populate "/chosen/linux,stdout-path" in the FDT
There is a device tree property "/chosen/linux,stdout-path" which indicates
which device should be used as stdout - ie. "the console".

Currently we don't specify anything, which means both firmware and Linux
choose something arbitrarily. Use the routine we added in the last patch
to pick a default vty and specify it as stdout.

Currently SLOF doesn't use the property, but we are hoping to update it
to do so.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
(cherry picked from commit 68f3a94c64)
2012-01-12 18:30:51 +01:00
David Gibson
447a3b3473 pseries: Add a routine to find a stable "default" vty and use it
In vty_lookup() we have a special case for supporting early debug in
the kernel. This accepts reg == 0 as a special case to mean "any vty".

We implement this by searching the vtys on the bus and returning the
first we find. This means that the vty we chose depends on the order
the vtys are specified on the QEMU command line - because that determines
the order of the vtys on the bus.

We'd rather the command line order was irrelevant, so instead return
the vty with the lowest reg value. This is still a guess as to what the
user really means, but it is at least stable WRT command line ordering.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>

[agraf] fix braces
(cherry picked from commit 98331f8ad6)
2012-01-12 18:30:50 +01:00
David Gibson
e295c31e25 pseries: Emit device tree nodes in reg order
Although in theory the device tree has no inherent ordering, in practice
the order of nodes in the device tree does effect the order that devices
are detected by software.

Currently the ordering is determined by the order the devices appear on
the QEMU command line. Although that does give the user control over the
ordering, it is fragile, especially when the user does not generate the
command line manually - eg. when using libvirt etc.

So order the device tree based on the reg value, ie. the address of on
the VIO bus of the devices. This gives us a sane and stable ordering.

Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>

[agraf] add braces
(cherry picked from commit 05c194384f)
2012-01-12 18:30:50 +01:00
Liu Yu-B13201
adf6c527b0 kvm-ppc: halt secondary cpus when guest reset
When guest reset, we need to halt secondary cpus until guest kick them.
This already works for tcg. The patch add the support for kvm.

Signed-off-by: Liu Yu <yu.liu@freescale.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
[agraf: remove in-kernel irqchip code]
(cherry picked from commit 157feeadba)
2012-01-12 18:30:50 +01:00
David Gibson
57ee5f77c0 pseries: Fix array overrun bug in PCI code
spapr_populate_pci_devices() containd a loop with PCI_NUM_REGIONS (7)
iterations.  However this overruns the 'bars' global array, which only has
6 elements. In fact we only want to run this loop for things listed in the
bars array, so this patch corrects the loop bounds to reflect that.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Alexander Graf <agraf@suse.de>
(cherry picked from commit 135712de61dfa22368e98914d65b8b0860ec8505)
2012-01-12 18:30:50 +01:00
Alexander Graf
986626efec console: Fix segfault on screendump without VGA adapter
When trying to create a screen dump without having any VGA adapter
inside the guest, QEMU segfaults.

This is because it's trying to switch back to the "previous" screen
it was on before dumping the VGA screen. Unfortunately, in my case
there simply is no previous screen so it accesses a NULL pointer.

Fix it by checking if previous_active_console is actually available.

This is 1.0 material.

Signed-off-by: Alexander Graf <agraf@suse.de>
2012-01-12 18:30:50 +01:00
Justin M. Forbes
85a4ca797d Merge branch 'master' of ssh://git.qemu.org/pub/git/qemu-stable-1.0 2012-01-10 14:41:17 -06:00
Josh Durgin
e47c212cb5 rbd: always set out parameter in qemu_rbd_snap_list
The caller expects psn_tab to be NULL when there are no snapshots or
an error occurs. This results in calling g_free on an invalid address.

Reported-by: Oliver Francke <Oliver@filoo.de>
Signed-off-by: Josh Durgin <josh.durgin@dreamhost.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-01-10 13:37:11 -06:00
Kevin Wolf
8afe984ef7 Documentation: Add qemu-img -t parameter in man page
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
2012-01-10 13:37:02 -06:00
Kevin Wolf
5bb37d151b qemu-img rebase: Fix for undersized backing files
Backing files may be smaller than the corresponding COW file. When
reading directly from the backing file, qemu-img rebase must consider
this and assume zero sectors after the end of backing files.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
2012-01-10 13:36:51 -06:00
Avi Kivity
fe5c13ebf1 coroutine: switch per-thread free pool to a global pool
ucontext-based coroutines use a free pool to reduce allocations and
deallocations of coroutine objects.  The pool is per-thread, presumably
to improve locality.  However, as coroutines are usually allocated in
a vcpu thread and freed in the I/O thread, the pool accounting gets
screwed up and we end allocating and freeing a coroutine for every I/O
request.  This is expensive since large objects are allocated via the
kernel, and are not cached by the C runtime.

Fix by switching to a global pool.  This is safe since we're protected
by the global mutex.

Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-01-10 13:36:39 -06:00
Paolo Bonzini
6061f16a8a qiov: prevent double free or use-after-free
qemu_iovec_destroy does not clear the QEMUIOVector fully, and the data
could thus be used after free or freed again.  While I do not know any
example in the tree, I observed this using virtio-scsi (and SCSI
scatter/gather) when canceling DMA requests.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2012-01-10 13:36:27 -06:00
Alexander Graf
fbcf305e5a PPC: Fix linker scripts on ppc hosts
When compiling qemu statically with multilib on PPC, we hit the
same issue that commit 845f2c2812
is fixing. Do the same here.

Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
(cherry picked from commit 665a04ae1c)
2012-01-10 18:30:12 +01:00
Aurelien Jarno
37769d2727 target-sh4: ignore ocbp and ocbwb instructions
ocbp and ocbwb controls the writeback of a cache line to memory. They
are supposed to do nothing in case of a cache miss. Given QEMU only
partially emulate caches, it is safe to ignore these instructions.

This fixes a kernel oops when trying to access an rtl8139 NIC with
recent versions.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
(cherry picked from commit 0cdb95549f)
2012-01-10 18:30:08 +01:00
Andriy Gapon
23201c64a7 usb-ohci: td.cbp incorrectly updated near page end
The current code that updates the cbp value after a transfer looks like this:
td.cbp += ret;
if ((td.cbp & 0xfff) + ret > 0xfff) {
	<handle page overflow>
because the 'ret' value is effectively added twice the check may fire too early
when the overflow hasn't happened yet.

Below is one of the possible changes that correct the behavior:

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-01-10 09:45:48 -06:00
Gerd Hoffmann
c936f649d4 usb-host: properly release port on unplug & exit
Factor out port release into a separate function.  Call release function
in exit notifier too.  Add explicit call the USBDEVFS_RELEASE_PORT
ioctl, just closing the hub file handle seems not to be enougth.  Make
sure we release the port before resetting the device, otherwise host
drivers will not re-attach.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-01-10 09:45:37 -06:00
Gerd Hoffmann
f63d074313 usb-storage: cancel I/O on reset
When resetting the usb-storage device we'll have to carefully cancel
and clear any requests which might be in flight, otherwise we'll confuse
the state machine.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-01-10 09:45:28 -06:00
Cao,Bing Bu
9b81fbdbb0 Fix parse of usb device description with multiple configurations
Changed From V1:
Use DPRINTF instead of fprintf,because it is not an error.

When testing ipod on QEMU by He Jie Xu<xuhj@linux.vnet.ibm.com>,qemu made a assertion.
We found that the ipod with 2 configurations,and the usb-linux did not parse the descriptor correctly.
The descr_len returned is the total length of the all configurations,not one configuration.
The older version will through the other configurations instead of skip,continue parsing the descriptor of interfaces/endpoints in other configurations,then went wrong.

This patch will put the configuration descriptor parse in loop outside and dispel the other configurations not requested.

Signed-off-by: Cao,Bing Bu <mars@linux.vnet.ibm.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-01-10 09:45:14 -06:00
Anthony Liguori
7e2191ae98 pc: fix event_idx compatibility for virtio devices
event_idx was introduced in 0.15 and must be disabled for all virtio-pci devices
(including virtio-balloon-pci).

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-01-10 09:37:20 -06:00
Anthony Liguori
a25808dc5b pc: add pc-0.15
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-01-10 09:33:28 -06:00
Stefan Sandstrom
3e8088148b cris: Handle conditional stores on CRISv10
Signed-off-by: Stefan Sandstrom <Stefan.Sandstrom@axis.com>
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
2012-01-10 09:32:19 -06:00
Brad
6d450bfbc8 configure: Enable build by default PIE / read-only relocation sections on OpenBSD amd64/i386.
Enable build by default PIE / read-only relocation sections for the QEMU
binaries on OpenBSD amd64/i386.

Signed-off-by: Brad Smith <brad@comstyle.com>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
2012-01-10 09:31:46 -06:00
Andreas Gustafsson
abf80f8804 target-i386: fix cmpxchg instruction emulation
When the i386 cmpxchg instruction is executed with a memory operand
and the comparison result is "unequal", do the memory write before
changing the accumulator instead of the other way around, because
otherwise the new accumulator value will incorrectly be used in the
comparison when the instruction is restarted after a page fault.

This bug was originally reported on 2010-04-25 as
https://bugs.launchpad.net/qemu/+bug/569760

Signed-off-by: Andreas Gustafsson <gson@gson.org>
2012-01-10 09:31:37 -06:00
Aneesh Kumar K.V
3d3ec7b809 hw/9pfs: Use the correct signed type for different variables
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
2012-01-10 09:29:25 -06:00
Stefan Hajnoczi
45d6cdff48 hw/9pfs: replace iovec manipulation with QEMUIOVector
The v9fs_read() and v9fs_write() functions rely on iovec[] manipulation
code should be replaced with QEMUIOVector to avoid duplicating code.
In the future it may be possible to make the code even more concise by
using QEMUIOVector consistently across virtio and 9pfs.

The "v" format specifier for pdu_marshal() and pdu_unmarshal() is
dropped since it does not actually pack/unpack anything.  The specifier
was also not implemented to update the offset variable and could only be
used at the end of a format string, another sign that this shouldn't
really be a format specifier.  Instead, see the new
v9fs_init_qiov_from_pdu() function.

This change avoids a possible iovec[] buffer overflow when indirect
vrings are used since the number of vectors is now limited by the
underlying VirtQueueElement and cannot be out-of-bounds.

Signed-off-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
2012-01-10 09:29:11 -06:00
Aneesh Kumar K.V
ed6857bf98 hw/9pfs: Use the correct file descriptor in Fsdriver Callback
Fsdriver callback that operate on file descriptor need to
differentiate between directory fd and file fd.

Based on the original patch from Sassan Panahinejad <sassan@sassan.me.uk>

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
2012-01-10 09:27:11 -06:00
Aneesh Kumar K.V
64dd41bc2d hw/9pfs: Add qdev.reset callback for virtio-9p-pci device
Add the device reset callback

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
2012-01-10 09:27:00 -06:00
Deepak C Shetty
c554919f74 hw/9pfs: Reset server state during TVERSION
As per the 9p rfc, during TVERSION its necessary to clean all the active
fids, so that we start the session from a clean state. Its also needed in
scenarios where the guest is booting off 9p, and boot fails, and client
restarts, without any knowledge of the past, it will issue a TVERSION again
so this ensures that we always start from a clean state.

Signed-off-by: Deepak C Shetty <deepakcs@linux.vnet.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
2012-01-10 09:26:50 -06:00
Aneesh Kumar K.V
77a0262181 hw/9pfs: use migration blockers to prevent live migration when virtfs export path is mounted
Now when you try to migrate with VirtFS export path mounted, you get a proper QMP error:

(qemu) migrate tcp:localhost:4444
Migration is disabled when VirtFS export path '/tmp/' is mounted in the guest using mount_tag 'v_tmp'
(qemu)

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
2012-01-10 09:26:39 -06:00
Aneesh Kumar K.V
f03969b952 hw/9pfs: Improve portability to older systems
handle fs driver require a set of newly added syscalls. Don't
Compile handle FS driver if those syscalls are not available.
Instead of adding #ifdef for all those syscalls we check for
open by handle syscall. If that is available then rest of the
syscalls used by the driver should be available.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
2012-01-10 09:26:28 -06:00
Andreas Färber
2061800b85 exec.c: Fix subpage memory access to RAM MemoryRegion
Commit 95c318f5e1 (Fix segfault in mmio
subpage handling code.) prevented a segfault by making all subpage
registrations over an existing memory page perform an unassigned access.
Symptoms were writes not taking effect and reads returning zero.

Very small page sizes are not currently supported either,
so subpage memory areas cannot fully be avoided.

Therefore change the previous fix to use a new IO_MEM_SUBPAGE_RAM
instead of IO_MEM_UNASSIGNED. Suggested by Avi.

Reviewed-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
Cc: Avi Kivity <avi@redhat.com>
Cc: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2012-01-10 09:22:55 -06:00
Stefan Weil
0b23c5d40e malta: Fix regression (i8259 interrupts did not work)
Commit 5632ae46d5 passes the address
of i8259 to qemu_irq_proxy. i8259 is an auto variable with undefined
value outside of mips_malta_init.

This made the interrupt proxy unusable: either QEMU crashes, or
the interrupt handler was not called.

Ethernet for example no longer worked with MIPS Malta.

v2:
While v1 used a static variable for i8259, this patch introduces
a qdev for the malta machine. i8259 is now part of the device status.
This is a minimal qdev implementation to keep the patch small.

Signed-off-by: Stefan Weil <sw@weilnetz.de>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
(cherry picked from commit e9b40fd34c)
2012-01-08 15:11:39 +01:00
1471 changed files with 43561 additions and 249877 deletions

13
.gitignore vendored
View File

@@ -39,16 +39,9 @@ qemu-img-cmds.texi
qemu-img-cmds.h
qemu-io
qemu-ga
qemu-bridge-helper
qemu-monitor.texi
QMP/qmp-commands.txt
test-coroutine
test-qmp-input-visitor
test-qmp-output-visitor
test-string-input-visitor
test-string-output-visitor
fsdev/virtfs-proxy-helper.1
fsdev/virtfs-proxy-helper.pod
.gdbinit
*.a
*.aux
@@ -76,14 +69,8 @@ patches
pc-bios/bios-pq/status
pc-bios/vgabios-pq/status
pc-bios/optionrom/linuxboot.bin
pc-bios/optionrom/linuxboot.raw
pc-bios/optionrom/linuxboot.img
pc-bios/optionrom/multiboot.bin
pc-bios/optionrom/multiboot.raw
pc-bios/optionrom/multiboot.img
pc-bios/optionrom/kvmvapic.bin
pc-bios/optionrom/kvmvapic.raw
pc-bios/optionrom/kvmvapic.img
.stgit-*
cscope.*
tags

View File

@@ -1,16 +0,0 @@
# This mailmap just translates the weird addresses from the original import into git
# into proper addresses so that they are counted properly in git shortlog output.
#
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <aliguori@us.ibm.com> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>
Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162>
Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>
Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
# There is also a:
# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162>
# for the cvs2svn initialization commit e63c3dc74bf.

View File

@@ -1,4 +1,4 @@
QEMU Coding Style
Qemu Coding Style
=================
Please use the script checkpatch.pl in the scripts directory to check
@@ -44,8 +44,7 @@ Rationale:
3. Naming
Variables are lower_case_with_underscores; easy to type and read. Structured
type names are in CamelCase; harder to type but standing out. Enum type
names and function type names should also be in CamelCase. Scalar type
type names are in CamelCase; harder to type but standing out. Scalar type
names are lower_case_with_underscores_ending_with_a_t, like the POSIX
uint64_t and family. Note that this last convention contradicts POSIX
and is therefore likely to be changed.

View File

@@ -78,7 +78,7 @@ version 0.10.2:
- fix savevm/loadvm (Anthony Liguori)
- live migration: fix dirty tracking windows (Glauber Costa)
- live migration: improve error propagation (Glauber Costa)
- live migration: improve error propogation (Glauber Costa)
- qcow2: fix image creation for > ~2TB images (Chris Wright)
- hotplug: fix error handling for if= parameter (Eduardo Habkost)
- qcow2: fix data corruption (Nolan Leake)
@@ -386,7 +386,7 @@ version 0.5.3:
- support of CD-ROM change
- multiple network interface support
- initial x86-64 host support (Gwenole Beauchesne)
- lret to outer privilege fix (OS/2 install fix)
- lret to outer priviledge fix (OS/2 install fix)
- task switch fixes (SkyOS boot)
- VM save/restore commands
- new timer API
@@ -447,7 +447,7 @@ version 0.5.0:
- multi-target build
- fixed: no error code in hardware interrupts
- fixed: pop ss, mov ss, x and sti disable hardware irqs for the next insn
- correct single stepping through string operations
- correct single stepping thru string operations
- preliminary SPARC target support (Thomas M. Ogrisegg)
- tun-fd option (Rusty Russell)
- automatic IDE geometry detection

10
HACKING
View File

@@ -77,13 +77,11 @@ avoided.
Use of the malloc/free/realloc/calloc/valloc/memalign/posix_memalign
APIs is not allowed in the QEMU codebase. Instead of these routines,
use the GLib memory allocation routines g_malloc/g_malloc0/g_new/
g_new0/g_realloc/g_free or QEMU's qemu_vmalloc/qemu_memalign/qemu_vfree
APIs.
use the replacement g_malloc/g_malloc0/g_realloc/g_free or
qemu_vmalloc/qemu_memalign/qemu_vfree APIs.
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.
Please note that NULL check for the g_malloc result is redundant and
that g_malloc() call with zero size is not allowed.
Memory allocated by qemu_vmalloc or qemu_memalign must be freed with
qemu_vfree, since breaking this will cause problems on Win32 and user

View File

@@ -6,7 +6,9 @@ The following points clarify the QEMU license:
GNU General Public License. Hence each source file contains its own
licensing information.
Many hardware device emulation sources are released under the BSD license.
In particular, the QEMU virtual CPU core library (libqemu.a) is
released under the GNU Lesser General Public License. Many hardware
device emulation sources are released under the BSD license.
3) The Tiny Code Generator (TCG) is released under the BSD license
(see license headers in files).

View File

@@ -20,7 +20,7 @@ Descriptions of section entries:
Supported: Someone is actually paid to look after this.
Maintained: Someone actually looks after it.
Odd Fixes: It has a maintainer but they don't have time to do
much other than throw the odd patch in. See below.
much other than throw the odd patch in. See below..
Orphan: No current maintainer [but maybe you could take the
role as you write your new code].
Obsolete: Old code. Something tagged obsolete generally means
@@ -78,7 +78,7 @@ F: target-lm32/
M68K
M: Paul Brook <paul@codesourcery.com>
S: Odd Fixes
S: Maintained
F: target-m68k/
MicroBlaze
@@ -88,12 +88,11 @@ F: target-microblaze/
MIPS
M: Aurelien Jarno <aurelien@aurel32.net>
S: Odd Fixes
S: Maintained
F: target-mips/
PowerPC
M: Alexander Graf <agraf@suse.de>
L: qemu-ppc@nongnu.org
S: Maintained
F: target-ppc/
@@ -104,7 +103,7 @@ F: target-s390x/
SH4
M: Aurelien Jarno <aurelien@aurel32.net>
S: Odd Fixes
S: Maintained
F: target-sh4/
SPARC
@@ -112,11 +111,6 @@ M: Blue Swirl <blauwirbel@gmail.com>
S: Maintained
F: target-sparc/
UniCore32
M: Guan Xuetao <gxt@mprc.pku.edu.cn>
S: Maintained
F: target-unicore32/
X86
M: qemu-devel@nongnu.org
S: Odd Fixes
@@ -166,42 +160,8 @@ S: Supported
F: xen-*
F: */xen*
Hosts:
------
LINUX
L: qemu-devel@nongnu.org
S: Maintained
F: linux-*
F: linux-headers/
POSIX
L: qemu-devel@nongnu.org
S: Maintained
F: *posix*
W32, W64
L: qemu-devel@nongnu.org
M: Stefan Weil <sw@weilnetz.de>
S: Maintained
F: *win32*
ARM Machines
------------
Exynos
M: Evgeny Voevodin <e.voevodin@samsung.com>
M: Maksim Kozlov <m.kozlov@samsung.com>
M: Igor Mitsyanko <i.mitsyanko@samsung.com>
M: Dmitry Solodkiy <d.solodkiy@samsung.com>
S: Maintained
F: hw/exynos*
Calxeda Highbank
M: Mark Langsdorf <mark.langsdorf@calxeda.com>
S: Supported
F: hw/highbank.c
F: hw/xgmac.c
Gumstix
M: qemu-devel@nongnu.org
S: Orphan
@@ -256,13 +216,6 @@ M: Peter Maydell <peter.maydell@linaro.org>
S: Maintained
F: hw/versatilepb.c
Xilinx Zynq
M: Peter Crosthwaite <peter.crosthwaite@petalogix.com>
S: Maintained
F: hw/xilinx_zynq.c
F: hw/zynq_slcr.c
F: hw/cadence_*
CRIS Machines
-------------
Axis Dev88
@@ -337,31 +290,23 @@ PowerPC Machines
----------------
405
M: Alexander Graf <agraf@suse.de>
L: qemu-ppc@nongnu.org
S: Maintained
F: hw/ppc405_boards.c
New World
M: Alexander Graf <agraf@suse.de>
L: qemu-ppc@nongnu.org
S: Maintained
F: hw/ppc_newworld.c
F: hw/unin_pci.c
F: hw/dec_pci.[hc]
Old World
M: Alexander Graf <agraf@suse.de>
L: qemu-ppc@nongnu.org
S: Maintained
F: hw/ppc_oldworld.c
F: hw/grackle_pci.c
PReP
M: Andreas Färber <andreas.faerber@web.de>
L: qemu-ppc@nongnu.org
S: Odd Fixes
F: hw/ppc_prep.c
F: hw/prep_pci.[hc]
SH4 Machines
------------
@@ -399,8 +344,7 @@ X86 Machines
PC
M: Anthony Liguori <aliguori@us.ibm.com>
S: Supported
F: hw/pc.[ch]
F: hw/pc_piix.c
F: hw/pc.[ch] hw/pc_piix.c
Xtensa Machines
---------------
@@ -433,16 +377,11 @@ F: hw/pci*
F: hw/piix*
SCSI
M: Paolo Bonzini <pbonzini@redhat.com>
S: Supported
F: hw/virtio-scsi.*
F: hw/scsi*
T: git git://github.com/bonzini/qemu.git scsi-next
LSI53C895A
M: Paul Brook <paul@codesourcery.com>
M: Kevin Wolf <kwolf@redhat.com>
S: Odd Fixes
F: hw/lsi53c895a.c
F: hw/scsi*
USB
M: Gerd Hoffmann <kraxel@redhat.com>
@@ -460,11 +399,9 @@ S: Supported
F: hw/virtio*
virtio-9p
M: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
M: Venkateswararao Jujjuri (JV) <jvrao@linux.vnet.ibm.com>
S: Supported
F: hw/9pfs/
F: fsdev/
T: git git://github.com/kvaneesh/QEMU.git
F: hw/virtio-9p*
virtio-blk
M: Kevin Wolf <kwolf@redhat.com>
@@ -536,28 +473,17 @@ M: Mark McLoughlin <markmc@redhat.com>
S: Maintained
F: net/
Network Block Device (NBD)
M: Paolo Bonzini <pbonzini@redhat.com>
S: Odd Fixes
F: block/nbd.c
F: nbd.*
F: qemu-nbd.c
T: git git://github.com/bonzini/qemu.git nbd-next
SLIRP
M: Jan Kiszka <jan.kiszka@siemens.com>
S: Maintained
F: slirp/
T: git git://git.kiszka.org/qemu.git queues/slirp
T: git://git.kiszka.org/qemu.git queues/slirp
Tracing
M: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
S: Maintained
F: trace/
F: scripts/tracetool.py
F: scripts/tracetool/
F: docs/tracing.txt
T: git git://github.com/stefanha/qemu.git tracing
T: git://repo.or.cz/qemu/stefanha.git tracing
Checkpatch
M: Blue Swirl <blauwirbel@gmail.com>
@@ -571,6 +497,11 @@ M: Blue Swirl <blauwirbel@gmail.com>
S: Maintained
F: bsd-user/
Darwin user
M: qemu-devel@nongnu.org
S: Orphan
F: darwin-user/
Linux user
M: Riku Voipio <riku.voipio@iki.fi>
S: Maintained
@@ -632,26 +563,4 @@ F: tcg/sparc/
TCI target
M: Stefan Weil <sw@weilnetz.de>
S: Maintained
F: tcg/tci/
Stable branches
---------------
Stable 1.0
L: qemu-stable@nongnu.org
T: git git://git.qemu.org/qemu-stable-1.0.git
S: Orphan
Stable 0.15
L: qemu-stable@nongnu.org
T: git git://git.qemu.org/qemu-stable-0.15.git
S: Orphan
Stable 0.14
L: qemu-stable@nongnu.org
T: git git://git.qemu.org/qemu-stable-0.14.git
S: Orphan
Stable 0.10
L: qemu-stable@nongnu.org
T: git git://git.qemu.org/qemu-stable-0.10.git
S: Orphan
F: tcg/tci

156
Makefile
View File

@@ -3,7 +3,13 @@
# Always point to the root of the build tree (needs GNU make).
BUILD_DIR=$(CURDIR)
# All following code might depend on configuration variables
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
ifeq ($(TRACE_BACKEND),dtrace)
GENERATED_HEADERS += trace-dtrace.h
endif
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
all: build-all
@@ -18,13 +24,6 @@ config-host.mak:
@exit 1
endif
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
ifeq ($(TRACE_BACKEND),dtrace)
GENERATED_HEADERS += trace-dtrace.h
endif
GENERATED_HEADERS += qmp-commands.h qapi-types.h qapi-visit.h
GENERATED_SOURCES += qmp-marshal.c qapi-types.c qapi-visit.c trace.c
# Don't try to regenerate Makefile or configure
# We don't generate any of them
Makefile: ;
@@ -37,13 +36,8 @@ $(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
LIBS+=-lz $(LIBS_TOOLS)
HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF)
ifdef BUILD_DOCS
DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1 qemu-nbd.8 QMP/qmp-commands.txt
ifdef CONFIG_VIRTFS
DOCS+=fsdev/virtfs-proxy-helper.1
endif
else
DOCS=
endif
@@ -82,7 +76,7 @@ defconfig:
-include config-all-devices.mak
build-all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all
build-all: $(DOCS) $(TOOLS) $(CHECKS) recurse-all
config-host.h: config-host.h-timestamp
config-host.h-timestamp: config-host.mak
@@ -98,12 +92,12 @@ ifneq ($(wildcard config-host.mak),)
include $(SRC_PATH)/Makefile.objs
endif
$(universal-obj-y) $(common-obj-y): $(GENERATED_HEADERS)
$(common-obj-y): $(GENERATED_HEADERS)
subdir-libcacard: $(oslib-obj-y) $(trace-obj-y) qemu-timer-common.o
$(filter %-softmmu,$(SUBDIR_RULES)): $(universal-obj-y) $(trace-obj-y) $(common-obj-y) subdir-libdis
$(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis
$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(universal-obj-y) $(trace-obj-y) subdir-libdis-user subdir-libuser
$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
romsubdir-%:
@@ -117,7 +111,7 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
QEMU_CFLAGS+=$(CURL_CFLAGS)
QEMU_CFLAGS += -I$(SRC_PATH)/include
QEMU_CFLAGS+=$(GLIB_CFLAGS)
ui/cocoa.o: ui/cocoa.m
@@ -153,60 +147,73 @@ endif
qemu-img.o: qemu-img-cmds.h
qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o cmd.o qemu-ga.o: $(GENERATED_HEADERS)
tools-obj-y = $(oslib-obj-y) $(trace-obj-y) qemu-tool.o qemu-timer.o \
qemu-timer-common.o main-loop.o notify.o iohandler.o cutils.o async.o
tools-obj-$(CONFIG_POSIX) += compatfd.o
tools-obj-y = qemu-tool.o $(oslib-obj-y) $(trace-obj-y) \
qemu-timer-common.o cutils.o
qemu-img$(EXESUF): qemu-img.o $(tools-obj-y) $(block-obj-y)
qemu-nbd$(EXESUF): qemu-nbd.o $(tools-obj-y) $(block-obj-y)
qemu-io$(EXESUF): qemu-io.o cmd.o $(tools-obj-y) $(block-obj-y)
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o
qemu-bridge-helper.o: $(GENERATED_HEADERS)
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/virtio-9p-marshal.o oslib-posix.o $(trace-obj-y)
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o test-coroutine.o: $(GENERATED_HEADERS)
check-qint: check-qint.o qint.o $(tools-obj-y)
check-qstring: check-qstring.o qstring.o $(tools-obj-y)
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(tools-obj-y)
check-qlist: check-qlist.o qlist.o qint.o $(tools-obj-y)
check-qfloat: check-qfloat.o qfloat.o $(tools-obj-y)
check-qjson: check-qjson.o $(qobject-obj-y) $(tools-obj-y)
test-coroutine: test-coroutine.o qemu-timer-common.o async.o $(coroutine-obj-y) $(tools-obj-y)
$(qapi-obj-y): $(GENERATED_HEADERS)
qapi-dir := $(BUILD_DIR)/qapi-generated
test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
qemu-ga$(EXESUF): LIBS = $(LIBS_QGA)
qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
gen-out-type = $(subst .,-,$(suffix $@))
ifneq ($(wildcard config-host.mak),)
include $(SRC_PATH)/tests/Makefile
endif
$(qapi-dir)/test-qapi-types.c $(qapi-dir)/test-qapi-types.h :\
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-types.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
$(qapi-dir)/test-qapi-visit.c $(qapi-dir)/test-qapi-visit.h :\
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-visit.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
$(qapi-dir)/test-qmp-commands.h $(qapi-dir)/test-qmp-marshal.c :\
$(SRC_PATH)/qapi-schema-test.json $(SRC_PATH)/scripts/qapi-commands.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "test-" < $<, " GEN $@")
$(qapi-dir)/qga-qapi-types.c $(qapi-dir)/qga-qapi-types.h :\
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-types.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
$(qapi-dir)/qga-qapi-visit.c $(qapi-dir)/qga-qapi-visit.h :\
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-visit.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
$(qapi-dir)/qga-qmp-commands.h $(qapi-dir)/qga-qmp-marshal.c :\
$(SRC_PATH)/qapi-schema-guest.json $(SRC_PATH)/scripts/qapi-commands.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
qapi-types.c qapi-types.h :\
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-types.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py $(gen-out-type) -o "." < $<, " GEN $@")
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-types.py -o "." < $<, " GEN $@")
qapi-visit.c qapi-visit.h :\
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-visit.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py $(gen-out-type) -o "." < $<, " GEN $@")
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-visit.py -o "." < $<, " GEN $@")
qmp-commands.h qmp-marshal.c :\
$(SRC_PATH)/qapi-schema.json $(SRC_PATH)/scripts/qapi-commands.py
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py $(gen-out-type) -m -o "." < $<, " GEN $@")
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-commands.py -m -o "." < $<, " GEN $@")
test-visitor.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h) $(qapi-obj-y)
test-visitor: test-visitor.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o
test-qmp-commands.o: $(addprefix $(qapi-dir)/, test-qapi-types.c test-qapi-types.h test-qapi-visit.c test-qapi-visit.h test-qmp-marshal.c test-qmp-commands.h) $(qapi-obj-y)
test-qmp-commands: test-qmp-commands.o $(qobject-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
QGALIB_OBJ=$(addprefix $(qapi-dir)/, qga-qapi-types.o qga-qapi-visit.o qga-qmp-marshal.o)
QGALIB_GEN=$(addprefix $(qapi-dir)/, qga-qapi-types.h qga-qapi-visit.h qga-qmp-commands.h)
$(QGALIB_OBJ): $(QGALIB_GEN) $(GENERATED_HEADERS)
$(qga-obj-y) qemu-ga.o: $(QGALIB_GEN) $(GENERATED_HEADERS)
qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(tools-obj-y) $(qapi-obj-y) $(qobject-obj-y) $(version-obj-y) $(QGALIB_OBJ)
qemu-ga$(EXESUF): qemu-ga.o $(qga-obj-y) $(qapi-obj-y) $(tools-obj-y) $(qobject-obj-y) $(version-obj-y) $(QGALIB_OBJ)
QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
@@ -214,19 +221,17 @@ 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
rm -f qemu-options.def
rm -f *.o *.d *.a *.lo $(TOOLS) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~
rm -f *.o *.d *.a *.lo $(TOOLS) $(CHECKS) qemu-ga TAGS cscope.* *.pod *~ */*~
rm -Rf .libs
rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d block/*.o block/*.d net/*.o net/*.d fsdev/*.o fsdev/*.d ui/*.o ui/*.d qapi/*.o qapi/*.d qga/*.o qga/*.d
rm -f qom/*.o qom/*.d
rm -f qemu-img-cmds.h
rm -f trace/*.o trace/*.d
rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
@# May not be present in GENERATED_HEADERS
rm -f trace-dtrace.h trace-dtrace.h-timestamp
rm -f $(foreach f,$(GENERATED_HEADERS),$(f) $(f)-timestamp)
rm -f $(foreach f,$(GENERATED_SOURCES),$(f) $(f)-timestamp)
rm -f $(GENERATED_SOURCES)
rm -rf $(qapi-dir)
$(MAKE) -C tests/tcg clean
$(MAKE) -C tests clean
for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
rm -f $$d/qemu-options.def; \
@@ -241,7 +246,6 @@ distclean: clean
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
rm -f qemu-doc.vr
rm -f config.log
rm -f linux-headers/asm
rm -f qemu-tech.info qemu-tech.aux qemu-tech.cp qemu-tech.dvi qemu-tech.fn qemu-tech.info qemu-tech.ky qemu-tech.log qemu-tech.pdf qemu-tech.pg qemu-tech.toc qemu-tech.tp qemu-tech.vr
for d in $(TARGET_DIRS) $(QEMULIBS); do \
rm -rf $$d || exit 1 ; \
@@ -257,10 +261,9 @@ vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
qemu-icon.bmp \
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
mpc8544ds.dtb \
multiboot.bin linuxboot.bin kvmvapic.bin \
multiboot.bin linuxboot.bin \
s390-zipl.rom \
spapr-rtas.bin slof.bin \
palcode-clipper
@@ -269,46 +272,33 @@ BLOBS=
endif
install-doc: $(DOCS)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.html qemu-tech.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DIR) "$(DESTDIR)$(docdir)"
$(INSTALL_DATA) qemu-doc.html qemu-tech.html "$(DESTDIR)$(docdir)"
ifdef CONFIG_POSIX
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) qemu.1 qemu-img.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
endif
ifdef CONFIG_VIRTFS
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) fsdev/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1"
endif
install-datadir:
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)"
install-sysconfig:
$(INSTALL_DIR) "$(DESTDIR)$(sysconfdir)/qemu"
$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(sysconfdir)/qemu"
install-confdir:
$(INSTALL_DIR) "$(DESTDIR)$(qemu_confdir)"
install-sysconfig: install-datadir install-confdir
$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/target-x86_64.conf "$(DESTDIR)$(qemu_confdir)"
$(INSTALL_DATA) $(SRC_PATH)/sysconfigs/target/cpus-x86_64.conf "$(DESTDIR)$(qemu_datadir)"
install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig install-datadir
install: all $(if $(BUILD_DOCS),install-doc) install-sysconfig
$(INSTALL_DIR) "$(DESTDIR)$(bindir)"
ifneq ($(TOOLS),)
$(INSTALL_PROG) $(STRIP_OPT) $(TOOLS) "$(DESTDIR)$(bindir)"
endif
ifneq ($(HELPERS-y),)
$(INSTALL_DIR) "$(DESTDIR)$(libexecdir)"
$(INSTALL_PROG) $(STRIP_OPT) $(HELPERS-y) "$(DESTDIR)$(libexecdir)"
endif
ifneq ($(BLOBS),)
$(INSTALL_DIR) "$(DESTDIR)$(datadir)"
set -e; for x in $(BLOBS); do \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(datadir)"; \
done
endif
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/keymaps"
set -e; for x in $(KEYMAPS); do \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(qemu_datadir)/keymaps"; \
$(INSTALL_DATA) $(SRC_PATH)/pc-bios/keymaps/$$x "$(DESTDIR)$(datadir)/keymaps"; \
done
for d in $(TARGET_DIRS); do \
$(MAKE) -C $$d $@ || exit 1 ; \
@@ -316,7 +306,13 @@ endif
# various test targets
test speed: all
$(MAKE) -C tests/tcg $@
$(MAKE) -C tests $@
.PHONY: check
check: $(patsubst %,run-check-%,$(CHECKS))
run-check-%: %
./$<
.PHONY: TAGS
TAGS:
@@ -335,7 +331,7 @@ TEXIFLAG=$(if $(V),,--quiet)
$(call quiet-command,texi2dvi $(TEXIFLAG) -I . $<," GEN $@")
%.html: %.texi
$(call quiet-command,LC_ALL=C $(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \
$(call quiet-command,$(MAKEINFO) $(MAKEINFOFLAGS) --html $< -o $@, \
" GEN $@")
%.info: %.texi
@@ -359,25 +355,19 @@ qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
$(POD2MAN) --section=1 --center=" " --release=" " qemu.pod > $@, \
pod2man --section=1 --center=" " --release=" " qemu.pod > $@, \
" GEN $@")
qemu-img.1: qemu-img.texi qemu-img-cmds.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-img.pod && \
$(POD2MAN) --section=1 --center=" " --release=" " qemu-img.pod > $@, \
" GEN $@")
fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< fsdev/virtfs-proxy-helper.pod && \
$(POD2MAN) --section=1 --center=" " --release=" " fsdev/virtfs-proxy-helper.pod > $@, \
pod2man --section=1 --center=" " --release=" " qemu-img.pod > $@, \
" GEN $@")
qemu-nbd.8: qemu-nbd.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu-nbd.pod && \
$(POD2MAN) --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
" GEN $@")
dvi: qemu-doc.dvi qemu-tech.dvi

View File

@@ -10,7 +10,7 @@ include $(SRC_PATH)/rules.mak
$(call set-vpath, $(SRC_PATH):$(SRC_PATH)/hw)
QEMU_CFLAGS+=-I..
QEMU_CFLAGS += -I$(SRC_PATH)/include
QEMU_CFLAGS += $(GLIB_CFLAGS)
include $(SRC_PATH)/Makefile.objs

View File

@@ -1,23 +1,9 @@
#######################################################################
# Target-independent parts used in system and user emulation
universal-obj-y =
#######################################################################
# QObject
qobject-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
qobject-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
qobject-obj-y += qerror.o error.o qemu-error.o
universal-obj-y += $(qobject-obj-y)
#######################################################################
# QOM
include $(SRC_PATH)/qom/Makefile
qom-obj-y = $(addprefix qom/, $(qom-y))
qom-obj-twice-y = $(addprefix qom/, $(qom-twice-y))
universal-obj-y += $(qom-obj-y)
#######################################################################
# oslib-obj-y is code depending on the OS (win32 vs posix)
oslib-obj-y = osdep.o
@@ -26,17 +12,12 @@ oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
#######################################################################
# coroutines
coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o qemu-coroutine-io.o
coroutine-obj-y += qemu-coroutine-sleep.o
coroutine-obj-y = qemu-coroutine.o qemu-coroutine-lock.o
ifeq ($(CONFIG_UCONTEXT_COROUTINE),y)
coroutine-obj-$(CONFIG_POSIX) += coroutine-ucontext.o
else
ifeq ($(CONFIG_SIGALTSTACK_COROUTINE),y)
coroutine-obj-$(CONFIG_POSIX) += coroutine-sigaltstack.o
else
coroutine-obj-$(CONFIG_POSIX) += coroutine-gthread.o
endif
endif
coroutine-obj-$(CONFIG_WIN32) += coroutine-win32.o
#######################################################################
@@ -53,7 +34,6 @@ block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow
block-nested-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o
block-nested-y += qed-check.o
block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o blkverify.o
block-nested-y += stream.o
block-nested-$(CONFIG_WIN32) += raw-win32.o
block-nested-$(CONFIG_POSIX) += raw-posix.o
block-nested-$(CONFIG_LIBISCSI) += iscsi.o
@@ -81,20 +61,21 @@ ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy)
# Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add.
# only pull in the actual virtio-9p device if we also enabled virtio.
CONFIG_REALLY_VIRTFS=y
fsdev-nested-y = qemu-fsdev.o virtio-9p-marshal.o
fsdev-nested-y = qemu-fsdev.o
else
fsdev-nested-y = qemu-fsdev-dummy.o
endif
fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
######################################################################
# Target independent part of system emulation. The long term path is to
# suppress *all* target specific code in case of system emulation, i.e. a
# single QEMU executable should support all CPUs and machines.
# libqemu_common.a: Target independent part of system emulation. The
# long term path is to suppress *all* target specific code in case of
# system emulation, i.e. a single QEMU executable should support all
# CPUs and machines.
common-obj-y = $(block-obj-y) blockdev.o
common-obj-y += $(net-obj-y)
common-obj-y += $(qom-obj-twice-y)
common-obj-y += $(qobject-obj-y)
common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
common-obj-y += readline.o console.o cursor.o
common-obj-y += $(oslib-obj-y)
@@ -121,19 +102,17 @@ common-obj-y += eeprom93xx.o
common-obj-y += scsi-disk.o cdrom.o
common-obj-y += scsi-generic.o scsi-bus.o
common-obj-y += hid.o
common-obj-y += usb/core.o usb/bus.o usb/desc.o usb/dev-hub.o
common-obj-y += usb/host-$(HOST_USB).o
common-obj-y += usb/dev-hid.o usb/dev-storage.o usb/dev-wacom.o
common-obj-y += usb/dev-serial.o usb/dev-network.o usb/dev-audio.o
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
common-obj-$(CONFIG_SSI) += ssi.o
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
common-obj-$(CONFIG_SD) += sd.o
common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o
common-obj-y += bt-hci-csr.o usb/dev-bluetooth.o
common-obj-y += bt.o bt-host.o bt-vhci.o bt-l2cap.o bt-sdp.o bt-hci.o bt-hid.o usb-bt.o
common-obj-y += bt-hci-csr.o
common-obj-y += buffered_file.o migration.o migration-tcp.o
common-obj-y += qemu-char.o #aio.o
common-obj-y += qemu-char.o savevm.o #aio.o
common-obj-y += msmouse.o ps2.o
common-obj-y += qdev.o qdev-properties.o qdev-monitor.o
common-obj-y += qdev.o qdev-properties.o
common-obj-y += block-migration.o iohandler.o
common-obj-y += pflib.o
common-obj-y += bitmap.o bitops.o
@@ -199,10 +178,7 @@ user-obj-y =
user-obj-y += envlist.o path.o
user-obj-y += tcg-runtime.o host-utils.o
user-obj-y += cutils.o cache-utils.o
user-obj-y += module.o
user-obj-y += qemu-user.o
user-obj-y += $(trace-obj-y)
user-obj-y += $(qom-obj-twice-y)
######################################################################
# libhw
@@ -210,13 +186,11 @@ user-obj-y += $(qom-obj-twice-y)
hw-obj-y =
hw-obj-y += vl.o loader.o
hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
hw-obj-y += usb/libhw.o
hw-obj-y += usb-libhw.o
hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
hw-obj-y += fw_cfg.o
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
hw-obj-$(CONFIG_PCI) += msix.o msi.o
hw-obj-$(CONFIG_PCI) += shpc.o
hw-obj-$(CONFIG_PCI) += slotid_cap.o
hw-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o
hw-obj-$(CONFIG_PCI) += ioh3420.o xio3130_upstream.o xio3130_downstream.o
hw-obj-y += watchdog.o
@@ -232,28 +206,25 @@ hw-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
hw-obj-$(CONFIG_SERIAL) += serial.o
hw-obj-$(CONFIG_PARALLEL) += parallel.o
hw-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
hw-obj-$(CONFIG_I8254) += i8254.o
hw-obj-$(CONFIG_PCSPK) += pcspk.o
hw-obj-$(CONFIG_PCKBD) += pckbd.o
hw-obj-$(CONFIG_USB_UHCI) += usb/hcd-uhci.o
hw-obj-$(CONFIG_USB_OHCI) += usb/hcd-ohci.o
hw-obj-$(CONFIG_USB_EHCI) += usb/hcd-ehci.o
hw-obj-$(CONFIG_USB_XHCI) += usb/hcd-xhci.o
hw-obj-$(CONFIG_USB_UHCI) += usb-uhci.o
hw-obj-$(CONFIG_USB_OHCI) += usb-ohci.o
hw-obj-$(CONFIG_USB_EHCI) += usb-ehci.o
hw-obj-$(CONFIG_FDC) += fdc.o
hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
hw-obj-$(CONFIG_DMA) += dma.o
hw-obj-$(CONFIG_I82374) += i82374.o
hw-obj-$(CONFIG_HPET) += hpet.o
hw-obj-$(CONFIG_APPLESMC) += applesmc.o
hw-obj-$(CONFIG_SMARTCARD) += usb/dev-smartcard-reader.o ccid-card-passthru.o
hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
hw-obj-$(CONFIG_SMARTCARD_NSS) += ccid-card-emulated.o
hw-obj-$(CONFIG_USB_REDIR) += usb/redirect.o
hw-obj-$(CONFIG_I8259) += i8259_common.o i8259.o
hw-obj-$(CONFIG_USB_REDIR) += usb-redir.o
hw-obj-$(CONFIG_I8259) += i8259.o
# PPC devices
hw-obj-$(CONFIG_PREP_PCI) += prep_pci.o
hw-obj-$(CONFIG_I82378) += i82378.o
# Mac shared devices
hw-obj-$(CONFIG_MACIO) += macio.o
hw-obj-$(CONFIG_CUDA) += cuda.o
@@ -272,7 +243,6 @@ hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
# MIPS devices
hw-obj-$(CONFIG_PIIX4) += piix4.o
hw-obj-$(CONFIG_G364FB) += g364fb.o
hw-obj-$(CONFIG_JAZZ_LED) += jazz_led.o
# PCI watchdog devices
hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
@@ -317,15 +287,12 @@ hw-obj-$(CONFIG_VGA_ISA) += vga-isa.o
hw-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o
hw-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o
hw-obj-$(CONFIG_VMMOUSE) += vmmouse.o
hw-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o
hw-obj-$(CONFIG_RC4030) += rc4030.o
hw-obj-$(CONFIG_DP8393X) += dp8393x.o
hw-obj-$(CONFIG_DS1225Y) += ds1225y.o
hw-obj-$(CONFIG_MIPSNET) += mipsnet.o
hw-obj-y += qtest.o
# Sound
sound-obj-y =
sound-obj-$(CONFIG_SB16) += sb16.o
@@ -345,9 +312,10 @@ hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-coth.o cofs.o codir.o cofile.o
9pfs-nested-$(CONFIG_VIRTFS) += coxattr.o virtio-9p-synth.o
9pfs-nested-$(CONFIG_OPEN_BY_HANDLE) += virtio-9p-handle.o
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-proxy.o
hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
$(addprefix 9pfs/, $(9pfs-nested-y)): QEMU_CFLAGS+=$(GLIB_CFLAGS)
######################################################################
# libdis
@@ -367,28 +335,22 @@ libdis-$(CONFIG_PPC_DIS) += ppc-dis.o
libdis-$(CONFIG_S390_DIS) += s390-dis.o
libdis-$(CONFIG_SH4_DIS) += sh4-dis.o
libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o
libdis-$(CONFIG_LM32_DIS) += lm32-dis.o
######################################################################
# trace
ifeq ($(TRACE_BACKEND),dtrace)
TRACE_H_EXTRA_DEPS=trace-dtrace.h
trace.h: trace.h-timestamp trace-dtrace.h
else
trace.h: trace.h-timestamp
endif
trace.h: trace.h-timestamp $(TRACE_H_EXTRA_DEPS)
trace.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--format=h \
--backend=$(TRACE_BACKEND) \
< $< > $@," GEN trace.h")
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h")
@cmp -s $@ trace.h || cp $@ trace.h
trace.c: trace.c-timestamp
trace.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--format=c \
--backend=$(TRACE_BACKEND) \
< $< > $@," GEN trace.c")
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -c < $< > $@," GEN trace.c")
@cmp -s $@ trace.c || cp $@ trace.c
trace.o: trace.c $(GENERATED_HEADERS)
@@ -401,14 +363,11 @@ trace-dtrace.h: trace-dtrace.dtrace
# rule file. So we use '.dtrace' instead
trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp
trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak
$(call quiet-command,$(TRACETOOL) \
--format=d \
--backend=$(TRACE_BACKEND) \
< $< > $@," GEN trace-dtrace.dtrace")
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace")
@cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace
trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS)
$(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o")
$(call quiet-command,dtrace -o $@ -G -s $<, " GEN trace-dtrace.o")
ifeq ($(LIBTOOL),)
trace-dtrace.lo: trace-dtrace.dtrace
@@ -446,26 +405,21 @@ libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o
######################################################################
# qapi
qapi-nested-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o
qapi-nested-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
qapi-nested-y += string-input-visitor.o string-output-visitor.o
qapi-nested-y = qapi-visit-core.o qmp-input-visitor.o qmp-output-visitor.o qapi-dealloc-visitor.o
qapi-nested-y += qmp-registry.o qmp-dispatch.o
qapi-obj-y = $(addprefix qapi/, $(qapi-nested-y))
common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o
common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o $(qapi-obj-y)
common-obj-y += qmp.o hmp.o
universal-obj-y += $(qapi-obj-y)
######################################################################
# guest agent
qga-nested-y = commands.o guest-agent-command-state.o
qga-nested-$(CONFIG_POSIX) += commands-posix.o channel-posix.o
qga-nested-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o
qga-nested-y = guest-agent-commands.o guest-agent-command-state.o
qga-obj-y = $(addprefix qga/, $(qga-nested-y))
qga-obj-y += qemu-ga.o module.o
qga-obj-y += qemu-ga.o qemu-sockets.o module.o qemu-option.o
qga-obj-$(CONFIG_WIN32) += oslib-win32.o
qga-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-sockets.o qemu-option.o
qga-obj-$(CONFIG_POSIX) += oslib-posix.o
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)

View File

@@ -22,24 +22,15 @@ QEMU_CFLAGS += -I.. -I$(TARGET_PATH) -DNEED_CPU_H
include $(SRC_PATH)/Makefile.objs
QEMU_CFLAGS+=-I$(SRC_PATH)/include
ifdef CONFIG_USER_ONLY
# user emulator name
QEMU_PROG=qemu-$(TARGET_ARCH2)
else
# system emulator name
ifneq (,$(findstring -mwindows,$(LIBS)))
# Terminate program name with a 'w' because the linker builds a windows executable.
QEMU_PROGW=qemu-system-$(TARGET_ARCH2)w$(EXESUF)
endif # windows executable
QEMU_PROG=qemu-system-$(TARGET_ARCH2)$(EXESUF)
endif
PROGS=$(QEMU_PROG)
ifdef QEMU_PROGW
PROGS+=$(QEMU_PROGW)
endif
STPFILES=
ifndef CONFIG_HAIKU
@@ -58,14 +49,13 @@ else
TARGET_TYPE=system
endif
$(QEMU_PROG).stp: $(SRC_PATH)/trace-events
$(call quiet-command,$(TRACETOOL) \
--format=stap \
--backend=$(TRACE_BACKEND) \
--binary=$(bindir)/$(QEMU_PROG) \
--target-arch=$(TARGET_ARCH) \
--target-type=$(TARGET_TYPE) \
< $< > $@," GEN $(QEMU_PROG).stp")
$(QEMU_PROG).stp:
$(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \
--$(TRACE_BACKEND) \
--binary $(bindir)/$(QEMU_PROG) \
--target-arch $(TARGET_ARCH) \
--target-type $(TARGET_TYPE) \
--stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp")
else
stap:
endif
@@ -81,24 +71,19 @@ libobj-y = exec.o translate-all.o cpu-exec.o translate.o
libobj-y += tcg/tcg.o tcg/optimize.o
libobj-$(CONFIG_TCG_INTERPRETER) += tci.o
libobj-y += fpu/softfloat.o
ifneq ($(TARGET_BASE_ARCH), sparc)
ifneq ($(TARGET_BASE_ARCH), alpha)
libobj-y += op_helper.o
endif
endif
libobj-y += helper.o
ifneq ($(TARGET_BASE_ARCH), ppc)
libobj-y += cpu.o
libobj-y += op_helper.o helper.o
ifeq ($(TARGET_BASE_ARCH), i386)
libobj-y += cpuid.o
endif
libobj-$(TARGET_SPARC64) += vis_helper.o
libobj-$(CONFIG_NEED_MMU) += mmu.o
libobj-$(TARGET_ARM) += neon_helper.o iwmmxt_helper.o
ifeq ($(TARGET_BASE_ARCH), sparc)
libobj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o
libobj-y += cpu_init.o
endif
libobj-$(TARGET_SPARC) += int32_helper.o
libobj-$(TARGET_SPARC64) += int64_helper.o
libobj-$(TARGET_ALPHA) += int_helper.o fpu_helper.o sys_helper.o mem_helper.o
libobj-y += disas.o
libobj-$(CONFIG_TCI_DIS) += tci-dis.o
@@ -107,12 +92,17 @@ tci-dis.o: QEMU_CFLAGS += -I$(SRC_PATH)/tcg -I$(SRC_PATH)/tcg/tci
$(libobj-y): $(GENERATED_HEADERS)
# HELPER_CFLAGS is used for all the legacy code compiled with static register
# libqemu
translate.o: translate.c cpu.h
translate-all.o: translate-all.c cpu.h
tcg/tcg.o: cpu.h
# HELPER_CFLAGS is used for all the code compiled with static register
# variables
ifneq ($(TARGET_BASE_ARCH), sparc)
op_helper.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
endif
user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
op_helper.o ldst_helper.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
# Note: this is a workaround. The real fix is to avoid compiling
# cpu_signal_handler() in user-exec.c.
@@ -145,13 +135,39 @@ obj-m68k-y += m68k-sim.o m68k-semi.o
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
obj-y += $(addprefix ../, $(universal-obj-y))
obj-y += $(addprefix ../libuser/, $(user-obj-y))
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
obj-y += $(libobj-y)
endif #CONFIG_LINUX_USER
#########################################################
# Darwin user emulator target
ifdef CONFIG_DARWIN_USER
$(call set-vpath, $(SRC_PATH)/darwin-user)
QEMU_CFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH)
# Leave some space for the regular program loading zone
LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
LIBS+=-lmx
obj-y = main.o commpage.o machload.o mmap.o signal.o syscall.o thunk.o \
gdbstub.o user-exec.o
obj-i386-y += ioport-user.o
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
obj-y += $(addprefix ../libuser/, $(user-obj-y))
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
obj-y += $(libobj-y)
endif #CONFIG_DARWIN_USER
#########################################################
# BSD user emulator target
@@ -168,7 +184,6 @@ obj-i386-y += ioport-user.o
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
obj-y += $(addprefix ../, $(universal-obj-y))
obj-y += $(addprefix ../libuser/, $(user-obj-y))
obj-y += $(addprefix ../libdis-user/, $(libdis-y))
obj-y += $(libobj-y)
@@ -184,28 +199,25 @@ obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o ioport.o
# need to fix this properly
obj-$(CONFIG_NO_PCI) += pci-stub.o
obj-$(CONFIG_VIRTIO) += virtio.o virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
obj-$(CONFIG_VIRTIO) += virtio-scsi.o
obj-y += vhost_net.o
obj-$(CONFIG_VHOST_NET) += vhost.o
obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
obj-$(CONFIG_NO_KVM) += kvm-stub.o
obj-$(CONFIG_VGA) += vga.o
obj-y += memory.o savevm.o cputlb.o
obj-y += memory.o
LIBS+=-lz
obj-i386-$(CONFIG_KVM) += hyperv.o
QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
QEMU_CFLAGS += $(VNC_SASL_CFLAGS)
QEMU_CFLAGS += $(VNC_JPEG_CFLAGS)
QEMU_CFLAGS += $(VNC_PNG_CFLAGS)
QEMU_CFLAGS += $(GLIB_CFLAGS)
# xen support
obj-$(CONFIG_XEN) += xen-all.o xen_machine_pv.o xen_domainbuild.o xen-mapcache.o
obj-$(CONFIG_NO_XEN) += xen-stub.o
obj-i386-$(CONFIG_XEN) += xen_platform.o xen_apic.o
obj-i386-$(CONFIG_XEN) += xen_platform.o
# Inter-VM PCI shared memory
CONFIG_IVSHMEM =
@@ -216,23 +228,20 @@ ifeq ($(CONFIG_KVM), y)
endif
obj-$(CONFIG_IVSHMEM) += ivshmem.o
# Generic hotplugging
obj-y += device-hotplug.o
# Hardware support
obj-i386-y += vga.o
obj-i386-y += mc146818rtc.o pc.o
obj-i386-y += apic_common.o apic.o kvmvapic.o
obj-i386-y += sga.o ioapic_common.o ioapic.o piix_pci.o
obj-i386-y += cirrus_vga.o sga.o apic.o ioapic.o piix_pci.o
obj-i386-y += vmport.o
obj-i386-y += pci-hotplug.o smbios.o wdt_ib700.o
obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
obj-i386-y += debugcon.o multiboot.o
obj-i386-y += pc_piix.o
obj-i386-y += pc_sysfw.o
obj-i386-$(CONFIG_KVM) += kvm/clock.o kvm/apic.o kvm/i8259.o kvm/ioapic.o kvm/i8254.o
obj-i386-$(CONFIG_KVM) += kvmclock.o
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
# shared objects
obj-ppc-y = ppc.o ppc_booke.o
obj-ppc-y += vga.o
# PREP target
obj-ppc-y += mc146818rtc.o
obj-ppc-y += ppc_prep.o
@@ -246,7 +255,7 @@ obj-ppc-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
obj-ppc-$(CONFIG_PSERIES) += spapr_pci.o device-hotplug.o pci-hotplug.o
# PowerPC 4xx boards
obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
obj-ppc-y += ppc440_bamboo.o
obj-ppc-y += ppc440.o ppc440_bamboo.o
# PowerPC E500 boards
obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o ppce500_spin.o
# PowerPC 440 Xilinx ML507 reference board.
@@ -286,12 +295,14 @@ obj-lm32-y += framebuffer.o
obj-mips-y = mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
obj-mips-y += mips_addr.o mips_timer.o mips_int.o
obj-mips-y += vga.o
obj-mips-y += jazz_led.o
obj-mips-y += gt64xxx.o mc146818rtc.o
obj-mips-y += cirrus_vga.o
obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o
obj-microblaze-y = petalogix_s3adsp1800_mmu.o
obj-microblaze-y += petalogix_ml605_mmu.o
obj-microblaze-y += microblaze_boot.o
obj-microblaze-y += microblaze_pic_cpu.o
obj-microblaze-y += xilinx_intc.o
@@ -317,7 +328,9 @@ obj-cris-y += etraxfs_ser.o
ifeq ($(TARGET_ARCH), sparc64)
obj-sparc-y = sun4u.o apb_pci.o
obj-sparc-y += vga.o
obj-sparc-y += mc146818rtc.o
obj-sparc-y += cirrus_vga.o
else
obj-sparc-y = sun4m.o lance.o tcx.o sun4m_iommu.o slavio_intctl.o
obj-sparc-y += slavio_timer.o slavio_misc.o sparc32_dma.o
@@ -330,22 +343,9 @@ endif
obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
obj-arm-y += versatile_pci.o
obj-arm-y += versatile_i2c.o
obj-arm-y += cadence_uart.o
obj-arm-y += cadence_ttc.o
obj-arm-y += cadence_gem.o
obj-arm-y += xilinx_zynq.o zynq_slcr.o
obj-arm-y += arm_gic.o
obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
obj-arm-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o
obj-arm-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o
obj-arm-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
obj-arm-y += arm_l2x0.o
obj-arm-y += arm_mptimer.o a15mpcore.o
obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
obj-arm-y += highbank.o
obj-arm-y += pl061.o
obj-arm-y += xgmac.o
obj-arm-y += arm-semi.o
obj-arm-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
obj-arm-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
@@ -356,16 +356,18 @@ obj-arm-y += omap1.o omap_lcdc.o omap_dma.o omap_clk.o omap_mmc.o omap_i2c.o \
obj-arm-y += omap2.o omap_dss.o soc_dma.o omap_gptimer.o omap_synctimer.o \
omap_gpmc.o omap_sdrc.o omap_spi.o omap_tap.o omap_l4.o
obj-arm-y += omap_sx1.o palm.o tsc210x.o
obj-arm-y += nseries.o blizzard.o onenand.o cbus.o tusb6010.o usb/hcd-musb.o
obj-arm-y += nseries.o blizzard.o onenand.o vga.o cbus.o tusb6010.o usb-musb.o
obj-arm-y += mst_fpga.o mainstone.o
obj-arm-y += z2.o
obj-arm-y += musicpal.o bitbang_i2c.o marvell_88w8618_audio.o
obj-arm-y += framebuffer.o
obj-arm-y += syborg.o syborg_fb.o syborg_interrupt.o syborg_keyboard.o
obj-arm-y += syborg_serial.o syborg_timer.o syborg_pointer.o syborg_rtc.o
obj-arm-y += syborg_virtio.o
obj-arm-y += vexpress.o
obj-arm-y += strongarm.o
obj-arm-y += collie.o
obj-arm-y += pl041.o lm4549.o
obj-arm-$(CONFIG_FDT) += device_tree.o
obj-sh4-y = shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
obj-sh4-y += sh_timer.o sh_serial.o sh_intc.o sh_pci.o sm501.o
@@ -377,6 +379,7 @@ obj-m68k-y += m68k-semi.o dummy_m68k.o
obj-s390x-y = s390-virtio-bus.o s390-virtio.o
obj-alpha-y = mc146818rtc.o
obj-alpha-y += vga.o cirrus_vga.o
obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o
obj-xtensa-y += xtensa_pic.o
@@ -384,7 +387,6 @@ obj-xtensa-y += xtensa_sim.o
obj-xtensa-y += xtensa_lx60.o
obj-xtensa-y += xtensa-semi.o
obj-xtensa-y += core-dc232b.o
obj-xtensa-y += core-dc233c.o
obj-xtensa-y += core-fsf.o
main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
@@ -393,7 +395,6 @@ monitor.o: hmp-commands.h qmp-commands-old.h
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
obj-y += $(addprefix ../, $(universal-obj-y))
obj-y += $(addprefix ../, $(common-obj-y))
obj-y += $(addprefix ../libdis/, $(libdis-y))
obj-y += $(libobj-y)
@@ -412,16 +413,9 @@ endif # CONFIG_LINUX_USER
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
ifdef QEMU_PROGW
# The linker builds a windows executable. Make also a console executable.
$(QEMU_PROGW): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
$(call LINK,$^)
$(QEMU_PROG): $(QEMU_PROGW)
$(call quiet-command,$(OBJCOPY) --subsystem console $(QEMU_PROGW) $(QEMU_PROG)," GEN $(TARGET_DIR)$(QEMU_PROG)")
else
$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
$(call LINK,$^)
endif
gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/scripts/feature_to_c.sh
$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/scripts/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@")
@@ -434,7 +428,7 @@ qmp-commands-old.h: $(SRC_PATH)/qmp-commands.hx
clean:
rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
rm -f *.d */*.d tcg/*.o ide/*.o 9pfs/*.o kvm/*.o
rm -f *.d */*.d tcg/*.o ide/*.o 9pfs/*.o
rm -f hmp-commands.h qmp-commands-old.h gdbstub-xml.c
ifdef CONFIG_TRACE_SYSTEMTAP
rm -f *.stp
@@ -448,8 +442,8 @@ ifneq ($(STRIP),)
endif
endif
ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset"
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/../systemtap/tapset"
$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(datadir)/../systemtap/tapset"
endif
# Include automatically generated dependency files

View File

@@ -9,7 +9,6 @@ include $(SRC_PATH)/rules.mak
$(call set-vpath, $(SRC_PATH))
QEMU_CFLAGS+=-I..
QEMU_CFLAGS += -I$(SRC_PATH)/include
include $(SRC_PATH)/Makefile.objs

126
QMP/qmp
View File

@@ -1,126 +0,0 @@
#!/usr/bin/python
#
# QMP command line tool
#
# Copyright IBM, Corp. 2011
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.com>
#
# This work is licensed under the terms of the GNU GPLv2 or later.
# See the COPYING file in the top-level directory.
import sys, os
from qmp import QEMUMonitorProtocol
def print_response(rsp, prefix=[]):
if type(rsp) == list:
i = 0
for item in rsp:
if prefix == []:
prefix = ['item']
print_response(item, prefix[:-1] + ['%s[%d]' % (prefix[-1], i)])
i += 1
elif type(rsp) == dict:
for key in rsp.keys():
print_response(rsp[key], prefix + [key])
else:
if len(prefix):
print '%s: %s' % ('.'.join(prefix), rsp)
else:
print '%s' % (rsp)
def main(args):
path = None
# Use QMP_PATH if it's set
if os.environ.has_key('QMP_PATH'):
path = os.environ['QMP_PATH']
while len(args):
arg = args[0]
if arg.startswith('--'):
arg = arg[2:]
if arg.find('=') == -1:
value = True
else:
arg, value = arg.split('=', 1)
if arg in ['path']:
if type(value) == str:
path = value
elif arg in ['help']:
os.execlp('man', 'man', 'qmp')
else:
print 'Unknown argument "%s"' % arg
args = args[1:]
else:
break
if not path:
print "QMP path isn't set, use --path=qmp-monitor-address or set QMP_PATH"
return 1
if len(args):
command, args = args[0], args[1:]
else:
print 'No command found'
print 'Usage: "qmp [--path=qmp-monitor-address] qmp-cmd arguments"'
return 1
if command in ['help']:
os.execlp('man', 'man', 'qmp')
srv = QEMUMonitorProtocol(path)
srv.connect()
def do_command(srv, cmd, **kwds):
rsp = srv.cmd(cmd, kwds)
if rsp.has_key('error'):
raise Exception(rsp['error']['desc'])
return rsp['return']
commands = map(lambda x: x['name'], do_command(srv, 'query-commands'))
srv.close()
if command not in commands:
fullcmd = 'qmp-%s' % command
try:
os.environ['QMP_PATH'] = path
os.execvp(fullcmd, [fullcmd] + args)
except OSError, (errno, msg):
if errno == 2:
print 'Command "%s" not found.' % (fullcmd)
return 1
raise
return 0
srv = QEMUMonitorProtocol(path)
srv.connect()
arguments = {}
for arg in args:
if not arg.startswith('--'):
print 'Unknown argument "%s"' % arg
return 1
arg = arg[2:]
if arg.find('=') == -1:
value = True
else:
arg, value = arg.split('=', 1)
if arg in ['help']:
os.execlp('man', 'man', 'qmp-%s' % command)
return 1
arguments[arg] = value
rsp = do_command(srv, command, **arguments)
print_response(rsp)
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))

View File

@@ -26,24 +26,6 @@ Example:
Note: If action is "stop", a STOP event will eventually follow the
BLOCK_IO_ERROR event.
DEVICE_TRAY_MOVED
-----------------
It's emitted whenever the tray of a removable device is moved by the guest
or by HMP/QMP commands.
Data:
- "device": device name (json-string)
- "tray-open": true if the tray has been opened or false if it has been closed
(json-bool)
{ "event": "DEVICE_TRAY_MOVED",
"data": { "device": "ide1-cd0",
"tray-open": true
},
"timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
RESET
-----
@@ -144,7 +126,7 @@ the authentication ID is not provided.
VNC_DISCONNECTED
----------------
Emitted when the connection is closed.
Emitted when the conection is closed.
Data:
@@ -282,56 +264,3 @@ Example:
Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
followed respectively by the RESET, SHUTDOWN, or STOP events.
BLOCK_JOB_COMPLETED
-------------------
Emitted when a block job has completed.
Data:
- "type": Job type ("stream" for image streaming, json-string)
- "device": Device name (json-string)
- "len": Maximum progress value (json-int)
- "offset": Current progress value (json-int)
On success this is equal to len.
On failure this is less than len.
- "speed": Rate limit, bytes per second (json-int)
- "error": Error message (json-string, optional)
Only present on failure. This field contains a human-readable
error message. There are no semantics other than that streaming
has failed and clients should not try to interpret the error
string.
Example:
{ "event": "BLOCK_JOB_COMPLETED",
"data": { "type": "stream", "device": "virtio-disk0",
"len": 10737418240, "offset": 10737418240,
"speed": 0 },
"timestamp": { "seconds": 1267061043, "microseconds": 959568 } }
BLOCK_JOB_CANCELLED
-------------------
Emitted when a block job has been cancelled.
Data:
- "type": Job type ("stream" for image streaming, json-string)
- "device": Device name (json-string)
- "len": Maximum progress value (json-int)
- "offset": Current progress value (json-int)
On success this is equal to len.
On failure this is less than len.
- "speed": Rate limit, bytes per second (json-int)
Example:
{ "event": "BLOCK_JOB_CANCELLED",
"data": { "type": "stream", "device": "virtio-disk0",
"len": 10737418240, "offset": 134217728,
"speed": 0 },
"timestamp": { "seconds": 1267061043, "microseconds": 959568 } }

View File

@@ -209,27 +209,13 @@ incompatible way are disabled by default and will be advertised by the
capabilities array (section '2.2 Server Greeting'). Thus, Clients can check
that array and enable the capabilities they support.
The QMP Server performs a type check on the arguments to a command. It
generates an error if a value does not have the expected type for its
key, or if it does not understand a key that the Client included. The
strictness of the Server catches wrong assumptions of Clients about
the Server's schema. Clients can assume that, when such validation
errors occur, they will be reported before the command generated any
side effect.
Additionally, Clients must not assume any particular:
However, Clients must not assume any particular:
- Length of json-arrays
- Size of json-objects; in particular, future versions of QEMU may add
new keys and Clients should be able to ignore them.
- Size of json-objects or length of json-arrays
- Order of json-object members or json-array elements
- Amount of errors generated by a command, that is, new errors can be added
to any existing command in newer versions of the Server
Of course, the Server does guarantee to send valid JSON. But apart from
this, a Client should be "conservative in what they send, and liberal in
what they accept".
6. Downstream extension of QMP
------------------------------

View File

@@ -128,12 +128,6 @@ class QEMUMonitorProtocol:
qmp_cmd['id'] = id
return self.cmd_obj(qmp_cmd)
def command(self, cmd, **kwds):
ret = self.cmd(cmd, kwds)
if ret.has_key('error'):
raise Exception(ret['error']['desc'])
return ret['return']
def get_events(self, wait=False):
"""
Get a list of available QMP events.

View File

@@ -1,138 +0,0 @@
#!/usr/bin/python
##
# QEMU Object Model test tools
#
# Copyright IBM, Corp. 2012
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.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.
##
import fuse, stat
from fuse import Fuse
import os, posix
from errno import *
from qmp import QEMUMonitorProtocol
fuse.fuse_python_api = (0, 2)
class QOMFS(Fuse):
def __init__(self, qmp, *args, **kwds):
Fuse.__init__(self, *args, **kwds)
self.qmp = qmp
self.qmp.connect()
self.ino_map = {}
self.ino_count = 1
def get_ino(self, path):
if self.ino_map.has_key(path):
return self.ino_map[path]
self.ino_map[path] = self.ino_count
self.ino_count += 1
return self.ino_map[path]
def is_object(self, path):
try:
items = self.qmp.command('qom-list', path=path)
return True
except:
return False
def is_property(self, path):
try:
path, prop = path.rsplit('/', 1)
for item in self.qmp.command('qom-list', path=path):
if item['name'] == prop:
return True
return False
except:
return False
def is_link(self, path):
try:
path, prop = path.rsplit('/', 1)
for item in self.qmp.command('qom-list', path=path):
if item['name'] == prop:
if item['type'].startswith('link<'):
return True
return False
return False
except:
return False
def read(self, path, length, offset):
if not self.is_property(path):
return -ENOENT
path, prop = path.rsplit('/', 1)
try:
data = str(self.qmp.command('qom-get', path=path, property=prop))
data += '\n' # make values shell friendly
except:
return -EPERM
if offset > len(data):
return ''
return str(data[offset:][:length])
def readlink(self, path):
if not self.is_link(path):
return False
path, prop = path.rsplit('/', 1)
prefix = '/'.join(['..'] * (len(path.split('/')) - 1))
return prefix + str(self.qmp.command('qom-get', path=path,
property=prop))
def getattr(self, path):
if self.is_link(path):
value = posix.stat_result((0755 | stat.S_IFLNK,
self.get_ino(path),
0,
2,
1000,
1000,
4096,
0,
0,
0))
elif self.is_object(path):
value = posix.stat_result((0755 | stat.S_IFDIR,
self.get_ino(path),
0,
2,
1000,
1000,
4096,
0,
0,
0))
elif self.is_property(path):
value = posix.stat_result((0644 | stat.S_IFREG,
self.get_ino(path),
0,
1,
1000,
1000,
4096,
0,
0,
0))
else:
value = -ENOENT
return value
def readdir(self, path, offset):
yield fuse.Direntry('.')
yield fuse.Direntry('..')
for item in self.qmp.command('qom-list', path=path):
yield fuse.Direntry(str(item['name']))
if __name__ == '__main__':
import sys, os
fs = QOMFS(QEMUMonitorProtocol(os.environ['QMP_SOCKET']))
fs.main(sys.argv)

View File

@@ -1,67 +0,0 @@
#!/usr/bin/python
##
# QEMU Object Model test tools
#
# Copyright IBM, Corp. 2011
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.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.
##
import sys
import os
from qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None
path = None
prop = None
def usage():
return '''environment variables:
QMP_SOCKET=<path | addr:port>
usage:
%s [-h] [-s <QMP socket path | addr:port>] <path>.<property>
''' % cmd
def usage_error(error_msg = "unspecified error"):
sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
exit(1)
if len(args) > 0:
if args[0] == "-h":
print usage()
exit(0);
elif args[0] == "-s":
try:
socket_path = args[1]
except:
usage_error("missing argument: QMP socket path or address");
args = args[2:]
if not socket_path:
if os.environ.has_key('QMP_SOCKET'):
socket_path = os.environ['QMP_SOCKET']
else:
usage_error("no QMP socket path or address given");
if len(args) > 0:
try:
path, prop = args[0].rsplit('.', 1)
except:
usage_error("invalid format for path/property/value")
else:
usage_error("not enough arguments")
srv = QEMUMonitorProtocol(socket_path)
srv.connect()
rsp = srv.command('qom-get', path=path, property=prop)
if type(rsp) == dict:
for i in rsp.keys():
print '%s: %s' % (i, rsp[i])
else:
print rsp

View File

@@ -1,64 +0,0 @@
#!/usr/bin/python
##
# QEMU Object Model test tools
#
# Copyright IBM, Corp. 2011
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.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.
##
import sys
import os
from qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None
path = None
prop = None
def usage():
return '''environment variables:
QMP_SOCKET=<path | addr:port>
usage:
%s [-h] [-s <QMP socket path | addr:port>] [<path>]
''' % cmd
def usage_error(error_msg = "unspecified error"):
sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
exit(1)
if len(args) > 0:
if args[0] == "-h":
print usage()
exit(0);
elif args[0] == "-s":
try:
socket_path = args[1]
except:
usage_error("missing argument: QMP socket path or address");
args = args[2:]
if not socket_path:
if os.environ.has_key('QMP_SOCKET'):
socket_path = os.environ['QMP_SOCKET']
else:
usage_error("no QMP socket path or address given");
srv = QEMUMonitorProtocol(socket_path)
srv.connect()
if len(args) == 0:
print '/'
sys.exit(0)
for item in srv.command('qom-list', path=args[0]):
if item['type'].startswith('child<'):
print '%s/' % item['name']
elif item['type'].startswith('link<'):
print '@%s/' % item['name']
else:
print '%s' % item['name']

View File

@@ -1,64 +0,0 @@
#!/usr/bin/python
##
# QEMU Object Model test tools
#
# Copyright IBM, Corp. 2011
#
# Authors:
# Anthony Liguori <aliguori@us.ibm.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.
##
import sys
import os
from qmp import QEMUMonitorProtocol
cmd, args = sys.argv[0], sys.argv[1:]
socket_path = None
path = None
prop = None
value = None
def usage():
return '''environment variables:
QMP_SOCKET=<path | addr:port>
usage:
%s [-h] [-s <QMP socket path | addr:port>] <path>.<property> <value>
''' % cmd
def usage_error(error_msg = "unspecified error"):
sys.stderr.write('%s\nERROR: %s\n' % (usage(), error_msg))
exit(1)
if len(args) > 0:
if args[0] == "-h":
print usage()
exit(0);
elif args[0] == "-s":
try:
socket_path = args[1]
except:
usage_error("missing argument: QMP socket path or address");
args = args[2:]
if not socket_path:
if os.environ.has_key('QMP_SOCKET'):
socket_path = os.environ['QMP_SOCKET']
else:
usage_error("no QMP socket path or address given");
if len(args) > 1:
try:
path, prop = args[0].rsplit('.', 1)
except:
usage_error("invalid format for path/property/value")
value = args[1]
else:
usage_error("not enough arguments")
srv = QEMUMonitorProtocol(socket_path)
srv.connect()
print srv.command('qom-set', path=path, property=prop, value=sys.argv[2])

4
README
View File

@@ -1,3 +1,3 @@
Read the documentation in qemu-doc.html or on http://wiki.qemu.org
Read the documentation in qemu-doc.html.
- QEMU team
Fabrice Bellard.

View File

@@ -1 +1 @@
1.0.92
1.0,1

174
aio.c
View File

@@ -9,8 +9,6 @@
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu-common.h"
@@ -35,6 +33,7 @@ struct AioHandler
IOHandler *io_read;
IOHandler *io_write;
AioFlushHandler *io_flush;
AioProcessQueue *io_process_queue;
int deleted;
void *opaque;
QLIST_ENTRY(AioHandler) node;
@@ -57,6 +56,7 @@ int qemu_aio_set_fd_handler(int fd,
IOHandler *io_read,
IOHandler *io_write,
AioFlushHandler *io_flush,
AioProcessQueue *io_process_queue,
void *opaque)
{
AioHandler *node;
@@ -89,6 +89,7 @@ int qemu_aio_set_fd_handler(int fd,
node->io_read = io_read;
node->io_write = io_write;
node->io_flush = io_flush;
node->io_process_queue = io_process_queue;
node->opaque = opaque;
}
@@ -99,96 +100,131 @@ int qemu_aio_set_fd_handler(int fd,
void qemu_aio_flush(void)
{
while (qemu_aio_wait());
AioHandler *node;
int ret;
do {
ret = 0;
/*
* If there are pending emulated aio start them now so flush
* will be able to return 1.
*/
qemu_aio_wait();
QLIST_FOREACH(node, &aio_handlers, node) {
if (node->io_flush) {
ret |= node->io_flush(node->opaque);
}
}
} while (qemu_bh_poll() || ret > 0);
}
bool qemu_aio_wait(void)
int qemu_aio_process_queue(void)
{
AioHandler *node;
fd_set rdfds, wrfds;
int max_fd = -1;
int ret;
bool busy;
/*
* If there are callbacks left that have been queued, we need to call then.
* Do not call select in this case, because it is possible that the caller
* does not need a complete flush (as is the case for qemu_aio_wait loops).
*/
if (qemu_bh_poll()) {
return true;
}
int ret = 0;
walking_handlers = 1;
FD_ZERO(&rdfds);
FD_ZERO(&wrfds);
/* fill fd sets */
busy = false;
QLIST_FOREACH(node, &aio_handlers, node) {
/* If there aren't pending AIO operations, don't invoke callbacks.
* Otherwise, if there are no AIO requests, qemu_aio_wait() would
* wait indefinitely.
*/
if (node->io_flush) {
if (node->io_flush(node->opaque) == 0) {
continue;
if (node->io_process_queue) {
if (node->io_process_queue(node->opaque)) {
ret = 1;
}
busy = true;
}
if (!node->deleted && node->io_read) {
FD_SET(node->fd, &rdfds);
max_fd = MAX(max_fd, node->fd + 1);
}
if (!node->deleted && node->io_write) {
FD_SET(node->fd, &wrfds);
max_fd = MAX(max_fd, node->fd + 1);
}
}
walking_handlers = 0;
/* No AIO operations? Get us out of here */
if (!busy) {
return false;
}
return ret;
}
/* wait until next event */
ret = select(max_fd, &rdfds, &wrfds, NULL, NULL);
void qemu_aio_wait(void)
{
int ret;
if (qemu_bh_poll())
return;
/*
* If there are callbacks left that have been queued, we need to call then.
* Return afterwards to avoid waiting needlessly in select().
*/
if (qemu_aio_process_queue())
return;
do {
AioHandler *node;
fd_set rdfds, wrfds;
int max_fd = -1;
/* if we have any readable fds, dispatch event */
if (ret > 0) {
walking_handlers = 1;
/* we have to walk very carefully in case
* qemu_aio_set_fd_handler is called while we're walking */
node = QLIST_FIRST(&aio_handlers);
while (node) {
AioHandler *tmp;
FD_ZERO(&rdfds);
FD_ZERO(&wrfds);
if (!node->deleted &&
FD_ISSET(node->fd, &rdfds) &&
node->io_read) {
node->io_read(node->opaque);
/* fill fd sets */
QLIST_FOREACH(node, &aio_handlers, node) {
/* If there aren't pending AIO operations, don't invoke callbacks.
* Otherwise, if there are no AIO requests, qemu_aio_wait() would
* wait indefinitely.
*/
if (node->io_flush && node->io_flush(node->opaque) == 0)
continue;
if (!node->deleted && node->io_read) {
FD_SET(node->fd, &rdfds);
max_fd = MAX(max_fd, node->fd + 1);
}
if (!node->deleted &&
FD_ISSET(node->fd, &wrfds) &&
node->io_write) {
node->io_write(node->opaque);
}
tmp = node;
node = QLIST_NEXT(node, node);
if (tmp->deleted) {
QLIST_REMOVE(tmp, node);
g_free(tmp);
if (!node->deleted && node->io_write) {
FD_SET(node->fd, &wrfds);
max_fd = MAX(max_fd, node->fd + 1);
}
}
walking_handlers = 0;
}
return true;
/* No AIO operations? Get us out of here */
if (max_fd == -1)
break;
/* wait until next event */
ret = select(max_fd, &rdfds, &wrfds, NULL, NULL);
if (ret == -1 && errno == EINTR)
continue;
/* if we have any readable fds, dispatch event */
if (ret > 0) {
walking_handlers = 1;
/* we have to walk very carefully in case
* qemu_aio_set_fd_handler is called while we're walking */
node = QLIST_FIRST(&aio_handlers);
while (node) {
AioHandler *tmp;
if (!node->deleted &&
FD_ISSET(node->fd, &rdfds) &&
node->io_read) {
node->io_read(node->opaque);
}
if (!node->deleted &&
FD_ISSET(node->fd, &wrfds) &&
node->io_write) {
node->io_write(node->opaque);
}
tmp = node;
node = QLIST_NEXT(node, node);
if (tmp->deleted) {
QLIST_REMOVE(tmp, node);
g_free(tmp);
}
}
walking_handlers = 0;
}
} while (ret == 0);
}

View File

@@ -41,8 +41,6 @@
#include "net.h"
#include "gdbstub.h"
#include "hw/smbios.h"
#include "exec-memory.h"
#include "hw/pcspk.h"
#ifdef TARGET_SPARC
int graphic_width = 1024;
@@ -54,6 +52,7 @@ int graphic_height = 600;
int graphic_depth = 15;
#endif
const char arch_config_name[] = CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf";
#if defined(TARGET_ALPHA)
#define QEMU_ARCH QEMU_ARCH_ALPHA
@@ -95,61 +94,14 @@ const uint32_t arch_type = QEMU_ARCH;
#define RAM_SAVE_FLAG_EOS 0x10
#define RAM_SAVE_FLAG_CONTINUE 0x20
#ifdef __ALTIVEC__
#include <altivec.h>
#define VECTYPE vector unsigned char
#define SPLAT(p) vec_splat(vec_ld(0, p), 0)
#define ALL_EQ(v1, v2) vec_all_eq(v1, v2)
#elif defined __SSE2__
#include <emmintrin.h>
#define VECTYPE __m128i
#define SPLAT(p) _mm_set1_epi8(*(p))
#define ALL_EQ(v1, v2) (_mm_movemask_epi8(_mm_cmpeq_epi8(v1, v2)) == 0xFFFF)
#else
#define VECTYPE unsigned long
#define SPLAT(p) (*(p) * (~0UL / 255))
#define ALL_EQ(v1, v2) ((v1) == (v2))
#endif
static struct defconfig_file {
const char *filename;
/* Indicates it is an user config file (disabled by -no-user-config) */
bool userconfig;
} default_config_files[] = {
{ CONFIG_QEMU_DATADIR "/cpus-" TARGET_ARCH ".conf", false },
{ CONFIG_QEMU_CONFDIR "/qemu.conf", true },
{ CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".conf", true },
{ NULL }, /* end of list */
};
int qemu_read_default_config_files(bool userconfig)
static int is_dup_page(uint8_t *page, uint8_t ch)
{
int ret;
struct defconfig_file *f;
for (f = default_config_files; f->filename; f++) {
if (!userconfig && f->userconfig) {
continue;
}
ret = qemu_read_config_file(f->filename);
if (ret < 0 && ret != -ENOENT) {
return ret;
}
}
return 0;
}
static int is_dup_page(uint8_t *page)
{
VECTYPE *p = (VECTYPE *)page;
VECTYPE val = SPLAT(page);
uint32_t val = ch << 24 | ch << 16 | ch << 8 | ch;
uint32_t *array = (uint32_t *)page;
int i;
for (i = 0; i < TARGET_PAGE_SIZE / sizeof(VECTYPE); i++) {
if (!ALL_EQ(val, p[i])) {
for (i = 0; i < (TARGET_PAGE_SIZE / 4); i++) {
if (array[i] != val) {
return 0;
}
}
@@ -164,25 +116,26 @@ static int ram_save_block(QEMUFile *f)
{
RAMBlock *block = last_block;
ram_addr_t offset = last_offset;
ram_addr_t current_addr;
int bytes_sent = 0;
MemoryRegion *mr;
if (!block)
block = QLIST_FIRST(&ram_list.blocks);
current_addr = block->offset + offset;
do {
mr = block->mr;
if (memory_region_get_dirty(mr, offset, TARGET_PAGE_SIZE,
DIRTY_MEMORY_MIGRATION)) {
if (cpu_physical_memory_get_dirty(current_addr, MIGRATION_DIRTY_FLAG)) {
uint8_t *p;
int cont = (block == last_block) ? RAM_SAVE_FLAG_CONTINUE : 0;
memory_region_reset_dirty(mr, offset, TARGET_PAGE_SIZE,
DIRTY_MEMORY_MIGRATION);
cpu_physical_memory_reset_dirty(current_addr,
current_addr + TARGET_PAGE_SIZE,
MIGRATION_DIRTY_FLAG);
p = memory_region_get_ram_ptr(mr) + offset;
p = block->host + offset;
if (is_dup_page(p)) {
if (is_dup_page(p, *p)) {
qemu_put_be64(f, offset | cont | RAM_SAVE_FLAG_COMPRESS);
if (!cont) {
qemu_put_byte(f, strlen(block->idstr));
@@ -212,7 +165,10 @@ static int ram_save_block(QEMUFile *f)
if (!block)
block = QLIST_FIRST(&ram_list.blocks);
}
} while (block != last_block || offset != last_offset);
current_addr = block->offset + offset;
} while (current_addr != last_block->offset + last_offset);
last_block = block;
last_offset = offset;
@@ -229,9 +185,9 @@ static ram_addr_t ram_save_remaining(void)
QLIST_FOREACH(block, &ram_list.blocks, next) {
ram_addr_t addr;
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
if (memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
DIRTY_MEMORY_MIGRATION)) {
for (addr = block->offset; addr < block->offset + block->length;
addr += TARGET_PAGE_SIZE) {
if (cpu_physical_memory_get_dirty(addr, MIGRATION_DIRTY_FLAG)) {
count++;
}
}
@@ -265,8 +221,12 @@ static int block_compar(const void *a, const void *b)
{
RAMBlock * const *ablock = a;
RAMBlock * const *bblock = b;
return strcmp((*ablock)->idstr, (*bblock)->idstr);
if ((*ablock)->offset < (*bblock)->offset) {
return -1;
} else if ((*ablock)->offset > (*bblock)->offset) {
return 1;
}
return 0;
}
static void sort_ram_list(void)
@@ -290,7 +250,7 @@ static void sort_ram_list(void)
g_free(blocks);
}
int ram_save_live(QEMUFile *f, int stage, void *opaque)
int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
{
ram_addr_t addr;
uint64_t bytes_transferred_last;
@@ -299,11 +259,14 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
int ret;
if (stage < 0) {
memory_global_dirty_log_stop();
cpu_physical_memory_set_dirty_tracking(0);
return 0;
}
memory_global_sync_dirty_bitmap(get_system_memory());
if (cpu_physical_sync_dirty_bitmap(0, TARGET_PHYS_ADDR_MAX) != 0) {
qemu_file_set_error(f, -EINVAL);
return -EINVAL;
}
if (stage == 1) {
RAMBlock *block;
@@ -314,15 +277,17 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
/* Make sure all dirty bits are set */
QLIST_FOREACH(block, &ram_list.blocks, next) {
for (addr = 0; addr < block->length; addr += TARGET_PAGE_SIZE) {
if (!memory_region_get_dirty(block->mr, addr, TARGET_PAGE_SIZE,
DIRTY_MEMORY_MIGRATION)) {
memory_region_set_dirty(block->mr, addr, TARGET_PAGE_SIZE);
for (addr = block->offset; addr < block->offset + block->length;
addr += TARGET_PAGE_SIZE) {
if (!cpu_physical_memory_get_dirty(addr,
MIGRATION_DIRTY_FLAG)) {
cpu_physical_memory_set_dirty(addr);
}
}
}
memory_global_dirty_log_start();
/* Enable dirty memory tracking */
cpu_physical_memory_set_dirty_tracking(1);
qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE);
@@ -367,7 +332,7 @@ int ram_save_live(QEMUFile *f, int stage, void *opaque)
while ((bytes_sent = ram_save_block(f)) != 0) {
bytes_transferred += bytes_sent;
}
memory_global_dirty_log_stop();
cpu_physical_memory_set_dirty_tracking(0);
}
qemu_put_be64(f, RAM_SAVE_FLAG_EOS);
@@ -391,7 +356,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
return NULL;
}
return memory_region_get_ram_ptr(block->mr) + offset;
return block->host + offset;
}
len = qemu_get_byte(f);
@@ -400,7 +365,7 @@ static inline void *host_from_stream_offset(QEMUFile *f,
QLIST_FOREACH(block, &ram_list.blocks, next) {
if (!strncmp(id, block->idstr, sizeof(id)))
return memory_region_get_ram_ptr(block->mr) + offset;
return block->host + offset;
}
fprintf(stderr, "Can't find block %s!\n", id);
@@ -413,7 +378,7 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
int flags;
int error;
if (version_id < 4 || version_id > 4) {
if (version_id < 3 || version_id > 4) {
return -EINVAL;
}
@@ -424,7 +389,11 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
addr &= TARGET_PAGE_MASK;
if (flags & RAM_SAVE_FLAG_MEM_SIZE) {
if (version_id == 4) {
if (version_id == 3) {
if (addr != ram_bytes_total()) {
return -EINVAL;
}
} else {
/* Synchronize RAM block list */
char id[256];
ram_addr_t length;
@@ -462,7 +431,10 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
void *host;
uint8_t ch;
host = host_from_stream_offset(f, addr, flags);
if (version_id == 3)
host = qemu_get_ram_ptr(addr);
else
host = host_from_stream_offset(f, addr, flags);
if (!host) {
return -EINVAL;
}
@@ -478,7 +450,10 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
} else if (flags & RAM_SAVE_FLAG_PAGE) {
void *host;
host = host_from_stream_offset(f, addr, flags);
if (version_id == 3)
host = qemu_get_ram_ptr(addr);
else
host = host_from_stream_offset(f, addr, flags);
qemu_get_buffer(f, host, TARGET_PAGE_SIZE);
}
@@ -498,14 +473,14 @@ struct soundhw {
int enabled;
int isa;
union {
int (*init_isa) (ISABus *bus);
int (*init_isa) (qemu_irq *pic);
int (*init_pci) (PCIBus *bus);
} init;
};
static struct soundhw soundhw[] = {
#ifdef HAS_AUDIO_CHOICE
#ifdef CONFIG_PCSPK
#if defined(TARGET_I386) || defined(TARGET_MIPS)
{
"pcspk",
"PC speaker",
@@ -653,15 +628,15 @@ void select_soundhw(const char *optarg)
}
}
void audio_init(ISABus *isa_bus, PCIBus *pci_bus)
void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus)
{
struct soundhw *c;
for (c = soundhw; c->name; ++c) {
if (c->enabled) {
if (c->isa) {
if (isa_bus) {
c->init.init_isa(isa_bus);
if (isa_pic) {
c->init.init_isa(isa_pic);
}
} else {
if (pci_bus) {
@@ -675,7 +650,7 @@ void audio_init(ISABus *isa_bus, PCIBus *pci_bus)
void select_soundhw(const char *optarg)
{
}
void audio_init(ISABus *isa_bus, PCIBus *pci_bus)
void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus)
{
}
#endif

View File

@@ -1,6 +1,8 @@
#ifndef QEMU_ARCH_INIT_H
#define QEMU_ARCH_INIT_H
extern const char arch_config_name[];
enum {
QEMU_ARCH_ALL = -1,
QEMU_ARCH_ALPHA = 1,
@@ -25,7 +27,7 @@ void do_acpitable_option(const char *optarg);
void do_smbios_option(const char *optarg);
void cpudef_init(void);
int audio_available(void);
void audio_init(ISABus *isa_bus, PCIBus *pci_bus);
void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus);
int tcg_available(void);
int kvm_available(void);
int xen_available(void);

View File

@@ -1624,7 +1624,7 @@ arm_decode_shift (long given, fprintf_function func, void *stream,
}
/* Print one coprocessor instruction on INFO->STREAM.
Return true if the instruction matched, false if this is not a
Return true if the instuction matched, false if this is not a
recognised coprocessor instruction. */
static bfd_boolean
@@ -2214,7 +2214,7 @@ print_arm_address (bfd_vma pc, struct disassemble_info *info, long given)
}
/* Print one neon instruction on INFO->STREAM.
Return true if the instruction matched, false if this is not a
Return true if the instuction matched, false if this is not a
recognised neon instruction. */
static bfd_boolean
@@ -3927,7 +3927,7 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
n = last_mapping_sym - 1;
/* No mapping symbol found at this address. Look backwards
for a preceding one. */
for a preceeding one. */
for (; n >= 0; n--)
{
if (get_sym_code_type (info, n, &type))

View File

@@ -37,26 +37,26 @@
#include "hw/arm-misc.h"
#endif
#define TARGET_SYS_OPEN 0x01
#define TARGET_SYS_CLOSE 0x02
#define TARGET_SYS_WRITEC 0x03
#define TARGET_SYS_WRITE0 0x04
#define TARGET_SYS_WRITE 0x05
#define TARGET_SYS_READ 0x06
#define TARGET_SYS_READC 0x07
#define TARGET_SYS_ISTTY 0x09
#define TARGET_SYS_SEEK 0x0a
#define TARGET_SYS_FLEN 0x0c
#define TARGET_SYS_TMPNAM 0x0d
#define TARGET_SYS_REMOVE 0x0e
#define TARGET_SYS_RENAME 0x0f
#define TARGET_SYS_CLOCK 0x10
#define TARGET_SYS_TIME 0x11
#define TARGET_SYS_SYSTEM 0x12
#define TARGET_SYS_ERRNO 0x13
#define TARGET_SYS_GET_CMDLINE 0x15
#define TARGET_SYS_HEAPINFO 0x16
#define TARGET_SYS_EXIT 0x18
#define SYS_OPEN 0x01
#define SYS_CLOSE 0x02
#define SYS_WRITEC 0x03
#define SYS_WRITE0 0x04
#define SYS_WRITE 0x05
#define SYS_READ 0x06
#define SYS_READC 0x07
#define SYS_ISTTY 0x09
#define SYS_SEEK 0x0a
#define SYS_FLEN 0x0c
#define SYS_TMPNAM 0x0d
#define SYS_REMOVE 0x0e
#define SYS_RENAME 0x0f
#define SYS_CLOCK 0x10
#define SYS_TIME 0x11
#define SYS_SYSTEM 0x12
#define SYS_ERRNO 0x13
#define SYS_GET_CMDLINE 0x15
#define SYS_HEAPINFO 0x16
#define SYS_EXIT 0x18
#ifndef O_BINARY
#define O_BINARY 0
@@ -108,7 +108,7 @@ static inline uint32_t set_swi_errno(TaskState *ts, uint32_t code)
return code;
}
#else
static inline uint32_t set_swi_errno(CPUARMState *env, uint32_t code)
static inline uint32_t set_swi_errno(CPUState *env, uint32_t code)
{
return code;
}
@@ -122,7 +122,7 @@ static target_ulong arm_semi_syscall_len;
static target_ulong syscall_err;
#endif
static void arm_semi_cb(CPUARMState *env, target_ulong ret, target_ulong err)
static void arm_semi_cb(CPUState *env, target_ulong ret, target_ulong err)
{
#ifdef CONFIG_USER_ONLY
TaskState *ts = env->opaque;
@@ -138,11 +138,11 @@ static void arm_semi_cb(CPUARMState *env, target_ulong ret, target_ulong err)
} else {
/* Fixup syscalls that use nonstardard return conventions. */
switch (env->regs[0]) {
case TARGET_SYS_WRITE:
case TARGET_SYS_READ:
case SYS_WRITE:
case SYS_READ:
env->regs[0] = arm_semi_syscall_len - ret;
break;
case TARGET_SYS_SEEK:
case SYS_SEEK:
env->regs[0] = 0;
break;
default:
@@ -152,7 +152,7 @@ static void arm_semi_cb(CPUARMState *env, target_ulong ret, target_ulong err)
}
}
static void arm_semi_flen_cb(CPUARMState *env, target_ulong ret, target_ulong err)
static void arm_semi_flen_cb(CPUState *env, target_ulong ret, target_ulong err)
{
/* The size is always stored in big-endian order, extract
the value. We assume the size always fit in 32 bits. */
@@ -174,7 +174,7 @@ static void arm_semi_flen_cb(CPUARMState *env, target_ulong ret, target_ulong er
__arg; \
})
#define SET_ARG(n, val) put_user_ual(val, args + (n) * 4)
uint32_t do_arm_semihosting(CPUARMState *env)
uint32_t do_arm_semihosting(CPUState *env)
{
target_ulong args;
char * s;
@@ -184,13 +184,13 @@ uint32_t do_arm_semihosting(CPUARMState *env)
#ifdef CONFIG_USER_ONLY
TaskState *ts = env->opaque;
#else
CPUARMState *ts = env;
CPUState *ts = env;
#endif
nr = env->regs[0];
args = env->regs[1];
switch (nr) {
case TARGET_SYS_OPEN:
case SYS_OPEN:
if (!(s = lock_user_string(ARG(0))))
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
@@ -211,14 +211,14 @@ uint32_t do_arm_semihosting(CPUARMState *env)
}
unlock_user(s, ARG(0), 0);
return ret;
case TARGET_SYS_CLOSE:
case SYS_CLOSE:
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "close,%x", ARG(0));
return env->regs[0];
} else {
return set_swi_errno(ts, close(ARG(0)));
}
case TARGET_SYS_WRITEC:
case SYS_WRITEC:
{
char c;
@@ -233,7 +233,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
return write(STDERR_FILENO, &c, 1);
}
}
case TARGET_SYS_WRITE0:
case SYS_WRITE0:
if (!(s = lock_user_string(args)))
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
@@ -246,7 +246,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
}
unlock_user(s, args, 0);
return ret;
case TARGET_SYS_WRITE:
case SYS_WRITE:
len = ARG(2);
if (use_gdb_syscalls()) {
arm_semi_syscall_len = len;
@@ -262,7 +262,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
return -1;
return len - ret;
}
case TARGET_SYS_READ:
case SYS_READ:
len = ARG(2);
if (use_gdb_syscalls()) {
arm_semi_syscall_len = len;
@@ -280,17 +280,17 @@ uint32_t do_arm_semihosting(CPUARMState *env)
return -1;
return len - ret;
}
case TARGET_SYS_READC:
case SYS_READC:
/* XXX: Read from debug cosole. Not implemented. */
return 0;
case TARGET_SYS_ISTTY:
case SYS_ISTTY:
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "isatty,%x", ARG(0));
return env->regs[0];
} else {
return isatty(ARG(0));
}
case TARGET_SYS_SEEK:
case SYS_SEEK:
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "lseek,%x,%x,0", ARG(0), ARG(1));
return env->regs[0];
@@ -300,7 +300,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
return -1;
return 0;
}
case TARGET_SYS_FLEN:
case SYS_FLEN:
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_flen_cb, "fstat,%x,%x",
ARG(0), env->regs[13]-64);
@@ -312,10 +312,10 @@ uint32_t do_arm_semihosting(CPUARMState *env)
return -1;
return buf.st_size;
}
case TARGET_SYS_TMPNAM:
case SYS_TMPNAM:
/* XXX: Not implemented. */
return -1;
case TARGET_SYS_REMOVE:
case SYS_REMOVE:
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "unlink,%s", ARG(0), (int)ARG(1)+1);
ret = env->regs[0];
@@ -327,7 +327,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
unlock_user(s, ARG(0), 0);
}
return ret;
case TARGET_SYS_RENAME:
case SYS_RENAME:
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "rename,%s,%s",
ARG(0), (int)ARG(1)+1, ARG(2), (int)ARG(3)+1);
@@ -347,11 +347,11 @@ uint32_t do_arm_semihosting(CPUARMState *env)
unlock_user(s, ARG(0), 0);
return ret;
}
case TARGET_SYS_CLOCK:
case SYS_CLOCK:
return clock() / (CLOCKS_PER_SEC / 100);
case TARGET_SYS_TIME:
case SYS_TIME:
return set_swi_errno(ts, time(NULL));
case TARGET_SYS_SYSTEM:
case SYS_SYSTEM:
if (use_gdb_syscalls()) {
gdb_do_syscall(arm_semi_cb, "system,%s", ARG(0), (int)ARG(1)+1);
return env->regs[0];
@@ -363,13 +363,13 @@ uint32_t do_arm_semihosting(CPUARMState *env)
unlock_user(s, ARG(0), 0);
return ret;
}
case TARGET_SYS_ERRNO:
case SYS_ERRNO:
#ifdef CONFIG_USER_ONLY
return ts->swi_errno;
#else
return syscall_err;
#endif
case TARGET_SYS_GET_CMDLINE:
case SYS_GET_CMDLINE:
{
/* Build a command-line from the original argv.
*
@@ -452,7 +452,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
return status;
}
case TARGET_SYS_HEAPINFO:
case SYS_HEAPINFO:
{
uint32_t *ptr;
uint32_t limit;
@@ -498,7 +498,7 @@ uint32_t do_arm_semihosting(CPUARMState *env)
#endif
return 0;
}
case TARGET_SYS_EXIT:
case SYS_EXIT:
gdb_exit(env, 0);
exit(0);
default:

View File

@@ -35,10 +35,10 @@ static struct QEMUBH *first_bh;
struct QEMUBH {
QEMUBHFunc *cb;
void *opaque;
int scheduled;
int idle;
int deleted;
QEMUBH *next;
bool scheduled;
bool idle;
bool deleted;
};
QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
@@ -120,7 +120,7 @@ void qemu_bh_delete(QEMUBH *bh)
bh->deleted = 1;
}
void qemu_bh_update_timeout(uint32_t *timeout)
void qemu_bh_update_timeout(int *timeout)
{
QEMUBH *bh;

View File

@@ -585,20 +585,17 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a
switch (as->fmt) {
case AUD_FMT_S8:
sign = 1;
/* fall through */
case AUD_FMT_U8:
break;
case AUD_FMT_S16:
sign = 1;
/* fall through */
case AUD_FMT_U16:
bits = 16;
break;
case AUD_FMT_S32:
sign = 1;
/* fall through */
case AUD_FMT_U32:
bits = 32;
break;
@@ -957,9 +954,7 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
total += isamp;
}
if (!(hw->ctl_caps & VOICE_VOLUME_CAP)) {
mixeng_volume (sw->buf, ret, &sw->vol);
}
mixeng_volume (sw->buf, ret, &sw->vol);
sw->clip (buf, sw->buf, ret);
sw->total_hw_samples_acquired += total;
@@ -1043,10 +1038,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
swlim = audio_MIN (swlim, samples);
if (swlim) {
sw->conv (sw->buf, buf, swlim);
if (!(sw->hw->ctl_caps & VOICE_VOLUME_CAP)) {
mixeng_volume (sw->buf, swlim, &sw->vol);
}
mixeng_volume (sw->buf, swlim, &sw->vol);
}
while (swlim) {
@@ -1673,7 +1665,7 @@ static void audio_pp_nb_voices (const char *typ, int nb)
printf ("Theoretically supports many %s voices\n", typ);
break;
default:
printf ("Theoretically supports up to %d %s voices\n", nb, typ);
printf ("Theoretically supports upto %d %s voices\n", nb, typ);
break;
}
@@ -2058,29 +2050,17 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
{
if (sw) {
HWVoiceOut *hw = sw->hw;
sw->vol.mute = mute;
sw->vol.l = nominal_volume.l * lvol / 255;
sw->vol.r = nominal_volume.r * rvol / 255;
if (hw->pcm_ops->ctl_out) {
hw->pcm_ops->ctl_out (hw, VOICE_VOLUME, sw);
}
}
}
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
{
if (sw) {
HWVoiceIn *hw = sw->hw;
sw->vol.mute = mute;
sw->vol.l = nominal_volume.l * lvol / 255;
sw->vol.r = nominal_volume.r * rvol / 255;
if (hw->pcm_ops->ctl_in) {
hw->pcm_ops->ctl_in (hw, VOICE_VOLUME, sw);
}
}
}

View File

@@ -82,7 +82,6 @@ typedef struct HWVoiceOut {
int samples;
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut;
@@ -102,7 +101,6 @@ typedef struct HWVoiceIn {
int samples;
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceIn) entries;
} HWVoiceIn;
@@ -152,7 +150,6 @@ struct audio_driver {
int max_voices_in;
int voice_size_out;
int voice_size_in;
int ctl_caps;
};
struct audio_pcm_ops {
@@ -234,9 +231,6 @@ void audio_run (const char *msg);
#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
#define VOICE_VOLUME 3
#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
static inline int audio_ring_dist (int dst, int src, int len)
{

View File

@@ -263,8 +263,6 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
}
hw->pcm_ops = drv->pcm_ops;
hw->ctl_caps = drv->ctl_caps;
QLIST_INIT (&hw->sw_head);
#ifdef DAC
QLIST_INIT (&hw->cap_head);

View File

@@ -201,7 +201,7 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
case AUD_FMT_S32:
case AUD_FMT_U32:
dolog ("Will use 16 instead of 32 bit samples\n");
/* fall through */
case AUD_FMT_S16:
case AUD_FMT_U16:
deffmt:

View File

@@ -2,7 +2,8 @@
#include "qemu-common.h"
#include "audio.h"
#include <pulse/pulseaudio.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#define AUDIO_CAP "pulseaudio"
#include "audio_int.h"
@@ -14,7 +15,7 @@ typedef struct {
int live;
int decr;
int rpos;
pa_stream *stream;
pa_simple *s;
void *pcm_buf;
struct audio_pt pt;
} PAVoiceOut;
@@ -25,23 +26,17 @@ typedef struct {
int dead;
int incr;
int wpos;
pa_stream *stream;
pa_simple *s;
void *pcm_buf;
struct audio_pt pt;
const void *read_data;
size_t read_index, read_length;
} PAVoiceIn;
typedef struct {
static struct {
int samples;
char *server;
char *sink;
char *source;
pa_threaded_mainloop *mainloop;
pa_context *context;
} paaudio;
static paaudio glob_paaudio = {
} conf = {
.samples = 4096,
};
@@ -56,146 +51,6 @@ static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
AUD_log (AUDIO_CAP, "Reason: %s\n", pa_strerror (err));
}
#ifndef PA_CONTEXT_IS_GOOD
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x)
{
return
x == PA_CONTEXT_CONNECTING ||
x == PA_CONTEXT_AUTHORIZING ||
x == PA_CONTEXT_SETTING_NAME ||
x == PA_CONTEXT_READY;
}
#endif
#ifndef PA_STREAM_IS_GOOD
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
{
return
x == PA_STREAM_CREATING ||
x == PA_STREAM_READY;
}
#endif
#define CHECK_SUCCESS_GOTO(c, rerror, expression, label) \
do { \
if (!(expression)) { \
if (rerror) { \
*(rerror) = pa_context_errno ((c)->context); \
} \
goto label; \
} \
} while (0);
#define CHECK_DEAD_GOTO(c, stream, rerror, label) \
do { \
if (!(c)->context || !PA_CONTEXT_IS_GOOD (pa_context_get_state((c)->context)) || \
!(stream) || !PA_STREAM_IS_GOOD (pa_stream_get_state ((stream)))) { \
if (((c)->context && pa_context_get_state ((c)->context) == PA_CONTEXT_FAILED) || \
((stream) && pa_stream_get_state ((stream)) == PA_STREAM_FAILED)) { \
if (rerror) { \
*(rerror) = pa_context_errno ((c)->context); \
} \
} else { \
if (rerror) { \
*(rerror) = PA_ERR_BADSTATE; \
} \
} \
goto label; \
} \
} while (0);
static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
{
paaudio *g = &glob_paaudio;
pa_threaded_mainloop_lock (g->mainloop);
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
while (length > 0) {
size_t l;
while (!p->read_data) {
int r;
r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
if (!p->read_data) {
pa_threaded_mainloop_wait (g->mainloop);
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
} else {
p->read_index = 0;
}
}
l = p->read_length < length ? p->read_length : length;
memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
data = (uint8_t *) data + l;
length -= l;
p->read_index += l;
p->read_length -= l;
if (!p->read_length) {
int r;
r = pa_stream_drop (p->stream);
p->read_data = NULL;
p->read_length = 0;
p->read_index = 0;
CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
}
}
pa_threaded_mainloop_unlock (g->mainloop);
return 0;
unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop);
return -1;
}
static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
{
paaudio *g = &glob_paaudio;
pa_threaded_mainloop_lock (g->mainloop);
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
while (length > 0) {
size_t l;
int r;
while (!(l = pa_stream_writable_size (p->stream))) {
pa_threaded_mainloop_wait (g->mainloop);
CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
}
CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
if (l > length) {
l = length;
}
r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
data = (const uint8_t *) data + l;
length -= l;
}
pa_threaded_mainloop_unlock (g->mainloop);
return 0;
unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop);
return -1;
}
static void *qpa_thread_out (void *arg)
{
PAVoiceOut *pa = arg;
@@ -222,7 +77,7 @@ static void *qpa_thread_out (void *arg)
}
}
decr = to_mix = audio_MIN (pa->live, glob_paaudio.samples >> 2);
decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
rpos = pa->rpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -236,8 +91,8 @@ static void *qpa_thread_out (void *arg)
hw->clip (pa->pcm_buf, src, chunk);
if (qpa_simple_write (pa, pa->pcm_buf,
chunk << hw->info.shift, &error) < 0) {
if (pa_simple_write (pa->s, pa->pcm_buf,
chunk << hw->info.shift, &error) < 0) {
qpa_logerr (error, "pa_simple_write failed\n");
return NULL;
}
@@ -314,7 +169,7 @@ static void *qpa_thread_in (void *arg)
}
}
incr = to_grab = audio_MIN (pa->dead, glob_paaudio.samples >> 2);
incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
wpos = pa->wpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
@@ -326,8 +181,8 @@ static void *qpa_thread_in (void *arg)
int chunk = audio_MIN (to_grab, hw->samples - wpos);
void *buf = advance (pa->pcm_buf, wpos);
if (qpa_simple_read (pa, buf,
chunk << hw->info.shift, &error) < 0) {
if (pa_simple_read (pa->s, buf,
chunk << hw->info.shift, &error) < 0) {
qpa_logerr (error, "pa_simple_read failed\n");
return NULL;
}
@@ -428,112 +283,6 @@ static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness)
}
}
static void context_state_cb (pa_context *c, void *userdata)
{
paaudio *g = &glob_paaudio;
switch (pa_context_get_state(c)) {
case PA_CONTEXT_READY:
case PA_CONTEXT_TERMINATED:
case PA_CONTEXT_FAILED:
pa_threaded_mainloop_signal (g->mainloop, 0);
break;
case PA_CONTEXT_UNCONNECTED:
case PA_CONTEXT_CONNECTING:
case PA_CONTEXT_AUTHORIZING:
case PA_CONTEXT_SETTING_NAME:
break;
}
}
static void stream_state_cb (pa_stream *s, void * userdata)
{
paaudio *g = &glob_paaudio;
switch (pa_stream_get_state (s)) {
case PA_STREAM_READY:
case PA_STREAM_FAILED:
case PA_STREAM_TERMINATED:
pa_threaded_mainloop_signal (g->mainloop, 0);
break;
case PA_STREAM_UNCONNECTED:
case PA_STREAM_CREATING:
break;
}
}
static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
{
paaudio *g = &glob_paaudio;
pa_threaded_mainloop_signal (g->mainloop, 0);
}
static pa_stream *qpa_simple_new (
const char *server,
const char *name,
pa_stream_direction_t dir,
const char *dev,
const char *stream_name,
const pa_sample_spec *ss,
const pa_channel_map *map,
const pa_buffer_attr *attr,
int *rerror)
{
paaudio *g = &glob_paaudio;
int r;
pa_stream *stream;
pa_threaded_mainloop_lock (g->mainloop);
stream = pa_stream_new (g->context, name, ss, map);
if (!stream) {
goto fail;
}
pa_stream_set_state_callback (stream, stream_state_cb, g);
pa_stream_set_read_callback (stream, stream_request_cb, g);
pa_stream_set_write_callback (stream, stream_request_cb, g);
if (dir == PA_STREAM_PLAYBACK) {
r = pa_stream_connect_playback (stream, dev, attr,
PA_STREAM_INTERPOLATE_TIMING
#ifdef PA_STREAM_ADJUST_LATENCY
|PA_STREAM_ADJUST_LATENCY
#endif
|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
} else {
r = pa_stream_connect_record (stream, dev, attr,
PA_STREAM_INTERPOLATE_TIMING
#ifdef PA_STREAM_ADJUST_LATENCY
|PA_STREAM_ADJUST_LATENCY
#endif
|PA_STREAM_AUTO_TIMING_UPDATE);
}
if (r < 0) {
goto fail;
}
pa_threaded_mainloop_unlock (g->mainloop);
return stream;
fail:
pa_threaded_mainloop_unlock (g->mainloop);
if (stream) {
pa_stream_unref (stream);
}
*rerror = pa_context_errno (g->context);
return NULL;
}
static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
{
int error;
@@ -557,24 +306,24 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->stream = qpa_simple_new (
glob_paaudio.server,
pa->s = pa_simple_new (
conf.server,
"qemu",
PA_STREAM_PLAYBACK,
glob_paaudio.sink,
conf.sink,
"pcm.playback",
&ss,
NULL, /* channel map */
&ba, /* buffering attributes */
&error
);
if (!pa->stream) {
if (!pa->s) {
qpa_logerr (error, "pa_simple_new for playback failed\n");
goto fail1;
}
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = glob_paaudio.samples;
hw->samples = conf.samples;
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
pa->rpos = hw->rpos;
if (!pa->pcm_buf) {
@@ -593,10 +342,8 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
g_free (pa->pcm_buf);
pa->pcm_buf = NULL;
fail2:
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
}
pa_simple_free (pa->s);
pa->s = NULL;
fail1:
return -1;
}
@@ -614,24 +361,24 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->stream = qpa_simple_new (
glob_paaudio.server,
pa->s = pa_simple_new (
conf.server,
"qemu",
PA_STREAM_RECORD,
glob_paaudio.source,
conf.source,
"pcm.capture",
&ss,
NULL, /* channel map */
NULL, /* buffering attributes */
&error
);
if (!pa->stream) {
if (!pa->s) {
qpa_logerr (error, "pa_simple_new for capture failed\n");
goto fail1;
}
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = glob_paaudio.samples;
hw->samples = conf.samples;
pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
pa->wpos = hw->wpos;
if (!pa->pcm_buf) {
@@ -650,10 +397,8 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
g_free (pa->pcm_buf);
pa->pcm_buf = NULL;
fail2:
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
}
pa_simple_free (pa->s);
pa->s = NULL;
fail1:
return -1;
}
@@ -668,9 +413,9 @@ static void qpa_fini_out (HWVoiceOut *hw)
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
if (pa->s) {
pa_simple_free (pa->s);
pa->s = NULL;
}
audio_pt_fini (&pa->pt, AUDIO_FUNC);
@@ -688,9 +433,9 @@ static void qpa_fini_in (HWVoiceIn *hw)
audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
if (pa->stream) {
pa_stream_unref (pa->stream);
pa->stream = NULL;
if (pa->s) {
pa_simple_free (pa->s);
pa->s = NULL;
}
audio_pt_fini (&pa->pt, AUDIO_FUNC);
@@ -700,213 +445,52 @@ static void qpa_fini_in (HWVoiceIn *hw)
static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
PAVoiceOut *pa = (PAVoiceOut *) hw;
pa_operation *op;
pa_cvolume v;
paaudio *g = &glob_paaudio;
#ifdef PA_CHECK_VERSION /* macro is present in 0.9.16+ */
pa_cvolume_init (&v); /* function is present in 0.9.13+ */
#endif
switch (cmd) {
case VOICE_VOLUME:
{
SWVoiceOut *sw;
va_list ap;
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceOut *);
va_end (ap);
v.channels = 2;
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
pa_threaded_mainloop_lock (g->mainloop);
op = pa_context_set_sink_input_volume (g->context,
pa_stream_get_index (pa->stream),
&v, NULL, NULL);
if (!op)
qpa_logerr (pa_context_errno (g->context),
"set_sink_input_volume() failed\n");
else
pa_operation_unref (op);
op = pa_context_set_sink_input_mute (g->context,
pa_stream_get_index (pa->stream),
sw->vol.mute, NULL, NULL);
if (!op) {
qpa_logerr (pa_context_errno (g->context),
"set_sink_input_mute() failed\n");
} else {
pa_operation_unref (op);
}
pa_threaded_mainloop_unlock (g->mainloop);
}
}
(void) hw;
(void) cmd;
return 0;
}
static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
PAVoiceIn *pa = (PAVoiceIn *) hw;
pa_operation *op;
pa_cvolume v;
paaudio *g = &glob_paaudio;
#ifdef PA_CHECK_VERSION
pa_cvolume_init (&v);
#endif
switch (cmd) {
case VOICE_VOLUME:
{
SWVoiceIn *sw;
va_list ap;
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceIn *);
va_end (ap);
v.channels = 2;
v.values[0] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.l) / UINT32_MAX;
v.values[1] = ((PA_VOLUME_NORM - PA_VOLUME_MUTED) * sw->vol.r) / UINT32_MAX;
pa_threaded_mainloop_lock (g->mainloop);
/* FIXME: use the upcoming "set_source_output_{volume,mute}" */
op = pa_context_set_source_volume_by_index (g->context,
pa_stream_get_device_index (pa->stream),
&v, NULL, NULL);
if (!op) {
qpa_logerr (pa_context_errno (g->context),
"set_source_volume() failed\n");
} else {
pa_operation_unref(op);
}
op = pa_context_set_source_mute_by_index (g->context,
pa_stream_get_index (pa->stream),
sw->vol.mute, NULL, NULL);
if (!op) {
qpa_logerr (pa_context_errno (g->context),
"set_source_mute() failed\n");
} else {
pa_operation_unref (op);
}
pa_threaded_mainloop_unlock (g->mainloop);
}
}
(void) hw;
(void) cmd;
return 0;
}
/* common */
static void *qpa_audio_init (void)
{
paaudio *g = &glob_paaudio;
g->mainloop = pa_threaded_mainloop_new ();
if (!g->mainloop) {
goto fail;
}
g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), glob_paaudio.server);
if (!g->context) {
goto fail;
}
pa_context_set_state_callback (g->context, context_state_cb, g);
if (pa_context_connect (g->context, glob_paaudio.server, 0, NULL) < 0) {
qpa_logerr (pa_context_errno (g->context),
"pa_context_connect() failed\n");
goto fail;
}
pa_threaded_mainloop_lock (g->mainloop);
if (pa_threaded_mainloop_start (g->mainloop) < 0) {
goto unlock_and_fail;
}
for (;;) {
pa_context_state_t state;
state = pa_context_get_state (g->context);
if (state == PA_CONTEXT_READY) {
break;
}
if (!PA_CONTEXT_IS_GOOD (state)) {
qpa_logerr (pa_context_errno (g->context),
"Wrong context state\n");
goto unlock_and_fail;
}
/* Wait until the context is ready */
pa_threaded_mainloop_wait (g->mainloop);
}
pa_threaded_mainloop_unlock (g->mainloop);
return &glob_paaudio;
unlock_and_fail:
pa_threaded_mainloop_unlock (g->mainloop);
fail:
AUD_log (AUDIO_CAP, "Failed to initialize PA context");
return NULL;
return &conf;
}
static void qpa_audio_fini (void *opaque)
{
paaudio *g = opaque;
if (g->mainloop) {
pa_threaded_mainloop_stop (g->mainloop);
}
if (g->context) {
pa_context_disconnect (g->context);
pa_context_unref (g->context);
g->context = NULL;
}
if (g->mainloop) {
pa_threaded_mainloop_free (g->mainloop);
}
g->mainloop = NULL;
(void) opaque;
}
struct audio_option qpa_options[] = {
{
.name = "SAMPLES",
.tag = AUD_OPT_INT,
.valp = &glob_paaudio.samples,
.valp = &conf.samples,
.descr = "buffer size in samples"
},
{
.name = "SERVER",
.tag = AUD_OPT_STR,
.valp = &glob_paaudio.server,
.valp = &conf.server,
.descr = "server address"
},
{
.name = "SINK",
.tag = AUD_OPT_STR,
.valp = &glob_paaudio.sink,
.valp = &conf.sink,
.descr = "sink device name"
},
{
.name = "SOURCE",
.tag = AUD_OPT_STR,
.valp = &glob_paaudio.source,
.valp = &conf.source,
.descr = "source device name"
},
{ /* End of list */ }
@@ -937,6 +521,5 @@ struct audio_driver pa_audio_driver = {
.max_voices_out = INT_MAX,
.max_voices_in = INT_MAX,
.voice_size_out = sizeof (PAVoiceOut),
.voice_size_in = sizeof (PAVoiceIn),
.ctl_caps = VOICE_VOLUME_CAP
.voice_size_in = sizeof (PAVoiceIn)
};

View File

@@ -202,26 +202,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
}
spice_server_playback_stop (&out->sin);
break;
case VOICE_VOLUME:
{
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
SWVoiceOut *sw;
va_list ap;
uint16_t vol[2];
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceOut *);
va_end (ap);
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
spice_server_playback_set_volume (&out->sin, 2, vol);
spice_server_playback_set_mute (&out->sin, sw->vol.mute);
#endif
break;
}
}
return 0;
}
@@ -323,26 +304,7 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
in->active = 0;
spice_server_record_stop (&in->sin);
break;
case VOICE_VOLUME:
{
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
SWVoiceIn *sw;
va_list ap;
uint16_t vol[2];
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceIn *);
va_end (ap);
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
spice_server_record_set_volume (&in->sin, 2, vol);
spice_server_record_set_mute (&in->sin, sw->vol.mute);
#endif
break;
}
}
return 0;
}
@@ -375,9 +337,6 @@ struct audio_driver spice_audio_driver = {
.max_voices_in = 1,
.voice_size_out = sizeof (SpiceVoiceOut),
.voice_size_in = sizeof (SpiceVoiceIn),
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
.ctl_caps = VOICE_VOLUME_CAP
#endif
};
void qemu_spice_audio_init (void)

View File

@@ -100,19 +100,31 @@ BalloonInfo *qmp_query_balloon(Error **errp)
return info;
}
void qmp_balloon(int64_t value, Error **errp)
/**
* do_balloon(): Request VM to change its memory allocation
*/
int do_balloon(Monitor *mon, const QDict *params,
MonitorCompletion cb, void *opaque)
{
int64_t target;
int ret;
if (kvm_enabled() && !kvm_has_sync_mmu()) {
error_set(errp, QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
return;
qerror_report(QERR_KVM_MISSING_CAP, "synchronous MMU", "balloon");
return -1;
}
if (value <= 0) {
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "target", "a size");
return;
target = qdict_get_int(params, "value");
if (target <= 0) {
qerror_report(QERR_INVALID_PARAMETER_VALUE, "target", "a size");
return -1;
}
if (qemu_balloon(value) == 0) {
error_set(errp, QERR_DEVICE_NOT_ACTIVE, "balloon");
ret = qemu_balloon(target);
if (ret == 0) {
qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
return -1;
}
cb(opaque, NULL);
return 0;
}

View File

@@ -24,4 +24,7 @@ int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
QEMUBalloonStatus *stat_func, void *opaque);
void qemu_remove_balloon_handler(void *opaque);
int do_balloon(Monitor *mon, const QDict *params,
MonitorCompletion cb, void *opaque);
#endif

View File

@@ -9,8 +9,6 @@
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu-common.h"
@@ -18,6 +16,7 @@
#include "hw/hw.h"
#include "qemu-queue.h"
#include "qemu-timer.h"
#include "monitor.h"
#include "block-migration.h"
#include "migration.h"
#include "blockdev.h"
@@ -203,7 +202,8 @@ static void blk_mig_read_cb(void *opaque, int ret)
assert(block_mig_state.submitted >= 0);
}
static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
static int mig_save_device_bulk(Monitor *mon, QEMUFile *f,
BlkMigDevState *bmds)
{
int64_t total_sectors = bmds->total_sectors;
int64_t cur_sector = bmds->cur_sector;
@@ -251,12 +251,22 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov,
nr_sectors, blk_mig_read_cb, blk);
if (!blk->aiocb) {
goto error;
}
block_mig_state.submitted++;
bdrv_reset_dirty(bs, cur_sector, nr_sectors);
bmds->cur_sector = cur_sector + nr_sectors;
return (bmds->cur_sector >= total_sectors);
error:
monitor_printf(mon, "Error reading sector %" PRId64 "\n", cur_sector);
qemu_file_set_error(f, -EIO);
g_free(blk->buf);
g_free(blk);
return 0;
}
static void set_dirty_tracking(int enable)
@@ -270,6 +280,7 @@ static void set_dirty_tracking(int enable)
static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
{
Monitor *mon = opaque;
BlkMigDevState *bmds;
int64_t sectors;
@@ -292,17 +303,19 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
block_mig_state.total_sector_sum += sectors;
if (bmds->shared_base) {
DPRINTF("Start migration for %s with shared base image\n",
bs->device_name);
monitor_printf(mon, "Start migration for %s with shared base "
"image\n",
bs->device_name);
} else {
DPRINTF("Start full migration for %s\n", bs->device_name);
monitor_printf(mon, "Start full migration for %s\n",
bs->device_name);
}
QSIMPLEQ_INSERT_TAIL(&block_mig_state.bmds_list, bmds, entry);
}
}
static void init_blk_migration(QEMUFile *f)
static void init_blk_migration(Monitor *mon, QEMUFile *f)
{
block_mig_state.submitted = 0;
block_mig_state.read_done = 0;
@@ -313,10 +326,10 @@ static void init_blk_migration(QEMUFile *f)
block_mig_state.total_time = 0;
block_mig_state.reads = 0;
bdrv_iterate(init_blk_migration_it, NULL);
bdrv_iterate(init_blk_migration_it, mon);
}
static int blk_mig_save_bulked_block(QEMUFile *f)
static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
{
int64_t completed_sector_sum = 0;
BlkMigDevState *bmds;
@@ -325,7 +338,7 @@ static int blk_mig_save_bulked_block(QEMUFile *f)
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
if (bmds->bulk_completed == 0) {
if (mig_save_device_bulk(f, bmds) == 1) {
if (mig_save_device_bulk(mon, f, bmds) == 1) {
/* completed bulk section for this device */
bmds->bulk_completed = 1;
}
@@ -347,7 +360,8 @@ static int blk_mig_save_bulked_block(QEMUFile *f)
block_mig_state.prev_progress = progress;
qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
| BLK_MIG_FLAG_PROGRESS);
DPRINTF("Completed %d %%\r", progress);
monitor_printf(mon, "Completed %d %%\r", progress);
monitor_flush(mon);
}
return ret;
@@ -362,8 +376,8 @@ static void blk_mig_reset_dirty_cursor(void)
}
}
static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
int is_async)
static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
BlkMigDevState *bmds, int is_async)
{
BlkMigBlock *blk;
int64_t total_sectors = bmds->total_sectors;
@@ -373,7 +387,7 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
if (bmds_aio_inflight(bmds, sector)) {
bdrv_drain_all();
qemu_aio_flush();
}
if (bdrv_get_dirty(bmds->bs, sector)) {
@@ -399,6 +413,9 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
nr_sectors, blk_mig_read_cb, blk);
if (!blk->aiocb) {
goto error;
}
block_mig_state.submitted++;
bmds_set_aio_inflight(bmds, sector, nr_sectors, 1);
} else {
@@ -422,20 +439,20 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds,
return (bmds->cur_dirty >= bmds->total_sectors);
error:
DPRINTF("Error reading sector %" PRId64 "\n", sector);
monitor_printf(mon, "Error reading sector %" PRId64 "\n", sector);
qemu_file_set_error(f, ret);
g_free(blk->buf);
g_free(blk);
return 0;
}
static int blk_mig_save_dirty_block(QEMUFile *f, int is_async)
static int blk_mig_save_dirty_block(Monitor *mon, QEMUFile *f, int is_async)
{
BlkMigDevState *bmds;
int ret = 0;
QSIMPLEQ_FOREACH(bmds, &block_mig_state.bmds_list, entry) {
if (mig_save_device_dirty(f, bmds, is_async) == 0) {
if (mig_save_device_dirty(mon, f, bmds, is_async) == 0) {
ret = 1;
break;
}
@@ -514,7 +531,7 @@ static int is_stage2_completed(void)
return 0;
}
static void blk_mig_cleanup(void)
static void blk_mig_cleanup(Monitor *mon)
{
BlkMigDevState *bmds;
BlkMigBlock *blk;
@@ -534,9 +551,11 @@ static void blk_mig_cleanup(void)
g_free(blk->buf);
g_free(blk);
}
monitor_printf(mon, "\n");
}
static int block_save_live(QEMUFile *f, int stage, void *opaque)
static int block_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
{
int ret;
@@ -544,7 +563,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
stage, block_mig_state.submitted, block_mig_state.transferred);
if (stage < 0) {
blk_mig_cleanup();
blk_mig_cleanup(mon);
return 0;
}
@@ -555,7 +574,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
}
if (stage == 1) {
init_blk_migration(f);
init_blk_migration(mon, f);
/* start track dirty blocks */
set_dirty_tracking(1);
@@ -565,7 +584,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
ret = qemu_file_get_error(f);
if (ret) {
blk_mig_cleanup();
blk_mig_cleanup(mon);
return ret;
}
@@ -578,12 +597,12 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
qemu_file_get_rate_limit(f)) {
if (block_mig_state.bulk_completed == 0) {
/* first finish the bulk phase */
if (blk_mig_save_bulked_block(f) == 0) {
if (blk_mig_save_bulked_block(mon, f) == 0) {
/* finished saving bulk on all devices */
block_mig_state.bulk_completed = 1;
}
} else {
if (blk_mig_save_dirty_block(f, 1) == 0) {
if (blk_mig_save_dirty_block(mon, f, 1) == 0) {
/* no more dirty blocks */
break;
}
@@ -594,7 +613,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
ret = qemu_file_get_error(f);
if (ret) {
blk_mig_cleanup();
blk_mig_cleanup(mon);
return ret;
}
}
@@ -604,8 +623,8 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
all async read completed */
assert(block_mig_state.submitted == 0);
while (blk_mig_save_dirty_block(f, 0) != 0);
blk_mig_cleanup();
while (blk_mig_save_dirty_block(mon, f, 0) != 0);
blk_mig_cleanup(mon);
/* report completion */
qemu_put_be64(f, (100 << BDRV_SECTOR_BITS) | BLK_MIG_FLAG_PROGRESS);
@@ -615,7 +634,7 @@ static int block_save_live(QEMUFile *f, int stage, void *opaque)
return ret;
}
DPRINTF("Block migration completed\n");
monitor_printf(mon, "Block migration completed\n");
}
qemu_put_be64(f, BLK_MIG_FLAG_EOS);

1435
block.c

File diff suppressed because it is too large Load Diff

72
block.h
View File

@@ -15,21 +15,14 @@ typedef struct BlockDriverInfo {
int cluster_size;
/* offset at which the VM state can be saved (0 if not possible) */
int64_t vm_state_offset;
bool is_dirty;
} BlockDriverInfo;
typedef struct BlockFragInfo {
uint64_t allocated_clusters;
uint64_t total_clusters;
uint64_t fragmented_clusters;
} BlockFragInfo;
typedef struct QEMUSnapshotInfo {
char id_str[128]; /* unique snapshot id */
/* the following fields are informative. They are not needed for
the consistency of the snapshot */
char name[256]; /* user chosen name */
uint64_t vm_state_size; /* VM state info size */
char name[256]; /* user choosen name */
uint32_t vm_state_size; /* VM state info size */
uint32_t date_sec; /* UTC date of the snapshot */
uint32_t date_nsec;
uint64_t vm_clock_nsec; /* VM clock relative to boot */
@@ -77,8 +70,6 @@ typedef struct BlockDevOps {
#define BDRV_O_NATIVE_AIO 0x0080 /* use native AIO instead of the thread pool */
#define BDRV_O_NO_BACKING 0x0100 /* don't open the backing file */
#define BDRV_O_NO_FLUSH 0x0200 /* disable flushing on this disk */
#define BDRV_O_COPY_ON_READ 0x0400 /* copy read backing sectors into image */
#define BDRV_O_INCOMING 0x0800 /* consistency hint for incoming migration */
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
@@ -93,25 +84,20 @@ typedef enum {
typedef enum {
BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
} BlockQMPEventAction;
} BlockMonEventAction;
void bdrv_iostatus_enable(BlockDriverState *bs);
void bdrv_iostatus_reset(BlockDriverState *bs);
void bdrv_iostatus_disable(BlockDriverState *bs);
bool bdrv_iostatus_is_enabled(const BlockDriverState *bs);
void bdrv_iostatus_set_err(BlockDriverState *bs, int error);
void bdrv_emit_qmp_error_event(const BlockDriverState *bdrv,
BlockQMPEventAction action, int is_read);
void bdrv_mon_event(const BlockDriverState *bdrv,
BlockMonEventAction action, int is_read);
void bdrv_info_print(Monitor *mon, const QObject *data);
void bdrv_info(Monitor *mon, QObject **ret_data);
void bdrv_stats_print(Monitor *mon, const QObject *data);
void bdrv_info_stats(Monitor *mon, QObject **ret_data);
/* disk I/O throttling */
void bdrv_io_limits_enable(BlockDriverState *bs);
void bdrv_io_limits_disable(BlockDriverState *bs);
bool bdrv_io_limits_enabled(BlockDriverState *bs);
void bdrv_init(void);
void bdrv_init_with_whitelist(void);
BlockDriver *bdrv_find_protocol(const char *filename);
@@ -122,7 +108,6 @@ int bdrv_create(BlockDriver *drv, const char* filename,
int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
BlockDriverState *bdrv_new(const char *device_name);
void bdrv_make_anon(BlockDriverState *bs);
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
void bdrv_delete(BlockDriverState *bs);
int bdrv_parse_cache_flags(const char *mode, int *flags);
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
@@ -151,29 +136,15 @@ int bdrv_pwrite_sync(BlockDriverState *bs, int64_t offset,
const void *buf, int count);
int coroutine_fn bdrv_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov);
int coroutine_fn bdrv_co_copy_on_readv(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov);
/*
* Efficiently zero a region of the disk image. Note that this is a regular
* I/O request like read or write and should have a reasonable size. This
* function is not suitable for zeroing the entire image in a single request
* because it may allocate memory for the entire region.
*/
int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
int nb_sectors);
int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum);
BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
const char *backing_file);
int bdrv_truncate(BlockDriverState *bs, int64_t offset);
int64_t bdrv_getlength(BlockDriverState *bs);
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
void bdrv_guess_geometry(BlockDriverState *bs, int *pcyls, int *pheads, int *psecs);
int bdrv_commit(BlockDriverState *bs);
int bdrv_commit_all(void);
void bdrv_commit_all(void);
int bdrv_change_backing_file(BlockDriverState *bs,
const char *backing_file, const char *backing_fmt);
void bdrv_register(BlockDriver *bdrv);
@@ -183,12 +154,13 @@ typedef struct BdrvCheckResult {
int corruptions;
int leaks;
int check_errors;
BlockFragInfo bfi;
} BdrvCheckResult;
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
/* async block I/O */
typedef struct BlockDriverAIOCB BlockDriverAIOCB;
typedef void BlockDriverCompletionFunc(void *opaque, int ret);
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
int sector_num);
BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
@@ -229,14 +201,11 @@ BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
void bdrv_invalidate_cache(BlockDriverState *bs);
void bdrv_invalidate_cache_all(void);
void bdrv_clear_incoming_migration_all(void);
/* Ensure contents are flushed to disk. */
int bdrv_flush(BlockDriverState *bs);
int coroutine_fn bdrv_co_flush(BlockDriverState *bs);
void bdrv_flush_all(void);
void bdrv_close_all(void);
void bdrv_drain_all(void);
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
@@ -262,17 +231,9 @@ typedef enum FDriveType {
FDRIVE_DRV_NONE = 0x03, /* No drive connected */
} FDriveType;
typedef enum FDriveRate {
FDRIVE_RATE_500K = 0x00, /* 500 Kbps */
FDRIVE_RATE_300K = 0x01, /* 300 Kbps */
FDRIVE_RATE_250K = 0x02, /* 250 Kbps */
FDRIVE_RATE_1M = 0x03, /* 1 Mbps */
} FDriveRate;
void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
int *max_track, int *last_sect,
FDriveType drive_in, FDriveType *drive,
FDriveRate *rate);
FDriveType drive_in, FDriveType *drive);
int bdrv_get_translation_hint(BlockDriverState *bs);
void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
BlockErrorAction on_write_error);
@@ -283,7 +244,7 @@ int bdrv_enable_write_cache(BlockDriverState *bs);
int bdrv_is_inserted(BlockDriverState *bs);
int bdrv_media_changed(BlockDriverState *bs);
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
void bdrv_eject(BlockDriverState *bs, int eject_flag);
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);
BlockDriverState *bdrv_find(const char *name);
BlockDriverState *bdrv_next(BlockDriverState *bs);
@@ -303,8 +264,6 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
void bdrv_get_backing_filename(BlockDriverState *bs,
char *filename, int filename_size);
void bdrv_get_full_backing_filename(BlockDriverState *bs,
char *dest, size_t sz);
int bdrv_can_snapshot(BlockDriverState *bs);
int bdrv_is_snapshot(BlockDriverState *bs);
BlockDriverState *bdrv_snapshots(void);
@@ -346,9 +305,6 @@ void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
int nr_sectors);
int64_t bdrv_get_dirty_count(BlockDriverState *bs);
void bdrv_enable_copy_on_read(BlockDriverState *bs);
void bdrv_disable_copy_on_read(BlockDriverState *bs);
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
int bdrv_in_use(BlockDriverState *bs);
@@ -448,10 +404,10 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
#define DEFINE_BLOCK_PROPERTIES(_state, _conf) \
DEFINE_PROP_DRIVE("drive", _state, _conf.bs), \
DEFINE_PROP_BLOCKSIZE("logical_block_size", _state, \
_conf.logical_block_size, 512), \
DEFINE_PROP_BLOCKSIZE("physical_block_size", _state, \
_conf.physical_block_size, 512), \
DEFINE_PROP_UINT16("logical_block_size", _state, \
_conf.logical_block_size, 512), \
DEFINE_PROP_UINT16("physical_block_size", _state, \
_conf.physical_block_size, 512), \
DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \
DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \
DEFINE_PROP_INT32("bootindex", _state, _conf.bootindex, -1), \

View File

@@ -292,10 +292,10 @@ static int blkdebug_open(BlockDriverState *bs, const char *filename, int flags)
return -EINVAL;
}
config = g_strdup(filename);
config = strdup(filename);
config[c - filename] = '\0';
ret = read_config(s, config);
g_free(config);
free(config);
if (ret < 0) {
return ret;
}
@@ -397,6 +397,12 @@ static void blkdebug_close(BlockDriverState *bs)
}
}
static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque)
{
return bdrv_aio_flush(bs->file, cb, opaque);
}
static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
BlkdebugVars *old_vars)
{
@@ -446,6 +452,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_aio_readv = blkdebug_aio_readv,
.bdrv_aio_writev = blkdebug_aio_writev,
.bdrv_aio_flush = blkdebug_aio_flush,
.bdrv_debug_event = blkdebug_debug_event,
};

View File

@@ -87,10 +87,10 @@ static int blkverify_open(BlockDriverState *bs, const char *filename, int flags)
return -EINVAL;
}
raw = g_strdup(filename);
raw = strdup(filename);
raw[c - filename] = '\0';
ret = bdrv_file_open(&bs->file, raw, flags);
g_free(raw);
free(raw);
if (ret < 0) {
return ret;
}
@@ -310,10 +310,14 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
blkverify_aio_cb, acb);
bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
blkverify_aio_cb, acb);
if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors,
blkverify_aio_cb, acb)) {
blkverify_aio_cb(acb, -EIO);
}
if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors,
blkverify_aio_cb, acb)) {
blkverify_aio_cb(acb, -EIO);
}
return &acb->common;
}
@@ -325,10 +329,14 @@ static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs,
BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
nb_sectors, cb, opaque);
bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
blkverify_aio_cb, acb);
bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
blkverify_aio_cb, acb);
if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors,
blkverify_aio_cb, acb)) {
blkverify_aio_cb(acb, -EIO);
}
if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors,
blkverify_aio_cb, acb)) {
blkverify_aio_cb(acb, -EIO);
}
return &acb->common;
}

View File

@@ -64,26 +64,15 @@ static int cow_open(BlockDriverState *bs, int flags)
struct cow_header_v2 cow_header;
int bitmap_size;
int64_t size;
int ret;
/* see if it is a cow image */
ret = bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header));
if (ret < 0) {
if (bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)) !=
sizeof(cow_header)) {
goto fail;
}
if (be32_to_cpu(cow_header.magic) != COW_MAGIC) {
ret = -EINVAL;
goto fail;
}
if (be32_to_cpu(cow_header.version) != COW_VERSION) {
char version[64];
snprintf(version, sizeof(version),
"COW version %d", cow_header.version);
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
bs->device_name, "cow", version);
ret = -ENOTSUP;
if (be32_to_cpu(cow_header.magic) != COW_MAGIC ||
be32_to_cpu(cow_header.version) != COW_VERSION) {
goto fail;
}
@@ -99,11 +88,11 @@ static int cow_open(BlockDriverState *bs, int flags)
qemu_co_mutex_init(&s->lock);
return 0;
fail:
return ret;
return -1;
}
/*
* XXX(hch): right now these functions are extremely inefficient.
* XXX(hch): right now these functions are extremly ineffcient.
* We should just read the whole bitmap we'll need in one go instead.
*/
static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum)
@@ -143,8 +132,8 @@ static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum)
/* Return true if first block has been changed (ie. current version is
* in COW file). Set the number of continuous blocks for which that
* is true. */
static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *num_same)
static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *num_same)
{
int changed;
@@ -182,30 +171,28 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
return error;
}
static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
static int cow_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
BDRVCowState *s = bs->opaque;
int ret, n;
while (nb_sectors > 0) {
if (bdrv_co_is_allocated(bs, sector_num, nb_sectors, &n)) {
if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) {
ret = bdrv_pread(bs->file,
s->cow_sectors_offset + sector_num * 512,
buf, n * 512);
if (ret < 0) {
return ret;
}
if (ret != n * 512)
return -1;
} else {
if (bs->backing_hd) {
/* read from the base image */
ret = bdrv_read(bs->backing_hd, sector_num, buf, n);
if (ret < 0) {
return ret;
}
if (ret < 0)
return -1;
} else {
memset(buf, 0, n * 512);
}
memset(buf, 0, n * 512);
}
}
nb_sectors -= n;
sector_num += n;
@@ -233,9 +220,8 @@ static int cow_write(BlockDriverState *bs, int64_t sector_num,
ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512,
buf, nb_sectors * 512);
if (ret < 0) {
return ret;
}
if (ret != nb_sectors * 512)
return -1;
return cow_update_bitmap(bs, sector_num, nb_sectors);
}
@@ -257,12 +243,12 @@ static void cow_close(BlockDriverState *bs)
static int cow_create(const char *filename, QEMUOptionParameter *options)
{
int fd, cow_fd;
struct cow_header_v2 cow_header;
struct stat st;
int64_t image_sectors = 0;
const char *image_filename = NULL;
int ret;
BlockDriverState *cow_bs;
/* Read out options */
while (options && options->name) {
@@ -274,16 +260,10 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
options++;
}
ret = bdrv_create_file(filename, options);
if (ret < 0) {
return ret;
}
ret = bdrv_file_open(&cow_bs, filename, BDRV_O_RDWR);
if (ret < 0) {
return ret;
}
cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
0644);
if (cow_fd < 0)
return -errno;
memset(&cow_header, 0, sizeof(cow_header));
cow_header.magic = cpu_to_be32(COW_MAGIC);
cow_header.version = cpu_to_be32(COW_VERSION);
@@ -291,9 +271,16 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
/* Note: if no file, we put a dummy mtime */
cow_header.mtime = cpu_to_be32(0);
if (stat(image_filename, &st) != 0) {
fd = open(image_filename, O_RDONLY | O_BINARY);
if (fd < 0) {
close(cow_fd);
goto mtime_fail;
}
if (fstat(fd, &st) != 0) {
close(fd);
goto mtime_fail;
}
close(fd);
cow_header.mtime = cpu_to_be32(st.st_mtime);
mtime_fail:
pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file),
@@ -301,23 +288,29 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
}
cow_header.sectorsize = cpu_to_be32(512);
cow_header.size = cpu_to_be64(image_sectors * 512);
ret = bdrv_pwrite(cow_bs, 0, &cow_header, sizeof(cow_header));
if (ret < 0) {
ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header));
if (ret != sizeof(cow_header)) {
ret = -errno;
goto exit;
}
/* resize to include at least all the bitmap */
ret = bdrv_truncate(cow_bs,
sizeof(cow_header) + ((image_sectors + 7) >> 3));
if (ret < 0) {
ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3));
if (ret) {
ret = -errno;
goto exit;
}
exit:
bdrv_delete(cow_bs);
close(cow_fd);
return ret;
}
static coroutine_fn int cow_co_flush(BlockDriverState *bs)
{
return bdrv_co_flush(bs->file);
}
static QEMUOptionParameter cow_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -343,7 +336,8 @@ static BlockDriver bdrv_cow = {
.bdrv_read = cow_co_read,
.bdrv_write = cow_co_write,
.bdrv_co_is_allocated = cow_co_is_allocated,
.bdrv_co_flush_to_disk = cow_co_flush,
.bdrv_is_allocated = cow_is_allocated,
.create_options = cow_create_options,
};

View File

@@ -89,17 +89,19 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
switch (action) {
case CURL_POLL_IN:
qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush, s);
qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush,
NULL, s);
break;
case CURL_POLL_OUT:
qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush, s);
qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush,
NULL, s);
break;
case CURL_POLL_INOUT:
qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do,
curl_aio_flush, s);
curl_aio_flush, NULL, s);
break;
case CURL_POLL_REMOVE:
qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL);
qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL);
break;
}
@@ -280,7 +282,7 @@ static CURLState *curl_init_state(BDRVCURLState *s)
break;
}
if (!state) {
g_usleep(100);
usleep(100);
curl_multi_do(s);
}
} while(!state);
@@ -507,6 +509,10 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
if (!acb) {
return NULL;
}
acb->qiov = qiov;
acb->sector_num = sector_num;
acb->nb_sectors = nb_sectors;

View File

@@ -108,7 +108,7 @@ iscsi_set_events(IscsiLun *iscsilun)
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), iscsi_process_read,
(iscsi_which_events(iscsi) & POLLOUT)
? iscsi_process_write : NULL,
iscsi_process_flush, iscsilun);
iscsi_process_flush, NULL, iscsilun);
}
static void
@@ -383,65 +383,6 @@ iscsi_aio_flush(BlockDriverState *bs,
return &acb->common;
}
static void
iscsi_unmap_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *opaque)
{
IscsiAIOCB *acb = opaque;
if (acb->canceled != 0) {
qemu_aio_release(acb);
scsi_free_scsi_task(acb->task);
acb->task = NULL;
return;
}
acb->status = 0;
if (status < 0) {
error_report("Failed to unmap data on iSCSI lun. %s",
iscsi_get_error(iscsi));
acb->status = -EIO;
}
iscsi_schedule_bh(iscsi_readv_writev_bh_cb, acb);
scsi_free_scsi_task(acb->task);
acb->task = NULL;
}
static BlockDriverAIOCB *
iscsi_aio_discard(BlockDriverState *bs,
int64_t sector_num, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
IscsiLun *iscsilun = bs->opaque;
struct iscsi_context *iscsi = iscsilun->iscsi;
IscsiAIOCB *acb;
struct unmap_list list[1];
acb = qemu_aio_get(&iscsi_aio_pool, bs, cb, opaque);
acb->iscsilun = iscsilun;
acb->canceled = 0;
list[0].lba = sector_qemu2lun(sector_num, iscsilun);
list[0].num = nb_sectors * BDRV_SECTOR_SIZE / iscsilun->block_size;
acb->task = iscsi_unmap_task(iscsi, iscsilun->lun,
0, 0, &list[0], 1,
iscsi_unmap_cb,
acb);
if (acb->task == NULL) {
error_report("iSCSI: Failed to send unmap command. %s",
iscsi_get_error(iscsi));
qemu_aio_release(acb);
return NULL;
}
iscsi_set_events(iscsilun);
return &acb->common;
}
static int64_t
iscsi_getlength(BlockDriverState *bs)
{
@@ -455,11 +396,11 @@ iscsi_getlength(BlockDriverState *bs)
}
static void
iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
iscsi_readcapacity10_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *opaque)
{
struct IscsiTask *itask = opaque;
struct scsi_readcapacity16 *rc16;
struct scsi_readcapacity10 *rc10;
struct scsi_task *task = command_data;
if (status != 0) {
@@ -471,25 +412,26 @@ iscsi_readcapacity16_cb(struct iscsi_context *iscsi, int status,
return;
}
rc16 = scsi_datain_unmarshall(task);
if (rc16 == NULL) {
error_report("iSCSI: Failed to unmarshall readcapacity16 data.");
rc10 = scsi_datain_unmarshall(task);
if (rc10 == NULL) {
error_report("iSCSI: Failed to unmarshall readcapacity10 data.");
itask->status = 1;
itask->complete = 1;
scsi_free_scsi_task(task);
return;
}
itask->iscsilun->block_size = rc16->block_length;
itask->iscsilun->num_blocks = rc16->returned_lba + 1;
itask->bs->total_sectors = itask->iscsilun->num_blocks *
itask->iscsilun->block_size / BDRV_SECTOR_SIZE ;
itask->iscsilun->block_size = rc10->block_size;
itask->iscsilun->num_blocks = rc10->lba;
itask->bs->total_sectors = (uint64_t)rc10->lba *
rc10->block_size / BDRV_SECTOR_SIZE ;
itask->status = 0;
itask->complete = 1;
scsi_free_scsi_task(task);
}
static void
iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
void *opaque)
@@ -503,119 +445,16 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data,
return;
}
task = iscsi_readcapacity16_task(iscsi, itask->iscsilun->lun,
iscsi_readcapacity16_cb, opaque);
task = iscsi_readcapacity10_task(iscsi, itask->iscsilun->lun, 0, 0,
iscsi_readcapacity10_cb, opaque);
if (task == NULL) {
error_report("iSCSI: failed to send readcapacity16 command.");
error_report("iSCSI: failed to send readcapacity command.");
itask->status = 1;
itask->complete = 1;
return;
}
}
static int parse_chap(struct iscsi_context *iscsi, const char *target)
{
QemuOptsList *list;
QemuOpts *opts;
const char *user = NULL;
const char *password = NULL;
list = qemu_find_opts("iscsi");
if (!list) {
return 0;
}
opts = qemu_opts_find(list, target);
if (opts == NULL) {
opts = QTAILQ_FIRST(&list->head);
if (!opts) {
return 0;
}
}
user = qemu_opt_get(opts, "user");
if (!user) {
return 0;
}
password = qemu_opt_get(opts, "password");
if (!password) {
error_report("CHAP username specified but no password was given");
return -1;
}
if (iscsi_set_initiator_username_pwd(iscsi, user, password)) {
error_report("Failed to set initiator username and password");
return -1;
}
return 0;
}
static void parse_header_digest(struct iscsi_context *iscsi, const char *target)
{
QemuOptsList *list;
QemuOpts *opts;
const char *digest = NULL;
list = qemu_find_opts("iscsi");
if (!list) {
return;
}
opts = qemu_opts_find(list, target);
if (opts == NULL) {
opts = QTAILQ_FIRST(&list->head);
if (!opts) {
return;
}
}
digest = qemu_opt_get(opts, "header-digest");
if (!digest) {
return;
}
if (!strcmp(digest, "CRC32C")) {
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C);
} else if (!strcmp(digest, "NONE")) {
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE);
} else if (!strcmp(digest, "CRC32C-NONE")) {
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
} else if (!strcmp(digest, "NONE-CRC32C")) {
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
} else {
error_report("Invalid header-digest setting : %s", digest);
}
}
static char *parse_initiator_name(const char *target)
{
QemuOptsList *list;
QemuOpts *opts;
const char *name = NULL;
list = qemu_find_opts("iscsi");
if (!list) {
return g_strdup("iqn.2008-11.org.linux-kvm");
}
opts = qemu_opts_find(list, target);
if (opts == NULL) {
opts = QTAILQ_FIRST(&list->head);
if (!opts) {
return g_strdup("iqn.2008-11.org.linux-kvm");
}
}
name = qemu_opt_get(opts, "initiator-name");
if (!name) {
return g_strdup("iqn.2008-11.org.linux-kvm");
}
return g_strdup(name);
}
/*
* We support iscsi url's on the form
* iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun>
@@ -626,7 +465,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
struct iscsi_context *iscsi = NULL;
struct iscsi_url *iscsi_url = NULL;
struct IscsiTask task;
char *initiator_name = NULL;
int ret;
if ((BDRV_SECTOR_SIZE % 512) != 0) {
@@ -636,6 +474,16 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
return -EINVAL;
}
memset(iscsilun, 0, sizeof(IscsiLun));
/* Should really append the KVM name after the ':' here */
iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:");
if (iscsi == NULL) {
error_report("iSCSI: Failed to create iSCSI context.");
ret = -ENOMEM;
goto failed;
}
iscsi_url = iscsi_parse_full_url(iscsi, filename);
if (iscsi_url == NULL) {
error_report("Failed to parse URL : %s %s", filename,
@@ -644,17 +492,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
goto failed;
}
memset(iscsilun, 0, sizeof(IscsiLun));
initiator_name = parse_initiator_name(iscsi_url->target);
iscsi = iscsi_create_context(initiator_name);
if (iscsi == NULL) {
error_report("iSCSI: Failed to create iSCSI context.");
ret = -ENOMEM;
goto failed;
}
if (iscsi_set_targetname(iscsi, iscsi_url->target)) {
error_report("iSCSI: Failed to set target name.");
ret = -EINVAL;
@@ -670,14 +507,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
goto failed;
}
}
/* check if we got CHAP username/password via the options */
if (parse_chap(iscsi, iscsi_url->target) != 0) {
error_report("iSCSI: Failed to set CHAP user/password");
ret = -EINVAL;
goto failed;
}
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
error_report("iSCSI: Failed to set session type to normal.");
ret = -EINVAL;
@@ -686,9 +515,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
/* check if we got HEADER_DIGEST via the options */
parse_header_digest(iscsi, iscsi_url->target);
task.iscsilun = iscsilun;
task.status = 0;
task.complete = 0;
@@ -722,9 +548,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags)
return 0;
failed:
if (initiator_name != NULL) {
g_free(initiator_name);
}
if (iscsi_url != NULL) {
iscsi_destroy_url(iscsi_url);
}
@@ -740,7 +563,7 @@ static void iscsi_close(BlockDriverState *bs)
IscsiLun *iscsilun = bs->opaque;
struct iscsi_context *iscsi = iscsilun->iscsi;
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL);
qemu_aio_set_fd_handler(iscsi_get_fd(iscsi), NULL, NULL, NULL, NULL, NULL);
iscsi_destroy_context(iscsi);
memset(iscsilun, 0, sizeof(IscsiLun));
}
@@ -758,8 +581,6 @@ static BlockDriver bdrv_iscsi = {
.bdrv_aio_readv = iscsi_aio_readv,
.bdrv_aio_writev = iscsi_aio_writev,
.bdrv_aio_flush = iscsi_aio_flush,
.bdrv_aio_discard = iscsi_aio_discard,
};
static void iscsi_block_init(void)

View File

@@ -46,25 +46,14 @@
#define logout(fmt, ...) ((void)0)
#endif
#define MAX_NBD_REQUESTS 16
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
typedef struct BDRVNBDState {
CoMutex lock;
int sock;
uint32_t nbdflags;
off_t size;
size_t blocksize;
char *export_name; /* An NBD server may export several devices */
CoMutex send_mutex;
CoMutex free_sema;
Coroutine *send_coroutine;
int in_flight;
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
struct nbd_reply reply;
/* If it begins with '/', this is a UNIX domain socket. Otherwise,
* it's a string of the form <hostname|ip4|\[ip6\]>:port
*/
@@ -117,141 +106,6 @@ out:
return err;
}
static void nbd_coroutine_start(BDRVNBDState *s, struct nbd_request *request)
{
int i;
/* Poor man semaphore. The free_sema is locked when no other request
* can be accepted, and unlocked after receiving one reply. */
if (s->in_flight >= MAX_NBD_REQUESTS - 1) {
qemu_co_mutex_lock(&s->free_sema);
assert(s->in_flight < MAX_NBD_REQUESTS);
}
s->in_flight++;
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
if (s->recv_coroutine[i] == NULL) {
s->recv_coroutine[i] = qemu_coroutine_self();
break;
}
}
assert(i < MAX_NBD_REQUESTS);
request->handle = INDEX_TO_HANDLE(s, i);
}
static int nbd_have_request(void *opaque)
{
BDRVNBDState *s = opaque;
return s->in_flight > 0;
}
static void nbd_reply_ready(void *opaque)
{
BDRVNBDState *s = opaque;
uint64_t i;
int ret;
if (s->reply.handle == 0) {
/* No reply already in flight. Fetch a header. It is possible
* that another thread has done the same thing in parallel, so
* the socket is not readable anymore.
*/
ret = nbd_receive_reply(s->sock, &s->reply);
if (ret == -EAGAIN) {
return;
}
if (ret < 0) {
s->reply.handle = 0;
goto fail;
}
}
/* There's no need for a mutex on the receive side, because the
* handler acts as a synchronization point and ensures that only
* one coroutine is called until the reply finishes. */
i = HANDLE_TO_INDEX(s, s->reply.handle);
if (i >= MAX_NBD_REQUESTS) {
goto fail;
}
if (s->recv_coroutine[i]) {
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
return;
}
fail:
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
if (s->recv_coroutine[i]) {
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
}
}
}
static void nbd_restart_write(void *opaque)
{
BDRVNBDState *s = opaque;
qemu_coroutine_enter(s->send_coroutine, NULL);
}
static int nbd_co_send_request(BDRVNBDState *s, struct nbd_request *request,
struct iovec *iov, int offset)
{
int rc, ret;
qemu_co_mutex_lock(&s->send_mutex);
s->send_coroutine = qemu_coroutine_self();
qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, nbd_restart_write,
nbd_have_request, s);
rc = nbd_send_request(s->sock, request);
if (rc >= 0 && iov) {
ret = qemu_co_sendv(s->sock, iov, request->len, offset);
if (ret != request->len) {
return -EIO;
}
}
qemu_aio_set_fd_handler(s->sock, nbd_reply_ready, NULL,
nbd_have_request, s);
s->send_coroutine = NULL;
qemu_co_mutex_unlock(&s->send_mutex);
return rc;
}
static void nbd_co_receive_reply(BDRVNBDState *s, struct nbd_request *request,
struct nbd_reply *reply,
struct iovec *iov, int offset)
{
int ret;
/* Wait until we're woken up by the read handler. TODO: perhaps
* peek at the next reply and avoid yielding if it's ours? */
qemu_coroutine_yield();
*reply = s->reply;
if (reply->handle != request->handle) {
reply->error = EIO;
} else {
if (iov && reply->error == 0) {
ret = qemu_co_recvv(s->sock, iov, request->len, offset);
if (ret != request->len) {
reply->error = EIO;
}
}
/* Tell the read handler to read another header. */
s->reply.handle = 0;
}
}
static void nbd_coroutine_end(BDRVNBDState *s, struct nbd_request *request)
{
int i = HANDLE_TO_INDEX(s, request->handle);
s->recv_coroutine[i] = NULL;
if (s->in_flight-- == MAX_NBD_REQUESTS) {
qemu_co_mutex_unlock(&s->free_sema);
}
}
static int nbd_establish_connection(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
@@ -267,7 +121,7 @@ static int nbd_establish_connection(BlockDriverState *bs)
}
/* Failed to establish connection */
if (sock < 0) {
if (sock == -1) {
logout("Failed to establish connection to NBD server\n");
return -errno;
}
@@ -275,17 +129,14 @@ static int nbd_establish_connection(BlockDriverState *bs)
/* NBD handshake */
ret = nbd_receive_negotiate(sock, s->export_name, &s->nbdflags, &size,
&blocksize);
if (ret < 0) {
if (ret == -1) {
logout("Failed to negotiate with the NBD server\n");
closesocket(sock);
return ret;
return -errno;
}
/* Now that we're connected, set the socket to be non-blocking and
* kick the reply mechanism. */
/* Now that we're connected, set the socket to be non-blocking */
socket_set_nonblock(sock);
qemu_aio_set_fd_handler(sock, nbd_reply_ready, NULL,
nbd_have_request, s);
s->sock = sock;
s->size = size;
@@ -301,11 +152,11 @@ static void nbd_teardown_connection(BlockDriverState *bs)
struct nbd_request request;
request.type = NBD_CMD_DISC;
request.handle = (uint64_t)(intptr_t)bs;
request.from = 0;
request.len = 0;
nbd_send_request(s->sock, &request);
qemu_aio_set_fd_handler(s->sock, NULL, NULL, NULL, NULL);
closesocket(s->sock);
}
@@ -314,9 +165,6 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
BDRVNBDState *s = bs->opaque;
int result;
qemu_co_mutex_init(&s->send_mutex);
qemu_co_mutex_init(&s->free_sema);
/* Pop the config into our state object. Exit if invalid. */
result = nbd_config(s, filename, flags);
if (result != 0) {
@@ -328,154 +176,90 @@ static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
*/
result = nbd_establish_connection(bs);
qemu_co_mutex_init(&s->lock);
return result;
}
static int nbd_co_readv_1(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov,
int offset)
static int nbd_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
BDRVNBDState *s = bs->opaque;
struct nbd_request request;
struct nbd_reply reply;
ssize_t ret;
request.type = NBD_CMD_READ;
request.from = sector_num * 512;
request.len = nb_sectors * 512;
nbd_coroutine_start(s, &request);
ret = nbd_co_send_request(s, &request, NULL, 0);
if (ret < 0) {
reply.error = -ret;
} else {
nbd_co_receive_reply(s, &request, &reply, qiov->iov, offset);
}
nbd_coroutine_end(s, &request);
return -reply.error;
}
static int nbd_co_writev_1(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov,
int offset)
{
BDRVNBDState *s = bs->opaque;
struct nbd_request request;
struct nbd_reply reply;
ssize_t ret;
request.type = NBD_CMD_WRITE;
if (!bdrv_enable_write_cache(bs) && (s->nbdflags & NBD_FLAG_SEND_FUA)) {
request.type |= NBD_CMD_FLAG_FUA;
}
request.from = sector_num * 512;
request.len = nb_sectors * 512;
nbd_coroutine_start(s, &request);
ret = nbd_co_send_request(s, &request, qiov->iov, offset);
if (ret < 0) {
reply.error = -ret;
} else {
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
}
nbd_coroutine_end(s, &request);
return -reply.error;
}
/* qemu-nbd has a limit of slightly less than 1M per request. Try to
* remain aligned to 4K. */
#define NBD_MAX_SECTORS 2040
static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
int offset = 0;
int ret;
while (nb_sectors > NBD_MAX_SECTORS) {
ret = nbd_co_readv_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
if (ret < 0) {
return ret;
}
offset += NBD_MAX_SECTORS * 512;
sector_num += NBD_MAX_SECTORS;
nb_sectors -= NBD_MAX_SECTORS;
}
return nbd_co_readv_1(bs, sector_num, nb_sectors, qiov, offset);
}
static int nbd_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
int offset = 0;
int ret;
while (nb_sectors > NBD_MAX_SECTORS) {
ret = nbd_co_writev_1(bs, sector_num, NBD_MAX_SECTORS, qiov, offset);
if (ret < 0) {
return ret;
}
offset += NBD_MAX_SECTORS * 512;
sector_num += NBD_MAX_SECTORS;
nb_sectors -= NBD_MAX_SECTORS;
}
return nbd_co_writev_1(bs, sector_num, nb_sectors, qiov, offset);
}
static int nbd_co_flush(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
struct nbd_request request;
struct nbd_reply reply;
ssize_t ret;
if (!(s->nbdflags & NBD_FLAG_SEND_FLUSH)) {
return 0;
}
request.type = NBD_CMD_FLUSH;
if (s->nbdflags & NBD_FLAG_SEND_FUA) {
request.type |= NBD_CMD_FLAG_FUA;
}
request.from = 0;
request.len = 0;
nbd_coroutine_start(s, &request);
ret = nbd_co_send_request(s, &request, NULL, 0);
if (ret < 0) {
reply.error = -ret;
} else {
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
}
nbd_coroutine_end(s, &request);
return -reply.error;
}
static int nbd_co_discard(BlockDriverState *bs, int64_t sector_num,
int nb_sectors)
{
BDRVNBDState *s = bs->opaque;
struct nbd_request request;
struct nbd_reply reply;
ssize_t ret;
if (!(s->nbdflags & NBD_FLAG_SEND_TRIM)) {
return 0;
}
request.type = NBD_CMD_TRIM;
request.handle = (uint64_t)(intptr_t)bs;
request.from = sector_num * 512;;
request.len = nb_sectors * 512;
nbd_coroutine_start(s, &request);
ret = nbd_co_send_request(s, &request, NULL, 0);
if (ret < 0) {
reply.error = -ret;
} else {
nbd_co_receive_reply(s, &request, &reply, NULL, 0);
}
nbd_coroutine_end(s, &request);
return -reply.error;
if (nbd_send_request(s->sock, &request) == -1)
return -errno;
if (nbd_receive_reply(s->sock, &reply) == -1)
return -errno;
if (reply.error !=0)
return -reply.error;
if (reply.handle != request.handle)
return -EIO;
if (nbd_wr_sync(s->sock, buf, request.len, 1) != request.len)
return -EIO;
return 0;
}
static int nbd_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
BDRVNBDState *s = bs->opaque;
struct nbd_request request;
struct nbd_reply reply;
request.type = NBD_CMD_WRITE;
request.handle = (uint64_t)(intptr_t)bs;
request.from = sector_num * 512;;
request.len = nb_sectors * 512;
if (nbd_send_request(s->sock, &request) == -1)
return -errno;
if (nbd_wr_sync(s->sock, (uint8_t*)buf, request.len, 0) != request.len)
return -EIO;
if (nbd_receive_reply(s->sock, &reply) == -1)
return -errno;
if (reply.error !=0)
return -reply.error;
if (reply.handle != request.handle)
return -EIO;
return 0;
}
static coroutine_fn int nbd_co_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
int ret;
BDRVNBDState *s = bs->opaque;
qemu_co_mutex_lock(&s->lock);
ret = nbd_read(bs, sector_num, buf, nb_sectors);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
static coroutine_fn int nbd_co_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
int ret;
BDRVNBDState *s = bs->opaque;
qemu_co_mutex_lock(&s->lock);
ret = nbd_write(bs, sector_num, buf, nb_sectors);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
static void nbd_close(BlockDriverState *bs)
@@ -495,16 +279,14 @@ static int64_t nbd_getlength(BlockDriverState *bs)
}
static BlockDriver bdrv_nbd = {
.format_name = "nbd",
.instance_size = sizeof(BDRVNBDState),
.bdrv_file_open = nbd_open,
.bdrv_co_readv = nbd_co_readv,
.bdrv_co_writev = nbd_co_writev,
.bdrv_close = nbd_close,
.bdrv_co_flush_to_os = nbd_co_flush,
.bdrv_co_discard = nbd_co_discard,
.bdrv_getlength = nbd_getlength,
.protocol_name = "nbd",
.format_name = "nbd",
.instance_size = sizeof(BDRVNBDState),
.bdrv_file_open = nbd_open,
.bdrv_read = nbd_co_read,
.bdrv_write = nbd_co_write,
.bdrv_close = nbd_close,
.bdrv_getlength = nbd_getlength,
.protocol_name = "nbd",
};
static void bdrv_nbd_init(void)

View File

@@ -95,13 +95,11 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
static int qcow_open(BlockDriverState *bs, int flags)
{
BDRVQcowState *s = bs->opaque;
int len, i, shift, ret;
int len, i, shift;
QCowHeader header;
ret = bdrv_pread(bs->file, 0, &header, sizeof(header));
if (ret < 0) {
if (bdrv_pread(bs->file, 0, &header, sizeof(header)) != sizeof(header))
goto fail;
}
be32_to_cpus(&header.magic);
be32_to_cpus(&header.version);
be64_to_cpus(&header.backing_file_offset);
@@ -111,31 +109,15 @@ static int qcow_open(BlockDriverState *bs, int flags)
be32_to_cpus(&header.crypt_method);
be64_to_cpus(&header.l1_table_offset);
if (header.magic != QCOW_MAGIC) {
ret = -EINVAL;
if (header.magic != QCOW_MAGIC || header.version != QCOW_VERSION)
goto fail;
}
if (header.version != QCOW_VERSION) {
char version[64];
snprintf(version, sizeof(version), "QCOW version %d", header.version);
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
bs->device_name, "qcow", version);
ret = -ENOTSUP;
if (header.size <= 1 || header.cluster_bits < 9)
goto fail;
}
if (header.size <= 1 || header.cluster_bits < 9) {
ret = -EINVAL;
if (header.crypt_method > QCOW_CRYPT_AES)
goto fail;
}
if (header.crypt_method > QCOW_CRYPT_AES) {
ret = -EINVAL;
goto fail;
}
s->crypt_method_header = header.crypt_method;
if (s->crypt_method_header) {
if (s->crypt_method_header)
bs->encrypted = 1;
}
s->cluster_bits = header.cluster_bits;
s->cluster_size = 1 << s->cluster_bits;
s->cluster_sectors = 1 << (s->cluster_bits - 9);
@@ -150,33 +132,33 @@ static int qcow_open(BlockDriverState *bs, int flags)
s->l1_table_offset = header.l1_table_offset;
s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
ret = bdrv_pread(bs->file, s->l1_table_offset, s->l1_table,
s->l1_size * sizeof(uint64_t));
if (ret < 0) {
if (!s->l1_table)
goto fail;
if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, s->l1_size * sizeof(uint64_t)) !=
s->l1_size * sizeof(uint64_t))
goto fail;
}
for(i = 0;i < s->l1_size; i++) {
be64_to_cpus(&s->l1_table[i]);
}
/* alloc L2 cache */
s->l2_cache = g_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
if (!s->l2_cache)
goto fail;
s->cluster_cache = g_malloc(s->cluster_size);
if (!s->cluster_cache)
goto fail;
s->cluster_data = g_malloc(s->cluster_size);
if (!s->cluster_data)
goto fail;
s->cluster_cache_offset = -1;
/* read the backing file name */
if (header.backing_file_offset != 0) {
len = header.backing_file_size;
if (len > 1023) {
if (len > 1023)
len = 1023;
}
ret = bdrv_pread(bs->file, header.backing_file_offset,
bs->backing_file, len);
if (ret < 0) {
if (bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len) != len)
goto fail;
}
bs->backing_file[len] = '\0';
}
@@ -194,7 +176,7 @@ static int qcow_open(BlockDriverState *bs, int flags)
g_free(s->l2_cache);
g_free(s->cluster_cache);
g_free(s->cluster_data);
return ret;
return -1;
}
static int qcow_set_key(BlockDriverState *bs, const char *key)
@@ -386,16 +368,14 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
return cluster_offset;
}
static int coroutine_fn qcow_co_is_allocated(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum)
static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum)
{
BDRVQcowState *s = bs->opaque;
int index_in_cluster, n;
uint64_t cluster_offset;
qemu_co_mutex_lock(&s->lock);
cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0);
qemu_co_mutex_unlock(&s->lock);
index_in_cluster = sector_num & (s->cluster_sectors - 1);
n = s->cluster_sectors - index_in_cluster;
if (n > nb_sectors)
@@ -453,7 +433,7 @@ 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,
static int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
BDRVQcowState *s = bs->opaque;
@@ -551,7 +531,7 @@ fail:
goto done;
}
static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
static int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
BDRVQcowState *s = bs->opaque;
@@ -644,14 +624,13 @@ static void qcow_close(BlockDriverState *bs)
static int qcow_create(const char *filename, QEMUOptionParameter *options)
{
int header_size, backing_filename_len, l1_size, shift, i;
int fd, header_size, backing_filename_len, l1_size, i, shift;
QCowHeader header;
uint8_t *tmp;
uint64_t tmp;
int64_t total_size = 0;
const char *backing_file = NULL;
int flags = 0;
int ret;
BlockDriverState *qcow_bs;
/* Read out options */
while (options && options->name) {
@@ -665,21 +644,9 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
options++;
}
ret = bdrv_create_file(filename, options);
if (ret < 0) {
return ret;
}
ret = bdrv_file_open(&qcow_bs, filename, BDRV_O_RDWR);
if (ret < 0) {
return ret;
}
ret = bdrv_truncate(qcow_bs, 0);
if (ret < 0) {
goto exit;
}
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0)
return -errno;
memset(&header, 0, sizeof(header));
header.magic = cpu_to_be32(QCOW_MAGIC);
header.version = cpu_to_be32(QCOW_VERSION);
@@ -715,34 +682,33 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
}
/* write all the data */
ret = bdrv_pwrite(qcow_bs, 0, &header, sizeof(header));
ret = qemu_write_full(fd, &header, sizeof(header));
if (ret != sizeof(header)) {
ret = -errno;
goto exit;
}
if (backing_file) {
ret = bdrv_pwrite(qcow_bs, sizeof(header),
backing_file, backing_filename_len);
ret = qemu_write_full(fd, backing_file, backing_filename_len);
if (ret != backing_filename_len) {
ret = -errno;
goto exit;
}
}
lseek(fd, header_size, SEEK_SET);
tmp = 0;
for(i = 0;i < l1_size; i++) {
ret = qemu_write_full(fd, &tmp, sizeof(tmp));
if (ret != sizeof(tmp)) {
ret = -errno;
goto exit;
}
}
tmp = g_malloc0(BDRV_SECTOR_SIZE);
for (i = 0; i < ((sizeof(uint64_t)*l1_size + BDRV_SECTOR_SIZE - 1)/
BDRV_SECTOR_SIZE); i++) {
ret = bdrv_pwrite(qcow_bs, header_size +
BDRV_SECTOR_SIZE*i, tmp, BDRV_SECTOR_SIZE);
if (ret != BDRV_SECTOR_SIZE) {
g_free(tmp);
goto exit;
}
}
g_free(tmp);
ret = 0;
exit:
bdrv_delete(qcow_bs);
close(fd);
return ret;
}
@@ -835,6 +801,11 @@ fail:
return ret;
}
static coroutine_fn int qcow_co_flush(BlockDriverState *bs)
{
return bdrv_co_flush(bs->file);
}
static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
{
BDRVQcowState *s = bs->opaque;
@@ -872,7 +843,8 @@ static BlockDriver bdrv_qcow = {
.bdrv_co_readv = qcow_co_readv,
.bdrv_co_writev = qcow_co_writev,
.bdrv_co_is_allocated = qcow_co_is_allocated,
.bdrv_co_flush_to_disk = qcow_co_flush,
.bdrv_is_allocated = qcow_is_allocated,
.bdrv_set_key = qcow_set_key,
.bdrv_make_empty = qcow_make_empty,

View File

@@ -25,7 +25,6 @@
#include "block_int.h"
#include "qemu-common.h"
#include "qcow2.h"
#include "trace.h"
typedef struct Qcow2CachedTable {
void* table;
@@ -101,9 +100,6 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
return 0;
}
trace_qcow2_cache_entry_flush(qemu_coroutine_self(),
c == s->l2_table_cache, i);
if (c->depends) {
ret = qcow2_cache_flush_dependency(bs, c);
} else if (c->depends_on_flush) {
@@ -136,13 +132,10 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
{
BDRVQcowState *s = bs->opaque;
int result = 0;
int ret;
int i;
trace_qcow2_cache_flush(qemu_coroutine_self(), c == s->l2_table_cache);
for (i = 0; i < c->size; i++) {
ret = qcow2_cache_entry_flush(bs, c, i);
if (ret < 0 && result != -ENOSPC) {
@@ -225,9 +218,6 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
int i;
int ret;
trace_qcow2_cache_get(qemu_coroutine_self(), c == s->l2_table_cache,
offset, read_from_disk);
/* Check if the table is already cached */
for (i = 0; i < c->size; i++) {
if (c->entries[i].offset == offset) {
@@ -237,8 +227,6 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
/* If not, write a table back and replace it */
i = qcow2_cache_find_entry_to_replace(c);
trace_qcow2_cache_get_replace_entry(qemu_coroutine_self(),
c == s->l2_table_cache, i);
if (i < 0) {
return i;
}
@@ -248,8 +236,6 @@ static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
return ret;
}
trace_qcow2_cache_get_read(qemu_coroutine_self(),
c == s->l2_table_cache, i);
c->entries[i].offset = 0;
if (read_from_disk) {
if (c == s->l2_table_cache) {
@@ -272,10 +258,6 @@ found:
c->entries[i].cache_hits++;
c->entries[i].ref++;
*table = c->entries[i].table;
trace_qcow2_cache_get_done(qemu_coroutine_self(),
c == s->l2_table_cache, i);
return 0;
}

View File

@@ -27,7 +27,6 @@
#include "qemu-common.h"
#include "block_int.h"
#include "block/qcow2.h"
#include "trace.h"
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
{
@@ -171,8 +170,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
old_l2_offset = s->l1_table[l1_index];
trace_qcow2_l2_allocate(bs, l1_index);
/* allocate a new l2 entry */
l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t));
@@ -187,7 +184,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
/* allocate a new entry in the l2 cache */
trace_qcow2_l2_allocate_get_empty(bs, l1_index);
ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table);
if (ret < 0) {
return ret;
@@ -195,7 +191,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
l2_table = *table;
if ((old_l2_offset & L1E_OFFSET_MASK) == 0) {
if (old_l2_offset == 0) {
/* if there was no old l2 table, clear the new table */
memset(l2_table, 0, s->l2_size * sizeof(uint64_t));
} else {
@@ -203,8 +199,7 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
/* if there was an old l2 table, read it from the disk */
BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ);
ret = qcow2_cache_get(bs, s->l2_table_cache,
old_l2_offset & L1E_OFFSET_MASK,
ret = qcow2_cache_get(bs, s->l2_table_cache, old_l2_offset,
(void**) &old_table);
if (ret < 0) {
goto fail;
@@ -221,7 +216,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
/* write the l2 table to the file */
BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE);
trace_qcow2_l2_allocate_write_l2(bs, l1_index);
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
ret = qcow2_cache_flush(bs, s->l2_table_cache);
if (ret < 0) {
@@ -229,7 +223,6 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
}
/* update the L1 entry */
trace_qcow2_l2_allocate_write_l1(bs, l1_index);
s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED;
ret = write_l1_entry(bs, l1_index);
if (ret < 0) {
@@ -237,54 +230,36 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
}
*table = l2_table;
trace_qcow2_l2_allocate_done(bs, l1_index, 0);
return 0;
fail:
trace_qcow2_l2_allocate_done(bs, l1_index, ret);
qcow2_cache_put(bs, s->l2_table_cache, (void**) table);
s->l1_table[l1_index] = old_l2_offset;
return ret;
}
/*
* Checks how many clusters in a given L2 table are contiguous in the image
* file. As soon as one of the flags in the bitmask stop_flags changes compared
* to the first cluster, the search is stopped and the cluster is not counted
* as contiguous. (This allows it, for example, to stop at the first compressed
* cluster which may require a different handling)
*/
static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size,
uint64_t *l2_table, uint64_t start, uint64_t stop_flags)
uint64_t *l2_table, uint64_t start, uint64_t mask)
{
int i;
uint64_t mask = stop_flags | L2E_OFFSET_MASK;
uint64_t offset = be64_to_cpu(l2_table[0]) & mask;
uint64_t offset = be64_to_cpu(l2_table[0]) & ~mask;
if (!offset)
return 0;
for (i = start; i < start + nb_clusters; i++) {
uint64_t l2_entry = be64_to_cpu(l2_table[i]) & mask;
if (offset + (uint64_t) i * cluster_size != l2_entry) {
for (i = start; i < start + nb_clusters; i++)
if (offset + (uint64_t) i * cluster_size != (be64_to_cpu(l2_table[i]) & ~mask))
break;
}
}
return (i - start);
}
static int count_contiguous_free_clusters(uint64_t nb_clusters, uint64_t *l2_table)
{
int i;
int i = 0;
for (i = 0; i < nb_clusters; i++) {
int type = qcow2_get_cluster_type(be64_to_cpu(l2_table[i]));
if (type != QCOW2_CLUSTER_UNALLOCATED) {
break;
}
}
while(nb_clusters-- && l2_table[i] == 0)
i++;
return i;
}
@@ -314,62 +289,89 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
}
}
static int coroutine_fn copy_sectors(BlockDriverState *bs,
uint64_t start_sect,
uint64_t cluster_offset,
int n_start, int n_end)
static int qcow2_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
BDRVQcowState *s = bs->opaque;
QEMUIOVector qiov;
int ret, index_in_cluster, n, n1;
uint64_t cluster_offset;
struct iovec iov;
QEMUIOVector qiov;
while (nb_sectors > 0) {
n = nb_sectors;
ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n,
&cluster_offset);
if (ret < 0) {
return ret;
}
index_in_cluster = sector_num & (s->cluster_sectors - 1);
if (!cluster_offset) {
if (bs->backing_hd) {
/* read from the base image */
iov.iov_base = buf;
iov.iov_len = n * 512;
qemu_iovec_init_external(&qiov, &iov, 1);
n1 = qcow2_backing_read1(bs->backing_hd, &qiov, sector_num, n);
if (n1 > 0) {
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING);
ret = bdrv_read(bs->backing_hd, sector_num, buf, n1);
if (ret < 0)
return -1;
}
} else {
memset(buf, 0, 512 * n);
}
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
if (qcow2_decompress_cluster(bs, cluster_offset) < 0)
return -1;
memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n);
} else {
BLKDBG_EVENT(bs->file, BLKDBG_READ);
ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512);
if (ret != n * 512)
return -1;
if (s->crypt_method) {
qcow2_encrypt_sectors(s, sector_num, buf, buf, n, 0,
&s->aes_decrypt_key);
}
}
nb_sectors -= n;
sector_num += n;
buf += n * 512;
}
return 0;
}
static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
uint64_t cluster_offset, int n_start, int n_end)
{
BDRVQcowState *s = bs->opaque;
int n, ret;
/*
* If this is the last cluster and it is only partially used, we must only
* copy until the end of the image, or bdrv_check_request will fail for the
* bdrv_read/write calls below.
*/
if (start_sect + n_end > bs->total_sectors) {
n_end = bs->total_sectors - start_sect;
}
n = n_end - n_start;
if (n <= 0) {
if (n <= 0)
return 0;
}
iov.iov_len = n * BDRV_SECTOR_SIZE;
iov.iov_base = qemu_blockalign(bs, iov.iov_len);
qemu_iovec_init_external(&qiov, &iov, 1);
BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
/* Call .bdrv_co_readv() directly instead of using the public block-layer
* interface. This avoids double I/O throttling and request tracking,
* which can lead to deadlock when block layer copy-on-read is enabled.
*/
ret = bs->drv->bdrv_co_readv(bs, start_sect + n_start, n, &qiov);
if (ret < 0) {
goto out;
}
ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n);
if (ret < 0)
return ret;
if (s->crypt_method) {
qcow2_encrypt_sectors(s, start_sect + n_start,
iov.iov_base, iov.iov_base, n, 1,
s->cluster_data,
s->cluster_data, n, 1,
&s->aes_encrypt_key);
}
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov);
if (ret < 0) {
goto out;
}
ret = 0;
out:
qemu_vfree(iov.iov_base);
return ret;
ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start,
s->cluster_data, n);
if (ret < 0)
return ret;
return 0;
}
@@ -384,9 +386,11 @@ out:
*
* on exit, *num is the number of contiguous sectors we can read.
*
* Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error
* cases.
* Return 0, if the offset is found
* Return -errno, otherwise.
*
*/
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *cluster_offset)
{
@@ -422,19 +426,19 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
/* seek the the l2 offset in the l1 table */
l1_index = offset >> l1_bits;
if (l1_index >= s->l1_size) {
ret = QCOW2_CLUSTER_UNALLOCATED;
if (l1_index >= s->l1_size)
goto out;
}
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
if (!l2_offset) {
ret = QCOW2_CLUSTER_UNALLOCATED;
l2_offset = s->l1_table[l1_index];
/* seek the l2 table of the given l2 offset */
if (!l2_offset)
goto out;
}
/* load the l2 table in memory */
l2_offset &= ~QCOW_OFLAG_COPIED;
ret = l2_load(bs, l2_offset, &l2_table);
if (ret < 0) {
return ret;
@@ -446,44 +450,26 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
*cluster_offset = be64_to_cpu(l2_table[l2_index]);
nb_clusters = size_to_clusters(s, nb_needed << 9);
ret = qcow2_get_cluster_type(*cluster_offset);
switch (ret) {
case QCOW2_CLUSTER_COMPRESSED:
/* Compressed clusters can only be processed one by one */
c = 1;
*cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK;
break;
case QCOW2_CLUSTER_ZERO:
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
&l2_table[l2_index], 0,
QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO);
*cluster_offset = 0;
break;
case QCOW2_CLUSTER_UNALLOCATED:
if (!*cluster_offset) {
/* how many empty clusters ? */
c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]);
*cluster_offset = 0;
break;
case QCOW2_CLUSTER_NORMAL:
} else {
/* how many allocated clusters ? */
c = count_contiguous_clusters(nb_clusters, s->cluster_size,
&l2_table[l2_index], 0,
QCOW_OFLAG_COMPRESSED | QCOW_OFLAG_ZERO);
*cluster_offset &= L2E_OFFSET_MASK;
break;
&l2_table[l2_index], 0, QCOW_OFLAG_COPIED);
}
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
nb_available = (c * s->cluster_sectors);
nb_available = (c * s->cluster_sectors);
out:
if (nb_available > nb_needed)
nb_available = nb_needed;
*num = nb_available - index_in_cluster;
return ret;
*cluster_offset &=~QCOW_OFLAG_COPIED;
return 0;
}
/*
@@ -499,6 +485,7 @@ out:
*/
static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
uint64_t **new_l2_table,
uint64_t *new_l2_offset,
int *new_l2_index)
{
BDRVQcowState *s = bs->opaque;
@@ -516,13 +503,13 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
return ret;
}
}
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
l2_offset = s->l1_table[l1_index];
/* seek the l2 table of the given l2 offset */
if (s->l1_table[l1_index] & QCOW_OFLAG_COPIED) {
if (l2_offset & QCOW_OFLAG_COPIED) {
/* load the l2 table in memory */
l2_offset &= ~QCOW_OFLAG_COPIED;
ret = l2_load(bs, l2_offset, &l2_table);
if (ret < 0) {
return ret;
@@ -538,7 +525,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
if (l2_offset) {
qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
}
l2_offset = s->l1_table[l1_index] & L1E_OFFSET_MASK;
l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED;
}
/* find the cluster offset for the given disk offset */
@@ -546,6 +533,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
*new_l2_table = l2_table;
*new_l2_offset = l2_offset;
*new_l2_index = l2_index;
return 0;
@@ -570,23 +558,24 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
int l2_index, ret;
uint64_t *l2_table;
uint64_t l2_offset, *l2_table;
int64_t cluster_offset;
int nb_csectors;
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
if (ret < 0) {
return 0;
}
/* Compression can't overwrite anything. Fail if the cluster was already
* allocated. */
cluster_offset = be64_to_cpu(l2_table[l2_index]);
if (cluster_offset & L2E_OFFSET_MASK) {
if (cluster_offset & QCOW_OFLAG_COPIED) {
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
return 0;
}
if (cluster_offset)
qcow2_free_any_clusters(bs, cluster_offset, 1);
cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
if (cluster_offset < 0) {
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
@@ -618,12 +607,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
{
BDRVQcowState *s = bs->opaque;
int i, j = 0, l2_index, ret;
uint64_t *old_cluster, start_sect, *l2_table;
uint64_t cluster_offset = m->alloc_offset;
uint64_t *old_cluster, start_sect, l2_offset, *l2_table;
uint64_t cluster_offset = m->cluster_offset;
bool cow = false;
trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters);
if (m->nb_clusters == 0)
return 0;
@@ -633,9 +620,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
if (m->n_start) {
cow = true;
qemu_co_mutex_unlock(&s->lock);
ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
qemu_co_mutex_lock(&s->lock);
if (ret < 0)
goto err;
}
@@ -643,10 +628,8 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
if (m->nb_available & (s->cluster_sectors - 1)) {
uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1);
cow = true;
qemu_co_mutex_unlock(&s->lock);
ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9),
m->nb_available - end, s->cluster_sectors);
qemu_co_mutex_lock(&s->lock);
if (ret < 0)
goto err;
}
@@ -663,7 +646,7 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
}
qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index);
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index);
if (ret < 0) {
goto err;
}
@@ -695,7 +678,8 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
*/
if (j != 0) {
for (i = 0; i < j; i++) {
qcow2_free_any_clusters(bs, be64_to_cpu(old_cluster[i]), 1);
qcow2_free_any_clusters(bs,
be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
}
}
@@ -705,129 +689,13 @@ err:
return ret;
}
/*
* 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,
* which must copy from the backing file)
*/
static int count_cow_clusters(BDRVQcowState *s, int nb_clusters,
uint64_t *l2_table, int l2_index)
{
int i;
for (i = 0; i < nb_clusters; i++) {
uint64_t l2_entry = be64_to_cpu(l2_table[l2_index + i]);
int cluster_type = qcow2_get_cluster_type(l2_entry);
switch(cluster_type) {
case QCOW2_CLUSTER_NORMAL:
if (l2_entry & QCOW_OFLAG_COPIED) {
goto out;
}
break;
case QCOW2_CLUSTER_UNALLOCATED:
case QCOW2_CLUSTER_COMPRESSED:
case QCOW2_CLUSTER_ZERO:
break;
default:
abort();
}
}
out:
assert(i <= nb_clusters);
return i;
}
/*
* Allocates new clusters for the given guest_offset.
*
* At most *nb_clusters are allocated, and on return *nb_clusters is updated to
* contain the number of clusters that have been allocated and are contiguous
* in the image file.
*
* If *host_offset is non-zero, it specifies the offset in the image file at
* which the new clusters must start. *nb_clusters can be 0 on return in this
* case if the cluster at host_offset is already in use. If *host_offset is
* zero, the clusters can be allocated anywhere in the image file.
*
* *host_offset is updated to contain the offset into the image file at which
* the first allocated cluster starts.
*
* Return 0 on success and -errno in error cases. -EAGAIN means that the
* function has been waiting for another request and the allocation must be
* restarted, but the whole request should not be failed.
*/
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
uint64_t *host_offset, unsigned int *nb_clusters)
{
BDRVQcowState *s = bs->opaque;
int64_t cluster_offset;
QCowL2Meta *old_alloc;
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
*host_offset, *nb_clusters);
/*
* Check if there already is an AIO write request in flight which allocates
* the same cluster. In this case we need to wait until the previous
* request has completed and updated the L2 table accordingly.
*/
QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
uint64_t start = guest_offset >> s->cluster_bits;
uint64_t end = start + *nb_clusters;
uint64_t old_start = old_alloc->offset >> s->cluster_bits;
uint64_t old_end = old_start + old_alloc->nb_clusters;
if (end < old_start || start > old_end) {
/* No intersection */
} else {
if (start < old_start) {
/* Stop at the start of a running allocation */
*nb_clusters = old_start - start;
} else {
*nb_clusters = 0;
}
if (*nb_clusters == 0) {
/* Wait for the dependency to complete. We need to recheck
* the free/allocated clusters when we continue. */
qemu_co_mutex_unlock(&s->lock);
qemu_co_queue_wait(&old_alloc->dependent_requests);
qemu_co_mutex_lock(&s->lock);
return -EAGAIN;
}
}
}
if (!*nb_clusters) {
abort();
}
/* Allocate new clusters */
trace_qcow2_cluster_alloc_phys(qemu_coroutine_self());
if (*host_offset == 0) {
cluster_offset = qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size);
} else {
cluster_offset = *host_offset;
*nb_clusters = qcow2_alloc_clusters_at(bs, cluster_offset, *nb_clusters);
}
if (cluster_offset < 0) {
return cluster_offset;
}
*host_offset = cluster_offset;
return 0;
}
/*
* alloc_cluster_offset
*
* For a given offset on the virtual disk, find the cluster offset in qcow2
* file. If the offset is not found, allocate a new cluster.
* For a given offset of the disk image, return cluster offset in qcow2 file.
* If the offset is not found, allocate a new cluster.
*
* If the cluster was already allocated, m->nb_clusters is set to 0 and
* If the cluster was already allocated, m->nb_clusters is set to 0,
* other fields in m are meaningless.
*
* If the cluster is newly allocated, m->nb_clusters is set to the number of
@@ -844,139 +712,134 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
int n_start, int n_end, int *num, QCowL2Meta *m)
{
BDRVQcowState *s = bs->opaque;
int l2_index, ret, sectors;
uint64_t *l2_table;
unsigned int nb_clusters, keep_clusters;
uint64_t cluster_offset;
int l2_index, ret;
uint64_t l2_offset, *l2_table;
int64_t cluster_offset;
unsigned int nb_clusters, i = 0;
QCowL2Meta *old_alloc;
trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset,
n_start, n_end);
/* Find L2 entry for the first involved cluster */
again:
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
if (ret < 0) {
return ret;
}
/*
* Calculate the number of clusters to look for. We stop at L2 table
* boundaries to keep things simple.
*/
nb_clusters = MIN(size_to_clusters(s, n_end << BDRV_SECTOR_BITS),
s->l2_size - l2_index);
again:
nb_clusters = size_to_clusters(s, n_end << 9);
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
cluster_offset = be64_to_cpu(l2_table[l2_index]);
/*
* Check how many clusters are already allocated and don't need COW, and how
* many need a new allocation.
*/
if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL
&& (cluster_offset & QCOW_OFLAG_COPIED))
{
/* We keep all QCOW_OFLAG_COPIED clusters */
keep_clusters =
count_contiguous_clusters(nb_clusters, s->cluster_size,
&l2_table[l2_index], 0,
QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO);
assert(keep_clusters <= nb_clusters);
nb_clusters -= keep_clusters;
} else {
keep_clusters = 0;
cluster_offset = 0;
/* We keep all QCOW_OFLAG_COPIED clusters */
if (cluster_offset & QCOW_OFLAG_COPIED) {
nb_clusters = count_contiguous_clusters(nb_clusters, s->cluster_size,
&l2_table[l2_index], 0, 0);
cluster_offset &= ~QCOW_OFLAG_COPIED;
m->nb_clusters = 0;
goto out;
}
if (nb_clusters > 0) {
/* For the moment, overwrite compressed clusters one by one */
uint64_t entry = be64_to_cpu(l2_table[l2_index + keep_clusters]);
if (entry & QCOW_OFLAG_COMPRESSED) {
nb_clusters = 1;
/* for the moment, multiple compressed clusters are not managed */
if (cluster_offset & QCOW_OFLAG_COMPRESSED)
nb_clusters = 1;
/* how many available clusters ? */
while (i < nb_clusters) {
i += count_contiguous_clusters(nb_clusters - i, s->cluster_size,
&l2_table[l2_index], i, 0);
if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) {
break;
}
i += count_contiguous_free_clusters(nb_clusters - i,
&l2_table[l2_index + i]);
if (i >= nb_clusters) {
break;
}
cluster_offset = be64_to_cpu(l2_table[l2_index + i]);
if ((cluster_offset & QCOW_OFLAG_COPIED) ||
(cluster_offset & QCOW_OFLAG_COMPRESSED))
break;
}
assert(i <= nb_clusters);
nb_clusters = i;
/*
* Check if there already is an AIO write request in flight which allocates
* the same cluster. In this case we need to wait until the previous
* request has completed and updated the L2 table accordingly.
*/
QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
uint64_t start = offset >> s->cluster_bits;
uint64_t end = start + nb_clusters;
uint64_t old_start = old_alloc->offset >> s->cluster_bits;
uint64_t old_end = old_start + old_alloc->nb_clusters;
if (end < old_start || start > old_end) {
/* No intersection */
} else {
nb_clusters = count_cow_clusters(s, nb_clusters, l2_table,
l2_index + keep_clusters);
if (start < old_start) {
/* Stop at the start of a running allocation */
nb_clusters = old_start - start;
} else {
nb_clusters = 0;
}
if (nb_clusters == 0) {
/* Wait for the dependency to complete. We need to recheck
* the free/allocated clusters when we continue. */
qemu_co_mutex_unlock(&s->lock);
qemu_co_queue_wait(&old_alloc->dependent_requests);
qemu_co_mutex_lock(&s->lock);
goto again;
}
}
}
cluster_offset &= L2E_OFFSET_MASK;
if (!nb_clusters) {
abort();
}
/*
* The L2 table isn't used any more after this. As long as the cache works
* synchronously, it's important to release it before calling
* do_alloc_cluster_offset, which may yield if we need to wait for another
* request to complete. If we still had the reference, we could use up the
* whole cache with sleeping requests.
*/
/* save info needed for meta data update */
m->offset = offset;
m->n_start = n_start;
m->nb_clusters = nb_clusters;
QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
/* allocate a new cluster */
cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size);
if (cluster_offset < 0) {
ret = cluster_offset;
goto fail;
}
out:
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
if (ret < 0) {
return ret;
goto fail_put;
}
/* If there is something left to allocate, do that now */
*m = (QCowL2Meta) {
.cluster_offset = cluster_offset,
.nb_clusters = 0,
};
qemu_co_queue_init(&m->dependent_requests);
m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end);
m->cluster_offset = cluster_offset;
if (nb_clusters > 0) {
uint64_t alloc_offset;
uint64_t alloc_cluster_offset;
uint64_t keep_bytes = keep_clusters * s->cluster_size;
/* Calculate start and size of allocation */
alloc_offset = offset + keep_bytes;
if (keep_clusters == 0) {
alloc_cluster_offset = 0;
} else {
alloc_cluster_offset = cluster_offset + keep_bytes;
}
/* Allocate, if necessary at a given offset in the image file */
ret = do_alloc_cluster_offset(bs, alloc_offset, &alloc_cluster_offset,
&nb_clusters);
if (ret == -EAGAIN) {
goto again;
} else if (ret < 0) {
goto fail;
}
/* save info needed for meta data update */
if (nb_clusters > 0) {
int requested_sectors = n_end - keep_clusters * s->cluster_sectors;
int avail_sectors = (keep_clusters + nb_clusters)
<< (s->cluster_bits - BDRV_SECTOR_BITS);
*m = (QCowL2Meta) {
.cluster_offset = keep_clusters == 0 ?
alloc_cluster_offset : cluster_offset,
.alloc_offset = alloc_cluster_offset,
.offset = alloc_offset,
.n_start = keep_clusters == 0 ? n_start : 0,
.nb_clusters = nb_clusters,
.nb_available = MIN(requested_sectors, avail_sectors),
};
qemu_co_queue_init(&m->dependent_requests);
QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
}
}
/* Some cleanup work */
sectors = (keep_clusters + nb_clusters) << (s->cluster_bits - 9);
if (sectors > n_end) {
sectors = n_end;
}
assert(sectors > n_start);
*num = sectors - n_start;
*num = m->nb_available - n_start;
return 0;
fail:
if (m->nb_clusters > 0) {
QLIST_REMOVE(m, next_in_flight);
}
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
fail_put:
QLIST_REMOVE(m, next_in_flight);
return ret;
}
@@ -1041,12 +904,12 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
unsigned int nb_clusters)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l2_table;
uint64_t l2_offset, *l2_table;
int l2_index;
int ret;
int i;
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index);
if (ret < 0) {
return ret;
}
@@ -1058,7 +921,9 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
uint64_t old_offset;
old_offset = be64_to_cpu(l2_table[l2_index + i]);
if ((old_offset & L2E_OFFSET_MASK) == 0) {
old_offset &= ~QCOW_OFLAG_COPIED;
if (old_offset == 0) {
continue;
}
@@ -1111,75 +976,3 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
return 0;
}
/*
* This zeroes as many clusters of nb_clusters as possible at once (i.e.
* all clusters in the same L2 table) and returns the number of zeroed
* clusters.
*/
static int zero_single_l2(BlockDriverState *bs, uint64_t offset,
unsigned int nb_clusters)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l2_table;
int l2_index;
int ret;
int i;
ret = get_cluster_table(bs, offset, &l2_table, &l2_index);
if (ret < 0) {
return ret;
}
/* Limit nb_clusters to one L2 table */
nb_clusters = MIN(nb_clusters, s->l2_size - l2_index);
for (i = 0; i < nb_clusters; i++) {
uint64_t old_offset;
old_offset = be64_to_cpu(l2_table[l2_index + i]);
/* Update L2 entries */
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
if (old_offset & QCOW_OFLAG_COMPRESSED) {
l2_table[l2_index + i] = cpu_to_be64(QCOW_OFLAG_ZERO);
qcow2_free_any_clusters(bs, old_offset, 1);
} else {
l2_table[l2_index + i] |= cpu_to_be64(QCOW_OFLAG_ZERO);
}
}
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
if (ret < 0) {
return ret;
}
return nb_clusters;
}
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors)
{
BDRVQcowState *s = bs->opaque;
unsigned int nb_clusters;
int ret;
/* The zero flag is only supported by version 3 and newer */
if (s->qcow_version < 3) {
return -ENOTSUP;
}
/* Each L2 table is handled by its own loop iteration */
nb_clusters = size_to_clusters(s, nb_sectors << BDRV_SECTOR_BITS);
while (nb_clusters > 0) {
ret = zero_single_l2(bs, offset, nb_clusters);
if (ret < 0) {
return ret;
}
nb_clusters -= ret;
offset += (ret * s->cluster_size);
}
return 0;
}

View File

@@ -167,7 +167,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
if (refcount_table_index < s->refcount_table_size) {
uint64_t refcount_block_offset =
s->refcount_table[refcount_table_index] & REFT_OFFSET_MASK;
s->refcount_table[refcount_table_index];
/* If it's already there, we're done */
if (refcount_block_offset) {
@@ -400,7 +400,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
return ret;
}
return 0;
return new_block;
fail_table:
g_free(new_table);
@@ -582,40 +582,6 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
return offset;
}
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
int nb_clusters)
{
BDRVQcowState *s = bs->opaque;
uint64_t cluster_index;
uint64_t old_free_cluster_index;
int i, refcount, ret;
/* Check how many clusters there are free */
cluster_index = offset >> s->cluster_bits;
for(i = 0; i < nb_clusters; i++) {
refcount = get_refcount(bs, cluster_index++);
if (refcount < 0) {
return refcount;
} else if (refcount != 0) {
break;
}
}
/* And then allocate them */
old_free_cluster_index = s->free_cluster_index;
s->free_cluster_index = cluster_index + i;
ret = update_refcount(bs, offset, i << s->cluster_bits, 1);
if (ret < 0) {
return ret;
}
s->free_cluster_index = old_free_cluster_index;
return i;
}
/* only used to allocate compressed sectors. We try to allocate
contiguous sectors. size must be <= cluster_size */
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
@@ -679,35 +645,32 @@ void qcow2_free_clusters(BlockDriverState *bs,
}
/*
* Free a cluster using its L2 entry (handles clusters of all types, e.g.
* normal cluster, compressed cluster, etc.)
* free_any_clusters
*
* free clusters according to its type: compressed or not
*
*/
void qcow2_free_any_clusters(BlockDriverState *bs,
uint64_t l2_entry, int nb_clusters)
uint64_t cluster_offset, int nb_clusters)
{
BDRVQcowState *s = bs->opaque;
switch (qcow2_get_cluster_type(l2_entry)) {
case QCOW2_CLUSTER_COMPRESSED:
{
int nb_csectors;
nb_csectors = ((l2_entry >> s->csize_shift) &
s->csize_mask) + 1;
qcow2_free_clusters(bs,
(l2_entry & s->cluster_offset_mask) & ~511,
nb_csectors * 512);
}
break;
case QCOW2_CLUSTER_NORMAL:
qcow2_free_clusters(bs, l2_entry & L2E_OFFSET_MASK,
nb_clusters << s->cluster_bits);
break;
case QCOW2_CLUSTER_UNALLOCATED:
case QCOW2_CLUSTER_ZERO:
break;
default:
abort();
/* free the cluster */
if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
int nb_csectors;
nb_csectors = ((cluster_offset >> s->csize_shift) &
s->csize_mask) + 1;
qcow2_free_clusters(bs,
(cluster_offset & s->cluster_offset_mask) & ~511,
nb_csectors * 512);
return;
}
qcow2_free_clusters(bs, cluster_offset, nb_clusters << s->cluster_bits);
return;
}
@@ -737,10 +700,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l2_table = NULL;
l1_table = NULL;
l1_size2 = l1_size * sizeof(uint64_t);
/* WARNING: qcow2_snapshot_goto relies on this function not using the
* l1_table_offset when it is the current s->l1_table_offset! Be careful
* when changing this! */
if (l1_table_offset != s->l1_table_offset) {
if (l1_size2 != 0) {
l1_table = g_malloc0(align_offset(l1_size2, 512));
@@ -767,7 +726,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l2_offset = l1_table[i];
if (l2_offset) {
old_l2_offset = l2_offset;
l2_offset &= L1E_OFFSET_MASK;
l2_offset &= ~QCOW_OFLAG_COPIED;
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
(void**) &l2_table);
@@ -799,11 +758,10 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
/* compressed clusters are never modified */
refcount = 2;
} else {
uint64_t cluster_index = (offset & L2E_OFFSET_MASK) >> s->cluster_bits;
if (addend != 0) {
refcount = update_cluster_refcount(bs, cluster_index, addend);
refcount = update_cluster_refcount(bs, offset >> s->cluster_bits, addend);
} else {
refcount = get_refcount(bs, cluster_index);
refcount = get_refcount(bs, offset >> s->cluster_bits);
}
if (refcount < 0) {
@@ -861,8 +819,7 @@ fail:
qcow2_cache_set_writethrough(bs, s->refcount_block_cache,
old_refcount_writethrough);
/* Update L1 only if it isn't deleted anyway (addend = -1) */
if (addend >= 0 && l1_modified) {
if (l1_modified) {
for(i = 0; i < l1_size; i++)
cpu_to_be64s(&l1_table[i]);
if (bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table,
@@ -941,7 +898,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
int check_copied)
{
BDRVQcowState *s = bs->opaque;
uint64_t *l2_table, l2_entry;
uint64_t *l2_table, offset;
int i, l2_size, nb_csectors, refcount;
/* Read L2 table from disk */
@@ -953,70 +910,54 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res,
/* Do the actual checks */
for(i = 0; i < s->l2_size; i++) {
l2_entry = be64_to_cpu(l2_table[i]);
switch (qcow2_get_cluster_type(l2_entry)) {
case QCOW2_CLUSTER_COMPRESSED:
/* Compressed clusters don't have QCOW_OFLAG_COPIED */
if (l2_entry & QCOW_OFLAG_COPIED) {
fprintf(stderr, "ERROR: cluster %" PRId64 ": "
"copied flag must never be set for compressed "
"clusters\n", l2_entry >> s->cluster_bits);
l2_entry &= ~QCOW_OFLAG_COPIED;
res->corruptions++;
}
/* Mark cluster as used */
nb_csectors = ((l2_entry >> s->csize_shift) &
s->csize_mask) + 1;
l2_entry &= s->cluster_offset_mask;
inc_refcounts(bs, res, refcount_table, refcount_table_size,
l2_entry & ~511, nb_csectors * 512);
break;
case QCOW2_CLUSTER_ZERO:
if ((l2_entry & L2E_OFFSET_MASK) == 0) {
break;
}
/* fall through */
case QCOW2_CLUSTER_NORMAL:
{
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
uint64_t offset = l2_entry & L2E_OFFSET_MASK;
if (check_copied) {
refcount = get_refcount(bs, offset >> s->cluster_bits);
if (refcount < 0) {
fprintf(stderr, "Can't get refcount for offset %"
PRIx64 ": %s\n", l2_entry, strerror(-refcount));
goto fail;
offset = be64_to_cpu(l2_table[i]);
if (offset != 0) {
if (offset & QCOW_OFLAG_COMPRESSED) {
/* Compressed clusters don't have QCOW_OFLAG_COPIED */
if (offset & QCOW_OFLAG_COPIED) {
fprintf(stderr, "ERROR: cluster %" PRId64 ": "
"copied flag must never be set for compressed "
"clusters\n", offset >> s->cluster_bits);
offset &= ~QCOW_OFLAG_COPIED;
res->corruptions++;
}
if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "ERROR OFLAG_COPIED: offset=%"
PRIx64 " refcount=%d\n", l2_entry, refcount);
/* Mark cluster as used */
nb_csectors = ((offset >> s->csize_shift) &
s->csize_mask) + 1;
offset &= s->cluster_offset_mask;
inc_refcounts(bs, res, refcount_table, refcount_table_size,
offset & ~511, nb_csectors * 512);
} else {
/* QCOW_OFLAG_COPIED must be set iff refcount == 1 */
if (check_copied) {
uint64_t entry = offset;
offset &= ~QCOW_OFLAG_COPIED;
refcount = get_refcount(bs, offset >> s->cluster_bits);
if (refcount < 0) {
fprintf(stderr, "Can't get refcount for offset %"
PRIx64 ": %s\n", entry, strerror(-refcount));
goto fail;
}
if ((refcount == 1) != ((entry & QCOW_OFLAG_COPIED) != 0)) {
fprintf(stderr, "ERROR OFLAG_COPIED: offset=%"
PRIx64 " refcount=%d\n", entry, refcount);
res->corruptions++;
}
}
/* Mark cluster as used */
offset &= ~QCOW_OFLAG_COPIED;
inc_refcounts(bs, res, refcount_table,refcount_table_size,
offset, s->cluster_size);
/* Correct offsets are cluster aligned */
if (offset & (s->cluster_size - 1)) {
fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
"properly aligned; L2 entry corrupted.\n", offset);
res->corruptions++;
}
}
/* Mark cluster as used */
inc_refcounts(bs, res, refcount_table,refcount_table_size,
offset, s->cluster_size);
/* Correct offsets are cluster aligned */
if (offset & (s->cluster_size - 1)) {
fprintf(stderr, "ERROR offset=%" PRIx64 ": Cluster is not "
"properly aligned; L2 entry corrupted.\n", offset);
res->corruptions++;
}
break;
}
case QCOW2_CLUSTER_UNALLOCATED:
break;
default:
abort();
}
}
@@ -1087,7 +1028,7 @@ static int check_refcounts_l1(BlockDriverState *bs,
}
/* Mark L2 table as used */
l2_offset &= L1E_OFFSET_MASK;
l2_offset &= ~QCOW_OFLAG_COPIED;
inc_refcounts(bs, res, refcount_table, refcount_table_size,
l2_offset, s->cluster_size);

View File

@@ -46,11 +46,6 @@ typedef struct QEMU_PACKED QCowSnapshotHeader {
/* name follows */
} QCowSnapshotHeader;
typedef struct QEMU_PACKED QCowSnapshotExtraData {
uint64_t vm_state_size_large;
uint64_t disk_size;
} QCowSnapshotExtraData;
void qcow2_free_snapshots(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
@@ -69,12 +64,10 @@ int qcow2_read_snapshots(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
QCowSnapshotHeader h;
QCowSnapshotExtraData extra;
QCowSnapshot *sn;
int i, id_str_size, name_size;
int64_t offset;
uint32_t extra_data_size;
int ret;
if (!s->nb_snapshots) {
s->snapshots = NULL;
@@ -84,15 +77,10 @@ int qcow2_read_snapshots(BlockDriverState *bs)
offset = s->snapshots_offset;
s->snapshots = g_malloc0(s->nb_snapshots * sizeof(QCowSnapshot));
for(i = 0; i < s->nb_snapshots; i++) {
/* Read statically sized part of the snapshot header */
offset = align_offset(offset, 8);
ret = bdrv_pread(bs->file, offset, &h, sizeof(h));
if (ret < 0) {
if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h))
goto fail;
}
offset += sizeof(h);
sn = s->snapshots + i;
sn->l1_table_offset = be64_to_cpu(h.l1_table_offset);
@@ -106,49 +94,25 @@ int qcow2_read_snapshots(BlockDriverState *bs)
id_str_size = be16_to_cpu(h.id_str_size);
name_size = be16_to_cpu(h.name_size);
/* Read extra data */
ret = bdrv_pread(bs->file, offset, &extra,
MIN(sizeof(extra), extra_data_size));
if (ret < 0) {
goto fail;
}
offset += extra_data_size;
if (extra_data_size >= 8) {
sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large);
}
if (extra_data_size >= 16) {
sn->disk_size = be64_to_cpu(extra.disk_size);
} else {
sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
}
/* Read snapshot ID */
sn->id_str = g_malloc(id_str_size + 1);
ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size);
if (ret < 0) {
if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size)
goto fail;
}
offset += id_str_size;
sn->id_str[id_str_size] = '\0';
/* Read snapshot name */
sn->name = g_malloc(name_size + 1);
ret = bdrv_pread(bs->file, offset, sn->name, name_size);
if (ret < 0) {
if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size)
goto fail;
}
offset += name_size;
sn->name[name_size] = '\0';
}
s->snapshots_size = offset - s->snapshots_offset;
return 0;
fail:
fail:
qcow2_free_snapshots(bs);
return ret;
return -1;
}
/* add at the end of the file a new list of snapshots */
@@ -157,14 +121,10 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn;
QCowSnapshotHeader h;
QCowSnapshotExtraData extra;
int i, name_size, id_str_size, snapshots_size;
struct {
uint32_t nb_snapshots;
uint64_t snapshots_offset;
} QEMU_PACKED header_data;
uint64_t data64;
uint32_t data32;
int64_t offset, snapshots_offset;
int ret;
/* compute the size of the snapshots */
offset = 0;
@@ -172,13 +132,11 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
sn = s->snapshots + i;
offset = align_offset(offset, 8);
offset += sizeof(h);
offset += sizeof(extra);
offset += strlen(sn->id_str);
offset += strlen(sn->name);
}
snapshots_size = offset;
/* Allocate space for the new snapshot list */
snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
bdrv_flush(bs->file);
offset = snapshots_offset;
@@ -186,86 +144,49 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
return offset;
}
/* Write all snapshots to the new list */
for(i = 0; i < s->nb_snapshots; i++) {
sn = s->snapshots + i;
memset(&h, 0, sizeof(h));
h.l1_table_offset = cpu_to_be64(sn->l1_table_offset);
h.l1_size = cpu_to_be32(sn->l1_size);
/* If it doesn't fit in 32 bit, older implementations should treat it
* as a disk-only snapshot rather than truncate the VM state */
if (sn->vm_state_size <= 0xffffffff) {
h.vm_state_size = cpu_to_be32(sn->vm_state_size);
}
h.vm_state_size = cpu_to_be32(sn->vm_state_size);
h.date_sec = cpu_to_be32(sn->date_sec);
h.date_nsec = cpu_to_be32(sn->date_nsec);
h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec);
h.extra_data_size = cpu_to_be32(sizeof(extra));
memset(&extra, 0, sizeof(extra));
extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size);
extra.disk_size = cpu_to_be64(sn->disk_size);
id_str_size = strlen(sn->id_str);
name_size = strlen(sn->name);
h.id_str_size = cpu_to_be16(id_str_size);
h.name_size = cpu_to_be16(name_size);
offset = align_offset(offset, 8);
ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h));
if (ret < 0) {
if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0)
goto fail;
}
offset += sizeof(h);
ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra));
if (ret < 0) {
if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0)
goto fail;
}
offset += sizeof(extra);
ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size);
if (ret < 0) {
goto fail;
}
offset += id_str_size;
ret = bdrv_pwrite(bs->file, offset, sn->name, name_size);
if (ret < 0) {
if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0)
goto fail;
}
offset += name_size;
}
/*
* Update the header to point to the new snapshot table. This requires the
* new table and its refcounts to be stable on disk.
*/
ret = bdrv_flush(bs);
if (ret < 0) {
/* update the various header fields */
data64 = cpu_to_be64(snapshots_offset);
if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset),
&data64, sizeof(data64)) < 0)
goto fail;
}
QEMU_BUILD_BUG_ON(offsetof(QCowHeader, snapshots_offset) !=
offsetof(QCowHeader, nb_snapshots) + sizeof(header_data.nb_snapshots));
header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots);
header_data.snapshots_offset = cpu_to_be64(snapshots_offset);
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots),
&header_data, sizeof(header_data));
if (ret < 0) {
data32 = cpu_to_be32(s->nb_snapshots);
if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots),
&data32, sizeof(data32)) < 0)
goto fail;
}
/* free the old snapshot table */
qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size);
s->snapshots_offset = snapshots_offset;
s->snapshots_size = snapshots_size;
return 0;
fail:
return ret;
fail:
return -1;
}
static void find_new_snapshot_id(BlockDriverState *bs,
@@ -315,93 +236,72 @@ static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name)
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
{
BDRVQcowState *s = bs->opaque;
QCowSnapshot *new_snapshot_list = NULL;
QCowSnapshot *old_snapshot_list = NULL;
QCowSnapshot sn1, *sn = &sn1;
QCowSnapshot *snapshots1, sn1, *sn = &sn1;
int i, ret;
uint64_t *l1_table = NULL;
int64_t l1_table_offset;
memset(sn, 0, sizeof(*sn));
/* Generate an ID if it wasn't passed */
if (sn_info->id_str[0] == '\0') {
/* compute a new id */
find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str));
}
/* Check that the ID is unique */
if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) {
return -EEXIST;
}
/* check that the ID is unique */
if (find_snapshot_by_id(bs, sn_info->id_str) >= 0)
return -ENOENT;
/* Populate sn with passed data */
sn->id_str = g_strdup(sn_info->id_str);
if (!sn->id_str)
goto fail;
sn->name = g_strdup(sn_info->name);
sn->disk_size = bs->total_sectors * BDRV_SECTOR_SIZE;
if (!sn->name)
goto fail;
sn->vm_state_size = sn_info->vm_state_size;
sn->date_sec = sn_info->date_sec;
sn->date_nsec = sn_info->date_nsec;
sn->vm_clock_nsec = sn_info->vm_clock_nsec;
/* Allocate the L1 table of the snapshot and copy the current one there. */
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
if (ret < 0)
goto fail;
/* create the L1 table of the snapshot */
l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t));
if (l1_table_offset < 0) {
ret = l1_table_offset;
goto fail;
}
bdrv_flush(bs->file);
sn->l1_table_offset = l1_table_offset;
sn->l1_size = s->l1_size;
l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
if (s->l1_size != 0) {
l1_table = g_malloc(s->l1_size * sizeof(uint64_t));
} else {
l1_table = NULL;
}
for(i = 0; i < s->l1_size; i++) {
l1_table[i] = cpu_to_be64(s->l1_table[i]);
}
ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table,
s->l1_size * sizeof(uint64_t));
if (ret < 0) {
if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset,
l1_table, s->l1_size * sizeof(uint64_t)) < 0)
goto fail;
}
g_free(l1_table);
l1_table = NULL;
/*
* Increase the refcounts of all clusters and make sure everything is
* stable on disk before updating the snapshot table to contain a pointer
* to the new L1 table.
*/
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1);
if (ret < 0) {
goto fail;
}
ret = bdrv_flush(bs);
if (ret < 0) {
goto fail;
}
/* Append the new snapshot to the snapshot list */
new_snapshot_list = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
snapshots1 = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot));
if (s->snapshots) {
memcpy(new_snapshot_list, s->snapshots,
s->nb_snapshots * sizeof(QCowSnapshot));
old_snapshot_list = s->snapshots;
memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot));
g_free(s->snapshots);
}
s->snapshots = new_snapshot_list;
s->snapshots = snapshots1;
s->snapshots[s->nb_snapshots++] = *sn;
ret = qcow2_write_snapshots(bs);
if (ret < 0) {
g_free(s->snapshots);
s->snapshots = old_snapshot_list;
if (qcow2_write_snapshots(bs) < 0)
goto fail;
}
g_free(old_snapshot_list);
#ifdef DEBUG_ALLOC
{
BdrvCheckResult result = {0};
@@ -409,13 +309,10 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
}
#endif
return 0;
fail:
g_free(sn->id_str);
fail:
g_free(sn->name);
g_free(l1_table);
return ret;
return -1;
}
/* copy the snapshot 'snapshot_name' into the current disk image */
@@ -425,99 +322,38 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
QCowSnapshot *sn;
int i, snapshot_index;
int cur_l1_bytes, sn_l1_bytes;
int ret;
uint64_t *sn_l1_table = NULL;
/* Search the snapshot */
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
if (snapshot_index < 0) {
if (snapshot_index < 0)
return -ENOENT;
}
sn = &s->snapshots[snapshot_index];
if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
error_report("qcow2: Loading snapshots with different disk "
"size is not implemented");
ret = -ENOTSUP;
if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
goto fail;
}
/*
* Make sure that the current L1 table is big enough to contain the whole
* L1 table of the snapshot. If the snapshot L1 table is smaller, the
* current one must be padded with zeros.
*/
ret = qcow2_grow_l1_table(bs, sn->l1_size, true);
if (ret < 0) {
if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0)
goto fail;
}
cur_l1_bytes = s->l1_size * sizeof(uint64_t);
sn_l1_bytes = sn->l1_size * sizeof(uint64_t);
/*
* Copy the snapshot L1 table to the current L1 table.
*
* Before overwriting the old current L1 table on disk, make sure to
* increase all refcounts for the clusters referenced by the new one.
* Decrease the refcount referenced by the old one only when the L1
* table is overwritten.
*/
sn_l1_table = g_malloc0(cur_l1_bytes);
ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes);
if (ret < 0) {
goto fail;
if (cur_l1_bytes > sn_l1_bytes) {
memset(s->l1_table + sn->l1_size, 0, cur_l1_bytes - sn_l1_bytes);
}
ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset,
sn->l1_size, 1);
if (ret < 0) {
/* copy the snapshot l1 table to the current l1 table */
if (bdrv_pread(bs->file, sn->l1_table_offset,
s->l1_table, sn_l1_bytes) < 0)
goto fail;
}
ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table,
cur_l1_bytes);
if (ret < 0) {
if (bdrv_pwrite_sync(bs->file, s->l1_table_offset,
s->l1_table, cur_l1_bytes) < 0)
goto fail;
}
/*
* Decrease refcount of clusters of current L1 table.
*
* At this point, the in-memory s->l1_table points to the old L1 table,
* whereas on disk we already have the new one.
*
* qcow2_update_snapshot_refcount special cases the current L1 table to use
* the in-memory data instead of really using the offset to load a new one,
* which is why this works.
*/
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset,
s->l1_size, -1);
/*
* Now update the in-memory L1 table to be in sync with the on-disk one. We
* need to do this even if updating refcounts failed.
*/
for(i = 0;i < s->l1_size; i++) {
s->l1_table[i] = be64_to_cpu(sn_l1_table[i]);
be64_to_cpus(&s->l1_table[i]);
}
if (ret < 0) {
if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0)
goto fail;
}
g_free(sn_l1_table);
sn_l1_table = NULL;
/*
* Update QCOW_OFLAG_COPIED in the active L1 table (it may have changed
* when we decreased the refcount of the old snapshot.
*/
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
if (ret < 0) {
goto fail;
}
#ifdef DEBUG_ALLOC
{
@@ -526,59 +362,39 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
}
#endif
return 0;
fail:
g_free(sn_l1_table);
return ret;
fail:
return -EIO;
}
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
{
BDRVQcowState *s = bs->opaque;
QCowSnapshot sn;
QCowSnapshot *sn;
int snapshot_index, ret;
/* Search the snapshot */
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
if (snapshot_index < 0) {
if (snapshot_index < 0)
return -ENOENT;
}
sn = s->snapshots[snapshot_index];
sn = &s->snapshots[snapshot_index];
/* Remove it from the snapshot list */
memmove(s->snapshots + snapshot_index,
s->snapshots + snapshot_index + 1,
(s->nb_snapshots - snapshot_index - 1) * sizeof(sn));
ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1);
if (ret < 0)
return ret;
/* must update the copied flag on the current cluster offsets */
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
if (ret < 0)
return ret;
qcow2_free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t));
g_free(sn->id_str);
g_free(sn->name);
memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn));
s->nb_snapshots--;
ret = qcow2_write_snapshots(bs);
if (ret < 0) {
/* XXX: restore snapshot if error ? */
return ret;
}
/*
* The snapshot is now unused, clean up. If we fail after this point, we
* won't recover but just leak clusters.
*/
g_free(sn.id_str);
g_free(sn.name);
/*
* Now decrease the refcounts of clusters referenced by the snapshot and
* free the L1 table.
*/
ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset,
sn.l1_size, -1);
if (ret < 0) {
return ret;
}
qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t));
/* must update the copied flag on the current cluster offsets */
ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0);
if (ret < 0) {
return ret;
}
#ifdef DEBUG_ALLOC
{
BdrvCheckResult result = {0};
@@ -619,42 +435,32 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
{
int i, snapshot_index;
int i, snapshot_index, l1_size2;
BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn;
uint64_t *new_l1_table;
int new_l1_bytes;
int ret;
assert(bs->read_only);
/* Search the snapshot */
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
if (snapshot_index < 0) {
return -ENOENT;
}
sn = &s->snapshots[snapshot_index];
/* Allocate and read in the snapshot's L1 table */
new_l1_bytes = s->l1_size * sizeof(uint64_t);
new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512));
ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes);
if (ret < 0) {
g_free(new_l1_table);
return ret;
s->l1_size = sn->l1_size;
l1_size2 = s->l1_size * sizeof(uint64_t);
if (s->l1_table != NULL) {
g_free(s->l1_table);
}
/* Switch the L1 table */
g_free(s->l1_table);
s->l1_size = sn->l1_size;
s->l1_table_offset = sn->l1_table_offset;
s->l1_table = new_l1_table;
s->l1_table = g_malloc0(align_offset(l1_size2, 512));
if (bdrv_pread(bs->file, sn->l1_table_offset,
s->l1_table, l1_size2) != l1_size2) {
return -1;
}
for(i = 0;i < s->l1_size; i++) {
be64_to_cpus(&s->l1_table[i]);
}
return 0;
}

View File

@@ -29,7 +29,6 @@
#include "block/qcow2.h"
#include "qemu-error.h"
#include "qerror.h"
#include "trace.h"
/*
Differences with QCOW:
@@ -54,7 +53,6 @@ typedef struct {
} QCowExtension;
#define QCOW2_EXT_MAGIC_END 0
#define QCOW2_EXT_MAGIC_BACKING_FORMAT 0xE2792ACA
#define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857
static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
{
@@ -62,7 +60,7 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
if (buf_size >= sizeof(QCowHeader) &&
be32_to_cpu(cow_header->magic) == QCOW_MAGIC &&
be32_to_cpu(cow_header->version) >= 2)
be32_to_cpu(cow_header->version) >= QCOW_VERSION)
return 100;
else
return 0;
@@ -77,12 +75,10 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
* return 0 upon success, non-0 otherwise
*/
static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
uint64_t end_offset, void **p_feature_table)
uint64_t end_offset)
{
BDRVQcowState *s = bs->opaque;
QCowExtension ext;
uint64_t offset;
int ret;
#ifdef DEBUG_EXT
printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset);
@@ -91,11 +87,12 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
while (offset < end_offset) {
#ifdef DEBUG_EXT
BDRVQcowState *s = bs->opaque;
/* Sanity check */
if (offset > s->cluster_size)
printf("qcow2_read_extension: suspicious offset %lu\n", offset);
printf("attempting to read extended header in offset %lu\n", offset);
printf("attemting to read extended header in offset %lu\n", offset);
#endif
if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) {
@@ -110,11 +107,6 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
#ifdef DEBUG_EXT
printf("ext.magic = 0x%x\n", ext.magic);
#endif
if (ext.len > end_offset - offset) {
error_report("Header extension too large");
return -EINVAL;
}
switch (ext.magic) {
case QCOW2_EXT_MAGIC_END:
return 0;
@@ -133,86 +125,19 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
#ifdef DEBUG_EXT
printf("Qcow2: Got format extension %s\n", bs->backing_format);
#endif
break;
case QCOW2_EXT_MAGIC_FEATURE_TABLE:
if (p_feature_table != NULL) {
void* feature_table = g_malloc0(ext.len + 2 * sizeof(Qcow2Feature));
ret = bdrv_pread(bs->file, offset , feature_table, ext.len);
if (ret < 0) {
return ret;
}
*p_feature_table = feature_table;
}
offset = ((offset + ext.len + 7) & ~7);
break;
default:
/* unknown magic - save it in case we need to rewrite the header */
{
Qcow2UnknownHeaderExtension *uext;
uext = g_malloc0(sizeof(*uext) + ext.len);
uext->magic = ext.magic;
uext->len = ext.len;
QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next);
ret = bdrv_pread(bs->file, offset , uext->data, uext->len);
if (ret < 0) {
return ret;
}
}
/* unknown magic -- just skip it */
offset = ((offset + ext.len + 7) & ~7);
break;
}
offset += ((ext.len + 7) & ~7);
}
return 0;
}
static void cleanup_unknown_header_ext(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
Qcow2UnknownHeaderExtension *uext, *next;
QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) {
QLIST_REMOVE(uext, next);
g_free(uext);
}
}
static void GCC_FMT_ATTR(2, 3) report_unsupported(BlockDriverState *bs,
const char *fmt, ...)
{
char msg[64];
va_list ap;
va_start(ap, fmt);
vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
bs->device_name, "qcow2", msg);
}
static void report_unsupported_feature(BlockDriverState *bs,
Qcow2Feature *table, uint64_t mask)
{
while (table && table->name[0] != '\0') {
if (table->type == QCOW2_FEAT_TYPE_INCOMPATIBLE) {
if (mask & (1 << table->bit)) {
report_unsupported(bs, "%.46s",table->name);
mask &= ~(1 << table->bit);
}
}
table++;
}
if (mask) {
report_unsupported(bs, "Unknown incompatible feature: %" PRIx64, mask);
}
}
static int qcow2_open(BlockDriverState *bs, int flags)
{
@@ -244,76 +169,14 @@ static int qcow2_open(BlockDriverState *bs, int flags)
ret = -EINVAL;
goto fail;
}
if (header.version < 2 || header.version > 3) {
report_unsupported(bs, "QCOW version %d", header.version);
if (header.version != QCOW_VERSION) {
char version[64];
snprintf(version, sizeof(version), "QCOW version %d", header.version);
qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE,
bs->device_name, "qcow2", version);
ret = -ENOTSUP;
goto fail;
}
s->qcow_version = header.version;
/* Initialise version 3 header fields */
if (header.version == 2) {
header.incompatible_features = 0;
header.compatible_features = 0;
header.autoclear_features = 0;
header.refcount_order = 4;
header.header_length = 72;
} else {
be64_to_cpus(&header.incompatible_features);
be64_to_cpus(&header.compatible_features);
be64_to_cpus(&header.autoclear_features);
be32_to_cpus(&header.refcount_order);
be32_to_cpus(&header.header_length);
}
if (header.header_length > sizeof(header)) {
s->unknown_header_fields_size = header.header_length - sizeof(header);
s->unknown_header_fields = g_malloc(s->unknown_header_fields_size);
ret = bdrv_pread(bs->file, sizeof(header), s->unknown_header_fields,
s->unknown_header_fields_size);
if (ret < 0) {
goto fail;
}
}
if (header.backing_file_offset) {
ext_end = header.backing_file_offset;
} else {
ext_end = 1 << header.cluster_bits;
}
/* Handle feature bits */
s->incompatible_features = header.incompatible_features;
s->compatible_features = header.compatible_features;
s->autoclear_features = header.autoclear_features;
if (s->incompatible_features != 0) {
void *feature_table = NULL;
qcow2_read_extensions(bs, header.header_length, ext_end,
&feature_table);
report_unsupported_feature(bs, feature_table,
s->incompatible_features);
ret = -ENOTSUP;
goto fail;
}
if (!bs->read_only && s->autoclear_features != 0) {
s->autoclear_features = 0;
ret = qcow2_update_header(bs);
if (ret < 0) {
goto fail;
}
}
/* Check support for various header values */
if (header.refcount_order != 4) {
report_unsupported(bs, "%d bit reference counts",
1 << header.refcount_order);
ret = -ENOTSUP;
goto fail;
}
if (header.cluster_bits < MIN_CLUSTER_BITS ||
header.cluster_bits > MAX_CLUSTER_BITS) {
ret = -EINVAL;
@@ -387,7 +250,12 @@ static int qcow2_open(BlockDriverState *bs, int flags)
QLIST_INIT(&s->cluster_allocs);
/* read qcow2 extensions */
if (qcow2_read_extensions(bs, header.header_length, ext_end, NULL)) {
if (header.backing_file_offset) {
ext_end = header.backing_file_offset;
} else {
ext_end = s->cluster_size;
}
if (qcow2_read_extensions(bs, sizeof(header), ext_end)) {
ret = -EINVAL;
goto fail;
}
@@ -405,9 +273,8 @@ static int qcow2_open(BlockDriverState *bs, int flags)
}
bs->backing_file[len] = '\0';
}
ret = qcow2_read_snapshots(bs);
if (ret < 0) {
if (qcow2_read_snapshots(bs) < 0) {
ret = -EINVAL;
goto fail;
}
@@ -423,8 +290,6 @@ static int qcow2_open(BlockDriverState *bs, int flags)
return ret;
fail:
g_free(s->unknown_header_fields);
cleanup_unknown_header_ext(bs);
qcow2_free_snapshots(bs);
qcow2_refcount_close(bs);
g_free(s->l1_table);
@@ -478,19 +343,16 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key)
return 0;
}
static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum)
static int qcow2_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum)
{
BDRVQcowState *s = bs->opaque;
uint64_t cluster_offset;
int ret;
*pnum = nb_sectors;
/* FIXME We can get errors here, but the bdrv_co_is_allocated interface
* can't pass them on today */
qemu_co_mutex_lock(&s->lock);
/* FIXME We can get errors here, but the bdrv_is_allocated interface can't
* pass them on today */
ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset);
qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
*pnum = 0;
}
@@ -515,7 +377,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
return n1;
}
static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
int remaining_sectors, QEMUIOVector *qiov)
{
BDRVQcowState *s = bs->opaque;
@@ -552,8 +414,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
qemu_iovec_copy(&hd_qiov, qiov, bytes_done,
cur_nr_sectors * 512);
switch (ret) {
case QCOW2_CLUSTER_UNALLOCATED:
if (!cluster_offset) {
if (bs->backing_hd) {
/* read from the base image */
@@ -573,17 +434,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
/* Note: in this case, no need to wait */
qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors);
}
break;
case QCOW2_CLUSTER_ZERO:
if (s->qcow_version < 3) {
ret = -EIO;
goto fail;
}
qemu_iovec_memset(&hd_qiov, 0, 512 * cur_nr_sectors);
break;
case QCOW2_CLUSTER_COMPRESSED:
} else if (cluster_offset & QCOW_OFLAG_COMPRESSED) {
/* add AIO support for compressed blocks ? */
ret = qcow2_decompress_cluster(bs, cluster_offset);
if (ret < 0) {
@@ -593,9 +444,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
qemu_iovec_from_buffer(&hd_qiov,
s->cluster_cache + index_in_cluster * 512,
512 * cur_nr_sectors);
break;
case QCOW2_CLUSTER_NORMAL:
} else {
if ((cluster_offset & 511) != 0) {
ret = -EIO;
goto fail;
@@ -636,12 +485,6 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
qemu_iovec_from_buffer(&hd_qiov, cluster_data,
512 * cur_nr_sectors);
}
break;
default:
g_assert_not_reached();
ret = -EIO;
goto fail;
}
remaining_sectors -= cur_nr_sectors;
@@ -669,12 +512,12 @@ static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m)
/* Restart all dependent requests */
if (!qemu_co_queue_empty(&m->dependent_requests)) {
qemu_co_mutex_unlock(&s->lock);
qemu_co_queue_restart_all(&m->dependent_requests);
while(qemu_co_queue_next(&m->dependent_requests));
qemu_co_mutex_lock(&s->lock);
}
}
static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
static int qcow2_co_writev(BlockDriverState *bs,
int64_t sector_num,
int remaining_sectors,
QEMUIOVector *qiov)
@@ -692,9 +535,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
.nb_clusters = 0,
};
trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num,
remaining_sectors);
qemu_co_queue_init(&l2meta.dependent_requests);
qemu_iovec_init(&hd_qiov, qiov->niov);
@@ -705,7 +545,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
while (remaining_sectors != 0) {
trace_qcow2_writev_start_part(qemu_coroutine_self());
index_in_cluster = sector_num & (s->cluster_sectors - 1);
n_end = index_in_cluster + remaining_sectors;
if (s->crypt_method &&
@@ -746,8 +585,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
qemu_co_mutex_unlock(&s->lock);
trace_qcow2_writev_data(qemu_coroutine_self(),
(cluster_offset >> 9) + index_in_cluster);
ret = bdrv_co_writev(bs->file,
(cluster_offset >> 9) + index_in_cluster,
cur_nr_sectors, &hd_qiov);
@@ -766,7 +603,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
remaining_sectors -= cur_nr_sectors;
sector_num += cur_nr_sectors;
bytes_done += cur_nr_sectors * 512;
trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_nr_sectors);
}
ret = 0;
@@ -777,7 +613,6 @@ fail:
qemu_iovec_destroy(&hd_qiov);
qemu_vfree(cluster_data);
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
return ret;
}
@@ -793,13 +628,9 @@ static void qcow2_close(BlockDriverState *bs)
qcow2_cache_destroy(bs, s->l2_table_cache);
qcow2_cache_destroy(bs, s->refcount_block_cache);
g_free(s->unknown_header_fields);
cleanup_unknown_header_ext(bs);
g_free(s->cluster_cache);
qemu_vfree(s->cluster_data);
qcow2_refcount_close(bs);
qcow2_free_snapshots(bs);
}
static void qcow2_invalidate_cache(BlockDriverState *bs)
@@ -833,191 +664,103 @@ static void qcow2_invalidate_cache(BlockDriverState *bs)
}
}
static size_t header_ext_add(char *buf, uint32_t magic, const void *s,
size_t len, size_t buflen)
{
QCowExtension *ext_backing_fmt = (QCowExtension*) buf;
size_t ext_len = sizeof(QCowExtension) + ((len + 7) & ~7);
if (buflen < ext_len) {
return -ENOSPC;
}
*ext_backing_fmt = (QCowExtension) {
.magic = cpu_to_be32(magic),
.len = cpu_to_be32(len),
};
memcpy(buf + sizeof(QCowExtension), s, len);
return ext_len;
}
/*
* Updates the qcow2 header, including the variable length parts of it, i.e.
* the backing file name and all extensions. qcow2 was not designed to allow
* such changes, so if we run out of space (we can only use the first cluster)
* this function may fail.
* Updates the variable length parts of the qcow2 header, i.e. the backing file
* name and all extensions. qcow2 was not designed to allow such changes, so if
* we run out of space (we can only use the first cluster) this function may
* fail.
*
* Returns 0 on success, -errno in error cases.
*/
int qcow2_update_header(BlockDriverState *bs)
static int qcow2_update_ext_header(BlockDriverState *bs,
const char *backing_file, const char *backing_fmt)
{
size_t backing_file_len = 0;
size_t backing_fmt_len = 0;
BDRVQcowState *s = bs->opaque;
QCowHeader *header;
char *buf;
size_t buflen = s->cluster_size;
QCowExtension ext_backing_fmt = {0, 0};
int ret;
uint64_t total_size;
uint32_t refcount_table_clusters;
size_t header_length;
Qcow2UnknownHeaderExtension *uext;
buf = qemu_blockalign(bs, buflen);
/* Header structure */
header = (QCowHeader*) buf;
if (buflen < sizeof(*header)) {
ret = -ENOSPC;
goto fail;
}
header_length = sizeof(*header) + s->unknown_header_fields_size;
total_size = bs->total_sectors * BDRV_SECTOR_SIZE;
refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3);
*header = (QCowHeader) {
/* Version 2 fields */
.magic = cpu_to_be32(QCOW_MAGIC),
.version = cpu_to_be32(s->qcow_version),
.backing_file_offset = 0,
.backing_file_size = 0,
.cluster_bits = cpu_to_be32(s->cluster_bits),
.size = cpu_to_be64(total_size),
.crypt_method = cpu_to_be32(s->crypt_method_header),
.l1_size = cpu_to_be32(s->l1_size),
.l1_table_offset = cpu_to_be64(s->l1_table_offset),
.refcount_table_offset = cpu_to_be64(s->refcount_table_offset),
.refcount_table_clusters = cpu_to_be32(refcount_table_clusters),
.nb_snapshots = cpu_to_be32(s->nb_snapshots),
.snapshots_offset = cpu_to_be64(s->snapshots_offset),
/* Version 3 fields */
.incompatible_features = cpu_to_be64(s->incompatible_features),
.compatible_features = cpu_to_be64(s->compatible_features),
.autoclear_features = cpu_to_be64(s->autoclear_features),
.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT),
.header_length = cpu_to_be32(header_length),
};
/* For older versions, write a shorter header */
switch (s->qcow_version) {
case 2:
ret = offsetof(QCowHeader, incompatible_features);
break;
case 3:
ret = sizeof(*header);
break;
default:
/* Backing file format doesn't make sense without a backing file */
if (backing_fmt && !backing_file) {
return -EINVAL;
}
buf += ret;
buflen -= ret;
memset(buf, 0, buflen);
/* Prepare the backing file format extension if needed */
if (backing_fmt) {
ext_backing_fmt.len = cpu_to_be32(strlen(backing_fmt));
ext_backing_fmt.magic = cpu_to_be32(QCOW2_EXT_MAGIC_BACKING_FORMAT);
backing_fmt_len = ((sizeof(ext_backing_fmt)
+ strlen(backing_fmt) + 7) & ~7);
}
/* Preserve any unknown field in the header */
if (s->unknown_header_fields_size) {
if (buflen < s->unknown_header_fields_size) {
ret = -ENOSPC;
goto fail;
/* Check if we can fit the new header into the first cluster */
if (backing_file) {
backing_file_len = strlen(backing_file);
}
size_t header_size = sizeof(QCowHeader) + backing_file_len
+ backing_fmt_len;
if (header_size > s->cluster_size) {
return -ENOSPC;
}
/* Rewrite backing file name and qcow2 extensions */
size_t ext_size = header_size - sizeof(QCowHeader);
uint8_t buf[ext_size];
size_t offset = 0;
size_t backing_file_offset = 0;
if (backing_file) {
if (backing_fmt) {
int padding = backing_fmt_len -
(sizeof(ext_backing_fmt) + strlen(backing_fmt));
memcpy(buf + offset, &ext_backing_fmt, sizeof(ext_backing_fmt));
offset += sizeof(ext_backing_fmt);
memcpy(buf + offset, backing_fmt, strlen(backing_fmt));
offset += strlen(backing_fmt);
memset(buf + offset, 0, padding);
offset += padding;
}
memcpy(buf, s->unknown_header_fields, s->unknown_header_fields_size);
buf += s->unknown_header_fields_size;
buflen -= s->unknown_header_fields_size;
memcpy(buf + offset, backing_file, backing_file_len);
backing_file_offset = sizeof(QCowHeader) + offset;
}
/* Backing file format header extension */
if (*bs->backing_format) {
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT,
bs->backing_format, strlen(bs->backing_format),
buflen);
if (ret < 0) {
goto fail;
}
buf += ret;
buflen -= ret;
}
/* Feature table */
Qcow2Feature features[] = {
/* no feature defined yet */
};
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_FEATURE_TABLE,
features, sizeof(features), buflen);
if (ret < 0) {
goto fail;
}
buf += ret;
buflen -= ret;
/* Keep unknown header extensions */
QLIST_FOREACH(uext, &s->unknown_header_ext, next) {
ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen);
if (ret < 0) {
goto fail;
}
buf += ret;
buflen -= ret;
}
/* End of header extensions */
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_END, NULL, 0, buflen);
ret = bdrv_pwrite_sync(bs->file, sizeof(QCowHeader), buf, ext_size);
if (ret < 0) {
goto fail;
}
buf += ret;
buflen -= ret;
/* Update header fields */
uint64_t be_backing_file_offset = cpu_to_be64(backing_file_offset);
uint32_t be_backing_file_size = cpu_to_be32(backing_file_len);
/* Backing file name */
if (*bs->backing_file) {
size_t backing_file_len = strlen(bs->backing_file);
if (buflen < backing_file_len) {
ret = -ENOSPC;
goto fail;
}
strncpy(buf, bs->backing_file, buflen);
header->backing_file_offset = cpu_to_be64(buf - ((char*) header));
header->backing_file_size = cpu_to_be32(backing_file_len);
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_offset),
&be_backing_file_offset, sizeof(uint64_t));
if (ret < 0) {
goto fail;
}
/* Write the new header */
ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size);
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_size),
&be_backing_file_size, sizeof(uint32_t));
if (ret < 0) {
goto fail;
}
ret = 0;
fail:
qemu_vfree(header);
return ret;
}
static int qcow2_change_backing_file(BlockDriverState *bs,
const char *backing_file, const char *backing_fmt)
{
pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: "");
pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: "");
return qcow2_update_header(bs);
return qcow2_update_ext_header(bs, backing_file, backing_fmt);
}
static int preallocate(BlockDriverState *bs)
@@ -1076,9 +819,9 @@ static int preallocate(BlockDriverState *bs)
static int qcow2_create2(const char *filename, int64_t total_size,
const char *backing_file, const char *backing_format,
int flags, size_t cluster_size, int prealloc,
QEMUOptionParameter *options, int version)
QEMUOptionParameter *options)
{
/* Calculate cluster_bits */
/* Calulate cluster_bits */
int cluster_bits;
cluster_bits = ffs(cluster_size) - 1;
if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS ||
@@ -1120,15 +863,13 @@ static int qcow2_create2(const char *filename, int64_t total_size,
/* Write the header */
memset(&header, 0, sizeof(header));
header.magic = cpu_to_be32(QCOW_MAGIC);
header.version = cpu_to_be32(version);
header.version = cpu_to_be32(QCOW_VERSION);
header.cluster_bits = cpu_to_be32(cluster_bits);
header.size = cpu_to_be64(0);
header.l1_table_offset = cpu_to_be64(0);
header.l1_size = cpu_to_be32(0);
header.refcount_table_offset = cpu_to_be64(cluster_size);
header.refcount_table_clusters = cpu_to_be32(1);
header.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT);
header.header_length = cpu_to_be32(sizeof(header));
if (flags & BLOCK_FLAG_ENCRYPT) {
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
@@ -1190,10 +931,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
/* And if we're supposed to preallocate metadata, do that now */
if (prealloc) {
BDRVQcowState *s = bs->opaque;
qemu_co_mutex_lock(&s->lock);
ret = preallocate(bs);
qemu_co_mutex_unlock(&s->lock);
if (ret < 0) {
goto out;
}
@@ -1213,7 +951,6 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
int flags = 0;
size_t cluster_size = DEFAULT_CLUSTER_SIZE;
int prealloc = 0;
int version = 2;
/* Read out options */
while (options && options->name) {
@@ -1239,16 +976,6 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
options->value.s);
return -EINVAL;
}
} else if (!strcmp(options->name, BLOCK_OPT_COMPAT_LEVEL)) {
if (!options->value.s || !strcmp(options->value.s, "0.10")) {
version = 2;
} else if (!strcmp(options->value.s, "1.1")) {
version = 3;
} else {
fprintf(stderr, "Invalid compatibility level: '%s'\n",
options->value.s);
return -EINVAL;
}
}
options++;
}
@@ -1260,7 +987,7 @@ static int qcow2_create(const char *filename, QEMUOptionParameter *options)
}
return qcow2_create2(filename, sectors, backing_file, backing_fmt, flags,
cluster_size, prealloc, options, version);
cluster_size, prealloc, options);
}
static int qcow2_make_empty(BlockDriverState *bs)
@@ -1283,26 +1010,6 @@ static int qcow2_make_empty(BlockDriverState *bs)
return 0;
}
static coroutine_fn int qcow2_co_write_zeroes(BlockDriverState *bs,
int64_t sector_num, int nb_sectors)
{
int ret;
BDRVQcowState *s = bs->opaque;
/* Emulate misaligned zero writes */
if (sector_num % s->cluster_sectors || nb_sectors % s->cluster_sectors) {
return -ENOTSUP;
}
/* Whatever is left can use real zero clusters */
qemu_co_mutex_lock(&s->lock);
ret = qcow2_zero_clusters(bs, sector_num << BDRV_SECTOR_BITS,
nb_sectors);
qemu_co_mutex_unlock(&s->lock);
return ret;
}
static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
int64_t sector_num, int nb_sectors)
{
@@ -1322,19 +1029,16 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset)
int ret, new_l1_size;
if (offset & 511) {
error_report("The new size must be a multiple of 512");
return -EINVAL;
}
/* cannot proceed if image has snapshots */
if (s->nb_snapshots) {
error_report("Can't resize an image which has snapshots");
return -ENOTSUP;
}
/* shrinking is currently not supported */
if (offset < bs->total_sectors * 512) {
error_report("qcow2 doesn't support shrinking images yet");
return -ENOTSUP;
}
@@ -1433,7 +1137,7 @@ fail:
return ret;
}
static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
static int qcow2_co_flush_to_os(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
int ret;
@@ -1455,6 +1159,11 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
return 0;
}
static int qcow2_co_flush_to_disk(BlockDriverState *bs)
{
return bdrv_co_flush(bs->file);
}
static int64_t qcow2_vm_state_offset(BDRVQcowState *s)
{
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
@@ -1531,11 +1240,6 @@ static QEMUOptionParameter qcow2_create_options[] = {
.type = OPT_SIZE,
.help = "Virtual disk size"
},
{
.name = BLOCK_OPT_COMPAT_LEVEL,
.type = OPT_STRING,
.help = "Compatibility level (0.10 or 1.1)"
},
{
.name = BLOCK_OPT_BACKING_FILE,
.type = OPT_STRING,
@@ -1572,15 +1276,15 @@ static BlockDriver bdrv_qcow2 = {
.bdrv_open = qcow2_open,
.bdrv_close = qcow2_close,
.bdrv_create = qcow2_create,
.bdrv_co_is_allocated = qcow2_co_is_allocated,
.bdrv_is_allocated = qcow2_is_allocated,
.bdrv_set_key = qcow2_set_key,
.bdrv_make_empty = qcow2_make_empty,
.bdrv_co_readv = qcow2_co_readv,
.bdrv_co_writev = qcow2_co_writev,
.bdrv_co_flush_to_os = qcow2_co_flush_to_os,
.bdrv_co_flush_to_disk = qcow2_co_flush_to_disk,
.bdrv_co_write_zeroes = qcow2_co_write_zeroes,
.bdrv_co_discard = qcow2_co_discard,
.bdrv_truncate = qcow2_truncate,
.bdrv_write_compressed = qcow2_write_compressed,

View File

@@ -33,6 +33,7 @@
//#define DEBUG_EXT
#define QCOW_MAGIC (('Q' << 24) | ('F' << 16) | ('I' << 8) | 0xfb)
#define QCOW_VERSION 2
#define QCOW_CRYPT_NONE 0
#define QCOW_CRYPT_AES 1
@@ -43,8 +44,6 @@
#define QCOW_OFLAG_COPIED (1LL << 63)
/* indicate that the cluster is compressed (they never have the copied flag) */
#define QCOW_OFLAG_COMPRESSED (1LL << 62)
/* The cluster reads as all zeros */
#define QCOW_OFLAG_ZERO (1LL << 0)
#define REFCOUNT_SHIFT 1 /* refcount size is 2 bytes */
@@ -72,14 +71,6 @@ typedef struct QCowHeader {
uint32_t refcount_table_clusters;
uint32_t nb_snapshots;
uint64_t snapshots_offset;
/* The following fields are only valid for version >= 3 */
uint64_t incompatible_features;
uint64_t compatible_features;
uint64_t autoclear_features;
uint32_t refcount_order;
uint32_t header_length;
} QCowHeader;
typedef struct QCowSnapshot {
@@ -87,8 +78,7 @@ typedef struct QCowSnapshot {
uint32_t l1_size;
char *id_str;
char *name;
uint64_t disk_size;
uint64_t vm_state_size;
uint32_t vm_state_size;
uint32_t date_sec;
uint32_t date_nsec;
uint64_t vm_clock_nsec;
@@ -97,25 +87,6 @@ typedef struct QCowSnapshot {
struct Qcow2Cache;
typedef struct Qcow2Cache Qcow2Cache;
typedef struct Qcow2UnknownHeaderExtension {
uint32_t magic;
uint32_t len;
QLIST_ENTRY(Qcow2UnknownHeaderExtension) next;
uint8_t data[];
} Qcow2UnknownHeaderExtension;
enum {
QCOW2_FEAT_TYPE_INCOMPATIBLE = 0,
QCOW2_FEAT_TYPE_COMPATIBLE = 1,
QCOW2_FEAT_TYPE_AUTOCLEAR = 2,
};
typedef struct Qcow2Feature {
uint8_t type;
uint8_t bit;
char name[46];
} QEMU_PACKED Qcow2Feature;
typedef struct BDRVQcowState {
int cluster_bits;
int cluster_size;
@@ -156,15 +127,6 @@ typedef struct BDRVQcowState {
QCowSnapshot *snapshots;
int flags;
int qcow_version;
uint64_t incompatible_features;
uint64_t compatible_features;
uint64_t autoclear_features;
size_t unknown_header_fields_size;
void* unknown_header_fields;
QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext;
} BDRVQcowState;
/* XXX: use std qcow open function ? */
@@ -185,7 +147,6 @@ typedef struct QCowL2Meta
{
uint64_t offset;
uint64_t cluster_offset;
uint64_t alloc_offset;
int n_start;
int nb_available;
int nb_clusters;
@@ -194,19 +155,6 @@ typedef struct QCowL2Meta
QLIST_ENTRY(QCowL2Meta) next_in_flight;
} QCowL2Meta;
enum {
QCOW2_CLUSTER_UNALLOCATED,
QCOW2_CLUSTER_NORMAL,
QCOW2_CLUSTER_COMPRESSED,
QCOW2_CLUSTER_ZERO
};
#define L1E_OFFSET_MASK 0x00ffffffffffff00ULL
#define L2E_OFFSET_MASK 0x00ffffffffffff00ULL
#define L2E_COMPRESSED_OFFSET_SIZE_MASK 0x3fffffffffffffffULL
#define REFT_OFFSET_MASK 0xffffffffffffff00ULL
static inline int size_to_clusters(BDRVQcowState *s, int64_t size)
{
return (size + (s->cluster_size - 1)) >> s->cluster_bits;
@@ -224,34 +172,18 @@ static inline int64_t align_offset(int64_t offset, int n)
return offset;
}
static inline int qcow2_get_cluster_type(uint64_t l2_entry)
{
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
return QCOW2_CLUSTER_COMPRESSED;
} else if (l2_entry & QCOW_OFLAG_ZERO) {
return QCOW2_CLUSTER_ZERO;
} else if (!(l2_entry & L2E_OFFSET_MASK)) {
return QCOW2_CLUSTER_UNALLOCATED;
} else {
return QCOW2_CLUSTER_NORMAL;
}
}
// FIXME Need qcow2_ prefix to global functions
/* qcow2.c functions */
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t sector_num, int nb_sectors);
int qcow2_update_header(BlockDriverState *bs);
/* qcow2-refcount.c functions */
int qcow2_refcount_init(BlockDriverState *bs);
void qcow2_refcount_close(BlockDriverState *bs);
int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
int nb_clusters);
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);
void qcow2_free_clusters(BlockDriverState *bs,
int64_t offset, int64_t size);
@@ -283,7 +215,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
int nb_sectors);
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
/* qcow2-snapshot.c functions */
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);

View File

@@ -68,7 +68,6 @@ static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
{
BDRVQEDState *s = check->s;
unsigned int i, num_invalid = 0;
uint64_t last_offset = 0;
for (i = 0; i < s->table_nelems; i++) {
uint64_t offset = table->offsets[i];
@@ -77,11 +76,6 @@ static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
qed_offset_is_zero_cluster(offset)) {
continue;
}
check->result->bfi.allocated_clusters++;
if (last_offset && (last_offset + s->header.cluster_size != offset)) {
check->result->bfi.fragmented_clusters++;
}
last_offset = offset;
/* Detect invalid cluster offset */
if (!qed_check_cluster_offset(s, offset)) {
@@ -206,9 +200,6 @@ int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
check.used_clusters = g_malloc0(((check.nclusters + 31) / 32) *
sizeof(check.used_clusters[0]));
check.result->bfi.total_clusters =
(s->header.image_size + s->header.cluster_size - 1) /
s->header.cluster_size;
ret = qed_check_l1_table(&check, s->l1_table);
if (ret == 0) {
/* Only check for leaks if entire image was scanned successfully */

View File

@@ -161,25 +161,11 @@ void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table)
return;
}
/* Evict an unused cache entry so we have space. If all entries are in use
* we can grow the cache temporarily and we try to shrink back down later.
*/
if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) {
CachedL2Table *next;
QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next) {
if (entry->ref > 1) {
continue;
}
QTAILQ_REMOVE(&l2_cache->entries, entry, node);
l2_cache->n_entries--;
qed_unref_l2_cache_entry(entry);
/* Stop evicting when we've shrunk back to max size */
if (l2_cache->n_entries < MAX_L2_CACHE_SIZE) {
break;
}
}
entry = QTAILQ_FIRST(&l2_cache->entries);
QTAILQ_REMOVE(&l2_cache->entries, entry, node);
l2_cache->n_entries--;
qed_unref_l2_cache_entry(entry);
}
l2_cache->n_entries++;

View File

@@ -29,7 +29,7 @@ static void qed_read_table_cb(void *opaque, int ret)
{
QEDReadTableCB *read_table_cb = opaque;
QEDTable *table = read_table_cb->table;
int noffsets = read_table_cb->qiov.size / sizeof(uint64_t);
int noffsets = read_table_cb->iov.iov_len / sizeof(uint64_t);
int i;
/* Handle I/O error */
@@ -54,6 +54,7 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb),
cb, opaque);
QEMUIOVector *qiov = &read_table_cb->qiov;
BlockDriverAIOCB *aiocb;
trace_qed_read_table(s, offset, table);
@@ -63,9 +64,12 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size,
qemu_iovec_init_external(qiov, &read_table_cb->iov, 1);
bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov,
qiov->size / BDRV_SECTOR_SIZE,
qed_read_table_cb, read_table_cb);
aiocb = bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov,
read_table_cb->iov.iov_len / BDRV_SECTOR_SIZE,
qed_read_table_cb, read_table_cb);
if (!aiocb) {
qed_read_table_cb(read_table_cb, -EIO);
}
}
typedef struct {
@@ -123,6 +127,7 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
BlockDriverCompletionFunc *cb, void *opaque)
{
QEDWriteTableCB *write_table_cb;
BlockDriverAIOCB *aiocb;
unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
unsigned int start, end, i;
size_t len_bytes;
@@ -153,10 +158,13 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
/* Adjust for offset into table */
offset += start * sizeof(uint64_t);
bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
&write_table_cb->qiov,
write_table_cb->qiov.size / BDRV_SECTOR_SIZE,
qed_write_table_cb, write_table_cb);
aiocb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
&write_table_cb->qiov,
write_table_cb->iov.iov_len / BDRV_SECTOR_SIZE,
qed_write_table_cb, write_table_cb);
if (!aiocb) {
qed_write_table_cb(write_table_cb, -EIO);
}
}
/**

View File

@@ -123,6 +123,7 @@ static void qed_write_header_read_cb(void *opaque, int ret)
{
QEDWriteHeaderCB *write_header_cb = opaque;
BDRVQEDState *s = write_header_cb->s;
BlockDriverAIOCB *acb;
if (ret) {
qed_write_header_cb(write_header_cb, ret);
@@ -132,9 +133,12 @@ static void qed_write_header_read_cb(void *opaque, int ret)
/* Update header */
qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf);
bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov,
write_header_cb->nsectors, qed_write_header_cb,
write_header_cb);
acb = bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov,
write_header_cb->nsectors, qed_write_header_cb,
write_header_cb);
if (!acb) {
qed_write_header_cb(write_header_cb, -EIO);
}
}
/**
@@ -152,6 +156,7 @@ static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb,
* them, and write back.
*/
BlockDriverAIOCB *acb;
int nsectors = (sizeof(QEDHeader) + BDRV_SECTOR_SIZE - 1) /
BDRV_SECTOR_SIZE;
size_t len = nsectors * BDRV_SECTOR_SIZE;
@@ -165,8 +170,11 @@ static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb,
write_header_cb->iov.iov_len = len;
qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1);
bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors,
qed_write_header_read_cb, write_header_cb);
acb = bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors,
qed_write_header_read_cb, write_header_cb);
if (!acb) {
qed_write_header_cb(write_header_cb, -EIO);
}
}
static uint64_t qed_max_image_size(uint32_t cluster_size, uint32_t table_size)
@@ -367,12 +375,6 @@ static void qed_cancel_need_check_timer(BDRVQEDState *s)
qemu_del_timer(s->need_check_timer);
}
static void bdrv_qed_rebind(BlockDriverState *bs)
{
BDRVQEDState *s = bs->opaque;
s->bs = bs;
}
static int bdrv_qed_open(BlockDriverState *bs, int flags)
{
BDRVQEDState *s = bs->opaque;
@@ -456,7 +458,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
* feature is no longer valid.
*/
if ((s->header.autoclear_features & ~QED_AUTOCLEAR_FEATURE_MASK) != 0 &&
!bdrv_is_read_only(bs->file) && !(flags & BDRV_O_INCOMING)) {
!bdrv_is_read_only(bs->file)) {
s->header.autoclear_features &= QED_AUTOCLEAR_FEATURE_MASK;
ret = qed_write_header_sync(s);
@@ -483,8 +485,7 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
* potentially inconsistent images to be opened read-only. This can
* aid data recovery from an otherwise inconsistent image.
*/
if (!bdrv_is_read_only(bs->file) &&
!(flags & BDRV_O_INCOMING)) {
if (!bdrv_is_read_only(bs->file)) {
BdrvCheckResult result = {0};
ret = qed_check(s, &result, true);
@@ -504,6 +505,12 @@ static int bdrv_qed_open(BlockDriverState *bs, int flags)
s->need_check_timer = qemu_new_timer_ns(vm_clock,
qed_need_check_timer_cb, s);
error_set(&s->migration_blocker,
QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED,
"qed", bs->device_name, "live migration");
migrate_add_blocker(s->migration_blocker);
out:
if (ret) {
qed_free_l2_cache(&s->l2_cache);
@@ -516,6 +523,9 @@ static void bdrv_qed_close(BlockDriverState *bs)
{
BDRVQEDState *s = bs->opaque;
migrate_del_blocker(s->migration_blocker);
error_free(s->migration_blocker);
qed_cancel_need_check_timer(s);
qemu_free_timer(s->need_check_timer);
@@ -651,7 +661,6 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options)
}
typedef struct {
Coroutine *co;
int is_allocated;
int *pnum;
} QEDIsAllocatedCB;
@@ -661,14 +670,10 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l
QEDIsAllocatedCB *cb = opaque;
*cb->pnum = len / BDRV_SECTOR_SIZE;
cb->is_allocated = (ret == QED_CLUSTER_FOUND || ret == QED_CLUSTER_ZERO);
if (cb->co) {
qemu_coroutine_enter(cb->co, NULL);
}
}
static int coroutine_fn bdrv_qed_co_is_allocated(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors, int *pnum)
static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum)
{
BDRVQEDState *s = bs->opaque;
uint64_t pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE;
@@ -681,10 +686,8 @@ static int coroutine_fn bdrv_qed_co_is_allocated(BlockDriverState *bs,
qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb);
/* Now sleep if the callback wasn't invoked immediately */
while (cb.is_allocated == -1) {
cb.co = qemu_coroutine_self();
qemu_coroutine_yield();
qemu_aio_wait();
}
qed_unref_l2_cache_entry(request.l2_table);
@@ -718,6 +721,7 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
QEMUIOVector *qiov,
BlockDriverCompletionFunc *cb, void *opaque)
{
BlockDriverAIOCB *aiocb;
uint64_t backing_length = 0;
size_t size;
@@ -749,8 +753,11 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos,
size = MIN((uint64_t)backing_length - pos, qiov->size);
BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING);
bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE,
qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
aiocb = bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE,
qiov, size / BDRV_SECTOR_SIZE, cb, opaque);
if (!aiocb) {
cb(opaque, -EIO);
}
}
typedef struct {
@@ -772,6 +779,7 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret)
{
CopyFromBackingFileCB *copy_cb = opaque;
BDRVQEDState *s = copy_cb->s;
BlockDriverAIOCB *aiocb;
if (ret) {
qed_copy_from_backing_file_cb(copy_cb, ret);
@@ -779,9 +787,13 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret)
}
BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE);
bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE,
&copy_cb->qiov, copy_cb->qiov.size / BDRV_SECTOR_SIZE,
qed_copy_from_backing_file_cb, copy_cb);
aiocb = bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE,
&copy_cb->qiov,
copy_cb->qiov.size / BDRV_SECTOR_SIZE,
qed_copy_from_backing_file_cb, copy_cb);
if (!aiocb) {
qed_copy_from_backing_file_cb(copy_cb, -EIO);
}
}
/**
@@ -873,12 +885,6 @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
qemu_iovec_destroy(&acb->cur_qiov);
qed_unref_l2_cache_entry(acb->request.l2_table);
/* Free the buffer we may have allocated for zero writes */
if (acb->flags & QED_AIOCB_ZERO) {
qemu_vfree(acb->qiov->iov[0].iov_base);
acb->qiov->iov[0].iov_base = NULL;
}
/* Arrange for a bh to invoke the completion function */
acb->bh_ret = ret;
acb->bh = qemu_bh_new(qed_aio_complete_bh, acb);
@@ -945,8 +951,9 @@ static void qed_aio_write_l1_update(void *opaque, int ret)
/**
* Update L2 table with new cluster offsets and write them out
*/
static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
static void qed_aio_write_l2_update(void *opaque, int ret)
{
QEDAIOCB *acb = opaque;
BDRVQEDState *s = acb_to_s(acb);
bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1;
int index;
@@ -962,7 +969,7 @@ static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
index = qed_l2_index(s, acb->cur_pos);
qed_update_l2_table(s, acb->request.l2_table->table, index, acb->cur_nclusters,
offset);
acb->cur_cluster);
if (need_alloc) {
/* Write out the whole new L2 table */
@@ -979,12 +986,6 @@ err:
qed_aio_complete(acb, ret);
}
static void qed_aio_write_l2_update_cb(void *opaque, int ret)
{
QEDAIOCB *acb = opaque;
qed_aio_write_l2_update(acb, ret, acb->cur_cluster);
}
/**
* Flush new data clusters before updating the L2 table
*
@@ -999,7 +1000,7 @@ static void qed_aio_write_flush_before_l2_update(void *opaque, int ret)
QEDAIOCB *acb = opaque;
BDRVQEDState *s = acb_to_s(acb);
if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update_cb, opaque)) {
if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update, opaque)) {
qed_aio_complete(acb, -EIO);
}
}
@@ -1014,6 +1015,7 @@ static void qed_aio_write_main(void *opaque, int ret)
uint64_t offset = acb->cur_cluster +
qed_offset_into_cluster(s, acb->cur_pos);
BlockDriverCompletionFunc *next_fn;
BlockDriverAIOCB *file_acb;
trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size);
@@ -1028,14 +1030,18 @@ static void qed_aio_write_main(void *opaque, int ret)
if (s->bs->backing_hd) {
next_fn = qed_aio_write_flush_before_l2_update;
} else {
next_fn = qed_aio_write_l2_update_cb;
next_fn = qed_aio_write_l2_update;
}
}
BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO);
bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
&acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE,
next_fn, acb);
file_acb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
&acb->cur_qiov,
acb->cur_qiov.size / BDRV_SECTOR_SIZE,
next_fn, acb);
if (!file_acb) {
qed_aio_complete(acb, -EIO);
}
}
/**
@@ -1090,18 +1096,6 @@ static bool qed_should_set_need_check(BDRVQEDState *s)
return !(s->header.features & QED_F_NEED_CHECK);
}
static void qed_aio_write_zero_cluster(void *opaque, int ret)
{
QEDAIOCB *acb = opaque;
if (ret) {
qed_aio_complete(acb, ret);
return;
}
qed_aio_write_l2_update(acb, 0, 1);
}
/**
* Write new data cluster
*
@@ -1113,7 +1107,6 @@ static void qed_aio_write_zero_cluster(void *opaque, int ret)
static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
{
BDRVQEDState *s = acb_to_s(acb);
BlockDriverCompletionFunc *cb;
/* Cancel timer when the first allocating request comes in */
if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) {
@@ -1131,26 +1124,14 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
acb->cur_nclusters = qed_bytes_to_clusters(s,
qed_offset_into_cluster(s, acb->cur_pos) + len);
acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters);
qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
if (acb->flags & QED_AIOCB_ZERO) {
/* Skip ahead if the clusters are already zero */
if (acb->find_cluster_ret == QED_CLUSTER_ZERO) {
qed_aio_next_io(acb, 0);
return;
}
cb = qed_aio_write_zero_cluster;
} else {
cb = qed_aio_write_prefill;
acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters);
}
if (qed_should_set_need_check(s)) {
s->header.features |= QED_F_NEED_CHECK;
qed_write_header(s, cb, acb);
qed_write_header(s, qed_aio_write_prefill, acb);
} else {
cb(acb, 0);
qed_aio_write_prefill(acb, 0);
}
}
@@ -1165,16 +1146,6 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
*/
static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len)
{
/* Allocate buffer for zero writes */
if (acb->flags & QED_AIOCB_ZERO) {
struct iovec *iov = acb->qiov->iov;
if (!iov->iov_base) {
iov->iov_base = qemu_blockalign(acb->common.bs, iov->iov_len);
memset(iov->iov_base, 0, iov->iov_len);
}
}
/* Calculate the I/O vector */
acb->cur_cluster = offset;
qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len);
@@ -1237,6 +1208,7 @@ static void qed_aio_read_data(void *opaque, int ret,
QEDAIOCB *acb = opaque;
BDRVQEDState *s = acb_to_s(acb);
BlockDriverState *bs = acb->common.bs;
BlockDriverAIOCB *file_acb;
/* Adjust offset into cluster */
offset += qed_offset_into_cluster(s, acb->cur_pos);
@@ -1261,9 +1233,14 @@ static void qed_aio_read_data(void *opaque, int ret,
}
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE,
&acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE,
qed_aio_next_io, acb);
file_acb = bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE,
&acb->cur_qiov,
acb->cur_qiov.size / BDRV_SECTOR_SIZE,
qed_aio_next_io, acb);
if (!file_acb) {
ret = -EIO;
goto err;
}
return;
err:
@@ -1277,8 +1254,8 @@ static void qed_aio_next_io(void *opaque, int ret)
{
QEDAIOCB *acb = opaque;
BDRVQEDState *s = acb_to_s(acb);
QEDFindClusterFunc *io_fn = (acb->flags & QED_AIOCB_WRITE) ?
qed_aio_write_data : qed_aio_read_data;
QEDFindClusterFunc *io_fn =
acb->is_write ? qed_aio_write_data : qed_aio_read_data;
trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size);
@@ -1308,14 +1285,14 @@ static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs,
int64_t sector_num,
QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb,
void *opaque, int flags)
void *opaque, bool is_write)
{
QEDAIOCB *acb = qemu_aio_get(&qed_aio_pool, bs, cb, opaque);
trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors,
opaque, flags);
opaque, is_write);
acb->flags = flags;
acb->is_write = is_write;
acb->finished = NULL;
acb->qiov = qiov;
acb->qiov_offset = 0;
@@ -1335,7 +1312,7 @@ static BlockDriverAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs,
BlockDriverCompletionFunc *cb,
void *opaque)
{
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, false);
}
static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
@@ -1344,55 +1321,14 @@ static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs,
BlockDriverCompletionFunc *cb,
void *opaque)
{
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb,
opaque, QED_AIOCB_WRITE);
return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, true);
}
typedef struct {
Coroutine *co;
int ret;
bool done;
} QEDWriteZeroesCB;
static void coroutine_fn qed_co_write_zeroes_cb(void *opaque, int ret)
static BlockDriverAIOCB *bdrv_qed_aio_flush(BlockDriverState *bs,
BlockDriverCompletionFunc *cb,
void *opaque)
{
QEDWriteZeroesCB *cb = opaque;
cb->done = true;
cb->ret = ret;
if (cb->co) {
qemu_coroutine_enter(cb->co, NULL);
}
}
static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors)
{
BlockDriverAIOCB *blockacb;
QEDWriteZeroesCB cb = { .done = false };
QEMUIOVector qiov;
struct iovec iov;
/* Zero writes start without an I/O buffer. If a buffer becomes necessary
* then it will be allocated during request processing.
*/
iov.iov_base = NULL,
iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE,
qemu_iovec_init_external(&qiov, &iov, 1);
blockacb = qed_aio_setup(bs, sector_num, &qiov, nb_sectors,
qed_co_write_zeroes_cb, &cb,
QED_AIOCB_WRITE | QED_AIOCB_ZERO);
if (!blockacb) {
return -EIO;
}
if (!cb.done) {
cb.co = qemu_coroutine_self();
qemu_coroutine_yield();
}
assert(cb.done);
return cb.ret;
return bdrv_aio_flush(bs->file, cb, opaque);
}
static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset)
@@ -1432,7 +1368,6 @@ static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
memset(bdi, 0, sizeof(*bdi));
bdi->cluster_size = s->header.cluster_size;
bdi->is_dirty = s->header.features & QED_F_NEED_CHECK;
return 0;
}
@@ -1508,15 +1443,6 @@ static int bdrv_qed_change_backing_file(BlockDriverState *bs,
return ret;
}
static void bdrv_qed_invalidate_cache(BlockDriverState *bs)
{
BDRVQEDState *s = bs->opaque;
bdrv_qed_close(bs);
memset(s, 0, sizeof(BDRVQEDState));
bdrv_qed_open(bs, bs->open_flags);
}
static int bdrv_qed_check(BlockDriverState *bs, BdrvCheckResult *result)
{
BDRVQEDState *s = bs->opaque;
@@ -1556,20 +1482,18 @@ static BlockDriver bdrv_qed = {
.create_options = qed_create_options,
.bdrv_probe = bdrv_qed_probe,
.bdrv_rebind = bdrv_qed_rebind,
.bdrv_open = bdrv_qed_open,
.bdrv_close = bdrv_qed_close,
.bdrv_create = bdrv_qed_create,
.bdrv_co_is_allocated = bdrv_qed_co_is_allocated,
.bdrv_is_allocated = bdrv_qed_is_allocated,
.bdrv_make_empty = bdrv_qed_make_empty,
.bdrv_aio_readv = bdrv_qed_aio_readv,
.bdrv_aio_writev = bdrv_qed_aio_writev,
.bdrv_co_write_zeroes = bdrv_qed_co_write_zeroes,
.bdrv_aio_flush = bdrv_qed_aio_flush,
.bdrv_truncate = bdrv_qed_truncate,
.bdrv_getlength = bdrv_qed_getlength,
.bdrv_get_info = bdrv_qed_get_info,
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
.bdrv_invalidate_cache = bdrv_qed_invalidate_cache,
.bdrv_check = bdrv_qed_check,
};

View File

@@ -123,17 +123,12 @@ typedef struct QEDRequest {
CachedL2Table *l2_table;
} QEDRequest;
enum {
QED_AIOCB_WRITE = 0x0001, /* read or write? */
QED_AIOCB_ZERO = 0x0002, /* zero write, used with QED_AIOCB_WRITE */
};
typedef struct QEDAIOCB {
BlockDriverAIOCB common;
QEMUBH *bh;
int bh_ret; /* final return status for completion bh */
QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */
int flags; /* QED_AIOCB_* bits ORed together */
bool is_write; /* false - read, true - write */
bool *finished; /* signal for cancel completion */
uint64_t end_pos; /* request end on block device, in bytes */
@@ -169,6 +164,8 @@ typedef struct {
/* Periodic flush and clear need check flag */
QEMUTimer *need_check_timer;
Error *migration_blocker;
} BDRVQEDState;
enum {

View File

@@ -9,8 +9,6 @@
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#ifndef QEMU_RAW_POSIX_AIO_H
#define QEMU_RAW_POSIX_AIO_H

View File

@@ -29,7 +29,7 @@
#include "module.h"
#include "block/raw-posix-aio.h"
#if defined(__APPLE__) && (__MACH__)
#ifdef CONFIG_COCOA
#include <paths.h>
#include <sys/param.h>
#include <IOKit/IOKitLib.h>
@@ -505,7 +505,7 @@ again:
}
if (size == 0)
#endif
#if defined(__APPLE__) && defined(__MACH__)
#ifdef CONFIG_COCOA
size = LONG_LONG_MAX;
#else
size = lseek(fd, 0LL, SEEK_END);
@@ -650,7 +650,7 @@ static BlockDriver bdrv_file = {
/***********************************************/
/* host device */
#if defined(__APPLE__) && defined(__MACH__)
#ifdef CONFIG_COCOA
static kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator );
static kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize );
@@ -728,7 +728,7 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRawState *s = bs->opaque;
#if defined(__APPLE__) && defined(__MACH__)
#ifdef CONFIG_COCOA
if (strstart(filename, "/dev/cdrom", NULL)) {
kern_return_t kernResult;
io_iterator_t mediaIterator;
@@ -994,7 +994,7 @@ static int floppy_media_changed(BlockDriverState *bs)
return ret;
}
static void floppy_eject(BlockDriverState *bs, bool eject_flag)
static void floppy_eject(BlockDriverState *bs, int eject_flag)
{
BDRVRawState *s = bs->opaque;
int fd;
@@ -1084,7 +1084,7 @@ static int cdrom_is_inserted(BlockDriverState *bs)
return 0;
}
static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
static void cdrom_eject(BlockDriverState *bs, int eject_flag)
{
BDRVRawState *s = bs->opaque;
@@ -1153,7 +1153,7 @@ static int cdrom_open(BlockDriverState *bs, const char *filename, int flags)
if (ret)
return ret;
/* make sure the door isn't locked at this time */
/* make sure the door isnt locked at this time */
ioctl(s->fd, CDIOCALLOW);
return 0;
}
@@ -1184,7 +1184,7 @@ static int cdrom_reopen(BlockDriverState *bs)
}
s->fd = fd;
/* make sure the door isn't locked at this time */
/* make sure the door isnt locked at this time */
ioctl(s->fd, CDIOCALLOW);
return 0;
}
@@ -1194,7 +1194,7 @@ static int cdrom_is_inserted(BlockDriverState *bs)
return raw_getlength(bs) > 0;
}
static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
static void cdrom_eject(BlockDriverState *bs, int eject_flag)
{
BDRVRawState *s = bs->opaque;

View File

@@ -25,6 +25,11 @@ static void raw_close(BlockDriverState *bs)
{
}
static int coroutine_fn raw_co_flush(BlockDriverState *bs)
{
return bdrv_co_flush(bs->file);
}
static int64_t raw_getlength(BlockDriverState *bs)
{
return bdrv_getlength(bs->file);
@@ -56,7 +61,7 @@ static int raw_media_changed(BlockDriverState *bs)
return bdrv_media_changed(bs->file);
}
static void raw_eject(BlockDriverState *bs, bool eject_flag)
static void raw_eject(BlockDriverState *bs, int eject_flag)
{
bdrv_eject(bs->file, eject_flag);
}
@@ -108,6 +113,7 @@ static BlockDriver bdrv_raw = {
.bdrv_co_readv = raw_co_readv,
.bdrv_co_writev = raw_co_writev,
.bdrv_co_flush_to_disk = raw_co_flush,
.bdrv_co_discard = raw_co_discard,
.bdrv_probe = raw_probe,

View File

@@ -7,8 +7,6 @@
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include <inttypes.h>
@@ -44,13 +42,6 @@
* leading "\".
*/
/* rbd_aio_discard added in 0.1.2 */
#if LIBRBD_VERSION_CODE >= LIBRBD_VERSION(0, 1, 2)
#define LIBRBD_SUPPORTS_DISCARD
#else
#undef LIBRBD_SUPPORTS_DISCARD
#endif
#define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER)
#define RBD_MAX_CONF_NAME_SIZE 128
@@ -60,19 +51,13 @@
#define RBD_MAX_SNAP_NAME_SIZE 128
#define RBD_MAX_SNAPS 100
typedef enum {
RBD_AIO_READ,
RBD_AIO_WRITE,
RBD_AIO_DISCARD
} RBDAIOCmd;
typedef struct RBDAIOCB {
BlockDriverAIOCB common;
QEMUBH *bh;
int ret;
QEMUIOVector *qiov;
char *bounce;
RBDAIOCmd cmd;
int write;
int64_t sector_num;
int error;
struct BDRVRBDState *s;
@@ -384,8 +369,7 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
r = rcb->ret;
if (acb->cmd == RBD_AIO_WRITE ||
acb->cmd == RBD_AIO_DISCARD) {
if (acb->write) {
if (r < 0) {
acb->ret = r;
acb->error = 1;
@@ -518,7 +502,7 @@ static int qemu_rbd_open(BlockDriverState *bs, const char *filename, int flags)
fcntl(s->fds[0], F_SETFL, O_NONBLOCK);
fcntl(s->fds[1], F_SETFL, O_NONBLOCK);
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], qemu_rbd_aio_event_reader,
NULL, qemu_rbd_aio_flush_cb, s);
NULL, qemu_rbd_aio_flush_cb, NULL, s);
return 0;
@@ -539,7 +523,8 @@ static void qemu_rbd_close(BlockDriverState *bs)
close(s->fds[0]);
close(s->fds[1]);
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL, NULL, NULL, NULL);
qemu_aio_set_fd_handler(s->fds[RBD_FD_READ], NULL , NULL, NULL, NULL,
NULL);
rbd_close(s->image);
rados_ioctx_destroy(s->io_ctx);
@@ -619,7 +604,7 @@ static void rbd_aio_bh_cb(void *opaque)
{
RBDAIOCB *acb = opaque;
if (acb->cmd == RBD_AIO_READ) {
if (!acb->write) {
qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size);
}
qemu_vfree(acb->bounce);
@@ -630,25 +615,12 @@ static void rbd_aio_bh_cb(void *opaque)
qemu_aio_release(acb);
}
static int rbd_aio_discard_wrapper(rbd_image_t image,
uint64_t off,
uint64_t len,
rbd_completion_t comp)
{
#ifdef LIBRBD_SUPPORTS_DISCARD
return rbd_aio_discard(image, off, len, comp);
#else
return -ENOTSUP;
#endif
}
static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
int64_t sector_num,
QEMUIOVector *qiov,
int nb_sectors,
BlockDriverCompletionFunc *cb,
void *opaque,
RBDAIOCmd cmd)
static BlockDriverAIOCB *rbd_aio_rw_vector(BlockDriverState *bs,
int64_t sector_num,
QEMUIOVector *qiov,
int nb_sectors,
BlockDriverCompletionFunc *cb,
void *opaque, int write)
{
RBDAIOCB *acb;
RADOSCB *rcb;
@@ -660,20 +632,19 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
BDRVRBDState *s = bs->opaque;
acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque);
acb->cmd = cmd;
acb->qiov = qiov;
if (cmd == RBD_AIO_DISCARD) {
acb->bounce = NULL;
} else {
acb->bounce = qemu_blockalign(bs, qiov->size);
if (!acb) {
return NULL;
}
acb->write = write;
acb->qiov = qiov;
acb->bounce = qemu_blockalign(bs, qiov->size);
acb->ret = 0;
acb->error = 0;
acb->s = s;
acb->cancelled = 0;
acb->bh = NULL;
if (cmd == RBD_AIO_WRITE) {
if (write) {
qemu_iovec_to_buffer(acb->qiov, acb->bounce);
}
@@ -695,18 +666,10 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
goto failed;
}
switch (cmd) {
case RBD_AIO_WRITE:
if (write) {
r = rbd_aio_write(s->image, off, size, buf, c);
break;
case RBD_AIO_READ:
} else {
r = rbd_aio_read(s->image, off, size, buf, c);
break;
case RBD_AIO_DISCARD:
r = rbd_aio_discard_wrapper(s->image, off, size, c);
break;
default:
r = -EINVAL;
}
if (r < 0) {
@@ -729,8 +692,7 @@ static BlockDriverAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
BlockDriverCompletionFunc *cb,
void *opaque)
{
return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
RBD_AIO_READ);
return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
}
static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
@@ -740,8 +702,7 @@ static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
BlockDriverCompletionFunc *cb,
void *opaque)
{
return rbd_start_aio(bs, sector_num, qiov, nb_sectors, cb, opaque,
RBD_AIO_WRITE);
return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
}
static int qemu_rbd_co_flush(BlockDriverState *bs)
@@ -829,26 +790,6 @@ static int qemu_rbd_snap_create(BlockDriverState *bs,
return 0;
}
static int qemu_rbd_snap_remove(BlockDriverState *bs,
const char *snapshot_name)
{
BDRVRBDState *s = bs->opaque;
int r;
r = rbd_snap_remove(s->image, snapshot_name);
return r;
}
static int qemu_rbd_snap_rollback(BlockDriverState *bs,
const char *snapshot_name)
{
BDRVRBDState *s = bs->opaque;
int r;
r = rbd_snap_rollback(s->image, snapshot_name);
return r;
}
static int qemu_rbd_snap_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_tab)
{
@@ -891,18 +832,6 @@ static int qemu_rbd_snap_list(BlockDriverState *bs,
return snap_count;
}
#ifdef LIBRBD_SUPPORTS_DISCARD
static BlockDriverAIOCB* qemu_rbd_aio_discard(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors,
BlockDriverCompletionFunc *cb,
void *opaque)
{
return rbd_start_aio(bs, sector_num, NULL, nb_sectors, cb, opaque,
RBD_AIO_DISCARD);
}
#endif
static QEMUOptionParameter qemu_rbd_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -933,14 +862,8 @@ static BlockDriver bdrv_rbd = {
.bdrv_aio_writev = qemu_rbd_aio_writev,
.bdrv_co_flush_to_disk = qemu_rbd_co_flush,
#ifdef LIBRBD_SUPPORTS_DISCARD
.bdrv_aio_discard = qemu_rbd_aio_discard,
#endif
.bdrv_snapshot_create = qemu_rbd_snap_create,
.bdrv_snapshot_delete = qemu_rbd_snap_remove,
.bdrv_snapshot_list = qemu_rbd_snap_list,
.bdrv_snapshot_goto = qemu_rbd_snap_rollback,
};
static void bdrv_rbd_init(void)

View File

@@ -7,9 +7,6 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu-common.h"
@@ -32,11 +29,9 @@
#define SD_OP_RELEASE_VDI 0x13
#define SD_OP_GET_VDI_INFO 0x14
#define SD_OP_READ_VDIS 0x15
#define SD_OP_FLUSH_VDI 0x16
#define SD_FLAG_CMD_WRITE 0x01
#define SD_FLAG_CMD_COW 0x02
#define SD_FLAG_CMD_CACHE 0x04
#define SD_RES_SUCCESS 0x00 /* Success */
#define SD_RES_UNKNOWN 0x01 /* Unknown error */
@@ -295,12 +290,10 @@ typedef struct BDRVSheepdogState {
char name[SD_MAX_VDI_LEN];
int is_snapshot;
uint8_t cache_enabled;
char *addr;
char *port;
int fd;
int flush_fd;
CoMutex lock;
Coroutine *co_send;
@@ -450,6 +443,129 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
return acb;
}
#ifdef _WIN32
struct msghdr {
struct iovec *msg_iov;
size_t msg_iovlen;
};
static ssize_t sendmsg(int s, const struct msghdr *msg, int flags)
{
size_t size = 0;
char *buf, *p;
int i, ret;
/* count the msg size */
for (i = 0; i < msg->msg_iovlen; i++) {
size += msg->msg_iov[i].iov_len;
}
buf = g_malloc(size);
p = buf;
for (i = 0; i < msg->msg_iovlen; i++) {
memcpy(p, msg->msg_iov[i].iov_base, msg->msg_iov[i].iov_len);
p += msg->msg_iov[i].iov_len;
}
ret = send(s, buf, size, flags);
g_free(buf);
return ret;
}
static ssize_t recvmsg(int s, struct msghdr *msg, int flags)
{
size_t size = 0;
char *buf, *p;
int i, ret;
/* count the msg size */
for (i = 0; i < msg->msg_iovlen; i++) {
size += msg->msg_iov[i].iov_len;
}
buf = g_malloc(size);
ret = qemu_recv(s, buf, size, flags);
if (ret < 0) {
goto out;
}
p = buf;
for (i = 0; i < msg->msg_iovlen; i++) {
memcpy(msg->msg_iov[i].iov_base, p, msg->msg_iov[i].iov_len);
p += msg->msg_iov[i].iov_len;
}
out:
g_free(buf);
return ret;
}
#endif
/*
* Send/recv data with iovec buffers
*
* This function send/recv data from/to the iovec buffer directly.
* The first `offset' bytes in the iovec buffer are skipped and next
* `len' bytes are used.
*
* For example,
*
* do_send_recv(sockfd, iov, len, offset, 1);
*
* is equals to
*
* char *buf = malloc(size);
* iov_to_buf(iov, iovcnt, buf, offset, size);
* send(sockfd, buf, size, 0);
* free(buf);
*/
static int do_send_recv(int sockfd, struct iovec *iov, int len, int offset,
int write)
{
struct msghdr msg;
int ret, diff;
memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;
len += offset;
while (iov->iov_len < len) {
len -= iov->iov_len;
iov++;
msg.msg_iovlen++;
}
diff = iov->iov_len - len;
iov->iov_len -= diff;
while (msg.msg_iov->iov_len <= offset) {
offset -= msg.msg_iov->iov_len;
msg.msg_iov++;
msg.msg_iovlen--;
}
msg.msg_iov->iov_base = (char *) msg.msg_iov->iov_base + offset;
msg.msg_iov->iov_len -= offset;
if (write) {
ret = sendmsg(sockfd, &msg, 0);
} else {
ret = recvmsg(sockfd, &msg, 0);
}
msg.msg_iov->iov_base = (char *) msg.msg_iov->iov_base - offset;
msg.msg_iov->iov_len += offset;
iov->iov_len += diff;
return ret;
}
static int connect_to_sdog(const char *addr, const char *port)
{
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
@@ -502,57 +618,103 @@ success:
return fd;
}
static int do_readv_writev(int sockfd, struct iovec *iov, int len,
int iov_offset, int write)
{
int ret;
again:
ret = do_send_recv(sockfd, iov, len, iov_offset, write);
if (ret < 0) {
if (errno == EINTR) {
goto again;
}
if (errno == EAGAIN) {
if (qemu_in_coroutine()) {
qemu_coroutine_yield();
}
goto again;
}
error_report("failed to recv a rsp, %s", strerror(errno));
return 1;
}
iov_offset += ret;
len -= ret;
if (len) {
goto again;
}
return 0;
}
static int do_readv(int sockfd, struct iovec *iov, int len, int iov_offset)
{
return do_readv_writev(sockfd, iov, len, iov_offset, 0);
}
static int do_writev(int sockfd, struct iovec *iov, int len, int iov_offset)
{
return do_readv_writev(sockfd, iov, len, iov_offset, 1);
}
static int do_read_write(int sockfd, void *buf, int len, int write)
{
struct iovec iov;
iov.iov_base = buf;
iov.iov_len = len;
return do_readv_writev(sockfd, &iov, len, 0, write);
}
static int do_read(int sockfd, void *buf, int len)
{
return do_read_write(sockfd, buf, len, 0);
}
static int do_write(int sockfd, void *buf, int len)
{
return do_read_write(sockfd, buf, len, 1);
}
static int send_req(int sockfd, SheepdogReq *hdr, void *data,
unsigned int *wlen)
{
int ret;
struct iovec iov[2];
ret = qemu_send_full(sockfd, hdr, sizeof(*hdr), 0);
if (ret < sizeof(*hdr)) {
error_report("failed to send a req, %s", strerror(errno));
return ret;
iov[0].iov_base = hdr;
iov[0].iov_len = sizeof(*hdr);
if (*wlen) {
iov[1].iov_base = data;
iov[1].iov_len = *wlen;
}
ret = qemu_send_full(sockfd, data, *wlen, 0);
if (ret < *wlen) {
ret = do_writev(sockfd, iov, sizeof(*hdr) + *wlen, 0);
if (ret) {
error_report("failed to send a req, %s", strerror(errno));
ret = -1;
}
return ret;
}
static int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
unsigned int *wlen)
{
int ret;
ret = qemu_co_send(sockfd, hdr, sizeof(*hdr));
if (ret < sizeof(*hdr)) {
error_report("failed to send a req, %s", strerror(errno));
return ret;
}
ret = qemu_co_send(sockfd, data, *wlen);
if (ret < *wlen) {
error_report("failed to send a req, %s", strerror(errno));
}
return ret;
}
static int do_req(int sockfd, SheepdogReq *hdr, void *data,
unsigned int *wlen, unsigned int *rlen)
{
int ret;
socket_set_block(sockfd);
ret = send_req(sockfd, hdr, data, wlen);
if (ret < 0) {
if (ret) {
ret = -1;
goto out;
}
ret = qemu_recv_full(sockfd, hdr, sizeof(*hdr), 0);
if (ret < sizeof(*hdr)) {
ret = do_read(sockfd, hdr, sizeof(*hdr));
if (ret) {
error_report("failed to get a rsp, %s", strerror(errno));
ret = -1;
goto out;
}
@@ -561,49 +723,15 @@ static int do_req(int sockfd, SheepdogReq *hdr, void *data,
}
if (*rlen) {
ret = qemu_recv_full(sockfd, data, *rlen, 0);
if (ret < *rlen) {
ret = do_read(sockfd, data, *rlen);
if (ret) {
error_report("failed to get the data, %s", strerror(errno));
ret = -1;
goto out;
}
}
ret = 0;
out:
socket_set_nonblock(sockfd);
return ret;
}
static int do_co_req(int sockfd, SheepdogReq *hdr, void *data,
unsigned int *wlen, unsigned int *rlen)
{
int ret;
socket_set_block(sockfd);
ret = send_co_req(sockfd, hdr, data, wlen);
if (ret < 0) {
goto out;
}
ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr));
if (ret < sizeof(*hdr)) {
error_report("failed to get a rsp, %s", strerror(errno));
goto out;
}
if (*rlen > hdr->data_length) {
*rlen = hdr->data_length;
}
if (*rlen) {
ret = qemu_co_recv(sockfd, data, *rlen);
if (ret < *rlen) {
error_report("failed to get the data, %s", strerror(errno));
goto out;
}
}
ret = 0;
out:
socket_set_nonblock(sockfd);
return ret;
}
@@ -665,8 +793,8 @@ static void coroutine_fn aio_read_response(void *opaque)
}
/* read a header */
ret = qemu_co_recv(fd, &rsp, sizeof(rsp));
if (ret < 0) {
ret = do_read(fd, &rsp, sizeof(rsp));
if (ret) {
error_report("failed to get the header, %s", strerror(errno));
goto out;
}
@@ -686,9 +814,6 @@ static void coroutine_fn aio_read_response(void *opaque)
switch (acb->aiocb_type) {
case AIOCB_WRITE_UDATA:
/* this coroutine context is no longer suitable for co_recv
* because we may send data to update vdi objects */
s->co_recv = NULL;
if (!is_data_obj(aio_req->oid)) {
break;
}
@@ -714,9 +839,9 @@ static void coroutine_fn aio_read_response(void *opaque)
}
break;
case AIOCB_READ_UDATA:
ret = qemu_co_recvv(fd, acb->qiov->iov, rsp.data_length,
aio_req->iov_offset);
if (ret < 0) {
ret = do_readv(fd, acb->qiov->iov, rsp.data_length,
aio_req->iov_offset);
if (ret) {
error_report("failed to get the data, %s", strerror(errno));
goto out;
}
@@ -765,6 +890,22 @@ static int aio_flush_request(void *opaque)
return !QLIST_EMPTY(&s->outstanding_aio_head);
}
#if !defined(SOL_TCP) || !defined(TCP_CORK)
static int set_cork(int fd, int v)
{
return 0;
}
#else
static int set_cork(int fd, int v)
{
return setsockopt(fd, SOL_TCP, TCP_CORK, &v, sizeof(v));
}
#endif
static int set_nodelay(int fd)
{
int ret, opt;
@@ -799,7 +940,8 @@ static int get_sheep_fd(BDRVSheepdogState *s)
return -1;
}
qemu_aio_set_fd_handler(fd, co_read_response, NULL, aio_flush_request, s);
qemu_aio_set_fd_handler(fd, co_read_response, NULL, aio_flush_request,
NULL, s);
return fd;
}
@@ -956,10 +1098,6 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
hdr.flags = SD_FLAG_CMD_WRITE | flags;
}
if (s->cache_enabled) {
hdr.flags |= SD_FLAG_CMD_CACHE;
}
hdr.oid = oid;
hdr.cow_oid = old_oid;
hdr.copies = s->inode.nr_copies;
@@ -972,29 +1110,29 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
qemu_co_mutex_lock(&s->lock);
s->co_send = qemu_coroutine_self();
qemu_aio_set_fd_handler(s->fd, co_read_response, co_write_request,
aio_flush_request, s);
socket_set_cork(s->fd, 1);
aio_flush_request, NULL, s);
set_cork(s->fd, 1);
/* send a header */
ret = qemu_co_send(s->fd, &hdr, sizeof(hdr));
if (ret < 0) {
ret = do_write(s->fd, &hdr, sizeof(hdr));
if (ret) {
qemu_co_mutex_unlock(&s->lock);
error_report("failed to send a req, %s", strerror(errno));
return -EIO;
}
if (wlen) {
ret = qemu_co_sendv(s->fd, iov, wlen, aio_req->iov_offset);
if (ret < 0) {
ret = do_writev(s->fd, iov, wlen, aio_req->iov_offset);
if (ret) {
qemu_co_mutex_unlock(&s->lock);
error_report("failed to send a data, %s", strerror(errno));
return -EIO;
}
}
socket_set_cork(s->fd, 0);
set_cork(s->fd, 0);
qemu_aio_set_fd_handler(s->fd, co_read_response, NULL,
aio_flush_request, s);
aio_flush_request, NULL, s);
qemu_co_mutex_unlock(&s->lock);
return 0;
@@ -1002,7 +1140,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
unsigned int datalen, uint64_t offset,
int write, int create, uint8_t cache)
int write, int create)
{
SheepdogObjReq hdr;
SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr;
@@ -1025,11 +1163,6 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
rlen = datalen;
hdr.opcode = SD_OP_READ_OBJ;
}
if (cache) {
hdr.flags |= SD_FLAG_CMD_CACHE;
}
hdr.oid = oid;
hdr.data_length = datalen;
hdr.offset = offset;
@@ -1051,18 +1184,15 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
}
static int read_object(int fd, char *buf, uint64_t oid, int copies,
unsigned int datalen, uint64_t offset, uint8_t cache)
unsigned int datalen, uint64_t offset)
{
return read_write_object(fd, buf, oid, copies, datalen, offset, 0, 0,
cache);
return read_write_object(fd, buf, oid, copies, datalen, offset, 0, 0);
}
static int write_object(int fd, char *buf, uint64_t oid, int copies,
unsigned int datalen, uint64_t offset, int create,
uint8_t cache)
unsigned int datalen, uint64_t offset, int create)
{
return read_write_object(fd, buf, oid, copies, datalen, offset, 1, create,
cache);
return read_write_object(fd, buf, oid, copies, datalen, offset, 1, create);
}
static int sd_open(BlockDriverState *bs, const char *filename, int flags)
@@ -1094,15 +1224,6 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
goto out;
}
if (flags & BDRV_O_CACHE_WB) {
s->cache_enabled = 1;
s->flush_fd = connect_to_sdog(s->addr, s->port);
if (s->flush_fd < 0) {
error_report("failed to connect");
goto out;
}
}
if (snapid) {
dprintf("%" PRIx32 " snapshot inode was open.\n", vid);
s->is_snapshot = 1;
@@ -1115,8 +1236,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
}
buf = g_malloc(SD_INODE_SIZE);
ret = read_object(fd, buf, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE, 0,
s->cache_enabled);
ret = read_object(fd, buf, vid_to_vdi_oid(vid), 0, SD_INODE_SIZE, 0);
closesocket(fd);
@@ -1134,7 +1254,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
g_free(buf);
return 0;
out:
qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL);
qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL);
if (s->fd >= 0) {
closesocket(s->fd);
}
@@ -1348,11 +1468,8 @@ static void sd_close(BlockDriverState *bs)
error_report("%s, %s", sd_strerror(rsp->result), s->name);
}
qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL);
qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL);
closesocket(s->fd);
if (s->cache_enabled) {
closesocket(s->flush_fd);
}
g_free(s->addr);
}
@@ -1386,7 +1503,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id);
s->inode.vdi_size = offset;
ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id),
s->inode.nr_copies, datalen, 0, 0, s->cache_enabled);
s->inode.nr_copies, datalen, 0, 0);
close(fd);
if (ret < 0) {
@@ -1468,7 +1585,7 @@ static int sd_create_branch(BDRVSheepdogState *s)
}
ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies,
SD_INODE_SIZE, 0, s->cache_enabled);
SD_INODE_SIZE, 0);
closesocket(fd);
@@ -1598,7 +1715,7 @@ out:
return 1;
}
static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
static int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
SheepdogAIOCB *acb;
@@ -1627,7 +1744,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
return acb->ret;
}
static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
static int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, QEMUIOVector *qiov)
{
SheepdogAIOCB *acb;
@@ -1656,44 +1773,6 @@ static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num,
return acb->ret;
}
static int coroutine_fn sd_co_flush_to_disk(BlockDriverState *bs)
{
BDRVSheepdogState *s = bs->opaque;
SheepdogObjReq hdr = { 0 };
SheepdogObjRsp *rsp = (SheepdogObjRsp *)&hdr;
SheepdogInode *inode = &s->inode;
int ret;
unsigned int wlen = 0, rlen = 0;
if (!s->cache_enabled) {
return 0;
}
hdr.opcode = SD_OP_FLUSH_VDI;
hdr.oid = vid_to_vdi_oid(inode->vdi_id);
ret = do_co_req(s->flush_fd, (SheepdogReq *)&hdr, NULL, &wlen, &rlen);
if (ret) {
error_report("failed to send a request to the sheep");
return ret;
}
if (rsp->result == SD_RES_INVALID_PARMS) {
dprintf("disable write cache since the server doesn't support it\n");
s->cache_enabled = 0;
closesocket(s->flush_fd);
return 0;
}
if (rsp->result != SD_RES_SUCCESS) {
error_report("%s", sd_strerror(rsp->result));
return -EIO;
}
return 0;
}
static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
{
BDRVSheepdogState *s = bs->opaque;
@@ -1729,7 +1808,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
}
ret = write_object(fd, (char *)&s->inode, vid_to_vdi_oid(s->inode.vdi_id),
s->inode.nr_copies, datalen, 0, 0, s->cache_enabled);
s->inode.nr_copies, datalen, 0, 0);
if (ret < 0) {
error_report("failed to write snapshot's inode.");
ret = -EIO;
@@ -1748,7 +1827,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
inode = (SheepdogInode *)g_malloc(datalen);
ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid),
s->inode.nr_copies, datalen, 0, s->cache_enabled);
s->inode.nr_copies, datalen, 0);
if (ret < 0) {
error_report("failed to read new inode info. %s", strerror(errno));
@@ -1803,7 +1882,7 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
buf = g_malloc(SD_INODE_SIZE);
ret = read_object(fd, buf, vid_to_vdi_oid(vid), s->inode.nr_copies,
SD_INODE_SIZE, 0, s->cache_enabled);
SD_INODE_SIZE, 0);
closesocket(fd);
@@ -1898,8 +1977,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
/* we don't need to read entire object */
ret = read_object(fd, (char *)&inode, vid_to_vdi_oid(vid),
0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0,
s->cache_enabled);
0, SD_INODE_SIZE - sizeof(inode.data_vdi_id), 0);
if (ret) {
continue;
@@ -1955,12 +2033,10 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
create = (offset == 0);
if (load) {
ret = read_object(fd, (char *)data, vmstate_oid,
s->inode.nr_copies, data_len, offset,
s->cache_enabled);
s->inode.nr_copies, data_len, offset);
} else {
ret = write_object(fd, (char *)data, vmstate_oid,
s->inode.nr_copies, data_len, offset, create,
s->cache_enabled);
s->inode.nr_copies, data_len, offset, create);
}
if (ret < 0) {
@@ -2026,7 +2102,6 @@ BlockDriver bdrv_sheepdog = {
.bdrv_co_readv = sd_co_readv,
.bdrv_co_writev = sd_co_writev,
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
.bdrv_snapshot_create = sd_snapshot_create,
.bdrv_snapshot_goto = sd_snapshot_goto,

View File

@@ -1,280 +0,0 @@
/*
* Image streaming
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "trace.h"
#include "block_int.h"
enum {
/*
* Size of data buffer for populating the image file. This should be large
* enough to process multiple clusters in a single call, so that populating
* contiguous regions of the image is efficient.
*/
STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */
};
#define SLICE_TIME 100000000ULL /* ns */
typedef struct {
int64_t next_slice_time;
uint64_t slice_quota;
uint64_t dispatched;
} RateLimit;
static int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
{
int64_t now = qemu_get_clock_ns(rt_clock);
if (limit->next_slice_time < now) {
limit->next_slice_time = now + SLICE_TIME;
limit->dispatched = 0;
}
if (limit->dispatched == 0 || limit->dispatched + n <= limit->slice_quota) {
limit->dispatched += n;
return 0;
} else {
limit->dispatched = n;
return limit->next_slice_time - now;
}
}
static void ratelimit_set_speed(RateLimit *limit, uint64_t speed)
{
limit->slice_quota = speed / (1000000000ULL / SLICE_TIME);
}
typedef struct StreamBlockJob {
BlockJob common;
RateLimit limit;
BlockDriverState *base;
char backing_file_id[1024];
} StreamBlockJob;
static int coroutine_fn stream_populate(BlockDriverState *bs,
int64_t sector_num, int nb_sectors,
void *buf)
{
struct iovec iov = {
.iov_base = buf,
.iov_len = nb_sectors * BDRV_SECTOR_SIZE,
};
QEMUIOVector qiov;
qemu_iovec_init_external(&qiov, &iov, 1);
/* Copy-on-read the unallocated clusters */
return bdrv_co_copy_on_readv(bs, sector_num, nb_sectors, &qiov);
}
static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
const char *base_id)
{
BlockDriverState *intermediate;
intermediate = top->backing_hd;
while (intermediate) {
BlockDriverState *unused;
/* reached base */
if (intermediate == base) {
break;
}
unused = intermediate;
intermediate = intermediate->backing_hd;
unused->backing_hd = NULL;
bdrv_delete(unused);
}
top->backing_hd = base;
}
/*
* Given an image chain: [BASE] -> [INTER1] -> [INTER2] -> [TOP]
*
* Return true if the given sector is allocated in top.
* Return false if the given sector is allocated in intermediate images.
* Return true otherwise.
*
* 'pnum' is set to the number of sectors (including and immediately following
* the specified sector) that are known to be in the same
* allocated/unallocated state.
*
*/
static int coroutine_fn is_allocated_base(BlockDriverState *top,
BlockDriverState *base,
int64_t sector_num,
int nb_sectors, int *pnum)
{
BlockDriverState *intermediate;
int ret, n;
ret = bdrv_co_is_allocated(top, sector_num, nb_sectors, &n);
if (ret) {
*pnum = n;
return ret;
}
/*
* Is the unallocated chunk [sector_num, n] also
* unallocated between base and top?
*/
intermediate = top->backing_hd;
while (intermediate != base) {
int pnum_inter;
ret = bdrv_co_is_allocated(intermediate, sector_num, nb_sectors,
&pnum_inter);
if (ret < 0) {
return ret;
} else if (ret) {
*pnum = pnum_inter;
return 0;
}
/*
* [sector_num, nb_sectors] is unallocated on top but intermediate
* might have
*
* [sector_num+x, nr_sectors] allocated.
*/
if (n > pnum_inter) {
n = pnum_inter;
}
intermediate = intermediate->backing_hd;
}
*pnum = n;
return 1;
}
static void coroutine_fn stream_run(void *opaque)
{
StreamBlockJob *s = opaque;
BlockDriverState *bs = s->common.bs;
BlockDriverState *base = s->base;
int64_t sector_num, end;
int ret = 0;
int n = 0;
void *buf;
s->common.len = bdrv_getlength(bs);
if (s->common.len < 0) {
block_job_complete(&s->common, s->common.len);
return;
}
end = s->common.len >> BDRV_SECTOR_BITS;
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
/* Turn on copy-on-read for the whole block device so that guest read
* requests help us make progress. Only do this when copying the entire
* backing chain since the copy-on-read operation does not take base into
* account.
*/
if (!base) {
bdrv_enable_copy_on_read(bs);
}
for (sector_num = 0; sector_num < end; sector_num += n) {
uint64_t delay_ns = 0;
wait:
/* Note that even when no rate limit is applied we need to yield
* with no pending I/O here so that qemu_aio_flush() returns.
*/
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
if (block_job_is_cancelled(&s->common)) {
break;
}
ret = is_allocated_base(bs, base, sector_num,
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
trace_stream_one_iteration(s, sector_num, n, ret);
if (ret == 0) {
if (s->common.speed) {
delay_ns = ratelimit_calculate_delay(&s->limit, n);
if (delay_ns > 0) {
goto wait;
}
}
ret = stream_populate(bs, sector_num, n, buf);
}
if (ret < 0) {
break;
}
ret = 0;
/* Publish progress */
s->common.offset += n * BDRV_SECTOR_SIZE;
}
if (!base) {
bdrv_disable_copy_on_read(bs);
}
if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) {
const char *base_id = NULL, *base_fmt = NULL;
if (base) {
base_id = s->backing_file_id;
if (base->drv) {
base_fmt = base->drv->format_name;
}
}
ret = bdrv_change_backing_file(bs, base_id, base_fmt);
close_unused_images(bs, base, base_id);
}
qemu_vfree(buf);
block_job_complete(&s->common, ret);
}
static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
{
StreamBlockJob *s = container_of(job, StreamBlockJob, common);
if (speed < 0) {
error_set(errp, QERR_INVALID_PARAMETER, "speed");
return;
}
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE);
}
static BlockJobType stream_job_type = {
.instance_size = sizeof(StreamBlockJob),
.job_type = "stream",
.set_speed = stream_set_speed,
};
void stream_start(BlockDriverState *bs, BlockDriverState *base,
const char *base_id, int64_t speed,
BlockDriverCompletionFunc *cb,
void *opaque, Error **errp)
{
StreamBlockJob *s;
s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
s->base = base;
if (base_id) {
pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
}
s->common.co = qemu_coroutine_create(stream_run);
trace_stream_start(bs, base, s, s->common.co, opaque);
qemu_coroutine_enter(s->common.co, s);
}

View File

@@ -1,7 +1,7 @@
/*
* Block driver for the Virtual Disk Image (VDI) format
*
* Copyright (c) 2009, 2012 Stefan Weil
* Copyright (c) 2009 Stefan Weil
*
* 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
@@ -143,6 +143,29 @@ void uuid_unparse(const uuid_t uu, char *out)
}
#endif
typedef struct {
BlockDriverAIOCB common;
int64_t sector_num;
QEMUIOVector *qiov;
uint8_t *buf;
/* Total number of sectors. */
int nb_sectors;
/* Number of sectors for current AIO. */
int n_sectors;
/* New allocated block map entry. */
uint32_t bmap_first;
uint32_t bmap_last;
/* Buffer for new allocated block. */
void *block_buffer;
void *orig_buf;
bool is_write;
int header_modified;
BlockDriverAIOCB *hd_aiocb;
struct iovec hd_iov;
QEMUIOVector hd_qiov;
QEMUBH *bh;
} VdiAIOCB;
typedef struct {
char text[0x40];
uint32_t signature;
@@ -449,8 +472,8 @@ static int vdi_open(BlockDriverState *bs, int flags)
return -1;
}
static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum)
static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum)
{
/* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
@@ -466,150 +489,358 @@ static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs,
return VDI_IS_ALLOCATED(bmap_entry);
}
static int vdi_co_read(BlockDriverState *bs,
int64_t sector_num, uint8_t *buf, int nb_sectors)
static void vdi_aio_cancel(BlockDriverAIOCB *blockacb)
{
BDRVVdiState *s = bs->opaque;
uint32_t bmap_entry;
uint32_t block_index;
uint32_t sector_in_block;
uint32_t n_sectors;
int ret = 0;
/* TODO: This code is untested. How can I get it executed? */
VdiAIOCB *acb = container_of(blockacb, VdiAIOCB, common);
logout("\n");
while (ret >= 0 && nb_sectors > 0) {
block_index = sector_num / s->block_sectors;
sector_in_block = sector_num % s->block_sectors;
n_sectors = s->block_sectors - sector_in_block;
if (n_sectors > nb_sectors) {
n_sectors = nb_sectors;
}
logout("will read %u sectors starting at sector %" PRIu64 "\n",
n_sectors, sector_num);
/* prepare next AIO request */
bmap_entry = le32_to_cpu(s->bmap[block_index]);
if (!VDI_IS_ALLOCATED(bmap_entry)) {
/* Block not allocated, return zeros, no need to wait. */
memset(buf, 0, n_sectors * SECTOR_SIZE);
ret = 0;
} else {
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
(uint64_t)bmap_entry * s->block_sectors +
sector_in_block;
ret = bdrv_read(bs->file, offset, buf, n_sectors);
}
logout("%u sectors read\n", n_sectors);
nb_sectors -= n_sectors;
sector_num += n_sectors;
buf += n_sectors * SECTOR_SIZE;
if (acb->hd_aiocb) {
bdrv_aio_cancel(acb->hd_aiocb);
}
return ret;
qemu_aio_release(acb);
}
static int vdi_co_write(BlockDriverState *bs,
int64_t sector_num, const uint8_t *buf, int nb_sectors)
static AIOPool vdi_aio_pool = {
.aiocb_size = sizeof(VdiAIOCB),
.cancel = vdi_aio_cancel,
};
static VdiAIOCB *vdi_aio_setup(BlockDriverState *bs, int64_t sector_num,
QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque, int is_write)
{
VdiAIOCB *acb;
logout("%p, %" PRId64 ", %p, %d, %p, %p, %d\n",
bs, sector_num, qiov, nb_sectors, cb, opaque, is_write);
acb = qemu_aio_get(&vdi_aio_pool, bs, cb, opaque);
if (acb) {
acb->hd_aiocb = NULL;
acb->sector_num = sector_num;
acb->qiov = qiov;
acb->is_write = is_write;
if (qiov->niov > 1) {
acb->buf = qemu_blockalign(bs, qiov->size);
acb->orig_buf = acb->buf;
if (is_write) {
qemu_iovec_to_buffer(qiov, acb->buf);
}
} else {
acb->buf = (uint8_t *)qiov->iov->iov_base;
}
acb->nb_sectors = nb_sectors;
acb->n_sectors = 0;
acb->bmap_first = VDI_UNALLOCATED;
acb->bmap_last = VDI_UNALLOCATED;
acb->block_buffer = NULL;
acb->header_modified = 0;
}
return acb;
}
static int vdi_schedule_bh(QEMUBHFunc *cb, VdiAIOCB *acb)
{
logout("\n");
if (acb->bh) {
return -EIO;
}
acb->bh = qemu_bh_new(cb, acb);
if (!acb->bh) {
return -EIO;
}
qemu_bh_schedule(acb->bh);
return 0;
}
static void vdi_aio_read_cb(void *opaque, int ret);
static void vdi_aio_write_cb(void *opaque, int ret);
static void vdi_aio_rw_bh(void *opaque)
{
VdiAIOCB *acb = opaque;
logout("\n");
qemu_bh_delete(acb->bh);
acb->bh = NULL;
if (acb->is_write) {
vdi_aio_write_cb(opaque, 0);
} else {
vdi_aio_read_cb(opaque, 0);
}
}
static void vdi_aio_read_cb(void *opaque, int ret)
{
VdiAIOCB *acb = opaque;
BlockDriverState *bs = acb->common.bs;
BDRVVdiState *s = bs->opaque;
uint32_t bmap_entry;
uint32_t block_index;
uint32_t sector_in_block;
uint32_t n_sectors;
uint32_t bmap_first = VDI_UNALLOCATED;
uint32_t bmap_last = VDI_UNALLOCATED;
uint8_t *block = NULL;
int ret = 0;
logout("%u sectors read\n", acb->n_sectors);
acb->hd_aiocb = NULL;
if (ret < 0) {
goto done;
}
acb->nb_sectors -= acb->n_sectors;
if (acb->nb_sectors == 0) {
/* request completed */
ret = 0;
goto done;
}
acb->sector_num += acb->n_sectors;
acb->buf += acb->n_sectors * SECTOR_SIZE;
block_index = acb->sector_num / s->block_sectors;
sector_in_block = acb->sector_num % s->block_sectors;
n_sectors = s->block_sectors - sector_in_block;
if (n_sectors > acb->nb_sectors) {
n_sectors = acb->nb_sectors;
}
logout("will read %u sectors starting at sector %" PRIu64 "\n",
n_sectors, acb->sector_num);
/* prepare next AIO request */
acb->n_sectors = n_sectors;
bmap_entry = le32_to_cpu(s->bmap[block_index]);
if (!VDI_IS_ALLOCATED(bmap_entry)) {
/* Block not allocated, return zeros, no need to wait. */
memset(acb->buf, 0, n_sectors * SECTOR_SIZE);
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
if (ret < 0) {
goto done;
}
} else {
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
(uint64_t)bmap_entry * s->block_sectors +
sector_in_block;
acb->hd_iov.iov_base = (void *)acb->buf;
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov,
n_sectors, vdi_aio_read_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
goto done;
}
}
return;
done:
if (acb->qiov->niov > 1) {
qemu_iovec_from_buffer(acb->qiov, acb->orig_buf, acb->qiov->size);
qemu_vfree(acb->orig_buf);
}
acb->common.cb(acb->common.opaque, ret);
qemu_aio_release(acb);
}
static BlockDriverAIOCB *vdi_aio_readv(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
VdiAIOCB *acb;
int ret;
logout("\n");
while (ret >= 0 && nb_sectors > 0) {
block_index = sector_num / s->block_sectors;
sector_in_block = sector_num % s->block_sectors;
n_sectors = s->block_sectors - sector_in_block;
if (n_sectors > nb_sectors) {
n_sectors = nb_sectors;
}
logout("will write %u sectors starting at sector %" PRIu64 "\n",
n_sectors, sector_num);
/* prepare next AIO request */
bmap_entry = le32_to_cpu(s->bmap[block_index]);
if (!VDI_IS_ALLOCATED(bmap_entry)) {
/* Allocate new block and write to it. */
uint64_t offset;
bmap_entry = s->header.blocks_allocated;
s->bmap[block_index] = cpu_to_le32(bmap_entry);
s->header.blocks_allocated++;
offset = s->header.offset_data / SECTOR_SIZE +
(uint64_t)bmap_entry * s->block_sectors;
if (block == NULL) {
block = g_malloc(s->block_size);
bmap_first = block_index;
}
bmap_last = block_index;
/* Copy data to be written to new block and zero unused parts. */
memset(block, 0, sector_in_block * SECTOR_SIZE);
memcpy(block + sector_in_block * SECTOR_SIZE,
buf, n_sectors * SECTOR_SIZE);
memset(block + (sector_in_block + n_sectors) * SECTOR_SIZE, 0,
(s->block_sectors - n_sectors - sector_in_block) * SECTOR_SIZE);
ret = bdrv_write(bs->file, offset, block, s->block_sectors);
} else {
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
(uint64_t)bmap_entry * s->block_sectors +
sector_in_block;
ret = bdrv_write(bs->file, offset, buf, n_sectors);
}
nb_sectors -= n_sectors;
sector_num += n_sectors;
buf += n_sectors * SECTOR_SIZE;
logout("%u sectors written\n", n_sectors);
acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
if (!acb) {
return NULL;
}
logout("finished data write\n");
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
if (ret < 0) {
return ret;
}
if (block) {
/* One or more new blocks were allocated. */
VdiHeader *header = (VdiHeader *) block;
uint8_t *base;
uint64_t offset;
logout("now writing modified header\n");
assert(VDI_IS_ALLOCATED(bmap_first));
*header = s->header;
vdi_header_to_le(header);
ret = bdrv_write(bs->file, 0, block, 1);
g_free(block);
block = NULL;
if (ret < 0) {
return ret;
if (acb->qiov->niov > 1) {
qemu_vfree(acb->orig_buf);
}
logout("now writing modified block map entry %u...%u\n",
bmap_first, bmap_last);
/* Write modified sectors from block map. */
bmap_first /= (SECTOR_SIZE / sizeof(uint32_t));
bmap_last /= (SECTOR_SIZE / sizeof(uint32_t));
n_sectors = bmap_last - bmap_first + 1;
offset = s->bmap_sector + bmap_first;
base = ((uint8_t *)&s->bmap[0]) + bmap_first * SECTOR_SIZE;
logout("will write %u block map sectors starting from entry %u\n",
n_sectors, bmap_first);
ret = bdrv_write(bs->file, offset, base, n_sectors);
qemu_aio_release(acb);
return NULL;
}
return ret;
return &acb->common;
}
static void vdi_aio_write_cb(void *opaque, int ret)
{
VdiAIOCB *acb = opaque;
BlockDriverState *bs = acb->common.bs;
BDRVVdiState *s = bs->opaque;
uint32_t bmap_entry;
uint32_t block_index;
uint32_t sector_in_block;
uint32_t n_sectors;
acb->hd_aiocb = NULL;
if (ret < 0) {
goto done;
}
acb->nb_sectors -= acb->n_sectors;
acb->sector_num += acb->n_sectors;
acb->buf += acb->n_sectors * SECTOR_SIZE;
if (acb->nb_sectors == 0) {
logout("finished data write\n");
acb->n_sectors = 0;
if (acb->header_modified) {
VdiHeader *header = acb->block_buffer;
logout("now writing modified header\n");
assert(VDI_IS_ALLOCATED(acb->bmap_first));
*header = s->header;
vdi_header_to_le(header);
acb->header_modified = 0;
acb->hd_iov.iov_base = acb->block_buffer;
acb->hd_iov.iov_len = SECTOR_SIZE;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1,
vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
goto done;
}
return;
} else if (VDI_IS_ALLOCATED(acb->bmap_first)) {
/* One or more new blocks were allocated. */
uint64_t offset;
uint32_t bmap_first;
uint32_t bmap_last;
g_free(acb->block_buffer);
acb->block_buffer = NULL;
bmap_first = acb->bmap_first;
bmap_last = acb->bmap_last;
logout("now writing modified block map entry %u...%u\n",
bmap_first, bmap_last);
/* Write modified sectors from block map. */
bmap_first /= (SECTOR_SIZE / sizeof(uint32_t));
bmap_last /= (SECTOR_SIZE / sizeof(uint32_t));
n_sectors = bmap_last - bmap_first + 1;
offset = s->bmap_sector + bmap_first;
acb->bmap_first = VDI_UNALLOCATED;
acb->hd_iov.iov_base = (void *)((uint8_t *)&s->bmap[0] +
bmap_first * SECTOR_SIZE);
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
logout("will write %u block map sectors starting from entry %u\n",
n_sectors, bmap_first);
acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
n_sectors, vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
goto done;
}
return;
}
ret = 0;
goto done;
}
logout("%u sectors written\n", acb->n_sectors);
block_index = acb->sector_num / s->block_sectors;
sector_in_block = acb->sector_num % s->block_sectors;
n_sectors = s->block_sectors - sector_in_block;
if (n_sectors > acb->nb_sectors) {
n_sectors = acb->nb_sectors;
}
logout("will write %u sectors starting at sector %" PRIu64 "\n",
n_sectors, acb->sector_num);
/* prepare next AIO request */
acb->n_sectors = n_sectors;
bmap_entry = le32_to_cpu(s->bmap[block_index]);
if (!VDI_IS_ALLOCATED(bmap_entry)) {
/* Allocate new block and write to it. */
uint64_t offset;
uint8_t *block;
bmap_entry = s->header.blocks_allocated;
s->bmap[block_index] = cpu_to_le32(bmap_entry);
s->header.blocks_allocated++;
offset = s->header.offset_data / SECTOR_SIZE +
(uint64_t)bmap_entry * s->block_sectors;
block = acb->block_buffer;
if (block == NULL) {
block = g_malloc0(s->block_size);
acb->block_buffer = block;
acb->bmap_first = block_index;
assert(!acb->header_modified);
acb->header_modified = 1;
}
acb->bmap_last = block_index;
memcpy(block + sector_in_block * SECTOR_SIZE,
acb->buf, n_sectors * SECTOR_SIZE);
acb->hd_iov.iov_base = (void *)block;
acb->hd_iov.iov_len = s->block_size;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
acb->hd_aiocb = bdrv_aio_writev(bs->file, offset,
&acb->hd_qiov, s->block_sectors,
vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
goto done;
}
} else {
uint64_t offset = s->header.offset_data / SECTOR_SIZE +
(uint64_t)bmap_entry * s->block_sectors +
sector_in_block;
acb->hd_iov.iov_base = (void *)acb->buf;
acb->hd_iov.iov_len = n_sectors * SECTOR_SIZE;
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov,
n_sectors, vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
goto done;
}
}
return;
done:
if (acb->qiov->niov > 1) {
qemu_vfree(acb->orig_buf);
}
acb->common.cb(acb->common.opaque, ret);
qemu_aio_release(acb);
}
static BlockDriverAIOCB *vdi_aio_writev(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
VdiAIOCB *acb;
int ret;
logout("\n");
acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
if (!acb) {
return NULL;
}
ret = vdi_schedule_bh(vdi_aio_rw_bh, acb);
if (ret < 0) {
if (acb->qiov->niov > 1) {
qemu_vfree(acb->orig_buf);
}
qemu_aio_release(acb);
return NULL;
}
return &acb->common;
}
static int vdi_create(const char *filename, QEMUOptionParameter *options)
@@ -725,6 +956,13 @@ static void vdi_close(BlockDriverState *bs)
error_free(s->migration_blocker);
}
static coroutine_fn int vdi_co_flush(BlockDriverState *bs)
{
logout("\n");
return bdrv_co_flush(bs->file);
}
static QEMUOptionParameter vdi_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
@@ -757,12 +995,13 @@ static BlockDriver bdrv_vdi = {
.bdrv_open = vdi_open,
.bdrv_close = vdi_close,
.bdrv_create = vdi_create,
.bdrv_co_is_allocated = vdi_co_is_allocated,
.bdrv_co_flush_to_disk = vdi_co_flush,
.bdrv_is_allocated = vdi_is_allocated,
.bdrv_make_empty = vdi_make_empty,
.bdrv_read = vdi_co_read,
.bdrv_aio_readv = vdi_aio_readv,
#if defined(CONFIG_VDI_WRITE)
.bdrv_write = vdi_co_write,
.bdrv_aio_writev = vdi_aio_writev,
#endif
.bdrv_get_info = vdi_get_info,

View File

@@ -453,7 +453,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs,
}
l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte)
* le64_to_cpu(header.granularity);
if (l1_entry_sectors == 0) {
if (l1_entry_sectors <= 0) {
return -EINVAL;
}
l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)
@@ -861,8 +861,8 @@ static VmdkExtent *find_extent(BDRVVmdkState *s,
return NULL;
}
static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum)
static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum)
{
BDRVVmdkState *s = bs->opaque;
int64_t index_in_cluster, n, ret;
@@ -873,10 +873,8 @@ static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs,
if (!extent) {
return 0;
}
qemu_co_mutex_lock(&s->lock);
ret = get_cluster_offset(bs, extent, NULL,
sector_num * 512, 0, &offset);
qemu_co_mutex_unlock(&s->lock);
/* get_cluster_offset returning 0 means success */
ret = !ret;
@@ -1525,10 +1523,10 @@ static void vmdk_close(BlockDriverState *bs)
static coroutine_fn int vmdk_co_flush(BlockDriverState *bs)
{
int i, ret, err;
BDRVVmdkState *s = bs->opaque;
int i, err;
int ret = 0;
ret = bdrv_co_flush(bs->file);
for (i = 0; i < s->num_extents; i++) {
err = bdrv_co_flush(s->extents[i].file);
if (err < 0) {
@@ -1598,7 +1596,7 @@ static BlockDriver bdrv_vmdk = {
.bdrv_close = vmdk_close,
.bdrv_create = vmdk_create,
.bdrv_co_flush_to_disk = vmdk_co_flush,
.bdrv_co_is_allocated = vmdk_co_is_allocated,
.bdrv_is_allocated = vmdk_is_allocated,
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
.create_options = vmdk_create_options,

View File

@@ -161,27 +161,13 @@ static int vpc_open(BlockDriverState *bs, int flags)
uint8_t buf[HEADER_SIZE];
uint32_t checksum;
int err = -1;
int disk_type = VHD_DYNAMIC;
if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE)
goto fail;
footer = (struct vhd_footer*) s->footer_buf;
if (strncmp(footer->creator, "conectix", 8)) {
int64_t offset = bdrv_getlength(bs->file);
if (offset < HEADER_SIZE) {
goto fail;
}
/* If a fixed disk, the footer is found only at the end of the file */
if (bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE)
!= HEADER_SIZE) {
goto fail;
}
if (strncmp(footer->creator, "conectix", 8)) {
goto fail;
}
disk_type = VHD_FIXED;
}
if (strncmp(footer->creator, "conectix", 8))
goto fail;
checksum = be32_to_cpu(footer->checksum);
footer->checksum = 0;
@@ -189,9 +175,6 @@ static int vpc_open(BlockDriverState *bs, int flags)
fprintf(stderr, "block-vpc: The header checksum of '%s' is "
"incorrect.\n", bs->filename);
/* Write 'checksum' back to footer, or else will leave it with zero. */
footer->checksum = be32_to_cpu(checksum);
// The visible size of a image in Virtual PC depends on the geometry
// rather than on the size stored in the footer (the size in the footer
// is too large usually)
@@ -203,54 +186,49 @@ static int vpc_open(BlockDriverState *bs, int flags)
goto fail;
}
if (disk_type == VHD_DYNAMIC) {
if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf,
HEADER_SIZE) != HEADER_SIZE) {
goto fail;
if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE)
!= HEADER_SIZE)
goto fail;
dyndisk_header = (struct vhd_dyndisk_header*) buf;
if (strncmp(dyndisk_header->magic, "cxsparse", 8))
goto fail;
s->block_size = be32_to_cpu(dyndisk_header->block_size);
s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
s->pagetable = g_malloc(s->max_table_entries * 4);
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
s->max_table_entries * 4) != s->max_table_entries * 4)
goto fail;
s->free_data_block_offset =
(s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
for (i = 0; i < s->max_table_entries; i++) {
be32_to_cpus(&s->pagetable[i]);
if (s->pagetable[i] != 0xFFFFFFFF) {
int64_t next = (512 * (int64_t) s->pagetable[i]) +
s->bitmap_size + s->block_size;
if (next> s->free_data_block_offset)
s->free_data_block_offset = next;
}
}
dyndisk_header = (struct vhd_dyndisk_header *) buf;
if (strncmp(dyndisk_header->magic, "cxsparse", 8)) {
goto fail;
}
s->block_size = be32_to_cpu(dyndisk_header->block_size);
s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
s->pagetable = g_malloc(s->max_table_entries * 4);
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
if (bdrv_pread(bs->file, s->bat_offset, s->pagetable,
s->max_table_entries * 4) != s->max_table_entries * 4) {
goto fail;
}
s->free_data_block_offset =
(s->bat_offset + (s->max_table_entries * 4) + 511) & ~511;
for (i = 0; i < s->max_table_entries; i++) {
be32_to_cpus(&s->pagetable[i]);
if (s->pagetable[i] != 0xFFFFFFFF) {
int64_t next = (512 * (int64_t) s->pagetable[i]) +
s->bitmap_size + s->block_size;
if (next > s->free_data_block_offset) {
s->free_data_block_offset = next;
}
}
}
s->last_bitmap_offset = (int64_t) -1;
s->last_bitmap_offset = (int64_t) -1;
#ifdef CACHE
s->pageentry_u8 = g_malloc(512);
s->pageentry_u32 = s->pageentry_u8;
s->pageentry_u16 = s->pageentry_u8;
s->last_pagetable = -1;
s->pageentry_u8 = g_malloc(512);
s->pageentry_u32 = s->pageentry_u8;
s->pageentry_u16 = s->pageentry_u8;
s->last_pagetable = -1;
#endif
}
qemu_co_mutex_init(&s->lock);
@@ -417,11 +395,7 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num,
int ret;
int64_t offset;
int64_t sectors, sectors_per_block;
struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf;
if (cpu_to_be32(footer->type) == VHD_FIXED) {
return bdrv_read(bs->file, sector_num, buf, nb_sectors);
}
while (nb_sectors > 0) {
offset = get_sector_offset(bs, sector_num, 0);
@@ -466,11 +440,7 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num,
int64_t offset;
int64_t sectors, sectors_per_block;
int ret;
struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf;
if (cpu_to_be32(footer->type) == VHD_FIXED) {
return bdrv_write(bs->file, sector_num, buf, nb_sectors);
}
while (nb_sectors > 0) {
offset = get_sector_offset(bs, sector_num, 1);
@@ -510,6 +480,11 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num,
return ret;
}
static coroutine_fn int vpc_co_flush(BlockDriverState *bs)
{
return bdrv_co_flush(bs->file);
}
/*
* Calculates the number of cylinders, heads and sectors per cylinder
* based on a given number of sectors. This is the algorithm described
@@ -558,14 +533,70 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
return 0;
}
static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors)
static int vpc_create(const char *filename, QEMUOptionParameter *options)
{
uint8_t buf[1024];
struct vhd_footer* footer = (struct vhd_footer*) buf;
struct vhd_dyndisk_header* dyndisk_header =
(struct vhd_dyndisk_header*) buf;
int fd, i;
uint16_t cyls = 0;
uint8_t heads = 0;
uint8_t secs_per_cyl = 0;
size_t block_size, num_bat_entries;
int i;
int64_t total_sectors = 0;
int ret = -EIO;
// Read out options
total_sectors = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n /
BDRV_SECTOR_SIZE;
// Create the file
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0)
return -EIO;
/* Calculate matching total_size and geometry. Increase the number of
sectors requested until we get enough (or fail). */
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
if (calculate_geometry(total_sectors + i,
&cyls, &heads, &secs_per_cyl)) {
ret = -EFBIG;
goto fail;
}
}
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
// Prepare the Hard Disk Footer
memset(buf, 0, 1024);
memcpy(footer->creator, "conectix", 8);
// TODO Check if "qemu" creator_app is ok for VPC
memcpy(footer->creator_app, "qemu", 4);
memcpy(footer->creator_os, "Wi2k", 4);
footer->features = be32_to_cpu(0x02);
footer->version = be32_to_cpu(0x00010000);
footer->data_offset = be64_to_cpu(HEADER_SIZE);
footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
// Version of Virtual PC 2007
footer->major = be16_to_cpu(0x0005);
footer->minor =be16_to_cpu(0x0003);
footer->orig_size = be64_to_cpu(total_sectors * 512);
footer->size = be64_to_cpu(total_sectors * 512);
footer->cyls = be16_to_cpu(cyls);
footer->heads = heads;
footer->secs_per_cyl = secs_per_cyl;
footer->type = be32_to_cpu(VHD_DYNAMIC);
// TODO uuid is missing
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
// Write the footer (twice: at the beginning and at the end)
block_size = 0x200000;
num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
@@ -593,6 +624,7 @@ static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors)
}
}
// Prepare the Dynamic Disk Header
memset(buf, 0, 1024);
@@ -620,129 +652,6 @@ static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors)
}
ret = 0;
fail:
return ret;
}
static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size)
{
int ret = -EIO;
/* Add footer to total size */
total_size += 512;
if (ftruncate(fd, total_size) != 0) {
ret = -errno;
goto fail;
}
if (lseek(fd, -512, SEEK_END) < 0) {
goto fail;
}
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
goto fail;
}
ret = 0;
fail:
return ret;
}
static int vpc_create(const char *filename, QEMUOptionParameter *options)
{
uint8_t buf[1024];
struct vhd_footer *footer = (struct vhd_footer *) buf;
QEMUOptionParameter *disk_type_param;
int fd, i;
uint16_t cyls = 0;
uint8_t heads = 0;
uint8_t secs_per_cyl = 0;
int64_t total_sectors;
int64_t total_size;
int disk_type;
int ret = -EIO;
/* Read out options */
total_size = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n;
disk_type_param = get_option_parameter(options, BLOCK_OPT_SUBFMT);
if (disk_type_param && disk_type_param->value.s) {
if (!strcmp(disk_type_param->value.s, "dynamic")) {
disk_type = VHD_DYNAMIC;
} else if (!strcmp(disk_type_param->value.s, "fixed")) {
disk_type = VHD_FIXED;
} else {
return -EINVAL;
}
} else {
disk_type = VHD_DYNAMIC;
}
/* Create the file */
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd < 0) {
return -EIO;
}
/*
* Calculate matching total_size and geometry. Increase the number of
* sectors requested until we get enough (or fail). This ensures that
* qemu-img convert doesn't truncate images, but rather rounds up.
*/
total_sectors = total_size / BDRV_SECTOR_SIZE;
for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
if (calculate_geometry(total_sectors + i, &cyls, &heads,
&secs_per_cyl))
{
ret = -EFBIG;
goto fail;
}
}
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
/* Prepare the Hard Disk Footer */
memset(buf, 0, 1024);
memcpy(footer->creator, "conectix", 8);
/* TODO Check if "qemu" creator_app is ok for VPC */
memcpy(footer->creator_app, "qemu", 4);
memcpy(footer->creator_os, "Wi2k", 4);
footer->features = be32_to_cpu(0x02);
footer->version = be32_to_cpu(0x00010000);
if (disk_type == VHD_DYNAMIC) {
footer->data_offset = be64_to_cpu(HEADER_SIZE);
} else {
footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL);
}
footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE);
/* Version of Virtual PC 2007 */
footer->major = be16_to_cpu(0x0005);
footer->minor = be16_to_cpu(0x0003);
if (disk_type == VHD_DYNAMIC) {
footer->orig_size = be64_to_cpu(total_sectors * 512);
footer->size = be64_to_cpu(total_sectors * 512);
} else {
footer->orig_size = be64_to_cpu(total_size);
footer->size = be64_to_cpu(total_size);
}
footer->cyls = be16_to_cpu(cyls);
footer->heads = heads;
footer->secs_per_cyl = secs_per_cyl;
footer->type = be32_to_cpu(disk_type);
/* TODO uuid is missing */
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
if (disk_type == VHD_DYNAMIC) {
ret = create_dynamic_disk(fd, buf, total_sectors);
} else {
ret = create_fixed_disk(fd, buf, total_size);
}
fail:
close(fd);
return ret;
@@ -766,13 +675,6 @@ static QEMUOptionParameter vpc_create_options[] = {
.type = OPT_SIZE,
.help = "Virtual disk size"
},
{
.name = BLOCK_OPT_SUBFMT,
.type = OPT_STRING,
.help =
"Type of virtual hard disk format. Supported formats are "
"{dynamic (default) | fixed} "
},
{ NULL }
};
@@ -787,6 +689,7 @@ static BlockDriver bdrv_vpc = {
.bdrv_read = vpc_co_read,
.bdrv_write = vpc_co_write,
.bdrv_co_flush_to_disk = vpc_co_flush,
.create_options = vpc_create_options,
};

View File

@@ -982,12 +982,6 @@ static BDRVVVFATState *vvv = NULL;
static int enable_write_target(BDRVVVFATState *s);
static int is_consistent(BDRVVVFATState *s);
static void vvfat_rebind(BlockDriverState *bs)
{
BDRVVVFATState *s = bs->opaque;
s->bs = bs;
}
static int vvfat_open(BlockDriverState *bs, const char* dirname, int flags)
{
BDRVVVFATState *s = bs->opaque;
@@ -2224,7 +2218,6 @@ static int commit_one_file(BDRVVVFATState* s,
}
if (offset > 0) {
if (lseek(fd, offset, SEEK_SET) != offset) {
close(fd);
g_free(cluster);
return -3;
}
@@ -2245,13 +2238,11 @@ static int commit_one_file(BDRVVVFATState* s,
(uint8_t*)cluster, (rest_size + 0x1ff) / 0x200);
if (ret < 0) {
close(fd);
g_free(cluster);
return ret;
}
if (write(fd, cluster, rest_size) < 0) {
close(fd);
g_free(cluster);
return -2;
}
@@ -2767,7 +2758,7 @@ static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
return ret;
}
static int coroutine_fn vvfat_co_is_allocated(BlockDriverState *bs,
static int vvfat_is_allocated(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int* n)
{
BDRVVVFATState* s = bs->opaque;
@@ -2861,11 +2852,10 @@ static BlockDriver bdrv_vvfat = {
.format_name = "vvfat",
.instance_size = sizeof(BDRVVVFATState),
.bdrv_file_open = vvfat_open,
.bdrv_rebind = vvfat_rebind,
.bdrv_read = vvfat_co_read,
.bdrv_write = vvfat_co_write,
.bdrv_close = vvfat_close,
.bdrv_co_is_allocated = vvfat_co_is_allocated,
.bdrv_is_allocated = vvfat_is_allocated,
.protocol_name = "fat",
};

View File

@@ -34,13 +34,6 @@
#define BLOCK_FLAG_ENCRYPT 1
#define BLOCK_FLAG_COMPAT6 4
#define BLOCK_IO_LIMIT_READ 0
#define BLOCK_IO_LIMIT_WRITE 1
#define BLOCK_IO_LIMIT_TOTAL 2
#define BLOCK_IO_SLICE_TIME 100000000
#define NANOSECONDS_PER_SECOND 1000000000.0
#define BLOCK_OPT_SIZE "size"
#define BLOCK_OPT_ENCRYPT "encryption"
#define BLOCK_OPT_COMPAT6 "compat6"
@@ -50,86 +43,12 @@
#define BLOCK_OPT_TABLE_SIZE "table_size"
#define BLOCK_OPT_PREALLOC "preallocation"
#define BLOCK_OPT_SUBFMT "subformat"
#define BLOCK_OPT_COMPAT_LEVEL "compat"
typedef struct BdrvTrackedRequest BdrvTrackedRequest;
typedef struct BlockIOLimit {
int64_t bps[3];
int64_t iops[3];
} BlockIOLimit;
typedef struct BlockIOBaseValue {
uint64_t bytes[2];
uint64_t ios[2];
} BlockIOBaseValue;
typedef struct BlockJob BlockJob;
/**
* BlockJobType:
*
* A class type for block job objects.
*/
typedef struct BlockJobType {
/** Derived BlockJob struct size */
size_t instance_size;
/** String describing the operation, part of query-block-jobs QMP API */
const char *job_type;
/** Optional callback for job types that support setting a speed limit */
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
} BlockJobType;
/**
* BlockJob:
*
* Long-running operation on a BlockDriverState.
*/
struct BlockJob {
/** The job type, including the job vtable. */
const BlockJobType *job_type;
/** The block device on which the job is operating. */
BlockDriverState *bs;
/**
* The coroutine that executes the job. If not NULL, it is
* reentered when busy is false and the job is cancelled.
*/
Coroutine *co;
/**
* Set to true if the job should cancel itself. The flag must
* always be tested just before toggling the busy flag from false
* to true. After a job has been cancelled, it should only yield
* if #qemu_aio_wait will ("sooner or later") reenter the coroutine.
*/
bool cancelled;
/**
* Set to false by the job while it is in a quiescent state, where
* no I/O is pending and the job has yielded on any condition
* that is not detected by #qemu_aio_wait, such as a timer.
*/
bool busy;
/** Offset that is published by the query-block-jobs QMP API */
int64_t offset;
/** Length that is published by the query-block-jobs QMP API */
int64_t len;
/** Speed that was set with @block_job_set_speed. */
int64_t speed;
/** The completion function that will be called when the job completes. */
BlockDriverCompletionFunc *cb;
/** The opaque value that is passed to the completion function. */
void *opaque;
};
typedef struct AIOPool {
void (*cancel)(BlockDriverAIOCB *acb);
int aiocb_size;
BlockDriverAIOCB *free_aiocb;
} AIOPool;
struct BlockDriver {
const char *format_name;
@@ -143,8 +62,9 @@ struct BlockDriver {
int (*bdrv_write)(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
void (*bdrv_close)(BlockDriverState *bs);
void (*bdrv_rebind)(BlockDriverState *bs);
int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum);
int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
int (*bdrv_make_empty)(BlockDriverState *bs);
/* aio */
@@ -164,18 +84,8 @@ struct BlockDriver {
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov);
/*
* Efficiently zero a region of the disk image. Typically an image format
* would use a compact metadata representation to implement this. This
* function pointer may be NULL and .bdrv_co_writev() will be called
* instead.
*/
int coroutine_fn (*bdrv_co_write_zeroes)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors);
int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors);
int coroutine_fn (*bdrv_co_is_allocated)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *pnum);
/*
* Invalidate any cached meta-data.
@@ -195,6 +105,12 @@ struct BlockDriver {
*/
int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs);
int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs,
int num_reqs);
int (*bdrv_merge_requests)(BlockDriverState *bs, BlockRequest* a,
BlockRequest *b);
const char *protocol_name;
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
int64_t (*bdrv_getlength)(BlockDriverState *bs);
@@ -224,7 +140,7 @@ struct BlockDriver {
/* removable device specific */
int (*bdrv_is_inserted)(BlockDriverState *bs);
int (*bdrv_media_changed)(BlockDriverState *bs);
void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag);
void (*bdrv_eject)(BlockDriverState *bs, int eject_flag);
void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked);
/* to control generic scsi devices */
@@ -254,12 +170,6 @@ struct BlockDriver {
QLIST_ENTRY(BlockDriver) list;
};
/*
* Note: the function bdrv_append() copies and swaps contents of
* BlockDriverStates, so if you add new fields to this struct, please
* inspect bdrv_append() to determine if the new fields need to be
* copied as well.
*/
struct BlockDriverState {
int64_t total_sectors; /* if we are reading a disk image, give its
size in sectors */
@@ -269,8 +179,6 @@ struct BlockDriverState {
int encrypted; /* if true, the media is encrypted */
int valid_key; /* if true, a valid encryption key has been set */
int sg; /* if true, the device is a /dev/sg* */
int copy_on_read; /* if true, copy read backing sectors into image
note this is a reference count */
BlockDriver *drv; /* NULL means no media */
void *opaque;
@@ -289,18 +197,9 @@ struct BlockDriverState {
BlockDriverState *backing_hd;
BlockDriverState *file;
/* number of in-flight copy-on-read requests */
unsigned int copy_on_read_in_flight;
/* async read/write emulation */
/* the time for latest disk I/O */
int64_t slice_time;
int64_t slice_start;
int64_t slice_end;
BlockIOLimit io_limits;
BlockIOBaseValue io_base;
CoQueue throttled_reqs;
QEMUTimer *block_timer;
bool io_limits_enabled;
void *sync_aiocb;
/* I/O stats (display with "info blockstats"). */
uint64_t nr_bytes[BDRV_MAX_IOTYPE];
@@ -328,129 +227,25 @@ struct BlockDriverState {
int64_t dirty_count;
int in_use; /* users other than guest access, eg. block migration */
QTAILQ_ENTRY(BlockDriverState) list;
void *private;
};
QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
/* long-running background operation */
BlockJob *job;
struct BlockDriverAIOCB {
AIOPool *pool;
BlockDriverState *bs;
BlockDriverCompletionFunc *cb;
void *opaque;
BlockDriverAIOCB *next;
};
void get_tmp_filename(char *filename, int size);
void bdrv_set_io_limits(BlockDriverState *bs,
BlockIOLimit *io_limits);
void *qemu_aio_get(AIOPool *pool, BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque);
void qemu_aio_release(void *p);
#ifdef _WIN32
int is_windows_drive(const char *filename);
#endif
/**
* block_job_create:
* @job_type: The class object for the newly-created job.
* @bs: The block
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @cb: Completion function for the job.
* @opaque: Opaque pointer value passed to @cb.
* @errp: Error object.
*
* Create a new long-running block device job and return it. The job
* will call @cb asynchronously when the job completes. Note that
* @bs may have been closed at the time the @cb it is called. If
* this is the case, the job may be reported as either cancelled or
* completed.
*
* This function is not part of the public job interface; it should be
* called from a wrapper that is specific to the job type.
*/
void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
int64_t speed, BlockDriverCompletionFunc *cb,
void *opaque, Error **errp);
/**
* block_job_sleep_ns:
* @job: The job that calls the function.
* @clock: The clock to sleep on.
* @ns: How many nanoseconds to stop for.
*
* Put the job to sleep (assuming that it wasn't canceled) for @ns
* nanoseconds. Canceling the job will interrupt the wait immediately.
*/
void block_job_sleep_ns(BlockJob *job, QEMUClock *clock, int64_t ns);
/**
* block_job_complete:
* @job: The job being completed.
* @ret: The status code.
*
* Call the completion function that was registered at creation time, and
* free @job.
*/
void block_job_complete(BlockJob *job, int ret);
/**
* block_job_set_speed:
* @job: The job to set the speed for.
* @speed: The new value
* @errp: Error object.
*
* Set a rate-limiting parameter for the job; the actual meaning may
* vary depending on the job type.
*/
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
/**
* block_job_cancel:
* @job: The job to be canceled.
*
* Asynchronously cancel the specified job.
*/
void block_job_cancel(BlockJob *job);
/**
* block_job_is_cancelled:
* @job: The job being queried.
*
* Returns whether the job is scheduled for cancellation.
*/
bool block_job_is_cancelled(BlockJob *job);
/**
* block_job_cancel:
* @job: The job to be canceled.
*
* Asynchronously cancel the job and wait for it to reach a quiescent
* state. Note that the completion callback will still be called
* asynchronously, hence it is *not* valid to call #bdrv_delete
* immediately after #block_job_cancel_sync. Users of block jobs
* will usually protect the BlockDriverState objects with a reference
* count, should this be a concern.
*
* Returns the return value from the job if the job actually completed
* during the call, or -ECANCELED if it was canceled.
*/
int block_job_cancel_sync(BlockJob *job);
/**
* stream_start:
* @bs: Block device to operate on.
* @base: Block device that will become the new base, or %NULL to
* flatten the whole backing file chain onto @bs.
* @base_id: The file name that will be written to @bs as the new
* backing file if the job completes. Ignored if @base is %NULL.
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
* @cb: Completion function for the job.
* @opaque: Opaque pointer value passed to @cb.
* @errp: Error object.
*
* Start a streaming operation on @bs. Clusters that are unallocated
* in @bs, but allocated in any image between @base and @bs (both
* exclusive) will be written to @bs. At the end of a successful
* streaming job, the backing file of @bs will be changed to
* @base_id in the written image and to @base in the live BlockDriverState.
*/
void stream_start(BlockDriverState *bs, BlockDriverState *base,
const char *base_id, int64_t speed,
BlockDriverCompletionFunc *cb,
void *opaque, Error **errp);
#endif /* BLOCK_INT_H */

View File

@@ -13,12 +13,8 @@
#include "qerror.h"
#include "qemu-option.h"
#include "qemu-config.h"
#include "qemu-objects.h"
#include "sysemu.h"
#include "block_int.h"
#include "qmp-commands.h"
#include "trace.h"
#include "arch_init.h"
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
@@ -64,9 +60,6 @@ void blockdev_mark_auto_del(BlockDriverState *bs)
{
DriveInfo *dinfo = drive_get_by_blockdev(bs);
if (bs->job) {
block_job_cancel(bs->job);
}
if (dinfo) {
dinfo->auto_del = 1;
}
@@ -206,37 +199,6 @@ void drive_get_ref(DriveInfo *dinfo)
dinfo->refcount++;
}
typedef struct {
QEMUBH *bh;
DriveInfo *dinfo;
} DrivePutRefBH;
static void drive_put_ref_bh(void *opaque)
{
DrivePutRefBH *s = opaque;
drive_put_ref(s->dinfo);
qemu_bh_delete(s->bh);
g_free(s);
}
/*
* Release a drive reference in a BH
*
* It is not possible to use drive_put_ref() from a callback function when the
* callers still need the drive. In such cases we schedule a BH to release the
* reference.
*/
static void drive_put_ref_bh_schedule(DriveInfo *dinfo)
{
DrivePutRefBH *s;
s = g_new(DrivePutRefBH, 1);
s->bh = qemu_bh_new(drive_put_ref_bh, s);
s->dinfo = dinfo;
qemu_bh_schedule(s->bh);
}
static int parse_block_error_action(const char *buf, int is_read)
{
if (!strcmp(buf, "ignore")) {
@@ -254,26 +216,6 @@ static int parse_block_error_action(const char *buf, int is_read)
}
}
static bool do_check_io_limits(BlockIOLimit *io_limits)
{
bool bps_flag;
bool iops_flag;
assert(io_limits);
bps_flag = (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] != 0)
&& ((io_limits->bps[BLOCK_IO_LIMIT_READ] != 0)
|| (io_limits->bps[BLOCK_IO_LIMIT_WRITE] != 0));
iops_flag = (io_limits->iops[BLOCK_IO_LIMIT_TOTAL] != 0)
&& ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0)
|| (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0));
if (bps_flag || iops_flag) {
return false;
}
return true;
}
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
{
const char *buf;
@@ -293,9 +235,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
int on_read_error, on_write_error;
const char *devaddr;
DriveInfo *dinfo;
BlockIOLimit io_limits;
int snapshot = 0;
bool copy_on_read;
int ret;
translation = BIOS_ATA_TRANSLATION_AUTO;
@@ -312,7 +252,6 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
snapshot = qemu_opt_get_bool(opts, "snapshot", 0);
ro = qemu_opt_get_bool(opts, "readonly", 0);
copy_on_read = qemu_opt_get_bool(opts, "copy-on-read", false);
file = qemu_opt_get(opts, "file");
serial = qemu_opt_get(opts, "serial");
@@ -414,26 +353,6 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
}
}
/* disk I/O throttling */
io_limits.bps[BLOCK_IO_LIMIT_TOTAL] =
qemu_opt_get_number(opts, "bps", 0);
io_limits.bps[BLOCK_IO_LIMIT_READ] =
qemu_opt_get_number(opts, "bps_rd", 0);
io_limits.bps[BLOCK_IO_LIMIT_WRITE] =
qemu_opt_get_number(opts, "bps_wr", 0);
io_limits.iops[BLOCK_IO_LIMIT_TOTAL] =
qemu_opt_get_number(opts, "iops", 0);
io_limits.iops[BLOCK_IO_LIMIT_READ] =
qemu_opt_get_number(opts, "iops_rd", 0);
io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
qemu_opt_get_number(opts, "iops_wr", 0);
if (!do_check_io_limits(&io_limits)) {
error_report("bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
"cannot be used at the same time");
return NULL;
}
on_write_error = BLOCK_ERR_STOP_ENOSPC;
if ((buf = qemu_opt_get(opts, "werror")) != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) {
@@ -535,16 +454,12 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
dinfo->unit = unit_id;
dinfo->opts = opts;
dinfo->refcount = 1;
if (serial) {
pstrcpy(dinfo->serial, sizeof(dinfo->serial), serial);
}
if (serial)
strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
/* disk I/O throttling */
bdrv_set_io_limits(dinfo->bdrv, &io_limits);
switch(type) {
case IF_IDE:
case IF_SCSI:
@@ -570,11 +485,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
case IF_VIRTIO:
/* add virtio block device */
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
if (arch_type == QEMU_ARCH_S390X) {
qemu_opt_set(opts, "driver", "virtio-blk-s390");
} else {
qemu_opt_set(opts, "driver", "virtio-blk-pci");
}
qemu_opt_set(opts, "driver", "virtio-blk");
qemu_opt_set(opts, "drive", dinfo->id);
if (devaddr)
qemu_opt_set(opts, "addr", devaddr);
@@ -591,20 +502,11 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
bdrv_flags |= (BDRV_O_SNAPSHOT|BDRV_O_CACHE_WB|BDRV_O_NO_FLUSH);
}
if (copy_on_read) {
bdrv_flags |= BDRV_O_COPY_ON_READ;
}
if (runstate_check(RUN_STATE_INMIGRATE)) {
bdrv_flags |= BDRV_O_INCOMING;
}
if (media == MEDIA_CDROM) {
/* CDROM is fine for any interface, don't check. */
ro = 1;
} else if (ro == 1) {
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY &&
type != IF_NONE && type != IF_PFLASH) {
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
error_report("readonly not supported by this bus type");
goto err;
}
@@ -635,354 +537,182 @@ void do_commit(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
BlockDriverState *bs;
int ret;
if (!strcmp(device, "all")) {
ret = bdrv_commit_all();
if (ret == -EBUSY) {
qerror_report(QERR_DEVICE_IN_USE, device);
return;
}
bdrv_commit_all();
} else {
bs = bdrv_find(device);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
return;
}
ret = bdrv_commit(bs);
if (ret == -EBUSY) {
qerror_report(QERR_DEVICE_IN_USE, device);
return;
}
bdrv_commit(bs);
}
}
static void blockdev_do_action(int kind, void *data, Error **errp)
{
BlockdevAction action;
BlockdevActionList list;
action.kind = kind;
action.data = data;
list.value = &action;
list.next = NULL;
qmp_transaction(&list, errp);
}
void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file,
bool has_format, const char *format,
bool has_mode, enum NewImageMode mode,
Error **errp)
{
BlockdevSnapshot snapshot = {
.device = (char *) device,
.snapshot_file = (char *) snapshot_file,
.has_format = has_format,
.format = (char *) format,
.has_mode = has_mode,
.mode = mode,
};
blockdev_do_action(BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, &snapshot,
errp);
}
/* New and old BlockDriverState structs for group snapshots */
typedef struct BlkTransactionStates {
BlockDriverState *old_bs;
BlockDriverState *new_bs;
QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
} BlkTransactionStates;
/*
* 'Atomic' group snapshots. The snapshots are taken as a set, and if any fail
* then we do not pivot any of the devices in the group, and abandon the
* snapshots
*/
void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
{
int ret = 0;
BlockdevActionList *dev_entry = dev_list;
BlkTransactionStates *states, *next;
QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states;
QSIMPLEQ_INIT(&snap_bdrv_states);
/* drain all i/o before any snapshots */
bdrv_drain_all();
/* We don't do anything in this loop that commits us to the snapshot */
while (NULL != dev_entry) {
BlockdevAction *dev_info = NULL;
BlockDriver *proto_drv;
BlockDriver *drv;
int flags;
enum NewImageMode mode;
const char *new_image_file;
const char *device;
const char *format = "qcow2";
dev_info = dev_entry->value;
dev_entry = dev_entry->next;
states = g_malloc0(sizeof(BlkTransactionStates));
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
switch (dev_info->kind) {
case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
device = dev_info->blockdev_snapshot_sync->device;
if (!dev_info->blockdev_snapshot_sync->has_mode) {
dev_info->blockdev_snapshot_sync->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
new_image_file = dev_info->blockdev_snapshot_sync->snapshot_file;
if (dev_info->blockdev_snapshot_sync->has_format) {
format = dev_info->blockdev_snapshot_sync->format;
}
mode = dev_info->blockdev_snapshot_sync->mode;
break;
default:
abort();
}
drv = bdrv_find_format(format);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
goto delete_and_fail;
}
states->old_bs = bdrv_find(device);
if (!states->old_bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
goto delete_and_fail;
}
if (!bdrv_is_inserted(states->old_bs)) {
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
goto delete_and_fail;
}
if (bdrv_in_use(states->old_bs)) {
error_set(errp, QERR_DEVICE_IN_USE, device);
goto delete_and_fail;
}
if (!bdrv_is_read_only(states->old_bs)) {
if (bdrv_flush(states->old_bs)) {
error_set(errp, QERR_IO_ERROR);
goto delete_and_fail;
}
}
flags = states->old_bs->open_flags;
proto_drv = bdrv_find_protocol(new_image_file);
if (!proto_drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
goto delete_and_fail;
}
/* create new image w/backing file */
if (mode != NEW_IMAGE_MODE_EXISTING) {
ret = bdrv_img_create(new_image_file, format,
states->old_bs->filename,
states->old_bs->drv->format_name,
NULL, -1, flags);
if (ret) {
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
goto delete_and_fail;
}
}
/* We will manually add the backing_hd field to the bs later */
states->new_bs = bdrv_new("");
ret = bdrv_open(states->new_bs, new_image_file,
flags | BDRV_O_NO_BACKING, drv);
if (ret != 0) {
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
goto delete_and_fail;
}
}
/* Now we are going to do the actual pivot. Everything up to this point
* is reversible, but we are committed at this point */
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
/* This removes our old bs from the bdrv_states, and adds the new bs */
bdrv_append(states->new_bs, states->old_bs);
}
/* success */
goto exit;
delete_and_fail:
/*
* failure, and it is all-or-none; abandon each new bs, and keep using
* the original bs for all images
*/
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
if (states->new_bs) {
bdrv_delete(states->new_bs);
}
}
exit:
QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
g_free(states);
}
return;
}
static void eject_device(BlockDriverState *bs, int force, Error **errp)
{
if (bdrv_in_use(bs)) {
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
return;
}
if (!bdrv_dev_has_removable_media(bs)) {
error_set(errp, QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
return;
}
if (bdrv_dev_is_medium_locked(bs) && !bdrv_dev_is_tray_open(bs)) {
bdrv_dev_eject_request(bs, force);
if (!force) {
error_set(errp, QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
return;
}
}
bdrv_close(bs);
}
void qmp_eject(const char *device, bool has_force, bool force, Error **errp)
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *device = qdict_get_str(qdict, "device");
const char *filename = qdict_get_try_str(qdict, "snapshot-file");
const char *format = qdict_get_try_str(qdict, "format");
BlockDriverState *bs;
BlockDriver *drv, *old_drv, *proto_drv;
int ret = 0;
int flags;
char old_filename[1024];
if (!filename) {
qerror_report(QERR_MISSING_PARAMETER, "snapshot-file");
ret = -1;
goto out;
}
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
qerror_report(QERR_DEVICE_NOT_FOUND, device);
ret = -1;
goto out;
}
eject_device(bs, force, errp);
pstrcpy(old_filename, sizeof(old_filename), bs->filename);
old_drv = bs->drv;
flags = bs->open_flags;
if (!format) {
format = "qcow2";
}
drv = bdrv_find_format(format);
if (!drv) {
qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
ret = -1;
goto out;
}
proto_drv = bdrv_find_protocol(filename);
if (!proto_drv) {
qerror_report(QERR_INVALID_BLOCK_FORMAT, format);
ret = -1;
goto out;
}
ret = bdrv_img_create(filename, format, bs->filename,
bs->drv->format_name, NULL, -1, flags);
if (ret) {
goto out;
}
qemu_aio_flush();
bdrv_flush(bs);
bdrv_close(bs);
ret = bdrv_open(bs, filename, flags, drv);
/*
* If reopening the image file we just created fails, fall back
* and try to re-open the original image. If that fails too, we
* are in serious trouble.
*/
if (ret != 0) {
ret = bdrv_open(bs, old_filename, flags, old_drv);
if (ret != 0) {
qerror_report(QERR_OPEN_FILE_FAILED, old_filename);
} else {
qerror_report(QERR_OPEN_FILE_FAILED, filename);
}
}
out:
if (ret) {
ret = -1;
}
return ret;
}
void qmp_block_passwd(const char *device, const char *password, Error **errp)
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
{
if (!bdrv_dev_has_removable_media(bs)) {
qerror_report(QERR_DEVICE_NOT_REMOVABLE, bdrv_get_device_name(bs));
return -1;
}
if (bdrv_dev_is_medium_locked(bs) && !bdrv_dev_is_tray_open(bs)) {
bdrv_dev_eject_request(bs, force);
if (!force) {
qerror_report(QERR_DEVICE_LOCKED, bdrv_get_device_name(bs));
return -1;
}
}
bdrv_close(bs);
return 0;
}
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
BlockDriverState *bs;
int force = qdict_get_try_bool(qdict, "force", 0);
const char *filename = qdict_get_str(qdict, "device");
bs = bdrv_find(filename);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, filename);
return -1;
}
return eject_device(mon, bs, force);
}
int do_block_set_passwd(Monitor *mon, const QDict *qdict,
QObject **ret_data)
{
BlockDriverState *bs;
int err;
bs = bdrv_find(device);
bs = bdrv_find(qdict_get_str(qdict, "device"));
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
qerror_report(QERR_DEVICE_NOT_FOUND, qdict_get_str(qdict, "device"));
return -1;
}
err = bdrv_set_key(bs, password);
err = bdrv_set_key(bs, qdict_get_str(qdict, "password"));
if (err == -EINVAL) {
error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
return;
qerror_report(QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
return -1;
} else if (err < 0) {
error_set(errp, QERR_INVALID_PASSWORD);
return;
qerror_report(QERR_INVALID_PASSWORD);
return -1;
}
return 0;
}
static void qmp_bdrv_open_encrypted(BlockDriverState *bs, const char *filename,
int bdrv_flags, BlockDriver *drv,
const char *password, Error **errp)
{
if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
error_set(errp, QERR_OPEN_FILE_FAILED, filename);
return;
}
if (bdrv_key_required(bs)) {
if (password) {
if (bdrv_set_key(bs, password) < 0) {
error_set(errp, QERR_INVALID_PASSWORD);
}
} else {
error_set(errp, QERR_DEVICE_ENCRYPTED, bdrv_get_device_name(bs),
bdrv_get_encrypted_filename(bs));
}
} else if (password) {
error_set(errp, QERR_DEVICE_NOT_ENCRYPTED, bdrv_get_device_name(bs));
}
}
void qmp_change_blockdev(const char *device, const char *filename,
bool has_format, const char *format, Error **errp)
int do_change_block(Monitor *mon, const char *device,
const char *filename, const char *fmt)
{
BlockDriverState *bs;
BlockDriver *drv = NULL;
int bdrv_flags;
Error *err = NULL;
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
qerror_report(QERR_DEVICE_NOT_FOUND, device);
return -1;
}
if (format) {
drv = bdrv_find_whitelisted_format(format);
if (fmt) {
drv = bdrv_find_whitelisted_format(fmt);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
qerror_report(QERR_INVALID_BLOCK_FORMAT, fmt);
return -1;
}
}
eject_device(bs, 0, &err);
if (error_is_set(&err)) {
error_propagate(errp, err);
return;
if (eject_device(mon, bs, 0) < 0) {
return -1;
}
bdrv_flags = bdrv_is_read_only(bs) ? 0 : BDRV_O_RDWR;
bdrv_flags |= bdrv_is_snapshot(bs) ? BDRV_O_SNAPSHOT : 0;
qmp_bdrv_open_encrypted(bs, filename, bdrv_flags, drv, NULL, errp);
}
/* throttling disk I/O limits */
void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
int64_t bps_wr, int64_t iops, int64_t iops_rd,
int64_t iops_wr, Error **errp)
{
BlockIOLimit io_limits;
BlockDriverState *bs;
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
io_limits.bps[BLOCK_IO_LIMIT_TOTAL] = bps;
io_limits.bps[BLOCK_IO_LIMIT_READ] = bps_rd;
io_limits.bps[BLOCK_IO_LIMIT_WRITE] = bps_wr;
io_limits.iops[BLOCK_IO_LIMIT_TOTAL]= iops;
io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd;
io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr;
if (!do_check_io_limits(&io_limits)) {
error_set(errp, QERR_INVALID_PARAMETER_COMBINATION);
return;
}
bs->io_limits = io_limits;
bs->slice_time = BLOCK_IO_SLICE_TIME;
if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) {
bdrv_io_limits_enable(bs);
} else if (bs->io_limits_enabled && !bdrv_io_limits_enabled(bs)) {
bdrv_io_limits_disable(bs);
} else {
if (bs->block_timer) {
qemu_mod_timer(bs->block_timer, qemu_get_clock_ns(vm_clock));
}
if (bdrv_open(bs, filename, bdrv_flags, drv) < 0) {
qerror_report(QERR_OPEN_FILE_FAILED, filename);
return -1;
}
return monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
}
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
@@ -1001,7 +731,7 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
}
/* quiesce block driver; prevent further io */
bdrv_drain_all();
qemu_aio_flush();
bdrv_flush(bs);
bdrv_close(bs);
@@ -1019,182 +749,32 @@ int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
return 0;
}
void qmp_block_resize(const char *device, int64_t size, Error **errp)
/*
* XXX: replace the QERR_UNDEFINED_ERROR errors with real values once the
* existing QERR_ macro mess is cleaned up. A good example for better
* error reports can be found in the qemu-img resize code.
*/
int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *device = qdict_get_str(qdict, "device");
int64_t size = qdict_get_int(qdict, "size");
BlockDriverState *bs;
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
qerror_report(QERR_DEVICE_NOT_FOUND, device);
return -1;
}
if (size < 0) {
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
return;
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
}
switch (bdrv_truncate(bs, size)) {
case 0:
break;
case -ENOMEDIUM:
error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
break;
case -ENOTSUP:
error_set(errp, QERR_UNSUPPORTED);
break;
case -EACCES:
error_set(errp, QERR_DEVICE_IS_READ_ONLY, device);
break;
case -EBUSY:
error_set(errp, QERR_DEVICE_IN_USE, device);
break;
default:
error_set(errp, QERR_UNDEFINED_ERROR);
break;
if (bdrv_truncate(bs, size)) {
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
}
}
static QObject *qobject_from_block_job(BlockJob *job)
{
return qobject_from_jsonf("{ 'type': %s,"
"'device': %s,"
"'len': %" PRId64 ","
"'offset': %" PRId64 ","
"'speed': %" PRId64 " }",
job->job_type->job_type,
bdrv_get_device_name(job->bs),
job->len,
job->offset,
job->speed);
}
static void block_stream_cb(void *opaque, int ret)
{
BlockDriverState *bs = opaque;
QObject *obj;
trace_block_stream_cb(bs, bs->job, ret);
assert(bs->job);
obj = qobject_from_block_job(bs->job);
if (ret < 0) {
QDict *dict = qobject_to_qdict(obj);
qdict_put(dict, "error", qstring_from_str(strerror(-ret)));
}
if (block_job_is_cancelled(bs->job)) {
monitor_protocol_event(QEVENT_BLOCK_JOB_CANCELLED, obj);
} else {
monitor_protocol_event(QEVENT_BLOCK_JOB_COMPLETED, obj);
}
qobject_decref(obj);
drive_put_ref_bh_schedule(drive_get_by_blockdev(bs));
}
void qmp_block_stream(const char *device, bool has_base,
const char *base, bool has_speed,
int64_t speed, Error **errp)
{
BlockDriverState *bs;
BlockDriverState *base_bs = NULL;
Error *local_err = NULL;
bs = bdrv_find(device);
if (!bs) {
error_set(errp, QERR_DEVICE_NOT_FOUND, device);
return;
}
if (base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
error_set(errp, QERR_BASE_NOT_FOUND, base);
return;
}
}
stream_start(bs, base_bs, base, has_speed ? speed : 0,
block_stream_cb, bs, &local_err);
if (error_is_set(&local_err)) {
error_propagate(errp, local_err);
return;
}
/* Grab a reference so hotplug does not delete the BlockDriverState from
* underneath us.
*/
drive_get_ref(drive_get_by_blockdev(bs));
trace_qmp_block_stream(bs, bs->job);
}
static BlockJob *find_block_job(const char *device)
{
BlockDriverState *bs;
bs = bdrv_find(device);
if (!bs || !bs->job) {
return NULL;
}
return bs->job;
}
void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
{
BlockJob *job = find_block_job(device);
if (!job) {
error_set(errp, QERR_DEVICE_NOT_ACTIVE, device);
return;
}
block_job_set_speed(job, speed, errp);
}
void qmp_block_job_cancel(const char *device, Error **errp)
{
BlockJob *job = find_block_job(device);
if (!job) {
error_set(errp, QERR_DEVICE_NOT_ACTIVE, device);
return;
}
trace_qmp_block_job_cancel(job);
block_job_cancel(job);
}
static void do_qmp_query_block_jobs_one(void *opaque, BlockDriverState *bs)
{
BlockJobInfoList **prev = opaque;
BlockJob *job = bs->job;
if (job) {
BlockJobInfoList *elem;
BlockJobInfo *info = g_new(BlockJobInfo, 1);
*info = (BlockJobInfo){
.type = g_strdup(job->job_type->job_type),
.device = g_strdup(bdrv_get_device_name(bs)),
.len = job->len,
.offset = job->offset,
.speed = job->speed,
};
elem = g_new0(BlockJobInfoList, 1);
elem->value = info;
(*prev)->next = elem;
*prev = elem;
}
}
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
{
/* Dummy is a fake list element for holding the head pointer */
BlockJobInfoList dummy = {};
BlockJobInfoList *prev = &dummy;
bdrv_iterate(do_qmp_query_block_jobs_one, &prev);
return dummy.next;
return 0;
}

View File

@@ -11,7 +11,6 @@
#define BLOCKDEV_H
#include "block.h"
#include "error.h"
#include "qemu-queue.h"
void blockdev_mark_auto_del(BlockDriverState *bs);
@@ -58,8 +57,13 @@ DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi);
DriveInfo *add_init_drive(const char *opts);
void qmp_change_blockdev(const char *device, const char *filename,
bool has_format, const char *format, Error **errp);
void do_commit(Monitor *mon, const QDict *qdict);
int do_eject(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_block_set_passwd(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_change_block(Monitor *mon, const char *device,
const char *filename, const char *fmt);
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_snapshot_blkdev(Monitor *mon, const QDict *qdict, QObject **ret_data);
int do_block_resize(Monitor *mon, const QDict *qdict, QObject **ret_data);
#endif

View File

@@ -196,7 +196,7 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
/* Something went wrong, return the inode and free the argument pages*/
for (i=0 ; i<MAX_ARG_PAGES ; i++) {
g_free(bprm.page[i]);
free(bprm.page[i]);
}
return(retval);
}

View File

@@ -641,7 +641,8 @@ static abi_ulong copy_elf_strings(int argc,char ** argv, void **page,
offset = p % TARGET_PAGE_SIZE;
pag = (char *)page[p/TARGET_PAGE_SIZE];
if (!pag) {
pag = g_try_malloc0(TARGET_PAGE_SIZE);
pag = (char *)malloc(TARGET_PAGE_SIZE);
memset(pag, 0, TARGET_PAGE_SIZE);
page[p/TARGET_PAGE_SIZE] = pag;
if (!pag)
return 0;
@@ -695,7 +696,7 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm,
info->rss++;
/* FIXME - check return value of memcpy_to_target() for failure */
memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE);
g_free(bprm->page[i]);
free(bprm->page[i]);
}
stack_base += TARGET_PAGE_SIZE;
}
@@ -993,12 +994,12 @@ static abi_ulong load_elf_interp(struct elfhdr * interp_elf_ex,
static int symfind(const void *s0, const void *s1)
{
target_ulong addr = *(target_ulong *)s0;
struct elf_sym *key = (struct elf_sym *)s0;
struct elf_sym *sym = (struct elf_sym *)s1;
int result = 0;
if (addr < sym->st_value) {
if (key->st_value < sym->st_value) {
result = -1;
} else if (addr >= sym->st_value + sym->st_size) {
} else if (key->st_value > sym->st_value + sym->st_size) {
result = 1;
}
return result;
@@ -1013,9 +1014,12 @@ static const char *lookup_symbolxx(struct syminfo *s, target_ulong orig_addr)
#endif
// binary search
struct elf_sym key;
struct elf_sym *sym;
sym = bsearch(&orig_addr, syms, s->disas_num_syms, sizeof(*syms), symfind);
key.st_value = orig_addr;
sym = bsearch(&key, syms, s->disas_num_syms, sizeof(*syms), symfind);
if (sym != NULL) {
return s->disas_strtab + sym->st_name;
}

View File

@@ -41,7 +41,6 @@ int singlestep;
unsigned long mmap_min_addr;
unsigned long guest_base;
int have_guest_base;
unsigned long reserved_va;
#endif
static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
@@ -64,18 +63,18 @@ void gemu_log(const char *fmt, ...)
}
#if defined(TARGET_I386)
int cpu_get_pic_interrupt(CPUX86State *env)
int cpu_get_pic_interrupt(CPUState *env)
{
return -1;
}
#endif
/* These are no-ops because we are not threadsafe. */
static inline void cpu_exec_start(CPUArchState *env)
static inline void cpu_exec_start(CPUState *env)
{
}
static inline void cpu_exec_end(CPUArchState *env)
static inline void cpu_exec_end(CPUState *env)
{
}
@@ -110,7 +109,7 @@ void cpu_list_unlock(void)
/***********************************************************/
/* CPUX86 core interface */
void cpu_smm_update(CPUX86State *env)
void cpu_smm_update(CPUState *env)
{
}
@@ -714,7 +713,7 @@ static void usage(void)
exit(1);
}
THREAD CPUArchState *thread_env;
THREAD CPUState *thread_env;
/* Assumes contents are already zeroed. */
void init_task_state(TaskState *ts)
@@ -738,7 +737,7 @@ int main(int argc, char **argv)
struct target_pt_regs regs1, *regs = &regs1;
struct image_info info1, *info = &info1;
TaskState ts1, *ts = &ts1;
CPUArchState *env;
CPUState *env;
int optind;
const char *r;
int gdbstub_port = 0;
@@ -749,8 +748,6 @@ int main(int argc, char **argv)
if (argc <= 1)
usage();
module_call_init(MODULE_INIT_QOM);
if ((envlist = envlist_create()) == NULL) {
(void) fprintf(stderr, "Unable to allocate envlist\n");
exit(1);
@@ -918,7 +915,7 @@ int main(int argc, char **argv)
exit(1);
}
#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
cpu_state_reset(env);
cpu_reset(env);
#endif
thread_env = env;

View File

@@ -139,8 +139,8 @@ abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
abi_long arg2, abi_long arg3, abi_long arg4,
abi_long arg5, abi_long arg6);
void gemu_log(const char *fmt, ...) GCC_FMT_ATTR(1, 2);
extern THREAD CPUArchState *thread_env;
void cpu_loop(CPUArchState *env);
extern THREAD CPUState *thread_env;
void cpu_loop(CPUState *env);
char *target_strerror(int err);
int get_osversion(void);
void fork_start(void);
@@ -167,13 +167,13 @@ void print_openbsd_syscall_ret(int num, abi_long ret);
extern int do_strace;
/* signal.c */
void process_pending_signals(CPUArchState *cpu_env);
void process_pending_signals(CPUState *cpu_env);
void signal_init(void);
//int queue_signal(CPUArchState *env, int sig, target_siginfo_t *info);
//int queue_signal(CPUState *env, int sig, target_siginfo_t *info);
//void host_to_target_siginfo(target_siginfo_t *tinfo, const siginfo_t *info);
//void target_to_host_siginfo(siginfo_t *info, const target_siginfo_t *tinfo);
long do_sigreturn(CPUArchState *env);
long do_rt_sigreturn(CPUArchState *env);
long do_sigreturn(CPUState *env);
long do_rt_sigreturn(CPUState *env);
abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp);
/* mmap.c */

View File

@@ -33,6 +33,6 @@ void signal_init(void)
{
}
void process_pending_signals(CPUArchState *cpu_env)
void process_pending_signals(CPUState *cpu_env)
{
}

View File

@@ -8,7 +8,7 @@ struct target_pt_regs {
abi_ulong r12;
abi_ulong rbp;
abi_ulong rbx;
/* arguments: non interrupts/non tracing syscalls only save up to here */
/* arguments: non interrupts/non tracing syscalls only save upto here*/
abi_ulong r11;
abi_ulong r10;
abi_ulong r9;

View File

@@ -130,7 +130,6 @@ static void bt_host_read(void *opaque)
pktlen = MIN(pkt[2] + 3, s->len);
s->len -= pktlen;
pkt += pktlen;
break;
default:
bad_pkt:

View File

@@ -9,8 +9,6 @@
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu-common.h"

View File

@@ -2,9 +2,6 @@
#define QEMU_CACHE_UTILS_H
#if defined(_ARCH_PPC)
#include <stdint.h> /* uintptr_t */
struct qemu_cache_conf {
unsigned long dcache_bsize;
unsigned long icache_bsize;
@@ -15,7 +12,7 @@ extern struct qemu_cache_conf qemu_cache_conf;
void qemu_cache_utils_init(char **envp);
/* mildly adjusted code from tcg-dyngen.c */
static inline void flush_icache_range(uintptr_t start, uintptr_t stop)
static inline void flush_icache_range(unsigned long start, unsigned long stop)
{
unsigned long p, start1, stop1;
unsigned long dsize = qemu_cache_conf.dcache_bsize;

View File

@@ -9,7 +9,7 @@
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include <glib.h>
#include <check.h>
#include "qint.h"
#include "qdict.h"
@@ -22,21 +22,22 @@
* (with some violations to access 'private' data)
*/
static void qdict_new_test(void)
START_TEST(qdict_new_test)
{
QDict *qdict;
qdict = qdict_new();
g_assert(qdict != NULL);
g_assert(qdict_size(qdict) == 0);
g_assert(qdict->base.refcnt == 1);
g_assert(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT);
fail_unless(qdict != NULL);
fail_unless(qdict_size(qdict) == 0);
fail_unless(qdict->base.refcnt == 1);
fail_unless(qobject_type(QOBJECT(qdict)) == QTYPE_QDICT);
// destroy doesn't exit yet
g_free(qdict);
free(qdict);
}
END_TEST
static void qdict_put_obj_test(void)
START_TEST(qdict_put_obj_test)
{
QInt *qi;
QDict *qdict;
@@ -48,10 +49,10 @@ static void qdict_put_obj_test(void)
// key "" will have tdb hash 12345
qdict_put_obj(qdict, "", QOBJECT(qint_from_int(num)));
g_assert(qdict_size(qdict) == 1);
fail_unless(qdict_size(qdict) == 1);
ent = QLIST_FIRST(&qdict->table[12345 % QDICT_BUCKET_MAX]);
qi = qobject_to_qint(ent->value);
g_assert(qint_get_int(qi) == num);
fail_unless(qint_get_int(qi) == num);
// destroy doesn't exit yet
QDECREF(qi);
@@ -59,8 +60,9 @@ static void qdict_put_obj_test(void)
g_free(ent);
g_free(qdict);
}
END_TEST
static void qdict_destroy_simple_test(void)
START_TEST(qdict_destroy_simple_test)
{
QDict *qdict;
@@ -70,138 +72,134 @@ static void qdict_destroy_simple_test(void)
QDECREF(qdict);
}
END_TEST
static void qdict_get_test(void)
static QDict *tests_dict = NULL;
static void qdict_setup(void)
{
tests_dict = qdict_new();
fail_unless(tests_dict != NULL);
}
static void qdict_teardown(void)
{
QDECREF(tests_dict);
tests_dict = NULL;
}
START_TEST(qdict_get_test)
{
QInt *qi;
QObject *obj;
const int value = -42;
const char *key = "test";
QDict *tests_dict = qdict_new();
qdict_put(tests_dict, key, qint_from_int(value));
obj = qdict_get(tests_dict, key);
g_assert(obj != NULL);
fail_unless(obj != NULL);
qi = qobject_to_qint(obj);
g_assert(qint_get_int(qi) == value);
QDECREF(tests_dict);
fail_unless(qint_get_int(qi) == value);
}
END_TEST
static void qdict_get_int_test(void)
START_TEST(qdict_get_int_test)
{
int ret;
const int value = 100;
const char *key = "int";
QDict *tests_dict = qdict_new();
qdict_put(tests_dict, key, qint_from_int(value));
ret = qdict_get_int(tests_dict, key);
g_assert(ret == value);
QDECREF(tests_dict);
fail_unless(ret == value);
}
END_TEST
static void qdict_get_try_int_test(void)
START_TEST(qdict_get_try_int_test)
{
int ret;
const int value = 100;
const char *key = "int";
QDict *tests_dict = qdict_new();
qdict_put(tests_dict, key, qint_from_int(value));
ret = qdict_get_try_int(tests_dict, key, 0);
g_assert(ret == value);
QDECREF(tests_dict);
fail_unless(ret == value);
}
END_TEST
static void qdict_get_str_test(void)
START_TEST(qdict_get_str_test)
{
const char *p;
const char *key = "key";
const char *str = "string";
QDict *tests_dict = qdict_new();
qdict_put(tests_dict, key, qstring_from_str(str));
p = qdict_get_str(tests_dict, key);
g_assert(p != NULL);
g_assert(strcmp(p, str) == 0);
QDECREF(tests_dict);
fail_unless(p != NULL);
fail_unless(strcmp(p, str) == 0);
}
END_TEST
static void qdict_get_try_str_test(void)
START_TEST(qdict_get_try_str_test)
{
const char *p;
const char *key = "key";
const char *str = "string";
QDict *tests_dict = qdict_new();
qdict_put(tests_dict, key, qstring_from_str(str));
p = qdict_get_try_str(tests_dict, key);
g_assert(p != NULL);
g_assert(strcmp(p, str) == 0);
QDECREF(tests_dict);
fail_unless(p != NULL);
fail_unless(strcmp(p, str) == 0);
}
END_TEST
static void qdict_haskey_not_test(void)
START_TEST(qdict_haskey_not_test)
{
QDict *tests_dict = qdict_new();
g_assert(qdict_haskey(tests_dict, "test") == 0);
QDECREF(tests_dict);
fail_unless(qdict_haskey(tests_dict, "test") == 0);
}
END_TEST
static void qdict_haskey_test(void)
START_TEST(qdict_haskey_test)
{
const char *key = "test";
QDict *tests_dict = qdict_new();
qdict_put(tests_dict, key, qint_from_int(0));
g_assert(qdict_haskey(tests_dict, key) == 1);
QDECREF(tests_dict);
fail_unless(qdict_haskey(tests_dict, key) == 1);
}
END_TEST
static void qdict_del_test(void)
START_TEST(qdict_del_test)
{
const char *key = "key test";
QDict *tests_dict = qdict_new();
qdict_put(tests_dict, key, qstring_from_str("foo"));
g_assert(qdict_size(tests_dict) == 1);
fail_unless(qdict_size(tests_dict) == 1);
qdict_del(tests_dict, key);
g_assert(qdict_size(tests_dict) == 0);
g_assert(qdict_haskey(tests_dict, key) == 0);
QDECREF(tests_dict);
fail_unless(qdict_size(tests_dict) == 0);
fail_unless(qdict_haskey(tests_dict, key) == 0);
}
END_TEST
static void qobject_to_qdict_test(void)
START_TEST(qobject_to_qdict_test)
{
QDict *tests_dict = qdict_new();
g_assert(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
QDECREF(tests_dict);
fail_unless(qobject_to_qdict(QOBJECT(tests_dict)) == tests_dict);
}
END_TEST
static void qdict_iterapi_test(void)
START_TEST(qdict_iterapi_test)
{
int count;
const QDictEntry *ent;
QDict *tests_dict = qdict_new();
g_assert(qdict_first(tests_dict) == NULL);
fail_unless(qdict_first(tests_dict) == NULL);
qdict_put(tests_dict, "key1", qint_from_int(1));
qdict_put(tests_dict, "key2", qint_from_int(2));
@@ -209,52 +207,47 @@ static void qdict_iterapi_test(void)
count = 0;
for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
count++;
}
g_assert(count == qdict_size(tests_dict));
fail_unless(count == qdict_size(tests_dict));
/* Do it again to test restarting */
count = 0;
for (ent = qdict_first(tests_dict); ent; ent = qdict_next(tests_dict, ent)){
g_assert(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
fail_unless(qdict_haskey(tests_dict, qdict_entry_key(ent)) == 1);
count++;
}
g_assert(count == qdict_size(tests_dict));
QDECREF(tests_dict);
fail_unless(count == qdict_size(tests_dict));
}
END_TEST
/*
* Errors test-cases
*/
static void qdict_put_exists_test(void)
START_TEST(qdict_put_exists_test)
{
int value;
const char *key = "exists";
QDict *tests_dict = qdict_new();
qdict_put(tests_dict, key, qint_from_int(1));
qdict_put(tests_dict, key, qint_from_int(2));
value = qdict_get_int(tests_dict, key);
g_assert(value == 2);
fail_unless(value == 2);
g_assert(qdict_size(tests_dict) == 1);
QDECREF(tests_dict);
fail_unless(qdict_size(tests_dict) == 1);
}
END_TEST
static void qdict_get_not_exists_test(void)
START_TEST(qdict_get_not_exists_test)
{
QDict *tests_dict = qdict_new();
g_assert(qdict_get(tests_dict, "foo") == NULL);
QDECREF(tests_dict);
fail_unless(qdict_get(tests_dict, "foo") == NULL);
}
END_TEST
/*
* Stress test-case
@@ -283,7 +276,7 @@ static QString *read_line(FILE *file, char *key)
#define reset_file(file) fseek(file, 0L, SEEK_SET)
static void qdict_stress_test(void)
START_TEST(qdict_stress_test)
{
size_t lines;
char key[128];
@@ -293,11 +286,11 @@ static void qdict_stress_test(void)
const char *test_file_path = "qdict-test-data.txt";
test_file = fopen(test_file_path, "r");
g_assert(test_file != NULL);
fail_unless(test_file != NULL);
// Create the dict
qdict = qdict_new();
g_assert(qdict != NULL);
fail_unless(qdict != NULL);
// Add everything from the test file
for (lines = 0;; lines++) {
@@ -307,7 +300,7 @@ static void qdict_stress_test(void)
qdict_put(qdict, key, value);
}
g_assert(qdict_size(qdict) == lines);
fail_unless(qdict_size(qdict) == lines);
// Check if everything is really in there
reset_file(test_file);
@@ -321,9 +314,9 @@ static void qdict_stress_test(void)
str1 = qstring_get_str(value);
str2 = qdict_get_str(qdict, key);
g_assert(str2 != NULL);
fail_unless(str2 != NULL);
g_assert(strcmp(str1, str2) == 0);
fail_unless(strcmp(str1, str2) == 0);
QDECREF(value);
}
@@ -338,41 +331,72 @@ static void qdict_stress_test(void)
qdict_del(qdict, key);
QDECREF(value);
g_assert(qdict_haskey(qdict, key) == 0);
fail_unless(qdict_haskey(qdict, key) == 0);
}
fclose(test_file);
g_assert(qdict_size(qdict) == 0);
fail_unless(qdict_size(qdict) == 0);
QDECREF(qdict);
}
END_TEST
int main(int argc, char **argv)
static Suite *qdict_suite(void)
{
g_test_init(&argc, &argv, NULL);
Suite *s;
TCase *qdict_public_tcase;
TCase *qdict_public2_tcase;
TCase *qdict_stress_tcase;
TCase *qdict_errors_tcase;
g_test_add_func("/public/new", qdict_new_test);
g_test_add_func("/public/put_obj", qdict_put_obj_test);
g_test_add_func("/public/destroy_simple", qdict_destroy_simple_test);
s = suite_create("QDict test-suite");
qdict_public_tcase = tcase_create("Public Interface");
suite_add_tcase(s, qdict_public_tcase);
tcase_add_test(qdict_public_tcase, qdict_new_test);
tcase_add_test(qdict_public_tcase, qdict_put_obj_test);
tcase_add_test(qdict_public_tcase, qdict_destroy_simple_test);
/* Continue, but now with fixtures */
g_test_add_func("/public/get", qdict_get_test);
g_test_add_func("/public/get_int", qdict_get_int_test);
g_test_add_func("/public/get_try_int", qdict_get_try_int_test);
g_test_add_func("/public/get_str", qdict_get_str_test);
g_test_add_func("/public/get_try_str", qdict_get_try_str_test);
g_test_add_func("/public/haskey_not", qdict_haskey_not_test);
g_test_add_func("/public/haskey", qdict_haskey_test);
g_test_add_func("/public/del", qdict_del_test);
g_test_add_func("/public/to_qdict", qobject_to_qdict_test);
g_test_add_func("/public/iterapi", qdict_iterapi_test);
qdict_public2_tcase = tcase_create("Public Interface (2)");
suite_add_tcase(s, qdict_public2_tcase);
tcase_add_checked_fixture(qdict_public2_tcase, qdict_setup, qdict_teardown);
tcase_add_test(qdict_public2_tcase, qdict_get_test);
tcase_add_test(qdict_public2_tcase, qdict_get_int_test);
tcase_add_test(qdict_public2_tcase, qdict_get_try_int_test);
tcase_add_test(qdict_public2_tcase, qdict_get_str_test);
tcase_add_test(qdict_public2_tcase, qdict_get_try_str_test);
tcase_add_test(qdict_public2_tcase, qdict_haskey_not_test);
tcase_add_test(qdict_public2_tcase, qdict_haskey_test);
tcase_add_test(qdict_public2_tcase, qdict_del_test);
tcase_add_test(qdict_public2_tcase, qobject_to_qdict_test);
tcase_add_test(qdict_public2_tcase, qdict_iterapi_test);
g_test_add_func("/errors/put_exists", qdict_put_exists_test);
g_test_add_func("/errors/get_not_exists", qdict_get_not_exists_test);
qdict_errors_tcase = tcase_create("Errors");
suite_add_tcase(s, qdict_errors_tcase);
tcase_add_checked_fixture(qdict_errors_tcase, qdict_setup, qdict_teardown);
tcase_add_test(qdict_errors_tcase, qdict_put_exists_test);
tcase_add_test(qdict_errors_tcase, qdict_get_not_exists_test);
/* The Big one */
if (g_test_slow()) {
g_test_add_func("/stress/test", qdict_stress_test);
}
qdict_stress_tcase = tcase_create("Stress Test");
suite_add_tcase(s, qdict_stress_tcase);
tcase_add_test(qdict_stress_tcase, qdict_stress_test);
return g_test_run();
return s;
}
int main(void)
{
int nf;
Suite *s;
SRunner *sr;
s = qdict_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
nf = srunner_ntests_failed(sr);
srunner_free(sr);
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

76
check-qfloat.c Normal file
View File

@@ -0,0 +1,76 @@
/*
* QFloat unit-tests.
*
* Copyright IBM, Corp. 2009
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include <check.h>
#include "qfloat.h"
#include "qemu-common.h"
/*
* Public Interface test-cases
*
* (with some violations to access 'private' data)
*/
START_TEST(qfloat_from_double_test)
{
QFloat *qf;
const double value = -42.23423;
qf = qfloat_from_double(value);
fail_unless(qf != NULL);
fail_unless(qf->value == value);
fail_unless(qf->base.refcnt == 1);
fail_unless(qobject_type(QOBJECT(qf)) == QTYPE_QFLOAT);
// destroy doesn't exit yet
g_free(qf);
}
END_TEST
START_TEST(qfloat_destroy_test)
{
QFloat *qf = qfloat_from_double(0.0);
QDECREF(qf);
}
END_TEST
static Suite *qfloat_suite(void)
{
Suite *s;
TCase *qfloat_public_tcase;
s = suite_create("QFloat test-suite");
qfloat_public_tcase = tcase_create("Public Interface");
suite_add_tcase(s, qfloat_public_tcase);
tcase_add_test(qfloat_public_tcase, qfloat_from_double_test);
tcase_add_test(qfloat_public_tcase, qfloat_destroy_test);
return s;
}
int main(void)
{
int nf;
Suite *s;
SRunner *sr;
s = qfloat_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
nf = srunner_ntests_failed(sr);
srunner_free(sr);
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

113
check-qint.c Normal file
View File

@@ -0,0 +1,113 @@
/*
* QInt unit-tests.
*
* Copyright (C) 2009 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include <check.h>
#include "qint.h"
#include "qemu-common.h"
/*
* Public Interface test-cases
*
* (with some violations to access 'private' data)
*/
START_TEST(qint_from_int_test)
{
QInt *qi;
const int value = -42;
qi = qint_from_int(value);
fail_unless(qi != NULL);
fail_unless(qi->value == value);
fail_unless(qi->base.refcnt == 1);
fail_unless(qobject_type(QOBJECT(qi)) == QTYPE_QINT);
// destroy doesn't exit yet
g_free(qi);
}
END_TEST
START_TEST(qint_destroy_test)
{
QInt *qi = qint_from_int(0);
QDECREF(qi);
}
END_TEST
START_TEST(qint_from_int64_test)
{
QInt *qi;
const int64_t value = 0x1234567890abcdefLL;
qi = qint_from_int(value);
fail_unless((int64_t) qi->value == value);
QDECREF(qi);
}
END_TEST
START_TEST(qint_get_int_test)
{
QInt *qi;
const int value = 123456;
qi = qint_from_int(value);
fail_unless(qint_get_int(qi) == value);
QDECREF(qi);
}
END_TEST
START_TEST(qobject_to_qint_test)
{
QInt *qi;
qi = qint_from_int(0);
fail_unless(qobject_to_qint(QOBJECT(qi)) == qi);
QDECREF(qi);
}
END_TEST
static Suite *qint_suite(void)
{
Suite *s;
TCase *qint_public_tcase;
s = suite_create("QInt test-suite");
qint_public_tcase = tcase_create("Public Interface");
suite_add_tcase(s, qint_public_tcase);
tcase_add_test(qint_public_tcase, qint_from_int_test);
tcase_add_test(qint_public_tcase, qint_destroy_test);
tcase_add_test(qint_public_tcase, qint_from_int64_test);
tcase_add_test(qint_public_tcase, qint_get_int_test);
tcase_add_test(qint_public_tcase, qobject_to_qint_test);
return s;
}
int main(void)
{
int nf;
Suite *s;
SRunner *sr;
s = qint_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
nf = srunner_ntests_failed(sr);
srunner_free(sr);
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@@ -8,7 +8,7 @@
* See the COPYING.LIB file in the top-level directory.
*
*/
#include <glib.h>
#include <check.h>
#include "qstring.h"
#include "qint.h"
@@ -20,7 +20,7 @@
#include "qemu-common.h"
static void escaped_string(void)
START_TEST(escaped_string)
{
int i;
struct {
@@ -52,23 +52,28 @@ static void escaped_string(void)
obj = qobject_from_json(test_cases[i].encoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QSTRING);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
str = qobject_to_qstring(obj);
g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0,
"%s != %s\n", qstring_get_str(str), test_cases[i].decoded);
if (test_cases[i].skip == 0) {
str = qobject_to_json(obj);
g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded);
fail_unless(strcmp(qstring_get_str(str),test_cases[i].encoded) == 0,
"%s != %s\n", qstring_get_str(str),
test_cases[i].encoded);
qobject_decref(obj);
}
QDECREF(str);
}
}
END_TEST
static void simple_string(void)
START_TEST(simple_string)
{
int i;
struct {
@@ -87,22 +92,23 @@ static void simple_string(void)
obj = qobject_from_json(test_cases[i].encoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QSTRING);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
str = qobject_to_qstring(obj);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
str = qobject_to_json(obj);
g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
qobject_decref(obj);
QDECREF(str);
}
}
END_TEST
static void single_quote_string(void)
START_TEST(single_quote_string)
{
int i;
struct {
@@ -121,17 +127,18 @@ static void single_quote_string(void)
obj = qobject_from_json(test_cases[i].encoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QSTRING);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
str = qobject_to_qstring(obj);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
QDECREF(str);
}
}
END_TEST
static void vararg_string(void)
START_TEST(vararg_string)
{
int i;
struct {
@@ -148,17 +155,18 @@ static void vararg_string(void)
obj = qobject_from_jsonf("%s", test_cases[i].decoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QSTRING);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QSTRING);
str = qobject_to_qstring(obj);
g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0);
QDECREF(str);
}
}
END_TEST
static void simple_number(void)
START_TEST(simple_number)
{
int i;
struct {
@@ -179,24 +187,25 @@ static void simple_number(void)
QInt *qint;
obj = qobject_from_json(test_cases[i].encoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QINT);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QINT);
qint = qobject_to_qint(obj);
g_assert(qint_get_int(qint) == test_cases[i].decoded);
fail_unless(qint_get_int(qint) == test_cases[i].decoded);
if (test_cases[i].skip == 0) {
QString *str;
str = qobject_to_json(obj);
g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
QDECREF(str);
}
QDECREF(qint);
}
}
END_TEST
static void float_number(void)
START_TEST(float_number)
{
int i;
struct {
@@ -216,25 +225,26 @@ static void float_number(void)
QFloat *qfloat;
obj = qobject_from_json(test_cases[i].encoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QFLOAT);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QFLOAT);
qfloat = qobject_to_qfloat(obj);
g_assert(qfloat_get_double(qfloat) == test_cases[i].decoded);
fail_unless(qfloat_get_double(qfloat) == test_cases[i].decoded);
if (test_cases[i].skip == 0) {
QString *str;
str = qobject_to_json(obj);
g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
fail_unless(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0);
QDECREF(str);
}
QDECREF(qfloat);
}
}
END_TEST
static void vararg_number(void)
START_TEST(vararg_number)
{
QObject *obj;
QInt *qint;
@@ -244,83 +254,85 @@ static void vararg_number(void)
double valuef = 2.323423423;
obj = qobject_from_jsonf("%d", value);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QINT);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QINT);
qint = qobject_to_qint(obj);
g_assert(qint_get_int(qint) == value);
fail_unless(qint_get_int(qint) == value);
QDECREF(qint);
obj = qobject_from_jsonf("%" PRId64, value64);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QINT);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QINT);
qint = qobject_to_qint(obj);
g_assert(qint_get_int(qint) == value64);
fail_unless(qint_get_int(qint) == value64);
QDECREF(qint);
obj = qobject_from_jsonf("%f", valuef);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QFLOAT);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QFLOAT);
qfloat = qobject_to_qfloat(obj);
g_assert(qfloat_get_double(qfloat) == valuef);
fail_unless(qfloat_get_double(qfloat) == valuef);
QDECREF(qfloat);
}
END_TEST
static void keyword_literal(void)
START_TEST(keyword_literal)
{
QObject *obj;
QBool *qbool;
QString *str;
obj = qobject_from_json("true");
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
qbool = qobject_to_qbool(obj);
g_assert(qbool_get_int(qbool) != 0);
fail_unless(qbool_get_int(qbool) != 0);
str = qobject_to_json(obj);
g_assert(strcmp(qstring_get_str(str), "true") == 0);
fail_unless(strcmp(qstring_get_str(str), "true") == 0);
QDECREF(str);
QDECREF(qbool);
obj = qobject_from_json("false");
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
qbool = qobject_to_qbool(obj);
g_assert(qbool_get_int(qbool) == 0);
fail_unless(qbool_get_int(qbool) == 0);
str = qobject_to_json(obj);
g_assert(strcmp(qstring_get_str(str), "false") == 0);
fail_unless(strcmp(qstring_get_str(str), "false") == 0);
QDECREF(str);
QDECREF(qbool);
obj = qobject_from_jsonf("%i", false);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
qbool = qobject_to_qbool(obj);
g_assert(qbool_get_int(qbool) == 0);
fail_unless(qbool_get_int(qbool) == 0);
QDECREF(qbool);
obj = qobject_from_jsonf("%i", true);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QBOOL);
qbool = qobject_to_qbool(obj);
g_assert(qbool_get_int(qbool) != 0);
fail_unless(qbool_get_int(qbool) != 0);
QDECREF(qbool);
}
END_TEST
typedef struct LiteralQDictEntry LiteralQDictEntry;
typedef struct LiteralQObject LiteralQObject;
@@ -414,7 +426,7 @@ static int compare_litqobj_to_qobj(LiteralQObject *lhs, QObject *rhs)
return 0;
}
static void simple_dict(void)
START_TEST(simple_dict)
{
int i;
struct {
@@ -448,25 +460,26 @@ static void simple_dict(void)
QString *str;
obj = qobject_from_json(test_cases[i].encoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QDICT);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QDICT);
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
str = qobject_to_json(obj);
qobject_decref(obj);
obj = qobject_from_json(qstring_get_str(str));
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QDICT);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QDICT);
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
qobject_decref(obj);
QDECREF(str);
}
}
END_TEST
static void simple_list(void)
START_TEST(simple_list)
{
int i;
struct {
@@ -511,25 +524,26 @@ static void simple_list(void)
QString *str;
obj = qobject_from_json(test_cases[i].encoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QLIST);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QLIST);
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
str = qobject_to_json(obj);
qobject_decref(obj);
obj = qobject_from_json(qstring_get_str(str));
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QLIST);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QLIST);
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
qobject_decref(obj);
QDECREF(str);
}
}
END_TEST
static void simple_whitespace(void)
START_TEST(simple_whitespace)
{
int i;
struct {
@@ -579,26 +593,27 @@ static void simple_whitespace(void)
QString *str;
obj = qobject_from_json(test_cases[i].encoded);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QLIST);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QLIST);
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
str = qobject_to_json(obj);
qobject_decref(obj);
obj = qobject_from_json(qstring_get_str(str));
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QLIST);
fail_unless(obj != NULL);
fail_unless(qobject_type(obj) == QTYPE_QLIST);
g_assert(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
fail_unless(compare_litqobj_to_qobj(&test_cases[i].decoded, obj) == 1);
qobject_decref(obj);
QDECREF(str);
}
}
END_TEST
static void simple_varargs(void)
START_TEST(simple_varargs)
{
QObject *embedded_obj;
QObject *obj;
@@ -612,117 +627,169 @@ static void simple_varargs(void)
{}}));
embedded_obj = qobject_from_json("[32, 42]");
g_assert(embedded_obj != NULL);
fail_unless(embedded_obj != NULL);
obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj);
g_assert(obj != NULL);
fail_unless(obj != NULL);
g_assert(compare_litqobj_to_qobj(&decoded, obj) == 1);
fail_unless(compare_litqobj_to_qobj(&decoded, obj) == 1);
qobject_decref(obj);
}
END_TEST
static void empty_input(void)
START_TEST(empty_input)
{
const char *empty = "";
QObject *obj = qobject_from_json(empty);
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void unterminated_string(void)
START_TEST(unterminated_string)
{
QObject *obj = qobject_from_json("\"abc");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void unterminated_sq_string(void)
START_TEST(unterminated_sq_string)
{
QObject *obj = qobject_from_json("'abc");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void unterminated_escape(void)
START_TEST(unterminated_escape)
{
QObject *obj = qobject_from_json("\"abc\\\"");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void unterminated_array(void)
START_TEST(unterminated_array)
{
QObject *obj = qobject_from_json("[32");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void unterminated_array_comma(void)
START_TEST(unterminated_array_comma)
{
QObject *obj = qobject_from_json("[32,");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void invalid_array_comma(void)
START_TEST(invalid_array_comma)
{
QObject *obj = qobject_from_json("[32,}");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void unterminated_dict(void)
START_TEST(unterminated_dict)
{
QObject *obj = qobject_from_json("{'abc':32");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void unterminated_dict_comma(void)
START_TEST(unterminated_dict_comma)
{
QObject *obj = qobject_from_json("{'abc':32,");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void invalid_dict_comma(void)
#if 0
START_TEST(invalid_dict_comma)
{
QObject *obj = qobject_from_json("{'abc':32,}");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
static void unterminated_literal(void)
START_TEST(unterminated_literal)
{
QObject *obj = qobject_from_json("nul");
g_assert(obj == NULL);
fail_unless(obj == NULL);
}
END_TEST
#endif
int main(int argc, char **argv)
static Suite *qjson_suite(void)
{
g_test_init(&argc, &argv, NULL);
Suite *suite;
TCase *string_literals, *number_literals, *keyword_literals;
TCase *dicts, *lists, *whitespace, *varargs, *errors;
g_test_add_func("/literals/string/simple", simple_string);
g_test_add_func("/literals/string/escaped", escaped_string);
g_test_add_func("/literals/string/single_quote", single_quote_string);
g_test_add_func("/literals/string/vararg", vararg_string);
string_literals = tcase_create("String Literals");
tcase_add_test(string_literals, simple_string);
tcase_add_test(string_literals, escaped_string);
tcase_add_test(string_literals, single_quote_string);
tcase_add_test(string_literals, vararg_string);
g_test_add_func("/literals/number/simple", simple_number);
g_test_add_func("/literals/number/float", float_number);
g_test_add_func("/literals/number/vararg", vararg_number);
number_literals = tcase_create("Number Literals");
tcase_add_test(number_literals, simple_number);
tcase_add_test(number_literals, float_number);
tcase_add_test(number_literals, vararg_number);
g_test_add_func("/literals/keyword", keyword_literal);
keyword_literals = tcase_create("Keywords");
tcase_add_test(keyword_literals, keyword_literal);
dicts = tcase_create("Objects");
tcase_add_test(dicts, simple_dict);
lists = tcase_create("Lists");
tcase_add_test(lists, simple_list);
g_test_add_func("/dicts/simple_dict", simple_dict);
g_test_add_func("/lists/simple_list", simple_list);
whitespace = tcase_create("Whitespace");
tcase_add_test(whitespace, simple_whitespace);
g_test_add_func("/whitespace/simple_whitespace", simple_whitespace);
varargs = tcase_create("Varargs");
tcase_add_test(varargs, simple_varargs);
g_test_add_func("/varargs/simple_varargs", simple_varargs);
errors = tcase_create("Invalid JSON");
tcase_add_test(errors, empty_input);
tcase_add_test(errors, unterminated_string);
tcase_add_test(errors, unterminated_escape);
tcase_add_test(errors, unterminated_sq_string);
tcase_add_test(errors, unterminated_array);
tcase_add_test(errors, unterminated_array_comma);
tcase_add_test(errors, invalid_array_comma);
tcase_add_test(errors, unterminated_dict);
tcase_add_test(errors, unterminated_dict_comma);
#if 0
/* FIXME: this print parse error messages on stderr. */
tcase_add_test(errors, invalid_dict_comma);
tcase_add_test(errors, unterminated_literal);
#endif
g_test_add_func("/errors/empty_input", empty_input);
g_test_add_func("/errors/unterminated/string", unterminated_string);
g_test_add_func("/errors/unterminated/escape", unterminated_escape);
g_test_add_func("/errors/unterminated/sq_string", unterminated_sq_string);
g_test_add_func("/errors/unterminated/array", unterminated_array);
g_test_add_func("/errors/unterminated/array_comma", unterminated_array_comma);
g_test_add_func("/errors/unterminated/dict", unterminated_dict);
g_test_add_func("/errors/unterminated/dict_comma", unterminated_dict_comma);
g_test_add_func("/errors/invalid_array_comma", invalid_array_comma);
g_test_add_func("/errors/invalid_dict_comma", invalid_dict_comma);
g_test_add_func("/errors/unterminated/literal", unterminated_literal);
suite = suite_create("QJSON test-suite");
suite_add_tcase(suite, string_literals);
suite_add_tcase(suite, number_literals);
suite_add_tcase(suite, keyword_literals);
suite_add_tcase(suite, dicts);
suite_add_tcase(suite, lists);
suite_add_tcase(suite, whitespace);
suite_add_tcase(suite, varargs);
suite_add_tcase(suite, errors);
return g_test_run();
return suite;
}
int main(void)
{
int nf;
Suite *s;
SRunner *sr;
s = qjson_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
nf = srunner_ntests_failed(sr);
srunner_free(sr);
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@@ -9,7 +9,7 @@
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include <glib.h>
#include <check.h>
#include "qint.h"
#include "qlist.h"
@@ -20,20 +20,21 @@
* (with some violations to access 'private' data)
*/
static void qlist_new_test(void)
START_TEST(qlist_new_test)
{
QList *qlist;
qlist = qlist_new();
g_assert(qlist != NULL);
g_assert(qlist->base.refcnt == 1);
g_assert(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST);
fail_unless(qlist != NULL);
fail_unless(qlist->base.refcnt == 1);
fail_unless(qobject_type(QOBJECT(qlist)) == QTYPE_QLIST);
// destroy doesn't exist yet
g_free(qlist);
}
END_TEST
static void qlist_append_test(void)
START_TEST(qlist_append_test)
{
QInt *qi;
QList *qlist;
@@ -45,28 +46,30 @@ static void qlist_append_test(void)
qlist_append(qlist, qi);
entry = QTAILQ_FIRST(&qlist->head);
g_assert(entry != NULL);
g_assert(entry->value == QOBJECT(qi));
fail_unless(entry != NULL);
fail_unless(entry->value == QOBJECT(qi));
// destroy doesn't exist yet
QDECREF(qi);
g_free(entry);
g_free(qlist);
}
END_TEST
static void qobject_to_qlist_test(void)
START_TEST(qobject_to_qlist_test)
{
QList *qlist;
qlist = qlist_new();
g_assert(qobject_to_qlist(QOBJECT(qlist)) == qlist);
fail_unless(qobject_to_qlist(QOBJECT(qlist)) == qlist);
// destroy doesn't exist yet
g_free(qlist);
}
END_TEST
static void qlist_destroy_test(void)
START_TEST(qlist_destroy_test)
{
int i;
QList *qlist;
@@ -78,6 +81,7 @@ static void qlist_destroy_test(void)
QDECREF(qlist);
}
END_TEST
static int iter_called;
static const int iter_max = 42;
@@ -86,16 +90,16 @@ static void iter_func(QObject *obj, void *opaque)
{
QInt *qi;
g_assert(opaque == NULL);
fail_unless(opaque == NULL);
qi = qobject_to_qint(obj);
g_assert(qi != NULL);
g_assert((qint_get_int(qi) >= 0) && (qint_get_int(qi) <= iter_max));
fail_unless(qi != NULL);
fail_unless((qint_get_int(qi) >= 0) && (qint_get_int(qi) <= iter_max));
iter_called++;
}
static void qlist_iter_test(void)
START_TEST(qlist_iter_test)
{
int i;
QList *qlist;
@@ -108,20 +112,42 @@ static void qlist_iter_test(void)
iter_called = 0;
qlist_iter(qlist, iter_func, NULL);
g_assert(iter_called == iter_max);
fail_unless(iter_called == iter_max);
QDECREF(qlist);
}
END_TEST
int main(int argc, char **argv)
static Suite *QList_suite(void)
{
g_test_init(&argc, &argv, NULL);
Suite *s;
TCase *qlist_public_tcase;
g_test_add_func("/public/new", qlist_new_test);
g_test_add_func("/public/append", qlist_append_test);
g_test_add_func("/public/to_qlist", qobject_to_qlist_test);
g_test_add_func("/public/destroy", qlist_destroy_test);
g_test_add_func("/public/iter", qlist_iter_test);
s = suite_create("QList suite");
return g_test_run();
qlist_public_tcase = tcase_create("Public Interface");
suite_add_tcase(s, qlist_public_tcase);
tcase_add_test(qlist_public_tcase, qlist_new_test);
tcase_add_test(qlist_public_tcase, qlist_append_test);
tcase_add_test(qlist_public_tcase, qobject_to_qlist_test);
tcase_add_test(qlist_public_tcase, qlist_destroy_test);
tcase_add_test(qlist_public_tcase, qlist_iter_test);
return s;
}
int main(void)
{
int nf;
Suite *s;
SRunner *sr;
s = QList_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
nf = srunner_ntests_failed(sr);
srunner_free(sr);
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

134
check-qstring.c Normal file
View File

@@ -0,0 +1,134 @@
/*
* QString unit-tests.
*
* Copyright (C) 2009 Red Hat Inc.
*
* Authors:
* Luiz Capitulino <lcapitulino@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*/
#include <check.h>
#include "qstring.h"
#include "qemu-common.h"
/*
* Public Interface test-cases
*
* (with some violations to access 'private' data)
*/
START_TEST(qstring_from_str_test)
{
QString *qstring;
const char *str = "QEMU";
qstring = qstring_from_str(str);
fail_unless(qstring != NULL);
fail_unless(qstring->base.refcnt == 1);
fail_unless(strcmp(str, qstring->string) == 0);
fail_unless(qobject_type(QOBJECT(qstring)) == QTYPE_QSTRING);
// destroy doesn't exit yet
g_free(qstring->string);
g_free(qstring);
}
END_TEST
START_TEST(qstring_destroy_test)
{
QString *qstring = qstring_from_str("destroy test");
QDECREF(qstring);
}
END_TEST
START_TEST(qstring_get_str_test)
{
QString *qstring;
const char *ret_str;
const char *str = "QEMU/KVM";
qstring = qstring_from_str(str);
ret_str = qstring_get_str(qstring);
fail_unless(strcmp(ret_str, str) == 0);
QDECREF(qstring);
}
END_TEST
START_TEST(qstring_append_chr_test)
{
int i;
QString *qstring;
const char *str = "qstring append char unit-test";
qstring = qstring_new();
for (i = 0; str[i]; i++)
qstring_append_chr(qstring, str[i]);
fail_unless(strcmp(str, qstring_get_str(qstring)) == 0);
QDECREF(qstring);
}
END_TEST
START_TEST(qstring_from_substr_test)
{
QString *qs;
qs = qstring_from_substr("virtualization", 3, 9);
fail_unless(qs != NULL);
fail_unless(strcmp(qstring_get_str(qs), "tualiza") == 0);
QDECREF(qs);
}
END_TEST
START_TEST(qobject_to_qstring_test)
{
QString *qstring;
qstring = qstring_from_str("foo");
fail_unless(qobject_to_qstring(QOBJECT(qstring)) == qstring);
QDECREF(qstring);
}
END_TEST
static Suite *qstring_suite(void)
{
Suite *s;
TCase *qstring_public_tcase;
s = suite_create("QString test-suite");
qstring_public_tcase = tcase_create("Public Interface");
suite_add_tcase(s, qstring_public_tcase);
tcase_add_test(qstring_public_tcase, qstring_from_str_test);
tcase_add_test(qstring_public_tcase, qstring_destroy_test);
tcase_add_test(qstring_public_tcase, qstring_get_str_test);
tcase_add_test(qstring_public_tcase, qstring_append_chr_test);
tcase_add_test(qstring_public_tcase, qstring_from_substr_test);
tcase_add_test(qstring_public_tcase, qobject_to_qstring_test);
return s;
}
int main(void)
{
int nf;
Suite *s;
SRunner *sr;
s = qstring_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
nf = srunner_ntests_failed(sr);
srunner_free(sr);
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

50
cmd.c
View File

@@ -25,7 +25,6 @@
#include "cmd.h"
#include "qemu-aio.h"
#include "main-loop.h"
#define _(x) x /* not gettext support yet */
@@ -147,7 +146,7 @@ static void prep_fetchline(void *opaque)
{
int *fetchable = opaque;
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
*fetchable= 1;
}
@@ -194,11 +193,12 @@ void command_loop(void)
if (!prompted) {
printf("%s", get_prompt());
fflush(stdout);
qemu_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, &fetchable);
qemu_aio_set_fd_handler(STDIN_FILENO, prep_fetchline, NULL, NULL,
NULL, &fetchable);
prompted = 1;
}
main_loop_wait(false);
qemu_aio_wait();
if (!fetchable) {
continue;
@@ -221,7 +221,7 @@ void command_loop(void)
prompted = 0;
fetchable = 0;
}
qemu_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL);
qemu_aio_set_fd_handler(STDIN_FILENO, NULL, NULL, NULL, NULL, NULL);
}
/* from libxcmd/input.c */
@@ -418,37 +418,31 @@ cvtstr(
char *str,
size_t size)
{
char *trim;
const char *suffix;
const char *fmt;
int precise;
precise = ((double)value * 1000 == (double)(int)value * 1000);
if (value >= EXABYTES(1)) {
suffix = " EiB";
snprintf(str, size - 4, "%.3f", TO_EXABYTES(value));
fmt = precise ? "%.f EiB" : "%.3f EiB";
snprintf(str, size, fmt, TO_EXABYTES(value));
} else if (value >= PETABYTES(1)) {
suffix = " PiB";
snprintf(str, size - 4, "%.3f", TO_PETABYTES(value));
fmt = precise ? "%.f PiB" : "%.3f PiB";
snprintf(str, size, fmt, TO_PETABYTES(value));
} else if (value >= TERABYTES(1)) {
suffix = " TiB";
snprintf(str, size - 4, "%.3f", TO_TERABYTES(value));
fmt = precise ? "%.f TiB" : "%.3f TiB";
snprintf(str, size, fmt, TO_TERABYTES(value));
} else if (value >= GIGABYTES(1)) {
suffix = " GiB";
snprintf(str, size - 4, "%.3f", TO_GIGABYTES(value));
fmt = precise ? "%.f GiB" : "%.3f GiB";
snprintf(str, size, fmt, TO_GIGABYTES(value));
} else if (value >= MEGABYTES(1)) {
suffix = " MiB";
snprintf(str, size - 4, "%.3f", TO_MEGABYTES(value));
fmt = precise ? "%.f MiB" : "%.3f MiB";
snprintf(str, size, fmt, TO_MEGABYTES(value));
} else if (value >= KILOBYTES(1)) {
suffix = " KiB";
snprintf(str, size - 4, "%.3f", TO_KILOBYTES(value));
fmt = precise ? "%.f KiB" : "%.3f KiB";
snprintf(str, size, fmt, TO_KILOBYTES(value));
} else {
suffix = " bytes";
snprintf(str, size - 6, "%f", value);
}
trim = strstr(str, ".000");
if (trim) {
strcpy(trim, suffix);
} else {
strcat(str, suffix);
snprintf(str, size, "%f bytes", value);
}
}

View File

@@ -9,8 +9,6 @@
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu-common.h"

View File

@@ -30,10 +30,8 @@
# define QEMU_PACKED __attribute__((packed))
#endif
#define cat(x,y) x ## y
#define cat2(x,y) cat(x,y)
#define QEMU_BUILD_BUG_ON(x) \
typedef char cat2(qemu_build_bug_on__,__LINE__)[(x)?-1:1];
typedef char qemu_build_bug_on__##__LINE__[(x)?-1:1];
#if defined __GNUC__
# if !QEMU_GNUC_PREREQ(4, 4)

668
configure vendored

File diff suppressed because it is too large Load Diff

View File

@@ -176,23 +176,17 @@ void vga_hw_invalidate(void)
void vga_hw_screen_dump(const char *filename)
{
TextConsole *previous_active_console;
bool cswitch;
previous_active_console = active_console;
cswitch = previous_active_console && previous_active_console->index != 0;
/* There is currently no way of specifying which screen we want to dump,
so always dump the first one. */
if (cswitch) {
console_select(0);
}
console_select(0);
if (consoles[0] && consoles[0]->hw_screen_dump) {
consoles[0]->hw_screen_dump(consoles[0]->hw, filename, cswitch);
} else {
error_report("screen dump not implemented");
consoles[0]->hw_screen_dump(consoles[0]->hw, filename);
}
if (cswitch) {
if (previous_active_console) {
console_select(previous_active_console->index);
}
}
@@ -1017,17 +1011,16 @@ static void console_putchar(TextConsole *s, int ch)
console_clear_xy(s, x, y);
}
}
break;
}
break;
}
case 'K':
switch (s->esc_params[0]) {
case 0:
/* clear to eol */
for(x = s->x; x < s->width; x++) {
/* clear to eol */
for(x = s->x; x < s->width; x++) {
console_clear_xy(s, x, s->y);
}
break;
}
break;
case 1:
/* clear from beginning of line */
for (x = 0; x <= s->x; x++) {
@@ -1039,12 +1032,12 @@ static void console_putchar(TextConsole *s, int ch)
for(x = 0; x < s->width; x++) {
console_clear_xy(s, x, s->y);
}
break;
}
break;
}
break;
case 'm':
console_handle_escape(s);
break;
console_handle_escape(s);
break;
case 'n':
/* report cursor position */
/* TODO: send ESC[row;colR */
@@ -1447,6 +1440,9 @@ void console_color_init(DisplayState *ds)
}
}
static int n_text_consoles;
static CharDriverState *text_consoles[128];
static void text_console_set_echo(CharDriverState *chr, bool echo)
{
TextConsole *s = chr->opaque;
@@ -1513,7 +1509,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds)
chr->init(chr);
}
CharDriverState *text_console_init(QemuOpts *opts)
int text_console_init(QemuOpts *opts, CharDriverState **_chr)
{
CharDriverState *chr;
TextConsole *s;
@@ -1522,6 +1518,13 @@ CharDriverState *text_console_init(QemuOpts *opts)
chr = g_malloc0(sizeof(CharDriverState));
if (n_text_consoles == 128) {
fprintf(stderr, "Too many text consoles\n");
exit(1);
}
text_consoles[n_text_consoles] = chr;
n_text_consoles++;
width = qemu_opt_get_number(opts, "width", 0);
if (width == 0)
width = qemu_opt_get_number(opts, "cols", 0) * FONT_WIDTH;
@@ -1538,7 +1541,7 @@ CharDriverState *text_console_init(QemuOpts *opts)
if (!s) {
g_free(chr);
return NULL;
return -EBUSY;
}
s->chr = chr;
@@ -1546,18 +1549,20 @@ CharDriverState *text_console_init(QemuOpts *opts)
s->g_height = height;
chr->opaque = s;
chr->chr_set_echo = text_console_set_echo;
return chr;
*_chr = chr;
return 0;
}
void text_consoles_set_display(DisplayState *ds)
{
int i;
for (i = 0; i < nb_consoles; i++) {
if (consoles[i]->console_type != GRAPHIC_CONSOLE) {
text_console_do_init(consoles[i]->chr, ds);
}
for (i = 0; i < n_text_consoles; i++) {
text_console_do_init(text_consoles[i], ds);
}
n_text_consoles = 0;
}
void qemu_console_resize(DisplayState *ds, int width, int height)
@@ -1684,7 +1689,6 @@ PixelFormat qemu_default_pixelformat(int bpp)
pf.rbits = 8;
pf.gbits = 8;
pf.bbits = 8;
break;
case 32:
pf.rmask = 0x00FF0000;
pf.gmask = 0x0000FF00;

View File

@@ -4,8 +4,8 @@
#include "qemu-char.h"
#include "qdict.h"
#include "notify.h"
#include "qerror.h"
#include "monitor.h"
#include "trace.h"
/* keyboard/mouse support */
@@ -74,6 +74,8 @@ struct MouseTransformInfo {
int a[7];
};
void do_info_mice_print(Monitor *mon, const QObject *data);
void do_info_mice(Monitor *mon, QObject **ret_data);
void do_mouse_set(Monitor *mon, const QDict *qdict);
/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx
@@ -203,13 +205,11 @@ static inline DisplaySurface* qemu_create_displaysurface(DisplayState *ds, int w
static inline DisplaySurface* qemu_resize_displaysurface(DisplayState *ds, int width, int height)
{
trace_displaysurface_resize(ds, ds->surface, width, height);
return ds->allocator->resize_displaysurface(ds->surface, width, height);
}
static inline void qemu_free_displaysurface(DisplayState *ds)
{
trace_displaysurface_free(ds, ds->surface);
ds->allocator->free_displaysurface(ds->surface);
}
@@ -343,7 +343,7 @@ static inline void console_write_ch(console_ch_t *dest, uint32_t ch)
typedef void (*vga_hw_update_ptr)(void *);
typedef void (*vga_hw_invalidate_ptr)(void *);
typedef void (*vga_hw_screen_dump_ptr)(void *, const char *, bool cswitch);
typedef void (*vga_hw_screen_dump_ptr)(void *, const char *);
typedef void (*vga_hw_text_update_ptr)(void *, console_ch_t *);
DisplayState *graphic_console_init(vga_hw_update_ptr update,
@@ -359,7 +359,7 @@ void vga_hw_text_update(console_ch_t *chardata);
int is_graphic_console(void);
int is_fixedsize_console(void);
CharDriverState *text_console_init(QemuOpts *opts);
int text_console_init(QemuOpts *opts, CharDriverState **_chr);
void text_consoles_set_display(DisplayState *ds);
void console_select(unsigned int index);
void console_color_init(DisplayState *ds);
@@ -386,10 +386,12 @@ int vnc_display_pw_expire(DisplayState *ds, time_t expires);
#else
static inline int vnc_display_password(DisplayState *ds, const char *password)
{
qerror_report(QERR_FEATURE_DISABLED, "vnc");
return -ENODEV;
}
static inline int vnc_display_pw_expire(DisplayState *ds, time_t expires)
{
qerror_report(QERR_FEATURE_DISABLED, "vnc");
return -ENODEV;
};
#endif

View File

@@ -26,105 +26,20 @@ typedef struct {
Coroutine base;
GThread *thread;
bool runnable;
bool free_on_thread_exit;
CoroutineAction action;
} CoroutineGThread;
static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
/* GLib 2.31 and beyond deprecated various parts of the thread API,
* but the new interfaces are not available in older GLib versions
* so we have to cope with both.
*/
#if GLIB_CHECK_VERSION(2, 31, 0)
/* Default zero-initialisation is sufficient for 2.31+ GCond */
static GCond the_coroutine_cond;
static GCond *coroutine_cond = &the_coroutine_cond;
static inline void init_coroutine_cond(void)
{
}
/* Awkwardly, the GPrivate API doesn't provide a way to update the
* GDestroyNotify handler for the coroutine key dynamically. So instead
* we track whether or not the CoroutineGThread should be freed on
* thread exit / coroutine key update using the free_on_thread_exit
* field.
*/
static void coroutine_destroy_notify(gpointer data)
{
CoroutineGThread *co = data;
if (co && co->free_on_thread_exit) {
g_free(co);
}
}
static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify);
static inline CoroutineGThread *get_coroutine_key(void)
{
return g_private_get(&coroutine_key);
}
static inline void set_coroutine_key(CoroutineGThread *co,
bool free_on_thread_exit)
{
/* Unlike g_static_private_set() this does not call the GDestroyNotify
* if the previous value of the key was NULL. Fortunately we only need
* the GDestroyNotify in the non-NULL key case.
*/
co->free_on_thread_exit = free_on_thread_exit;
g_private_replace(&coroutine_key, co);
}
static inline GThread *create_thread(GThreadFunc func, gpointer data)
{
return g_thread_new("coroutine", func, data);
}
#else
/* Handle older GLib versions */
static GCond *coroutine_cond;
static inline void init_coroutine_cond(void)
{
coroutine_cond = g_cond_new();
}
static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;
static inline CoroutineGThread *get_coroutine_key(void)
{
return g_static_private_get(&coroutine_key);
}
static inline void set_coroutine_key(CoroutineGThread *co,
bool free_on_thread_exit)
{
g_static_private_set(&coroutine_key, co,
free_on_thread_exit ? (GDestroyNotify)g_free : NULL);
}
static inline GThread *create_thread(GThreadFunc func, gpointer data)
{
return g_thread_create_full(func, data, 0, TRUE, TRUE,
G_THREAD_PRIORITY_NORMAL, NULL);
}
#endif
static void __attribute__((constructor)) coroutine_init(void)
{
if (!g_thread_supported()) {
#if !GLIB_CHECK_VERSION(2, 31, 0)
g_thread_init(NULL);
#else
fprintf(stderr, "glib threading failed to initialize.\n");
exit(1);
#endif
}
init_coroutine_cond();
coroutine_cond = g_cond_new();
}
static void coroutine_wait_runnable_locked(CoroutineGThread *co)
@@ -145,7 +60,7 @@ static gpointer coroutine_thread(gpointer opaque)
{
CoroutineGThread *co = opaque;
set_coroutine_key(co, false);
g_static_private_set(&coroutine_key, co, NULL);
coroutine_wait_runnable(co);
co->base.entry(co->base.entry_arg);
qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE);
@@ -157,7 +72,8 @@ Coroutine *qemu_coroutine_new(void)
CoroutineGThread *co;
co = g_malloc0(sizeof(*co));
co->thread = create_thread(coroutine_thread, co);
co->thread = g_thread_create_full(coroutine_thread, co, 0, TRUE, TRUE,
G_THREAD_PRIORITY_NORMAL, NULL);
if (!co->thread) {
g_free(co);
return NULL;
@@ -196,11 +112,12 @@ CoroutineAction qemu_coroutine_switch(Coroutine *from_,
Coroutine *qemu_coroutine_self(void)
{
CoroutineGThread *co = get_coroutine_key();
CoroutineGThread *co = g_static_private_get(&coroutine_key);
if (!co) {
co = g_malloc0(sizeof(*co));
co->runnable = true;
set_coroutine_key(co, true);
g_static_private_set(&coroutine_key, co, (GDestroyNotify)g_free);
}
return &co->base;
@@ -208,7 +125,7 @@ Coroutine *qemu_coroutine_self(void)
bool qemu_in_coroutine(void)
{
CoroutineGThread *co = get_coroutine_key();
CoroutineGThread *co = g_static_private_get(&coroutine_key);
return co && co->base.caller;
}

View File

@@ -1,334 +0,0 @@
/*
* sigaltstack coroutine initialization code
*
* Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
* Copyright (C) 2011 Kevin Wolf <kwolf@redhat.com>
* Copyright (C) 2012 Alex Barcelo <abarcelo@ac.upc.edu>
** This file is partly based on pth_mctx.c, from the GNU Portable Threads
** Copyright (c) 1999-2006 Ralf S. Engelschall <rse@engelschall.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
/* XXX Is there a nicer way to disable glibc's stack check for longjmp? */
#ifdef _FORTIFY_SOURCE
#undef _FORTIFY_SOURCE
#endif
#include <stdlib.h>
#include <setjmp.h>
#include <stdint.h>
#include <pthread.h>
#include <signal.h>
#include "qemu-common.h"
#include "qemu-coroutine-int.h"
enum {
/* Maximum free pool size prevents holding too many freed coroutines */
POOL_MAX_SIZE = 64,
};
/** Free list to speed up creation */
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
static unsigned int pool_size;
typedef struct {
Coroutine base;
void *stack;
jmp_buf env;
} CoroutineUContext;
/**
* Per-thread coroutine bookkeeping
*/
typedef struct {
/** Currently executing coroutine */
Coroutine *current;
/** The default coroutine */
CoroutineUContext leader;
/** Information for the signal handler (trampoline) */
jmp_buf tr_reenter;
volatile sig_atomic_t tr_called;
void *tr_handler;
} CoroutineThreadState;
static pthread_key_t thread_state_key;
static CoroutineThreadState *coroutine_get_thread_state(void)
{
CoroutineThreadState *s = pthread_getspecific(thread_state_key);
if (!s) {
s = g_malloc0(sizeof(*s));
s->current = &s->leader.base;
pthread_setspecific(thread_state_key, s);
}
return s;
}
static void qemu_coroutine_thread_cleanup(void *opaque)
{
CoroutineThreadState *s = opaque;
g_free(s);
}
static void __attribute__((destructor)) coroutine_cleanup(void)
{
Coroutine *co;
Coroutine *tmp;
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
g_free(co);
}
}
static void __attribute__((constructor)) coroutine_init(void)
{
int ret;
ret = pthread_key_create(&thread_state_key, qemu_coroutine_thread_cleanup);
if (ret != 0) {
fprintf(stderr, "unable to create leader key: %s\n", strerror(errno));
abort();
}
}
/* "boot" function
* This is what starts the coroutine, is called from the trampoline
* (from the signal handler when it is not signal handling, read ahead
* for more information).
*/
static void coroutine_bootstrap(CoroutineUContext *self, Coroutine *co)
{
/* Initialize longjmp environment and switch back the caller */
if (!setjmp(self->env)) {
longjmp(*(jmp_buf *)co->entry_arg, 1);
}
while (true) {
co->entry(co->entry_arg);
qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE);
}
}
/*
* This is used as the signal handler. This is called with the brand new stack
* (thanks to sigaltstack). We have to return, given that this is a signal
* handler and the sigmask and some other things are changed.
*/
static void coroutine_trampoline(int signal)
{
CoroutineUContext *self;
Coroutine *co;
CoroutineThreadState *coTS;
/* Get the thread specific information */
coTS = coroutine_get_thread_state();
self = coTS->tr_handler;
coTS->tr_called = 1;
co = &self->base;
/*
* Here we have to do a bit of a ping pong between the caller, given that
* this is a signal handler and we have to do a return "soon". Then the
* caller can reestablish everything and do a longjmp here again.
*/
if (!setjmp(coTS->tr_reenter)) {
return;
}
/*
* Ok, the caller has longjmp'ed back to us, so now prepare
* us for the real machine state switching. We have to jump
* into another function here to get a new stack context for
* the auto variables (which have to be auto-variables
* because the start of the thread happens later). Else with
* PIC (i.e. Position Independent Code which is used when PTH
* is built as a shared library) most platforms would
* horrible core dump as experience showed.
*/
coroutine_bootstrap(self, co);
}
static Coroutine *coroutine_new(void)
{
const size_t stack_size = 1 << 20;
CoroutineUContext *co;
CoroutineThreadState *coTS;
struct sigaction sa;
struct sigaction osa;
struct sigaltstack ss;
struct sigaltstack oss;
sigset_t sigs;
sigset_t osigs;
jmp_buf old_env;
/* The way to manipulate stack is with the sigaltstack function. We
* prepare a stack, with it delivering a signal to ourselves and then
* put setjmp/longjmp where needed.
* This has been done keeping coroutine-ucontext as a model and with the
* pth ideas (GNU Portable Threads). See coroutine-ucontext for the basics
* of the coroutines and see pth_mctx.c (from the pth project) for the
* sigaltstack way of manipulating stacks.
*/
co = g_malloc0(sizeof(*co));
co->stack = g_malloc(stack_size);
co->base.entry_arg = &old_env; /* stash away our jmp_buf */
coTS = coroutine_get_thread_state();
coTS->tr_handler = co;
/*
* Preserve the SIGUSR2 signal state, block SIGUSR2,
* and establish our signal handler. The signal will
* later transfer control onto the signal stack.
*/
sigemptyset(&sigs);
sigaddset(&sigs, SIGUSR2);
pthread_sigmask(SIG_BLOCK, &sigs, &osigs);
sa.sa_handler = coroutine_trampoline;
sigfillset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK;
if (sigaction(SIGUSR2, &sa, &osa) != 0) {
abort();
}
/*
* Set the new stack.
*/
ss.ss_sp = co->stack;
ss.ss_size = stack_size;
ss.ss_flags = 0;
if (sigaltstack(&ss, &oss) < 0) {
abort();
}
/*
* Now transfer control onto the signal stack and set it up.
* It will return immediately via "return" after the setjmp()
* was performed. Be careful here with race conditions. The
* signal can be delivered the first time sigsuspend() is
* called.
*/
coTS->tr_called = 0;
pthread_kill(pthread_self(), SIGUSR2);
sigfillset(&sigs);
sigdelset(&sigs, SIGUSR2);
while (!coTS->tr_called) {
sigsuspend(&sigs);
}
/*
* Inform the system that we are back off the signal stack by
* removing the alternative signal stack. Be careful here: It
* first has to be disabled, before it can be removed.
*/
sigaltstack(NULL, &ss);
ss.ss_flags = SS_DISABLE;
if (sigaltstack(&ss, NULL) < 0) {
abort();
}
sigaltstack(NULL, &ss);
if (!(oss.ss_flags & SS_DISABLE)) {
sigaltstack(&oss, NULL);
}
/*
* Restore the old SIGUSR2 signal handler and mask
*/
sigaction(SIGUSR2, &osa, NULL);
pthread_sigmask(SIG_SETMASK, &osigs, NULL);
/*
* Now enter the trampoline again, but this time not as a signal
* handler. Instead we jump into it directly. The functionally
* redundant ping-pong pointer arithmetic is necessary to avoid
* type-conversion warnings related to the `volatile' qualifier and
* the fact that `jmp_buf' usually is an array type.
*/
if (!setjmp(old_env)) {
longjmp(coTS->tr_reenter, 1);
}
/*
* Ok, we returned again, so now we're finished
*/
return &co->base;
}
Coroutine *qemu_coroutine_new(void)
{
Coroutine *co;
co = QSLIST_FIRST(&pool);
if (co) {
QSLIST_REMOVE_HEAD(&pool, pool_next);
pool_size--;
} else {
co = coroutine_new();
}
return co;
}
void qemu_coroutine_delete(Coroutine *co_)
{
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
if (pool_size < POOL_MAX_SIZE) {
QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
co->base.caller = NULL;
pool_size++;
return;
}
g_free(co->stack);
g_free(co);
}
CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,
CoroutineAction action)
{
CoroutineUContext *from = DO_UPCAST(CoroutineUContext, base, from_);
CoroutineUContext *to = DO_UPCAST(CoroutineUContext, base, to_);
CoroutineThreadState *s = coroutine_get_thread_state();
int ret;
s->current = to_;
ret = setjmp(from->env);
if (ret == 0) {
longjmp(to->env, action);
}
return ret;
}
Coroutine *qemu_coroutine_self(void)
{
CoroutineThreadState *s = coroutine_get_thread_state();
return s->current;
}
bool qemu_in_coroutine(void)
{
CoroutineThreadState *s = pthread_getspecific(thread_state_key);
return s && s->current->caller;
}

View File

@@ -36,7 +36,7 @@ enum {
};
/** Free list to speed up creation */
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
static QLIST_HEAD(, Coroutine) pool = QLIST_HEAD_INITIALIZER(pool);
static unsigned int pool_size;
typedef struct {
@@ -92,7 +92,7 @@ static void __attribute__((destructor)) coroutine_cleanup(void)
Coroutine *co;
Coroutine *tmp;
QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
QLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
g_free(co);
}
@@ -175,9 +175,9 @@ Coroutine *qemu_coroutine_new(void)
{
Coroutine *co;
co = QSLIST_FIRST(&pool);
co = QLIST_FIRST(&pool);
if (co) {
QSLIST_REMOVE_HEAD(&pool, pool_next);
QLIST_REMOVE(co, pool_next);
pool_size--;
} else {
co = coroutine_new();
@@ -190,7 +190,7 @@ void qemu_coroutine_delete(Coroutine *co_)
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
if (pool_size < POOL_MAX_SIZE) {
QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
QLIST_INSERT_HEAD(&pool, &co->base, pool_next);
co->base.caller = NULL;
pool_size++;
return;

160
cpu-all.h
View File

@@ -197,15 +197,14 @@ extern unsigned long reserved_va;
#endif
/* All direct uses of g2h and h2g need to go away for usermode softmmu. */
#define g2h(x) ((void *)((unsigned long)(target_ulong)(x) + GUEST_BASE))
#define g2h(x) ((void *)((unsigned long)(x) + GUEST_BASE))
#if HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS
#define h2g_valid(x) 1
#else
#define h2g_valid(x) ({ \
unsigned long __guest = (unsigned long)(x) - GUEST_BASE; \
(__guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS)) && \
(!RESERVED_VA || (__guest < RESERVED_VA)); \
__guest < (1ul << TARGET_VIRT_ADDR_SPACE_BITS); \
})
#endif
@@ -222,8 +221,8 @@ extern unsigned long reserved_va;
#else /* !CONFIG_USER_ONLY */
/* NOTE: we use double casts if pointers and target_ulong have
different sizes */
#define saddr(x) (uint8_t *)(intptr_t)(x)
#define laddr(x) (uint8_t *)(intptr_t)(x)
#define saddr(x) (uint8_t *)(long)(x)
#define laddr(x) (uint8_t *)(long)(x)
#endif
#define ldub_raw(p) ldub_p(laddr((p)))
@@ -260,21 +259,12 @@ extern unsigned long reserved_va;
#define stfl(p, v) stfl_raw(p, v)
#define stfq(p, v) stfq_raw(p, v)
#ifndef CONFIG_TCG_PASS_AREG0
#define ldub_code(p) ldub_raw(p)
#define ldsb_code(p) ldsb_raw(p)
#define lduw_code(p) lduw_raw(p)
#define ldsw_code(p) ldsw_raw(p)
#define ldl_code(p) ldl_raw(p)
#define ldq_code(p) ldq_raw(p)
#else
#define cpu_ldub_code(env1, p) ldub_raw(p)
#define cpu_ldsb_code(env1, p) ldsb_raw(p)
#define cpu_lduw_code(env1, p) lduw_raw(p)
#define cpu_ldsw_code(env1, p) ldsw_raw(p)
#define cpu_ldl_code(env1, p) ldl_raw(p)
#define cpu_ldq_code(env1, p) ldq_raw(p)
#endif
#define ldub_kernel(p) ldub_raw(p)
#define ldsb_kernel(p) ldsb_raw(p)
@@ -299,10 +289,10 @@ extern unsigned long reserved_va;
#define TARGET_PAGE_MASK ~(TARGET_PAGE_SIZE - 1)
#define TARGET_PAGE_ALIGN(addr) (((addr) + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK)
/* ??? These should be the larger of uintptr_t and target_ulong. */
extern uintptr_t qemu_real_host_page_size;
extern uintptr_t qemu_host_page_size;
extern uintptr_t qemu_host_page_mask;
/* ??? These should be the larger of unsigned long and target_ulong. */
extern unsigned long qemu_real_host_page_size;
extern unsigned long qemu_host_page_size;
extern unsigned long qemu_host_page_mask;
#define HOST_PAGE_ALIGN(addr) (((addr) + qemu_host_page_size - 1) & qemu_host_page_mask)
@@ -332,21 +322,21 @@ void page_set_flags(target_ulong start, target_ulong end, int flags);
int page_check_range(target_ulong start, target_ulong len, int flags);
#endif
CPUArchState *cpu_copy(CPUArchState *env);
CPUArchState *qemu_get_cpu(int cpu);
CPUState *cpu_copy(CPUState *env);
CPUState *qemu_get_cpu(int cpu);
#define CPU_DUMP_CODE 0x00010000
void cpu_dump_state(CPUArchState *env, FILE *f, fprintf_function cpu_fprintf,
void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
int flags);
void cpu_dump_statistics(CPUArchState *env, FILE *f, fprintf_function cpu_fprintf,
void cpu_dump_statistics(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
int flags);
void QEMU_NORETURN cpu_abort(CPUArchState *env, const char *fmt, ...)
void QEMU_NORETURN cpu_abort(CPUState *env, const char *fmt, ...)
GCC_FMT_ATTR(2, 3);
extern CPUArchState *first_cpu;
DECLARE_TLS(CPUArchState *,cpu_single_env);
#define cpu_single_env tls_var(cpu_single_env)
extern CPUState *first_cpu;
DECLARE_TLS(CPUState *,cpu_single_env);
#define cpu_single_env get_tls(cpu_single_env)
/* Flags for use in ENV->INTERRUPT_PENDING.
@@ -378,16 +368,15 @@ DECLARE_TLS(CPUArchState *,cpu_single_env);
#define CPU_INTERRUPT_TGT_EXT_4 0x1000
/* Several target-specific internal interrupts. These differ from the
preceding target-specific interrupts in that they are intended to
preceeding target-specific interrupts in that they are intended to
originate from within the cpu itself, typically in response to some
instruction being executed. These, therefore, are not masked while
single-stepping within the debugger. */
#define CPU_INTERRUPT_TGT_INT_0 0x0100
#define CPU_INTERRUPT_TGT_INT_1 0x0400
#define CPU_INTERRUPT_TGT_INT_2 0x0800
#define CPU_INTERRUPT_TGT_INT_3 0x2000
/* First unused bit: 0x4000. */
/* First unused bit: 0x2000. */
/* The set of all bits that should be masked when single-stepping. */
#define CPU_INTERRUPT_SSTEP_MASK \
@@ -399,23 +388,23 @@ DECLARE_TLS(CPUArchState *,cpu_single_env);
| CPU_INTERRUPT_TGT_EXT_4)
#ifndef CONFIG_USER_ONLY
typedef void (*CPUInterruptHandler)(CPUArchState *, int);
typedef void (*CPUInterruptHandler)(CPUState *, int);
extern CPUInterruptHandler cpu_interrupt_handler;
static inline void cpu_interrupt(CPUArchState *s, int mask)
static inline void cpu_interrupt(CPUState *s, int mask)
{
cpu_interrupt_handler(s, mask);
}
#else /* USER_ONLY */
void cpu_interrupt(CPUArchState *env, int mask);
void cpu_interrupt(CPUState *env, int mask);
#endif /* USER_ONLY */
void cpu_reset_interrupt(CPUArchState *env, int mask);
void cpu_reset_interrupt(CPUState *env, int mask);
void cpu_exit(CPUArchState *s);
void cpu_exit(CPUState *s);
bool qemu_cpu_has_work(CPUArchState *env);
bool qemu_cpu_has_work(CPUState *env);
/* Breakpoint/watchpoint flags */
#define BP_MEM_READ 0x01
@@ -426,26 +415,26 @@ bool qemu_cpu_has_work(CPUArchState *env);
#define BP_GDB 0x10
#define BP_CPU 0x20
int cpu_breakpoint_insert(CPUArchState *env, target_ulong pc, int flags,
int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags,
CPUBreakpoint **breakpoint);
int cpu_breakpoint_remove(CPUArchState *env, target_ulong pc, int flags);
void cpu_breakpoint_remove_by_ref(CPUArchState *env, CPUBreakpoint *breakpoint);
void cpu_breakpoint_remove_all(CPUArchState *env, int mask);
int cpu_watchpoint_insert(CPUArchState *env, target_ulong addr, target_ulong len,
int cpu_breakpoint_remove(CPUState *env, target_ulong pc, int flags);
void cpu_breakpoint_remove_by_ref(CPUState *env, CPUBreakpoint *breakpoint);
void cpu_breakpoint_remove_all(CPUState *env, int mask);
int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len,
int flags, CPUWatchpoint **watchpoint);
int cpu_watchpoint_remove(CPUArchState *env, target_ulong addr,
int cpu_watchpoint_remove(CPUState *env, target_ulong addr,
target_ulong len, int flags);
void cpu_watchpoint_remove_by_ref(CPUArchState *env, CPUWatchpoint *watchpoint);
void cpu_watchpoint_remove_all(CPUArchState *env, int mask);
void cpu_watchpoint_remove_by_ref(CPUState *env, CPUWatchpoint *watchpoint);
void cpu_watchpoint_remove_all(CPUState *env, int mask);
#define SSTEP_ENABLE 0x1 /* Enable simulated HW single stepping */
#define SSTEP_NOIRQ 0x2 /* Do not use IRQ while single stepping */
#define SSTEP_NOTIMER 0x4 /* Do not Timers while single stepping */
void cpu_single_step(CPUArchState *env, int enabled);
void cpu_state_reset(CPUArchState *s);
int cpu_is_stopped(CPUArchState *env);
void run_on_cpu(CPUArchState *env, void (*func)(void *data), void *data);
void cpu_single_step(CPUState *env, int enabled);
void cpu_reset(CPUState *s);
int cpu_is_stopped(CPUState *env);
void run_on_cpu(CPUState *env, void (*func)(void *data), void *data);
#define CPU_LOG_TB_OUT_ASM (1 << 0)
#define CPU_LOG_TB_IN_ASM (1 << 1)
@@ -476,7 +465,7 @@ int cpu_str_to_log_mask(const char *str);
/* Return the physical page corresponding to a virtual one. Use it
only for debugging because no protection checks are done. Return -1
if no page found. */
target_phys_addr_t cpu_get_phys_page_debug(CPUArchState *env, target_ulong addr);
target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr);
/* memory API */
@@ -487,7 +476,6 @@ extern ram_addr_t ram_size;
#define RAM_PREALLOC_MASK (1 << 0)
typedef struct RAMBlock {
struct MemoryRegion *mr;
uint8_t *host;
ram_addr_t offset;
ram_addr_t length;
@@ -508,6 +496,14 @@ extern RAMList ram_list;
extern const char *mem_path;
extern int mem_prealloc;
/* physical memory access */
/* MMIO pages are identified by a combination of an IO device index and
3 flags. The ROMD code stores the page ram offset in iotlb entry,
so only a limited number of ids are avaiable. */
#define IO_MEM_NB_ENTRIES (1 << (TARGET_PAGE_BITS - IO_MEM_SHIFT))
/* Flags stored in the low bits of the TLB virtual address. These are
defined so that fast path ram access is all zeros. */
/* Zero if TLB entry is valid. */
@@ -518,10 +514,74 @@ extern int mem_prealloc;
/* Set if TLB entry is an IO callback. */
#define TLB_MMIO (1 << 5)
#define VGA_DIRTY_FLAG 0x01
#define CODE_DIRTY_FLAG 0x02
#define MIGRATION_DIRTY_FLAG 0x08
/* read dirty bit (return 0 or 1) */
static inline int cpu_physical_memory_is_dirty(ram_addr_t addr)
{
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] == 0xff;
}
static inline int cpu_physical_memory_get_dirty_flags(ram_addr_t addr)
{
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS];
}
static inline int cpu_physical_memory_get_dirty(ram_addr_t addr,
int dirty_flags)
{
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] & dirty_flags;
}
static inline void cpu_physical_memory_set_dirty(ram_addr_t addr)
{
ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] = 0xff;
}
static inline int cpu_physical_memory_set_dirty_flags(ram_addr_t addr,
int dirty_flags)
{
return ram_list.phys_dirty[addr >> TARGET_PAGE_BITS] |= dirty_flags;
}
static inline void cpu_physical_memory_mask_dirty_range(ram_addr_t start,
int length,
int dirty_flags)
{
int i, mask, len;
uint8_t *p;
len = length >> TARGET_PAGE_BITS;
mask = ~dirty_flags;
p = ram_list.phys_dirty + (start >> TARGET_PAGE_BITS);
for (i = 0; i < len; i++) {
p[i] &= mask;
}
}
void cpu_physical_memory_reset_dirty(ram_addr_t start, ram_addr_t end,
int dirty_flags);
void cpu_tlb_update_dirty(CPUState *env);
int cpu_physical_memory_set_dirty_tracking(int enable);
int cpu_physical_memory_get_dirty_tracking(void);
int cpu_physical_sync_dirty_bitmap(target_phys_addr_t start_addr,
target_phys_addr_t end_addr);
int cpu_physical_log_start(target_phys_addr_t start_addr,
ram_addr_t size);
int cpu_physical_log_stop(target_phys_addr_t start_addr,
ram_addr_t size);
void dump_exec_info(FILE *f, fprintf_function cpu_fprintf);
#endif /* !CONFIG_USER_ONLY */
int cpu_memory_rw_debug(CPUArchState *env, target_ulong addr,
int cpu_memory_rw_debug(CPUState *env, target_ulong addr,
uint8_t *buf, int len, int is_write);
#endif /* CPU_ALL_H */

View File

@@ -1,7 +1,7 @@
#ifndef CPU_COMMON_H
#define CPU_COMMON_H 1
/* CPU interfaces that are target independent. */
/* CPU interfaces that are target indpendent. */
#ifdef TARGET_PHYS_ADDR_BITS
#include "targphys.h"
@@ -28,9 +28,9 @@ typedef uint64_t ram_addr_t;
# define RAM_ADDR_MAX UINT64_MAX
# define RAM_ADDR_FMT "%" PRIx64
#else
typedef uintptr_t ram_addr_t;
# define RAM_ADDR_MAX UINTPTR_MAX
# define RAM_ADDR_FMT "%" PRIxPTR
typedef unsigned long ram_addr_t;
# define RAM_ADDR_MAX ULONG_MAX
# define RAM_ADDR_FMT "%lx"
#endif
/* memory API */
@@ -38,6 +38,34 @@ typedef uintptr_t ram_addr_t;
typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
void cpu_register_physical_memory_log(target_phys_addr_t start_addr,
ram_addr_t size,
ram_addr_t phys_offset,
ram_addr_t region_offset,
bool log_dirty);
static inline void cpu_register_physical_memory_offset(target_phys_addr_t start_addr,
ram_addr_t size,
ram_addr_t phys_offset,
ram_addr_t region_offset)
{
cpu_register_physical_memory_log(start_addr, size, phys_offset,
region_offset, false);
}
static inline void cpu_register_physical_memory(target_phys_addr_t start_addr,
ram_addr_t size,
ram_addr_t phys_offset)
{
cpu_register_physical_memory_offset(start_addr, size, phys_offset, 0);
}
ram_addr_t cpu_get_physical_page_desc(target_phys_addr_t addr);
ram_addr_t qemu_ram_alloc_from_ptr(DeviceState *dev, const char *name,
ram_addr_t size, void *host);
ram_addr_t qemu_ram_alloc(DeviceState *dev, const char *name, ram_addr_t size);
void qemu_ram_free(ram_addr_t addr);
void qemu_ram_free_from_ptr(ram_addr_t addr);
void qemu_ram_remap(ram_addr_t addr, ram_addr_t length);
/* This should only be used for ram local to a device. */
void *qemu_get_ram_ptr(ram_addr_t addr);
@@ -49,7 +77,11 @@ void qemu_put_ram_ptr(void *addr);
/* This should not be used by devices. */
int qemu_ram_addr_from_host(void *ptr, ram_addr_t *ram_addr);
ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr);
void qemu_ram_set_idstr(ram_addr_t addr, const char *name, DeviceState *dev);
int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read,
CPUWriteMemoryFunc * const *mem_write,
void *opaque, enum device_endian endian);
void cpu_unregister_io_memory(int table_address);
void cpu_physical_memory_rw(target_phys_addr_t addr, uint8_t *buf,
int len, int is_write);
@@ -71,11 +103,38 @@ void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len,
void *cpu_register_map_client(void *opaque, void (*callback)(void *opaque));
void cpu_unregister_map_client(void *cookie);
struct CPUPhysMemoryClient;
typedef struct CPUPhysMemoryClient CPUPhysMemoryClient;
struct CPUPhysMemoryClient {
void (*set_memory)(struct CPUPhysMemoryClient *client,
target_phys_addr_t start_addr,
ram_addr_t size,
ram_addr_t phys_offset,
bool log_dirty);
int (*sync_dirty_bitmap)(struct CPUPhysMemoryClient *client,
target_phys_addr_t start_addr,
target_phys_addr_t end_addr);
int (*migration_log)(struct CPUPhysMemoryClient *client,
int enable);
int (*log_start)(struct CPUPhysMemoryClient *client,
target_phys_addr_t phys_addr, ram_addr_t size);
int (*log_stop)(struct CPUPhysMemoryClient *client,
target_phys_addr_t phys_addr, ram_addr_t size);
QLIST_ENTRY(CPUPhysMemoryClient) list;
};
void cpu_register_phys_memory_client(CPUPhysMemoryClient *);
void cpu_unregister_phys_memory_client(CPUPhysMemoryClient *);
/* Coalesced MMIO regions are areas where write operations can be reordered.
* This usually implies that write operations are side-effect free. This allows
* batching which can make a major impact on performance when using
* virtualization.
*/
void qemu_register_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size);
void qemu_unregister_coalesced_mmio(target_phys_addr_t addr, ram_addr_t size);
void qemu_flush_coalesced_mmio_buffer(void);
uint32_t ldub_phys(target_phys_addr_t addr);
@@ -107,10 +166,17 @@ void stq_phys(target_phys_addr_t addr, uint64_t val);
void cpu_physical_memory_write_rom(target_phys_addr_t addr,
const uint8_t *buf, int len);
extern struct MemoryRegion io_mem_ram;
extern struct MemoryRegion io_mem_rom;
extern struct MemoryRegion io_mem_unassigned;
extern struct MemoryRegion io_mem_notdirty;
#define IO_MEM_SHIFT 3
#define IO_MEM_RAM (0 << IO_MEM_SHIFT) /* hardcoded offset */
#define IO_MEM_ROM (1 << IO_MEM_SHIFT) /* hardcoded offset */
#define IO_MEM_UNASSIGNED (2 << IO_MEM_SHIFT)
#define IO_MEM_NOTDIRTY (3 << IO_MEM_SHIFT)
#define IO_MEM_SUBPAGE_RAM (4 << IO_MEM_SHIFT)
/* Acts like a ROM when read and like a device when written. */
#define IO_MEM_ROMD (1)
#define IO_MEM_SUBPAGE (2)
#endif

View File

@@ -60,6 +60,8 @@ typedef uint64_t target_ulong __attribute__((aligned(TARGET_LONG_ALIGNMENT)));
#error TARGET_LONG_SIZE undefined
#endif
#define HOST_LONG_SIZE (HOST_LONG_BITS / 8)
#define EXCP_INTERRUPT 0x10000 /* async interruption */
#define EXCP_HLT 0x10001 /* hlt instruction reached */
#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
@@ -98,12 +100,12 @@ typedef struct CPUTLBEntry {
target_ulong addr_code;
/* Addend to virtual address to get host address. IO accesses
use the corresponding iotlb value. */
uintptr_t addend;
unsigned long addend;
/* padding to get a power of two size */
uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) -
(sizeof(target_ulong) * 3 +
((-sizeof(target_ulong) * 3) & (sizeof(uintptr_t) - 1)) +
sizeof(uintptr_t))];
uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) -
(sizeof(target_ulong) * 3 +
((-sizeof(target_ulong) * 3) & (sizeof(unsigned long) - 1)) +
sizeof(unsigned long))];
} CPUTLBEntry;
extern int CPUTLBEntry_wrong_size[sizeof(CPUTLBEntry) == (1 << CPU_TLB_ENTRY_BITS) ? 1 : -1];
@@ -151,14 +153,6 @@ typedef struct CPUWatchpoint {
QTAILQ_ENTRY(CPUWatchpoint) entry;
} CPUWatchpoint;
#ifdef _WIN32
#define CPU_COMMON_THREAD \
void *hThread;
#else
#define CPU_COMMON_THREAD
#endif
#define CPU_TEMP_BUF_NLONGS 128
#define CPU_COMMON \
struct TranslationBlock *current_tb; /* currently executing TB */ \
@@ -166,8 +160,8 @@ typedef struct CPUWatchpoint {
/* in order to avoid passing too many arguments to the MMIO \
helpers, we store some rarely used information in the CPU \
context) */ \
uintptr_t mem_io_pc; /* host pc at which the memory was \
accessed */ \
unsigned long mem_io_pc; /* host pc at which the memory was \
accessed */ \
target_ulong mem_io_vaddr; /* target virtual addr at which the \
memory was accessed */ \
uint32_t halted; /* Nonzero if the CPU is in suspend state */ \
@@ -202,7 +196,7 @@ typedef struct CPUWatchpoint {
jmp_buf jmp_env; \
int exception_index; \
\
CPUArchState *next_cpu; /* next CPU sharing TB cache */ \
CPUState *next_cpu; /* next CPU sharing TB cache */ \
int cpu_index; /* CPU index (informative) */ \
uint32_t host_tid; /* host thread ID */ \
int numa_node; /* NUMA node this cpu is belonging to */ \
@@ -217,7 +211,6 @@ typedef struct CPUWatchpoint {
uint32_t stop; /* Stop request */ \
uint32_t stopped; /* Artificially stopped */ \
struct QemuThread *thread; \
CPU_COMMON_THREAD \
struct QemuCond *halt_cond; \
int thread_kicked; \
struct qemu_work_item *queued_work_first, *queued_work_last; \

View File

@@ -1,5 +1,5 @@
/*
* emulator main execution loop
* i386 emulator main execution loop
*
* Copyright (c) 2003-2005 Fabrice Bellard
*
@@ -21,18 +21,17 @@
#include "disas.h"
#include "tcg.h"
#include "qemu-barrier.h"
#include "qtest.h"
int tb_invalidated_flag;
//#define CONFIG_DEBUG_EXEC
bool qemu_cpu_has_work(CPUArchState *env)
bool qemu_cpu_has_work(CPUState *env)
{
return cpu_has_work(env);
}
void cpu_loop_exit(CPUArchState *env)
void cpu_loop_exit(CPUState *env)
{
env->current_tb = NULL;
longjmp(env->jmp_env, 1);
@@ -42,7 +41,7 @@ void cpu_loop_exit(CPUArchState *env)
restored in a state compatible with the CPU emulator
*/
#if defined(CONFIG_SOFTMMU)
void cpu_resume_from_signal(CPUArchState *env, void *puc)
void cpu_resume_from_signal(CPUState *env, void *puc)
{
/* XXX: restore cpu registers saved in host registers */
@@ -53,10 +52,10 @@ void cpu_resume_from_signal(CPUArchState *env, void *puc)
/* Execute the code without caching the generated code. An interpreter
could be used if available. */
static void cpu_exec_nocache(CPUArchState *env, int max_cycles,
static void cpu_exec_nocache(CPUState *env, int max_cycles,
TranslationBlock *orig_tb)
{
tcg_target_ulong next_tb;
unsigned long next_tb;
TranslationBlock *tb;
/* Should never happen.
@@ -80,7 +79,7 @@ static void cpu_exec_nocache(CPUArchState *env, int max_cycles,
tb_free(tb);
}
static TranslationBlock *tb_find_slow(CPUArchState *env,
static TranslationBlock *tb_find_slow(CPUState *env,
target_ulong pc,
target_ulong cs_base,
uint64_t flags)
@@ -136,7 +135,7 @@ static TranslationBlock *tb_find_slow(CPUArchState *env,
return tb;
}
static inline TranslationBlock *tb_find_fast(CPUArchState *env)
static inline TranslationBlock *tb_find_fast(CPUState *env)
{
TranslationBlock *tb;
target_ulong cs_base, pc;
@@ -164,7 +163,7 @@ CPUDebugExcpHandler *cpu_set_debug_excp_handler(CPUDebugExcpHandler *handler)
return old_handler;
}
static void cpu_handle_debug_exception(CPUArchState *env)
static void cpu_handle_debug_exception(CPUState *env)
{
CPUWatchpoint *wp;
@@ -182,12 +181,12 @@ static void cpu_handle_debug_exception(CPUArchState *env)
volatile sig_atomic_t exit_request;
int cpu_exec(CPUArchState *env)
int cpu_exec(CPUState *env)
{
int ret, interrupt_request;
TranslationBlock *tb;
uint8_t *tc_ptr;
tcg_target_ulong next_tb;
unsigned long next_tb;
if (env->halted) {
if (!cpu_has_work(env)) {
@@ -305,7 +304,7 @@ int cpu_exec(CPUArchState *env)
env->hflags2 |= HF2_NMI_MASK;
do_interrupt_x86_hardirq(env, EXCP02_NMI, 1);
next_tb = 0;
} else if (interrupt_request & CPU_INTERRUPT_MCE) {
} else if (interrupt_request & CPU_INTERRUPT_MCE) {
env->interrupt_request &= ~CPU_INTERRUPT_MCE;
do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0);
next_tb = 0;
@@ -340,9 +339,11 @@ int cpu_exec(CPUArchState *env)
}
}
#elif defined(TARGET_PPC)
#if 0
if ((interrupt_request & CPU_INTERRUPT_RESET)) {
cpu_state_reset(env);
cpu_reset(env);
}
#endif
if (interrupt_request & CPU_INTERRUPT_HARD) {
ppc_hw_interrupt(env);
if (env->pending_interrupts == 0)
@@ -389,7 +390,7 @@ int cpu_exec(CPUArchState *env)
next_tb = 0;
}
}
}
}
#elif defined(TARGET_ARM)
if (interrupt_request & CPU_INTERRUPT_FIQ
&& !(env->uncached_cpsr & CPSR_F)) {
@@ -428,7 +429,7 @@ int cpu_exec(CPUArchState *env)
{
int idx = -1;
/* ??? This hard-codes the OSF/1 interrupt levels. */
switch (env->pal_mode ? 7 : env->ps & PS_INT_MASK) {
switch (env->pal_mode ? 7 : env->ps & PS_INT_MASK) {
case 0 ... 3:
if (interrupt_request & CPU_INTERRUPT_HARD) {
idx = EXCP_DEV_INTERRUPT;
@@ -541,8 +542,8 @@ int cpu_exec(CPUArchState *env)
tb_invalidated_flag = 0;
}
#ifdef CONFIG_DEBUG_EXEC
qemu_log_mask(CPU_LOG_EXEC, "Trace %p [" TARGET_FMT_lx "] %s\n",
tb->tc_ptr, tb->pc,
qemu_log_mask(CPU_LOG_EXEC, "Trace 0x%08lx [" TARGET_FMT_lx "] %s\n",
(long)tb->tc_ptr, tb->pc,
lookup_symbol(tb->pc));
#endif
/* see if we can patch the calling TB. When the TB
@@ -561,12 +562,12 @@ int cpu_exec(CPUArchState *env)
barrier();
if (likely(!env->exit_request)) {
tc_ptr = tb->tc_ptr;
/* execute the generated code */
/* execute the generated code */
next_tb = tcg_qemu_tb_exec(env, tc_ptr);
if ((next_tb & 3) == 2) {
/* Instruction counter expired. */
int insns_left;
tb = (TranslationBlock *)(next_tb & ~3);
tb = (TranslationBlock *)(long)(next_tb & ~3);
/* Restore PC. */
cpu_pc_from_tb(env, tb);
insns_left = env->icount_decr.u32;

302
cpus.c
View File

@@ -34,7 +34,6 @@
#include "qemu-thread.h"
#include "cpus.h"
#include "qtest.h"
#include "main-loop.h"
#ifndef _WIN32
@@ -59,7 +58,7 @@
#endif /* CONFIG_LINUX */
static CPUArchState *next_cpu;
static CPUState *next_cpu;
/***********************************************************/
/* guest cycle counter */
@@ -90,7 +89,7 @@ TimersState timers_state;
int64_t cpu_get_icount(void)
{
int64_t icount;
CPUArchState *env = cpu_single_env;
CPUState *env = cpu_single_env;;
icount = qemu_icount;
if (env) {
@@ -239,20 +238,6 @@ static void icount_warp_rt(void *opaque)
vm_clock_warp_start = -1;
}
void qtest_clock_warp(int64_t dest)
{
int64_t clock = qemu_get_clock_ns(vm_clock);
assert(qtest_enabled());
while (clock < dest) {
int64_t deadline = qemu_clock_deadline(vm_clock);
int64_t warp = MIN(dest - clock, deadline);
qemu_icount_bias += warp;
qemu_run_timers(vm_clock);
clock = qemu_get_clock_ns(vm_clock);
}
qemu_notify_event();
}
void qemu_clock_warp(QEMUClock *clock)
{
int64_t deadline;
@@ -279,11 +264,6 @@ void qemu_clock_warp(QEMUClock *clock)
return;
}
if (qtest_enabled()) {
/* When testing, qtest commands advance icount. */
return;
}
vm_clock_warp_start = qemu_get_clock_ns(rt_clock);
deadline = qemu_clock_deadline(vm_clock);
if (deadline > 0) {
@@ -301,7 +281,7 @@ void qemu_clock_warp(QEMUClock *clock)
* (related to the time left until the next event) has passed. This
* rt_clock timer will do this. This avoids that the warps are too
* visible externally---for example, you will not be sending network
* packets continuously instead of every 100ms.
* packets continously instead of every 100ms.
*/
qemu_mod_timer(icount_warp_timer, vm_clock_warp_start + deadline);
} else {
@@ -359,7 +339,7 @@ void configure_icount(const char *option)
void hw_error(const char *fmt, ...)
{
va_list ap;
CPUArchState *env;
CPUState *env;
va_start(ap, fmt);
fprintf(stderr, "qemu: hardware error: ");
@@ -379,7 +359,7 @@ void hw_error(const char *fmt, ...)
void cpu_synchronize_all_states(void)
{
CPUArchState *cpu;
CPUState *cpu;
for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
cpu_synchronize_state(cpu);
@@ -388,7 +368,7 @@ void cpu_synchronize_all_states(void)
void cpu_synchronize_all_post_reset(void)
{
CPUArchState *cpu;
CPUState *cpu;
for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
cpu_synchronize_post_reset(cpu);
@@ -397,14 +377,14 @@ void cpu_synchronize_all_post_reset(void)
void cpu_synchronize_all_post_init(void)
{
CPUArchState *cpu;
CPUState *cpu;
for (cpu = first_cpu; cpu; cpu = cpu->next_cpu) {
cpu_synchronize_post_init(cpu);
}
}
int cpu_is_stopped(CPUArchState *env)
int cpu_is_stopped(CPUState *env)
{
return !runstate_is_running() || env->stopped;
}
@@ -416,13 +396,13 @@ static void do_vm_stop(RunState state)
pause_all_vcpus();
runstate_set(state);
vm_state_notify(0, state);
bdrv_drain_all();
qemu_aio_flush();
bdrv_flush_all();
monitor_protocol_event(QEVENT_STOP, NULL);
}
}
static int cpu_can_run(CPUArchState *env)
static int cpu_can_run(CPUState *env)
{
if (env->stop) {
return 0;
@@ -433,7 +413,7 @@ static int cpu_can_run(CPUArchState *env)
return 1;
}
static bool cpu_thread_is_idle(CPUArchState *env)
static bool cpu_thread_is_idle(CPUState *env)
{
if (env->stop || env->queued_work_first) {
return false;
@@ -441,7 +421,8 @@ static bool cpu_thread_is_idle(CPUArchState *env)
if (env->stopped || !runstate_is_running()) {
return true;
}
if (!env->halted || qemu_cpu_has_work(env) || kvm_irqchip_in_kernel()) {
if (!env->halted || qemu_cpu_has_work(env) ||
(kvm_enabled() && kvm_irqchip_in_kernel())) {
return false;
}
return true;
@@ -449,7 +430,7 @@ static bool cpu_thread_is_idle(CPUArchState *env)
bool all_cpu_threads_idle(void)
{
CPUArchState *env;
CPUState *env;
for (env = first_cpu; env != NULL; env = env->next_cpu) {
if (!cpu_thread_is_idle(env)) {
@@ -459,7 +440,7 @@ bool all_cpu_threads_idle(void)
return true;
}
static void cpu_handle_guest_debug(CPUArchState *env)
static void cpu_handle_guest_debug(CPUState *env)
{
gdb_set_stop_cpu(env);
qemu_system_debug_request();
@@ -513,7 +494,7 @@ static void qemu_init_sigbus(void)
prctl(PR_MCE_KILL, PR_MCE_KILL_SET, PR_MCE_KILL_EARLY, 0, 0);
}
static void qemu_kvm_eat_signals(CPUArchState *env)
static void qemu_kvm_eat_signals(CPUState *env)
{
struct timespec ts = { 0, 0 };
siginfo_t siginfo;
@@ -556,7 +537,7 @@ static void qemu_init_sigbus(void)
{
}
static void qemu_kvm_eat_signals(CPUArchState *env)
static void qemu_kvm_eat_signals(CPUState *env)
{
}
#endif /* !CONFIG_LINUX */
@@ -566,7 +547,7 @@ static void dummy_signal(int sig)
{
}
static void qemu_kvm_init_cpu_signals(CPUArchState *env)
static void qemu_kvm_init_cpu_signals(CPUState *env)
{
int r;
sigset_t set;
@@ -584,6 +565,14 @@ static void qemu_kvm_init_cpu_signals(CPUArchState *env)
fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(-r));
exit(1);
}
sigdelset(&set, SIG_IPI);
sigdelset(&set, SIGBUS);
r = kvm_set_signal_mask(env, &set);
if (r) {
fprintf(stderr, "kvm_set_signal_mask: %s\n", strerror(-r));
exit(1);
}
}
static void qemu_tcg_init_cpu_signals(void)
@@ -601,7 +590,7 @@ static void qemu_tcg_init_cpu_signals(void)
}
#else /* _WIN32 */
static void qemu_kvm_init_cpu_signals(CPUArchState *env)
static void qemu_kvm_init_cpu_signals(CPUState *env)
{
abort();
}
@@ -638,7 +627,7 @@ void qemu_init_cpu_loop(void)
qemu_thread_get_self(&io_thread);
}
void run_on_cpu(CPUArchState *env, void (*func)(void *data), void *data)
void run_on_cpu(CPUState *env, void (*func)(void *data), void *data)
{
struct qemu_work_item wi;
@@ -660,14 +649,14 @@ void run_on_cpu(CPUArchState *env, void (*func)(void *data), void *data)
qemu_cpu_kick(env);
while (!wi.done) {
CPUArchState *self_env = cpu_single_env;
CPUState *self_env = cpu_single_env;
qemu_cond_wait(&qemu_work_cond, &qemu_global_mutex);
cpu_single_env = self_env;
}
}
static void flush_queued_work(CPUArchState *env)
static void flush_queued_work(CPUState *env)
{
struct qemu_work_item *wi;
@@ -684,7 +673,7 @@ static void flush_queued_work(CPUArchState *env)
qemu_cond_broadcast(&qemu_work_cond);
}
static void qemu_wait_io_event_common(CPUArchState *env)
static void qemu_wait_io_event_common(CPUState *env)
{
if (env->stop) {
env->stop = 0;
@@ -697,7 +686,7 @@ static void qemu_wait_io_event_common(CPUArchState *env)
static void qemu_tcg_wait_io_event(void)
{
CPUArchState *env;
CPUState *env;
while (all_cpu_threads_idle()) {
/* Start accounting real time to the virtual clock if the CPUs
@@ -715,7 +704,7 @@ static void qemu_tcg_wait_io_event(void)
}
}
static void qemu_kvm_wait_io_event(CPUArchState *env)
static void qemu_kvm_wait_io_event(CPUState *env)
{
while (cpu_thread_is_idle(env)) {
qemu_cond_wait(env->halt_cond, &qemu_global_mutex);
@@ -727,13 +716,12 @@ static void qemu_kvm_wait_io_event(CPUArchState *env)
static void *qemu_kvm_cpu_thread_fn(void *arg)
{
CPUArchState *env = arg;
CPUState *env = arg;
int r;
qemu_mutex_lock(&qemu_global_mutex);
qemu_thread_get_self(env->thread);
env->thread_id = qemu_get_thread_id();
cpu_single_env = env;
r = kvm_init_vcpu(env);
if (r < 0) {
@@ -760,53 +748,11 @@ static void *qemu_kvm_cpu_thread_fn(void *arg)
return NULL;
}
static void *qemu_dummy_cpu_thread_fn(void *arg)
{
#ifdef _WIN32
fprintf(stderr, "qtest is not supported under Windows\n");
exit(1);
#else
CPUArchState *env = arg;
sigset_t waitset;
int r;
qemu_mutex_lock_iothread();
qemu_thread_get_self(env->thread);
env->thread_id = qemu_get_thread_id();
sigemptyset(&waitset);
sigaddset(&waitset, SIG_IPI);
/* signal CPU creation */
env->created = 1;
qemu_cond_signal(&qemu_cpu_cond);
cpu_single_env = env;
while (1) {
cpu_single_env = NULL;
qemu_mutex_unlock_iothread();
do {
int sig;
r = sigwait(&waitset, &sig);
} while (r == -1 && (errno == EAGAIN || errno == EINTR));
if (r == -1) {
perror("sigwait");
exit(1);
}
qemu_mutex_lock_iothread();
cpu_single_env = env;
qemu_wait_io_event_common(env);
}
return NULL;
#endif
}
static void tcg_exec_all(void);
static void *qemu_tcg_cpu_thread_fn(void *arg)
{
CPUArchState *env = arg;
CPUState *env = arg;
qemu_tcg_init_cpu_signals();
qemu_thread_get_self(env->thread);
@@ -822,11 +768,6 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
/* wait for initial kick-off after machine start */
while (first_cpu->stopped) {
qemu_cond_wait(tcg_halt_cond, &qemu_global_mutex);
/* process any pending work */
for (env = first_cpu; env != NULL; env = env->next_cpu) {
qemu_wait_io_event_common(env);
}
}
while (1) {
@@ -840,7 +781,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg)
return NULL;
}
static void qemu_cpu_kick_thread(CPUArchState *env)
static void qemu_cpu_kick_thread(CPUState *env)
{
#ifndef _WIN32
int err;
@@ -852,19 +793,19 @@ static void qemu_cpu_kick_thread(CPUArchState *env)
}
#else /* _WIN32 */
if (!qemu_cpu_is_self(env)) {
SuspendThread(env->hThread);
SuspendThread(env->thread->thread);
cpu_signal(0);
ResumeThread(env->hThread);
ResumeThread(env->thread->thread);
}
#endif
}
void qemu_cpu_kick(void *_env)
{
CPUArchState *env = _env;
CPUState *env = _env;
qemu_cond_broadcast(env->halt_cond);
if (!tcg_enabled() && !env->thread_kicked) {
if (kvm_enabled() && !env->thread_kicked) {
qemu_cpu_kick_thread(env);
env->thread_kicked = true;
}
@@ -886,14 +827,14 @@ void qemu_cpu_kick_self(void)
int qemu_cpu_is_self(void *_env)
{
CPUArchState *env = _env;
CPUState *env = _env;
return qemu_thread_is_self(env->thread);
}
void qemu_mutex_lock_iothread(void)
{
if (!tcg_enabled()) {
if (kvm_enabled()) {
qemu_mutex_lock(&qemu_global_mutex);
} else {
iothread_requesting_mutex = true;
@@ -913,13 +854,13 @@ void qemu_mutex_unlock_iothread(void)
static int all_vcpus_paused(void)
{
CPUArchState *penv = first_cpu;
CPUState *penv = first_cpu;
while (penv) {
if (!penv->stopped) {
return 0;
}
penv = penv->next_cpu;
penv = (CPUState *)penv->next_cpu;
}
return 1;
@@ -927,25 +868,13 @@ static int all_vcpus_paused(void)
void pause_all_vcpus(void)
{
CPUArchState *penv = first_cpu;
CPUState *penv = first_cpu;
qemu_clock_enable(vm_clock, false);
while (penv) {
penv->stop = 1;
qemu_cpu_kick(penv);
penv = penv->next_cpu;
}
if (!qemu_thread_is_self(&io_thread)) {
cpu_stop_current();
if (!kvm_enabled()) {
while (penv) {
penv->stop = 0;
penv->stopped = 1;
penv = penv->next_cpu;
}
return;
}
penv = (CPUState *)penv->next_cpu;
}
while (!all_vcpus_paused()) {
@@ -953,27 +882,27 @@ void pause_all_vcpus(void)
penv = first_cpu;
while (penv) {
qemu_cpu_kick(penv);
penv = penv->next_cpu;
penv = (CPUState *)penv->next_cpu;
}
}
}
void resume_all_vcpus(void)
{
CPUArchState *penv = first_cpu;
CPUState *penv = first_cpu;
qemu_clock_enable(vm_clock, true);
while (penv) {
penv->stop = 0;
penv->stopped = 0;
qemu_cpu_kick(penv);
penv = penv->next_cpu;
penv = (CPUState *)penv->next_cpu;
}
}
static void qemu_tcg_init_vcpu(void *_env)
{
CPUArchState *env = _env;
CPUState *env = _env;
/* share a single thread for all cpus with TCG */
if (!tcg_cpu_thread) {
@@ -981,11 +910,7 @@ static void qemu_tcg_init_vcpu(void *_env)
env->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(env->halt_cond);
tcg_halt_cond = env->halt_cond;
qemu_thread_create(env->thread, qemu_tcg_cpu_thread_fn, env,
QEMU_THREAD_JOINABLE);
#ifdef _WIN32
env->hThread = qemu_thread_get_handle(env->thread);
#endif
qemu_thread_create(env->thread, qemu_tcg_cpu_thread_fn, env);
while (env->created == 0) {
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
}
@@ -996,25 +921,12 @@ static void qemu_tcg_init_vcpu(void *_env)
}
}
static void qemu_kvm_start_vcpu(CPUArchState *env)
static void qemu_kvm_start_vcpu(CPUState *env)
{
env->thread = g_malloc0(sizeof(QemuThread));
env->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(env->halt_cond);
qemu_thread_create(env->thread, qemu_kvm_cpu_thread_fn, env,
QEMU_THREAD_JOINABLE);
while (env->created == 0) {
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
}
}
static void qemu_dummy_start_vcpu(CPUArchState *env)
{
env->thread = g_malloc0(sizeof(QemuThread));
env->halt_cond = g_malloc0(sizeof(QemuCond));
qemu_cond_init(env->halt_cond);
qemu_thread_create(env->thread, qemu_dummy_cpu_thread_fn, env,
QEMU_THREAD_JOINABLE);
qemu_thread_create(env->thread, qemu_kvm_cpu_thread_fn, env);
while (env->created == 0) {
qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
}
@@ -1022,17 +934,15 @@ static void qemu_dummy_start_vcpu(CPUArchState *env)
void qemu_init_vcpu(void *_env)
{
CPUArchState *env = _env;
CPUState *env = _env;
env->nr_cores = smp_cores;
env->nr_threads = smp_threads;
env->stopped = 1;
if (kvm_enabled()) {
qemu_kvm_start_vcpu(env);
} else if (tcg_enabled()) {
qemu_tcg_init_vcpu(env);
} else {
qemu_dummy_start_vcpu(env);
qemu_tcg_init_vcpu(env);
}
}
@@ -1071,7 +981,7 @@ void vm_stop_force_state(RunState state)
}
}
static int tcg_cpu_exec(CPUArchState *env)
static int tcg_cpu_exec(CPUState *env)
{
int ret;
#ifdef CONFIG_PROFILER
@@ -1120,7 +1030,7 @@ static void tcg_exec_all(void)
next_cpu = first_cpu;
}
for (; next_cpu != NULL && !exit_request; next_cpu = next_cpu->next_cpu) {
CPUArchState *env = next_cpu;
CPUState *env = next_cpu;
qemu_clock_enable(vm_clock,
(env->singlestep_enabled & SSTEP_NOTIMER) == 0);
@@ -1140,7 +1050,7 @@ static void tcg_exec_all(void)
void set_numa_modes(void)
{
CPUArchState *env;
CPUState *env;
int i;
for (env = first_cpu; env != NULL; env = env->next_cpu) {
@@ -1186,7 +1096,7 @@ void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg)
CpuInfoList *qmp_query_cpus(Error **errp)
{
CpuInfoList *head = NULL, *cur_item = NULL;
CPUArchState *env;
CPUState *env;
for(env = first_cpu; env != NULL; env = env->next_cpu) {
CpuInfoList *info;
@@ -1226,97 +1136,3 @@ CpuInfoList *qmp_query_cpus(Error **errp)
return head;
}
void qmp_memsave(int64_t addr, int64_t size, const char *filename,
bool has_cpu, int64_t cpu_index, Error **errp)
{
FILE *f;
uint32_t l;
CPUArchState *env;
uint8_t buf[1024];
if (!has_cpu) {
cpu_index = 0;
}
for (env = first_cpu; env; env = env->next_cpu) {
if (cpu_index == env->cpu_index) {
break;
}
}
if (env == NULL) {
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "cpu-index",
"a CPU number");
return;
}
f = fopen(filename, "wb");
if (!f) {
error_set(errp, QERR_OPEN_FILE_FAILED, filename);
return;
}
while (size != 0) {
l = sizeof(buf);
if (l > size)
l = size;
cpu_memory_rw_debug(env, addr, buf, l, 0);
if (fwrite(buf, 1, l, f) != l) {
error_set(errp, QERR_IO_ERROR);
goto exit;
}
addr += l;
size -= l;
}
exit:
fclose(f);
}
void qmp_pmemsave(int64_t addr, int64_t size, const char *filename,
Error **errp)
{
FILE *f;
uint32_t l;
uint8_t buf[1024];
f = fopen(filename, "wb");
if (!f) {
error_set(errp, QERR_OPEN_FILE_FAILED, filename);
return;
}
while (size != 0) {
l = sizeof(buf);
if (l > size)
l = size;
cpu_physical_memory_rw(addr, buf, l, 0);
if (fwrite(buf, 1, l, f) != l) {
error_set(errp, QERR_IO_ERROR);
goto exit;
}
addr += l;
size -= l;
}
exit:
fclose(f);
}
void qmp_inject_nmi(Error **errp)
{
#if defined(TARGET_I386)
CPUArchState *env;
for (env = first_cpu; env != NULL; env = env->next_cpu) {
if (!env->apic_state) {
cpu_interrupt(env, CPU_INTERRUPT_NMI);
} else {
apic_deliver_nmi(env->apic_state);
}
}
#else
error_set(errp, QERR_UNSUPPORTED);
#endif
}

2
cpus.h
View File

@@ -11,8 +11,6 @@ void cpu_synchronize_all_states(void);
void cpu_synchronize_all_post_reset(void);
void cpu_synchronize_all_post_init(void);
void qtest_clock_warp(int64_t dest);
/* vl.c */
extern int smp_cores;
extern int smp_threads;

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