summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorVaporeon2017-11-01 23:36:35 +1100
committerVaporeon2017-11-01 23:36:35 +1100
commitca7a46333893766416173a33ee0b5f73a1447d8b (patch)
tree9186e977720486f4aa0931cac16cac194143f19d
downloadaur-ca7a46333893766416173a33ee0b5f73a1447d8b.tar.gz
add patched package
-rw-r--r--.SRCINFO127
-rw-r--r--65-kvm.rules2
-rw-r--r--PKGBUILD244
-rw-r--r--allow_elf64.patch27
-rw-r--r--audio-improvements.patch1125
-rw-r--r--cpu-pinning.patch186
-rw-r--r--qemu-ga.service9
-rw-r--r--qemu.install8
-rw-r--r--qemu.sysusers1
9 files changed, 1729 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..056e4d4abec4
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,127 @@
+# Generated by mksrcinfo v8
+# Wed Nov 1 12:36:02 UTC 2017
+pkgbase = qemu-patched
+ pkgdesc = A generic and open source machine emulator and virtualizer - Patched for extra functionality
+ pkgver = 2.10.1
+ pkgrel = 1
+ url = http://wiki.qemu.org/
+ arch = i686
+ arch = x86_64
+ license = GPL2
+ license = LGPL2.1
+ makedepends = spice-protocol
+ makedepends = python2
+ makedepends = ceph
+ makedepends = libiscsi
+ makedepends = glusterfs
+ depends = virglrenderer
+ depends = sdl2
+ depends = vte3
+ depends = brltty
+ depends = seabios
+ depends = gnutls
+ depends = libpng
+ depends = libaio
+ depends = numactl
+ depends = jemalloc
+ depends = xfsprogs
+ depends = libnfs
+ depends = lzo
+ depends = snappy
+ depends = curl
+ depends = vde2
+ depends = libcap-ng
+ depends = spice
+ depends = libcacard
+ depends = usbredir
+ source = http://wiki.qemu.org//download/qemu-2.10.1.tar.bz2
+ source = http://wiki.qemu.org//download/qemu-2.10.1.tar.bz2.sig
+ source = qemu.sysusers
+ source = qemu-ga.service
+ source = 65-kvm.rules
+ source = allow_elf64.patch
+ source = cpu-pinning.patch
+ source = audio-improvements.patch
+ sha256sums = 8e040bc7556401ebb3a347a8f7878e9d4028cf71b2744b1a1699f4e741966ba8
+ sha256sums = SKIP
+ sha256sums = dd43e2ef062b071a0b9d0d5ea54737f41600ca8a84a8aefbebb1ff09f978acfb
+ sha256sums = c39bcde4a09165e64419fd2033b3532378bba84d509d39e2d51694d44c1f8d88
+ sha256sums = 60dcde5002c7c0b983952746e6fb2cf06d6c5b425d64f340f819356e561e7fc7
+ sha256sums = 13a6d9e678bdc9e1f051006cfd0555f5a80582368f54c8a1bb5a78ece3832ac4
+ sha256sums = 8d4a7e35ab1a0a465f737cf60fc0392afc430e22354a40a89505f8766a3a3ee8
+ sha256sums = 23338655345d0ee535f34acc124f1ddd75e5ad4483e2bd87294b7ac4fe3fa859
+
+pkgname = qemu-patched
+ optdepends = qemu-patched-arch-extra: extra architectures support
+ provides = qemu-headless
+ provides = qemu
+ conflicts = qemu-headless
+ conflicts = qemu
+ replaces = qemu-kvm
+
+pkgname = qemu-patched-headless
+ pkgdesc = QEMU without GUI
+ depends = seabios
+ depends = gnutls
+ depends = libpng
+ depends = libaio
+ depends = numactl
+ depends = jemalloc
+ depends = xfsprogs
+ depends = libnfs
+ depends = lzo
+ depends = snappy
+ depends = curl
+ depends = vde2
+ depends = libcap-ng
+ depends = spice
+ depends = libcacard
+ depends = usbredir
+ optdepends = qemu-patched-headless-arch-extra: extra architectures support
+ conflicts = qemu-headless
+
+pkgname = qemu-patched-arch-extra
+ pkgdesc = QEMU for foreign architectures
+ depends = qemu
+ provides = qemu-headless-arch-extra
+ provides = qemu-arch-extra
+ conflicts = qemu-headless-arch-extra
+ conflicts = qemu-arch-extra
+ options = !strip
+
+pkgname = qemu-patched-headless-arch-extra
+ pkgdesc = QEMU without GUI, for foreign architectures
+ depends = qemu-headless
+ provides = qemu-headless-arch-extra
+ conflicts = qemu-headless-arch-extra
+ options = !strip
+
+pkgname = qemu-patched-block-iscsi
+ pkgdesc = QEMU iSCSI block module
+ depends = glib2
+ depends = libiscsi
+ depends = jemalloc
+ provides = qemu-block-iscsi
+ conflicts = qemu-block-iscsi
+
+pkgname = qemu-patched-block-rbd
+ pkgdesc = QEMU RBD block module
+ depends = glib2
+ depends = ceph
+ provides = qemu-block-rbd
+ conflicts = qemu-block-rbd
+
+pkgname = qemu-patched-block-gluster
+ pkgdesc = QEMU GlusterFS block module
+ depends = glib2
+ depends = glusterfs
+ provides = qemu-block-gluster
+ conflicts = qemu-block-gluster
+
+pkgname = qemu-patched-guest-agent
+ pkgdesc = QEMU Guest Agent
+ depends = gcc-libs
+ depends = glib2
+ provides = qemu-guest-agent
+ conflicts = qemu-guest-agent
+
diff --git a/65-kvm.rules b/65-kvm.rules
new file mode 100644
index 000000000000..569ded9f972f
--- /dev/null
+++ b/65-kvm.rules
@@ -0,0 +1,2 @@
+KERNEL=="kvm", GROUP="kvm", MODE="0660"
+KERNEL=="vhost-net", GROUP="kvm", MODE="0660", TAG+="uaccess", OPTIONS+="static_node=vhost-net"
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..d61cc8536ce7
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,244 @@
+# Maintainer: Vaporeon <vaporeon@vaporeon.io>
+# Contributor: Tobias Powalowski <tpowa@archlinux.org>
+# Contributor: Sébastien "Seblu" Luttringer <seblu@seblu.net>
+
+pkgbase=qemu-patched
+pkgname=(qemu-patched qemu-patched-headless qemu-patched-arch-extra qemu-patched-headless-arch-extra
+ qemu-patched-block-{iscsi,rbd,gluster} qemu-patched-guest-agent)
+_pkgname=qemu
+pkgdesc="A generic and open source machine emulator and virtualizer - Patched for extra functionality"
+pkgver=2.10.1
+pkgrel=1
+arch=(i686 x86_64)
+license=(GPL2 LGPL2.1)
+url="http://wiki.qemu.org/"
+_headlessdeps=(seabios gnutls libpng libaio numactl jemalloc xfsprogs libnfs
+ lzo snappy curl vde2 libcap-ng spice libcacard usbredir)
+depends=(virglrenderer sdl2 vte3 brltty "${_headlessdeps[@]}")
+makedepends=(spice-protocol python2 ceph libiscsi glusterfs)
+source=("$url/download/${_pkgname}-${pkgver}.tar.bz2"{,.sig}
+ qemu.sysusers
+ qemu-ga.service
+ 65-kvm.rules
+ allow_elf64.patch
+ cpu-pinning.patch
+ audio-improvements.patch)
+sha256sums=('8e040bc7556401ebb3a347a8f7878e9d4028cf71b2744b1a1699f4e741966ba8'
+ 'SKIP'
+ 'dd43e2ef062b071a0b9d0d5ea54737f41600ca8a84a8aefbebb1ff09f978acfb'
+ 'c39bcde4a09165e64419fd2033b3532378bba84d509d39e2d51694d44c1f8d88'
+ '60dcde5002c7c0b983952746e6fb2cf06d6c5b425d64f340f819356e561e7fc7'
+ '13a6d9e678bdc9e1f051006cfd0555f5a80582368f54c8a1bb5a78ece3832ac4'
+ '8d4a7e35ab1a0a465f737cf60fc0392afc430e22354a40a89505f8766a3a3ee8'
+ '23338655345d0ee535f34acc124f1ddd75e5ad4483e2bd87294b7ac4fe3fa859')
+validpgpkeys=('CEACC9E15534EBABB82D3FA03353C9CEF108B584')
+
+case $CARCH in
+ i?86) _corearch=i386 ;;
+ x86_64) _corearch=x86_64 ;;
+esac
+
+prepare() {
+ mkdir build-{full,headless}
+ mkdir -p extra-arch-{full,headless}/usr/{bin,share/qemu}
+
+ cd ${_pkgname}-${pkgver}
+ sed -i 's/vte-2\.90/vte-2.91/g' configure
+
+ patch -p1 < ../allow_elf64.patch
+ patch -p1 < ../cpu-pinning.patch
+ patch -p0 < ../audio-improvements.patch
+}
+
+build() {
+ _build full \
+ --audio-drv-list="pa alsa sdl"
+
+ _build headless \
+ --audio-drv-list= \
+ --disable-bluez \
+ --disable-sdl \
+ --disable-gtk \
+ --disable-vte \
+ --disable-opengl \
+ --disable-virglrenderer \
+ --disable-brlapi
+}
+
+_build() (
+ cd build-$1
+
+ # qemu vs. make 4 == bad
+ export ARFLAGS=rv
+
+ # http://permalink.gmane.org/gmane.comp.emulators.qemu/238740
+ export CFLAGS+=" -fPIC"
+
+ ../${_pkgname}-${pkgver}/configure \
+ --prefix=/usr \
+ --sysconfdir=/etc \
+ --localstatedir=/var \
+ --libexecdir=/usr/lib/qemu \
+ --python=/usr/bin/python2 \
+ --smbd=/usr/bin/smbd \
+ --with-gtkabi=3.0 \
+ --with-sdlabi=2.0 \
+ --enable-modules \
+ --enable-jemalloc \
+ "${@:2}"
+
+ make
+)
+
+package_qemu-patched() {
+ optdepends=('qemu-patched-arch-extra: extra architectures support')
+ provides=(qemu-headless qemu)
+ conflicts=(qemu-headless qemu)
+ replaces=(qemu-kvm)
+
+ _package full
+}
+
+package_qemu-patched-headless() {
+ pkgdesc="QEMU without GUI"
+ depends=("${_headlessdeps[@]}")
+ optdepends=('qemu-patched-headless-arch-extra: extra architectures support')
+ conflicts=(qemu-headless)
+ _package headless
+}
+
+_package() {
+ optdepends+=('ovmf: Tianocore UEFI firmware for qemu'
+ 'samba: SMB/CIFS server support'
+ 'qemu-patched-block-iscsi: iSCSI block support'
+ 'qemu-patched-block-rbd: RBD block support'
+ 'qemu-patched-block-gluster: glusterfs block support')
+ install=qemu.install
+ options=(!strip)
+
+ make -C build-$1 DESTDIR="$pkgdir" install "${@:2}"
+
+ # systemd stuff
+ install -Dm644 65-kvm.rules "$pkgdir/usr/lib/udev/rules.d/65-kvm.rules"
+ install -Dm644 qemu.sysusers "$pkgdir/usr/lib/sysusers.d/qemu.conf"
+
+ # remove conflicting /var/run directory
+ cd "$pkgdir"
+ rm -r var
+
+ cd usr/lib
+ tidy_strip
+
+ # bridge_helper needs suid
+ # https://bugs.archlinux.org/task/32565
+ chmod u+s qemu/qemu-bridge-helper
+
+ # remove split block modules
+ rm qemu/block-{iscsi,rbd,gluster}.so
+
+ cd ../bin
+ tidy_strip
+
+ # remove extra arch
+ for _bin in qemu-*; do
+ [[ -f $_bin ]] || continue
+
+ case ${_bin#qemu-} in
+ # guest agent
+ ga) rm "$_bin"; continue ;;
+
+ # tools
+ img|io|nbd) continue ;;
+
+ # core emu
+ system-${_corearch}) continue ;;
+ esac
+
+ mv "$_bin" "$srcdir/extra-arch-$1/usr/bin"
+ done
+
+ cd ../share/qemu
+ for _blob in *; do
+ [[ -f $_blob ]] || continue
+
+ case $_blob in
+ # provided by seabios package
+ bios.bin|acpi-dsdt.aml|bios-256k.bin|vgabios-cirrus.bin|vgabios-qxl.bin|\
+ vgabios-stdvga.bin|vgabios-vmware.bin) rm "$_blob"; continue ;;
+
+ # iPXE ROMs
+ efi-*|pxe-*) continue ;;
+
+ # core blobs
+ kvmvapic.bin|linuxboot*|multiboot.bin|sgabios.bin|vgabios*) continue ;;
+
+ # Trace events definitions
+ trace-events*) continue ;;
+
+ # Logos
+ *.bmp|*.svg) continue ;;
+ esac
+
+ mv "$_blob" "$srcdir/extra-arch-$1/usr/share/qemu"
+ done
+}
+
+package_qemu-patched-arch-extra() {
+ pkgdesc="QEMU for foreign architectures"
+ depends=(qemu)
+ provides=(qemu-headless-arch-extra qemu-arch-extra)
+ conflicts=(qemu-headless-arch-extra qemu-arch-extra)
+ options=(!strip)
+
+ mv extra-arch-full/usr "$pkgdir"
+}
+
+package_qemu-patched-headless-arch-extra() {
+ pkgdesc="QEMU without GUI, for foreign architectures"
+ depends=(qemu-headless)
+ options=(!strip)
+ conflicts=(qemu-headless-arch-extra)
+ provides=(qemu-headless-arch-extra)
+
+ mv extra-arch-headless/usr "$pkgdir"
+}
+
+package_qemu-patched-block-iscsi() {
+ pkgdesc="QEMU iSCSI block module"
+ depends=(glib2 libiscsi jemalloc)
+ conflicts=(qemu-block-iscsi)
+ provides=(qemu-block-iscsi)
+
+ install -D build-full/block-iscsi.so "$pkgdir/usr/lib/qemu/block-iscsi.so"
+}
+
+package_qemu-patched-block-rbd() {
+ pkgdesc="QEMU RBD block module"
+ depends=(glib2 ceph)
+ conflicts=(qemu-block-rbd)
+ provides=(qemu-block-rbd)
+
+ install -D build-full/block-rbd.so "$pkgdir/usr/lib/qemu/block-rbd.so"
+}
+
+package_qemu-patched-block-gluster() {
+ pkgdesc="QEMU GlusterFS block module"
+ depends=(glib2 glusterfs)
+ conflicts=(qemu-block-gluster)
+ provides=(qemu-block-gluster)
+
+ install -D build-full/block-gluster.so "$pkgdir/usr/lib/qemu/block-gluster.so"
+}
+
+package_qemu-patched-guest-agent() {
+ pkgdesc="QEMU Guest Agent"
+ depends=(gcc-libs glib2)
+ conflicts=(qemu-guest-agent)
+ provides=(qemu-guest-agent)
+
+ install -D build-full/qemu-ga "$pkgdir/usr/bin/qemu-ga"
+ install -Dm644 qemu-ga.service "$pkgdir/usr/lib/systemd/system/qemu-ga.service"
+ install -Dm755 "$srcdir/${_pkgname}-${pkgver}/scripts/qemu-guest-agent/fsfreeze-hook" "$pkgdir/etc/qemu/fsfreeze-hook"
+}
+
+# vim:set ts=2 sw=2 et:
diff --git a/allow_elf64.patch b/allow_elf64.patch
new file mode 100644
index 000000000000..07f27a038b1b
--- /dev/null
+++ b/allow_elf64.patch
@@ -0,0 +1,27 @@
+commit 3c72765ec760a51f0e879dc792be82c93141e318
+Author: Anatol Pomozov <anatol.pomozov@gmail.com>
+Date: Tue Jun 6 20:07:03 2017 -0700
+
+ Remove restriction that prevents bootimg elf64 images
+
+ It is possible to create a 64 bit elf image that has valid multiboot header.
+ qemu should be able to boot such images.
+
+ Signed-off-by: Anatol Pomozov <anatol.pomozov@gmail.com>
+
+diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c
+index 663f35a658..cf1b4f5fb3 100644
+--- a/hw/i386/multiboot.c
++++ b/hw/i386/multiboot.c
+@@ -192,11 +192,6 @@ int load_multiboot(FWCfgState *fw_cfg,
+ int kernel_size;
+ fclose(f);
+
+- if (((struct elf64_hdr*)header)->e_machine == EM_X86_64) {
+- fprintf(stderr, "Cannot load x86-64 image, give a 32bit one.\n");
+- exit(1);
+- }
+-
+ kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+ &elf_low, &elf_high, 0, I386_ELF_MACHINE,
+ 0, 0);
diff --git a/audio-improvements.patch b/audio-improvements.patch
new file mode 100644
index 000000000000..47ae53e8d7b1
--- /dev/null
+++ b/audio-improvements.patch
@@ -0,0 +1,1125 @@
+diff --git audio/audio.c audio/audio.c
+index beafed209b..6f42a019b0 100644
+--- audio/audio.c
++++ audio/audio.c
+@@ -2066,3 +2066,8 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
+ }
+ }
+ }
++
++int64_t audio_get_timer_ticks(void)
++{
++ return conf.period.ticks;
++}
+diff --git audio/audio_int.h audio/audio_int.h
+index 5bcb1c60e1..2f7fc4f8ac 100644
+--- audio/audio_int.h
++++ audio/audio_int.h
+@@ -214,6 +214,8 @@ extern struct audio_driver pa_audio_driver;
+ extern struct audio_driver spice_audio_driver;
+ extern const struct mixeng_volume nominal_volume;
+
++int64_t audio_get_timer_ticks(void);
++
+ 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);
+
+diff --git audio/paaudio.c audio/paaudio.c
+index 65beb6f010..b46beeea92 100644
+--- audio/paaudio.c
++++ audio/paaudio.c
+@@ -1,16 +1,22 @@
+ /* public domain */
+ #include "qemu/osdep.h"
+-#include "qemu-common.h"
++#include "qemu/timer.h"
+ #include "audio.h"
+
+ #include <pulse/pulseaudio.h>
+
+ #define AUDIO_CAP "pulseaudio"
++#define DEBUG
+ #include "audio_int.h"
+-#include "audio_pt_int.h"
+
+ typedef struct {
+- int samples;
++ int buffer_size_out;
++ int buffer_size_in;
++ int tlength;
++ int fragsize;
++ int maxlength_in;
++ int adjust_latency_out;
++ int adjust_latency_in;
+ char *server;
+ char *sink;
+ char *source;
+@@ -24,28 +30,18 @@ typedef struct {
+
+ typedef struct {
+ HWVoiceOut hw;
+- int done;
+- int live;
+- int decr;
+- int rpos;
+ pa_stream *stream;
+- void *pcm_buf;
+- struct audio_pt pt;
+ paaudio *g;
++ pa_sample_spec ss;
++ pa_buffer_attr ba;
+ } PAVoiceOut;
+
+ typedef struct {
+ HWVoiceIn hw;
+- int done;
+- int dead;
+- int incr;
+- int wpos;
+ pa_stream *stream;
+- void *pcm_buf;
+- struct audio_pt pt;
+- const void *read_data;
+- size_t read_index, read_length;
+ paaudio *g;
++ pa_sample_spec ss;
++ pa_buffer_attr ba;
+ } PAVoiceIn;
+
+ static void qpa_audio_fini(void *opaque);
+@@ -109,182 +105,59 @@ static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x)
+ } \
+ } while (0);
+
+-static int qpa_simple_read (PAVoiceIn *p, void *data, size_t length, int *rerror)
+-{
+- paaudio *g = p->g;
+-
+- pa_threaded_mainloop_lock (g->mainloop);
+-
+- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+-
+- while (length > 0) {
+- size_t l;
+-
+- while (!p->read_data) {
+- int r;
+-
+- r = pa_stream_peek (p->stream, &p->read_data, &p->read_length);
+- CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
+-
+- if (!p->read_data) {
+- pa_threaded_mainloop_wait (g->mainloop);
+- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+- } else {
+- p->read_index = 0;
+- }
+- }
+-
+- l = p->read_length < length ? p->read_length : length;
+- memcpy (data, (const uint8_t *) p->read_data+p->read_index, l);
+-
+- data = (uint8_t *) data + l;
+- length -= l;
+-
+- p->read_index += l;
+- p->read_length -= l;
+-
+- if (!p->read_length) {
+- int r;
+-
+- r = pa_stream_drop (p->stream);
+- p->read_data = NULL;
+- p->read_length = 0;
+- p->read_index = 0;
+-
+- CHECK_SUCCESS_GOTO (g, rerror, r == 0, unlock_and_fail);
+- }
+- }
+-
+- pa_threaded_mainloop_unlock (g->mainloop);
+- return 0;
+-
+-unlock_and_fail:
+- pa_threaded_mainloop_unlock (g->mainloop);
+- return -1;
+-}
+-
+-static int qpa_simple_write (PAVoiceOut *p, const void *data, size_t length, int *rerror)
++static int qpa_run_out(HWVoiceOut *hw, int live)
+ {
+- paaudio *g = p->g;
+-
+- pa_threaded_mainloop_lock (g->mainloop);
+-
+- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+-
+- while (length > 0) {
+- size_t l;
+- int r;
+-
+- while (!(l = pa_stream_writable_size (p->stream))) {
+- pa_threaded_mainloop_wait (g->mainloop);
+- CHECK_DEAD_GOTO (g, p->stream, rerror, unlock_and_fail);
+- }
+-
+- CHECK_SUCCESS_GOTO (g, rerror, l != (size_t) -1, unlock_and_fail);
+-
+- if (l > length) {
+- l = length;
+- }
+-
+- r = pa_stream_write (p->stream, data, l, NULL, 0LL, PA_SEEK_RELATIVE);
+- CHECK_SUCCESS_GOTO (g, rerror, r >= 0, unlock_and_fail);
+-
+- data = (const uint8_t *) data + l;
+- length -= l;
+- }
+-
+- pa_threaded_mainloop_unlock (g->mainloop);
+- return 0;
+-
+-unlock_and_fail:
+- pa_threaded_mainloop_unlock (g->mainloop);
+- return -1;
+-}
+-
+-static void *qpa_thread_out (void *arg)
+-{
+- PAVoiceOut *pa = arg;
+- HWVoiceOut *hw = &pa->hw;
+-
+- if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+- return NULL;
+- }
++ PAVoiceOut *pa = (PAVoiceOut *) hw;
++ int rpos, decr, samples;
++ size_t avail_bytes, max_bytes;
++ struct st_sample *src;
++ void *pa_dst;
++ int error = 0;
++ int *rerror = &error;
++ int r;
+
+- for (;;) {
+- int decr, to_mix, rpos;
++ decr = 0;
++ rpos = hw->rpos;
+
+- for (;;) {
+- if (pa->done) {
+- goto exit;
+- }
++ pa_threaded_mainloop_lock(pa->g->mainloop);
++ CHECK_DEAD_GOTO(pa->g, pa->stream, rerror, fail);
+
+- if (pa->live > 0) {
+- break;
+- }
++ avail_bytes = (size_t) live << hw->info.shift;
+
+- if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
+- goto exit;
+- }
+- }
++ max_bytes = pa_stream_writable_size(pa->stream);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, max_bytes != -1, fail);
+
+- decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2);
+- rpos = pa->rpos;
++ samples = (int)(audio_MIN(avail_bytes, max_bytes)) >> hw->info.shift;
++ while (samples) {
++ int convert_samples = audio_MIN(samples, hw->samples - rpos);
++ size_t b_wanted = (size_t) convert_samples << hw->info.shift;
++ size_t b_effective = b_wanted;
+
+- if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
+- return NULL;
+- }
++ r = pa_stream_begin_write(pa->stream, &pa_dst, &b_effective);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
++ CHECK_SUCCESS_GOTO(pa->g, (int *)0, b_effective == b_wanted, fail);
+
+- while (to_mix) {
+- int error;
+- int chunk = audio_MIN (to_mix, hw->samples - rpos);
+- struct st_sample *src = hw->mix_buf + rpos;
++ src = hw->mix_buf + rpos;
++ hw->clip(pa_dst, src, convert_samples);
+
+- hw->clip (pa->pcm_buf, src, chunk);
+-
+- if (qpa_simple_write (pa, pa->pcm_buf,
+- chunk << hw->info.shift, &error) < 0) {
+- qpa_logerr (error, "pa_simple_write failed\n");
+- return NULL;
+- }
++ r = pa_stream_write(pa->stream, pa_dst, b_effective,
++ NULL, 0LL, PA_SEEK_RELATIVE);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, r >= 0, fail);
+
+- rpos = (rpos + chunk) % hw->samples;
+- to_mix -= chunk;
+- }
+-
+- if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+- return NULL;
+- }
+-
+- pa->rpos = rpos;
+- pa->live -= decr;
+- pa->decr += decr;
++ rpos = (rpos + convert_samples) % hw->samples;
++ samples -= convert_samples;
++ decr += convert_samples;
+ }
+
+- exit:
+- audio_pt_unlock (&pa->pt, AUDIO_FUNC);
+- return NULL;
+-}
+-
+-static int qpa_run_out (HWVoiceOut *hw, int live)
+-{
+- int decr;
+- PAVoiceOut *pa = (PAVoiceOut *) hw;
+-
+- if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+- return 0;
+- }
++ bail:
++ pa_threaded_mainloop_unlock(pa->g->mainloop);
+
+- decr = audio_MIN (live, pa->decr);
+- pa->decr -= decr;
+- pa->live = live - decr;
+- hw->rpos = pa->rpos;
+- if (pa->live > 0) {
+- audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
+- }
+- else {
+- audio_pt_unlock (&pa->pt, AUDIO_FUNC);
+- }
++ hw->rpos = rpos;
+ return decr;
++
++fail:
++ qpa_logerr(error, "qpa_run_out failed\n");
++ goto bail;
+ }
+
+ static int qpa_write (SWVoiceOut *sw, void *buf, int len)
+@@ -292,92 +165,68 @@ static int qpa_write (SWVoiceOut *sw, void *buf, int len)
+ return audio_pcm_sw_write (sw, buf, len);
+ }
+
+-/* capture */
+-static void *qpa_thread_in (void *arg)
++static int qpa_run_in(HWVoiceIn *hw)
+ {
+- PAVoiceIn *pa = arg;
+- HWVoiceIn *hw = &pa->hw;
++ PAVoiceIn *pa = (PAVoiceIn *) hw;
++ int wpos, incr;
++ char *pa_src;
++ int error = 0;
++ int *rerror = &error;
++ int r;
++ size_t pa_avail;
++ incr = 0;
++ wpos = hw->wpos;
+
+- if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+- return NULL;
+- }
++ pa_threaded_mainloop_lock(pa->g->mainloop);
++ CHECK_DEAD_GOTO(pa->g, pa->stream, rerror, fail);
+
+- for (;;) {
+- int incr, to_grab, wpos;
++ size_t bytes_wanted = ((unsigned int)
++ (hw->samples - audio_pcm_hw_get_live_in(hw)) << hw->info.shift);
+
+- for (;;) {
+- if (pa->done) {
+- goto exit;
+- }
++ if (bytes_wanted == 0) {
++ /* no room */
++ goto bail;
++ }
+
+- if (pa->dead > 0) {
+- break;
+- }
++ size_t bytes_avail = pa_stream_readable_size(pa->stream);
+
+- if (audio_pt_wait (&pa->pt, AUDIO_FUNC)) {
+- goto exit;
+- }
+- }
++ if (bytes_wanted > bytes_avail) {
++ bytes_wanted = bytes_avail;
++ }
+
+- incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2);
+- wpos = pa->wpos;
++ while (bytes_wanted) {
++ r = pa_stream_peek(pa->stream, (const void **)&pa_src, &pa_avail);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
+
+- if (audio_pt_unlock (&pa->pt, AUDIO_FUNC)) {
+- return NULL;
++ if (pa_avail == 0 || pa_avail > bytes_wanted) {
++ break;
+ }
+
+- while (to_grab) {
+- int error;
+- int chunk = audio_MIN (to_grab, hw->samples - wpos);
+- void *buf = advance (pa->pcm_buf, wpos);
++ bytes_wanted -= pa_avail;
+
+- if (qpa_simple_read (pa, buf,
+- chunk << hw->info.shift, &error) < 0) {
+- qpa_logerr (error, "pa_simple_read failed\n");
+- return NULL;
+- }
+-
+- hw->conv (hw->conv_buf + wpos, buf, chunk);
++ while (pa_avail) {
++ int chunk = audio_MIN(
++ (int)(pa_avail >> hw->info.shift), hw->samples - wpos);
++ hw->conv(hw->conv_buf + wpos, pa_src, chunk);
+ wpos = (wpos + chunk) % hw->samples;
+- to_grab -= chunk;
+- }
+-
+- if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+- return NULL;
++ pa_src += chunk << hw->info.shift;
++ pa_avail -= chunk << hw->info.shift;
++ incr += chunk;
+ }
+
+- pa->wpos = wpos;
+- pa->dead -= incr;
+- pa->incr += incr;
++ r = pa_stream_drop(pa->stream);
++ CHECK_SUCCESS_GOTO(pa->g, rerror, r == 0, fail);
+ }
+
+- exit:
+- audio_pt_unlock (&pa->pt, AUDIO_FUNC);
+- return NULL;
+-}
+-
+-static int qpa_run_in (HWVoiceIn *hw)
+-{
+- int live, incr, dead;
+- PAVoiceIn *pa = (PAVoiceIn *) hw;
+-
+- if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) {
+- return 0;
+- }
++bail:
++ pa_threaded_mainloop_unlock(pa->g->mainloop);
+
+- live = audio_pcm_hw_get_live_in (hw);
+- dead = hw->samples - live;
+- incr = audio_MIN (dead, pa->incr);
+- pa->incr -= incr;
+- pa->dead = dead - incr;
+- hw->wpos = pa->wpos;
+- if (pa->dead > 0) {
+- audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
+- }
+- else {
+- audio_pt_unlock (&pa->pt, AUDIO_FUNC);
+- }
++ hw->wpos = wpos;
+ return incr;
++
++fail:
++ qpa_logerr(error, "qpa_run_in failed\n");
++ goto bail;
+ }
+
+ static int qpa_read (SWVoiceIn *sw, void *buf, int len)
+@@ -470,13 +319,6 @@ static void stream_state_cb (pa_stream *s, void * userdata)
+ }
+ }
+
+-static void stream_request_cb (pa_stream *s, size_t length, void *userdata)
+-{
+- paaudio *g = userdata;
+-
+- pa_threaded_mainloop_signal (g->mainloop, 0);
+-}
+-
+ static pa_stream *qpa_simple_new (
+ paaudio *g,
+ const char *name,
+@@ -498,23 +340,17 @@ static pa_stream *qpa_simple_new (
+ }
+
+ pa_stream_set_state_callback (stream, stream_state_cb, g);
+- pa_stream_set_read_callback (stream, stream_request_cb, g);
+- pa_stream_set_write_callback (stream, stream_request_cb, g);
+
+ if (dir == PA_STREAM_PLAYBACK) {
+- r = pa_stream_connect_playback (stream, dev, attr,
+- PA_STREAM_INTERPOLATE_TIMING
+-#ifdef PA_STREAM_ADJUST_LATENCY
+- |PA_STREAM_ADJUST_LATENCY
+-#endif
+- |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
++ r = pa_stream_connect_playback(stream, dev, attr,
++ PA_STREAM_INTERPOLATE_TIMING
++ | (g->conf.adjust_latency_out ? PA_STREAM_ADJUST_LATENCY : 0)
++ | PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
+ } else {
+- r = pa_stream_connect_record (stream, dev, attr,
+- PA_STREAM_INTERPOLATE_TIMING
+-#ifdef PA_STREAM_ADJUST_LATENCY
+- |PA_STREAM_ADJUST_LATENCY
+-#endif
+- |PA_STREAM_AUTO_TIMING_UPDATE);
++ r = pa_stream_connect_record(stream, dev, attr,
++ PA_STREAM_INTERPOLATE_TIMING
++ | (g->conf.adjust_latency_in ? PA_STREAM_ADJUST_LATENCY : 0)
++ | PA_STREAM_AUTO_TIMING_UPDATE);
+ }
+
+ if (r < 0) {
+@@ -541,165 +377,167 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
+ void *drv_opaque)
+ {
+ int error;
+- pa_sample_spec ss;
+- pa_buffer_attr ba;
+ struct audsettings obt_as = *as;
+ PAVoiceOut *pa = (PAVoiceOut *) hw;
+ paaudio *g = pa->g = drv_opaque;
+
+- ss.format = audfmt_to_pa (as->fmt, as->endianness);
+- ss.channels = as->nchannels;
+- ss.rate = as->freq;
+-
+- /*
+- * qemu audio tick runs at 100 Hz (by default), so processing
+- * data chunks worth 10 ms of sound should be a good fit.
+- */
+- ba.tlength = pa_usec_to_bytes (10 * 1000, &ss);
+- ba.minreq = pa_usec_to_bytes (5 * 1000, &ss);
+- ba.maxlength = -1;
+- ba.prebuf = -1;
+-
+- obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
+-
+- pa->stream = qpa_simple_new (
+- g,
+- "qemu",
+- PA_STREAM_PLAYBACK,
+- g->conf.sink,
+- &ss,
+- NULL, /* channel map */
+- &ba, /* buffering attributes */
+- &error
+- );
++ int64_t timer_tick_duration =
++ audio_MAX(audio_get_timer_ticks(), 1 * SCALE_MS);
++ int64_t frames_per_tick_x1000 =
++ ((timer_tick_duration * as->freq * 1000LL) / NANOSECONDS_PER_SECOND);
++
++ int64_t tlength = g->conf.tlength;
++ if (tlength == 0) {
++ tlength = (frames_per_tick_x1000) / 400;
++ }
++ int64_t buflen = g->conf.buffer_size_out;
++ if (buflen == 0) {
++ buflen = frames_per_tick_x1000 / 400;
++ }
++
++ ldebug("tick duration: %.2f ms (%.3f frames)\n",
++ ((float)timer_tick_duration) / SCALE_MS,
++ (float)frames_per_tick_x1000 / 1000.0f);
++
++ ldebug("OUT internal buffer: %.2f ms (%"PRId64" frames)\n",
++ buflen * (1000.0f / as->freq),
++ buflen);
++
++ ldebug("OUT tlength: %.2f ms (%"PRId64" frames)\n",
++ tlength * (1000.0f / as->freq),
++ tlength);
++
++ ldebug("OUT adjust latency: %s\n",
++ g->conf.adjust_latency_out ? "yes" : "no");
++
++ pa->ss.format = audfmt_to_pa(as->fmt, as->endianness);
++ pa->ss.channels = as->nchannels;
++ pa->ss.rate = as->freq;
++
++ pa->ba.tlength = tlength * pa_frame_size(&pa->ss);
++ pa->ba.maxlength = -1;
++ pa->ba.minreq = -1;
++ pa->ba.prebuf = -1;
++
++ obt_as.fmt = pa_to_audfmt(pa->ss.format, &obt_as.endianness);
++
++ pa->stream = qpa_simple_new(
++ g,
++ "qemu",
++ PA_STREAM_PLAYBACK,
++ g->conf.sink,
++ &pa->ss,
++ NULL, /* channel map */
++ &pa->ba, /* buffering attributes */
++ &error
++ );
+ if (!pa->stream) {
+ qpa_logerr (error, "pa_simple_new for playback failed\n");
+ goto fail1;
+ }
+
+- audio_pcm_init_info (&hw->info, &obt_as);
+- hw->samples = g->conf.samples;
+- pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+- pa->rpos = hw->rpos;
+- if (!pa->pcm_buf) {
+- dolog ("Could not allocate buffer (%d bytes)\n",
+- hw->samples << hw->info.shift);
+- goto fail2;
+- }
+-
+- if (audio_pt_init (&pa->pt, qpa_thread_out, hw, AUDIO_CAP, AUDIO_FUNC)) {
+- goto fail3;
+- }
++ audio_pcm_init_info(&hw->info, &obt_as);
++ hw->samples = buflen;
+
+ return 0;
+
+- fail3:
+- g_free (pa->pcm_buf);
+- pa->pcm_buf = NULL;
+- fail2:
+- if (pa->stream) {
+- pa_stream_unref (pa->stream);
+- pa->stream = NULL;
+- }
+- fail1:
++fail1:
+ return -1;
+ }
+
+ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
+ {
+ int error;
+- pa_sample_spec ss;
+ struct audsettings obt_as = *as;
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+ paaudio *g = pa->g = drv_opaque;
+
+- ss.format = audfmt_to_pa (as->fmt, as->endianness);
+- ss.channels = as->nchannels;
+- ss.rate = as->freq;
+-
+- obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness);
+-
+- pa->stream = qpa_simple_new (
+- g,
+- "qemu",
+- PA_STREAM_RECORD,
+- g->conf.source,
+- &ss,
+- NULL, /* channel map */
+- NULL, /* buffering attributes */
+- &error
+- );
++ int64_t timer_tick_duration =
++ audio_MAX(audio_get_timer_ticks(), 1 * SCALE_MS);
++ int64_t frames_per_tick_x1000 =
++ ((timer_tick_duration * as->freq * 1000LL) / NANOSECONDS_PER_SECOND);
++
++ int64_t fragsize = g->conf.fragsize;
++ if (fragsize == 0) {
++ fragsize = frames_per_tick_x1000 / 1000;
++ }
++ int64_t buflen = g->conf.buffer_size_in;
++ if (buflen == 0) {
++ buflen = frames_per_tick_x1000 / 400;
++ }
++ int64_t maxlength = g->conf.maxlength_in;
++ if (maxlength == 0) {
++ maxlength = fragsize * 2;
++ }
++
++ ldebug("IN internal buffer: %.2f ms (%"PRId64" frames)\n",
++ buflen * (1000.0f / as->freq),
++ buflen);
++
++ ldebug("IN fragsize: %.2f ms (%"PRId64" frames)\n",
++ fragsize * (1000.0f / as->freq),
++ fragsize);
++
++ ldebug("IN maxlength: %.2f ms (%"PRId64" frames)\n",
++ maxlength * (1000.0f / as->freq),
++ maxlength);
++
++ ldebug("IN adjust latency: %s\n",
++ g->conf.adjust_latency_in ? "yes" : "no");
++
++ pa->ss.format = audfmt_to_pa(as->fmt, as->endianness);
++ pa->ss.channels = as->nchannels;
++ pa->ss.rate = as->freq;
++
++ pa->ba.fragsize = fragsize * pa_frame_size(&pa->ss);
++ pa->ba.maxlength = maxlength * pa_frame_size(&pa->ss);
++ pa->ba.minreq = -1;
++ pa->ba.prebuf = -1;
++
++ obt_as.fmt = pa_to_audfmt(pa->ss.format, &obt_as.endianness);
++
++ pa->stream = qpa_simple_new(
++ g,
++ "qemu",
++ PA_STREAM_RECORD,
++ g->conf.source,
++ &pa->ss,
++ NULL, /* channel map */
++ &pa->ba, /* buffering attributes */
++ &error
++ );
+ if (!pa->stream) {
+ qpa_logerr (error, "pa_simple_new for capture failed\n");
+ goto fail1;
+ }
+
+- audio_pcm_init_info (&hw->info, &obt_as);
+- hw->samples = g->conf.samples;
+- pa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+- pa->wpos = hw->wpos;
+- if (!pa->pcm_buf) {
+- dolog ("Could not allocate buffer (%d bytes)\n",
+- hw->samples << hw->info.shift);
+- goto fail2;
+- }
+-
+- if (audio_pt_init (&pa->pt, qpa_thread_in, hw, AUDIO_CAP, AUDIO_FUNC)) {
+- goto fail3;
+- }
++ audio_pcm_init_info(&hw->info, &obt_as);
++ hw->samples = buflen;
+
+ return 0;
+
+- fail3:
+- g_free (pa->pcm_buf);
+- pa->pcm_buf = NULL;
+- fail2:
+- if (pa->stream) {
+- pa_stream_unref (pa->stream);
+- pa->stream = NULL;
+- }
+- fail1:
++ fail1:
+ return -1;
+ }
+
+ static void qpa_fini_out (HWVoiceOut *hw)
+ {
+- void *ret;
+ PAVoiceOut *pa = (PAVoiceOut *) hw;
+
+- audio_pt_lock (&pa->pt, AUDIO_FUNC);
+- pa->done = 1;
+- audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
+- audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
+-
+ if (pa->stream) {
+ pa_stream_unref (pa->stream);
+ pa->stream = NULL;
+ }
+-
+- audio_pt_fini (&pa->pt, AUDIO_FUNC);
+- g_free (pa->pcm_buf);
+- pa->pcm_buf = NULL;
+ }
+
+ static void qpa_fini_in (HWVoiceIn *hw)
+ {
+- void *ret;
+ PAVoiceIn *pa = (PAVoiceIn *) hw;
+
+- audio_pt_lock (&pa->pt, AUDIO_FUNC);
+- pa->done = 1;
+- audio_pt_unlock_and_signal (&pa->pt, AUDIO_FUNC);
+- audio_pt_join (&pa->pt, &ret, AUDIO_FUNC);
+-
+ if (pa->stream) {
+ pa_stream_unref (pa->stream);
+ pa->stream = NULL;
+ }
+-
+- audio_pt_fini (&pa->pt, AUDIO_FUNC);
+- g_free (pa->pcm_buf);
+- pa->pcm_buf = NULL;
+ }
+
+ static int qpa_ctl_out (HWVoiceOut *hw, int cmd, ...)
+@@ -809,7 +647,8 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...)
+
+ /* common */
+ static PAConf glob_conf = {
+- .samples = 4096,
++ .adjust_latency_out = 0,
++ .adjust_latency_in = 1,
+ };
+
+ static void *qpa_audio_init (void)
+@@ -897,10 +736,46 @@ static void qpa_audio_fini (void *opaque)
+
+ struct audio_option qpa_options[] = {
+ {
+- .name = "SAMPLES",
++ .name = "BUFFER_SIZE_OUT",
++ .tag = AUD_OPT_INT,
++ .valp = &glob_conf.buffer_size_out,
++ .descr = "internal buffer size in frames for playback device"
++ },
++ {
++ .name = "BUFFER_SIZE_IN",
++ .tag = AUD_OPT_INT,
++ .valp = &glob_conf.buffer_size_in,
++ .descr = "internal buffer size in frames for recording device"
++ },
++ {
++ .name = "TLENGTH",
+ .tag = AUD_OPT_INT,
+- .valp = &glob_conf.samples,
+- .descr = "buffer size in samples"
++ .valp = &glob_conf.tlength,
++ .descr = "playback buffer target length in frames"
++ },
++ {
++ .name = "FRAGSIZE",
++ .tag = AUD_OPT_INT,
++ .valp = &glob_conf.fragsize,
++ .descr = "fragment length of recording device in frames"
++ },
++ {
++ .name = "MAXLENGTH_IN",
++ .tag = AUD_OPT_INT,
++ .valp = &glob_conf.maxlength_in,
++ .descr = "maximum length of PA recording buffer in frames"
++ },
++ {
++ .name = "ADJUST_LATENCY_OUT",
++ .tag = AUD_OPT_BOOL,
++ .valp = &glob_conf.adjust_latency_out,
++ .descr = "instruct PA to adjust latency for playback device"
++ },
++ {
++ .name = "ADJUST_LATENCY_IN",
++ .tag = AUD_OPT_BOOL,
++ .valp = &glob_conf.adjust_latency_in,
++ .descr = "instruct PA to adjust latency for recording device"
+ },
+ {
+ .name = "SERVER",
+diff --git hw/audio/hda-codec.c hw/audio/hda-codec.c
+index 5402cd196c..ab89158bfc 100644
+--- hw/audio/hda-codec.c
++++ hw/audio/hda-codec.c
+@@ -18,6 +18,7 @@
+ */
+
+ #include "qemu/osdep.h"
++#include "qemu/atomic.h"
+ #include "hw/hw.h"
+ #include "hw/pci/pci.h"
+ #include "intel-hda.h"
+@@ -126,6 +127,11 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as)
+ #define PARAM nomixemu
+ #include "hda-codec-common.h"
+
++#define HDA_TIMER_TICKS (SCALE_MS)
++#define MAX_CORR (SCALE_US * 100)
++#define B_SIZE sizeof(st->buf)
++#define B_MASK (sizeof(st->buf) - 1)
++
+ /* -------------------------------------------------------------------------- */
+
+ static const char *fmt2name[] = {
+@@ -154,8 +160,13 @@ struct HDAAudioStream {
+ SWVoiceIn *in;
+ SWVoiceOut *out;
+ } voice;
+- uint8_t buf[HDA_BUFFER_SIZE];
+- uint32_t bpos;
++ uint8_t compat_buf[HDA_BUFFER_SIZE];
++ uint32_t compat_bpos;
++ uint8_t buf[8192]; /* size must be power of two */
++ int64_t rpos;
++ int64_t wpos;
++ QEMUTimer *buft;
++ int64_t buft_start;
+ };
+
+ #define TYPE_HDA_AUDIO "hda-audio"
+@@ -176,53 +187,146 @@ struct HDAAudioState {
+ bool mixer;
+ };
+
++static inline int64_t hda_bytes_per_second(HDAAudioStream *st)
++{
++ return 2 * st->as.nchannels * st->as.freq;
++}
++
++static inline void hda_timer_sync_adjust(HDAAudioStream *st, int64_t target_pos)
++{
++ int64_t corr =
++ NANOSECONDS_PER_SECOND * target_pos / hda_bytes_per_second(st);
++ if (corr > MAX_CORR) {
++ corr = MAX_CORR;
++ } else if (corr < -MAX_CORR) {
++ corr = -MAX_CORR;
++ }
++ atomic_fetch_add(&st->buft_start, corr);
++}
++
++static void hda_audio_input_timer(void *opaque)
++{
++ HDAAudioStream *st = opaque;
++
++ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
++
++ int64_t buft_start = atomic_fetch_add(&st->buft_start, 0);
++ int64_t wpos = atomic_fetch_add(&st->wpos, 0);
++ int64_t rpos = atomic_fetch_add(&st->rpos, 0);
++
++ int64_t wanted_rpos = hda_bytes_per_second(st) * (now - buft_start)
++ / NANOSECONDS_PER_SECOND;
++ wanted_rpos &= -4; /* IMPORTANT! clip to frames */
++
++ if (wanted_rpos <= rpos) {
++ /* we already transmitted the data */
++ goto out_timer;
++ }
++
++ int64_t to_transfer = audio_MIN(wpos - rpos, wanted_rpos - rpos);
++ while (to_transfer) {
++ uint32_t start = (rpos & B_MASK);
++ uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
++ int rc = hda_codec_xfer(
++ &st->state->hda, st->stream, false, st->buf + start, chunk);
++ if (!rc) {
++ break;
++ }
++ rpos += chunk;
++ to_transfer -= chunk;
++ atomic_fetch_add(&st->rpos, chunk);
++ }
++
++out_timer:
++
++ if (st->running) {
++ timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
++ }
++}
++
+ static void hda_audio_input_cb(void *opaque, int avail)
+ {
+ HDAAudioStream *st = opaque;
+- int recv = 0;
+- int len;
+- bool rc;
+-
+- while (avail - recv >= sizeof(st->buf)) {
+- if (st->bpos != sizeof(st->buf)) {
+- len = AUD_read(st->voice.in, st->buf + st->bpos,
+- sizeof(st->buf) - st->bpos);
+- st->bpos += len;
+- recv += len;
+- if (st->bpos != sizeof(st->buf)) {
+- break;
+- }
++
++ int64_t wpos = atomic_fetch_add(&st->wpos, 0);
++ int64_t rpos = atomic_fetch_add(&st->rpos, 0);
++
++ int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), avail);
++
++ hda_timer_sync_adjust(st, -((wpos - rpos) + to_transfer - (B_SIZE >> 1)));
++
++ while (to_transfer) {
++ uint32_t start = (uint32_t) (wpos & B_MASK);
++ uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer);
++ uint32_t read = AUD_read(st->voice.in, st->buf + start, chunk);
++ wpos += read;
++ to_transfer -= read;
++ atomic_fetch_add(&st->wpos, read);
++ if (chunk != read) {
++ break;
+ }
+- rc = hda_codec_xfer(&st->state->hda, st->stream, false,
+- st->buf, sizeof(st->buf));
++ }
++}
++
++static void hda_audio_output_timer(void *opaque)
++{
++ HDAAudioStream *st = opaque;
++
++ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
++
++ int64_t buft_start = atomic_fetch_add(&st->buft_start, 0);
++ int64_t wpos = atomic_fetch_add(&st->wpos, 0);
++ int64_t rpos = atomic_fetch_add(&st->rpos, 0);
++
++ int64_t wanted_wpos = hda_bytes_per_second(st) * (now - buft_start)
++ / NANOSECONDS_PER_SECOND;
++ wanted_wpos &= -4; /* IMPORTANT! clip to frames */
++
++ if (wanted_wpos <= wpos) {
++ /* we already received the data */
++ goto out_timer;
++ }
++
++ int64_t to_transfer = audio_MIN(B_SIZE - (wpos - rpos), wanted_wpos - wpos);
++ while (to_transfer) {
++ uint32_t start = (wpos & B_MASK);
++ uint32_t chunk = audio_MIN(B_SIZE - start, to_transfer);
++ int rc = hda_codec_xfer(
++ &st->state->hda, st->stream, true, st->buf + start, chunk);
+ if (!rc) {
+ break;
+ }
+- st->bpos = 0;
++ wpos += chunk;
++ to_transfer -= chunk;
++ atomic_fetch_add(&st->wpos, chunk);
++ }
++
++out_timer:
++
++ if (st->running) {
++ timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
+ }
+ }
+
+ static void hda_audio_output_cb(void *opaque, int avail)
+ {
+ HDAAudioStream *st = opaque;
+- int sent = 0;
+- int len;
+- bool rc;
+-
+- while (avail - sent >= sizeof(st->buf)) {
+- if (st->bpos == sizeof(st->buf)) {
+- rc = hda_codec_xfer(&st->state->hda, st->stream, true,
+- st->buf, sizeof(st->buf));
+- if (!rc) {
+- break;
+- }
+- st->bpos = 0;
+- }
+- len = AUD_write(st->voice.out, st->buf + st->bpos,
+- sizeof(st->buf) - st->bpos);
+- st->bpos += len;
+- sent += len;
+- if (st->bpos != sizeof(st->buf)) {
++
++ int64_t wpos = atomic_fetch_add(&st->wpos, 0);
++ int64_t rpos = atomic_fetch_add(&st->rpos, 0);
++
++ int64_t to_transfer = audio_MIN(wpos - rpos, avail);
++
++ hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1));
++
++ while (to_transfer) {
++ uint32_t start = (uint32_t) (rpos & B_MASK);
++ uint32_t chunk = (uint32_t) audio_MIN(B_SIZE - start, to_transfer);
++ uint32_t written = AUD_write(st->voice.out, st->buf + start, chunk);
++ rpos += written;
++ to_transfer -= written;
++ atomic_fetch_add(&st->rpos, written);
++ if (chunk != written) {
+ break;
+ }
+ }
+@@ -239,6 +343,15 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running)
+ st->running = running;
+ dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name,
+ st->running ? "on" : "off", st->stream);
++ if (running) {
++ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
++ st->rpos = 0;
++ st->wpos = 0;
++ st->buft_start = now;
++ timer_mod_anticipate_ns(st->buft, now + HDA_TIMER_TICKS);
++ } else {
++ timer_del(st->buft);
++ }
+ if (st->output) {
+ AUD_set_active_out(st->voice.out, st->running);
+ } else {
+@@ -286,10 +399,12 @@ static void hda_audio_setup(HDAAudioStream *st)
+ st->voice.out = AUD_open_out(&st->state->card, st->voice.out,
+ st->node->name, st,
+ hda_audio_output_cb, &st->as);
++ st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, hda_audio_output_timer, st);
+ } else {
+ st->voice.in = AUD_open_in(&st->state->card, st->voice.in,
+ st->node->name, st,
+ hda_audio_input_cb, &st->as);
++ st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, hda_audio_input_timer, st);
+ }
+ }
+
+@@ -505,7 +620,6 @@ static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc)
+ /* unmute output by default */
+ st->gain_left = QEMU_HDA_AMP_STEPS;
+ st->gain_right = QEMU_HDA_AMP_STEPS;
+- st->bpos = sizeof(st->buf);
+ st->output = true;
+ } else {
+ st->output = false;
+@@ -532,6 +646,7 @@ static void hda_audio_exit(HDACodecDevice *hda)
+ if (st->node == NULL) {
+ continue;
+ }
++ timer_del(st->buft);
+ if (st->output) {
+ AUD_close_out(&a->card, st->voice.out);
+ } else {
+@@ -592,8 +707,8 @@ static const VMStateDescription vmstate_hda_audio_stream = {
+ VMSTATE_UINT32(gain_right, HDAAudioStream),
+ VMSTATE_BOOL(mute_left, HDAAudioStream),
+ VMSTATE_BOOL(mute_right, HDAAudioStream),
+- VMSTATE_UINT32(bpos, HDAAudioStream),
+- VMSTATE_BUFFER(buf, HDAAudioStream),
++ VMSTATE_UINT32(compat_bpos, HDAAudioStream),
++ VMSTATE_BUFFER(compat_buf, HDAAudioStream),
+ VMSTATE_END_OF_LIST()
+ }
+ };
+diff --git hw/audio/intel-hda.c hw/audio/intel-hda.c
+index 18a50a8f83..721eba792d 100644
+--- hw/audio/intel-hda.c
++++ hw/audio/intel-hda.c
+@@ -407,13 +407,6 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output,
+ if (st->bpl == NULL) {
+ return false;
+ }
+- if (st->ctl & (1 << 26)) {
+- /*
+- * Wait with the next DMA xfer until the guest
+- * has acked the buffer completion interrupt
+- */
+- return false;
+- }
+
+ left = len;
+ s = st->bentries;
diff --git a/cpu-pinning.patch b/cpu-pinning.patch
new file mode 100644
index 000000000000..46efee5d88a5
--- /dev/null
+++ b/cpu-pinning.patch
@@ -0,0 +1,186 @@
+From e392e5516e6ae66db0f05775a22c0abf39f033f0 Mon Sep 17 00:00:00 2001
+From: Saverio Miroddi <saverio.pub2@gmail.com>
+Date: Tue, 31 Oct 2017 20:59:05 +0100
+Subject: [PATCH] Current pinning patch
+
+Changes 2017/10/31:
+
+- Fix: the MAX_VCPUS was arbitrary; it's now set to CPU_SETSIZE
+- Fix: the allowed vcpus were equated to the cores number, without accounting sockets and threads
+- Change: removed all the debug information, and a now unneded warning
+- Change: cleaned spacing
+---
+ cpus.c | 12 +++++++++++
+ qemu-options.hx | 10 ++++++++++
+ vl.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 84 insertions(+)
+
+diff --git a/cpus.c b/cpus.c
+index 9bed61eefcc..7437e3a00c7 100644
+--- a/cpus.c
++++ b/cpus.c
+@@ -55,6 +55,9 @@
+ #ifdef CONFIG_LINUX
+
+ #include <sys/prctl.h>
++#include <unistd.h>
++#include <stdint.h>
++#include <inttypes.h>
+
+ #ifndef PR_MCE_KILL
+ #define PR_MCE_KILL 33
+@@ -1722,9 +1725,11 @@ static void qemu_hax_start_vcpu(CPUState *cpu)
+ }
+ }
+
++extern int vcpu_affinity[];
+ static void qemu_kvm_start_vcpu(CPUState *cpu)
+ {
+ char thread_name[VCPU_THREAD_NAME_SIZE];
++ cpu_set_t cpuset;
+
+ cpu->thread = g_malloc0(sizeof(QemuThread));
+ cpu->halt_cond = g_malloc0(sizeof(QemuCond));
+@@ -1733,6 +1738,13 @@ static void qemu_kvm_start_vcpu(CPUState *cpu)
+ cpu->cpu_index);
+ qemu_thread_create(cpu->thread, thread_name, qemu_kvm_cpu_thread_fn,
+ cpu, QEMU_THREAD_JOINABLE);
++
++ if (vcpu_affinity[cpu->cpu_index] != -1) {
++ CPU_ZERO(&cpuset);
++ CPU_SET(vcpu_affinity[cpu->cpu_index], &cpuset);
++ pthread_setaffinity_np((cpu->thread)->thread, sizeof(cpu_set_t), &cpuset);
++ }
++
+ while (!cpu->created) {
+ qemu_cond_wait(&qemu_cpu_cond, &qemu_global_mutex);
+ }
+diff --git a/qemu-options.hx b/qemu-options.hx
+index 9f6e2adfffb..1d38fc86c81 100644
+--- a/qemu-options.hx
++++ b/qemu-options.hx
+@@ -160,6 +160,16 @@ given, the total number of CPUs @var{n} can be omitted. @var{maxcpus}
+ specifies the maximum number of hotpluggable CPUs.
+ ETEXI
+
++DEF("vcpu", HAS_ARG, QEMU_OPTION_vcpu,
++ "-vcpu [vcpunum=]n[,affinity=affinity]\n"
++ "-vcpu [vcpunum=]n[,affinity=affinity]\n", QEMU_ARCH_ALL)
++STEXI
++@item -vcpu [vcpunum=]@var{n}[,affinity=@var{affinity}]
++@itemx -vcpu [vcpunum=]@var{n}[,affinity=@var{affinity}]
++@findex -vcpu
++VCPU Affinity. If specified, specify for all the CPUs.
++ETEXI
++
+ DEF("numa", HAS_ARG, QEMU_OPTION_numa,
+ "-numa node[,mem=size][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n"
+ "-numa node[,memdev=id][,cpus=firstcpu[-lastcpu]][,nodeid=node]\n"
+diff --git a/vl.c b/vl.c
+index d63269332fe..754a03c9a5f 100644
+--- a/vl.c
++++ b/vl.c
+@@ -135,6 +135,7 @@ int main(int argc, char **argv)
+ #define MAX_VIRTIO_CONSOLES 1
+ #define MAX_SCLP_CONSOLES 1
+
++#define MAX_VCPUS CPU_SETSIZE
+ static const char *data_dir[16];
+ static int data_dir_idx;
+ const char *bios_name = NULL;
+@@ -167,6 +168,8 @@ int smp_cpus = 1;
+ int max_cpus = 1;
+ int smp_cores = 1;
+ int smp_threads = 1;
++int vcpu_affinity[MAX_VCPUS];
++int num_affinity = 0;
+ int acpi_enabled = 1;
+ int no_hpet = 0;
+ int fd_bootchk = 1;
+@@ -1212,6 +1215,57 @@ static QemuOptsList qemu_smp_opts = {
+ },
+ };
+
++static QemuOptsList qemu_vcpu_opts = {
++ .name = "vcpu-opts",
++ .implied_opt_name = "vcpunum",
++ .head = QTAILQ_HEAD_INITIALIZER(qemu_vcpu_opts.head),
++ .desc = {
++ {
++ .name = "vcpunum",
++ .type = QEMU_OPT_NUMBER,
++ }, {
++ .name = "affinity",
++ .type = QEMU_OPT_NUMBER,
++ },
++ { /*End of list */ }
++ },
++};
++
++static int parse_vcpu(void *opaque, QemuOpts *opts, Error **errp)
++{
++ if (opts) {
++ unsigned vcpu = qemu_opt_get_number(opts, "vcpunum", 0);
++ unsigned affinity = qemu_opt_get_number(opts,"affinity", 0);
++
++ if (vcpu < smp_cpus * smp_cores * smp_threads) {
++ if (vcpu_affinity[vcpu] == -1) {
++ vcpu_affinity[vcpu] = affinity;
++ }
++ else {
++ error_report("Duplicate affinity statement for vcpu %d\n", vcpu);
++ return -1;
++ }
++ num_affinity += 1;
++ }
++ else {
++ error_report("VCPU %d is more than allowed %d VCPUs in the system\n", vcpu, smp_cores);
++ return -1;
++ }
++ }
++ return 0;
++}
++
++static void parse_vcpu_opts(MachineClass *mc)
++{
++ int i;
++ for (i = 0; i < MAX_VCPUS; i++)
++ vcpu_affinity[i] = -1;
++
++ if (qemu_opts_foreach(qemu_find_opts("vcpu-opts"), parse_vcpu, NULL, NULL)) {
++ exit(1);
++ }
++}
++
+ static void smp_parse(QemuOpts *opts)
+ {
+ if (opts) {
+@@ -3067,6 +3121,7 @@ int main(int argc, char **argv, char **envp)
+ qemu_add_opts(&qemu_accel_opts);
+ qemu_add_opts(&qemu_mem_opts);
+ qemu_add_opts(&qemu_smp_opts);
++ qemu_add_opts(&qemu_vcpu_opts);
+ qemu_add_opts(&qemu_boot_opts);
+ qemu_add_opts(&qemu_sandbox_opts);
+ qemu_add_opts(&qemu_add_fd_opts);
+@@ -3818,6 +3873,12 @@ int main(int argc, char **argv, char **envp)
+ exit(1);
+ }
+ break;
++ case QEMU_OPTION_vcpu:
++ if (!qemu_opts_parse_noisily(qemu_find_opts("vcpu-opts"),
++ optarg, true)) {
++ exit(1);
++ }
++ break;
+ case QEMU_OPTION_vnc:
+ vnc_parse(optarg, &error_fatal);
+ break;
+@@ -4243,6 +4304,7 @@ int main(int argc, char **argv, char **envp)
+ exit(1);
+ }
+
++ parse_vcpu_opts(machine_class);
+ /*
+ * Get the default machine options from the machine if it is not already
+ * specified either by the configuration file or by the command line.
diff --git a/qemu-ga.service b/qemu-ga.service
new file mode 100644
index 000000000000..abbb6ab9dfe2
--- /dev/null
+++ b/qemu-ga.service
@@ -0,0 +1,9 @@
+[Unit]
+Description=QEMU Guest Agent
+ConditionPathExists=/dev/virtio-ports/org.qemu.guest_agent.0
+
+[Service]
+ExecStart=/usr/bin/qemu-ga
+
+[Install]
+WantedBy=multi-user.target
diff --git a/qemu.install b/qemu.install
new file mode 100644
index 000000000000..9fe850d4ed7e
--- /dev/null
+++ b/qemu.install
@@ -0,0 +1,8 @@
+# Arg 1: the new package version
+post_install() {
+ # trigger events on modules files when already loaded
+ for _f in /sys/devices/virtual/misc/{kvm,vhost-net}; do
+ [[ -e "$_f" ]] && udevadm trigger "$_f"
+ done
+ :
+}
diff --git a/qemu.sysusers b/qemu.sysusers
new file mode 100644
index 000000000000..4c03e8242d92
--- /dev/null
+++ b/qemu.sysusers
@@ -0,0 +1 @@
+g kvm 78 -