Compare commits

..

29 Commits

Author SHA1 Message Date
Anthony Liguori
472de0c851 Update version for 0.13.0-rc2
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-10-11 16:37:35 -05:00
Avi Kivity
0131c8c2dd Fix ivshmem build on 32-bit hosts
stat() fields can be more or less anything depending on configuration, cast
explicitly to uint64_t to avoid printf() format mismatches.

Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
(cherry picked from commit ad0a4ac1c0)
2010-10-11 16:34:38 -05:00
Jes Sorensen
d3c5b2e670 hw/ivshmem.c don't check for negative values on unsigned data types
There is no need to check for dest < 0 or vector >= 0 as both are
uint16_t.

This should fix problems with broken build with aggressive compiler
flags. Reported by Xudong Hao <xudong.hao@intel.com>

Signed-off-by: Jes Sorensen <Jes.Sorensen@redhat.com>
Acked-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
(cherry picked from commit 1b27d7a1e8)
2010-10-11 16:34:30 -05:00
Cam Macdonell
a385adb70e Disable build of ivshmem on non-KVM systems
Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
(cherry picked from commit 3dcbf8f9ca)
2010-10-11 16:34:02 -05:00
Cam Macdonell
41de2f0c86 Add kvm_set_ioeventfd_mmio_long definition for non-KVM systems
Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
(cherry picked from commit 1fd7401275)
2010-10-11 16:33:56 -05:00
Cam Macdonell
ec810f662a RESEND: Inter-VM shared memory PCI device
resend for bug fix related to removal of irqfd

Support an inter-vm shared memory device that maps a shared-memory object as a
PCI device in the guest.  This patch also supports interrupts between guest by
communicating over a unix domain socket.  This patch applies to the qemu-kvm
repository.

    -device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>]

Interrupts are supported between multiple VMs by using a shared memory server
by using a chardev socket.

    -device ivshmem,size=<size in format accepted by -m>[,shm=<shm name>]
           [,chardev=<id>][,msi=on][,ioeventfd=on][,vectors=n][,role=peer|master]
    -chardev socket,path=<path>,id=<id>

The shared memory server, sample programs and init scripts are in a git repo here:

    www.gitorious.org/nahanni

Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
(cherry picked from commit 6cbf4c8c64)
2010-10-11 16:33:42 -05:00
Cam Macdonell
9367dbbe6d Support marking a device as non-migratable
A non-migratable device should be removed before migration and re-added after.

Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
(cherry picked from commit 2431296806)
2010-10-11 16:33:32 -05:00
Cam Macdonell
089c672520 Add function to assign ioeventfd to MMIO.
Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
(cherry picked from commit 44f1a3d876)
2010-10-11 16:33:25 -05:00
Cam Macdonell
6f8d14beb2 Device specification for shared memory PCI device
Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
(cherry picked from commit b6828931eb)
2010-10-11 16:33:15 -05:00
Cam Macdonell
5b500f974b Add qemu_ram_alloc_from_ptr function
Provide a function to add an allocated region of memory to the qemu RAM.

This patch is copied from Marcelo's qemu_ram_map() in qemu-kvm and given the
clearer name qemu_ram_alloc_from_ptr().

Signed-off-by: Cam Macdonell <cam@cs.ualberta.ca>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
(cherry picked from commit 84b89d782f)
2010-10-11 16:33:09 -05:00
Anthony Liguori
72230c523b Update version for 0.13.0-rc1
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-08-31 08:19:23 -05:00
Andrew de Quincey
a9b56f8289 posix-aio-compat: Fix async_conmtext for ioctl
Set the async_context_id field when queuing an async ioctl call

Signed-off-by: Andrew de Quincey <adq@lidskialf.net>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 34cf008129)
2010-08-30 18:44:22 +02:00
Loïc Minier
f891f9f74d vvfat: fat_chksum(): fix access above array bounds
Signed-off-by: Loïc Minier <loic.minier@linaro.org>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 2aa326be0d)
2010-08-30 18:44:13 +02:00
Kevin Wolf
271a24e7bf qemu-img rebase: Open new backing file read-only
We never write to a backing file, so opening rw is useless. It just means that
you can't rebase on top of a file for which you don't have write permissions.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit cdbae85169)
2010-08-30 18:44:03 +02:00
Kevin Wolf
2c1064ed2d block: Fix image re-open in bdrv_commit
Arguably we should re-open the backing file with the backing file format and
not with the format of the snapshot image.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit ee1811965f)

Conflicts:

	block.c

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2010-08-30 18:42:15 +02:00
Kevin Wolf
55ee7b38e8 virtio-blk: Fix migration of queued requests
in_sg[].iovec and out_sg[].ioved are pointer to (source) host memory and
therefore invalid after migration. When loading the device state we must
create a new mapping on the destination host.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit b6a4805b55)
2010-08-30 18:38:36 +02:00
Kevin Wolf
6674dc4269 virtio: Factor virtqueue_map_sg out
Separate the mapping of requests to host memory from the descriptor iteration.
The next patch will make use of it in a different context.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 42fb2e0720)
2010-08-30 18:38:35 +02:00
Andrea Arcangeli
96638e706c ide: Avoid canceling IDE DMA
The reason for not actually canceling the I/O is because with
virtualization and lots of VM running, a guest fs may mistake a
overload of the host, as an IDE timeout. So rather than canceling the
I/O, it's safer to wait I/O completion and simulate that the I/O has
completed just before the io cancellation was requested by the
guest. This way if ntfs or an app writes data without checking for
-EIO retval, and it thinks the write has succeeded, it's less likely
to run into troubles. Similar issues for reads.

Furthermore because the DMA operation is splitted into many synchronous
aio_read/write if there's more than one entry in the SG table, without this
patch the DMA would be cancelled in the middle, something we've no idea if it
happens on real hardware too or not. Overall this seems a great risk for zero
gain.

This approach is sure safer than previous code given we can't pretend all guest
fs code out there to check for errors and reply the DMA if it was completed
partially, given a timeout would never materialize on a real harddisk unless
there are defective blocks (and defective blocks are practically only an issue
for reads never for writes in any recent hardware as writing to blocks is the
way to fix them) or the harddisk breaks as a whole.

Signed-off-by: Izik Eidus <ieidus@redhat.com>
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 953844d102)
2010-08-03 16:39:54 +02:00
Markus Armbruster
08e90b3cad block: Change bdrv_eject() not to drop the image
bdrv_eject() gets called when a device model opens or closes the tray.

If the block driver implements method bdrv_eject(), that method gets
called.  Drivers host_cdrom implements it, and it opens and closes the
physical tray, and nothing else.  When a device model opens, then
closes the tray, media changes only if the user actively changes the
physical media while the tray is open.  This is matches how physical
hardware behaves.

If the block driver doesn't implement method bdrv_eject(), we do
something quite different: opening the tray severs the connection to
the image by calling bdrv_close(), and closing the tray does nothing.
When the device model opens, then closes the tray, media is gone,
unless the user actively inserts another one while the tray is open,
with a suitable change command in the monitor.  This isn't how
physical hardware behaves.  Rather inconvenient when programs
"helpfully" eject media to give you a chance to change it.  The way
bdrv_eject() behaves here turns that chance into a must, which is not
what these programs or their users expect.

Change the default action not to call bdrv_close().  Instead, note the
tray status in new BlockDriverState member tray_open.  Use it in
bdrv_is_inserted().

Arguably, the device models should keep track of tray status
themselves.  But this is less invasive.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 4be9762adb)
2010-08-03 16:39:54 +02:00
Kevin Wolf
ada70b4522 block: Fix bdrv_has_zero_init
Assuming that any image on a block device is not properly zero-initialized is
actually wrong: Only raw images have this problem. Any other image format
shouldn't care about it, they initialize everything properly themselves.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 336c1c1255)
2010-08-03 16:39:53 +02:00
Alex Williamson
8f6e28789f savevm: Fix memory leak of compat struct
Forgot to check for and free these.

Found-by: Zachary Amsden <zamsden@redhat.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
(cherry picked from commit 69e58af92c)
2010-07-30 23:02:03 +02:00
Aurelien Jarno
e14aad448b linux-user: fix build on hosts not using guest base
Commit 68a1c81686 broke qemu on hosts not
using guest base. It uses reserved_va unconditionally in mmap.c. To
avoid to many #ifdef #endif blocks, define RESERVED_VA as either
reserved_va or 0ul, and use it instead of reserved_va, similarly to what
has been done with guest_base/GUEST_BASE.
(cherry picked from commit 18e9ea8a3f)
2010-07-30 21:12:59 +02:00
Blue Swirl
7829bc6c9f Fix -snapshot deleting images on disk change
Block device change command did not copy BDRV_O_SNAPSHOT flag. Thus
the new image did not have this flag and the file got deleted during
opening.

Fix by copying BDRV_O_SNAPSHOT flag.

Signed-off-by: Blue Swirl <blauwirbel@gmail.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 199630b62e)

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-28 14:04:25 -05:00
Stefan Weil
32b8bb3b3b block: Use error codes from lower levels for error message
"No such file or directory" is a misleading error message
when a user tries to open a file with wrong permissions.

Cc: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Weil <weil@mail.berlios.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit c98ac35d87)

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-28 14:04:24 -05:00
Christoph Hellwig
cc12b5c748 block: default to 0 minimal / optiomal I/O size
Currently we set them to 512 bytes unless manually specified.  Unforuntaly
some brain-dead partitioning tools create unaligned partitions if they
get low enough optiomal I/O size values, so don't report any at all
unless explicitly set.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 55459498b2)

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-28 14:04:24 -05:00
Bruce Rogers
50aa457e1d move 'unsafe' to end of caching modes in help
Libvirt parses qemu help output to determine qemu features. In particular
it probes for the following: "cache=writethrough|writeback|none". The
addition of the unsafe cache mode was inserted within this string, as
opposed to being added to the end, which impacted libvirt's probe.
Unbreak libvirt by keeping the existing cache modes intact and add
unsafe to the end.

This problem only manifests itself if a caching mode is explicitly
specified in the libvirt xml, in which case older syntax for caching is
passed to qemu, which it  no longer understands.

Signed-off-by: Bruce Rogers <brogers@novell.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 6c6b6ba20a)

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-28 14:04:24 -05:00
Alex Williamson
6546605650 virtio-blk: Create exit function to unregister savevm
Otherwise we can't migrate after we've removed a virtio block device.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 9d0d313859)

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-28 14:04:24 -05:00
Yoshiaki Tamura
42ccca964c block migration: propagate return value when bdrv_write() returns < 0
Currently block_load() doesn't check return value of bdrv_write(), and
even the destination weren't prepared to execute block migration, it
proceeds and guest boots on the target.  This patch fix this issue.

Signed-off-by: Yoshiaki Tamura <tamura.yoshiaki@lab.ntt.co.jp>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit b02bea3a85)

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-28 14:04:24 -05:00
Aurelien Jarno
966444248f ide/atapi: add support for GET EVENT STATUS NOTIFICATION
The GET EVENT STATUS NOTIFICATION is a mandatory command according
to MMC-3, even if event status notification is not supported.

This patch adds support for this command. It returns NEA ("No Event
Available") with an empty "Supported Event Classes" to show that it
doesn't event support status notification. If asychronous operation is
requested, which requires NCQ support, it returns an error according
to the specifications.

This fixes HAL support on FreeBSD and derivatives, which fill up the
logs every second with:

  acd0: FAILURE - unknown CMD (0x03) ILLEGAL REQUEST asc=0x20 ascq=0x00

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 253cb7b990)

Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
2010-07-28 14:04:24 -05:00
1194 changed files with 38467 additions and 147515 deletions

16
.gitignore vendored
View File

@@ -2,11 +2,6 @@ config-devices.*
config-all-devices.*
config-host.*
config-target.*
trace.h
trace.c
trace-dtrace.h
trace-dtrace.dtrace
*-timestamp
*-softmmu
*-darwin-user
*-linux-user
@@ -15,7 +10,6 @@ libdis*
libhw32
libhw64
libuser
qapi-generated
qemu-doc.html
qemu-tech.html
qemu-doc.info
@@ -33,7 +27,6 @@ qemu-options.texi
qemu-img-cmds.texi
qemu-img-cmds.h
qemu-io
qemu-ga
qemu-monitor.texi
QMP/qmp-commands.txt
.gdbinit
@@ -46,18 +39,12 @@ QMP/qmp-commands.txt
*.ky
*.log
*.pdf
*.cps
*.fns
*.kys
*.pg
*.pyc
*.toc
*.tp
*.vr
*.d
*.o
*.swp
*.orig
.pc
patches
pc-bios/bios-pq/status
@@ -67,6 +54,3 @@ pc-bios/optionrom/multiboot.bin
pc-bios/optionrom/multiboot.raw
.stgit-*
cscope.*
tags
TAGS
*~

6
.gitmodules vendored
View File

@@ -4,9 +4,3 @@
[submodule "roms/seabios"]
path = roms/seabios
url = git://git.qemu.org/seabios.git/
[submodule "roms/SLOF"]
path = roms/SLOF
url = git://git.qemu.org/SLOF.git
[submodule "roms/ipxe"]
path = roms/ipxe
url = git://git.qemu.org/ipxe.git

View File

@@ -1,9 +1,6 @@
Qemu Coding Style
=================
Please use the script checkpatch.pl in the scripts directory to check
patches before submitting.
1. Whitespace
Of course, the most important aspect in any coding style is whitespace.
@@ -49,6 +46,9 @@ 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.
Typedefs are used to eliminate the redundant 'struct' keyword. It is the
QEMU coding style.
When wrapping standard library functions, use the prefix qemu_ to alert
readers that they are seeing a wrapped version; otherwise avoid this prefix.

View File

@@ -1,9 +1,3 @@
This file documents changes for QEMU releases 0.12 and earlier.
For changelog information for later releases, see
http://wiki.qemu.org/ChangeLog or look at the git history for
more detailed information.
version 0.12.0:
- Update to SeaBIOS 0.5.0
@@ -531,7 +525,7 @@ version 0.1.5:
- ppc64 support + personality() patch (Rusty Russell)
- first Alpha CPU patches (Falk Hueffner)
- removed bfd.h dependency
- removed bfd.h dependancy
- fixed shrd, shld, idivl and divl on PowerPC.
- fixed buggy glibc PowerPC rint() function (test-i386 passes now on PowerPC).

122
HACKING
View File

@@ -1,122 +0,0 @@
1. Preprocessor
For variadic macros, stick with this C99-like syntax:
#define DPRINTF(fmt, ...) \
do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0)
2. C types
It should be common sense to use the right type, but we have collected
a few useful guidelines here.
2.1. Scalars
If you're using "int" or "long", odds are good that there's a better type.
If a variable is counting something, it should be declared with an
unsigned type.
If it's host memory-size related, size_t should be a good choice (use
ssize_t only if required). Guest RAM memory offsets must use ram_addr_t,
but only for RAM, it may not cover whole guest address space.
If it's file-size related, use off_t.
If it's file-offset related (i.e., signed), use off_t.
If it's just counting small numbers use "unsigned int";
(on all but oddball embedded systems, you can assume that that
type is at least four bytes wide).
In the event that you require a specific width, use a standard type
like int32_t, uint32_t, uint64_t, etc. The specific types are
mandatory for VMState fields.
Don't use Linux kernel internal types like u32, __u32 or __le32.
Use target_phys_addr_t for guest physical addresses except pcibus_t
for PCI addresses. In addition, ram_addr_t is a QEMU internal address
space that maps guest RAM physical addresses into an intermediate
address space that can map to host virtual address spaces. Generally
speaking, the size of guest memory can always fit into ram_addr_t but
it would not be correct to store an actual guest physical address in a
ram_addr_t.
Use target_ulong (or abi_ulong) for CPU virtual addresses, however
devices should not need to use target_ulong.
Of course, take all of the above with a grain of salt. If you're about
to use some system interface that requires a type like size_t, pid_t or
off_t, use matching types for any corresponding variables.
Also, if you try to use e.g., "unsigned int" as a type, and that
conflicts with the signedness of a related variable, sometimes
it's best just to use the *wrong* type, if "pulling the thread"
and fixing all related variables would be too invasive.
Finally, while using descriptive types is important, be careful not to
go overboard. If whatever you're doing causes warnings, or requires
casts, then reconsider or ask for help.
2.2. Pointers
Ensure that all of your pointers are "const-correct".
Unless a pointer is used to modify the pointed-to storage,
give it the "const" attribute. That way, the reader knows
up-front that this is a read-only pointer. Perhaps more
importantly, if we're diligent about this, when you see a non-const
pointer, you're guaranteed that it is used to modify the storage
it points to, or it is aliased to another pointer that is.
2.3. Typedefs
Typedefs are used to eliminate the redundant 'struct' keyword.
2.4. Reserved namespaces in C and POSIX
Underscore capital, double underscore, and underscore 't' suffixes should be
avoided.
3. Low level memory management
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 replacement qemu_malloc/qemu_mallocz/qemu_realloc/qemu_free or
qemu_vmalloc/qemu_memalign/qemu_vfree APIs.
Please note that NULL check for the qemu_malloc result is redundant and
that qemu_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
emulators.
4. String manipulation
Do not use the strncpy function. According to the man page, it does
*not* guarantee a NULL-terminated buffer, which makes it extremely dangerous
to use. Instead, use functionally equivalent function:
void pstrcpy(char *buf, int buf_size, const char *str)
Don't use strcat because it can't check for buffer overflows, but:
char *pstrcat(char *buf, int buf_size, const char *s)
The same limitation exists with sprintf and vsprintf, so use snprintf and
vsnprintf.
QEMU provides other useful string functions:
int strstart(const char *str, const char *val, const char **ptr)
int stristart(const char *str, const char *val, const char **ptr)
int qemu_strnlen(const char *s, int max_len)
There are also replacement character processing macros for isxyz and toxyz,
so instead of e.g. isalnum you should use qemu_isalnum.
Because of the memory management rules, you must use qemu_strdup/qemu_strndup
instead of plain strdup/strndup.
5. Printf-style functions
Whenever you add a new printf-style function, i.e., one with a format
string argument and following "..." in its prototype, be sure to use
gcc's printf attribute directive in the prototype.
This makes it so gcc's -Wformat and -Wformat-security options can do
their jobs and cross-check format strings with the number and types
of arguments.

View File

@@ -1,506 +1,88 @@
QEMU Maintainers
================
The intention of this file is not to establish who owns what portions of the
code base, but to provide a set of names that developers can consult when they
have a question about a particular subset and also to provide a set of names
to be CC'd when submitting a patch to obtain appropriate review.
In general, if you have a question about inclusion of a patch, you should
consult qemu-devel and not any specific individual privately.
Descriptions of section entries:
M: Mail patches to: FullName <address@domain>
L: Mailing list that is relevant to this area
W: Web-page with status/info
Q: Patchwork web based patch tracking system site
T: SCM tree type and location. Type is one of: git, hg, quilt, stgit.
S: Status, one of the following:
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..
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
it has been replaced by a better system and you
should be using that.
F: Files and directories with wildcard patterns.
A trailing slash includes all files and subdirectory files.
F: drivers/net/ all files in and below drivers/net
F: drivers/net/* all files in drivers/net, but not below
F: */net/* all files in "any top level directory"/net
One pattern per line. Multiple F: lines acceptable.
X: Files and directories that are NOT maintained, same rules as F:
Files exclusions are tested before file matches.
Can be useful for excluding a specific subdirectory, for instance:
F: net/
X: net/ipv6/
matches all files in and below net excluding net/ipv6/
K: Keyword perl extended regex pattern to match content in a
patch or file. For instance:
K: of_get_profile
matches patches or files that contain "of_get_profile"
K: \b(printk|pr_(info|err))\b
matches patches or files that contain one or more of the words
printk, pr_info or pr_err
One regex pattern per line. Multiple K: lines acceptable.
General Project Administration
------------------------------
M: Anthony Liguori <aliguori@us.ibm.com>
M: Paul Brook <paul@codesourcery.com>
Guest CPU cores (TCG):
----------------------
Alpha
M: Richard Henderson <rth@twiddle.net>
S: Maintained
F: target-alpha/
ARM
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: target-arm/
CRIS
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
S: Maintained
F: target-cris/
LM32
M: Michael Walle <michael@walle.cc>
S: Maintained
F: target-lm32/
M68K
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: target-m68k/
MicroBlaze
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
S: Maintained
F: target-microblaze/
MIPS
M: Aurelien Jarno <aurelien@aurel32.net>
S: Maintained
F: target-mips/
PowerPC
M: Alexander Graf <agraf@suse.de>
S: Maintained
F: target-ppc/
S390
M: Alexander Graf <agraf@suse.de>
S: Maintained
F: target-s390x/
SH4
M: Aurelien Jarno <aurelien@aurel32.net>
S: Maintained
F: target-sh4/
SPARC
M: Blue Swirl <blauwirbel@gmail.com>
S: Maintained
F: target-sparc/
X86
M: qemu-devel@nongnu.org
S: Odd Fixes
F: target-i386/
Guest CPU Cores (KVM):
----------------------
Overall
M: Avi Kivity <avi@redhat.com>
M: Marcelo Tosatti <mtosatti@redhat.com>
L: kvm@vger.kernel.org
S: Supported
F: kvm-*
F: */kvm.*
PPC
M: Alexander Graf <agraf@suse.de>
S: Maintained
F: target-ppc/kvm.c
S390
M: Alexander Graf <agraf@suse.de>
S: Maintained
F: target-s390x/kvm.c
X86
M: Avi Kivity <avi@redhat.com>
M: Marcelo Tosatti <mtosatti@redhat.com>
L: kvm@vger.kernel.org
S: Supported
F: target-i386/kvm.c
ARM Machines
------------
Gumstix
M: qemu-devel@nongnu.org
S: Orphan
F: hw/gumstix.c
Integrator CP
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: hw/integratorcp.c
Mainstone
M: qemu-devel@nongnu.org
S: Orphan
F: hw/mainstone.c
Musicpal
M: Jan Kiszka <jan.kiszka@web.de>
S: Maintained
F: hw/musicpal.c
nSeries
M: Andrzej Zaborowski <balrogg@gmail.com>
S: Maintained
F: hw/nseries.c
Palm
M: Andrzej Zaborowski <balrogg@gmail.com>
S: Maintained
F: hw/palm.c
Real View
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: hw/realview*
Spitz
M: Andrzej Zaborowski <balrogg@gmail.com>
S: Maintained
F: hw/spitz.c
Stellaris
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: hw/stellaris.c
Versatile PB
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: hw/versatilepb.c
CRIS Machines
-------------
Axis Dev88
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
S: Maintained
F: hw/axis_dev88.c
etraxfs
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
S: Maintained
F: hw/etraxfs.c
LM32 Machines
-------------
EVR32 and uclinux BSP
M: Michael Walle <michael@walle.cc>
S: Maintained
F: hw/lm32_boards.c
milkymist
M: Michael Walle <michael@walle.cc>
S: Maintained
F: hw/milkymist.c
M68K Machines
-------------
an5206
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: hw/an5206.c
dummy_m68k
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: hw/dummy_m68k.c
mcf5208
M: Paul Brook <paul@codesourcery.com>
S: Maintained
F: hw/mcf5208.c
MicroBlaze Machines
-------------------
petalogix_s3adsp1800
M: Edgar E. Iglesias <edgar.iglesias@gmail.com>
S: Maintained
F: hw/petalogix_s3adsp1800.c
MIPS Machines
-------------
Jazz
M: Hervé Poussineau <hpoussin@reactos.org>
S: Maintained
F: hw/mips_jazz.c
Malta
M: Aurelien Jarno <aurelien@aurel32.net>
S: Maintained
F: hw/mips_malta.c
Mipssim
M: qemu-devel@nongnu.org
S: Orphan
F: hw/mips_mipssim.c
R4000
M: Aurelien Jarno <aurelien@aurel32.net>
S: Maintained
F: hw/mips_r4k.c
PowerPC Machines
Project leaders:
----------------
405
M: Alexander Graf <agraf@suse.de>
S: Maintained
F: hw/ppc405_boards.c
New World
M: Alexander Graf <agraf@suse.de>
S: Maintained
F: hw/ppc_newworld.c
Fabrice Bellard
Paul Brook
Old World
M: Alexander Graf <agraf@suse.de>
S: Maintained
F: hw/ppc_oldworld.c
Prep
M: qemu-devel@nongnu.org
S: Orphan
F: hw/ppc_prep.c
SH4 Machines
------------
R2D
M: Magnus Damm <magnus.damm@gmail.com>
S: Maintained
F: hw/r2d.c
Shix
M: Magnus Damm <magnus.damm@gmail.com>
S: Orphan
F: hw/shix.c
SPARC Machines
--------------
Sun4m
M: Blue Swirl <blauwirbel@gmail.com>
S: Maintained
F: hw/sun4m.c
Sun4u
M: Blue Swirl <blauwirbel@gmail.com>
S: Maintained
F: hw/sun4u.c
S390 Machines
-------------
S390 Virtio
M: Alexander Graf <agraf@suse.de>
S: Maintained
F: hw/s390-*.c
X86 Machines
------------
PC
M: Anthony Liguori <aliguori@us.ibm.com>
S: Supported
F: hw/pc.[ch] hw/pc_piix.c
Devices
-------
IDE
M: Kevin Wolf <kwolf@redhat.com>
S: Odd Fixes
F: hw/ide/
PCI
M: Michael S. Tsirkin <mst@redhat.com>
S: Supported
F: hw/pci*
F: hw/piix*
SCSI
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>
S: Maintained
F: hw/usb*
vhost
M: Michael S. Tsirkin <mst@redhat.com>
S: Supported
F: hw/vhost*
virtio
M: Anthony Liguori <aliguori@us.ibm.com>
S: Supported
F: hw/virtio*
virtio-9p
M: Venkateswararao Jujjuri (JV) <jvrao@linux.vnet.ibm.com>
S: Supported
F: hw/virtio-9p*
virtio-blk
M: Kevin Wolf <kwolf@redhat.com>
S: Supported
F: hw/virtio-blk*
virtio-serial
M: Amit Shah <amit.shah@redhat.com>
S: Supported
F: hw/virtio-serial*
F: hw/virtio-console*
Subsystems
CPU cores:
----------
Audio
M: Vassili Karpov (malc) <av1474@comtv.ru>
S: Maintained
F: audio/
Block
M: Kevin Wolf <kwolf@redhat.com>
S: Supported
F: block*
F: block/
x86 Fabrice Bellard
ARM Paul Brook
SPARC Blue Swirl
MIPS ?
PowerPC ?
M68K Paul Brook
SH4 ?
CRIS Edgar E. Iglesias
Alpha ?
MicroBlaze Edgar E. Iglesias
S390 ?
Character Devices
M: Anthony Liguori <aliguori@us.ibm.com>
S: Maintained
F: qemu-char.c
GDB stub
M: qemu-devel@nongnu.org
S: Odd Fixes
F: gdbstub*
F: gdb-xml/
SPICE
M: Gerd Hoffmann <kraxel@redhat.com>
S: Supported
F: ui/qemu-spice.h
F: ui/spice-*.c
F: audio/spiceaudio.c
F: hw/qxl*
Graphics
M: Anthony Liguori <aliguori@us.ibm.com>
S: Maintained
F: ui/
Main loop
M: Anthony Liguori <aliguori@us.ibm.com>
S: Supported
F: vl.c
Monitor (QMP/HMP)
M: Luiz Capitulino <lcapitulino@redhat.com>
M: Markus Armbruster <armbru@redhat.com>
S: Supported
F: monitor.c
Network device layer
M: Anthony Liguori <aliguori@us.ibm.com>
M: Mark McLoughlin <markmc@redhat.com>
S: Maintained
F: net/
SLIRP
M: qemu-devel@nongnu.org
S: Orphan
F: slirp/
Usermode Emulation
------------------
BSD user
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
F: linux-user/
Tiny Code Generator (TCG)
Machines (sorted by CPU):
-------------------------
Common code
M: qemu-devel@nongnu.org
S: Maintained
F: tcg/
ARM target
M: Andrzej Zaborowski <balrogg@gmail.com>
S: Maintained
F: tcg/arm/
x86
pc.c Fabrice Bellard (new maintainer needed)
ARM
integratorcp.c Paul Brook
versatilepb.c Paul Brook
Real View Paul Brook
spitz.c Andrzej Zaborowski
palm.c Andrzej Zaborowski
nseries.c Andrzej Zaborowski
stellaris.c Paul Brook
gumstix.c Thorsten Zitterell
mainstone.c Armin Kuster
musicpal.c Jan Kiszka
SPARC
sun4u.c Blue Swirl
sun4m.c Blue Swirl
MIPS
mips_r4k.c Aurelien Jarno
mips_malta.c Aurelien Jarno
mips_jazz.c Hervé Poussineau
mips_mipssim.c ?
PowerPC
ppc_prep.c ?
ppc_oldworld.c Fabrice Bellard
ppc_chrp.c Fabrice Bellard
ppc405_boards.c ?
M86K
mcf5208.c Paul Brook
an5206.c Paul Brook
dummy_m68k.c Paul Brook
SH4
shix.c ?
r2d.c Magnus Damm
CRIS
etraxfs.c Edgar E. Iglesias
axis_dev88.c Edgar E. Iglesias
Alpha
MicroBlaze
petalogix_s3adsp1800.c Edgar E. Iglesias
S390
s390-*.c Alexander Graf
HPPA target
M: Richard Henderson <rth@twiddle.net>
S: Maintained
F: tcg/hppa/
Generic Subsystems:
-------------------
i386 target
M: qemu-devel@nongnu.org
S: Maintained
F: tcg/i386/
IA64 target
M: Aurelien Jarno <aurelien@aurel32.net>
S: Maintained
F: tcg/ia64/
MIPS target
M: Aurelien Jarno <aurelien@aurel32.net>
S: Maintained
F: tcg/mips/
PPC
M: Vassili Karpov (malc) <av1474@comtv.ru>
S: Maintained
F: tcg/ppc/
PPC64 target
M: Vassili Karpov (malc) <av1474@comtv.ru>
S: Maintained
F: tcg/ppc64/
S390 target
M: Alexander Graf <agraf@suse.de>
M: Richard Henderson <rth@twiddle.net>
S: Maintained
F: tcg/s390/
SPARC target
M: Blue Swirl <blauwirbel@gmail.com>
S: Maintained
F: tcg/sparc/
Dynamic translator Fabrice Bellard
Main loop Fabrice Bellard (new maintainer needed)
TCG Fabrice Bellard
IDE device ?
SCSI device Paul Brook
PCI layer Michael S. Tsirkin
USB layer ?
Block layer ?
Graphic layer ?
Audio device layer Vassili Karpov (malc)
Character device layer ?
Network device layer ?
GDB stub ?
Linux user ?
Darwin user ?
SLIRP ?

175
Makefile
View File

@@ -1,9 +1,6 @@
# Makefile for QEMU.
GENERATED_HEADERS = config-host.h trace.h qemu-options.def
ifeq ($(TRACE_BACKEND),dtrace)
GENERATED_HEADERS += trace-dtrace.h
endif
GENERATED_HEADERS = config-host.h
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
@@ -39,17 +36,14 @@ endif
SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory)
SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS))
SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %/config-devices.mak.d, $(TARGET_DIRS))
config-all-devices.mak: $(SUBDIR_DEVICES_MAK)
$(call quiet-command,cat $(SUBDIR_DEVICES_MAK) | grep =y | sort -u > $@," GEN $@")
-include $(SUBDIR_DEVICES_MAK_DEP)
%/config-devices.mak: default-configs/%.mak
$(call quiet-command,$(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $@ $<, " GEN $@")
$(call quiet-command,cat $< > $@.tmp, " GEN $@")
@if test -f $@; then \
if cmp -s $@.old $@; then \
if cmp -s $@.old $@ || cmp -s $@ $@.tmp; then \
mv $@.tmp $@; \
cp -p $@ $@.old; \
else \
@@ -75,8 +69,6 @@ build-all: $(DOCS) $(TOOLS) recurse-all
config-host.h: config-host.h-timestamp
config-host.h-timestamp: config-host.mak
qemu-options.def: $(SRC_PATH)/qemu-options.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS))
@@ -88,11 +80,9 @@ include $(SRC_PATH)/Makefile.objs
endif
$(common-obj-y): $(GENERATED_HEADERS)
subdir-libcacard: $(oslib-obj-y) $(trace-obj-y) qemu-malloc.o qemu-timer-common.o
$(filter %-softmmu,$(SUBDIR_RULES)): $(common-obj-y) subdir-libdis
$(filter %-softmmu,$(SUBDIR_RULES)): $(trace-obj-y) $(common-obj-y) subdir-libdis
$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) $(trace-obj-y) subdir-libdis-user subdir-libuser
$(filter %-user,$(SUBDIR_RULES)): $(GENERATED_HEADERS) subdir-libdis-user subdir-libuser
ROMSUBDIR_RULES=$(patsubst %,romsubdir-%, $(ROMS))
romsubdir-%:
@@ -106,8 +96,6 @@ audio/audio.o audio/fmodaudio.o: QEMU_CFLAGS += $(FMOD_CFLAGS)
QEMU_CFLAGS+=$(CURL_CFLAGS)
QEMU_CFLAGS+=$(GLIB_CFLAGS)
ui/cocoa.o: ui/cocoa.m
ui/sdl.o audio/sdlaudio.o ui/sdl_zoom.o baum.o: QEMU_CFLAGS += $(SDL_CFLAGS)
@@ -116,118 +104,48 @@ ui/vnc.o: QEMU_CFLAGS += $(VNC_TLS_CFLAGS)
bt-host.o: QEMU_CFLAGS += $(BLUEZ_CFLAGS)
version.o: $(SRC_PATH)/version.rc config-host.mak
$(call quiet-command,$(WINDRES) -I. -o $@ $<," RC $(TARGET_DIR)$@")
version-obj-$(CONFIG_WIN32) += version.o
######################################################################
# Support building shared library libcacard
.PHONY: libcacard.la install-libcacard
ifeq ($(LIBTOOL),)
libcacard.la:
@echo "libtool is missing, please install and rerun configure"; exit 1
install-libcacard:
@echo "libtool is missing, please install and rerun configure"; exit 1
else
libcacard.la: $(GENERATED_HEADERS) $(oslib-obj-y) qemu-malloc.o qemu-timer-common.o $(addsuffix .lo, $(basename $(trace-obj-y)))
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" libcacard.la,)
install-libcacard: libcacard.la
$(call quiet-command,$(MAKE) $(SUBDIR_MAKEFLAGS) -C libcacard V="$(V)" TARGET_DIR="$*/" install-libcacard,)
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)
qemu-img.o qemu-tool.o qemu-nbd.o qemu-io.o: $(GENERATED_HEADERS)
qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
qemu-img$(EXESUF): qemu-img.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
qemu-nbd$(EXESUF): qemu-nbd.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) qemu-timer-common.o
qemu-io$(EXESUF): qemu-io.o cmd.o qemu-tool.o qemu-error.o $(block-obj-y) $(qobject-obj-y)
qemu-img-cmds.h: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $@")
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $@")
check-qint.o check-qstring.o check-qdict.o check-qlist.o check-qfloat.o check-qjson.o: $(GENERATED_HEADERS)
CHECK_PROG_DEPS = qemu-malloc.o $(oslib-obj-y) $(trace-obj-y) qemu-tool.o
check-qint: check-qint.o qint.o $(CHECK_PROG_DEPS)
check-qstring: check-qstring.o qstring.o $(CHECK_PROG_DEPS)
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qlist.o $(CHECK_PROG_DEPS)
check-qlist: check-qlist.o qlist.o qint.o $(CHECK_PROG_DEPS)
check-qfloat: check-qfloat.o qfloat.o $(CHECK_PROG_DEPS)
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o error.o qerror.o qemu-error.o $(CHECK_PROG_DEPS)
$(qapi-obj-y): $(GENERATED_HEADERS)
qapi-dir := qapi-generated
test-visitor.o test-qmp-commands.o qemu-ga$(EXESUF): QEMU_CFLAGS += -I $(qapi-dir)
$(qapi-dir)/test-qapi-types.c: $(qapi-dir)/test-qapi-types.h
$(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
$(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
$(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
$(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 -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
$(qapi-dir)/qga-qapi-visit.c: $(qapi-dir)/qga-qapi-visit.h
$(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 -o "$(qapi-dir)" -p "qga-" < $<, " GEN $@")
$(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 -o "$(qapi-dir)" -p "qga-" < $<, " 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 qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(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 qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o $(qapi-obj-y) error.o osdep.o qemu-malloc.o $(oslib-obj-y) qjson.o json-streamer.o json-lexer.o json-parser.o qerror.o qemu-error.o qemu-tool.o $(qapi-dir)/test-qapi-visit.o $(qapi-dir)/test-qapi-types.o $(qapi-dir)/test-qmp-marshal.o module.o
QGALIB=qga/guest-agent-command-state.o qga/guest-agent-commands.o
QGALIB_GEN=$(addprefix $(qapi-dir)/, qga-qapi-types.c qga-qapi-types.h qga-qapi-visit.c qga-qmp-marshal.c)
$(QGALIB_GEN): $(GENERATED_HEADERS)
$(QGALIB) qemu-ga.o: $(QGALIB_GEN) $(qapi-obj-y)
qemu-ga$(EXESUF): qemu-ga.o $(QGALIB) qemu-tool.o qemu-error.o error.o $(oslib-obj-y) $(trace-obj-y) $(block-obj-y) $(qobject-obj-y) $(version-obj-y) $(qapi-obj-y) qemu-timer-common.o qemu-sockets.o module.o qapi/qmp-dispatch.o qapi/qmp-registry.o $(qapi-dir)/qga-qapi-visit.o $(qapi-dir)/qga-qapi-types.o $(qapi-dir)/qga-qmp-marshal.o
QEMULIBS=libhw32 libhw64 libuser libdis libdis-user
check-qint: check-qint.o qint.o qemu-malloc.o
check-qstring: check-qstring.o qstring.o qemu-malloc.o
check-qdict: check-qdict.o qdict.o qfloat.o qint.o qstring.o qbool.o qemu-malloc.o qlist.o
check-qlist: check-qlist.o qlist.o qint.o qemu-malloc.o
check-qfloat: check-qfloat.o qfloat.o qemu-malloc.o
check-qjson: check-qjson.o qfloat.o qint.o qdict.o qstring.o qlist.o qbool.o qjson.o json-streamer.o json-lexer.o json-parser.o qemu-malloc.o
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) 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 *.o *.d *.a $(TOOLS) TAGS cscope.* *.pod *~ */*~
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
rm -f qemu-img-cmds.h
rm -f trace.c trace.h trace.c-timestamp trace.h-timestamp
rm -f trace-dtrace.dtrace trace-dtrace.dtrace-timestamp
rm -f trace-dtrace.h trace-dtrace.h-timestamp
rm -rf $(qapi-dir)
$(MAKE) -C tests clean
for d in $(ALL_SUBDIRS) $(QEMULIBS) libcacard; do \
for d in $(ALL_SUBDIRS) libhw32 libhw64 libuser libdis libdis-user; do \
if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \
rm -f $$d/qemu-options.def; \
done
distclean: clean
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi
rm -f qemu-options.def
rm -f config-all-devices.mak
rm -f roms/seabios/config.mak roms/vgabios/config.mak
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps qemu-doc.dvi
rm -f qemu-doc.fn qemu-doc.fns qemu-doc.info qemu-doc.ky qemu-doc.kys
rm -f qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp
rm -f qemu-doc.vr
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.dvi qemu-doc.fn qemu-doc.info qemu-doc.ky qemu-doc.log qemu-doc.pdf qemu-doc.pg qemu-doc.toc qemu-doc.tp qemu-doc.vr
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 \
for d in $(TARGET_DIRS) libhw32 libhw64 libuser libdis libdis-user; do \
rm -rf $$d || exit 1 ; \
done
@@ -236,16 +154,16 @@ ar de en-us fi fr-be hr it lv nl pl ru th \
common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
ifdef INSTALL_BLOBS
BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin \
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 \
bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
mpc8544ds.dtb \
BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin ppc_rom.bin \
video.x openbios-sparc32 openbios-sparc64 openbios-ppc \
gpxe-eepro100-80861209.rom \
gpxe-eepro100-80861229.rom \
pxe-e1000.bin \
pxe-ne2k_pci.bin pxe-pcnet.bin \
pxe-rtl8139.bin pxe-virtio.bin \
bamboo.dtb petalogix-s3adsp1800.dtb \
multiboot.bin linuxboot.bin \
s390-zipl.rom \
spapr-rtas.bin slof.bin
s390-zipl.rom
else
BLOBS=
endif
@@ -314,32 +232,32 @@ TEXIFLAG=$(if $(V),,--quiet)
$(call quiet-command,texi2pdf $(TEXIFLAG) -I . $<," GEN $@")
qemu-options.texi: $(SRC_PATH)/qemu-options.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@")
qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
qemu-monitor.texi: $(SRC_PATH)/qemu-monitor.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@")
QMP/qmp-commands.txt: $(SRC_PATH)/qmp-commands.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -q < $< > $@," GEN $@")
QMP/qmp-commands.txt: $(SRC_PATH)/qemu-monitor.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -q < $< > $@," GEN $@")
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@," GEN $@")
$(call quiet-command,sh $(SRC_PATH)/hxtool -t < $< > $@," GEN $@")
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi
$(call quiet-command, \
perl -Ww -- $(SRC_PATH)/scripts/texi2pod.pl $< qemu.pod && \
perl -Ww -- $(SRC_PATH)/texi2pod.pl $< 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 && \
perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-img.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 && \
perl -Ww -- $(SRC_PATH)/texi2pod.pl $< qemu-nbd.pod && \
pod2man --section=8 --center=" " --release=" " qemu-nbd.pod > $@, \
" GEN $@")
@@ -384,15 +302,14 @@ tarbin:
$(datadir)/vgabios.bin \
$(datadir)/vgabios-cirrus.bin \
$(datadir)/ppc_rom.bin \
$(datadir)/video.x \
$(datadir)/openbios-sparc32 \
$(datadir)/openbios-sparc64 \
$(datadir)/openbios-ppc \
$(datadir)/pxe-e1000.rom \
$(datadir)/pxe-eepro100.rom \
$(datadir)/pxe-ne2k_pci.rom \
$(datadir)/pxe-pcnet.rom \
$(datadir)/pxe-rtl8139.rom \
$(datadir)/pxe-virtio.rom \
$(datadir)/pxe-ne2k_pci.bin \
$(datadir)/pxe-rtl8139.bin \
$(datadir)/pxe-pcnet.bin \
$(datadir)/pxe-e1000.bin \
$(docdir)/qemu-doc.html \
$(docdir)/qemu-tech.html \
$(mandir)/man1/qemu.1 \
@@ -400,4 +317,4 @@ tarbin:
$(mandir)/man8/qemu-nbd.8
# Include automatically generated dependency files
-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d qapi/*.d qga/*.d)
-include $(wildcard *.d audio/*.d slirp/*.d block/*.d net/*.d ui/*.d)

View File

@@ -2,31 +2,22 @@
# 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
#######################################################################
# oslib-obj-y is code depending on the OS (win32 vs posix)
oslib-obj-y = osdep.o
oslib-obj-$(CONFIG_WIN32) += oslib-win32.o qemu-thread-win32.o
oslib-obj-$(CONFIG_POSIX) += oslib-posix.o qemu-thread-posix.o
qobject-obj-y += qerror.o
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o async.o
block-obj-y += nbd.o block.o aio.o aes.o qemu-config.o qemu-progress.o qemu-sockets.o
block-obj-y = cutils.o cache-utils.o qemu-malloc.o qemu-option.o module.o
block-obj-y += nbd.o block.o aio.o aes.o osdep.o qemu-config.o
block-obj-$(CONFIG_POSIX) += posix-aio-compat.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block-nested-y += raw.o cow.o qcow.o vdi.o vmdk.o cloop.o dmg.o bochs.o vpc.o vvfat.o
block-nested-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o
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 += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o
block-nested-y += parallels.o nbd.o blkdebug.o sheepdog.o
block-nested-$(CONFIG_WIN32) += raw-win32.o
block-nested-$(CONFIG_POSIX) += raw-posix.o
block-nested-$(CONFIG_CURL) += curl.o
block-nested-$(CONFIG_RBD) += rbd.o
block-obj-y += $(addprefix block/, $(block-nested-y))
@@ -40,19 +31,11 @@ net-nested-$(CONFIG_WIN32) += tap-win32.o
net-nested-$(CONFIG_BSD) += tap-bsd.o
net-nested-$(CONFIG_SOLARIS) += tap-solaris.o
net-nested-$(CONFIG_AIX) += tap-aix.o
net-nested-$(CONFIG_HAIKU) += tap-haiku.o
net-nested-$(CONFIG_SLIRP) += slirp.o
net-nested-$(CONFIG_VDE) += vde.o
net-obj-y += $(addprefix net/, $(net-nested-y))
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
else
fsdev-nested-y = qemu-fsdev-dummy.o
endif
fsdev-nested-$(CONFIG_VIRTFS) = qemu-fsdev.o
fsdev-obj-$(CONFIG_VIRTFS) += $(addprefix fsdev/, $(fsdev-nested-y))
######################################################################
@@ -65,8 +48,7 @@ common-obj-y = $(block-obj-y) blockdev.o
common-obj-y += $(net-obj-y)
common-obj-y += $(qobject-obj-y)
common-obj-$(CONFIG_LINUX) += $(fsdev-obj-$(CONFIG_LINUX))
common-obj-y += readline.o console.o cursor.o qemu-error.o
common-obj-y += $(oslib-obj-y)
common-obj-y += readline.o console.o cursor.o async.o qemu-error.o
common-obj-$(CONFIG_WIN32) += os-win32.o
common-obj-$(CONFIG_POSIX) += os-posix.o
@@ -90,30 +72,24 @@ 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 += 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-y += usb-serial.o usb-net.o usb-bus.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 usb-bt.o
common-obj-y += bt-hci-csr.o
common-obj-y += buffered_file.o migration.o migration-tcp.o
common-obj-y += buffered_file.o migration.o migration-tcp.o qemu-sockets.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
common-obj-y += block-migration.o iohandler.o
common-obj-y += pflib.o
common-obj-y += bitmap.o bitops.o
common-obj-y += block-migration.o
common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
common-obj-$(CONFIG_WIN32) += version.o
common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o spice-qemu-char.o
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
audio-obj-$(CONFIG_SDL) += sdlaudio.o
audio-obj-$(CONFIG_OSS) += ossaudio.o
audio-obj-$(CONFIG_SPICE) += spiceaudio.o
audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o
audio-obj-$(CONFIG_ALSA) += alsaaudio.o
audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o
@@ -128,26 +104,24 @@ common-obj-y += $(addprefix audio/, $(audio-obj-y))
ui-obj-y += keymaps.o
ui-obj-$(CONFIG_SDL) += sdl.o sdl_zoom.o x_keymap.o
ui-obj-$(CONFIG_COCOA) += cocoa.o
ui-obj-$(CONFIG_CURSES) += curses.o
vnc-obj-y += vnc.o d3des.o
vnc-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
vnc-obj-y += vnc-enc-tight.o vnc-palette.o
vnc-obj-y += vnc-enc-zrle.o
vnc-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
vnc-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
ui-obj-y += vnc.o d3des.o
ui-obj-y += vnc-enc-zlib.o vnc-enc-hextile.o
ui-obj-y += vnc-enc-tight.o vnc-palette.o
ui-obj-$(CONFIG_VNC_TLS) += vnc-tls.o vnc-auth-vencrypt.o
ui-obj-$(CONFIG_VNC_SASL) += vnc-auth-sasl.o
ui-obj-$(CONFIG_COCOA) += cocoa.o
ifdef CONFIG_VNC_THREAD
vnc-obj-y += vnc-jobs-async.o
ui-obj-y += vnc-jobs-async.o
else
vnc-obj-y += vnc-jobs-sync.o
ui-obj-y += vnc-jobs-sync.o
endif
common-obj-y += $(addprefix ui/, $(ui-obj-y))
common-obj-$(CONFIG_VNC) += $(addprefix ui/, $(vnc-obj-y))
common-obj-y += iov.o acl.o
common-obj-$(CONFIG_POSIX) += compatfd.o
common-obj-$(CONFIG_THREAD) += qemu-thread.o
common-obj-y += notify.o event_notifier.o
common-obj-y += qemu-timer.o qemu-timer-common.o
common-obj-y += qemu-timer.o
slirp-obj-y = cksum.o if.o ip_icmp.o ip_input.o ip_output.o
slirp-obj-y += slirp.o mbuf.o misc.o sbuf.o socket.o tcp_input.o tcp_output.o
@@ -155,8 +129,8 @@ slirp-obj-y += tcp_subr.o tcp_timer.o udp.o bootp.o tftp.o
common-obj-$(CONFIG_SLIRP) += $(addprefix slirp/, $(slirp-obj-y))
# xen backend driver support
common-obj-$(CONFIG_XEN_BACKEND) += xen_backend.o xen_devconfig.o
common-obj-$(CONFIG_XEN_BACKEND) += xen_console.o xenfb.o xen_disk.o xen_nic.o
common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o
common-obj-$(CONFIG_XEN) += xen_console.o xenfb.o xen_disk.o xen_nic.o
######################################################################
# libuser
@@ -171,13 +145,8 @@ user-obj-y += cutils.o cache-utils.o
hw-obj-y =
hw-obj-y += vl.o loader.o
hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
hw-obj-y += fw_cfg.o
hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o
hw-obj-$(CONFIG_PCI) += msix.o msi.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 += virtio.o virtio-console.o
hw-obj-y += fw_cfg.o pci.o pci_host.o pcie_host.o
hw-obj-y += watchdog.o
hw-obj-$(CONFIG_ISA_MMIO) += isa_mmio.o
hw-obj-$(CONFIG_ECC) += ecc.o
@@ -195,17 +164,10 @@ hw-obj-$(CONFIG_I8254) += i8254.o
hw-obj-$(CONFIG_PCSPK) += pcspk.o
hw-obj-$(CONFIG_PCKBD) += pckbd.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_HPET) += hpet.o
hw-obj-$(CONFIG_APPLESMC) += applesmc.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-redir.o
# PPC devices
hw-obj-$(CONFIG_OPENPIC) += openpic.o
@@ -229,24 +191,21 @@ hw-obj-$(CONFIG_PPCE500_PCI) += ppce500_pci.o
hw-obj-$(CONFIG_PIIX4) += piix4.o
# PCI watchdog devices
hw-obj-$(CONFIG_PCI) += wdt_i6300esb.o
hw-obj-y += wdt_i6300esb.o
hw-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o
hw-obj-y += msix.o
# PCI network cards
hw-obj-$(CONFIG_NE2000_PCI) += ne2000.o
hw-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o
hw-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o
hw-obj-$(CONFIG_PCNET_COMMON) += pcnet.o
hw-obj-$(CONFIG_E1000_PCI) += e1000.o
hw-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o
hw-obj-y += ne2000.o
hw-obj-y += eepro100.o
hw-obj-y += pcnet.o
hw-obj-$(CONFIG_SMC91C111) += smc91c111.o
hw-obj-$(CONFIG_LAN9118) += lan9118.o
hw-obj-$(CONFIG_NE2000_ISA) += ne2000-isa.o
# IDE
hw-obj-$(CONFIG_IDE_CORE) += ide/core.o ide/atapi.o
hw-obj-$(CONFIG_IDE_CORE) += ide/core.o
hw-obj-$(CONFIG_IDE_QDEV) += ide/qdev.o
hw-obj-$(CONFIG_IDE_PCI) += ide/pci.o
hw-obj-$(CONFIG_IDE_ISA) += ide/isa.o
@@ -254,11 +213,9 @@ hw-obj-$(CONFIG_IDE_PIIX) += ide/piix.o
hw-obj-$(CONFIG_IDE_CMD646) += ide/cmd646.o
hw-obj-$(CONFIG_IDE_MACIO) += ide/macio.o
hw-obj-$(CONFIG_IDE_VIA) += ide/via.o
hw-obj-$(CONFIG_AHCI) += ide/ahci.o
hw-obj-$(CONFIG_AHCI) += ide/ich.o
# SCSI layer
hw-obj-$(CONFIG_LSI_SCSI_PCI) += lsi53c895a.o
hw-obj-y += lsi53c895a.o
hw-obj-$(CONFIG_ESP) += esp.o
hw-obj-y += dma-helpers.o sysbus.o isa-bus.o
@@ -269,7 +226,6 @@ hw-obj-$(CONFIG_VGA_PCI) += vga-pci.o
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_RC4030) += rc4030.o
hw-obj-$(CONFIG_DP8393X) += dp8393x.o
@@ -284,17 +240,11 @@ sound-obj-$(CONFIG_AC97) += ac97.o
sound-obj-$(CONFIG_ADLIB) += fmopl.o adlib.o
sound-obj-$(CONFIG_GUS) += gus.o gusemu_hal.o gusemu_mixer.o
sound-obj-$(CONFIG_CS4231A) += cs4231a.o
sound-obj-$(CONFIG_HDA) += intel-hda.o hda-audio.o
adlib.o fmopl.o: QEMU_CFLAGS += -DBUILD_Y8950=0
hw-obj-$(CONFIG_SOUND) += $(sound-obj-y)
9pfs-nested-$(CONFIG_VIRTFS) = virtio-9p.o virtio-9p-debug.o
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-local.o virtio-9p-xattr.o
9pfs-nested-$(CONFIG_VIRTFS) += virtio-9p-xattr-user.o virtio-9p-posix-acl.o
hw-obj-$(CONFIG_REALLY_VIRTFS) += $(addprefix 9pfs/, $(9pfs-nested-y))
hw-obj-$(CONFIG_VIRTFS) += virtio-9p-debug.o virtio-9p-local.o
######################################################################
# libdis
@@ -315,73 +265,14 @@ libdis-$(CONFIG_S390_DIS) += s390-dis.o
libdis-$(CONFIG_SH4_DIS) += sh4-dis.o
libdis-$(CONFIG_SPARC_DIS) += sparc-dis.o
######################################################################
# trace
ifeq ($(TRACE_BACKEND),dtrace)
trace.h: trace.h-timestamp trace-dtrace.h
else
trace.h: trace.h-timestamp
endif
trace.h-timestamp: $(SRC_PATH)/trace-events config-host.mak
$(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 config-host.mak
$(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)
trace-dtrace.h: trace-dtrace.dtrace
$(call quiet-command,dtrace -o $@ -h -s $<, " GEN trace-dtrace.h")
# Normal practice is to name DTrace probe file with a '.d' extension
# but that gets picked up by QEMU's Makefile as an external dependency
# rule file. So we use '.dtrace' instead
trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp
trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events config-host.mak
$(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")
ifeq ($(LIBTOOL),)
trace-dtrace.lo: trace-dtrace.dtrace
@echo "missing libtool. please install and rerun configure."; exit 1
else
trace-dtrace.lo: trace-dtrace.dtrace
$(call quiet-command,libtool --mode=compile --tag=CC dtrace -o $@ -G -s $<, " lt GEN trace-dtrace.o")
endif
simpletrace.o: simpletrace.c $(GENERATED_HEADERS)
ifeq ($(TRACE_BACKEND),dtrace)
trace-obj-y = trace-dtrace.o
else
trace-obj-y = trace.o
ifeq ($(TRACE_BACKEND),simple)
trace-obj-y += simpletrace.o
user-obj-y += qemu-timer-common.o
endif
endif
######################################################################
# smartcard
libcacard-y = cac.o event.o vcard.o vreader.o vcard_emul_nss.o vcard_emul_type.o card_7816.o
######################################################################
# qapi
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))
vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
vl.o: QEMU_CFLAGS+=$(SDL_CFLAGS)
vl.o: QEMU_CFLAGS+=$(GLIB_CFLAGS)
vl.o: qemu-options.def
os-posix.o: qemu-options.def
os-win32.o: qemu-options.def
qemu-options.def: $(SRC_PATH)/qemu-options.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")

View File

@@ -1,9 +1,7 @@
# -*- Mode: makefile -*-
GENERATED_HEADERS = config-target.h
CONFIG_NO_PCI = $(if $(subst n,,$(CONFIG_PCI)),n,y)
CONFIG_NO_KVM = $(if $(subst n,,$(CONFIG_KVM)),n,y)
CONFIG_NO_XEN = $(if $(subst n,,$(CONFIG_XEN)),n,y)
include ../config-host.mak
include config-devices.mak
@@ -15,10 +13,7 @@ endif
TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
$(call set-vpath, $(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw)
ifdef CONFIG_LINUX
QEMU_CFLAGS += -I../linux-headers
endif
QEMU_CFLAGS += -I.. -I$(TARGET_PATH) -DNEED_CPU_H
QEMU_CFLAGS+= -I.. -I$(TARGET_PATH) -DNEED_CPU_H
include $(SRC_PATH)/Makefile.objs
@@ -35,36 +30,15 @@ endif
endif
PROGS=$(QEMU_PROG)
STPFILES=
ifndef CONFIG_HAIKU
LIBS+=-lm
endif
kvm.o kvm-all.o vhost.o vhost_net.o: QEMU_CFLAGS+=$(KVM_CFLAGS)
config-target.h: config-target.h-timestamp
config-target.h-timestamp: config-target.mak
ifdef CONFIG_SYSTEMTAP_TRACE
stap: $(QEMU_PROG).stp
ifdef CONFIG_USER_ONLY
TARGET_TYPE=user
else
TARGET_TYPE=system
endif
$(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
all: $(PROGS) stap
all: $(PROGS)
# Dummy command so that make thinks it has done something
@true
@@ -73,7 +47,8 @@ all: $(PROGS) stap
# cpu emulator library
libobj-y = exec.o translate-all.o cpu-exec.o translate.o
libobj-y += tcg/tcg.o
libobj-y += fpu/softfloat.o
libobj-$(CONFIG_SOFTFLOAT) += fpu/softfloat.o
libobj-$(CONFIG_NOSOFTFLOAT) += fpu/softfloat-native.o
libobj-y += op_helper.o helper.o
ifeq ($(TARGET_BASE_ARCH), i386)
libobj-y += cpuid.o
@@ -95,10 +70,10 @@ tcg/tcg.o: cpu.h
# HELPER_CFLAGS is used for all the code compiled with static register
# variables
op_helper.o user-exec.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
op_helper.o cpu-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.
# cpu_signal_handler() in cpu-exec.c.
signal.o: QEMU_CFLAGS += $(HELPER_CFLAGS)
#########################################################
@@ -108,10 +83,10 @@ ifdef CONFIG_LINUX_USER
$(call set-vpath, $(SRC_PATH)/linux-user:$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR))
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) -I$(SRC_PATH)/linux-user
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)
obj-y = main.o syscall.o strace.o mmap.o signal.o thunk.o \
elfload.o linuxload.o uaccess.o gdbstub.o cpu-uname.o \
qemu-malloc.o user-exec.o $(oslib-obj-y)
qemu-malloc.o
obj-$(TARGET_HAS_BFLT) += flatload.o
@@ -149,7 +124,7 @@ 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
gdbstub.o
obj-i386-y += ioport-user.o
@@ -171,7 +146,7 @@ $(call set-vpath, $(SRC_PATH)/bsd-user)
QEMU_CFLAGS+=-I$(SRC_PATH)/bsd-user -I$(SRC_PATH)/bsd-user/$(TARGET_ARCH)
obj-y = main.o bsdload.o elfload.o mmap.o signal.o strace.o syscall.o \
gdbstub.o uaccess.o user-exec.o
gdbstub.o uaccess.o
obj-i386-y += ioport-user.o
@@ -190,11 +165,11 @@ ifdef CONFIG_SOFTMMU
obj-y = arch_init.o cpus.o monitor.o machine.o gdbstub.o balloon.o
# virtio has to be here due to weird dependency between PCI and virtio-net.
# 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-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-serial-bus.o
obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
obj-y += vhost_net.o
obj-$(CONFIG_VHOST_NET) += vhost.o
obj-$(CONFIG_REALLY_VIRTFS) += 9pfs/virtio-9p-device.o
obj-$(CONFIG_VIRTFS) += virtio-9p.o
obj-y += rwhandler.o
obj-$(CONFIG_KVM) += kvm.o kvm-all.o
obj-$(CONFIG_NO_KVM) += kvm-stub.o
@@ -204,33 +179,28 @@ 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
# xen backend driver support
obj-$(CONFIG_XEN) += xen_machine_pv.o xen_domainbuild.o
obj-i386-$(CONFIG_XEN) += xen_platform.o
# USB layer
obj-$(CONFIG_USB_OHCI) += usb-ohci.o
# PCI network cards
obj-y += rtl8139.o
obj-y += e1000.o
# Inter-VM PCI shared memory
CONFIG_IVSHMEM =
ifeq ($(CONFIG_KVM), y)
ifeq ($(CONFIG_PCI), y)
CONFIG_IVSHMEM = y
endif
endif
obj-$(CONFIG_IVSHMEM) += ivshmem.o
obj-$(CONFIG_KVM) += ivshmem.o
# Hardware support
obj-i386-y += vga.o
obj-i386-y += mc146818rtc.o i8259.o pc.o
obj-i386-y += cirrus_vga.o sga.o apic.o ioapic.o piix_pci.o
obj-i386-y += vmport.o
obj-i386-y += cirrus_vga.o apic.o ioapic.o piix_pci.o
obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.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-$(CONFIG_KVM) += kvmclock.o
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
# shared objects
obj-ppc-y = ppc.o
@@ -242,49 +212,14 @@ obj-ppc-y += ppc_prep.o
obj-ppc-y += ppc_oldworld.o
# NewWorld PowerMac
obj-ppc-y += ppc_newworld.o
# IBM pSeries (sPAPR)
ifeq ($(CONFIG_FDT)$(TARGET_PPC64),yy)
obj-ppc-y += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
obj-ppc-y += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
endif
# PowerPC 4xx boards
obj-ppc-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
obj-ppc-y += ppc440.o ppc440_bamboo.o
# PowerPC E500 boards
obj-ppc-y += ppce500_mpc8544ds.o mpc8544_guts.o
# PowerPC 440 Xilinx ML507 reference board.
obj-ppc-y += virtex_ml507.o
obj-ppc-y += ppce500_mpc8544ds.o
obj-ppc-$(CONFIG_KVM) += kvm_ppc.o
obj-ppc-$(CONFIG_FDT) += device_tree.o
# Xilinx PPC peripherals
obj-ppc-y += xilinx_intc.o
obj-ppc-y += xilinx_timer.o
obj-ppc-y += xilinx_uartlite.o
obj-ppc-y += xilinx_ethlite.o
# LM32 boards
obj-lm32-y += lm32_boards.o
obj-lm32-y += milkymist.o
# LM32 peripherals
obj-lm32-y += lm32_pic.o
obj-lm32-y += lm32_juart.o
obj-lm32-y += lm32_timer.o
obj-lm32-y += lm32_uart.o
obj-lm32-y += lm32_sys.o
obj-lm32-y += milkymist-ac97.o
obj-lm32-y += milkymist-hpdmc.o
obj-lm32-y += milkymist-memcard.o
obj-lm32-y += milkymist-minimac2.o
obj-lm32-y += milkymist-pfpu.o
obj-lm32-y += milkymist-softusb.o
obj-lm32-y += milkymist-sysctl.o
obj-lm32-$(CONFIG_OPENGL) += milkymist-tmu2.o
obj-lm32-y += milkymist-uart.o
obj-lm32-y += milkymist-vgafb.o
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 i8259.o
@@ -294,15 +229,12 @@ 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_pic_cpu.o
obj-microblaze-y += xilinx_intc.o
obj-microblaze-y += xilinx_timer.o
obj-microblaze-y += xilinx_uartlite.o
obj-microblaze-y += xilinx_ethlite.o
obj-microblaze-y += xilinx_axidma.o
obj-microblaze-y += xilinx_axienet.o
obj-microblaze-$(CONFIG_FDT) += device_tree.o
@@ -327,10 +259,7 @@ 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
obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o leon3.o
# GRLIB
obj-sparc-y += grlib_gptimer.o grlib_irqmp.o grlib_apbuart.o
obj-sparc-y += cs4231.o eccmemctl.o sbi.o sun4c_intctl.o
endif
obj-arm-y = integratorcp.o versatilepb.o arm_pic.o arm_timer.o
@@ -356,9 +285,6 @@ 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-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
@@ -369,12 +295,11 @@ obj-m68k-y += m68k-semi.o dummy_m68k.o
obj-s390x-y = s390-virtio-bus.o s390-virtio.o
obj-alpha-y = i8259.o mc146818rtc.o
obj-alpha-y += vga.o cirrus_vga.o
obj-alpha-y = alpha_palcode.o
main.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
monitor.o: hmp-commands.h qmp-commands.h
monitor.o: qemu-monitor.h
$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y): $(GENERATED_HEADERS)
@@ -385,46 +310,26 @@ obj-y += $(addprefix $(HWDIR)/, $(hw-obj-y))
endif # CONFIG_SOFTMMU
ifndef CONFIG_LINUX_USER
# libcacard needs qemu-thread support, and besides is only needed by devices
# so not requires with linux-user targets
obj-$(CONFIG_SMARTCARD_NSS) += $(addprefix ../libcacard/, $(libcacard-y))
endif # CONFIG_LINUX_USER
obj-y += $(addprefix ../, $(trace-obj-y))
obj-$(CONFIG_GDBSTUB_XML) += gdbstub-xml.o
$(QEMU_PROG): $(obj-y) $(obj-$(TARGET_BASE_ARCH)-y)
$(call LINK,$(obj-y) $(obj-$(TARGET_BASE_ARCH)-y))
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)$@")
gdbstub-xml.c: $(TARGET_XML_FILES) $(SRC_PATH)/feature_to_c.sh
$(call quiet-command,rm -f $@ && $(SHELL) $(SRC_PATH)/feature_to_c.sh $@ $(TARGET_XML_FILES)," GEN $(TARGET_DIR)$@")
hmp-commands.h: $(SRC_PATH)/hmp-commands.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
qmp-commands.h: $(SRC_PATH)/qmp-commands.hx
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
qemu-monitor.h: $(SRC_PATH)/qemu-monitor.hx
$(call quiet-command,sh $(SRC_PATH)/hxtool -h < $< > $@," GEN $(TARGET_DIR)$@")
clean:
rm -f *.o *.a *~ $(PROGS) nwfpe/*.o fpu/*.o
rm -f *.d */*.d tcg/*.o ide/*.o 9pfs/*.o
rm -f hmp-commands.h qmp-commands.h gdbstub-xml.c
ifdef CONFIG_SYSTEMTAP_TRACE
rm -f *.stp
endif
rm -f *.d */*.d tcg/*.o ide/*.o
rm -f qemu-monitor.h gdbstub-xml.c
install: all
ifneq ($(PROGS),)
$(INSTALL) -m 755 $(PROGS) "$(DESTDIR)$(bindir)"
ifneq ($(STRIP),)
$(STRIP) $(patsubst %,"$(DESTDIR)$(bindir)/%",$(PROGS))
endif
endif
ifdef CONFIG_SYSTEMTAP_TRACE
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/../systemtap/tapset"
$(INSTALL_DATA) $(QEMU_PROG).stp "$(DESTDIR)$(datadir)/../systemtap/tapset"
$(INSTALL) -m 755 $(STRIP_OPT) $(PROGS) "$(DESTDIR)$(bindir)"
endif
# Include automatically generated dependency files

View File

@@ -7,82 +7,60 @@ Introduction
The QEMU Monitor Protocol (QMP) allows applications to communicate with
QEMU's Monitor.
QMP is JSON[1] based and currently has the following features:
QMP is JSON[1] based and has the following features:
- Lightweight, text-based, easy to parse data format
- Asynchronous messages support (ie. events)
- Capabilities Negotiation
- Asynchronous events support
- Stability
For detailed information on QMP's usage, please, refer to the following files:
For more information, please, refer to the following files:
o qmp-spec.txt QEMU Monitor Protocol current specification
o qmp-commands.txt QMP supported commands (auto-generated at build-time)
o qmp-commands.txt QMP supported commands
o qmp-events.txt List of available asynchronous events
There is also a simple Python script called 'qmp-shell' available.
IMPORTANT: It's strongly recommended to read the 'Stability Considerations'
section in the qmp-commands.txt file before making any serious use of QMP.
There are also two simple Python scripts available:
o qmp-shell A shell
o vm-info Show some information about the Virtual Machine
[1] http://www.json.org
Usage
-----
To enable QMP, you need a QEMU monitor instance in "control mode". There are
two ways of doing this.
To enable QMP, QEMU has to be started in "control mode". There are
two ways of doing this, the simplest one is using the the '-qmp'
command-line option.
The simplest one is using the '-qmp' command-line option. The following
example makes QMP available on localhost port 4444:
For example:
$ qemu [...] -qmp tcp:localhost:4444,server
$ qemu [...] -qmp tcp:localhost:4444,server
However, in order to have more complex combinations, like multiple monitors,
the '-mon' command-line option should be used along with the '-chardev' one.
For instance, the following example creates one user monitor on stdio and one
QMP monitor on localhost port 4444.
Will start QEMU in control mode, waiting for a client TCP connection
on localhost port 4444.
$ qemu [...] -chardev stdio,id=mon0 -mon chardev=mon0,mode=readline \
-chardev socket,id=mon1,host=localhost,port=4444,server \
-mon chardev=mon1,mode=control
Please, refer to QEMU's manpage for more information.
It is also possible to use the '-mon' command-line option to have
more complex combinations. Please, refer to the QEMU's manpage for
more information.
Simple Testing
--------------
To manually test QMP one can connect with telnet and issue commands by hand:
To manually test QMP one can connect with telnet and issue commands:
$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
{"QMP": {"version": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}, "capabilities": []}}
{"QMP": {"version": {"qemu": "0.12.50", "package": ""}, "capabilities": []}}
{ "execute": "qmp_capabilities" }
{"return": {}}
{ "execute": "query-version" }
{"return": {"qemu": {"micro": 50, "minor": 13, "major": 0}, "package": ""}}
{"return": {"qemu": "0.12.50", "package": ""}}
Development Process
-------------------
Contact
-------
When changing QMP's interface (by adding new commands, events or modifying
existing ones) it's mandatory to update the relevant documentation, which is
one (or more) of the files listed in the 'Introduction' section*.
Also, it's strongly recommended to send the documentation patch first, before
doing any code change. This is so because:
1. Avoids the code dictating the interface
2. Review can improve your interface. Letting that happen before
you implement it can save you work.
* The qmp-commands.txt file is generated from the qmp-commands.hx one, which
is the file that should be edited.
Homepage
--------
http://wiki.qemu.org/QMP
http://www.linux-kvm.org/page/MonitorProtocol
Luiz Fernando N. Capitulino <lcapitulino@redhat.com>

View File

@@ -89,7 +89,7 @@ Data: None.
Example:
{ "event": "STOP",
{ "event": "SHUTDOWN",
"timestamp": { "seconds": 1267041730, "microseconds": 281295 } }
VNC_CONNECTED
@@ -182,70 +182,6 @@ Example:
"host": "127.0.0.1", "sasl_username": "luiz" } },
"timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
SPICE_CONNECTED, SPICE_DISCONNECTED
-----------------------------------
Emitted when a SPICE client connects or disconnects.
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "client": Client information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
Example:
{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
"event": "SPICE_CONNECTED",
"data": {
"server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
"client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
}}
SPICE_INITIALIZED
-----------------
Emitted after initial handshake and authentication takes place (if any)
and the SPICE channel is up'n'running
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "auth": authentication method (json-string, optional)
- "client": Client information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "connection-id": spice connection id. All channels with the same id
belong to the same spice session (json-int)
- "channel-type": channel type. "1" is the main control channel, filter for
this one if you want track spice sessions only (json-int)
- "channel-id": channel id. Usually "0", might be different needed when
multiple channels of the same type exist, such as multiple
display channels in a multihead setup (json-int)
- "tls": whevener the channel is encrypted (json-bool)
Example:
{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
"event": "SPICE_INITIALIZED",
"data": {"server": {"auth": "spice", "port": "5921",
"family": "ipv4", "host": "127.0.0.1"},
"client": {"port": "49004", "family": "ipv4", "channel-type": 3,
"connection-id": 1804289383, "host": "127.0.0.1",
"channel-id": 0, "tls": true}
}}
WATCHDOG
--------

View File

@@ -1,8 +1,8 @@
#!/usr/bin/python
#
# Low-level QEMU shell on top of QMP.
# Simple QEMU shell on top of QMP
#
# Copyright (C) 2009, 2010 Red Hat Inc.
# Copyright (C) 2009 Red Hat Inc.
#
# Authors:
# Luiz Capitulino <lcapitulino@redhat.com>
@@ -14,246 +14,60 @@
#
# Start QEMU with:
#
# # qemu [...] -qmp unix:./qmp-sock,server
# $ qemu [...] -monitor control,unix:./qmp,server
#
# Run the shell:
#
# $ qmp-shell ./qmp-sock
# $ qmp-shell ./qmp
#
# Commands have the following format:
#
# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
# < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
#
# For example:
#
# (QEMU) device_add driver=e1000 id=net1
# {u'return': {}}
# (QEMU)
# (QEMU) info item=network
import qmp
import readline
import sys
from sys import argv,exit
class QMPCompleter(list):
def complete(self, text, state):
for cmd in self:
if cmd.startswith(text):
if not state:
return cmd
else:
state -= 1
class QMPShellError(Exception):
pass
class QMPShellBadPort(QMPShellError):
pass
# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
# _execute_cmd()). Let's design a better one.
class QMPShell(qmp.QEMUMonitorProtocol):
def __init__(self, address):
qmp.QEMUMonitorProtocol.__init__(self, self.__get_address(address))
self._greeting = None
self._completer = None
def __get_address(self, arg):
"""
Figure out if the argument is in the port:host form, if it's not it's
probably a file path.
"""
addr = arg.split(':')
if len(addr) == 2:
try:
port = int(addr[1])
except ValueError:
raise QMPShellBadPort
return ( addr[0], port )
# socket path
return arg
def _fill_completion(self):
for cmd in self.cmd('query-commands')['return']:
self._completer.append(cmd['name'])
def __completer_setup(self):
self._completer = QMPCompleter()
self._fill_completion()
readline.set_completer(self._completer.complete)
readline.parse_and_bind("tab: complete")
# XXX: default delimiters conflict with some command names (eg. query-),
# clearing everything as it doesn't seem to matter
readline.set_completer_delims('')
def __build_cmd(self, cmdline):
"""
Build a QMP input object from a user provided command-line in the
following format:
< command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
"""
cmdargs = cmdline.split()
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
for arg in cmdargs[1:]:
opt = arg.split('=')
try:
value = int(opt[1])
except ValueError:
value = opt[1]
qmpcmd['arguments'][opt[0]] = value
return qmpcmd
def _execute_cmd(self, cmdline):
try:
qmpcmd = self.__build_cmd(cmdline)
except:
print 'command format: <command-name> ',
print '[arg-name1=arg1] ... [arg-nameN=argN]'
return True
resp = self.cmd_obj(qmpcmd)
if resp is None:
print 'Disconnected'
return False
print resp
return True
def connect(self):
self._greeting = qmp.QEMUMonitorProtocol.connect(self)
self.__completer_setup()
def show_banner(self, msg='Welcome to the QMP low-level shell!'):
print msg
version = self._greeting['QMP']['version']['qemu']
print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
def read_exec_command(self, prompt):
"""
Read and execute a command.
@return True if execution was ok, return False if disconnected.
"""
try:
cmdline = raw_input(prompt)
except EOFError:
print
return False
if cmdline == '':
for ev in self.get_events():
print ev
self.clear_events()
return True
else:
return self._execute_cmd(cmdline)
class HMPShell(QMPShell):
def __init__(self, address):
QMPShell.__init__(self, address)
self.__cpu_index = 0
def __cmd_completion(self):
for cmd in self.__cmd_passthrough('help')['return'].split('\r\n'):
if cmd and cmd[0] != '[' and cmd[0] != '\t':
name = cmd.split()[0] # drop help text
if name == 'info':
continue
if name.find('|') != -1:
# Command in the form 'foobar|f' or 'f|foobar', take the
# full name
opt = name.split('|')
if len(opt[0]) == 1:
name = opt[1]
else:
name = opt[0]
self._completer.append(name)
self._completer.append('help ' + name) # help completion
def __info_completion(self):
for cmd in self.__cmd_passthrough('info')['return'].split('\r\n'):
if cmd:
self._completer.append('info ' + cmd.split()[1])
def __other_completion(self):
# special cases
self._completer.append('help info')
def _fill_completion(self):
self.__cmd_completion()
self.__info_completion()
self.__other_completion()
def __cmd_passthrough(self, cmdline, cpu_index = 0):
return self.cmd_obj({ 'execute': 'human-monitor-command', 'arguments':
{ 'command-line': cmdline,
'cpu-index': cpu_index } })
def _execute_cmd(self, cmdline):
if cmdline.split()[0] == "cpu":
# trap the cpu command, it requires special setting
try:
idx = int(cmdline.split()[1])
if not 'return' in self.__cmd_passthrough('info version', idx):
print 'bad CPU index'
return True
self.__cpu_index = idx
except ValueError:
print 'cpu command takes an integer argument'
return True
resp = self.__cmd_passthrough(cmdline, self.__cpu_index)
if resp is None:
print 'Disconnected'
return False
assert 'return' in resp or 'error' in resp
if 'return' in resp:
# Success
if len(resp['return']) > 0:
print resp['return'],
else:
# Error
print '%s: %s' % (resp['error']['class'], resp['error']['desc'])
return True
def show_banner(self):
QMPShell.show_banner(self, msg='Welcome to the HMP shell!')
def die(msg):
sys.stderr.write('ERROR: %s\n' % msg)
sys.exit(1)
def fail_cmdline(option=None):
if option:
sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
sys.stderr.write('qemu-shell [ -H ] < UNIX socket path> | < TCP address:port >\n')
sys.exit(1)
def shell_help():
print 'bye exit from the shell'
def main():
addr = ''
try:
if len(sys.argv) == 2:
qemu = QMPShell(sys.argv[1])
addr = sys.argv[1]
elif len(sys.argv) == 3:
if sys.argv[1] != '-H':
fail_cmdline(sys.argv[1])
qemu = HMPShell(sys.argv[2])
addr = sys.argv[2]
if len(argv) != 2:
print 'qemu-shell <unix-socket>'
exit(1)
qemu = qmp.QEMUMonitorProtocol(argv[1])
qemu.connect()
qemu.send("qmp_capabilities")
print 'Connected!'
while True:
try:
cmd = raw_input('(QEMU) ')
except EOFError:
print
break
if cmd == '':
continue
elif cmd == 'bye':
break
elif cmd == 'help':
shell_help()
else:
fail_cmdline()
except QMPShellBadPort:
die('bad port number in command-line')
try:
qemu.connect()
except qmp.QMPConnectError:
die('Didn\'t get QMP greeting message')
except qmp.QMPCapabilitiesError:
die('Could not negotiate capabilities')
except qemu.error:
die('Could not connect to %s' % addr)
qemu.show_banner()
while qemu.read_exec_command('(QEMU) '):
pass
qemu.close()
try:
resp = qemu.send(cmd)
if resp == None:
print 'Disconnected'
break
print resp
except IndexError:
print '-> command format: <command-name> ',
print '[arg-name1=arg1] ... [arg-nameN=argN]'
if __name__ == '__main__':
main()

View File

@@ -1,6 +1,6 @@
# QEMU Monitor Protocol Python class
#
# Copyright (C) 2009, 2010 Red Hat Inc.
# Copyright (C) 2009 Red Hat Inc.
#
# Authors:
# Luiz Capitulino <lcapitulino@redhat.com>
@@ -8,9 +8,7 @@
# This work is licensed under the terms of the GNU GPL, version 2. See
# the COPYING file in the top-level directory.
import json
import errno
import socket
import socket, json
class QMPError(Exception):
pass
@@ -18,140 +16,61 @@ class QMPError(Exception):
class QMPConnectError(QMPError):
pass
class QMPCapabilitiesError(QMPError):
pass
class QEMUMonitorProtocol:
def __init__(self, address, server=False):
"""
Create a QEMUMonitorProtocol class.
@param address: QEMU address, can be either a unix socket path (string)
or a tuple in the form ( address, port ) for a TCP
connection
@param server: server mode listens on the socket (bool)
@raise socket.error on socket connection errors
@note No connection is established, this is done by the connect() or
accept() methods
"""
self.__events = []
self.__address = address
self.__sock = self.__get_sock()
if server:
self.__sock.bind(self.__address)
self.__sock.listen(1)
def __get_sock(self):
if isinstance(self.__address, tuple):
family = socket.AF_INET
else:
family = socket.AF_UNIX
return socket.socket(family, socket.SOCK_STREAM)
def __negotiate_capabilities(self):
self.__sockfile = self.__sock.makefile()
greeting = self.__json_read()
if greeting is None or not greeting.has_key('QMP'):
raise QMPConnectError
# Greeting seems ok, negotiate capabilities
resp = self.cmd('qmp_capabilities')
if "return" in resp:
return greeting
raise QMPCapabilitiesError
def __json_read(self, only_event=False):
while True:
data = self.__sockfile.readline()
if not data:
return
resp = json.loads(data)
if 'event' in resp:
self.__events.append(resp)
if not only_event:
continue
return resp
error = socket.error
def connect(self):
"""
Connect to the QMP Monitor and perform capabilities negotiation.
@return QMP greeting dict
@raise socket.error on socket connection errors
@raise QMPConnectError if the greeting is not received
@raise QMPCapabilitiesError if fails to negotiate capabilities
"""
self.__sock.connect(self.__address)
return self.__negotiate_capabilities()
def accept(self):
"""
Await connection from QMP Monitor and perform capabilities negotiation.
@return QMP greeting dict
@raise socket.error on socket connection errors
@raise QMPConnectError if the greeting is not received
@raise QMPCapabilitiesError if fails to negotiate capabilities
"""
self.__sock, _ = self.__sock.accept()
return self.__negotiate_capabilities()
def cmd_obj(self, qmp_cmd):
"""
Send a QMP command to the QMP Monitor.
@param qmp_cmd: QMP command to be sent as a Python dict
@return QMP response as a Python dict or None if the connection has
been closed
"""
try:
self.__sock.sendall(json.dumps(qmp_cmd))
except socket.error, err:
if err[0] == errno.EPIPE:
return
raise socket.error(err)
return self.__json_read()
def cmd(self, name, args=None, id=None):
"""
Build a QMP command and send it to the QMP Monitor.
@param name: command name (string)
@param args: command arguments (dict)
@param id: command id (dict, list, string or int)
"""
qmp_cmd = { 'execute': name }
if args:
qmp_cmd['arguments'] = args
if id:
qmp_cmd['id'] = id
return self.cmd_obj(qmp_cmd)
def get_events(self, wait=False):
"""
Get a list of available QMP events.
@param wait: block until an event is available (bool)
"""
self.__sock.setblocking(0)
try:
self.__json_read()
except socket.error, err:
if err[0] == errno.EAGAIN:
# No data available
pass
self.__sock.setblocking(1)
if not self.__events and wait:
self.__json_read(only_event=True)
return self.__events
def clear_events(self):
"""
Clear current list of pending events.
"""
self.__events = []
self.sock.connect(self.filename)
data = self.__json_read()
if data == None:
raise QMPConnectError
if not data.has_key('QMP'):
raise QMPConnectError
return data['QMP']['capabilities']
def close(self):
self.__sock.close()
self.__sockfile.close()
self.sock.close()
def send_raw(self, line):
self.sock.send(str(line))
return self.__json_read()
def send(self, cmdline):
cmd = self.__build_cmd(cmdline)
self.__json_send(cmd)
resp = self.__json_read()
if resp == None:
return
elif resp.has_key('error'):
return resp['error']
else:
return resp['return']
def __build_cmd(self, cmdline):
cmdargs = cmdline.split()
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
for arg in cmdargs[1:]:
opt = arg.split('=')
try:
value = int(opt[1])
except ValueError:
value = opt[1]
qmpcmd['arguments'][opt[0]] = value
return qmpcmd
def __json_send(self, cmd):
# XXX: We have to send any additional char, otherwise
# the Server won't read our input
self.sock.send(json.dumps(cmd) + ' ')
def __json_read(self):
try:
while True:
line = json.loads(self.sockfile.readline())
if not 'event' in line:
return line
except ValueError:
return
def __init__(self, filename):
self.filename = filename
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sockfile = self.sock.makefile()

33
QMP/vm-info Executable file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/python
#
# Print Virtual Machine information
#
# Usage:
#
# Start QEMU with:
#
# $ qemu [...] -monitor control,unix:./qmp,server
#
# Run vm-info:
#
# $ vm-info ./qmp
#
# Luiz Capitulino <lcapitulino@redhat.com>
import qmp
from sys import argv,exit
def main():
if len(argv) != 2:
print 'vm-info <unix-socket>'
exit(1)
qemu = qmp.QEMUMonitorProtocol(argv[1])
qemu.connect()
qemu.send("qmp_capabilities")
for cmd in [ 'version', 'kvm', 'status', 'uuid', 'balloon' ]:
print cmd + ': ' + str(qemu.send('query-' + cmd))
if __name__ == '__main__':
main()

View File

@@ -1 +1 @@
0.15.1
0.12.92

1
acl.c
View File

@@ -24,6 +24,7 @@
#include "qemu-common.h"
#include "sysemu.h"
#include "acl.h"
#ifdef CONFIG_FNMATCH

View File

@@ -22,9 +22,6 @@ along with this file; see the file COPYING. If not, see
#include <stdio.h>
#include "dis-asm.h"
/* MAX is redefined below, so remove any previous definition. */
#undef MAX
/* The opcode table is an array of struct alpha_opcode. */
struct alpha_opcode
@@ -238,6 +235,10 @@ extern const unsigned alpha_num_operands;
#define AXP_REG_SP 30
#define AXP_REG_ZERO 31
#define bfd_mach_alpha_ev4 0x10
#define bfd_mach_alpha_ev5 0x20
#define bfd_mach_alpha_ev6 0x30
enum bfd_reloc_code_real {
BFD_RELOC_23_PCREL_S2,
BFD_RELOC_ALPHA_HINT

View File

@@ -23,7 +23,6 @@
*/
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#ifndef _WIN32
#include <sys/types.h>
#include <sys/mman.h>
@@ -64,8 +63,6 @@ const char arch_config_name[] = CONFIG_QEMU_CONFDIR "/target-" TARGET_ARCH ".con
#define QEMU_ARCH QEMU_ARCH_I386
#elif defined(TARGET_M68K)
#define QEMU_ARCH QEMU_ARCH_M68K
#elif defined(TARGET_LM32)
#define QEMU_ARCH QEMU_ARCH_LM32
#elif defined(TARGET_MICROBLAZE)
#define QEMU_ARCH QEMU_ARCH_MICROBLAZE
#elif defined(TARGET_MIPS)
@@ -85,12 +82,12 @@ const uint32_t arch_type = QEMU_ARCH;
/***********************************************************/
/* ram save/restore */
#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */
#define RAM_SAVE_FLAG_COMPRESS 0x02
#define RAM_SAVE_FLAG_MEM_SIZE 0x04
#define RAM_SAVE_FLAG_PAGE 0x08
#define RAM_SAVE_FLAG_EOS 0x10
#define RAM_SAVE_FLAG_CONTINUE 0x20
#define RAM_SAVE_FLAG_FULL 0x01 /* Obsolete, not used anymore */
#define RAM_SAVE_FLAG_COMPRESS 0x02
#define RAM_SAVE_FLAG_MEM_SIZE 0x04
#define RAM_SAVE_FLAG_PAGE 0x08
#define RAM_SAVE_FLAG_EOS 0x10
#define RAM_SAVE_FLAG_CONTINUE 0x20
static int is_dup_page(uint8_t *page, uint8_t ch)
{
@@ -107,11 +104,10 @@ static int is_dup_page(uint8_t *page, uint8_t ch)
return 1;
}
static RAMBlock *last_block;
static ram_addr_t last_offset;
static int ram_save_block(QEMUFile *f)
{
static RAMBlock *last_block = NULL;
static ram_addr_t last_offset = 0;
RAMBlock *block = last_block;
ram_addr_t offset = last_offset;
ram_addr_t current_addr;
@@ -215,39 +211,6 @@ uint64_t ram_bytes_total(void)
return total;
}
static int block_compar(const void *a, const void *b)
{
RAMBlock * const *ablock = a;
RAMBlock * const *bblock = b;
if ((*ablock)->offset < (*bblock)->offset) {
return -1;
} else if ((*ablock)->offset > (*bblock)->offset) {
return 1;
}
return 0;
}
static void sort_ram_list(void)
{
RAMBlock *block, *nblock, **blocks;
int n;
n = 0;
QLIST_FOREACH(block, &ram_list.blocks, next) {
++n;
}
blocks = qemu_malloc(n * sizeof *blocks);
n = 0;
QLIST_FOREACH_SAFE(block, &ram_list.blocks, next, nblock) {
blocks[n++] = block;
QLIST_REMOVE(block, next);
}
qsort(blocks, n, sizeof *blocks, block_compar);
while (--n >= 0) {
QLIST_INSERT_HEAD(&ram_list.blocks, blocks[n], next);
}
qemu_free(blocks);
}
int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
{
ram_addr_t addr;
@@ -268,9 +231,6 @@ int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque)
if (stage == 1) {
RAMBlock *block;
bytes_transferred = 0;
last_block = NULL;
last_offset = 0;
sort_ram_list();
/* Make sure all dirty bits are set */
QLIST_FOREACH(block, &ram_list.blocks, next) {
@@ -427,16 +387,13 @@ int ram_load(QEMUFile *f, void *opaque, int version_id)
host = qemu_get_ram_ptr(addr);
else
host = host_from_stream_offset(f, addr, flags);
if (!host) {
return -EINVAL;
}
ch = qemu_get_byte(f);
memset(host, ch, TARGET_PAGE_SIZE);
#ifndef _WIN32
if (ch == 0 &&
(!kvm_enabled() || kvm_has_sync_mmu())) {
qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED);
madvise(host, TARGET_PAGE_SIZE, MADV_DONTNEED);
}
#endif
} else if (flags & RAM_SAVE_FLAG_PAGE) {
@@ -463,18 +420,7 @@ void qemu_service_io(void)
}
#ifdef HAS_AUDIO
struct soundhw {
const char *name;
const char *descr;
int enabled;
int isa;
union {
int (*init_isa) (qemu_irq *pic);
int (*init_pci) (PCIBus *bus);
} init;
};
static struct soundhw soundhw[] = {
struct soundhw soundhw[] = {
#ifdef HAS_AUDIO_CHOICE
#if defined(TARGET_I386) || defined(TARGET_MIPS)
{
@@ -550,16 +496,6 @@ static struct soundhw soundhw[] = {
},
#endif
#ifdef CONFIG_HDA
{
"hda",
"Intel HD Audio",
0,
0,
{ .init_pci = intel_hda_and_codec_init }
},
#endif
#endif /* HAS_AUDIO_CHOICE */
{ NULL, NULL, 0, 0, { NULL } }
@@ -623,32 +559,10 @@ void select_soundhw(const char *optarg)
}
}
}
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_pic) {
c->init.init_isa(isa_pic);
}
} else {
if (pci_bus) {
c->init.init_pci(pci_bus);
}
}
}
}
}
#else
void select_soundhw(const char *optarg)
{
}
void audio_init(qemu_irq *isa_pic, PCIBus *pci_bus)
{
}
#endif
int qemu_uuid_parse(const char *str, uint8_t *uuid)
@@ -709,11 +623,6 @@ int audio_available(void)
#endif
}
int tcg_available(void)
{
return 1;
}
int kvm_available(void)
{
#ifdef CONFIG_KVM

View File

@@ -10,24 +10,23 @@ enum {
QEMU_ARCH_CRIS = 4,
QEMU_ARCH_I386 = 8,
QEMU_ARCH_M68K = 16,
QEMU_ARCH_LM32 = 32,
QEMU_ARCH_MICROBLAZE = 64,
QEMU_ARCH_MIPS = 128,
QEMU_ARCH_PPC = 256,
QEMU_ARCH_S390X = 512,
QEMU_ARCH_SH4 = 1024,
QEMU_ARCH_SPARC = 2048,
QEMU_ARCH_MICROBLAZE = 32,
QEMU_ARCH_MIPS = 64,
QEMU_ARCH_PPC = 128,
QEMU_ARCH_S390X = 256,
QEMU_ARCH_SH4 = 512,
QEMU_ARCH_SPARC = 1024,
};
extern const uint32_t arch_type;
void select_soundhw(const char *optarg);
int ram_save_live(Monitor *mon, QEMUFile *f, int stage, void *opaque);
int ram_load(QEMUFile *f, void *opaque, int version_id);
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(qemu_irq *isa_pic, PCIBus *pci_bus);
int tcg_available(void);
int kvm_available(void);
int xen_available(void);

View File

@@ -1587,7 +1587,7 @@ arm_decode_bitfield (const char *ptr, unsigned long insn,
}
static void
arm_decode_shift (long given, fprintf_function func, void *stream,
arm_decode_shift (long given, fprintf_ftype func, void *stream,
int print_shift)
{
func (stream, "%s", arm_regnames[given & 0xf]);
@@ -1633,7 +1633,7 @@ print_insn_coprocessor (bfd_vma pc, struct disassemble_info *info, long given,
{
const struct opcode32 *insn;
void *stream = info->stream;
fprintf_function func = info->fprintf_func;
fprintf_ftype func = info->fprintf_func;
unsigned long mask;
unsigned long value;
int cond;
@@ -2127,7 +2127,7 @@ static void
print_arm_address (bfd_vma pc, struct disassemble_info *info, long given)
{
void *stream = info->stream;
fprintf_function func = info->fprintf_func;
fprintf_ftype func = info->fprintf_func;
if (((given & 0x000f0000) == 0x000f0000)
&& ((given & 0x02000000) == 0))
@@ -2222,7 +2222,7 @@ print_insn_neon (struct disassemble_info *info, long given, bfd_boolean thumb)
{
const struct opcode32 *insn;
void *stream = info->stream;
fprintf_function func = info->fprintf_func;
fprintf_ftype func = info->fprintf_func;
if (thumb)
{
@@ -2676,7 +2676,7 @@ print_insn_arm_internal (bfd_vma pc, struct disassemble_info *info, long given)
{
const struct opcode32 *insn;
void *stream = info->stream;
fprintf_function func = info->fprintf_func;
fprintf_ftype func = info->fprintf_func;
if (print_insn_coprocessor (pc, info, given, false))
return;
@@ -3036,7 +3036,7 @@ print_insn_thumb16 (bfd_vma pc, struct disassemble_info *info, long given)
{
const struct opcode16 *insn;
void *stream = info->stream;
fprintf_function func = info->fprintf_func;
fprintf_ftype func = info->fprintf_func;
for (insn = thumb_opcodes; insn->assembler; insn++)
if ((given & insn->mask) == insn->value)
@@ -3312,7 +3312,7 @@ print_insn_thumb32 (bfd_vma pc, struct disassemble_info *info, long given)
{
const struct opcode32 *insn;
void *stream = info->stream;
fprintf_function func = info->fprintf_func;
fprintf_ftype func = info->fprintf_func;
if (print_insn_coprocessor (pc, info, given, true))
return;
@@ -4101,30 +4101,6 @@ print_insn_arm (bfd_vma pc, struct disassemble_info *info)
addresses, since the addend is not currently pc-relative. */
pc = 0;
/* We include the hexdump of the instruction. The format here
matches that used by objdump and the ARM ARM (in particular,
32 bit Thumb instructions are displayed as pairs of halfwords,
not as a single word.) */
if (is_thumb)
{
if (size == 2)
{
info->fprintf_func(info->stream, "%04lx ",
((unsigned long)given) & 0xffff);
}
else
{
info->fprintf_func(info->stream, "%04lx %04lx ",
(((unsigned long)given) >> 16) & 0xffff,
((unsigned long)given) & 0xffff);
}
}
else
{
info->fprintf_func(info->stream, "%08lx ",
((unsigned long)given) & 0xffffffff);
}
printer (pc, info, given);
if (is_thumb)

View File

@@ -33,8 +33,8 @@
#define ARM_ANGEL_HEAP_SIZE (128 * 1024 * 1024)
#else
#include "qemu-common.h"
#include "sysemu.h"
#include "gdbstub.h"
#include "hw/arm-misc.h"
#endif
#define SYS_OPEN 0x01
@@ -370,88 +370,49 @@ uint32_t do_arm_semihosting(CPUState *env)
return syscall_err;
#endif
case SYS_GET_CMDLINE:
#ifdef CONFIG_USER_ONLY
/* Build a commandline from the original argv. */
{
/* Build a command-line from the original argv.
*
* The inputs are:
* * ARG(0), pointer to a buffer of at least the size
* specified in ARG(1).
* * ARG(1), size of the buffer pointed to by ARG(0) in
* bytes.
*
* The outputs are:
* * ARG(0), pointer to null-terminated string of the
* command line.
* * ARG(1), length of the string pointed to by ARG(0).
*/
char **arg = ts->info->host_argv;
int len = ARG(1);
/* lock the buffer on the ARM side */
char *cmdline_buffer = (char*)lock_user(VERIFY_WRITE, ARG(0), len, 0);
char *output_buffer;
size_t input_size = ARG(1);
size_t output_size;
int status = 0;
if (!cmdline_buffer)
/* FIXME - should this error code be -TARGET_EFAULT ? */
return (uint32_t)-1;
/* Compute the size of the output string. */
#if !defined(CONFIG_USER_ONLY)
output_size = strlen(ts->boot_info->kernel_filename)
+ 1 /* Separating space. */
+ strlen(ts->boot_info->kernel_cmdline)
+ 1; /* Terminating null byte. */
#else
unsigned int i;
s = cmdline_buffer;
while (*arg && len > 2) {
int n = strlen(*arg);
output_size = ts->info->arg_end - ts->info->arg_start;
if (!output_size) {
/* We special-case the "empty command line" case (argc==0).
Just provide the terminating 0. */
output_size = 1;
}
#endif
if (output_size > input_size) {
/* Not enough space to store command-line arguments. */
return -1;
}
/* Adjust the command-line length. */
SET_ARG(1, output_size - 1);
/* Lock the buffer on the ARM side. */
output_buffer = lock_user(VERIFY_WRITE, ARG(0), output_size, 0);
if (!output_buffer) {
return -1;
}
/* Copy the command-line arguments. */
#if !defined(CONFIG_USER_ONLY)
pstrcpy(output_buffer, output_size, ts->boot_info->kernel_filename);
pstrcat(output_buffer, output_size, " ");
pstrcat(output_buffer, output_size, ts->boot_info->kernel_cmdline);
#else
if (output_size == 1) {
/* Empty command-line. */
output_buffer[0] = '\0';
goto out;
}
if (copy_from_user(output_buffer, ts->info->arg_start,
output_size)) {
status = -1;
goto out;
}
/* Separate arguments by white spaces. */
for (i = 0; i < output_size - 1; i++) {
if (output_buffer[i] == 0) {
output_buffer[i] = ' ';
if (s != cmdline_buffer) {
*(s++) = ' ';
len--;
}
if (n >= len)
n = len - 1;
memcpy(s, *arg, n);
s += n;
len -= n;
arg++;
}
out:
#endif
/* Unlock the buffer on the ARM side. */
unlock_user(output_buffer, ARG(0), output_size);
/* Null terminate the string. */
*s = 0;
len = s - cmdline_buffer;
return status;
/* Unlock the buffer on the ARM side. */
unlock_user(cmdline_buffer, ARG(0), len);
/* Adjust the commandline length argument. */
SET_ARG(1, len);
/* Return success if commandline fit into buffer. */
return *arg ? -1 : 0;
}
#else
return -1;
#endif
case SYS_HEAPINFO:
{
uint32_t *ptr;
@@ -461,16 +422,15 @@ uint32_t do_arm_semihosting(CPUState *env)
/* Some C libraries assume the heap immediately follows .bss, so
allocate it using sbrk. */
if (!ts->heap_limit) {
abi_ulong ret;
long ret;
ts->heap_base = do_brk(0);
limit = ts->heap_base + ARM_ANGEL_HEAP_SIZE;
/* Try a big heap, and reduce the size if that fails. */
for (;;) {
ret = do_brk(limit);
if (ret >= limit) {
if (ret != -1)
break;
}
limit = (ts->heap_base >> 1) + (limit >> 1);
}
ts->heap_limit = limit;

12
arm.ld
View File

@@ -71,23 +71,23 @@ SECTIONS
.data1 : { *(.data1) }
.preinit_array :
{
PROVIDE (__preinit_array_start = .);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE (__preinit_array_end = .);
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE (__init_array_start = .);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE (__init_array_end = .);
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE (__fini_array_start = .);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(.fini_array))
KEEP (*(SORT(.fini_array.*)))
PROVIDE (__fini_array_end = .);
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{

View File

@@ -137,12 +137,11 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
int qemu_bh_poll(void)
{
QEMUBH *bh, **bhp, *next;
QEMUBH *bh, **bhp;
int ret;
ret = 0;
for (bh = async_context->first_bh; bh; bh = next) {
next = bh->next;
for (bh = async_context->first_bh; bh; bh = bh->next) {
if (!bh->deleted && bh->scheduled) {
bh->scheduled = 0;
if (!bh->idle)

View File

@@ -318,7 +318,7 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt)
{
switch (fmt) {
case AUD_FMT_S8:
@@ -328,36 +328,16 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness)
return SND_PCM_FORMAT_U8;
case AUD_FMT_S16:
if (endianness) {
return SND_PCM_FORMAT_S16_BE;
}
else {
return SND_PCM_FORMAT_S16_LE;
}
return SND_PCM_FORMAT_S16_LE;
case AUD_FMT_U16:
if (endianness) {
return SND_PCM_FORMAT_U16_BE;
}
else {
return SND_PCM_FORMAT_U16_LE;
}
return SND_PCM_FORMAT_U16_LE;
case AUD_FMT_S32:
if (endianness) {
return SND_PCM_FORMAT_S32_BE;
}
else {
return SND_PCM_FORMAT_S32_LE;
}
return SND_PCM_FORMAT_S32_LE;
case AUD_FMT_U32:
if (endianness) {
return SND_PCM_FORMAT_U32_BE;
}
else {
return SND_PCM_FORMAT_U32_LE;
}
return SND_PCM_FORMAT_U32_LE;
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
@@ -829,7 +809,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
snd_pcm_t *handle;
struct audsettings obt_as;
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
req.fmt = aud_to_alsafmt (as->fmt);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.period_size = conf.period_size_out;
@@ -863,15 +843,11 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as)
return 0;
}
#define VOICE_CTL_PAUSE 0
#define VOICE_CTL_PREPARE 1
#define VOICE_CTL_START 2
static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
{
int err;
if (ctl == VOICE_CTL_PAUSE) {
if (pause) {
err = snd_pcm_drop (handle);
if (err < 0) {
alsa_logerr (err, "Could not stop %s\n", typ);
@@ -884,13 +860,6 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
alsa_logerr (err, "Could not prepare handle for %s\n", typ);
return -1;
}
if (ctl == VOICE_CTL_START) {
err = snd_pcm_start(handle);
if (err < 0) {
alsa_logerr (err, "Could not start handle for %s\n", typ);
return -1;
}
}
}
return 0;
@@ -915,16 +884,12 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
poll_mode = 0;
}
hw->poll_mode = poll_mode;
return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE);
return alsa_voice_ctl (alsa->handle, "playback", 0);
}
case VOICE_DISABLE:
ldebug ("disabling voice\n");
if (hw->poll_mode) {
hw->poll_mode = 0;
alsa_fini_poll (&alsa->pollhlp);
}
return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
return alsa_voice_ctl (alsa->handle, "playback", 1);
}
return -1;
@@ -938,7 +903,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as)
snd_pcm_t *handle;
struct audsettings obt_as;
req.fmt = aud_to_alsafmt (as->fmt, as->endianness);
req.fmt = aud_to_alsafmt (as->fmt);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.period_size = conf.period_size_in;
@@ -1097,7 +1062,7 @@ static int alsa_run_in (HWVoiceIn *hw)
}
}
hw->conv (dst, src, nread);
hw->conv (dst, src, nread, &nominal_volume);
src = advance (src, nread << hwshift);
dst += nread;
@@ -1137,7 +1102,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
}
hw->poll_mode = poll_mode;
return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START);
return alsa_voice_ctl (alsa->handle, "capture", 0);
}
case VOICE_DISABLE:
@@ -1146,7 +1111,7 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
hw->poll_mode = 0;
alsa_fini_poll (&alsa->pollhlp);
}
return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
return alsa_voice_ctl (alsa->handle, "capture", 1);
}
return -1;

View File

@@ -44,9 +44,6 @@
that we generate the list.
*/
static struct audio_driver *drvtab[] = {
#ifdef CONFIG_SPICE
&spice_audio_driver,
#endif
CONFIG_AUDIO_DRIVERS
&no_audio_driver,
&wav_audio_driver
@@ -104,7 +101,7 @@ static struct {
static AudioState glob_audio_state;
const struct mixeng_volume nominal_volume = {
struct mixeng_volume nominal_volume = {
.mute = 0,
#ifdef FLOAT_MIXENG
.r = 1.0,
@@ -702,11 +699,13 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
/*
* Capture
*/
static void noop_conv (struct st_sample *dst, const void *src, int samples)
static void noop_conv (struct st_sample *dst, const void *src,
int samples, struct mixeng_volume *vol)
{
(void) src;
(void) dst;
(void) samples;
(void) vol;
}
static CaptureVoiceOut *audio_pcm_capture_find_specific (
@@ -954,8 +953,6 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
total += isamp;
}
mixeng_volume (sw->buf, ret, &sw->vol);
sw->clip (buf, sw->buf, ret);
sw->total_hw_samples_acquired += total;
return ret << sw->info.shift;
@@ -1037,8 +1034,7 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
swlim = ((int64_t) dead << 32) / sw->ratio;
swlim = audio_MIN (swlim, samples);
if (swlim) {
sw->conv (sw->buf, buf, swlim);
mixeng_volume (sw->buf, swlim, &sw->vol);
sw->conv (sw->buf, buf, swlim, &sw->vol);
}
while (swlim) {
@@ -1097,6 +1093,15 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
/*
* Timer
*/
static void audio_timer (void *opaque)
{
AudioState *s = opaque;
audio_run ("timer");
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
}
static int audio_is_timer_needed (void)
{
HWVoiceIn *hwi = NULL;
@@ -1111,22 +1116,18 @@ static int audio_is_timer_needed (void)
return 0;
}
static void audio_reset_timer (AudioState *s)
static void audio_reset_timer (void)
{
AudioState *s = &glob_audio_state;
if (audio_is_timer_needed ()) {
qemu_mod_timer (s->ts, qemu_get_clock_ns (vm_clock) + 1);
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
}
else {
qemu_del_timer (s->ts);
}
}
static void audio_timer (void *opaque)
{
audio_run ("timer");
audio_reset_timer (opaque);
}
/*
* Public API
*/
@@ -1191,7 +1192,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
hw->enabled = 1;
if (s->vm_running) {
hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out);
audio_reset_timer (s);
audio_reset_timer ();
}
}
}
@@ -1236,7 +1237,6 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
hw->enabled = 1;
if (s->vm_running) {
hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in);
audio_reset_timer (s);
}
}
sw->total_hw_samples_acquired = hw->total_samples_captured;
@@ -1758,7 +1758,7 @@ static void audio_vm_change_state_handler (void *opaque, int running,
while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) {
hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in);
}
audio_reset_timer (s);
audio_reset_timer ();
}
static void audio_atexit (void)
@@ -1820,7 +1820,7 @@ static void audio_init (void)
QLIST_INIT (&s->cap_head);
atexit (audio_atexit);
s->ts = qemu_new_timer_ns (vm_clock, audio_timer, s);
s->ts = qemu_new_timer (vm_clock, audio_timer, s);
if (!s->ts) {
hw_error("Could not create audio timer\n");
}

View File

@@ -86,8 +86,12 @@ typedef struct QEMUAudioTimeStamp {
uint64_t old_ts;
} QEMUAudioTimeStamp;
void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0);
void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void AUD_vlog (const char *cap, const char *fmt, va_list ap);
void AUD_log (const char *cap, const char *fmt, ...)
#ifdef __GNUC__
__attribute__ ((__format__ (__printf__, 2, 3)))
#endif
;
void AUD_help (void);
void AUD_register_card (const char *name, QEMUSoundCard *card);

View File

@@ -209,9 +209,8 @@ extern struct audio_driver coreaudio_audio_driver;
extern struct audio_driver dsound_audio_driver;
extern struct audio_driver esd_audio_driver;
extern struct audio_driver pa_audio_driver;
extern struct audio_driver spice_audio_driver;
extern struct audio_driver winwave_audio_driver;
extern const struct mixeng_volume nominal_volume;
extern struct mixeng_volume nominal_volume;
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
@@ -237,6 +236,14 @@ static inline int audio_ring_dist (int dst, int src, int len)
return (dst >= src) ? (dst - src) : (len - src + dst);
}
#if defined __GNUC__
#define GCC_ATTR __attribute__ ((__unused__, __format__ (__printf__, 1, 2)))
#define GCC_FMT_ATTR(n, m) __attribute__ ((__format__ (__printf__, n, m)))
#else
#define GCC_ATTR /**/
#define GCC_FMT_ATTR(n, m)
#endif
static void GCC_ATTR dolog (const char *fmt, ...)
{
va_list ap;

View File

@@ -6,8 +6,7 @@
#include "audio_int.h"
#include "audio_pt_int.h"
static void GCC_FMT_ATTR(3, 4) logerr (struct audio_pt *pt, int err,
const char *fmt, ...)
static void logerr (struct audio_pt *pt, int err, const char *fmt, ...)
{
va_list ap;
@@ -24,16 +23,9 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
{
int err, err2;
const char *efunc;
sigset_t set, old_set;
p->drv = drv;
err = sigfillset (&set);
if (err) {
logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC);
return -1;
}
err = pthread_mutex_init (&p->mutex, NULL);
if (err) {
efunc = "pthread_mutex_init";
@@ -46,23 +38,7 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
goto err1;
}
err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
if (err) {
efunc = "pthread_sigmask";
goto err2;
}
err = pthread_create (&p->thread, NULL, func, opaque);
err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
if (err2) {
logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed",
cap, AUDIO_FUNC);
/* We have failed to restore original signal mask, all bets are off,
so terminate the process */
exit (EXIT_FAILURE);
}
if (err) {
efunc = "pthread_create";
goto err2;

View File

@@ -108,7 +108,11 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
{
int samples;
#ifdef DAC
samples = sw->hw->samples;
#else
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
#endif
sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (struct st_sample));
if (!sw->buf) {

View File

@@ -56,7 +56,7 @@ typedef struct coreaudioVoiceOut {
static void coreaudio_logstatus (OSStatus status)
{
const char *str = "BUG";
char *str = "BUG";
switch(status) {
case kAudioHardwareNoError:
@@ -104,7 +104,7 @@ static void coreaudio_logstatus (OSStatus status)
break;
default:
AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
return;
}
@@ -360,8 +360,8 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
&core->audioDevicePropertyBufferFrameSize);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Could not set device buffer frame size %" PRIu32 "\n",
(uint32_t)core->audioDevicePropertyBufferFrameSize);
"Could not set device buffer frame size %ld\n",
core->audioDevicePropertyBufferFrameSize);
return -1;
}

View File

@@ -831,11 +831,11 @@ static int dsound_run_in (HWVoiceIn *hw)
decr = len1 + len2;
if (p1 && len1) {
hw->conv (hw->conv_buf + hw->wpos, p1, len1);
hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
}
if (p2 && len2) {
hw->conv (hw->conv_buf, p2, len2);
hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
}
dsound_unlock_in (dscb, p1, p2, blen1, blen2);

View File

@@ -24,6 +24,7 @@
#include <esd.h>
#include "qemu-common.h"
#include "audio.h"
#include <signal.h>
#define AUDIO_CAP "esd"
#include "audio_int.h"
@@ -189,6 +190,10 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
ESDVoiceOut *esd = (ESDVoiceOut *) hw;
struct audsettings obt_as = *as;
int esdfmt = ESD_STREAM | ESD_PLAY;
int err;
sigset_t set, old_set;
sigfillset (&set);
esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
switch (as->fmt) {
@@ -226,25 +231,43 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as)
return -1;
}
esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
if (esd->fd < 0) {
qesd_logerr (errno, "esd_play_stream failed\n");
esd->fd = -1;
err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
if (err) {
qesd_logerr (err, "pthread_sigmask failed\n");
goto fail1;
}
if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
if (esd->fd < 0) {
qesd_logerr (errno, "esd_play_stream failed\n");
goto fail2;
}
if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
goto fail3;
}
err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
if (err) {
qesd_logerr (err, "pthread_sigmask(restore) failed\n");
}
return 0;
fail2:
fail3:
if (close (esd->fd)) {
qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
AUDIO_FUNC, esd->fd);
}
esd->fd = -1;
fail2:
err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
if (err) {
qesd_logerr (err, "pthread_sigmask(restore) failed\n");
}
fail1:
qemu_free (esd->pcm_buf);
esd->pcm_buf = NULL;
@@ -346,7 +369,8 @@ static void *qesd_thread_in (void *arg)
break;
}
hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift);
hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
&nominal_volume);
wpos = (wpos + chunk) % hw->samples;
to_grab -= chunk;
}
@@ -399,6 +423,10 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
ESDVoiceIn *esd = (ESDVoiceIn *) hw;
struct audsettings obt_as = *as;
int esdfmt = ESD_STREAM | ESD_RECORD;
int err;
sigset_t set, old_set;
sigfillset (&set);
esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
switch (as->fmt) {
@@ -433,25 +461,44 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as)
return -1;
}
esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
if (esd->fd < 0) {
qesd_logerr (errno, "esd_record_stream failed\n");
esd->fd = -1;
err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
if (err) {
qesd_logerr (err, "pthread_sigmask failed\n");
goto fail1;
}
if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
if (esd->fd < 0) {
qesd_logerr (errno, "esd_record_stream failed\n");
goto fail2;
}
if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
goto fail3;
}
err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
if (err) {
qesd_logerr (err, "pthread_sigmask(restore) failed\n");
}
return 0;
fail2:
fail3:
if (close (esd->fd)) {
qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
AUDIO_FUNC, esd->fd);
}
esd->fd = -1;
fail2:
err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
if (err) {
qesd_logerr (err, "pthread_sigmask(restore) failed\n");
}
fail1:
qemu_free (esd->pcm_buf);
esd->pcm_buf = NULL;

View File

@@ -488,10 +488,10 @@ static int fmod_run_in (HWVoiceIn *hw)
decr = len1 + len2;
if (p1 && blen1) {
hw->conv (hw->conv_buf + hw->wpos, p1, len1);
hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume);
}
if (p2 && len2) {
hw->conv (hw->conv_buf, p2, len2);
hw->conv (hw->conv_buf, p2, len2, &nominal_volume);
}
fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2);

View File

@@ -333,28 +333,3 @@ void mixeng_clear (struct st_sample *buf, int len)
{
memset (buf, 0, len * sizeof (struct st_sample));
}
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol)
{
#ifdef CONFIG_MIXEMU
if (vol->mute) {
mixeng_clear (buf, len);
return;
}
while (len--) {
#ifdef FLOAT_MIXENG
buf->l = buf->l * vol->l;
buf->r = buf->r * vol->r;
#else
buf->l = (buf->l * vol->l) >> 32;
buf->r = (buf->r * vol->r) >> 32;
#endif
buf += 1;
}
#else
(void) buf;
(void) len;
(void) vol;
#endif
}

View File

@@ -33,7 +33,8 @@ struct mixeng_volume { int mute; int64_t r; int64_t l; };
struct st_sample { int64_t l; int64_t r; };
#endif
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
typedef void (t_sample) (struct st_sample *dst, const void *src,
int samples, struct mixeng_volume *vol);
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
extern t_sample *mixeng_conv[2][2][2][3];
@@ -46,6 +47,5 @@ void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *o
int *isamp, int *osamp);
void st_rate_stop (void *opaque);
void mixeng_clear (struct st_sample *buf, int len);
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);
#endif /* mixeng.h */

View File

@@ -31,6 +31,16 @@
#define HALF (IN_MAX >> 1)
#endif
#ifdef CONFIG_MIXEMU
#ifdef FLOAT_MIXENG
#define VOL(a, b) ((a) * (b))
#else
#define VOL(a, b) ((a) * (b)) >> 32
#endif
#else
#define VOL(a, b) a
#endif
#define ET glue (ENDIAN_CONVERSION, glue (_, IN_T))
#ifdef FLOAT_MIXENG
@@ -46,7 +56,7 @@ static mixeng_real inline glue (conv_, ET) (IN_T v)
#endif
#else /* !RECIPROCAL */
#ifdef SIGNED
return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN);
return nv / (mixeng_real) (IN_MAX - IN_MIN);
#else
return (nv - HALF) / (mixeng_real) IN_MAX;
#endif
@@ -63,7 +73,7 @@ static IN_T inline glue (clip_, ET) (mixeng_real v)
}
#ifdef SIGNED
return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN)));
return ENDIAN_CONVERT ((IN_T) (v * (IN_MAX - IN_MIN)));
#else
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
#endif
@@ -99,26 +109,40 @@ static inline IN_T glue (clip_, ET) (int64_t v)
#endif
static void glue (glue (conv_, ET), _to_stereo)
(struct st_sample *dst, const void *src, int samples)
(struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol)
{
struct st_sample *out = dst;
IN_T *in = (IN_T *) src;
#ifdef CONFIG_MIXEMU
if (vol->mute) {
mixeng_clear (dst, samples);
return;
}
#else
(void) vol;
#endif
while (samples--) {
out->l = glue (conv_, ET) (*in++);
out->r = glue (conv_, ET) (*in++);
out->l = VOL (glue (conv_, ET) (*in++), vol->l);
out->r = VOL (glue (conv_, ET) (*in++), vol->r);
out += 1;
}
}
static void glue (glue (conv_, ET), _to_mono)
(struct st_sample *dst, const void *src, int samples)
(struct st_sample *dst, const void *src, int samples, struct mixeng_volume *vol)
{
struct st_sample *out = dst;
IN_T *in = (IN_T *) src;
#ifdef CONFIG_MIXEMU
if (vol->mute) {
mixeng_clear (dst, samples);
return;
}
#else
(void) vol;
#endif
while (samples--) {
out->l = glue (conv_, ET) (in[0]);
out->l = VOL (glue (conv_, ET) (in[0]), vol->l);
out->r = out->l;
out += 1;
in += 1;
@@ -150,3 +174,4 @@ static void glue (glue (clip_, ET), _from_mono)
#undef ET
#undef HALF
#undef VOL

View File

@@ -46,7 +46,7 @@ static int no_run_out (HWVoiceOut *hw, int live)
int64_t ticks;
int64_t bytes;
now = qemu_get_clock_ns (vm_clock);
now = qemu_get_clock (vm_clock);
ticks = now - no->old_ticks;
bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
bytes = audio_MIN (bytes, INT_MAX);
@@ -102,7 +102,7 @@ static int no_run_in (HWVoiceIn *hw)
int samples = 0;
if (dead) {
int64_t now = qemu_get_clock_ns (vm_clock);
int64_t now = qemu_get_clock (vm_clock);
int64_t ticks = now - no->old_ticks;
int64_t bytes =
muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
@@ -117,14 +117,11 @@ static int no_run_in (HWVoiceIn *hw)
static int no_read (SWVoiceIn *sw, void *buf, int size)
{
/* use custom code here instead of audio_pcm_sw_read() to avoid
* useless resampling/mixing */
int samples = size >> sw->info.shift;
int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
int to_clear = audio_MIN (samples, total);
sw->total_hw_samples_acquired += total;
audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
return to_clear << sw->info.shift;
return to_clear;
}
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)

View File

@@ -161,7 +161,7 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len)
return audio_pcm_sw_write (sw, buf, len);
}
static int aud_to_ossfmt (audfmt_e fmt, int endianness)
static int aud_to_ossfmt (audfmt_e fmt)
{
switch (fmt) {
case AUD_FMT_S8:
@@ -171,20 +171,10 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness)
return AFMT_U8;
case AUD_FMT_S16:
if (endianness) {
return AFMT_S16_BE;
}
else {
return AFMT_S16_LE;
}
return AFMT_S16_LE;
case AUD_FMT_U16:
if (endianness) {
return AFMT_U16_BE;
}
else {
return AFMT_U16_LE;
}
return AFMT_U16_LE;
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
@@ -526,7 +516,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as)
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.fmt = aud_to_ossfmt (as->fmt);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.fragsize = conf.fragsize;
@@ -692,7 +682,7 @@ static int oss_init_in (HWVoiceIn *hw, struct audsettings *as)
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt, as->endianness);
req.fmt = aud_to_ossfmt (as->fmt);
req.freq = as->freq;
req.nchannels = as->nchannels;
req.fragsize = conf.fragsize;
@@ -788,7 +778,8 @@ static int oss_run_in (HWVoiceIn *hw)
hw->info.align + 1);
}
read_samples += nread >> hwshift;
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift,
&nominal_volume);
}
if (bufs[i].len - nread) {

View File

@@ -33,11 +33,13 @@ typedef struct {
static struct {
int samples;
int divisor;
char *server;
char *sink;
char *source;
} conf = {
.samples = 4096,
.samples = 1024,
.divisor = 2,
};
static void GCC_FMT_ATTR (2, 3) qpa_logerr (int err, const char *fmt, ...)
@@ -55,6 +57,9 @@ static void *qpa_thread_out (void *arg)
{
PAVoiceOut *pa = arg;
HWVoiceOut *hw = &pa->hw;
int threshold;
threshold = conf.divisor ? hw->samples / conf.divisor : 0;
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return NULL;
@@ -68,7 +73,7 @@ static void *qpa_thread_out (void *arg)
goto exit;
}
if (pa->live > 0) {
if (pa->live > threshold) {
break;
}
@@ -77,8 +82,8 @@ static void *qpa_thread_out (void *arg)
}
}
decr = to_mix = audio_MIN (pa->live, conf.samples >> 2);
rpos = pa->rpos;
decr = to_mix = pa->live;
rpos = hw->rpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
return NULL;
@@ -147,6 +152,9 @@ static void *qpa_thread_in (void *arg)
{
PAVoiceIn *pa = arg;
HWVoiceIn *hw = &pa->hw;
int threshold;
threshold = conf.divisor ? hw->samples / conf.divisor : 0;
if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
return NULL;
@@ -160,7 +168,7 @@ static void *qpa_thread_in (void *arg)
goto exit;
}
if (pa->dead > 0) {
if (pa->dead > threshold) {
break;
}
@@ -169,8 +177,8 @@ static void *qpa_thread_in (void *arg)
}
}
incr = to_grab = audio_MIN (pa->dead, conf.samples >> 2);
wpos = pa->wpos;
incr = to_grab = pa->dead;
wpos = hw->wpos;
if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
return NULL;
@@ -187,7 +195,7 @@ static void *qpa_thread_in (void *arg)
return NULL;
}
hw->conv (hw->conv_buf + wpos, buf, chunk);
hw->conv (hw->conv_buf + wpos, buf, chunk, &nominal_volume);
wpos = (wpos + chunk) % hw->samples;
to_grab -= chunk;
}
@@ -287,7 +295,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
{
int error;
static pa_sample_spec ss;
static pa_buffer_attr ba;
struct audsettings obt_as = *as;
PAVoiceOut *pa = (PAVoiceOut *) hw;
@@ -295,15 +302,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
ss.channels = as->nchannels;
ss.rate = as->freq;
/*
* qemu audio tick runs at 250 Hz (by default), so processing
* data chunks worth 4 ms of sound should be a good fit.
*/
ba.tlength = pa_usec_to_bytes (4 * 1000, &ss);
ba.minreq = pa_usec_to_bytes (2 * 1000, &ss);
ba.maxlength = -1;
ba.prebuf = -1;
obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
pa->s = pa_simple_new (
@@ -314,7 +312,7 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
"pcm.playback",
&ss,
NULL, /* channel map */
&ba, /* buffering attributes */
NULL, /* buffering attributes */
&error
);
if (!pa->s) {
@@ -325,7 +323,6 @@ static int qpa_init_out (HWVoiceOut *hw, struct audsettings *as)
audio_pcm_init_info (&hw->info, &obt_as);
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) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
@@ -380,7 +377,6 @@ static int qpa_init_in (HWVoiceIn *hw, struct audsettings *as)
audio_pcm_init_info (&hw->info, &obt_as);
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) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
@@ -475,6 +471,12 @@ struct audio_option qpa_options[] = {
.valp = &conf.samples,
.descr = "buffer size in samples"
},
{
.name = "DIVISOR",
.tag = AUD_OPT_INT,
.valp = &conf.divisor,
.descr = "threshold divisor"
},
{
.name = "SERVER",
.tag = AUD_OPT_STR,

View File

@@ -32,6 +32,7 @@
#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
#include <pthread.h>
#endif
#include <signal.h>
#endif
#define AUDIO_CAP "sdl"
@@ -138,36 +139,36 @@ static int aud_to_sdlfmt (audfmt_e fmt)
}
}
static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness)
static int sdl_to_audfmt (int sdlfmt, audfmt_e *fmt, int *endianess)
{
switch (sdlfmt) {
case AUDIO_S8:
*endianness = 0;
*endianess = 0;
*fmt = AUD_FMT_S8;
break;
case AUDIO_U8:
*endianness = 0;
*endianess = 0;
*fmt = AUD_FMT_U8;
break;
case AUDIO_S16LSB:
*endianness = 0;
*endianess = 0;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16LSB:
*endianness = 0;
*endianess = 0;
*fmt = AUD_FMT_U16;
break;
case AUDIO_S16MSB:
*endianness = 1;
*endianess = 1;
*fmt = AUD_FMT_S16;
break;
case AUDIO_U16MSB:
*endianness = 1;
*endianess = 1;
*fmt = AUD_FMT_U16;
break;
@@ -183,20 +184,11 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
{
int status;
#ifndef _WIN32
int err;
sigset_t new, old;
/* Make sure potential threads created by SDL don't hog signals. */
err = sigfillset (&new);
if (err) {
dolog ("sdl_open: sigfillset failed: %s\n", strerror (errno));
return -1;
}
err = pthread_sigmask (SIG_BLOCK, &new, &old);
if (err) {
dolog ("sdl_open: pthread_sigmask failed: %s\n", strerror (err));
return -1;
}
sigfillset (&new);
pthread_sigmask (SIG_BLOCK, &new, &old);
#endif
status = SDL_OpenAudio (req, obt);
@@ -205,14 +197,7 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
}
#ifndef _WIN32
err = pthread_sigmask (SIG_SETMASK, &old, NULL);
if (err) {
dolog ("sdl_open: pthread_sigmask (restore) failed: %s\n",
strerror (errno));
/* We have failed to restore original signal mask, all bets are off,
so exit the process */
exit (EXIT_FAILURE);
}
pthread_sigmask (SIG_SETMASK, &old, NULL);
#endif
return status;
}
@@ -337,7 +322,7 @@ static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDLAudioState *s = &glob_sdl;
SDL_AudioSpec req, obt;
int endianness;
int endianess;
int err;
audfmt_e effective_fmt;
struct audsettings obt_as;
@@ -353,7 +338,7 @@ static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
return -1;
}
err = sdl_to_audfmt(obt.format, &effective_fmt, &endianness);
err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
if (err) {
sdl_close (s);
return -1;
@@ -362,7 +347,7 @@ static int sdl_init_out (HWVoiceOut *hw, struct audsettings *as)
obt_as.freq = obt.freq;
obt_as.nchannels = obt.channels;
obt_as.fmt = effective_fmt;
obt_as.endianness = endianness;
obt_as.endianness = endianess;
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;

View File

@@ -1,345 +0,0 @@
/*
* Copyright (C) 2010 Red Hat, Inc.
*
* maintained by Gerd Hoffmann <kraxel@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/hw.h"
#include "qemu-timer.h"
#include "ui/qemu-spice.h"
#define AUDIO_CAP "spice"
#include "audio.h"
#include "audio_int.h"
#define LINE_IN_SAMPLES 1024
#define LINE_OUT_SAMPLES 1024
typedef struct SpiceRateCtl {
int64_t start_ticks;
int64_t bytes_sent;
} SpiceRateCtl;
typedef struct SpiceVoiceOut {
HWVoiceOut hw;
SpicePlaybackInstance sin;
SpiceRateCtl rate;
int active;
uint32_t *frame;
uint32_t *fpos;
uint32_t fsize;
} SpiceVoiceOut;
typedef struct SpiceVoiceIn {
HWVoiceIn hw;
SpiceRecordInstance sin;
SpiceRateCtl rate;
int active;
uint32_t samples[LINE_IN_SAMPLES];
} SpiceVoiceIn;
static const SpicePlaybackInterface playback_sif = {
.base.type = SPICE_INTERFACE_PLAYBACK,
.base.description = "playback",
.base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR,
.base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR,
};
static const SpiceRecordInterface record_sif = {
.base.type = SPICE_INTERFACE_RECORD,
.base.description = "record",
.base.major_version = SPICE_INTERFACE_RECORD_MAJOR,
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
};
static void *spice_audio_init (void)
{
if (!using_spice) {
return NULL;
}
return &spice_audio_init;
}
static void spice_audio_fini (void *opaque)
{
/* nothing */
}
static void rate_start (SpiceRateCtl *rate)
{
memset (rate, 0, sizeof (*rate));
rate->start_ticks = qemu_get_clock_ns (vm_clock);
}
static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
{
int64_t now;
int64_t ticks;
int64_t bytes;
int64_t samples;
now = qemu_get_clock_ns (vm_clock);
ticks = now - rate->start_ticks;
bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
samples = (bytes - rate->bytes_sent) >> info->shift;
if (samples < 0 || samples > 65536) {
fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples);
rate_start (rate);
samples = 0;
}
rate->bytes_sent += samples << info->shift;
return samples;
}
/* playback */
static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
{
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
struct audsettings settings;
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
settings.fmt = AUD_FMT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings);
hw->samples = LINE_OUT_SAMPLES;
out->active = 0;
out->sin.base.sif = &playback_sif.base;
qemu_spice_add_interface (&out->sin.base);
return 0;
}
static void line_out_fini (HWVoiceOut *hw)
{
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
spice_server_remove_interface (&out->sin.base);
}
static int line_out_run (HWVoiceOut *hw, int live)
{
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
int rpos, decr;
int samples;
if (!live) {
return 0;
}
decr = rate_get_samples (&hw->info, &out->rate);
decr = audio_MIN (live, decr);
samples = decr;
rpos = hw->rpos;
while (samples) {
int left_till_end_samples = hw->samples - rpos;
int len = audio_MIN (samples, left_till_end_samples);
if (!out->frame) {
spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
out->fpos = out->frame;
}
if (out->frame) {
len = audio_MIN (len, out->fsize);
hw->clip (out->fpos, hw->mix_buf + rpos, len);
out->fsize -= len;
out->fpos += len;
if (out->fsize == 0) {
spice_server_playback_put_samples (&out->sin, out->frame);
out->frame = out->fpos = NULL;
}
}
rpos = (rpos + len) % hw->samples;
samples -= len;
}
hw->rpos = rpos;
return decr;
}
static int line_out_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
{
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
switch (cmd) {
case VOICE_ENABLE:
if (out->active) {
break;
}
out->active = 1;
rate_start (&out->rate);
spice_server_playback_start (&out->sin);
break;
case VOICE_DISABLE:
if (!out->active) {
break;
}
out->active = 0;
if (out->frame) {
memset (out->fpos, 0, out->fsize << 2);
spice_server_playback_put_samples (&out->sin, out->frame);
out->frame = out->fpos = NULL;
}
spice_server_playback_stop (&out->sin);
break;
}
return 0;
}
/* record */
static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
struct audsettings settings;
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
settings.fmt = AUD_FMT_S16;
settings.endianness = AUDIO_HOST_ENDIANNESS;
audio_pcm_init_info (&hw->info, &settings);
hw->samples = LINE_IN_SAMPLES;
in->active = 0;
in->sin.base.sif = &record_sif.base;
qemu_spice_add_interface (&in->sin.base);
return 0;
}
static void line_in_fini (HWVoiceIn *hw)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
spice_server_remove_interface (&in->sin.base);
}
static int line_in_run (HWVoiceIn *hw)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
int num_samples;
int ready;
int len[2];
uint64_t delta_samp;
const uint32_t *samples;
if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
return 0;
}
delta_samp = rate_get_samples (&hw->info, &in->rate);
num_samples = audio_MIN (num_samples, delta_samp);
ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
samples = in->samples;
if (ready == 0) {
static const uint32_t silence[LINE_IN_SAMPLES];
samples = silence;
ready = LINE_IN_SAMPLES;
}
num_samples = audio_MIN (ready, num_samples);
if (hw->wpos + num_samples > hw->samples) {
len[0] = hw->samples - hw->wpos;
len[1] = num_samples - len[0];
} else {
len[0] = num_samples;
len[1] = 0;
}
hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
if (len[1]) {
hw->conv (hw->conv_buf, samples + len[0], len[1]);
}
hw->wpos = (hw->wpos + num_samples) % hw->samples;
return num_samples;
}
static int line_in_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, size);
}
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
switch (cmd) {
case VOICE_ENABLE:
if (in->active) {
break;
}
in->active = 1;
rate_start (&in->rate);
spice_server_record_start (&in->sin);
break;
case VOICE_DISABLE:
if (!in->active) {
break;
}
in->active = 0;
spice_server_record_stop (&in->sin);
break;
}
return 0;
}
static struct audio_option audio_options[] = {
{ /* end of list */ },
};
static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init,
.fini_out = line_out_fini,
.run_out = line_out_run,
.write = line_out_write,
.ctl_out = line_out_ctl,
.init_in = line_in_init,
.fini_in = line_in_fini,
.run_in = line_in_run,
.read = line_in_read,
.ctl_in = line_in_ctl,
};
struct audio_driver spice_audio_driver = {
.name = "spice",
.descr = "spice audio driver",
.options = audio_options,
.init = spice_audio_init,
.fini = spice_audio_fini,
.pcm_ops = &audio_callbacks,
.max_voices_out = 1,
.max_voices_in = 1,
.voice_size_out = sizeof (SpiceVoiceOut),
.voice_size_in = sizeof (SpiceVoiceIn),
};
void qemu_spice_audio_init (void)
{
spice_audio_driver.can_be_default = 1;
}

View File

@@ -52,7 +52,7 @@ static int wav_run_out (HWVoiceOut *hw, int live)
int rpos, decr, samples;
uint8_t *dst;
struct st_sample *src;
int64_t now = qemu_get_clock_ns (vm_clock);
int64_t now = qemu_get_clock (vm_clock);
int64_t ticks = now - wav->old_ticks;
int64_t bytes =
muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());

View File

@@ -581,7 +581,8 @@ static int winwave_run_in (HWVoiceIn *hw)
int conv = audio_MIN (left, decr);
hw->conv (hw->conv_buf + hw->wpos,
advance (wave->pcm_buf, wave->rpos << hw->info.shift),
conv);
conv,
&nominal_volume);
wave->rpos = (wave->rpos + conv) % hw->samples;
hw->wpos = (hw->wpos + conv) % hw->samples;

View File

@@ -1,9 +1,7 @@
/*
* Generic Balloon handlers and management
* QEMU System Emulator
*
* Copyright (c) 2003-2008 Fabrice Bellard
* Copyright (C) 2011 Red Hat, Inc.
* Copyright (C) 2011 Amit Shah <amit.shah@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,61 +22,51 @@
* THE SOFTWARE.
*/
#include "sysemu.h"
#include "monitor.h"
#include "qjson.h"
#include "qint.h"
#include "cpu-common.h"
#include "kvm.h"
#include "balloon.h"
#include "trace.h"
static QEMUBalloonEvent *balloon_event_fn;
static QEMUBalloonStatus *balloon_stat_fn;
static void *balloon_opaque;
int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
QEMUBalloonStatus *stat_func, void *opaque)
static QEMUBalloonEvent *qemu_balloon_event;
void *qemu_balloon_event_opaque;
void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque)
{
if (balloon_event_fn || balloon_stat_fn || balloon_opaque) {
/* We're already registered one balloon handler. How many can
* a guest really have?
*/
error_report("Another balloon device already registered");
return -1;
}
balloon_event_fn = event_func;
balloon_stat_fn = stat_func;
balloon_opaque = opaque;
return 0;
qemu_balloon_event = func;
qemu_balloon_event_opaque = opaque;
}
static int qemu_balloon(ram_addr_t target)
int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque)
{
if (!balloon_event_fn) {
if (qemu_balloon_event) {
qemu_balloon_event(qemu_balloon_event_opaque, target, cb, opaque);
return 1;
} else {
return 0;
}
trace_balloon_event(balloon_opaque, target);
balloon_event_fn(balloon_opaque, target);
return 1;
}
static int qemu_balloon_status(MonitorCompletion cb, void *opaque)
int qemu_balloon_status(MonitorCompletion cb, void *opaque)
{
if (!balloon_stat_fn) {
if (qemu_balloon_event) {
qemu_balloon_event(qemu_balloon_event_opaque, 0, cb, opaque);
return 1;
} else {
return 0;
}
balloon_stat_fn(balloon_opaque, cb, opaque);
return 1;
}
static void print_balloon_stat(const char *key, QObject *obj, void *opaque)
{
Monitor *mon = opaque;
if (strcmp(key, "actual")) {
if (strcmp(key, "actual"))
monitor_printf(mon, ",%s=%" PRId64, key,
qint_get_int(qobject_to_qint(obj)));
}
}
void monitor_print_balloon(Monitor *mon, const QObject *data)
@@ -86,9 +74,9 @@ void monitor_print_balloon(Monitor *mon, const QObject *data)
QDict *qdict;
qdict = qobject_to_qdict(data);
if (!qdict_haskey(qdict, "actual")) {
if (!qdict_haskey(qdict, "actual"))
return;
}
monitor_printf(mon, "balloon: actual=%" PRId64,
qdict_get_int(qdict, "actual") >> 20);
qdict_iter(qdict, print_balloon_stat, mon);
@@ -140,7 +128,6 @@ int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque)
int do_balloon(Monitor *mon, const QDict *params,
MonitorCompletion cb, void *opaque)
{
int64_t target;
int ret;
if (kvm_enabled() && !kvm_has_sync_mmu()) {
@@ -148,12 +135,7 @@ int do_balloon(Monitor *mon, const QDict *params,
return -1;
}
target = qdict_get_int(params, "value");
if (target <= 0) {
qerror_report(QERR_INVALID_PARAMETER_VALUE, "target", "a size");
return -1;
}
ret = qemu_balloon(target);
ret = qemu_balloon(qdict_get_int(params, "value"), cb, opaque);
if (ret == 0) {
qerror_report(QERR_DEVICE_NOT_ACTIVE, "balloon");
return -1;

View File

@@ -16,12 +16,14 @@
#include "monitor.h"
typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target);
typedef void (QEMUBalloonStatus)(void *opaque, MonitorCompletion cb,
void *cb_data);
typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target,
MonitorCompletion cb, void *cb_data);
int qemu_add_balloon_handler(QEMUBalloonEvent *event_func,
QEMUBalloonStatus *stat_func, void *opaque);
void qemu_add_balloon_handler(QEMUBalloonEvent *func, void *opaque);
int qemu_balloon(ram_addr_t target, MonitorCompletion cb, void *opaque);
int qemu_balloon_status(MonitorCompletion cb, void *opaque);
void monitor_print_balloon(Monitor *mon, const QObject *data);
int do_info_balloon(Monitor *mon, MonitorCompletion cb, void *opaque);

256
bitmap.c
View File

@@ -1,256 +0,0 @@
/*
* Bitmap Module
*
* Stolen from linux/src/lib/bitmap.c
*
* Copyright (C) 2010 Corentin Chary
*
* This source code is licensed under the GNU General Public License,
* Version 2.
*/
#include "bitops.h"
#include "bitmap.h"
/*
* bitmaps provide an array of bits, implemented using an an
* array of unsigned longs. The number of valid bits in a
* given bitmap does _not_ need to be an exact multiple of
* BITS_PER_LONG.
*
* The possible unused bits in the last, partially used word
* of a bitmap are 'don't care'. The implementation makes
* no particular effort to keep them zero. It ensures that
* their value will not affect the results of any operation.
* The bitmap operations that return Boolean (bitmap_empty,
* for example) or scalar (bitmap_weight, for example) results
* carefully filter out these unused bits from impacting their
* results.
*
* These operations actually hold to a slightly stronger rule:
* if you don't input any bitmaps to these ops that have some
* unused bits set, then they won't output any set unused bits
* in output bitmaps.
*
* The byte ordering of bitmaps is more natural on little
* endian architectures.
*/
int slow_bitmap_empty(const unsigned long *bitmap, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
if (bitmap[k]) {
return 0;
}
}
if (bits % BITS_PER_LONG) {
if (bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) {
return 0;
}
}
return 1;
}
int slow_bitmap_full(const unsigned long *bitmap, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
if (~bitmap[k]) {
return 0;
}
}
if (bits % BITS_PER_LONG) {
if (~bitmap[k] & BITMAP_LAST_WORD_MASK(bits)) {
return 0;
}
}
return 1;
}
int slow_bitmap_equal(const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
if (bitmap1[k] != bitmap2[k]) {
return 0;
}
}
if (bits % BITS_PER_LONG) {
if ((bitmap1[k] ^ bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) {
return 0;
}
}
return 1;
}
void slow_bitmap_complement(unsigned long *dst, const unsigned long *src,
int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
dst[k] = ~src[k];
}
if (bits % BITS_PER_LONG) {
dst[k] = ~src[k] & BITMAP_LAST_WORD_MASK(bits);
}
}
int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
unsigned long result = 0;
for (k = 0; k < nr; k++) {
result |= (dst[k] = bitmap1[k] & bitmap2[k]);
}
return result != 0;
}
void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
for (k = 0; k < nr; k++) {
dst[k] = bitmap1[k] | bitmap2[k];
}
}
void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
for (k = 0; k < nr; k++) {
dst[k] = bitmap1[k] ^ bitmap2[k];
}
}
int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k;
int nr = BITS_TO_LONGS(bits);
unsigned long result = 0;
for (k = 0; k < nr; k++) {
result |= (dst[k] = bitmap1[k] & ~bitmap2[k]);
}
return result != 0;
}
#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG))
void bitmap_set(unsigned long *map, int start, int nr)
{
unsigned long *p = map + BIT_WORD(start);
const int size = start + nr;
int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
while (nr - bits_to_set >= 0) {
*p |= mask_to_set;
nr -= bits_to_set;
bits_to_set = BITS_PER_LONG;
mask_to_set = ~0UL;
p++;
}
if (nr) {
mask_to_set &= BITMAP_LAST_WORD_MASK(size);
*p |= mask_to_set;
}
}
void bitmap_clear(unsigned long *map, int start, int nr)
{
unsigned long *p = map + BIT_WORD(start);
const int size = start + nr;
int bits_to_clear = BITS_PER_LONG - (start % BITS_PER_LONG);
unsigned long mask_to_clear = BITMAP_FIRST_WORD_MASK(start);
while (nr - bits_to_clear >= 0) {
*p &= ~mask_to_clear;
nr -= bits_to_clear;
bits_to_clear = BITS_PER_LONG;
mask_to_clear = ~0UL;
p++;
}
if (nr) {
mask_to_clear &= BITMAP_LAST_WORD_MASK(size);
*p &= ~mask_to_clear;
}
}
#define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
/**
* bitmap_find_next_zero_area - find a contiguous aligned zero area
* @map: The address to base the search on
* @size: The bitmap size in bits
* @start: The bitnumber to start searching at
* @nr: The number of zeroed bits we're looking for
* @align_mask: Alignment mask for zero area
*
* The @align_mask should be one less than a power of 2; the effect is that
* the bit offset of all zero areas this function finds is multiples of that
* power of 2. A @align_mask of 0 means no alignment is required.
*/
unsigned long bitmap_find_next_zero_area(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned int nr,
unsigned long align_mask)
{
unsigned long index, end, i;
again:
index = find_next_zero_bit(map, size, start);
/* Align allocation */
index = ALIGN_MASK(index, align_mask);
end = index + nr;
if (end > size) {
return end;
}
i = find_next_bit(map, end, index);
if (i < end) {
start = i + 1;
goto again;
}
return index;
}
int slow_bitmap_intersects(const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits)
{
int k, lim = bits/BITS_PER_LONG;
for (k = 0; k < lim; ++k) {
if (bitmap1[k] & bitmap2[k]) {
return 1;
}
}
if (bits % BITS_PER_LONG) {
if ((bitmap1[k] & bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits)) {
return 1;
}
}
return 0;
}

222
bitmap.h
View File

@@ -1,222 +0,0 @@
/*
* Bitmap Module
*
* Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
*
* Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h
*
* 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.
*/
#ifndef BITMAP_H
#define BITMAP_H
#include "qemu-common.h"
#include "bitops.h"
/*
* The available bitmap operations and their rough meaning in the
* case that the bitmap is a single unsigned long are thus:
*
* Note that nbits should be always a compile time evaluable constant.
* Otherwise many inlines will generate horrible code.
*
* bitmap_zero(dst, nbits) *dst = 0UL
* bitmap_fill(dst, nbits) *dst = ~0UL
* bitmap_copy(dst, src, nbits) *dst = *src
* bitmap_and(dst, src1, src2, nbits) *dst = *src1 & *src2
* bitmap_or(dst, src1, src2, nbits) *dst = *src1 | *src2
* bitmap_xor(dst, src1, src2, nbits) *dst = *src1 ^ *src2
* bitmap_andnot(dst, src1, src2, nbits) *dst = *src1 & ~(*src2)
* bitmap_complement(dst, src, nbits) *dst = ~(*src)
* bitmap_equal(src1, src2, nbits) Are *src1 and *src2 equal?
* bitmap_intersects(src1, src2, nbits) Do *src1 and *src2 overlap?
* bitmap_empty(src, nbits) Are all bits zero in *src?
* bitmap_full(src, nbits) Are all bits set in *src?
* bitmap_set(dst, pos, nbits) Set specified bit area
* bitmap_clear(dst, pos, nbits) Clear specified bit area
* bitmap_find_next_zero_area(buf, len, pos, n, mask) Find bit free area
*/
/*
* Also the following operations apply to bitmaps.
*
* set_bit(bit, addr) *addr |= bit
* clear_bit(bit, addr) *addr &= ~bit
* change_bit(bit, addr) *addr ^= bit
* test_bit(bit, addr) Is bit set in *addr?
* test_and_set_bit(bit, addr) Set bit and return old value
* test_and_clear_bit(bit, addr) Clear bit and return old value
* test_and_change_bit(bit, addr) Change bit and return old value
* find_first_zero_bit(addr, nbits) Position first zero bit in *addr
* find_first_bit(addr, nbits) Position first set bit in *addr
* find_next_zero_bit(addr, nbits, bit) Position next zero bit in *addr >= bit
* find_next_bit(addr, nbits, bit) Position next set bit in *addr >= bit
*/
#define BITMAP_LAST_WORD_MASK(nbits) \
( \
((nbits) % BITS_PER_LONG) ? \
(1UL<<((nbits) % BITS_PER_LONG))-1 : ~0UL \
)
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
#define small_nbits(nbits) \
((nbits) <= BITS_PER_LONG)
int slow_bitmap_empty(const unsigned long *bitmap, int bits);
int slow_bitmap_full(const unsigned long *bitmap, int bits);
int slow_bitmap_equal(const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
void slow_bitmap_complement(unsigned long *dst, const unsigned long *src,
int bits);
void slow_bitmap_shift_right(unsigned long *dst,
const unsigned long *src, int shift, int bits);
void slow_bitmap_shift_left(unsigned long *dst,
const unsigned long *src, int shift, int bits);
int slow_bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
void slow_bitmap_or(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
void slow_bitmap_xor(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
int slow_bitmap_andnot(unsigned long *dst, const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
int slow_bitmap_intersects(const unsigned long *bitmap1,
const unsigned long *bitmap2, int bits);
static inline unsigned long *bitmap_new(int nbits)
{
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
return qemu_mallocz(len);
}
static inline void bitmap_zero(unsigned long *dst, int nbits)
{
if (small_nbits(nbits)) {
*dst = 0UL;
} else {
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
memset(dst, 0, len);
}
}
static inline void bitmap_fill(unsigned long *dst, int nbits)
{
size_t nlongs = BITS_TO_LONGS(nbits);
if (!small_nbits(nbits)) {
int len = (nlongs - 1) * sizeof(unsigned long);
memset(dst, 0xff, len);
}
dst[nlongs - 1] = BITMAP_LAST_WORD_MASK(nbits);
}
static inline void bitmap_copy(unsigned long *dst, const unsigned long *src,
int nbits)
{
if (small_nbits(nbits)) {
*dst = *src;
} else {
int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
memcpy(dst, src, len);
}
}
static inline int bitmap_and(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_nbits(nbits)) {
return (*dst = *src1 & *src2) != 0;
}
return slow_bitmap_and(dst, src1, src2, nbits);
}
static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_nbits(nbits)) {
*dst = *src1 | *src2;
} else {
slow_bitmap_or(dst, src1, src2, nbits);
}
}
static inline void bitmap_xor(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_nbits(nbits)) {
*dst = *src1 ^ *src2;
} else {
slow_bitmap_xor(dst, src1, src2, nbits);
}
}
static inline int bitmap_andnot(unsigned long *dst, const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_nbits(nbits)) {
return (*dst = *src1 & ~(*src2)) != 0;
}
return slow_bitmap_andnot(dst, src1, src2, nbits);
}
static inline void bitmap_complement(unsigned long *dst, const unsigned long *src,
int nbits)
{
if (small_nbits(nbits)) {
*dst = ~(*src) & BITMAP_LAST_WORD_MASK(nbits);
} else {
slow_bitmap_complement(dst, src, nbits);
}
}
static inline int bitmap_equal(const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_nbits(nbits)) {
return ! ((*src1 ^ *src2) & BITMAP_LAST_WORD_MASK(nbits));
} else {
return slow_bitmap_equal(src1, src2, nbits);
}
}
static inline int bitmap_empty(const unsigned long *src, int nbits)
{
if (small_nbits(nbits)) {
return ! (*src & BITMAP_LAST_WORD_MASK(nbits));
} else {
return slow_bitmap_empty(src, nbits);
}
}
static inline int bitmap_full(const unsigned long *src, int nbits)
{
if (small_nbits(nbits)) {
return ! (~(*src) & BITMAP_LAST_WORD_MASK(nbits));
} else {
return slow_bitmap_full(src, nbits);
}
}
static inline int bitmap_intersects(const unsigned long *src1,
const unsigned long *src2, int nbits)
{
if (small_nbits(nbits)) {
return ((*src1 & *src2) & BITMAP_LAST_WORD_MASK(nbits)) != 0;
} else {
return slow_bitmap_intersects(src1, src2, nbits);
}
}
void bitmap_set(unsigned long *map, int i, int len);
void bitmap_clear(unsigned long *map, int start, int nr);
unsigned long bitmap_find_next_zero_area(unsigned long *map,
unsigned long size,
unsigned long start,
unsigned int nr,
unsigned long align_mask);
#endif /* BITMAP_H */

142
bitops.c
View File

@@ -1,142 +0,0 @@
/*
* Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com)
* Copyright (C) 2008 IBM Corporation
* Written by Rusty Russell <rusty@rustcorp.com.au>
* (Inspired by David Howell's find_next_bit implementation)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include "bitops.h"
#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
/*
* Find the next set bit in a memory region.
*/
unsigned long find_next_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
const unsigned long *p = addr + BITOP_WORD(offset);
unsigned long result = offset & ~(BITS_PER_LONG-1);
unsigned long tmp;
if (offset >= size) {
return size;
}
size -= result;
offset %= BITS_PER_LONG;
if (offset) {
tmp = *(p++);
tmp &= (~0UL << offset);
if (size < BITS_PER_LONG) {
goto found_first;
}
if (tmp) {
goto found_middle;
}
size -= BITS_PER_LONG;
result += BITS_PER_LONG;
}
while (size & ~(BITS_PER_LONG-1)) {
if ((tmp = *(p++))) {
goto found_middle;
}
result += BITS_PER_LONG;
size -= BITS_PER_LONG;
}
if (!size) {
return result;
}
tmp = *p;
found_first:
tmp &= (~0UL >> (BITS_PER_LONG - size));
if (tmp == 0UL) { /* Are any bits set? */
return result + size; /* Nope. */
}
found_middle:
return result + bitops_ffsl(tmp);
}
/*
* This implementation of find_{first,next}_zero_bit was stolen from
* Linus' asm-alpha/bitops.h.
*/
unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size,
unsigned long offset)
{
const unsigned long *p = addr + BITOP_WORD(offset);
unsigned long result = offset & ~(BITS_PER_LONG-1);
unsigned long tmp;
if (offset >= size) {
return size;
}
size -= result;
offset %= BITS_PER_LONG;
if (offset) {
tmp = *(p++);
tmp |= ~0UL >> (BITS_PER_LONG - offset);
if (size < BITS_PER_LONG) {
goto found_first;
}
if (~tmp) {
goto found_middle;
}
size -= BITS_PER_LONG;
result += BITS_PER_LONG;
}
while (size & ~(BITS_PER_LONG-1)) {
if (~(tmp = *(p++))) {
goto found_middle;
}
result += BITS_PER_LONG;
size -= BITS_PER_LONG;
}
if (!size) {
return result;
}
tmp = *p;
found_first:
tmp |= ~0UL << size;
if (tmp == ~0UL) { /* Are any bits zero? */
return result + size; /* Nope. */
}
found_middle:
return result + ffz(tmp);
}
unsigned long find_last_bit(const unsigned long *addr, unsigned long size)
{
unsigned long words;
unsigned long tmp;
/* Start at final word. */
words = size / BITS_PER_LONG;
/* Partial final word? */
if (size & (BITS_PER_LONG-1)) {
tmp = (addr[words] & (~0UL >> (BITS_PER_LONG
- (size & (BITS_PER_LONG-1)))));
if (tmp) {
goto found;
}
}
while (words) {
tmp = addr[--words];
if (tmp) {
found:
return words * BITS_PER_LONG + bitops_flsl(tmp);
}
}
/* Not found */
return size;
}

272
bitops.h
View File

@@ -1,272 +0,0 @@
/*
* Bitops Module
*
* Copyright (C) 2010 Corentin Chary <corentin.chary@gmail.com>
*
* Mostly inspired by (stolen from) linux/bitmap.h and linux/bitops.h
*
* 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.
*/
#ifndef BITOPS_H
#define BITOPS_H
#include "qemu-common.h"
#define BITS_PER_BYTE CHAR_BIT
#define BITS_PER_LONG (sizeof (unsigned long) * BITS_PER_BYTE)
#define BIT(nr) (1UL << (nr))
#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
/**
* bitops_ffs - find first bit in word.
* @word: The word to search
*
* Undefined if no bit exists, so code should check against 0 first.
*/
static unsigned long bitops_ffsl(unsigned long word)
{
int num = 0;
#if LONG_MAX > 0x7FFFFFFF
if ((word & 0xffffffff) == 0) {
num += 32;
word >>= 32;
}
#endif
if ((word & 0xffff) == 0) {
num += 16;
word >>= 16;
}
if ((word & 0xff) == 0) {
num += 8;
word >>= 8;
}
if ((word & 0xf) == 0) {
num += 4;
word >>= 4;
}
if ((word & 0x3) == 0) {
num += 2;
word >>= 2;
}
if ((word & 0x1) == 0) {
num += 1;
}
return num;
}
/**
* bitops_fls - find last (most-significant) set bit in a long word
* @word: the word to search
*
* Undefined if no set bit exists, so code should check against 0 first.
*/
static inline unsigned long bitops_flsl(unsigned long word)
{
int num = BITS_PER_LONG - 1;
#if LONG_MAX > 0x7FFFFFFF
if (!(word & (~0ul << 32))) {
num -= 32;
word <<= 32;
}
#endif
if (!(word & (~0ul << (BITS_PER_LONG-16)))) {
num -= 16;
word <<= 16;
}
if (!(word & (~0ul << (BITS_PER_LONG-8)))) {
num -= 8;
word <<= 8;
}
if (!(word & (~0ul << (BITS_PER_LONG-4)))) {
num -= 4;
word <<= 4;
}
if (!(word & (~0ul << (BITS_PER_LONG-2)))) {
num -= 2;
word <<= 2;
}
if (!(word & (~0ul << (BITS_PER_LONG-1))))
num -= 1;
return num;
}
/**
* ffz - find first zero in word.
* @word: The word to search
*
* Undefined if no zero exists, so code should check against ~0UL first.
*/
static inline unsigned long ffz(unsigned long word)
{
return bitops_ffsl(~word);
}
/**
* set_bit - Set a bit in memory
* @nr: the bit to set
* @addr: the address to start counting from
*/
static inline void set_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
*p |= mask;
}
/**
* clear_bit - Clears a bit in memory
* @nr: Bit to clear
* @addr: Address to start counting from
*/
static inline void clear_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
*p &= ~mask;
}
/**
* change_bit - Toggle a bit in memory
* @nr: Bit to change
* @addr: Address to start counting from
*/
static inline void change_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
*p ^= mask;
}
/**
* test_and_set_bit - Set a bit and return its old value
* @nr: Bit to set
* @addr: Address to count from
*/
static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
unsigned long old = *p;
*p = old | mask;
return (old & mask) != 0;
}
/**
* test_and_clear_bit - Clear a bit and return its old value
* @nr: Bit to clear
* @addr: Address to count from
*/
static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
unsigned long old = *p;
*p = old & ~mask;
return (old & mask) != 0;
}
/**
* test_and_change_bit - Change a bit and return its old value
* @nr: Bit to change
* @addr: Address to count from
*/
static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
{
unsigned long mask = BIT_MASK(nr);
unsigned long *p = ((unsigned long *)addr) + BIT_WORD(nr);
unsigned long old = *p;
*p = old ^ mask;
return (old & mask) != 0;
}
/**
* test_bit - Determine whether a bit is set
* @nr: bit number to test
* @addr: Address to start counting from
*/
static inline int test_bit(int nr, const volatile unsigned long *addr)
{
return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
}
/**
* find_last_bit - find the last set bit in a memory region
* @addr: The address to start the search at
* @size: The maximum size to search
*
* Returns the bit number of the first set bit, or size.
*/
unsigned long find_last_bit(const unsigned long *addr,
unsigned long size);
/**
* find_next_bit - find the next set bit in a memory region
* @addr: The address to base the search on
* @offset: The bitnumber to start searching at
* @size: The bitmap size in bits
*/
unsigned long find_next_bit(const unsigned long *addr,
unsigned long size, unsigned long offset);
/**
* find_next_zero_bit - find the next cleared bit in a memory region
* @addr: The address to base the search on
* @offset: The bitnumber to start searching at
* @size: The bitmap size in bits
*/
unsigned long find_next_zero_bit(const unsigned long *addr,
unsigned long size,
unsigned long offset);
/**
* find_first_bit - find the first set bit in a memory region
* @addr: The address to start the search at
* @size: The maximum size to search
*
* Returns the bit number of the first set bit.
*/
static inline unsigned long find_first_bit(const unsigned long *addr,
unsigned long size)
{
return find_next_bit(addr, size, 0);
}
/**
* find_first_zero_bit - find the first cleared bit in a memory region
* @addr: The address to start the search at
* @size: The maximum size to search
*
* Returns the bit number of the first cleared bit.
*/
static inline unsigned long find_first_zero_bit(const unsigned long *addr,
unsigned long size)
{
return find_next_zero_bit(addr, size, 0);
}
static inline unsigned long hweight_long(unsigned long w)
{
unsigned long count;
for (count = 0; w; w >>= 1) {
count += w & 1;
}
return count;
}
#endif

View File

@@ -19,7 +19,6 @@
#include "monitor.h"
#include "block-migration.h"
#include "migration.h"
#include "blockdev.h"
#include <assert.h>
#define BLOCK_SIZE (BDRV_SECTORS_PER_DIRTY_CHUNK << BDRV_SECTOR_BITS)
@@ -50,18 +49,17 @@ typedef struct BlkMigDevState {
int64_t total_sectors;
int64_t dirty;
QSIMPLEQ_ENTRY(BlkMigDevState) entry;
unsigned long *aio_bitmap;
} BlkMigDevState;
typedef struct BlkMigBlock {
uint8_t *buf;
BlkMigDevState *bmds;
int64_t sector;
int nr_sectors;
struct iovec iov;
QEMUIOVector qiov;
BlockDriverAIOCB *aiocb;
int ret;
int64_t time;
QSIMPLEQ_ENTRY(BlkMigBlock) entry;
} BlkMigBlock;
@@ -77,7 +75,6 @@ typedef struct BlkMigState {
int prev_progress;
int bulk_completed;
long double total_time;
long double prev_time_offset;
int reads;
} BlkMigState;
@@ -131,71 +128,29 @@ uint64_t blk_mig_bytes_total(void)
return sum << BDRV_SECTOR_BITS;
}
static inline void add_avg_read_time(int64_t time)
{
block_mig_state.reads++;
block_mig_state.total_time += time;
}
static inline long double compute_read_bwidth(void)
{
assert(block_mig_state.total_time != 0);
return (block_mig_state.reads / block_mig_state.total_time) * BLOCK_SIZE;
}
static int bmds_aio_inflight(BlkMigDevState *bmds, int64_t sector)
{
int64_t chunk = sector / (int64_t)BDRV_SECTORS_PER_DIRTY_CHUNK;
if ((sector << BDRV_SECTOR_BITS) < bdrv_getlength(bmds->bs)) {
return !!(bmds->aio_bitmap[chunk / (sizeof(unsigned long) * 8)] &
(1UL << (chunk % (sizeof(unsigned long) * 8))));
} else {
return 0;
}
}
static void bmds_set_aio_inflight(BlkMigDevState *bmds, int64_t sector_num,
int nb_sectors, int set)
{
int64_t start, end;
unsigned long val, idx, bit;
start = sector_num / BDRV_SECTORS_PER_DIRTY_CHUNK;
end = (sector_num + nb_sectors - 1) / BDRV_SECTORS_PER_DIRTY_CHUNK;
for (; start <= end; start++) {
idx = start / (sizeof(unsigned long) * 8);
bit = start % (sizeof(unsigned long) * 8);
val = bmds->aio_bitmap[idx];
if (set) {
val |= 1UL << bit;
} else {
val &= ~(1UL << bit);
}
bmds->aio_bitmap[idx] = val;
}
}
static void alloc_aio_bitmap(BlkMigDevState *bmds)
{
BlockDriverState *bs = bmds->bs;
int64_t bitmap_size;
bitmap_size = (bdrv_getlength(bs) >> BDRV_SECTOR_BITS) +
BDRV_SECTORS_PER_DIRTY_CHUNK * 8 - 1;
bitmap_size /= BDRV_SECTORS_PER_DIRTY_CHUNK * 8;
bmds->aio_bitmap = qemu_mallocz(bitmap_size);
return (block_mig_state.reads * BLOCK_SIZE)/ block_mig_state.total_time;
}
static void blk_mig_read_cb(void *opaque, int ret)
{
long double curr_time = qemu_get_clock_ns(rt_clock);
BlkMigBlock *blk = opaque;
blk->ret = ret;
block_mig_state.reads++;
block_mig_state.total_time += (curr_time - block_mig_state.prev_time_offset);
block_mig_state.prev_time_offset = curr_time;
blk->time = qemu_get_clock_ns(rt_clock) - blk->time;
add_avg_read_time(blk->time);
QSIMPLEQ_INSERT_TAIL(&block_mig_state.blk_list, blk, entry);
bmds_set_aio_inflight(blk->bmds, blk->sector, blk->nr_sectors, 0);
block_mig_state.submitted--;
block_mig_state.read_done++;
@@ -239,15 +194,12 @@ static int mig_save_device_bulk(Monitor *mon, QEMUFile *f,
blk->buf = qemu_malloc(BLOCK_SIZE);
blk->bmds = bmds;
blk->sector = cur_sector;
blk->nr_sectors = nr_sectors;
blk->iov.iov_base = blk->buf;
blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
if (block_mig_state.submitted == 0) {
block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock);
}
blk->time = qemu_get_clock_ns(rt_clock);
blk->aiocb = bdrv_aio_readv(bs, cur_sector, &blk->qiov,
nr_sectors, blk_mig_read_cb, blk);
@@ -296,9 +248,6 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
bmds->total_sectors = sectors;
bmds->completed_sectors = 0;
bmds->shared_base = block_mig_state.shared_base;
alloc_aio_bitmap(bmds);
drive_get_ref(drive_get_by_blockdev(bs));
bdrv_set_in_use(bs, 1);
block_mig_state.total_sector_sum += sectors;
@@ -350,12 +299,7 @@ static int blk_mig_save_bulked_block(Monitor *mon, QEMUFile *f)
}
}
if (block_mig_state.total_sector_sum != 0) {
progress = completed_sector_sum * 100 /
block_mig_state.total_sector_sum;
} else {
progress = 100;
}
progress = completed_sector_sum * 100 / block_mig_state.total_sector_sum;
if (progress != block_mig_state.prev_progress) {
block_mig_state.prev_progress = progress;
qemu_put_be64(f, (progress << BDRV_SECTOR_BITS)
@@ -385,9 +329,6 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
int nr_sectors;
for (sector = bmds->cur_dirty; sector < bmds->total_sectors;) {
if (bmds_aio_inflight(bmds, sector)) {
qemu_aio_flush();
}
if (bdrv_get_dirty(bmds->bs, sector)) {
if (total_sectors - sector < BDRV_SECTORS_PER_DIRTY_CHUNK) {
@@ -399,16 +340,13 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
blk->buf = qemu_malloc(BLOCK_SIZE);
blk->bmds = bmds;
blk->sector = sector;
blk->nr_sectors = nr_sectors;
if (is_async) {
blk->iov.iov_base = blk->buf;
blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE;
qemu_iovec_init_external(&blk->qiov, &blk->iov, 1);
if (block_mig_state.submitted == 0) {
block_mig_state.prev_time_offset = qemu_get_clock_ns(rt_clock);
}
blk->time = qemu_get_clock_ns(rt_clock);
blk->aiocb = bdrv_aio_readv(bmds->bs, sector, &blk->qiov,
nr_sectors, blk_mig_read_cb, blk);
@@ -416,7 +354,6 @@ static int mig_save_device_dirty(Monitor *mon, QEMUFile *f,
goto error;
}
block_mig_state.submitted++;
bmds_set_aio_inflight(bmds, sector, nr_sectors, 1);
} else {
if (bdrv_read(bmds->bs, sector, blk->buf,
nr_sectors) < 0) {
@@ -512,13 +449,13 @@ static int is_stage2_completed(void)
if (block_mig_state.bulk_completed == 1) {
remaining_dirty = get_remaining_dirty();
if (remaining_dirty == 0) {
return 1;
}
if (remaining_dirty == 0) {
return 1;
}
bwidth = compute_read_bwidth();
bwidth = compute_read_bwidth();
if ((remaining_dirty / bwidth) <=
if ((remaining_dirty / bwidth) <=
migrate_max_downtime()) {
/* finish stage2 because we think that we can finish remaing work
below max_downtime */
@@ -535,13 +472,8 @@ static void blk_mig_cleanup(Monitor *mon)
BlkMigDevState *bmds;
BlkMigBlock *blk;
set_dirty_tracking(0);
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
bdrv_set_in_use(bmds->bs, 0);
drive_put_ref(drive_get_by_blockdev(bmds->bs));
qemu_free(bmds->aio_bitmap);
qemu_free(bmds);
}
@@ -551,6 +483,8 @@ static void blk_mig_cleanup(Monitor *mon)
qemu_free(blk);
}
set_dirty_tracking(0);
monitor_printf(mon, "\n");
}
@@ -642,10 +576,8 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
int len, flags;
char device_name[256];
int64_t addr;
BlockDriverState *bs, *bs_prev = NULL;
BlockDriverState *bs;
uint8_t *buf;
int64_t total_sectors = 0;
int nr_sectors;
do {
addr = qemu_get_be64(f);
@@ -667,26 +599,10 @@ static int block_load(QEMUFile *f, void *opaque, int version_id)
return -EINVAL;
}
if (bs != bs_prev) {
bs_prev = bs;
total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
if (total_sectors <= 0) {
error_report("Error getting length of block device %s",
device_name);
return -EINVAL;
}
}
if (total_sectors - addr < BDRV_SECTORS_PER_DIRTY_CHUNK) {
nr_sectors = total_sectors - addr;
} else {
nr_sectors = BDRV_SECTORS_PER_DIRTY_CHUNK;
}
buf = qemu_malloc(BLOCK_SIZE);
qemu_get_buffer(f, buf, BLOCK_SIZE);
ret = bdrv_write(bs, addr, buf, nr_sectors);
ret = bdrv_write(bs, addr, buf, BDRV_SECTORS_PER_DIRTY_CHUNK);
qemu_free(buf);
if (ret < 0) {

601
block.c
View File

@@ -23,7 +23,6 @@
*/
#include "config-host.h"
#include "qemu-common.h"
#include "trace.h"
#include "monitor.h"
#include "block_int.h"
#include "module.h"
@@ -70,39 +69,6 @@ static BlockDriverState *bs_snapshots;
/* If non-zero, use only whitelisted block drivers */
static int use_bdrv_whitelist;
#ifdef _WIN32
static int is_windows_drive_prefix(const char *filename)
{
return (((filename[0] >= 'a' && filename[0] <= 'z') ||
(filename[0] >= 'A' && filename[0] <= 'Z')) &&
filename[1] == ':');
}
int is_windows_drive(const char *filename)
{
if (is_windows_drive_prefix(filename) &&
filename[2] == '\0')
return 1;
if (strstart(filename, "\\\\.\\", NULL) ||
strstart(filename, "//./", NULL))
return 1;
return 0;
}
#endif
/* check if the path starts with "<protocol>:" */
static int path_has_protocol(const char *path)
{
#ifdef _WIN32
if (is_windows_drive(path) ||
is_windows_drive_prefix(path)) {
return 0;
}
#endif
return strchr(path, ':') != NULL;
}
int path_is_absolute(const char *path)
{
const char *p;
@@ -248,7 +214,7 @@ int bdrv_create_file(const char* filename, QEMUOptionParameter *options)
drv = bdrv_find_protocol(filename);
if (drv == NULL) {
return -ENOENT;
drv = bdrv_find_format("file");
}
return bdrv_create(drv, filename, options);
@@ -277,6 +243,26 @@ void get_tmp_filename(char *filename, int size)
}
#endif
#ifdef _WIN32
static int is_windows_drive_prefix(const char *filename)
{
return (((filename[0] >= 'a' && filename[0] <= 'z') ||
(filename[0] >= 'A' && filename[0] <= 'Z')) &&
filename[1] == ':');
}
int is_windows_drive(const char *filename)
{
if (is_windows_drive_prefix(filename) &&
filename[2] == '\0')
return 1;
if (strstart(filename, "\\\\.\\", NULL) ||
strstart(filename, "//./", NULL))
return 1;
return 0;
}
#endif
/*
* Detect host devices. By convention, /dev/cdrom[N] is always
* recognized as a host CDROM.
@@ -320,11 +306,16 @@ BlockDriver *bdrv_find_protocol(const char *filename)
return drv1;
}
if (!path_has_protocol(filename)) {
#ifdef _WIN32
if (is_windows_drive(filename) ||
is_windows_drive_prefix(filename))
return bdrv_find_format("file");
#endif
p = strchr(filename, ':');
if (!p) {
return bdrv_find_format("file");
}
p = strchr(filename, ':');
assert(p != NULL);
len = p - filename;
if (len > sizeof(protocol) - 1)
len = sizeof(protocol) - 1;
@@ -439,7 +430,13 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
bs->drv = drv;
bs->opaque = qemu_mallocz(drv->instance_size);
if (flags & BDRV_O_CACHE_WB)
/*
* Yes, BDRV_O_NOCACHE aka O_DIRECT means we have to present a
* write cache to the guest. We do need the fdatasync to flush
* out transactions for block allocations, and we maybe have a
* volatile write cache in our backing device to deal with.
*/
if (flags & (BDRV_O_CACHE_WB|BDRV_O_NOCACHE))
bs->enable_write_cache = 1;
/*
@@ -449,7 +446,7 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
open_flags = flags & ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
/*
* Snapshots should be writable.
* Snapshots should be writeable.
*/
if (bs->is_temporary) {
open_flags |= BDRV_O_RDWR;
@@ -526,6 +523,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
BlockDriver *drv)
{
int ret;
int probed = 0;
if (flags & BDRV_O_SNAPSHOT) {
BlockDriverState *bs1;
@@ -586,6 +584,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
/* Find the right image format driver */
if (!drv) {
ret = find_image_format(filename, &drv);
probed = 1;
}
if (!drv) {
@@ -598,6 +597,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
goto unlink_and_fail;
}
bs->probed = probed;
/* If there is a backing file, use it */
if ((flags & BDRV_O_NO_BACKING) == 0 && bs->backing_file[0] != '\0') {
char backing_filename[PATH_MAX];
@@ -605,18 +606,10 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
BlockDriver *back_drv = NULL;
bs->backing_hd = bdrv_new("");
if (path_has_protocol(bs->backing_file)) {
pstrcpy(backing_filename, sizeof(backing_filename),
bs->backing_file);
} else {
path_combine(backing_filename, sizeof(backing_filename),
filename, bs->backing_file);
}
if (bs->backing_format[0] != '\0') {
path_combine(backing_filename, sizeof(backing_filename),
filename, bs->backing_file);
if (bs->backing_format[0] != '\0')
back_drv = bdrv_find_format(bs->backing_format);
}
/* backing files always opened read-only */
back_flags =
@@ -639,7 +632,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
bs->change_cb(bs->change_opaque);
}
return 0;
@@ -678,7 +671,7 @@ void bdrv_close(BlockDriverState *bs)
/* call the change callback */
bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
bs->change_cb(bs->change_opaque);
}
}
@@ -691,22 +684,14 @@ void bdrv_close_all(void)
}
}
/* make a BlockDriverState anonymous by removing from bdrv_state list.
Also, NULL terminate the device_name to prevent double remove */
void bdrv_make_anon(BlockDriverState *bs)
{
if (bs->device_name[0] != '\0') {
QTAILQ_REMOVE(&bdrv_states, bs, list);
}
bs->device_name[0] = '\0';
}
void bdrv_delete(BlockDriverState *bs)
{
assert(!bs->peer);
/* remove from list, if necessary */
bdrv_make_anon(bs);
if (bs->device_name[0] != '\0') {
QTAILQ_REMOVE(&bdrv_states, bs, list);
}
bdrv_close(bs);
if (bs->file != NULL) {
@@ -741,7 +726,7 @@ DeviceState *bdrv_get_attached(BlockDriverState *bs)
* Run consistency checks on an image
*
* Returns 0 if the check could be completed (it doesn't mean that the image is
* free of errors) or -errno when an internal error occurred. The results of the
* free of errors) or -errno when an internal error occured. The results of the
* check are stored in res.
*/
int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
@@ -754,17 +739,15 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res)
return bs->drv->bdrv_check(bs, res);
}
#define COMMIT_BUF_SECTORS 2048
/* commit COW file into the raw image */
int bdrv_commit(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
BlockDriver *backing_drv;
int64_t sector, total_sectors;
int n, ro, open_flags;
int64_t i, total_sectors;
int n, j, ro, open_flags;
int ret = 0, rw_ret = 0;
uint8_t *buf;
unsigned char sector[BDRV_SECTOR_SIZE];
char filename[1024];
BlockDriverState *bs_rw, *bs_ro;
@@ -810,20 +793,22 @@ int bdrv_commit(BlockDriverState *bs)
}
total_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS;
buf = qemu_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
for (i = 0; i < total_sectors;) {
if (drv->bdrv_is_allocated(bs, i, 65536, &n)) {
for(j = 0; j < n; j++) {
if (bdrv_read(bs, i, sector, 1) != 0) {
ret = -EIO;
goto ro_cleanup;
}
for (sector = 0; sector < total_sectors; sector += n) {
if (drv->bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) {
if (bdrv_read(bs, sector, buf, n) != 0) {
ret = -EIO;
goto ro_cleanup;
}
if (bdrv_write(bs->backing_hd, sector, buf, n) != 0) {
ret = -EIO;
goto ro_cleanup;
}
if (bdrv_write(bs->backing_hd, i, sector, 1) != 0) {
ret = -EIO;
goto ro_cleanup;
}
i++;
}
} else {
i += n;
}
}
@@ -840,7 +825,6 @@ int bdrv_commit(BlockDriverState *bs)
bdrv_flush(bs->backing_hd);
ro_cleanup:
qemu_free(buf);
if (ro) {
/* re-open as RO */
@@ -948,14 +932,14 @@ static void set_dirty_bitmap(BlockDriverState *bs, int64_t sector_num,
bit = start % (sizeof(unsigned long) * 8);
val = bs->dirty_bitmap[idx];
if (dirty) {
if (!(val & (1UL << bit))) {
if (!(val & (1 << bit))) {
bs->dirty_count++;
val |= 1UL << bit;
val |= 1 << bit;
}
} else {
if (val & (1UL << bit)) {
if (val & (1 << bit)) {
bs->dirty_count--;
val &= ~(1UL << bit);
val &= ~(1 << bit);
}
}
bs->dirty_bitmap[idx] = val;
@@ -1134,37 +1118,13 @@ int bdrv_truncate(BlockDriverState *bs, int64_t offset)
return -ENOTSUP;
if (bs->read_only)
return -EACCES;
if (bdrv_in_use(bs))
return -EBUSY;
ret = drv->bdrv_truncate(bs, offset);
if (ret == 0) {
ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
if (bs->change_cb) {
bs->change_cb(bs->change_opaque, CHANGE_SIZE);
}
}
return ret;
}
/**
* Length of a allocated file in bytes. Sparse files are counted by actual
* allocated space. Return < 0 if error or unknown.
*/
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
{
BlockDriver *drv = bs->drv;
if (!drv) {
return -ENOMEDIUM;
}
if (drv->bdrv_get_allocated_file_size) {
return drv->bdrv_get_allocated_file_size(bs);
}
if (bs->file) {
return bdrv_get_allocated_file_size(bs->file);
}
return -ENOTSUP;
}
/**
* Length of a file in bytes. Return < 0 if error or unknown.
*/
@@ -1174,12 +1134,14 @@ int64_t bdrv_getlength(BlockDriverState *bs)
if (!drv)
return -ENOMEDIUM;
if (bs->growable || bs->removable) {
if (drv->bdrv_getlength) {
return drv->bdrv_getlength(bs);
}
/* Fixed size devices use the total_sectors value for speed instead of
issuing a length query (like lseek) on each call. Also, legacy block
drivers don't provide a bdrv_getlength function and must use
total_sectors. */
if (!bs->growable || !drv->bdrv_getlength) {
return bs->total_sectors * BDRV_SECTOR_SIZE;
}
return bs->total_sectors * BDRV_SECTOR_SIZE;
return drv->bdrv_getlength(bs);
}
/* return 0 as number of sectors if no device present or error */
@@ -1318,6 +1280,13 @@ void bdrv_set_geometry_hint(BlockDriverState *bs,
bs->secs = secs;
}
void bdrv_set_type_hint(BlockDriverState *bs, int type)
{
bs->type = type;
bs->removable = ((type == BDRV_TYPE_CDROM ||
type == BDRV_TYPE_FLOPPY));
}
void bdrv_set_translation_hint(BlockDriverState *bs, int translation)
{
bs->translation = translation;
@@ -1331,107 +1300,9 @@ void bdrv_get_geometry_hint(BlockDriverState *bs,
*psecs = bs->secs;
}
/* Recognize floppy formats */
typedef struct FDFormat {
FDriveType drive;
uint8_t last_sect;
uint8_t max_track;
uint8_t max_head;
} FDFormat;
static const FDFormat fd_formats[] = {
/* First entry is default format */
/* 1.44 MB 3"1/2 floppy disks */
{ FDRIVE_DRV_144, 18, 80, 1, },
{ FDRIVE_DRV_144, 20, 80, 1, },
{ FDRIVE_DRV_144, 21, 80, 1, },
{ FDRIVE_DRV_144, 21, 82, 1, },
{ FDRIVE_DRV_144, 21, 83, 1, },
{ FDRIVE_DRV_144, 22, 80, 1, },
{ FDRIVE_DRV_144, 23, 80, 1, },
{ FDRIVE_DRV_144, 24, 80, 1, },
/* 2.88 MB 3"1/2 floppy disks */
{ FDRIVE_DRV_288, 36, 80, 1, },
{ FDRIVE_DRV_288, 39, 80, 1, },
{ FDRIVE_DRV_288, 40, 80, 1, },
{ FDRIVE_DRV_288, 44, 80, 1, },
{ FDRIVE_DRV_288, 48, 80, 1, },
/* 720 kB 3"1/2 floppy disks */
{ FDRIVE_DRV_144, 9, 80, 1, },
{ FDRIVE_DRV_144, 10, 80, 1, },
{ FDRIVE_DRV_144, 10, 82, 1, },
{ FDRIVE_DRV_144, 10, 83, 1, },
{ FDRIVE_DRV_144, 13, 80, 1, },
{ FDRIVE_DRV_144, 14, 80, 1, },
/* 1.2 MB 5"1/4 floppy disks */
{ FDRIVE_DRV_120, 15, 80, 1, },
{ FDRIVE_DRV_120, 18, 80, 1, },
{ FDRIVE_DRV_120, 18, 82, 1, },
{ FDRIVE_DRV_120, 18, 83, 1, },
{ FDRIVE_DRV_120, 20, 80, 1, },
/* 720 kB 5"1/4 floppy disks */
{ FDRIVE_DRV_120, 9, 80, 1, },
{ FDRIVE_DRV_120, 11, 80, 1, },
/* 360 kB 5"1/4 floppy disks */
{ FDRIVE_DRV_120, 9, 40, 1, },
{ FDRIVE_DRV_120, 9, 40, 0, },
{ FDRIVE_DRV_120, 10, 41, 1, },
{ FDRIVE_DRV_120, 10, 42, 1, },
/* 320 kB 5"1/4 floppy disks */
{ FDRIVE_DRV_120, 8, 40, 1, },
{ FDRIVE_DRV_120, 8, 40, 0, },
/* 360 kB must match 5"1/4 better than 3"1/2... */
{ FDRIVE_DRV_144, 9, 80, 0, },
/* end */
{ FDRIVE_DRV_NONE, -1, -1, 0, },
};
void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
int *max_track, int *last_sect,
FDriveType drive_in, FDriveType *drive)
int bdrv_get_type_hint(BlockDriverState *bs)
{
const FDFormat *parse;
uint64_t nb_sectors, size;
int i, first_match, match;
bdrv_get_geometry_hint(bs, nb_heads, max_track, last_sect);
if (*nb_heads != 0 && *max_track != 0 && *last_sect != 0) {
/* User defined disk */
} else {
bdrv_get_geometry(bs, &nb_sectors);
match = -1;
first_match = -1;
for (i = 0; ; i++) {
parse = &fd_formats[i];
if (parse->drive == FDRIVE_DRV_NONE) {
break;
}
if (drive_in == parse->drive ||
drive_in == FDRIVE_DRV_NONE) {
size = (parse->max_head + 1) * parse->max_track *
parse->last_sect;
if (nb_sectors == size) {
match = i;
break;
}
if (first_match == -1) {
first_match = i;
}
}
}
if (match == -1) {
if (first_match == -1) {
match = 1;
} else {
match = first_match;
}
parse = &fd_formats[match];
}
*nb_heads = parse->max_head + 1;
*max_track = parse->max_track;
*last_sect = parse->last_sect;
*drive = parse->drive;
}
return bs->type;
}
int bdrv_get_translation_hint(BlockDriverState *bs)
@@ -1481,8 +1352,7 @@ int bdrv_enable_write_cache(BlockDriverState *bs)
/* XXX: no longer used */
void bdrv_set_change_cb(BlockDriverState *bs,
void (*change_cb)(void *opaque, int reason),
void *opaque)
void (*change_cb)(void *opaque), void *opaque)
{
bs->change_cb = change_cb;
bs->change_opaque = opaque;
@@ -1527,7 +1397,7 @@ int bdrv_set_key(BlockDriverState *bs, const char *key)
/* call the change callback now, we skipped it on open */
bs->media_changed = 1;
if (bs->change_cb)
bs->change_cb(bs->change_opaque, CHANGE_MEDIA);
bs->change_cb(bs->change_opaque);
}
return ret;
}
@@ -1585,27 +1455,14 @@ const char *bdrv_get_device_name(BlockDriverState *bs)
return bs->device_name;
}
int bdrv_flush(BlockDriverState *bs)
void bdrv_flush(BlockDriverState *bs)
{
if (bs->open_flags & BDRV_O_NO_FLUSH) {
return 0;
return;
}
if (bs->drv && bs->drv->bdrv_flush) {
return bs->drv->bdrv_flush(bs);
}
/*
* Some block drivers always operate in either writethrough or unsafe mode
* and don't support bdrv_flush therefore. Usually qemu doesn't know how
* the server works (because the behaviour is hardcoded or depends on
* server-side configuration), so we can't ensure that everything is safe
* on disk. Returning an error doesn't work because that would break guests
* even if the server operates in writethrough mode.
*
* Let's hope the user knows what he's doing.
*/
return 0;
if (bs->drv && bs->drv->bdrv_flush)
bs->drv->bdrv_flush(bs);
}
void bdrv_flush_all(void)
@@ -1631,17 +1488,6 @@ int bdrv_has_zero_init(BlockDriverState *bs)
return 1;
}
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
{
if (!bs->drv) {
return -ENOMEDIUM;
}
if (!bs->drv->bdrv_discard) {
return 0;
}
return bs->drv->bdrv_discard(bs, sector_num, nb_sectors);
}
/*
* Returns true iff the specified sector is present in the disk image. Drivers
* not implementing the functionality are assumed to not support backing files,
@@ -1705,8 +1551,9 @@ static void bdrv_print_dict(QObject *obj, void *opaque)
bs_dict = qobject_to_qdict(obj);
monitor_printf(mon, "%s: removable=%d",
monitor_printf(mon, "%s: type=%s removable=%d",
qdict_get_str(bs_dict, "device"),
qdict_get_str(bs_dict, "type"),
qdict_get_bool(bs_dict, "removable"));
if (qdict_get_bool(bs_dict, "removable")) {
@@ -1747,10 +1594,23 @@ void bdrv_info(Monitor *mon, QObject **ret_data)
QTAILQ_FOREACH(bs, &bdrv_states, list) {
QObject *bs_obj;
const char *type = "unknown";
bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': 'unknown', "
switch(bs->type) {
case BDRV_TYPE_HD:
type = "hd";
break;
case BDRV_TYPE_CDROM:
type = "cdrom";
break;
case BDRV_TYPE_FLOPPY:
type = "floppy";
break;
}
bs_obj = qobject_from_jsonf("{ 'device': %s, 'type': %s, "
"'removable': %i, 'locked': %i }",
bs->device_name, bs->removable,
bs->device_name, type, bs->removable,
bs->locked);
if (bs->drv) {
@@ -2041,22 +1901,6 @@ int bdrv_snapshot_list(BlockDriverState *bs,
return -ENOTSUP;
}
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
const char *snapshot_name)
{
BlockDriver *drv = bs->drv;
if (!drv) {
return -ENOMEDIUM;
}
if (!bs->read_only) {
return -EINVAL;
}
if (drv->bdrv_snapshot_load_tmp) {
return drv->bdrv_snapshot_load_tmp(bs, snapshot_name);
}
return -ENOTSUP;
}
#define NB_SUFFIXES 4
char *get_human_readable_size(char *buf, int buf_size, int64_t size)
@@ -2141,8 +1985,6 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
BlockDriver *drv = bs->drv;
BlockDriverAIOCB *ret;
trace_bdrv_aio_readv(bs, sector_num, nb_sectors, opaque);
if (!drv)
return NULL;
if (bdrv_check_request(bs, sector_num, nb_sectors))
@@ -2160,51 +2002,12 @@ BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
return ret;
}
typedef struct BlockCompleteData {
BlockDriverCompletionFunc *cb;
void *opaque;
BlockDriverState *bs;
int64_t sector_num;
int nb_sectors;
} BlockCompleteData;
static void block_complete_cb(void *opaque, int ret)
{
BlockCompleteData *b = opaque;
if (b->bs->dirty_bitmap) {
set_dirty_bitmap(b->bs, b->sector_num, b->nb_sectors, 1);
}
b->cb(b->opaque, ret);
qemu_free(b);
}
static BlockCompleteData *blk_dirty_cb_alloc(BlockDriverState *bs,
int64_t sector_num,
int nb_sectors,
BlockDriverCompletionFunc *cb,
void *opaque)
{
BlockCompleteData *blkdata = qemu_mallocz(sizeof(BlockCompleteData));
blkdata->bs = bs;
blkdata->cb = cb;
blkdata->opaque = opaque;
blkdata->sector_num = sector_num;
blkdata->nb_sectors = nb_sectors;
return blkdata;
}
BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
BlockDriver *drv = bs->drv;
BlockDriverAIOCB *ret;
BlockCompleteData *blk_cb_data;
trace_bdrv_aio_writev(bs, sector_num, nb_sectors, opaque);
if (!drv)
return NULL;
@@ -2214,10 +2017,7 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
return NULL;
if (bs->dirty_bitmap) {
blk_cb_data = blk_dirty_cb_alloc(bs, sector_num, nb_sectors, cb,
opaque);
cb = &block_complete_cb;
opaque = blk_cb_data;
set_dirty_bitmap(bs, sector_num, nb_sectors, 1);
}
ret = drv->bdrv_aio_writev(bs, sector_num, qiov, nb_sectors,
@@ -2266,8 +2066,6 @@ static void multiwrite_cb(void *opaque, int ret)
{
MultiwriteCB *mcb = opaque;
trace_multiwrite_cb(mcb, ret);
if (ret < 0 && !mcb->error) {
mcb->error = ret;
}
@@ -2391,14 +2189,6 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
MultiwriteCB *mcb;
int i;
/* don't submit writes if we don't have a medium */
if (bs->drv == NULL) {
for (i = 0; i < num_reqs; i++) {
reqs[i].error = -ENOMEDIUM;
}
return -1;
}
if (num_reqs == 0) {
return 0;
}
@@ -2416,8 +2206,6 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
// Check for mergable requests
num_reqs = multiwrite_merge(bs, reqs, num_reqs, mcb);
trace_bdrv_aio_multiwrite(mcb, mcb->num_callbacks, num_reqs);
/*
* Run the aio requests. As soon as one request can't be submitted
* successfully, fail all requests that are not yet submitted (we must
@@ -2439,7 +2227,6 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
*/
mcb->num_requests = 1;
// Run the aio requests
for (i = 0; i < num_reqs; i++) {
mcb->num_requests++;
acb = bdrv_aio_writev(bs, reqs[i].sector, reqs[i].qiov,
@@ -2450,10 +2237,8 @@ int bdrv_aio_multiwrite(BlockDriverState *bs, BlockRequest *reqs, int num_reqs)
// submitted yet. Otherwise we'll wait for the submitted AIOs to
// complete and report the error in the callback.
if (i == 0) {
trace_bdrv_aio_multiwrite_earlyfail(mcb);
goto fail;
} else {
trace_bdrv_aio_multiwrite_latefail(mcb, i);
multiwrite_cb(mcb, -EIO);
break;
}
@@ -2478,8 +2263,6 @@ BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
{
BlockDriver *drv = bs->drv;
trace_bdrv_aio_flush(bs, opaque);
if (bs->open_flags & BDRV_O_NO_FLUSH) {
return bdrv_aio_noop_em(bs, cb, opaque);
}
@@ -2802,8 +2585,6 @@ void bdrv_set_locked(BlockDriverState *bs, int locked)
{
BlockDriver *drv = bs->drv;
trace_bdrv_set_locked(bs, locked);
bs->locked = locked;
if (drv && drv->bdrv_set_locked) {
drv->bdrv_set_locked(bs, locked);
@@ -2866,8 +2647,8 @@ int bdrv_get_dirty(BlockDriverState *bs, int64_t sector)
if (bs->dirty_bitmap &&
(sector << BDRV_SECTOR_BITS) < bdrv_getlength(bs)) {
return !!(bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
(1UL << (chunk % (sizeof(unsigned long) * 8))));
return bs->dirty_bitmap[chunk / (sizeof(unsigned long) * 8)] &
(1 << (chunk % (sizeof(unsigned long) * 8)));
} else {
return 0;
}
@@ -2883,157 +2664,3 @@ int64_t bdrv_get_dirty_count(BlockDriverState *bs)
{
return bs->dirty_count;
}
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
{
assert(bs->in_use != in_use);
bs->in_use = in_use;
}
int bdrv_in_use(BlockDriverState *bs)
{
return bs->in_use;
}
int bdrv_img_create(const char *filename, const char *fmt,
const char *base_filename, const char *base_fmt,
char *options, uint64_t img_size, int flags)
{
QEMUOptionParameter *param = NULL, *create_options = NULL;
QEMUOptionParameter *backing_fmt, *backing_file, *size;
BlockDriverState *bs = NULL;
BlockDriver *drv, *proto_drv;
BlockDriver *backing_drv = NULL;
int ret = 0;
/* Find driver and parse its options */
drv = bdrv_find_format(fmt);
if (!drv) {
error_report("Unknown file format '%s'", fmt);
ret = -EINVAL;
goto out;
}
proto_drv = bdrv_find_protocol(filename);
if (!proto_drv) {
error_report("Unknown protocol '%s'", filename);
ret = -EINVAL;
goto out;
}
create_options = append_option_parameters(create_options,
drv->create_options);
create_options = append_option_parameters(create_options,
proto_drv->create_options);
/* Create parameter list with default values */
param = parse_option_parameters("", create_options, param);
set_option_parameter_int(param, BLOCK_OPT_SIZE, img_size);
/* Parse -o options */
if (options) {
param = parse_option_parameters(options, create_options, param);
if (param == NULL) {
error_report("Invalid options for file format '%s'.", fmt);
ret = -EINVAL;
goto out;
}
}
if (base_filename) {
if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
base_filename)) {
error_report("Backing file not supported for file format '%s'",
fmt);
ret = -EINVAL;
goto out;
}
}
if (base_fmt) {
if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
error_report("Backing file format not supported for file "
"format '%s'", fmt);
ret = -EINVAL;
goto out;
}
}
backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
if (backing_file && backing_file->value.s) {
if (!strcmp(filename, backing_file->value.s)) {
error_report("Error: Trying to create an image with the "
"same filename as the backing file");
ret = -EINVAL;
goto out;
}
}
backing_fmt = get_option_parameter(param, BLOCK_OPT_BACKING_FMT);
if (backing_fmt && backing_fmt->value.s) {
backing_drv = bdrv_find_format(backing_fmt->value.s);
if (!backing_drv) {
error_report("Unknown backing file format '%s'",
backing_fmt->value.s);
ret = -EINVAL;
goto out;
}
}
// The size for the image must always be specified, with one exception:
// If we are using a backing file, we can obtain the size from there
size = get_option_parameter(param, BLOCK_OPT_SIZE);
if (size && size->value.n == -1) {
if (backing_file && backing_file->value.s) {
uint64_t size;
char buf[32];
bs = bdrv_new("");
ret = bdrv_open(bs, backing_file->value.s, flags, backing_drv);
if (ret < 0) {
error_report("Could not open '%s'", backing_file->value.s);
goto out;
}
bdrv_get_geometry(bs, &size);
size *= 512;
snprintf(buf, sizeof(buf), "%" PRId64, size);
set_option_parameter(param, BLOCK_OPT_SIZE, buf);
} else {
error_report("Image creation needs a size parameter");
ret = -EINVAL;
goto out;
}
}
printf("Formatting '%s', fmt=%s ", filename, fmt);
print_option_parameters(param);
puts("");
ret = bdrv_create(drv, filename, param);
if (ret < 0) {
if (ret == -ENOTSUP) {
error_report("Formatting or formatting option not supported for "
"file format '%s'", fmt);
} else if (ret == -EFBIG) {
error_report("The image size is too large for file format '%s'",
fmt);
} else {
error_report("%s: error while creating %s: %s", filename, fmt,
strerror(-ret));
}
}
out:
free_option_parameters(create_options);
free_option_parameters(param);
if (bs) {
bdrv_delete(bs);
}
return ret;
}

39
block.h
View File

@@ -35,7 +35,7 @@ typedef struct QEMUSnapshotInfo {
#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_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB)
#define BDRV_SECTOR_BITS 9
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
@@ -66,7 +66,6 @@ int bdrv_create(BlockDriver *drv, const char* filename,
QEMUOptionParameter *options);
int bdrv_create_file(const char* filename, QEMUOptionParameter *options);
BlockDriverState *bdrv_new(const char *device_name);
void bdrv_make_anon(BlockDriverState *bs);
void bdrv_delete(BlockDriverState *bs);
int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags);
int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
@@ -89,7 +88,6 @@ int bdrv_write_sync(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
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);
@@ -111,7 +109,7 @@ int bdrv_check(BlockDriverState *bs, BdrvCheckResult *res);
typedef struct BlockDriverAIOCB BlockDriverAIOCB;
typedef void BlockDriverCompletionFunc(void *opaque, int ret);
typedef void BlockDriverDirtyHandler(BlockDriverState *bs, int64_t sector,
int sector_num);
int sector_num);
BlockDriverAIOCB *bdrv_aio_readv(BlockDriverState *bs, int64_t sector_num,
QEMUIOVector *iov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque);
@@ -119,7 +117,7 @@ BlockDriverAIOCB *bdrv_aio_writev(BlockDriverState *bs, int64_t sector_num,
QEMUIOVector *iov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque);
BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque);
BlockDriverCompletionFunc *cb, void *opaque);
void bdrv_aio_cancel(BlockDriverAIOCB *acb);
typedef struct BlockRequest {
@@ -144,15 +142,17 @@ BlockDriverAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque);
/* Ensure contents are flushed to disk. */
int bdrv_flush(BlockDriverState *bs);
void bdrv_flush(BlockDriverState *bs);
void bdrv_flush_all(void);
void bdrv_close_all(void);
int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
int bdrv_has_zero_init(BlockDriverState *bs);
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
int *pnum);
int *pnum);
#define BDRV_TYPE_HD 0
#define BDRV_TYPE_CDROM 1
#define BDRV_TYPE_FLOPPY 2
#define BIOS_ATA_TRANSLATION_AUTO 0
#define BIOS_ATA_TRANSLATION_NONE 1
#define BIOS_ATA_TRANSLATION_LBA 2
@@ -161,19 +161,11 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
void bdrv_set_geometry_hint(BlockDriverState *bs,
int cyls, int heads, int secs);
void bdrv_set_type_hint(BlockDriverState *bs, int type);
void bdrv_set_translation_hint(BlockDriverState *bs, int translation);
void bdrv_get_geometry_hint(BlockDriverState *bs,
int *pcyls, int *pheads, int *psecs);
typedef enum FDriveType {
FDRIVE_DRV_144 = 0x00, /* 1.44 MB 3"5 drive */
FDRIVE_DRV_288 = 0x01, /* 2.88 MB 3"5 drive */
FDRIVE_DRV_120 = 0x02, /* 1.2 MB 5"25 drive */
FDRIVE_DRV_NONE = 0x03, /* No drive connected */
} FDriveType;
void bdrv_get_floppy_geometry_hint(BlockDriverState *bs, int *nb_heads,
int *max_track, int *last_sect,
FDriveType drive_in, FDriveType *drive);
int bdrv_get_type_hint(BlockDriverState *bs);
int bdrv_get_translation_hint(BlockDriverState *bs);
void bdrv_set_on_error(BlockDriverState *bs, BlockErrorAction on_read_error,
BlockErrorAction on_write_error);
@@ -189,8 +181,7 @@ int bdrv_is_locked(BlockDriverState *bs);
void bdrv_set_locked(BlockDriverState *bs, int locked);
int bdrv_eject(BlockDriverState *bs, int eject_flag);
void bdrv_set_change_cb(BlockDriverState *bs,
void (*change_cb)(void *opaque, int reason),
void *opaque);
void (*change_cb)(void *opaque), void *opaque);
void bdrv_get_format(BlockDriverState *bs, char *buf, int buf_size);
BlockDriverState *bdrv_find(const char *name);
BlockDriverState *bdrv_next(BlockDriverState *bs);
@@ -220,8 +211,6 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
int bdrv_snapshot_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info);
int bdrv_snapshot_load_tmp(BlockDriverState *bs,
const char *snapshot_name);
char *bdrv_snapshot_dump(char *buf, int buf_size, QEMUSnapshotInfo *sn);
char *get_human_readable_size(char *buf, int buf_size, int64_t size);
@@ -236,10 +225,6 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
int64_t pos, int size);
int bdrv_img_create(const char *filename, const char *fmt,
const char *base_filename, const char *base_fmt,
char *options, uint64_t img_size, int flags);
#define BDRV_SECTORS_PER_DIRTY_CHUNK 2048
void bdrv_set_dirty_tracking(BlockDriverState *bs, int enable);
@@ -248,8 +233,6 @@ void bdrv_reset_dirty(BlockDriverState *bs, int64_t cur_sector,
int nr_sectors);
int64_t bdrv_get_dirty_count(BlockDriverState *bs);
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
int bdrv_in_use(BlockDriverState *bs);
typedef enum {
BLKDBG_L1_UPDATE,

View File

@@ -397,9 +397,9 @@ static void blkdebug_close(BlockDriverState *bs)
}
}
static int blkdebug_flush(BlockDriverState *bs)
static void blkdebug_flush(BlockDriverState *bs)
{
return bdrv_flush(bs->file);
bdrv_flush(bs->file);
}
static BlockDriverAIOCB *blkdebug_aio_flush(BlockDriverState *bs,
@@ -439,7 +439,9 @@ static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
struct BlkdebugRule *rule;
BlkdebugVars old_vars = s->vars;
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
if (event < 0 || event >= BLKDBG_EVENT_MAX) {
return;
}
QLIST_FOREACH(rule, &s->rules[event], next) {
process_rule(bs, rule, &old_vars);

View File

@@ -1,383 +0,0 @@
/*
* Block protocol for block driver correctness testing
*
* Copyright (C) 2010 IBM, Corp.
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include <stdarg.h>
#include "qemu_socket.h" /* for EINPROGRESS on Windows */
#include "block_int.h"
typedef struct {
BlockDriverState *test_file;
} BDRVBlkverifyState;
typedef struct BlkverifyAIOCB BlkverifyAIOCB;
struct BlkverifyAIOCB {
BlockDriverAIOCB common;
QEMUBH *bh;
/* Request metadata */
bool is_write;
int64_t sector_num;
int nb_sectors;
int ret; /* first completed request's result */
unsigned int done; /* completion counter */
bool *finished; /* completion signal for cancel */
QEMUIOVector *qiov; /* user I/O vector */
QEMUIOVector raw_qiov; /* cloned I/O vector for raw file */
void *buf; /* buffer for raw file I/O */
void (*verify)(BlkverifyAIOCB *acb);
};
static void blkverify_aio_cancel(BlockDriverAIOCB *blockacb)
{
BlkverifyAIOCB *acb = (BlkverifyAIOCB *)blockacb;
bool finished = false;
/* Wait until request completes, invokes its callback, and frees itself */
acb->finished = &finished;
while (!finished) {
qemu_aio_wait();
}
}
static AIOPool blkverify_aio_pool = {
.aiocb_size = sizeof(BlkverifyAIOCB),
.cancel = blkverify_aio_cancel,
};
static void GCC_FMT_ATTR(2, 3) blkverify_err(BlkverifyAIOCB *acb,
const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "blkverify: %s sector_num=%" PRId64 " nb_sectors=%d ",
acb->is_write ? "write" : "read", acb->sector_num,
acb->nb_sectors);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
exit(1);
}
/* Valid blkverify filenames look like blkverify:path/to/raw_image:path/to/image */
static int blkverify_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVBlkverifyState *s = bs->opaque;
int ret;
char *raw, *c;
/* Parse the blkverify: prefix */
if (strncmp(filename, "blkverify:", strlen("blkverify:"))) {
return -EINVAL;
}
filename += strlen("blkverify:");
/* Parse the raw image filename */
c = strchr(filename, ':');
if (c == NULL) {
return -EINVAL;
}
raw = strdup(filename);
raw[c - filename] = '\0';
ret = bdrv_file_open(&bs->file, raw, flags);
free(raw);
if (ret < 0) {
return ret;
}
filename = c + 1;
/* Open the test file */
s->test_file = bdrv_new("");
ret = bdrv_open(s->test_file, filename, flags, NULL);
if (ret < 0) {
bdrv_delete(s->test_file);
s->test_file = NULL;
return ret;
}
return 0;
}
static void blkverify_close(BlockDriverState *bs)
{
BDRVBlkverifyState *s = bs->opaque;
bdrv_delete(s->test_file);
s->test_file = NULL;
}
static int blkverify_flush(BlockDriverState *bs)
{
BDRVBlkverifyState *s = bs->opaque;
/* Only flush test file, the raw file is not important */
return bdrv_flush(s->test_file);
}
static int64_t blkverify_getlength(BlockDriverState *bs)
{
BDRVBlkverifyState *s = bs->opaque;
return bdrv_getlength(s->test_file);
}
/**
* Check that I/O vector contents are identical
*
* @a: I/O vector
* @b: I/O vector
* @ret: Offset to first mismatching byte or -1 if match
*/
static ssize_t blkverify_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
{
int i;
ssize_t offset = 0;
assert(a->niov == b->niov);
for (i = 0; i < a->niov; i++) {
size_t len = 0;
uint8_t *p = (uint8_t *)a->iov[i].iov_base;
uint8_t *q = (uint8_t *)b->iov[i].iov_base;
assert(a->iov[i].iov_len == b->iov[i].iov_len);
while (len < a->iov[i].iov_len && *p++ == *q++) {
len++;
}
offset += len;
if (len != a->iov[i].iov_len) {
return offset;
}
}
return -1;
}
typedef struct {
int src_index;
struct iovec *src_iov;
void *dest_base;
} IOVectorSortElem;
static int sortelem_cmp_src_base(const void *a, const void *b)
{
const IOVectorSortElem *elem_a = a;
const IOVectorSortElem *elem_b = b;
/* Don't overflow */
if (elem_a->src_iov->iov_base < elem_b->src_iov->iov_base) {
return -1;
} else if (elem_a->src_iov->iov_base > elem_b->src_iov->iov_base) {
return 1;
} else {
return 0;
}
}
static int sortelem_cmp_src_index(const void *a, const void *b)
{
const IOVectorSortElem *elem_a = a;
const IOVectorSortElem *elem_b = b;
return elem_a->src_index - elem_b->src_index;
}
/**
* Copy contents of I/O vector
*
* The relative relationships of overlapping iovecs are preserved. This is
* necessary to ensure identical semantics in the cloned I/O vector.
*/
static void blkverify_iovec_clone(QEMUIOVector *dest, const QEMUIOVector *src,
void *buf)
{
IOVectorSortElem sortelems[src->niov];
void *last_end;
int i;
/* Sort by source iovecs by base address */
for (i = 0; i < src->niov; i++) {
sortelems[i].src_index = i;
sortelems[i].src_iov = &src->iov[i];
}
qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_base);
/* Allocate buffer space taking into account overlapping iovecs */
last_end = NULL;
for (i = 0; i < src->niov; i++) {
struct iovec *cur = sortelems[i].src_iov;
ptrdiff_t rewind = 0;
/* Detect overlap */
if (last_end && last_end > cur->iov_base) {
rewind = last_end - cur->iov_base;
}
sortelems[i].dest_base = buf - rewind;
buf += cur->iov_len - MIN(rewind, cur->iov_len);
last_end = MAX(cur->iov_base + cur->iov_len, last_end);
}
/* Sort by source iovec index and build destination iovec */
qsort(sortelems, src->niov, sizeof(sortelems[0]), sortelem_cmp_src_index);
for (i = 0; i < src->niov; i++) {
qemu_iovec_add(dest, sortelems[i].dest_base, src->iov[i].iov_len);
}
}
static BlkverifyAIOCB *blkverify_aio_get(BlockDriverState *bs, bool is_write,
int64_t sector_num, QEMUIOVector *qiov,
int nb_sectors,
BlockDriverCompletionFunc *cb,
void *opaque)
{
BlkverifyAIOCB *acb = qemu_aio_get(&blkverify_aio_pool, bs, cb, opaque);
acb->bh = NULL;
acb->is_write = is_write;
acb->sector_num = sector_num;
acb->nb_sectors = nb_sectors;
acb->ret = -EINPROGRESS;
acb->done = 0;
acb->qiov = qiov;
acb->buf = NULL;
acb->verify = NULL;
acb->finished = NULL;
return acb;
}
static void blkverify_aio_bh(void *opaque)
{
BlkverifyAIOCB *acb = opaque;
qemu_bh_delete(acb->bh);
if (acb->buf) {
qemu_iovec_destroy(&acb->raw_qiov);
qemu_vfree(acb->buf);
}
acb->common.cb(acb->common.opaque, acb->ret);
if (acb->finished) {
*acb->finished = true;
}
qemu_aio_release(acb);
}
static void blkverify_aio_cb(void *opaque, int ret)
{
BlkverifyAIOCB *acb = opaque;
switch (++acb->done) {
case 1:
acb->ret = ret;
break;
case 2:
if (acb->ret != ret) {
blkverify_err(acb, "return value mismatch %d != %d", acb->ret, ret);
}
if (acb->verify) {
acb->verify(acb);
}
acb->bh = qemu_bh_new(blkverify_aio_bh, acb);
qemu_bh_schedule(acb->bh);
break;
}
}
static void blkverify_verify_readv(BlkverifyAIOCB *acb)
{
ssize_t offset = blkverify_iovec_compare(acb->qiov, &acb->raw_qiov);
if (offset != -1) {
blkverify_err(acb, "contents mismatch in sector %" PRId64,
acb->sector_num + (int64_t)(offset / BDRV_SECTOR_SIZE));
}
}
static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
BDRVBlkverifyState *s = bs->opaque;
BlkverifyAIOCB *acb = blkverify_aio_get(bs, false, sector_num, qiov,
nb_sectors, cb, opaque);
acb->verify = blkverify_verify_readv;
acb->buf = qemu_blockalign(bs->file, qiov->size);
qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov);
blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf);
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;
}
static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
BDRVBlkverifyState *s = bs->opaque;
BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov,
nb_sectors, cb, opaque);
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;
}
static BlockDriverAIOCB *blkverify_aio_flush(BlockDriverState *bs,
BlockDriverCompletionFunc *cb,
void *opaque)
{
BDRVBlkverifyState *s = bs->opaque;
/* Only flush test file, the raw file is not important */
return bdrv_aio_flush(s->test_file, cb, opaque);
}
static BlockDriver bdrv_blkverify = {
.format_name = "blkverify",
.protocol_name = "blkverify",
.instance_size = sizeof(BDRVBlkverifyState),
.bdrv_getlength = blkverify_getlength,
.bdrv_file_open = blkverify_open,
.bdrv_close = blkverify_close,
.bdrv_flush = blkverify_flush,
.bdrv_aio_readv = blkverify_aio_readv,
.bdrv_aio_writev = blkverify_aio_writev,
.bdrv_aio_flush = blkverify_aio_flush,
};
static void bdrv_blkverify_init(void)
{
bdrv_register(&bdrv_blkverify);
}
block_init(bdrv_blkverify_init);

View File

@@ -282,9 +282,9 @@ exit:
return ret;
}
static int cow_flush(BlockDriverState *bs)
static void cow_flush(BlockDriverState *bs)
{
return bdrv_flush(bs->file);
bdrv_flush(bs->file);
}
static QEMUOptionParameter cow_create_options[] = {

View File

@@ -29,154 +29,70 @@
#include "qemu-common.h"
#include "nbd.h"
#include "module.h"
#include "qemu_socket.h"
#include <sys/types.h>
#include <unistd.h>
#define EN_OPTSTR ":exportname="
/* #define DEBUG_NBD */
#if defined(DEBUG_NBD)
#define logout(fmt, ...) \
fprintf(stderr, "nbd\t%-24s" fmt, __func__, ##__VA_ARGS__)
#else
#define logout(fmt, ...) ((void)0)
#endif
typedef struct BDRVNBDState {
int sock;
off_t size;
size_t blocksize;
char *export_name; /* An NBD server may export several devices */
/* If it begins with '/', this is a UNIX domain socket. Otherwise,
* it's a string of the form <hostname|ip4|\[ip6\]>:port
*/
char *host_spec;
} BDRVNBDState;
static int nbd_config(BDRVNBDState *s, const char *filename, int flags)
{
char *file;
char *export_name;
const char *host_spec;
const char *unixpath;
int err = -EINVAL;
file = qemu_strdup(filename);
export_name = strstr(file, EN_OPTSTR);
if (export_name) {
if (export_name[strlen(EN_OPTSTR)] == 0) {
goto out;
}
export_name[0] = 0; /* truncate 'file' */
export_name += strlen(EN_OPTSTR);
s->export_name = qemu_strdup(export_name);
}
/* extract the host_spec - fail if it's not nbd:... */
if (!strstart(file, "nbd:", &host_spec)) {
goto out;
}
/* are we a UNIX or TCP socket? */
if (strstart(host_spec, "unix:", &unixpath)) {
if (unixpath[0] != '/') { /* We demand an absolute path*/
goto out;
}
s->host_spec = qemu_strdup(unixpath);
} else {
s->host_spec = qemu_strdup(host_spec);
}
err = 0;
out:
qemu_free(file);
if (err != 0) {
qemu_free(s->export_name);
qemu_free(s->host_spec);
}
return err;
}
static int nbd_establish_connection(BlockDriverState *bs)
static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
{
BDRVNBDState *s = bs->opaque;
const char *host;
const char *unixpath;
int sock;
int ret;
off_t size;
size_t blocksize;
uint32_t nbdflags;
int ret;
if (!strstart(filename, "nbd:", &host))
return -EINVAL;
if (strstart(host, "unix:", &unixpath)) {
if (unixpath[0] != '/')
return -EINVAL;
sock = unix_socket_outgoing(unixpath);
if (s->host_spec[0] == '/') {
sock = unix_socket_outgoing(s->host_spec);
} else {
sock = tcp_socket_outgoing_spec(s->host_spec);
uint16_t port;
char *p, *r;
char hostname[128];
pstrcpy(hostname, 128, host);
p = strchr(hostname, ':');
if (p == NULL)
return -EINVAL;
*p = '\0';
p++;
port = strtol(p, &r, 0);
if (r == p)
return -EINVAL;
sock = tcp_socket_outgoing(hostname, port);
}
/* Failed to establish connection */
if (sock == -1) {
logout("Failed to establish connection to NBD server\n");
if (sock == -1)
return -errno;
}
/* NBD handshake */
ret = nbd_receive_negotiate(sock, s->export_name, &nbdflags, &size,
&blocksize);
if (ret == -1) {
logout("Failed to negotiate with the NBD server\n");
closesocket(sock);
ret = nbd_receive_negotiate(sock, &size, &blocksize);
if (ret == -1)
return -errno;
}
/* Now that we're connected, set the socket to be non-blocking */
socket_set_nonblock(sock);
s->sock = sock;
s->size = size;
s->blocksize = blocksize;
logout("Established connection with NBD server\n");
return 0;
}
static void nbd_teardown_connection(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
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);
closesocket(s->sock);
}
static int nbd_open(BlockDriverState *bs, const char* filename, int flags)
{
BDRVNBDState *s = bs->opaque;
int result;
/* Pop the config into our state object. Exit if invalid. */
result = nbd_config(s, filename, flags);
if (result != 0) {
return result;
}
/* establish TCP connection, return error if it fails
* TODO: Configurable retry-until-timeout behaviour.
*/
result = nbd_establish_connection(bs);
return result;
}
static int nbd_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
@@ -240,10 +156,15 @@ static int nbd_write(BlockDriverState *bs, int64_t sector_num,
static void nbd_close(BlockDriverState *bs)
{
BDRVNBDState *s = bs->opaque;
qemu_free(s->export_name);
qemu_free(s->host_spec);
struct nbd_request request;
nbd_teardown_connection(bs);
request.type = NBD_CMD_DISC;
request.handle = (uint64_t)(intptr_t)bs;
request.from = 0;
request.len = 0;
nbd_send_request(s->sock, &request);
close(s->sock);
}
static int64_t nbd_getlength(BlockDriverState *bs)

View File

@@ -54,6 +54,7 @@ typedef struct QCowHeader {
#define L2_CACHE_SIZE 16
typedef struct BDRVQcowState {
BlockDriverState *hd;
int cluster_bits;
int cluster_size;
int cluster_sectors;
@@ -496,8 +497,6 @@ typedef struct QCowAIOCB {
uint64_t cluster_offset;
uint8_t *cluster_data;
struct iovec hd_iov;
bool is_write;
QEMUBH *bh;
QEMUIOVector hd_qiov;
BlockDriverAIOCB *hd_aiocb;
} QCowAIOCB;
@@ -527,8 +526,6 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
acb->hd_aiocb = NULL;
acb->sector_num = sector_num;
acb->qiov = qiov;
acb->is_write = is_write;
if (qiov->niov > 1) {
acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size);
if (is_write)
@@ -542,38 +539,6 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs,
return acb;
}
static void qcow_aio_read_cb(void *opaque, int ret);
static void qcow_aio_write_cb(void *opaque, int ret);
static void qcow_aio_rw_bh(void *opaque)
{
QCowAIOCB *acb = opaque;
qemu_bh_delete(acb->bh);
acb->bh = NULL;
if (acb->is_write) {
qcow_aio_write_cb(opaque, 0);
} else {
qcow_aio_read_cb(opaque, 0);
}
}
static int qcow_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb)
{
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 qcow_aio_read_cb(void *opaque, int ret)
{
QCowAIOCB *acb = opaque;
@@ -625,10 +590,8 @@ static void qcow_aio_read_cb(void *opaque, int ret)
qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1);
acb->hd_aiocb = bdrv_aio_readv(bs->backing_hd, acb->sector_num,
&acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
if (acb->hd_aiocb == NULL)
goto done;
}
} else {
/* Note: in this case, no need to wait */
memset(acb->buf, 0, 512 * acb->n);
@@ -636,10 +599,8 @@ static void qcow_aio_read_cb(void *opaque, int ret)
}
} else if (acb->cluster_offset & QCOW_OFLAG_COMPRESSED) {
/* add AIO support for compressed blocks ? */
if (decompress_cluster(bs, acb->cluster_offset) < 0) {
ret = -EIO;
if (decompress_cluster(bs, acb->cluster_offset) < 0)
goto done;
}
memcpy(acb->buf,
s->cluster_cache + index_in_cluster * 512, 512 * acb->n);
goto redo;
@@ -654,10 +615,8 @@ static void qcow_aio_read_cb(void *opaque, int ret)
acb->hd_aiocb = bdrv_aio_readv(bs->file,
(acb->cluster_offset >> 9) + index_in_cluster,
&acb->hd_qiov, acb->n, qcow_aio_read_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
if (acb->hd_aiocb == NULL)
goto done;
}
}
return;
@@ -676,21 +635,12 @@ static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque)
{
QCowAIOCB *acb;
int ret;
acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
if (!acb)
return NULL;
ret = qcow_schedule_bh(qcow_aio_rw_bh, acb);
if (ret < 0) {
if (acb->qiov->niov > 1) {
qemu_vfree(acb->orig_buf);
}
qemu_aio_release(acb);
return NULL;
}
qcow_aio_read_cb(acb, 0);
return &acb->common;
}
@@ -751,10 +701,8 @@ static void qcow_aio_write_cb(void *opaque, int ret)
(cluster_offset >> 9) + index_in_cluster,
&acb->hd_qiov, acb->n,
qcow_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
if (acb->hd_aiocb == NULL)
goto done;
}
return;
done:
@@ -770,7 +718,6 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs,
{
BDRVQcowState *s = bs->opaque;
QCowAIOCB *acb;
int ret;
s->cluster_cache_offset = -1; /* disable compressed cache */
@@ -779,15 +726,7 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs,
return NULL;
ret = qcow_schedule_bh(qcow_aio_rw_bh, acb);
if (ret < 0) {
if (acb->qiov->niov > 1) {
qemu_vfree(acb->orig_buf);
}
qemu_aio_release(acb);
return NULL;
}
qcow_aio_write_cb(acb, 0);
return &acb->common;
}
@@ -971,9 +910,9 @@ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num,
return 0;
}
static int qcow_flush(BlockDriverState *bs)
static void qcow_flush(BlockDriverState *bs)
{
return bdrv_flush(bs->file);
bdrv_flush(bs->file);
}
static BlockDriverAIOCB *qcow_aio_flush(BlockDriverState *bs,

View File

@@ -1,326 +0,0 @@
/*
* L2/refcount table cache for the QCOW2 format
*
* Copyright (c) 2010 Kevin Wolf <kwolf@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "block_int.h"
#include "qemu-common.h"
#include "qcow2.h"
typedef struct Qcow2CachedTable {
void* table;
int64_t offset;
bool dirty;
int cache_hits;
int ref;
} Qcow2CachedTable;
struct Qcow2Cache {
Qcow2CachedTable* entries;
struct Qcow2Cache* depends;
int size;
bool depends_on_flush;
bool writethrough;
};
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
bool writethrough)
{
BDRVQcowState *s = bs->opaque;
Qcow2Cache *c;
int i;
c = qemu_mallocz(sizeof(*c));
c->size = num_tables;
c->entries = qemu_mallocz(sizeof(*c->entries) * num_tables);
c->writethrough = writethrough;
for (i = 0; i < c->size; i++) {
c->entries[i].table = qemu_blockalign(bs, s->cluster_size);
}
return c;
}
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c)
{
int i;
for (i = 0; i < c->size; i++) {
assert(c->entries[i].ref == 0);
qemu_vfree(c->entries[i].table);
}
qemu_free(c->entries);
qemu_free(c);
return 0;
}
static int qcow2_cache_flush_dependency(BlockDriverState *bs, Qcow2Cache *c)
{
int ret;
ret = qcow2_cache_flush(bs, c->depends);
if (ret < 0) {
return ret;
}
c->depends = NULL;
c->depends_on_flush = false;
return 0;
}
static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i)
{
BDRVQcowState *s = bs->opaque;
int ret = 0;
if (!c->entries[i].dirty || !c->entries[i].offset) {
return 0;
}
if (c->depends) {
ret = qcow2_cache_flush_dependency(bs, c);
} else if (c->depends_on_flush) {
ret = bdrv_flush(bs->file);
if (ret >= 0) {
c->depends_on_flush = false;
}
}
if (ret < 0) {
return ret;
}
if (c == s->refcount_block_cache) {
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
} else if (c == s->l2_table_cache) {
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
}
ret = bdrv_pwrite(bs->file, c->entries[i].offset, c->entries[i].table,
s->cluster_size);
if (ret < 0) {
return ret;
}
c->entries[i].dirty = false;
return 0;
}
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c)
{
int result = 0;
int ret;
int i;
for (i = 0; i < c->size; i++) {
ret = qcow2_cache_entry_flush(bs, c, i);
if (ret < 0 && result != -ENOSPC) {
result = ret;
}
}
if (result == 0) {
ret = bdrv_flush(bs->file);
if (ret < 0) {
result = ret;
}
}
return result;
}
int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
Qcow2Cache *dependency)
{
int ret;
if (dependency->depends) {
ret = qcow2_cache_flush_dependency(bs, dependency);
if (ret < 0) {
return ret;
}
}
if (c->depends && (c->depends != dependency)) {
ret = qcow2_cache_flush_dependency(bs, c);
if (ret < 0) {
return ret;
}
}
c->depends = dependency;
return 0;
}
void qcow2_cache_depends_on_flush(Qcow2Cache *c)
{
c->depends_on_flush = true;
}
static int qcow2_cache_find_entry_to_replace(Qcow2Cache *c)
{
int i;
int min_count = INT_MAX;
int min_index = -1;
for (i = 0; i < c->size; i++) {
if (c->entries[i].ref) {
continue;
}
if (c->entries[i].cache_hits < min_count) {
min_index = i;
min_count = c->entries[i].cache_hits;
}
/* Give newer hits priority */
/* TODO Check how to optimize the replacement strategy */
c->entries[i].cache_hits /= 2;
}
if (min_index == -1) {
/* This can't happen in current synchronous code, but leave the check
* here as a reminder for whoever starts using AIO with the cache */
abort();
}
return min_index;
}
static int qcow2_cache_do_get(BlockDriverState *bs, Qcow2Cache *c,
uint64_t offset, void **table, bool read_from_disk)
{
BDRVQcowState *s = bs->opaque;
int i;
int ret;
/* Check if the table is already cached */
for (i = 0; i < c->size; i++) {
if (c->entries[i].offset == offset) {
goto found;
}
}
/* If not, write a table back and replace it */
i = qcow2_cache_find_entry_to_replace(c);
if (i < 0) {
return i;
}
ret = qcow2_cache_entry_flush(bs, c, i);
if (ret < 0) {
return ret;
}
c->entries[i].offset = 0;
if (read_from_disk) {
if (c == s->l2_table_cache) {
BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
}
ret = bdrv_pread(bs->file, offset, c->entries[i].table, s->cluster_size);
if (ret < 0) {
return ret;
}
}
/* Give the table some hits for the start so that it won't be replaced
* immediately. The number 32 is completely arbitrary. */
c->entries[i].cache_hits = 32;
c->entries[i].offset = offset;
/* And return the right table */
found:
c->entries[i].cache_hits++;
c->entries[i].ref++;
*table = c->entries[i].table;
return 0;
}
int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
void **table)
{
return qcow2_cache_do_get(bs, c, offset, table, true);
}
int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
void **table)
{
return qcow2_cache_do_get(bs, c, offset, table, false);
}
int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table)
{
int i;
for (i = 0; i < c->size; i++) {
if (c->entries[i].table == *table) {
goto found;
}
}
return -ENOENT;
found:
c->entries[i].ref--;
*table = NULL;
assert(c->entries[i].ref >= 0);
if (c->writethrough) {
return qcow2_cache_entry_flush(bs, c, i);
} else {
return 0;
}
}
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table)
{
int i;
for (i = 0; i < c->size; i++) {
if (c->entries[i].table == table) {
goto found;
}
}
abort();
found:
c->entries[i].dirty = true;
}
bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
bool enable)
{
bool old = c->writethrough;
if (!old && enable) {
qcow2_cache_flush(bs, c);
}
c->writethrough = enable;
return old;
}

View File

@@ -28,7 +28,7 @@
#include "block_int.h"
#include "block/qcow2.h"
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size)
{
BDRVQcowState *s = bs->opaque;
int new_l1_size, new_l1_size2, ret, i;
@@ -36,22 +36,15 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
int64_t new_l1_table_offset;
uint8_t data[12];
if (min_size <= s->l1_size)
new_l1_size = s->l1_size;
if (min_size <= new_l1_size)
return 0;
if (exact_size) {
new_l1_size = min_size;
} else {
/* Bump size up to reduce the number of times we have to grow */
new_l1_size = s->l1_size;
if (new_l1_size == 0) {
new_l1_size = 1;
}
while (min_size > new_l1_size) {
new_l1_size = (new_l1_size * 3 + 1) / 2;
}
if (new_l1_size == 0) {
new_l1_size = 1;
}
while (min_size > new_l1_size) {
new_l1_size = (new_l1_size * 3 + 1) / 2;
}
#ifdef DEBUG_ALLOC2
printf("grow l1_table from %d to %d\n", s->l1_size, new_l1_size);
#endif
@@ -68,11 +61,6 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
return new_l1_table_offset;
}
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
goto fail;
}
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE);
for(i = 0; i < s->l1_size; i++)
new_l1_table[i] = cpu_to_be64(new_l1_table[i]);
@@ -85,7 +73,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
/* set new table */
BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE);
cpu_to_be32w((uint32_t*)data, new_l1_size);
cpu_to_be64wu((uint64_t*)(data + 4), new_l1_table_offset);
cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset);
ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data));
if (ret < 0) {
goto fail;
@@ -102,6 +90,63 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size)
return ret;
}
void qcow2_l2_cache_reset(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
memset(s->l2_cache, 0, s->l2_size * L2_CACHE_SIZE * sizeof(uint64_t));
memset(s->l2_cache_offsets, 0, L2_CACHE_SIZE * sizeof(uint64_t));
memset(s->l2_cache_counts, 0, L2_CACHE_SIZE * sizeof(uint32_t));
}
static inline int l2_cache_new_entry(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
uint32_t min_count;
int min_index, i;
/* find a new entry in the least used one */
min_index = 0;
min_count = 0xffffffff;
for(i = 0; i < L2_CACHE_SIZE; i++) {
if (s->l2_cache_counts[i] < min_count) {
min_count = s->l2_cache_counts[i];
min_index = i;
}
}
return min_index;
}
/*
* seek_l2_table
*
* seek l2_offset in the l2_cache table
* if not found, return NULL,
* if found,
* increments the l2 cache hit count of the entry,
* if counter overflow, divide by two all counters
* return the pointer to the l2 cache entry
*
*/
static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset)
{
int i, j;
for(i = 0; i < L2_CACHE_SIZE; i++) {
if (l2_offset == s->l2_cache_offsets[i]) {
/* increment the hit count */
if (++s->l2_cache_counts[i] == 0xffffffff) {
for(j = 0; j < L2_CACHE_SIZE; j++) {
s->l2_cache_counts[j] >>= 1;
}
}
return s->l2_cache + (i << s->l2_bits);
}
}
return NULL;
}
/*
* l2_load
*
@@ -116,11 +161,32 @@ static int l2_load(BlockDriverState *bs, uint64_t l2_offset,
uint64_t **l2_table)
{
BDRVQcowState *s = bs->opaque;
int min_index;
int ret;
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset, (void**) l2_table);
/* seek if the table for the given offset is in the cache */
return ret;
*l2_table = seek_l2_table(s, l2_offset);
if (*l2_table != NULL) {
return 0;
}
/* not found: load a new entry in the least used one */
min_index = l2_cache_new_entry(bs);
*l2_table = s->l2_cache + (min_index << s->l2_bits);
BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD);
ret = bdrv_pread(bs->file, l2_offset, *l2_table,
s->l2_size * sizeof(uint64_t));
if (ret < 0) {
return ret;
}
s->l2_cache_offsets[min_index] = l2_offset;
s->l2_cache_counts[min_index] = 1;
return 0;
}
/*
@@ -163,6 +229,7 @@ static int write_l1_entry(BlockDriverState *bs, int l1_index)
static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
{
BDRVQcowState *s = bs->opaque;
int min_index;
uint64_t old_l2_offset;
uint64_t *l2_table;
int64_t l2_offset;
@@ -177,47 +244,27 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
return l2_offset;
}
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
if (ret < 0) {
goto fail;
}
/* allocate a new entry in the l2 cache */
ret = qcow2_cache_get_empty(bs, s->l2_table_cache, l2_offset, (void**) table);
if (ret < 0) {
return ret;
}
l2_table = *table;
min_index = l2_cache_new_entry(bs);
l2_table = s->l2_cache + (min_index << s->l2_bits);
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 {
uint64_t* old_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,
(void**) &old_table);
if (ret < 0) {
goto fail;
}
memcpy(l2_table, old_table, s->cluster_size);
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &old_table);
ret = bdrv_pread(bs->file, old_l2_offset, l2_table,
s->l2_size * sizeof(uint64_t));
if (ret < 0) {
goto fail;
}
}
/* write the l2 table to the file */
BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE);
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
ret = qcow2_cache_flush(bs, s->l2_table_cache);
ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table,
s->l2_size * sizeof(uint64_t));
if (ret < 0) {
goto fail;
}
@@ -229,12 +276,17 @@ static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table)
goto fail;
}
/* update the l2 cache entry */
s->l2_cache_offsets[min_index] = l2_offset;
s->l2_cache_counts[min_index] = 1;
*table = l2_table;
return 0;
fail:
qcow2_cache_put(bs, s->l2_table_cache, (void**) table);
s->l1_table[l1_index] = old_l2_offset;
qcow2_l2_cache_reset(bs);
return ret;
}
@@ -290,14 +342,12 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
}
static int qcow2_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
static int qcow_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
BDRVQcowState *s = bs->opaque;
int ret, index_in_cluster, n, n1;
uint64_t cluster_offset;
struct iovec iov;
QEMUIOVector qiov;
while (nb_sectors > 0) {
n = nb_sectors;
@@ -312,11 +362,7 @@ static int qcow2_read(BlockDriverState *bs, int64_t sector_num,
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);
n1 = qcow2_backing_read1(bs->backing_hd, sector_num, buf, n);
if (n1 > 0) {
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING);
ret = bdrv_read(bs->backing_hd, sector_num, buf, n1);
@@ -357,7 +403,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
if (n <= 0)
return 0;
BLKDBG_EVENT(bs->file, BLKDBG_COW_READ);
ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n);
ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n);
if (ret < 0)
return ret;
if (s->crypt_method) {
@@ -367,7 +413,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect,
&s->aes_encrypt_key);
}
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start,
ret = bdrv_write_sync(bs->file, (cluster_offset >> 9) + n_start,
s->cluster_data, n);
if (ret < 0)
return ret;
@@ -459,8 +505,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
&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);
out:
if (nb_available > nb_needed)
@@ -498,7 +542,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
l1_index = offset >> (s->l2_bits + s->cluster_bits);
if (l1_index >= s->l1_size) {
ret = qcow2_grow_l1_table(bs, l1_index + 1, false);
ret = qcow2_grow_l1_table(bs, l1_index + 1);
if (ret < 0) {
return ret;
}
@@ -515,16 +559,12 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset,
return ret;
}
} else {
/* First allocate a new L2 table (and do COW if needed) */
if (l2_offset)
qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
ret = l2_allocate(bs, l1_index, &l2_table);
if (ret < 0) {
return ret;
}
/* Then decrease the refcount of the old table */
if (l2_offset) {
qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t));
}
l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED;
}
@@ -576,7 +616,6 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
cluster_offset = qcow2_alloc_bytes(bs, compressed_size);
if (cluster_offset < 0) {
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
return 0;
}
@@ -591,23 +630,46 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
/* compressed clusters never have the copied flag */
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED);
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
l2_table[l2_index] = cpu_to_be64(cluster_offset);
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
if (ret < 0) {
if (bdrv_pwrite_sync(bs->file,
l2_offset + l2_index * sizeof(uint64_t),
l2_table + l2_index,
sizeof(uint64_t)) < 0)
return 0;
}
return cluster_offset;
}
/*
* Write L2 table updates to disk, writing whole sectors to avoid a
* read-modify-write in bdrv_pwrite
*/
#define L2_ENTRIES_PER_SECTOR (512 / 8)
static int write_l2_entries(BlockDriverState *bs, uint64_t *l2_table,
uint64_t l2_offset, int l2_index, int num)
{
int l2_start_index = l2_index & ~(L1_ENTRIES_PER_SECTOR - 1);
int start_offset = (8 * l2_index) & ~511;
int end_offset = (8 * (l2_index + num) + 511) & ~511;
size_t len = end_offset - start_offset;
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE);
ret = bdrv_pwrite_sync(bs->file, l2_offset + start_offset,
&l2_table[l2_start_index], len);
if (ret < 0) {
return ret;
}
return 0;
}
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_offset, *l2_table;
uint64_t cluster_offset = m->cluster_offset;
bool cow = false;
if (m->nb_clusters == 0)
return 0;
@@ -617,7 +679,6 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
/* copy content of unmodified sectors */
start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
if (m->n_start) {
cow = true;
ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
if (ret < 0)
goto err;
@@ -625,30 +686,17 @@ 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;
ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9),
m->nb_available - end, s->cluster_sectors);
if (ret < 0)
goto err;
}
/*
* Update L2 table.
*
* Before we update the L2 table to actually point to the new cluster, we
* need to be sure that the refcounts have been increased and COW was
* handled.
*/
if (cow) {
qcow2_cache_depends_on_flush(s->l2_table_cache);
}
qcow2_cache_set_dependency(bs, s->l2_table_cache, s->refcount_block_cache);
/* update L2 table */
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index);
if (ret < 0) {
goto err;
}
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
for (i = 0; i < m->nb_clusters; i++) {
/* if two concurrent writes happen to the same unallocated cluster
@@ -664,22 +712,15 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
(i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
}
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
ret = write_l2_entries(bs, l2_table, l2_offset, l2_index, m->nb_clusters);
if (ret < 0) {
qcow2_l2_cache_reset(bs);
goto err;
}
/*
* If this was a COW, we need to decrease the refcount of the old cluster.
* Also flush bs->file to get the right order for L2 and refcount update.
*/
if (j != 0) {
for (i = 0; i < j; i++) {
qcow2_free_any_clusters(bs,
be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
}
}
for (i = 0; i < j; i++)
qcow2_free_any_clusters(bs,
be64_to_cpu(old_cluster[i]) & ~QCOW_OFLAG_COPIED, 1);
ret = 0;
err:
@@ -796,8 +837,7 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
m->depends_on = old_alloc;
m->nb_clusters = 0;
*num = 0;
goto out_wait_dependency;
return 0;
}
}
}
@@ -812,8 +852,8 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size);
if (cluster_offset < 0) {
ret = cluster_offset;
goto fail;
QLIST_REMOVE(m, next_in_flight);
return cluster_offset;
}
/* save info needed for meta data update */
@@ -822,26 +862,12 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
m->nb_clusters = nb_clusters;
out:
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
if (ret < 0) {
goto fail_put;
}
m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end);
m->cluster_offset = cluster_offset;
*num = m->nb_available - n_start;
return 0;
out_wait_dependency:
return qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
fail:
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
fail_put:
QLIST_REMOVE(m, next_in_flight);
return ret;
}
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
@@ -885,95 +911,13 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED);
ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors);
if (ret < 0) {
return ret;
return -1;
}
if (decompress_buffer(s->cluster_cache, s->cluster_size,
s->cluster_data + sector_offset, csize) < 0) {
return -EIO;
return -1;
}
s->cluster_cache_offset = coffset;
}
return 0;
}
/*
* This discards as many clusters of nb_clusters as possible at once (i.e.
* all clusters in the same L2 table) and returns the number of discarded
* clusters.
*/
static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
unsigned int nb_clusters)
{
BDRVQcowState *s = bs->opaque;
uint64_t l2_offset, *l2_table;
int l2_index;
int ret;
int i;
ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &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]);
old_offset &= ~QCOW_OFLAG_COPIED;
if (old_offset == 0) {
continue;
}
/* First remove L2 entries */
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
l2_table[l2_index + i] = cpu_to_be64(0);
/* Then decrease the refcount */
qcow2_free_any_clusters(bs, old_offset, 1);
}
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
if (ret < 0) {
return ret;
}
return nb_clusters;
}
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
int nb_sectors)
{
BDRVQcowState *s = bs->opaque;
uint64_t end_offset;
unsigned int nb_clusters;
int ret;
end_offset = offset + (nb_sectors << BDRV_SECTOR_BITS);
/* Round start up and end down */
offset = align_offset(offset, s->cluster_size);
end_offset &= ~(s->cluster_size - 1);
if (offset > end_offset) {
return 0;
}
nb_clusters = size_to_clusters(s, end_offset - offset);
/* Each L2 table is handled by its own loop iteration */
while (nb_clusters > 0) {
ret = discard_single_l2(bs, offset, nb_clusters);
if (ret < 0) {
return ret;
}
nb_clusters -= ret;
offset += (ret * s->cluster_size);
}
return 0;
}

View File

@@ -32,6 +32,27 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
int addend);
static int cache_refcount_updates = 0;
static int write_refcount_block(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
size_t size = s->cluster_size;
if (s->refcount_block_cache_offset == 0) {
return 0;
}
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE);
if (bdrv_pwrite_sync(bs->file, s->refcount_block_cache_offset,
s->refcount_block_cache, size) < 0)
{
return -EIO;
}
return 0;
}
/*********************************************************/
/* refcount handling */
@@ -40,6 +61,7 @@ int qcow2_refcount_init(BlockDriverState *bs)
BDRVQcowState *s = bs->opaque;
int ret, refcount_table_size2, i;
s->refcount_block_cache = qemu_malloc(s->cluster_size);
refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
s->refcount_table = qemu_malloc(refcount_table_size2);
if (s->refcount_table_size > 0) {
@@ -59,22 +81,33 @@ int qcow2_refcount_init(BlockDriverState *bs)
void qcow2_refcount_close(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
qemu_free(s->refcount_block_cache);
qemu_free(s->refcount_table);
}
static int load_refcount_block(BlockDriverState *bs,
int64_t refcount_block_offset,
void **refcount_block)
int64_t refcount_block_offset)
{
BDRVQcowState *s = bs->opaque;
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD);
ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
refcount_block);
if (cache_refcount_updates) {
ret = write_refcount_block(bs);
if (ret < 0) {
return ret;
}
}
return ret;
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_LOAD);
ret = bdrv_pread(bs->file, refcount_block_offset, s->refcount_block_cache,
s->cluster_size);
if (ret < 0) {
return ret;
}
s->refcount_block_cache_offset = refcount_block_offset;
return 0;
}
/*
@@ -88,8 +121,6 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
int refcount_table_index, block_index;
int64_t refcount_block_offset;
int ret;
uint16_t *refcount_block;
uint16_t refcount;
refcount_table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
if (refcount_table_index >= s->refcount_table_size)
@@ -97,24 +128,16 @@ static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
refcount_block_offset = s->refcount_table[refcount_table_index];
if (!refcount_block_offset)
return 0;
ret = qcow2_cache_get(bs, s->refcount_block_cache, refcount_block_offset,
(void**) &refcount_block);
if (ret < 0) {
return ret;
if (refcount_block_offset != s->refcount_block_cache_offset) {
/* better than nothing: return allocated if read error */
ret = load_refcount_block(bs, refcount_block_offset);
if (ret < 0) {
return ret;
}
}
block_index = cluster_index &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
refcount = be16_to_cpu(refcount_block[block_index]);
ret = qcow2_cache_put(bs, s->refcount_block_cache,
(void**) &refcount_block);
if (ret < 0) {
return ret;
}
return refcount;
return be16_to_cpu(s->refcount_block_cache[block_index]);
}
/*
@@ -150,10 +173,9 @@ static int in_same_refcount_block(BDRVQcowState *s, uint64_t offset_a,
* Loads a refcount block. If it doesn't exist yet, it is allocated first
* (including growing the refcount table if needed).
*
* Returns 0 on success or -errno in error case
* Returns the offset of the refcount block on success or -errno in error case
*/
static int alloc_refcount_block(BlockDriverState *bs,
int64_t cluster_index, uint16_t **refcount_block)
static int64_t alloc_refcount_block(BlockDriverState *bs, int64_t cluster_index)
{
BDRVQcowState *s = bs->opaque;
unsigned int refcount_table_index;
@@ -171,8 +193,13 @@ static int alloc_refcount_block(BlockDriverState *bs,
/* If it's already there, we're done */
if (refcount_block_offset) {
return load_refcount_block(bs, refcount_block_offset,
(void**) refcount_block);
if (refcount_block_offset != s->refcount_block_cache_offset) {
ret = load_refcount_block(bs, refcount_block_offset);
if (ret < 0) {
return ret;
}
}
return refcount_block_offset;
}
}
@@ -198,10 +225,12 @@ static int alloc_refcount_block(BlockDriverState *bs,
* refcount block into the cache
*/
*refcount_block = NULL;
/* We write to the refcount table, so we might depend on L2 tables */
qcow2_cache_flush(bs, s->l2_table_cache);
if (cache_refcount_updates) {
ret = write_refcount_block(bs);
if (ret < 0) {
return ret;
}
}
/* Allocate the refcount block itself and mark it as used */
int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
@@ -217,18 +246,13 @@ static int alloc_refcount_block(BlockDriverState *bs,
if (in_same_refcount_block(s, new_block, cluster_index << s->cluster_bits)) {
/* Zero the new refcount block before updating it */
ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
(void**) refcount_block);
if (ret < 0) {
goto fail_block;
}
memset(*refcount_block, 0, s->cluster_size);
memset(s->refcount_block_cache, 0, s->cluster_size);
s->refcount_block_cache_offset = new_block;
/* The block describes itself, need to update the cache */
int block_index = (new_block >> s->cluster_bits) &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
(*refcount_block)[block_index] = cpu_to_be16(1);
s->refcount_block_cache[block_index] = cpu_to_be16(1);
} else {
/* Described somewhere else. This can recurse at most twice before we
* arrive at a block that describes itself. */
@@ -237,23 +261,16 @@ static int alloc_refcount_block(BlockDriverState *bs,
goto fail_block;
}
bdrv_flush(bs->file);
/* Initialize the new refcount block only after updating its refcount,
* update_refcount uses the refcount cache itself */
ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
(void**) refcount_block);
if (ret < 0) {
goto fail_block;
}
memset(*refcount_block, 0, s->cluster_size);
memset(s->refcount_block_cache, 0, s->cluster_size);
s->refcount_block_cache_offset = new_block;
}
/* Now the new refcount block needs to be written to disk */
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE);
qcow2_cache_entry_mark_dirty(s->refcount_block_cache, *refcount_block);
ret = qcow2_cache_flush(bs, s->refcount_block_cache);
ret = bdrv_pwrite_sync(bs->file, new_block, s->refcount_block_cache,
s->cluster_size);
if (ret < 0) {
goto fail_block;
}
@@ -270,12 +287,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
}
s->refcount_table[refcount_table_index] = new_block;
return 0;
}
ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
if (ret < 0) {
goto fail_block;
return new_block;
}
/*
@@ -395,9 +407,9 @@ static int alloc_refcount_block(BlockDriverState *bs,
qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t));
s->free_cluster_index = old_free_cluster_index;
ret = load_refcount_block(bs, new_block, (void**) refcount_block);
ret = load_refcount_block(bs, new_block);
if (ret < 0) {
return ret;
goto fail_block;
}
return new_block;
@@ -405,20 +417,52 @@ static int alloc_refcount_block(BlockDriverState *bs,
fail_table:
qemu_free(new_table);
fail_block:
if (*refcount_block != NULL) {
qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
}
s->refcount_block_cache_offset = 0;
return ret;
}
#define REFCOUNTS_PER_SECTOR (512 >> REFCOUNT_SHIFT)
static int write_refcount_block_entries(BlockDriverState *bs,
int64_t refcount_block_offset, int first_index, int last_index)
{
BDRVQcowState *s = bs->opaque;
size_t size;
int ret;
if (cache_refcount_updates) {
return 0;
}
if (first_index < 0) {
return 0;
}
first_index &= ~(REFCOUNTS_PER_SECTOR - 1);
last_index = (last_index + REFCOUNTS_PER_SECTOR)
& ~(REFCOUNTS_PER_SECTOR - 1);
size = (last_index - first_index) << REFCOUNT_SHIFT;
BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_UPDATE_PART);
ret = bdrv_pwrite_sync(bs->file,
refcount_block_offset + (first_index << REFCOUNT_SHIFT),
&s->refcount_block_cache[first_index], size);
if (ret < 0) {
return ret;
}
return 0;
}
/* XXX: cache several refcount block clusters ? */
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
int64_t offset, int64_t length, int addend)
{
BDRVQcowState *s = bs->opaque;
int64_t start, last, cluster_offset;
uint16_t *refcount_block = NULL;
int64_t old_table_index = -1;
int64_t refcount_block_offset = 0;
int64_t table_index = -1, old_table_index;
int first_index = -1, last_index = -1;
int ret;
#ifdef DEBUG_ALLOC2
@@ -431,11 +475,6 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
return 0;
}
if (addend < 0) {
qcow2_cache_set_dependency(bs, s->refcount_block_cache,
s->l2_table_cache);
}
start = offset & ~(s->cluster_size - 1);
last = (offset + length - 1) & ~(s->cluster_size - 1);
for(cluster_offset = start; cluster_offset <= last;
@@ -443,33 +482,42 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
{
int block_index, refcount;
int64_t cluster_index = cluster_offset >> s->cluster_bits;
int64_t table_index =
cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
int64_t new_block;
/* Only write refcount block to disk when we are done with it */
old_table_index = table_index;
table_index = cluster_index >> (s->cluster_bits - REFCOUNT_SHIFT);
if ((old_table_index >= 0) && (table_index != old_table_index)) {
ret = write_refcount_block_entries(bs, refcount_block_offset,
first_index, last_index);
if (ret < 0) {
return ret;
}
first_index = -1;
last_index = -1;
}
/* Load the refcount block and allocate it if needed */
if (table_index != old_table_index) {
if (refcount_block) {
ret = qcow2_cache_put(bs, s->refcount_block_cache,
(void**) &refcount_block);
if (ret < 0) {
goto fail;
}
}
ret = alloc_refcount_block(bs, cluster_index, &refcount_block);
if (ret < 0) {
goto fail;
}
new_block = alloc_refcount_block(bs, cluster_index);
if (new_block < 0) {
ret = new_block;
goto fail;
}
old_table_index = table_index;
qcow2_cache_entry_mark_dirty(s->refcount_block_cache, refcount_block);
refcount_block_offset = new_block;
/* we can update the count and save it */
block_index = cluster_index &
((1 << (s->cluster_bits - REFCOUNT_SHIFT)) - 1);
if (first_index == -1 || block_index < first_index) {
first_index = block_index;
}
if (block_index > last_index) {
last_index = block_index;
}
refcount = be16_to_cpu(refcount_block[block_index]);
refcount = be16_to_cpu(s->refcount_block_cache[block_index]);
refcount += addend;
if (refcount < 0 || refcount > 0xffff) {
ret = -EINVAL;
@@ -478,16 +526,17 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
if (refcount == 0 && cluster_index < s->free_cluster_index) {
s->free_cluster_index = cluster_index;
}
refcount_block[block_index] = cpu_to_be16(refcount);
s->refcount_block_cache[block_index] = cpu_to_be16(refcount);
}
ret = 0;
fail:
/* Write last changed block to disk */
if (refcount_block) {
if (refcount_block_offset != 0) {
int wret;
wret = qcow2_cache_put(bs, s->refcount_block_cache,
(void**) &refcount_block);
wret = write_refcount_block_entries(bs, refcount_block_offset,
first_index, last_index);
if (wret < 0) {
return ret < 0 ? ret : wret;
}
@@ -500,7 +549,6 @@ fail:
if (ret < 0) {
int dummy;
dummy = update_refcount(bs, offset, cluster_offset - offset, -addend);
(void)dummy;
}
return ret;
@@ -525,8 +573,6 @@ static int update_cluster_refcount(BlockDriverState *bs,
return ret;
}
bdrv_flush(bs->file);
return get_refcount(bs, cluster_index);
}
@@ -578,7 +624,6 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
if (ret < 0) {
return ret;
}
return offset;
}
@@ -626,8 +671,6 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
goto redo;
}
}
bdrv_flush(bs->file);
return offset;
}
@@ -705,15 +748,10 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
BDRVQcowState *s = bs->opaque;
uint64_t *l1_table, *l2_table, l2_offset, offset, l1_size2, l1_allocated;
int64_t old_offset, old_l2_offset;
int i, j, l1_modified = 0, nb_csectors, refcount;
int ret;
bool old_l2_writethrough, old_refcount_writethrough;
int l2_size, i, j, l1_modified, l2_modified, nb_csectors, refcount;
/* Switch caches to writeback mode during update */
old_l2_writethrough =
qcow2_cache_set_writethrough(bs, s->l2_table_cache, false);
old_refcount_writethrough =
qcow2_cache_set_writethrough(bs, s->refcount_block_cache, false);
qcow2_l2_cache_reset(bs);
cache_refcount_updates = 1;
l2_table = NULL;
l1_table = NULL;
@@ -727,11 +765,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l1_allocated = 1;
if (bdrv_pread(bs->file, l1_table_offset,
l1_table, l1_size2) != l1_size2)
{
ret = -EIO;
goto fail;
}
for(i = 0;i < l1_size; i++)
be64_to_cpus(&l1_table[i]);
} else {
@@ -740,18 +774,17 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
l1_allocated = 0;
}
l2_size = s->l2_size * sizeof(uint64_t);
l2_table = qemu_malloc(l2_size);
l1_modified = 0;
for(i = 0; i < l1_size; i++) {
l2_offset = l1_table[i];
if (l2_offset) {
old_l2_offset = l2_offset;
l2_offset &= ~QCOW_OFLAG_COPIED;
ret = qcow2_cache_get(bs, s->l2_table_cache, l2_offset,
(void**) &l2_table);
if (ret < 0) {
l2_modified = 0;
if (bdrv_pread(bs->file, l2_offset, l2_table, l2_size) != l2_size)
goto fail;
}
for(j = 0; j < s->l2_size; j++) {
offset = be64_to_cpu(l2_table[j]);
if (offset != 0) {
@@ -768,10 +801,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
if (ret < 0) {
goto fail;
}
/* TODO Flushing once for the whole function should
* be enough */
bdrv_flush(bs->file);
}
/* compressed clusters are never modified */
refcount = 2;
@@ -783,7 +812,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
if (refcount < 0) {
ret = -EIO;
goto fail;
}
}
@@ -792,29 +820,23 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
offset |= QCOW_OFLAG_COPIED;
}
if (offset != old_offset) {
if (addend > 0) {
qcow2_cache_set_dependency(bs, s->l2_table_cache,
s->refcount_block_cache);
}
l2_table[j] = cpu_to_be64(offset);
qcow2_cache_entry_mark_dirty(s->l2_table_cache, l2_table);
l2_modified = 1;
}
}
}
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
if (ret < 0) {
goto fail;
if (l2_modified) {
if (bdrv_pwrite_sync(bs->file,
l2_offset, l2_table, l2_size) < 0)
goto fail;
}
if (addend != 0) {
refcount = update_cluster_refcount(bs, l2_offset >> s->cluster_bits, addend);
} else {
refcount = get_refcount(bs, l2_offset >> s->cluster_bits);
}
if (refcount < 0) {
ret = -EIO;
goto fail;
} else if (refcount == 1) {
l2_offset |= QCOW_OFLAG_COPIED;
@@ -825,18 +847,6 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
}
}
}
ret = 0;
fail:
if (l2_table) {
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
}
/* Enable writethrough cache mode again */
qcow2_cache_set_writethrough(bs, s->l2_table_cache, old_l2_writethrough);
qcow2_cache_set_writethrough(bs, s->refcount_block_cache,
old_refcount_writethrough);
if (l1_modified) {
for(i = 0; i < l1_size; i++)
cpu_to_be64s(&l1_table[i]);
@@ -848,7 +858,17 @@ fail:
}
if (l1_allocated)
qemu_free(l1_table);
return ret;
qemu_free(l2_table);
cache_refcount_updates = 0;
write_refcount_block(bs);
return 0;
fail:
if (l1_allocated)
qemu_free(l1_table);
qemu_free(l2_table);
cache_refcount_updates = 0;
write_refcount_block(bs);
return -EIO;
}
@@ -1079,7 +1099,7 @@ fail:
* Checks an image for refcount consistency.
*
* Returns 0 if no errors are found, the number of errors in case the image is
* detected as corrupted, and -errno when an internal error occurred.
* detected as corrupted, and -errno when an internal error occured.
*/
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
{
@@ -1102,7 +1122,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
s->l1_table_offset, s->l1_size, 1);
if (ret < 0) {
goto fail;
return ret;
}
/* snapshots */
@@ -1111,7 +1131,7 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
ret = check_refcounts_l1(bs, res, refcount_table, nb_clusters,
sn->l1_table_offset, sn->l1_size, 0);
if (ret < 0) {
goto fail;
return ret;
}
}
inc_refcounts(bs, res, refcount_table, nb_clusters,
@@ -1175,11 +1195,8 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res)
}
}
ret = 0;
fail:
qemu_free(refcount_table);
return ret;
return 0;
}

View File

@@ -116,7 +116,7 @@ int qcow2_read_snapshots(BlockDriverState *bs)
}
/* add at the end of the file a new list of snapshots */
static int qcow2_write_snapshots(BlockDriverState *bs)
static int qcow_write_snapshots(BlockDriverState *bs)
{
BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn;
@@ -138,7 +138,6 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
snapshots_size = offset;
snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size);
bdrv_flush(bs->file);
offset = snapshots_offset;
if (offset < 0) {
return offset;
@@ -272,7 +271,6 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
if (l1_table_offset < 0) {
goto fail;
}
bdrv_flush(bs->file);
sn->l1_table_offset = l1_table_offset;
sn->l1_size = s->l1_size;
@@ -300,7 +298,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
s->snapshots = snapshots1;
s->snapshots[s->nb_snapshots++] = *sn;
if (qcow2_write_snapshots(bs) < 0)
if (qcow_write_snapshots(bs) < 0)
goto fail;
#ifdef DEBUG_ALLOC
qcow2_check_refcounts(bs);
@@ -317,8 +315,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
{
BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn;
int i, snapshot_index;
int cur_l1_bytes, sn_l1_bytes;
int i, snapshot_index, l1_size2;
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id);
if (snapshot_index < 0)
@@ -328,22 +325,17 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0)
goto fail;
if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0)
if (qcow2_grow_l1_table(bs, sn->l1_size) < 0)
goto fail;
cur_l1_bytes = s->l1_size * sizeof(uint64_t);
sn_l1_bytes = sn->l1_size * sizeof(uint64_t);
if (cur_l1_bytes > sn_l1_bytes) {
memset(s->l1_table + sn->l1_size, 0, cur_l1_bytes - sn_l1_bytes);
}
s->l1_size = sn->l1_size;
l1_size2 = s->l1_size * sizeof(uint64_t);
/* 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)
s->l1_table, l1_size2) != l1_size2)
goto fail;
if (bdrv_pwrite_sync(bs->file, s->l1_table_offset,
s->l1_table, cur_l1_bytes) < 0)
s->l1_table, l1_size2) < 0)
goto fail;
for(i = 0;i < s->l1_size; i++) {
be64_to_cpus(&s->l1_table[i]);
@@ -384,7 +376,7 @@ int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
qemu_free(sn->name);
memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn));
s->nb_snapshots--;
ret = qcow2_write_snapshots(bs);
ret = qcow_write_snapshots(bs);
if (ret < 0) {
/* XXX: restore snapshot if error ? */
return ret;
@@ -424,34 +416,3 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
return s->nb_snapshots;
}
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
{
int i, snapshot_index, l1_size2;
BDRVQcowState *s = bs->opaque;
QCowSnapshot *sn;
snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name);
if (snapshot_index < 0) {
return -ENOENT;
}
sn = &s->snapshots[snapshot_index];
s->l1_size = sn->l1_size;
l1_size2 = s->l1_size * sizeof(uint64_t);
if (s->l1_table != NULL) {
qemu_free(s->l1_table);
}
s->l1_table_offset = sn->l1_table_offset;
s->l1_table = qemu_mallocz(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;
}

File diff suppressed because it is too large Load Diff

View File

@@ -51,11 +51,6 @@
#define L2_CACHE_SIZE 16
/* Must be at least 4 to cover all cases of refcount table growth */
#define REFCOUNT_CACHE_SIZE 4
#define DEFAULT_CLUSTER_SIZE 65536
typedef struct QCowHeader {
uint32_t magic;
uint32_t version;
@@ -83,10 +78,8 @@ typedef struct QCowSnapshot {
uint64_t vm_clock_nsec;
} QCowSnapshot;
struct Qcow2Cache;
typedef struct Qcow2Cache Qcow2Cache;
typedef struct BDRVQcowState {
BlockDriverState *hd;
int cluster_bits;
int cluster_size;
int cluster_sectors;
@@ -99,10 +92,9 @@ typedef struct BDRVQcowState {
uint64_t cluster_offset_mask;
uint64_t l1_table_offset;
uint64_t *l1_table;
Qcow2Cache* l2_table_cache;
Qcow2Cache* refcount_block_cache;
uint64_t *l2_cache;
uint64_t l2_cache_offsets[L2_CACHE_SIZE];
uint32_t l2_cache_counts[L2_CACHE_SIZE];
uint8_t *cluster_cache;
uint8_t *cluster_data;
uint64_t cluster_cache_offset;
@@ -111,6 +103,8 @@ typedef struct BDRVQcowState {
uint64_t *refcount_table;
uint64_t refcount_table_offset;
uint32_t refcount_table_size;
uint64_t refcount_block_cache_offset;
uint16_t *refcount_block_cache;
int64_t free_cluster_index;
int64_t free_byte_offset;
@@ -172,8 +166,8 @@ static inline int64_t align_offset(int64_t offset, int n)
// 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_backing_read1(BlockDriverState *bs,
int64_t sector_num, uint8_t *buf, int nb_sectors);
/* qcow2-refcount.c functions */
int qcow2_refcount_init(BlockDriverState *bs);
@@ -194,7 +188,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs,
int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res);
/* qcow2-cluster.c functions */
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size, bool exact_size);
int qcow2_grow_l1_table(BlockDriverState *bs, int min_size);
void qcow2_l2_cache_reset(BlockDriverState *bs);
int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
@@ -211,36 +205,14 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
int compressed_size);
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
int nb_sectors);
/* qcow2-snapshot.c functions */
int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info);
int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id);
int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id);
int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab);
int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name);
void qcow2_free_snapshots(BlockDriverState *bs);
int qcow2_read_snapshots(BlockDriverState *bs);
/* qcow2-cache.c functions */
Qcow2Cache *qcow2_cache_create(BlockDriverState *bs, int num_tables,
bool writethrough);
int qcow2_cache_destroy(BlockDriverState* bs, Qcow2Cache *c);
bool qcow2_cache_set_writethrough(BlockDriverState *bs, Qcow2Cache *c,
bool enable);
void qcow2_cache_entry_mark_dirty(Qcow2Cache *c, void *table);
int qcow2_cache_flush(BlockDriverState *bs, Qcow2Cache *c);
int qcow2_cache_set_dependency(BlockDriverState *bs, Qcow2Cache *c,
Qcow2Cache *dependency);
void qcow2_cache_depends_on_flush(Qcow2Cache *c);
int qcow2_cache_get(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
void **table);
int qcow2_cache_get_empty(BlockDriverState *bs, Qcow2Cache *c, uint64_t offset,
void **table);
int qcow2_cache_put(BlockDriverState *bs, Qcow2Cache *c, void **table);
#endif

View File

@@ -1,211 +0,0 @@
/*
* QEMU Enhanced Disk Format Consistency Check
*
* Copyright IBM, Corp. 2010
*
* 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 "qed.h"
typedef struct {
BDRVQEDState *s;
BdrvCheckResult *result;
bool fix; /* whether to fix invalid offsets */
uint64_t nclusters;
uint32_t *used_clusters; /* referenced cluster bitmap */
QEDRequest request;
} QEDCheck;
static bool qed_test_bit(uint32_t *bitmap, uint64_t n) {
return !!(bitmap[n / 32] & (1 << (n % 32)));
}
static void qed_set_bit(uint32_t *bitmap, uint64_t n) {
bitmap[n / 32] |= 1 << (n % 32);
}
/**
* Set bitmap bits for clusters
*
* @check: Check structure
* @offset: Starting offset in bytes
* @n: Number of clusters
*/
static bool qed_set_used_clusters(QEDCheck *check, uint64_t offset,
unsigned int n)
{
uint64_t cluster = qed_bytes_to_clusters(check->s, offset);
unsigned int corruptions = 0;
while (n-- != 0) {
/* Clusters should only be referenced once */
if (qed_test_bit(check->used_clusters, cluster)) {
corruptions++;
}
qed_set_bit(check->used_clusters, cluster);
cluster++;
}
check->result->corruptions += corruptions;
return corruptions == 0;
}
/**
* Check an L2 table
*
* @ret: Number of invalid cluster offsets
*/
static unsigned int qed_check_l2_table(QEDCheck *check, QEDTable *table)
{
BDRVQEDState *s = check->s;
unsigned int i, num_invalid = 0;
for (i = 0; i < s->table_nelems; i++) {
uint64_t offset = table->offsets[i];
if (qed_offset_is_unalloc_cluster(offset) ||
qed_offset_is_zero_cluster(offset)) {
continue;
}
/* Detect invalid cluster offset */
if (!qed_check_cluster_offset(s, offset)) {
if (check->fix) {
table->offsets[i] = 0;
} else {
check->result->corruptions++;
}
num_invalid++;
continue;
}
qed_set_used_clusters(check, offset, 1);
}
return num_invalid;
}
/**
* Descend tables and check each cluster is referenced once only
*/
static int qed_check_l1_table(QEDCheck *check, QEDTable *table)
{
BDRVQEDState *s = check->s;
unsigned int i, num_invalid_l1 = 0;
int ret, last_error = 0;
/* Mark L1 table clusters used */
qed_set_used_clusters(check, s->header.l1_table_offset,
s->header.table_size);
for (i = 0; i < s->table_nelems; i++) {
unsigned int num_invalid_l2;
uint64_t offset = table->offsets[i];
if (qed_offset_is_unalloc_cluster(offset)) {
continue;
}
/* Detect invalid L2 offset */
if (!qed_check_table_offset(s, offset)) {
/* Clear invalid offset */
if (check->fix) {
table->offsets[i] = 0;
} else {
check->result->corruptions++;
}
num_invalid_l1++;
continue;
}
if (!qed_set_used_clusters(check, offset, s->header.table_size)) {
continue; /* skip an invalid table */
}
ret = qed_read_l2_table_sync(s, &check->request, offset);
if (ret) {
check->result->check_errors++;
last_error = ret;
continue;
}
num_invalid_l2 = qed_check_l2_table(check,
check->request.l2_table->table);
/* Write out fixed L2 table */
if (num_invalid_l2 > 0 && check->fix) {
ret = qed_write_l2_table_sync(s, &check->request, 0,
s->table_nelems, false);
if (ret) {
check->result->check_errors++;
last_error = ret;
continue;
}
}
}
/* Drop reference to final table */
qed_unref_l2_cache_entry(check->request.l2_table);
check->request.l2_table = NULL;
/* Write out fixed L1 table */
if (num_invalid_l1 > 0 && check->fix) {
ret = qed_write_l1_table_sync(s, 0, s->table_nelems);
if (ret) {
check->result->check_errors++;
last_error = ret;
}
}
return last_error;
}
/**
* Check for unreferenced (leaked) clusters
*/
static void qed_check_for_leaks(QEDCheck *check)
{
BDRVQEDState *s = check->s;
uint64_t i;
for (i = s->header.header_size; i < check->nclusters; i++) {
if (!qed_test_bit(check->used_clusters, i)) {
check->result->leaks++;
}
}
}
int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix)
{
QEDCheck check = {
.s = s,
.result = result,
.nclusters = qed_bytes_to_clusters(s, s->file_size),
.request = { .l2_table = NULL },
.fix = fix,
};
int ret;
check.used_clusters = qemu_mallocz(((check.nclusters + 31) / 32) *
sizeof(check.used_clusters[0]));
ret = qed_check_l1_table(&check, s->l1_table);
if (ret == 0) {
/* Only check for leaks if entire image was scanned successfully */
qed_check_for_leaks(&check);
}
qemu_free(check.used_clusters);
return ret;
}

View File

@@ -1,165 +0,0 @@
/*
* QEMU Enhanced Disk Format Cluster functions
*
* Copyright IBM, Corp. 2010
*
* Authors:
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
* Anthony Liguori <aliguori@us.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 "qed.h"
/**
* Count the number of contiguous data clusters
*
* @s: QED state
* @table: L2 table
* @index: First cluster index
* @n: Maximum number of clusters
* @offset: Set to first cluster offset
*
* This function scans tables for contiguous clusters. A contiguous run of
* clusters may be allocated, unallocated, or zero.
*/
static unsigned int qed_count_contiguous_clusters(BDRVQEDState *s,
QEDTable *table,
unsigned int index,
unsigned int n,
uint64_t *offset)
{
unsigned int end = MIN(index + n, s->table_nelems);
uint64_t last = table->offsets[index];
unsigned int i;
*offset = last;
for (i = index + 1; i < end; i++) {
if (qed_offset_is_unalloc_cluster(last)) {
/* Counting unallocated clusters */
if (!qed_offset_is_unalloc_cluster(table->offsets[i])) {
break;
}
} else if (qed_offset_is_zero_cluster(last)) {
/* Counting zero clusters */
if (!qed_offset_is_zero_cluster(table->offsets[i])) {
break;
}
} else {
/* Counting allocated clusters */
if (table->offsets[i] != last + s->header.cluster_size) {
break;
}
last = table->offsets[i];
}
}
return i - index;
}
typedef struct {
BDRVQEDState *s;
uint64_t pos;
size_t len;
QEDRequest *request;
/* User callback */
QEDFindClusterFunc *cb;
void *opaque;
} QEDFindClusterCB;
static void qed_find_cluster_cb(void *opaque, int ret)
{
QEDFindClusterCB *find_cluster_cb = opaque;
BDRVQEDState *s = find_cluster_cb->s;
QEDRequest *request = find_cluster_cb->request;
uint64_t offset = 0;
size_t len = 0;
unsigned int index;
unsigned int n;
if (ret) {
goto out;
}
index = qed_l2_index(s, find_cluster_cb->pos);
n = qed_bytes_to_clusters(s,
qed_offset_into_cluster(s, find_cluster_cb->pos) +
find_cluster_cb->len);
n = qed_count_contiguous_clusters(s, request->l2_table->table,
index, n, &offset);
if (qed_offset_is_unalloc_cluster(offset)) {
ret = QED_CLUSTER_L2;
} else if (qed_offset_is_zero_cluster(offset)) {
ret = QED_CLUSTER_ZERO;
} else if (qed_check_cluster_offset(s, offset)) {
ret = QED_CLUSTER_FOUND;
} else {
ret = -EINVAL;
}
len = MIN(find_cluster_cb->len, n * s->header.cluster_size -
qed_offset_into_cluster(s, find_cluster_cb->pos));
out:
find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len);
qemu_free(find_cluster_cb);
}
/**
* Find the offset of a data cluster
*
* @s: QED state
* @request: L2 cache entry
* @pos: Byte position in device
* @len: Number of bytes
* @cb: Completion function
* @opaque: User data for completion function
*
* This function translates a position in the block device to an offset in the
* image file. It invokes the cb completion callback to report back the
* translated offset or unallocated range in the image file.
*
* If the L2 table exists, request->l2_table points to the L2 table cache entry
* and the caller must free the reference when they are finished. The cache
* entry is exposed in this way to avoid callers having to read the L2 table
* again later during request processing. If request->l2_table is non-NULL it
* will be unreferenced before taking on the new cache entry.
*/
void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
size_t len, QEDFindClusterFunc *cb, void *opaque)
{
QEDFindClusterCB *find_cluster_cb;
uint64_t l2_offset;
/* Limit length to L2 boundary. Requests are broken up at the L2 boundary
* so that a request acts on one L2 table at a time.
*/
len = MIN(len, (((pos >> s->l1_shift) + 1) << s->l1_shift) - pos);
l2_offset = s->l1_table->offsets[qed_l1_index(s, pos)];
if (qed_offset_is_unalloc_cluster(l2_offset)) {
cb(opaque, QED_CLUSTER_L1, 0, len);
return;
}
if (!qed_check_table_offset(s, l2_offset)) {
cb(opaque, -EINVAL, 0, 0);
return;
}
find_cluster_cb = qemu_malloc(sizeof(*find_cluster_cb));
find_cluster_cb->s = s;
find_cluster_cb->pos = pos;
find_cluster_cb->len = len;
find_cluster_cb->cb = cb;
find_cluster_cb->opaque = opaque;
find_cluster_cb->request = request;
qed_read_l2_table(s, request, l2_offset,
qed_find_cluster_cb, find_cluster_cb);
}

View File

@@ -1,32 +0,0 @@
/*
* QEMU Enhanced Disk Format
*
* Copyright IBM, Corp. 2010
*
* 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 "qed.h"
void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque)
{
GenericCB *gencb = qemu_malloc(len);
gencb->cb = cb;
gencb->opaque = opaque;
return gencb;
}
void gencb_complete(void *opaque, int ret)
{
GenericCB *gencb = opaque;
BlockDriverCompletionFunc *cb = gencb->cb;
void *user_opaque = gencb->opaque;
qemu_free(gencb);
cb(user_opaque, ret);
}

View File

@@ -1,173 +0,0 @@
/*
* QEMU Enhanced Disk Format L2 Cache
*
* Copyright IBM, Corp. 2010
*
* Authors:
* Anthony Liguori <aliguori@us.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.
*
*/
/*
* L2 table cache usage is as follows:
*
* An open image has one L2 table cache that is used to avoid accessing the
* image file for recently referenced L2 tables.
*
* Cluster offset lookup translates the logical offset within the block device
* to a cluster offset within the image file. This is done by indexing into
* the L1 and L2 tables which store cluster offsets. It is here where the L2
* table cache serves up recently referenced L2 tables.
*
* If there is a cache miss, that L2 table is read from the image file and
* committed to the cache. Subsequent accesses to that L2 table will be served
* from the cache until the table is evicted from the cache.
*
* L2 tables are also committed to the cache when new L2 tables are allocated
* in the image file. Since the L2 table cache is write-through, the new L2
* table is first written out to the image file and then committed to the
* cache.
*
* Multiple I/O requests may be using an L2 table cache entry at any given
* time. That means an entry may be in use across several requests and
* reference counting is needed to free the entry at the correct time. In
* particular, an entry evicted from the cache will only be freed once all
* references are dropped.
*
* An in-flight I/O request will hold a reference to a L2 table cache entry for
* the period during which it needs to access the L2 table. This includes
* cluster offset lookup, L2 table allocation, and L2 table update when a new
* data cluster has been allocated.
*
* An interesting case occurs when two requests need to access an L2 table that
* is not in the cache. Since the operation to read the table from the image
* file takes some time to complete, both requests may see a cache miss and
* start reading the L2 table from the image file. The first to finish will
* commit its L2 table into the cache. When the second tries to commit its
* table will be deleted in favor of the existing cache entry.
*/
#include "trace.h"
#include "qed.h"
/* Each L2 holds 2GB so this let's us fully cache a 100GB disk */
#define MAX_L2_CACHE_SIZE 50
/**
* Initialize the L2 cache
*/
void qed_init_l2_cache(L2TableCache *l2_cache)
{
QTAILQ_INIT(&l2_cache->entries);
l2_cache->n_entries = 0;
}
/**
* Free the L2 cache
*/
void qed_free_l2_cache(L2TableCache *l2_cache)
{
CachedL2Table *entry, *next_entry;
QTAILQ_FOREACH_SAFE(entry, &l2_cache->entries, node, next_entry) {
qemu_vfree(entry->table);
qemu_free(entry);
}
}
/**
* Allocate an uninitialized entry from the cache
*
* The returned entry has a reference count of 1 and is owned by the caller.
* The caller must allocate the actual table field for this entry and it must
* be freeable using qemu_vfree().
*/
CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache)
{
CachedL2Table *entry;
entry = qemu_mallocz(sizeof(*entry));
entry->ref++;
trace_qed_alloc_l2_cache_entry(l2_cache, entry);
return entry;
}
/**
* Decrease an entry's reference count and free if necessary when the reference
* count drops to zero.
*/
void qed_unref_l2_cache_entry(CachedL2Table *entry)
{
if (!entry) {
return;
}
entry->ref--;
trace_qed_unref_l2_cache_entry(entry, entry->ref);
if (entry->ref == 0) {
qemu_vfree(entry->table);
qemu_free(entry);
}
}
/**
* Find an entry in the L2 cache. This may return NULL and it's up to the
* caller to satisfy the cache miss.
*
* For a cached entry, this function increases the reference count and returns
* the entry.
*/
CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset)
{
CachedL2Table *entry;
QTAILQ_FOREACH(entry, &l2_cache->entries, node) {
if (entry->offset == offset) {
trace_qed_find_l2_cache_entry(l2_cache, entry, offset, entry->ref);
entry->ref++;
return entry;
}
}
return NULL;
}
/**
* Commit an L2 cache entry into the cache. This is meant to be used as part of
* the process to satisfy a cache miss. A caller would allocate an entry which
* is not actually in the L2 cache and then once the entry was valid and
* present on disk, the entry can be committed into the cache.
*
* Since the cache is write-through, it's important that this function is not
* called until the entry is present on disk and the L1 has been updated to
* point to the entry.
*
* N.B. This function steals a reference to the l2_table from the caller so the
* caller must obtain a new reference by issuing a call to
* qed_find_l2_cache_entry().
*/
void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table)
{
CachedL2Table *entry;
entry = qed_find_l2_cache_entry(l2_cache, l2_table->offset);
if (entry) {
qed_unref_l2_cache_entry(entry);
qed_unref_l2_cache_entry(l2_table);
return;
}
if (l2_cache->n_entries >= MAX_L2_CACHE_SIZE) {
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++;
QTAILQ_INSERT_TAIL(&l2_cache->entries, l2_table, node);
}

View File

@@ -1,319 +0,0 @@
/*
* QEMU Enhanced Disk Format Table I/O
*
* Copyright IBM, Corp. 2010
*
* Authors:
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
* Anthony Liguori <aliguori@us.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 "qemu_socket.h" /* for EINPROGRESS on Windows */
#include "qed.h"
typedef struct {
GenericCB gencb;
BDRVQEDState *s;
QEDTable *table;
struct iovec iov;
QEMUIOVector qiov;
} QEDReadTableCB;
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->iov.iov_len / sizeof(uint64_t);
int i;
/* Handle I/O error */
if (ret) {
goto out;
}
/* Byteswap offsets */
for (i = 0; i < noffsets; i++) {
table->offsets[i] = le64_to_cpu(table->offsets[i]);
}
out:
/* Completion */
trace_qed_read_table_cb(read_table_cb->s, read_table_cb->table, ret);
gencb_complete(&read_table_cb->gencb, ret);
}
static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
BlockDriverCompletionFunc *cb, void *opaque)
{
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);
read_table_cb->s = s;
read_table_cb->table = table;
read_table_cb->iov.iov_base = table->offsets,
read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size,
qemu_iovec_init_external(qiov, &read_table_cb->iov, 1);
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 {
GenericCB gencb;
BDRVQEDState *s;
QEDTable *orig_table;
QEDTable *table;
bool flush; /* flush after write? */
struct iovec iov;
QEMUIOVector qiov;
} QEDWriteTableCB;
static void qed_write_table_cb(void *opaque, int ret)
{
QEDWriteTableCB *write_table_cb = opaque;
trace_qed_write_table_cb(write_table_cb->s,
write_table_cb->orig_table,
write_table_cb->flush,
ret);
if (ret) {
goto out;
}
if (write_table_cb->flush) {
/* We still need to flush first */
write_table_cb->flush = false;
bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb,
write_table_cb);
return;
}
out:
qemu_vfree(write_table_cb->table);
gencb_complete(&write_table_cb->gencb, ret);
return;
}
/**
* Write out an updated part or all of a table
*
* @s: QED state
* @offset: Offset of table in image file, in bytes
* @table: Table
* @index: Index of first element
* @n: Number of elements
* @flush: Whether or not to sync to disk
* @cb: Completion function
* @opaque: Argument for completion function
*/
static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
unsigned int index, unsigned int n, bool flush,
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;
trace_qed_write_table(s, offset, table, index, n);
/* Calculate indices of the first and one after last elements */
start = index & ~sector_mask;
end = (index + n + sector_mask) & ~sector_mask;
len_bytes = (end - start) * sizeof(uint64_t);
write_table_cb = gencb_alloc(sizeof(*write_table_cb), cb, opaque);
write_table_cb->s = s;
write_table_cb->orig_table = table;
write_table_cb->flush = flush;
write_table_cb->table = qemu_blockalign(s->bs, len_bytes);
write_table_cb->iov.iov_base = write_table_cb->table->offsets;
write_table_cb->iov.iov_len = len_bytes;
qemu_iovec_init_external(&write_table_cb->qiov, &write_table_cb->iov, 1);
/* Byteswap table */
for (i = start; i < end; i++) {
uint64_t le_offset = cpu_to_le64(table->offsets[i]);
write_table_cb->table->offsets[i - start] = le_offset;
}
/* Adjust for offset into table */
offset += start * sizeof(uint64_t);
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);
}
}
/**
* Propagate return value from async callback
*/
static void qed_sync_cb(void *opaque, int ret)
{
*(int *)opaque = ret;
}
int qed_read_l1_table_sync(BDRVQEDState *s)
{
int ret = -EINPROGRESS;
async_context_push();
qed_read_table(s, s->header.l1_table_offset,
s->l1_table, qed_sync_cb, &ret);
while (ret == -EINPROGRESS) {
qemu_aio_wait();
}
async_context_pop();
return ret;
}
void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
BlockDriverCompletionFunc *cb, void *opaque)
{
BLKDBG_EVENT(s->bs->file, BLKDBG_L1_UPDATE);
qed_write_table(s, s->header.l1_table_offset,
s->l1_table, index, n, false, cb, opaque);
}
int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
unsigned int n)
{
int ret = -EINPROGRESS;
async_context_push();
qed_write_l1_table(s, index, n, qed_sync_cb, &ret);
while (ret == -EINPROGRESS) {
qemu_aio_wait();
}
async_context_pop();
return ret;
}
typedef struct {
GenericCB gencb;
BDRVQEDState *s;
uint64_t l2_offset;
QEDRequest *request;
} QEDReadL2TableCB;
static void qed_read_l2_table_cb(void *opaque, int ret)
{
QEDReadL2TableCB *read_l2_table_cb = opaque;
QEDRequest *request = read_l2_table_cb->request;
BDRVQEDState *s = read_l2_table_cb->s;
CachedL2Table *l2_table = request->l2_table;
uint64_t l2_offset = read_l2_table_cb->l2_offset;
if (ret) {
/* can't trust loaded L2 table anymore */
qed_unref_l2_cache_entry(l2_table);
request->l2_table = NULL;
} else {
l2_table->offset = l2_offset;
qed_commit_l2_cache_entry(&s->l2_cache, l2_table);
/* This is guaranteed to succeed because we just committed the entry
* to the cache.
*/
request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset);
assert(request->l2_table != NULL);
}
gencb_complete(&read_l2_table_cb->gencb, ret);
}
void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
BlockDriverCompletionFunc *cb, void *opaque)
{
QEDReadL2TableCB *read_l2_table_cb;
qed_unref_l2_cache_entry(request->l2_table);
/* Check for cached L2 entry */
request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, offset);
if (request->l2_table) {
cb(opaque, 0);
return;
}
request->l2_table = qed_alloc_l2_cache_entry(&s->l2_cache);
request->l2_table->table = qed_alloc_table(s);
read_l2_table_cb = gencb_alloc(sizeof(*read_l2_table_cb), cb, opaque);
read_l2_table_cb->s = s;
read_l2_table_cb->l2_offset = offset;
read_l2_table_cb->request = request;
BLKDBG_EVENT(s->bs->file, BLKDBG_L2_LOAD);
qed_read_table(s, offset, request->l2_table->table,
qed_read_l2_table_cb, read_l2_table_cb);
}
int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request, uint64_t offset)
{
int ret = -EINPROGRESS;
async_context_push();
qed_read_l2_table(s, request, offset, qed_sync_cb, &ret);
while (ret == -EINPROGRESS) {
qemu_aio_wait();
}
async_context_pop();
return ret;
}
void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
unsigned int index, unsigned int n, bool flush,
BlockDriverCompletionFunc *cb, void *opaque)
{
BLKDBG_EVENT(s->bs->file, BLKDBG_L2_UPDATE);
qed_write_table(s, request->l2_table->offset,
request->l2_table->table, index, n, flush, cb, opaque);
}
int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
unsigned int index, unsigned int n, bool flush)
{
int ret = -EINPROGRESS;
async_context_push();
qed_write_l2_table(s, request, index, n, flush, qed_sync_cb, &ret);
while (ret == -EINPROGRESS) {
qemu_aio_wait();
}
async_context_pop();
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,334 +0,0 @@
/*
* QEMU Enhanced Disk Format
*
* Copyright IBM, Corp. 2010
*
* Authors:
* Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
* Anthony Liguori <aliguori@us.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.
*
*/
#ifndef BLOCK_QED_H
#define BLOCK_QED_H
#include "block_int.h"
/* The layout of a QED file is as follows:
*
* +--------+----------+----------+----------+-----+
* | header | L1 table | cluster0 | cluster1 | ... |
* +--------+----------+----------+----------+-----+
*
* There is a 2-level pagetable for cluster allocation:
*
* +----------+
* | L1 table |
* +----------+
* ,------' | '------.
* +----------+ | +----------+
* | L2 table | ... | L2 table |
* +----------+ +----------+
* ,------' | '------.
* +----------+ | +----------+
* | Data | ... | Data |
* +----------+ +----------+
*
* The L1 table is fixed size and always present. L2 tables are allocated on
* demand. The L1 table size determines the maximum possible image size; it
* can be influenced using the cluster_size and table_size values.
*
* All fields are little-endian on disk.
*/
enum {
QED_MAGIC = 'Q' | 'E' << 8 | 'D' << 16 | '\0' << 24,
/* The image supports a backing file */
QED_F_BACKING_FILE = 0x01,
/* The image needs a consistency check before use */
QED_F_NEED_CHECK = 0x02,
/* The backing file format must not be probed, treat as raw image */
QED_F_BACKING_FORMAT_NO_PROBE = 0x04,
/* Feature bits must be used when the on-disk format changes */
QED_FEATURE_MASK = QED_F_BACKING_FILE | /* supported feature bits */
QED_F_NEED_CHECK |
QED_F_BACKING_FORMAT_NO_PROBE,
QED_COMPAT_FEATURE_MASK = 0, /* supported compat feature bits */
QED_AUTOCLEAR_FEATURE_MASK = 0, /* supported autoclear feature bits */
/* Data is stored in groups of sectors called clusters. Cluster size must
* be large to avoid keeping too much metadata. I/O requests that have
* sub-cluster size will require read-modify-write.
*/
QED_MIN_CLUSTER_SIZE = 4 * 1024, /* in bytes */
QED_MAX_CLUSTER_SIZE = 64 * 1024 * 1024,
QED_DEFAULT_CLUSTER_SIZE = 64 * 1024,
/* Allocated clusters are tracked using a 2-level pagetable. Table size is
* a multiple of clusters so large maximum image sizes can be supported
* without jacking up the cluster size too much.
*/
QED_MIN_TABLE_SIZE = 1, /* in clusters */
QED_MAX_TABLE_SIZE = 16,
QED_DEFAULT_TABLE_SIZE = 4,
/* Delay to flush and clean image after last allocating write completes */
QED_NEED_CHECK_TIMEOUT = 5, /* in seconds */
};
typedef struct {
uint32_t magic; /* QED\0 */
uint32_t cluster_size; /* in bytes */
uint32_t table_size; /* for L1 and L2 tables, in clusters */
uint32_t header_size; /* in clusters */
uint64_t features; /* format feature bits */
uint64_t compat_features; /* compatible feature bits */
uint64_t autoclear_features; /* self-resetting feature bits */
uint64_t l1_table_offset; /* in bytes */
uint64_t image_size; /* total logical image size, in bytes */
/* if (features & QED_F_BACKING_FILE) */
uint32_t backing_filename_offset; /* in bytes from start of header */
uint32_t backing_filename_size; /* in bytes */
} QEDHeader;
typedef struct {
uint64_t offsets[0]; /* in bytes */
} QEDTable;
/* The L2 cache is a simple write-through cache for L2 structures */
typedef struct CachedL2Table {
QEDTable *table;
uint64_t offset; /* offset=0 indicates an invalidate entry */
QTAILQ_ENTRY(CachedL2Table) node;
int ref;
} CachedL2Table;
typedef struct {
QTAILQ_HEAD(, CachedL2Table) entries;
unsigned int n_entries;
} L2TableCache;
typedef struct QEDRequest {
CachedL2Table *l2_table;
} QEDRequest;
typedef struct QEDAIOCB {
BlockDriverAIOCB common;
QEMUBH *bh;
int bh_ret; /* final return status for completion bh */
QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */
bool is_write; /* false - read, true - write */
bool *finished; /* signal for cancel completion */
uint64_t end_pos; /* request end on block device, in bytes */
/* User scatter-gather list */
QEMUIOVector *qiov;
size_t qiov_offset; /* byte count already processed */
/* Current cluster scatter-gather list */
QEMUIOVector cur_qiov;
uint64_t cur_pos; /* position on block device, in bytes */
uint64_t cur_cluster; /* cluster offset in image file */
unsigned int cur_nclusters; /* number of clusters being accessed */
int find_cluster_ret; /* used for L1/L2 update */
QEDRequest request;
} QEDAIOCB;
typedef struct {
BlockDriverState *bs; /* device */
uint64_t file_size; /* length of image file, in bytes */
QEDHeader header; /* always cpu-endian */
QEDTable *l1_table;
L2TableCache l2_cache; /* l2 table cache */
uint32_t table_nelems;
uint32_t l1_shift;
uint32_t l2_shift;
uint32_t l2_mask;
/* Allocating write request queue */
QSIMPLEQ_HEAD(, QEDAIOCB) allocating_write_reqs;
bool allocating_write_reqs_plugged;
/* Periodic flush and clear need check flag */
QEMUTimer *need_check_timer;
} BDRVQEDState;
enum {
QED_CLUSTER_FOUND, /* cluster found */
QED_CLUSTER_ZERO, /* zero cluster found */
QED_CLUSTER_L2, /* cluster missing in L2 */
QED_CLUSTER_L1, /* cluster missing in L1 */
};
/**
* qed_find_cluster() completion callback
*
* @opaque: User data for completion callback
* @ret: QED_CLUSTER_FOUND Success
* QED_CLUSTER_L2 Data cluster unallocated in L2
* QED_CLUSTER_L1 L2 unallocated in L1
* -errno POSIX error occurred
* @offset: Data cluster offset
* @len: Contiguous bytes starting from cluster offset
*
* This function is invoked when qed_find_cluster() completes.
*
* On success ret is QED_CLUSTER_FOUND and offset/len are a contiguous range
* in the image file.
*
* On failure ret is QED_CLUSTER_L2 or QED_CLUSTER_L1 for missing L2 or L1
* table offset, respectively. len is number of contiguous unallocated bytes.
*/
typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len);
/**
* Generic callback for chaining async callbacks
*/
typedef struct {
BlockDriverCompletionFunc *cb;
void *opaque;
} GenericCB;
void *gencb_alloc(size_t len, BlockDriverCompletionFunc *cb, void *opaque);
void gencb_complete(void *opaque, int ret);
/**
* L2 cache functions
*/
void qed_init_l2_cache(L2TableCache *l2_cache);
void qed_free_l2_cache(L2TableCache *l2_cache);
CachedL2Table *qed_alloc_l2_cache_entry(L2TableCache *l2_cache);
void qed_unref_l2_cache_entry(CachedL2Table *entry);
CachedL2Table *qed_find_l2_cache_entry(L2TableCache *l2_cache, uint64_t offset);
void qed_commit_l2_cache_entry(L2TableCache *l2_cache, CachedL2Table *l2_table);
/**
* Table I/O functions
*/
int qed_read_l1_table_sync(BDRVQEDState *s);
void qed_write_l1_table(BDRVQEDState *s, unsigned int index, unsigned int n,
BlockDriverCompletionFunc *cb, void *opaque);
int qed_write_l1_table_sync(BDRVQEDState *s, unsigned int index,
unsigned int n);
int qed_read_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
uint64_t offset);
void qed_read_l2_table(BDRVQEDState *s, QEDRequest *request, uint64_t offset,
BlockDriverCompletionFunc *cb, void *opaque);
void qed_write_l2_table(BDRVQEDState *s, QEDRequest *request,
unsigned int index, unsigned int n, bool flush,
BlockDriverCompletionFunc *cb, void *opaque);
int qed_write_l2_table_sync(BDRVQEDState *s, QEDRequest *request,
unsigned int index, unsigned int n, bool flush);
/**
* Cluster functions
*/
void qed_find_cluster(BDRVQEDState *s, QEDRequest *request, uint64_t pos,
size_t len, QEDFindClusterFunc *cb, void *opaque);
/**
* Consistency check
*/
int qed_check(BDRVQEDState *s, BdrvCheckResult *result, bool fix);
QEDTable *qed_alloc_table(BDRVQEDState *s);
/**
* Round down to the start of a cluster
*/
static inline uint64_t qed_start_of_cluster(BDRVQEDState *s, uint64_t offset)
{
return offset & ~(uint64_t)(s->header.cluster_size - 1);
}
static inline uint64_t qed_offset_into_cluster(BDRVQEDState *s, uint64_t offset)
{
return offset & (s->header.cluster_size - 1);
}
static inline uint64_t qed_bytes_to_clusters(BDRVQEDState *s, uint64_t bytes)
{
return qed_start_of_cluster(s, bytes + (s->header.cluster_size - 1)) /
(s->header.cluster_size - 1);
}
static inline unsigned int qed_l1_index(BDRVQEDState *s, uint64_t pos)
{
return pos >> s->l1_shift;
}
static inline unsigned int qed_l2_index(BDRVQEDState *s, uint64_t pos)
{
return (pos >> s->l2_shift) & s->l2_mask;
}
/**
* Test if a cluster offset is valid
*/
static inline bool qed_check_cluster_offset(BDRVQEDState *s, uint64_t offset)
{
uint64_t header_size = (uint64_t)s->header.header_size *
s->header.cluster_size;
if (offset & (s->header.cluster_size - 1)) {
return false;
}
return offset >= header_size && offset < s->file_size;
}
/**
* Test if a table offset is valid
*/
static inline bool qed_check_table_offset(BDRVQEDState *s, uint64_t offset)
{
uint64_t end_offset = offset + (s->header.table_size - 1) *
s->header.cluster_size;
/* Overflow check */
if (end_offset <= offset) {
return false;
}
return qed_check_cluster_offset(s, offset) &&
qed_check_cluster_offset(s, end_offset);
}
static inline bool qed_offset_is_cluster_aligned(BDRVQEDState *s,
uint64_t offset)
{
if (qed_offset_into_cluster(s, offset)) {
return false;
}
return true;
}
static inline bool qed_offset_is_unalloc_cluster(uint64_t offset)
{
if (offset == 0) {
return true;
}
return false;
}
static inline bool qed_offset_is_zero_cluster(uint64_t offset)
{
if (offset == 1) {
return true;
}
return false;
}
#endif /* BLOCK_QED_H */

View File

@@ -43,17 +43,16 @@
#ifdef __sun__
#define _POSIX_PTHREAD_SEMANTICS 1
#include <signal.h>
#include <sys/dkio.h>
#endif
#ifdef __linux__
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <linux/cdrom.h>
#include <linux/fd.h>
#endif
#if defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
#include <signal.h>
#include <sys/disk.h>
#include <sys/cdio.h>
#endif
@@ -64,22 +63,11 @@
#include <sys/dkio.h>
#endif
#ifdef __NetBSD__
#include <sys/ioctl.h>
#include <sys/disklabel.h>
#include <sys/dkio.h>
#include <sys/disk.h>
#endif
#ifdef __DragonFly__
#include <sys/ioctl.h>
#include <sys/diskslice.h>
#endif
#ifdef CONFIG_XFS
#include <xfs/xfs.h>
#endif
//#define DEBUG_FLOPPY
//#define DEBUG_BLOCK
@@ -108,11 +96,11 @@
#define FTYPE_CD 1
#define FTYPE_FD 2
/* if the FD is not accessed during that time (in ns), we try to
reopen it to see if the disk has been changed */
#define FD_OPEN_TIMEOUT (1000000000)
#define ALIGNED_BUFFER_SIZE (32 * 512)
#define MAX_BLOCKSIZE 4096
/* if the FD is not accessed during that time (in ms), we try to
reopen it to see if the disk has been changed */
#define FD_OPEN_TIMEOUT 1000
typedef struct BDRVRawState {
int fd;
@@ -129,11 +117,7 @@ typedef struct BDRVRawState {
int use_aio;
void *aio_ctx;
#endif
uint8_t *aligned_buf;
unsigned aligned_buf_size;
#ifdef CONFIG_XFS
bool is_xfs : 1;
#endif
uint8_t* aligned_buf;
} BDRVRawState;
static int fd_open(BlockDriverState *bs);
@@ -143,55 +127,12 @@ static int64_t raw_getlength(BlockDriverState *bs);
static int cdrom_reopen(BlockDriverState *bs);
#endif
#if defined(__NetBSD__)
static int raw_normalize_devicepath(const char **filename)
{
static char namebuf[PATH_MAX];
const char *dp, *fname;
struct stat sb;
fname = *filename;
dp = strrchr(fname, '/');
if (lstat(fname, &sb) < 0) {
fprintf(stderr, "%s: stat failed: %s\n",
fname, strerror(errno));
return -errno;
}
if (!S_ISBLK(sb.st_mode)) {
return 0;
}
if (dp == NULL) {
snprintf(namebuf, PATH_MAX, "r%s", fname);
} else {
snprintf(namebuf, PATH_MAX, "%.*s/r%s",
(int)(dp - fname), fname, dp + 1);
}
fprintf(stderr, "%s is a block device", fname);
*filename = namebuf;
fprintf(stderr, ", using %s\n", *filename);
return 0;
}
#else
static int raw_normalize_devicepath(const char **filename)
{
return 0;
}
#endif
static int raw_open_common(BlockDriverState *bs, const char *filename,
int bdrv_flags, int open_flags)
{
BDRVRawState *s = bs->opaque;
int fd, ret;
ret = raw_normalize_devicepath(&filename);
if (ret != 0) {
return ret;
}
s->open_flags = open_flags | O_BINARY;
s->open_flags &= ~O_ACCMODE;
if (bdrv_flags & BDRV_O_RDWR) {
@@ -204,7 +145,7 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
* and O_DIRECT for no caching. */
if ((bdrv_flags & BDRV_O_NOCACHE))
s->open_flags |= O_DIRECT;
if (!(bdrv_flags & BDRV_O_CACHE_WB))
else if (!(bdrv_flags & BDRV_O_CACHE_WB))
s->open_flags |= O_DSYNC;
s->fd = -1;
@@ -219,12 +160,7 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
s->aligned_buf = NULL;
if ((bdrv_flags & BDRV_O_NOCACHE)) {
/*
* Allocate a buffer for read/modify/write cycles. Chose the size
* pessimistically as we don't know the block size yet.
*/
s->aligned_buf_size = 32 * MAX_BLOCKSIZE;
s->aligned_buf = qemu_memalign(MAX_BLOCKSIZE, s->aligned_buf_size);
s->aligned_buf = qemu_blockalign(bs, ALIGNED_BUFFER_SIZE);
if (s->aligned_buf == NULL) {
goto out_close;
}
@@ -253,12 +189,6 @@ static int raw_open_common(BlockDriverState *bs, const char *filename,
#endif
}
#ifdef CONFIG_XFS
if (platform_test_xfs_fd(s->fd)) {
s->is_xfs = 1;
}
#endif
return 0;
out_free_buf:
@@ -347,9 +277,8 @@ static int raw_pread_aligned(BlockDriverState *bs, int64_t offset,
}
/*
* offset and count are in bytes, but must be multiples of the sector size
* for files opened with O_DIRECT. buf must be aligned to sector size bytes
* then.
* offset and count are in bytes, but must be multiples of 512 for files
* opened with O_DIRECT. buf must be aligned to 512 bytes then.
*
* This function may be called without alignment if the caller ensures
* that O_DIRECT is not in effect.
@@ -386,25 +315,24 @@ static int raw_pread(BlockDriverState *bs, int64_t offset,
uint8_t *buf, int count)
{
BDRVRawState *s = bs->opaque;
unsigned sector_mask = bs->buffer_alignment - 1;
int size, ret, shift, sum;
sum = 0;
if (s->aligned_buf != NULL) {
if (offset & sector_mask) {
/* align offset on a sector size bytes boundary */
if (offset & 0x1ff) {
/* align offset on a 512 bytes boundary */
shift = offset & sector_mask;
size = (shift + count + sector_mask) & ~sector_mask;
if (size > s->aligned_buf_size)
size = s->aligned_buf_size;
shift = offset & 0x1ff;
size = (shift + count + 0x1ff) & ~0x1ff;
if (size > ALIGNED_BUFFER_SIZE)
size = ALIGNED_BUFFER_SIZE;
ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, size);
if (ret < 0)
return ret;
size = bs->buffer_alignment - shift;
size = 512 - shift;
if (size > count)
size = count;
memcpy(buf, s->aligned_buf + shift, size);
@@ -417,15 +345,15 @@ static int raw_pread(BlockDriverState *bs, int64_t offset,
if (count == 0)
return sum;
}
if (count & sector_mask || (uintptr_t) buf & sector_mask) {
if (count & 0x1ff || (uintptr_t) buf & 0x1ff) {
/* read on aligned buffer */
while (count) {
size = (count + sector_mask) & ~sector_mask;
if (size > s->aligned_buf_size)
size = s->aligned_buf_size;
size = (count + 0x1ff) & ~0x1ff;
if (size > ALIGNED_BUFFER_SIZE)
size = ALIGNED_BUFFER_SIZE;
ret = raw_pread_aligned(bs, offset, s->aligned_buf, size);
if (ret < 0) {
@@ -475,28 +403,25 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset,
const uint8_t *buf, int count)
{
BDRVRawState *s = bs->opaque;
unsigned sector_mask = bs->buffer_alignment - 1;
int size, ret, shift, sum;
sum = 0;
if (s->aligned_buf != NULL) {
if (offset & sector_mask) {
/* align offset on a sector size bytes boundary */
shift = offset & sector_mask;
ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf,
bs->buffer_alignment);
if (offset & 0x1ff) {
/* align offset on a 512 bytes boundary */
shift = offset & 0x1ff;
ret = raw_pread_aligned(bs, offset - shift, s->aligned_buf, 512);
if (ret < 0)
return ret;
size = bs->buffer_alignment - shift;
size = 512 - shift;
if (size > count)
size = count;
memcpy(s->aligned_buf + shift, buf, size);
ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf,
bs->buffer_alignment);
ret = raw_pwrite_aligned(bs, offset - shift, s->aligned_buf, 512);
if (ret < 0)
return ret;
@@ -508,12 +433,12 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset,
if (count == 0)
return sum;
}
if (count & sector_mask || (uintptr_t) buf & sector_mask) {
if (count & 0x1ff || (uintptr_t) buf & 0x1ff) {
while ((size = (count & ~sector_mask)) != 0) {
while ((size = (count & ~0x1ff)) != 0) {
if (size > s->aligned_buf_size)
size = s->aligned_buf_size;
if (size > ALIGNED_BUFFER_SIZE)
size = ALIGNED_BUFFER_SIZE;
memcpy(s->aligned_buf, buf, size);
@@ -526,16 +451,14 @@ static int raw_pwrite(BlockDriverState *bs, int64_t offset,
count -= ret;
sum += ret;
}
/* here, count < sector_size because (count & ~sector_mask) == 0 */
/* here, count < 512 because (count & ~0x1ff) == 0 */
if (count) {
ret = raw_pread_aligned(bs, offset, s->aligned_buf,
bs->buffer_alignment);
ret = raw_pread_aligned(bs, offset, s->aligned_buf, 512);
if (ret < 0)
return ret;
memcpy(s->aligned_buf, buf, count);
ret = raw_pwrite_aligned(bs, offset, s->aligned_buf,
bs->buffer_alignment);
ret = raw_pwrite_aligned(bs, offset, s->aligned_buf, 512);
if (ret < 0)
return ret;
if (count < ret)
@@ -563,12 +486,12 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num,
/*
* Check if all memory in this vector is sector aligned.
*/
static int qiov_is_aligned(BlockDriverState *bs, QEMUIOVector *qiov)
static int qiov_is_aligned(QEMUIOVector *qiov)
{
int i;
for (i = 0; i < qiov->niov; i++) {
if ((uintptr_t) qiov->iov[i].iov_base % bs->buffer_alignment) {
if ((uintptr_t) qiov->iov[i].iov_base % BDRV_SECTOR_SIZE) {
return 0;
}
}
@@ -591,7 +514,7 @@ static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
* driver that it needs to copy the buffer.
*/
if (s->aligned_buf) {
if (!qiov_is_aligned(bs, qiov)) {
if (!qiov_is_aligned(qiov)) {
type |= QEMU_AIO_MISALIGNED;
#ifdef CONFIG_LINUX_AIO
} else if (s->use_aio) {
@@ -672,31 +595,6 @@ static int64_t raw_getlength(BlockDriverState *bs)
} else
return st.st_size;
}
#elif defined(__NetBSD__)
static int64_t raw_getlength(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
int fd = s->fd;
struct stat st;
if (fstat(fd, &st))
return -1;
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
struct dkwedge_info dkw;
if (ioctl(fd, DIOCGWEDGEINFO, &dkw) != -1) {
return dkw.dkw_size * 512;
} else {
struct disklabel dl;
if (ioctl(fd, DIOCGDINFO, &dl))
return -1;
return (uint64_t)dl.d_secsize *
dl.d_partitions[DISKPART(st.st_rdev)].p_size;
}
} else
return st.st_size;
}
#elif defined(__sun__)
static int64_t raw_getlength(BlockDriverState *bs)
{
@@ -793,17 +691,6 @@ static int64_t raw_getlength(BlockDriverState *bs)
}
#endif
static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
{
struct stat st;
BDRVRawState *s = bs->opaque;
if (fstat(s->fd, &st) < 0) {
return -errno;
}
return (int64_t)st.st_blocks * 512;
}
static int raw_create(const char *filename, QEMUOptionParameter *options)
{
int fd;
@@ -833,43 +720,12 @@ static int raw_create(const char *filename, QEMUOptionParameter *options)
return result;
}
static int raw_flush(BlockDriverState *bs)
static void raw_flush(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
return qemu_fdatasync(s->fd);
qemu_fdatasync(s->fd);
}
#ifdef CONFIG_XFS
static int xfs_discard(BDRVRawState *s, int64_t sector_num, int nb_sectors)
{
struct xfs_flock64 fl;
memset(&fl, 0, sizeof(fl));
fl.l_whence = SEEK_SET;
fl.l_start = sector_num << 9;
fl.l_len = (int64_t)nb_sectors << 9;
if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) {
DEBUG_BLOCK_PRINT("cannot punch hole (%s)\n", strerror(errno));
return -errno;
}
return 0;
}
#endif
static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
{
#ifdef CONFIG_XFS
BDRVRawState *s = bs->opaque;
if (s->is_xfs) {
return xfs_discard(s, sector_num, nb_sectors);
}
#endif
return 0;
}
static QEMUOptionParameter raw_create_options[] = {
{
@@ -891,7 +747,6 @@ static BlockDriver bdrv_file = {
.bdrv_close = raw_close,
.bdrv_create = raw_create,
.bdrv_flush = raw_flush,
.bdrv_discard = raw_discard,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
@@ -899,8 +754,6 @@ static BlockDriver bdrv_file = {
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
.create_options = raw_create_options,
};
@@ -1015,13 +868,8 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
s->type = FTYPE_FILE;
#if defined(__linux__)
{
char resolved_path[ MAXPATHLEN ], *temp;
temp = realpath(filename, resolved_path);
if (temp && strstart(temp, "/dev/sg", NULL)) {
bs->sg = 1;
}
if (strstart(filename, "/dev/sg", NULL)) {
bs->sg = 1;
}
#endif
@@ -1041,7 +889,7 @@ static int fd_open(BlockDriverState *bs)
return 0;
last_media_present = (s->fd >= 0);
if (s->fd >= 0 &&
(get_clock() - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
(qemu_get_clock(rt_clock) - s->fd_open_time) >= FD_OPEN_TIMEOUT) {
close(s->fd);
s->fd = -1;
#ifdef DEBUG_FLOPPY
@@ -1050,7 +898,7 @@ static int fd_open(BlockDriverState *bs)
}
if (s->fd < 0) {
if (s->fd_got_error &&
(get_clock() - s->fd_error_time) < FD_OPEN_TIMEOUT) {
(qemu_get_clock(rt_clock) - s->fd_error_time) < FD_OPEN_TIMEOUT) {
#ifdef DEBUG_FLOPPY
printf("No floppy (open delayed)\n");
#endif
@@ -1058,7 +906,7 @@ static int fd_open(BlockDriverState *bs)
}
s->fd = open(bs->filename, s->open_flags & ~O_NONBLOCK);
if (s->fd < 0) {
s->fd_error_time = get_clock();
s->fd_error_time = qemu_get_clock(rt_clock);
s->fd_got_error = 1;
if (last_media_present)
s->fd_media_changed = 1;
@@ -1073,7 +921,7 @@ static int fd_open(BlockDriverState *bs)
}
if (!last_media_present)
s->fd_media_changed = 1;
s->fd_open_time = get_clock();
s->fd_open_time = qemu_get_clock(rt_clock);
s->fd_got_error = 0;
return 0;
}
@@ -1169,8 +1017,6 @@ static BlockDriver bdrv_host_device = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* generic scsi device */
#ifdef __linux__
@@ -1205,7 +1051,6 @@ static int floppy_probe_device(const char *filename)
int fd, ret;
int prio = 0;
struct floppy_struct fdparam;
struct stat st;
if (strstart(filename, "/dev/fd", NULL))
prio = 50;
@@ -1214,17 +1059,12 @@ static int floppy_probe_device(const char *filename)
if (fd < 0) {
goto out;
}
ret = fstat(fd, &st);
if (ret == -1 || !S_ISBLK(st.st_mode)) {
goto outc;
}
/* Attempt to detect via a floppy specific ioctl */
ret = ioctl(fd, FDGETPRM, &fdparam);
if (ret >= 0)
prio = 100;
outc:
close(fd);
out:
return prio;
@@ -1292,8 +1132,6 @@ static BlockDriver bdrv_host_floppy = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = floppy_is_inserted,
@@ -1315,23 +1153,20 @@ static int cdrom_probe_device(const char *filename)
{
int fd, ret;
int prio = 0;
struct stat st;
if (strstart(filename, "/dev/cd", NULL))
prio = 50;
fd = open(filename, O_RDONLY | O_NONBLOCK);
if (fd < 0) {
goto out;
}
ret = fstat(fd, &st);
if (ret == -1 || !S_ISBLK(st.st_mode)) {
goto outc;
}
/* Attempt to detect via a CDROM specific ioctl */
ret = ioctl(fd, CDROM_DRIVE_STATUS, CDSL_CURRENT);
if (ret >= 0)
prio = 100;
outc:
close(fd);
out:
return prio;
@@ -1397,8 +1232,6 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = cdrom_is_inserted,
@@ -1522,8 +1355,6 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
/* removable device support */
.bdrv_is_inserted = cdrom_is_inserted,

View File

@@ -88,9 +88,9 @@ static int raw_open(BlockDriverState *bs, const char *filename, int flags)
}
overlapped = FILE_ATTRIBUTE_NORMAL;
if (flags & BDRV_O_NOCACHE)
overlapped |= FILE_FLAG_NO_BUFFERING;
if (!(flags & BDRV_O_CACHE_WB))
if ((flags & BDRV_O_NOCACHE))
overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
else if (!(flags & BDRV_O_CACHE_WB))
overlapped |= FILE_FLAG_WRITE_THROUGH;
s->hfile = CreateFile(filename, access_flags,
FILE_SHARE_READ, NULL,
@@ -147,17 +147,10 @@ static int raw_write(BlockDriverState *bs, int64_t sector_num,
return ret_count;
}
static int raw_flush(BlockDriverState *bs)
static void raw_flush(BlockDriverState *bs)
{
BDRVRawState *s = bs->opaque;
int ret;
ret = FlushFileBuffers(s->hfile);
if (ret == 0) {
return -EIO;
}
return 0;
FlushFileBuffers(s->hfile);
}
static void raw_close(BlockDriverState *bs)
@@ -213,31 +206,6 @@ static int64_t raw_getlength(BlockDriverState *bs)
return l.QuadPart;
}
static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
{
typedef DWORD (WINAPI * get_compressed_t)(const char *filename,
DWORD * high);
get_compressed_t get_compressed;
struct _stati64 st;
const char *filename = bs->filename;
/* WinNT support GetCompressedFileSize to determine allocate size */
get_compressed =
(get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"),
"GetCompressedFileSizeA");
if (get_compressed) {
DWORD high, low;
low = get_compressed(filename, &high);
if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) {
return (((int64_t) high) << 32) + low;
}
}
if (_stati64(filename, &st) < 0) {
return -1;
}
return st.st_size;
}
static int raw_create(const char *filename, QEMUOptionParameter *options)
{
int fd;
@@ -282,8 +250,6 @@ static BlockDriver bdrv_file = {
.bdrv_write = raw_write,
.bdrv_truncate = raw_truncate,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
.create_options = raw_create_options,
};
@@ -376,9 +342,9 @@ static int hdev_open(BlockDriverState *bs, const char *filename, int flags)
create_flags = OPEN_EXISTING;
overlapped = FILE_ATTRIBUTE_NORMAL;
if (flags & BDRV_O_NOCACHE)
overlapped |= FILE_FLAG_NO_BUFFERING;
if (!(flags & BDRV_O_CACHE_WB))
if ((flags & BDRV_O_NOCACHE))
overlapped |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
else if (!(flags & BDRV_O_CACHE_WB))
overlapped |= FILE_FLAG_WRITE_THROUGH;
s->hfile = CreateFile(filename, access_flags,
FILE_SHARE_READ, NULL,
@@ -446,8 +412,6 @@ static BlockDriver bdrv_host_device = {
.bdrv_read = raw_read,
.bdrv_write = raw_write,
.bdrv_getlength = raw_getlength,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
};
static void bdrv_file_init(void)

View File

@@ -9,15 +9,82 @@ static int raw_open(BlockDriverState *bs, int flags)
return 0;
}
/* check for the user attempting to write something that looks like a
block format header to the beginning of the image and fail out.
*/
static int check_for_block_signature(BlockDriverState *bs, const uint8_t *buf)
{
static const uint8_t signatures[][4] = {
{ 'Q', 'F', 'I', 0xfb }, /* qcow/qcow2 */
{ 'C', 'O', 'W', 'D' }, /* VMDK3 */
{ 'V', 'M', 'D', 'K' }, /* VMDK4 */
{ 'O', 'O', 'O', 'M' }, /* UML COW */
{}
};
int i;
for (i = 0; signatures[i][0] != 0; i++) {
if (memcmp(buf, signatures[i], 4) == 0) {
return 1;
}
}
return 0;
}
static int check_write_unsafe(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
/* assume that if the user specifies the format explicitly, then assume
that they will continue to do so and provide no safety net */
if (!bs->probed) {
return 0;
}
if (sector_num == 0 && nb_sectors > 0) {
return check_for_block_signature(bs, buf);
}
return 0;
}
static int raw_read(BlockDriverState *bs, int64_t sector_num,
uint8_t *buf, int nb_sectors)
{
return bdrv_read(bs->file, sector_num, buf, nb_sectors);
}
static int raw_write_scrubbed_bootsect(BlockDriverState *bs,
const uint8_t *buf)
{
uint8_t bootsect[512];
/* scrub the dangerous signature */
memcpy(bootsect, buf, 512);
memset(bootsect, 0, 4);
return bdrv_write(bs->file, 0, bootsect, 1);
}
static int raw_write(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors)
{
if (check_write_unsafe(bs, sector_num, buf, nb_sectors)) {
int ret;
ret = raw_write_scrubbed_bootsect(bs, buf);
if (ret < 0) {
return ret;
}
ret = bdrv_write(bs->file, 1, buf + 512, nb_sectors - 1);
if (ret < 0) {
return ret;
}
return ret + 512;
}
return bdrv_write(bs->file, sector_num, buf, nb_sectors);
}
@@ -28,10 +95,73 @@ static BlockDriverAIOCB *raw_aio_readv(BlockDriverState *bs,
return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
}
typedef struct RawScrubberBounce
{
BlockDriverCompletionFunc *cb;
void *opaque;
QEMUIOVector qiov;
} RawScrubberBounce;
static void raw_aio_writev_scrubbed(void *opaque, int ret)
{
RawScrubberBounce *b = opaque;
if (ret < 0) {
b->cb(b->opaque, ret);
} else {
b->cb(b->opaque, ret + 512);
}
qemu_iovec_destroy(&b->qiov);
qemu_free(b);
}
static BlockDriverAIOCB *raw_aio_writev(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque)
{
const uint8_t *first_buf;
int first_buf_index = 0, i;
/* This is probably being paranoid, but handle cases of zero size
vectors. */
for (i = 0; i < qiov->niov; i++) {
if (qiov->iov[i].iov_len) {
assert(qiov->iov[i].iov_len >= 512);
first_buf_index = i;
break;
}
}
first_buf = qiov->iov[first_buf_index].iov_base;
if (check_write_unsafe(bs, sector_num, first_buf, nb_sectors)) {
RawScrubberBounce *b;
int ret;
/* write the first sector using sync I/O */
ret = raw_write_scrubbed_bootsect(bs, first_buf);
if (ret < 0) {
return NULL;
}
/* adjust request to be everything but first sector */
b = qemu_malloc(sizeof(*b));
b->cb = cb;
b->opaque = opaque;
qemu_iovec_init(&b->qiov, qiov->nalloc);
qemu_iovec_concat(&b->qiov, qiov, qiov->size);
b->qiov.size -= 512;
b->qiov.iov[first_buf_index].iov_base += 512;
b->qiov.iov[first_buf_index].iov_len -= 512;
return bdrv_aio_writev(bs->file, sector_num + 1, &b->qiov,
nb_sectors - 1, raw_aio_writev_scrubbed, b);
}
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
}
@@ -39,9 +169,9 @@ static void raw_close(BlockDriverState *bs)
{
}
static int raw_flush(BlockDriverState *bs)
static void raw_flush(BlockDriverState *bs)
{
return bdrv_flush(bs->file);
bdrv_flush(bs->file);
}
static BlockDriverAIOCB *raw_aio_flush(BlockDriverState *bs,
@@ -65,11 +195,6 @@ static int raw_probe(const uint8_t *buf, int buf_size, const char *filename)
return 1; /* everything can be opened as raw image */
}
static int raw_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
{
return bdrv_discard(bs->file, sector_num, nb_sectors);
}
static int raw_is_inserted(BlockDriverState *bs)
{
return bdrv_is_inserted(bs->file);
@@ -135,7 +260,6 @@ static BlockDriver bdrv_raw = {
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev,
.bdrv_aio_flush = raw_aio_flush,
.bdrv_discard = raw_discard,
.bdrv_is_inserted = raw_is_inserted,
.bdrv_eject = raw_eject,

View File

@@ -1,811 +0,0 @@
/*
* QEMU Block driver for RADOS (Ceph)
*
* Copyright (C) 2010-2011 Christian Brunner <chb@muc.de>,
* Josh Durgin <josh.durgin@dreamhost.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#include <inttypes.h>
#include "qemu-common.h"
#include "qemu-error.h"
#include "block_int.h"
#include <rbd/librbd.h>
/*
* When specifying the image filename use:
*
* rbd:poolname/devicename[@snapshotname][:option1=value1[:option2=value2...]]
*
* poolname must be the name of an existing rados pool
*
* devicename is the basename for all objects used to
* emulate the raw device.
*
* Each option given is used to configure rados, and may be
* any Ceph option, or "conf". The "conf" option specifies
* a Ceph configuration file to read.
*
* Metadata information (image size, ...) is stored in an
* object with the name "devicename.rbd".
*
* The raw device is split into 4MB sized objects by default.
* The sequencenumber is encoded in a 12 byte long hex-string,
* and is attached to the devicename, separated by a dot.
* e.g. "devicename.1234567890ab"
*
*/
#define OBJ_MAX_SIZE (1UL << OBJ_DEFAULT_OBJ_ORDER)
#define RBD_MAX_CONF_NAME_SIZE 128
#define RBD_MAX_CONF_VAL_SIZE 512
#define RBD_MAX_CONF_SIZE 1024
#define RBD_MAX_POOL_NAME_SIZE 128
#define RBD_MAX_SNAP_NAME_SIZE 128
#define RBD_MAX_SNAPS 100
typedef struct RBDAIOCB {
BlockDriverAIOCB common;
QEMUBH *bh;
int ret;
QEMUIOVector *qiov;
char *bounce;
int write;
int64_t sector_num;
int error;
struct BDRVRBDState *s;
int cancelled;
} RBDAIOCB;
typedef struct RADOSCB {
int rcbid;
RBDAIOCB *acb;
struct BDRVRBDState *s;
int done;
int64_t size;
char *buf;
int ret;
} RADOSCB;
#define RBD_FD_READ 0
#define RBD_FD_WRITE 1
typedef struct BDRVRBDState {
int fds[2];
rados_t cluster;
rados_ioctx_t io_ctx;
rbd_image_t image;
char name[RBD_MAX_IMAGE_NAME_SIZE];
int qemu_aio_count;
char *snap;
int event_reader_pos;
RADOSCB *event_rcb;
} BDRVRBDState;
static void rbd_aio_bh_cb(void *opaque);
static int qemu_rbd_next_tok(char *dst, int dst_len,
char *src, char delim,
const char *name,
char **p)
{
int l;
char *end;
*p = NULL;
if (delim != '\0') {
end = strchr(src, delim);
if (end) {
*p = end + 1;
*end = '\0';
}
}
l = strlen(src);
if (l >= dst_len) {
error_report("%s too long", name);
return -EINVAL;
} else if (l == 0) {
error_report("%s too short", name);
return -EINVAL;
}
pstrcpy(dst, dst_len, src);
return 0;
}
static int qemu_rbd_parsename(const char *filename,
char *pool, int pool_len,
char *snap, int snap_len,
char *name, int name_len,
char *conf, int conf_len)
{
const char *start;
char *p, *buf;
int ret;
if (!strstart(filename, "rbd:", &start)) {
return -EINVAL;
}
buf = qemu_strdup(start);
p = buf;
*snap = '\0';
*conf = '\0';
ret = qemu_rbd_next_tok(pool, pool_len, p, '/', "pool name", &p);
if (ret < 0 || !p) {
ret = -EINVAL;
goto done;
}
if (strchr(p, '@')) {
ret = qemu_rbd_next_tok(name, name_len, p, '@', "object name", &p);
if (ret < 0) {
goto done;
}
ret = qemu_rbd_next_tok(snap, snap_len, p, ':', "snap name", &p);
} else {
ret = qemu_rbd_next_tok(name, name_len, p, ':', "object name", &p);
}
if (ret < 0 || !p) {
goto done;
}
ret = qemu_rbd_next_tok(conf, conf_len, p, '\0', "configuration", &p);
done:
qemu_free(buf);
return ret;
}
static int qemu_rbd_set_conf(rados_t cluster, const char *conf)
{
char *p, *buf;
char name[RBD_MAX_CONF_NAME_SIZE];
char value[RBD_MAX_CONF_VAL_SIZE];
int ret = 0;
buf = qemu_strdup(conf);
p = buf;
while (p) {
ret = qemu_rbd_next_tok(name, sizeof(name), p,
'=', "conf option name", &p);
if (ret < 0) {
break;
}
if (!p) {
error_report("conf option %s has no value", name);
ret = -EINVAL;
break;
}
ret = qemu_rbd_next_tok(value, sizeof(value), p,
':', "conf option value", &p);
if (ret < 0) {
break;
}
if (strcmp(name, "conf")) {
ret = rados_conf_set(cluster, name, value);
if (ret < 0) {
error_report("invalid conf option %s", name);
ret = -EINVAL;
break;
}
} else {
ret = rados_conf_read_file(cluster, value);
if (ret < 0) {
error_report("error reading conf file %s", value);
break;
}
}
}
qemu_free(buf);
return ret;
}
static int qemu_rbd_create(const char *filename, QEMUOptionParameter *options)
{
int64_t bytes = 0;
int64_t objsize;
int obj_order = 0;
char pool[RBD_MAX_POOL_NAME_SIZE];
char name[RBD_MAX_IMAGE_NAME_SIZE];
char snap_buf[RBD_MAX_SNAP_NAME_SIZE];
char conf[RBD_MAX_CONF_SIZE];
rados_t cluster;
rados_ioctx_t io_ctx;
int ret;
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
snap_buf, sizeof(snap_buf),
name, sizeof(name),
conf, sizeof(conf)) < 0) {
return -EINVAL;
}
/* Read out options */
while (options && options->name) {
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
bytes = options->value.n;
} else if (!strcmp(options->name, BLOCK_OPT_CLUSTER_SIZE)) {
if (options->value.n) {
objsize = options->value.n;
if ((objsize - 1) & objsize) { /* not a power of 2? */
error_report("obj size needs to be power of 2");
return -EINVAL;
}
if (objsize < 4096) {
error_report("obj size too small");
return -EINVAL;
}
obj_order = ffs(objsize) - 1;
}
}
options++;
}
if (rados_create(&cluster, NULL) < 0) {
error_report("error initializing");
return -EIO;
}
if (strstr(conf, "conf=") == NULL) {
if (rados_conf_read_file(cluster, NULL) < 0) {
error_report("error reading config file");
rados_shutdown(cluster);
return -EIO;
}
}
if (conf[0] != '\0' &&
qemu_rbd_set_conf(cluster, conf) < 0) {
error_report("error setting config options");
rados_shutdown(cluster);
return -EIO;
}
if (rados_connect(cluster) < 0) {
error_report("error connecting");
rados_shutdown(cluster);
return -EIO;
}
if (rados_ioctx_create(cluster, pool, &io_ctx) < 0) {
error_report("error opening pool %s", pool);
rados_shutdown(cluster);
return -EIO;
}
ret = rbd_create(io_ctx, name, bytes, &obj_order);
rados_ioctx_destroy(io_ctx);
rados_shutdown(cluster);
return ret;
}
/*
* This aio completion is being called from qemu_rbd_aio_event_reader()
* and runs in qemu context. It schedules a bh, but just in case the aio
* was not cancelled before.
*/
static void qemu_rbd_complete_aio(RADOSCB *rcb)
{
RBDAIOCB *acb = rcb->acb;
int64_t r;
if (acb->cancelled) {
qemu_vfree(acb->bounce);
qemu_aio_release(acb);
goto done;
}
r = rcb->ret;
if (acb->write) {
if (r < 0) {
acb->ret = r;
acb->error = 1;
} else if (!acb->error) {
acb->ret = rcb->size;
}
} else {
if (r < 0) {
memset(rcb->buf, 0, rcb->size);
acb->ret = r;
acb->error = 1;
} else if (r < rcb->size) {
memset(rcb->buf + r, 0, rcb->size - r);
if (!acb->error) {
acb->ret = rcb->size;
}
} else if (!acb->error) {
acb->ret = r;
}
}
/* Note that acb->bh can be NULL in case where the aio was cancelled */
acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb);
qemu_bh_schedule(acb->bh);
done:
qemu_free(rcb);
}
/*
* aio fd read handler. It runs in the qemu context and calls the
* completion handling of completed rados aio operations.
*/
static void qemu_rbd_aio_event_reader(void *opaque)
{
BDRVRBDState *s = opaque;
ssize_t ret;
do {
char *p = (char *)&s->event_rcb;
/* now read the rcb pointer that was sent from a non qemu thread */
if ((ret = read(s->fds[RBD_FD_READ], p + s->event_reader_pos,
sizeof(s->event_rcb) - s->event_reader_pos)) > 0) {
if (ret > 0) {
s->event_reader_pos += ret;
if (s->event_reader_pos == sizeof(s->event_rcb)) {
s->event_reader_pos = 0;
qemu_rbd_complete_aio(s->event_rcb);
s->qemu_aio_count--;
}
}
}
} while (ret < 0 && errno == EINTR);
}
static int qemu_rbd_aio_flush_cb(void *opaque)
{
BDRVRBDState *s = opaque;
return (s->qemu_aio_count > 0);
}
static int qemu_rbd_open(BlockDriverState *bs, const char *filename, int flags)
{
BDRVRBDState *s = bs->opaque;
char pool[RBD_MAX_POOL_NAME_SIZE];
char snap_buf[RBD_MAX_SNAP_NAME_SIZE];
char conf[RBD_MAX_CONF_SIZE];
int r;
if (qemu_rbd_parsename(filename, pool, sizeof(pool),
snap_buf, sizeof(snap_buf),
s->name, sizeof(s->name),
conf, sizeof(conf)) < 0) {
return -EINVAL;
}
s->snap = NULL;
if (snap_buf[0] != '\0') {
s->snap = qemu_strdup(snap_buf);
}
r = rados_create(&s->cluster, NULL);
if (r < 0) {
error_report("error initializing");
return r;
}
if (strstr(conf, "conf=") == NULL) {
r = rados_conf_read_file(s->cluster, NULL);
if (r < 0) {
error_report("error reading config file");
rados_shutdown(s->cluster);
return r;
}
}
if (conf[0] != '\0') {
r = qemu_rbd_set_conf(s->cluster, conf);
if (r < 0) {
error_report("error setting config options");
rados_shutdown(s->cluster);
return r;
}
}
r = rados_connect(s->cluster);
if (r < 0) {
error_report("error connecting");
rados_shutdown(s->cluster);
return r;
}
r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
if (r < 0) {
error_report("error opening pool %s", pool);
rados_shutdown(s->cluster);
return r;
}
r = rbd_open(s->io_ctx, s->name, &s->image, s->snap);
if (r < 0) {
error_report("error reading header from %s", s->name);
rados_ioctx_destroy(s->io_ctx);
rados_shutdown(s->cluster);
return r;
}
bs->read_only = (s->snap != NULL);
s->event_reader_pos = 0;
r = qemu_pipe(s->fds);
if (r < 0) {
error_report("error opening eventfd");
goto failed;
}
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, NULL, s);
return 0;
failed:
rbd_close(s->image);
rados_ioctx_destroy(s->io_ctx);
rados_shutdown(s->cluster);
return r;
}
static void qemu_rbd_close(BlockDriverState *bs)
{
BDRVRBDState *s = bs->opaque;
close(s->fds[0]);
close(s->fds[1]);
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);
qemu_free(s->snap);
rados_shutdown(s->cluster);
}
/*
* Cancel aio. Since we don't reference acb in a non qemu threads,
* it is safe to access it here.
*/
static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb)
{
RBDAIOCB *acb = (RBDAIOCB *) blockacb;
acb->cancelled = 1;
}
static AIOPool rbd_aio_pool = {
.aiocb_size = sizeof(RBDAIOCB),
.cancel = qemu_rbd_aio_cancel,
};
static int qemu_rbd_send_pipe(BDRVRBDState *s, RADOSCB *rcb)
{
int ret = 0;
while (1) {
fd_set wfd;
int fd = s->fds[RBD_FD_WRITE];
/* send the op pointer to the qemu thread that is responsible
for the aio/op completion. Must do it in a qemu thread context */
ret = write(fd, (void *)&rcb, sizeof(rcb));
if (ret >= 0) {
break;
}
if (errno == EINTR) {
continue;
}
if (errno != EAGAIN) {
break;
}
FD_ZERO(&wfd);
FD_SET(fd, &wfd);
do {
ret = select(fd + 1, NULL, &wfd, NULL, NULL);
} while (ret < 0 && errno == EINTR);
}
return ret;
}
/*
* This is the callback function for rbd_aio_read and _write
*
* Note: this function is being called from a non qemu thread so
* we need to be careful about what we do here. Generally we only
* write to the block notification pipe, and do the rest of the
* io completion handling from qemu_rbd_aio_event_reader() which
* runs in a qemu context.
*/
static void rbd_finish_aiocb(rbd_completion_t c, RADOSCB *rcb)
{
int ret;
rcb->ret = rbd_aio_get_return_value(c);
rbd_aio_release(c);
ret = qemu_rbd_send_pipe(rcb->s, rcb);
if (ret < 0) {
error_report("failed writing to acb->s->fds");
qemu_free(rcb);
}
}
/* Callback when all queued rbd_aio requests are complete */
static void rbd_aio_bh_cb(void *opaque)
{
RBDAIOCB *acb = opaque;
if (!acb->write) {
qemu_iovec_from_buffer(acb->qiov, acb->bounce, acb->qiov->size);
}
qemu_vfree(acb->bounce);
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
qemu_bh_delete(acb->bh);
acb->bh = NULL;
qemu_aio_release(acb);
}
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;
rbd_completion_t c;
int64_t off, size;
char *buf;
int r;
BDRVRBDState *s = bs->opaque;
acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque);
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 (write) {
qemu_iovec_to_buffer(acb->qiov, acb->bounce);
}
buf = acb->bounce;
off = sector_num * BDRV_SECTOR_SIZE;
size = nb_sectors * BDRV_SECTOR_SIZE;
s->qemu_aio_count++; /* All the RADOSCB */
rcb = qemu_malloc(sizeof(RADOSCB));
rcb->done = 0;
rcb->acb = acb;
rcb->buf = buf;
rcb->s = acb->s;
rcb->size = size;
r = rbd_aio_create_completion(rcb, (rbd_callback_t) rbd_finish_aiocb, &c);
if (r < 0) {
goto failed;
}
if (write) {
r = rbd_aio_write(s->image, off, size, buf, c);
} else {
r = rbd_aio_read(s->image, off, size, buf, c);
}
if (r < 0) {
goto failed;
}
return &acb->common;
failed:
qemu_free(rcb);
s->qemu_aio_count--;
qemu_aio_release(acb);
return NULL;
}
static BlockDriverAIOCB *qemu_rbd_aio_readv(BlockDriverState *bs,
int64_t sector_num,
QEMUIOVector *qiov,
int nb_sectors,
BlockDriverCompletionFunc *cb,
void *opaque)
{
return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
}
static BlockDriverAIOCB *qemu_rbd_aio_writev(BlockDriverState *bs,
int64_t sector_num,
QEMUIOVector *qiov,
int nb_sectors,
BlockDriverCompletionFunc *cb,
void *opaque)
{
return rbd_aio_rw_vector(bs, sector_num, qiov, nb_sectors, cb, opaque, 1);
}
static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi)
{
BDRVRBDState *s = bs->opaque;
rbd_image_info_t info;
int r;
r = rbd_stat(s->image, &info, sizeof(info));
if (r < 0) {
return r;
}
bdi->cluster_size = info.obj_size;
return 0;
}
static int64_t qemu_rbd_getlength(BlockDriverState *bs)
{
BDRVRBDState *s = bs->opaque;
rbd_image_info_t info;
int r;
r = rbd_stat(s->image, &info, sizeof(info));
if (r < 0) {
return r;
}
return info.size;
}
static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset)
{
BDRVRBDState *s = bs->opaque;
int r;
r = rbd_resize(s->image, offset);
if (r < 0) {
return r;
}
return 0;
}
static int qemu_rbd_snap_create(BlockDriverState *bs,
QEMUSnapshotInfo *sn_info)
{
BDRVRBDState *s = bs->opaque;
int r;
if (sn_info->name[0] == '\0') {
return -EINVAL; /* we need a name for rbd snapshots */
}
/*
* rbd snapshots are using the name as the user controlled unique identifier
* we can't use the rbd snapid for that purpose, as it can't be set
*/
if (sn_info->id_str[0] != '\0' &&
strcmp(sn_info->id_str, sn_info->name) != 0) {
return -EINVAL;
}
if (strlen(sn_info->name) >= sizeof(sn_info->id_str)) {
return -ERANGE;
}
r = rbd_snap_create(s->image, sn_info->name);
if (r < 0) {
error_report("failed to create snap: %s", strerror(-r));
return r;
}
return 0;
}
static int qemu_rbd_snap_list(BlockDriverState *bs,
QEMUSnapshotInfo **psn_tab)
{
BDRVRBDState *s = bs->opaque;
QEMUSnapshotInfo *sn_info, *sn_tab = NULL;
int i, snap_count;
rbd_snap_info_t *snaps;
int max_snaps = RBD_MAX_SNAPS;
do {
snaps = qemu_malloc(sizeof(*snaps) * max_snaps);
snap_count = rbd_snap_list(s->image, snaps, &max_snaps);
if (snap_count < 0) {
qemu_free(snaps);
}
} while (snap_count == -ERANGE);
if (snap_count <= 0) {
return snap_count;
}
sn_tab = qemu_mallocz(snap_count * sizeof(QEMUSnapshotInfo));
for (i = 0; i < snap_count; i++) {
const char *snap_name = snaps[i].name;
sn_info = sn_tab + i;
pstrcpy(sn_info->id_str, sizeof(sn_info->id_str), snap_name);
pstrcpy(sn_info->name, sizeof(sn_info->name), snap_name);
sn_info->vm_state_size = snaps[i].size;
sn_info->date_sec = 0;
sn_info->date_nsec = 0;
sn_info->vm_clock_nsec = 0;
}
rbd_snap_list_end(snaps);
*psn_tab = sn_tab;
return snap_count;
}
static QEMUOptionParameter qemu_rbd_create_options[] = {
{
.name = BLOCK_OPT_SIZE,
.type = OPT_SIZE,
.help = "Virtual disk size"
},
{
.name = BLOCK_OPT_CLUSTER_SIZE,
.type = OPT_SIZE,
.help = "RBD object size"
},
{NULL}
};
static BlockDriver bdrv_rbd = {
.format_name = "rbd",
.instance_size = sizeof(BDRVRBDState),
.bdrv_file_open = qemu_rbd_open,
.bdrv_close = qemu_rbd_close,
.bdrv_create = qemu_rbd_create,
.bdrv_get_info = qemu_rbd_getinfo,
.create_options = qemu_rbd_create_options,
.bdrv_getlength = qemu_rbd_getlength,
.bdrv_truncate = qemu_rbd_truncate,
.protocol_name = "rbd",
.bdrv_aio_readv = qemu_rbd_aio_readv,
.bdrv_aio_writev = qemu_rbd_aio_writev,
.bdrv_snapshot_create = qemu_rbd_snap_create,
.bdrv_snapshot_list = qemu_rbd_snap_list,
};
static void bdrv_rbd_init(void)
{
bdrv_register(&bdrv_rbd);
}
block_init(bdrv_rbd_init);

View File

@@ -8,12 +8,21 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef _WIN32
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <netinet/tcp.h>
#define closesocket(s) close(s)
#endif
#include "qemu-common.h"
#include "qemu-error.h"
#include "qemu_socket.h"
#include "block_int.h"
#include "bitops.h"
#define SD_PROTO_VER 0x01
@@ -196,7 +205,7 @@ static inline uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval)
return hval;
}
static inline int is_data_obj_writable(SheepdogInode *inode, unsigned int idx)
static inline int is_data_obj_writeable(SheepdogInode *inode, unsigned int idx)
{
return inode->vdi_id == inode->data_vdi_id[idx];
}
@@ -444,12 +453,18 @@ static SheepdogAIOCB *sd_aio_setup(BlockDriverState *bs, QEMUIOVector *qiov,
static int sd_schedule_bh(QEMUBHFunc *cb, SheepdogAIOCB *acb)
{
if (acb->bh) {
error_report("bug: %d %d", acb->aiocb_type, acb->aiocb_type);
error_report("bug: %d %d\n", acb->aiocb_type, acb->aiocb_type);
return -EIO;
}
acb->bh = qemu_bh_new(cb, acb);
if (!acb->bh) {
error_report("oom: %d %d\n", acb->aiocb_type, acb->aiocb_type);
return -EIO;
}
qemu_bh_schedule(acb->bh);
return 0;
}
@@ -496,7 +511,7 @@ static ssize_t recvmsg(int s, struct msghdr *msg, int flags)
}
buf = qemu_malloc(size);
ret = qemu_recv(s, buf, size, flags);
ret = recv(s, buf, size, flags);
if (ret < 0) {
goto out;
}
@@ -592,7 +607,7 @@ static int connect_to_sdog(const char *addr, const char *port)
ret = getaddrinfo(addr, port, &hints, &res0);
if (ret) {
error_report("unable to get address info %s, %s",
error_report("unable to get address info %s, %s\n",
addr, strerror(errno));
return -1;
}
@@ -622,7 +637,7 @@ static int connect_to_sdog(const char *addr, const char *port)
goto success;
}
fd = -1;
error_report("failed connect to %s:%s", addr, port);
error_report("failed connect to %s:%s\n", addr, port);
success:
freeaddrinfo(res0);
return fd;
@@ -638,7 +653,7 @@ again:
if (errno == EINTR || errno == EAGAIN) {
goto again;
}
error_report("failed to recv a rsp, %s", strerror(errno));
error_report("failed to recv a rsp, %s\n", strerror(errno));
return 1;
}
@@ -697,7 +712,7 @@ static int send_req(int sockfd, SheepdogReq *hdr, void *data,
ret = do_writev(sockfd, iov, sizeof(*hdr) + *wlen, 0);
if (ret) {
error_report("failed to send a req, %s", strerror(errno));
error_report("failed to send a req, %s\n", strerror(errno));
ret = -1;
}
@@ -717,7 +732,7 @@ static int do_req(int sockfd, SheepdogReq *hdr, void *data,
ret = do_read(sockfd, hdr, sizeof(*hdr));
if (ret) {
error_report("failed to get a rsp, %s", strerror(errno));
error_report("failed to get a rsp, %s\n", strerror(errno));
ret = -1;
goto out;
}
@@ -729,7 +744,7 @@ static int do_req(int sockfd, SheepdogReq *hdr, void *data,
if (*rlen) {
ret = do_read(sockfd, data, *rlen);
if (ret) {
error_report("failed to get the data, %s", strerror(errno));
error_report("failed to get the data, %s\n", strerror(errno));
ret = -1;
goto out;
}
@@ -766,7 +781,7 @@ static void send_pending_req(BDRVSheepdogState *s, uint64_t oid, uint32_t id)
ret = add_aio_request(s, aio_req, acb->qiov->iov,
acb->qiov->niov, 0, acb->aiocb_type);
if (ret < 0) {
error_report("add_aio_request is failed");
error_report("add_aio_request is failed\n");
free_aio_req(s, aio_req);
if (QLIST_EMPTY(&acb->aioreq_head)) {
sd_finish_aiocb(acb);
@@ -799,7 +814,7 @@ static void aio_read_response(void *opaque)
/* read a header */
ret = do_read(fd, &rsp, sizeof(rsp));
if (ret) {
error_report("failed to get the header, %s", strerror(errno));
error_report("failed to get the header, %s\n", strerror(errno));
return;
}
@@ -810,7 +825,7 @@ static void aio_read_response(void *opaque)
}
}
if (!aio_req) {
error_report("cannot find aio_req %x", rsp.id);
error_report("cannot find aio_req %x\n", rsp.id);
return;
}
@@ -846,7 +861,7 @@ static void aio_read_response(void *opaque)
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));
error_report("failed to get the data, %s\n", strerror(errno));
return;
}
break;
@@ -854,7 +869,7 @@ static void aio_read_response(void *opaque)
if (rsp.result != SD_RES_SUCCESS) {
acb->ret = -EIO;
error_report("%s", sd_strerror(rsp.result));
error_report("%s\n", sd_strerror(rsp.result));
}
rest = free_aio_req(s, aio_req);
@@ -911,7 +926,7 @@ static int get_sheep_fd(BDRVSheepdogState *s)
fd = connect_to_sdog(s->addr, s->port);
if (fd < 0) {
error_report("%s", strerror(errno));
error_report("%s\n", strerror(errno));
return -1;
}
@@ -919,7 +934,7 @@ static int get_sheep_fd(BDRVSheepdogState *s)
ret = set_nodelay(fd);
if (ret) {
error_report("%s", strerror(errno));
error_report("%s\n", strerror(errno));
closesocket(fd);
return -1;
}
@@ -1035,7 +1050,7 @@ static int find_vdi_name(BDRVSheepdogState *s, char *filename, uint32_t snapid,
}
if (rsp->result != SD_RES_SUCCESS) {
error_report("cannot get vdi info, %s, %s %d %s",
error_report("cannot get vdi info, %s, %s %d %s\n",
sd_strerror(rsp->result), filename, snapid, tag);
ret = -1;
goto out;
@@ -1063,7 +1078,7 @@ static int add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
uint64_t old_oid = aio_req->base_oid;
if (!nr_copies) {
error_report("bug");
error_report("bug\n");
}
memset(&hdr, 0, sizeof(hdr));
@@ -1096,14 +1111,14 @@ static int add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req,
/* send a header */
ret = do_write(s->fd, &hdr, sizeof(hdr));
if (ret) {
error_report("failed to send a req, %s", strerror(errno));
error_report("failed to send a req, %s\n", strerror(errno));
return -EIO;
}
if (wlen) {
ret = do_writev(s->fd, iov, wlen, aio_req->iov_offset);
if (ret) {
error_report("failed to send a data, %s", strerror(errno));
error_report("failed to send a data, %s\n", strerror(errno));
return -EIO;
}
}
@@ -1145,7 +1160,7 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
ret = do_req(fd, (SheepdogReq *)&hdr, buf, &wlen, &rlen);
if (ret) {
error_report("failed to send a request to the sheep");
error_report("failed to send a request to the sheep\n");
return -1;
}
@@ -1153,7 +1168,7 @@ static int read_write_object(int fd, char *buf, uint64_t oid, int copies,
case SD_RES_SUCCESS:
return 0;
default:
error_report("%s", sd_strerror(rsp->result));
error_report("%s\n", sd_strerror(rsp->result));
return -1;
}
}
@@ -1206,7 +1221,7 @@ static int sd_open(BlockDriverState *bs, const char *filename, int flags)
fd = connect_to_sdog(s->addr, s->port);
if (fd < 0) {
error_report("failed to connect");
error_report("failed to connect\n");
goto out;
}
@@ -1275,7 +1290,7 @@ static int do_sd_create(char *filename, int64_t vdi_size,
}
if (rsp->result != SD_RES_SUCCESS) {
error_report("%s, %s", sd_strerror(rsp->result), filename);
error_report("%s, %s\n", sd_strerror(rsp->result), filename);
return -EIO;
}
@@ -1286,92 +1301,26 @@ static int do_sd_create(char *filename, int64_t vdi_size,
return 0;
}
static int sd_prealloc(const char *filename)
{
BlockDriverState *bs = NULL;
uint32_t idx, max_idx;
int64_t vdi_size;
void *buf = qemu_mallocz(SD_DATA_OBJ_SIZE);
int ret;
ret = bdrv_file_open(&bs, filename, BDRV_O_RDWR);
if (ret < 0) {
goto out;
}
vdi_size = bdrv_getlength(bs);
if (vdi_size < 0) {
ret = vdi_size;
goto out;
}
max_idx = DIV_ROUND_UP(vdi_size, SD_DATA_OBJ_SIZE);
for (idx = 0; idx < max_idx; idx++) {
/*
* The created image can be a cloned image, so we need to read
* a data from the source image.
*/
ret = bdrv_pread(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
if (ret < 0) {
goto out;
}
ret = bdrv_pwrite(bs, idx * SD_DATA_OBJ_SIZE, buf, SD_DATA_OBJ_SIZE);
if (ret < 0) {
goto out;
}
}
out:
if (bs) {
bdrv_delete(bs);
}
qemu_free(buf);
return ret;
}
static int sd_create(const char *filename, QEMUOptionParameter *options)
{
int ret;
uint32_t vid = 0, base_vid = 0;
uint32_t vid = 0;
int64_t vdi_size = 0;
char *backing_file = NULL;
BDRVSheepdogState s;
char vdi[SD_MAX_VDI_LEN], tag[SD_MAX_VDI_TAG_LEN];
uint32_t snapid;
int prealloc = 0;
const char *vdiname;
strstart(filename, "sheepdog:", &vdiname);
memset(&s, 0, sizeof(s));
memset(vdi, 0, sizeof(vdi));
memset(tag, 0, sizeof(tag));
if (parse_vdiname(&s, vdiname, vdi, &snapid, tag) < 0) {
error_report("invalid filename");
return -EINVAL;
}
strstart(filename, "sheepdog:", (const char **)&filename);
while (options && options->name) {
if (!strcmp(options->name, BLOCK_OPT_SIZE)) {
vdi_size = options->value.n;
} else if (!strcmp(options->name, BLOCK_OPT_BACKING_FILE)) {
backing_file = options->value.s;
} else if (!strcmp(options->name, BLOCK_OPT_PREALLOC)) {
if (!options->value.s || !strcmp(options->value.s, "off")) {
prealloc = 0;
} else if (!strcmp(options->value.s, "full")) {
prealloc = 1;
} else {
error_report("Invalid preallocation mode: '%s'",
options->value.s);
return -EINVAL;
}
}
options++;
}
if (vdi_size > SD_MAX_VDI_SIZE) {
error_report("too big image size");
error_report("too big image size\n");
return -EINVAL;
}
@@ -1383,7 +1332,7 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
/* Currently, only Sheepdog backing image is supported. */
drv = bdrv_find_protocol(backing_file);
if (!drv || strcmp(drv->protocol_name, "sheepdog") != 0) {
error_report("backing_file must be a sheepdog image");
error_report("backing_file must be a sheepdog image\n");
return -EINVAL;
}
@@ -1394,21 +1343,16 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
s = bs->opaque;
if (!is_snapshot(&s->inode)) {
error_report("cannot clone from a non snapshot vdi");
error_report("cannot clone from a non snapshot vdi\n");
bdrv_delete(bs);
return -EINVAL;
}
base_vid = s->inode.vdi_id;
vid = s->inode.vdi_id;
bdrv_delete(bs);
}
ret = do_sd_create(vdi, vdi_size, base_vid, &vid, 0, s.addr, s.port);
if (!prealloc || ret) {
return ret;
}
return sd_prealloc(filename);
return do_sd_create((char *)filename, vdi_size, vid, NULL, 0, NULL, NULL);
}
static void sd_close(BlockDriverState *bs)
@@ -1439,7 +1383,7 @@ static void sd_close(BlockDriverState *bs)
if (!ret && rsp->result != SD_RES_SUCCESS &&
rsp->result != SD_RES_VDI_NOT_LOCKED) {
error_report("%s, %s", sd_strerror(rsp->result), s->name);
error_report("%s, %s\n", sd_strerror(rsp->result), s->name);
}
qemu_aio_set_fd_handler(s->fd, NULL, NULL, NULL, NULL, NULL);
@@ -1461,10 +1405,10 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
unsigned int datalen;
if (offset < s->inode.vdi_size) {
error_report("shrinking is not supported");
error_report("shrinking is not supported\n");
return -EINVAL;
} else if (offset > SD_MAX_VDI_SIZE) {
error_report("too big image size");
error_report("too big image size\n");
return -EINVAL;
}
@@ -1481,7 +1425,7 @@ static int sd_truncate(BlockDriverState *bs, int64_t offset)
close(fd);
if (ret < 0) {
error_report("failed to update an inode.");
error_report("failed to update an inode.\n");
return -EIO;
}
@@ -1554,7 +1498,7 @@ static int sd_create_branch(BDRVSheepdogState *s)
fd = connect_to_sdog(s->addr, s->port);
if (fd < 0) {
error_report("failed to connect");
error_report("failed to connect\n");
goto out;
}
@@ -1631,7 +1575,7 @@ static void sd_readv_writev_bh_cb(void *p)
create = 1;
} else if (acb->aiocb_type == AIOCB_WRITE_UDATA
&& !is_data_obj_writable(inode, idx)) {
&& !is_data_obj_writeable(inode, idx)) {
/* Copy-On-Write */
create = 1;
old_oid = oid;
@@ -1672,7 +1616,7 @@ static void sd_readv_writev_bh_cb(void *p)
ret = add_aio_request(s, aio_req, acb->qiov->iov, acb->qiov->niov,
create, acb->aiocb_type);
if (ret < 0) {
error_report("add_aio_request is failed");
error_report("add_aio_request is failed\n");
free_aio_req(s, aio_req);
acb->ret = -EIO;
goto out;
@@ -1749,7 +1693,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
if (s->is_snapshot) {
error_report("You can't create a snapshot of a snapshot VDI, "
"%s (%" PRIu32 ").", s->name, s->inode.vdi_id);
"%s (%" PRIu32 ").\n", s->name, s->inode.vdi_id);
return -EINVAL;
}
@@ -1772,7 +1716,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);
if (ret < 0) {
error_report("failed to write snapshot's inode.");
error_report("failed to write snapshot's inode.\n");
ret = -EIO;
goto cleanup;
}
@@ -1780,7 +1724,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
ret = do_sd_create(s->name, s->inode.vdi_size, s->inode.vdi_id, &new_vid, 1,
s->addr, s->port);
if (ret < 0) {
error_report("failed to create inode for snapshot. %s",
error_report("failed to create inode for snapshot. %s\n",
strerror(errno));
ret = -EIO;
goto cleanup;
@@ -1792,7 +1736,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
s->inode.nr_copies, datalen, 0);
if (ret < 0) {
error_report("failed to read new inode info. %s", strerror(errno));
error_report("failed to read new inode info. %s\n", strerror(errno));
ret = -EIO;
goto cleanup;
}
@@ -1831,14 +1775,14 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
ret = find_vdi_name(s, vdi, snapid, tag, &vid, 1);
if (ret) {
error_report("Failed to find_vdi_name");
error_report("Failed to find_vdi_name\n");
ret = -ENOENT;
goto out;
}
fd = connect_to_sdog(s->addr, s->port);
if (fd < 0) {
error_report("failed to connect");
error_report("failed to connect\n");
goto out;
}
@@ -1856,7 +1800,7 @@ static int sd_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
memcpy(&s->inode, buf, sizeof(s->inode));
if (!s->inode.vm_state_size) {
error_report("Invalid snapshot");
error_report("Invalid snapshot\n");
ret = -ENOENT;
goto out;
}
@@ -1873,7 +1817,7 @@ out:
qemu_free(buf);
qemu_free(old_s);
error_report("failed to open. recover old bdrv_sd_state.");
error_report("failed to open. recover old bdrv_sd_state.\n");
return ret;
}
@@ -1884,6 +1828,20 @@ static int sd_snapshot_delete(BlockDriverState *bs, const char *snapshot_id)
return 0;
}
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
#define BITS_PER_BYTE 8
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
#define BITS_PER_LONG (BITS_PER_BYTE * sizeof(long))
static inline int test_bit(unsigned int nr, const unsigned long *addr)
{
return ((1UL << (nr % BITS_PER_LONG)) &
(((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
}
static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
{
BDRVSheepdogState *s = bs->opaque;
@@ -1928,7 +1886,7 @@ static int sd_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab)
fd = connect_to_sdog(s->addr, s->port);
if (fd < 0) {
error_report("failed to connect");
error_report("failed to connect\n");
goto out;
}
@@ -2002,7 +1960,7 @@ static int do_load_save_vmstate(BDRVSheepdogState *s, uint8_t *data,
}
if (ret < 0) {
error_report("failed to save vmstate %s", strerror(errno));
error_report("failed to save vmstate %s\n", strerror(errno));
ret = -EIO;
goto cleanup;
}
@@ -2044,11 +2002,6 @@ static QEMUOptionParameter sd_create_options[] = {
.type = OPT_STRING,
.help = "File name of a base image"
},
{
.name = BLOCK_OPT_PREALLOC,
.type = OPT_STRING,
.help = "Preallocation mode (allowed values: off, full)"
},
{ NULL }
};

View File

@@ -87,7 +87,6 @@ void uuid_unparse(const uuid_t uu, char *out);
#define MiB (KiB * KiB)
#define SECTOR_SIZE 512
#define DEFAULT_CLUSTER_SIZE (1 * MiB)
#if defined(CONFIG_VDI_DEBUG)
#define logout(fmt, ...) \
@@ -114,19 +113,19 @@ void uuid_unparse(const uuid_t uu, char *out);
*/
#define VDI_TEXT "<<< QEMU VM Virtual Disk Image >>>\n"
/* Unallocated blocks use this index (no need to convert endianness). */
/* Unallocated blocks use this index (no need to convert endianess). */
#define VDI_UNALLOCATED UINT32_MAX
#if !defined(CONFIG_UUID)
void uuid_generate(uuid_t out)
{
memset(out, 0, sizeof(uuid_t));
memset(out, 0, sizeof(out));
}
int uuid_is_null(const uuid_t uu)
{
uuid_t null_uuid = { 0 };
return memcmp(uu, null_uuid, sizeof(uuid_t)) == 0;
return memcmp(uu, null_uuid, sizeof(uu)) == 0;
}
void uuid_unparse(const uuid_t uu, char *out)
@@ -152,7 +151,6 @@ typedef struct {
/* Buffer for new allocated block. */
void *block_buffer;
void *orig_buf;
bool is_write;
int header_modified;
BlockDriverAIOCB *hd_aiocb;
struct iovec hd_iov;
@@ -188,6 +186,7 @@ typedef struct {
} VdiHeader;
typedef struct {
BlockDriverState *hd;
/* The block map entries are little endian (even in memory). */
uint32_t *bmap;
/* Size of block (bytes). */
@@ -196,7 +195,7 @@ typedef struct {
uint32_t block_sectors;
/* First sector of block map. */
uint32_t bmap_sector;
/* VDI header (converted to host endianness). */
/* VDI header (converted to host endianess). */
VdiHeader header;
} BDRVVdiState;
@@ -505,8 +504,6 @@ static VdiAIOCB *vdi_aio_setup(BlockDriverState *bs, int64_t sector_num,
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;
@@ -545,20 +542,14 @@ static int vdi_schedule_bh(QEMUBHFunc *cb, VdiAIOCB *acb)
}
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)
static void vdi_aio_read_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);
}
vdi_aio_read_cb(opaque, 0);
}
static void vdi_aio_read_cb(void *opaque, int ret)
@@ -606,7 +597,7 @@ static void vdi_aio_read_cb(void *opaque, int ret)
if (bmap_entry == VDI_UNALLOCATED) {
/* 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);
ret = vdi_schedule_bh(vdi_aio_read_bh, acb);
if (ret < 0) {
goto done;
}
@@ -620,7 +611,6 @@ static void vdi_aio_read_cb(void *opaque, int ret)
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;
}
}
@@ -639,23 +629,12 @@ static BlockDriverAIOCB *vdi_aio_readv(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque)
{
VdiAIOCB *acb;
int ret;
logout("\n");
acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0);
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;
}
vdi_aio_read_cb(acb, 0);
return &acb->common;
}
@@ -695,7 +674,6 @@ static void vdi_aio_write_cb(void *opaque, int ret)
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;
@@ -725,7 +703,6 @@ static void vdi_aio_write_cb(void *opaque, int ret)
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;
@@ -776,7 +753,6 @@ static void vdi_aio_write_cb(void *opaque, int ret)
&acb->hd_qiov, s->block_sectors,
vdi_aio_write_cb, acb);
if (acb->hd_aiocb == NULL) {
ret = -EIO;
goto done;
}
} else {
@@ -789,7 +765,6 @@ static void vdi_aio_write_cb(void *opaque, int ret)
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;
}
}
@@ -809,23 +784,12 @@ static BlockDriverAIOCB *vdi_aio_writev(BlockDriverState *bs,
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;
}
vdi_aio_write_cb(acb, 0);
return &acb->common;
}
@@ -835,7 +799,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
int result = 0;
uint64_t bytes = 0;
uint32_t blocks;
size_t block_size = DEFAULT_CLUSTER_SIZE;
size_t block_size = 1 * MiB;
uint32_t image_type = VDI_TYPE_DYNAMIC;
VdiHeader header;
size_t i;
@@ -936,10 +900,10 @@ static void vdi_close(BlockDriverState *bs)
{
}
static int vdi_flush(BlockDriverState *bs)
static void vdi_flush(BlockDriverState *bs)
{
logout("\n");
return bdrv_flush(bs->file);
bdrv_flush(bs->file);
}
@@ -953,8 +917,7 @@ static QEMUOptionParameter vdi_create_options[] = {
{
.name = BLOCK_OPT_CLUSTER_SIZE,
.type = OPT_SIZE,
.help = "VDI cluster (block) size",
.value = { .n = DEFAULT_CLUSTER_SIZE },
.help = "VDI cluster (block) size"
},
#endif
#if defined(CONFIG_VDI_STATIC_IMAGE)

File diff suppressed because it is too large Load Diff

View File

@@ -110,6 +110,8 @@ struct vhd_dyndisk_header {
};
typedef struct BDRVVPCState {
BlockDriverState *hd;
uint8_t footer_buf[HEADER_SIZE];
uint64_t free_data_block_offset;
int max_table_entries;
@@ -437,10 +439,6 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num,
return 0;
}
static int vpc_flush(BlockDriverState *bs)
{
return bdrv_flush(bs->file);
}
/*
* Calculates the number of cylinders, heads and sectors per cylinder
@@ -502,11 +500,14 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
uint8_t secs_per_cyl = 0;
size_t block_size, num_bat_entries;
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;
while (options && options->name) {
if (!strcmp(options->name, "size")) {
total_sectors = options->value.n / 512;
}
options++;
}
// Create the file
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
@@ -518,8 +519,7 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
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;
return -EFBIG;
}
}
total_sectors = (int64_t) cyls * heads * secs_per_cyl;
@@ -558,28 +558,22 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
block_size = 0x200000;
num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512);
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
goto fail;
}
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE)
return -EIO;
if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0) {
goto fail;
}
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) {
goto fail;
}
if (lseek(fd, 1536 + ((num_bat_entries * 4 + 511) & ~511), SEEK_SET) < 0)
return -EIO;
if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE)
return -EIO;
// Write the initial BAT
if (lseek(fd, 3 * 512, SEEK_SET) < 0) {
goto fail;
}
if (lseek(fd, 3 * 512, SEEK_SET) < 0)
return -EIO;
memset(buf, 0xFF, 512);
for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++) {
if (write(fd, buf, 512) != 512) {
goto fail;
}
}
for (i = 0; i < (num_bat_entries * 4 + 511) / 512; i++)
if (write(fd, buf, 512) != 512)
return -EIO;
// Prepare the Dynamic Disk Header
@@ -596,18 +590,13 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
dyndisk_header->checksum = be32_to_cpu(vpc_checksum(buf, 1024));
// Write the header
if (lseek(fd, 512, SEEK_SET) < 0) {
goto fail;
}
if (lseek(fd, 512, SEEK_SET) < 0)
return -EIO;
if (write(fd, buf, 1024) != 1024)
return -EIO;
if (write(fd, buf, 1024) != 1024) {
goto fail;
}
ret = 0;
fail:
close(fd);
return ret;
return 0;
}
static void vpc_close(BlockDriverState *bs)
@@ -629,15 +618,14 @@ static QEMUOptionParameter vpc_create_options[] = {
};
static BlockDriver bdrv_vpc = {
.format_name = "vpc",
.instance_size = sizeof(BDRVVPCState),
.bdrv_probe = vpc_probe,
.bdrv_open = vpc_open,
.bdrv_read = vpc_read,
.bdrv_write = vpc_write,
.bdrv_flush = vpc_flush,
.bdrv_close = vpc_close,
.bdrv_create = vpc_create,
.format_name = "vpc",
.instance_size = sizeof(BDRVVPCState),
.bdrv_probe = vpc_probe,
.bdrv_open = vpc_open,
.bdrv_read = vpc_read,
.bdrv_write = vpc_write,
.bdrv_close = vpc_close,
.bdrv_create = vpc_create,
.create_options = vpc_create_options,
};

View File

@@ -756,7 +756,6 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
if (st.st_size > 0x7fffffff) {
fprintf(stderr, "File %s is larger than 2GB\n", buffer);
free(buffer);
closedir(dir);
return -2;
}
direntry->size=cpu_to_le32(S_ISDIR(st.st_mode)?0:st.st_size);
@@ -2283,6 +2282,7 @@ static void check1(BDRVVVFATState* s)
fprintf(stderr, "deleted\n");
continue;
}
assert(mapping->dir_index >= 0);
assert(mapping->dir_index < s->directory.next);
direntry_t* direntry = array_get(&(s->directory), mapping->dir_index);
assert(mapping->begin == begin_of_direntry(direntry) || mapping->first_mapping_index >= 0);
@@ -2665,11 +2665,6 @@ static int vvfat_write(BlockDriverState *bs, int64_t sector_num,
DLOG(checkpoint());
/* Check if we're operating in read-only mode */
if (s->qcow == NULL) {
return -EACCES;
}
vvfat_close_current_file(s);
/*
@@ -2768,12 +2763,12 @@ static int vvfat_is_allocated(BlockDriverState *bs,
static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
const uint8_t* buffer, int nb_sectors) {
BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
BDRVVVFATState* s = bs->opaque;
return try_commit(s);
}
static void write_target_close(BlockDriverState *bs) {
BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
BDRVVVFATState* s = bs->opaque;
bdrv_delete(s->qcow);
free(s->qcow_filename);
}
@@ -2788,7 +2783,6 @@ static int enable_write_target(BDRVVVFATState *s)
{
BlockDriver *bdrv_qcow;
QEMUOptionParameter *options;
int ret;
int size = sector2cluster(s, s->sector_count);
s->used_clusters = calloc(size, 1);
@@ -2804,16 +2798,11 @@ static int enable_write_target(BDRVVVFATState *s)
if (bdrv_create(bdrv_qcow, s->qcow_filename, options) < 0)
return -1;
s->qcow = bdrv_new("");
if (s->qcow == NULL) {
return -1;
}
ret = bdrv_open(s->qcow, s->qcow_filename,
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
if (ret < 0) {
return ret;
if (s->qcow == NULL ||
bdrv_open(s->qcow, s->qcow_filename, BDRV_O_RDWR, bdrv_qcow) < 0)
{
return -1;
}
#ifndef _WIN32
@@ -2822,8 +2811,7 @@ static int enable_write_target(BDRVVVFATState *s)
s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
s->bs->backing_hd->drv = &vvfat_write_target;
s->bs->backing_hd->opaque = qemu_malloc(sizeof(void*));
*(void**)s->bs->backing_hd->opaque = s;
s->bs->backing_hd->opaque = s;
return 0;
}

View File

@@ -29,6 +29,7 @@
#include "qemu-queue.h"
#define BLOCK_FLAG_ENCRYPT 1
#define BLOCK_FLAG_COMPRESS 2
#define BLOCK_FLAG_COMPAT6 4
#define BLOCK_OPT_SIZE "size"
@@ -37,9 +38,7 @@
#define BLOCK_OPT_BACKING_FILE "backing_file"
#define BLOCK_OPT_BACKING_FMT "backing_fmt"
#define BLOCK_OPT_CLUSTER_SIZE "cluster_size"
#define BLOCK_OPT_TABLE_SIZE "table_size"
#define BLOCK_OPT_PREALLOC "preallocation"
#define BLOCK_OPT_SUBFMT "subformat"
typedef struct AIOPool {
void (*cancel)(BlockDriverAIOCB *acb);
@@ -60,7 +59,7 @@ struct BlockDriver {
const uint8_t *buf, int nb_sectors);
void (*bdrv_close)(BlockDriverState *bs);
int (*bdrv_create)(const char *filename, QEMUOptionParameter *options);
int (*bdrv_flush)(BlockDriverState *bs);
void (*bdrv_flush)(BlockDriverState *bs);
int (*bdrv_is_allocated)(BlockDriverState *bs, int64_t sector_num,
int nb_sectors, int *pnum);
int (*bdrv_set_key)(BlockDriverState *bs, const char *key);
@@ -74,8 +73,6 @@ struct BlockDriver {
BlockDriverCompletionFunc *cb, void *opaque);
BlockDriverAIOCB *(*bdrv_aio_flush)(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque);
int (*bdrv_discard)(BlockDriverState *bs, int64_t sector_num,
int nb_sectors);
int (*bdrv_aio_multiwrite)(BlockDriverState *bs, BlockRequest *reqs,
int num_reqs);
@@ -86,7 +83,6 @@ struct BlockDriver {
const char *protocol_name;
int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset);
int64_t (*bdrv_getlength)(BlockDriverState *bs);
int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
int (*bdrv_write_compressed)(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors);
@@ -97,8 +93,6 @@ struct BlockDriver {
int (*bdrv_snapshot_delete)(BlockDriverState *bs, const char *snapshot_id);
int (*bdrv_snapshot_list)(BlockDriverState *bs,
QEMUSnapshotInfo **psn_info);
int (*bdrv_snapshot_load_tmp)(BlockDriverState *bs,
const char *snapshot_name);
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
int (*bdrv_save_vmstate)(BlockDriverState *bs, const uint8_t *buf,
@@ -154,8 +148,9 @@ 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 probed; /* if true, format was probed automatically */
/* event callback when inserting/removing */
void (*change_cb)(void *opaque, int reason);
void (*change_cb)(void *opaque);
void *change_opaque;
BlockDriver *drv; /* NULL means no media */
@@ -196,18 +191,15 @@ struct BlockDriverState {
/* NOTE: the following infos are only hints for real hardware
drivers. They are not used by the block driver */
int cyls, heads, secs, translation;
int type;
BlockErrorAction on_read_error, on_write_error;
char device_name[32];
unsigned long *dirty_bitmap;
int64_t dirty_count;
int in_use; /* users other than guest access, eg. block migration */
QTAILQ_ENTRY(BlockDriverState) list;
void *private;
};
#define CHANGE_MEDIA 0x01
#define CHANGE_SIZE 0x02
struct BlockDriverAIOCB {
AIOPool *pool;
BlockDriverState *bs;
@@ -234,8 +226,6 @@ typedef struct BlockConf {
uint16_t logical_block_size;
uint16_t min_io_size;
uint32_t opt_io_size;
int32_t bootindex;
uint32_t discard_granularity;
} BlockConf;
static inline unsigned int get_physical_block_exp(BlockConf *conf)
@@ -258,9 +248,6 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
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), \
DEFINE_PROP_UINT32("discard_granularity", _state, \
_conf.discard_granularity, 0)
DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0)
#endif /* BLOCK_INT_H */

View File

@@ -14,42 +14,9 @@
#include "qemu-option.h"
#include "qemu-config.h"
#include "sysemu.h"
#include "hw/qdev.h"
#include "block_int.h"
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
static const char *const if_name[IF_COUNT] = {
[IF_NONE] = "none",
[IF_IDE] = "ide",
[IF_SCSI] = "scsi",
[IF_FLOPPY] = "floppy",
[IF_PFLASH] = "pflash",
[IF_MTD] = "mtd",
[IF_SD] = "sd",
[IF_VIRTIO] = "virtio",
[IF_XEN] = "xen",
};
static const int if_max_devs[IF_COUNT] = {
/*
* Do not change these numbers! They govern how drive option
* index maps to unit and bus. That mapping is ABI.
*
* All controllers used to imlement if=T drives need to support
* if_max_devs[T] units, for any T with if_max_devs[T] != 0.
* Otherwise, some index values map to "impossible" bus, unit
* values.
*
* For instance, if you change [IF_SCSI] to 255, -drive
* if=scsi,index=12 no longer means bus=1,unit=5, but
* bus=0,unit=12. With an lsi53c895a controller (7 units max),
* the drive can't be set up. Regression.
*/
[IF_IDE] = 2,
[IF_SCSI] = 7,
};
/*
* We automatically delete the drive when a device using it gets
* unplugged. Questionable feature, but we can't just drop it.
@@ -61,54 +28,32 @@ void blockdev_mark_auto_del(BlockDriverState *bs)
{
DriveInfo *dinfo = drive_get_by_blockdev(bs);
if (dinfo) {
dinfo->auto_del = 1;
}
dinfo->auto_del = 1;
}
void blockdev_auto_del(BlockDriverState *bs)
{
DriveInfo *dinfo = drive_get_by_blockdev(bs);
if (dinfo && dinfo->auto_del) {
drive_put_ref(dinfo);
if (dinfo->auto_del) {
drive_uninit(dinfo);
}
}
static int drive_index_to_bus_id(BlockInterfaceType type, int index)
{
int max_devs = if_max_devs[type];
return max_devs ? index / max_devs : 0;
}
static int drive_index_to_unit_id(BlockInterfaceType type, int index)
{
int max_devs = if_max_devs[type];
return max_devs ? index % max_devs : index;
}
QemuOpts *drive_def(const char *optstr)
{
return qemu_opts_parse(qemu_find_opts("drive"), optstr, 0);
}
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
const char *optstr)
QemuOpts *drive_add(const char *file, const char *fmt, ...)
{
va_list ap;
char optstr[1024];
QemuOpts *opts;
char buf[32];
opts = drive_def(optstr);
va_start(ap, fmt);
vsnprintf(optstr, sizeof(optstr), fmt, ap);
va_end(ap);
opts = qemu_opts_parse(&qemu_drive_opts, optstr, 0);
if (!opts) {
return NULL;
}
if (type != IF_DEFAULT) {
qemu_opt_set(opts, "if", if_name[type]);
}
if (index >= 0) {
snprintf(buf, sizeof(buf), "%d", index);
qemu_opt_set(opts, "index", buf);
}
if (file)
qemu_opt_set(opts, "file", file);
return opts;
@@ -130,13 +75,6 @@ DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit)
return NULL;
}
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index)
{
return drive_get(type,
drive_index_to_bus_id(type, index),
drive_index_to_unit_id(type, index));
}
int drive_get_max_bus(BlockInterfaceType type)
{
int max_bus;
@@ -151,16 +89,6 @@ int drive_get_max_bus(BlockInterfaceType type)
return max_bus;
}
/* Get a block device. This should only be used for single-drive devices
(e.g. SD/Floppy/MTD). Multi-disk devices (scsi/ide) should use the
appropriate bus. */
DriveInfo *drive_get_next(BlockInterfaceType type)
{
static int next_block_unit[IF_COUNT];
return drive_get(type, 0, next_block_unit[type]++);
}
DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
{
DriveInfo *dinfo;
@@ -175,31 +103,17 @@ DriveInfo *drive_get_by_blockdev(BlockDriverState *bs)
static void bdrv_format_print(void *opaque, const char *name)
{
error_printf(" %s", name);
fprintf(stderr, " %s", name);
}
static void drive_uninit(DriveInfo *dinfo)
void drive_uninit(DriveInfo *dinfo)
{
qemu_opts_del(dinfo->opts);
bdrv_delete(dinfo->bdrv);
qemu_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next);
qemu_free(dinfo);
}
void drive_put_ref(DriveInfo *dinfo)
{
assert(dinfo->refcount);
if (--dinfo->refcount == 0) {
drive_uninit(dinfo);
}
}
void drive_get_ref(DriveInfo *dinfo)
{
dinfo->refcount++;
}
static int parse_block_error_action(const char *buf, int is_read)
{
if (!strcmp(buf, "ignore")) {
@@ -211,13 +125,13 @@ static int parse_block_error_action(const char *buf, int is_read)
} else if (!strcmp(buf, "report")) {
return BLOCK_ERR_REPORT;
} else {
error_report("'%s' invalid %s error action",
buf, is_read ? "read" : "write");
fprintf(stderr, "qemu: '%s' invalid %s error action\n",
buf, is_read ? "read" : "write");
return -1;
}
}
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi, int *fatal_error)
{
const char *buf;
const char *file = NULL;
@@ -239,7 +153,19 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
int snapshot = 0;
int ret;
*fatal_error = 1;
translation = BIOS_ATA_TRANSLATION_AUTO;
if (default_to_scsi) {
type = IF_SCSI;
max_devs = MAX_SCSI_DEVS;
pstrcpy(devname, sizeof(devname), "scsi");
} else {
type = IF_IDE;
max_devs = MAX_IDE_DEVS;
pstrcpy(devname, sizeof(devname), "ide");
}
media = MEDIA_DISK;
/* extract parameters */
@@ -259,38 +185,59 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
if ((buf = qemu_opt_get(opts, "if")) != NULL) {
pstrcpy(devname, sizeof(devname), buf);
for (type = 0; type < IF_COUNT && strcmp(buf, if_name[type]); type++)
;
if (type == IF_COUNT) {
error_report("unsupported bus type '%s'", buf);
if (!strcmp(buf, "ide")) {
type = IF_IDE;
max_devs = MAX_IDE_DEVS;
} else if (!strcmp(buf, "scsi")) {
type = IF_SCSI;
max_devs = MAX_SCSI_DEVS;
} else if (!strcmp(buf, "floppy")) {
type = IF_FLOPPY;
max_devs = 0;
} else if (!strcmp(buf, "pflash")) {
type = IF_PFLASH;
max_devs = 0;
} else if (!strcmp(buf, "mtd")) {
type = IF_MTD;
max_devs = 0;
} else if (!strcmp(buf, "sd")) {
type = IF_SD;
max_devs = 0;
} else if (!strcmp(buf, "virtio")) {
type = IF_VIRTIO;
max_devs = 0;
} else if (!strcmp(buf, "xen")) {
type = IF_XEN;
max_devs = 0;
} else if (!strcmp(buf, "none")) {
type = IF_NONE;
max_devs = 0;
} else {
fprintf(stderr, "qemu: unsupported bus type '%s'\n", buf);
return NULL;
}
} else {
type = default_to_scsi ? IF_SCSI : IF_IDE;
pstrcpy(devname, sizeof(devname), if_name[type]);
}
max_devs = if_max_devs[type];
if (cyls || heads || secs) {
if (cyls < 1 || (type == IF_IDE && cyls > 16383)) {
error_report("invalid physical cyls number");
fprintf(stderr, "qemu: '%s' invalid physical cyls number\n", buf);
return NULL;
}
if (heads < 1 || (type == IF_IDE && heads > 16)) {
error_report("invalid physical heads number");
fprintf(stderr, "qemu: '%s' invalid physical heads number\n", buf);
return NULL;
}
if (secs < 1 || (type == IF_IDE && secs > 63)) {
error_report("invalid physical secs number");
fprintf(stderr, "qemu: '%s' invalid physical secs number\n", buf);
return NULL;
}
}
if ((buf = qemu_opt_get(opts, "trans")) != NULL) {
if (!cyls) {
error_report("'%s' trans must be used with cyls, heads and secs",
buf);
fprintf(stderr,
"qemu: '%s' trans must be used with cyls,heads and secs\n",
buf);
return NULL;
}
if (!strcmp(buf, "none"))
@@ -300,7 +247,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
else if (!strcmp(buf, "auto"))
translation = BIOS_ATA_TRANSLATION_AUTO;
else {
error_report("'%s' invalid translation type", buf);
fprintf(stderr, "qemu: '%s' invalid translation type\n", buf);
return NULL;
}
}
@@ -310,19 +257,20 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
media = MEDIA_DISK;
} else if (!strcmp(buf, "cdrom")) {
if (cyls || secs || heads) {
error_report("CHS can't be set with media=%s", buf);
fprintf(stderr,
"qemu: '%s' invalid physical CHS format\n", buf);
return NULL;
}
media = MEDIA_CDROM;
} else {
error_report("'%s' invalid media", buf);
fprintf(stderr, "qemu: '%s' invalid media\n", buf);
return NULL;
}
}
if ((buf = qemu_opt_get(opts, "cache")) != NULL) {
if (!strcmp(buf, "off") || !strcmp(buf, "none")) {
bdrv_flags |= BDRV_O_NOCACHE | BDRV_O_CACHE_WB;
bdrv_flags |= BDRV_O_NOCACHE;
} else if (!strcmp(buf, "writeback")) {
bdrv_flags |= BDRV_O_CACHE_WB;
} else if (!strcmp(buf, "unsafe")) {
@@ -331,7 +279,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
} else if (!strcmp(buf, "writethrough")) {
/* this is the default */
} else {
error_report("invalid cache option");
fprintf(stderr, "qemu: invalid cache option\n");
return NULL;
}
}
@@ -343,7 +291,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
} else if (!strcmp(buf, "threads")) {
/* this is the default */
} else {
error_report("invalid aio option");
fprintf(stderr, "qemu: invalid aio option\n");
return NULL;
}
}
@@ -351,14 +299,14 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
if ((buf = qemu_opt_get(opts, "format")) != NULL) {
if (strcmp(buf, "?") == 0) {
error_printf("Supported formats:");
bdrv_iterate_format(bdrv_format_print, NULL);
error_printf("\n");
return NULL;
fprintf(stderr, "qemu: Supported formats:");
bdrv_iterate_format(bdrv_format_print, NULL);
fprintf(stderr, "\n");
return NULL;
}
drv = bdrv_find_whitelisted_format(buf);
if (!drv) {
error_report("'%s' invalid format", buf);
fprintf(stderr, "qemu: '%s' invalid format\n", buf);
return NULL;
}
}
@@ -366,7 +314,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
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) {
error_report("werror is not supported by this bus type");
fprintf(stderr, "werror is no supported by this format\n");
return NULL;
}
@@ -378,8 +326,8 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
on_read_error = BLOCK_ERR_REPORT;
if ((buf = qemu_opt_get(opts, "rerror")) != NULL) {
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) {
error_report("rerror is not supported by this bus type");
if (type != IF_IDE && type != IF_VIRTIO && type != IF_NONE) {
fprintf(stderr, "rerror is no supported by this format\n");
return NULL;
}
@@ -391,7 +339,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
if ((devaddr = qemu_opt_get(opts, "addr")) != NULL) {
if (type != IF_VIRTIO) {
error_report("addr is not supported by this bus type");
fprintf(stderr, "addr is not supported\n");
return NULL;
}
}
@@ -400,11 +348,18 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
if (index != -1) {
if (bus_id != 0 || unit_id != -1) {
error_report("index cannot be used with bus and unit");
fprintf(stderr,
"qemu: index cannot be used with bus and unit\n");
return NULL;
}
bus_id = drive_index_to_bus_id(type, index);
unit_id = drive_index_to_unit_id(type, index);
if (max_devs == 0)
{
unit_id = index;
bus_id = 0;
} else {
unit_id = index % max_devs;
bus_id = index / max_devs;
}
}
/* if user doesn't specify a unit_id,
@@ -425,18 +380,17 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
/* check unit id */
if (max_devs && unit_id >= max_devs) {
error_report("unit %d too big (max is %d)",
unit_id, max_devs - 1);
fprintf(stderr, "qemu: unit %d too big (max is %d)\n",
unit_id, max_devs - 1);
return NULL;
}
/*
* catch multiple definitions
* ignore multiple definitions
*/
if (drive_get(type, bus_id, unit_id) != NULL) {
error_report("drive with bus=%d, unit=%d (index=%d) exists",
bus_id, unit_id, index);
*fatal_error = 0;
return NULL;
}
@@ -463,7 +417,6 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
dinfo->bus = bus_id;
dinfo->unit = unit_id;
dinfo->opts = opts;
dinfo->refcount = 1;
if (serial)
strncpy(dinfo->serial, serial, sizeof(dinfo->serial) - 1);
QTAILQ_INSERT_TAIL(&drives, dinfo, next);
@@ -483,8 +436,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
}
break;
case MEDIA_CDROM:
bdrv_set_removable(dinfo->bdrv, 1);
dinfo->media_cd = 1;
bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_CDROM);
break;
}
break;
@@ -492,24 +444,25 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
/* FIXME: This isn't really a floppy, but it's a reasonable
approximation. */
case IF_FLOPPY:
bdrv_set_removable(dinfo->bdrv, 1);
bdrv_set_type_hint(dinfo->bdrv, BDRV_TYPE_FLOPPY);
break;
case IF_PFLASH:
case IF_MTD:
break;
case IF_VIRTIO:
/* add virtio block device */
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0);
qemu_opt_set(opts, "driver", "virtio-blk");
opts = qemu_opts_create(&qemu_device_opts, NULL, 0);
qemu_opt_set(opts, "driver", "virtio-blk-pci");
qemu_opt_set(opts, "drive", dinfo->id);
if (devaddr)
qemu_opt_set(opts, "addr", devaddr);
break;
default:
case IF_COUNT:
abort();
}
if (!file || !*file) {
return dinfo;
*fatal_error = 0;
return NULL;
}
if (snapshot) {
/* always use cache=unsafe with snapshot */
@@ -522,8 +475,8 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
ro = 1;
} else if (ro == 1) {
if (type != IF_SCSI && type != IF_VIRTIO && type != IF_FLOPPY && type != IF_NONE) {
error_report("readonly not supported by this bus type");
goto err;
fprintf(stderr, "qemu: readonly flag not supported for drive with this interface\n");
return NULL;
}
}
@@ -531,21 +484,15 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
ret = bdrv_open(dinfo->bdrv, file, bdrv_flags, drv);
if (ret < 0) {
error_report("could not open disk image %s: %s",
file, strerror(-ret));
goto err;
fprintf(stderr, "qemu: could not open disk image %s: %s\n",
file, strerror(-ret));
return NULL;
}
if (bdrv_key_required(dinfo->bdrv))
autostart = 0;
*fatal_error = 0;
return dinfo;
err:
bdrv_delete(dinfo->bdrv);
qemu_free(dinfo->id);
QTAILQ_REMOVE(&drives, dinfo, next);
qemu_free(dinfo);
return NULL;
}
void do_commit(Monitor *mon, const QDict *qdict)
@@ -565,85 +512,6 @@ void do_commit(Monitor *mon, const QDict *qdict)
}
}
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) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
ret = -1;
goto out;
}
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;
}
static int eject_device(Monitor *mon, BlockDriverState *bs, int force)
{
if (!force) {
@@ -729,67 +597,3 @@ int do_change_block(Monitor *mon, const char *device,
}
return monitor_read_bdrv_key_start(mon, bs, NULL, NULL);
}
int do_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *id = qdict_get_str(qdict, "id");
BlockDriverState *bs;
bs = bdrv_find(id);
if (!bs) {
qerror_report(QERR_DEVICE_NOT_FOUND, id);
return -1;
}
if (bdrv_in_use(bs)) {
qerror_report(QERR_DEVICE_IN_USE, id);
return -1;
}
/* quiesce block driver; prevent further io */
qemu_aio_flush();
bdrv_flush(bs);
bdrv_close(bs);
/* if we have a device associated with this BlockDriverState (bs->peer)
* then we need to make the drive anonymous until the device
* can be removed. If this is a drive with no device backing
* then we can just get rid of the block driver state right here.
*/
if (bs->peer) {
bdrv_make_anon(bs);
} else {
drive_uninit(drive_get_by_blockdev(bs));
}
return 0;
}
/*
* 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) {
qerror_report(QERR_DEVICE_NOT_FOUND, device);
return -1;
}
if (size < 0) {
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
}
if (bdrv_truncate(bs, size)) {
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
}
return 0;
}

View File

@@ -16,16 +16,15 @@
void blockdev_mark_auto_del(BlockDriverState *bs);
void blockdev_auto_del(BlockDriverState *bs);
#define BLOCK_SERIAL_STRLEN 20
typedef enum {
IF_DEFAULT = -1, /* for use with drive_add() only */
IF_NONE,
IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
IF_COUNT
} BlockInterfaceType;
struct DriveInfo {
#define BLOCK_SERIAL_STRLEN 20
typedef struct DriveInfo {
BlockDriverState *bdrv;
char *id;
const char *devaddr;
@@ -33,25 +32,22 @@ struct DriveInfo {
int bus;
int unit;
int auto_del; /* see blockdev_mark_auto_del() */
int media_cd;
QemuOpts *opts;
char serial[BLOCK_SERIAL_STRLEN + 1];
QTAILQ_ENTRY(DriveInfo) next;
int refcount;
};
} DriveInfo;
DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
DriveInfo *drive_get_by_index(BlockInterfaceType type, int index);
int drive_get_max_bus(BlockInterfaceType type);
DriveInfo *drive_get_next(BlockInterfaceType type);
void drive_get_ref(DriveInfo *dinfo);
void drive_put_ref(DriveInfo *dinfo);
DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
#define MAX_IDE_DEVS 2
#define MAX_SCSI_DEVS 7
QemuOpts *drive_def(const char *optstr);
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
const char *optstr);
DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi);
extern DriveInfo *drive_get(BlockInterfaceType type, int bus, int unit);
extern int drive_get_max_bus(BlockInterfaceType type);
extern void drive_uninit(DriveInfo *dinfo);
extern DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
extern QemuOpts *drive_add(const char *file, const char *fmt, ...);
extern DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi,
int *fatal_error);
/* device-hotplug */
@@ -62,8 +58,5 @@ 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

@@ -176,6 +176,8 @@ int loader_exec(const char * filename, char ** argv, char ** envp,
retval = prepare_binprm(&bprm);
infop->host_argv = argv;
if(retval>=0) {
if (bprm.buf[0] == 0x7f
&& bprm.buf[1] == 'E'

View File

@@ -1044,7 +1044,7 @@ static void load_symbols(struct elfhdr *hdr, int fd)
struct elf_shdr sechdr, symtab, strtab;
char *strings;
struct syminfo *s;
struct elf_sym *syms, *new_syms;
struct elf_sym *syms;
lseek(fd, hdr->e_shoff, SEEK_SET);
for (i = 0; i < hdr->e_shnum; i++) {
@@ -1072,24 +1072,15 @@ static void load_symbols(struct elfhdr *hdr, int fd)
/* Now know where the strtab and symtab are. Snarf them. */
s = malloc(sizeof(*s));
syms = malloc(symtab.sh_size);
if (!syms) {
free(s);
if (!syms)
return;
}
s->disas_strtab = strings = malloc(strtab.sh_size);
if (!s->disas_strtab) {
free(s);
free(syms);
if (!s->disas_strtab)
return;
}
lseek(fd, symtab.sh_offset, SEEK_SET);
if (read(fd, syms, symtab.sh_size) != symtab.sh_size) {
free(s);
free(syms);
free(strings);
if (read(fd, syms, symtab.sh_size) != symtab.sh_size)
return;
}
nsyms = symtab.sh_size / sizeof(struct elf_sym);
@@ -1114,29 +1105,13 @@ static void load_symbols(struct elfhdr *hdr, int fd)
#endif
i++;
}
/* Attempt to free the storage associated with the local symbols
that we threw away. Whether or not this has any effect on the
memory allocation depends on the malloc implementation and how
many symbols we managed to discard. */
new_syms = realloc(syms, nsyms * sizeof(*syms));
if (new_syms == NULL) {
free(s);
free(syms);
free(strings);
return;
}
syms = new_syms;
syms = realloc(syms, nsyms * sizeof(*syms));
qsort(syms, nsyms, sizeof(*syms), symcmp);
lseek(fd, strtab.sh_offset, SEEK_SET);
if (read(fd, strings, strtab.sh_size) != strtab.sh_size) {
free(s);
free(syms);
free(strings);
if (read(fd, strings, strtab.sh_size) != strtab.sh_size)
return;
}
s->disas_num_syms = nsyms;
#if ELF_CLASS == ELFCLASS32
s->disas_symtab.elf32 = syms;

View File

@@ -29,7 +29,7 @@
#include "qemu.h"
#include "qemu-common.h"
/* For tb_lock */
#include "cpu.h"
#include "exec-all.h"
#include "tcg.h"
#include "qemu-timer.h"
#include "envlist.h"
@@ -237,7 +237,7 @@ void cpu_loop(CPUX86State *env)
break;
#ifndef TARGET_ABI32
case EXCP_SYSCALL:
/* syscall from syscall instruction */
/* syscall from syscall intruction */
if (bsd_type == target_freebsd)
env->regs[R_EAX] = do_freebsd_syscall(env,
env->regs[R_EAX],
@@ -690,8 +690,7 @@ static void usage(void)
"-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
"\n"
"Debug options:\n"
"-d options activate log (default logfile=%s)\n"
"-D logfile override default logfile location\n"
"-d options activate log (logfile=%s)\n"
"-p pagesize set the host page size to 'pagesize'\n"
"-singlestep always run in singlestep mode\n"
"-strace log system calls\n"
@@ -732,8 +731,6 @@ int main(int argc, char **argv)
{
const char *filename;
const char *cpu_model;
const char *log_file = DEBUG_LOGFILE;
const char *log_mask = NULL;
struct target_pt_regs regs1, *regs = &regs1;
struct image_info info1, *info = &info1;
TaskState ts1, *ts = &ts1;
@@ -748,6 +745,9 @@ int main(int argc, char **argv)
if (argc <= 1)
usage();
/* init debug */
cpu_set_log_filename(DEBUG_LOGFILE);
if ((envlist = envlist_create()) == NULL) {
(void) fprintf(stderr, "Unable to allocate envlist\n");
exit(1);
@@ -775,25 +775,26 @@ int main(int argc, char **argv)
if (!strcmp(r, "-")) {
break;
} else if (!strcmp(r, "d")) {
if (optind >= argc) {
int mask;
const CPULogItem *item;
if (optind >= argc)
break;
r = argv[optind++];
mask = cpu_str_to_log_mask(r);
if (!mask) {
printf("Log items (comma separated):\n");
for(item = cpu_log_items; item->mask != 0; item++) {
printf("%-10s %s\n", item->name, item->help);
}
exit(1);
}
log_mask = argv[optind++];
} else if (!strcmp(r, "D")) {
if (optind >= argc) {
break;
}
log_file = argv[optind++];
cpu_set_log(mask);
} else if (!strcmp(r, "E")) {
r = argv[optind++];
if (envlist_setenv(envlist, r) != 0)
usage();
} else if (!strcmp(r, "ignore-environment")) {
envlist_free(envlist);
if ((envlist = envlist_create()) == NULL) {
(void) fprintf(stderr, "Unable to allocate envlist\n");
exit(1);
}
} else if (!strcmp(r, "U")) {
r = argv[optind++];
if (envlist_unsetenv(envlist, r) != 0)
@@ -856,27 +857,8 @@ int main(int argc, char **argv)
usage();
}
}
/* init debug */
cpu_set_log_filename(log_file);
if (log_mask) {
int mask;
const CPULogItem *item;
mask = cpu_str_to_log_mask(log_mask);
if (!mask) {
printf("Log items (comma separated):\n");
for (item = cpu_log_items; item->mask != 0; item++) {
printf("%-10s %s\n", item->name, item->help);
}
exit(1);
}
cpu_set_log(mask);
}
if (optind >= argc) {
if (optind >= argc)
usage();
}
filename = argv[optind];
/* Zero out regs */

View File

@@ -50,6 +50,7 @@ struct image_info {
abi_ulong entry;
abi_ulong code_offset;
abi_ulong data_offset;
char **host_argv;
int personality;
};
@@ -138,7 +139,7 @@ abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
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);
void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
extern THREAD CPUState *thread_env;
void cpu_loop(CPUState *env);
char *target_strerror(int err);
@@ -323,7 +324,7 @@ abi_long copy_from_user(void *hptr, abi_ulong gaddr, size_t len);
abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len);
/* Functions for accessing guest memory. The tget and tput functions
read/write single values, byteswapping as necessary. The lock_user
read/write single values, byteswapping as neccessary. The lock_user
gets a pointer to a contiguous area of guest memory, but does not perform
and byteswapping. lock_user may return either a pointer to the guest
memory, or a temporary buffer. */

View File

@@ -31,6 +31,7 @@
#include <sys/syscall.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <signal.h>
#include <utime.h>
#include "qemu.h"

15
bswap.h
View File

@@ -144,7 +144,6 @@ CPU_CONVERT(le, 64, uint64_t)
#define cpu_to_be16wu(p, v) cpu_to_be16w(p, v)
#define cpu_to_be32wu(p, v) cpu_to_be32w(p, v)
#define cpu_to_be64wu(p, v) cpu_to_be64w(p, v)
#else
@@ -202,20 +201,6 @@ static inline void cpu_to_be32wu(uint32_t *p, uint32_t v)
p1[3] = v & 0xff;
}
static inline void cpu_to_be64wu(uint64_t *p, uint64_t v)
{
uint8_t *p1 = (uint8_t *)p;
p1[0] = v >> 56;
p1[1] = v >> 48;
p1[2] = v >> 40;
p1[3] = v >> 32;
p1[4] = v >> 24;
p1[5] = v >> 16;
p1[6] = v >> 8;
p1[7] = v & 0xff;
}
#endif
#ifdef HOST_WORDS_BIGENDIAN

View File

@@ -19,6 +19,7 @@
#include "qemu-common.h"
#include "qemu-char.h"
#include "sysemu.h"
#include "net.h"
#include "bt-host.h"

View File

@@ -19,6 +19,7 @@
#include "qemu-common.h"
#include "qemu-char.h"
#include "sysemu.h"
#include "net.h"
#include "hw/bt.h"

View File

@@ -14,6 +14,7 @@
#include "qemu-common.h"
#include "hw/hw.h"
#include "qemu-timer.h"
#include "sysemu.h"
#include "qemu-char.h"
#include "buffered_file.h"
@@ -155,14 +156,6 @@ static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, in
offset = size;
}
if (pos == 0 && size == 0) {
DPRINTF("file is ready\n");
if (s->bytes_xfer <= s->xfer_limit) {
DPRINTF("notifying client\n");
s->put_ready(s->opaque);
}
}
return offset;
}
@@ -205,23 +198,20 @@ static int buffered_rate_limit(void *opaque)
return 0;
}
static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate)
static size_t buffered_set_rate_limit(void *opaque, size_t new_rate)
{
QEMUFileBuffered *s = opaque;
if (s->has_error)
goto out;
if (new_rate > SIZE_MAX) {
new_rate = SIZE_MAX;
}
s->xfer_limit = new_rate / 10;
out:
return s->xfer_limit;
}
static int64_t buffered_get_rate_limit(void *opaque)
static size_t buffered_get_rate_limit(void *opaque)
{
QEMUFileBuffered *s = opaque;
@@ -232,12 +222,10 @@ static void buffered_rate_tick(void *opaque)
{
QEMUFileBuffered *s = opaque;
if (s->has_error) {
buffered_close(s);
if (s->has_error)
return;
}
qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100);
qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
if (s->freeze_output)
return;
@@ -273,9 +261,9 @@ QEMUFile *qemu_fopen_ops_buffered(void *opaque,
buffered_set_rate_limit,
buffered_get_rate_limit);
s->timer = qemu_new_timer_ms(rt_clock, buffered_rate_tick, s);
s->timer = qemu_new_timer(rt_clock, buffered_rate_tick, s);
qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 100);
qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 100);
return s->file;
}

View File

@@ -79,7 +79,7 @@ static void ppc_init_cacheline_sizes(void)
qemu_cache_conf.dcache_bsize = cacheline;
qemu_cache_conf.icache_bsize = cacheline;
}
#endif
#endif
#ifdef __linux__
void qemu_cache_utils_init(char **envp)

View File

@@ -9,7 +9,7 @@ struct qemu_cache_conf {
extern struct qemu_cache_conf qemu_cache_conf;
void qemu_cache_utils_init(char **envp);
extern void qemu_cache_utils_init(char **envp);
/* mildly adjusted code from tcg-dyngen.c */
static inline void flush_icache_range(unsigned long start, unsigned long stop)

View File

@@ -267,9 +267,8 @@ static QString *read_line(FILE *file, char *key)
{
char value[128];
if (fscanf(file, "%127s%127s", key, value) == EOF) {
if (fscanf(file, "%s%s", key, value) == EOF)
return NULL;
}
remove_dots(key);
return qstring_from_str(value);
}

View File

@@ -33,8 +33,7 @@ START_TEST(escaped_string)
{ "\"\\n\"", "\n" },
{ "\"\\r\"", "\r" },
{ "\"\\t\"", "\t" },
{ "\"/\"", "/" },
{ "\"\\/\"", "/", .skip = 1 },
{ "\"\\/\"", "\\/" },
{ "\"\\\\\"", "\\" },
{ "\"\\\"\"", "\"" },
{ "\"hello world \\\"embedded string\\\"\"",
@@ -640,9 +639,7 @@ END_TEST
START_TEST(empty_input)
{
const char *empty = "";
QObject *obj = qobject_from_json(empty);
QObject *obj = qobject_from_json("");
fail_unless(obj == NULL);
}
END_TEST

6
cmd.c
View File

@@ -486,7 +486,7 @@ timestr(
snprintf(ts, size, "%u:%02u.%02u",
(unsigned int) MINUTES(tv->tv_sec),
(unsigned int) SECONDS(tv->tv_sec),
(unsigned int) (usec * 100));
(unsigned int) usec * 100);
return;
}
format |= VERBOSE_FIXED_TIME; /* fallback if hours needed */
@@ -497,9 +497,9 @@ timestr(
(unsigned int) HOURS(tv->tv_sec),
(unsigned int) MINUTES(tv->tv_sec),
(unsigned int) SECONDS(tv->tv_sec),
(unsigned int) (usec * 100));
(unsigned int) usec * 100);
} else {
snprintf(ts, size, "0.%04u sec", (unsigned int) (usec * 10000));
snprintf(ts, size, "0.%04u sec", (unsigned int) usec * 10000);
}
}

36
cmd.h
View File

@@ -38,33 +38,33 @@ typedef struct cmdinfo {
extern cmdinfo_t *cmdtab;
extern int ncmds;
void help_init(void);
void quit_init(void);
extern void help_init(void);
extern void quit_init(void);
typedef int (*argsfunc_t)(int index);
typedef int (*checkfunc_t)(const cmdinfo_t *ci);
void add_command(const cmdinfo_t *ci);
void add_user_command(char *optarg);
void add_args_command(argsfunc_t af);
void add_check_command(checkfunc_t cf);
extern void add_command(const cmdinfo_t *ci);
extern void add_user_command(char *optarg);
extern void add_args_command(argsfunc_t af);
extern void add_check_command(checkfunc_t cf);
const cmdinfo_t *find_command(const char *cmd);
extern const cmdinfo_t *find_command(const char *cmd);
void command_loop(void);
int command_usage(const cmdinfo_t *ci);
int command(const cmdinfo_t *ci, int argc, char **argv);
extern void command_loop(void);
extern int command_usage(const cmdinfo_t *ci);
extern int command(const cmdinfo_t *ci, int argc, char **argv);
/* from input.h */
char **breakline(char *input, int *count);
void doneline(char *input, char **vec);
char *fetchline(void);
extern char **breakline(char *input, int *count);
extern void doneline(char *input, char **vec);
extern char *fetchline(void);
long long cvtnum(char *s);
void cvtstr(double value, char *str, size_t sz);
extern long long cvtnum(char *s);
extern void cvtstr(double value, char *str, size_t sz);
struct timeval tsub(struct timeval t1, struct timeval t2);
double tdiv(double value, struct timeval tv);
extern struct timeval tsub(struct timeval t1, struct timeval t2);
extern double tdiv(double value, struct timeval tv);
enum {
DEFAULT_TIME = 0x0,
@@ -72,7 +72,7 @@ enum {
VERBOSE_FIXED_TIME = 0x2
};
void timestr(struct timeval *tv, char *str, size_t sz, int flags);
extern void timestr(struct timeval *tv, char *str, size_t sz, int flags);
extern char *progname;

View File

@@ -1,128 +0,0 @@
/*
* signalfd/eventfd compatibility
*
* Copyright IBM, Corp. 2008
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#include "qemu-common.h"
#include "compatfd.h"
#include <sys/syscall.h>
#include <pthread.h>
struct sigfd_compat_info
{
sigset_t mask;
int fd;
};
static void *sigwait_compat(void *opaque)
{
struct sigfd_compat_info *info = opaque;
sigset_t all;
sigfillset(&all);
pthread_sigmask(SIG_BLOCK, &all, NULL);
while (1) {
int sig;
int err;
err = sigwait(&info->mask, &sig);
if (err != 0) {
if (errno == EINTR) {
continue;
} else {
return NULL;
}
} else {
struct qemu_signalfd_siginfo buffer;
size_t offset = 0;
memset(&buffer, 0, sizeof(buffer));
buffer.ssi_signo = sig;
while (offset < sizeof(buffer)) {
ssize_t len;
len = write(info->fd, (char *)&buffer + offset,
sizeof(buffer) - offset);
if (len == -1 && errno == EINTR)
continue;
if (len <= 0) {
return NULL;
}
offset += len;
}
}
}
}
static int qemu_signalfd_compat(const sigset_t *mask)
{
pthread_attr_t attr;
pthread_t tid;
struct sigfd_compat_info *info;
int fds[2];
info = malloc(sizeof(*info));
if (info == NULL) {
errno = ENOMEM;
return -1;
}
if (pipe(fds) == -1) {
free(info);
return -1;
}
qemu_set_cloexec(fds[0]);
qemu_set_cloexec(fds[1]);
memcpy(&info->mask, mask, sizeof(*mask));
info->fd = fds[1];
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, sigwait_compat, info);
pthread_attr_destroy(&attr);
return fds[0];
}
int qemu_signalfd(const sigset_t *mask)
{
#if defined(CONFIG_SIGNALFD)
int ret;
ret = syscall(SYS_signalfd, -1, mask, _NSIG / 8);
if (ret != -1) {
qemu_set_cloexec(ret);
return ret;
}
#endif
return qemu_signalfd_compat(mask);
}
bool qemu_signalfd_available(void)
{
#ifdef CONFIG_SIGNALFD
errno = 0;
syscall(SYS_signalfd, -1, NULL, _NSIG / 8);
return errno != ENOSYS;
#else
return false;
#endif
}

View File

@@ -1,44 +0,0 @@
/*
* signalfd/eventfd compatibility
*
* Copyright IBM, Corp. 2008
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#ifndef QEMU_COMPATFD_H
#define QEMU_COMPATFD_H
#include <signal.h>
struct qemu_signalfd_siginfo {
uint32_t ssi_signo; /* Signal number */
int32_t ssi_errno; /* Error number (unused) */
int32_t ssi_code; /* Signal code */
uint32_t ssi_pid; /* PID of sender */
uint32_t ssi_uid; /* Real UID of sender */
int32_t ssi_fd; /* File descriptor (SIGIO) */
uint32_t ssi_tid; /* Kernel timer ID (POSIX timers) */
uint32_t ssi_band; /* Band event (SIGIO) */
uint32_t ssi_overrun; /* POSIX timer overrun count */
uint32_t ssi_trapno; /* Trap number that caused signal */
int32_t ssi_status; /* Exit status or signal (SIGCHLD) */
int32_t ssi_int; /* Integer sent by sigqueue(2) */
uint64_t ssi_ptr; /* Pointer sent by sigqueue(2) */
uint64_t ssi_utime; /* User CPU time consumed (SIGCHLD) */
uint64_t ssi_stime; /* System CPU time consumed (SIGCHLD) */
uint64_t ssi_addr; /* Address that generated signal
(for hardware-generated signals) */
uint8_t pad[48]; /* Pad size to 128 bytes (allow for
additional fields in the future) */
};
int qemu_signalfd(const sigset_t *mask);
bool qemu_signalfd_available(void);
#endif

View File

@@ -1,34 +0,0 @@
/* public domain */
#ifndef COMPILER_H
#define COMPILER_H
#include "config-host.h"
#define QEMU_NORETURN __attribute__ ((__noreturn__))
#ifdef CONFIG_GCC_ATTRIBUTE_WARN_UNUSED_RESULT
#define QEMU_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
#else
#define QEMU_WARN_UNUSED_RESULT
#endif
#define QEMU_BUILD_BUG_ON(x) \
typedef char qemu_build_bug_on__##__LINE__[(x)?-1:1];
#if defined __GNUC__
# if (__GNUC__ < 4) || \
defined(__GNUC_MINOR__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 4)
/* gcc versions before 4.4.x don't support gnu_printf, so use printf. */
# define GCC_ATTR __attribute__((__unused__, format(printf, 1, 2)))
# define GCC_FMT_ATTR(n, m) __attribute__((format(printf, n, m)))
# else
/* Use gnu_printf when supported (qemu uses standard format strings). */
# define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2)))
# define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m)))
# endif
#else
#define GCC_ATTR /**/
#define GCC_FMT_ATTR(n, m)
#endif
#endif /* COMPILER_H */

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