From f28d0dfdce5754d80d2a5993fff2f5312b32cac1 Mon Sep 17 00:00:00 2001 From: "Emilio G. Cota" Date: Fri, 22 Jun 2018 13:45:31 -0400 Subject: [PATCH 1/8] tcg: fix --disable-tcg build breakage Fix the --disable-tcg breakage introduced by tb_lock's removal by relying on the fact that tcg_enabled() is set to 0 at compile-time under --disable-tcg. While at it, add further asserts to fix builds that enable both --disable-tcg and --enable-debug, which were broken even before tb_lock's removal. Tested to build x86_64-softmmu and i386-softmmu targets. Reported-by: Peter Maydell Signed-off-by: Emilio G. Cota Reviewed-by: Peter Maydell Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- accel/stubs/tcg-stub.c | 4 ---- cpus.c | 4 ++++ exec.c | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index ee575a8718..76ae461749 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -21,10 +21,6 @@ void tb_flush(CPUState *cpu) { } -void tb_unlock(void) -{ -} - void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { } diff --git a/cpus.c b/cpus.c index 19c5d37108..b5844b7103 100644 --- a/cpus.c +++ b/cpus.c @@ -1355,6 +1355,7 @@ static int tcg_cpu_exec(CPUState *cpu) int64_t ti; #endif + assert(tcg_enabled()); #ifdef CONFIG_PROFILER ti = profile_getclock(); #endif @@ -1397,6 +1398,7 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) { CPUState *cpu = arg; + assert(tcg_enabled()); rcu_register_thread(); tcg_register_thread(); @@ -1631,6 +1633,7 @@ static void *qemu_tcg_cpu_thread_fn(void *arg) { CPUState *cpu = arg; + assert(tcg_enabled()); g_assert(!use_icount); rcu_register_thread(); @@ -1854,6 +1857,7 @@ static void qemu_tcg_init_vcpu(CPUState *cpu) static QemuThread *single_tcg_cpu_thread; static int tcg_region_inited; + assert(tcg_enabled()); /* * Initialize TCG regions--once. Now is a good time, because: * (1) TCG's init context, prologue and target globals have been set up. diff --git a/exec.c b/exec.c index 28f9bdcbf9..88edb59060 100644 --- a/exec.c +++ b/exec.c @@ -1323,6 +1323,7 @@ static void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length) RAMBlock *block; ram_addr_t end; + assert(tcg_enabled()); end = TARGET_PAGE_ALIGN(start + length); start &= TARGET_PAGE_MASK; @@ -2655,6 +2656,7 @@ void memory_notdirty_write_prepare(NotDirtyInfo *ndi, void memory_notdirty_write_complete(NotDirtyInfo *ndi) { if (ndi->pages) { + assert(tcg_enabled()); page_collection_unlock(ndi->pages); ndi->pages = NULL; } @@ -3046,6 +3048,7 @@ static void tcg_commit(MemoryListener *listener) CPUAddressSpace *cpuas; AddressSpaceDispatch *d; + assert(tcg_enabled()); /* since each CPU stores ram addresses in its TLB cache, we must reset the modified entries */ cpuas = container_of(listener, CPUAddressSpace, tcg_as_listener); From bf20b675cc5a2568f1105c3577663978c5a13108 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 21 Jun 2018 14:54:51 -0300 Subject: [PATCH 2/8] Partially revert "python: futurize -f libfuturize.fixes.fix_absolute_import" Since commit 068cf7a44cd4d65c05aa877dbebced295be5ce44, qmp-shell is broken: $ ./scripts/qmp/qmp-shell Traceback (most recent call last): File "./scripts/qmp/qmp-shell", line 70, in from . import qmp ValueError: Attempted relative import in non-package Relative imports don't work on scripts that are executed directly, so revert the change on the scripts inside scripts/qmp. Fixes: 068cf7a44cd4d65c05aa877dbebced295be5ce44 Reported-by: John Snow Signed-off-by: Eduardo Habkost Message-Id: <20180621175451.7948-1-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/qmp/qemu-ga-client | 3 +-- scripts/qmp/qmp | 3 +-- scripts/qmp/qmp-shell | 3 +-- scripts/qmp/qom-fuse | 3 +-- scripts/qmp/qom-get | 3 +-- scripts/qmp/qom-list | 3 +-- scripts/qmp/qom-set | 3 +-- scripts/qmp/qom-tree | 3 +-- 8 files changed, 8 insertions(+), 16 deletions(-) diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index 976e69e05f..e8cb7646a0 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -37,11 +37,10 @@ # from __future__ import print_function -from __future__ import absolute_import import base64 import random -from . import qmp +import qmp class QemuGuestAgent(qmp.QEMUMonitorProtocol): diff --git a/scripts/qmp/qmp b/scripts/qmp/qmp index 33a0d6b73a..6cb46fdae2 100755 --- a/scripts/qmp/qmp +++ b/scripts/qmp/qmp @@ -11,9 +11,8 @@ # See the COPYING file in the top-level directory. from __future__ import print_function -from __future__ import absolute_import import sys, os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol def print_response(rsp, prefix=[]): if type(rsp) == list: diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index 26418dab95..a42306dd89 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -66,8 +66,7 @@ # sent to QEMU, which is useful for debugging and documentation generation. from __future__ import print_function -from __future__ import absolute_import -from . import qmp +import qmp import json import ast import readline diff --git a/scripts/qmp/qom-fuse b/scripts/qmp/qom-fuse index e524e798fc..4d85970a78 100755 --- a/scripts/qmp/qom-fuse +++ b/scripts/qmp/qom-fuse @@ -11,12 +11,11 @@ # the COPYING file in the top-level directory. ## -from __future__ import absolute_import import fuse, stat from fuse import Fuse import os, posix from errno import * -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol fuse.fuse_python_api = (0, 2) diff --git a/scripts/qmp/qom-get b/scripts/qmp/qom-get index a3f5d7660e..ec5275d53a 100755 --- a/scripts/qmp/qom-get +++ b/scripts/qmp/qom-get @@ -12,10 +12,9 @@ ## from __future__ import print_function -from __future__ import absolute_import import sys import os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-list b/scripts/qmp/qom-list index 2ba25e1792..0f97440973 100755 --- a/scripts/qmp/qom-list +++ b/scripts/qmp/qom-list @@ -12,10 +12,9 @@ ## from __future__ import print_function -from __future__ import absolute_import import sys import os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-set b/scripts/qmp/qom-set index 0352668812..26ed9e3263 100755 --- a/scripts/qmp/qom-set +++ b/scripts/qmp/qom-set @@ -12,10 +12,9 @@ ## from __future__ import print_function -from __future__ import absolute_import import sys import os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None diff --git a/scripts/qmp/qom-tree b/scripts/qmp/qom-tree index 32e708a13e..31603c681f 100755 --- a/scripts/qmp/qom-tree +++ b/scripts/qmp/qom-tree @@ -14,10 +14,9 @@ ## from __future__ import print_function -from __future__ import absolute_import import sys import os -from .qmp import QEMUMonitorProtocol +from qmp import QEMUMonitorProtocol cmd, args = sys.argv[0], sys.argv[1:] socket_path = None From 280c1e1cdb24d80ecdfcdfc679ccc5e8ed7af45d Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:11:56 +0200 Subject: [PATCH 3/8] audio/hda: create millisecond timers that handle IO Currently, the HDA device tries to sync itself with the QEMU audio backend by waiting for the guest driver to handle buffer completion interrupts. This causes the backend to often read too much data from the device, as well as running out of data whenever the guest takes too long to handle the interrupt. According to the HDA specification, the guest is also not required to use interrupts, but can also sync itself by polling the LPIB registers. This patch will introduce high frequency (1000Hz) timers that interface with the device and allow for much smoother emulation of the LPIB registers. Since the timing is now provided by these timers, the need to wait for buffer completion interrupts also ceases. Signed-off-by: Martin Schrodt Signed-off-by: Gerd Hoffmann Message-id: 20180622111200.30561-2-kraxel@redhat.com Message-id: 20171015184033.2951-3-martin@schrodt.org [ kraxel: keep old code for compatibility with older qemu versions, add property to switch code paths at runtime ] [ kraxel: new code is disabled by default, use-timer=on enables it ] Signed-off-by: Gerd Hoffmann --- hw/audio/hda-codec.c | 275 ++++++++++++++++++++++++++++++++++++++----- hw/audio/intel-hda.c | 7 -- 2 files changed, 243 insertions(+), 39 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index e8aa7842e6..c62e78c859 100644 --- a/hw/audio/hda-codec.c +++ b/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" @@ -174,55 +185,201 @@ struct HDAAudioState { /* properties */ uint32_t debug; bool mixer; + bool use_timer; }; +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; + + 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; + } + } +} + +static void hda_audio_compat_input_cb(void *opaque, int avail) +{ + HDAAudioStream *st = opaque; + int recv = 0; + int len; + bool rc; + + while (avail - recv >= sizeof(st->compat_buf)) { + if (st->compat_bpos != sizeof(st->compat_buf)) { + len = AUD_read(st->voice.in, st->compat_buf + st->compat_bpos, + sizeof(st->compat_buf) - st->compat_bpos); + st->compat_bpos += len; + recv += len; + if (st->compat_bpos != sizeof(st->compat_buf)) { + break; + } + } + rc = hda_codec_xfer(&st->state->hda, st->stream, false, + st->compat_buf, sizeof(st->compat_buf)); + if (!rc) { + break; + } + st->compat_bpos = 0; + } +} + +static void hda_audio_compat_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)) { + while (avail - sent >= sizeof(st->compat_buf)) { + if (st->compat_bpos == sizeof(st->compat_buf)) { rc = hda_codec_xfer(&st->state->hda, st->stream, true, - st->buf, sizeof(st->buf)); + st->compat_buf, sizeof(st->compat_buf)); if (!rc) { break; } - st->bpos = 0; + st->compat_bpos = 0; } - len = AUD_write(st->voice.out, st->buf + st->bpos, - sizeof(st->buf) - st->bpos); - st->bpos += len; + len = AUD_write(st->voice.out, st->compat_buf + st->compat_bpos, + sizeof(st->compat_buf) - st->compat_bpos); + st->compat_bpos += len; sent += len; - if (st->bpos != sizeof(st->buf)) { + if (st->compat_bpos != sizeof(st->compat_buf)) { break; } } @@ -239,6 +396,17 @@ 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 (st->state->use_timer) { + 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 { @@ -274,6 +442,9 @@ static void hda_audio_set_amp(HDAAudioStream *st) static void hda_audio_setup(HDAAudioStream *st) { + bool use_timer = st->state->use_timer; + audio_callback_fn cb; + if (st->node == NULL) { return; } @@ -283,13 +454,25 @@ static void hda_audio_setup(HDAAudioStream *st) fmt2name[st->as.fmt], st->as.freq); if (st->output) { + if (use_timer) { + cb = hda_audio_output_cb; + st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hda_audio_output_timer, st); + } else { + cb = hda_audio_compat_output_cb; + } st->voice.out = AUD_open_out(&st->state->card, st->voice.out, - st->node->name, st, - hda_audio_output_cb, &st->as); + st->node->name, st, cb, &st->as); } else { + if (use_timer) { + cb = hda_audio_input_cb; + st->buft = timer_new_ns(QEMU_CLOCK_VIRTUAL, + hda_audio_input_timer, st); + } else { + cb = hda_audio_compat_input_cb; + } st->voice.in = AUD_open_in(&st->state->card, st->voice.in, - st->node->name, st, - hda_audio_input_cb, &st->as); + st->node->name, st, cb, &st->as); } } @@ -505,7 +688,7 @@ 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->compat_bpos = sizeof(st->compat_buf); st->output = true; } else { st->output = false; @@ -532,6 +715,9 @@ static void hda_audio_exit(HDACodecDevice *hda) if (st->node == NULL) { continue; } + if (a->use_timer) { + timer_del(st->buft); + } if (st->output) { AUD_close_out(&a->card, st->voice.out); } else { @@ -581,6 +767,26 @@ static void hda_audio_reset(DeviceState *dev) } } +static bool vmstate_hda_audio_stream_buf_needed(void *opaque) +{ + HDAAudioStream *st = opaque; + return st->state->use_timer; +} + +static const VMStateDescription vmstate_hda_audio_stream_buf = { + .name = "hda-audio-stream/buffer", + .version_id = 1, + .needed = vmstate_hda_audio_stream_buf_needed, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(buf, HDAAudioStream), + VMSTATE_INT64(rpos, HDAAudioStream), + VMSTATE_INT64(wpos, HDAAudioStream), + VMSTATE_TIMER_PTR(buft, HDAAudioStream), + VMSTATE_INT64(buft_start, HDAAudioStream), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_hda_audio_stream = { .name = "hda-audio-stream", .version_id = 1, @@ -592,9 +798,13 @@ 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() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_hda_audio_stream_buf, + NULL } }; @@ -615,6 +825,7 @@ static const VMStateDescription vmstate_hda_audio = { static Property hda_audio_properties[] = { DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), + DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 948268afd8..23a2cf6484 100644 --- a/hw/audio/intel-hda.c +++ b/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; From 0a373bb310c1533e24aa5e3edbf206507fb342ea Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:11:57 +0200 Subject: [PATCH 4/8] audio/hda: turn some dprintfs into trace points MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gerd Hoffmann Reviewed-by: Philippe Mathieu-Daudé Message-id: 20180622111200.30561-3-kraxel@redhat.com --- hw/audio/hda-codec.c | 9 ++++----- hw/audio/trace-events | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index c62e78c859..a08516cbf8 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -24,6 +24,7 @@ #include "intel-hda.h" #include "intel-hda-defs.h" #include "audio/audio.h" +#include "trace.h" /* -------------------------------------------------------------------------- */ @@ -394,8 +395,7 @@ static void hda_audio_set_running(HDAAudioStream *st, bool running) return; } st->running = running; - dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, - st->running ? "on" : "off", st->stream); + trace_hda_audio_running(st->node->name, st->stream, st->running); if (st->state->use_timer) { if (running) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -449,9 +449,8 @@ static void hda_audio_setup(HDAAudioStream *st) return; } - dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n", - st->node->name, st->as.nchannels, - fmt2name[st->as.fmt], st->as.freq); + trace_hda_audio_format(st->node->name, st->as.nchannels, + fmt2name[st->as.fmt], st->as.freq); if (st->output) { if (use_timer) { diff --git a/hw/audio/trace-events b/hw/audio/trace-events index fa1646d169..03340b9359 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -17,3 +17,7 @@ milkymist_ac97_in_cb(int avail, uint32_t remaining) "avail %d remaining %u" milkymist_ac97_in_cb_transferred(int transferred) "transferred %d" milkymist_ac97_out_cb(int free, uint32_t remaining) "free %d remaining %u" milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" + +# hw/audio/hda-codec.c +hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" +hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" From 8ced0669237b2bbedac3e4ce6fcf7aaaafaae663 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:11:58 +0200 Subject: [PATCH 5/8] audio/hda: tweak timer adjust logic We have some jitter in the audio timer call frequency and buffer sizes. So it is rather pointless trying to be very exact, effect is a constant up+down adjustment. So adjust only in case we are off too much. Signed-off-by: Gerd Hoffmann Message-id: 20180622111200.30561-4-kraxel@redhat.com --- hw/audio/hda-codec.c | 20 +++++++++++++------- hw/audio/trace-events | 1 + 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index a08516cbf8..870448a687 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -129,7 +129,6 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) #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) @@ -196,13 +195,20 @@ static inline int64_t hda_bytes_per_second(HDAAudioStream *st) 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; + int64_t limit = B_SIZE / 8; + int64_t corr = 0; + + if (target_pos > limit) { + corr = HDA_TIMER_TICKS; } + if (target_pos < -limit) { + corr = -HDA_TIMER_TICKS; + } + if (corr == 0) { + return; + } + + trace_hda_audio_adjust(st->node->name, target_pos); atomic_fetch_add(&st->buft_start, corr); } diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 03340b9359..30112d97c4 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -21,3 +21,4 @@ milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" # hw/audio/hda-codec.c hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" +hda_audio_adjust(const char *stream, int pos) "st %s, pos %d" From 4501ee16c76e89e0a2b2beb95f3b93f965997391 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:11:59 +0200 Subject: [PATCH 6/8] audio/hda: detect output buffer overruns If some event caused some larger playback hickup the fine-grained timer adjust isn't able to recover. Use a buffer overruns as indicator for that. Reset timer adjust logic in case we detected one. Signed-off-by: Gerd Hoffmann Message-id: 20180622111200.30561-5-kraxel@redhat.com --- hw/audio/hda-codec.c | 9 +++++++++ hw/audio/trace-events | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 870448a687..ac67b9aada 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -325,6 +325,15 @@ static void hda_audio_output_cb(void *opaque, int avail) int64_t to_transfer = audio_MIN(wpos - rpos, avail); + if (wpos - rpos == B_SIZE) { + /* drop buffer, reset timer adjust */ + st->rpos = 0; + st->wpos = 0; + st->buft_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + trace_hda_audio_overrun(st->node->name); + return; + } + hda_timer_sync_adjust(st, (wpos - rpos) - to_transfer - (B_SIZE >> 1)); while (to_transfer) { diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 30112d97c4..5891b4e2b9 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -22,3 +22,4 @@ milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" hda_audio_adjust(const char *stream, int pos) "st %s, pos %d" +hda_audio_overrun(const char *stream) "st %s" From bc753dc09ff33d99bc9004d7286c50de1d5bece6 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Fri, 22 Jun 2018 13:12:00 +0200 Subject: [PATCH 7/8] audio/hda: enable new timer code by default. Also add a compat property to disable it for old machine types, needed for live migration compatibility. Signed-off-by: Gerd Hoffmann Message-id: 20180622111200.30561-6-kraxel@redhat.com --- hw/audio/hda-codec.c | 2 +- include/hw/compat.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index ac67b9aada..fc4945086b 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -839,7 +839,7 @@ static const VMStateDescription vmstate_hda_audio = { static Property hda_audio_properties[] = { DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), DEFINE_PROP_BOOL("mixer", HDAAudioState, mixer, true), - DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, false), + DEFINE_PROP_BOOL("use-timer", HDAAudioState, use_timer, true), DEFINE_PROP_END_OF_LIST(), }; diff --git a/include/hw/compat.h b/include/hw/compat.h index 563908b874..44d5964060 100644 --- a/include/hw/compat.h +++ b/include/hw/compat.h @@ -6,6 +6,10 @@ .driver = "migration",\ .property = "decompress-error-check",\ .value = "off",\ + },{\ + .driver = "hda-audio",\ + .property = "use-timer",\ + .value = "false",\ }, #define HW_COMPAT_2_11 \ From 441b345313f1355fb2464f3b37a07a1ded85b61a Mon Sep 17 00:00:00 2001 From: Nia Alarie Date: Fri, 16 Mar 2018 14:40:47 +0000 Subject: [PATCH 8/8] audio: Convert use of atoi to qemu_strtoi If qemu_strtoi indicates an error, return the default value. Signed-off-by: Nia Alarie Message-Id: <20180316144047.30904-1-nia.alarie@gmail.com> Signed-off-by: Gerd Hoffmann --- audio/audio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 6eccdb17ee..d6e91901aa 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -335,9 +335,8 @@ static int audio_get_conf_int (const char *key, int defval, int *defaultp) char *strval; strval = getenv (key); - if (strval) { + if (strval && !qemu_strtoi(strval, NULL, 10, &val)) { *defaultp = 0; - val = atoi (strval); return val; } else {