Compare commits

..
74 Commits
Author SHA1 Message Date
Michael Roth 99c5874a9b Update version for 4.1.1 release
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-14 12:04:03 -06:00
Kevin Wolf e092a17d38 mirror: Keep mirror_top_bs drained after dropping permissions
mirror_top_bs is currently implicitly drained through its connection to
the source or the target node. However, the drain section for target_bs
ends early after moving mirror_top_bs from src to target_bs, so that
requests can already be restarted while mirror_top_bs is still present
in the chain, but has dropped all permissions and therefore runs into an
assertion failure like this:

    qemu-system-x86_64: block/io.c:1634: bdrv_co_write_req_prepare:
    Assertion `child->perm & BLK_PERM_WRITE' failed.

Keep mirror_top_bs drained until all graph changes have completed.

Cc: qemu-stable@nongnu.org
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit d2da5e288a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 16:31:49 -06:00
Philippe Mathieu-Daudé 088f1e8fd9 block/create: Do not abort if a block driver is not available
The 'blockdev-create' QMP command was introduced as experimental
feature in commit b0292b851b, using the assert() debug call.
It got promoted to 'stable' command in 3fb588a0f2, but the
assert call was not removed.

Some block drivers are optional, and bdrv_find_format() might
return a NULL value, triggering the assertion.

Stable code is not expected to abort, so return an error instead.

This is easily reproducible when libnfs is not installed:

  ./configure
  [...]
  module support    no
  Block whitelist (rw)
  Block whitelist (ro)
  libiscsi support  yes
  libnfs support    no
  [...]

Start QEMU:

  $ qemu-system-x86_64 -S -qmp unix:/tmp/qemu.qmp,server,nowait

Send the 'blockdev-create' with the 'nfs' driver:

  $ ( cat << 'EOF'
  {'execute': 'qmp_capabilities'}
  {'execute': 'blockdev-create', 'arguments': {'job-id': 'x', 'options': {'size': 0, 'driver': 'nfs', 'location': {'path': '/', 'server': {'host': '::1', 'type': 'inet'}}}}, 'id': 'x'}
  EOF
  ) | socat STDIO UNIX:/tmp/qemu.qmp
  {"QMP": {"version": {"qemu": {"micro": 50, "minor": 1, "major": 4}, "package": "v4.1.0-733-g89ea03a7dc"}, "capabilities": ["oob"]}}
  {"return": {}}

QEMU crashes:

  $ gdb qemu-system-x86_64 core
  Program received signal SIGSEGV, Segmentation fault.
  (gdb) bt
  #0  0x00007ffff510957f in raise () at /lib64/libc.so.6
  #1  0x00007ffff50f3895 in abort () at /lib64/libc.so.6
  #2  0x00007ffff50f3769 in _nl_load_domain.cold.0 () at /lib64/libc.so.6
  #3  0x00007ffff5101a26 in .annobin_assert.c_end () at /lib64/libc.so.6
  #4  0x0000555555d7e1f1 in qmp_blockdev_create (job_id=0x555556baee40 "x", options=0x555557666610, errp=0x7fffffffc770) at block/create.c:69
  #5  0x0000555555c96b52 in qmp_marshal_blockdev_create (args=0x7fffdc003830, ret=0x7fffffffc7f8, errp=0x7fffffffc7f0) at qapi/qapi-commands-block-core.c:1314
  #6  0x0000555555deb0a0 in do_qmp_dispatch (cmds=0x55555645de70 <qmp_commands>, request=0x7fffdc005c70, allow_oob=false, errp=0x7fffffffc898) at qapi/qmp-dispatch.c:131
  #7  0x0000555555deb2a1 in qmp_dispatch (cmds=0x55555645de70 <qmp_commands>, request=0x7fffdc005c70, allow_oob=false) at qapi/qmp-dispatch.c:174

With this patch applied, QEMU returns a QMP error:

  {'execute': 'blockdev-create', 'arguments': {'job-id': 'x', 'options': {'size': 0, 'driver': 'nfs', 'location': {'path': '/', 'server': {'host': '::1', 'type': 'inet'}}}}, 'id': 'x'}
  {"id": "x", "error": {"class": "GenericError", "desc": "Block driver 'nfs' not found or not supported"}}

Cc: qemu-stable@nongnu.org
Reported-by: Xu Tian <xutian@redhat.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit d90d5cae2b)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 16:31:38 -06:00
Dr. David Alan Gilbert 145b562990 vhost: Fix memory region section comparison
Using memcmp to compare structures wasn't safe,
as I found out on ARM when I was getting falce miscompares.

Use the helper function for comparing the MRSs.

Fixes: ade6d081fc ("vhost: Regenerate region list from changed sections list")
Cc: qemu-stable@nongnu.org
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Message-Id: <20190814175535.2023-4-dgilbert@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 3fc4a64cba)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 16:23:54 -06:00
Dr. David Alan Gilbert 42b6571357 memory: Provide an equality function for MemoryRegionSections
Provide a comparison function that checks all the fields are the same.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20190814175535.2023-3-dgilbert@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 9366cf02e4)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 16:23:54 -06:00
Dr. David Alan Gilbert c0aca9352d memory: Align MemoryRegionSections fields
MemoryRegionSection includes an Int128 'size' field;
on some platforms the compiler causes an alignment of this to
a 128bit boundary, leaving 8 bytes of dead space.
This deadspace can be filled with junk.

Move the size field to the top avoiding unnecessary alignment.

Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20190814175535.2023-2-dgilbert@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 44f85d3276)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 16:23:49 -06:00
Daniel P. Berrangé 54c130493c tests: make filemonitor test more robust to event ordering
The ordering of events that are emitted during the rmdir
test have changed with kernel >= 5.3. Semantically both
new & old orderings are correct, so we must be able to
cope with either.

To cope with this, when we see an unexpected event, we
push it back onto the queue and look and the subsequent
event to see if that matches instead.

Tested-by: Peter Xu <peterx@redhat.com>
Tested-by: Wei Yang <richardw.yang@linux.intel.com>
Tested-by: Cornelia Huck <cohuck@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit bf9e0313c2)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Nir Soffer 3d018ff3bd block: posix: Always allocate the first block
When creating an image with preallocation "off" or "falloc", the first
block of the image is typically not allocated. When using Gluster
storage backed by XFS filesystem, reading this block using direct I/O
succeeds regardless of request length, fooling alignment detection.

In this case we fallback to a safe value (4096) instead of the optimal
value (512), which may lead to unneeded data copying when aligning
requests.  Allocating the first block avoids the fallback.

Since we allocate the first block even with preallocation=off, we no
longer create images with zero disk size:

    $ ./qemu-img create -f raw test.raw 1g
    Formatting 'test.raw', fmt=raw size=1073741824

    $ ls -lhs test.raw
    4.0K -rw-r--r--. 1 nsoffer nsoffer 1.0G Aug 16 23:48 test.raw

And converting the image requires additional cluster:

    $ ./qemu-img measure -f raw -O qcow2 test.raw
    required size: 458752
    fully allocated size: 1074135040

When using format like vmdk with multiple files per image, we allocate
one block per file:

    $ ./qemu-img create -f vmdk -o subformat=twoGbMaxExtentFlat test.vmdk 4g
    Formatting 'test.vmdk', fmt=vmdk size=4294967296 compat6=off hwversion=undefined subformat=twoGbMaxExtentFlat

    $ ls -lhs test*.vmdk
    4.0K -rw-r--r--. 1 nsoffer nsoffer 2.0G Aug 27 03:23 test-f001.vmdk
    4.0K -rw-r--r--. 1 nsoffer nsoffer 2.0G Aug 27 03:23 test-f002.vmdk
    4.0K -rw-r--r--. 1 nsoffer nsoffer  353 Aug 27 03:23 test.vmdk

I did quick performance test for copying disks with qemu-img convert to
new raw target image to Gluster storage with sector size of 512 bytes:

    for i in $(seq 10); do
        rm -f dst.raw
        sleep 10
        time ./qemu-img convert -f raw -O raw -t none -T none src.raw dst.raw
    done

Here is a table comparing the total time spent:

Type    Before(s)   After(s)    Diff(%)
---------------------------------------
real      530.028    469.123      -11.4
user       17.204     10.768      -37.4
sys        17.881      7.011      -60.7

We can see very clear improvement in CPU usage.

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Message-id: 20190827010528.8818-2-nsoffer@redhat.com
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>

(cherry picked from commit 3a20013fbb)

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Nir Soffer f0d3fa265d file-posix: Handle undetectable alignment
In some cases buf_align or request_alignment cannot be detected:

1. With Gluster, buf_align cannot be detected since the actual I/O is
   done on Gluster server, and qemu buffer alignment does not matter.
   Since we don't have alignment requirement, buf_align=1 is the best
   value.

2. With local XFS filesystem, buf_align cannot be detected if reading
   from unallocated area. In this we must align the buffer, but we don't
   know what is the correct size. Using the wrong alignment results in
   I/O error.

3. With Gluster backed by XFS, request_alignment cannot be detected if
   reading from unallocated area. In this case we need to use the
   correct alignment, and failing to do so results in I/O errors.

4. With NFS, the server does not use direct I/O, so both buf_align cannot
   be detected. In this case we don't need any alignment so we can use
   buf_align=1 and request_alignment=1.

These cases seems to work when storage sector size is 512 bytes, because
the current code starts checking align=512. If the check succeeds
because alignment cannot be detected we use 512. But this does not work
for storage with 4k sector size.

To determine if we can detect the alignment, we probe first with
align=1. If probing succeeds, maybe there are no alignment requirement
(cases 1, 4) or we are probing unallocated area (cases 2, 3). Since we
don't have any way to tell, we treat this as undetectable alignment. If
probing with align=1 fails with EINVAL, but probing with one of the
expected alignments succeeds, we know that we found a working alignment.

Practically the alignment requirements are the same for buffer
alignment, buffer length, and offset in file. So in case we cannot
detect buf_align, we can use request alignment. If we cannot detect
request alignment, we can fallback to a safe value. To use this logic,
we probe first request alignment instead of buf_align.

Here is a table showing the behaviour with current code (the value in
parenthesis is the optimal value).

Case    Sector    buf_align (opt)   request_alignment (opt)     result
======================================================================
1       512       512   (1)          512   (512)                 OK
1       4096      512   (1)          4096  (4096)                FAIL
----------------------------------------------------------------------
2       512       512   (512)        512   (512)                 OK
2       4096      512   (4096)       4096  (4096)                FAIL
----------------------------------------------------------------------
3       512       512   (1)          512   (512)                 OK
3       4096      512   (1)          512   (4096)                FAIL
----------------------------------------------------------------------
4       512       512   (1)          512   (1)                   OK
4       4096      512   (1)          512   (1)                   OK

Same cases with this change:

Case    Sector    buf_align (opt)   request_alignment (opt)     result
======================================================================
1       512       512   (1)          512   (512)                 OK
1       4096      4096  (1)          4096  (4096)                OK
----------------------------------------------------------------------
2       512       512   (512)        512   (512)                 OK
2       4096      4096  (4096)       4096  (4096)                OK
----------------------------------------------------------------------
3       512       4096  (1)          4096  (512)                 OK
3       4096      4096  (1)          4096  (4096)                OK
----------------------------------------------------------------------
4       512       4096  (1)          4096  (1)                   OK
4       4096      4096  (1)          4096  (1)                   OK

I tested that provisioning VMs and copying disks on local XFS and
Gluster with 4k bytes sector size work now, resolving bugs [1],[2].
I tested also on XFS, NFS, Gluster with 512 bytes sector size.

[1] https://bugzilla.redhat.com/1737256
[2] https://bugzilla.redhat.com/1738657

Signed-off-by: Nir Soffer <nsoffer@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>

(cherry picked from commit a6b257a08e)

Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Max Reitz 7db05c8a73 block/file-posix: Let post-EOF fallocate serialize
The XFS kernel driver has a bug that may cause data corruption for qcow2
images as of qemu commit c8bb23cbdb.  We can work around it by
treating post-EOF fallocates as serializing up until infinity (INT64_MAX
in practice).

Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20191101152510.11719-4-mreitz@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 292d06b925)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Max Reitz d9b88f7e0d block: Add bdrv_co_get_self_request()
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20191101152510.11719-3-mreitz@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit c28107e9e5)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Max Reitz 590cff8230 block: Make wait/mark serialising requests public
Make both bdrv_mark_request_serialising() and
bdrv_wait_serialising_requests() public so they can be used from block
drivers.

Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20191101152510.11719-2-mreitz@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 304d9d7f03)
 Conflicts:
	block/io.c
*drop context dependency on 1acc3466a2
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Vladimir Sementsov-Ogievskiy 2e2ad02f2c block/io: refactor padding
We have similar padding code in bdrv_co_pwritev,
bdrv_co_do_pwrite_zeroes and bdrv_co_preadv. Let's combine and unify
it.

[Squashed in Vladimir's qemu-iotests 077 fix
--Stefan]

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 20190604161514.262241-4-vsementsov@virtuozzo.com
Message-Id: <20190604161514.262241-4-vsementsov@virtuozzo.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 7a3f542fbd)
*prereq for 292d06b9
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Vladimir Sementsov-Ogievskiy b3b76fc643 util/iov: improve qemu_iovec_is_zero
We'll need to check a part of qiov soon, so implement it now.

Optimization with align down to 4 * sizeof(long) is dropped due to:
1. It is strange: it aligns length of the buffer, but where is a
   guarantee that buffer pointer is aligned itself?
2. buffer_is_zero() is a better place for optimizations and it has
   them.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 20190604161514.262241-3-vsementsov@virtuozzo.com
Message-Id: <20190604161514.262241-3-vsementsov@virtuozzo.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit f76889e7b9)
*prereq for 292d06b9
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Vladimir Sementsov-Ogievskiy cff024fe85 util/iov: introduce qemu_iovec_init_extended
Introduce new initialization API, to create requests with padding. Will
be used in the following patch. New API uses qemu_iovec_init_buf if
resulting io vector has only one element, to avoid extra allocations.
So, we need to update qemu_iovec_destroy to support destroying such
QIOVs.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-id: 20190604161514.262241-2-vsementsov@virtuozzo.com
Message-Id: <20190604161514.262241-2-vsementsov@virtuozzo.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit d953169d48)
*prereq for 292d06b9
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Tuguoyi 40df4a1bf7 qcow2-bitmap: Fix uint64_t left-shift overflow
There are two issues in In check_constraints_on_bitmap(),
1) The sanity check on the granularity will cause uint64_t
integer left-shift overflow when cluster_size is 2M and the
granularity is BIGGER than 32K.
2) The way to calculate image size that the maximum bitmap
supported can map to is a bit incorrect.
This patch fix it by add a helper function to calculate the
number of bytes needed by a normal bitmap in image and compare
it to the maximum bitmap bytes supported by qemu.

Fixes: 5f72826e7f
Signed-off-by: Guoyi Tu <tu.guoyi@h3c.com>
Message-id: 4ba40cd1e7ee4a708b40899952e49f22@h3c.com
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 570542ecb1)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:58 -06:00
Max Reitz b156178553 iotests: Add peek_file* functions
Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-id: 20191011152814.14791-16-mreitz@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit fc8ba423ca)
*prereq for 570542ec
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:59:33 -06:00
Max Reitz 15f5e8c367 iotests: Add test for 4G+ compressed qcow2 write
Test what qemu-img check says about an image after one has written
compressed data to an offset above 4 GB.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20191028161841.1198-3-mreitz@redhat.com
Reviewed-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit b7cd2c11f7)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-12 11:35:07 -06:00
Max Reitz 405deba14f qcow2: Fix QCOW2_COMPRESSED_SECTOR_MASK
Masks for L2 table entries should have 64 bit.

Fixes: b6c246942b
Buglink: https://bugs.launchpad.net/qemu/+bug/1850000
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20191028161841.1198-2-mreitz@redhat.com
Reviewed-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 24552feb6a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-11 15:40:36 -06:00
Philippe Mathieu-Daudé 01be50603b virtio-blk: Cancel the pending BH when the dataplane is reset
When 'system_reset' is called, the main loop clear the memory
region cache before the BH has a chance to execute. Later when
the deferred function is called, some assumptions that were
made when scheduling them are no longer true when they actually
execute.

This is what happens using a virtio-blk device (fresh RHEL7.8 install):

 $ (sleep 12.3; echo system_reset; sleep 12.3; echo system_reset; sleep 1; echo q) \
   | qemu-system-x86_64 -m 4G -smp 8 -boot menu=on \
     -device virtio-blk-pci,id=image1,drive=drive_image1 \
     -drive file=/var/lib/libvirt/images/rhel78.qcow2,if=none,id=drive_image1,format=qcow2,cache=none \
     -device virtio-net-pci,netdev=net0,id=nic0,mac=52:54:00:c4:e7:84 \
     -netdev tap,id=net0,script=/bin/true,downscript=/bin/true,vhost=on \
     -monitor stdio -serial null -nographic
  (qemu) system_reset
  (qemu) system_reset
  (qemu) qemu-system-x86_64: hw/virtio/virtio.c:225: vring_get_region_caches: Assertion `caches != NULL' failed.
  Aborted

  (gdb) bt
  Thread 1 (Thread 0x7f109c17b680 (LWP 10939)):
  #0  0x00005604083296d1 in vring_get_region_caches (vq=0x56040a24bdd0) at hw/virtio/virtio.c:227
  #1  0x000056040832972b in vring_avail_flags (vq=0x56040a24bdd0) at hw/virtio/virtio.c:235
  #2  0x000056040832d13d in virtio_should_notify (vdev=0x56040a240630, vq=0x56040a24bdd0) at hw/virtio/virtio.c:1648
  #3  0x000056040832d1f8 in virtio_notify_irqfd (vdev=0x56040a240630, vq=0x56040a24bdd0) at hw/virtio/virtio.c:1662
  #4  0x00005604082d213d in notify_guest_bh (opaque=0x56040a243ec0) at hw/block/dataplane/virtio-blk.c:75
  #5  0x000056040883dc35 in aio_bh_call (bh=0x56040a243f10) at util/async.c:90
  #6  0x000056040883dccd in aio_bh_poll (ctx=0x560409161980) at util/async.c:118
  #7  0x0000560408842af7 in aio_dispatch (ctx=0x560409161980) at util/aio-posix.c:460
  #8  0x000056040883e068 in aio_ctx_dispatch (source=0x560409161980, callback=0x0, user_data=0x0) at util/async.c:261
  #9  0x00007f10a8fca06d in g_main_context_dispatch () at /lib64/libglib-2.0.so.0
  #10 0x0000560408841445 in glib_pollfds_poll () at util/main-loop.c:215
  #11 0x00005604088414bf in os_host_main_loop_wait (timeout=0) at util/main-loop.c:238
  #12 0x00005604088415c4 in main_loop_wait (nonblocking=0) at util/main-loop.c:514
  #13 0x0000560408416b1e in main_loop () at vl.c:1923
  #14 0x000056040841e0e8 in main (argc=20, argv=0x7ffc2c3f9c58, envp=0x7ffc2c3f9d00) at vl.c:4578

Fix this by cancelling the BH when the virtio dataplane is stopped.

[This is version of the patch was modified as discussed with Philippe on
the mailing list thread.
--Stefan]

Reported-by: Yihuang Yu <yihyu@redhat.com>
Suggested-by: Stefan Hajnoczi <stefanha@redhat.com>
Fixes: https://bugs.launchpad.net/qemu/+bug/1839428
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-Id: <20190816171503.24761-1-philmd@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit ebb6ff25cd)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-05 13:43:41 -06:00
Paolo Bonzini 051c9b3cbc scsi: lsi: exit infinite loop while executing script (CVE-2019-12068)
When executing script in lsi_execute_script(), the LSI scsi adapter
emulator advances 's->dsp' index to read next opcode. This can lead
to an infinite loop if the next opcode is empty. Move the existing
loop exit after 10k iterations so that it covers no-op opcodes as
well.

Reported-by: Bugs SysSec <bugs-syssec@rub.de>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Prasad J Pandit <pjp@fedoraproject.org>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit de594e4765)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-05 13:39:31 -06:00
Max Filippov b387531323 target/xtensa: regenerate and re-import test_mmuhifi_c3 core
Overlay part of the test_mmuhifi_c3 core has GPL3 copyright headers in
it. Fix that by regenerating test_mmuhifi_c3 core overlay and
re-importing it.

Fixes: d848ea7767 ("target/xtensa: add test_mmuhifi_c3 core")
Reported-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
(cherry picked from commit d5eaec84e5)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-05 12:11:47 -06:00
Christophe Lyon cdc6896659 target/arm: Allow reading flags from FPSCR for M-profile
rt==15 is a special case when reading the flags: it means the
destination is APSR. This patch avoids rejecting
vmrs apsr_nzcv, fpscr
as illegal instruction.

Cc: qemu-stable@nongnu.org
Signed-off-by: Christophe Lyon <christophe.lyon@linaro.org>
Message-id: 20191025095711.10853-1-christophe.lyon@linaro.org
[PMM: updated the comment]
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 2529ab43b8)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:34:45 -06:00
Vladimir Sementsov-Ogievskiy c0b35d87de hbitmap: handle set/reset with zero length
Passing zero length to these functions leads to unpredicted results.
Zero-length set/reset may occur in active-mirror, on zero-length write
(which is unlikely, but not guaranteed to never happen).

Let's just do nothing on zero-length request.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-id: 20191011090711.19940-2-vsementsov@virtuozzo.com
Reviewed-by: Max Reitz <mreitz@redhat.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit fed33bd175)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:31:55 -06:00
Vladimir Sementsov-Ogievskiy fcd7cba6ac util/hbitmap: strict hbitmap_reset
hbitmap_reset has an unobvious property: it rounds requested region up.
It may provoke bugs, like in recently fixed write-blocking mode of
mirror: user calls reset on unaligned region, not keeping in mind that
there are possible unrelated dirty bytes, covered by rounded-up region
and information of this unrelated "dirtiness" will be lost.

Make hbitmap_reset strict: assert that arguments are aligned, allowing
only one exception when @start + @count == hb->orig_size. It's needed
to comfort users of hbitmap_next_dirty_area, which cares about
hb->orig_size.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-Id: <20190806152611.280389-1-vsementsov@virtuozzo.com>
[Maintainer edit: Max's suggestions from on-list. --js]
[Maintainer edit: Eric's suggestion for aligned macro. --js]
Signed-off-by: John Snow <jsnow@redhat.com>
(cherry picked from commit 48557b1383)
*prereq for fed33bd175
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:31:40 -06:00
Fan Yang aea18ef938 COLO-compare: Fix incorrect if logic
'colo_mark_tcp_pkt' should return 'true' when packets are the same, and
'false' otherwise.  However, it returns 'true' when
'colo_compare_packet_payload' returns non-zero while
'colo_compare_packet_payload' is just a 'memcmp'.  The result is that
COLO-compare reports inconsistent TCP packets when they are actually
the same.

Fixes: f449c9e549 ("colo: compare the packet based on the tcp sequence number")
Cc: qemu-stable@nongnu.org
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Fan Yang <Fan_Yang@sjtu.edu.cn>
Signed-off-by: Jason Wang <jasowang@redhat.com>
(cherry picked from commit 1e907a32b7)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:25:01 -06:00
Mikhail Sennikovsky 4887acf574 virtio-net: prevent offloads reset on migration
Currently offloads disabled by guest via the VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET
command are not preserved on VM migration.
Instead all offloads reported by guest features (via VIRTIO_PCI_GUEST_FEATURES)
get enabled.
What happens is: first the VirtIONet::curr_guest_offloads gets restored and offloads
are getting set correctly:

 #0  qemu_set_offload (nc=0x555556a11400, csum=1, tso4=0, tso6=0, ecn=0, ufo=0) at net/net.c:474
 #1  virtio_net_apply_guest_offloads (n=0x555557701ca0) at hw/net/virtio-net.c:720
 #2  virtio_net_post_load_device (opaque=0x555557701ca0, version_id=11) at hw/net/virtio-net.c:2334
 #3  vmstate_load_state (f=0x5555569dc010, vmsd=0x555556577c80 <vmstate_virtio_net_device>, opaque=0x555557701ca0, version_id=11)
     at migration/vmstate.c:168
 #4  virtio_load (vdev=0x555557701ca0, f=0x5555569dc010, version_id=11) at hw/virtio/virtio.c:2197
 #5  virtio_device_get (f=0x5555569dc010, opaque=0x555557701ca0, size=0, field=0x55555668cd00 <__compound_literal.5>) at hw/virtio/virtio.c:2036
 #6  vmstate_load_state (f=0x5555569dc010, vmsd=0x555556577ce0 <vmstate_virtio_net>, opaque=0x555557701ca0, version_id=11) at migration/vmstate.c:143
 #7  vmstate_load (f=0x5555569dc010, se=0x5555578189e0) at migration/savevm.c:829
 #8  qemu_loadvm_section_start_full (f=0x5555569dc010, mis=0x5555569eee20) at migration/savevm.c:2211
 #9  qemu_loadvm_state_main (f=0x5555569dc010, mis=0x5555569eee20) at migration/savevm.c:2395
 #10 qemu_loadvm_state (f=0x5555569dc010) at migration/savevm.c:2467
 #11 process_incoming_migration_co (opaque=0x0) at migration/migration.c:449

However later on the features are getting restored, and offloads get reset to
everything supported by features:

 #0  qemu_set_offload (nc=0x555556a11400, csum=1, tso4=1, tso6=1, ecn=0, ufo=0) at net/net.c:474
 #1  virtio_net_apply_guest_offloads (n=0x555557701ca0) at hw/net/virtio-net.c:720
 #2  virtio_net_set_features (vdev=0x555557701ca0, features=5104441767) at hw/net/virtio-net.c:773
 #3  virtio_set_features_nocheck (vdev=0x555557701ca0, val=5104441767) at hw/virtio/virtio.c:2052
 #4  virtio_load (vdev=0x555557701ca0, f=0x5555569dc010, version_id=11) at hw/virtio/virtio.c:2220
 #5  virtio_device_get (f=0x5555569dc010, opaque=0x555557701ca0, size=0, field=0x55555668cd00 <__compound_literal.5>) at hw/virtio/virtio.c:2036
 #6  vmstate_load_state (f=0x5555569dc010, vmsd=0x555556577ce0 <vmstate_virtio_net>, opaque=0x555557701ca0, version_id=11) at migration/vmstate.c:143
 #7  vmstate_load (f=0x5555569dc010, se=0x5555578189e0) at migration/savevm.c:829
 #8  qemu_loadvm_section_start_full (f=0x5555569dc010, mis=0x5555569eee20) at migration/savevm.c:2211
 #9  qemu_loadvm_state_main (f=0x5555569dc010, mis=0x5555569eee20) at migration/savevm.c:2395
 #10 qemu_loadvm_state (f=0x5555569dc010) at migration/savevm.c:2467
 #11 process_incoming_migration_co (opaque=0x0) at migration/migration.c:449

Fix this by preserving the state in saved_guest_offloads field and
pushing out offload initialization to the new post load hook.

Cc: qemu-stable@nongnu.org
Signed-off-by: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
(cherry picked from commit 7788c3f2e2)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:24:34 -06:00
Michael S. Tsirkin 8010d3fce0 virtio: new post_load hook
Post load hook in virtio vmsd is called early while device is processed,
and when VirtIODevice core isn't fully initialized.  Most device
specific code isn't ready to deal with a device in such state, and
behaves weirdly.

Add a new post_load hook in a device class instead.  Devices should use
this unless they specifically want to verify the migration stream as
it's processed, e.g. for bounds checking.

Cc: qemu-stable@nongnu.org
Suggested-by: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
Cc: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Jason Wang <jasowang@redhat.com>
(cherry picked from commit 1dd713837c)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:23:53 -06:00
Hikaru Nishida 6705b9344f ui: Fix hanging up Cocoa display on macOS 10.15 (Catalina)
macOS API documentation says that before applicationDidFinishLaunching
is called, any events will not be processed. However, some events are
fired before it is called in macOS Catalina. This causes deadlock of
iothread_lock in handleEvent while it will be released after the
app_started_sem is posted.
This patch avoids processing events before the app_started_sem is
posted to prevent this deadlock.

Buglink: https://bugs.launchpad.net/qemu/+bug/1847906
Signed-off-by: Hikaru Nishida <hikarupsp@gmail.com>
Message-id: 20191015010734.85229-1-hikarupsp@gmail.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(cherry picked from commit dff742ad27)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:19:19 -06:00
Max Reitz c0e2fbf124 mirror: Do not dereference invalid pointers
mirror_exit_common() may be called twice (if it is called from
mirror_prepare() and fails, it will be called from mirror_abort()
again).

In such a case, many of the pointers in the MirrorBlockJob object will
already be freed.  This can be seen most reliably for s->target, which
is set to NULL (and then dereferenced by blk_bs()).

Cc: qemu-stable@nongnu.org
Fixes: 737efc1eda
Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-id: 20191014153931.20699-2-mreitz@redhat.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit f93c3add3a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:18:06 -06:00
Max Reitz b077ac637d iotests: Test large write request to qcow2 file
Without HEAD^, the following happens when you attempt a large write
request to a qcow2 file such that the number of bytes covered by all
clusters involved in a single allocation will exceed INT_MAX:

(A) handle_alloc_space() decides to fill the whole area with zeroes and
    fails because bdrv_co_pwrite_zeroes() fails (the request is too
    large).

(B) If handle_alloc_space() does not do anything, but merge_cow()
    decides that the requests can be merged, it will create a too long
    IOV that later cannot be written.

(C) Otherwise, all parts will be written separately, so those requests
    will work.

In either B or C, though, qcow2_alloc_cluster_link_l2() will have an
overflow: We use an int (i) to iterate over nb_clusters, and then
calculate the L2 entry based on "i << s->cluster_bits" -- which will
overflow if the range covers more than INT_MAX bytes.  This then leads
to image corruption because the L2 entry will be wrong (it will be
recognized as a compressed cluster).

Even if that were not the case, the .cow_end area would be empty
(because handle_alloc() will cap avail_bytes and nb_bytes at INT_MAX, so
their difference (which is the .cow_end size) will be 0).

So this test checks that on such large requests, the image will not be
corrupted.  Unfortunately, we cannot check whether COW will be handled
correctly, because that data is discarded when it is written to null-co
(but we have to use null-co, because writing 2 GB of data in a test is
not quite reasonable).

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit a1406a9262)
 Conflicts:
	tests/qemu-iotests/group
*drop context dep. on tests not in 4.1
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:16:16 -06:00
Max Reitz 9e51c5306c qcow2: Limit total allocation range to INT_MAX
When the COW areas are included, the size of an allocation can exceed
INT_MAX.  This is kind of limited by handle_alloc() in that it already
caps avail_bytes at INT_MAX, but the number of clusters still reflects
the original length.

This can have all sorts of effects, ranging from the storage layer write
call failing to image corruption.  (If there were no image corruption,
then I suppose there would be data loss because the .cow_end area is
forced to be empty, even though there might be something we need to
COW.)

Fix all of it by limiting nb_clusters so the equivalent number of bytes
will not exceed INT_MAX.

Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit d1b9d19f99)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:15:25 -06:00
Thomas Huth aae0faa5d3 hw/core/loader: Fix possible crash in rom_copy()
Both, "rom->addr" and "addr" are derived from the binary image
that can be loaded with the "-kernel" paramer. The code in
rom_copy() then calculates:

    d = dest + (rom->addr - addr);

and uses "d" as destination in a memcpy() some lines later. Now with
bad kernel images, it is possible that rom->addr is smaller than addr,
thus "rom->addr - addr" gets negative and the memcpy() then tries to
copy contents from the image to a bad memory location. This could
maybe be used to inject code from a kernel image into the QEMU binary,
so we better fix it with an additional sanity check here.

Cc: qemu-stable@nongnu.org
Reported-by: Guangming Liu
Buglink: https://bugs.launchpad.net/qemu/+bug/1844635
Message-Id: <20190925130331.27825-1-thuth@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
(cherry picked from commit e423455c4f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:10:48 -06:00
Adrian Moreno 7b404cae7f vhost-user: save features if the char dev is closed
That way the state can be correctly restored when the device is opened
again. This might happen if the backend is restarted.

Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1738768
Reported-by: Pei Zhang <pezhang@redhat.com>
Fixes: 6ab79a20af ("do not call vhost_net_cleanup() on running net from char user event")
Cc: ddstreet@canonical.com
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Adrian Moreno <amorenoz@redhat.com>
Message-Id: <20190924162044.11414-1-amorenoz@redhat.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit c6beefd674)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:09:49 -06:00
Kevin Wolf d868d30db6 iotests: Test internal snapshots with -blockdev
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Tested-by: Peter Krempa <pkrempa@redhat.com>
(cherry picked from commit 92b22e7b17)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:07:08 -06:00
Kevin Wolf 7a8aa6c734 block/snapshot: Restrict set of snapshot nodes
Nodes involved in internal snapshots were those that were returned by
bdrv_next(), inserted and not read-only. bdrv_next() in turn returns all
nodes that are either the root node of a BlockBackend or monitor-owned
nodes.

With the typical -drive use, this worked well enough. However, in the
typical -blockdev case, the user defines one node per option, making all
nodes monitor-owned nodes. This includes protocol nodes etc. which often
are not snapshottable, so "savevm" only returns an error.

Change the conditions so that internal snapshot still include all nodes
that have a BlockBackend attached (we definitely want to snapshot
anything attached to a guest device and probably also the built-in NBD
server; snapshotting block job BlockBackends is more of an accident, but
a preexisting one), but other monitor-owned nodes are only included if
they have no parents.

This makes internal snapshots usable again with typical -blockdev
configurations.

Cc: qemu-stable@nongnu.org
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
Tested-by: Peter Krempa <pkrempa@redhat.com>
(cherry picked from commit 05f4aced65)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-11-04 08:06:36 -06:00
Matthew Rosato 331c08d300 s390: PCI: fix IOMMU region init
The fix in dbe9cf606c shrinks the IOMMU memory region to a size
that seems reasonable on the surface, however is actually too
small as it is based against a 0-mapped address space.  This
causes breakage with small guests as they can overrun the IOMMU window.

Let's go back to the prior method of initializing iommu for now.

Fixes: dbe9cf606c ("s390x/pci: Set the iommu region size mpcifc request")
Cc: qemu-stable@nongnu.org
Reviewed-by: Pierre Morel <pmorel@linux.ibm.com>
Reported-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
Tested-by: Boris Fiuczynski <fiuczy@linux.ibm.com>
Reported-by: Stefan Zimmerman <stzi@linux.ibm.com>
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Message-Id: <1569507036-15314-1-git-send-email-mjrosato@linux.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
(cherry picked from commit 7df1dac5f1)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:42:13 -05:00
Michael Roth fc5afb1a92 roms/Makefile.edk2: don't pull in submodules when building from tarball
Currently the `make efi` target pulls submodules nested under the
roms/edk2 submodule as dependencies. However, when we attempt to build
from a tarball this fails since we are no longer in a git tree.

A preceding patch will pre-populate these submodules in the tarball,
so assume this build dependency is only needed when building from a
git tree.

Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Bruce Rogers <brogers@suse.com>
Cc: qemu-stable@nongnu.org # v4.1.0
Reported-by: Bruce Rogers <brogers@suse.com>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-Id: <20190912231202.12327-3-mdroth@linux.vnet.ibm.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
(cherry picked from commit f3e330e3c3)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:39:09 -05:00
Michael Roth c5c9b1362d make-release: pull in edk2 submodules so we can build it from tarballs
The `make efi` target added by 536d2173 is built from the roms/edk2
submodule, which in turn relies on additional submodules nested under
roms/edk2.

The make-release script currently only pulls in top-level submodules,
so these nested submodules are missing in the resulting tarball.

We could try to address this situation more generally by recursively
pulling in all submodules, but this doesn't necessarily ensure the
end-result will build properly (this case also required other changes).

Additionally, due to the nature of submodules, we may not always have
control over how these sorts of things are dealt with, so for now we
continue to handle it on a case-by-case in the make-release script.

Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Bruce Rogers <brogers@suse.com>
Cc: qemu-stable@nongnu.org # v4.1.0
Reported-by: Bruce Rogers <brogers@suse.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
Message-Id: <20190912231202.12327-2-mdroth@linux.vnet.ibm.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
(cherry picked from commit 45c61c6c23)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:38:57 -05:00
Peter Maydell 220816989c hw/arm/boot.c: Set NSACR.{CP11,CP10} for NS kernel boots
If we're booting a Linux kernel directly into Non-Secure
state on a CPU which has Secure state, then make sure we
set the NSACR CP11 and CP10 bits, so that Non-Secure is allowed
to access the FPU. Otherwise an AArch32 kernel will UNDEF as
soon as it tries to use the FPU.

It used to not matter that we didn't do this until commit
fc1120a7f5, where we implemented actually honouring
these NSACR bits.

The problem only exists for CPUs where EL3 is AArch32; the
equivalent AArch64 trap bits are in CPTR_EL3 and are "0 to
not trap, 1 to trap", so the reset value of the register
permits NS access, unlike NSACR.

Fixes: fc1120a7f5
Fixes: https://bugs.launchpad.net/qemu/+bug/1844597
Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20190920174039.3916-1-peter.maydell@linaro.org
(cherry picked from commit ece628fcf6)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:37:26 -05:00
Vladimir Sementsov-Ogievskiy 783e7eb52c block/backup: fix backup_cow_with_offload for last cluster
We shouldn't try to copy bytes beyond EOF. Fix it.

Fixes: 9ded4a0114
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Message-id: 20190920142056.12778-3-vsementsov@virtuozzo.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 1048ddf0a3)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Vladimir Sementsov-Ogievskiy e01ed1a1ae block/backup: fix max_transfer handling for copy_range
Of course, QEMU_ALIGN_UP is a typo, it should be QEMU_ALIGN_DOWN, as we
are trying to find aligned size which satisfy both source and target.
Also, don't ignore too small max_transfer. In this case seems safer to
disable copy_range.

Fixes: 9ded4a0114
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-id: 20190920142056.12778-2-vsementsov@virtuozzo.com
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 981fb5810a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Kevin Wolf 416a692e51 qcow2: Fix corruption bug in qcow2_detect_metadata_preallocation()
qcow2_detect_metadata_preallocation() calls qcow2_get_refcount() which
requires s->lock to be taken to protect its accesses to the refcount
table and refcount blocks. However, nothing in this code path actually
took the lock. This could cause the same cache entry to be used by two
requests at the same time, for different tables at different offsets,
resulting in image corruption.

As it would be preferable to base the detection on consistent data (even
though it's just heuristics), let's take the lock not only around the
qcow2_get_refcount() calls, but around the whole function.

This patch takes the lock in qcow2_co_block_status() earlier and asserts
in qcow2_detect_metadata_preallocation() that we hold the lock.

Fixes: 69f47505ee
Cc: qemu-stable@nongnu.org
Reported-by: Michael Weiser <michael.weiser@gmx.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Tested-by: Michael Weiser <michael.weiser@gmx.de>
Reviewed-by: Michael Weiser <michael.weiser@gmx.de>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 5e97855052)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Kevin Wolf e9bb3d942e coroutine: Add qemu_co_mutex_assert_locked()
Some functions require that the caller holds a certain CoMutex for them
to operate correctly. Add a function so that they can assert the lock is
really held.

Cc: qemu-stable@nongnu.org
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Tested-by: Michael Weiser <michael.weiser@gmx.de>
Reviewed-by: Michael Weiser <michael.weiser@gmx.de>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Denis V. Lunev <den@openvz.org>
Reviewed-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 944f3d5dd2)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Maxim Levitsky 84f22c7285 block/qcow2: Fix corruption introduced by commit 8ac0f15f33
This fixes subtle corruption introduced by luks threaded encryption
in commit 8ac0f15f33

Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1745922

The corruption happens when we do a write that
   * writes to two or more unallocated clusters at once
   * doesn't fully cover the first sector
   * doesn't fully cover the last sector
   * uses luks encryption

In this case, when allocating the new clusters we COW both areas
prior to the write and after the write, and we encrypt them.

The above mentioned commit accidentally made it so we encrypt the
second COW area using the physical cluster offset of the first area.

The problem is that offset_in_cluster in do_perform_cow_encrypt
can be larger that the cluster size, thus cluster_offset
will no longer point to the start of the cluster at which encrypted
area starts.

Next patch in this series will refactor the code to avoid all these
assumptions.

In the bugreport that was triggered by rebasing a luks image to new,
zero filled base, which lot of such writes, and causes some files
with zero areas to contain garbage there instead.
But as described above it can happen elsewhere as well

Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Message-id: 20190915203655.21638-2-mlevitsk@redhat.com
Reviewed-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 38e7d54bdc)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Sergio Lopez 86b0f4022b blockjob: update nodes head while removing all bdrv
block_job_remove_all_bdrv() iterates through job->nodes, calling
bdrv_root_unref_child() for each entry. The call to the latter may
reach child_job_[can_]set_aio_ctx(), which will also attempt to
traverse job->nodes, potentially finding entries that where freed
on previous iterations.

To avoid this situation, update job->nodes head on each iteration to
ensure that already freed entries are no longer linked to the list.

RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1746631
Signed-off-by: Sergio Lopez <slp@redhat.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190911100316.32282-1-mreitz@redhat.com
Reviewed-by: Sergio Lopez <slp@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit d876bf676f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Max Reitz 2d86df1f78 curl: Handle success in multi_check_completion
Background: As of cURL 7.59.0, it verifies that several functions are
not called from within a callback.  Among these functions is
curl_multi_add_handle().

curl_read_cb() is a callback from cURL and not a coroutine.  Waking up
acb->co will lead to entering it then and there, which means the current
request will settle and the caller (if it runs in the same coroutine)
may then issue the next request.  In such a case, we will enter
curl_setup_preadv() effectively from within curl_read_cb().

Calling curl_multi_add_handle() will then fail and the new request will
not be processed.

Fix this by not letting curl_read_cb() wake up acb->co.  Instead, leave
the whole business of settling the AIOCB objects to
curl_multi_check_completion() (which is called from our timer callback
and our FD handler, so not from any cURL callbacks).

Reported-by: Natalie Gavrielov <ngavrilo@redhat.com>
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1740193
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190910124136.10565-7-mreitz@redhat.com
Reviewed-by: John Snow <jsnow@redhat.com>
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit bfb23b480a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Max Reitz 18e1b71937 curl: Report only ready sockets
Instead of reporting all sockets to cURL, only report the one that has
caused curl_multi_do_locked() to be called.  This lets us get rid of the
QLIST_FOREACH_SAFE() list, which was actually wrong: SAFE foreaches are
only safe when the current element is removed in each iteration.  If it
possible for the list to be concurrently modified, we cannot guarantee
that only the current element will be removed.  Therefore, we must not
use QLIST_FOREACH_SAFE() here.

Fixes: ff5ca1664a
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190910124136.10565-6-mreitz@redhat.com
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 9abaf9fc47)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Max Reitz 0888ddac8e curl: Pass CURLSocket to curl_multi_do()
curl_multi_do_locked() currently marks all sockets as ready.  That is
not only inefficient, but in fact unsafe (the loop is).  A follow-up
patch will change that, but to do so, curl_multi_do_locked() needs to
know exactly which socket is ready; and that is accomplished by this
patch here.

Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190910124136.10565-5-mreitz@redhat.com
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 9dbad87d25)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Max Reitz 4be97ef966 curl: Check completion in curl_multi_do()
While it is more likely that transfers complete after some file
descriptor has data ready to read, we probably should not rely on it.
Better be safe than sorry and call curl_multi_check_completion() in
curl_multi_do(), too, just like it is done in curl_multi_read().

With this change, curl_multi_do() and curl_multi_read() are actually the
same, so drop curl_multi_read() and use curl_multi_do() as the sole FD
handler.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190910124136.10565-4-mreitz@redhat.com
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 948403bcb1)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Max Reitz 78ea94e389 curl: Keep *socket until the end of curl_sock_cb()
This does not really change anything, but it makes the code a bit easier
to follow once we use @socket as the opaque pointer for
aio_set_fd_handler().

Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190910124136.10565-3-mreitz@redhat.com
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 007f339b10)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Max Reitz 3648493495 curl: Keep pointer to the CURLState in CURLSocket
A follow-up patch will make curl_multi_do() and curl_multi_read() take a
CURLSocket instead of the CURLState.  They still need the latter,
though, so add a pointer to it to the former.

Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Message-id: 20190910124136.10565-2-mreitz@redhat.com
Reviewed-by: Maxim Levitsky <mlevitsk@redhat.com>
Signed-off-by: Max Reitz <mreitz@redhat.com>
(cherry picked from commit 0487861685)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-30 11:34:26 -05:00
Peter Lieven 0694c489cd block/nfs: tear down aio before nfs_close
nfs_close is a sync call from libnfs and has its own event
handler polling on the nfs FD. Avoid that both QEMU and libnfs
are intefering here.

CC: qemu-stable@nongnu.org
Signed-off-by: Peter Lieven <pl@kamp.de>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 601dc65597)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-28 00:03:19 -05:00
Alberto Garcia c9ffb12754 qcow2: Fix the calculation of the maximum L2 cache size
The size of the qcow2 L2 cache defaults to 32 MB, which can be easily
larger than the maximum amount of L2 metadata that the image can have.
For example: with 64 KB clusters the user would need a qcow2 image
with a virtual size of 256 GB in order to have 32 MB of L2 metadata.

Because of that, since commit b749562d98
we forbid the L2 cache to become larger than the maximum amount of L2
metadata for the image, calculated using this formula:

    uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8);

The problem with this formula is that the result should be rounded up
to the cluster size because an L2 table on disk always takes one full
cluster.

For example, a 1280 MB qcow2 image with 64 KB clusters needs exactly
160 KB of L2 metadata, but we need 192 KB on disk (3 clusters) even if
the last 32 KB of those are not going to be used.

However QEMU rounds the numbers down and only creates 2 cache tables
(128 KB), which is not enough for the image.

A quick test doing 4KB random writes on a 1280 MB image gives me
around 500 IOPS, while with the correct cache size I get 16K IOPS.

Cc: qemu-stable@nongnu.org
Signed-off-by: Alberto Garcia <berto@igalia.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit b70d08205b)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-28 00:00:03 -05:00
Johannes Berg 28a9a3558a libvhost-user: fix SLAVE_SEND_FD handling
It doesn't look like this could possibly work properly since
VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD is defined to 10, but the
dev->protocol_features has a bitmap. I suppose the peer this
was tested with also supported VHOST_USER_PROTOCOL_F_LOG_SHMFD,
in which case the test would always be false, but nevertheless
the code seems wrong.

Use has_feature() to fix this.

Fixes: d84599f56c ("libvhost-user: support host notifier")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Message-Id: <20190903200422.11693-1-johannes@sipsolutions.net>
Reviewed-by: Tiwei Bie <tiwei.bie@intel.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 8726b70b44)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:59:27 -05:00
Peter Maydell 9027d3fba6 target/arm: Don't abort on M-profile exception return in linux-user mode
An attempt to do an exception-return (branch to one of the magic
addresses) in linux-user mode for M-profile should behave like
a normal branch, because linux-user mode is always going to be
in 'handler' mode. This used to work, but we broke it when we added
support for the M-profile security extension in commit d02a8698d7.

In that commit we allowed even handler-mode calls to magic return
values to be checked for and dealt with by causing an
EXCP_EXCEPTION_EXIT exception to be taken, because this is
needed for the FNC_RETURN return-from-non-secure-function-call
handling. For system mode we added a check in do_v7m_exception_exit()
to make any spurious calls from Handler mode behave correctly, but
forgot that linux-user mode would also be affected.

How an attempted return-from-non-secure-function-call in linux-user
mode should be handled is not clear -- on real hardware it would
result in return to secure code (not to the Linux kernel) which
could then handle the error in any way it chose. For QEMU we take
the simple approach of treating this erroneous return the same way
it would be handled on a CPU without the security extensions --
treat it as a normal branch.

The upshot of all this is that for linux-user mode we should never
do any of the bx_excret magic, so the code change is simple.

This ought to be a weird corner case that only affects broken guest
code (because Linux user processes should never be attempting to do
exception returns or NS function returns), except that the code that
assigns addresses in RAM for the process and stack in our linux-user
code does not attempt to avoid this magic address range, so
legitimate code attempting to return to a trampoline routine on the
stack can fall into this case. This change fixes those programs,
but we should also look at restricting the range of memory we
use for M-profile linux-user guests to the area that would be
real RAM in hardware.

Cc: qemu-stable@nongnu.org
Reported-by: Christophe Lyon <christophe.lyon@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20190822131534.16602-1-peter.maydell@linaro.org
Fixes: https://bugs.launchpad.net/qemu/+bug/1840922
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
(cherry picked from commit 5e5584c89f)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:58:01 -05:00
Peter Maydell 38fb634853 target/arm: Free TCG temps in trans_VMOV_64_sp()
The function neon_store_reg32() doesn't free the TCG temp that it
is passed, so the caller must do that. We got this right in most
places but forgot to free the TCG temps in trans_VMOV_64_sp().

Cc: qemu-stable@nongnu.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Message-id: 20190827121931.26836-1-peter.maydell@linaro.org
(cherry picked from commit 342d27581b)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:57:16 -05:00
Max Reitz ad95e0573e iotests: Test blockdev-create for vpc
Signed-off-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit cb73747e1a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:55:44 -05:00
Max Reitz 593beeaf81 iotests: Restrict nbd Python tests to nbd
We have two Python unittest-style tests that test NBD.  As such, they
should specify supported_protocols=['nbd'] so they are skipped when the
user wants to test some other protocol.

Furthermore, we should restrict their choice of formats to 'raw'.  The
idea of a protocol/format combination is to use some format over some
protocol; but we always use the raw format over NBD.  It does not really
matter what the NBD server uses on its end, and it is not a useful test
of the respective format driver anyway.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 7c932a1d69)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:55:39 -05:00
Max Reitz eee776fbc0 iotests: Restrict file Python tests to file
Most of our Python unittest-style tests only support the file protocol.
You can run them with any other protocol, but the test will simply
ignore your choice and use file anyway.

We should let them signal that they require the file protocol so they
are skipped when you want to test some other protocol.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 103cbc771e)
 Conflicts:
	tests/qemu-iotests/257
*drop changes for tests not in 4.1.0
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:55:10 -05:00
Max Reitz 819ba23575 iotests: Add supported protocols to execute_test()
Signed-off-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 88d2aa533a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:53:52 -05:00
John Snow 4d9bdd3149 iotests: add testing shim for script-style python tests
Because the new-style python tests don't use the iotests.main() test
launcher, we don't turn on the debugger logging for these scripts
when invoked via ./check -d.

Refactor the launcher shim into new and old style shims so that they
share environmental configuration.

Two cleanup notes: debug was not actually used as a global, and there
was no reason to create a class in an inner scope just to achieve
default variables; we can simply create an instance of the runner with
the values we want instead.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
Message-id: 20190709232550.10724-14-jsnow@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
(cherry picked from commit 456a2d5ac7)
*prereq for 88d2aa533a
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:53:35 -05:00
Max Reitz 97c478c355 vpc: Return 0 from vpc_co_create() on success
blockdev_create_run() directly uses .bdrv_co_create()'s return value as
the job's return value.  Jobs must return 0 on success, not just any
nonnegative value.  Therefore, using blockdev-create for VPC images may
currently fail as the vpc driver may return a positive integer.

Because there is no point in returning a positive integer anywhere in
the block layer (all non-negative integers are generally treated as
complete success), we probably do not want to add more such cases.
Therefore, fix this problem by making the vpc driver always return 0 in
case of success.

Suggested-by: Kevin Wolf <kwolf@redhat.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 1a37e31244)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:50:55 -05:00
Igor Mammedov 725dfa851f x86: do not advertise die-id in query-hotpluggbale-cpus if '-smp dies' is not set
Commit 176d2cda0 (i386/cpu: Consolidate die-id validity in smp context) added
new 'die-id' topology property to CPUs and exposed it via QMP command
query-hotpluggable-cpus, which broke -device/device_add cpu-foo for existing
users that do not support die-id/dies yet. That's would be fine if it happened
to new machine type only but it also happened to old machine types,
which breaks migration from old QEMU to the new one, for example following CLI:

  OLD-QEMU -M pc-i440fx-4.0 -smp 1,max_cpus=2 \
           -device qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id
is not able to start with new QEMU, complaining about invalid die-id.

After discovering regression, the patch
   "pc: Don't make die-id mandatory unless necessary"
makes die-id optional so old CLI would work.

However it's not enough as new QEMU still exposes die-id via query-hotpluggbale-cpus
QMP command, so the users that started old machine type on new QEMU, using all
properties (including die-id) received from QMP command (as required), won't be
able to start old QEMU using the same properties since it doesn't support die-id.

Fix it by hiding die-id in query-hotpluggbale-cpus for all machine types in case
'-smp dies' is not provided on CLI or -smp dies = 1', in which case smp_dies == 1
and APIC ID is calculated in default way (as it was before DIE support) so we won't
need compat code as in both cases the topology provided to guest via CPUID is the same.

Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Message-Id: <20190902120222.6179-1-imammedo@redhat.com>
Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
(cherry picked from commit c6c1bb89fb)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:50:01 -05:00
Markus Armbruster 57fdf4a13f pr-manager: Fix invalid g_free() crash bug
pr_manager_worker() passes its @opaque argument to g_free().  Wrong;
it points to pr_manager_worker()'s automatic @data.  Broken when
commit 2f3a7ab39b converted @data from heap- to stack-allocated.  Fix
by deleting the g_free().

Fixes: 2f3a7ab39b
Cc: qemu-stable@nongnu.org
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 6b9d62c2a9)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:47:31 -05:00
Max Reitz 3361d03ff0 iotests: Test reverse sub-cluster qcow2 writes
This exercises the regression introduced in commit
50ba5b2d99.  On my machine, it has close
to a 50 % false-negative rate, but that should still be sufficient to
test the fix.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Tested-by: Stefano Garzarella <sgarzare@redhat.com>
Tested-by: John Snow <jsnow@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit ae6ef01909)
 Conflicts:
	tests/qemu-iotests/group
*fix context deps on tests not in 4.1.0
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:46:09 -05:00
Max Reitz 6f1a94035b block/file-posix: Reduce xfsctl() use
This patch removes xfs_write_zeroes() and xfs_discard().  Both functions
have been added just before the same feature was present through
fallocate():

- fallocate() has supported PUNCH_HOLE for XFS since Linux 2.6.38 (March
  2011); xfs_discard() was added in December 2010.

- fallocate() has supported ZERO_RANGE for XFS since Linux 3.15 (June
  2014); xfs_write_zeroes() was added in November 2013.

Nowadays, all systems that qemu runs on should support both fallocate()
features (RHEL 7's kernel does).

xfsctl() is still useful for getting the request alignment for O_DIRECT,
so this patch does not remove our dependency on it completely.

Note that xfs_write_zeroes() had a bug: It calls ftruncate() when the
file is shorter than the specified range (because ZERO_RANGE does not
increase the file length).  ftruncate() may yield and then discard data
that parallel write requests have written past the EOF in the meantime.
Dropping the function altogether fixes the bug.

Suggested-by: Paolo Bonzini <pbonzini@redhat.com>
Fixes: 50ba5b2d99
Reported-by: Lukáš Doktor <ldoktor@redhat.com>
Cc: qemu-stable@nongnu.org
Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: John Snow <jsnow@redhat.com>
Tested-by: Stefano Garzarella <sgarzare@redhat.com>
Tested-by: John Snow <jsnow@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit b2c6f23f4a)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:45:18 -05:00
Paul Durrant c12adfd8f6 xen-bus: check whether the frontend is active during device reset...
...not the backend

Commit cb323146 "xen-bus: Fix backend state transition on device reset"
contained a subtle mistake. The hunk

@@ -539,11 +556,11 @@ static void xen_device_backend_changed(void *opaque)

     /*
      * If the toolstack (or unplug request callback) has set the backend
-     * state to Closing, but there is no active frontend (i.e. the
-     * state is not Connected) then set the backend state to Closed.
+     * state to Closing, but there is no active frontend then set the
+     * backend state to Closed.
      */
     if (xendev->backend_state == XenbusStateClosing &&
-        xendev->frontend_state != XenbusStateConnected) {
+        !xen_device_state_is_active(state)) {
         xen_device_backend_set_state(xendev, XenbusStateClosed);
     }

mistakenly replaced the check of 'xendev->frontend_state' with a check
(now in a helper function) of 'state', which actually equates to
'xendev->backend_state'.

This patch fixes the mistake.

Fixes: cb32314607
Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
Message-Id: <20190910171753.3775-1-paul.durrant@citrix.com>
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
(cherry picked from commit df6180bb56)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:43:19 -05:00
Anthony PERARD b6cedc911e xen-bus: Fix backend state transition on device reset
When a frontend wants to reset its state and the backend one, it
starts with setting "Closing", then waits for the backend (QEMU) to do
the same.

But when QEMU is setting "Closing" to its state, it triggers an event
(xenstore watch) that re-execute xen_device_backend_changed() and set
the backend state to "Closed". QEMU should wait for the frontend to
set "Closed" before doing the same.

Before setting "Closed" to the backend_state, we are also going to
check if there is a frontend. If that the case, when the backend state
is set to "Closing" the frontend should react and sets its state to
"Closing" then "Closed". The backend should wait for that to happen.

Fixes: b6af8926fb
Signed-off-by: Anthony PERARD <anthony.perard@citrix.com>
Reviewed-by: Paul Durrant <paul.durrant@citrix.com>
Message-Id: <20190823101534.465-2-anthony.perard@citrix.com>
(cherry picked from commit cb32314607)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:42:16 -05:00
Eduardo Habkost 7ebcd375ad pc: Don't make die-id mandatory unless necessary
We have this issue reported when using libvirt to hotplug CPUs:
https://bugzilla.redhat.com/show_bug.cgi?id=1741451

Basically, libvirt is not copying die-id from
query-hotpluggable-cpus, but die-id is now mandatory.

We could blame libvirt and say it is not following the documented
interface, because we have this buried in the QAPI schema
documentation:

> Note: currently there are 5 properties that could be present
> but management should be prepared to pass through other
> properties with device_add command to allow for future
> interface extension. This also requires the filed names to be kept in
> sync with the properties passed to -device/device_add.

But I don't think this would be reasonable from us.  We can just
make QEMU more flexible and let die-id to be omitted when there's
no ambiguity.  This will allow us to keep compatibility with
existing libvirt versions.

Test case included to ensure we don't break this again.

Fixes: commit 176d2cda0d ("i386/cpu: Consolidate die-id validity in smp context")
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Message-Id: <20190816170750.23910-1-ehabkost@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
(cherry picked from commit fea374e7c8)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:41:00 -05:00
Aurelien Jarno 4bfd496be3 target/alpha: fix tlb_fill trap_arg2 value for instruction fetch
Commit e41c945297 ("target/alpha: Convert to CPUClass::tlb_fill")
slightly changed the way the trap_arg2 value is computed in case of TLB
fill. The type of the variable used in the ternary operator has been
changed from an int to an enum. This causes the -1 value to not be
sign-extended to 64-bit in case of an instruction fetch. The trap_arg2
ends up with 0xffffffff instead of 0xffffffffffffffff. Fix that by
changing the -1 into -1LL.

This fixes the execution of user space processes in qemu-system-alpha.

Fixes: e41c945297
Cc: qemu-stable@nongnu.org
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
[rth: Test MMU_DATA_LOAD and MMU_DATA_STORE instead of implying them.]
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
(cherry picked from commit cb1de55a83)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:31:56 -05:00
David Hildenbrand 499a5d6bb4 s390x/tcg: Fix VERIM with 32/64 bit elements
Wrong order of operands. The constant always comes last. Makes QEMU crash
reliably on specific git fetch invocations.

Reported-by: Stefano Brivio <sbrivio@redhat.com>
Signed-off-by: David Hildenbrand <david@redhat.com>
Message-Id: <20190814151242.27199-1-david@redhat.com>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Fixes: 5c4b0ab460 ("s390x/tcg: Implement VECTOR ELEMENT ROTATE AND INSERT UNDER MASK")
Cc: qemu-stable@nongnu.org
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
(cherry picked from commit 25bcb45d1b)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:31:08 -05:00
John Snow 73a5bf4729 Revert "ide/ahci: Check for -ECANCELED in aio callbacks"
This reverts commit 0d910cfeaf.

It's not correct to just ignore an error code in a callback; we need to
handle that error and possible report failure to the guest so that they
don't wait indefinitely for an operation that will now never finish.

This ought to help cases reported by Nutanix where iSCSI returns a
legitimate -ECANCELED for certain operations which should be propagated
normally.

Reported-by: Shaju Abraham <shaju.abraham@nutanix.com>
Signed-off-by: John Snow <jsnow@redhat.com>
Message-id: 20190729223605.7163-1-jsnow@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
(cherry picked from commit 8ec41c4265)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:28:24 -05:00
Paolo Bonzini fbde196c30 dma-helpers: ensure AIO callback is invoked after cancellation
dma_aio_cancel unschedules the BH if there is one, which corresponds
to the reschedule_dma case of dma_blk_cb.  This can stall the DMA
permanently, because dma_complete will never get invoked and therefore
nobody will ever invoke the original AIO callback in dbs->common.cb.

Fix this by invoking the callback (which is ensured to happen after
a bdrv_aio_cancel_async, or done manually in the dbs->bh case), and
add assertions to check that the DMA state machine is indeed waiting
for dma_complete or reschedule_dma, but never both.

Reported-by: John Snow <jsnow@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 20190729213416.1972-1-pbonzini@redhat.com
Signed-off-by: John Snow <jsnow@redhat.com>
(cherry picked from commit 539343c0a4)
Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
2019-10-27 23:28:10 -05:00
4035 changed files with 98623 additions and 226051 deletions

No files matched your search

+1 -16
View File
@@ -22,21 +22,6 @@ macos_task:
install_script:
- brew install pkg-config python gnu-sed glib pixman make sdl2
script:
- mkdir build
- cd build
- ../configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
- gmake -j$(sysctl -n hw.ncpu)
- gmake check -j$(sysctl -n hw.ncpu)
macos_xcode_task:
osx_instance:
# this is an alias for the latest Xcode
image: mojave-xcode
install_script:
- brew install pkg-config gnu-sed glib pixman make sdl2
script:
- mkdir build
- cd build
- ../configure --cc=clang || { cat config.log; exit 1; }
- ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; }
- gmake -j$(sysctl -n hw.ncpu)
- gmake check -j$(sysctl -n hw.ncpu)
-34
View File
@@ -1,34 +0,0 @@
# Configuration for Repo Lockdown - https://github.com/dessant/repo-lockdown
# Close issues and pull requests
close: true
# Lock issues and pull requests
lock: true
issues:
comment: |
Thank you for your interest in the QEMU project.
This repository is a read-only mirror of the project's master
repostories hosted on https://git.qemu.org/git/qemu.git.
The project does not process issues filed on GitHub.
The project issues are tracked on Launchpad:
https://bugs.launchpad.net/qemu
QEMU welcomes bug report contributions. You can file new ones on:
https://bugs.launchpad.net/qemu/+filebug
pulls:
comment: |
Thank you for your interest in the QEMU project.
This repository is a read-only mirror of the project's master
repostories hosted on https://git.qemu.org/git/qemu.git.
The project does not process merge requests filed on GitHub.
QEMU welcomes contributions of code (either fixing bugs or adding new
functionality). However, we get a lot of patches, and so we have some
guidelines about contributing on the project website:
https://www.qemu.org/contribute/
+3 -6
View File
@@ -6,7 +6,6 @@
/config-target.*
/config.status
/config-temp
/tools/virtiofsd/50-qemu-virtiofsd.json
/elf2dmp
/trace-events-all
/trace/generated-events.h
@@ -38,14 +37,15 @@
/qapi/qapi-emit-events.[ch]
/qapi/qapi-events-*.[ch]
/qapi/qapi-events.[ch]
/qapi/qapi-init-commands.[ch]
/qapi/qapi-introspect.[ch]
/qapi/qapi-types-*.[ch]
/qapi/qapi-types.[ch]
/qapi/qapi-visit-*.[ch]
!/qapi/qapi-visit-core.c
/qapi/qapi-visit.[ch]
/qapi/qapi-doc.texi
/qemu-doc.html
/qemu-doc.info
/qemu-doc.txt
/qemu-edid
/qemu-img
/qemu-nbd
@@ -65,8 +65,6 @@
/scsi/qemu-pr-helper
/vhost-user-scsi
/vhost-user-blk
/vhost-user-gpu
/vhost-user-input
/fsdev/virtfs-proxy-helper
*.tmp
*.[1-9]
@@ -133,7 +131,6 @@
/docs/interop/qemu-qmp-ref.info*
/docs/interop/qemu-qmp-ref.txt
/docs/version.texi
/contrib/vhost-user-gpu/50-qemu-gpu.json
*.tps
.stgit-*
.git-submodule-status
-49
View File
@@ -1,49 +0,0 @@
docker-edk2:
stage: build
rules: # Only run this job when the Dockerfile is modified
- changes:
- .gitlab-ci-edk2.yml
- .gitlab-ci.d/edk2/Dockerfile
when: always
image: docker:19.03.1
services:
- docker:19.03.1-dind
variables:
GIT_DEPTH: 3
IMAGE_TAG: $CI_REGISTRY_IMAGE:edk2-cross-build
# We don't use TLS
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker pull $IMAGE_TAG || true
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--tag $IMAGE_TAG .gitlab-ci.d/edk2
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $IMAGE_TAG
build-edk2:
rules: # Only run this job when ...
- changes: # ... roms/edk2/ is modified (submodule updated)
- roms/edk2/*
when: always
- if: '$CI_COMMIT_REF_NAME =~ /^edk2/' # or the branch/tag starts with 'edk2'
when: always
- if: '$CI_COMMIT_MESSAGE =~ /edk2/i' # or last commit description contains 'EDK2'
when: always
artifacts:
paths: # 'artifacts.zip' will contains the following files:
- pc-bios/edk2*bz2
- pc-bios/edk2-licenses.txt
- edk2-stdout.log
- edk2-stderr.log
image: $CI_REGISTRY_IMAGE:edk2-cross-build
variables:
GIT_DEPTH: 3
script: # Clone the required submodules and build EDK2
- git submodule update --init roms/edk2
- git -C roms/edk2 submodule update --init
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
- echo "=== Using ${JOBS} simultaneous jobs ==="
- make -j${JOBS} -C roms efi 2>&1 1>edk2-stdout.log | tee -a edk2-stderr.log >&2
-63
View File
@@ -1,63 +0,0 @@
docker-opensbi:
stage: build
rules: # Only run this job when the Dockerfile is modified
- changes:
- .gitlab-ci-opensbi.yml
- .gitlab-ci.d/opensbi/Dockerfile
when: always
image: docker:19.03.1
services:
- docker:19.03.1-dind
variables:
GIT_DEPTH: 3
IMAGE_TAG: $CI_REGISTRY_IMAGE:opensbi-cross-build
# We don't use TLS
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker pull $IMAGE_TAG || true
- docker build --cache-from $IMAGE_TAG --tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
--tag $IMAGE_TAG .gitlab-ci.d/opensbi
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $IMAGE_TAG
build-opensbi:
rules: # Only run this job when ...
- changes: # ... roms/opensbi/ is modified (submodule updated)
- roms/opensbi/*
when: always
- if: '$CI_COMMIT_REF_NAME =~ /^opensbi/' # or the branch/tag starts with 'opensbi'
when: always
- if: '$CI_COMMIT_MESSAGE =~ /opensbi/i' # or last commit description contains 'OpenSBI'
when: always
artifacts:
paths: # 'artifacts.zip' will contains the following files:
- pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin
- pc-bios/opensbi-riscv32-virt-fw_jump.bin
- pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
- pc-bios/opensbi-riscv64-virt-fw_jump.bin
- opensbi32-virt-stdout.log
- opensbi32-virt-stderr.log
- opensbi64-virt-stdout.log
- opensbi64-virt-stderr.log
- opensbi32-sifive_u-stdout.log
- opensbi32-sifive_u-stderr.log
- opensbi64-sifive_u-stdout.log
- opensbi64-sifive_u-stderr.log
image: $CI_REGISTRY_IMAGE:opensbi-cross-build
variables:
GIT_DEPTH: 3
script: # Clone the required submodules and build OpenSBI
- git submodule update --init roms/opensbi
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
- echo "=== Using ${JOBS} simultaneous jobs ==="
- make -j${JOBS} -C roms/opensbi clean
- make -j${JOBS} -C roms opensbi32-virt 2>&1 1>opensbi32-virt-stdout.log | tee -a opensbi32-virt-stderr.log >&2
- make -j${JOBS} -C roms/opensbi clean
- make -j${JOBS} -C roms opensbi64-virt 2>&1 1>opensbi64-virt-stdout.log | tee -a opensbi64-virt-stderr.log >&2
- make -j${JOBS} -C roms/opensbi clean
- make -j${JOBS} -C roms opensbi32-sifive_u 2>&1 1>opensbi32-sifive_u-stdout.log | tee -a opensbi32-sifive_u-stderr.log >&2
- make -j${JOBS} -C roms/opensbi clean
- make -j${JOBS} -C roms opensbi64-sifive_u 2>&1 1>opensbi64-sifive_u-stdout.log | tee -a opensbi64-sifive_u-stderr.log >&2
-27
View File
@@ -1,27 +0,0 @@
#
# Docker image to cross-compile EDK2 firmware binaries
#
FROM ubuntu:16.04
MAINTAINER Philippe Mathieu-Daudé <philmd@redhat.com>
# Install packages required to build EDK2
RUN apt update \
&& \
\
DEBIAN_FRONTEND=noninteractive \
apt install --assume-yes --no-install-recommends \
build-essential \
ca-certificates \
dos2unix \
gcc-aarch64-linux-gnu \
gcc-arm-linux-gnueabi \
git \
iasl \
make \
nasm \
python \
uuid-dev \
&& \
\
rm -rf /var/lib/apt/lists/*
-33
View File
@@ -1,33 +0,0 @@
#
# Docker image to cross-compile OpenSBI firmware binaries
#
FROM ubuntu:18.04
MAINTAINER Bin Meng <bmeng.cn@gmail.com>
# Install packages required to build OpenSBI
RUN apt update \
&& \
\
DEBIAN_FRONTEND=noninteractive \
apt install --assume-yes --no-install-recommends \
build-essential \
ca-certificates \
git \
make \
wget \
&& \
\
rm -rf /var/lib/apt/lists/*
# Manually install the kernel.org "Crosstool" based toolchains for gcc-8.3
RUN wget -O - \
https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv32-linux.tar.xz \
| tar -C /opt -xJ
RUN wget -O - \
https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/x86_64/8.3.0/x86_64-gcc-8.3.0-nolibc-riscv64-linux.tar.xz \
| tar -C /opt -xJ
# Export the toolchains to the system path
ENV PATH="/opt/gcc-8.3.0-nolibc/riscv32-linux/bin:${PATH}"
ENV PATH="/opt/gcc-8.3.0-nolibc/riscv64-linux/bin:${PATH}"
+26 -42
View File
@@ -1,7 +1,3 @@
include:
- local: '/.gitlab-ci-edk2.yml'
- local: '/.gitlab-ci-opensbi.yml'
before_script:
- apt-get update -qq
- apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage
@@ -9,10 +5,8 @@ before_script:
build-system1:
script:
- apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev
libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev libvdeplug-dev
- mkdir build
- cd build
- ../configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev
- ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu
cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu
mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu"
- make -j2
@@ -22,10 +16,7 @@ build-system2:
script:
- apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev
libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev
libzstd-dev
- mkdir build
- cd build
- ../configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
- ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu
microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu
sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu"
- make -j2
@@ -33,9 +24,7 @@ build-system2:
build-disabled:
script:
- mkdir build
- cd build
- ../configure --enable-werror --disable-rdma --disable-slirp --disable-curl
- ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl
--disable-capstone --disable-live-block-migration --disable-glusterfs
--disable-replication --disable-coroutine-pool --disable-smartcard
--disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm
@@ -47,38 +36,37 @@ build-disabled:
build-tcg-disabled:
script:
- apt-get install -y -qq clang libgtk-3-dev libusb-dev
- mkdir build
- cd build
- ../configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
- apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev
- ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list=""
- make -j2
- make check-unit
- make check-qapi-schema
- cd tests/qemu-iotests/
- ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048
052 063 077 086 101 104 106 113 148 150 151 152 157 159 160 163
170 171 183 184 192 194 197 208 215 221 222 226 227 236 253 277
- ./check -qcow2 028 051 056 057 058 065 067 068 082 085 091 095 096 102 122
124 132 139 142 144 145 151 152 155 157 165 194 196 197 200 202
208 209 215 216 218 222 227 234 246 247 248 250 254 255 257 258
260 261 262 263 264 270 272 273 277 279
052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160
163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236
- ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019
020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038
039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058
060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082
085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108
110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138
139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158
161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196
197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234
build-user:
script:
- mkdir build
- cd build
- ../configure --enable-werror --disable-system --disable-guest-agent
- ./configure --enable-werror --disable-system --disable-guest-agent
--disable-capstone --disable-slirp --disable-fdt
- make -j2
- make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user
build-clang:
script:
- apt-get install -y -qq clang libsdl2-dev libattr1-dev libcap-ng-dev
- apt-get install -y -qq clang libsdl2-dev
xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev
- mkdir build
- cd build
- ../configure --cc=clang --cxx=clang++ --enable-werror
- ./configure --cc=clang --cxx=clang++ --enable-werror
--target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu
ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user"
- make -j2
@@ -87,18 +75,14 @@ build-clang:
build-tci:
script:
- TARGETS="aarch64 alpha arm hppa m68k microblaze moxie ppc64 s390x x86_64"
- mkdir build
- cd build
- ../configure --enable-tcg-interpreter
- ./configure --enable-tcg-interpreter
--target-list="$(for tg in $TARGETS; do echo -n ${tg}'-softmmu '; done)"
- make -j2
- make run-tcg-tests-x86_64-softmmu
- make tests/qtest/boot-serial-test tests/qtest/cdrom-test tests/qtest/pxe-test
- make tests/boot-serial-test tests/cdrom-test tests/pxe-test
- for tg in $TARGETS ; do
export QTEST_QEMU_BINARY="${tg}-softmmu/qemu-system-${tg}" ;
./tests/qtest/boot-serial-test || exit 1 ;
./tests/qtest/cdrom-test || exit 1 ;
./tests/boot-serial-test || exit 1 ;
./tests/cdrom-test || exit 1 ;
done
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/qtest/pxe-test
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x"
./tests/qtest/pxe-test -m slow
- QTEST_QEMU_BINARY="x86_64-softmmu/qemu-system-x86_64" ./tests/pxe-test
- QTEST_QEMU_BINARY="s390x-softmmu/qemu-system-s390x" ./tests/pxe-test -m slow
+3 -3
View File
@@ -10,6 +10,9 @@
[submodule "roms/openbios"]
path = roms/openbios
url = https://git.qemu.org/git/openbios.git
[submodule "roms/openhackware"]
path = roms/openhackware
url = https://git.qemu.org/git/openhackware.git
[submodule "roms/qemu-palcode"]
path = roms/qemu-palcode
url = https://git.qemu.org/git/qemu-palcode.git
@@ -55,6 +58,3 @@
[submodule "roms/opensbi"]
path = roms/opensbi
url = https://git.qemu.org/git/opensbi.git
[submodule "roms/qboot"]
path = roms/qboot
url = https://github.com/bonzini/qboot
+8 -133
View File
@@ -1,29 +1,23 @@
# This mailmap fixes up author names/addresses.
#
# If you are adding to this file consider if a similar change needs to
# be made to contrib/gitdm/aliases. They are not however completely
# analogous. .mailmap is concerned with fixing up damaged author
# fields where as the gitdm equivalent is more concerned with making
# sure multiple email addresses get mapped onto the same author.
#
# From man git-shortlog the forms are:
#
# Proper Name <commit@email.xx>
# <proper@email.xx> <commit@email.xx>
# Proper Name <proper@email.xx> <commit@email.xx>
# Proper Name <proper@email.xx> Commit Name <commit@email.xx>
#
# The first section translates weird addresses from the original git import
# into proper addresses so that they are counted properly by git shortlog.
Andrzej Zaborowski <balrogg@gmail.com> balrog <balrog@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <anthony@codemonkey.ws> aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
Aurelien Jarno <aurelien@aurel32.net> aurel32 <aurel32@c046a42c-6fe2-441c-8c8c-71466251a162>
Blue Swirl <blauwirbel@gmail.com> blueswir1 <blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162>
Edgar E. Iglesias <edgar.iglesias@gmail.com> edgar_igl <edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162>
Fabrice Bellard <fabrice@bellard.org> bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
Jocelyn Mayer <l_indien@magic.fr> j_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Paul Brook <paul@codesourcery.com> pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162>
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@mips.com>
Aleksandar Markovic <amarkovic@wavecomp.com> <aleksandar.markovic@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
Thiemo Seufer <ths@networkno.de> ths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>
malc <av1474@comtv.ru> malc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>
@@ -38,127 +32,8 @@ Ian McKellar <ianloic@google.com> Ian McKellar via Qemu-devel <qemu-devel@nongnu
Julia Suvorova <jusual@mail.ru> Julia Suvorova via Qemu-devel <qemu-devel@nongnu.org>
Justin Terry (VM) <juterry@microsoft.com> Justin Terry (VM) via Qemu-devel <qemu-devel@nongnu.org>
# Next, replace old addresses by a more recent one.
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@mips.com>
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <aleksandar.markovic@imgtec.com>
Aleksandar Markovic <aleksandar.qemu.devel@gmail.com> <amarkovic@wavecomp.com>
Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> <arikalo@wavecomp.com>
Anthony Liguori <anthony@codemonkey.ws> Anthony Liguori <aliguori@us.ibm.com>
James Hogan <jhogan@kernel.org> <james.hogan@imgtec.com>
Leif Lindholm <leif@nuviainc.com> <leif.lindholm@linaro.org>
Paul Burton <pburton@wavecomp.com> <paul.burton@mips.com>
Paul Burton <pburton@wavecomp.com> <paul.burton@imgtec.com>
Paul Burton <pburton@wavecomp.com> <paul@archlinuxmips.org>
Philippe Mathieu-Daudé <philmd@redhat.com> <f4bug@amsat.org>
Yongbok Kim <yongbok.kim@mips.com> <yongbok.kim@imgtec.com>
# Also list preferred name forms where people have changed their
# git author config, or had utf8/latin1 encoding issues.
Aaron Lindsay <aaron@os.amperecomputing.com>
Alexey Gerasimenko <x1917x@gmail.com>
Alex Ivanov <void@aleksoft.net>
Andreas Färber <afaerber@suse.de>
Bandan Das <bsd@redhat.com>
Benjamin MARSILI <mlspirat42@gmail.com>
Benoît Canet <benoit.canet@gmail.com>
Benoît Canet <benoit.canet@irqsave.net>
Benoît Canet <benoit.canet@nodalink.com>
Boqun Feng <boqun.feng@gmail.com>
Boqun Feng <boqun.feng@intel.com>
Brad Smith <brad@comstyle.com>
Brijesh Singh <brijesh.singh@amd.com>
Brilly Wu <brillywu@viatech.com.cn>
Cédric Vincent <cedric.vincent@st.com>
CheneyLin <linzc@zju.edu.cn>
Chen Gang <chengang@emindsoft.com.cn>
Chen Gang <gang.chen.5i5j@gmail.com>
Chen Gang <gang.chen@sunrus.com.cn>
Chen Wei-Ren <chenwj@iis.sinica.edu.tw>
Christophe Lyon <christophe.lyon@st.com>
Collin L. Walling <walling@linux.ibm.com>
Daniel P. Berrangé <berrange@redhat.com>
Eduardo Otubo <otubo@redhat.com>
Fabrice Desclaux <fabrice.desclaux@cea.fr>
Fernando Luis Vázquez Cao <fernando_b1@lab.ntt.co.jp>
Fernando Luis Vázquez Cao <fernando@oss.ntt.co.jp>
Gautham R. Shenoy <ego@in.ibm.com>
Gautham R. Shenoy <ego@linux.vnet.ibm.com>
Gonglei (Arei) <arei.gonglei@huawei.com>
Guang Wang <wang.guang55@zte.com.cn>
Hailiang Zhang <zhang.zhanghailiang@huawei.com>
Hervé Poussineau <hpoussin@reactos.org>
Jakub Jermář <jakub@jermar.eu>
Jakub Jermář <jakub.jermar@kernkonzept.com>
Jean-Christophe Dubois <jcd@tribudubois.net>
Jindřich Makovička <makovick@gmail.com>
John Arbuckle <programmingkidx@gmail.com>
Juha Riihimäki <juha.riihimaki@nokia.com>
Juha Riihimäki <Juha.Riihimaki@nokia.com>
Jun Li <junmuzi@gmail.com>
Laurent Vivier <Laurent@lvivier.info>
Leandro Lupori <leandro.lupori@gmail.com>
Li Guang <lig.fnst@cn.fujitsu.com>
Liming Wang <walimisdev@gmail.com>
linzhecheng <linzc@zju.edu.cn>
Liran Schour <lirans@il.ibm.com>
Liu Yu <yu.liu@freescale.com>
Liu Yu <Yu.Liu@freescale.com>
Li Zhang <zhlcindy@gmail.com>
Li Zhang <zhlcindy@linux.vnet.ibm.com>
Lluís Vilanova <vilanova@ac.upc.edu>
Lluís Vilanova <xscript@gmx.net>
Longpeng (Mike) <longpeng2@huawei.com>
Luc Michel <luc.michel@git.antfield.fr>
Luc Michel <luc.michel@greensocs.com>
Marc Marí <marc.mari.barcelo@gmail.com>
Marc Marí <markmb@redhat.com>
Michael Avdienko <whitearchey@gmail.com>
Michael S. Tsirkin <mst@redhat.com>
Munkyu Im <munkyu.im@samsung.com>
Nicholas Bellinger <nab@linux-iscsi.org>
Nicholas Thomas <nick@bytemark.co.uk>
Nikunj A Dadhania <nikunj@linux.vnet.ibm.com>
Orit Wasserman <owasserm@redhat.com>
Paolo Bonzini <pbonzini@redhat.com>
Pavel Dovgaluk <dovgaluk@ispras.ru>
Pavel Dovgaluk <pavel.dovgaluk@gmail.com>
Pavel Dovgaluk <Pavel.Dovgaluk@ispras.ru>
Peter Crosthwaite <crosthwaite.peter@gmail.com>
Peter Crosthwaite <peter.crosthwaite@petalogix.com>
Peter Crosthwaite <peter.crosthwaite@xilinx.com>
Prasad J Pandit <pjp@fedoraproject.org>
Prasad J Pandit <ppandit@redhat.com>
Qiao Nuohan <qiaonuohan@cn.fujitsu.com>
Reimar Döffinger <Reimar.Doeffinger@gmx.de>
Remy Noel <remy.noel@blade-group.com>
Roger Pau Monné <roger.pau@citrix.com>
Shin'ichiro Kawasaki <kawasaki@juno.dti.ne.jp>
Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
Sochin Jiang <sochin.jiang@huawei.com>
Takashi Yoshii <takasi-y@ops.dti.ne.jp>
Thomas Huth <thuth@redhat.com>
Thomas Knych <thomaswk@google.com>
Timothy Baldwin <T.E.Baldwin99@members.leeds.ac.uk>
Tony Nguyen <tony.nguyen@bt.com>
Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
Vibi Sreenivasan <vibi_sreenivasan@cms.com>
Vijaya Kumar K <vijayak@cavium.com>
Vijaya Kumar K <Vijaya.Kumar@cavium.com>
Vijay Kumar <vijaykumar@bravegnu.org>
Vijay Kumar <vijaykumar@zilogic.com>
Wang Guang <wang.guang55@zte.com.cn>
Wenchao Xia <xiawenc@linux.vnet.ibm.com>
Wenshuang Ma <kevinnma@tencent.com>
Xiaoqiang Zhao <zxq_yx_007@163.com>
Xinhua Cao <caoxinhua@huawei.com>
Xiong Zhang <xiong.y.zhang@intel.com>
Yin Yin <yin.yin@cs2c.com.cn>
Yu-Chen Lin <npes87184@gmail.com>
Yu-Chen Lin <npes87184@gmail.com> <yuchenlin@synology.com>
YunQiang Su <syq@debian.org>
YunQiang Su <ysu@wavecomp.com>
Yuri Pudgorodskiy <yur@virtuozzo.com>
Zhengui Li <lizhengui@huawei.com>
Zhenwei Pi <pizhenwei@bytedance.com>
Zhenwei Pi <zhenwei.pi@youruncloud.com>
Zhuang Yanying <ann.zhuangyanying@huawei.com>
-20
View File
@@ -1,20 +0,0 @@
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# We want all the document formats
formats: all
# For consistency, we require that QEMU's Sphinx extensions
# run with at least the same minimum version of Python that
# we require for other Python in our codebase (our conf.py
# enforces this, and some code needs it.)
python:
version: 3.5
+1 -3
View File
@@ -35,7 +35,5 @@ build:
options: "-e HOME=/root"
ci:
- unset CC
- mkdir build
- cd build
- ../configure --disable-docs ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
- ./configure ${QEMU_CONFIGURE_OPTS} --target-list=${TARGET_LIST}
- make -j$(($(getconf _NPROCESSORS_ONLN) + 1))
+82 -375
View File
@@ -1,23 +1,11 @@
# The current Travis default is a VM based 16.04 Xenial on GCE
# Additional builds with specific requirements for a full VM need to
# be added as additional matrix: entries later on
os: linux
dist: xenial
language: c
compiler:
- gcc
cache:
# There is one cache per branch and compiler version.
# characteristics of each job are used to identify the cache:
# - OS name (currently, linux, osx, or windows)
# - OS distribution (for Linux, xenial, trusty, or precise)
# - macOS image name (e.g., xcode7.2)
# - Names and values of visible environment variables set in .travis.yml or Settings panel
timeout: 1200
ccache: true
pip: true
directories:
- $HOME/avocado/data/cache
cache: ccache
addons:
@@ -29,7 +17,7 @@ addons:
- libbrlapi-dev
- libcap-ng-dev
- libgcc-4.8-dev
- libgnutls28-dev
- libgnutls-dev
- libgtk-3-dev
- libiscsi-dev
- liblttng-ust-dev
@@ -37,24 +25,25 @@ addons:
- libnfs-dev
- libnss3-dev
- libpixman-1-dev
- libpng-dev
- libpng12-dev
- librados-dev
- libsdl2-dev
- libsdl2-image-dev
- libsdl1.2-dev
- libseccomp-dev
- libspice-protocol-dev
- libspice-server-dev
- libssh-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvdeplug-dev
- libvte-2.91-dev
- libzstd-dev
- sparse
- uuid-dev
- gcovr
# Tests dependencies
- genisoimage
homebrew:
packages:
- glib
- pixman
- gnu-sed
update: true
# The channel name "irc.oftc.net#qemu" is encrypted against qemu/qemu
@@ -70,274 +59,190 @@ notifications:
env:
global:
- SRC_DIR=".."
- BUILD_DIR="build"
- SRC_DIR="."
- BUILD_DIR="."
- BASE_CONFIG="--disable-docs --disable-tools"
- TEST_BUILD_CMD=""
- TEST_CMD="make check V=1"
- TEST_CMD="make check -j3 V=1"
# This is broadly a list of "mainline" softmmu targets which have support across the major distros
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
- CCACHE_SLOPPINESS="include_file_ctime,include_file_mtime"
- CCACHE_MAXSIZE=1G
- G_MESSAGES_DEBUG=error
- MAIN_SOFTMMU_TARGETS="aarch64-softmmu,arm-softmmu,i386-softmmu,mips-softmmu,mips64-softmmu,ppc64-softmmu,riscv64-softmmu,s390x-softmmu,x86_64-softmmu"
git:
# we want to do this ourselves
submodules: false
# Common first phase for all steps
before_install:
- if command -v ccache ; then ccache --zero-stats ; fi
- export JOBS=$(($(getconf _NPROCESSORS_ONLN) + 1))
- echo "=== Using ${JOBS} simultaneous jobs ==="
# Configure step - may be overridden
before_script:
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
# Main build & test - rarely overridden - controlled by TEST_CMD
script:
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
- |
if [ "$BUILD_RC" -eq 0 ] && [ -n "$TEST_BUILD_CMD" ]; then
${TEST_BUILD_CMD} || BUILD_RC=$?
else
$(exit $BUILD_RC);
fi
- |
if [ "$BUILD_RC" -eq 0 ] ; then
${TEST_CMD} ;
else
$(exit $BUILD_RC);
fi
after_script:
- if command -v ccache ; then ccache --show-stats ; fi
- make -j3 && travis_retry ${TEST_CMD}
jobs:
matrix:
include:
- name: "GCC static (user)"
env:
- env:
- CONFIG="--disable-system --static"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
# we split the system builds as it takes a while to build them all
- name: "GCC (main-softmmu)"
env:
- env:
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- name: "GCC (other-softmmu)"
env:
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- env:
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
# Just build tools and run minimal unit and softfloat checks
- name: "GCC check-softfloat (user)"
env:
- env:
- BASE_CONFIG="--enable-tools"
- CONFIG="--disable-user --disable-system"
- TEST_CMD="make check-unit check-softfloat -j${JOBS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- TEST_CMD="make check-unit check-softfloat -j3"
- env:
- CONFIG="--enable-debug --enable-debug-tcg --disable-user"
# --enable-debug implies --enable-debug-tcg, also runs quite a bit slower
- name: "GCC debug (main-softmmu)"
env:
- CONFIG="--enable-debug --target-list=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug"
# TCG debug can be run just on its own and is mostly agnostic to user/softmmu distinctions
- name: "GCC debug (user)"
env:
# TCG debug can be run just on it's own and is mostly agnostic to user/softmmu distinctions
- env:
- CONFIG="--enable-debug-tcg --disable-system"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
- name: "GCC some libs disabled (main-softmmu)"
env:
- env:
- CONFIG="--disable-linux-aio --disable-cap-ng --disable-attr --disable-brlapi --disable-libusb --disable-replication --target-list=${MAIN_SOFTMMU_TARGETS}"
# Module builds are mostly of interest to major distros
- name: "GCC modules (main-softmmu)"
env:
- env:
- CONFIG="--enable-modules --target-list=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
# Alternate coroutines implementations are only really of interest to KVM users
# However we can't test against KVM on Travis so we can only run unit tests
- name: "check-unit coroutine=ucontext"
env:
- env:
- CONFIG="--with-coroutine=ucontext --disable-tcg"
- TEST_CMD="make check-unit -j${JOBS} V=1"
- TEST_CMD="make check-unit -j3 V=1"
- name: "check-unit coroutine=sigaltstack"
env:
- env:
- CONFIG="--with-coroutine=sigaltstack --disable-tcg"
- TEST_CMD="make check-unit -j${JOBS} V=1"
- TEST_CMD="make check-unit -j3 V=1"
# Check we can build docs and tools (out of tree)
- name: "tools and docs (bionic)"
dist: bionic
env:
- env:
- BUILD_DIR="out-of-tree/build/dir" SRC_DIR="../../.."
- BASE_CONFIG="--enable-tools --enable-docs"
- CONFIG="--target-list=x86_64-softmmu,aarch64-linux-user"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
addons:
apt:
packages:
- python3-sphinx
- python-sphinx
- texinfo
- perl
# Test with Clang for compile portability (Travis uses clang-5.0)
- name: "Clang (user)"
env:
- env:
- CONFIG="--disable-system"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
compiler: clang
- name: "Clang (main-softmmu)"
env:
- env:
- CONFIG="--disable-user --target-list=${MAIN_SOFTMMU_TARGETS}"
compiler: clang
- env:
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS} "
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-sanitize"
compiler: clang
before_script:
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
- ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
- ./configure ${CONFIG} --extra-cflags="-fsanitize=undefined -Werror" || { cat config.log && exit 1; }
- name: "Clang (other-softmmu)"
env:
- env:
- CONFIG="--disable-user --target-list-exclude=${MAIN_SOFTMMU_TARGETS}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-clang-default"
compiler: clang
# gprof/gcov are GCC features
- name: "GCC gprof/gcov"
env:
- env:
- CONFIG="--enable-gprof --enable-gcov --disable-pie --target-list=${MAIN_SOFTMMU_TARGETS}"
after_success:
- ${SRC_DIR}/scripts/travis/coverage-summary.sh
# We manually include builds which we disable "make check" for
- name: "GCC without-default-devices (softmmu)"
env:
- env:
- CONFIG="--without-default-devices --disable-user"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
- TEST_CMD=""
# Check the TCG interpreter (TCI)
- name: "GCC TCI"
env:
- CONFIG="--enable-debug-tcg --enable-tcg-interpreter --disable-kvm --disable-containers
--target-list=alpha-softmmu,arm-softmmu,hppa-softmmu,m68k-softmmu,microblaze-softmmu,moxie-softmmu,ppc-softmmu,s390x-softmmu,x86_64-softmmu"
- TEST_CMD="make check-qtest check-tcg V=1"
# We manually include builds which we disable "make check" for
- env:
- CONFIG="--enable-debug --enable-tcg-interpreter"
- TEST_CMD=""
# We don't need to exercise every backend with every front-end
- name: "GCC trace log,simple,syslog (user)"
env:
- env:
- CONFIG="--enable-trace-backends=log,simple,syslog --disable-system"
- TEST_CMD=""
- name: "GCC trace ftrace (x86_64-softmmu)"
env:
- env:
- CONFIG="--enable-trace-backends=ftrace --target-list=x86_64-softmmu"
- TEST_CMD=""
- name: "GCC trace ust (x86_64-softmmu)"
env:
- env:
- CONFIG="--enable-trace-backends=ust --target-list=x86_64-softmmu"
- TEST_CMD=""
# MacOSX builds - cirrus.yml also tests some MacOS builds including latest Xcode
# MacOSX builds
- env:
- CONFIG="--target-list=${MAIN_SOFTMMU_TARGETS}"
os: osx
osx_image: xcode9.4
compiler: clang
- name: "OSX Xcode 10.3"
env:
- BASE_CONFIG="--disable-docs --enable-tools"
- env:
- CONFIG="--target-list=i386-softmmu,ppc-softmmu,ppc64-softmmu,m68k-softmmu,x86_64-softmmu"
os: osx
osx_image: xcode10.3
osx_image: xcode10.2
compiler: clang
addons:
homebrew:
packages:
- ccache
- glib
- pixman
- gnu-sed
- python
update: true
before_script:
- brew link --overwrite python
- export PATH="/usr/local/opt/ccache/libexec:$PATH"
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
- ${SRC_DIR}/configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
# Python builds
- name: "GCC Python 3.5 (x86_64-softmmu)"
env:
- env:
- CONFIG="--target-list=x86_64-softmmu"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
language: python
python: 3.5
python:
- "3.4"
- name: "GCC Python 3.6 (x86_64-softmmu)"
env:
- env:
- CONFIG="--target-list=x86_64-softmmu"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
language: python
python: 3.6
python:
- "3.6"
# Acceptance (Functional) tests
- name: "GCC check-acceptance"
dist: bionic
env:
- CONFIG="--enable-tools --target-list=aarch64-softmmu,alpha-softmmu,arm-softmmu,m68k-softmmu,microblaze-softmmu,mips-softmmu,mips64el-softmmu,nios2-softmmu,or1k-softmmu,ppc-softmmu,ppc64-softmmu,s390x-softmmu,sparc-softmmu,x86_64-softmmu,xtensa-softmmu"
- env:
- CONFIG="--python=/usr/bin/python3 --target-list=x86_64-softmmu,mips-softmmu,mips64el-softmmu,aarch64-softmmu,arm-softmmu,s390x-softmmu,alpha-softmmu"
- TEST_CMD="make check-acceptance"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-acceptance"
after_script:
- python3 -c 'import json; r = json.load(open("tests/results/latest/results.json")); [print(t["logfile"]) for t in r["tests"] if t["status"] not in ("PASS", "SKIP")]' | xargs cat
- du -chs $HOME/avocado/data/cache
after_failure:
- cat tests/results/latest/job.log
addons:
apt:
packages:
- python3-pil
- python3-pip
- python3-numpy
- python3-opencv
- python3-venv
- rpm2cpio
- tesseract-ocr
- tesseract-ocr-eng
- python3.5-venv
# Using newer GCC with sanitizers
- name: "GCC9 with sanitizers (softmmu)"
addons:
- addons:
apt:
update: true
sources:
@@ -362,8 +267,7 @@ jobs:
- libpixman-1-dev
- libpng12-dev
- librados-dev
- libsdl2-dev
- libsdl2-image-dev
- libsdl1.2-dev
- libseccomp-dev
- libspice-protocol-dev
- libspice-server-dev
@@ -380,212 +284,15 @@ jobs:
- CONFIG="--cc=gcc-9 --cxx=g++-9 --disable-pie --disable-linux-user"
- TEST_CMD=""
before_script:
- mkdir -p ${BUILD_DIR} && cd ${BUILD_DIR}
- ${SRC_DIR}/configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread" --extra-ldflags="-fuse-ld=gold" || { cat config.log && exit 1; }
- ./configure ${CONFIG} --extra-cflags="-g3 -O0 -Wno-error=stringop-truncation -fsanitize=thread -fuse-ld=gold" || { cat config.log && exit 1; }
# Run check-tcg against linux-user
- name: "GCC check-tcg (user)"
env:
- CONFIG="--disable-system --enable-debug-tcg"
- TEST_BUILD_CMD="make build-tcg"
- TEST_CMD="make check-tcg"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
# Run check-tcg against linux-user (with plugins)
# we skip sparc64-linux-user until it has been fixed somewhat
# we skip cris-linux-user as it doesn't use the common run loop
- name: "GCC plugins check-tcg (user)"
env:
- CONFIG="--disable-system --enable-plugins --enable-debug-tcg --target-list-exclude=sparc64-linux-user,cris-linux-user"
- TEST_BUILD_CMD="make build-tcg"
- TEST_CMD="make check-tcg"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
- env:
- CONFIG="--disable-system"
- TEST_CMD="make -j3 check-tcg V=1"
# Run check-tcg against softmmu targets
- name: "GCC check-tcg (some-softmmu)"
env:
- CONFIG="--enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
- TEST_BUILD_CMD="make build-tcg"
- TEST_CMD="make check-tcg"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
# Run check-tcg against softmmu targets (with plugins)
- name: "GCC plugins check-tcg (some-softmmu)"
env:
- CONFIG="--enable-plugins --enable-debug-tcg --target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
- TEST_BUILD_CMD="make build-tcg"
- TEST_CMD="make check-tcg"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-debug-tcg"
- name: "[aarch64] GCC check-tcg"
arch: arm64
dist: xenial
addons:
apt_packages:
- libaio-dev
- libattr1-dev
- libbrlapi-dev
- libcap-ng-dev
- libgcrypt20-dev
- libgnutls28-dev
- libgtk-3-dev
- libiscsi-dev
- liblttng-ust-dev
- libncurses5-dev
- libnfs-dev
- libnss3-dev
- libpixman-1-dev
- libpng-dev
- librados-dev
- libsdl2-dev
- libseccomp-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvdeplug-dev
- libvte-2.91-dev
# Tests dependencies
- genisoimage
env:
- TEST_CMD="make check check-tcg V=1"
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS}"
- name: "[ppc64] GCC check-tcg"
arch: ppc64le
dist: xenial
addons:
apt_packages:
- libaio-dev
- libattr1-dev
- libbrlapi-dev
- libcap-ng-dev
- libgcrypt20-dev
- libgnutls28-dev
- libgtk-3-dev
- libiscsi-dev
- liblttng-ust-dev
- libncurses5-dev
- libnfs-dev
- libnss3-dev
- libpixman-1-dev
- libpng-dev
- librados-dev
- libsdl2-dev
- libseccomp-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvdeplug-dev
- libvte-2.91-dev
# Tests dependencies
- genisoimage
env:
- TEST_CMD="make check check-tcg V=1"
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},ppc64le-linux-user"
- name: "[s390x] GCC check-tcg"
arch: s390x
dist: bionic
addons:
apt_packages:
- libaio-dev
- libattr1-dev
- libbrlapi-dev
- libcap-ng-dev
- libgcrypt20-dev
- libgnutls28-dev
- libgtk-3-dev
- libiscsi-dev
- liblttng-ust-dev
- libncurses5-dev
- libnfs-dev
- libnss3-dev
- libpixman-1-dev
- libpng-dev
- librados-dev
- libsdl2-dev
- libseccomp-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvdeplug-dev
- libvte-2.91-dev
# Tests dependencies
- genisoimage
env:
- TEST_CMD="make check check-tcg V=1"
- CONFIG="--disable-containers --target-list=${MAIN_SOFTMMU_TARGETS},s390x-linux-user"
script:
- ( cd ${SRC_DIR} ; git submodule update --init roms/SLOF )
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
- |
if [ "$BUILD_RC" -eq 0 ] ; then
mv pc-bios/s390-ccw/*.img pc-bios/ ;
${TEST_CMD} ;
else
$(exit $BUILD_RC);
fi
- name: "[s390x] GCC check (KVM)"
arch: s390x
dist: bionic
addons:
apt_packages:
- libaio-dev
- libattr1-dev
- libbrlapi-dev
- libcap-ng-dev
- libgcrypt20-dev
- libgnutls28-dev
- libgtk-3-dev
- libiscsi-dev
- liblttng-ust-dev
- libncurses5-dev
- libnfs-dev
- libnss3-dev
- libpixman-1-dev
- libpng-dev
- librados-dev
- libsdl2-dev
- libseccomp-dev
- liburcu-dev
- libusb-1.0-0-dev
- libvdeplug-dev
- libvte-2.91-dev
# Tests dependencies
- genisoimage
env:
- TEST_CMD="make check-unit"
- CONFIG="--disable-containers --disable-tcg --enable-kvm --disable-tools"
script:
- ( cd ${SRC_DIR} ; git submodule update --init roms/SLOF )
- BUILD_RC=0 && make -j${JOBS} || BUILD_RC=$?
- |
if [ "$BUILD_RC" -eq 0 ] ; then
mv pc-bios/s390-ccw/*.img pc-bios/ ;
${TEST_CMD} ;
else
$(exit $BUILD_RC);
fi
# Release builds
# The make-release script expect a QEMU version, so our tag must start with a 'v'.
# This is the case when release candidate tags are created.
- name: "Release tarball"
if: tag IS present AND tag =~ /^v\d+\.\d+(\.\d+)?(-\S*)?$/
env:
# We want to build from the release tarball
- BUILD_DIR="release/build/dir" SRC_DIR="../../.."
- BASE_CONFIG="--prefix=$PWD/dist"
- CONFIG="--target-list=x86_64-softmmu,aarch64-softmmu,armeb-linux-user,ppc-linux-user"
- TEST_CMD="make install -j${JOBS}"
- QEMU_VERSION="${TRAVIS_TAG:1}"
- CACHE_NAME="${TRAVIS_BRANCH}-linux-gcc-default"
script:
- make -C ${SRC_DIR} qemu-${QEMU_VERSION}.tar.bz2
- ls -l ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2
- tar -xf ${SRC_DIR}/qemu-${QEMU_VERSION}.tar.bz2 && cd qemu-${QEMU_VERSION}
- mkdir -p release-build && cd release-build
- ../configure ${BASE_CONFIG} ${CONFIG} || { cat config.log && exit 1; }
- make install
- env:
- CONFIG="--target-list=xtensa-softmmu,arm-softmmu,aarch64-softmmu,alpha-softmmu"
- TEST_CMD="make -j3 check-tcg V=1"
+216
View File
@@ -0,0 +1,216 @@
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.
Crusty old coders who have trouble spotting the glasses on their noses
can tell the difference between a tab and eight spaces from a distance
of approximately fifteen parsecs. Many a flamewar has been fought and
lost on this issue.
QEMU indents are four spaces. Tabs are never used, except in Makefiles
where they have been irreversibly coded into the syntax.
Spaces of course are superior to tabs because:
- You have just one way to specify whitespace, not two. Ambiguity breeds
mistakes.
- The confusion surrounding 'use tabs to indent, spaces to justify' is gone.
- Tab indents push your code to the right, making your screen seriously
unbalanced.
- Tabs will be rendered incorrectly on editors who are misconfigured not
to use tab stops of eight positions.
- Tabs are rendered badly in patches, causing off-by-one errors in almost
every line.
- It is the QEMU coding style.
Do not leave whitespace dangling off the ends of lines.
1.1 Multiline Indent
There are several places where indent is necessary:
- if/else
- while/for
- function definition & call
When breaking up a long line to fit within line width, we need a proper indent
for the following lines.
In case of if/else, while/for, align the secondary lines just after the
opening parenthesis of the first.
For example:
if (a == 1 &&
b == 2) {
while (a == 1 &&
b == 2) {
In case of function, there are several variants:
* 4 spaces indent from the beginning
* align the secondary lines just after the opening parenthesis of the
first
For example:
do_something(x, y,
z);
do_something(x, y,
z);
do_something(x, do_another(y,
z));
2. Line width
Lines should be 80 characters; try not to make them longer.
Sometimes it is hard to do, especially when dealing with QEMU subsystems
that use long function or symbol names. Even in that case, do not make
lines much longer than 80 characters.
Rationale:
- Some people like to tile their 24" screens with a 6x4 matrix of 80x24
xterms and use vi in all of them. The best way to punish them is to
let them keep doing it.
- Code and especially patches is much more readable if limited to a sane
line length. Eighty is traditional.
- The four-space indentation makes the most common excuse ("But look
at all that white space on the left!") moot.
- It is the QEMU coding style.
3. Naming
Variables are lower_case_with_underscores; easy to type and read. Structured
type names are in CamelCase; harder to type but standing out. Enum type
names and function type names should also be in CamelCase. Scalar type
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.
When wrapping standard library functions, use the prefix qemu_ to alert
readers that they are seeing a wrapped version; otherwise avoid this prefix.
4. Block structure
Every indented statement is braced; even if the block contains just one
statement. The opening brace is on the line that contains the control
flow statement that introduces the new block; the closing brace is on the
same line as the else keyword, or on a line by itself if there is no else
keyword. Example:
if (a == 5) {
printf("a was 5.\n");
} else if (a == 6) {
printf("a was 6.\n");
} else {
printf("a was something else entirely.\n");
}
Note that 'else if' is considered a single statement; otherwise a long if/
else if/else if/.../else sequence would need an indent for every else
statement.
An exception is the opening brace for a function; for reasons of tradition
and clarity it comes on a line by itself:
void a_function(void)
{
do_something();
}
Rationale: a consistent (except for functions...) bracing style reduces
ambiguity and avoids needless churn when lines are added or removed.
Furthermore, it is the QEMU coding style.
5. Declarations
Mixed declarations (interleaving statements and declarations within
blocks) are generally not allowed; declarations should be at the beginning
of blocks.
Every now and then, an exception is made for declarations inside a
#ifdef or #ifndef block: if the code looks nicer, such declarations can
be placed at the top of the block even if there are statements above.
On the other hand, however, it's often best to move that #ifdef/#ifndef
block to a separate function altogether.
6. Conditional statements
When comparing a variable for (in)equality with a constant, list the
constant on the right, as in:
if (a == 1) {
/* Reads like: "If a equals 1" */
do_something();
}
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
Besides, good compilers already warn users when '==' is mis-typed as '=',
even when the constant is on the right.
7. Comment style
We use traditional C-style /* */ comments and avoid // comments.
Rationale: The // form is valid in C99, so this is purely a matter of
consistency of style. The checkpatch script will warn you about this.
Multiline comment blocks should have a row of stars on the left,
and the initial /* and terminating */ both on their own lines:
/*
* like
* this
*/
This is the same format required by the Linux kernel coding style.
(Some of the existing comments in the codebase use the GNU Coding
Standards form which does not have stars on the left, or other
variations; avoid these when writing new comments, but don't worry
about converting to the preferred form unless you're editing that
comment anyway.)
Rationale: Consistency, and ease of visually picking out a multiline
comment from the surrounding code.
8. trace-events style
8.1 0x prefix
In trace-events files, use a '0x' prefix to specify hex numbers, as in:
some_trace(unsigned x, uint64_t y) "x 0x%x y 0x" PRIx64
An exception is made for groups of numbers that are hexadecimal by
convention and separated by the symbols '.', '/', ':', or ' ' (such as
PCI bus id):
another_trace(int cssid, int ssid, int dev_num) "bus id: %x.%x.%04x"
However, you can use '0x' for such groups if you want. Anyway, be sure that
it is obvious that numbers are in hex, ex.:
data_dump(uint8_t c1, uint8_t c2, uint8_t c3) "bytes (in hex): %02x %02x %02x"
Rationale: hex numbers are hard to read in logs when there is no 0x prefix,
especially when (occasionally) the representation doesn't contain any letters
and especially in one line with other decimal numbers. Number groups are allowed
to not use '0x' because for some things notations like %x.%x.%x are used not
only in Qemu. Also dumping raw data bytes with '0x' is less readable.
8.2 '#' printf flag
Do not use printf flag '#', like '%#x'.
Rationale: there are two ways to add a '0x' prefix to printed number: '0x%...'
and '%#...'. For consistency the only one way should be used. Arguments for
'0x%' are:
- it is more popular
- '%#' omits the 0x for the value 0 which makes output inconsistent
-641
View File
@@ -1,641 +0,0 @@
=================
QEMU Coding Style
=================
.. contents:: Table of Contents
Please use the script checkpatch.pl in the scripts directory to check
patches before submitting.
Formatting and style
********************
Whitespace
==========
Of course, the most important aspect in any coding style is whitespace.
Crusty old coders who have trouble spotting the glasses on their noses
can tell the difference between a tab and eight spaces from a distance
of approximately fifteen parsecs. Many a flamewar has been fought and
lost on this issue.
QEMU indents are four spaces. Tabs are never used, except in Makefiles
where they have been irreversibly coded into the syntax.
Spaces of course are superior to tabs because:
* You have just one way to specify whitespace, not two. Ambiguity breeds
mistakes.
* The confusion surrounding 'use tabs to indent, spaces to justify' is gone.
* Tab indents push your code to the right, making your screen seriously
unbalanced.
* Tabs will be rendered incorrectly on editors who are misconfigured not
to use tab stops of eight positions.
* Tabs are rendered badly in patches, causing off-by-one errors in almost
every line.
* It is the QEMU coding style.
Do not leave whitespace dangling off the ends of lines.
Multiline Indent
----------------
There are several places where indent is necessary:
* if/else
* while/for
* function definition & call
When breaking up a long line to fit within line width, we need a proper indent
for the following lines.
In case of if/else, while/for, align the secondary lines just after the
opening parenthesis of the first.
For example:
.. code-block:: c
if (a == 1 &&
b == 2) {
while (a == 1 &&
b == 2) {
In case of function, there are several variants:
* 4 spaces indent from the beginning
* align the secondary lines just after the opening parenthesis of the first
For example:
.. code-block:: c
do_something(x, y,
z);
do_something(x, y,
z);
do_something(x, do_another(y,
z));
Line width
==========
Lines should be 80 characters; try not to make them longer.
Sometimes it is hard to do, especially when dealing with QEMU subsystems
that use long function or symbol names. Even in that case, do not make
lines much longer than 80 characters.
Rationale:
* Some people like to tile their 24" screens with a 6x4 matrix of 80x24
xterms and use vi in all of them. The best way to punish them is to
let them keep doing it.
* Code and especially patches is much more readable if limited to a sane
line length. Eighty is traditional.
* The four-space indentation makes the most common excuse ("But look
at all that white space on the left!") moot.
* It is the QEMU coding style.
Naming
======
Variables are lower_case_with_underscores; easy to type and read. Structured
type names are in CamelCase; harder to type but standing out. Enum type
names and function type names should also be in CamelCase. Scalar type
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.
When wrapping standard library functions, use the prefix ``qemu_`` to alert
readers that they are seeing a wrapped version; otherwise avoid this prefix.
Block structure
===============
Every indented statement is braced; even if the block contains just one
statement. The opening brace is on the line that contains the control
flow statement that introduces the new block; the closing brace is on the
same line as the else keyword, or on a line by itself if there is no else
keyword. Example:
.. code-block:: c
if (a == 5) {
printf("a was 5.\n");
} else if (a == 6) {
printf("a was 6.\n");
} else {
printf("a was something else entirely.\n");
}
Note that 'else if' is considered a single statement; otherwise a long if/
else if/else if/.../else sequence would need an indent for every else
statement.
An exception is the opening brace for a function; for reasons of tradition
and clarity it comes on a line by itself:
.. code-block:: c
void a_function(void)
{
do_something();
}
Rationale: a consistent (except for functions...) bracing style reduces
ambiguity and avoids needless churn when lines are added or removed.
Furthermore, it is the QEMU coding style.
Declarations
============
Mixed declarations (interleaving statements and declarations within
blocks) are generally not allowed; declarations should be at the beginning
of blocks.
Every now and then, an exception is made for declarations inside a
#ifdef or #ifndef block: if the code looks nicer, such declarations can
be placed at the top of the block even if there are statements above.
On the other hand, however, it's often best to move that #ifdef/#ifndef
block to a separate function altogether.
Conditional statements
======================
When comparing a variable for (in)equality with a constant, list the
constant on the right, as in:
.. code-block:: c
if (a == 1) {
/* Reads like: "If a equals 1" */
do_something();
}
Rationale: Yoda conditions (as in 'if (1 == a)') are awkward to read.
Besides, good compilers already warn users when '==' is mis-typed as '=',
even when the constant is on the right.
Comment style
=============
We use traditional C-style /``*`` ``*``/ comments and avoid // comments.
Rationale: The // form is valid in C99, so this is purely a matter of
consistency of style. The checkpatch script will warn you about this.
Multiline comment blocks should have a row of stars on the left,
and the initial /``*`` and terminating ``*``/ both on their own lines:
.. code-block:: c
/*
* like
* this
*/
This is the same format required by the Linux kernel coding style.
(Some of the existing comments in the codebase use the GNU Coding
Standards form which does not have stars on the left, or other
variations; avoid these when writing new comments, but don't worry
about converting to the preferred form unless you're editing that
comment anyway.)
Rationale: Consistency, and ease of visually picking out a multiline
comment from the surrounding code.
Language usage
**************
Preprocessor
============
Variadic macros
---------------
For variadic macros, stick with this C99-like syntax:
.. code-block:: c
#define DPRINTF(fmt, ...) \
do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0)
Include directives
------------------
Order include directives as follows:
.. code-block:: c
#include "qemu/osdep.h" /* Always first... */
#include <...> /* then system headers... */
#include "..." /* and finally QEMU headers. */
The "qemu/osdep.h" header contains preprocessor macros that affect the behavior
of core system headers like <stdint.h>. It must be the first include so that
core system headers included by external libraries get the preprocessor macros
that QEMU depends on.
Do not include "qemu/osdep.h" from header files since the .c file will have
already included it.
C types
=======
It should be common sense to use the right type, but we have collected
a few useful guidelines here.
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 hwaddr 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.
For CPU virtual addresses there are several possible types.
vaddr is the best type to use to hold a CPU virtual address in
target-independent code. It is guaranteed to be large enough to hold a
virtual address for any target, and it does not change size from target
to target. It is always unsigned.
target_ulong is a type the size of a virtual address on the CPU; this means
it may be 32 or 64 bits depending on which target is being built. It should
therefore be used only in target-specific code, and in some
performance-critical built-per-target core code such as the TLB code.
There is also a signed version, target_long.
abi_ulong is for the ``*``-user targets, and represents a type the size of
'void ``*``' in that target's ABI. (This may not be the same as the size of a
full CPU virtual address in the case of target ABIs which use 32 bit pointers
on 64 bit CPUs, like sparc32plus.) Definitions of structures that must match
the target's ABI must use this type for anything that on the target is defined
to be an 'unsigned long' or a pointer type.
There is also a signed version, abi_long.
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.
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.
Typedefs
--------
Typedefs are used to eliminate the redundant 'struct' keyword, since type
names have a different style than other identifiers ("CamelCase" versus
"snake_case"). Each named struct type should have a CamelCase name and a
corresponding typedef.
Since certain C compilers choke on duplicated typedefs, you should avoid
them and declare a typedef only in one header file. For common types,
you can use "include/qemu/typedefs.h" for example. However, as a matter
of convenience it is also perfectly fine to use forward struct
definitions instead of typedefs in headers and function prototypes; this
avoids problems with duplicated typedefs and reduces the need to include
headers from other headers.
Reserved namespaces in C and POSIX
----------------------------------
Underscore capital, double underscore, and underscore 't' suffixes should be
avoided.
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 GLib memory allocation routines g_malloc/g_malloc0/g_new/
g_new0/g_realloc/g_free or QEMU's qemu_memalign/qemu_blockalign/qemu_vfree
APIs.
Please note that g_malloc will exit on allocation failure, so there
is no need to test for failure (as you would have to with malloc).
Calling g_malloc with a zero size is valid and will return NULL.
Prefer g_new(T, n) instead of g_malloc(sizeof(T) ``*`` n) for the following
reasons:
* It catches multiplication overflowing size_t;
* It returns T ``*`` instead of void ``*``, letting compiler catch more type errors.
Declarations like
.. code-block:: c
T *v = g_malloc(sizeof(*v))
are acceptable, though.
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
qemu_vfree, since breaking this will cause problems on Win32.
String manipulation
===================
Do not use the strncpy function. As mentioned in the man page, it does *not*
guarantee a NULL-terminated buffer, which makes it extremely dangerous to use.
It also zeros trailing destination bytes out to the specified length. Instead,
use this similar function when possible, but note its different signature:
.. code-block:: c
void pstrcpy(char *dest, int dest_buf_size, const char *src)
Don't use strcat because it can't check for buffer overflows, but:
.. code-block:: c
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:
.. code-block:: c
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 g_strdup/g_strndup
instead of plain strdup/strndup.
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.
C standard, implementation defined and undefined behaviors
==========================================================
C code in QEMU should be written to the C99 language specification. A copy
of the final version of the C99 standard with corrigenda TC1, TC2, and TC3
included, formatted as a draft, can be downloaded from:
`<http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf>`_
The C language specification defines regions of undefined behavior and
implementation defined behavior (to give compiler authors enough leeway to
produce better code). In general, code in QEMU should follow the language
specification and avoid both undefined and implementation defined
constructs. ("It works fine on the gcc I tested it with" is not a valid
argument...) However there are a few areas where we allow ourselves to
assume certain behaviors because in practice all the platforms we care about
behave in the same way and writing strictly conformant code would be
painful. These are:
* you may assume that integers are 2s complement representation
* you may assume that right shift of a signed integer duplicates
the sign bit (ie it is an arithmetic shift, not a logical shift)
In addition, QEMU assumes that the compiler does not use the latitude
given in C99 and C11 to treat aspects of signed '<<' as undefined, as
documented in the GNU Compiler Collection manual starting at version 4.0.
Automatic memory deallocation
=============================
QEMU has a mandatory dependency either the GCC or CLang compiler. As
such it has the freedom to make use of a C language extension for
automatically running a cleanup function when a stack variable goes
out of scope. This can be used to simplify function cleanup paths,
often allowing many goto jumps to be eliminated, through automatic
free'ing of memory.
The GLib2 library provides a number of functions/macros for enabling
automatic cleanup:
`<https://developer.gnome.org/glib/stable/glib-Miscellaneous-Macros.html>`_
Most notably:
* g_autofree - will invoke g_free() on the variable going out of scope
* g_autoptr - for structs / objects, will invoke the cleanup func created
by a previous use of G_DEFINE_AUTOPTR_CLEANUP_FUNC. This is
supported for most GLib data types and GObjects
For example, instead of
.. code-block:: c
int somefunc(void) {
int ret = -1;
char *foo = g_strdup_printf("foo%", "wibble");
GList *bar = .....
if (eek) {
goto cleanup;
}
ret = 0;
cleanup:
g_free(foo);
g_list_free(bar);
return ret;
}
Using g_autofree/g_autoptr enables the code to be written as:
.. code-block:: c
int somefunc(void) {
g_autofree char *foo = g_strdup_printf("foo%", "wibble");
g_autoptr (GList) bar = .....
if (eek) {
return -1;
}
return 0;
}
While this generally results in simpler, less leak-prone code, there
are still some caveats to beware of
* Variables declared with g_auto* MUST always be initialized,
otherwise the cleanup function will use uninitialized stack memory
* If a variable declared with g_auto* holds a value which must
live beyond the life of the function, that value must be saved
and the original variable NULL'd out. This can be simpler using
g_steal_pointer
.. code-block:: c
char *somefunc(void) {
g_autofree char *foo = g_strdup_printf("foo%", "wibble");
g_autoptr (GList) bar = .....
if (eek) {
return NULL;
}
return g_steal_pointer(&foo);
}
QEMU Specific Idioms
********************
Error handling and reporting
============================
Reporting errors to the human user
----------------------------------
Do not use printf(), fprintf() or monitor_printf(). Instead, use
error_report() or error_vreport() from error-report.h. This ensures the
error is reported in the right place (current monitor or stderr), and in
a uniform format.
Use error_printf() & friends to print additional information.
error_report() prints the current location. In certain common cases
like command line parsing, the current location is tracked
automatically. To manipulate it manually, use the loc_``*``() from
error-report.h.
Propagating errors
------------------
An error can't always be reported to the user right where it's detected,
but often needs to be propagated up the call chain to a place that can
handle it. This can be done in various ways.
The most flexible one is Error objects. See error.h for usage
information.
Use the simplest suitable method to communicate success / failure to
callers. Stick to common methods: non-negative on success / -1 on
error, non-negative / -errno, non-null / null, or Error objects.
Example: when a function returns a non-null pointer on success, and it
can fail only in one way (as far as the caller is concerned), returning
null on failure is just fine, and certainly simpler and a lot easier on
the eyes than propagating an Error object through an Error ``*````*`` parameter.
Example: when a function's callers need to report details on failure
only the function really knows, use Error ``*````*``, and set suitable errors.
Do not report an error to the user when you're also returning an error
for somebody else to handle. Leave the reporting to the place that
consumes the error returned.
Handling errors
---------------
Calling exit() is fine when handling configuration errors during
startup. It's problematic during normal operation. In particular,
monitor commands should never exit().
Do not call exit() or abort() to handle an error that can be triggered
by the guest (e.g., some unimplemented corner case in guest code
translation or device emulation). Guests should not be able to
terminate QEMU.
Note that &error_fatal is just another way to exit(1), and &error_abort
is just another way to abort().
trace-events style
==================
0x prefix
---------
In trace-events files, use a '0x' prefix to specify hex numbers, as in:
.. code-block::
some_trace(unsigned x, uint64_t y) "x 0x%x y 0x" PRIx64
An exception is made for groups of numbers that are hexadecimal by
convention and separated by the symbols '.', '/', ':', or ' ' (such as
PCI bus id):
.. code-block::
another_trace(int cssid, int ssid, int dev_num) "bus id: %x.%x.%04x"
However, you can use '0x' for such groups if you want. Anyway, be sure that
it is obvious that numbers are in hex, ex.:
.. code-block::
data_dump(uint8_t c1, uint8_t c2, uint8_t c3) "bytes (in hex): %02x %02x %02x"
Rationale: hex numbers are hard to read in logs when there is no 0x prefix,
especially when (occasionally) the representation doesn't contain any letters
and especially in one line with other decimal numbers. Number groups are allowed
to not use '0x' because for some things notations like %x.%x.%x are used not
only in Qemu. Also dumping raw data bytes with '0x' is less readable.
'#' printf flag
---------------
Do not use printf flag '#', like '%#x'.
Rationale: there are two ways to add a '0x' prefix to printed number: '0x%...'
and '%#...'. For consistency the only one way should be used. Arguments for
'0x%' are:
* it is more popular
* '%#' omits the 0x for the value 0 which makes output inconsistent
+245
View File
@@ -0,0 +1,245 @@
1. Preprocessor
1.1. Variadic macros
For variadic macros, stick with this C99-like syntax:
#define DPRINTF(fmt, ...) \
do { printf("IRQ: " fmt, ## __VA_ARGS__); } while (0)
1.2. Include directives
Order include directives as follows:
#include "qemu/osdep.h" /* Always first... */
#include <...> /* then system headers... */
#include "..." /* and finally QEMU headers. */
The "qemu/osdep.h" header contains preprocessor macros that affect the behavior
of core system headers like <stdint.h>. It must be the first include so that
core system headers included by external libraries get the preprocessor macros
that QEMU depends on.
Do not include "qemu/osdep.h" from header files since the .c file will have
already included it.
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 hwaddr 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.
For CPU virtual addresses there are several possible types.
vaddr is the best type to use to hold a CPU virtual address in
target-independent code. It is guaranteed to be large enough to hold a
virtual address for any target, and it does not change size from target
to target. It is always unsigned.
target_ulong is a type the size of a virtual address on the CPU; this means
it may be 32 or 64 bits depending on which target is being built. It should
therefore be used only in target-specific code, and in some
performance-critical built-per-target core code such as the TLB code.
There is also a signed version, target_long.
abi_ulong is for the *-user targets, and represents a type the size of
'void *' in that target's ABI. (This may not be the same as the size of a
full CPU virtual address in the case of target ABIs which use 32 bit pointers
on 64 bit CPUs, like sparc32plus.) Definitions of structures that must match
the target's ABI must use this type for anything that on the target is defined
to be an 'unsigned long' or a pointer type.
There is also a signed version, abi_long.
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 GLib memory allocation routines g_malloc/g_malloc0/g_new/
g_new0/g_realloc/g_free or QEMU's qemu_memalign/qemu_blockalign/qemu_vfree
APIs.
Please note that g_malloc will exit on allocation failure, so there
is no need to test for failure (as you would have to with malloc).
Calling g_malloc with a zero size is valid and will return NULL.
Prefer g_new(T, n) instead of g_malloc(sizeof(T) * n) for the following
reasons:
a. It catches multiplication overflowing size_t;
b. It returns T * instead of void *, letting compiler catch more type
errors.
Declarations like T *v = g_malloc(sizeof(*v)) are acceptable, though.
Memory allocated by qemu_memalign or qemu_blockalign must be freed with
qemu_vfree, since breaking this will cause problems on Win32.
4. String manipulation
Do not use the strncpy function. As mentioned in the man page, it does *not*
guarantee a NULL-terminated buffer, which makes it extremely dangerous to use.
It also zeros trailing destination bytes out to the specified length. Instead,
use this similar function when possible, but note its different signature:
void pstrcpy(char *dest, int dest_buf_size, const char *src)
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 g_strdup/g_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.
6. C standard, implementation defined and undefined behaviors
C code in QEMU should be written to the C99 language specification. A copy
of the final version of the C99 standard with corrigenda TC1, TC2, and TC3
included, formatted as a draft, can be downloaded from:
http://www.open-std.org/jtc1/sc22/WG14/www/docs/n1256.pdf
The C language specification defines regions of undefined behavior and
implementation defined behavior (to give compiler authors enough leeway to
produce better code). In general, code in QEMU should follow the language
specification and avoid both undefined and implementation defined
constructs. ("It works fine on the gcc I tested it with" is not a valid
argument...) However there are a few areas where we allow ourselves to
assume certain behaviors because in practice all the platforms we care about
behave in the same way and writing strictly conformant code would be
painful. These are:
* you may assume that integers are 2s complement representation
* you may assume that right shift of a signed integer duplicates
the sign bit (ie it is an arithmetic shift, not a logical shift)
In addition, QEMU assumes that the compiler does not use the latitude
given in C99 and C11 to treat aspects of signed '<<' as undefined, as
documented in the GNU Compiler Collection manual starting at version 4.0.
7. Error handling and reporting
7.1 Reporting errors to the human user
Do not use printf(), fprintf() or monitor_printf(). Instead, use
error_report() or error_vreport() from error-report.h. This ensures the
error is reported in the right place (current monitor or stderr), and in
a uniform format.
Use error_printf() & friends to print additional information.
error_report() prints the current location. In certain common cases
like command line parsing, the current location is tracked
automatically. To manipulate it manually, use the loc_*() from
error-report.h.
7.2 Propagating errors
An error can't always be reported to the user right where it's detected,
but often needs to be propagated up the call chain to a place that can
handle it. This can be done in various ways.
The most flexible one is Error objects. See error.h for usage
information.
Use the simplest suitable method to communicate success / failure to
callers. Stick to common methods: non-negative on success / -1 on
error, non-negative / -errno, non-null / null, or Error objects.
Example: when a function returns a non-null pointer on success, and it
can fail only in one way (as far as the caller is concerned), returning
null on failure is just fine, and certainly simpler and a lot easier on
the eyes than propagating an Error object through an Error ** parameter.
Example: when a function's callers need to report details on failure
only the function really knows, use Error **, and set suitable errors.
Do not report an error to the user when you're also returning an error
for somebody else to handle. Leave the reporting to the place that
consumes the error returned.
7.3 Handling errors
Calling exit() is fine when handling configuration errors during
startup. It's problematic during normal operation. In particular,
monitor commands should never exit().
Do not call exit() or abort() to handle an error that can be triggered
by the guest (e.g., some unimplemented corner case in guest code
translation or device emulation). Guests should not be able to
terminate QEMU.
Note that &error_fatal is just another way to exit(1), and &error_abort
is just another way to abort().
-6
View File
@@ -25,15 +25,9 @@ config TPM
config VHOST_USER
bool
select VHOST
config VHOST_KERNEL
bool
select VHOST
config XEN
bool
select FSDEV_9P if VIRTFS
config VIRTFS
bool
+2 -3
View File
@@ -18,9 +18,8 @@ As of July 2013, contributions under version 2 of the GNU General Public
License (and no later version) are only accepted for the following files
or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*.
3) The Tiny Code Generator (TCG) is mostly under the BSD or MIT licenses;
but some parts may be GPLv2 or other licenses. Again, see the
specific licensing information in each source file.
3) The Tiny Code Generator (TCG) is released under the BSD license
(see license headers in files).
4) QEMU is a trademark of Fabrice Bellard.
+112 -367
View File
File diff suppressed because it is too large. Load diff
+121 -232
View File
@@ -15,6 +15,9 @@ UNCHECKED_GOALS := %clean TAGS cscope ctags dist \
help check-help print-% \
docker docker-% vm-help vm-test vm-build-%
print-%:
@echo '$*=$($*)'
# All following code might depend on configuration variables
ifneq ($(wildcard config-host.mak),)
# Put the all: rule here so that config-host.mak can contain dependencies.
@@ -71,12 +74,6 @@ CONFIG_ALL=y
config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION
@echo $@ is out-of-date, running configure
@./config.status
# Force configure to re-run if the API symbols are updated
ifeq ($(CONFIG_PLUGIN),y)
config-host.mak: $(SRC_PATH)/plugins/qemu-plugins.symbols
endif
else
config-host.mak:
ifneq ($(filter-out $(UNCHECKED_GOALS),$(MAKECMDGOALS)),$(if $(MAKECMDGOALS),,fail))
@@ -87,7 +84,8 @@ endif
include $(SRC_PATH)/rules.mak
# lor is defined in rules.mak
# notempy and lor are defined in rules.mak
CONFIG_TOOLS := $(call notempty,$(TOOLS))
CONFIG_BLOCK := $(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS))
# Create QEMU_PKGVERSION and FULL_VERSION strings
@@ -114,7 +112,6 @@ GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c
GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h)
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c)
GENERATED_QAPI_FILES += qapi/qapi-init-commands.h qapi/qapi-init-commands.c
GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h)
GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c)
@@ -125,28 +122,7 @@ GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c)
GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h
GENERATED_QAPI_FILES += qapi/qapi-doc.texi
# The following list considers only the storage daemon main module. All other
# modules are currently shared with the main schema, so we don't actually
# generate additional files.
GENERATED_STORAGE_DAEMON_QAPI_FILES = storage-daemon/qapi/qapi-commands.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-commands.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-emit-events.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-events.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-init-commands.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-introspect.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-types.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.h
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-visit.c
GENERATED_STORAGE_DAEMON_QAPI_FILES += storage-daemon/qapi/qapi-doc.texi
generated-files-y += $(GENERATED_QAPI_FILES)
generated-files-y += $(GENERATED_STORAGE_DAEMON_QAPI_FILES)
generated-files-y += trace/generated-tcg-tracers.h
@@ -340,45 +316,26 @@ HELPERS-y =
HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = qemu-bridge-helper$(EXESUF)
ifeq ($(CONFIG_LINUX)$(CONFIG_VIRGL)$(CONFIG_GBM)$(CONFIG_TOOLS),yyyy)
ifdef CONFIG_LINUX
ifdef CONFIG_VIRGL
ifdef CONFIG_GBM
HELPERS-y += vhost-user-gpu$(EXESUF)
vhost-user-json-y += contrib/vhost-user-gpu/50-qemu-gpu.json
endif
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
HELPERS-y += virtiofsd$(EXESUF)
vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json
endif
# Sphinx does not allow building manuals into the same directory as
# the source files, so if we're doing an in-tree QEMU build we must
# build the manuals into a subdirectory (and then install them from
# there for 'make install'). For an out-of-tree build we can just
# use the docs/ subdirectory in the build tree as normal.
ifeq ($(realpath $(SRC_PATH)),$(realpath .))
MANUAL_BUILDDIR := docs/built
else
MANUAL_BUILDDIR := docs
endif
ifdef BUILD_DOCS
DOCS+=$(MANUAL_BUILDDIR)/system/qemu.1
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-img.1
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-nbd.8
DOCS+=$(MANUAL_BUILDDIR)/interop/qemu-ga.8
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
DOCS+=$(MANUAL_BUILDDIR)/tools/virtiofsd.1
endif
DOCS+=$(MANUAL_BUILDDIR)/system/qemu-block-drivers.7
DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8
DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7
DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7
DOCS+=$(MANUAL_BUILDDIR)/system/qemu-cpu-models.7
DOCS+=$(MANUAL_BUILDDIR)/index.html
DOCS+=docs/qemu-block-drivers.7
DOCS+=docs/qemu-cpu-models.7
ifdef CONFIG_VIRTFS
DOCS+=$(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1
DOCS+=fsdev/virtfs-proxy-helper.1
endif
ifdef CONFIG_TRACE_SYSTEMTAP
DOCS+=$(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1
DOCS+=scripts/qemu-trace-stap.1
endif
else
DOCS=
@@ -413,16 +370,14 @@ MINIKCONF_ARGS = \
CONFIG_OPENGL=$(CONFIG_OPENGL) \
CONFIG_X11=$(CONFIG_X11) \
CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \
CONFIG_VHOST_KERNEL=$(CONFIG_VHOST_KERNEL) \
CONFIG_VIRTFS=$(CONFIG_VIRTFS) \
CONFIG_LINUX=$(CONFIG_LINUX) \
CONFIG_PVRDMA=$(CONFIG_PVRDMA)
MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig
MINIKCONF_DEPS = $(MINIKCONF_INPUTS) $(wildcard $(SRC_PATH)/hw/*/Kconfig)
MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_DEPS) $(BUILD_DIR)/config-host.mak
$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak
$(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp")
$(call quiet-command, if test -f $@; then \
if cmp -s $@.old $@; then \
@@ -458,7 +413,6 @@ dummy := $(call unnest-vars,, \
elf2dmp-obj-y \
ivshmem-client-obj-y \
ivshmem-server-obj-y \
virtiofsd-obj-y \
rdmacm-mux-obj-y \
libvhost-user-obj-y \
vhost-user-scsi-obj-y \
@@ -468,13 +422,16 @@ dummy := $(call unnest-vars,, \
qga-vss-dll-obj-y \
block-obj-y \
block-obj-m \
storage-daemon-obj-y \
storage-daemon-obj-m \
crypto-obj-y \
crypto-user-obj-y \
qom-obj-y \
io-obj-y \
common-obj-y \
common-obj-m \
ui-obj-y \
ui-obj-m \
audio-obj-y \
audio-obj-m \
trace-obj-y)
include $(SRC_PATH)/tests/Makefile.include
@@ -497,28 +454,16 @@ config-host.h-timestamp: config-host.mak
qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -h < $< > $@,"GEN","$@")
TARGET_DIRS_RULES := $(foreach t, all fuzz clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
TARGET_DIRS_RULES := $(foreach t, all clean install, $(addsuffix /$(t), $(TARGET_DIRS)))
SOFTMMU_ALL_RULES=$(filter %-softmmu/all, $(TARGET_DIRS_RULES))
$(SOFTMMU_ALL_RULES): $(authz-obj-y)
$(SOFTMMU_ALL_RULES): $(block-obj-y)
$(SOFTMMU_ALL_RULES): $(storage-daemon-obj-y)
$(SOFTMMU_ALL_RULES): $(chardev-obj-y)
$(SOFTMMU_ALL_RULES): $(crypto-obj-y)
$(SOFTMMU_ALL_RULES): $(io-obj-y)
$(SOFTMMU_ALL_RULES): config-all-devices.mak
ifdef DECOMPRESS_EDK2_BLOBS
$(SOFTMMU_ALL_RULES): $(edk2-decompressed)
endif
SOFTMMU_FUZZ_RULES=$(filter %-softmmu/fuzz, $(TARGET_DIRS_RULES))
$(SOFTMMU_FUZZ_RULES): $(authz-obj-y)
$(SOFTMMU_FUZZ_RULES): $(block-obj-y)
$(SOFTMMU_FUZZ_RULES): $(chardev-obj-y)
$(SOFTMMU_FUZZ_RULES): $(crypto-obj-y)
$(SOFTMMU_FUZZ_RULES): $(io-obj-y)
$(SOFTMMU_FUZZ_RULES): config-all-devices.mak
$(SOFTMMU_FUZZ_RULES): $(edk2-decompressed)
.PHONY: $(TARGET_DIRS_RULES)
# The $(TARGET_DIRS_RULES) are of the form SUBDIR/GOAL, so that
@@ -532,7 +477,7 @@ DTC_CPPFLAGS=-I$(BUILD_DIR)/dtc -I$(SRC_PATH)/dtc -I$(SRC_PATH)/dtc/libfdt
.PHONY: dtc/all
dtc/all: .git-submodule-status dtc/libfdt dtc/tests
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
$(call quiet-command,$(MAKE) $(DTC_MAKE_ARGS) CPPFLAGS="$(DTC_CPPFLAGS)" CFLAGS="$(DTC_CFLAGS)" LDFLAGS="$(LDFLAGS)" ARFLAGS="$(ARFLAGS)" CC="$(CC)" AR="$(AR)" LD="$(LD)" $(SUBDIR_MAKEFLAGS) libfdt/libfdt.a,)
dtc/%: .git-submodule-status
@mkdir -p $@
@@ -555,11 +500,7 @@ capstone/all: .git-submodule-status
.PHONY: slirp/all
slirp/all: .git-submodule-status
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp \
BUILD_DIR="$(BUILD_DIR)/slirp" \
PKG_CONFIG="$(PKG_CONFIG)" \
CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" \
CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(QEMU_LDFLAGS)")
$(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp BUILD_DIR="$(BUILD_DIR)/slirp" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(QEMU_CFLAGS) $(CFLAGS)" LDFLAGS="$(LDFLAGS)")
# Compatibility gunk to keep make working across the rename of targets
# for recursion, to be removed some time after 4.1.
@@ -568,9 +509,6 @@ subdir-capstone: capstone/all
subdir-slirp: slirp/all
$(filter %/all, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
$(qom-obj-y)
$(filter %/fuzz, $(TARGET_DIRS_RULES)): libqemuutil.a $(common-obj-y) \
$(qom-obj-y) $(crypto-user-obj-$(CONFIG_USER_ONLY))
ROM_DIRS = $(addprefix pc-bios/, $(ROMS))
@@ -582,7 +520,6 @@ $(ROM_DIRS_RULES):
.PHONY: recurse-all recurse-clean recurse-install
recurse-all: $(addsuffix /all, $(TARGET_DIRS) $(ROM_DIRS))
recurse-fuzz: $(addsuffix /fuzz, $(TARGET_DIRS) $(ROM_DIRS))
recurse-clean: $(addsuffix /clean, $(TARGET_DIRS) $(ROM_DIRS))
recurse-install: $(addsuffix /install, $(TARGET_DIRS))
$(addsuffix /install, $(TARGET_DIRS)): all
@@ -607,7 +544,6 @@ qemu-img.o: qemu-img-cmds.h
qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
qemu-storage-daemon$(EXESUF): qemu-storage-daemon.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(chardev-obj-y) $(io-obj-y) $(qom-obj-y) $(storage-daemon-obj-y) $(COMMON_LDADDS)
qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS)
@@ -616,6 +552,7 @@ qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS)
qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS)
fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS)
fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap
scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS)
ifdef CONFIG_MPATH
@@ -631,26 +568,18 @@ qemu-ga$(EXESUF): QEMU_CFLAGS += -I qga/qapi-generated
qemu-keymap$(EXESUF): LIBS += $(XKBCOMMON_LIBS)
qemu-keymap$(EXESUF): QEMU_CFLAGS += $(XKBCOMMON_CFLAGS)
qapi-py = $(SRC_PATH)/scripts/qapi/__init__.py \
$(SRC_PATH)/scripts/qapi/commands.py \
$(SRC_PATH)/scripts/qapi/common.py \
$(SRC_PATH)/scripts/qapi/doc.py \
$(SRC_PATH)/scripts/qapi/error.py \
qapi-py = $(SRC_PATH)/scripts/qapi/commands.py \
$(SRC_PATH)/scripts/qapi/events.py \
$(SRC_PATH)/scripts/qapi/expr.py \
$(SRC_PATH)/scripts/qapi/gen.py \
$(SRC_PATH)/scripts/qapi/introspect.py \
$(SRC_PATH)/scripts/qapi/parser.py \
$(SRC_PATH)/scripts/qapi/schema.py \
$(SRC_PATH)/scripts/qapi/source.py \
$(SRC_PATH)/scripts/qapi/types.py \
$(SRC_PATH)/scripts/qapi/visit.py \
$(SRC_PATH)/scripts/qapi/common.py \
$(SRC_PATH)/scripts/qapi/doc.py \
$(SRC_PATH)/scripts/qapi-gen.py
qga/qapi-generated/qga-qapi-types.c qga/qapi-generated/qga-qapi-types.h \
qga/qapi-generated/qga-qapi-visit.c qga/qapi-generated/qga-qapi-visit.h \
qga/qapi-generated/qga-qapi-commands.h qga/qapi-generated/qga-qapi-commands.c \
qga/qapi-generated/qga-qapi-init-commands.h qga/qapi-generated/qga-qapi-init-commands.c \
qga/qapi-generated/qga-qapi-doc.texi: \
qga/qapi-generated/qapi-gen-timestamp ;
qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-py)
@@ -669,18 +598,7 @@ qapi-gen-timestamp: $(qapi-modules) $(qapi-py)
"GEN","$(@:%-timestamp=%)")
@>$@
qapi-modules-storage-daemon = \
$(SRC_PATH)/storage-daemon/qapi/qapi-schema.json \
$(QAPI_MODULES_STORAGE_DAEMON:%=$(SRC_PATH)/qapi/%.json)
$(GENERATED_STORAGE_DAEMON_QAPI_FILES): storage-daemon/qapi/qapi-gen-timestamp ;
storage-daemon/qapi/qapi-gen-timestamp: $(qapi-modules-storage-daemon) $(qapi-py)
$(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \
-o "storage-daemon/qapi" $<, \
"GEN","$(@:%-timestamp=%)")
@>$@
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h qga-qapi-init-commands.h)
QGALIB_GEN=$(addprefix qga/qapi-generated/, qga-qapi-types.h qga-qapi-visit.h qga-qapi-commands.h)
$(qga-obj-y): $(QGALIB_GEN)
qemu-ga$(EXESUF): $(qga-obj-y) $(COMMON_LDADDS)
@@ -726,12 +644,6 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad"
rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS)
$(call LINK, $^)
# relies on Linux-specific syscalls
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS)
$(call LINK, $^)
endif
vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a libqemustub.a
$(call LINK, $^)
@@ -769,7 +681,7 @@ clean: recurse-clean
! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \
-exec rm {} +
rm -f $(edk2-decompressed)
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) TAGS cscope.* *.pod *~ */*~
rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga$(EXESUF) TAGS cscope.* *.pod *~ */*~
rm -f fsdev/*.pod scsi/*.pod
rm -f qemu-img-cmds.h
rm -f ui/shader/*-vert.h ui/shader/*-frag.h
@@ -778,7 +690,6 @@ clean: recurse-clean
rm -f trace/generated-tracers-dtrace.h*
rm -f $(foreach f,$(generated-files-y),$(f) $(f)-timestamp)
rm -f qapi-gen-timestamp
rm -f storage-daemon/qapi/qapi-gen-timestamp
rm -rf qga/qapi-generated
rm -f config-all-devices.mak
@@ -789,19 +700,32 @@ dist: qemu-$(VERSION).tar.bz2
qemu-%.tar.bz2:
$(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)"
# Sphinx does not allow building manuals into the same directory as
# the source files, so if we're doing an in-tree QEMU build we must
# build the manuals into a subdirectory (and then install them from
# there for 'make install'). For an out-of-tree build we can just
# use the docs/ subdirectory in the build tree as normal.
ifeq ($(realpath $(SRC_PATH)),$(realpath .))
MANUAL_BUILDDIR := docs/built
else
MANUAL_BUILDDIR := docs
endif
define clean-manual =
rm -rf $(MANUAL_BUILDDIR)/$1/_static
rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(MANUAL_BUILDDIR)/$1/*.html
endef
distclean: clean
rm -f config-host.mak config-host.h* $(DOCS)
rm -f tests/tcg/config-*.mak
rm -f config-host.mak config-host.h* config-host.ld $(DOCS) qemu-options.texi qemu-img-cmds.texi qemu-monitor.texi qemu-monitor-info.texi
rm -f config-all-devices.mak config-all-disas.mak config.status
rm -f $(SUBDIR_DEVICES_MAK)
rm -f po/*.mo tests/qemu-iotests/common.env
rm -f roms/seabios/config.mak roms/vgabios/config.mak
rm -f qemu-plugins-ld.symbols qemu-plugins-ld64.symbols
rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps
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 qemu-doc.txt
rm -f config.log
rm -f linux-headers/asm
rm -f docs/version.texi
@@ -810,13 +734,12 @@ distclean: clean
rm -f docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html
rm -f docs/qemu-block-drivers.7
rm -f docs/qemu-cpu-models.7
rm -rf .doctrees
$(call clean-manual,devel)
$(call clean-manual,interop)
$(call clean-manual,specs)
$(call clean-manual,system)
$(call clean-manual,tools)
$(call clean-manual,user)
for d in $(TARGET_DIRS); do \
rm -rf $$d || exit 1 ; \
done
@@ -829,10 +752,10 @@ de-ch es fo fr-ca hu ja mk pt sl tr \
bepo cz
ifdef INSTALL_BLOBS
BLOBS=bios.bin bios-256k.bin bios-microvm.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
BLOBS=bios.bin bios-256k.bin sgabios.bin vgabios.bin vgabios-cirrus.bin \
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin vgabios-virtio.bin \
vgabios-ramfb.bin vgabios-bochs-display.bin vgabios-ati.bin \
openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc QEMU,tcx.bin QEMU,cgthree.bin \
pxe-e1000.rom pxe-eepro100.rom pxe-ne2k_pci.rom \
pxe-pcnet.rom pxe-rtl8139.rom pxe-virtio.rom \
efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \
@@ -842,13 +765,13 @@ qemu-nsis.bmp \
bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \
multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \
s390-ccw.img s390-netboot.img \
slof.bin skiboot.lid \
spapr-rtas.bin slof.bin skiboot.lid \
palcode-clipper \
u-boot.e500 u-boot-sam460-20100605.bin \
qemu_vga.ndrv \
edk2-licenses.txt \
hppa-firmware.img \
opensbi-riscv32-sifive_u-fw_jump.bin opensbi-riscv32-virt-fw_jump.bin \
opensbi-riscv32-virt-fw_jump.bin \
opensbi-riscv64-sifive_u-fw_jump.bin opensbi-riscv64-virt-fw_jump.bin
@@ -860,11 +783,10 @@ DESCS=
endif
# Note that we manually filter-out the non-Sphinx documentation which
# is currently built into the docs/interop directory in the build tree,
# and also any sphinx-built manpages.
# is currently built into the docs/interop directory in the build tree.
define install-manual =
for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done
for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name '*.[0-9]' -o -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done
endef
# Note that we deliberately do not install the "devel" manual: it is
@@ -873,32 +795,30 @@ endef
install-sphinxdocs: sphinxdocs
$(call install-manual,interop)
$(call install-manual,specs)
$(call install-manual,system)
$(call install-manual,tools)
$(call install-manual,user)
install-doc: $(DOCS) install-sphinxdocs
$(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/index.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.txt "$(DESTDIR)$(qemu_docdir)"
ifdef CONFIG_POSIX
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) qemu.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/system/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
ifeq ($(CONFIG_TOOLS),y)
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-img.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7"
$(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7"
ifneq ($(TOOLS),)
$(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8"
endif
ifdef CONFIG_TRACE_SYSTEMTAP
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1"
endif
ifneq (,$(findstring qemu-ga,$(TOOLS)))
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/interop/qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.txt "$(DESTDIR)$(qemu_docdir)"
$(INSTALL_DATA) docs/interop/qemu-ga-ref.7 "$(DESTDIR)$(mandir)/man7"
@@ -906,10 +826,7 @@ endif
endif
ifdef CONFIG_VIRTFS
$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1"
endif
ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy)
$(INSTALL_DATA) $(MANUAL_BUILDDIR)/tools/virtiofsd.1 "$(DESTDIR)$(mandir)/man1"
$(INSTALL_DATA) fsdev/virtfs-proxy-helper.1 "$(DESTDIR)$(mandir)/man1"
endif
install-datadir:
@@ -924,15 +841,11 @@ endif
ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512
install-includedir:
$(INSTALL_DIR) "$(DESTDIR)$(includedir)"
install: all $(if $(BUILD_DOCS),install-doc) \
install-datadir install-localstatedir install-includedir \
install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir \
$(if $(INSTALL_BLOBS),$(edk2-decompressed)) \
recurse-install
ifneq ($(TOOLS),)
$(call install-prog,$(TOOLS),$(DESTDIR)$(bindir))
$(call install-prog,$(subst qemu-ga,qemu-ga$(EXESUF),$(TOOLS)),$(DESTDIR)$(bindir))
endif
ifneq ($(CONFIG_MODULES),)
$(INSTALL_DIR) "$(DESTDIR)$(qemu_moddir)"
@@ -990,9 +903,6 @@ endif
"$(DESTDIR)$(qemu_desktopdir)/qemu.desktop"
ifdef CONFIG_GTK
$(MAKE) -C po $@
endif
ifeq ($(CONFIG_PLUGIN),y)
$(INSTALL_DATA) $(SRC_PATH)/include/qemu/qemu-plugin.h "$(DESTDIR)$(includedir)/qemu-plugin.h"
endif
$(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/keymaps"
set -e; for x in $(KEYMAPS); do \
@@ -1064,64 +974,33 @@ docs/version.texi: $(SRC_PATH)/VERSION config-host.mak
# and handles "don't rebuild things unless necessary" itself.
# The '.doctrees' files are cached information to speed this up.
.PHONY: sphinxdocs
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html \
$(MANUAL_BUILDDIR)/interop/index.html \
$(MANUAL_BUILDDIR)/specs/index.html \
$(MANUAL_BUILDDIR)/system/index.html \
$(MANUAL_BUILDDIR)/tools/index.html \
$(MANUAL_BUILDDIR)/user/index.html
sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html $(MANUAL_BUILDDIR)/specs/index.html
# Canned command to build a single manual
# Arguments: $1 = manual name, $2 = Sphinx builder ('html' or 'man')
# Note the use of different doctree for each (manual, builder) tuple;
# this works around Sphinx not handling parallel invocation on
# a single doctree: https://github.com/sphinx-doc/sphinx/issues/2946
build-manual = $(call quiet-command,CONFDIR="$(qemu_confdir)" $(SPHINX_BUILD) $(if $(V),,-q) $(SPHINX_WERROR) -b $2 -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1-$2 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
build-manual = $(call quiet-command,sphinx-build $(if $(V),,-q) -W -n -b html -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1")
# We assume all RST files in the manual's directory are used in it
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst $(SRC_PATH)/docs/$1/*/*.rst) \
$(SRC_PATH)/docs/defs.rst.inc \
$(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py \
$(SRC_PATH)/docs/sphinx/*.py
# Macro to write out the rule and dependencies for building manpages
# Usage: $(call define-manpage-rule,manualname,manpage1 manpage2...[,extradeps])
# 'extradeps' is optional, and specifies extra files (eg .hx files) that
# the manual page depends on.
define define-manpage-rule
$(call atomic,$(foreach manpage,$2,$(MANUAL_BUILDDIR)/$1/$(manpage)),$(call manual-deps,$1) $3)
$(call build-manual,$1,man)
endef
manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py
$(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel)
$(call build-manual,devel,html)
$(call build-manual,devel)
$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop)
$(call build-manual,interop,html)
$(call build-manual,interop)
$(MANUAL_BUILDDIR)/specs/index.html: $(call manual-deps,specs)
$(call build-manual,specs,html)
$(call build-manual,specs)
$(MANUAL_BUILDDIR)/system/index.html: $(call manual-deps,system) $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/qemu-options.hx
$(call build-manual,system,html)
qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
$(MANUAL_BUILDDIR)/tools/index.html: $(call manual-deps,tools) $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc
$(call build-manual,tools,html)
qemu-monitor.texi: $(SRC_PATH)/hmp-commands.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
$(MANUAL_BUILDDIR)/user/index.html: $(call manual-deps,user)
$(call build-manual,user,html)
qemu-monitor-info.texi: $(SRC_PATH)/hmp-commands-info.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
$(call define-manpage-rule,interop,qemu-ga.8)
$(call define-manpage-rule,system,qemu.1 qemu-block-drivers.7 qemu-cpu-models.7)
$(call define-manpage-rule,tools,\
qemu-img.1 qemu-nbd.8 qemu-trace-stap.1\
virtiofsd.1 virtfs-proxy-helper.1,\
$(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/docs/qemu-option-trace.rst.inc)
$(MANUAL_BUILDDIR)/index.html: $(SRC_PATH)/docs/index.html.in qemu-version.h
@mkdir -p "$(MANUAL_BUILDDIR)"
$(call quiet-command, sed "s|@@VERSION@@|${VERSION}|g" $< >$@, \
"GEN","$@")
qemu-img-cmds.texi: $(SRC_PATH)/qemu-img-cmds.hx $(SRC_PATH)/scripts/hxtool
$(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@")
docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
@cp -p $< $@
@@ -1129,10 +1008,27 @@ docs/interop/qemu-qmp-qapi.texi: qapi/qapi-doc.texi
docs/interop/qemu-ga-qapi.texi: qga/qapi-generated/qga-qapi-doc.texi
@cp -p $< $@
html: docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
info: docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
pdf: docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
txt: docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
qemu.1: qemu-doc.texi qemu-options.texi qemu-monitor.texi qemu-monitor-info.texi
qemu.1: qemu-option-trace.texi
qemu-img.1: qemu-img.texi qemu-option-trace.texi qemu-img-cmds.texi
fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi
qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi
qemu-ga.8: qemu-ga.texi
docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi
docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi
scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi
html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs
info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info
pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf
txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt
qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \
qemu-img.texi qemu-nbd.texi qemu-options.texi \
qemu-tech.texi qemu-option-trace.texi \
qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \
qemu-monitor-info.texi docs/qemu-block-drivers.texi \
docs/qemu-cpu-models.texi docs/security.texi
docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \
docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \
@@ -1235,57 +1131,50 @@ endif
include $(SRC_PATH)/tests/docker/Makefile.include
include $(SRC_PATH)/tests/vm/Makefile.include
print-help-run = printf " %-30s - %s\\n" "$1" "$2"
print-help = $(quiet-@)$(call print-help-run,$1,$2)
.PHONY: help
help:
@echo 'Generic targets:'
$(call print-help,all,Build all)
@echo ' all - Build all'
ifdef CONFIG_MODULES
$(call print-help,modules,Build all modules)
@echo ' modules - Build all modules'
endif
$(call print-help,dir/file.o,Build specified target only)
$(call print-help,install,Install QEMU, documentation and tools)
$(call print-help,ctags/TAGS,Generate tags file for editors)
$(call print-help,cscope,Generate cscope index)
@echo ' dir/file.o - Build specified target only'
@echo ' install - Install QEMU, documentation and tools'
@echo ' ctags/TAGS - Generate tags file for editors'
@echo ' cscope - Generate cscope index'
@echo ''
@$(if $(TARGET_DIRS), \
echo 'Architecture specific targets:'; \
$(foreach t, $(TARGET_DIRS), \
$(call print-help-run,$(t)/all,Build for $(t));) \
echo '')
@$(if $(TOOLS), \
echo 'Tools targets:'; \
$(foreach t, $(TOOLS), \
$(call print-help-run,$(t),Build $(shell basename $(t)) tool);) \
printf " %-30s - Build for %s\\n" $(t)/all $(t);) \
echo '')
@echo 'Cleaning targets:'
$(call print-help,clean,Remove most generated files but keep the config)
@echo ' clean - Remove most generated files but keep the config'
ifdef CONFIG_GCOV
$(call print-help,clean-coverage,Remove coverage files)
@echo ' clean-coverage - Remove coverage files'
endif
$(call print-help,distclean,Remove all generated files)
$(call print-help,dist,Build a distributable tarball)
@echo ' distclean - Remove all generated files'
@echo ' dist - Build a distributable tarball'
@echo ''
@echo 'Test targets:'
$(call print-help,check,Run all tests (check-help for details))
$(call print-help,docker,Help about targets running tests inside containers)
$(call print-help,vm-help,Help about targets running tests inside VM)
@echo ' check - Run all tests (check-help for details)'
@echo ' docker - Help about targets running tests inside Docker containers'
@echo ' vm-help - Help about targets running tests inside VM'
@echo ''
@echo 'Documentation targets:'
$(call print-help,html info pdf txt,Build documentation in specified format)
@echo ' html info pdf txt'
@echo ' - Build documentation in specified format'
ifdef CONFIG_GCOV
$(call print-help,coverage-report,Create code coverage report)
@echo ' coverage-report - Create code coverage report'
endif
@echo ''
ifdef CONFIG_WIN32
@echo 'Windows targets:'
$(call print-help,installer,Build NSIS-based installer for QEMU)
@echo ' installer - Build NSIS-based installer for QEMU'
ifdef QEMU_GA_MSI_ENABLED
$(call print-help,msi,Build MSI-based installer for qemu-ga)
@echo ' msi - Build MSI-based installer for qemu-ga'
endif
@echo ''
endif
$(call print-help,$(MAKE) [targets],(quiet build, default))
$(call print-help,$(MAKE) V=1 [targets],(verbose build))
@echo ' $(MAKE) [targets] (quiet build, default)'
@echo ' $(MAKE) V=1 [targets] (verbose build)'
+29 -28
View File
@@ -1,18 +1,18 @@
#######################################################################
# Common libraries for tools and emulators
stub-obj-y = stubs/
util-obj-y = crypto/ util/ qobject/ qapi/
qom-obj-y = qom/
#######################################################################
# code used by both qemu system emulation and qemu-img
ifeq ($(call lor,$(CONFIG_SOFTMMU),$(CONFIG_TOOLS)),y)
stub-obj-y = stubs/ util/ crypto/
util-obj-y = util/ qobject/ qapi/
chardev-obj-y = chardev/
#######################################################################
# authz-obj-y is code used by both qemu system emulation and qemu-img
authz-obj-y = authz/
#######################################################################
# block-obj-y is code used by both qemu system emulation and qemu-img
block-obj-y = nbd/
block-obj-y += block.o blockjob.o job.o
block-obj-y += block/ scsi/
@@ -21,20 +21,21 @@ block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-m = block/
#######################################################################
# crypto-obj-y is code used by both qemu system emulation and qemu-img
crypto-obj-y = crypto/
io-obj-y = io/
endif # CONFIG_SOFTMMU or CONFIG_TOOLS
crypto-user-obj-y = crypto/
#######################################################################
# storage-daemon-obj-y is code used by qemu-storage-daemon (these objects are
# used for system emulation, too, but specified separately there)
# qom-obj-y is code used by both qemu system emulation and qemu-img
storage-daemon-obj-y = block/ monitor/ qapi/ qom/ storage-daemon/
storage-daemon-obj-y += blockdev.o blockdev-nbd.o iothread.o job-qmp.o
storage-daemon-obj-$(CONFIG_WIN32) += os-win32.o
storage-daemon-obj-$(CONFIG_POSIX) += os-posix.o
qom-obj-y = qom/
#######################################################################
# io-obj-y is code used by both qemu system emulation and qemu-img
io-obj-y = io/
######################################################################
# Target independent part of system emulation. The long term path is to
@@ -48,13 +49,12 @@ common-obj-y += dump/
common-obj-y += job-qmp.o
common-obj-y += monitor/
common-obj-y += net/
common-obj-y += qdev-monitor.o
common-obj-y += qdev-monitor.o device-hotplug.o
common-obj-$(CONFIG_WIN32) += os-win32.o
common-obj-$(CONFIG_POSIX) += os-posix.o
common-obj-$(CONFIG_LINUX) += fsdev/
common-obj-y += accel/
common-obj-y += migration/
common-obj-y += audio/
@@ -65,8 +65,12 @@ common-obj-y += replay/
common-obj-y += ui/
common-obj-m += ui/
common-obj-y += bt-host.o bt-vhci.o
bt-host.o-cflags := $(BLUEZ_CFLAGS)
common-obj-y += dma-helpers.o
common-obj-y += vl.o
vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS)
common-obj-$(CONFIG_TPM) += tpm.o
common-obj-y += backends/
@@ -78,9 +82,11 @@ qemu-seccomp.o-libs := $(SECCOMP_LIBS)
common-obj-$(CONFIG_FDT) += device_tree.o
common-obj-y += qapi/
######################################################################
# qapi
endif # CONFIG_SOFTMMU
common-obj-y += qapi/
endif
#######################################################################
# Target-independent parts used in system and user emulation
@@ -119,13 +125,11 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/
rdmacm-mux-obj-y = contrib/rdmacm-mux/
vhost-user-input-obj-y = contrib/vhost-user-input/
vhost-user-gpu-obj-y = contrib/vhost-user-gpu/
virtiofsd-obj-y = tools/virtiofsd/
######################################################################
trace-events-subdirs =
trace-events-subdirs += accel/kvm
trace-events-subdirs += accel/tcg
trace-events-subdirs += backends
trace-events-subdirs += crypto
trace-events-subdirs += monitor
ifeq ($(CONFIG_USER_ONLY),y)
@@ -139,8 +143,8 @@ trace-events-subdirs += nbd
trace-events-subdirs += scsi
endif
ifeq ($(CONFIG_SOFTMMU),y)
trace-events-subdirs += audio
trace-events-subdirs += chardev
trace-events-subdirs += audio
trace-events-subdirs += hw/9pfs
trace-events-subdirs += hw/acpi
trace-events-subdirs += hw/alpha
@@ -169,13 +173,11 @@ trace-events-subdirs += hw/pci-host
trace-events-subdirs += hw/ppc
trace-events-subdirs += hw/rdma
trace-events-subdirs += hw/rdma/vmw
trace-events-subdirs += hw/rtc
trace-events-subdirs += hw/s390x
trace-events-subdirs += hw/scsi
trace-events-subdirs += hw/sd
trace-events-subdirs += hw/sparc
trace-events-subdirs += hw/sparc64
trace-events-subdirs += hw/ssi
trace-events-subdirs += hw/timer
trace-events-subdirs += hw/tpm
trace-events-subdirs += hw/usb
@@ -189,7 +191,6 @@ trace-events-subdirs += migration
trace-events-subdirs += net
trace-events-subdirs += ui
endif
trace-events-subdirs += hw/core
trace-events-subdirs += hw/display
trace-events-subdirs += qapi
trace-events-subdirs += qom
+9 -24
View File
@@ -12,7 +12,7 @@ endif
$(call set-vpath, $(SRC_PATH):$(BUILD_DIR))
ifdef CONFIG_LINUX
QEMU_CFLAGS += -isystem ../linux-headers
QEMU_CFLAGS += -I../linux-headers
endif
QEMU_CFLAGS += -iquote .. -iquote $(SRC_PATH)/target/$(TARGET_BASE_ARCH) -DNEED_CPU_H
@@ -39,6 +39,9 @@ endif
PROGS=$(QEMU_PROG) $(QEMU_PROGW)
STPFILES=
# Makefile Tests
include $(SRC_PATH)/tests/tcg/Makefile.include
config-target.h: config-target.h-timestamp
config-target.h-timestamp: config-target.mak
@@ -107,7 +110,7 @@ obj-y += trace/
#########################################################
# cpu emulator library
obj-y += exec.o exec-vary.o
obj-y += exec.o
obj-y += accel/
obj-$(CONFIG_TCG) += tcg/tcg.o tcg/tcg-op.o tcg/tcg-op-vec.o tcg/tcg-op-gvec.o
obj-$(CONFIG_TCG) += tcg/tcg-common.o tcg/optimize.o
@@ -119,8 +122,6 @@ obj-y += disas.o
obj-$(call notempty,$(TARGET_XML_FILES)) += gdbstub-xml.o
LIBS := $(libs_cpu) $(LIBS)
obj-$(CONFIG_PLUGIN) += plugins/
#########################################################
# Linux user emulator target
@@ -128,8 +129,7 @@ ifdef CONFIG_LINUX_USER
QEMU_CFLAGS+=-I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR) \
-I$(SRC_PATH)/linux-user/host/$(ARCH) \
-I$(SRC_PATH)/linux-user \
-Ilinux-user/$(TARGET_ABI_DIR)
-I$(SRC_PATH)/linux-user
obj-y += linux-user/
obj-y += gdbstub.o thunk.o
@@ -161,7 +161,6 @@ obj-y += qapi/
obj-y += memory.o
obj-y += memory_mapping.o
obj-y += migration/ram.o
obj-y += softmmu/
LIBS := $(libs_softmmu) $(LIBS)
# Hardware support
@@ -186,6 +185,7 @@ dummy := $(call unnest-vars,.., \
block-obj-m \
chardev-obj-y \
crypto-obj-y \
crypto-user-obj-y \
qom-obj-y \
io-obj-y \
common-obj-y \
@@ -194,6 +194,7 @@ all-obj-y += $(common-obj-y)
all-obj-y += $(qom-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(authz-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-obj-y)
all-obj-$(CONFIG_USER_ONLY) += $(crypto-user-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(crypto-obj-y)
all-obj-$(CONFIG_SOFTMMU) += $(io-obj-y)
@@ -204,7 +205,7 @@ endif
COMMON_LDADDS = ../libqemuutil.a
# build either PROG or PROGW
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS) $(softmmu-main-y)
$(QEMU_PROG_BUILD): $(all-obj-y) $(COMMON_LDADDS)
$(call LINK, $(filter-out %.mak, $^))
ifdef CONFIG_DARWIN
$(call quiet-command,Rez -append $(SRC_PATH)/pc-bios/qemu.rsrc -o $@,"REZ","$(TARGET_DIR)$@")
@@ -229,22 +230,6 @@ ifdef CONFIG_TRACE_SYSTEMTAP
rm -f *.stp
endif
ifdef CONFIG_FUZZ
include $(SRC_PATH)/tests/qtest/fuzz/Makefile.include
include $(SRC_PATH)/tests/qtest/Makefile.include
fuzz: fuzz-vars
fuzz-vars: QEMU_CFLAGS := $(FUZZ_CFLAGS) $(QEMU_CFLAGS)
fuzz-vars: QEMU_LDFLAGS := $(FUZZ_LDFLAGS) $(QEMU_LDFLAGS)
fuzz-vars: $(QEMU_PROG_FUZZ)
dummy := $(call unnest-vars,, fuzz-obj-y)
$(QEMU_PROG_FUZZ): config-devices.mak $(all-obj-y) $(COMMON_LDADDS) $(fuzz-obj-y)
$(call LINK, $(filter-out %.mak, $^))
endif
install: all
ifneq ($(PROGS),)
$(call install-prog,$(PROGS),$(DESTDIR)$(bindir))
+18 -29
View File
@@ -1,6 +1,5 @@
===========
QEMU README
===========
QEMU README
===========
QEMU is a generic and open source machine & userspace emulator and
virtualizer.
@@ -38,9 +37,6 @@ QEMU is multi-platform software intended to be buildable on all modern
Linux platforms, OS-X, Win32 (via the Mingw64 toolchain) and a variety
of other UNIX targets. The simple steps to build QEMU are:
.. code-block:: shell
mkdir build
cd build
../configure
@@ -48,9 +44,9 @@ of other UNIX targets. The simple steps to build QEMU are:
Additional information can also be found online via the QEMU website:
* `<https://qemu.org/Hosts/Linux>`_
* `<https://qemu.org/Hosts/Mac>`_
* `<https://qemu.org/Hosts/W32>`_
https://qemu.org/Hosts/Linux
https://qemu.org/Hosts/Mac
https://qemu.org/Hosts/W32
Submitting patches
@@ -58,29 +54,24 @@ Submitting patches
The QEMU source code is maintained under the GIT version control system.
.. code-block:: shell
git clone https://git.qemu.org/git/qemu.git
When submitting patches, one common approach is to use 'git
format-patch' and/or 'git send-email' to format & send the mail to the
qemu-devel@nongnu.org mailing list. All patches submitted must contain
a 'Signed-off-by' line from the author. Patches should follow the
guidelines set out in the CODING_STYLE.rst file.
guidelines set out in the HACKING and CODING_STYLE files.
Additional information on submitting patches can be found online via
the QEMU website
* `<https://qemu.org/Contribute/SubmitAPatch>`_
* `<https://qemu.org/Contribute/TrivialPatches>`_
https://qemu.org/Contribute/SubmitAPatch
https://qemu.org/Contribute/TrivialPatches
The QEMU website is also maintained under source control.
.. code-block:: shell
git clone https://git.qemu.org/git/qemu-web.git
* `<https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/>`_
https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/
A 'git-publish' utility was created to make above process less
cumbersome, and is highly recommended for making regular contributions,
@@ -91,12 +82,10 @@ manually for once.
For installation instructions, please go to
* `<https://github.com/stefanha/git-publish>`_
https://github.com/stefanha/git-publish
The workflow with 'git-publish' is:
.. code-block:: shell
$ git checkout master -b my-feature
$ # work on new commits, add your 'Signed-off-by' lines to each
$ git publish
@@ -106,8 +95,6 @@ back to it in the future.
Sending v2:
.. code-block:: shell
$ git checkout my-feature # same topic branch
$ # making changes to the commits (using 'git rebase', for example)
$ git publish
@@ -122,7 +109,7 @@ The QEMU project uses Launchpad as its primary upstream bug tracker. Bugs
found when running code built from QEMU git or upstream released sources
should be reported via:
* `<https://bugs.launchpad.net/qemu/>`_
https://bugs.launchpad.net/qemu/
If using QEMU via an operating system vendor pre-built binary package, it
is preferable to report bugs to the vendor's own bug tracker first. If
@@ -131,7 +118,7 @@ reported via launchpad.
For additional information on bug reporting consult:
* `<https://qemu.org/Contribute/ReportABug>`_
https://qemu.org/Contribute/ReportABug
Contact
@@ -140,11 +127,13 @@ Contact
The QEMU community can be contacted in a number of ways, with the two
main methods being email and IRC
* `<mailto:qemu-devel@nongnu.org>`_
* `<https://lists.nongnu.org/mailman/listinfo/qemu-devel>`_
* #qemu on irc.oftc.net
- qemu-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/qemu-devel
- #qemu on irc.oftc.net
Information on additional methods of contacting the community can be
found online via the QEMU website:
* `<https://qemu.org/Contribute/StartHere>`_
https://qemu.org/Contribute/StartHere
-- End
+1 -1
View File
@@ -1 +1 @@
5.0.1
4.1.1
+1 -1
View File
@@ -1,4 +1,4 @@
common-obj-$(CONFIG_SOFTMMU) += accel.o
obj-$(CONFIG_SOFTMMU) += accel.o
obj-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_POSIX)) += qtest.o
obj-$(CONFIG_KVM) += kvm/
obj-$(CONFIG_TCG) += tcg/
+67 -5
View File
@@ -28,7 +28,13 @@
#include "hw/boards.h"
#include "sysemu/arch_init.h"
#include "sysemu/sysemu.h"
#include "sysemu/kvm.h"
#include "sysemu/qtest.h"
#include "hw/xen/xen.h"
#include "qom/object.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
#include "qapi/error.h"
static const TypeInfo accel_type = {
.name = TYPE_ACCEL,
@@ -38,7 +44,7 @@ static const TypeInfo accel_type = {
};
/* Lookup AccelClass from opt_name. Returns NULL if not found */
AccelClass *accel_find(const char *opt_name)
static AccelClass *accel_find(const char *opt_name)
{
char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name);
AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name));
@@ -46,9 +52,11 @@ AccelClass *accel_find(const char *opt_name)
return ac;
}
int accel_init_machine(AccelState *accel, MachineState *ms)
static int accel_init_machine(AccelClass *acc, MachineState *ms)
{
AccelClass *acc = ACCEL_GET_CLASS(accel);
ObjectClass *oc = OBJECT_CLASS(acc);
const char *cname = object_class_get_name(oc);
AccelState *accel = ACCEL(object_new(cname));
int ret;
ms->accelerator = accel;
*(acc->allowed) = true;
@@ -63,9 +71,63 @@ int accel_init_machine(AccelState *accel, MachineState *ms)
return ret;
}
AccelState *current_accel(void)
void configure_accelerator(MachineState *ms, const char *progname)
{
return current_machine->accelerator;
const char *accel;
char **accel_list, **tmp;
int ret;
bool accel_initialised = false;
bool init_failed = false;
AccelClass *acc = NULL;
accel = qemu_opt_get(qemu_get_machine_opts(), "accel");
if (accel == NULL) {
/* Select the default accelerator */
int pnlen = strlen(progname);
if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) {
/* If the program name ends with "kvm", we prefer KVM */
accel = "kvm:tcg";
} else {
#if defined(CONFIG_TCG)
accel = "tcg";
#elif defined(CONFIG_KVM)
accel = "kvm";
#else
error_report("No accelerator selected and"
" no default accelerator available");
exit(1);
#endif
}
}
accel_list = g_strsplit(accel, ":", 0);
for (tmp = accel_list; !accel_initialised && tmp && *tmp; tmp++) {
acc = accel_find(*tmp);
if (!acc) {
continue;
}
ret = accel_init_machine(acc, ms);
if (ret < 0) {
init_failed = true;
error_report("failed to initialize %s: %s",
acc->name, strerror(-ret));
} else {
accel_initialised = true;
}
}
g_strfreev(accel_list);
if (!accel_initialised) {
if (!init_failed) {
error_report("-machine accel=%s: No accelerator found", accel);
}
exit(1);
}
if (init_failed) {
error_report("Back to %s accelerator", acc->name);
}
}
void accel_setup_post(MachineState *ms)
+118 -331
View File
@@ -23,27 +23,22 @@
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "hw/s390x/adapter.h"
#include "exec/gdbstub.h"
#include "sysemu/kvm_int.h"
#include "sysemu/runstate.h"
#include "sysemu/cpus.h"
#include "sysemu/sysemu.h"
#include "qemu/bswap.h"
#include "exec/memory.h"
#include "exec/ram_addr.h"
#include "exec/address-spaces.h"
#include "qemu/event_notifier.h"
#include "qemu/main-loop.h"
#include "trace.h"
#include "hw/irq.h"
#include "sysemu/sev.h"
#include "sysemu/balloon.h"
#include "qapi/visitor.h"
#include "qapi/qapi-types-common.h"
#include "qapi/qapi-visit-common.h"
#include "hw/boards.h"
@@ -55,7 +50,7 @@
/* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We
* need to use the real host PAGE_SIZE, as that's what KVM will use.
*/
#define PAGE_SIZE qemu_real_host_page_size
#define PAGE_SIZE getpagesize()
//#define DEBUG_KVM
@@ -95,10 +90,6 @@ struct KVMState
int max_nested_state_len;
int many_ioeventfds;
int intx_set_mask;
int kvm_shadow_mem;
bool kernel_irqchip_allowed;
bool kernel_irqchip_required;
OnOffAuto kernel_irqchip_split;
bool sync_mmu;
bool manual_dirty_log_protect;
/* The man page (and posix) say ioctl numbers are signed int, but
@@ -147,7 +138,6 @@ bool kvm_direct_msi_allowed;
bool kvm_ioeventfd_any_length_allowed;
bool kvm_msi_use_devid;
static bool kvm_immediate_exit;
static hwaddr kvm_max_slot_size = ~0;
static const KVMCapabilityInfo kvm_required_capabilites[] = {
KVM_CAP_INFO(USER_MEMORY),
@@ -156,15 +146,12 @@ static const KVMCapabilityInfo kvm_required_capabilites[] = {
KVM_CAP_LAST_INFO
};
static NotifierList kvm_irqchip_change_notifiers =
NOTIFIER_LIST_INITIALIZER(kvm_irqchip_change_notifiers);
#define kvm_slots_lock(kml) qemu_mutex_lock(&(kml)->slots_lock)
#define kvm_slots_unlock(kml) qemu_mutex_unlock(&(kml)->slots_lock)
int kvm_get_max_memslots(void)
{
KVMState *s = KVM_STATE(current_accel());
KVMState *s = KVM_STATE(current_machine->accelerator);
return s->nr_slots;
}
@@ -308,23 +295,13 @@ static int kvm_set_user_memory_region(KVMMemoryListener *kml, KVMSlot *slot, boo
/* Set the slot size to 0 before setting the slot to the desired
* value. This is needed based on KVM commit 75d61fbc. */
mem.memory_size = 0;
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
if (ret < 0) {
goto err;
}
kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
}
mem.memory_size = slot->memory_size;
ret = kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem);
slot->old_flags = mem.flags;
err:
trace_kvm_set_user_memory(mem.slot, mem.flags, mem.guest_phys_addr,
mem.memory_size, mem.userspace_addr, ret);
if (ret < 0) {
error_report("%s: KVM_SET_USER_MEMORY_REGION failed, slot=%d,"
" start=0x%" PRIx64 ", size=0x%" PRIx64 ": %s",
__func__, mem.slot, slot->start_addr,
(uint64_t)mem.memory_size, strerror(errno));
}
return ret;
}
@@ -458,7 +435,7 @@ static int kvm_slot_update_flags(KVMMemoryListener *kml, KVMSlot *mem,
static int kvm_section_update_flags(KVMMemoryListener *kml,
MemoryRegionSection *section)
{
hwaddr start_addr, size, slot_size;
hwaddr start_addr, size;
KVMSlot *mem;
int ret = 0;
@@ -469,19 +446,14 @@ static int kvm_section_update_flags(KVMMemoryListener *kml,
kvm_slots_lock(kml);
while (size && !ret) {
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
if (!mem) {
/* We don't have a slot if we want to trap every access. */
goto out;
}
ret = kvm_slot_update_flags(kml, mem, section->mr);
start_addr += slot_size;
size -= slot_size;
mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) {
/* We don't have a slot if we want to trap every access. */
goto out;
}
ret = kvm_slot_update_flags(kml, mem, section->mr);
out:
kvm_slots_unlock(kml);
return ret;
@@ -527,7 +499,7 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
{
ram_addr_t start = section->offset_within_region +
memory_region_get_ram_addr(section->mr);
ram_addr_t pages = int128_get64(section->size) / qemu_real_host_page_size;
ram_addr_t pages = int128_get64(section->size) / getpagesize();
cpu_physical_memory_set_dirty_lebitmap(bitmap, start, pages);
return 0;
@@ -535,27 +507,6 @@ static int kvm_get_dirty_pages_log_range(MemoryRegionSection *section,
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
/* Allocate the dirty bitmap for a slot */
static void kvm_memslot_init_dirty_bitmap(KVMSlot *mem)
{
/*
* XXX bad kernel interface alert
* For dirty bitmap, kernel allocates array of size aligned to
* bits-per-long. But for case when the kernel is 64bits and
* the userspace is 32bits, userspace can't align to the same
* bits-per-long, since sizeof(long) is different between kernel
* and user space. This way, userspace will provide buffer which
* may be 4 bytes less than the kernel will use, resulting in
* userspace memory corruption (which is not detectable by valgrind
* too, in most cases).
* So for now, let's align to 64 instead of HOST_LONG_BITS here, in
* a hope that sizeof(long) won't become >8 any time soon.
*/
hwaddr bitmap_size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
/*HOST_LONG_BITS*/ 64) / 8;
mem->dirty_bmap = g_malloc0(bitmap_size);
}
/**
* kvm_physical_sync_dirty_bitmap - Sync dirty bitmap from kernel space
*
@@ -574,23 +525,33 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
struct kvm_dirty_log d = {};
KVMSlot *mem;
hwaddr start_addr, size;
hwaddr slot_size, slot_offset = 0;
int ret = 0;
size = kvm_align_section(section, &start_addr);
while (size) {
MemoryRegionSection subsection = *section;
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
if (size) {
mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) {
/* We don't have a slot if we want to trap every access. */
goto out;
}
/* XXX bad kernel interface alert
* For dirty bitmap, kernel allocates array of size aligned to
* bits-per-long. But for case when the kernel is 64bits and
* the userspace is 32bits, userspace can't align to the same
* bits-per-long, since sizeof(long) is different between kernel
* and user space. This way, userspace will provide buffer which
* may be 4 bytes less than the kernel will use, resulting in
* userspace memory corruption (which is not detectable by valgrind
* too, in most cases).
* So for now, let's align to 64 instead of HOST_LONG_BITS here, in
* a hope that sizeof(long) won't become >8 any time soon.
*/
size = ALIGN(((mem->memory_size) >> TARGET_PAGE_BITS),
/*HOST_LONG_BITS*/ 64) / 8;
if (!mem->dirty_bmap) {
/* Allocate on the first log_sync, once and for all */
kvm_memslot_init_dirty_bitmap(mem);
mem->dirty_bmap = g_malloc0(size);
}
d.dirty_bitmap = mem->dirty_bmap;
@@ -601,13 +562,7 @@ static int kvm_physical_sync_dirty_bitmap(KVMMemoryListener *kml,
goto out;
}
subsection.offset_within_region += slot_offset;
subsection.size = int128_make64(slot_size);
kvm_get_dirty_pages_log_range(&subsection, d.dirty_bitmap);
slot_offset += slot_size;
start_addr += slot_size;
size -= slot_size;
kvm_get_dirty_pages_log_range(section, d.dirty_bitmap);
}
out:
return ret;
@@ -618,22 +573,63 @@ out:
#define KVM_CLEAR_LOG_ALIGN (qemu_real_host_page_size << KVM_CLEAR_LOG_SHIFT)
#define KVM_CLEAR_LOG_MASK (-KVM_CLEAR_LOG_ALIGN)
static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start,
uint64_t size)
/**
* kvm_physical_log_clear - Clear the kernel's dirty bitmap for range
*
* NOTE: this will be a no-op if we haven't enabled manual dirty log
* protection in the host kernel because in that case this operation
* will be done within log_sync().
*
* @kml: the kvm memory listener
* @section: the memory range to clear dirty bitmap
*/
static int kvm_physical_log_clear(KVMMemoryListener *kml,
MemoryRegionSection *section)
{
KVMState *s = kvm_state;
uint64_t end, bmap_start, start_delta, bmap_npages;
struct kvm_clear_dirty_log d;
uint64_t start, end, bmap_start, start_delta, bmap_npages, size;
unsigned long *bmap_clear = NULL, psize = qemu_real_host_page_size;
int ret;
KVMSlot *mem = NULL;
int ret, i;
if (!s->manual_dirty_log_protect) {
/* No need to do explicit clear */
return 0;
}
start = section->offset_within_address_space;
size = int128_get64(section->size);
if (!size) {
/* Nothing more we can do... */
return 0;
}
kvm_slots_lock(kml);
/* Find any possible slot that covers the section */
for (i = 0; i < s->nr_slots; i++) {
mem = &kml->slots[i];
if (mem->start_addr <= start &&
start + size <= mem->start_addr + mem->memory_size) {
break;
}
}
/*
* We should always find one memslot until this point, otherwise
* there could be something wrong from the upper layer
*/
assert(mem && i != s->nr_slots);
/*
* We need to extend either the start or the size or both to
* satisfy the KVM interface requirement. Firstly, do the start
* page alignment on 64 host pages
*/
bmap_start = start & KVM_CLEAR_LOG_MASK;
start_delta = start - bmap_start;
bmap_start = (start - mem->start_addr) & KVM_CLEAR_LOG_MASK;
start_delta = start - mem->start_addr - bmap_start;
bmap_start /= psize;
/*
@@ -696,7 +692,7 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start,
/* It should never overflow. If it happens, say something */
assert(bmap_npages <= UINT32_MAX);
d.num_pages = bmap_npages;
d.slot = mem->slot | (as_id << 16);
d.slot = mem->slot | (kml->as_id << 16);
if (kvm_vm_ioctl(s, KVM_CLEAR_DIRTY_LOG, &d) == -1) {
ret = -errno;
@@ -719,66 +715,6 @@ static int kvm_log_clear_one_slot(KVMSlot *mem, int as_id, uint64_t start,
size / psize);
/* This handles the NULL case well */
g_free(bmap_clear);
return ret;
}
/**
* kvm_physical_log_clear - Clear the kernel's dirty bitmap for range
*
* NOTE: this will be a no-op if we haven't enabled manual dirty log
* protection in the host kernel because in that case this operation
* will be done within log_sync().
*
* @kml: the kvm memory listener
* @section: the memory range to clear dirty bitmap
*/
static int kvm_physical_log_clear(KVMMemoryListener *kml,
MemoryRegionSection *section)
{
KVMState *s = kvm_state;
uint64_t start, size, offset, count;
KVMSlot *mem;
int ret = 0, i;
if (!s->manual_dirty_log_protect) {
/* No need to do explicit clear */
return ret;
}
start = section->offset_within_address_space;
size = int128_get64(section->size);
if (!size) {
/* Nothing more we can do... */
return ret;
}
kvm_slots_lock(kml);
for (i = 0; i < s->nr_slots; i++) {
mem = &kml->slots[i];
/* Discard slots that are empty or do not overlap the section */
if (!mem->memory_size ||
mem->start_addr > start + size - 1 ||
start > mem->start_addr + mem->memory_size - 1) {
continue;
}
if (start >= mem->start_addr) {
/* The slot starts before section or is aligned to it. */
offset = start - mem->start_addr;
count = MIN(mem->memory_size - offset, size);
} else {
/* The slot starts after section. */
offset = 0;
count = MIN(mem->memory_size, size - (mem->start_addr - start));
}
ret = kvm_log_clear_one_slot(mem, kml->as_id, offset, count);
if (ret < 0) {
break;
}
}
kvm_slots_unlock(kml);
@@ -1015,14 +951,6 @@ kvm_check_extension_list(KVMState *s, const KVMCapabilityInfo *list)
return NULL;
}
void kvm_set_max_memslot_size(hwaddr max_slot_size)
{
g_assert(
ROUND_UP(max_slot_size, qemu_real_host_page_size) == max_slot_size
);
kvm_max_slot_size = max_slot_size;
}
static void kvm_set_phys_mem(KVMMemoryListener *kml,
MemoryRegionSection *section, bool add)
{
@@ -1030,7 +958,7 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
int err;
MemoryRegion *mr = section->mr;
bool writeable = !mr->readonly && !mr->rom_device;
hwaddr start_addr, size, slot_size;
hwaddr start_addr, size;
void *ram;
if (!memory_region_is_ram(mr)) {
@@ -1055,59 +983,41 @@ static void kvm_set_phys_mem(KVMMemoryListener *kml,
kvm_slots_lock(kml);
if (!add) {
do {
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_lookup_matching_slot(kml, start_addr, slot_size);
if (!mem) {
goto out;
}
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
kvm_physical_sync_dirty_bitmap(kml, section);
}
mem = kvm_lookup_matching_slot(kml, start_addr, size);
if (!mem) {
goto out;
}
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
kvm_physical_sync_dirty_bitmap(kml, section);
}
/* unregister the slot */
g_free(mem->dirty_bmap);
mem->dirty_bmap = NULL;
mem->memory_size = 0;
mem->flags = 0;
err = kvm_set_user_memory_region(kml, mem, false);
if (err) {
fprintf(stderr, "%s: error unregistering slot: %s\n",
__func__, strerror(-err));
abort();
}
start_addr += slot_size;
size -= slot_size;
} while (size);
/* unregister the slot */
g_free(mem->dirty_bmap);
mem->dirty_bmap = NULL;
mem->memory_size = 0;
mem->flags = 0;
err = kvm_set_user_memory_region(kml, mem, false);
if (err) {
fprintf(stderr, "%s: error unregistering slot: %s\n",
__func__, strerror(-err));
abort();
}
goto out;
}
/* register the new slot */
do {
slot_size = MIN(kvm_max_slot_size, size);
mem = kvm_alloc_slot(kml);
mem->memory_size = slot_size;
mem->start_addr = start_addr;
mem->ram = ram;
mem->flags = kvm_mem_flags(mr);
mem = kvm_alloc_slot(kml);
mem->memory_size = size;
mem->start_addr = start_addr;
mem->ram = ram;
mem->flags = kvm_mem_flags(mr);
if (mem->flags & KVM_MEM_LOG_DIRTY_PAGES) {
/*
* Reallocate the bmap; it means it doesn't disappear in
* middle of a migrate.
*/
kvm_memslot_init_dirty_bitmap(mem);
}
err = kvm_set_user_memory_region(kml, mem, true);
if (err) {
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
strerror(-err));
abort();
}
start_addr += slot_size;
ram += slot_size;
size -= slot_size;
} while (size);
err = kvm_set_user_memory_region(kml, mem, true);
if (err) {
fprintf(stderr, "%s: error registering slot: %s\n", __func__,
strerror(-err));
abort();
}
out:
kvm_slots_unlock(kml);
@@ -1430,21 +1340,6 @@ void kvm_irqchip_release_virq(KVMState *s, int virq)
trace_kvm_irqchip_release_virq(virq);
}
void kvm_irqchip_add_change_notifier(Notifier *n)
{
notifier_list_add(&kvm_irqchip_change_notifiers, n);
}
void kvm_irqchip_remove_change_notifier(Notifier *n)
{
notifier_remove(n);
}
void kvm_irqchip_change_notify(void)
{
notifier_list_notify(&kvm_irqchip_change_notifiers, NULL);
}
static unsigned int kvm_hash_msi(uint32_t data)
{
/* This is optimized for IA32 MSI layout. However, no other arch shall
@@ -1789,11 +1684,10 @@ void kvm_irqchip_set_qemuirq_gsi(KVMState *s, qemu_irq irq, int gsi)
g_hash_table_insert(s->gsimap, irq, GINT_TO_POINTER(gsi));
}
static void kvm_irqchip_create(KVMState *s)
static void kvm_irqchip_create(MachineState *machine, KVMState *s)
{
int ret;
assert(s->kernel_irqchip_split != ON_OFF_AUTO_AUTO);
if (kvm_check_extension(s, KVM_CAP_IRQCHIP)) {
;
} else if (kvm_check_extension(s, KVM_CAP_S390_IRQCHIP)) {
@@ -1808,9 +1702,9 @@ static void kvm_irqchip_create(KVMState *s)
/* First probe and see if there's a arch-specific hook to create the
* in-kernel irqchip for us */
ret = kvm_arch_irqchip_create(s);
ret = kvm_arch_irqchip_create(machine, s);
if (ret == 0) {
if (s->kernel_irqchip_split == ON_OFF_AUTO_ON) {
if (machine_kernel_irqchip_split(machine)) {
perror("Split IRQ chip mode not supported.");
exit(1);
} else {
@@ -1858,7 +1752,7 @@ static int kvm_max_vcpu_id(KVMState *s)
bool kvm_vcpu_id_is_valid(int vcpu_id)
{
KVMState *s = KVM_STATE(current_accel());
KVMState *s = KVM_STATE(current_machine->accelerator);
return vcpu_id >= 0 && vcpu_id < kvm_max_vcpu_id(s);
}
@@ -1891,7 +1785,7 @@ static int kvm_init(MachineState *ms)
* even with KVM. TARGET_PAGE_SIZE is assumed to be the minimum
* page size for the system though.
*/
assert(TARGET_PAGE_SIZE <= qemu_real_host_page_size);
assert(TARGET_PAGE_SIZE <= getpagesize());
s->sigmask_len = 8;
@@ -2081,12 +1975,8 @@ static int kvm_init(MachineState *ms)
goto err;
}
if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) {
s->kernel_irqchip_split = mc->default_kernel_irqchip_split ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF;
}
if (s->kernel_irqchip_allowed) {
kvm_irqchip_create(s);
if (machine_kernel_irqchip_allowed(ms)) {
kvm_irqchip_create(ms, s);
}
if (kvm_eventfds_allowed) {
@@ -2188,9 +2078,9 @@ void kvm_flush_coalesced_mmio_buffer(void)
ent = &ring->coalesced_mmio[ring->first];
if (ent->pio == 1) {
address_space_write(&address_space_io, ent->phys_addr,
MEMTXATTRS_UNSPECIFIED, ent->data,
ent->len);
address_space_rw(&address_space_io, ent->phys_addr,
MEMTXATTRS_UNSPECIFIED, ent->data,
ent->len, true);
} else {
cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len);
}
@@ -2967,7 +2857,6 @@ static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
for (i = 0; i < kvm->nr_as; ++i) {
if (kvm->as[i].as == as && kvm->as[i].ml) {
size = MIN(kvm_max_slot_size, size);
return NULL != kvm_lookup_matching_slot(kvm->as[i].ml,
start_addr, size);
}
@@ -2976,95 +2865,6 @@ static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as,
return false;
}
static void kvm_get_kvm_shadow_mem(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
KVMState *s = KVM_STATE(obj);
int64_t value = s->kvm_shadow_mem;
visit_type_int(v, name, &value, errp);
}
static void kvm_set_kvm_shadow_mem(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
KVMState *s = KVM_STATE(obj);
Error *error = NULL;
int64_t value;
visit_type_int(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
}
s->kvm_shadow_mem = value;
}
static void kvm_set_kernel_irqchip(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
Error *err = NULL;
KVMState *s = KVM_STATE(obj);
OnOffSplit mode;
visit_type_OnOffSplit(v, name, &mode, &err);
if (err) {
error_propagate(errp, err);
return;
} else {
switch (mode) {
case ON_OFF_SPLIT_ON:
s->kernel_irqchip_allowed = true;
s->kernel_irqchip_required = true;
s->kernel_irqchip_split = ON_OFF_AUTO_OFF;
break;
case ON_OFF_SPLIT_OFF:
s->kernel_irqchip_allowed = false;
s->kernel_irqchip_required = false;
s->kernel_irqchip_split = ON_OFF_AUTO_OFF;
break;
case ON_OFF_SPLIT_SPLIT:
s->kernel_irqchip_allowed = true;
s->kernel_irqchip_required = true;
s->kernel_irqchip_split = ON_OFF_AUTO_ON;
break;
default:
/* The value was checked in visit_type_OnOffSplit() above. If
* we get here, then something is wrong in QEMU.
*/
abort();
}
}
}
bool kvm_kernel_irqchip_allowed(void)
{
return kvm_state->kernel_irqchip_allowed;
}
bool kvm_kernel_irqchip_required(void)
{
return kvm_state->kernel_irqchip_required;
}
bool kvm_kernel_irqchip_split(void)
{
return kvm_state->kernel_irqchip_split == ON_OFF_AUTO_ON;
}
static void kvm_accel_instance_init(Object *obj)
{
KVMState *s = KVM_STATE(obj);
s->kvm_shadow_mem = -1;
s->kernel_irqchip_allowed = true;
s->kernel_irqchip_split = ON_OFF_AUTO_AUTO;
}
static void kvm_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
@@ -3072,24 +2872,11 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data)
ac->init_machine = kvm_init;
ac->has_memory = kvm_accel_has_memory;
ac->allowed = &kvm_allowed;
object_class_property_add(oc, "kernel-irqchip", "on|off|split",
NULL, kvm_set_kernel_irqchip,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "kernel-irqchip",
"Configure KVM in-kernel irqchip", &error_abort);
object_class_property_add(oc, "kvm-shadow-mem", "int",
kvm_get_kvm_shadow_mem, kvm_set_kvm_shadow_mem,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "kvm-shadow-mem",
"KVM shadow MMU size", &error_abort);
}
static const TypeInfo kvm_accel_type = {
.name = TYPE_KVM_ACCEL,
.parent = TYPE_ACCEL,
.instance_init = kvm_accel_instance_init,
.class_init = kvm_accel_class_init,
.instance_size = sizeof(KVMState),
};
-12
View File
@@ -138,18 +138,6 @@ void kvm_irqchip_commit_routes(KVMState *s)
{
}
void kvm_irqchip_add_change_notifier(Notifier *n)
{
}
void kvm_irqchip_remove_change_notifier(Notifier *n)
{
}
void kvm_irqchip_change_notify(void)
{
}
int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter)
{
return -ENOSYS;
+2
View File
@@ -11,8 +11,10 @@
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "tcg/tcg.h"
#include "exec/cpu-common.h"
#include "exec/exec-all.h"
void tb_flush(CPUState *cpu)
-1
View File
@@ -6,4 +6,3 @@ obj-y += translator.o
obj-$(CONFIG_USER_ONLY) += user-exec.o
obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o
obj-$(CONFIG_PLUGIN) += plugin-gen.o
-54
View File
@@ -1,54 +0,0 @@
/*
* Common Atomic Helper Functions
*
* This file should be included before the various instantiations of
* the atomic_template.h helpers.
*
* Copyright (c) 2019 Linaro
* Written by Alex Bennée <alex.bennee@linaro.org>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
static inline
void atomic_trace_rmw_pre(CPUArchState *env, target_ulong addr, uint16_t info)
{
CPUState *cpu = env_cpu(env);
trace_guest_mem_before_exec(cpu, addr, info);
trace_guest_mem_before_exec(cpu, addr, info | TRACE_MEM_ST);
}
static inline void
atomic_trace_rmw_post(CPUArchState *env, target_ulong addr, uint16_t info)
{
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info | TRACE_MEM_ST);
}
static inline
void atomic_trace_ld_pre(CPUArchState *env, target_ulong addr, uint16_t info)
{
trace_guest_mem_before_exec(env_cpu(env), addr, info);
}
static inline
void atomic_trace_ld_post(CPUArchState *env, target_ulong addr, uint16_t info)
{
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
}
static inline
void atomic_trace_st_pre(CPUArchState *env, target_ulong addr, uint16_t info)
{
trace_guest_mem_before_exec(env_cpu(env), addr, info);
}
static inline
void atomic_trace_st_post(CPUArchState *env, target_ulong addr, uint16_t info)
{
qemu_plugin_vcpu_mem_cb(env_cpu(env), addr, info);
}
+49 -52
View File
@@ -18,7 +18,6 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/plugin.h"
#include "trace/mem.h"
#if DATA_SIZE == 16
@@ -60,14 +59,37 @@
# define ABI_TYPE uint32_t
#endif
#define ATOMIC_TRACE_RMW do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
\
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
trace_guest_mem_before_exec(env_cpu(env), addr, \
info | TRACE_MEM_ST); \
} while (0)
#define ATOMIC_TRACE_LD do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, false); \
\
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
} while (0)
# define ATOMIC_TRACE_ST do { \
uint8_t info = glue(trace_mem_build_info_no_se, MEND)(SHIFT, true); \
\
trace_guest_mem_before_exec(env_cpu(env), addr, info); \
} while (0)
/* Define host-endian atomic operations. Note that END is used within
the ATOMIC_NAME macro, and redefined below. */
#if DATA_SIZE == 1
# define END
# define MEND _be /* either le or be would be fine */
#elif defined(HOST_WORDS_BIGENDIAN)
# define END _be
# define MEND _be
#else
# define END _le
# define MEND _le
#endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
@@ -76,17 +98,14 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
ATOMIC_MMU_IDX);
atomic_trace_rmw_pre(env, addr, info);
ATOMIC_TRACE_RMW;
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, cmpv, newv);
#else
ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv);
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return ret;
}
@@ -96,13 +115,10 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
ATOMIC_MMU_IDX);
atomic_trace_ld_pre(env, addr, info);
ATOMIC_TRACE_LD;
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
atomic_trace_ld_post(env, addr, info);
return val;
}
@@ -111,13 +127,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
uint16_t info = trace_mem_build_info(SHIFT, false, 0, true,
ATOMIC_MMU_IDX);
atomic_trace_st_pre(env, addr, info);
ATOMIC_TRACE_ST;
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_st_post(env, addr, info);
}
#endif
#else
@@ -127,29 +140,24 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false,
ATOMIC_MMU_IDX);
atomic_trace_rmw_pre(env, addr, info);
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return ret;
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \
ABI_TYPE val EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
ATOMIC_MMU_IDX); \
atomic_trace_rmw_pre(env, addr, info); \
\
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, val); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return ret; \
}
@@ -178,9 +186,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE cmp, old, new, val = xval; \
uint16_t info = trace_mem_build_info(SHIFT, false, 0, false, \
ATOMIC_MMU_IDX); \
atomic_trace_rmw_pre(env, addr, info); \
\
ATOMIC_TRACE_RMW; \
smp_mb(); \
cmp = atomic_read__nocheck(haddr); \
do { \
@@ -188,7 +195,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
cmp = atomic_cmpxchg__nocheck(haddr, old, new); \
} while (cmp != old); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return RET; \
}
@@ -206,6 +212,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
#endif /* DATA SIZE >= 16 */
#undef END
#undef MEND
#if DATA_SIZE > 1
@@ -213,8 +220,10 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
within the ATOMIC_NAME macro. */
#ifdef HOST_WORDS_BIGENDIAN
# define END _le
# define MEND _le
#else
# define END _be
# define MEND _be
#endif
ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
@@ -223,17 +232,14 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
DATA_TYPE ret;
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
ATOMIC_MMU_IDX);
atomic_trace_rmw_pre(env, addr, info);
ATOMIC_TRACE_RMW;
#if DATA_SIZE == 16
ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv));
#else
ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv));
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return BSWAP(ret);
}
@@ -243,13 +249,10 @@ ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS)
{
ATOMIC_MMU_DECLS;
DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP;
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
ATOMIC_MMU_IDX);
atomic_trace_ld_pre(env, addr, info);
ATOMIC_TRACE_LD;
val = atomic16_read(haddr);
ATOMIC_MMU_CLEANUP;
atomic_trace_ld_post(env, addr, info);
return BSWAP(val);
}
@@ -258,15 +261,11 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr,
{
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, true,
ATOMIC_MMU_IDX);
val = BSWAP(val);
atomic_trace_st_pre(env, addr, info);
ATOMIC_TRACE_ST;
val = BSWAP(val);
atomic16_set(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_st_post(env, addr, info);
}
#endif
#else
@@ -276,29 +275,24 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr,
ATOMIC_MMU_DECLS;
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP;
ABI_TYPE ret;
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, false,
ATOMIC_MMU_IDX);
atomic_trace_rmw_pre(env, addr, info);
ATOMIC_TRACE_RMW;
ret = atomic_xchg__nocheck(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr, info);
return BSWAP(ret);
}
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ABI_TYPE val EXTRA_ARGS) \
ABI_TYPE val EXTRA_ARGS) \
{ \
ATOMIC_MMU_DECLS; \
DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
DATA_TYPE ret; \
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
false, ATOMIC_MMU_IDX); \
atomic_trace_rmw_pre(env, addr, info); \
\
ATOMIC_TRACE_RMW; \
ret = atomic_##X(haddr, BSWAP(val)); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return BSWAP(ret); \
}
@@ -325,9 +319,8 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ATOMIC_MMU_DECLS; \
XDATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; \
XDATA_TYPE ldo, ldn, old, new, val = xval; \
uint16_t info = trace_mem_build_info(SHIFT, false, MO_BSWAP, \
false, ATOMIC_MMU_IDX); \
atomic_trace_rmw_pre(env, addr, info); \
\
ATOMIC_TRACE_RMW; \
smp_mb(); \
ldn = atomic_read__nocheck(haddr); \
do { \
@@ -335,7 +328,6 @@ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, target_ulong addr, \
ldn = atomic_cmpxchg__nocheck(haddr, ldo, BSWAP(new)); \
} while (ldo != ldn); \
ATOMIC_MMU_CLEANUP; \
atomic_trace_rmw_post(env, addr, info); \
return RET; \
}
@@ -360,8 +352,13 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
#endif /* DATA_SIZE >= 16 */
#undef END
#undef MEND
#endif /* DATA_SIZE > 1 */
#undef ATOMIC_TRACE_ST
#undef ATOMIC_TRACE_LD
#undef ATOMIC_TRACE_RMW
#undef BSWAP
#undef ABI_TYPE
#undef DATA_TYPE
+17 -17
View File
@@ -23,7 +23,7 @@
#include "trace.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg.h"
#include "tcg.h"
#include "qemu/atomic.h"
#include "sysemu/qtest.h"
#include "qemu/timer.h"
@@ -156,7 +156,7 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
#if defined(DEBUG_DISAS)
if (qemu_loglevel_mask(CPU_LOG_TB_CPU)
&& qemu_log_in_addr_range(itb->pc)) {
FILE *logfile = qemu_log_lock();
qemu_log_lock();
int flags = 0;
if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) {
flags |= CPU_DUMP_FPU;
@@ -165,10 +165,11 @@ static inline tcg_target_ulong cpu_tb_exec(CPUState *cpu, TranslationBlock *itb)
flags |= CPU_DUMP_CCOP;
#endif
log_cpu_state(cpu, flags);
qemu_log_unlock(logfile);
qemu_log_unlock();
}
#endif /* DEBUG_DISAS */
cpu->can_do_io = !use_icount;
ret = tcg_qemu_tb_exec(env, tb_ptr);
cpu->can_do_io = 1;
last_tb = (TranslationBlock *)(ret & ~TB_EXIT_MASK);
@@ -238,10 +239,10 @@ void cpu_exec_step_atomic(CPUState *cpu)
uint32_t flags;
uint32_t cflags = 1;
uint32_t cf_mask = cflags & CF_HASH_MASK;
/* volatile because we modify it between setjmp and longjmp */
volatile bool in_exclusive_region = false;
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
start_exclusive();
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
if (tb == NULL) {
mmap_lock();
@@ -249,8 +250,11 @@ void cpu_exec_step_atomic(CPUState *cpu)
mmap_unlock();
}
start_exclusive();
/* Since we got here, we know that parallel_cpus must be true. */
parallel_cpus = false;
in_exclusive_region = true;
cc->cpu_exec_enter(cpu);
/* execute the generated code */
trace_exec_tb(tb, pc);
@@ -268,18 +272,16 @@ void cpu_exec_step_atomic(CPUState *cpu)
qemu_mutex_unlock_iothread();
}
assert_no_pages_locked();
qemu_plugin_disable_mem_helpers(cpu);
}
/*
* As we start the exclusive region before codegen we must still
* be in the region if we longjump out of either the codegen or
* the execution.
*/
g_assert(cpu_in_exclusive_context(cpu));
parallel_cpus = true;
end_exclusive();
if (in_exclusive_region) {
/* We might longjump out of either the codegen or the
* execution, so must make sure we only end the exclusive
* region if we started it.
*/
parallel_cpus = true;
end_exclusive();
}
}
struct tb_desc {
@@ -703,8 +705,6 @@ int cpu_exec(CPUState *cpu)
if (qemu_mutex_iothread_locked()) {
qemu_mutex_unlock_iothread();
}
qemu_plugin_disable_mem_helpers(cpu);
assert_no_pages_locked();
}
+395 -865
View File
File diff suppressed because it is too large. Load diff
-932
View File
@@ -1,932 +0,0 @@
/*
* plugin-gen.c - TCG-related bits of plugin infrastructure
*
* Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
* License: GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*
* We support instrumentation at an instruction granularity. That is,
* if a plugin wants to instrument the memory accesses performed by a
* particular instruction, it can just do that instead of instrumenting
* all memory accesses. Thus, in order to do this we first have to
* translate a TB, so that plugins can decide what/where to instrument.
*
* Injecting the desired instrumentation could be done with a second
* translation pass that combined the instrumentation requests, but that
* would be ugly and inefficient since we would decode the guest code twice.
* Instead, during TB translation we add "empty" instrumentation calls for all
* possible instrumentation events, and then once we collect the instrumentation
* requests from plugins, we either "fill in" those empty events or remove them
* if they have no requests.
*
* When "filling in" an event we first copy the empty callback's TCG ops. This
* might seem unnecessary, but it is done to support an arbitrary number
* of callbacks per event. Take for example a regular instruction callback.
* We first generate a callback to an empty helper function. Then, if two
* plugins register one callback each for this instruction, we make two copies
* of the TCG ops generated for the empty callback, substituting the function
* pointer that points to the empty helper function with the plugins' desired
* callback functions. After that we remove the empty callback's ops.
*
* Note that the location in TCGOp.args[] of the pointer to a helper function
* varies across different guest and host architectures. Instead of duplicating
* the logic that figures this out, we rely on the fact that the empty
* callbacks point to empty functions that are unique pointers in the program.
* Thus, to find the right location we just have to look for a match in
* TCGOp.args[]. This is the main reason why we first copy an empty callback's
* TCG ops and then fill them in; regardless of whether we have one or many
* callbacks for that event, the logic to add all of them is the same.
*
* When generating more than one callback per event, we make a small
* optimization to avoid generating redundant operations. For instance, for the
* second and all subsequent callbacks of an event, we do not need to reload the
* CPU's index into a TCG temp, since the first callback did it already.
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "tcg/tcg.h"
#include "tcg/tcg-op.h"
#include "trace/mem.h"
#include "exec/exec-all.h"
#include "exec/plugin-gen.h"
#include "exec/translator.h"
#ifdef CONFIG_SOFTMMU
# define CONFIG_SOFTMMU_GATE 1
#else
# define CONFIG_SOFTMMU_GATE 0
#endif
/*
* plugin_cb_start TCG op args[]:
* 0: enum plugin_gen_from
* 1: enum plugin_gen_cb
* 2: set to 1 for mem callback that is a write, 0 otherwise.
*/
enum plugin_gen_from {
PLUGIN_GEN_FROM_TB,
PLUGIN_GEN_FROM_INSN,
PLUGIN_GEN_FROM_MEM,
PLUGIN_GEN_AFTER_INSN,
PLUGIN_GEN_N_FROMS,
};
enum plugin_gen_cb {
PLUGIN_GEN_CB_UDATA,
PLUGIN_GEN_CB_INLINE,
PLUGIN_GEN_CB_MEM,
PLUGIN_GEN_ENABLE_MEM_HELPER,
PLUGIN_GEN_DISABLE_MEM_HELPER,
PLUGIN_GEN_N_CBS,
};
/*
* These helpers are stubs that get dynamically switched out for calls
* direct to the plugin if they are subscribed to.
*/
void HELPER(plugin_vcpu_udata_cb)(uint32_t cpu_index, void *udata)
{ }
void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index,
qemu_plugin_meminfo_t info, uint64_t vaddr,
void *userdata)
{ }
static void do_gen_mem_cb(TCGv vaddr, uint32_t info)
{
TCGv_i32 cpu_index = tcg_temp_new_i32();
TCGv_i32 meminfo = tcg_const_i32(info);
TCGv_i64 vaddr64 = tcg_temp_new_i64();
TCGv_ptr udata = tcg_const_ptr(NULL);
tcg_gen_ld_i32(cpu_index, cpu_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
tcg_gen_extu_tl_i64(vaddr64, vaddr);
gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, vaddr64, udata);
tcg_temp_free_ptr(udata);
tcg_temp_free_i64(vaddr64);
tcg_temp_free_i32(meminfo);
tcg_temp_free_i32(cpu_index);
}
static void gen_empty_udata_cb(void)
{
TCGv_i32 cpu_index = tcg_temp_new_i32();
TCGv_ptr udata = tcg_const_ptr(NULL); /* will be overwritten later */
tcg_gen_ld_i32(cpu_index, cpu_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
gen_helper_plugin_vcpu_udata_cb(cpu_index, udata);
tcg_temp_free_ptr(udata);
tcg_temp_free_i32(cpu_index);
}
/*
* For now we only support addi_i64.
* When we support more ops, we can generate one empty inline cb for each.
*/
static void gen_empty_inline_cb(void)
{
TCGv_i64 val = tcg_temp_new_i64();
TCGv_ptr ptr = tcg_const_ptr(NULL); /* overwritten later */
tcg_gen_ld_i64(val, ptr, 0);
/* pass an immediate != 0 so that it doesn't get optimized away */
tcg_gen_addi_i64(val, val, 0xdeadface);
tcg_gen_st_i64(val, ptr, 0);
tcg_temp_free_ptr(ptr);
tcg_temp_free_i64(val);
}
static void gen_empty_mem_cb(TCGv addr, uint32_t info)
{
do_gen_mem_cb(addr, info);
}
/*
* Share the same function for enable/disable. When enabling, the NULL
* pointer will be overwritten later.
*/
static void gen_empty_mem_helper(void)
{
TCGv_ptr ptr;
ptr = tcg_const_ptr(NULL);
tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) -
offsetof(ArchCPU, env));
tcg_temp_free_ptr(ptr);
}
static inline
void gen_plugin_cb_start(enum plugin_gen_from from,
enum plugin_gen_cb type, unsigned wr)
{
TCGOp *op;
tcg_gen_plugin_cb_start(from, type, wr);
op = tcg_last_op();
QSIMPLEQ_INSERT_TAIL(&tcg_ctx->plugin_ops, op, plugin_link);
}
static void gen_wrapped(enum plugin_gen_from from,
enum plugin_gen_cb type, void (*func)(void))
{
gen_plugin_cb_start(from, type, 0);
func();
tcg_gen_plugin_cb_end();
}
static inline void plugin_gen_empty_callback(enum plugin_gen_from from)
{
switch (from) {
case PLUGIN_GEN_AFTER_INSN:
gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER,
gen_empty_mem_helper);
break;
case PLUGIN_GEN_FROM_INSN:
/*
* Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being
* the first callback of an instruction
*/
gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER,
gen_empty_mem_helper);
/* fall through */
case PLUGIN_GEN_FROM_TB:
gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb);
gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb);
break;
default:
g_assert_not_reached();
}
}
union mem_gen_fn {
void (*mem_fn)(TCGv, uint32_t);
void (*inline_fn)(void);
};
static void gen_mem_wrapped(enum plugin_gen_cb type,
const union mem_gen_fn *f, TCGv addr,
uint32_t info, bool is_mem)
{
int wr = !!(info & TRACE_MEM_ST);
gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, wr);
if (is_mem) {
f->mem_fn(addr, info);
} else {
f->inline_fn();
}
tcg_gen_plugin_cb_end();
}
void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info)
{
union mem_gen_fn fn;
fn.mem_fn = gen_empty_mem_cb;
gen_mem_wrapped(PLUGIN_GEN_CB_MEM, &fn, addr, info, true);
fn.inline_fn = gen_empty_inline_cb;
gen_mem_wrapped(PLUGIN_GEN_CB_INLINE, &fn, 0, info, false);
}
static TCGOp *find_op(TCGOp *op, TCGOpcode opc)
{
while (op) {
if (op->opc == opc) {
return op;
}
op = QTAILQ_NEXT(op, link);
}
return NULL;
}
static TCGOp *rm_ops_range(TCGOp *begin, TCGOp *end)
{
TCGOp *ret = QTAILQ_NEXT(end, link);
QTAILQ_REMOVE_SEVERAL(&tcg_ctx->ops, begin, end, link);
return ret;
}
/* remove all ops until (and including) plugin_cb_end */
static TCGOp *rm_ops(TCGOp *op)
{
TCGOp *end_op = find_op(op, INDEX_op_plugin_cb_end);
tcg_debug_assert(end_op);
return rm_ops_range(op, end_op);
}
static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op)
{
*begin_op = QTAILQ_NEXT(*begin_op, link);
tcg_debug_assert(*begin_op);
op = tcg_op_insert_after(tcg_ctx, op, (*begin_op)->opc);
memcpy(op->args, (*begin_op)->args, sizeof(op->args));
return op;
}
static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc)
{
op = copy_op_nocheck(begin_op, op);
tcg_debug_assert((*begin_op)->opc == opc);
return op;
}
static TCGOp *copy_extu_i32_i64(TCGOp **begin_op, TCGOp *op)
{
if (TCG_TARGET_REG_BITS == 32) {
/* mov_i32 */
op = copy_op(begin_op, op, INDEX_op_mov_i32);
/* movi_i32 */
op = copy_op(begin_op, op, INDEX_op_movi_i32);
} else {
/* extu_i32_i64 */
op = copy_op(begin_op, op, INDEX_op_extu_i32_i64);
}
return op;
}
static TCGOp *copy_mov_i64(TCGOp **begin_op, TCGOp *op)
{
if (TCG_TARGET_REG_BITS == 32) {
/* 2x mov_i32 */
op = copy_op(begin_op, op, INDEX_op_mov_i32);
op = copy_op(begin_op, op, INDEX_op_mov_i32);
} else {
/* mov_i64 */
op = copy_op(begin_op, op, INDEX_op_mov_i64);
}
return op;
}
static TCGOp *copy_movi_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
{
if (TCG_TARGET_REG_BITS == 32) {
/* 2x movi_i32 */
op = copy_op(begin_op, op, INDEX_op_movi_i32);
op->args[1] = v;
op = copy_op(begin_op, op, INDEX_op_movi_i32);
op->args[1] = v >> 32;
} else {
/* movi_i64 */
op = copy_op(begin_op, op, INDEX_op_movi_i64);
op->args[1] = v;
}
return op;
}
static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
{
if (UINTPTR_MAX == UINT32_MAX) {
/* movi_i32 */
op = copy_op(begin_op, op, INDEX_op_movi_i32);
op->args[1] = (uintptr_t)ptr;
} else {
/* movi_i64 */
op = copy_movi_i64(begin_op, op, (uint64_t)(uintptr_t)ptr);
}
return op;
}
static TCGOp *copy_const_i64(TCGOp **begin_op, TCGOp *op, uint64_t v)
{
return copy_movi_i64(begin_op, op, v);
}
static TCGOp *copy_extu_tl_i64(TCGOp **begin_op, TCGOp *op)
{
if (TARGET_LONG_BITS == 32) {
/* extu_i32_i64 */
op = copy_extu_i32_i64(begin_op, op);
} else {
/* mov_i64 */
op = copy_mov_i64(begin_op, op);
}
return op;
}
static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op)
{
if (TCG_TARGET_REG_BITS == 32) {
/* 2x ld_i32 */
op = copy_op(begin_op, op, INDEX_op_ld_i32);
op = copy_op(begin_op, op, INDEX_op_ld_i32);
} else {
/* ld_i64 */
op = copy_op(begin_op, op, INDEX_op_ld_i64);
}
return op;
}
static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op)
{
if (TCG_TARGET_REG_BITS == 32) {
/* 2x st_i32 */
op = copy_op(begin_op, op, INDEX_op_st_i32);
op = copy_op(begin_op, op, INDEX_op_st_i32);
} else {
/* st_i64 */
op = copy_op(begin_op, op, INDEX_op_st_i64);
}
return op;
}
static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op)
{
if (TCG_TARGET_REG_BITS == 32) {
/* all 32-bit backends must implement add2_i32 */
g_assert(TCG_TARGET_HAS_add2_i32);
op = copy_op(begin_op, op, INDEX_op_add2_i32);
} else {
op = copy_op(begin_op, op, INDEX_op_add_i64);
}
return op;
}
static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op)
{
if (UINTPTR_MAX == UINT32_MAX) {
/* st_i32 */
op = copy_op(begin_op, op, INDEX_op_st_i32);
} else {
/* st_i64 */
op = copy_st_i64(begin_op, op);
}
return op;
}
static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func,
void *func, unsigned tcg_flags, int *cb_idx)
{
/* copy all ops until the call */
do {
op = copy_op_nocheck(begin_op, op);
} while (op->opc != INDEX_op_call);
/* fill in the op call */
op->param1 = (*begin_op)->param1;
op->param2 = (*begin_op)->param2;
tcg_debug_assert(op->life == 0);
if (*cb_idx == -1) {
int i;
/*
* Instead of working out the position of the callback in args[], just
* look for @empty_func, since it should be a unique pointer.
*/
for (i = 0; i < MAX_OPC_PARAM_ARGS; i++) {
if ((uintptr_t)(*begin_op)->args[i] == (uintptr_t)empty_func) {
*cb_idx = i;
break;
}
}
tcg_debug_assert(i < MAX_OPC_PARAM_ARGS);
}
op->args[*cb_idx] = (uintptr_t)func;
op->args[*cb_idx + 1] = tcg_flags;
return op;
}
static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb,
TCGOp *begin_op, TCGOp *op, int *cb_idx)
{
/* const_ptr */
op = copy_const_ptr(&begin_op, op, cb->userp);
/* copy the ld_i32, but note that we only have to copy it once */
begin_op = QTAILQ_NEXT(begin_op, link);
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
if (*cb_idx == -1) {
op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
memcpy(op->args, begin_op->args, sizeof(op->args));
}
/* call */
op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb),
cb->f.vcpu_udata, cb->tcg_flags, cb_idx);
return op;
}
static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb,
TCGOp *begin_op, TCGOp *op,
int *unused)
{
/* const_ptr */
op = copy_const_ptr(&begin_op, op, cb->userp);
/* ld_i64 */
op = copy_ld_i64(&begin_op, op);
/* const_i64 */
op = copy_const_i64(&begin_op, op, cb->inline_insn.imm);
/* add_i64 */
op = copy_add_i64(&begin_op, op);
/* st_i64 */
op = copy_st_i64(&begin_op, op);
return op;
}
static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb,
TCGOp *begin_op, TCGOp *op, int *cb_idx)
{
enum plugin_gen_cb type = begin_op->args[1];
tcg_debug_assert(type == PLUGIN_GEN_CB_MEM);
/* const_i32 == movi_i32 ("info", so it remains as is) */
op = copy_op(&begin_op, op, INDEX_op_movi_i32);
/* const_ptr */
op = copy_const_ptr(&begin_op, op, cb->userp);
/* copy the ld_i32, but note that we only have to copy it once */
begin_op = QTAILQ_NEXT(begin_op, link);
tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
if (*cb_idx == -1) {
op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
memcpy(op->args, begin_op->args, sizeof(op->args));
}
/* extu_tl_i64 */
op = copy_extu_tl_i64(&begin_op, op);
if (type == PLUGIN_GEN_CB_MEM) {
/* call */
op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb),
cb->f.vcpu_udata, cb->tcg_flags, cb_idx);
}
return op;
}
typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb,
TCGOp *begin_op, TCGOp *op, int *intp);
typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb);
static bool op_ok(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
{
return true;
}
static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb)
{
int w;
w = op->args[2];
return !!(cb->rw & (w + 1));
}
static inline
void inject_cb_type(const GArray *cbs, TCGOp *begin_op, inject_fn inject,
op_ok_fn ok)
{
TCGOp *end_op;
TCGOp *op;
int cb_idx = -1;
int i;
if (!cbs || cbs->len == 0) {
rm_ops(begin_op);
return;
}
end_op = find_op(begin_op, INDEX_op_plugin_cb_end);
tcg_debug_assert(end_op);
op = end_op;
for (i = 0; i < cbs->len; i++) {
struct qemu_plugin_dyn_cb *cb =
&g_array_index(cbs, struct qemu_plugin_dyn_cb, i);
if (!ok(begin_op, cb)) {
continue;
}
op = inject(cb, begin_op, op, &cb_idx);
}
rm_ops_range(begin_op, end_op);
}
static void
inject_udata_cb(const GArray *cbs, TCGOp *begin_op)
{
inject_cb_type(cbs, begin_op, append_udata_cb, op_ok);
}
static void
inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok)
{
inject_cb_type(cbs, begin_op, append_inline_cb, ok);
}
static void
inject_mem_cb(const GArray *cbs, TCGOp *begin_op)
{
inject_cb_type(cbs, begin_op, append_mem_cb, op_rw);
}
/* we could change the ops in place, but we can reuse more code by copying */
static void inject_mem_helper(TCGOp *begin_op, GArray *arr)
{
TCGOp *orig_op = begin_op;
TCGOp *end_op;
TCGOp *op;
end_op = find_op(begin_op, INDEX_op_plugin_cb_end);
tcg_debug_assert(end_op);
/* const ptr */
op = copy_const_ptr(&begin_op, end_op, arr);
/* st_ptr */
op = copy_st_ptr(&begin_op, op);
rm_ops_range(orig_op, end_op);
}
/*
* Tracking memory accesses performed from helpers requires extra work.
* If an instruction is emulated with helpers, we do two things:
* (1) copy the CB descriptors, and keep track of it so that they can be
* freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptors, so
* that we can read them at run-time (i.e. when the helper executes).
* This run-time access is performed from qemu_plugin_vcpu_mem_cb.
*
* Note that plugin_gen_disable_mem_helpers undoes (2). Since it
* is possible that the code we generate after the instruction is
* dead, we also add checks before generating tb_exit etc.
*/
static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn,
TCGOp *begin_op)
{
GArray *cbs[2];
GArray *arr;
size_t n_cbs, i;
cbs[0] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR];
cbs[1] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
n_cbs = 0;
for (i = 0; i < ARRAY_SIZE(cbs); i++) {
n_cbs += cbs[i]->len;
}
plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs;
if (likely(!plugin_insn->mem_helper)) {
rm_ops(begin_op);
return;
}
arr = g_array_sized_new(false, false,
sizeof(struct qemu_plugin_dyn_cb), n_cbs);
for (i = 0; i < ARRAY_SIZE(cbs); i++) {
g_array_append_vals(arr, cbs[i]->data, cbs[i]->len);
}
qemu_plugin_add_dyn_cb_arr(arr);
inject_mem_helper(begin_op, arr);
}
static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn,
TCGOp *begin_op)
{
if (likely(!plugin_insn->mem_helper)) {
rm_ops(begin_op);
return;
}
inject_mem_helper(begin_op, NULL);
}
/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
void plugin_gen_disable_mem_helpers(void)
{
TCGv_ptr ptr;
if (likely(tcg_ctx->plugin_insn == NULL ||
!tcg_ctx->plugin_insn->mem_helper)) {
return;
}
ptr = tcg_const_ptr(NULL);
tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) -
offsetof(ArchCPU, env));
tcg_temp_free_ptr(ptr);
tcg_ctx->plugin_insn->mem_helper = false;
}
static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op)
{
inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op);
}
static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op)
{
inject_inline_cb(ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok);
}
static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int insn_idx)
{
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op);
}
static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int insn_idx)
{
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
inject_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE],
begin_op, op_ok);
}
static void plugin_gen_mem_regular(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int insn_idx)
{
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
inject_mem_cb(insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], begin_op);
}
static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int insn_idx)
{
const GArray *cbs;
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE];
inject_inline_cb(cbs, begin_op, op_rw);
}
static void plugin_gen_enable_mem_helper(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int insn_idx)
{
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
inject_mem_enable_helper(insn, begin_op);
}
static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int insn_idx)
{
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
inject_mem_disable_helper(insn, begin_op);
}
static void plugin_inject_cb(const struct qemu_plugin_tb *ptb, TCGOp *begin_op,
int insn_idx)
{
enum plugin_gen_from from = begin_op->args[0];
enum plugin_gen_cb type = begin_op->args[1];
switch (from) {
case PLUGIN_GEN_FROM_TB:
switch (type) {
case PLUGIN_GEN_CB_UDATA:
plugin_gen_tb_udata(ptb, begin_op);
return;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_tb_inline(ptb, begin_op);
return;
default:
g_assert_not_reached();
}
case PLUGIN_GEN_FROM_INSN:
switch (type) {
case PLUGIN_GEN_CB_UDATA:
plugin_gen_insn_udata(ptb, begin_op, insn_idx);
return;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_insn_inline(ptb, begin_op, insn_idx);
return;
case PLUGIN_GEN_ENABLE_MEM_HELPER:
plugin_gen_enable_mem_helper(ptb, begin_op, insn_idx);
return;
default:
g_assert_not_reached();
}
case PLUGIN_GEN_FROM_MEM:
switch (type) {
case PLUGIN_GEN_CB_MEM:
plugin_gen_mem_regular(ptb, begin_op, insn_idx);
return;
case PLUGIN_GEN_CB_INLINE:
plugin_gen_mem_inline(ptb, begin_op, insn_idx);
return;
default:
g_assert_not_reached();
}
case PLUGIN_GEN_AFTER_INSN:
switch (type) {
case PLUGIN_GEN_DISABLE_MEM_HELPER:
plugin_gen_disable_mem_helper(ptb, begin_op, insn_idx);
return;
default:
g_assert_not_reached();
}
default:
g_assert_not_reached();
}
}
/* #define DEBUG_PLUGIN_GEN_OPS */
static void pr_ops(void)
{
#ifdef DEBUG_PLUGIN_GEN_OPS
TCGOp *op;
int i = 0;
QTAILQ_FOREACH(op, &tcg_ctx->ops, link) {
const char *name = "";
const char *type = "";
if (op->opc == INDEX_op_plugin_cb_start) {
switch (op->args[0]) {
case PLUGIN_GEN_FROM_TB:
name = "tb";
break;
case PLUGIN_GEN_FROM_INSN:
name = "insn";
break;
case PLUGIN_GEN_FROM_MEM:
name = "mem";
break;
case PLUGIN_GEN_AFTER_INSN:
name = "after insn";
break;
default:
break;
}
switch (op->args[1]) {
case PLUGIN_GEN_CB_UDATA:
type = "udata";
break;
case PLUGIN_GEN_CB_INLINE:
type = "inline";
break;
case PLUGIN_GEN_CB_MEM:
type = "mem";
break;
case PLUGIN_GEN_ENABLE_MEM_HELPER:
type = "enable mem helper";
break;
case PLUGIN_GEN_DISABLE_MEM_HELPER:
type = "disable mem helper";
break;
default:
break;
}
}
printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type);
i++;
}
#endif
}
static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb)
{
TCGOp *op;
int insn_idx;
pr_ops();
insn_idx = -1;
QSIMPLEQ_FOREACH(op, &tcg_ctx->plugin_ops, plugin_link) {
enum plugin_gen_from from = op->args[0];
enum plugin_gen_cb type = op->args[1];
tcg_debug_assert(op->opc == INDEX_op_plugin_cb_start);
/* ENABLE_MEM_HELPER is the first callback of an instruction */
if (from == PLUGIN_GEN_FROM_INSN &&
type == PLUGIN_GEN_ENABLE_MEM_HELPER) {
insn_idx++;
}
plugin_inject_cb(plugin_tb, op, insn_idx);
}
pr_ops();
}
bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb)
{
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
bool ret = false;
if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) {
ret = true;
QSIMPLEQ_INIT(&tcg_ctx->plugin_ops);
ptb->vaddr = tb->pc;
ptb->vaddr2 = -1;
get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1);
ptb->haddr2 = NULL;
plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
}
return ret;
}
void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
{
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
struct qemu_plugin_insn *pinsn;
pinsn = qemu_plugin_tb_insn_get(ptb);
tcg_ctx->plugin_insn = pinsn;
pinsn->vaddr = db->pc_next;
plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
/*
* Detect page crossing to get the new host address.
* Note that we skip this when haddr1 == NULL, e.g. when we're
* fetching instructions from a region not backed by RAM.
*/
if (likely(ptb->haddr1 != NULL && ptb->vaddr2 == -1) &&
unlikely((db->pc_next & TARGET_PAGE_MASK) !=
(db->pc_first & TARGET_PAGE_MASK))) {
get_page_addr_code_hostp(cpu->env_ptr, db->pc_next,
&ptb->haddr2);
ptb->vaddr2 = db->pc_next;
}
if (likely(ptb->vaddr2 == -1)) {
pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr;
} else {
pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2;
}
}
void plugin_gen_insn_end(void)
{
plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN);
}
void plugin_gen_tb_end(CPUState *cpu)
{
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
int i;
/* collect instrumentation requests */
qemu_plugin_tb_trans_cb(cpu, ptb);
/* inject the instrumentation at the appropriate places */
plugin_gen_inject(ptb);
/* clean up */
for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) {
if (ptb->cbs[i]) {
g_array_set_size(ptb->cbs[i], 0);
}
}
ptb->n = 0;
tcg_ctx->plugin_insn = NULL;
}
-5
View File
@@ -1,5 +0,0 @@
#ifdef CONFIG_PLUGIN
/* Note: no TCG flags because those are overwritten later */
DEF_HELPER_2(plugin_vcpu_udata_cb, void, i32, ptr)
DEF_HELPER_4(plugin_vcpu_mem_cb, void, i32, i32, i64, ptr)
#endif
+5 -145
View File
@@ -25,28 +25,14 @@
#include "qemu/osdep.h"
#include "sysemu/accel.h"
#include "sysemu/sysemu.h"
#include "sysemu/tcg.h"
#include "qom/object.h"
#include "cpu.h"
#include "sysemu/cpus.h"
#include "qemu/main-loop.h"
#include "tcg/tcg.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
#include "hw/boards.h"
#include "qapi/qapi-builtin-visit.h"
typedef struct TCGState {
AccelState parent_obj;
bool mttcg_enabled;
unsigned long tb_size;
} TCGState;
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
#define TCG_STATE(obj) \
OBJECT_CHECK(TCGState, (obj), TYPE_TCG_ACCEL)
unsigned long tcg_tb_size;
/* mask must never be zero, except for A20 change call */
static void tcg_handle_interrupt(CPUState *cpu, int mask)
@@ -73,153 +59,27 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask)
}
}
/*
* We default to false if we know other options have been enabled
* which are currently incompatible with MTTCG. Otherwise when each
* guest (target) has been updated to support:
* - atomic instructions
* - memory ordering primitives (barriers)
* they can set the appropriate CONFIG flags in ${target}-softmmu.mak
*
* Once a guest architecture has been converted to the new primitives
* there are two remaining limitations to check.
*
* - The guest can't be oversized (e.g. 64 bit guest on 32 bit host)
* - The host must have a stronger memory order than the guest
*
* It may be possible in future to support strong guests on weak hosts
* but that will require tagging all load/stores in a guest with their
* implicit memory order requirements which would likely slow things
* down a lot.
*/
static bool check_tcg_memory_orders_compatible(void)
{
#if defined(TCG_GUEST_DEFAULT_MO) && defined(TCG_TARGET_DEFAULT_MO)
return (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) == 0;
#else
return false;
#endif
}
static bool default_mttcg_enabled(void)
{
if (use_icount || TCG_OVERSIZED_GUEST) {
return false;
} else {
#ifdef TARGET_SUPPORTS_MTTCG
return check_tcg_memory_orders_compatible();
#else
return false;
#endif
}
}
static void tcg_accel_instance_init(Object *obj)
{
TCGState *s = TCG_STATE(obj);
s->mttcg_enabled = default_mttcg_enabled();
}
static int tcg_init(MachineState *ms)
{
TCGState *s = TCG_STATE(current_accel());
tcg_exec_init(s->tb_size * 1024 * 1024);
tcg_exec_init(tcg_tb_size * 1024 * 1024);
cpu_interrupt_handler = tcg_handle_interrupt;
mttcg_enabled = s->mttcg_enabled;
return 0;
}
static char *tcg_get_thread(Object *obj, Error **errp)
{
TCGState *s = TCG_STATE(obj);
return g_strdup(s->mttcg_enabled ? "multi" : "single");
}
static void tcg_set_thread(Object *obj, const char *value, Error **errp)
{
TCGState *s = TCG_STATE(obj);
if (strcmp(value, "multi") == 0) {
if (TCG_OVERSIZED_GUEST) {
error_setg(errp, "No MTTCG when guest word size > hosts");
} else if (use_icount) {
error_setg(errp, "No MTTCG when icount is enabled");
} else {
#ifndef TARGET_SUPPORTS_MTTCG
warn_report("Guest not yet converted to MTTCG - "
"you may get unexpected results");
#endif
if (!check_tcg_memory_orders_compatible()) {
warn_report("Guest expects a stronger memory ordering "
"than the host provides");
error_printf("This may cause strange/hard to debug errors\n");
}
s->mttcg_enabled = true;
}
} else if (strcmp(value, "single") == 0) {
s->mttcg_enabled = false;
} else {
error_setg(errp, "Invalid 'thread' setting %s", value);
}
}
static void tcg_get_tb_size(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
TCGState *s = TCG_STATE(obj);
uint32_t value = s->tb_size;
visit_type_uint32(v, name, &value, errp);
}
static void tcg_set_tb_size(Object *obj, Visitor *v,
const char *name, void *opaque,
Error **errp)
{
TCGState *s = TCG_STATE(obj);
Error *error = NULL;
uint32_t value;
visit_type_uint32(v, name, &value, &error);
if (error) {
error_propagate(errp, error);
return;
}
s->tb_size = value;
}
static void tcg_accel_class_init(ObjectClass *oc, void *data)
{
AccelClass *ac = ACCEL_CLASS(oc);
ac->name = "tcg";
ac->init_machine = tcg_init;
ac->allowed = &tcg_allowed;
object_class_property_add_str(oc, "thread",
tcg_get_thread,
tcg_set_thread,
NULL);
object_class_property_add(oc, "tb-size", "int",
tcg_get_tb_size, tcg_set_tb_size,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "tb-size",
"TCG translation block cache size", &error_abort);
}
#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg")
static const TypeInfo tcg_accel_type = {
.name = TYPE_TCG_ACCEL,
.parent = TYPE_ACCEL,
.instance_init = tcg_accel_instance_init,
.class_init = tcg_accel_class_init,
.instance_size = sizeof(TCGState),
};
static void register_accel_types(void)
+183 -117
View File
@@ -21,9 +21,51 @@
#include "qemu/host-utils.h"
#include "cpu.h"
#include "exec/helper-proto.h"
#include "tcg/tcg-gvec-desc.h"
#include "tcg-gvec-desc.h"
/* Virtually all hosts support 16-byte vectors. Those that don't can emulate
* them via GCC's generic vector extension. This turns out to be simpler and
* more reliable than getting the compiler to autovectorize.
*
* In tcg-op-gvec.c, we asserted that both the size and alignment of the data
* are multiples of 16.
*
* When the compiler does not support all of the operations we require, the
* loops are written so that we can always fall back on the base types.
*/
#ifdef CONFIG_VECTOR16
typedef uint8_t vec8 __attribute__((vector_size(16)));
typedef uint16_t vec16 __attribute__((vector_size(16)));
typedef uint32_t vec32 __attribute__((vector_size(16)));
typedef uint64_t vec64 __attribute__((vector_size(16)));
typedef int8_t svec8 __attribute__((vector_size(16)));
typedef int16_t svec16 __attribute__((vector_size(16)));
typedef int32_t svec32 __attribute__((vector_size(16)));
typedef int64_t svec64 __attribute__((vector_size(16)));
#define DUP16(X) { X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X }
#define DUP8(X) { X, X, X, X, X, X, X, X }
#define DUP4(X) { X, X, X, X }
#define DUP2(X) { X, X }
#else
typedef uint8_t vec8;
typedef uint16_t vec16;
typedef uint32_t vec32;
typedef uint64_t vec64;
typedef int8_t svec8;
typedef int16_t svec16;
typedef int32_t svec32;
typedef int64_t svec64;
#define DUP16(X) X
#define DUP8(X) X
#define DUP4(X) X
#define DUP2(X) X
#endif /* CONFIG_VECTOR16 */
static inline void clear_high(void *d, intptr_t oprsz, uint32_t desc)
{
intptr_t maxsz = simd_maxsz(desc);
@@ -41,8 +83,8 @@ void HELPER(gvec_add8)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) + *(uint8_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = *(vec8 *)(a + i) + *(vec8 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -52,8 +94,8 @@ void HELPER(gvec_add16)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) + *(uint16_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = *(vec16 *)(a + i) + *(vec16 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -63,8 +105,8 @@ void HELPER(gvec_add32)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) + *(uint32_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = *(vec32 *)(a + i) + *(vec32 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -74,8 +116,8 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) + *(uint64_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) + *(vec64 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -83,10 +125,11 @@ void HELPER(gvec_add64)(void *d, void *a, void *b, uint32_t desc)
void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec8 vecb = (vec8)DUP16(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) + (uint8_t)b;
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = *(vec8 *)(a + i) + vecb;
}
clear_high(d, oprsz, desc);
}
@@ -94,10 +137,11 @@ void HELPER(gvec_adds8)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec16 vecb = (vec16)DUP8(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) + (uint16_t)b;
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = *(vec16 *)(a + i) + vecb;
}
clear_high(d, oprsz, desc);
}
@@ -105,10 +149,11 @@ void HELPER(gvec_adds16)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec32 vecb = (vec32)DUP4(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) + (uint32_t)b;
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = *(vec32 *)(a + i) + vecb;
}
clear_high(d, oprsz, desc);
}
@@ -116,10 +161,11 @@ void HELPER(gvec_adds32)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_adds64)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec64 vecb = (vec64)DUP2(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) + b;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) + vecb;
}
clear_high(d, oprsz, desc);
}
@@ -129,8 +175,8 @@ void HELPER(gvec_sub8)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) - *(uint8_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = *(vec8 *)(a + i) - *(vec8 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -140,8 +186,8 @@ void HELPER(gvec_sub16)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) - *(uint16_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = *(vec16 *)(a + i) - *(vec16 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -151,8 +197,8 @@ void HELPER(gvec_sub32)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) - *(uint32_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = *(vec32 *)(a + i) - *(vec32 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -162,8 +208,8 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) - *(uint64_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) - *(vec64 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -171,10 +217,11 @@ void HELPER(gvec_sub64)(void *d, void *a, void *b, uint32_t desc)
void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec8 vecb = (vec8)DUP16(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) - (uint8_t)b;
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = *(vec8 *)(a + i) - vecb;
}
clear_high(d, oprsz, desc);
}
@@ -182,10 +229,11 @@ void HELPER(gvec_subs8)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec16 vecb = (vec16)DUP8(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) - (uint16_t)b;
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = *(vec16 *)(a + i) - vecb;
}
clear_high(d, oprsz, desc);
}
@@ -193,10 +241,11 @@ void HELPER(gvec_subs16)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec32 vecb = (vec32)DUP4(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) - (uint32_t)b;
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = *(vec32 *)(a + i) - vecb;
}
clear_high(d, oprsz, desc);
}
@@ -204,10 +253,11 @@ void HELPER(gvec_subs32)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_subs64)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec64 vecb = (vec64)DUP2(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) - b;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) - vecb;
}
clear_high(d, oprsz, desc);
}
@@ -217,8 +267,8 @@ void HELPER(gvec_mul8)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) * *(uint8_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = *(vec8 *)(a + i) * *(vec8 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -228,8 +278,8 @@ void HELPER(gvec_mul16)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) * *(uint16_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = *(vec16 *)(a + i) * *(vec16 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -239,8 +289,8 @@ void HELPER(gvec_mul32)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) * *(uint32_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = *(vec32 *)(a + i) * *(vec32 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -250,8 +300,8 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) * *(uint64_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) * *(vec64 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -259,10 +309,11 @@ void HELPER(gvec_mul64)(void *d, void *a, void *b, uint32_t desc)
void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec8 vecb = (vec8)DUP16(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) * (uint8_t)b;
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = *(vec8 *)(a + i) * vecb;
}
clear_high(d, oprsz, desc);
}
@@ -270,10 +321,11 @@ void HELPER(gvec_muls8)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec16 vecb = (vec16)DUP8(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) * (uint16_t)b;
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = *(vec16 *)(a + i) * vecb;
}
clear_high(d, oprsz, desc);
}
@@ -281,10 +333,11 @@ void HELPER(gvec_muls16)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec32 vecb = (vec32)DUP4(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) * (uint32_t)b;
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = *(vec32 *)(a + i) * vecb;
}
clear_high(d, oprsz, desc);
}
@@ -292,10 +345,11 @@ void HELPER(gvec_muls32)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_muls64)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec64 vecb = (vec64)DUP2(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) * b;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) * vecb;
}
clear_high(d, oprsz, desc);
}
@@ -305,8 +359,8 @@ void HELPER(gvec_neg8)(void *d, void *a, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = -*(uint8_t *)(a + i);
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = -*(vec8 *)(a + i);
}
clear_high(d, oprsz, desc);
}
@@ -316,8 +370,8 @@ void HELPER(gvec_neg16)(void *d, void *a, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = -*(uint16_t *)(a + i);
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = -*(vec16 *)(a + i);
}
clear_high(d, oprsz, desc);
}
@@ -327,8 +381,8 @@ void HELPER(gvec_neg32)(void *d, void *a, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = -*(uint32_t *)(a + i);
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = -*(vec32 *)(a + i);
}
clear_high(d, oprsz, desc);
}
@@ -338,8 +392,8 @@ void HELPER(gvec_neg64)(void *d, void *a, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = -*(uint64_t *)(a + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = -*(vec64 *)(a + i);
}
clear_high(d, oprsz, desc);
}
@@ -445,8 +499,8 @@ void HELPER(gvec_not)(void *d, void *a, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = ~*(uint64_t *)(a + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = ~*(vec64 *)(a + i);
}
clear_high(d, oprsz, desc);
}
@@ -456,8 +510,8 @@ void HELPER(gvec_and)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) & *(uint64_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) & *(vec64 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -467,8 +521,8 @@ void HELPER(gvec_or)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) | *(uint64_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) | *(vec64 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -478,8 +532,8 @@ void HELPER(gvec_xor)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ *(uint64_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) ^ *(vec64 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -489,8 +543,8 @@ void HELPER(gvec_andc)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) &~ *(uint64_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) &~ *(vec64 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -500,8 +554,8 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) |~ *(uint64_t *)(b + i);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) |~ *(vec64 *)(b + i);
}
clear_high(d, oprsz, desc);
}
@@ -511,8 +565,8 @@ void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) & *(uint64_t *)(b + i));
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i));
}
clear_high(d, oprsz, desc);
}
@@ -522,8 +576,8 @@ void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) | *(uint64_t *)(b + i));
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i));
}
clear_high(d, oprsz, desc);
}
@@ -533,8 +587,8 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = ~(*(uint64_t *)(a + i) ^ *(uint64_t *)(b + i));
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i));
}
clear_high(d, oprsz, desc);
}
@@ -542,10 +596,11 @@ void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc)
void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec64 vecb = (vec64)DUP2(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) & b;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) & vecb;
}
clear_high(d, oprsz, desc);
}
@@ -553,10 +608,11 @@ void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec64 vecb = (vec64)DUP2(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) ^ b;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) ^ vecb;
}
clear_high(d, oprsz, desc);
}
@@ -564,10 +620,11 @@ void HELPER(gvec_xors)(void *d, void *a, uint64_t b, uint32_t desc)
void HELPER(gvec_ors)(void *d, void *a, uint64_t b, uint32_t desc)
{
intptr_t oprsz = simd_oprsz(desc);
vec64 vecb = (vec64)DUP2(b);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) | b;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) | vecb;
}
clear_high(d, oprsz, desc);
}
@@ -578,8 +635,8 @@ void HELPER(gvec_shl8i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) << shift;
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = *(vec8 *)(a + i) << shift;
}
clear_high(d, oprsz, desc);
}
@@ -590,8 +647,8 @@ void HELPER(gvec_shl16i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) << shift;
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = *(vec16 *)(a + i) << shift;
}
clear_high(d, oprsz, desc);
}
@@ -602,8 +659,8 @@ void HELPER(gvec_shl32i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) << shift;
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = *(vec32 *)(a + i) << shift;
}
clear_high(d, oprsz, desc);
}
@@ -614,8 +671,8 @@ void HELPER(gvec_shl64i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) << shift;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) << shift;
}
clear_high(d, oprsz, desc);
}
@@ -626,8 +683,8 @@ void HELPER(gvec_shr8i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(uint8_t *)(d + i) = *(uint8_t *)(a + i) >> shift;
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(vec8 *)(d + i) = *(vec8 *)(a + i) >> shift;
}
clear_high(d, oprsz, desc);
}
@@ -638,8 +695,8 @@ void HELPER(gvec_shr16i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(uint16_t *)(d + i) = *(uint16_t *)(a + i) >> shift;
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(vec16 *)(d + i) = *(vec16 *)(a + i) >> shift;
}
clear_high(d, oprsz, desc);
}
@@ -650,8 +707,8 @@ void HELPER(gvec_shr32i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(uint32_t *)(d + i) = *(uint32_t *)(a + i) >> shift;
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(vec32 *)(d + i) = *(vec32 *)(a + i) >> shift;
}
clear_high(d, oprsz, desc);
}
@@ -662,8 +719,8 @@ void HELPER(gvec_shr64i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(uint64_t *)(d + i) = *(uint64_t *)(a + i) >> shift;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(vec64 *)(d + i) = *(vec64 *)(a + i) >> shift;
}
clear_high(d, oprsz, desc);
}
@@ -674,8 +731,8 @@ void HELPER(gvec_sar8i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint8_t)) {
*(int8_t *)(d + i) = *(int8_t *)(a + i) >> shift;
for (i = 0; i < oprsz; i += sizeof(vec8)) {
*(svec8 *)(d + i) = *(svec8 *)(a + i) >> shift;
}
clear_high(d, oprsz, desc);
}
@@ -686,8 +743,8 @@ void HELPER(gvec_sar16i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint16_t)) {
*(int16_t *)(d + i) = *(int16_t *)(a + i) >> shift;
for (i = 0; i < oprsz; i += sizeof(vec16)) {
*(svec16 *)(d + i) = *(svec16 *)(a + i) >> shift;
}
clear_high(d, oprsz, desc);
}
@@ -698,8 +755,8 @@ void HELPER(gvec_sar32i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint32_t)) {
*(int32_t *)(d + i) = *(int32_t *)(a + i) >> shift;
for (i = 0; i < oprsz; i += sizeof(vec32)) {
*(svec32 *)(d + i) = *(svec32 *)(a + i) >> shift;
}
clear_high(d, oprsz, desc);
}
@@ -710,8 +767,8 @@ void HELPER(gvec_sar64i)(void *d, void *a, uint32_t desc)
int shift = simd_data(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
*(int64_t *)(d + i) = *(int64_t *)(a + i) >> shift;
for (i = 0; i < oprsz; i += sizeof(vec64)) {
*(svec64 *)(d + i) = *(svec64 *)(a + i) >> shift;
}
clear_high(d, oprsz, desc);
}
@@ -860,30 +917,39 @@ void HELPER(gvec_sar64v)(void *d, void *a, void *b, uint32_t desc)
clear_high(d, oprsz, desc);
}
/* If vectors are enabled, the compiler fills in -1 for true.
Otherwise, we must take care of this by hand. */
#ifdef CONFIG_VECTOR16
# define DO_CMP0(X) X
#else
# define DO_CMP0(X) -(X)
#endif
#define DO_CMP1(NAME, TYPE, OP) \
void HELPER(NAME)(void *d, void *a, void *b, uint32_t desc) \
{ \
intptr_t oprsz = simd_oprsz(desc); \
intptr_t i; \
for (i = 0; i < oprsz; i += sizeof(TYPE)) { \
*(TYPE *)(d + i) = -(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
*(TYPE *)(d + i) = DO_CMP0(*(TYPE *)(a + i) OP *(TYPE *)(b + i)); \
} \
clear_high(d, oprsz, desc); \
}
#define DO_CMP2(SZ) \
DO_CMP1(gvec_eq##SZ, uint##SZ##_t, ==) \
DO_CMP1(gvec_ne##SZ, uint##SZ##_t, !=) \
DO_CMP1(gvec_lt##SZ, int##SZ##_t, <) \
DO_CMP1(gvec_le##SZ, int##SZ##_t, <=) \
DO_CMP1(gvec_ltu##SZ, uint##SZ##_t, <) \
DO_CMP1(gvec_leu##SZ, uint##SZ##_t, <=)
DO_CMP1(gvec_eq##SZ, vec##SZ, ==) \
DO_CMP1(gvec_ne##SZ, vec##SZ, !=) \
DO_CMP1(gvec_lt##SZ, svec##SZ, <) \
DO_CMP1(gvec_le##SZ, svec##SZ, <=) \
DO_CMP1(gvec_ltu##SZ, vec##SZ, <) \
DO_CMP1(gvec_leu##SZ, vec##SZ, <=)
DO_CMP2(8)
DO_CMP2(16)
DO_CMP2(32)
DO_CMP2(64)
#undef DO_CMP0
#undef DO_CMP1
#undef DO_CMP2
@@ -1384,11 +1450,11 @@ void HELPER(gvec_bitsel)(void *d, void *a, void *b, void *c, uint32_t desc)
intptr_t oprsz = simd_oprsz(desc);
intptr_t i;
for (i = 0; i < oprsz; i += sizeof(uint64_t)) {
uint64_t aa = *(uint64_t *)(a + i);
uint64_t bb = *(uint64_t *)(b + i);
uint64_t cc = *(uint64_t *)(c + i);
*(uint64_t *)(d + i) = (bb & aa) | (cc & ~aa);
for (i = 0; i < oprsz; i += sizeof(vec64)) {
vec64 aa = *(vec64 *)(a + i);
vec64 bb = *(vec64 *)(b + i);
vec64 cc = *(vec64 *)(c + i);
*(vec64 *)(d + i) = (bb & aa) | (cc & ~aa);
}
clear_high(d, oprsz, desc);
}
-1
View File
@@ -30,7 +30,6 @@
#include "exec/tb-lookup.h"
#include "disas/disas.h"
#include "exec/log.h"
#include "tcg/tcg.h"
/* 32-bit helpers */
+107 -84
View File
@@ -18,7 +18,6 @@
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu-common.h"
#define NO_CPU_IO_DEFS
@@ -26,7 +25,7 @@
#include "trace.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg.h"
#include "tcg.h"
#if defined(CONFIG_USER_ONLY)
#include "qemu.h"
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@@ -892,61 +891,43 @@ static void page_lock_pair(PageDesc **ret_p1, tb_page_addr_t phys1,
}
}
#if defined(CONFIG_USER_ONLY)
/* Currently it is not recommended to allocate big chunks of data in
user mode. It will change when a dedicated libc will be used. */
/* ??? 64-bit hosts ought to have no problem mmaping data outside the
region in which the guest needs to run. Revisit this. */
#define USE_STATIC_CODE_GEN_BUFFER
#endif
/* Minimum size of the code gen buffer. This number is randomly chosen,
but not so small that we can't have a fair number of TB's live. */
#define MIN_CODE_GEN_BUFFER_SIZE (1 * MiB)
#define MIN_CODE_GEN_BUFFER_SIZE (1024u * 1024)
/* Maximum size of the code gen buffer we'd like to use. Unless otherwise
indicated, this is constrained by the range of direct branches on the
host cpu, as used by the TCG implementation of goto_tb. */
#if defined(__x86_64__)
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
#elif defined(__sparc__)
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
#elif defined(__powerpc64__)
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
#elif defined(__powerpc__)
# define MAX_CODE_GEN_BUFFER_SIZE (32 * MiB)
# define MAX_CODE_GEN_BUFFER_SIZE (32u * 1024 * 1024)
#elif defined(__aarch64__)
# define MAX_CODE_GEN_BUFFER_SIZE (2 * GiB)
# define MAX_CODE_GEN_BUFFER_SIZE (2ul * 1024 * 1024 * 1024)
#elif defined(__s390x__)
/* We have a +- 4GB range on the branches; leave some slop. */
# define MAX_CODE_GEN_BUFFER_SIZE (3 * GiB)
# define MAX_CODE_GEN_BUFFER_SIZE (3ul * 1024 * 1024 * 1024)
#elif defined(__mips__)
/* We have a 256MB branch region, but leave room to make sure the
main executable is also within that region. */
# define MAX_CODE_GEN_BUFFER_SIZE (128 * MiB)
# define MAX_CODE_GEN_BUFFER_SIZE (128ul * 1024 * 1024)
#else
# define MAX_CODE_GEN_BUFFER_SIZE ((size_t)-1)
#endif
#if TCG_TARGET_REG_BITS == 32
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32 * MiB)
#ifdef CONFIG_USER_ONLY
/*
* For user mode on smaller 32 bit systems we may run into trouble
* allocating big chunks of data in the right place. On these systems
* we utilise a static code generation buffer directly in the binary.
*/
#define USE_STATIC_CODE_GEN_BUFFER
#endif
#else /* TCG_TARGET_REG_BITS == 64 */
#ifdef CONFIG_USER_ONLY
/*
* As user-mode emulation typically means running multiple instances
* of the translator don't go too nuts with our default code gen
* buffer lest we make things too hard for the OS.
*/
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (128 * MiB)
#else
/*
* We expect most system emulation to run one or two guests per host.
* Users running large scale system emulation may want to tweak their
* runtime setup via the tb-size control on the command line.
*/
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (1 * GiB)
#endif
#endif
#define DEFAULT_CODE_GEN_BUFFER_SIZE_1 (32u * 1024 * 1024)
#define DEFAULT_CODE_GEN_BUFFER_SIZE \
(DEFAULT_CODE_GEN_BUFFER_SIZE_1 < MAX_CODE_GEN_BUFFER_SIZE \
@@ -956,7 +937,15 @@ static inline size_t size_code_gen_buffer(size_t tb_size)
{
/* Size the buffer. */
if (tb_size == 0) {
#ifdef USE_STATIC_CODE_GEN_BUFFER
tb_size = DEFAULT_CODE_GEN_BUFFER_SIZE;
#else
/* ??? Needs adjustments. */
/* ??? If we relax the requirement that CONFIG_USER_ONLY use the
static buffer, we could size this on RESERVED_VA, on the text
segment size of the executable, or continue to use the default. */
tb_size = (unsigned long)(ram_size / 4);
#endif
}
if (tb_size < MIN_CODE_GEN_BUFFER_SIZE) {
tb_size = MIN_CODE_GEN_BUFFER_SIZE;
@@ -1043,20 +1032,47 @@ static inline void *alloc_code_gen_buffer(void)
{
int prot = PROT_WRITE | PROT_READ | PROT_EXEC;
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
uintptr_t start = 0;
size_t size = tcg_ctx->code_gen_buffer_size;
void *buf;
buf = mmap(NULL, size, prot, flags, -1, 0);
/* Constrain the position of the buffer based on the host cpu.
Note that these addresses are chosen in concert with the
addresses assigned in the relevant linker script file. */
# if defined(__PIE__) || defined(__PIC__)
/* Don't bother setting a preferred location if we're building
a position-independent executable. We're more likely to get
an address near the main executable if we let the kernel
choose the address. */
# elif defined(__x86_64__) && defined(MAP_32BIT)
/* Force the memory down into low memory with the executable.
Leave the choice of exact location with the kernel. */
flags |= MAP_32BIT;
/* Cannot expect to map more than 800MB in low memory. */
if (size > 800u * 1024 * 1024) {
tcg_ctx->code_gen_buffer_size = size = 800u * 1024 * 1024;
}
# elif defined(__sparc__)
start = 0x40000000ul;
# elif defined(__s390x__)
start = 0x90000000ul;
# elif defined(__mips__)
# if _MIPS_SIM == _ABI64
start = 0x128000000ul;
# else
start = 0x08000000ul;
# endif
# endif
buf = mmap((void *)start, size, prot, flags, -1, 0);
if (buf == MAP_FAILED) {
return NULL;
}
#ifdef __mips__
if (cross_256mb(buf, size)) {
/*
* Try again, with the original still mapped, to avoid re-acquiring
* the same 256mb crossing.
*/
/* Try again, with the original still mapped, to avoid re-acquiring
that 256mb crossing. This time don't specify an address. */
size_t size2;
void *buf2 = mmap(NULL, size, prot, flags, -1, 0);
switch ((int)(buf2 != MAP_FAILED)) {
@@ -1140,6 +1156,23 @@ void tcg_exec_init(unsigned long tb_size)
#endif
}
/*
* Allocate a new translation block. Flush the translation buffer if
* too many translation blocks or too much generated code.
*/
static TranslationBlock *tb_alloc(target_ulong pc)
{
TranslationBlock *tb;
assert_memory_lock();
tb = tcg_tb_alloc(tcg_ctx);
if (unlikely(tb == NULL)) {
return NULL;
}
return tb;
}
/* call with @p->lock held */
static inline void invalidate_page_bitmap(PageDesc *p)
{
@@ -1198,8 +1231,6 @@ static gboolean tb_host_size_iter(gpointer key, gpointer value, gpointer data)
/* flush all the translation blocks */
static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
{
bool did_flush = false;
mmap_lock();
/* If it is already been done on request of another CPU,
* just retry.
@@ -1207,7 +1238,6 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
if (tb_ctx.tb_flush_count != tb_flush_count.host_int) {
goto done;
}
did_flush = true;
if (DEBUG_TB_FLUSH_GATE) {
size_t nb_tbs = tcg_nb_tbs();
@@ -1232,22 +1262,14 @@ static void do_tb_flush(CPUState *cpu, run_on_cpu_data tb_flush_count)
done:
mmap_unlock();
if (did_flush) {
qemu_plugin_flush_cb();
}
}
void tb_flush(CPUState *cpu)
{
if (tcg_enabled()) {
unsigned tb_flush_count = atomic_mb_read(&tb_ctx.tb_flush_count);
if (cpu_in_exclusive_context(cpu)) {
do_tb_flush(cpu, RUN_ON_CPU_HOST_INT(tb_flush_count));
} else {
async_safe_run_on_cpu(cpu, do_tb_flush,
RUN_ON_CPU_HOST_INT(tb_flush_count));
}
async_safe_run_on_cpu(cpu, do_tb_flush,
RUN_ON_CPU_HOST_INT(tb_flush_count));
}
}
@@ -1659,7 +1681,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
TCGProfile *prof = &tcg_ctx->prof;
int64_t ti;
#endif
assert_memory_lock();
phys_pc = get_page_addr_code(env, pc);
@@ -1685,7 +1706,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
}
buffer_overflow:
tb = tcg_tb_alloc(tcg_ctx);
tb = tb_alloc(pc);
if (unlikely(!tb)) {
/* flush must be done */
tb_flush(cpu);
@@ -1701,7 +1722,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
tb->cs_base = cs_base;
tb->flags = flags;
tb->cflags = cflags;
tb->orig_tb = NULL;
tb->trace_vcpu_dstate = *cpu->trace_dstate;
tcg_ctx->tb_cflags = cflags;
tb_overflow:
@@ -1788,7 +1808,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
#ifdef DEBUG_DISAS
if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) &&
qemu_log_in_addr_range(tb->pc)) {
FILE *logfile = qemu_log_lock();
qemu_log_lock();
qemu_log("OUT: [size=%d]\n", gen_code_size);
if (tcg_ctx->data_gen_ptr) {
size_t code_size = tcg_ctx->data_gen_ptr - tb->tc.ptr;
@@ -1813,7 +1833,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu,
}
qemu_log("\n");
qemu_log_flush();
qemu_log_unlock(logfile);
qemu_log_unlock();
}
#endif
@@ -1869,7 +1889,7 @@ static void
tb_invalidate_phys_page_range__locked(struct page_collection *pages,
PageDesc *p, tb_page_addr_t start,
tb_page_addr_t end,
uintptr_t retaddr)
int is_cpu_write_access)
{
TranslationBlock *tb;
tb_page_addr_t tb_start, tb_end;
@@ -1877,9 +1897,9 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
#ifdef TARGET_HAS_PRECISE_SMC
CPUState *cpu = current_cpu;
CPUArchState *env = NULL;
bool current_tb_not_found = retaddr != 0;
bool current_tb_modified = false;
int current_tb_not_found = is_cpu_write_access;
TranslationBlock *current_tb = NULL;
int current_tb_modified = 0;
target_ulong current_pc = 0;
target_ulong current_cs_base = 0;
uint32_t current_flags = 0;
@@ -1911,21 +1931,24 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
if (!(tb_end <= start || tb_start >= end)) {
#ifdef TARGET_HAS_PRECISE_SMC
if (current_tb_not_found) {
current_tb_not_found = false;
/* now we have a real cpu fault */
current_tb = tcg_tb_lookup(retaddr);
current_tb_not_found = 0;
current_tb = NULL;
if (cpu->mem_io_pc) {
/* now we have a real cpu fault */
current_tb = tcg_tb_lookup(cpu->mem_io_pc);
}
}
if (current_tb == tb &&
(tb_cflags(current_tb) & CF_COUNT_MASK) != 1) {
/*
* If we are modifying the current TB, we must stop
* its execution. We could be more precise by checking
* that the modification is after the current PC, but it
* would require a specialized function to partially
* restore the CPU state.
*/
current_tb_modified = true;
cpu_restore_state_from_tb(cpu, current_tb, retaddr, true);
/* If we are modifying the current TB, we must stop
its execution. We could be more precise by checking
that the modification is after the current PC, but it
would require a specialized function to partially
restore the CPU state */
current_tb_modified = 1;
cpu_restore_state_from_tb(cpu, current_tb,
cpu->mem_io_pc, true);
cpu_get_tb_cpu_state(env, &current_pc, &current_cs_base,
&current_flags);
}
@@ -1960,7 +1983,8 @@ tb_invalidate_phys_page_range__locked(struct page_collection *pages,
*
* Called with mmap_lock held for user-mode emulation
*/
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end)
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
int is_cpu_write_access)
{
struct page_collection *pages;
PageDesc *p;
@@ -1972,7 +1996,8 @@ void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end)
return;
}
pages = page_collection_lock(start, end);
tb_invalidate_phys_page_range__locked(pages, p, start, end, 0);
tb_invalidate_phys_page_range__locked(pages, p, start, end,
is_cpu_write_access);
page_collection_unlock(pages);
}
@@ -2019,8 +2044,7 @@ void tb_invalidate_phys_range(target_ulong start, target_ulong end)
* Call with all @pages in the range [@start, @start + len[ locked.
*/
void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len,
uintptr_t retaddr)
tb_page_addr_t start, int len)
{
PageDesc *p;
@@ -2047,8 +2071,7 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages,
}
} else {
do_invalidate:
tb_invalidate_phys_page_range__locked(pages, p, start, start + len,
retaddr);
tb_invalidate_phys_page_range__locked(pages, p, start, start + len, 1);
}
}
#else
@@ -2122,16 +2145,16 @@ static bool tb_invalidate_phys_page(tb_page_addr_t addr, uintptr_t pc)
#endif
/* user-mode: call with mmap_lock held */
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr)
void tb_check_watchpoint(CPUState *cpu)
{
TranslationBlock *tb;
assert_memory_lock();
tb = tcg_tb_lookup(retaddr);
tb = tcg_tb_lookup(cpu->mem_io_pc);
if (tb) {
/* We can use retranslation to find the PC. */
cpu_restore_state_from_tb(cpu, tb, retaddr, true);
cpu_restore_state_from_tb(cpu, tb, cpu->mem_io_pc, true);
tb_phys_invalidate(tb, -1);
} else {
/* The exception probably happened in a helper. The CPU state should
+4 -4
View File
@@ -27,10 +27,10 @@ struct page_collection *page_collection_lock(tb_page_addr_t start,
tb_page_addr_t end);
void page_collection_unlock(struct page_collection *set);
void tb_invalidate_phys_page_fast(struct page_collection *pages,
tb_page_addr_t start, int len,
uintptr_t retaddr);
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end);
void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr);
tb_page_addr_t start, int len);
void tb_invalidate_phys_page_range(tb_page_addr_t start, tb_page_addr_t end,
int is_cpu_write_access);
void tb_check_watchpoint(CPUState *cpu);
#ifdef CONFIG_USER_ONLY
int page_unprotect(target_ulong address, uintptr_t pc);
+3 -22
View File
@@ -16,7 +16,6 @@
#include "exec/gen-icount.h"
#include "exec/log.h"
#include "exec/translator.h"
#include "exec/plugin-gen.h"
/* Pairs with tcg_clear_temp_count.
To be called by #TranslatorOps.{translate_insn,tb_stop} if
@@ -35,7 +34,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
CPUState *cpu, TranslationBlock *tb, int max_insns)
{
int bp_insn = 0;
bool plugin_enabled;
/* Initialize DisasContext */
db->tb = tb;
@@ -57,17 +55,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
ops->tb_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
plugin_enabled = plugin_gen_tb_start(cpu, tb);
while (true) {
db->num_insns++;
ops->insn_start(db, cpu);
tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */
if (plugin_enabled) {
plugin_gen_insn_start(cpu, db);
}
/* Pass breakpoint hits to target for further processing */
if (!db->singlestep_enabled
&& unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
@@ -98,6 +90,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
/* Accept I/O on the last instruction. */
gen_io_start();
ops->translate_insn(db, cpu);
gen_io_end();
} else {
ops->translate_insn(db, cpu);
}
@@ -107,14 +100,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
break;
}
/*
* We can't instrument after instructions that change control
* flow although this only really affects post-load operations.
*/
if (plugin_enabled) {
plugin_gen_insn_end();
}
/* Stop translation if the output buffer is full,
or we have executed all of the allowed instructions. */
if (tcg_op_buf_full() || db->num_insns >= db->max_insns) {
@@ -127,10 +112,6 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
ops->tb_stop(db, cpu);
gen_tb_end(db->tb, db->num_insns - bp_insn);
if (plugin_enabled) {
plugin_gen_tb_end(cpu);
}
/* The disas_log hook may use these values rather than recompute. */
db->tb->size = db->pc_next - db->pc_first;
db->tb->icount = db->num_insns;
@@ -138,11 +119,11 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
#ifdef DEBUG_DISAS
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
&& qemu_log_in_addr_range(db->pc_first)) {
FILE *logfile = qemu_log_lock();
qemu_log_lock();
qemu_log("----------------\n");
ops->disas_log(db, cpu);
qemu_log("\n");
qemu_log_unlock(logfile);
qemu_log_unlock();
}
#endif
}
+1 -1
View File
@@ -1,5 +1,5 @@
#include "qemu/osdep.h"
#include "hw/core/cpu.h"
#include "qom/cpu.h"
#include "sysemu/replay.h"
#include "sysemu/sysemu.h"
+1 -272
View File
@@ -20,14 +20,12 @@
#include "cpu.h"
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg/tcg.h"
#include "tcg.h"
#include "qemu/bitops.h"
#include "exec/cpu_ldst.h"
#include "translate-all.h"
#include "exec/helper-proto.h"
#include "qemu/atomic128.h"
#include "trace-root.h"
#include "trace/mem.h"
#undef EAX
#undef ECX
@@ -190,38 +188,6 @@ static inline int handle_cpu_signal(uintptr_t pc, siginfo_t *info,
g_assert_not_reached();
}
void *probe_access(CPUArchState *env, target_ulong addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{
int flags;
g_assert(-(addr | TARGET_PAGE_MASK) >= size);
switch (access_type) {
case MMU_DATA_STORE:
flags = PAGE_WRITE;
break;
case MMU_DATA_LOAD:
flags = PAGE_READ;
break;
case MMU_INST_FETCH:
flags = PAGE_EXEC;
break;
default:
g_assert_not_reached();
}
if (!guest_addr_valid(addr) || page_check_range(addr, size, flags) < 0) {
CPUState *cpu = env_cpu(env);
CPUClass *cc = CPU_GET_CLASS(cpu);
cc->tlb_fill(cpu, addr, size, access_type, MMU_USER_IDX, false,
retaddr);
g_assert_not_reached();
}
return size ? g2h(addr) : NULL;
}
#if defined(__i386__)
#if defined(__NetBSD__)
@@ -736,240 +702,6 @@ int cpu_signal_handler(int host_signum, void *pinfo,
/* The softmmu versions of these helpers are in cputlb.c. */
uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, false);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
ret = ldub_p(g2h(ptr));
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
return ret;
}
int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr)
{
int ret;
uint16_t meminfo = trace_mem_get_info(MO_SB, MMU_USER_IDX, false);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
ret = ldsb_p(g2h(ptr));
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
return ret;
}
uint32_t cpu_lduw_data(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
uint16_t meminfo = trace_mem_get_info(MO_TEUW, MMU_USER_IDX, false);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
ret = lduw_p(g2h(ptr));
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
return ret;
}
int cpu_ldsw_data(CPUArchState *env, abi_ptr ptr)
{
int ret;
uint16_t meminfo = trace_mem_get_info(MO_TESW, MMU_USER_IDX, false);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
ret = ldsw_p(g2h(ptr));
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
return ret;
}
uint32_t cpu_ldl_data(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
uint16_t meminfo = trace_mem_get_info(MO_TEUL, MMU_USER_IDX, false);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
ret = ldl_p(g2h(ptr));
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
return ret;
}
uint64_t cpu_ldq_data(CPUArchState *env, abi_ptr ptr)
{
uint64_t ret;
uint16_t meminfo = trace_mem_get_info(MO_TEQ, MMU_USER_IDX, false);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
ret = ldq_p(g2h(ptr));
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
return ret;
}
uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
{
uint32_t ret;
set_helper_retaddr(retaddr);
ret = cpu_ldub_data(env, ptr);
clear_helper_retaddr();
return ret;
}
int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
{
int ret;
set_helper_retaddr(retaddr);
ret = cpu_ldsb_data(env, ptr);
clear_helper_retaddr();
return ret;
}
uint32_t cpu_lduw_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
{
uint32_t ret;
set_helper_retaddr(retaddr);
ret = cpu_lduw_data(env, ptr);
clear_helper_retaddr();
return ret;
}
int cpu_ldsw_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
{
int ret;
set_helper_retaddr(retaddr);
ret = cpu_ldsw_data(env, ptr);
clear_helper_retaddr();
return ret;
}
uint32_t cpu_ldl_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
{
uint32_t ret;
set_helper_retaddr(retaddr);
ret = cpu_ldl_data(env, ptr);
clear_helper_retaddr();
return ret;
}
uint64_t cpu_ldq_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t retaddr)
{
uint64_t ret;
set_helper_retaddr(retaddr);
ret = cpu_ldq_data(env, ptr);
clear_helper_retaddr();
return ret;
}
void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
{
uint16_t meminfo = trace_mem_get_info(MO_UB, MMU_USER_IDX, true);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
stb_p(g2h(ptr), val);
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
}
void cpu_stw_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
{
uint16_t meminfo = trace_mem_get_info(MO_TEUW, MMU_USER_IDX, true);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
stw_p(g2h(ptr), val);
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
}
void cpu_stl_data(CPUArchState *env, abi_ptr ptr, uint32_t val)
{
uint16_t meminfo = trace_mem_get_info(MO_TEUL, MMU_USER_IDX, true);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
stl_p(g2h(ptr), val);
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
}
void cpu_stq_data(CPUArchState *env, abi_ptr ptr, uint64_t val)
{
uint16_t meminfo = trace_mem_get_info(MO_TEQ, MMU_USER_IDX, true);
trace_guest_mem_before_exec(env_cpu(env), ptr, meminfo);
stq_p(g2h(ptr), val);
qemu_plugin_vcpu_mem_cb(env_cpu(env), ptr, meminfo);
}
void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr,
uint32_t val, uintptr_t retaddr)
{
set_helper_retaddr(retaddr);
cpu_stb_data(env, ptr, val);
clear_helper_retaddr();
}
void cpu_stw_data_ra(CPUArchState *env, abi_ptr ptr,
uint32_t val, uintptr_t retaddr)
{
set_helper_retaddr(retaddr);
cpu_stw_data(env, ptr, val);
clear_helper_retaddr();
}
void cpu_stl_data_ra(CPUArchState *env, abi_ptr ptr,
uint32_t val, uintptr_t retaddr)
{
set_helper_retaddr(retaddr);
cpu_stl_data(env, ptr, val);
clear_helper_retaddr();
}
void cpu_stq_data_ra(CPUArchState *env, abi_ptr ptr,
uint64_t val, uintptr_t retaddr)
{
set_helper_retaddr(retaddr);
cpu_stq_data(env, ptr, val);
clear_helper_retaddr();
}
uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
set_helper_retaddr(1);
ret = ldub_p(g2h(ptr));
clear_helper_retaddr();
return ret;
}
uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
set_helper_retaddr(1);
ret = lduw_p(g2h(ptr));
clear_helper_retaddr();
return ret;
}
uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr)
{
uint32_t ret;
set_helper_retaddr(1);
ret = ldl_p(g2h(ptr));
clear_helper_retaddr();
return ret;
}
uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr)
{
uint64_t ret;
set_helper_retaddr(1);
ret = ldq_p(g2h(ptr));
clear_helper_retaddr();
return ret;
}
/* Do not allow unaligned operations to proceed. Return the host address. */
static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
int size, uintptr_t retaddr)
@@ -987,13 +719,10 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr,
#define ATOMIC_MMU_DECLS do {} while (0)
#define ATOMIC_MMU_LOOKUP atomic_mmu_lookup(env, addr, DATA_SIZE, GETPC())
#define ATOMIC_MMU_CLEANUP do { clear_helper_retaddr(); } while (0)
#define ATOMIC_MMU_IDX MMU_USER_IDX
#define ATOMIC_NAME(X) HELPER(glue(glue(atomic_ ## X, SUFFIX), END))
#define EXTRA_ARGS
#include "atomic_common.inc.c"
#define DATA_SIZE 1
#include "atomic_template.h"
+11 -6
View File
@@ -38,10 +38,6 @@
int graphic_width = 1024;
int graphic_height = 768;
int graphic_depth = 8;
#elif defined(TARGET_M68K)
int graphic_width = 800;
int graphic_height = 600;
int graphic_depth = 8;
#else
int graphic_width = 800;
int graphic_height = 600;
@@ -77,8 +73,6 @@ int graphic_depth = 32;
#define QEMU_ARCH QEMU_ARCH_PPC
#elif defined(TARGET_RISCV)
#define QEMU_ARCH QEMU_ARCH_RISCV
#elif defined(TARGET_RX)
#define QEMU_ARCH QEMU_ARCH_RX
#elif defined(TARGET_S390X)
#define QEMU_ARCH QEMU_ARCH_S390X
#elif defined(TARGET_SH4)
@@ -112,3 +106,14 @@ int xen_available(void)
return 0;
#endif
}
TargetInfo *qmp_query_target(Error **errp)
{
TargetInfo *info = g_malloc0(sizeof(*info));
info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
&error_abort);
return info;
}
+1
View File
@@ -2,6 +2,7 @@ common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o
common-obj-$(CONFIG_SPICE) += spiceaudio.o
common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o
common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o
common-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o
common-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o
common-obj-y += wavcapture.o
+285 -134
View File
@@ -39,11 +39,13 @@ struct pollhlp {
struct pollfd *pfds;
int count;
int mask;
AudioState *s;
};
typedef struct ALSAVoiceOut {
HWVoiceOut hw;
int wpos;
int pending;
void *pcm_buf;
snd_pcm_t *handle;
struct pollhlp pollhlp;
Audiodev *dev;
@@ -52,6 +54,7 @@ typedef struct ALSAVoiceOut {
typedef struct ALSAVoiceIn {
HWVoiceIn hw;
snd_pcm_t *handle;
void *pcm_buf;
struct pollhlp pollhlp;
Audiodev *dev;
} ALSAVoiceIn;
@@ -196,11 +199,11 @@ static void alsa_poll_handler (void *opaque)
break;
case SND_PCM_STATE_PREPARED:
audio_run(hlp->s, "alsa run (prepared)");
audio_run ("alsa run (prepared)");
break;
case SND_PCM_STATE_RUNNING:
audio_run(hlp->s, "alsa run (running)");
audio_run ("alsa run (running)");
break;
default:
@@ -266,6 +269,11 @@ static int alsa_poll_in (HWVoiceIn *hw)
return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
}
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 (AudioFormat fmt, int endianness)
{
switch (fmt) {
@@ -307,13 +315,6 @@ static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
return SND_PCM_FORMAT_U32_LE;
}
case AUDIO_FORMAT_F32:
if (endianness) {
return SND_PCM_FORMAT_FLOAT_BE;
} else {
return SND_PCM_FORMAT_FLOAT_LE;
}
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_AUDIO
@@ -377,16 +378,6 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt,
*fmt = AUDIO_FORMAT_U32;
break;
case SND_PCM_FORMAT_FLOAT_LE:
*endianness = 0;
*fmt = AUDIO_FORMAT_F32;
break;
case SND_PCM_FORMAT_FLOAT_BE:
*endianness = 1;
*fmt = AUDIO_FORMAT_F32;
break;
default:
dolog ("Unrecognized audio format %d\n", alsafmt);
return -1;
@@ -510,6 +501,13 @@ static int alsa_open(bool in, struct alsa_params_req *req,
goto err;
}
if (nchannels != 1 && nchannels != 2) {
alsa_logerr2 (err, typ,
"Can not handle obtained number of channels %d\n",
nchannels);
goto err;
}
if (apdo->buffer_length) {
int dir = 0;
unsigned int btime = apdo->buffer_length;
@@ -608,64 +606,102 @@ static int alsa_open(bool in, struct alsa_params_req *req,
return -1;
}
static size_t alsa_write(HWVoiceOut *hw, void *buf, size_t len)
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
size_t pos = 0;
size_t len_frames = len / hw->info.bytes_per_frame;
snd_pcm_sframes_t avail;
while (len_frames) {
char *src = advance(buf, pos);
snd_pcm_sframes_t written;
written = snd_pcm_writei(alsa->handle, src, len_frames);
if (written <= 0) {
switch (written) {
case 0:
trace_alsa_wrote_zero(len_frames);
return pos;
case -EPIPE:
if (alsa_recover(alsa->handle)) {
alsa_logerr(written, "Failed to write %zu frames\n",
len_frames);
return pos;
}
trace_alsa_xrun_out();
continue;
case -ESTRPIPE:
/*
* stream is suspended and waiting for an application
* recovery
*/
if (alsa_resume(alsa->handle)) {
alsa_logerr(written, "Failed to write %zu frames\n",
len_frames);
return pos;
}
trace_alsa_resume_out();
continue;
case -EAGAIN:
return pos;
default:
alsa_logerr(written, "Failed to write %zu frames from %p\n",
len, src);
return pos;
avail = snd_pcm_avail_update (handle);
if (avail < 0) {
if (avail == -EPIPE) {
if (!alsa_recover (handle)) {
avail = snd_pcm_avail_update (handle);
}
}
pos += written * hw->info.bytes_per_frame;
if (written < len_frames) {
break;
if (avail < 0) {
alsa_logerr (avail,
"Could not obtain number of available frames\n");
return -1;
}
len_frames -= written;
}
return pos;
return avail;
}
static void alsa_write_pending (ALSAVoiceOut *alsa)
{
HWVoiceOut *hw = &alsa->hw;
while (alsa->pending) {
int left_till_end_samples = hw->samples - alsa->wpos;
int len = audio_MIN (alsa->pending, left_till_end_samples);
char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);
while (len) {
snd_pcm_sframes_t written;
written = snd_pcm_writei (alsa->handle, src, len);
if (written <= 0) {
switch (written) {
case 0:
trace_alsa_wrote_zero(len);
return;
case -EPIPE:
if (alsa_recover (alsa->handle)) {
alsa_logerr (written, "Failed to write %d frames\n",
len);
return;
}
trace_alsa_xrun_out();
continue;
case -ESTRPIPE:
/* stream is suspended and waiting for an
application recovery */
if (alsa_resume (alsa->handle)) {
alsa_logerr (written, "Failed to write %d frames\n",
len);
return;
}
trace_alsa_resume_out();
continue;
case -EAGAIN:
return;
default:
alsa_logerr (written, "Failed to write %d frames from %p\n",
len, src);
return;
}
}
alsa->wpos = (alsa->wpos + written) % hw->samples;
alsa->pending -= written;
len -= written;
}
}
}
static int alsa_run_out (HWVoiceOut *hw, int live)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
int decr;
snd_pcm_sframes_t avail;
avail = alsa_get_avail (alsa->handle);
if (avail < 0) {
dolog ("Could not get number of available playback frames\n");
return 0;
}
decr = audio_MIN (live, avail);
decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
alsa->pending += decr;
alsa_write_pending (alsa);
return decr;
}
static void alsa_fini_out (HWVoiceOut *hw)
@@ -674,6 +710,9 @@ static void alsa_fini_out (HWVoiceOut *hw)
ldebug ("alsa_fini\n");
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
g_free(alsa->pcm_buf);
alsa->pcm_buf = NULL;
}
static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
@@ -702,7 +741,14 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
alsa->pollhlp.s = hw->s;
alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
if (!alsa->pcm_buf) {
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
alsa_anal_close1 (&handle);
return -1;
}
alsa->handle = handle;
alsa->dev = dev;
return 0;
@@ -741,28 +787,34 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl)
return 0;
}
static void alsa_enable_out(HWVoiceOut *hw, bool enable)
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out;
if (enable) {
bool poll_mode = apdo->try_poll;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = apdo->try_poll;
ldebug("enabling voice\n");
if (poll_mode && alsa_poll_out(hw)) {
poll_mode = 0;
ldebug ("enabling voice\n");
if (poll_mode && alsa_poll_out (hw)) {
poll_mode = 0;
}
hw->poll_mode = poll_mode;
return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PREPARE);
}
hw->poll_mode = poll_mode;
alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PREPARE);
} else {
ldebug("disabling voice\n");
case VOICE_DISABLE:
ldebug ("disabling voice\n");
if (hw->poll_mode) {
hw->poll_mode = 0;
alsa_fini_poll(&alsa->pollhlp);
alsa_fini_poll (&alsa->pollhlp);
}
alsa_voice_ctl(alsa->handle, "playback", VOICE_CTL_PAUSE);
return alsa_voice_ctl (alsa->handle, "playback", VOICE_CTL_PAUSE);
}
return -1;
}
static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@@ -790,7 +842,14 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
alsa->pollhlp.s = hw->s;
alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!alsa->pcm_buf) {
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
alsa_anal_close1 (&handle);
return -1;
}
alsa->handle = handle;
alsa->dev = dev;
return 0;
@@ -801,74 +860,165 @@ static void alsa_fini_in (HWVoiceIn *hw)
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
alsa_anal_close (&alsa->handle, &alsa->pollhlp);
g_free(alsa->pcm_buf);
alsa->pcm_buf = NULL;
}
static size_t alsa_read(HWVoiceIn *hw, void *buf, size_t len)
static int alsa_run_in (HWVoiceIn *hw)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
size_t pos = 0;
int hwshift = hw->info.shift;
int i;
int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live;
int decr;
struct {
int add;
int len;
} bufs[2] = {
{ .add = hw->wpos, .len = 0 },
{ .add = 0, .len = 0 }
};
snd_pcm_sframes_t avail;
snd_pcm_uframes_t read_samples = 0;
while (len) {
void *dst = advance(buf, pos);
snd_pcm_sframes_t nread;
nread = snd_pcm_readi(
alsa->handle, dst, len / hw->info.bytes_per_frame);
if (nread <= 0) {
switch (nread) {
case 0:
trace_alsa_read_zero(len);
return pos;
case -EPIPE:
if (alsa_recover(alsa->handle)) {
alsa_logerr(nread, "Failed to read %zu frames\n", len);
return pos;
}
trace_alsa_xrun_in();
continue;
case -EAGAIN:
return pos;
default:
alsa_logerr(nread, "Failed to read %zu frames to %p\n",
len, dst);
return pos;
}
}
pos += nread * hw->info.bytes_per_frame;
len -= nread * hw->info.bytes_per_frame;
if (!dead) {
return 0;
}
return pos;
avail = alsa_get_avail (alsa->handle);
if (avail < 0) {
dolog ("Could not get number of captured frames\n");
return 0;
}
if (!avail) {
snd_pcm_state_t state;
state = snd_pcm_state (alsa->handle);
switch (state) {
case SND_PCM_STATE_PREPARED:
avail = hw->samples;
break;
case SND_PCM_STATE_SUSPENDED:
/* stream is suspended and waiting for an application recovery */
if (alsa_resume (alsa->handle)) {
dolog ("Failed to resume suspended input stream\n");
return 0;
}
trace_alsa_resume_in();
break;
default:
trace_alsa_no_frames(state);
return 0;
}
}
decr = audio_MIN (dead, avail);
if (!decr) {
return 0;
}
if (hw->wpos + decr > hw->samples) {
bufs[0].len = (hw->samples - hw->wpos);
bufs[1].len = (decr - (hw->samples - hw->wpos));
}
else {
bufs[0].len = decr;
}
for (i = 0; i < 2; ++i) {
void *src;
struct st_sample *dst;
snd_pcm_sframes_t nread;
snd_pcm_uframes_t len;
len = bufs[i].len;
src = advance (alsa->pcm_buf, bufs[i].add << hwshift);
dst = hw->conv_buf + bufs[i].add;
while (len) {
nread = snd_pcm_readi (alsa->handle, src, len);
if (nread <= 0) {
switch (nread) {
case 0:
trace_alsa_read_zero(len);
goto exit;
case -EPIPE:
if (alsa_recover (alsa->handle)) {
alsa_logerr (nread, "Failed to read %ld frames\n", len);
goto exit;
}
trace_alsa_xrun_in();
continue;
case -EAGAIN:
goto exit;
default:
alsa_logerr (
nread,
"Failed to read %ld frames from %p\n",
len,
src
);
goto exit;
}
}
hw->conv (dst, src, nread);
src = advance (src, nread << hwshift);
dst += nread;
read_samples += nread;
len -= nread;
}
}
exit:
hw->wpos = (hw->wpos + read_samples) % hw->samples;
return read_samples;
}
static void alsa_enable_in(HWVoiceIn *hw, bool enable)
static int alsa_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, size);
}
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in;
if (enable) {
bool poll_mode = apdo->try_poll;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = apdo->try_poll;
ldebug("enabling voice\n");
if (poll_mode && alsa_poll_in(hw)) {
poll_mode = 0;
ldebug ("enabling voice\n");
if (poll_mode && alsa_poll_in (hw)) {
poll_mode = 0;
}
hw->poll_mode = poll_mode;
return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_START);
}
hw->poll_mode = poll_mode;
alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_START);
} else {
case VOICE_DISABLE:
ldebug ("disabling voice\n");
if (hw->poll_mode) {
hw->poll_mode = 0;
alsa_fini_poll(&alsa->pollhlp);
alsa_fini_poll (&alsa->pollhlp);
}
alsa_voice_ctl(alsa->handle, "capture", VOICE_CTL_PAUSE);
return alsa_voice_ctl (alsa->handle, "capture", VOICE_CTL_PAUSE);
}
return -1;
}
static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo)
@@ -922,14 +1072,15 @@ static void alsa_audio_fini (void *opaque)
static struct audio_pcm_ops alsa_pcm_ops = {
.init_out = alsa_init_out,
.fini_out = alsa_fini_out,
.run_out = alsa_run_out,
.write = alsa_write,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = alsa_enable_out,
.ctl_out = alsa_ctl_out,
.init_in = alsa_init_in,
.fini_in = alsa_fini_in,
.run_in = alsa_run_in,
.read = alsa_read,
.enable_in = alsa_enable_in,
.ctl_in = alsa_ctl_in,
};
static struct audio_driver alsa_audio_driver = {
+249 -618
View File
File diff suppressed because it is too large. Load diff
+22 -25
View File
@@ -27,7 +27,6 @@
#include "qemu/queue.h"
#include "qapi/qapi-types-audio.h"
#include "hw/qdev-properties.h"
typedef void (*audio_callback_fn) (void *opaque, int avail);
@@ -79,10 +78,8 @@ typedef struct SWVoiceOut SWVoiceOut;
typedef struct CaptureVoiceOut CaptureVoiceOut;
typedef struct SWVoiceIn SWVoiceIn;
typedef struct AudioState AudioState;
typedef struct QEMUSoundCard {
char *name;
AudioState *state;
QLIST_ENTRY (QEMUSoundCard) entries;
} QEMUSoundCard;
@@ -95,8 +92,7 @@ void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void AUD_register_card (const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture(
AudioState *s,
CaptureVoiceOut *AUD_add_capture (
struct audsettings *as,
struct audio_capture_ops *ops,
void *opaque
@@ -113,7 +109,7 @@ SWVoiceOut *AUD_open_out (
);
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size);
int AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
int AUD_get_buffer_size_out (SWVoiceOut *sw);
void AUD_set_active_out (SWVoiceOut *sw, int on);
int AUD_is_active_out (SWVoiceOut *sw);
@@ -124,16 +120,6 @@ uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol);
void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol);
#define AUDIO_MAX_CHANNELS 16
typedef struct Volume {
bool mute;
int channels;
uint8_t vol[AUDIO_MAX_CHANNELS];
} Volume;
void audio_set_volume_out(SWVoiceOut *sw, Volume *vol);
void audio_set_volume_in(SWVoiceIn *sw, Volume *vol);
SWVoiceIn *AUD_open_in (
QEMUSoundCard *card,
SWVoiceIn *sw,
@@ -144,7 +130,7 @@ SWVoiceIn *AUD_open_in (
);
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size);
int AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
void AUD_set_active_in (SWVoiceIn *sw, int on);
int AUD_is_active_in (SWVoiceIn *sw);
@@ -157,8 +143,25 @@ static inline void *advance (void *p, int incr)
return (d + incr);
}
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
int freq, int bits, int nchannels);
#ifdef __GNUC__
#define audio_MIN(a, b) ( __extension__ ({ \
__typeof (a) ta = a; \
__typeof (b) tb = b; \
((ta)>(tb)?(tb):(ta)); \
}))
#define audio_MAX(a, b) ( __extension__ ({ \
__typeof (a) ta = a; \
__typeof (b) tb = b; \
((ta)<(tb)?(tb):(ta)); \
}))
#else
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
#endif
int wav_start_capture (CaptureState *s, const char *path, int freq,
int bits, int nchannels);
bool audio_is_cleaning_up(void);
void audio_cleanup(void);
@@ -172,10 +175,4 @@ void audio_parse_option(const char *opt);
void audio_init_audiodevs(void);
void audio_legacy_help(void);
AudioState *audio_state_by_name(const char *name);
const char *audio_get_id(QEMUSoundCard *card);
#define DEFINE_AUDIO_PROPERTIES(_s, _f) \
DEFINE_PROP_AUDIODEV("audiodev", _s, _f)
#endif /* QEMU_AUDIO_H */
+40 -71
View File
@@ -40,74 +40,66 @@ struct audio_callback {
struct audio_pcm_info {
int bits;
bool is_signed;
bool is_float;
int sign;
int freq;
int nchannels;
int bytes_per_frame;
int align;
int shift;
int bytes_per_second;
int swap_endianness;
};
typedef struct AudioState AudioState;
typedef struct SWVoiceCap SWVoiceCap;
typedef struct STSampleBuffer {
size_t pos, size;
st_sample samples[];
} STSampleBuffer;
typedef struct HWVoiceOut {
AudioState *s;
int enabled;
int poll_mode;
int pending_disable;
struct audio_pcm_info info;
f_sample *clip;
int rpos;
uint64_t ts_helper;
STSampleBuffer *mix_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;
struct st_sample *mix_buf;
size_t samples;
int samples;
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut;
typedef struct HWVoiceIn {
AudioState *s;
int enabled;
int poll_mode;
struct audio_pcm_info info;
t_sample *conv;
size_t total_samples_captured;
int wpos;
int total_samples_captured;
uint64_t ts_helper;
STSampleBuffer *conv_buf;
void *buf_emul;
size_t pos_emul, pending_emul, size_emul;
struct st_sample *conv_buf;
size_t samples;
int samples;
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
int ctl_caps;
struct audio_pcm_ops *pcm_ops;
QLIST_ENTRY (HWVoiceIn) entries;
} HWVoiceIn;
struct SWVoiceOut {
QEMUSoundCard *card;
AudioState *s;
struct audio_pcm_info info;
t_sample *conv;
int64_t ratio;
struct st_sample *buf;
void *rate;
size_t total_hw_samples_mixed;
int total_hw_samples_mixed;
int active;
int empty;
HWVoiceOut *hw;
@@ -119,12 +111,11 @@ struct SWVoiceOut {
struct SWVoiceIn {
QEMUSoundCard *card;
AudioState *s;
int active;
struct audio_pcm_info info;
int64_t ratio;
void *rate;
size_t total_hw_samples_acquired;
int total_hw_samples_acquired;
struct st_sample *buf;
f_sample *clip;
HWVoiceIn *hw;
@@ -146,46 +137,24 @@ struct audio_driver {
int max_voices_in;
int voice_size_out;
int voice_size_in;
int ctl_caps;
QLIST_ENTRY(audio_driver) next;
};
struct audio_pcm_ops {
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw);
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
void (*run_buffer_out)(HWVoiceOut *hw);
/*
* get a buffer that after later can be passed to put_buffer_out; optional
* returns the buffer, and writes it's size to size (in bytes)
* this is unrelated to the above buffer_size_out function
*/
void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
/*
* put back the buffer returned by get_buffer_out; optional
* buf must be equal the pointer returned by get_buffer_out,
* size may be smaller
*/
size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
void (*enable_out)(HWVoiceOut *hw, bool enable);
void (*volume_out)(HWVoiceOut *hw, Volume *vol);
int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
void (*fini_out)(HWVoiceOut *hw);
int (*run_out) (HWVoiceOut *hw, int live);
int (*write) (SWVoiceOut *sw, void *buf, int size);
int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw);
size_t (*read) (HWVoiceIn *hw, void *buf, size_t size);
void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
void (*enable_in)(HWVoiceIn *hw, bool enable);
void (*volume_in)(HWVoiceIn *hw, Volume *vol);
int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
void (*fini_in) (HWVoiceIn *hw);
int (*run_in) (HWVoiceIn *hw);
int (*read) (SWVoiceIn *sw, void *buf, int size);
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
};
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
void audio_generic_run_buffer_out(HWVoiceOut *hw);
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
struct capture_callback {
struct audio_capture_ops ops;
void *opaque;
@@ -219,11 +188,6 @@ typedef struct AudioState {
int nb_hw_voices_in;
int vm_running;
int64_t period_ticks;
bool timer_running;
uint64_t timer_last;
QTAILQ_ENTRY(AudioState) list;
} AudioState;
extern const struct mixeng_volume nominal_volume;
@@ -236,21 +200,26 @@ audio_driver *audio_driver_lookup(const char *name);
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);
int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
int audio_pcm_hw_get_live_in (HWVoiceIn *hw);
int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
int live, int pending);
int audio_bug (const char *funcname, int cond);
void *audio_calloc (const char *funcname, int nmemb, size_t size);
void audio_run(AudioState *s, const char *msg);
void audio_run (const char *msg);
typedef struct RateCtl {
int64_t start_ticks;
int64_t bytes_sent;
} RateCtl;
#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
#define VOICE_VOLUME 3
void audio_rate_start(RateCtl *rate);
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
size_t bytes_avail);
#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
static inline int audio_ring_dist (int dst, int src, int len)
{
return (dst >= src) ? (dst - src) : (len - src + dst);
}
+173
View File
@@ -0,0 +1,173 @@
#include "qemu/osdep.h"
#include "audio.h"
#define AUDIO_CAP "audio-pt"
#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, ...)
{
va_list ap;
va_start (ap, fmt);
AUD_vlog (pt->drv, fmt, ap);
va_end (ap);
AUD_log (NULL, "\n");
AUD_log (pt->drv, "Reason: %s\n", strerror (err));
}
int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
void *opaque, const char *drv, const char *cap)
{
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, __func__);
return -1;
}
err = pthread_mutex_init (&p->mutex, NULL);
if (err) {
efunc = "pthread_mutex_init";
goto err0;
}
err = pthread_cond_init (&p->cond, NULL);
if (err) {
efunc = "pthread_cond_init";
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, __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;
}
return 0;
err2:
err2 = pthread_cond_destroy (&p->cond);
if (err2) {
logerr(p, err2, "%s(%s): pthread_cond_destroy failed", cap, __func__);
}
err1:
err2 = pthread_mutex_destroy (&p->mutex);
if (err2) {
logerr(p, err2, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
}
err0:
logerr(p, err, "%s(%s): %s failed", cap, __func__, efunc);
return -1;
}
int audio_pt_fini (struct audio_pt *p, const char *cap)
{
int err, ret = 0;
err = pthread_cond_destroy (&p->cond);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_destroy failed", cap, __func__);
ret = -1;
}
err = pthread_mutex_destroy (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_destroy failed", cap, __func__);
ret = -1;
}
return ret;
}
int audio_pt_lock (struct audio_pt *p, const char *cap)
{
int err;
err = pthread_mutex_lock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_lock failed", cap, __func__);
return -1;
}
return 0;
}
int audio_pt_unlock (struct audio_pt *p, const char *cap)
{
int err;
err = pthread_mutex_unlock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
return -1;
}
return 0;
}
int audio_pt_wait (struct audio_pt *p, const char *cap)
{
int err;
err = pthread_cond_wait (&p->cond, &p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_wait failed", cap, __func__);
return -1;
}
return 0;
}
int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
{
int err;
err = pthread_mutex_unlock (&p->mutex);
if (err) {
logerr(p, err, "%s(%s): pthread_mutex_unlock failed", cap, __func__);
return -1;
}
err = pthread_cond_signal (&p->cond);
if (err) {
logerr(p, err, "%s(%s): pthread_cond_signal failed", cap, __func__);
return -1;
}
return 0;
}
int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
{
int err;
void *ret;
err = pthread_join (p->thread, &ret);
if (err) {
logerr(p, err, "%s(%s): pthread_join failed", cap, __func__);
return -1;
}
*arg = ret;
return 0;
}
+22
View File
@@ -0,0 +1,22 @@
#ifndef QEMU_AUDIO_PT_INT_H
#define QEMU_AUDIO_PT_INT_H
#include <pthread.h>
struct audio_pt {
const char *drv;
pthread_t thread;
pthread_cond_t cond;
pthread_mutex_t mutex;
};
int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
const char *, const char *);
int audio_pt_fini (struct audio_pt *, const char *);
int audio_pt_lock (struct audio_pt *, const char *);
int audio_pt_unlock (struct audio_pt *, const char *);
int audio_pt_wait (struct audio_pt *, const char *);
int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
int audio_pt_join (struct audio_pt *, void **, const char *);
#endif /* QEMU_AUDIO_PT_INT_H */
+54 -76
View File
@@ -36,9 +36,9 @@
#define HWBUF hw->conv_buf
#endif
static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
struct audio_driver *drv)
static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
{
AudioState *s = &glob_audio_state;
int max_voices = glue (drv->max_voices_, TYPE);
int voice_size = glue (drv->voice_size_, TYPE);
@@ -71,24 +71,20 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
{
g_free(hw->buf_emul);
g_free (HWBUF);
HWBUF = NULL;
}
static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
{
if (glue(audio_get_pdo_, TYPE)(hw->s->dev)->mixing_engine) {
size_t samples = hw->samples;
if (audio_bug(__func__, samples == 0)) {
dolog("Attempted to allocate empty buffer\n");
}
HWBUF = g_malloc0(sizeof(STSampleBuffer) + sizeof(st_sample) * samples);
HWBUF->size = samples;
} else {
HWBUF = NULL;
HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
if (!HWBUF) {
dolog ("Could not allocate " NAME " buffer (%d samples)\n",
hw->samples);
return -1;
}
return 0;
}
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
@@ -107,11 +103,7 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
{
int samples;
if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) {
return 0;
}
samples = ((int64_t) sw->HWBUF->size << 32) / sw->ratio;
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
sw->buf = audio_calloc(__func__, samples, sizeof(struct st_sample));
if (!sw->buf) {
@@ -153,23 +145,15 @@ static int glue (audio_pcm_sw_init_, TYPE) (
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
#endif
if (sw->info.is_float) {
#ifdef DAC
sw->conv = mixeng_conv_float[sw->info.nchannels == 2];
sw->conv = mixeng_conv
#else
sw->clip = mixeng_clip_float[sw->info.nchannels == 2];
sw->clip = mixeng_clip
#endif
} else {
#ifdef DAC
sw->conv = mixeng_conv
#else
sw->clip = mixeng_clip
#endif
[sw->info.nchannels == 2]
[sw->info.is_signed]
[sw->info.swap_endianness]
[audio_bits_to_index(sw->info.bits)];
}
[sw->info.nchannels == 2]
[sw->info.sign]
[sw->info.swap_endianness]
[audio_bits_to_index (sw->info.bits)];
sw->name = g_strdup (name);
err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
@@ -199,8 +183,8 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
{
AudioState *s = &glob_audio_state;
HW *hw = *hwp;
AudioState *s = hw->s;
if (!hw->sw_head.lh_first) {
#ifdef DAC
@@ -215,14 +199,15 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
}
}
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
{
AudioState *s = &glob_audio_state;
return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
}
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
{
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (hw->enabled) {
return hw;
}
@@ -230,10 +215,12 @@ static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
return NULL;
}
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
struct audsettings *as)
static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
HW *hw,
struct audsettings *as
)
{
while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
if (audio_pcm_info_eq (&hw->info, as)) {
return hw;
}
@@ -241,10 +228,10 @@ static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
return NULL;
}
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
struct audsettings *as)
static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
{
HW *hw;
AudioState *s = &glob_audio_state;
struct audio_driver *drv = s->drv;
if (!glue (s->nb_hw_voices_, TYPE)) {
@@ -268,8 +255,8 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
return NULL;
}
hw->s = s;
hw->pcm_ops = drv->pcm_ops;
hw->ctl_caps = drv->ctl_caps;
QLIST_INIT (&hw->sw_head);
#ifdef DAC
@@ -280,29 +267,23 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
}
if (audio_bug(__func__, hw->samples <= 0)) {
dolog("hw->samples=%zd\n", hw->samples);
dolog ("hw->samples=%d\n", hw->samples);
goto err1;
}
if (hw->info.is_float) {
#ifdef DAC
hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
hw->clip = mixeng_clip
#else
hw->conv = mixeng_conv_float[hw->info.nchannels == 2];
hw->conv = mixeng_conv
#endif
} else {
#ifdef DAC
hw->clip = mixeng_clip
#else
hw->conv = mixeng_conv
#endif
[hw->info.nchannels == 2]
[hw->info.is_signed]
[hw->info.swap_endianness]
[audio_bits_to_index(hw->info.bits)];
}
[hw->info.nchannels == 2]
[hw->info.sign]
[hw->info.swap_endianness]
[audio_bits_to_index (hw->info.bits)];
glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
goto err1;
}
QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
glue (s->nb_hw_voices_, TYPE) -= 1;
@@ -347,33 +328,33 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
abort();
}
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
{
HW *hw;
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (!pdo->mixing_engine || pdo->fixed_settings) {
hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
if (!pdo->mixing_engine || hw) {
if (pdo->fixed_settings) {
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (hw) {
return hw;
}
}
hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as);
if (hw) {
return hw;
}
hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
if (hw) {
return hw;
}
return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
return glue (audio_pcm_hw_find_any_, TYPE) (NULL);
}
static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
AudioState *s,
static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
const char *sw_name,
struct audsettings *as
)
@@ -381,6 +362,7 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
SW *sw;
HW *hw;
struct audsettings hw_as;
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (pdo->fixed_settings) {
@@ -396,9 +378,8 @@ static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
sw_name ? sw_name : "unknown", sizeof (*sw));
goto err1;
}
sw->s = s;
hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as);
if (!hw) {
goto err2;
}
@@ -449,8 +430,8 @@ SW *glue (AUD_open_, TYPE) (
struct audsettings *as
)
{
AudioState *s;
AudiodevPerDirectionOptions *pdo;
AudioState *s = &glob_audio_state;
AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
dolog ("card=%p name=%p callback_fn=%p as=%p\n",
@@ -458,9 +439,6 @@ SW *glue (AUD_open_, TYPE) (
goto fail;
}
s = card->state;
pdo = glue(audio_get_pdo_, TYPE)(s->dev);
ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
name, as->freq, as->nchannels, as->fmt);
@@ -498,7 +476,7 @@ SW *glue (AUD_open_, TYPE) (
}
}
else {
sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
if (!sw) {
dolog ("Failed to create voice `%s'\n", name);
return NULL;
+75 -56
View File
@@ -43,6 +43,9 @@ typedef struct coreaudioVoiceOut {
UInt32 audioDevicePropertyBufferFrameSize;
AudioStreamBasicDescription outputStreamBasicDescription;
AudioDeviceIOProcID ioprocid;
int live;
int decr;
int rpos;
} coreaudioVoiceOut;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
@@ -394,29 +397,31 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
return 0;
}
#define COREAUDIO_WRAPPER_FUNC(name, ret_type, args_decl, args) \
static ret_type glue(coreaudio_, name)args_decl \
{ \
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; \
ret_type ret; \
\
if (coreaudio_lock(core, "coreaudio_" #name)) { \
return 0; \
} \
\
ret = glue(audio_generic_, name)args; \
\
coreaudio_unlock(core, "coreaudio_" #name); \
return ret; \
static int coreaudio_run_out (HWVoiceOut *hw, int live)
{
int decr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (coreaudio_lock (core, "coreaudio_run_out")) {
return 0;
}
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size))
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
(HWVoiceOut *hw, void *buf, size_t size),
(hw, buf, size))
COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
(hw, buf, size))
#undef COREAUDIO_WRAPPER_FUNC
if (core->decr > live) {
ldebug ("core->decr %d live %d core->live %d\n",
core->decr,
live,
core->live);
}
decr = audio_MIN (core->decr, live);
core->decr -= decr;
core->live = live - decr;
hw->rpos = core->rpos;
coreaudio_unlock (core, "coreaudio_run_out");
return decr;
}
/* callback to feed audiooutput buffer */
static OSStatus audioDeviceIOProc(
@@ -428,11 +433,19 @@ static OSStatus audioDeviceIOProc(
const AudioTimeStamp* inOutputTime,
void* hwptr)
{
UInt32 frameCount, pending_frames;
void *out = outOutputData->mBuffers[0].mData;
UInt32 frame, frameCount;
float *out = outOutputData->mBuffers[0].mData;
HWVoiceOut *hw = hwptr;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
size_t len;
int rpos, live;
struct st_sample *src;
#ifndef FLOAT_MIXENG
#ifdef RECIPROCAL
const float scale = 1.f / UINT_MAX;
#else
const float scale = UINT_MAX;
#endif
#endif
if (coreaudio_lock (core, "audioDeviceIOProc")) {
inInputTime = 0;
@@ -440,37 +453,47 @@ static OSStatus audioDeviceIOProc(
}
frameCount = core->audioDevicePropertyBufferFrameSize;
pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
live = core->live;
/* if there are not enough samples, set signal and return */
if (pending_frames < frameCount) {
if (live < frameCount) {
inInputTime = 0;
coreaudio_unlock (core, "audioDeviceIOProc(empty)");
return 0;
}
len = frameCount * hw->info.bytes_per_frame;
while (len) {
size_t write_len;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
rpos = core->rpos;
src = hw->mix_buf + rpos;
write_len = MIN(MIN(hw->pending_emul, len),
hw->size_emul - start);
memcpy(out, hw->buf_emul + start, write_len);
hw->pending_emul -= write_len;
len -= write_len;
out += write_len;
/* fill buffer */
for (frame = 0; frame < frameCount; frame++) {
#ifdef FLOAT_MIXENG
*out++ = src[frame].l; /* left channel */
*out++ = src[frame].r; /* right channel */
#else
#ifdef RECIPROCAL
*out++ = src[frame].l * scale; /* left channel */
*out++ = src[frame].r * scale; /* right channel */
#else
*out++ = src[frame].l / scale; /* left channel */
*out++ = src[frame].r / scale; /* right channel */
#endif
#endif
}
rpos = (rpos + frameCount) % hw->samples;
core->decr += frameCount;
core->rpos = rpos;
coreaudio_unlock (core, "audioDeviceIOProc");
return 0;
}
static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
void *drv_opaque)
{
@@ -482,7 +505,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
Audiodev *dev = drv_opaque;
AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
int frames;
struct audsettings fake_as;
/* create mutex */
err = pthread_mutex_init(&core->mutex, NULL);
@@ -491,9 +513,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
return -1;
}
fake_as = *as;
as = &fake_as;
as->fmt = AUDIO_FORMAT_F32;
audio_pcm_init_info (&hw->info, as);
status = coreaudio_get_voice(&core->outputDeviceID);
@@ -562,7 +581,6 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
/* set Samplerate */
core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
status = coreaudio_set_streamformat(core->outputDeviceID,
&core->outputStreamBasicDescription);
if (status != kAudioHardwareNoError) {
@@ -629,12 +647,13 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
}
}
static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
OSStatus status;
coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
if (enable) {
switch (cmd) {
case VOICE_ENABLE:
/* start playback */
if (!isPlaying(core->outputDeviceID)) {
status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
@@ -642,7 +661,9 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
coreaudio_logerr (status, "Could not resume playback\n");
}
}
} else {
break;
case VOICE_DISABLE:
/* stop playback */
if (!audio_is_cleaning_up()) {
if (isPlaying(core->outputDeviceID)) {
@@ -653,7 +674,9 @@ static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
}
}
}
break;
}
return 0;
}
static void *coreaudio_audio_init(Audiodev *dev)
@@ -668,13 +691,9 @@ static void coreaudio_audio_fini (void *opaque)
static struct audio_pcm_ops coreaudio_pcm_ops = {
.init_out = coreaudio_init_out,
.fini_out = coreaudio_fini_out,
/* wrapper for audio_generic_write */
.run_out = coreaudio_run_out,
.write = coreaudio_write,
/* wrapper for audio_generic_get_buffer_out */
.get_buffer_out = coreaudio_get_buffer_out,
/* wrapper for audio_generic_put_buffer_out */
.put_buffer_out = coreaudio_put_buffer_out,
.enable_out = coreaudio_enable_out
.ctl_out = coreaudio_ctl_out
};
static struct audio_driver coreaudio_audio_driver = {
+26 -28
View File
@@ -29,8 +29,6 @@
#define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
#define FIELD dsound_capture_buffer
#define FIELD2 dsound_capture
#define HWVOICE HWVoiceIn
#define DSOUNDVOICE DSoundVoiceIn
#else
#define NAME "playback buffer"
#define NAME2 "DirectSound"
@@ -39,8 +37,6 @@
#define BUFPTR LPDIRECTSOUNDBUFFER
#define FIELD dsound_buffer
#define FIELD2 dsound
#define HWVOICE HWVoiceOut
#define DSOUNDVOICE DSoundVoiceOut
#endif
static int glue (dsound_unlock_, TYPE) (
@@ -76,6 +72,8 @@ static int glue (dsound_lock_, TYPE) (
)
{
HRESULT hr;
LPVOID p1 = NULL, p2 = NULL;
DWORD blen1 = 0, blen2 = 0;
DWORD flag;
#ifdef DSBTYPE_IN
@@ -83,7 +81,7 @@ static int glue (dsound_lock_, TYPE) (
#else
flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
#endif
hr = glue(IFACE, _Lock)(buf, pos, len, p1p, blen1p, p2p, blen2p, flag);
hr = glue(IFACE, _Lock)(buf, pos, len, &p1, &blen1, &p2, &blen2, flag);
if (FAILED (hr)) {
#ifndef DSBTYPE_IN
@@ -98,34 +96,34 @@ static int glue (dsound_lock_, TYPE) (
goto fail;
}
if ((p1p && *p1p && (*blen1p % info->bytes_per_frame)) ||
(p2p && *p2p && (*blen2p % info->bytes_per_frame))) {
dolog("DirectSound returned misaligned buffer %ld %ld\n",
*blen1p, *blen2p);
glue(dsound_unlock_, TYPE)(buf, *p1p, p2p ? *p2p : NULL, *blen1p,
blen2p ? *blen2p : 0);
if ((p1 && (blen1 & info->align)) || (p2 && (blen2 & info->align))) {
dolog ("DirectSound returned misaligned buffer %ld %ld\n",
blen1, blen2);
glue (dsound_unlock_, TYPE) (buf, p1, p2, blen1, blen2);
goto fail;
}
if (p1p && !*p1p && *blen1p) {
dolog("warning: !p1 && blen1=%ld\n", *blen1p);
*blen1p = 0;
if (!p1 && blen1) {
dolog ("warning: !p1 && blen1=%ld\n", blen1);
blen1 = 0;
}
if (p2p && !*p2p && *blen2p) {
dolog("warning: !p2 && blen2=%ld\n", *blen2p);
*blen2p = 0;
if (!p2 && blen2) {
dolog ("warning: !p2 && blen2=%ld\n", blen2);
blen2 = 0;
}
*p1p = p1;
*p2p = p2;
*blen1p = blen1;
*blen2p = blen2;
return 0;
fail:
*p1p = NULL - 1;
*p2p = NULL - 1;
*blen1p = -1;
if (p2p) {
*p2p = NULL - 1;
*blen2p = -1;
}
*blen2p = -1;
return -1;
}
@@ -244,23 +242,25 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
goto fail0;
}
ds->first_time = true;
ds->first_time = 1;
obt_as.endianness = 0;
audio_pcm_init_info (&hw->info, &obt_as);
if (bc.dwBufferBytes % hw->info.bytes_per_frame) {
if (bc.dwBufferBytes & hw->info.align) {
dolog (
"GetCaps returned misaligned buffer size %ld, alignment %d\n",
bc.dwBufferBytes, hw->info.bytes_per_frame
bc.dwBufferBytes, hw->info.align + 1
);
}
hw->size_emul = bc.dwBufferBytes;
hw->samples = bc.dwBufferBytes / hw->info.bytes_per_frame;
hw->samples = bc.dwBufferBytes >> hw->info.shift;
ds->s = s;
#ifdef DEBUG_DSOUND
dolog ("caps %ld, desc %ld\n",
bc.dwBufferBytes, bd.dwBufferBytes);
dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
#endif
return 0;
@@ -276,5 +276,3 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as,
#undef BUFPTR
#undef FIELD
#undef FIELD2
#undef HWVOICE
#undef DSOUNDVOICE
+286 -118
View File
@@ -53,14 +53,20 @@ typedef struct {
typedef struct {
HWVoiceOut hw;
LPDIRECTSOUNDBUFFER dsound_buffer;
bool first_time;
DWORD old_pos;
int first_time;
dsound *s;
#ifdef DEBUG_DSOUND
DWORD old_ppos;
DWORD played;
DWORD mixed;
#endif
} DSoundVoiceOut;
typedef struct {
HWVoiceIn hw;
int first_time;
LPDIRECTSOUNDCAPTUREBUFFER dsound_capture_buffer;
bool first_time;
dsound *s;
} DSoundVoiceIn;
@@ -237,6 +243,11 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 (
dsound_log_hresult (hr);
}
static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs)
{
return muldiv64(usecs, info->bytes_per_second, 1000000);
}
#ifdef DEBUG_DSOUND
static void print_wave_format (WAVEFORMATEX *wfx)
{
@@ -279,7 +290,7 @@ static int dsound_get_status_out (LPDIRECTSOUNDBUFFER dsb, DWORD *statusp,
return -1;
}
if (*statusp & DSBSTATUS_BUFFERLOST) {
if (*statusp & DSERR_BUFFERLOST) {
dsound_restore_out(dsb, s);
return -1;
}
@@ -301,6 +312,33 @@ static int dsound_get_status_in (LPDIRECTSOUNDCAPTUREBUFFER dscb,
return 0;
}
static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
{
int src_len1 = dst_len;
int src_len2 = 0;
int pos = hw->rpos + dst_len;
struct st_sample *src1 = hw->mix_buf + hw->rpos;
struct st_sample *src2 = NULL;
if (pos > hw->samples) {
src_len1 = hw->samples - hw->rpos;
src2 = hw->mix_buf;
src_len2 = dst_len - src_len1;
pos = src_len2;
}
if (src_len1) {
hw->clip (dst, src1, src_len1);
}
if (src_len2) {
dst = advance (dst, src_len1 << hw->info.shift);
hw->clip (dst, src2, src_len2);
}
hw->rpos = pos % hw->samples;
}
static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsound *s)
{
@@ -312,7 +350,7 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
dsb,
&hw->info,
0,
hw->size_emul,
hw->samples << hw->info.shift,
&p1, &p2,
&blen1, &blen2,
1,
@@ -322,8 +360,8 @@ static void dsound_clear_sample (HWVoiceOut *hw, LPDIRECTSOUNDBUFFER dsb,
return;
}
len1 = blen1 / hw->info.bytes_per_frame;
len2 = blen2 / hw->info.bytes_per_frame;
len1 = blen1 >> hw->info.shift;
len2 = blen2 >> hw->info.shift;
#ifdef DEBUG_DSOUND
dolog ("clear %p,%ld,%ld %p,%ld,%ld\n",
@@ -363,7 +401,7 @@ static int dsound_open (dsound *s)
return 0;
}
static void dsound_enable_out(HWVoiceOut *hw, bool enable)
static int dsound_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
HRESULT hr;
DWORD status;
@@ -373,17 +411,18 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
if (!dsb) {
dolog ("Attempt to control voice without a buffer\n");
return;
return 0;
}
if (enable) {
switch (cmd) {
case VOICE_ENABLE:
if (dsound_get_status_out (dsb, &status, s)) {
return;
return -1;
}
if (status & DSBSTATUS_PLAYING) {
dolog ("warning: Voice is already playing\n");
return;
return 0;
}
dsound_clear_sample (hw, dsb, s);
@@ -391,85 +430,170 @@ static void dsound_enable_out(HWVoiceOut *hw, bool enable)
hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not start playing buffer\n");
return;
return -1;
}
} else {
break;
case VOICE_DISABLE:
if (dsound_get_status_out (dsb, &status, s)) {
return;
return -1;
}
if (status & DSBSTATUS_PLAYING) {
hr = IDirectSoundBuffer_Stop (dsb);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not stop playing buffer\n");
return;
return -1;
}
}
else {
dolog ("warning: Voice is not playing\n");
}
break;
}
return 0;
}
static void *dsound_get_buffer_out(HWVoiceOut *hw, size_t *size)
static int dsound_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int dsound_run_out (HWVoiceOut *hw, int live)
{
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
HRESULT hr;
DWORD ppos, wpos, act_size;
size_t req_size;
int err;
void *ret;
hr = IDirectSoundBuffer_GetCurrentPosition(
dsb, &ppos, ds->first_time ? &wpos : NULL);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get playback buffer position\n");
*size = 0;
return NULL;
}
if (ds->first_time) {
hw->pos_emul = wpos;
ds->first_time = false;
}
req_size = audio_ring_dist(ppos, hw->pos_emul, hw->size_emul);
req_size = MIN(req_size, hw->size_emul - hw->pos_emul);
if (req_size == 0) {
*size = 0;
return NULL;
}
err = dsound_lock_out(dsb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
&act_size, NULL, false, ds->s);
if (err) {
dolog("Failed to lock buffer\n");
*size = 0;
return NULL;
}
*size = act_size;
return ret;
}
static size_t dsound_put_buffer_out(HWVoiceOut *hw, void *buf, size_t len)
{
HRESULT hr;
DSoundVoiceOut *ds = (DSoundVoiceOut *) hw;
LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer;
int err = dsound_unlock_out(dsb, buf, NULL, len, 0);
int len, hwshift;
DWORD blen1, blen2;
DWORD len1, len2;
DWORD decr;
DWORD wpos, ppos, old_pos;
LPVOID p1, p2;
int bufsize;
dsound *s = ds->s;
AudiodevDsoundOptions *dso = &s->dev->u.dsound;
if (err) {
dolog("Failed to unlock buffer!!\n");
if (!dsb) {
dolog ("Attempt to run empty with playback buffer\n");
return 0;
}
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
return len;
hwshift = hw->info.shift;
bufsize = hw->samples << hwshift;
hr = IDirectSoundBuffer_GetCurrentPosition (
dsb,
&ppos,
ds->first_time ? &wpos : NULL
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not get playback buffer position\n");
return 0;
}
len = live << hwshift;
if (ds->first_time) {
if (dso->latency) {
DWORD cur_blat;
cur_blat = audio_ring_dist (wpos, ppos, bufsize);
ds->first_time = 0;
old_pos = wpos;
old_pos +=
usecs_to_bytes(&hw->info, dso->latency) - cur_blat;
old_pos %= bufsize;
old_pos &= ~hw->info.align;
}
else {
old_pos = wpos;
}
#ifdef DEBUG_DSOUND
ds->played = 0;
ds->mixed = 0;
#endif
}
else {
if (ds->old_pos == ppos) {
#ifdef DEBUG_DSOUND
dolog ("old_pos == ppos\n");
#endif
return 0;
}
#ifdef DEBUG_DSOUND
ds->played += audio_ring_dist (ds->old_pos, ppos, hw->bufsize);
#endif
old_pos = ds->old_pos;
}
if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
len = ppos - old_pos;
}
else {
if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
len = bufsize - old_pos + ppos;
}
}
if (audio_bug(__func__, len < 0 || len > bufsize)) {
dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
len, bufsize, old_pos, ppos);
return 0;
}
len &= ~hw->info.align;
if (!len) {
return 0;
}
#ifdef DEBUG_DSOUND
ds->old_ppos = ppos;
#endif
err = dsound_lock_out (
dsb,
&hw->info,
old_pos,
len,
&p1, &p2,
&blen1, &blen2,
0,
s
);
if (err) {
return 0;
}
len1 = blen1 >> hwshift;
len2 = blen2 >> hwshift;
decr = len1 + len2;
if (p1 && len1) {
dsound_write_sample (hw, p1, len1);
}
if (p2 && len2) {
dsound_write_sample (hw, p2, len2);
}
dsound_unlock_out (dsb, p1, p2, blen1, blen2);
ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
#ifdef DEBUG_DSOUND
ds->mixed += decr << hwshift;
dolog ("played %lu mixed %lu diff %ld sec %f\n",
ds->played,
ds->mixed,
ds->mixed - ds->played,
abs (ds->mixed - ds->played) / (double) hw->info.bytes_per_second);
#endif
return decr;
}
static void dsound_enable_in(HWVoiceIn *hw, bool enable)
static int dsound_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
HRESULT hr;
DWORD status;
@@ -478,17 +602,18 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable)
if (!dscb) {
dolog ("Attempt to control capture voice without a buffer\n");
return;
return -1;
}
if (enable) {
switch (cmd) {
case VOICE_ENABLE:
if (dsound_get_status_in (dscb, &status)) {
return;
return -1;
}
if (status & DSCBSTATUS_CAPTURING) {
dolog ("warning: Voice is already capturing\n");
return;
return 0;
}
/* clear ?? */
@@ -496,80 +621,125 @@ static void dsound_enable_in(HWVoiceIn *hw, bool enable)
hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not start capturing\n");
return;
return -1;
}
} else {
break;
case VOICE_DISABLE:
if (dsound_get_status_in (dscb, &status)) {
return;
return -1;
}
if (status & DSCBSTATUS_CAPTURING) {
hr = IDirectSoundCaptureBuffer_Stop (dscb);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not stop capturing\n");
return;
return -1;
}
}
else {
dolog ("warning: Voice is not capturing\n");
}
break;
}
return 0;
}
static void *dsound_get_buffer_in(HWVoiceIn *hw, size_t *size)
static int dsound_read (SWVoiceIn *sw, void *buf, int len)
{
return audio_pcm_sw_read (sw, buf, len);
}
static int dsound_run_in (HWVoiceIn *hw)
{
int err;
HRESULT hr;
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
HRESULT hr;
DWORD cpos, rpos, act_size;
size_t req_size;
int err;
void *ret;
int live, len, dead;
DWORD blen1, blen2;
DWORD len1, len2;
DWORD decr;
DWORD cpos, rpos;
LPVOID p1, p2;
int hwshift;
dsound *s = ds->s;
hr = IDirectSoundCaptureBuffer_GetCurrentPosition(
dscb, &cpos, ds->first_time ? &rpos : NULL);
if (FAILED(hr)) {
dsound_logerr(hr, "Could not get capture buffer position\n");
*size = 0;
return NULL;
if (!dscb) {
dolog ("Attempt to run without capture buffer\n");
return 0;
}
hwshift = hw->info.shift;
live = audio_pcm_hw_get_live_in (hw);
dead = hw->samples - live;
if (!dead) {
return 0;
}
hr = IDirectSoundCaptureBuffer_GetCurrentPosition (
dscb,
&cpos,
ds->first_time ? &rpos : NULL
);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not get capture buffer position\n");
return 0;
}
if (ds->first_time) {
hw->pos_emul = rpos;
ds->first_time = false;
ds->first_time = 0;
if (rpos & hw->info.align) {
ldebug ("warning: Misaligned capture read position %ld(%d)\n",
rpos, hw->info.align);
}
hw->wpos = rpos >> hwshift;
}
req_size = audio_ring_dist(cpos, hw->pos_emul, hw->size_emul);
req_size = MIN(*size, MIN(req_size, hw->size_emul - hw->pos_emul));
if (req_size == 0) {
*size = 0;
return NULL;
if (cpos & hw->info.align) {
ldebug ("warning: Misaligned capture position %ld(%d)\n",
cpos, hw->info.align);
}
cpos >>= hwshift;
err = dsound_lock_in(dscb, &hw->info, hw->pos_emul, req_size, &ret, NULL,
&act_size, NULL, false, ds->s);
len = audio_ring_dist (cpos, hw->wpos, hw->samples);
if (!len) {
return 0;
}
len = audio_MIN (len, dead);
err = dsound_lock_in (
dscb,
&hw->info,
hw->wpos << hwshift,
len << hwshift,
&p1,
&p2,
&blen1,
&blen2,
0,
s
);
if (err) {
dolog("Failed to lock buffer\n");
*size = 0;
return NULL;
return 0;
}
*size = act_size;
return ret;
}
len1 = blen1 >> hwshift;
len2 = blen2 >> hwshift;
decr = len1 + len2;
static void dsound_put_buffer_in(HWVoiceIn *hw, void *buf, size_t len)
{
DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
LPDIRECTSOUNDCAPTUREBUFFER dscb = ds->dsound_capture_buffer;
int err = dsound_unlock_in(dscb, buf, NULL, len, 0);
if (err) {
dolog("Failed to unlock buffer!!\n");
return;
if (p1 && len1) {
hw->conv (hw->conv_buf + hw->wpos, p1, len1);
}
hw->pos_emul = (hw->pos_emul + len) % hw->size_emul;
if (p2 && len2) {
hw->conv (hw->conv_buf, p2, len2);
}
dsound_unlock_in (dscb, p1, p2, blen1, blen2);
hw->wpos = (hw->wpos + decr) % hw->samples;
return decr;
}
static void dsound_audio_fini (void *opaque)
@@ -685,17 +855,15 @@ static void *dsound_audio_init(Audiodev *dev)
static struct audio_pcm_ops dsound_pcm_ops = {
.init_out = dsound_init_out,
.fini_out = dsound_fini_out,
.write = audio_generic_write,
.get_buffer_out = dsound_get_buffer_out,
.put_buffer_out = dsound_put_buffer_out,
.enable_out = dsound_enable_out,
.run_out = dsound_run_out,
.write = dsound_write,
.ctl_out = dsound_ctl_out,
.init_in = dsound_init_in,
.fini_in = dsound_fini_in,
.read = audio_generic_read,
.get_buffer_in = dsound_get_buffer_in,
.put_buffer_in = dsound_put_buffer_in,
.enable_in = dsound_enable_in,
.run_in = dsound_run_in,
.read = dsound_read,
.ctl_in = dsound_ctl_in
};
static struct audio_driver dsound_audio_driver = {
-70
View File
@@ -267,76 +267,6 @@ f_sample *mixeng_clip[2][2][2][3] = {
}
};
#ifdef FLOAT_MIXENG
#define CONV_NATURAL_FLOAT(x) (x)
#define CLIP_NATURAL_FLOAT(x) (x)
#else
static const float float_scale = UINT_MAX / 2.f;
#define CONV_NATURAL_FLOAT(x) ((x) * float_scale)
#ifdef RECIPROCAL
static const float float_scale_reciprocal = 2.f / UINT_MAX;
#define CLIP_NATURAL_FLOAT(x) ((x) * float_scale_reciprocal)
#else
#define CLIP_NATURAL_FLOAT(x) ((x) / float_scale)
#endif
#endif
static void conv_natural_float_to_mono(struct st_sample *dst, const void *src,
int samples)
{
float *in = (float *)src;
while (samples--) {
dst->r = dst->l = CONV_NATURAL_FLOAT(*in++);
dst++;
}
}
static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src,
int samples)
{
float *in = (float *)src;
while (samples--) {
dst->l = CONV_NATURAL_FLOAT(*in++);
dst->r = CONV_NATURAL_FLOAT(*in++);
dst++;
}
}
t_sample *mixeng_conv_float[2] = {
conv_natural_float_to_mono,
conv_natural_float_to_stereo,
};
static void clip_natural_float_from_mono(void *dst, const struct st_sample *src,
int samples)
{
float *out = (float *)dst;
while (samples--) {
*out++ = CLIP_NATURAL_FLOAT(src->l + src->r);
src++;
}
}
static void clip_natural_float_from_stereo(
void *dst, const struct st_sample *src, int samples)
{
float *out = (float *)dst;
while (samples--) {
*out++ = CLIP_NATURAL_FLOAT(src->l);
*out++ = CLIP_NATURAL_FLOAT(src->r);
src++;
}
}
f_sample *mixeng_clip_float[2] = {
clip_natural_float_from_mono,
clip_natural_float_from_stereo,
};
void audio_sample_to_uint64(void *samples, int pos,
uint64_t *left, uint64_t *right)
+4 -10
View File
@@ -33,24 +33,18 @@ struct st_sample { mixeng_real l; mixeng_real r; };
struct mixeng_volume { int mute; int64_t r; int64_t l; };
struct st_sample { int64_t l; int64_t r; };
#endif
typedef struct st_sample st_sample;
typedef void (t_sample) (struct st_sample *dst, const void *src, int samples);
typedef void (f_sample) (void *dst, const struct st_sample *src, int samples);
/* indices: [stereo][signed][swap endiannes][8, 16 or 32-bits] */
extern t_sample *mixeng_conv[2][2][2][3];
extern f_sample *mixeng_clip[2][2][2][3];
/* indices: [stereo] */
extern t_sample *mixeng_conv_float[2];
extern f_sample *mixeng_clip_float[2];
void *st_rate_start (int inrate, int outrate);
void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
size_t *isamp, size_t *osamp);
void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf,
size_t *isamp, size_t *osamp);
void st_rate_flow (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
int *isamp, int *osamp);
void st_rate_flow_mix (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
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);
+12 -10
View File
@@ -41,31 +41,32 @@ static inline mixeng_real glue (conv_, ET) (IN_T v)
#ifdef RECIPROCAL
#ifdef SIGNED
return nv * (2.f / ((mixeng_real)IN_MAX - IN_MIN));
return nv * (1.f / (mixeng_real) (IN_MAX - IN_MIN));
#else
return (nv - HALF) * (2.f / (mixeng_real)IN_MAX);
return (nv - HALF) * (1.f / (mixeng_real) IN_MAX);
#endif
#else /* !RECIPROCAL */
#ifdef SIGNED
return nv / (((mixeng_real)IN_MAX - IN_MIN) / 2.f);
return nv / (mixeng_real) ((mixeng_real) IN_MAX - IN_MIN);
#else
return (nv - HALF) / ((mixeng_real)IN_MAX / 2.f);
return (nv - HALF) / (mixeng_real) IN_MAX;
#endif
#endif
}
static inline IN_T glue (clip_, ET) (mixeng_real v)
{
if (v >= 1.f) {
if (v >= 0.5) {
return IN_MAX;
} else if (v < -1.f) {
}
else if (v < -0.5) {
return IN_MIN;
}
#ifdef SIGNED
return ENDIAN_CONVERT((IN_T)(v * (((mixeng_real)IN_MAX - IN_MIN) / 2.f)));
return ENDIAN_CONVERT ((IN_T) (v * ((mixeng_real) IN_MAX - IN_MIN)));
#else
return ENDIAN_CONVERT((IN_T)((v * ((mixeng_real)IN_MAX / 2.f)) + HALF));
return ENDIAN_CONVERT ((IN_T) ((v * IN_MAX) + HALF));
#endif
}
@@ -83,9 +84,10 @@ static inline int64_t glue (conv_, ET) (IN_T v)
static inline IN_T glue (clip_, ET) (int64_t v)
{
if (v >= 0x7fffffffLL) {
if (v >= 0x7f000000) {
return IN_MAX;
} else if (v < -2147483648LL) {
}
else if (v < -2147483648LL) {
return IN_MIN;
}
+62 -28
View File
@@ -33,27 +33,43 @@
typedef struct NoVoiceOut {
HWVoiceOut hw;
RateCtl rate;
int64_t old_ticks;
} NoVoiceOut;
typedef struct NoVoiceIn {
HWVoiceIn hw;
RateCtl rate;
int64_t old_ticks;
} NoVoiceIn;
static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
static int no_run_out (HWVoiceOut *hw, int live)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
return audio_rate_get_bytes(&hw->info, &no->rate, len);
int decr, samples;
int64_t now;
int64_t ticks;
int64_t bytes;
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - no->old_ticks;
bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
bytes = audio_MIN(bytes, INT_MAX);
samples = bytes >> hw->info.shift;
no->old_ticks = now;
decr = audio_MIN (live, samples);
hw->rpos = (hw->rpos + decr) % hw->samples;
return decr;
}
static int no_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write(sw, buf, len);
}
static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
audio_pcm_init_info (&hw->info, as);
hw->samples = 1024;
audio_rate_start(&no->rate);
return 0;
}
@@ -62,22 +78,17 @@ static void no_fini_out (HWVoiceOut *hw)
(void) hw;
}
static void no_enable_out(HWVoiceOut *hw, bool enable)
static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
NoVoiceOut *no = (NoVoiceOut *) hw;
if (enable) {
audio_rate_start(&no->rate);
}
(void) hw;
(void) cmd;
return 0;
}
static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
audio_pcm_init_info (&hw->info, as);
hw->samples = 1024;
audio_rate_start(&no->rate);
return 0;
}
@@ -86,22 +97,44 @@ static void no_fini_in (HWVoiceIn *hw)
(void) hw;
}
static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
static int no_run_in (HWVoiceIn *hw)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);
int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live;
int samples = 0;
audio_pcm_info_clear_buf(&hw->info, buf, bytes / hw->info.bytes_per_frame);
return bytes;
if (dead) {
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t ticks = now - no->old_ticks;
int64_t bytes =
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
no->old_ticks = now;
bytes = audio_MIN (bytes, INT_MAX);
samples = bytes >> hw->info.shift;
samples = audio_MIN (samples, dead);
}
return samples;
}
static void no_enable_in(HWVoiceIn *hw, bool enable)
static int no_read (SWVoiceIn *sw, void *buf, int size)
{
NoVoiceIn *no = (NoVoiceIn *) hw;
/* 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;
}
if (enable) {
audio_rate_start(&no->rate);
}
static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
(void) hw;
(void) cmd;
return 0;
}
static void *no_audio_init(Audiodev *dev)
@@ -117,14 +150,15 @@ static void no_audio_fini (void *opaque)
static struct audio_pcm_ops no_pcm_ops = {
.init_out = no_init_out,
.fini_out = no_fini_out,
.run_out = no_run_out,
.write = no_write,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = no_enable_out,
.ctl_out = no_ctl_out,
.init_in = no_init_in,
.fini_in = no_fini_in,
.run_in = no_run_in,
.read = no_read,
.enable_in = no_enable_in
.ctl_in = no_ctl_in
};
static struct audio_driver no_audio_driver = {
+256 -155
View File
@@ -40,15 +40,19 @@
typedef struct OSSVoiceOut {
HWVoiceOut hw;
void *pcm_buf;
int fd;
int wpos;
int nfrags;
int fragsize;
int mmapped;
int pending;
Audiodev *dev;
} OSSVoiceOut;
typedef struct OSSVoiceIn {
HWVoiceIn hw;
void *pcm_buf;
int fd;
int nfrags;
int fragsize;
@@ -106,28 +110,33 @@ static void oss_anal_close (int *fdp)
static void oss_helper_poll_out (void *opaque)
{
AudioState *s = opaque;
audio_run(s, "oss_poll_out");
(void) opaque;
audio_run ("oss_poll_out");
}
static void oss_helper_poll_in (void *opaque)
{
AudioState *s = opaque;
audio_run(s, "oss_poll_in");
(void) opaque;
audio_run ("oss_poll_in");
}
static void oss_poll_out (HWVoiceOut *hw)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
qemu_set_fd_handler(oss->fd, NULL, oss_helper_poll_out, hw->s);
qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL);
}
static void oss_poll_in (HWVoiceIn *hw)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
qemu_set_fd_handler(oss->fd, oss_helper_poll_in, NULL, hw->s);
qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL);
}
static int oss_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int aud_to_ossfmt (AudioFormat fmt, int endianness)
@@ -367,96 +376,97 @@ static int oss_open(int in, struct oss_params *req, audsettings *as,
return -1;
}
static size_t oss_get_available_bytes(OSSVoiceOut *oss)
static void oss_write_pending (OSSVoiceOut *oss)
{
int err;
struct count_info cntinfo;
assert(oss->mmapped);
err = ioctl(oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
if (err < 0) {
oss_logerr(errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
}
static void oss_run_buffer_out(HWVoiceOut *hw)
{
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
if (!oss->mmapped) {
audio_generic_run_buffer_out(hw);
}
}
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
if (oss->mmapped) {
*size = MIN(oss_get_available_bytes(oss), hw->size_emul - hw->pos_emul);
return hw->buf_emul + hw->pos_emul;
} else {
return audio_generic_get_buffer_out(hw, size);
}
}
static size_t oss_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
if (oss->mmapped) {
assert(buf == hw->buf_emul + hw->pos_emul && size < hw->size_emul);
hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
return size;
} else {
return audio_generic_put_buffer_out(hw, buf, size);
}
}
static size_t oss_write(HWVoiceOut *hw, void *buf, size_t len)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
size_t pos;
HWVoiceOut *hw = &oss->hw;
if (oss->mmapped) {
size_t total_len;
len = MIN(len, oss_get_available_bytes(oss));
total_len = len;
while (len) {
size_t to_copy = MIN(len, hw->size_emul - hw->pos_emul);
memcpy(hw->buf_emul + hw->pos_emul, buf, to_copy);
hw->pos_emul = (hw->pos_emul + to_copy) % hw->size_emul;
buf += to_copy;
len -= to_copy;
}
return total_len;
return;
}
pos = 0;
while (len) {
while (oss->pending) {
int samples_written;
ssize_t bytes_written;
void *pcm = advance(buf, pos);
int samples_till_end = hw->samples - oss->wpos;
int samples_to_write = audio_MIN (oss->pending, samples_till_end);
int bytes_to_write = samples_to_write << hw->info.shift;
void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift);
bytes_written = write(oss->fd, pcm, len);
bytes_written = write (oss->fd, pcm, bytes_to_write);
if (bytes_written < 0) {
if (errno != EAGAIN) {
oss_logerr(errno, "failed to write %zu bytes\n",
len);
oss_logerr (errno, "failed to write %d bytes\n",
bytes_to_write);
}
return pos;
}
pos += bytes_written;
if (bytes_written < len) {
break;
}
len -= bytes_written;
if (bytes_written & hw->info.align) {
dolog ("misaligned write asked for %d, but got %zd\n",
bytes_to_write, bytes_written);
return;
}
samples_written = bytes_written >> hw->info.shift;
oss->pending -= samples_written;
oss->wpos = (oss->wpos + samples_written) % hw->samples;
if (bytes_written - bytes_to_write) {
break;
}
}
return pos;
}
static int oss_run_out (HWVoiceOut *hw, int live)
{
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
int err, decr;
struct audio_buf_info abinfo;
struct count_info cntinfo;
int bufsize;
bufsize = hw->samples << hw->info.shift;
if (oss->mmapped) {
int bytes, pos;
err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
if (err < 0) {
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
pos = hw->rpos << hw->info.shift;
bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize);
decr = audio_MIN (bytes >> hw->info.shift, live);
}
else {
err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
if (err < 0) {
oss_logerr (errno, "SNDCTL_DSP_GETOPTR failed\n");
return 0;
}
if (abinfo.bytes > bufsize) {
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
abinfo.bytes = bufsize;
}
if (abinfo.bytes < 0) {
trace_oss_invalid_available_size(abinfo.bytes, bufsize);
return 0;
}
decr = audio_MIN (abinfo.bytes >> hw->info.shift, live);
if (!decr) {
return 0;
}
}
decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending);
oss->pending += decr;
oss_write_pending (oss);
return decr;
}
static void oss_fini_out (HWVoiceOut *hw)
@@ -467,13 +477,18 @@ static void oss_fini_out (HWVoiceOut *hw)
ldebug ("oss_fini\n");
oss_anal_close (&oss->fd);
if (oss->mmapped && hw->buf_emul) {
err = munmap(hw->buf_emul, hw->size_emul);
if (err) {
oss_logerr(errno, "Failed to unmap buffer %p, size %zu\n",
hw->buf_emul, hw->size_emul);
if (oss->pcm_buf) {
if (oss->mmapped) {
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
if (err) {
oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
oss->pcm_buf, hw->samples << hw->info.shift);
}
}
hw->buf_emul = NULL;
else {
g_free (oss->pcm_buf);
}
oss->pcm_buf = NULL;
}
}
@@ -515,29 +530,28 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
oss->nfrags = obt.nfrags;
oss->fragsize = obt.fragsize;
if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {
if (obt.nfrags * obt.fragsize & hw->info.align) {
dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
obt.nfrags * obt.fragsize, hw->info.bytes_per_frame);
obt.nfrags * obt.fragsize, hw->info.align + 1);
}
hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame;
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->mmapped = 0;
if (oopts->has_try_mmap && oopts->try_mmap) {
hw->size_emul = hw->samples * hw->info.bytes_per_frame;
hw->buf_emul = mmap(
oss->pcm_buf = mmap (
NULL,
hw->size_emul,
hw->samples << hw->info.shift,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0
);
if (hw->buf_emul == MAP_FAILED) {
oss_logerr(errno, "Failed to map %zu bytes of DAC\n",
hw->size_emul);
hw->buf_emul = NULL;
} else {
if (oss->pcm_buf == MAP_FAILED) {
oss_logerr (errno, "Failed to map %d bytes of DAC\n",
hw->samples << hw->info.shift);
}
else {
int err;
int trig = 0;
if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
@@ -557,63 +571,88 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as,
}
if (!oss->mmapped) {
err = munmap(hw->buf_emul, hw->size_emul);
err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
if (err) {
oss_logerr(errno, "Failed to unmap buffer %p size %zu\n",
hw->buf_emul, hw->size_emul);
oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
oss->pcm_buf, hw->samples << hw->info.shift);
}
hw->buf_emul = NULL;
}
}
}
if (!oss->mmapped) {
oss->pcm_buf = audio_calloc(__func__,
hw->samples,
1 << hw->info.shift);
if (!oss->pcm_buf) {
dolog (
"Could not allocate DAC buffer (%d samples, each %d bytes)\n",
hw->samples,
1 << hw->info.shift
);
oss_anal_close (&fd);
return -1;
}
}
oss->fd = fd;
oss->dev = dev;
return 0;
}
static void oss_enable_out(HWVoiceOut *hw, bool enable)
static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
int trig;
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
if (enable) {
hw->poll_mode = opdo->try_poll;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = opdo->try_poll;
ldebug("enabling voice\n");
if (hw->poll_mode) {
oss_poll_out(hw);
}
ldebug ("enabling voice\n");
if (poll_mode) {
oss_poll_out (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
if (!oss->mmapped) {
return;
}
if (!oss->mmapped) {
return 0;
}
audio_pcm_info_clear_buf(&hw->info, hw->buf_emul, hw->samples);
trig = PCM_ENABLE_OUTPUT;
if (ioctl(oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
oss_logerr(errno,
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n");
return;
audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples);
trig = PCM_ENABLE_OUTPUT;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
oss_logerr (
errno,
"SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
);
return -1;
}
}
} else {
break;
case VOICE_DISABLE:
if (hw->poll_mode) {
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
hw->poll_mode = 0;
}
if (!oss->mmapped) {
return;
return 0;
}
ldebug ("disabling voice\n");
trig = 0;
if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
oss_logerr (errno, "SNDCTL_DSP_SETTRIGGER 0 failed\n");
return;
return -1;
}
break;
}
return 0;
}
static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
@@ -651,12 +690,19 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
oss->nfrags = obt.nfrags;
oss->fragsize = obt.fragsize;
if (obt.nfrags * obt.fragsize % hw->info.bytes_per_frame) {
if (obt.nfrags * obt.fragsize & hw->info.align) {
dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
obt.nfrags * obt.fragsize, hw->info.bytes_per_frame);
obt.nfrags * obt.fragsize, hw->info.align + 1);
}
hw->samples = (obt.nfrags * obt.fragsize) / hw->info.bytes_per_frame;
hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
oss->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!oss->pcm_buf) {
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
oss_anal_close (&fd);
return -1;
}
oss->fd = fd;
oss->dev = dev;
@@ -668,55 +714,111 @@ static void oss_fini_in (HWVoiceIn *hw)
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
oss_anal_close (&oss->fd);
g_free(oss->pcm_buf);
oss->pcm_buf = NULL;
}
static size_t oss_read(HWVoiceIn *hw, void *buf, size_t len)
static int oss_run_in (HWVoiceIn *hw)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
size_t pos = 0;
int hwshift = hw->info.shift;
int i;
int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live;
size_t read_samples = 0;
struct {
int add;
int len;
} bufs[2] = {
{ .add = hw->wpos, .len = 0 },
{ .add = 0, .len = 0 }
};
while (len) {
if (!dead) {
return 0;
}
if (hw->wpos + dead > hw->samples) {
bufs[0].len = (hw->samples - hw->wpos) << hwshift;
bufs[1].len = (dead - (hw->samples - hw->wpos)) << hwshift;
}
else {
bufs[0].len = dead << hwshift;
}
for (i = 0; i < 2; ++i) {
ssize_t nread;
void *dst = advance(buf, pos);
nread = read(oss->fd, dst, len);
if (bufs[i].len) {
void *p = advance (oss->pcm_buf, bufs[i].add << hwshift);
nread = read (oss->fd, p, bufs[i].len);
if (nread == -1) {
switch (errno) {
case EINTR:
case EAGAIN:
break;
default:
oss_logerr(errno, "Failed to read %zu bytes of audio (to %p)\n",
len, dst);
if (nread > 0) {
if (nread & hw->info.align) {
dolog ("warning: Misaligned read %zd (requested %d), "
"alignment %d\n", nread, bufs[i].add << hwshift,
hw->info.align + 1);
}
read_samples += nread >> hwshift;
hw->conv (hw->conv_buf + bufs[i].add, p, nread >> hwshift);
}
if (bufs[i].len - nread) {
if (nread == -1) {
switch (errno) {
case EINTR:
case EAGAIN:
break;
default:
oss_logerr (
errno,
"Failed to read %d bytes of audio (to %p)\n",
bufs[i].len, p
);
break;
}
}
break;
}
}
pos += nread;
len -= nread;
}
return pos;
hw->wpos = (hw->wpos + read_samples) % hw->samples;
return read_samples;
}
static void oss_enable_in(HWVoiceIn *hw, bool enable)
static int oss_read (SWVoiceIn *sw, void *buf, int size)
{
return audio_pcm_sw_read (sw, buf, size);
}
static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
OSSVoiceIn *oss = (OSSVoiceIn *) hw;
AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out;
if (enable) {
hw->poll_mode = opdo->try_poll;
switch (cmd) {
case VOICE_ENABLE:
{
bool poll_mode = opdo->try_poll;
if (hw->poll_mode) {
oss_poll_in(hw);
if (poll_mode) {
oss_poll_in (hw);
poll_mode = 0;
}
hw->poll_mode = poll_mode;
}
} else {
break;
case VOICE_DISABLE:
if (hw->poll_mode) {
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
hw->poll_mode = 0;
qemu_set_fd_handler (oss->fd, NULL, NULL, NULL);
}
break;
}
return 0;
}
static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo)
@@ -752,16 +854,15 @@ static void oss_audio_fini (void *opaque)
static struct audio_pcm_ops oss_pcm_ops = {
.init_out = oss_init_out,
.fini_out = oss_fini_out,
.run_out = oss_run_out,
.write = oss_write,
.run_buffer_out = oss_run_buffer_out,
.get_buffer_out = oss_get_buffer_out,
.put_buffer_out = oss_put_buffer_out,
.enable_out = oss_enable_out,
.ctl_out = oss_ctl_out,
.init_in = oss_init_in,
.fini_in = oss_fini_in,
.run_in = oss_run_in,
.read = oss_read,
.enable_in = oss_enable_in
.ctl_in = oss_ctl_in
};
static struct audio_driver oss_audio_driver = {
+527 -443
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -28,7 +28,7 @@
* Return number of samples processed.
*/
void NAME (void *opaque, struct st_sample *ibuf, struct st_sample *obuf,
size_t *isamp, size_t *osamp)
int *isamp, int *osamp)
{
struct rate *rate = opaque;
struct st_sample *istart, *iend;
+65 -76
View File
@@ -41,6 +41,8 @@
typedef struct SDLVoiceOut {
HWVoiceOut hw;
int live;
int decr;
} SDLVoiceOut;
static struct SDLAudioState {
@@ -77,14 +79,6 @@ static int aud_to_sdlfmt (AudioFormat fmt)
case AUDIO_FORMAT_U16:
return AUDIO_U16LSB;
case AUDIO_FORMAT_S32:
return AUDIO_S32LSB;
/* no unsigned 32-bit support in SDL */
case AUDIO_FORMAT_F32:
return AUDIO_F32LSB;
default:
dolog ("Internal logic error: Bad audio format %d\n", fmt);
#ifdef DEBUG_AUDIO
@@ -127,26 +121,6 @@ static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness)
*fmt = AUDIO_FORMAT_U16;
break;
case AUDIO_S32LSB:
*endianness = 0;
*fmt = AUDIO_FORMAT_S32;
break;
case AUDIO_S32MSB:
*endianness = 1;
*fmt = AUDIO_FORMAT_S32;
break;
case AUDIO_F32LSB:
*endianness = 0;
*fmt = AUDIO_FORMAT_F32;
break;
case AUDIO_F32MSB:
*endianness = 1;
*fmt = AUDIO_FORMAT_F32;
break;
default:
dolog ("Unrecognized SDL audio format %d\n", sdlfmt);
return -1;
@@ -210,59 +184,67 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
SDLVoiceOut *sdl = opaque;
SDLAudioState *s = &glob_sdl;
HWVoiceOut *hw = &sdl->hw;
int samples = len >> hw->info.shift;
int to_mix, decr;
if (s->exit) {
if (s->exit || !sdl->live) {
return;
}
/* dolog ("in callback samples=%zu live=%zu\n", samples, sdl->live); */
/* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */
while (hw->pending_emul && len) {
size_t write_len;
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
if (start < 0) {
start += hw->size_emul;
}
assert(start >= 0 && start < hw->size_emul);
to_mix = audio_MIN(samples, sdl->live);
decr = to_mix;
while (to_mix) {
int chunk = audio_MIN(to_mix, hw->samples - hw->rpos);
struct st_sample *src = hw->mix_buf + hw->rpos;
write_len = MIN(MIN(hw->pending_emul, len),
hw->size_emul - start);
memcpy(buf, hw->buf_emul + start, write_len);
hw->pending_emul -= write_len;
len -= write_len;
buf += write_len;
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
hw->clip(buf, src, chunk);
hw->rpos = (hw->rpos + chunk) % hw->samples;
to_mix -= chunk;
buf += chunk << hw->info.shift;
}
samples -= decr;
sdl->live -= decr;
sdl->decr += decr;
/* clear remaining buffer that we couldn't fill with data */
if (len) {
memset(buf, 0, len);
/* dolog ("done len=%d\n", len); */
/* SDL2 does not clear the remaining buffer for us, so do it on our own */
if (samples) {
memset(buf, 0, samples << hw->info.shift);
}
}
#define SDL_WRAPPER_FUNC(name, ret_type, args_decl, args, fail, unlock) \
static ret_type glue(sdl_, name)args_decl \
{ \
ret_type ret; \
\
SDL_LockAudio(); \
\
ret = glue(audio_generic_, name)args; \
\
SDL_UnlockAudio(); \
return ret; \
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
static int sdl_run_out (HWVoiceOut *hw, int live)
{
int decr;
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
SDL_LockAudio();
if (sdl->decr > live) {
ldebug ("sdl->decr %d live %d sdl->live %d\n",
sdl->decr,
live,
sdl->live);
}
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
(hw, size), *size = 0, sdl_unlock)
SDL_WRAPPER_FUNC(put_buffer_out, size_t,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
/*nothing*/, sdl_unlock_and_post)
SDL_WRAPPER_FUNC(write, size_t,
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
/*nothing*/, sdl_unlock_and_post)
decr = audio_MIN (sdl->decr, live);
sdl->decr -= decr;
#undef SDL_WRAPPER_FUNC
sdl->live = live;
SDL_UnlockAudio();
return decr;
}
static void sdl_fini_out (HWVoiceOut *hw)
{
@@ -313,9 +295,20 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as,
return 0;
}
static void sdl_enable_out(HWVoiceOut *hw, bool enable)
static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
SDL_PauseAudio(!enable);
(void) hw;
switch (cmd) {
case VOICE_ENABLE:
SDL_PauseAudio (0);
break;
case VOICE_DISABLE:
SDL_PauseAudio (1);
break;
}
return 0;
}
static void *sdl_audio_init(Audiodev *dev)
@@ -348,13 +341,9 @@ static void sdl_audio_fini (void *opaque)
static struct audio_pcm_ops sdl_pcm_ops = {
.init_out = sdl_init_out,
.fini_out = sdl_fini_out,
/* wrapper for audio_generic_write */
.write = sdl_write,
/* wrapper for audio_generic_get_buffer_out */
.get_buffer_out = sdl_get_buffer_out,
/* wrapper for audio_generic_put_buffer_out */
.put_buffer_out = sdl_put_buffer_out,
.enable_out = sdl_enable_out,
.run_out = sdl_run_out,
.write = sdl_write_out,
.ctl_out = sdl_ctl_out,
};
static struct audio_driver sdl_audio_driver = {
+180 -87
View File
@@ -18,6 +18,7 @@
*/
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "qemu/host-utils.h"
#include "qemu/module.h"
#include "qemu/error-report.h"
@@ -40,21 +41,27 @@
#define LINE_IN_SAMPLES (256 * 4)
#endif
typedef struct SpiceRateCtl {
int64_t start_ticks;
int64_t bytes_sent;
} SpiceRateCtl;
typedef struct SpiceVoiceOut {
HWVoiceOut hw;
SpicePlaybackInstance sin;
RateCtl rate;
SpiceRateCtl rate;
int active;
uint32_t *frame;
uint32_t fpos;
uint32_t *fpos;
uint32_t fsize;
} SpiceVoiceOut;
typedef struct SpiceVoiceIn {
HWVoiceIn hw;
SpiceRecordInstance sin;
RateCtl rate;
SpiceRateCtl rate;
int active;
uint32_t samples[LINE_IN_SAMPLES];
} SpiceVoiceIn;
static const SpicePlaybackInterface playback_sif = {
@@ -84,6 +91,32 @@ static void spice_audio_fini (void *opaque)
/* nothing */
}
static void rate_start (SpiceRateCtl *rate)
{
memset (rate, 0, sizeof (*rate));
rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}
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_clock_get_ns(QEMU_CLOCK_VIRTUAL);
ticks = now - rate->start_ticks;
bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
samples = (bytes - rate->bytes_sent) >> info->shift;
if (samples < 0 || samples > 65536) {
error_report("Resetting rate control (%" PRId64 " samples)", 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,
@@ -120,78 +153,98 @@ static void line_out_fini (HWVoiceOut *hw)
spice_server_remove_interface (&out->sin.base);
}
static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
static int line_out_run (HWVoiceOut *hw, int live)
{
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
int rpos, decr;
int samples;
if (!out->frame) {
spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize);
out->fpos = 0;
if (!live) {
return 0;
}
if (out->frame) {
*size = audio_rate_get_bytes(
&hw->info, &out->rate,
(out->fsize - out->fpos) * hw->info.bytes_per_frame);
} else {
audio_rate_start(&out->rate);
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;
}
return out->frame + out->fpos;
hw->rpos = rpos;
return decr;
}
static size_t line_out_put_buffer(HWVoiceOut *hw, void *buf, size_t size)
static int line_out_write (SWVoiceOut *sw, void *buf, int len)
{
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
assert(buf == out->frame + out->fpos && out->fpos <= out->fsize);
out->fpos += size >> 2;
if (out->fpos == out->fsize) { /* buffer full */
spice_server_playback_put_samples(&out->sin, out->frame);
out->frame = NULL;
}
return size;
return audio_pcm_sw_write (sw, buf, len);
}
static void line_out_enable(HWVoiceOut *hw, bool enable)
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
{
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
if (enable) {
switch (cmd) {
case VOICE_ENABLE:
if (out->active) {
return;
break;
}
out->active = 1;
audio_rate_start(&out->rate);
rate_start (&out->rate);
spice_server_playback_start (&out->sin);
} else {
break;
case VOICE_DISABLE:
if (!out->active) {
return;
break;
}
out->active = 0;
if (out->frame) {
memset(out->frame + out->fpos, 0, (out->fsize - out->fpos) << 2);
memset (out->fpos, 0, out->fsize << 2);
spice_server_playback_put_samples (&out->sin, out->frame);
out->frame = NULL;
out->frame = out->fpos = NULL;
}
spice_server_playback_stop (&out->sin);
}
}
break;
case VOICE_VOLUME:
{
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
static void line_out_volume(HWVoiceOut *hw, Volume *vol)
{
SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
uint16_t svol[2];
SWVoiceOut *sw;
va_list ap;
uint16_t vol[2];
assert(vol->channels == 2);
svol[0] = vol->vol[0] * 257;
svol[1] = vol->vol[1] * 257;
spice_server_playback_set_volume(&out->sin, 2, svol);
spice_server_playback_set_mute(&out->sin, vol->mute);
}
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceOut *);
va_end (ap);
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
spice_server_playback_set_volume (&out->sin, 2, vol);
spice_server_playback_set_mute (&out->sin, sw->vol.mute);
#endif
break;
}
}
return 0;
}
/* record */
@@ -228,74 +281,111 @@ static void line_in_fini (HWVoiceIn *hw)
spice_server_remove_interface (&in->sin.base);
}
static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
static int line_in_run (HWVoiceIn *hw)
{
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2;
size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);
int num_samples;
int ready;
int len[2];
uint64_t delta_samp;
const uint32_t *samples;
/* XXX: do we need this? */
if (ready == 0) {
memset(buf, 0, to_read << 2);
ready = to_read;
if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
return 0;
}
return ready << 2;
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 void line_in_enable(HWVoiceIn *hw, bool enable)
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);
if (enable) {
switch (cmd) {
case VOICE_ENABLE:
if (in->active) {
return;
break;
}
in->active = 1;
audio_rate_start(&in->rate);
rate_start (&in->rate);
spice_server_record_start (&in->sin);
} else {
break;
case VOICE_DISABLE:
if (!in->active) {
return;
break;
}
in->active = 0;
spice_server_record_stop (&in->sin);
}
}
break;
case VOICE_VOLUME:
{
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
static void line_in_volume(HWVoiceIn *hw, Volume *vol)
{
SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
uint16_t svol[2];
SWVoiceIn *sw;
va_list ap;
uint16_t vol[2];
assert(vol->channels == 2);
svol[0] = vol->vol[0] * 257;
svol[1] = vol->vol[1] * 257;
spice_server_record_set_volume(&in->sin, 2, svol);
spice_server_record_set_mute(&in->sin, vol->mute);
}
va_start (ap, cmd);
sw = va_arg (ap, SWVoiceIn *);
va_end (ap);
vol[0] = sw->vol.l / ((1ULL << 16) + 1);
vol[1] = sw->vol.r / ((1ULL << 16) + 1);
spice_server_record_set_volume (&in->sin, 2, vol);
spice_server_record_set_mute (&in->sin, sw->vol.mute);
#endif
break;
}
}
return 0;
}
static struct audio_pcm_ops audio_callbacks = {
.init_out = line_out_init,
.fini_out = line_out_fini,
.write = audio_generic_write,
.get_buffer_out = line_out_get_buffer,
.put_buffer_out = line_out_put_buffer,
.enable_out = line_out_enable,
#if (SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && \
(SPICE_INTERFACE_PLAYBACK_MINOR >= 2)
.volume_out = line_out_volume,
#endif
.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,
.enable_in = line_in_enable,
#if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
.volume_in = line_in_volume,
#endif
.ctl_in = line_in_ctl,
};
static struct audio_driver spice_audio_driver = {
@@ -308,6 +398,9 @@ static struct audio_driver spice_audio_driver = {
.max_voices_in = 1,
.voice_size_out = sizeof (SpiceVoiceOut),
.voice_size_in = sizeof (SpiceVoiceIn),
#if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
.ctl_caps = VOICE_VOLUME_CAP
#endif
};
void qemu_spice_audio_init (void)
+63 -20
View File
@@ -35,23 +35,58 @@
typedef struct WAVVoiceOut {
HWVoiceOut hw;
FILE *f;
RateCtl rate;
int64_t old_ticks;
void *pcm_buf;
int total_samples;
} WAVVoiceOut;
static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
static int wav_run_out (HWVoiceOut *hw, int live)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len);
assert(bytes % hw->info.bytes_per_frame == 0);
int rpos, decr, samples;
uint8_t *dst;
struct st_sample *src;
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
int64_t ticks = now - wav->old_ticks;
int64_t bytes =
muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);
if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n",
bytes, strerror(errno));
if (bytes > INT_MAX) {
samples = INT_MAX >> hw->info.shift;
}
else {
samples = bytes >> hw->info.shift;
}
wav->total_samples += bytes / hw->info.bytes_per_frame;
return bytes;
wav->old_ticks = now;
decr = audio_MIN (live, samples);
samples = decr;
rpos = hw->rpos;
while (samples) {
int left_till_end_samples = hw->samples - rpos;
int convert_samples = audio_MIN (samples, left_till_end_samples);
src = hw->mix_buf + rpos;
dst = advance (wav->pcm_buf, rpos << hw->info.shift);
hw->clip (dst, src, convert_samples);
if (fwrite (dst, convert_samples << hw->info.shift, 1, wav->f) != 1) {
dolog ("wav_run_out: fwrite of %d bytes failed\nReaons: %s\n",
convert_samples << hw->info.shift, strerror (errno));
}
rpos = (rpos + convert_samples) % hw->samples;
samples -= convert_samples;
wav->total_samples += convert_samples;
}
hw->rpos = rpos;
return decr;
}
static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
/* VICE code: Store number as little endian. */
@@ -107,6 +142,13 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
audio_pcm_init_info (&hw->info, &wav_as);
hw->samples = 1024;
wav->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!wav->pcm_buf) {
dolog ("Could not allocate buffer (%d bytes)\n",
hw->samples << hw->info.shift);
return -1;
}
le_store (hdr + 22, hw->info.nchannels, 2);
le_store (hdr + 24, hw->info.freq, 4);
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
@@ -116,6 +158,8 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n",
wav_path, strerror(errno));
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
return -1;
}
@@ -124,8 +168,6 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
strerror(errno));
return -1;
}
audio_rate_start(&wav->rate);
return 0;
}
@@ -134,7 +176,7 @@ static void wav_fini_out (HWVoiceOut *hw)
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
uint8_t rlen[4];
uint8_t dlen[4];
uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame;
uint32_t datalen = wav->total_samples << hw->info.shift;
uint32_t rifflen = datalen + 36;
if (!wav->f) {
@@ -171,15 +213,16 @@ static void wav_fini_out (HWVoiceOut *hw)
wav->f, strerror (errno));
}
wav->f = NULL;
g_free (wav->pcm_buf);
wav->pcm_buf = NULL;
}
static void wav_enable_out(HWVoiceOut *hw, bool enable)
static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
if (enable) {
audio_rate_start(&wav->rate);
}
(void) hw;
(void) cmd;
return 0;
}
static void *wav_audio_init(Audiodev *dev)
@@ -196,9 +239,9 @@ static void wav_audio_fini (void *opaque)
static struct audio_pcm_ops wav_pcm_ops = {
.init_out = wav_init_out,
.fini_out = wav_fini_out,
.run_out = wav_run_out,
.write = wav_write_out,
.run_buffer_out = audio_generic_run_buffer_out,
.enable_out = wav_enable_out,
.ctl_out = wav_ctl_out,
};
static struct audio_driver wav_audio_driver = {
+4 -3
View File
@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "monitor/monitor.h"
#include "qapi/error.h"
#include "qemu/error-report.h"
@@ -104,8 +105,8 @@ static struct capture_ops wav_capture_ops = {
.info = wav_capture_info
};
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
int freq, int bits, int nchannels)
int wav_start_capture (CaptureState *s, const char *path, int freq,
int bits, int nchannels)
{
WAVState *wav;
uint8_t hdr[] = {
@@ -170,7 +171,7 @@ int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
goto error_free;
}
cap = AUD_add_capture(state, &as, &ops, wav);
cap = AUD_add_capture (&as, &ops, wav);
if (!cap) {
error_report("Failed to add audio capture");
goto error_free;
+1 -1
View File
@@ -239,7 +239,7 @@ qauthz_list_file_init(Object *obj)
authz->file_watch = -1;
#ifdef CONFIG_INOTIFY1
authz->refresh = true;
authz->refresh = TRUE;
#endif
}
+1 -5
View File
@@ -1,4 +1,4 @@
common-obj-y += rng.o rng-egd.o rng-builtin.o
common-obj-y += rng.o rng-egd.o
common-obj-$(CONFIG_POSIX) += rng-random.o
common-obj-$(CONFIG_TPM) += tpm.o
@@ -17,7 +17,3 @@ endif
common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_VIRTIO)) += vhost-user.o
common-obj-$(CONFIG_LINUX) += hostmem-memfd.o
common-obj-$(CONFIG_GIO) += dbus-vmstate.o
dbus-vmstate.o-cflags = $(GIO_CFLAGS)
dbus-vmstate.o-libs = $(GIO_LIBS)
+1
View File
@@ -23,6 +23,7 @@
#include "qemu/osdep.h"
#include "sysemu/cryptodev.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "standard-headers/linux/virtio_crypto.h"
#include "crypto/cipher.h"
+2 -6
View File
@@ -22,6 +22,7 @@
*/
#include "qemu/osdep.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
@@ -152,7 +153,7 @@ cryptodev_vhost_claim_chardev(CryptoDevBackendVhostUser *s,
return chr;
}
static void cryptodev_vhost_user_event(void *opaque, QEMUChrEvent event)
static void cryptodev_vhost_user_event(void *opaque, int event)
{
CryptoDevBackendVhostUser *s = opaque;
CryptoDevBackend *b = CRYPTODEV_BACKEND(s);
@@ -171,11 +172,6 @@ static void cryptodev_vhost_user_event(void *opaque, QEMUChrEvent event)
b->ready = false;
cryptodev_vhost_user_stop(queues, s);
break;
case CHR_EVENT_BREAK:
case CHR_EVENT_MUX_IN:
case CHR_EVENT_MUX_OUT:
/* Ignore */
break;
}
}
+11 -1
View File
@@ -23,6 +23,7 @@
#include "qemu/osdep.h"
#include "sysemu/cryptodev.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "qemu/config-file.h"
@@ -176,10 +177,19 @@ cryptodev_backend_complete(UserCreatable *uc, Error **errp)
{
CryptoDevBackend *backend = CRYPTODEV_BACKEND(uc);
CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_GET_CLASS(uc);
Error *local_err = NULL;
if (bc->init) {
bc->init(backend, errp);
bc->init(backend, &local_err);
if (local_err) {
goto out;
}
}
return;
out:
error_propagate(errp, local_err);
}
void cryptodev_backend_set_used(CryptoDevBackend *backend, bool used)
-511
View File
@@ -1,511 +0,0 @@
/*
* QEMU dbus-vmstate
*
* Copyright (C) 2019 Red Hat Inc
*
* Authors:
* Marc-André Lureau <marcandre.lureau@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/units.h"
#include "qemu/dbus.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qom/object_interfaces.h"
#include "qapi/qmp/qerror.h"
#include "migration/vmstate.h"
#include "trace.h"
typedef struct DBusVMState DBusVMState;
typedef struct DBusVMStateClass DBusVMStateClass;
#define TYPE_DBUS_VMSTATE "dbus-vmstate"
#define DBUS_VMSTATE(obj) \
OBJECT_CHECK(DBusVMState, (obj), TYPE_DBUS_VMSTATE)
#define DBUS_VMSTATE_GET_CLASS(obj) \
OBJECT_GET_CLASS(DBusVMStateClass, (obj), TYPE_DBUS_VMSTATE)
#define DBUS_VMSTATE_CLASS(klass) \
OBJECT_CLASS_CHECK(DBusVMStateClass, (klass), TYPE_DBUS_VMSTATE)
struct DBusVMStateClass {
ObjectClass parent_class;
};
struct DBusVMState {
Object parent;
GDBusConnection *bus;
char *dbus_addr;
char *id_list;
uint32_t data_size;
uint8_t *data;
};
static const GDBusPropertyInfo vmstate_property_info[] = {
{ -1, (char *) "Id", (char *) "s",
G_DBUS_PROPERTY_INFO_FLAGS_READABLE, NULL },
};
static const GDBusPropertyInfo * const vmstate_property_info_pointers[] = {
&vmstate_property_info[0],
NULL
};
static const GDBusInterfaceInfo vmstate1_interface_info = {
-1,
(char *) "org.qemu.VMState1",
(GDBusMethodInfo **) NULL,
(GDBusSignalInfo **) NULL,
(GDBusPropertyInfo **) &vmstate_property_info_pointers,
NULL,
};
#define DBUS_VMSTATE_SIZE_LIMIT (1 * MiB)
static GHashTable *
get_id_list_set(DBusVMState *self)
{
g_auto(GStrv) ids = NULL;
g_autoptr(GHashTable) set = NULL;
int i;
if (!self->id_list) {
return NULL;
}
ids = g_strsplit(self->id_list, ",", -1);
set = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
for (i = 0; ids[i]; i++) {
g_hash_table_add(set, ids[i]);
ids[i] = NULL;
}
return g_steal_pointer(&set);
}
static GHashTable *
dbus_get_proxies(DBusVMState *self, GError **err)
{
g_autoptr(GHashTable) proxies = NULL;
g_autoptr(GHashTable) ids = NULL;
g_auto(GStrv) names = NULL;
Error *error = NULL;
size_t i;
ids = get_id_list_set(self);
proxies = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_object_unref);
names = qemu_dbus_get_queued_owners(self->bus, "org.qemu.VMState1", &error);
if (!names) {
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED, "%s",
error_get_pretty(error));
error_free(error);
return NULL;
}
for (i = 0; names[i]; i++) {
g_autoptr(GDBusProxy) proxy = NULL;
g_autoptr(GVariant) result = NULL;
g_autofree char *id = NULL;
size_t size;
proxy = g_dbus_proxy_new_sync(self->bus, G_DBUS_PROXY_FLAGS_NONE,
(GDBusInterfaceInfo *) &vmstate1_interface_info,
names[i],
"/org/qemu/VMState1",
"org.qemu.VMState1",
NULL, err);
if (!proxy) {
return NULL;
}
result = g_dbus_proxy_get_cached_property(proxy, "Id");
if (!result) {
g_set_error_literal(err, G_IO_ERROR, G_IO_ERROR_FAILED,
"VMState Id property is missing.");
return NULL;
}
id = g_variant_dup_string(result, &size);
if (ids && !g_hash_table_remove(ids, id)) {
g_clear_pointer(&id, g_free);
g_clear_object(&proxy);
continue;
}
if (size == 0 || size >= 256) {
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
"VMState Id '%s' is invalid.", id);
return NULL;
}
if (!g_hash_table_insert(proxies, id, proxy)) {
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
"Duplicated VMState Id '%s'", id);
return NULL;
}
id = NULL;
proxy = NULL;
g_clear_pointer(&result, g_variant_unref);
}
if (ids) {
g_autofree char **left = NULL;
left = (char **)g_hash_table_get_keys_as_array(ids, NULL);
if (*left) {
g_autofree char *leftids = g_strjoinv(",", left);
g_set_error(err, G_IO_ERROR, G_IO_ERROR_FAILED,
"Required VMState Id are missing: %s", leftids);
return NULL;
}
}
return g_steal_pointer(&proxies);
}
static int
dbus_load_state_proxy(GDBusProxy *proxy, const uint8_t *data, size_t size)
{
g_autoptr(GError) err = NULL;
g_autoptr(GVariant) result = NULL;
g_autoptr(GVariant) value = NULL;
value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE,
data, size, sizeof(char));
result = g_dbus_proxy_call_sync(proxy, "Load",
g_variant_new("(@ay)",
g_steal_pointer(&value)),
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, NULL, &err);
if (!result) {
error_report("%s: Failed to Load: %s", __func__, err->message);
return -1;
}
return 0;
}
static int dbus_vmstate_post_load(void *opaque, int version_id)
{
DBusVMState *self = DBUS_VMSTATE(opaque);
g_autoptr(GInputStream) m = NULL;
g_autoptr(GDataInputStream) s = NULL;
g_autoptr(GError) err = NULL;
g_autoptr(GHashTable) proxies = NULL;
uint32_t nelem;
trace_dbus_vmstate_post_load(version_id);
proxies = dbus_get_proxies(self, &err);
if (!proxies) {
error_report("%s: Failed to get proxies: %s", __func__, err->message);
return -1;
}
m = g_memory_input_stream_new_from_data(self->data, self->data_size, NULL);
s = g_data_input_stream_new(m);
g_data_input_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
nelem = g_data_input_stream_read_uint32(s, NULL, &err);
if (err) {
goto error;
}
while (nelem > 0) {
GDBusProxy *proxy = NULL;
uint32_t len;
gsize bytes_read, avail;
char id[256];
len = g_data_input_stream_read_uint32(s, NULL, &err);
if (err) {
goto error;
}
if (len >= 256) {
error_report("%s: Invalid DBus vmstate proxy name %u",
__func__, len);
return -1;
}
if (!g_input_stream_read_all(G_INPUT_STREAM(s), id, len,
&bytes_read, NULL, &err)) {
goto error;
}
g_return_val_if_fail(bytes_read == len, -1);
id[len] = 0;
trace_dbus_vmstate_loading(id);
proxy = g_hash_table_lookup(proxies, id);
if (!proxy) {
error_report("%s: Failed to find proxy Id '%s'", __func__, id);
return -1;
}
len = g_data_input_stream_read_uint32(s, NULL, &err);
avail = g_buffered_input_stream_get_available(
G_BUFFERED_INPUT_STREAM(s));
if (len > DBUS_VMSTATE_SIZE_LIMIT || len > avail) {
error_report("%s: Invalid vmstate size: %u", __func__, len);
return -1;
}
if (dbus_load_state_proxy(proxy,
g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(s),
NULL),
len) < 0) {
error_report("%s: Failed to restore Id '%s'", __func__, id);
return -1;
}
if (!g_seekable_seek(G_SEEKABLE(s), len, G_SEEK_CUR, NULL, &err)) {
goto error;
}
nelem -= 1;
}
return 0;
error:
error_report("%s: Failed to read from stream: %s", __func__, err->message);
return -1;
}
static void
dbus_save_state_proxy(gpointer key,
gpointer value,
gpointer user_data)
{
GDataOutputStream *s = user_data;
const char *id = key;
GDBusProxy *proxy = value;
g_autoptr(GVariant) result = NULL;
g_autoptr(GVariant) child = NULL;
g_autoptr(GError) err = NULL;
const uint8_t *data;
gsize size;
trace_dbus_vmstate_saving(id);
result = g_dbus_proxy_call_sync(proxy, "Save",
NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1, NULL, &err);
if (!result) {
error_report("%s: Failed to Save: %s", __func__, err->message);
return;
}
child = g_variant_get_child_value(result, 0);
data = g_variant_get_fixed_array(child, &size, sizeof(char));
if (!data) {
error_report("%s: Failed to Save: not a byte array", __func__);
return;
}
if (size > DBUS_VMSTATE_SIZE_LIMIT) {
error_report("%s: Too large vmstate data to save: %zu",
__func__, (size_t)size);
return;
}
if (!g_data_output_stream_put_uint32(s, strlen(id), NULL, &err) ||
!g_data_output_stream_put_string(s, id, NULL, &err) ||
!g_data_output_stream_put_uint32(s, size, NULL, &err) ||
!g_output_stream_write_all(G_OUTPUT_STREAM(s),
data, size, NULL, NULL, &err)) {
error_report("%s: Failed to write to stream: %s",
__func__, err->message);
}
}
static int dbus_vmstate_pre_save(void *opaque)
{
DBusVMState *self = DBUS_VMSTATE(opaque);
g_autoptr(GOutputStream) m = NULL;
g_autoptr(GDataOutputStream) s = NULL;
g_autoptr(GHashTable) proxies = NULL;
g_autoptr(GError) err = NULL;
trace_dbus_vmstate_pre_save();
proxies = dbus_get_proxies(self, &err);
if (!proxies) {
error_report("%s: Failed to get proxies: %s", __func__, err->message);
return -1;
}
m = g_memory_output_stream_new_resizable();
s = g_data_output_stream_new(m);
g_data_output_stream_set_byte_order(s, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN);
if (!g_data_output_stream_put_uint32(s, g_hash_table_size(proxies),
NULL, &err)) {
error_report("%s: Failed to write to stream: %s",
__func__, err->message);
return -1;
}
g_hash_table_foreach(proxies, dbus_save_state_proxy, s);
if (g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m))
> UINT32_MAX) {
error_report("%s: DBus vmstate buffer is too large", __func__);
return -1;
}
if (!g_output_stream_close(G_OUTPUT_STREAM(m), NULL, &err)) {
error_report("%s: Failed to close stream: %s", __func__, err->message);
return -1;
}
g_free(self->data);
self->data_size =
g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(m));
self->data =
g_memory_output_stream_steal_data(G_MEMORY_OUTPUT_STREAM(m));
return 0;
}
static const VMStateDescription dbus_vmstate = {
.name = TYPE_DBUS_VMSTATE,
.version_id = 0,
.pre_save = dbus_vmstate_pre_save,
.post_load = dbus_vmstate_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32(data_size, DBusVMState),
VMSTATE_VBUFFER_ALLOC_UINT32(data, DBusVMState, 0, 0, data_size),
VMSTATE_END_OF_LIST()
}
};
static void
dbus_vmstate_complete(UserCreatable *uc, Error **errp)
{
DBusVMState *self = DBUS_VMSTATE(uc);
g_autoptr(GError) err = NULL;
if (!object_resolve_path_type("", TYPE_DBUS_VMSTATE, NULL)) {
error_setg(errp, "There is already an instance of %s",
TYPE_DBUS_VMSTATE);
return;
}
if (!self->dbus_addr) {
error_setg(errp, QERR_MISSING_PARAMETER, "addr");
return;
}
self->bus = g_dbus_connection_new_for_address_sync(self->dbus_addr,
G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT |
G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION,
NULL, NULL, &err);
if (err) {
error_setg(errp, "failed to connect to DBus: '%s'", err->message);
return;
}
if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY,
&dbus_vmstate, self) < 0) {
error_setg(errp, "Failed to register vmstate");
}
}
static void
dbus_vmstate_finalize(Object *o)
{
DBusVMState *self = DBUS_VMSTATE(o);
vmstate_unregister(VMSTATE_IF(self), &dbus_vmstate, self);
g_clear_object(&self->bus);
g_free(self->dbus_addr);
g_free(self->id_list);
g_free(self->data);
}
static char *
get_dbus_addr(Object *o, Error **errp)
{
DBusVMState *self = DBUS_VMSTATE(o);
return g_strdup(self->dbus_addr);
}
static void
set_dbus_addr(Object *o, const char *str, Error **errp)
{
DBusVMState *self = DBUS_VMSTATE(o);
g_free(self->dbus_addr);
self->dbus_addr = g_strdup(str);
}
static char *
get_id_list(Object *o, Error **errp)
{
DBusVMState *self = DBUS_VMSTATE(o);
return g_strdup(self->id_list);
}
static void
set_id_list(Object *o, const char *str, Error **errp)
{
DBusVMState *self = DBUS_VMSTATE(o);
g_free(self->id_list);
self->id_list = g_strdup(str);
}
static char *
dbus_vmstate_get_id(VMStateIf *vmif)
{
return g_strdup(TYPE_DBUS_VMSTATE);
}
static void
dbus_vmstate_class_init(ObjectClass *oc, void *data)
{
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
VMStateIfClass *vc = VMSTATE_IF_CLASS(oc);
ucc->complete = dbus_vmstate_complete;
vc->get_id = dbus_vmstate_get_id;
object_class_property_add_str(oc, "addr",
get_dbus_addr, set_dbus_addr,
&error_abort);
object_class_property_add_str(oc, "id-list",
get_id_list, set_id_list,
&error_abort);
}
static const TypeInfo dbus_vmstate_info = {
.name = TYPE_DBUS_VMSTATE,
.parent = TYPE_OBJECT,
.instance_size = sizeof(DBusVMState),
.instance_finalize = dbus_vmstate_finalize,
.class_size = sizeof(DBusVMStateClass),
.class_init = dbus_vmstate_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_USER_CREATABLE },
{ TYPE_VMSTATE_IF },
{ }
}
};
static void
register_types(void)
{
type_register_static(&dbus_vmstate_info);
}
type_init(register_types);
+30
View File
@@ -18,6 +18,13 @@
#include "sysemu/sysemu.h"
#include "qom/object_interfaces.h"
/* hostmem-file.c */
/**
* @TYPE_MEMORY_BACKEND_FILE:
* name of backend that uses mmap on a file descriptor
*/
#define TYPE_MEMORY_BACKEND_FILE "memory-backend-file"
#define MEMORY_BACKEND_FILE(obj) \
OBJECT_CHECK(HostMemoryBackendFile, (obj), TYPE_MEMORY_BACKEND_FILE)
@@ -51,6 +58,29 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
return;
}
/*
* Verify pmem file size since starting a guest with an incorrect size
* leads to confusing failures inside the guest.
*/
if (fb->is_pmem) {
Error *local_err = NULL;
uint64_t size;
size = qemu_get_pmem_size(fb->mem_path, &local_err);
if (!size) {
error_propagate(errp, local_err);
return;
}
if (backend->size > size) {
error_setg(errp, "size property %" PRIu64 " is larger than "
"pmem file \"%s\" size %" PRIu64, backend->size,
fb->mem_path, size);
return;
}
}
backend->force_prealloc = mem_prealloc;
name = host_memory_backend_get_name(backend);
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend),
name,
+1
View File
@@ -45,6 +45,7 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
return;
}
backend->force_prealloc = mem_prealloc;
fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size,
m->hugetlb, m->hugetlbsize, m->seal ?
F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL : 0,
+2
View File
@@ -16,6 +16,8 @@
#include "qemu/module.h"
#include "qom/object_interfaces.h"
#define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram"
static void
ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
{
+17 -46
View File
@@ -12,7 +12,6 @@
#include "qemu/osdep.h"
#include "sysemu/hostmem.h"
#include "sysemu/sysemu.h"
#include "hw/boards.h"
#include "qapi/error.h"
#include "qapi/qapi-builtin-visit.h"
@@ -215,7 +214,7 @@ static bool host_memory_backend_get_prealloc(Object *obj, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
return backend->prealloc;
return backend->prealloc || backend->force_prealloc;
}
static void host_memory_backend_set_prealloc(Object *obj, bool value,
@@ -223,6 +222,15 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
{
Error *local_err = NULL;
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
MachineState *ms = MACHINE(qdev_get_machine());
if (backend->force_prealloc) {
if (value) {
error_setg(errp,
"remove -mem-prealloc to use the prealloc property");
return;
}
}
if (!host_memory_backend_mr_inited(backend)) {
backend->prealloc = value;
@@ -234,7 +242,7 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
void *ptr = memory_region_get_ram_ptr(&backend->mr);
uint64_t sz = memory_region_size(&backend->mr);
os_mem_prealloc(fd, ptr, sz, backend->prealloc_threads, &local_err);
os_mem_prealloc(fd, ptr, sz, ms->smp.cpus, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
@@ -243,44 +251,14 @@ static void host_memory_backend_set_prealloc(Object *obj, bool value,
}
}
static void host_memory_backend_get_prealloc_threads(Object *obj, Visitor *v,
const char *name, void *opaque, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
visit_type_uint32(v, name, &backend->prealloc_threads, errp);
}
static void host_memory_backend_set_prealloc_threads(Object *obj, Visitor *v,
const char *name, void *opaque, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
Error *local_err = NULL;
uint32_t value;
visit_type_uint32(v, name, &value, &local_err);
if (local_err) {
goto out;
}
if (value <= 0) {
error_setg(&local_err,
"property '%s' of %s doesn't take value '%d'",
name, object_get_typename(obj), value);
goto out;
}
backend->prealloc_threads = value;
out:
error_propagate(errp, local_err);
}
static void host_memory_backend_init(Object *obj)
{
HostMemoryBackend *backend = MEMORY_BACKEND(obj);
MachineState *machine = MACHINE(qdev_get_machine());
/* TODO: convert access to globals to compat properties */
backend->merge = machine_mem_merge(machine);
backend->dump = machine_dump_guest_core(machine);
backend->prealloc_threads = 1;
backend->prealloc = mem_prealloc;
}
static void host_memory_backend_post_init(Object *obj)
@@ -325,7 +303,7 @@ size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
#else
size_t host_memory_backend_pagesize(HostMemoryBackend *memdev)
{
return qemu_real_host_page_size;
return getpagesize();
}
#endif
@@ -334,6 +312,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
{
HostMemoryBackend *backend = MEMORY_BACKEND(uc);
HostMemoryBackendClass *bc = MEMORY_BACKEND_GET_CLASS(uc);
MachineState *ms = MACHINE(qdev_get_machine());
Error *local_err = NULL;
void *ptr;
uint64_t sz;
@@ -383,10 +362,8 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
assert(sizeof(backend->host_nodes) >=
BITS_TO_LONGS(MAX_NODES + 1) * sizeof(unsigned long));
assert(maxnode <= MAX_NODES);
if (maxnode &&
mbind(ptr, sz, backend->policy, backend->host_nodes, maxnode + 1,
flags)) {
if (mbind(ptr, sz, backend->policy,
maxnode ? backend->host_nodes : NULL, maxnode + 1, flags)) {
if (backend->policy != MPOL_DEFAULT || errno != ENOSYS) {
error_setg_errno(errp, errno,
"cannot bind memory to host NUMA nodes");
@@ -400,7 +377,7 @@ host_memory_backend_memory_complete(UserCreatable *uc, Error **errp)
*/
if (backend->prealloc) {
os_mem_prealloc(memory_region_get_fd(&backend->mr), ptr, sz,
backend->prealloc_threads, &local_err);
ms->smp.cpus, &local_err);
if (local_err) {
goto out;
}
@@ -478,12 +455,6 @@ host_memory_backend_class_init(ObjectClass *oc, void *data)
host_memory_backend_set_prealloc, &error_abort);
object_class_property_set_description(oc, "prealloc",
"Preallocate memory", &error_abort);
object_class_property_add(oc, "prealloc-threads", "int",
host_memory_backend_get_prealloc_threads,
host_memory_backend_set_prealloc_threads,
NULL, NULL, &error_abort);
object_class_property_set_description(oc, "prealloc-threads",
"Number of CPU threads to use for prealloc", &error_abort);
object_class_property_add(oc, "size", "int",
host_memory_backend_get_size,
host_memory_backend_set_size,
-77
View File
@@ -1,77 +0,0 @@
/*
* QEMU Builtin Random Number Generator Backend
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "sysemu/rng.h"
#include "qemu/main-loop.h"
#include "qemu/guest-random.h"
#define RNG_BUILTIN(obj) OBJECT_CHECK(RngBuiltin, (obj), TYPE_RNG_BUILTIN)
typedef struct RngBuiltin {
RngBackend parent;
QEMUBH *bh;
} RngBuiltin;
static void rng_builtin_receive_entropy_bh(void *opaque)
{
RngBuiltin *s = opaque;
while (!QSIMPLEQ_EMPTY(&s->parent.requests)) {
RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
qemu_guest_getrandom_nofail(req->data, req->size);
req->receive_entropy(req->opaque, req->data, req->size);
rng_backend_finalize_request(&s->parent, req);
}
}
static void rng_builtin_request_entropy(RngBackend *b, RngRequest *req)
{
RngBuiltin *s = RNG_BUILTIN(b);
qemu_bh_schedule(s->bh);
}
static void rng_builtin_init(Object *obj)
{
RngBuiltin *s = RNG_BUILTIN(obj);
s->bh = qemu_bh_new(rng_builtin_receive_entropy_bh, s);
}
static void rng_builtin_finalize(Object *obj)
{
RngBuiltin *s = RNG_BUILTIN(obj);
qemu_bh_delete(s->bh);
}
static void rng_builtin_class_init(ObjectClass *klass, void *data)
{
RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
rbc->request_entropy = rng_builtin_request_entropy;
}
static const TypeInfo rng_builtin_info = {
.name = TYPE_RNG_BUILTIN,
.parent = TYPE_RNG_BACKEND,
.instance_size = sizeof(RngBuiltin),
.instance_init = rng_builtin_init,
.instance_finalize = rng_builtin_finalize,
.class_init = rng_builtin_class_init,
};
static void register_types(void)
{
type_register_static(&rng_builtin_info);
}
type_init(register_types);
-7
View File
@@ -1,7 +0,0 @@
# See docs/devel/tracing.txt for syntax documentation.
# dbus-vmstate.c
dbus_vmstate_pre_save(void)
dbus_vmstate_post_load(int version_id) "version_id: %d"
dbus_vmstate_loading(const char *id) "id: %s"
dbus_vmstate_saving(const char *id) "id: %s"
+2 -1
View File
@@ -12,6 +12,7 @@
#include "qemu/osdep.h"
#include "hw/qdev.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/error-report.h"
@@ -46,7 +47,7 @@ vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev,
b->vdev = vdev;
b->dev.nvqs = nvqs;
b->dev.vqs = g_new0(struct vhost_virtqueue, nvqs);
b->dev.vqs = g_new(struct vhost_virtqueue, nvqs);
ret = vhost_dev_init(&b->dev, &b->vhost_user, VHOST_BACKEND_TYPE_USER, 0);
if (ret < 0) {
+1
View File
@@ -26,6 +26,7 @@
#include "qemu/osdep.h"
#include "qemu/atomic.h"
#include "exec/cpu-common.h"
#include "sysemu/kvm.h"
#include "sysemu/balloon.h"
#include "trace-root.h"
+227 -476
View File
File diff suppressed because it is too large. Load diff
+1 -12
View File
@@ -18,7 +18,6 @@ block-obj-y += block-backend.o snapshot.o qapi.o
block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o
block-obj-$(CONFIG_POSIX) += file-posix.o
block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
block-obj-$(CONFIG_LINUX_IO_URING) += io_uring.o
block-obj-y += null.o mirror.o commit.o io.o create.o
block-obj-y += throttle-groups.o
block-obj-$(CONFIG_LINUX) += nvme.o
@@ -38,18 +37,10 @@ block-obj-y += write-threshold.o
block-obj-y += backup.o
block-obj-$(CONFIG_REPLICATION) += replication.o
block-obj-y += throttle.o copy-on-read.o
block-obj-y += block-copy.o
block-obj-y += crypto.o
block-obj-y += aio_task.o
block-obj-y += backup-top.o
block-obj-y += filter-compress.o
common-obj-y += monitor/
block-obj-y += stream.o
common-obj-y += qapi-sysemu.o
common-obj-y += stream.o
nfs.o-libs := $(LIBNFS_LIBS)
iscsi.o-cflags := $(LIBISCSI_CFLAGS)
@@ -70,7 +61,5 @@ block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o
dmg-lzfse.o-libs := $(LZFSE_LIBS)
qcow.o-libs := -lz
linux-aio.o-libs := -laio
io_uring.o-cflags := $(LINUX_IO_URING_CFLAGS)
io_uring.o-libs := $(LINUX_IO_URING_LIBS)
parallels.o-cflags := $(LIBXML2_CFLAGS)
parallels.o-libs := $(LIBXML2_LIBS)
-6
View File
@@ -195,10 +195,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
assert(cookie->type < BLOCK_MAX_IOTYPE);
if (cookie->type == BLOCK_ACCT_NONE) {
return;
}
qemu_mutex_lock(&stats->lock);
if (failed) {
@@ -221,8 +217,6 @@ static void block_account_one_io(BlockAcctStats *stats, BlockAcctCookie *cookie,
}
qemu_mutex_unlock(&stats->lock);
cookie->type = BLOCK_ACCT_NONE;
}
void block_acct_done(BlockAcctStats *stats, BlockAcctCookie *cookie)
-124
View File
@@ -1,124 +0,0 @@
/*
* Aio tasks loops
*
* Copyright (c) 2019 Virtuozzo International GmbH.
*
* 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 "qemu/osdep.h"
#include "block/aio.h"
#include "block/aio_task.h"
struct AioTaskPool {
Coroutine *main_co;
int status;
int max_busy_tasks;
int busy_tasks;
bool waiting;
};
static void coroutine_fn aio_task_co(void *opaque)
{
AioTask *task = opaque;
AioTaskPool *pool = task->pool;
assert(pool->busy_tasks < pool->max_busy_tasks);
pool->busy_tasks++;
task->ret = task->func(task);
pool->busy_tasks--;
if (task->ret < 0 && pool->status == 0) {
pool->status = task->ret;
}
g_free(task);
if (pool->waiting) {
pool->waiting = false;
aio_co_wake(pool->main_co);
}
}
void coroutine_fn aio_task_pool_wait_one(AioTaskPool *pool)
{
assert(pool->busy_tasks > 0);
assert(qemu_coroutine_self() == pool->main_co);
pool->waiting = true;
qemu_coroutine_yield();
assert(!pool->waiting);
assert(pool->busy_tasks < pool->max_busy_tasks);
}
void coroutine_fn aio_task_pool_wait_slot(AioTaskPool *pool)
{
if (pool->busy_tasks < pool->max_busy_tasks) {
return;
}
aio_task_pool_wait_one(pool);
}
void coroutine_fn aio_task_pool_wait_all(AioTaskPool *pool)
{
while (pool->busy_tasks > 0) {
aio_task_pool_wait_one(pool);
}
}
void coroutine_fn aio_task_pool_start_task(AioTaskPool *pool, AioTask *task)
{
aio_task_pool_wait_slot(pool);
task->pool = pool;
qemu_coroutine_enter(qemu_coroutine_create(aio_task_co, task));
}
AioTaskPool *coroutine_fn aio_task_pool_new(int max_busy_tasks)
{
AioTaskPool *pool = g_new0(AioTaskPool, 1);
pool->main_co = qemu_coroutine_self();
pool->max_busy_tasks = max_busy_tasks;
return pool;
}
void aio_task_pool_free(AioTaskPool *pool)
{
g_free(pool);
}
int aio_task_pool_status(AioTaskPool *pool)
{
if (!pool) {
return 0; /* Sugar for lazy allocation of aio pool */
}
return pool->status;
}
bool aio_task_pool_empty(AioTaskPool *pool)
{
return pool->busy_tasks == 0;
}
-285
View File
@@ -1,285 +0,0 @@
/*
* backup-top filter driver
*
* The driver performs Copy-Before-Write (CBW) operation: it is injected above
* some node, and before each write it copies _old_ data to the target node.
*
* Copyright (c) 2018-2019 Virtuozzo International GmbH.
*
* Author:
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.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 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "sysemu/block-backend.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "block/block-copy.h"
#include "block/backup-top.h"
typedef struct BDRVBackupTopState {
BlockCopyState *bcs;
BdrvChild *target;
bool active;
int64_t cluster_size;
} BDRVBackupTopState;
static coroutine_fn int backup_top_co_preadv(
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
}
static coroutine_fn int backup_top_cbw(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, BdrvRequestFlags flags)
{
BDRVBackupTopState *s = bs->opaque;
uint64_t off, end;
if (flags & BDRV_REQ_WRITE_UNCHANGED) {
return 0;
}
off = QEMU_ALIGN_DOWN(offset, s->cluster_size);
end = QEMU_ALIGN_UP(offset + bytes, s->cluster_size);
return block_copy(s->bcs, off, end - off, NULL);
}
static int coroutine_fn backup_top_co_pdiscard(BlockDriverState *bs,
int64_t offset, int bytes)
{
int ret = backup_top_cbw(bs, offset, bytes, 0);
if (ret < 0) {
return ret;
}
return bdrv_co_pdiscard(bs->backing, offset, bytes);
}
static int coroutine_fn backup_top_co_pwrite_zeroes(BlockDriverState *bs,
int64_t offset, int bytes, BdrvRequestFlags flags)
{
int ret = backup_top_cbw(bs, offset, bytes, flags);
if (ret < 0) {
return ret;
}
return bdrv_co_pwrite_zeroes(bs->backing, offset, bytes, flags);
}
static coroutine_fn int backup_top_co_pwritev(BlockDriverState *bs,
uint64_t offset,
uint64_t bytes,
QEMUIOVector *qiov, int flags)
{
int ret = backup_top_cbw(bs, offset, bytes, flags);
if (ret < 0) {
return ret;
}
return bdrv_co_pwritev(bs->backing, offset, bytes, qiov, flags);
}
static int coroutine_fn backup_top_co_flush(BlockDriverState *bs)
{
if (!bs->backing) {
return 0;
}
return bdrv_co_flush(bs->backing->bs);
}
static void backup_top_refresh_filename(BlockDriverState *bs)
{
if (bs->backing == NULL) {
/*
* we can be here after failed bdrv_attach_child in
* bdrv_set_backing_hd
*/
return;
}
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
bs->backing->bs->filename);
}
static void backup_top_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *reopen_queue,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
BDRVBackupTopState *s = bs->opaque;
if (!s->active) {
/*
* The filter node may be in process of bdrv_append(), which firstly do
* bdrv_set_backing_hd() and then bdrv_replace_node(). This means that
* we can't unshare BLK_PERM_WRITE during bdrv_append() operation. So,
* let's require nothing during bdrv_append() and refresh permissions
* after it (see bdrv_backup_top_append()).
*/
*nperm = 0;
*nshared = BLK_PERM_ALL;
return;
}
if (role == &child_file) {
/*
* Target child
*
* Share write to target (child_file), to not interfere
* with guest writes to its disk which may be in target backing chain.
*/
*nshared = BLK_PERM_ALL;
*nperm = BLK_PERM_WRITE;
} else {
/* Source child */
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
nperm, nshared);
if (perm & BLK_PERM_WRITE) {
*nperm = *nperm | BLK_PERM_CONSISTENT_READ;
}
*nshared &= ~BLK_PERM_WRITE;
}
}
BlockDriver bdrv_backup_top_filter = {
.format_name = "backup-top",
.instance_size = sizeof(BDRVBackupTopState),
.bdrv_co_preadv = backup_top_co_preadv,
.bdrv_co_pwritev = backup_top_co_pwritev,
.bdrv_co_pwrite_zeroes = backup_top_co_pwrite_zeroes,
.bdrv_co_pdiscard = backup_top_co_pdiscard,
.bdrv_co_flush = backup_top_co_flush,
.bdrv_co_block_status = bdrv_co_block_status_from_backing,
.bdrv_refresh_filename = backup_top_refresh_filename,
.bdrv_child_perm = backup_top_child_perm,
.is_filter = true,
};
BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
BlockDriverState *target,
const char *filter_node_name,
uint64_t cluster_size,
BdrvRequestFlags write_flags,
BlockCopyState **bcs,
Error **errp)
{
Error *local_err = NULL;
BDRVBackupTopState *state;
BlockDriverState *top = bdrv_new_open_driver(&bdrv_backup_top_filter,
filter_node_name,
BDRV_O_RDWR, errp);
bool appended = false;
if (!top) {
return NULL;
}
state = top->opaque;
top->total_sectors = source->total_sectors;
top->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
(BDRV_REQ_FUA & source->supported_write_flags);
top->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
source->supported_zero_flags);
bdrv_ref(target);
state->target = bdrv_attach_child(top, target, "target", &child_file, errp);
if (!state->target) {
bdrv_unref(target);
bdrv_unref(top);
return NULL;
}
bdrv_drained_begin(source);
bdrv_ref(top);
bdrv_append(top, source, &local_err);
if (local_err) {
error_prepend(&local_err, "Cannot append backup-top filter: ");
goto fail;
}
appended = true;
/*
* bdrv_append() finished successfully, now we can require permissions
* we want.
*/
state->active = true;
bdrv_child_refresh_perms(top, top->backing, &local_err);
if (local_err) {
error_prepend(&local_err,
"Cannot set permissions for backup-top filter: ");
goto fail;
}
state->cluster_size = cluster_size;
state->bcs = block_copy_state_new(top->backing, state->target,
cluster_size, write_flags, &local_err);
if (local_err) {
error_prepend(&local_err, "Cannot create block-copy-state: ");
goto fail;
}
*bcs = state->bcs;
bdrv_drained_end(source);
return top;
fail:
if (appended) {
state->active = false;
bdrv_backup_top_drop(top);
} else {
bdrv_unref(top);
}
bdrv_drained_end(source);
error_propagate(errp, local_err);
return NULL;
}
void bdrv_backup_top_drop(BlockDriverState *bs)
{
BDRVBackupTopState *s = bs->opaque;
bdrv_drained_begin(bs);
block_copy_state_free(s->bcs);
s->active = false;
bdrv_child_refresh_perms(bs, bs->backing, &error_abort);
bdrv_replace_node(bs, backing_bs(bs), &error_abort);
bdrv_set_backing_hd(bs, NULL, &error_abort);
bdrv_drained_end(bs);
bdrv_unref(bs);
}
-41
View File
@@ -1,41 +0,0 @@
/*
* backup-top filter driver
*
* The driver performs Copy-Before-Write (CBW) operation: it is injected above
* some node, and before each write it copies _old_ data to the target node.
*
* Copyright (c) 2018-2019 Virtuozzo International GmbH.
*
* Author:
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.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 of the License, or
* (at your option) any later version.
*
* 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/>.
*/
#ifndef BACKUP_TOP_H
#define BACKUP_TOP_H
#include "block/block_int.h"
#include "block/block-copy.h"
BlockDriverState *bdrv_backup_top_append(BlockDriverState *source,
BlockDriverState *target,
const char *filter_node_name,
uint64_t cluster_size,
BdrvRequestFlags write_flags,
BlockCopyState **bcs,
Error **errp);
void bdrv_backup_top_drop(BlockDriverState *bs);
#endif /* BACKUP_TOP_H */
+374 -152
View File
@@ -2,7 +2,6 @@
* QEMU backup
*
* Copyright (C) 2013 Proxmox Server Solutions
* Copyright (c) 2019 Virtuozzo International GmbH.
*
* Authors:
* Dietmar Maurer (dietmar@proxmox.com)
@@ -19,7 +18,6 @@
#include "block/block_int.h"
#include "block/blockjob_int.h"
#include "block/block_backup.h"
#include "block/block-copy.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qemu/ratelimit.h"
@@ -28,82 +26,262 @@
#include "qemu/bitmap.h"
#include "qemu/error-report.h"
#include "block/backup-top.h"
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
typedef struct CowRequest {
int64_t start_byte;
int64_t end_byte;
QLIST_ENTRY(CowRequest) list;
CoQueue wait_queue; /* coroutines blocked on this request */
} CowRequest;
typedef struct BackupBlockJob {
BlockJob common;
BlockDriverState *backup_top;
BlockDriverState *source_bs;
BlockBackend *target;
/* bitmap for sync=incremental */
BdrvDirtyBitmap *sync_bitmap;
MirrorSyncMode sync_mode;
BitmapSyncMode bitmap_mode;
BlockdevOnError on_source_error;
BlockdevOnError on_target_error;
CoRwlock flush_rwlock;
uint64_t len;
uint64_t bytes_read;
int64_t cluster_size;
bool compress;
NotifierWithReturn before_write;
QLIST_HEAD(, CowRequest) inflight_reqs;
BlockCopyState *bcs;
HBitmap *copy_bitmap;
bool use_copy_range;
int64_t copy_range_size;
bool serialize_target_writes;
} BackupBlockJob;
static const BlockJobDriver backup_job_driver;
static void backup_progress_bytes_callback(int64_t bytes, void *opaque)
/* See if in-flight requests overlap and wait for them to complete */
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
int64_t start,
int64_t end)
{
BackupBlockJob *s = opaque;
CowRequest *req;
bool retry;
s->bytes_read += bytes;
do {
retry = false;
QLIST_FOREACH(req, &job->inflight_reqs, list) {
if (end > req->start_byte && start < req->end_byte) {
qemu_co_queue_wait(&req->wait_queue, NULL);
retry = true;
break;
}
}
} while (retry);
}
/* Keep track of an in-flight request */
static void cow_request_begin(CowRequest *req, BackupBlockJob *job,
int64_t start, int64_t end)
{
req->start_byte = start;
req->end_byte = end;
qemu_co_queue_init(&req->wait_queue);
QLIST_INSERT_HEAD(&job->inflight_reqs, req, list);
}
/* Forget about a completed request */
static void cow_request_end(CowRequest *req)
{
QLIST_REMOVE(req, list);
qemu_co_queue_restart_all(&req->wait_queue);
}
/* Copy range to target with a bounce buffer and return the bytes copied. If
* error occurred, return a negative error number */
static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
int64_t start,
int64_t end,
bool is_write_notifier,
bool *error_is_read,
void **bounce_buffer)
{
int ret;
BlockBackend *blk = job->common.blk;
int nbytes;
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
assert(QEMU_IS_ALIGNED(start, job->cluster_size));
hbitmap_reset(job->copy_bitmap, start, job->cluster_size);
nbytes = MIN(job->cluster_size, job->len - start);
if (!*bounce_buffer) {
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
}
ret = blk_co_pread(blk, start, nbytes, *bounce_buffer, read_flags);
if (ret < 0) {
trace_backup_do_cow_read_fail(job, start, ret);
if (error_is_read) {
*error_is_read = true;
}
goto fail;
}
if (buffer_is_zero(*bounce_buffer, nbytes)) {
ret = blk_co_pwrite_zeroes(job->target, start,
nbytes, write_flags | BDRV_REQ_MAY_UNMAP);
} else {
ret = blk_co_pwrite(job->target, start,
nbytes, *bounce_buffer, write_flags |
(job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0));
}
if (ret < 0) {
trace_backup_do_cow_write_fail(job, start, ret);
if (error_is_read) {
*error_is_read = false;
}
goto fail;
}
return nbytes;
fail:
hbitmap_set(job->copy_bitmap, start, job->cluster_size);
return ret;
}
/* Copy range to target and return the bytes copied. If error occurred, return a
* negative error number. */
static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
int64_t start,
int64_t end,
bool is_write_notifier)
{
int ret;
int nr_clusters;
BlockBackend *blk = job->common.blk;
int nbytes;
int read_flags = is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0;
int write_flags = job->serialize_target_writes ? BDRV_REQ_SERIALISING : 0;
assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
assert(QEMU_IS_ALIGNED(start, job->cluster_size));
nbytes = MIN(job->copy_range_size, MIN(end, job->len) - start);
nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
hbitmap_reset(job->copy_bitmap, start, job->cluster_size * nr_clusters);
ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
read_flags, write_flags);
if (ret < 0) {
trace_backup_do_cow_copy_range_fail(job, start, ret);
hbitmap_set(job->copy_bitmap, start, job->cluster_size * nr_clusters);
return ret;
}
return nbytes;
}
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
int64_t offset, uint64_t bytes,
bool *error_is_read)
bool *error_is_read,
bool is_write_notifier)
{
CowRequest cow_request;
int ret = 0;
int64_t start, end; /* bytes */
void *bounce_buffer = NULL;
qemu_co_rwlock_rdlock(&job->flush_rwlock);
start = QEMU_ALIGN_DOWN(offset, job->cluster_size);
end = QEMU_ALIGN_UP(bytes + offset, job->cluster_size);
trace_backup_do_cow_enter(job, start, offset, bytes);
ret = block_copy(job->bcs, start, end - start, error_is_read);
wait_for_overlapping_requests(job, start, end);
cow_request_begin(&cow_request, job, start, end);
while (start < end) {
int64_t dirty_end;
if (!hbitmap_get(job->copy_bitmap, start)) {
trace_backup_do_cow_skip(job, start);
start += job->cluster_size;
continue; /* already copied */
}
dirty_end = hbitmap_next_zero(job->copy_bitmap, start, (end - start));
if (dirty_end < 0) {
dirty_end = end;
}
trace_backup_do_cow_process(job, start);
if (job->use_copy_range) {
ret = backup_cow_with_offload(job, start, dirty_end,
is_write_notifier);
if (ret < 0) {
job->use_copy_range = false;
}
}
if (!job->use_copy_range) {
ret = backup_cow_with_bounce_buffer(job, start, dirty_end,
is_write_notifier,
error_is_read, &bounce_buffer);
}
if (ret < 0) {
break;
}
/* Publish progress, guest I/O counts as progress too. Note that the
* offset field is an opaque progress value, it is not a disk offset.
*/
start += ret;
job->bytes_read += ret;
job_progress_update(&job->common.job, ret);
ret = 0;
}
if (bounce_buffer) {
qemu_vfree(bounce_buffer);
}
cow_request_end(&cow_request);
trace_backup_do_cow_return(job, offset, bytes, ret);
qemu_co_rwlock_unlock(&job->flush_rwlock);
return ret;
}
static int coroutine_fn backup_before_write_notify(
NotifierWithReturn *notifier,
void *opaque)
{
BackupBlockJob *job = container_of(notifier, BackupBlockJob, before_write);
BdrvTrackedRequest *req = opaque;
assert(req->bs == blk_bs(job->common.blk));
assert(QEMU_IS_ALIGNED(req->offset, BDRV_SECTOR_SIZE));
assert(QEMU_IS_ALIGNED(req->bytes, BDRV_SECTOR_SIZE));
return backup_do_cow(job, req->offset, req->bytes, NULL, true);
}
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
{
BdrvDirtyBitmap *bm;
bool sync = (((ret == 0) || (job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS)) \
&& (job->bitmap_mode != BITMAP_SYNC_MODE_NEVER));
BlockDriverState *bs = blk_bs(job->common.blk);
if (sync) {
/*
* We succeeded, or we always intended to sync the bitmap.
* Delete this bitmap and install the child.
*/
bm = bdrv_dirty_bitmap_abdicate(job->sync_bitmap, NULL);
if (ret < 0) {
/* Merge the successor back into the parent, delete nothing. */
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
assert(bm);
} else {
/*
* We failed, or we never intended to sync the bitmap anyway.
* Merge the successor back into the parent, keeping all data.
*/
bm = bdrv_reclaim_dirty_bitmap(job->sync_bitmap, NULL);
}
assert(bm);
if (ret < 0 && job->bitmap_mode == BITMAP_SYNC_MODE_ALWAYS) {
/* If we failed and synced, merge in the bits we didn't copy: */
bdrv_dirty_bitmap_merge_internal(bm, block_copy_dirty_bitmap(job->bcs),
NULL, true);
/* Everything is fine, delete this bitmap and install the backup. */
bm = bdrv_dirty_bitmap_abdicate(bs, job->sync_bitmap, NULL);
assert(bm);
}
}
@@ -126,7 +304,14 @@ static void backup_abort(Job *job)
static void backup_clean(Job *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
bdrv_backup_top_drop(s->backup_top);
assert(s->target);
blk_unref(s->target);
s->target = NULL;
if (s->copy_bitmap) {
hbitmap_free(s->copy_bitmap);
s->copy_bitmap = NULL;
}
}
void backup_do_checkpoint(BlockJob *job, Error **errp)
@@ -141,8 +326,22 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
return;
}
bdrv_set_dirty_bitmap(block_copy_dirty_bitmap(backup_job->bcs), 0,
backup_job->len);
hbitmap_set(backup_job->copy_bitmap, 0, backup_job->len);
}
static void backup_drain(BlockJob *job)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common);
/* Need to keep a reference in case blk_drain triggers execution
* of backup_complete...
*/
if (s->target) {
BlockBackend *target = s->target;
blk_ref(target);
blk_drain(target);
blk_unref(target);
}
}
static BlockErrorAction backup_error_action(BackupBlockJob *job,
@@ -165,10 +364,8 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
return true;
}
/*
* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
* return. Without a yield, the VM would not reboot.
*/
/* We need to yield even for delay_ns = 0 so that bdrv_drain_all() can
* return. Without a yield, the VM would not reboot. */
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
job->bytes_read = 0;
job_sleep_ns(&job->common.job, delay_ns);
@@ -180,102 +377,117 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
return false;
}
static bool bdrv_is_unallocated_range(BlockDriverState *bs,
int64_t offset, int64_t bytes)
{
int64_t end = offset + bytes;
while (offset < end && !bdrv_is_allocated(bs, offset, bytes, &bytes)) {
if (bytes == 0) {
return true;
}
offset += bytes;
bytes = end - offset;
}
return offset >= end;
}
static int coroutine_fn backup_loop(BackupBlockJob *job)
{
int ret;
bool error_is_read;
int64_t offset;
BdrvDirtyBitmapIter *bdbi;
int ret = 0;
HBitmapIter hbi;
BlockDriverState *bs = blk_bs(job->common.blk);
hbitmap_iter_init(&hbi, job->copy_bitmap, 0);
while ((offset = hbitmap_iter_next(&hbi)) != -1) {
if (job->sync_mode == MIRROR_SYNC_MODE_TOP &&
bdrv_is_unallocated_range(bs, offset, job->cluster_size))
{
hbitmap_reset(job->copy_bitmap, offset, job->cluster_size);
continue;
}
bdbi = bdrv_dirty_iter_new(block_copy_dirty_bitmap(job->bcs));
while ((offset = bdrv_dirty_iter_next(bdbi)) != -1) {
do {
if (yield_and_check(job)) {
goto out;
return 0;
}
ret = backup_do_cow(job, offset, job->cluster_size, &error_is_read);
ret = backup_do_cow(job, offset,
job->cluster_size, &error_is_read, false);
if (ret < 0 && backup_error_action(job, error_is_read, -ret) ==
BLOCK_ERROR_ACTION_REPORT)
{
goto out;
return ret;
}
} while (ret < 0);
}
out:
bdrv_dirty_iter_free(bdbi);
return ret;
return 0;
}
static void backup_init_bcs_bitmap(BackupBlockJob *job)
/* init copy_bitmap from sync_bitmap */
static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
{
bool ret;
uint64_t estimate;
BdrvDirtyBitmap *bcs_bitmap = block_copy_dirty_bitmap(job->bcs);
uint64_t offset = 0;
uint64_t bytes = job->len;
if (job->sync_mode == MIRROR_SYNC_MODE_BITMAP) {
ret = bdrv_dirty_bitmap_merge_internal(bcs_bitmap, job->sync_bitmap,
NULL, true);
assert(ret);
} else {
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
/*
* We can't hog the coroutine to initialize this thoroughly.
* Set a flag and resume work when we are able to yield safely.
*/
block_copy_set_skip_unallocated(job->bcs, true);
while (bdrv_dirty_bitmap_next_dirty_area(job->sync_bitmap,
&offset, &bytes))
{
hbitmap_set(job->copy_bitmap, offset, bytes);
offset += bytes;
if (offset >= job->len) {
break;
}
bdrv_set_dirty_bitmap(bcs_bitmap, 0, job->len);
bytes = job->len - offset;
}
estimate = bdrv_get_dirty_count(bcs_bitmap);
job_progress_set_remaining(&job->common.job, estimate);
/* TODO job_progress_set_remaining() would make more sense */
job_progress_update(&job->common.job,
job->len - hbitmap_count(job->copy_bitmap));
}
static int coroutine_fn backup_run(Job *job, Error **errp)
{
BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
BlockDriverState *bs = blk_bs(s->common.blk);
int ret = 0;
backup_init_bcs_bitmap(s);
QLIST_INIT(&s->inflight_reqs);
qemu_co_rwlock_init(&s->flush_rwlock);
if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
int64_t offset = 0;
int64_t count;
job_progress_set_remaining(job, s->len);
for (offset = 0; offset < s->len; ) {
if (yield_and_check(s)) {
ret = -ECANCELED;
goto out;
}
ret = block_copy_reset_unallocated(s->bcs, offset, &count);
if (ret < 0) {
goto out;
}
offset += count;
}
block_copy_set_skip_unallocated(s->bcs, false);
if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
backup_incremental_init_copy_bitmap(s);
} else {
hbitmap_set(s->copy_bitmap, 0, s->len);
}
s->before_write.notify = backup_before_write_notify;
bdrv_add_before_write_notifier(bs, &s->before_write);
if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
/*
* All bits are set in bcs bitmap to allow any cluster to be copied.
* This does not actually require them to be copied.
*/
/* All bits are set in copy_bitmap to allow any cluster to be copied.
* This does not actually require them to be copied. */
while (!job_is_cancelled(job)) {
/*
* Yield until the job is cancelled. We just let our before_write
* notify callback service CoW requests.
*/
/* Yield until the job is cancelled. We just let our before_write
* notify callback service CoW requests. */
job_yield(job);
}
} else {
ret = backup_loop(s);
}
out:
notifier_with_return_remove(&s->before_write);
/* wait until pending backup_do_cow() calls have completed */
qemu_co_rwlock_wrlock(&s->flush_rwlock);
qemu_co_rwlock_unlock(&s->flush_rwlock);
return ret;
}
@@ -285,11 +497,13 @@ static const BlockJobDriver backup_job_driver = {
.job_type = JOB_TYPE_BACKUP,
.free = block_job_free,
.user_resume = block_job_user_resume,
.drain = block_job_drain,
.run = backup_run,
.commit = backup_commit,
.abort = backup_abort,
.clean = backup_clean,
}
},
.drain = backup_drain,
};
static int64_t backup_calculate_cluster_size(BlockDriverState *target,
@@ -331,9 +545,7 @@ static int64_t backup_calculate_cluster_size(BlockDriverState *target,
BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
BlockDriverState *target, int64_t speed,
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
BitmapSyncMode bitmap_mode,
bool compress,
const char *filter_node_name,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error,
int creation_flags,
@@ -342,18 +554,13 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
{
int64_t len;
BackupBlockJob *job = NULL;
int ret;
int64_t cluster_size;
BdrvRequestFlags write_flags;
BlockDriverState *backup_top = NULL;
BlockCopyState *bcs = NULL;
HBitmap *copy_bitmap = NULL;
assert(bs);
assert(target);
/* QMP interface protects us from these cases */
assert(sync_mode != MIRROR_SYNC_MODE_INCREMENTAL);
assert(sync_bitmap || sync_mode != MIRROR_SYNC_MODE_BITMAP);
if (bs == target) {
error_setg(errp, "Source and target cannot be the same");
return NULL;
@@ -371,7 +578,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
return NULL;
}
if (compress && !block_driver_can_compress(target->drv)) {
if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
error_setg(errp, "Compression is not supported for this drive %s",
bdrv_get_device_name(target));
return NULL;
@@ -385,17 +592,23 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
return NULL;
}
if (sync_bitmap) {
/* If we need to write to this bitmap, check that we can: */
if (bitmap_mode != BITMAP_SYNC_MODE_NEVER &&
bdrv_dirty_bitmap_check(sync_bitmap, BDRV_BITMAP_DEFAULT, errp)) {
if (sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
if (!sync_bitmap) {
error_setg(errp, "must provide a valid bitmap name for "
"\"incremental\" sync mode");
return NULL;
}
/* Create a new bitmap, and freeze/disable this one. */
if (bdrv_dirty_bitmap_create_successor(sync_bitmap, errp) < 0) {
if (bdrv_dirty_bitmap_create_successor(bs, sync_bitmap, errp) < 0) {
return NULL;
}
} else if (sync_bitmap) {
error_setg(errp,
"a sync_bitmap was provided to backup_run, "
"but received an incompatible sync_mode (%s)",
MirrorSyncMode_str(sync_mode));
return NULL;
}
len = bdrv_getlength(bs);
@@ -410,63 +623,72 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
goto error;
}
/*
* If source is in backing chain of target assume that target is going to be
* used for "image fleecing", i.e. it should represent a kind of snapshot of
* source at backup-start point in time. And target is going to be read by
* somebody (for example, used as NBD export) during backup job.
*
* In this case, we need to add BDRV_REQ_SERIALISING write flag to avoid
* intersection of backup writes and third party reads from target,
* otherwise reading from target we may occasionally read already updated by
* guest data.
*
* For more information see commit f8d59dfb40bb and test
* tests/qemu-iotests/222
*/
write_flags = (bdrv_chain_contains(target, bs) ? BDRV_REQ_SERIALISING : 0) |
(compress ? BDRV_REQ_WRITE_COMPRESSED : 0),
backup_top = bdrv_backup_top_append(bs, target, filter_node_name,
cluster_size, write_flags, &bcs, errp);
if (!backup_top) {
goto error;
}
copy_bitmap = hbitmap_alloc(len, ctz32(cluster_size));
/* job->len is fixed, so we can't allow resize */
job = block_job_create(job_id, &backup_job_driver, txn, backup_top,
0, BLK_PERM_ALL,
job = block_job_create(job_id, &backup_job_driver, txn, bs,
BLK_PERM_CONSISTENT_READ,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
speed, creation_flags, cb, opaque, errp);
if (!job) {
goto error;
}
job->backup_top = backup_top;
job->source_bs = bs;
/* The target must match the source in size, so no resize here either */
job->target = blk_new(job->common.job.aio_context,
BLK_PERM_WRITE,
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD);
ret = blk_insert_bs(job->target, target, errp);
if (ret < 0) {
goto error;
}
job->on_source_error = on_source_error;
job->on_target_error = on_target_error;
job->sync_mode = sync_mode;
job->sync_bitmap = sync_bitmap;
job->bitmap_mode = bitmap_mode;
job->bcs = bcs;
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
sync_bitmap : NULL;
job->compress = compress;
/* Detect image-fleecing (and similar) schemes */
job->serialize_target_writes = bdrv_chain_contains(target, bs);
job->cluster_size = cluster_size;
job->len = len;
job->copy_bitmap = copy_bitmap;
copy_bitmap = NULL;
job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
blk_get_max_transfer(job->target));
job->copy_range_size = QEMU_ALIGN_DOWN(job->copy_range_size,
job->cluster_size);
/*
* Set use_copy_range, consider the following:
* 1. Compression is not supported for copy_range.
* 2. copy_range does not respect max_transfer (it's a TODO), so we factor
* that in here. If max_transfer is smaller than the job->cluster_size,
* we do not use copy_range (in that case it's zero after aligning down
* above).
*/
job->use_copy_range = !compress && job->copy_range_size > 0;
block_copy_set_progress_callback(bcs, backup_progress_bytes_callback, job);
block_copy_set_progress_meter(bcs, &job->common.job.progress);
/* Required permissions are already taken by backup-top target */
/* Required permissions are already taken with target's blk_new() */
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
&error_abort);
job->len = len;
return &job->common;
error:
if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(sync_bitmap, NULL);
if (copy_bitmap) {
assert(!job || !job->copy_bitmap);
hbitmap_free(copy_bitmap);
}
if (backup_top) {
bdrv_backup_top_drop(backup_top);
if (sync_bitmap) {
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
}
if (job) {
backup_clean(&job->common.job);
job_early_fail(&job->common.job);
}
return NULL;
+1 -92
View File
@@ -28,14 +28,10 @@
#include "qemu/cutils.h"
#include "qemu/config-file.h"
#include "block/block_int.h"
#include "block/qdict.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qlist.h"
#include "qapi/qmp/qstring.h"
#include "qapi/qobject-input-visitor.h"
#include "sysemu/qtest.h"
typedef struct BDRVBlkdebugState {
@@ -48,9 +44,6 @@ typedef struct BDRVBlkdebugState {
uint64_t opt_discard;
uint64_t max_discard;
uint64_t take_child_perms;
uint64_t unshare_child_perms;
/* For blkdebug_refresh_filename() */
char *config_file;
@@ -351,69 +344,6 @@ static void blkdebug_parse_filename(const char *filename, QDict *options,
qdict_put_str(options, "x-image", filename);
}
static int blkdebug_parse_perm_list(uint64_t *dest, QDict *options,
const char *prefix, Error **errp)
{
int ret = 0;
QDict *subqdict = NULL;
QObject *crumpled_subqdict = NULL;
Visitor *v = NULL;
BlockPermissionList *perm_list = NULL, *element;
Error *local_err = NULL;
*dest = 0;
qdict_extract_subqdict(options, &subqdict, prefix);
if (!qdict_size(subqdict)) {
goto out;
}
crumpled_subqdict = qdict_crumple(subqdict, errp);
if (!crumpled_subqdict) {
ret = -EINVAL;
goto out;
}
v = qobject_input_visitor_new(crumpled_subqdict);
visit_type_BlockPermissionList(v, NULL, &perm_list, &local_err);
if (local_err) {
error_propagate(errp, local_err);
ret = -EINVAL;
goto out;
}
for (element = perm_list; element; element = element->next) {
*dest |= bdrv_qapi_perm_to_blk_perm(element->value);
}
out:
qapi_free_BlockPermissionList(perm_list);
visit_free(v);
qobject_unref(subqdict);
qobject_unref(crumpled_subqdict);
return ret;
}
static int blkdebug_parse_perms(BDRVBlkdebugState *s, QDict *options,
Error **errp)
{
int ret;
ret = blkdebug_parse_perm_list(&s->take_child_perms, options,
"take-child-perms.", errp);
if (ret < 0) {
return ret;
}
ret = blkdebug_parse_perm_list(&s->unshare_child_perms, options,
"unshare-child-perms.", errp);
if (ret < 0) {
return ret;
}
return 0;
}
static QemuOptsList runtime_opts = {
.name = "blkdebug",
.head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
@@ -489,12 +419,6 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
/* Set initial state */
s->state = 1;
/* Parse permissions modifiers before opening the image file */
ret = blkdebug_parse_perms(s, options, errp);
if (ret < 0) {
goto out;
}
/* Open the image file */
bs->file = bdrv_open_child(qemu_opt_get(opts, "x-image"), options, "image",
bs, &child_file, false, &local_err);
@@ -992,21 +916,6 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state,
return 0;
}
static void blkdebug_child_perm(BlockDriverState *bs, BdrvChild *c,
const BdrvChildRole *role,
BlockReopenQueue *reopen_queue,
uint64_t perm, uint64_t shared,
uint64_t *nperm, uint64_t *nshared)
{
BDRVBlkdebugState *s = bs->opaque;
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
nperm, nshared);
*nperm |= s->take_child_perms;
*nshared &= ~s->unshare_child_perms;
}
static const char *const blkdebug_strong_runtime_opts[] = {
"config",
"inject-error.",
@@ -1031,7 +940,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_file_open = blkdebug_open,
.bdrv_close = blkdebug_close,
.bdrv_reopen_prepare = blkdebug_reopen_prepare,
.bdrv_child_perm = blkdebug_child_perm,
.bdrv_child_perm = bdrv_filter_default_perms,
.bdrv_getlength = blkdebug_getlength,
.bdrv_refresh_filename = blkdebug_refresh_filename,
-8
View File
@@ -126,12 +126,6 @@ static int coroutine_fn blkreplay_co_flush(BlockDriverState *bs)
return ret;
}
static int blkreplay_snapshot_goto(BlockDriverState *bs,
const char *snapshot_id)
{
return bdrv_snapshot_goto(bs->file->bs, snapshot_id, NULL);
}
static BlockDriver bdrv_blkreplay = {
.format_name = "blkreplay",
.instance_size = 0,
@@ -146,8 +140,6 @@ static BlockDriver bdrv_blkreplay = {
.bdrv_co_pwrite_zeroes = blkreplay_co_pwrite_zeroes,
.bdrv_co_pdiscard = blkreplay_co_pdiscard,
.bdrv_co_flush = blkreplay_co_flush,
.bdrv_snapshot_goto = blkreplay_snapshot_goto,
};
static void bdrv_blkreplay_init(void)
+10 -10
View File
@@ -268,18 +268,18 @@ static int blkverify_co_flush(BlockDriverState *bs)
return bdrv_co_flush(s->test_file->bs);
}
static bool blkverify_recurse_can_replace(BlockDriverState *bs,
BlockDriverState *to_replace)
static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs,
BlockDriverState *candidate)
{
BDRVBlkverifyState *s = bs->opaque;
/*
* blkverify quits the whole qemu process if there is a mismatch
* between bs->file->bs and s->test_file->bs. Therefore, we know
* know that both must match bs and we can recurse down to either.
*/
return bdrv_recurse_can_replace(bs->file->bs, to_replace) ||
bdrv_recurse_can_replace(s->test_file->bs, to_replace);
bool perm = bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
if (perm) {
return true;
}
return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate);
}
static void blkverify_refresh_filename(BlockDriverState *bs)
@@ -327,7 +327,7 @@ static BlockDriver bdrv_blkverify = {
.bdrv_co_flush = blkverify_co_flush,
.is_filter = true,
.bdrv_recurse_can_replace = blkverify_recurse_can_replace,
.bdrv_recurse_is_first_non_filter = blkverify_recurse_is_first_non_filter,
};
static void bdrv_blkverify_init(void)
+91 -186
View File
@@ -15,15 +15,11 @@
#include "block/block_int.h"
#include "block/blockjob.h"
#include "block/throttle-groups.h"
#include "hw/qdev-core.h"
#include "sysemu/blockdev.h"
#include "sysemu/runstate.h"
#include "sysemu/sysemu.h"
#include "sysemu/replay.h"
#include "qapi/error.h"
#include "qapi/qapi-events-block.h"
#include "qemu/id.h"
#include "qemu/main-loop.h"
#include "qemu/option.h"
#include "trace.h"
#include "migration/misc.h"
@@ -83,9 +79,6 @@ struct BlockBackend {
QLIST_HEAD(, BlockBackendAioNotifier) aio_notifiers;
int quiesce_counter;
CoQueue queued_requests;
bool disable_request_queuing;
VMChangeStateEntry *vmsh;
bool force_allow_inactivate;
@@ -346,7 +339,6 @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm)
block_acct_init(&blk->stats);
qemu_co_queue_init(&blk->queued_requests);
notifier_list_init(&blk->remove_bs_notifiers);
notifier_list_init(&blk->insert_bs_notifiers);
QLIST_INIT(&blk->aio_notifiers);
@@ -1104,11 +1096,6 @@ void blk_set_allow_aio_context_change(BlockBackend *blk, bool allow)
blk->allow_aio_context_change = allow;
}
void blk_set_disable_request_queuing(BlockBackend *blk, bool disable)
{
blk->disable_request_queuing = disable;
}
static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
size_t size)
{
@@ -1140,30 +1127,13 @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
return 0;
}
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
static void coroutine_fn blk_wait_while_drained(BlockBackend *blk)
{
assert(blk->in_flight > 0);
if (blk->quiesce_counter && !blk->disable_request_queuing) {
blk_dec_in_flight(blk);
qemu_co_queue_wait(&blk->queued_requests, NULL);
blk_inc_in_flight(blk);
}
}
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
static int coroutine_fn
blk_do_preadv(BlockBackend *blk, int64_t offset, unsigned int bytes,
QEMUIOVector *qiov, BdrvRequestFlags flags)
int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
{
int ret;
BlockDriverState *bs;
BlockDriverState *bs = blk_bs(blk);
blk_wait_while_drained(blk);
/* Call blk_bs() only after waiting, the graph may have changed */
bs = blk_bs(blk);
trace_blk_co_preadv(blk, bs, offset, bytes, flags);
ret = blk_check_byte_request(blk, offset, bytes);
@@ -1184,32 +1154,13 @@ blk_do_preadv(BlockBackend *blk, int64_t offset, unsigned int bytes,
return ret;
}
int coroutine_fn blk_co_preadv(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
{
int ret;
BlockDriverState *bs = blk_bs(blk);
blk_inc_in_flight(blk);
ret = blk_do_preadv(blk, offset, bytes, qiov, flags);
blk_dec_in_flight(blk);
return ret;
}
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
static int coroutine_fn
blk_do_pwritev_part(BlockBackend *blk, int64_t offset, unsigned int bytes,
QEMUIOVector *qiov, size_t qiov_offset,
BdrvRequestFlags flags)
{
int ret;
BlockDriverState *bs;
blk_wait_while_drained(blk);
/* Call blk_bs() only after waiting, the graph may have changed */
bs = blk_bs(blk);
trace_blk_co_pwritev(blk, bs, offset, bytes, flags);
ret = blk_check_byte_request(blk, offset, bytes);
@@ -1228,33 +1179,11 @@ blk_do_pwritev_part(BlockBackend *blk, int64_t offset, unsigned int bytes,
flags |= BDRV_REQ_FUA;
}
ret = bdrv_co_pwritev_part(blk->root, offset, bytes, qiov, qiov_offset,
flags);
ret = bdrv_co_pwritev(blk->root, offset, bytes, qiov, flags);
bdrv_dec_in_flight(bs);
return ret;
}
int coroutine_fn blk_co_pwritev_part(BlockBackend *blk, int64_t offset,
unsigned int bytes,
QEMUIOVector *qiov, size_t qiov_offset,
BdrvRequestFlags flags)
{
int ret;
blk_inc_in_flight(blk);
ret = blk_do_pwritev_part(blk, offset, bytes, qiov, qiov_offset, flags);
blk_dec_in_flight(blk);
return ret;
}
int coroutine_fn blk_co_pwritev(BlockBackend *blk, int64_t offset,
unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags)
{
return blk_co_pwritev_part(blk, offset, bytes, qiov, 0, flags);
}
typedef struct BlkRwCo {
BlockBackend *blk;
int64_t offset;
@@ -1268,7 +1197,7 @@ static void blk_read_entry(void *opaque)
BlkRwCo *rwco = opaque;
QEMUIOVector *qiov = rwco->iobuf;
rwco->ret = blk_do_preadv(rwco->blk, rwco->offset, qiov->size,
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size,
qiov, rwco->flags);
aio_wait_kick();
}
@@ -1278,8 +1207,8 @@ static void blk_write_entry(void *opaque)
BlkRwCo *rwco = opaque;
QEMUIOVector *qiov = rwco->iobuf;
rwco->ret = blk_do_pwritev_part(rwco->blk, rwco->offset, qiov->size,
qiov, 0, rwco->flags);
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size,
qiov, rwco->flags);
aio_wait_kick();
}
@@ -1296,7 +1225,6 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
.ret = NOT_DONE,
};
blk_inc_in_flight(blk);
if (qemu_in_coroutine()) {
/* Fast-path if already in coroutine context */
co_entry(&rwco);
@@ -1305,11 +1233,26 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
bdrv_coroutine_enter(blk_bs(blk), co);
BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE);
}
blk_dec_in_flight(blk);
return rwco.ret;
}
int blk_pread_unthrottled(BlockBackend *blk, int64_t offset, uint8_t *buf,
int count)
{
int ret;
ret = blk_check_byte_request(blk, offset, count);
if (ret < 0) {
return ret;
}
blk_root_drained_begin(blk->root);
ret = blk_pread(blk, offset, buf, count);
blk_root_drained_end(blk->root, NULL);
return ret;
}
int blk_pwrite_zeroes(BlockBackend *blk, int64_t offset,
int bytes, BdrvRequestFlags flags)
{
@@ -1353,8 +1296,7 @@ BlockAIOCB *blk_abort_aio_request(BlockBackend *blk,
acb->blk = blk;
acb->ret = ret;
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
error_callback_bh, acb);
aio_bh_schedule_oneshot(blk_get_aio_context(blk), error_callback_bh, acb);
return &acb->common;
}
@@ -1410,8 +1352,8 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
acb->has_returned = true;
if (acb->rwco.ret != NOT_DONE) {
replay_bh_schedule_oneshot_event(blk_get_aio_context(blk),
blk_aio_complete_bh, acb);
aio_bh_schedule_oneshot(blk_get_aio_context(blk),
blk_aio_complete_bh, acb);
}
return &acb->common;
@@ -1424,7 +1366,7 @@ static void blk_aio_read_entry(void *opaque)
QEMUIOVector *qiov = rwco->iobuf;
assert(qiov->size == acb->bytes);
rwco->ret = blk_do_preadv(rwco->blk, rwco->offset, acb->bytes,
rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, acb->bytes,
qiov, rwco->flags);
blk_aio_complete(acb);
}
@@ -1436,8 +1378,8 @@ static void blk_aio_write_entry(void *opaque)
QEMUIOVector *qiov = rwco->iobuf;
assert(!qiov || qiov->size == acb->bytes);
rwco->ret = blk_do_pwritev_part(rwco->blk, rwco->offset, acb->bytes,
qiov, 0, rwco->flags);
rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, acb->bytes,
qiov, rwco->flags);
blk_aio_complete(acb);
}
@@ -1512,6 +1454,38 @@ BlockAIOCB *blk_aio_pwritev(BlockBackend *blk, int64_t offset,
blk_aio_write_entry, flags, cb, opaque);
}
static void blk_aio_flush_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_co_flush(rwco->blk);
blk_aio_complete(acb);
}
BlockAIOCB *blk_aio_flush(BlockBackend *blk,
BlockCompletionFunc *cb, void *opaque)
{
return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque);
}
static void blk_aio_pdiscard_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, acb->bytes);
blk_aio_complete(acb);
}
BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk,
int64_t offset, int bytes,
BlockCompletionFunc *cb, void *opaque)
{
return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0,
cb, opaque);
}
void blk_aio_cancel(BlockAIOCB *acb)
{
bdrv_aio_cancel(acb);
@@ -1522,12 +1496,8 @@ void blk_aio_cancel_async(BlockAIOCB *acb)
bdrv_aio_cancel_async(acb);
}
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
static int coroutine_fn
blk_do_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
int blk_co_ioctl(BlockBackend *blk, unsigned long int req, void *buf)
{
blk_wait_while_drained(blk);
if (!blk_is_available(blk)) {
return -ENOMEDIUM;
}
@@ -1540,7 +1510,8 @@ static void blk_ioctl_entry(void *opaque)
BlkRwCo *rwco = opaque;
QEMUIOVector *qiov = rwco->iobuf;
rwco->ret = blk_do_ioctl(rwco->blk, rwco->offset, qiov->iov[0].iov_base);
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset,
qiov->iov[0].iov_base);
aio_wait_kick();
}
@@ -1554,7 +1525,7 @@ static void blk_aio_ioctl_entry(void *opaque)
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_do_ioctl(rwco->blk, rwco->offset, rwco->iobuf);
rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, rwco->iobuf);
blk_aio_complete(acb);
}
@@ -1565,15 +1536,9 @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
return blk_aio_prwv(blk, req, 0, buf, blk_aio_ioctl_entry, 0, cb, opaque);
}
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
static int coroutine_fn
blk_do_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
{
int ret;
blk_wait_while_drained(blk);
ret = blk_check_byte_request(blk, offset, bytes);
int ret = blk_check_byte_request(blk, offset, bytes);
if (ret < 0) {
return ret;
}
@@ -1581,53 +1546,8 @@ blk_do_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
return bdrv_co_pdiscard(blk->root, offset, bytes);
}
static void blk_aio_pdiscard_entry(void *opaque)
int blk_co_flush(BlockBackend *blk)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_do_pdiscard(rwco->blk, rwco->offset, acb->bytes);
blk_aio_complete(acb);
}
BlockAIOCB *blk_aio_pdiscard(BlockBackend *blk,
int64_t offset, int bytes,
BlockCompletionFunc *cb, void *opaque)
{
return blk_aio_prwv(blk, offset, bytes, NULL, blk_aio_pdiscard_entry, 0,
cb, opaque);
}
int coroutine_fn blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
{
int ret;
blk_inc_in_flight(blk);
ret = blk_do_pdiscard(blk, offset, bytes);
blk_dec_in_flight(blk);
return ret;
}
static void blk_pdiscard_entry(void *opaque)
{
BlkRwCo *rwco = opaque;
QEMUIOVector *qiov = rwco->iobuf;
rwco->ret = blk_do_pdiscard(rwco->blk, rwco->offset, qiov->size);
aio_wait_kick();
}
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
{
return blk_prw(blk, offset, NULL, bytes, blk_pdiscard_entry, 0);
}
/* To be called between exactly one pair of blk_inc/dec_in_flight() */
static int coroutine_fn blk_do_flush(BlockBackend *blk)
{
blk_wait_while_drained(blk);
if (!blk_is_available(blk)) {
return -ENOMEDIUM;
}
@@ -1635,36 +1555,10 @@ static int coroutine_fn blk_do_flush(BlockBackend *blk)
return bdrv_co_flush(blk_bs(blk));
}
static void blk_aio_flush_entry(void *opaque)
{
BlkAioEmAIOCB *acb = opaque;
BlkRwCo *rwco = &acb->rwco;
rwco->ret = blk_do_flush(rwco->blk);
blk_aio_complete(acb);
}
BlockAIOCB *blk_aio_flush(BlockBackend *blk,
BlockCompletionFunc *cb, void *opaque)
{
return blk_aio_prwv(blk, 0, 0, NULL, blk_aio_flush_entry, 0, cb, opaque);
}
int coroutine_fn blk_co_flush(BlockBackend *blk)
{
int ret;
blk_inc_in_flight(blk);
ret = blk_do_flush(blk);
blk_dec_in_flight(blk);
return ret;
}
static void blk_flush_entry(void *opaque)
{
BlkRwCo *rwco = opaque;
rwco->ret = blk_do_flush(rwco->blk);
rwco->ret = blk_co_flush(rwco->blk);
aio_wait_kick();
}
@@ -2136,15 +2030,29 @@ int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
BDRV_REQ_WRITE_COMPRESSED);
}
int blk_truncate(BlockBackend *blk, int64_t offset, bool exact,
PreallocMode prealloc, Error **errp)
int blk_truncate(BlockBackend *blk, int64_t offset, PreallocMode prealloc,
Error **errp)
{
if (!blk_is_available(blk)) {
error_setg(errp, "No medium inserted");
return -ENOMEDIUM;
}
return bdrv_truncate(blk->root, offset, exact, prealloc, errp);
return bdrv_truncate(blk->root, offset, prealloc, errp);
}
static void blk_pdiscard_entry(void *opaque)
{
BlkRwCo *rwco = opaque;
QEMUIOVector *qiov = rwco->iobuf;
rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size);
aio_wait_kick();
}
int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes)
{
return blk_prw(blk, offset, NULL, bytes, blk_pdiscard_entry, 0);
}
int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
@@ -2340,9 +2248,6 @@ static void blk_root_drained_end(BdrvChild *child, int *drained_end_counter)
if (blk->dev_ops && blk->dev_ops->drained_end) {
blk->dev_ops->drained_end(blk->dev_opaque);
}
while (qemu_co_enter_next(&blk->queued_requests, NULL)) {
/* Resume all queued requests */
}
}
}
-578
View File
@@ -1,578 +0,0 @@
/*
* block_copy API
*
* Copyright (C) 2013 Proxmox Server Solutions
* Copyright (c) 2019 Virtuozzo International GmbH.
*
* Authors:
* Dietmar Maurer (dietmar@proxmox.com)
* Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "trace.h"
#include "qapi/error.h"
#include "block/block-copy.h"
#include "sysemu/block-backend.h"
#include "qemu/units.h"
#define BLOCK_COPY_MAX_COPY_RANGE (16 * MiB)
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
#define BLOCK_COPY_MAX_MEM (128 * MiB)
typedef struct BlockCopyInFlightReq {
int64_t offset;
int64_t bytes;
QLIST_ENTRY(BlockCopyInFlightReq) list;
CoQueue wait_queue; /* coroutines blocked on this request */
} BlockCopyInFlightReq;
typedef struct BlockCopyState {
/*
* BdrvChild objects are not owned or managed by block-copy. They are
* provided by block-copy user and user is responsible for appropriate
* permissions on these children.
*/
BdrvChild *source;
BdrvChild *target;
BdrvDirtyBitmap *copy_bitmap;
int64_t in_flight_bytes;
int64_t cluster_size;
bool use_copy_range;
int64_t copy_size;
uint64_t len;
QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs;
BdrvRequestFlags write_flags;
/*
* skip_unallocated:
*
* Used by sync=top jobs, which first scan the source node for unallocated
* areas and clear them in the copy_bitmap. During this process, the bitmap
* is thus not fully initialized: It may still have bits set for areas that
* are unallocated and should actually not be copied.
*
* This is indicated by skip_unallocated.
*
* In this case, block_copy() will query the source’s allocation status,
* skip unallocated regions, clear them in the copy_bitmap, and invoke
* block_copy_reset_unallocated() every time it does.
*/
bool skip_unallocated;
ProgressMeter *progress;
/* progress_bytes_callback: called when some copying progress is done. */
ProgressBytesCallbackFunc progress_bytes_callback;
void *progress_opaque;
SharedResource *mem;
} BlockCopyState;
static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s,
int64_t offset,
int64_t bytes)
{
BlockCopyInFlightReq *req;
QLIST_FOREACH(req, &s->inflight_reqs, list) {
if (offset + bytes > req->offset && offset < req->offset + req->bytes) {
return req;
}
}
return NULL;
}
/*
* If there are no intersecting requests return false. Otherwise, wait for the
* first found intersecting request to finish and return true.
*/
static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
int64_t bytes)
{
BlockCopyInFlightReq *req = find_conflicting_inflight_req(s, offset, bytes);
if (!req) {
return false;
}
qemu_co_queue_wait(&req->wait_queue, NULL);
return true;
}
/* Called only on full-dirty region */
static void block_copy_inflight_req_begin(BlockCopyState *s,
BlockCopyInFlightReq *req,
int64_t offset, int64_t bytes)
{
assert(!find_conflicting_inflight_req(s, offset, bytes));
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
s->in_flight_bytes += bytes;
req->offset = offset;
req->bytes = bytes;
qemu_co_queue_init(&req->wait_queue);
QLIST_INSERT_HEAD(&s->inflight_reqs, req, list);
}
/*
* block_copy_inflight_req_shrink
*
* Drop the tail of the request to be handled later. Set dirty bits back and
* wake up all requests waiting for us (may be some of them are not intersecting
* with shrunk request)
*/
static void coroutine_fn block_copy_inflight_req_shrink(BlockCopyState *s,
BlockCopyInFlightReq *req, int64_t new_bytes)
{
if (new_bytes == req->bytes) {
return;
}
assert(new_bytes > 0 && new_bytes < req->bytes);
s->in_flight_bytes -= req->bytes - new_bytes;
bdrv_set_dirty_bitmap(s->copy_bitmap,
req->offset + new_bytes, req->bytes - new_bytes);
req->bytes = new_bytes;
qemu_co_queue_restart_all(&req->wait_queue);
}
static void coroutine_fn block_copy_inflight_req_end(BlockCopyState *s,
BlockCopyInFlightReq *req,
int ret)
{
s->in_flight_bytes -= req->bytes;
if (ret < 0) {
bdrv_set_dirty_bitmap(s->copy_bitmap, req->offset, req->bytes);
}
QLIST_REMOVE(req, list);
qemu_co_queue_restart_all(&req->wait_queue);
}
void block_copy_state_free(BlockCopyState *s)
{
if (!s) {
return;
}
bdrv_release_dirty_bitmap(s->copy_bitmap);
shres_destroy(s->mem);
g_free(s);
}
static uint32_t block_copy_max_transfer(BdrvChild *source, BdrvChild *target)
{
return MIN_NON_ZERO(INT_MAX,
MIN_NON_ZERO(source->bs->bl.max_transfer,
target->bs->bl.max_transfer));
}
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
int64_t cluster_size,
BdrvRequestFlags write_flags, Error **errp)
{
BlockCopyState *s;
BdrvDirtyBitmap *copy_bitmap;
copy_bitmap = bdrv_create_dirty_bitmap(source->bs, cluster_size, NULL,
errp);
if (!copy_bitmap) {
return NULL;
}
bdrv_disable_dirty_bitmap(copy_bitmap);
s = g_new(BlockCopyState, 1);
*s = (BlockCopyState) {
.source = source,
.target = target,
.copy_bitmap = copy_bitmap,
.cluster_size = cluster_size,
.len = bdrv_dirty_bitmap_size(copy_bitmap),
.write_flags = write_flags,
.mem = shres_create(BLOCK_COPY_MAX_MEM),
};
if (block_copy_max_transfer(source, target) < cluster_size) {
/*
* copy_range does not respect max_transfer. We don't want to bother
* with requests smaller than block-copy cluster size, so fallback to
* buffered copying (read and write respect max_transfer on their
* behalf).
*/
s->use_copy_range = false;
s->copy_size = cluster_size;
} else if (write_flags & BDRV_REQ_WRITE_COMPRESSED) {
/* Compression supports only cluster-size writes and no copy-range. */
s->use_copy_range = false;
s->copy_size = cluster_size;
} else {
/*
* We enable copy-range, but keep small copy_size, until first
* successful copy_range (look at block_copy_do_copy).
*/
s->use_copy_range = true;
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
}
QLIST_INIT(&s->inflight_reqs);
return s;
}
void block_copy_set_progress_callback(
BlockCopyState *s,
ProgressBytesCallbackFunc progress_bytes_callback,
void *progress_opaque)
{
s->progress_bytes_callback = progress_bytes_callback;
s->progress_opaque = progress_opaque;
}
void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm)
{
s->progress = pm;
}
/*
* block_copy_do_copy
*
* Do copy of cluster-aligned chunk. Requested region is allowed to exceed
* s->len only to cover last cluster when s->len is not aligned to clusters.
*
* No sync here: nor bitmap neighter intersecting requests handling, only copy.
*
* Returns 0 on success.
*/
static int coroutine_fn block_copy_do_copy(BlockCopyState *s,
int64_t offset, int64_t bytes,
bool zeroes, bool *error_is_read)
{
int ret;
int64_t nbytes = MIN(offset + bytes, s->len) - offset;
void *bounce_buffer = NULL;
assert(offset >= 0 && bytes > 0 && INT64_MAX - offset >= bytes);
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
assert(offset < s->len);
assert(offset + bytes <= s->len ||
offset + bytes == QEMU_ALIGN_UP(s->len, s->cluster_size));
assert(nbytes < INT_MAX);
if (zeroes) {
ret = bdrv_co_pwrite_zeroes(s->target, offset, nbytes, s->write_flags &
~BDRV_REQ_WRITE_COMPRESSED);
if (ret < 0) {
trace_block_copy_write_zeroes_fail(s, offset, ret);
if (error_is_read) {
*error_is_read = false;
}
}
return ret;
}
if (s->use_copy_range) {
ret = bdrv_co_copy_range(s->source, offset, s->target, offset, nbytes,
0, s->write_flags);
if (ret < 0) {
trace_block_copy_copy_range_fail(s, offset, ret);
s->use_copy_range = false;
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
/* Fallback to read+write with allocated buffer */
} else {
if (s->use_copy_range) {
/*
* Successful copy-range. Now increase copy_size. copy_range
* does not respect max_transfer (it's a TODO), so we factor
* that in here.
*
* Note: we double-check s->use_copy_range for the case when
* parallel block-copy request unsets it during previous
* bdrv_co_copy_range call.
*/
s->copy_size =
MIN(MAX(s->cluster_size, BLOCK_COPY_MAX_COPY_RANGE),
QEMU_ALIGN_DOWN(block_copy_max_transfer(s->source,
s->target),
s->cluster_size));
}
goto out;
}
}
/*
* In case of failed copy_range request above, we may proceed with buffered
* request larger than BLOCK_COPY_MAX_BUFFER. Still, further requests will
* be properly limited, so don't care too much. Moreover the most likely
* case (copy_range is unsupported for the configuration, so the very first
* copy_range request fails) is handled by setting large copy_size only
* after first successful copy_range.
*/
bounce_buffer = qemu_blockalign(s->source->bs, nbytes);
ret = bdrv_co_pread(s->source, offset, nbytes, bounce_buffer, 0);
if (ret < 0) {
trace_block_copy_read_fail(s, offset, ret);
if (error_is_read) {
*error_is_read = true;
}
goto out;
}
ret = bdrv_co_pwrite(s->target, offset, nbytes, bounce_buffer,
s->write_flags);
if (ret < 0) {
trace_block_copy_write_fail(s, offset, ret);
if (error_is_read) {
*error_is_read = false;
}
goto out;
}
out:
qemu_vfree(bounce_buffer);
return ret;
}
static int block_copy_block_status(BlockCopyState *s, int64_t offset,
int64_t bytes, int64_t *pnum)
{
int64_t num;
BlockDriverState *base;
int ret;
if (s->skip_unallocated && s->source->bs->backing) {
base = s->source->bs->backing->bs;
} else {
base = NULL;
}
ret = bdrv_block_status_above(s->source->bs, base, offset, bytes, &num,
NULL, NULL);
if (ret < 0 || num < s->cluster_size) {
/*
* On error or if failed to obtain large enough chunk just fallback to
* copy one cluster.
*/
num = s->cluster_size;
ret = BDRV_BLOCK_ALLOCATED | BDRV_BLOCK_DATA;
} else if (offset + num == s->len) {
num = QEMU_ALIGN_UP(num, s->cluster_size);
} else {
num = QEMU_ALIGN_DOWN(num, s->cluster_size);
}
*pnum = num;
return ret;
}
/*
* Check if the cluster starting at offset is allocated or not.
* return via pnum the number of contiguous clusters sharing this allocation.
*/
static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
int64_t *pnum)
{
BlockDriverState *bs = s->source->bs;
int64_t count, total_count = 0;
int64_t bytes = s->len - offset;
int ret;
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
while (true) {
ret = bdrv_is_allocated(bs, offset, bytes, &count);
if (ret < 0) {
return ret;
}
total_count += count;
if (ret || count == 0) {
/*
* ret: partial segment(s) are considered allocated.
* otherwise: unallocated tail is treated as an entire segment.
*/
*pnum = DIV_ROUND_UP(total_count, s->cluster_size);
return ret;
}
/* Unallocated segment(s) with uncertain following segment(s) */
if (total_count >= s->cluster_size) {
*pnum = total_count / s->cluster_size;
return 0;
}
offset += count;
bytes -= count;
}
}
/*
* Reset bits in copy_bitmap starting at offset if they represent unallocated
* data in the image. May reset subsequent contiguous bits.
* @return 0 when the cluster at @offset was unallocated,
* 1 otherwise, and -ret on error.
*/
int64_t block_copy_reset_unallocated(BlockCopyState *s,
int64_t offset, int64_t *count)
{
int ret;
int64_t clusters, bytes;
ret = block_copy_is_cluster_allocated(s, offset, &clusters);
if (ret < 0) {
return ret;
}
bytes = clusters * s->cluster_size;
if (!ret) {
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
progress_set_remaining(s->progress,
bdrv_get_dirty_count(s->copy_bitmap) +
s->in_flight_bytes);
}
*count = bytes;
return ret;
}
/*
* block_copy_dirty_clusters
*
* Copy dirty clusters in @offset/@bytes range.
* Returns 1 if dirty clusters found and successfully copied, 0 if no dirty
* clusters found and -errno on failure.
*/
static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
int64_t offset, int64_t bytes,
bool *error_is_read)
{
int ret = 0;
bool found_dirty = false;
/*
* block_copy() user is responsible for keeping source and target in same
* aio context
*/
assert(bdrv_get_aio_context(s->source->bs) ==
bdrv_get_aio_context(s->target->bs));
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
while (bytes) {
BlockCopyInFlightReq req;
int64_t next_zero, cur_bytes, status_bytes;
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
trace_block_copy_skip(s, offset);
offset += s->cluster_size;
bytes -= s->cluster_size;
continue; /* already copied */
}
found_dirty = true;
cur_bytes = MIN(bytes, s->copy_size);
next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset,
cur_bytes);
if (next_zero >= 0) {
assert(next_zero > offset); /* offset is dirty */
assert(next_zero < offset + cur_bytes); /* no need to do MIN() */
cur_bytes = next_zero - offset;
}
block_copy_inflight_req_begin(s, &req, offset, cur_bytes);
ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
assert(ret >= 0); /* never fail */
cur_bytes = MIN(cur_bytes, status_bytes);
block_copy_inflight_req_shrink(s, &req, cur_bytes);
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
block_copy_inflight_req_end(s, &req, 0);
progress_set_remaining(s->progress,
bdrv_get_dirty_count(s->copy_bitmap) +
s->in_flight_bytes);
trace_block_copy_skip_range(s, offset, status_bytes);
offset += status_bytes;
bytes -= status_bytes;
continue;
}
trace_block_copy_process(s, offset);
co_get_from_shres(s->mem, cur_bytes);
ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
error_is_read);
co_put_to_shres(s->mem, cur_bytes);
block_copy_inflight_req_end(s, &req, ret);
if (ret < 0) {
return ret;
}
progress_work_done(s->progress, cur_bytes);
s->progress_bytes_callback(cur_bytes, s->progress_opaque);
offset += cur_bytes;
bytes -= cur_bytes;
}
return found_dirty;
}
/*
* block_copy
*
* Copy requested region, accordingly to dirty bitmap.
* Collaborate with parallel block_copy requests: if they succeed it will help
* us. If they fail, we will retry not-copied regions. So, if we return error,
* it means that some I/O operation failed in context of _this_ block_copy call,
* not some parallel operation.
*/
int coroutine_fn block_copy(BlockCopyState *s, int64_t offset, int64_t bytes,
bool *error_is_read)
{
int ret;
do {
ret = block_copy_dirty_clusters(s, offset, bytes, error_is_read);
if (ret == 0) {
ret = block_copy_wait_one(s, offset, bytes);
}
/*
* We retry in two cases:
* 1. Some progress done
* Something was copied, which means that there were yield points
* and some new dirty bits may have appeared (due to failed parallel
* block-copy requests).
* 2. We have waited for some intersecting block-copy request
* It may have failed and produced new dirty bits.
*/
} while (ret > 0);
return ret;
}
BdrvDirtyBitmap *block_copy_dirty_bitmap(BlockCopyState *s)
{
return s->copy_bitmap;
}
void block_copy_set_skip_unallocated(BlockCopyState *s, bool skip)
{
s->skip_unallocated = skip;
}
+2 -2
View File
@@ -248,8 +248,8 @@ bochs_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector local_qiov;
int ret;
assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
qemu_iovec_init(&local_qiov, qiov->niov);
qemu_co_mutex_lock(&s->lock);
+2 -2
View File
@@ -253,8 +253,8 @@ cloop_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
int nb_sectors = bytes >> BDRV_SECTOR_BITS;
int ret, i;
assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
qemu_co_mutex_lock(&s->lock);
+28 -17
View File
@@ -43,6 +43,27 @@ typedef struct CommitBlockJob {
char *backing_file_str;
} CommitBlockJob;
static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
int64_t offset, uint64_t bytes,
void *buf)
{
int ret = 0;
assert(bytes < SIZE_MAX);
ret = blk_co_pread(bs, offset, bytes, buf, 0);
if (ret < 0) {
return ret;
}
ret = blk_co_pwrite(base, offset, bytes, buf, 0);
if (ret < 0) {
return ret;
}
return 0;
}
static int commit_prepare(Job *job)
{
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
@@ -119,6 +140,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
int ret = 0;
int64_t n = 0; /* bytes */
void *buf = NULL;
int bytes_written = 0;
int64_t len, base_len;
ret = len = blk_getlength(s->top);
@@ -133,7 +155,7 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
}
if (base_len < len) {
ret = blk_truncate(s->base, len, false, PREALLOC_MODE_OFF, NULL);
ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL);
if (ret) {
goto out;
}
@@ -143,7 +165,6 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
for (offset = 0; offset < len; offset += n) {
bool copy;
bool error_in_source = true;
/* Note that even when no rate limit is applied we need to yield
* with no pending I/O here so that bdrv_drain_all() returns.
@@ -158,20 +179,12 @@ static int coroutine_fn commit_run(Job *job, Error **errp)
copy = (ret == 1);
trace_commit_one_iteration(s, offset, n, ret);
if (copy) {
assert(n < SIZE_MAX);
ret = blk_co_pread(s->top, offset, n, buf, 0);
if (ret >= 0) {
ret = blk_co_pwrite(s->base, offset, n, buf, 0);
if (ret < 0) {
error_in_source = false;
}
}
ret = commit_populate(s->top, s->base, offset, n, buf);
bytes_written += n;
}
if (ret < 0) {
BlockErrorAction action =
block_job_error_action(&s->common, s->on_error,
error_in_source, -ret);
block_job_error_action(&s->common, false, s->on_error, -ret);
if (action == BLOCK_ERROR_ACTION_REPORT) {
goto out;
} else {
@@ -203,6 +216,7 @@ static const BlockJobDriver commit_job_driver = {
.job_type = JOB_TYPE_COMMIT,
.free = block_job_free,
.user_resume = block_job_user_resume,
.drain = block_job_drain,
.run = commit_run,
.prepare = commit_prepare,
.abort = commit_abort,
@@ -336,7 +350,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
if (ret < 0) {
goto fail;
}
blk_set_disable_request_queuing(s->base, true);
s->base_bs = base;
/* Required permissions are already taken with block_job_add_bdrv() */
@@ -345,7 +358,6 @@ void commit_start(const char *job_id, BlockDriverState *bs,
if (ret < 0) {
goto fail;
}
blk_set_disable_request_queuing(s->top, true);
s->backing_file_str = g_strdup(backing_file_str);
s->on_error = on_error;
@@ -458,8 +470,7 @@ int bdrv_commit(BlockDriverState *bs)
* grow the backing file image if possible. If not possible,
* we must return an error */
if (length > backing_length) {
ret = blk_truncate(backing, length, false, PREALLOC_MODE_OFF,
&local_err);
ret = blk_truncate(backing, length, PREALLOC_MODE_OFF, &local_err);
if (ret < 0) {
error_report_err(local_err);
goto ro_cleanup;
+17
View File
@@ -73,6 +73,13 @@ static int64_t cor_getlength(BlockDriverState *bs)
}
static int coroutine_fn cor_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{
return bdrv_co_truncate(bs->file, offset, prealloc, errp);
}
static int coroutine_fn cor_co_preadv(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, int flags)
@@ -118,6 +125,13 @@ static void cor_lock_medium(BlockDriverState *bs, bool locked)
}
static bool cor_recurse_is_first_non_filter(BlockDriverState *bs,
BlockDriverState *candidate)
{
return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate);
}
static BlockDriver bdrv_copy_on_read = {
.format_name = "copy-on-read",
@@ -125,6 +139,7 @@ static BlockDriver bdrv_copy_on_read = {
.bdrv_child_perm = cor_child_perm,
.bdrv_getlength = cor_getlength,
.bdrv_co_truncate = cor_co_truncate,
.bdrv_co_preadv = cor_co_preadv,
.bdrv_co_pwritev = cor_co_pwritev,
@@ -136,6 +151,8 @@ static BlockDriver bdrv_copy_on_read = {
.bdrv_co_block_status = bdrv_co_block_status_from_file,
.bdrv_recurse_is_first_non_filter = cor_recurse_is_first_non_filter,
.has_variable_length = true,
.is_filter = true,
};
-1
View File
@@ -25,7 +25,6 @@
#include "qemu/osdep.h"
#include "block/block_int.h"
#include "qemu/job.h"
#include "qemu/main-loop.h"
#include "qapi/qapi-commands-block-core.h"
#include "qapi/qapi-visit-block-core.h"
#include "qapi/clone-visitor.h"
+7 -112
View File
@@ -30,7 +30,6 @@
#include "qapi/error.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "qemu/cutils.h"
#include "crypto.h"
typedef struct BlockCrypto BlockCrypto;
@@ -75,7 +74,6 @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
struct BlockCryptoCreateData {
BlockBackend *blk;
uint64_t size;
PreallocMode prealloc;
};
@@ -114,8 +112,8 @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
* available to the guest, so we must take account of that
* which will be used by the crypto header
*/
return blk_truncate(data->blk, data->size + headerlen, false,
data->prealloc, errp);
return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF,
errp);
}
@@ -253,7 +251,6 @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
static int block_crypto_co_create_generic(BlockDriverState *bs,
int64_t size,
QCryptoBlockCreateOptions *opts,
PreallocMode prealloc,
Error **errp)
{
int ret;
@@ -269,14 +266,9 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
goto cleanup;
}
if (prealloc == PREALLOC_MODE_METADATA) {
prealloc = PREALLOC_MODE_OFF;
}
data = (struct BlockCryptoCreateData) {
.blk = blk,
.size = size,
.prealloc = prealloc,
};
crypto = qcrypto_block_create(opts, NULL,
@@ -298,7 +290,7 @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
}
static int coroutine_fn
block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
block_crypto_co_truncate(BlockDriverState *bs, int64_t offset,
PreallocMode prealloc, Error **errp)
{
BlockCrypto *crypto = bs->opaque;
@@ -312,7 +304,7 @@ block_crypto_co_truncate(BlockDriverState *bs, int64_t offset, bool exact,
offset += payload_offset;
return bdrv_co_truncate(bs->file, offset, exact, prealloc, errp);
return bdrv_co_truncate(bs->file, offset, prealloc, errp);
}
static void block_crypto_close(BlockDriverState *bs)
@@ -485,67 +477,6 @@ static int64_t block_crypto_getlength(BlockDriverState *bs)
}
static BlockMeasureInfo *block_crypto_measure(QemuOpts *opts,
BlockDriverState *in_bs,
Error **errp)
{
g_autoptr(QCryptoBlockCreateOptions) create_opts = NULL;
Error *local_err = NULL;
BlockMeasureInfo *info;
uint64_t size;
size_t luks_payload_size;
QDict *cryptoopts;
/*
* Preallocation mode doesn't affect size requirements but we must consume
* the option.
*/
g_free(qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC));
size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
if (in_bs) {
int64_t ssize = bdrv_getlength(in_bs);
if (ssize < 0) {
error_setg_errno(&local_err, -ssize,
"Unable to get image virtual_size");
goto err;
}
size = ssize;
}
cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
&block_crypto_create_opts_luks, true);
qdict_put_str(cryptoopts, "format", "luks");
create_opts = block_crypto_create_opts_init(cryptoopts, &local_err);
qobject_unref(cryptoopts);
if (!create_opts) {
goto err;
}
if (!qcrypto_block_calculate_payload_offset(create_opts, NULL,
&luks_payload_size,
&local_err)) {
goto err;
}
/*
* Unallocated blocks are still encrypted so allocation status makes no
* difference to the file size.
*/
info = g_new(BlockMeasureInfo, 1);
info->fully_allocated = luks_payload_size + size;
info->required = luks_payload_size + size;
return info;
err:
error_propagate(errp, local_err);
return NULL;
}
static int block_crypto_probe_luks(const uint8_t *buf,
int buf_size,
const char *filename) {
@@ -569,7 +500,6 @@ block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
BlockdevCreateOptionsLUKS *luks_opts;
BlockDriverState *bs = NULL;
QCryptoBlockCreateOptions create_opts;
PreallocMode preallocation = PREALLOC_MODE_OFF;
int ret;
assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
@@ -585,12 +515,8 @@ block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
.u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts),
};
if (luks_opts->has_preallocation) {
preallocation = luks_opts->preallocation;
}
ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts,
preallocation, errp);
errp);
if (ret < 0) {
goto fail;
}
@@ -601,32 +527,19 @@ fail:
return ret;
}
static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv,
const char *filename,
static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
QemuOpts *opts,
Error **errp)
{
QCryptoBlockCreateOptions *create_opts = NULL;
BlockDriverState *bs = NULL;
QDict *cryptoopts;
PreallocMode prealloc;
char *buf = NULL;
int64_t size;
int ret;
Error *local_err = NULL;
/* Parse options */
size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
buf = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC);
prealloc = qapi_enum_parse(&PreallocMode_lookup, buf,
PREALLOC_MODE_OFF, &local_err);
g_free(buf);
if (local_err) {
error_propagate(errp, local_err);
return -EINVAL;
}
cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
&block_crypto_create_opts_luks,
true);
@@ -652,30 +565,13 @@ static int coroutine_fn block_crypto_co_create_opts_luks(BlockDriver *drv,
}
/* Create format layer */
ret = block_crypto_co_create_generic(bs, size, create_opts, prealloc, errp);
ret = block_crypto_co_create_generic(bs, size, create_opts, errp);
if (ret < 0) {
goto fail;
}
ret = 0;
fail:
/*
* If an error occurred, delete 'filename'. Even if the file existed
* beforehand, it has been truncated and corrupted in the process.
*/
if (ret && bs) {
Error *local_delete_err = NULL;
int r_del = bdrv_co_delete_file(bs, &local_delete_err);
/*
* ENOTSUP will happen if the block driver doesn't support
* the 'bdrv_co_delete_file' interface. This is a predictable
* scenario and shouldn't be reported back to the user.
*/
if ((r_del < 0) && (r_del != -ENOTSUP)) {
error_report_err(local_delete_err);
}
}
bdrv_unref(bs);
qapi_free_QCryptoBlockCreateOptions(create_opts);
qobject_unref(cryptoopts);
@@ -750,7 +646,6 @@ static BlockDriver bdrv_crypto_luks = {
.bdrv_co_preadv = block_crypto_co_preadv,
.bdrv_co_pwritev = block_crypto_co_pwritev,
.bdrv_getlength = block_crypto_getlength,
.bdrv_measure = block_crypto_measure,
.bdrv_get_info = block_crypto_get_info_luks,
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
+5 -35
View File
@@ -214,35 +214,11 @@ static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
{
BDRVCURLState *s = opaque;
size_t realsize = size * nmemb;
const char *header = (char *)ptr;
const char *end = header + realsize;
const char *accept_ranges = "accept-ranges:";
const char *bytes = "bytes";
const char *accept_line = "Accept-Ranges: bytes";
if (realsize >= strlen(accept_ranges)
&& g_ascii_strncasecmp(header, accept_ranges,
strlen(accept_ranges)) == 0) {
char *p = strchr(header, ':') + 1;
/* Skip whitespace between the header name and value. */
while (p < end && *p && g_ascii_isspace(*p)) {
p++;
}
if (end - p >= strlen(bytes)
&& strncmp(p, bytes, strlen(bytes)) == 0) {
/* Check that there is nothing but whitespace after the value. */
p += strlen(bytes);
while (p < end && *p && g_ascii_isspace(*p)) {
p++;
}
if (p == end || !*p) {
s->accept_range = true;
}
}
if (realsize >= strlen(accept_line)
&& strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) {
s->accept_range = true;
}
return realsize;
@@ -906,13 +882,7 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
trace_curl_setup_preadv(acb->bytes, start, state->range);
curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
if (curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) {
state->acb[0] = NULL;
acb->ret = -EIO;
curl_clean_state(state);
goto out;
}
curl_multi_add_handle(s->multi, state->curl);
/* Tell curl it needs to kick things off */
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
Loaded 100 of 4035 files, more files were not shown because too many files have changed in this diff. Show more